blob: 84ad0e677cf29ca00fbc60ca2d0762f6b5bcff73 [file] [log] [blame]
// ============================================================================
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