| // ============================================================================ | |
| 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 | |