| // ============================================================================ |
| |
| module iserdes_idelay_histogram # |
| ( |
| parameter UART_PRESCALER = 868, // UART prescaler |
| parameter ISERDES_MODE = "DDR", |
| parameter ISERDES_WIDTH = 8 |
| ) |
| ( |
| // Closk & reset |
| input wire CLK, |
| input wire RST, |
| |
| // UART |
| input wire UART_RX, |
| output wire UART_TX, |
| |
| // Input and output pins |
| output wire O, |
| input wire I, |
| |
| // Signals at the input of the comparator (for monitoring) |
| output wire REF_O, |
| output wire REF_I, |
| output wire REF_C, |
| |
| // IDELAY delay setting output |
| output wire [4:0] DELAY |
| ); |
| |
| // ============================================================================ |
| // Data generator |
| |
| // Serialized data clock generator |
| reg [2:0] ce_cnt; |
| wire ce_x2_p; |
| wire ce_x2_n; |
| wire ce_x1_p; |
| wire ce_x1_n; |
| |
| initial ce_cnt <= 0; |
| always @(posedge CLK) |
| ce_cnt <= ce_cnt + 1; |
| |
| assign ce_x2_p = (ce_cnt[0:0] == 0) && !ce_cnt[1]; |
| assign ce_x2_n = (ce_cnt[0:0] == 0) && ce_cnt[1]; |
| |
| assign ce_x1_p = (ce_cnt[1:0] == 0) && !ce_cnt[2]; |
| assign ce_x1_n = (ce_cnt[1:0] == 0) && ce_cnt[2]; |
| |
| // LFSR |
| wire [15:0] lfsr_r; |
| |
| lfsr lfsr |
| ( |
| .clk (CLK), |
| .rst (RST), |
| .ce ((ISERDES_MODE == "SDR") ? ce_x1_p : ce_x2_p), |
| .r (lfsr_r) |
| ); |
| |
| // Data serializer |
| reg o_clk; |
| reg o_stb; |
| wire o_dat; |
| |
| always @(posedge CLK) |
| if (RST) |
| o_clk <= 1'b0; |
| else if (ce_x1_p) |
| o_clk <= 1'b1; |
| else if (ce_x1_n) |
| o_clk <= 1'b0; |
| |
| always @(posedge CLK) |
| if (RST) |
| o_stb <= 1'b0; |
| else begin |
| if (ISERDES_MODE == "SDR") |
| o_stb <= ce_x1_p; |
| else if (ISERDES_MODE == "DDR") |
| o_stb <= ce_x2_p; |
| end |
| |
| assign o_dat = lfsr_r[0]; |
| |
| // Output the data, the clock is routed internally. |
| assign O = o_dat; |
| |
| // ============================================================================ |
| // Data input with IDELAY and ISERDES |
| wire dly_dat; |
| wire dly_ld; |
| wire [4:0] dly_cnt; |
| |
| wire [7:0] ser_dat; |
| wire ser_dat_ref; |
| wire ser_rst; |
| reg ser_clk; |
| wire ser_clkdiv; |
| |
| // Delay the ISERDES clock by 1 CLK. This aligns serialized clock and data |
| // edges and allow to expose influende of differend IDELAY settings. |
| always @(posedge CLK) |
| ser_clk <= o_clk; |
| |
| // ISERDES reset generator (required for it to work properly!) |
| reg [3:0] ser_rst_sr; |
| initial ser_rst_sr <= 4'hF; |
| |
| always @(posedge ser_clkdiv or posedge RST) |
| if (RST) ser_rst_sr <= 4'hF; |
| else ser_rst_sr <= ser_rst_sr >> 1; |
| |
| assign ser_rst = ser_rst_sr[0]; |
| |
| // BUFR - generation of CLKDIV |
| localparam DIVIDE = (ISERDES_MODE == "SDR" && ISERDES_WIDTH == 2) ? "2" : |
| (ISERDES_MODE == "SDR" && ISERDES_WIDTH == 3) ? "3" : |
| (ISERDES_MODE == "SDR" && ISERDES_WIDTH == 4) ? "4" : |
| (ISERDES_MODE == "SDR" && ISERDES_WIDTH == 5) ? "5" : |
| (ISERDES_MODE == "SDR" && ISERDES_WIDTH == 6) ? "6" : |
| (ISERDES_MODE == "SDR" && ISERDES_WIDTH == 7) ? "7" : |
| (ISERDES_MODE == "SDR" && ISERDES_WIDTH == 8) ? "8" : |
| |
| (ISERDES_MODE == "DDR" && ISERDES_WIDTH == 4) ? "2" : |
| (ISERDES_MODE == "DDR" && ISERDES_WIDTH == 6) ? "3" : |
| (ISERDES_MODE == "DDR" && ISERDES_WIDTH == 8) ? "4" : "BYPASS"; |
| BUFR # |
| ( |
| .BUFR_DIVIDE (DIVIDE) |
| ) |
| bufr |
| ( |
| .I (ser_clk), |
| .O (ser_clkdiv), |
| .CLR (RST), |
| .CE (1'd1) |
| ); |
| |
| // IDELAY |
| IDELAYE2 # |
| ( |
| .IDELAY_TYPE ("VAR_LOAD"), |
| .DELAY_SRC ("IDATAIN") |
| ) |
| idelay |
| ( |
| .IDATAIN (I), |
| .DATAOUT (dly_dat), |
| |
| .C (CLK), |
| .LD (dly_ld), |
| .CNTVALUEIN (dly_cnt), |
| .CNTVALUEOUT (DELAY) |
| ); |
| |
| // ISERDES |
| ISERDESE2 # |
| ( |
| .DATA_RATE (ISERDES_MODE), |
| .DATA_WIDTH (ISERDES_WIDTH), |
| .INTERFACE_TYPE ("NETWORKING"), |
| .NUM_CE (1), |
| .IOBDELAY ("BOTH"), |
| .IS_CLKB_INVERTED (1) |
| ) |
| iserdes |
| ( |
| .DDLY (dly_dat), |
| .O (ser_dat_ref), |
| .Q1 (ser_dat[0]), |
| .Q2 (ser_dat[1]), |
| .Q3 (ser_dat[2]), |
| .Q4 (ser_dat[3]), |
| .Q5 (ser_dat[4]), |
| .Q6 (ser_dat[5]), |
| .Q7 (ser_dat[6]), |
| .Q8 (ser_dat[7]), |
| .CLK (ser_clk), |
| .CLKB (ser_clk), |
| .CLKDIV (ser_clkdiv), |
| .CE1 (1'b1), |
| .RST (ser_rst), |
| .BITSLIP (1'b0) |
| ); |
| |
| // ============================================================================ |
| // Data serializer |
| |
| // ISERDES clock edge detector |
| reg ser_clk_r; |
| reg ser_clkdiv_r; |
| |
| always @(posedge CLK) |
| ser_clk_r <= ser_clk; |
| always @(posedge CLK) |
| ser_clkdiv_r <= ser_clkdiv; |
| |
| wire ser_clk_e = (ISERDES_MODE == "SDR") ? (ser_clk && !ser_clk_r) : |
| (ISERDES_MODE == "DDR") ? (ser_clk ^ ser_clk_r) : 0; |
| |
| wire ser_clkdiv_e = ser_clkdiv && !ser_clkdiv_r; |
| |
| // Data serializer |
| reg [7:0] ser2_sr; |
| wire ser2_stb; |
| wire ser2_dat; |
| |
| always @(posedge CLK) |
| if (ser_clk_e && ser_clkdiv_e) |
| ser2_sr <= ser_dat; |
| else if (ser_clk_e) |
| ser2_sr <= ser2_sr << 1; |
| |
| assign ser2_stb = ser_clk_e; |
| assign ser2_dat = ser2_sr[ISERDES_WIDTH-1]; |
| |
| // ============================================================================ |
| // Output data delay needed to compensate ISERDES latency |
| reg [23:0] bit_sr; |
| reg bit_stb; |
| wire bit_dat; |
| |
| always @(posedge CLK) |
| bit_stb <= o_stb; |
| always @(posedge CLK) |
| if (o_stb) bit_sr <= (bit_sr << 1) | o_dat; |
| |
| localparam BIT_DELAY = (ISERDES_MODE == "SDR") ? (ISERDES_WIDTH * 2 - 2) : |
| /*(ISERDES_MODE == "DDR")*/ (ISERDES_WIDTH + 4); |
| |
| assign bit_dat = bit_sr[BIT_DELAY]; |
| |
| // ============================================================================ |
| // Data comparator |
| reg cmp_s0_stb; |
| reg cmp_s0_o_dat; |
| reg cmp_s0_i_dat; |
| |
| reg cmp_s1_stb; |
| reg cmp_s1_err; |
| |
| always @(posedge CLK) |
| if (RST) |
| cmp_s0_stb <= 1'b0; |
| else |
| cmp_s0_stb <= bit_stb; |
| |
| always @(posedge CLK) |
| if (o_stb) |
| cmp_s0_o_dat <= bit_dat; |
| always @(posedge CLK) |
| if (o_stb) |
| cmp_s0_i_dat <= ser2_dat; |
| |
| |
| always @(posedge CLK) |
| if (RST) |
| cmp_s1_stb <= 1'b0; |
| else |
| cmp_s1_stb <= cmp_s0_stb; |
| |
| always @(posedge CLK) |
| cmp_s1_err <= cmp_s0_o_dat ^ cmp_s0_i_dat; |
| |
| |
| reg o_dat_r; |
| |
| always @(posedge CLK) |
| o_dat_r <= o_dat; |
| |
| // Reference output |
| assign REF_O = o_dat_r; |
| assign REF_I = ser_dat_ref; |
| assign REF_C = ser_clk; |
| |
| // ============================================================================ |
| // Error counter |
| wire cnt_stb; |
| wire [32*24-1:0] cnt_dat; |
| |
| error_counter # |
| ( |
| .COUNT_WIDTH (24), |
| .DELAY_TAPS (32), |
| |
| .TRIGGER_INTERVAL (50000000), |
| .HOLDOFF_TIME (100), |
| .MEASURE_TIME (10000) |
| ) |
| error_counter |
| ( |
| .CLK (CLK), |
| .RST (RST), |
| |
| .I_STB (cmp_s1_stb), |
| .I_ERR (cmp_s1_err), |
| |
| .DLY_LD (dly_ld), |
| .DLY_CNT(dly_cnt), |
| |
| .O_STB (cnt_stb), |
| .O_DAT (cnt_dat) |
| ); |
| |
| // ============================================================================ |
| // Message formatter |
| wire uart_x_stb; |
| wire [7:0] uart_x_dat; |
| |
| message_formatter # |
| ( |
| .WIDTH (24), |
| .COUNT (32), |
| .TX_INTERVAL (UART_PRESCALER * 11) // 10 bits plus one more. |
| ) |
| message_formatter |
| ( |
| .CLK (CLK), |
| .RST (RST), |
| |
| .I_STB (cnt_stb), |
| .I_DAT (cnt_dat), |
| |
| .O_STB (uart_x_stb), |
| .O_DAT (uart_x_dat) |
| ); |
| |
| // ============================================================================ |
| // UART |
| |
| // Baudrate prescaler initializer |
| reg [7:0] reg_div_we_sr; |
| wire reg_div_we; |
| |
| always @(posedge CLK) |
| if (RST) reg_div_we_sr <= 8'h01; |
| else reg_div_we_sr <= {reg_div_we_sr[6:0], 1'd0}; |
| |
| assign reg_div_we = reg_div_we_sr[7]; |
| |
| // The UART |
| simpleuart uart |
| ( |
| .clk (CLK), |
| .resetn (!RST), |
| |
| .ser_rx (UART_RX), |
| .ser_tx (UART_TX), |
| |
| .reg_div_we ({reg_div_we, reg_div_we, reg_div_we, reg_div_we}), |
| .reg_div_di (UART_PRESCALER), |
| .reg_div_do (), |
| |
| .reg_dat_we (uart_x_stb), |
| .reg_dat_re (1'd0), |
| .reg_dat_di ({24'd0, uart_x_dat}), |
| .reg_dat_do (), |
| .reg_dat_wait () |
| ); |
| |
| // Debug |
| always @(posedge CLK) |
| if (uart_x_stb) |
| $display("%c", uart_x_dat); |
| |
| endmodule |
| |