Instantiates the LVDS ISERDES interfaces for all image sensor channels, along with their bit, word, and channel alignment training hardware. We coordinate all the training in this module.
`default_nettype none
module sensor_data_rx_all_channels
#(
parameter CHANNEL_COUNT = 18,
// IDELAYCTRL and IDELAY
parameter IODELAY_GROUP = "IMAGE_RX",
parameter IODELAY_HIGH_PERFORMANCE_MODE = "FALSE", // Should match that of the clock in clocking.v
// Input Buffers
parameter IBUF_DIFF_TERM = "FALSE", // Since Bank 14 has Vcco of 3V3, this must be off (see UG471, LVDS_25 I/O Standard)
parameter IBUF_LOW_PWR = "TRUE",
parameter IBUF_IOSTANDARD = "LVDS_25",
// SERDES
parameter WORD_WIDTH = 12, // CONSTANT, DO NOT CHANGE.
// For bit alignment
parameter TAP_OFFSET = 2, // 2 or greater. How much the initial N IDELAY lags the P IDELAY, for bit alignment.
// For word alignment
parameter [WORD_WIDTH-1:0] TRAINING_WORD = 'b011110001101,
parameter BITSLIP_INTERVAL = 3, // 3 or greater
// For channel alignment
parameter CHANNEL_PIPELINE_DEPTH = 16, // only 16 or 32 allowed
// For CDC FIFO
parameter FIFO_DEPTH = 16,
parameter FIFO_RAMSTYLE = "distributed",
parameter WORD_WIDTH_TOTAL = WORD_WIDTH * CHANNEL_COUNT
)
(
// clk_rxio domain (data and training)
input wire clk_rxio_io,
input wire clk_rxio_frame,
input wire rst_rxio_frame_n,
input wire clk_rxio_idelayref,
input wire rst_rxio_idelayref_n,
output wire idelayctrl_ready, // Reset and retrain if this drops
input wire [CHANNEL_COUNT-1:0] datain_n, // High-speed serial I/O data in
input wire [CHANNEL_COUNT-1:0] datain_p,
// clk_main domain (control)
input wire clk_main,
input wire rst_main_n,
input wire datain_parallel_enable, // While low, sink the data output (datain_parallel_valid stays low, data is discarded)
output wire datain_parallel_valid, // There is a handshake, but only some FIFO slack for stalling!
input wire datain_parallel_ready, // Do not let FIFO fill, else data is lost.
output wire [WORD_WIDTH_TOTAL-1:0] datain_parallel, // Deserialized positive data, framed by datain_parallel_valid
output reg sync_train, // Signal sensor to output training word
input wire start_alignment, // Preferably a one-cycle pulse
output wire done_alignment // Pulsed high means serdes data is word-aligned
);
localparam CHANNEL_COUNT_ZERO = {CHANNEL_COUNT{1'b0}};
localparam CHANNEL_COUNT_ALL_ONES = {CHANNEL_COUNT{1'b1}};
initial begin
sync_train = 1'b0;
end
TODO: add logic to reset the IDELAYCTRL before each training, and to signal a loss of IDELAYCTRL (but not restart training)
iserdes_idelayctrl
#(
.IODELAY_GROUP (IODELAY_GROUP)
)
input_delay_calibration
(
.clk_rxio_logic (clk_rxio_idelayref), // High-speed I/O clock for incoming serial data, and reference clock
.rst_rxio_n (rst_rxio_idelayref_n),
.idelayctrl_ready (idelayctrl_ready) // If this drops, reset the IDELAYCTRL and retrain
);
wire [CHANNEL_COUNT-1:0] datain_parallel_valid_internal;
wire [CHANNEL_COUNT-1:0] datain_parallel_ready_internal;
wire [WORD_WIDTH_TOTAL-1:0] datain_parallel_internal;
wire [CHANNEL_COUNT-1:0] sync_train_channel;
wire [CHANNEL_COUNT-1:0] done_bit_word_alignment_channel;
wire [CHANNEL_COUNT-1:0] done_bit_word_alignment_channel_latched;
wire start_channel_alignment_channel; // All channel alignments start in sync
wire [CHANNEL_COUNT-1:0] done_channel_alignment_channel;
wire [CHANNEL_COUNT-1:0] done_channel_alignment_channel_latched;
generate
genvar i;
for(i=0; i < CHANNEL_COUNT; i=i+1) begin: per_channel
// The channels parameters must all match, as all channels must
// work in lock-step (once trained).
sensor_data_rx_channel
#(
// IDELAYCTRL and IDELAY
.IODELAY_GROUP (IODELAY_GROUP),
.IODELAY_HIGH_PERFORMANCE_MODE (IODELAY_HIGH_PERFORMANCE_MODE), // Should match that of the clock in clocking.v
// Input Buffers
.IBUF_DIFF_TERM (IBUF_DIFF_TERM), // Since Bank 14 has Vcco of 3V3, this must be off (see UG471, LVDS_25 I/O Standard)
.IBUF_LOW_PWR (IBUF_LOW_PWR),
.IBUF_IOSTANDARD (IBUF_IOSTANDARD),
// SERDES
.WORD_WIDTH (WORD_WIDTH), // CONSTANT, DO NOT CHANGE.
// For bit alignment
.TAP_OFFSET (TAP_OFFSET), // 2 or greater. How much the initial N IDELAY lags the P IDELAY, for bit alignment.
// For word alignment
.TRAINING_WORD (TRAINING_WORD),
.BITSLIP_INTERVAL (BITSLIP_INTERVAL), // 3 or greater
// For channel alignment
.CHANNEL_PIPELINE_DEPTH (CHANNEL_PIPELINE_DEPTH), // only 16 or 32 allowed
// For CDC FIFO of data into main clock domain
.FIFO_DEPTH (FIFO_DEPTH),
.FIFO_RAMSTYLE (FIFO_RAMSTYLE)
)
sensor_channel
(
// clk_rxio_frame domain (data and training)
.clk_rxio_io (clk_rxio_io),
.clk_rxio_frame (clk_rxio_frame),
.rst_rxio_frame_n (rst_rxio_frame_n),
.datain_n (datain_n [i]), // High-speed serial I/O data in
.datain_p (datain_p [i]),
// clk_main domain (control)
.clk_main (clk_main),
.rst_main_n (rst_main_n),
.datain_parallel_enable (datain_parallel_enable), // While low, sink the data output (datain_parallel_valid stays low, data is discarded)
.datain_parallel_valid (datain_parallel_valid_internal [i]), // There is a handshake, but only some FIFO slack for stalling!
.datain_parallel_ready (datain_parallel_ready_internal [i]), // Do not let FIFO fill, else data is lost.
.datain_parallel (datain_parallel_internal [WORD_WIDTH*i +: WORD_WIDTH]), // Deserialized positive data, framed by datain_parallel_valid
.sync_train (sync_train_channel [i]), // Signal sensor to output training word
.start_bit_word_alignment (start_alignment), // Preferably a one-cycle pulse
.done_bit_word_alignment (done_bit_word_alignment_channel [i]), // Pulsed high means serdes data is bit and word-aligned
.start_channel_alignment (start_channel_alignment_channel), // Preferably a one-cycle pulse
.done_channel_alignment (done_channel_alignment_channel [i]) // Pulsed high means serdes data is channel-aligned
);
// Channels mostly manage their training themselves, but we have
// to make sure we start the channel alignments all together, and
// only after all bit and word alignements are done. We do this so
// all channels start counting the channel delay at the same time.
// So we latch when each channel is done a stage, so we can check
// for an "all done" condition.
Pulse_Latch
#(
.RESET_VALUE (1'b0)
)
training_bit_word_done
(
.clock (clk_main),
.clear (start_alignment || ~rst_main_n),
.pulse_in (done_bit_word_alignment_channel [i]),
.level_out (done_bit_word_alignment_channel_latched [i])
);
Pulse_Latch
#(
.RESET_VALUE (1'b0)
)
training_channel_done
(
.clock (clk_main),
.clear (start_channel_alignment_channel || ~rst_main_n),
.pulse_in (done_channel_alignment_channel [i]),
.level_out (done_channel_alignment_channel_latched [i])
);
end
endgenerate
Pipeline_Join_Lazy
#(
.WORD_WIDTH (WORD_WIDTH),
.INPUT_COUNT (CHANNEL_COUNT)
)
channel_data_join
(
.input_valid (datain_parallel_valid_internal),
.input_ready (datain_parallel_ready_internal),
.input_data (datain_parallel_internal),
.output_valid (datain_parallel_valid),
.output_ready (datain_parallel_ready),
.output_data (datain_parallel)
);
reg bit_word_alignment_all_done = 1'b0;
reg channel_alignment_all_done = 1'b0;
always @(*) begin
bit_word_alignment_all_done = done_bit_word_alignment_channel_latched == CHANNEL_COUNT_ALL_ONES;
channel_alignment_all_done = done_channel_alignment_channel_latched == CHANNEL_COUNT_ALL_ONES;
end
Pulse_Generator
bit_word_all_done_signal
(
.clock (clk_main),
.level_in (bit_word_alignment_all_done),
.pulse_posedge_out (start_channel_alignment_channel),
// verilator lint_off PINCONNECTEMPTY
.pulse_negedge_out (),
.pulse_anyedge_out ()
// verilator lint_on PINCONNECTEMPTY
);
Pulse_Generator
channel_all_done_signal
(
.clock (clk_main),
.level_in (channel_alignment_all_done),
.pulse_posedge_out (done_alignment),
// verilator lint_off PINCONNECTEMPTY
.pulse_negedge_out (),
.pulse_anyedge_out ()
// verilator lint_on PINCONNECTEMPTY
);
It happens that we can simply OR-reduce all the per-channel sync_train signals: they will overlap as needed when we need continuous training data.
always @(*) begin
sync_train = sync_train_channel != CHANNEL_COUNT_ZERO;
end
endmodule