mirror of https://github.com/openXC7/prjxray.git
370 lines
7.8 KiB
Verilog
370 lines
7.8 KiB
Verilog
// ============================================================================
|
|
|
|
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
|
|
|