Instantiates the LVDS ISERDES interfaces for one image sensor channel, along with its bit, word, and channel alignment training hardware.
The training of all channels will be coordinated one level up.
`default_nettype none
module sensor_data_rx_channel
#(
// 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 is allowed
// For CDC FIFO of data into main clock domain
parameter FIFO_DEPTH = 16,
parameter FIFO_RAMSTYLE = "distributed"
)
(
// clk_rxio domain (data and training)
input wire clk_rxio_io,
input wire clk_rxio_frame,
input wire rst_rxio_frame_n,
input wire datain_n, // High-speed serial I/O data in
input wire 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-1:0] datain_parallel, // Deserialized positive data, framed by datain_parallel_valid
output wire sync_train, // Signal sensor to output training word
input wire start_bit_word_alignment, // Preferably a one-cycle pulse
output wire done_bit_word_alignment, // Pulsed high means serdes data is bit and word-aligned
input wire start_channel_alignment, // Preferably a one-cycle pulse
output wire done_channel_alignment // Pulsed high means serdes data is channel-aligned
);
`include "clog2_function.vh"
localparam CHANNEL_DELAY_WIDTH = clog2(CHANNEL_PIPELINE_DEPTH);
// Hardcoded in lower modules. Do not change.
localparam TAP_COUNTER_WIDTH = 5;
wire [TAP_COUNTER_WIDTH-1:0] tap_p_current;
wire [TAP_COUNTER_WIDTH-1:0] tap_p_load_value;
wire tap_p_load;
wire datain_p_bitslip;
wire datain_p_wordflip;
wire [TAP_COUNTER_WIDTH-1:0] tap_n_current;
wire [TAP_COUNTER_WIDTH-1:0] tap_n_load_value;
wire tap_n_load;
wire datain_n_bitslip;
wire datain_n_wordflip;
wire channel_delay_valid; // Load a new channel alignment tap delay
wire channel_delay_ready;
wire [CHANNEL_DELAY_WIDTH-1:0] channel_delay;
wire datain_parallel_valid_internal;
wire datain_parallel_ready_internal;
wire [WORD_WIDTH-1:0] datain_p_parallel_internal;
wire [WORD_WIDTH-1:0] datain_n_parallel_internal;
iserdes_1_to_12_data_diff
#(
// For the input buffer of each data line
.IBUF_DIFF_TERM (IBUF_DIFF_TERM),
.IBUF_LOW_PWR (IBUF_LOW_PWR),
.IBUF_IOSTANDARD (IBUF_IOSTANDARD),
.IODELAY_GROUP (IODELAY_GROUP), // Must match IODELAY_GROUP applied to IDELAYCTRL module in same I/O Bank.
.IODELAY_HIGH_PERFORMANCE_MODE (IODELAY_HIGH_PERFORMANCE_MODE), // Should match that of the clock in clocking.v
.CHANNEL_PIPELINE_DEPTH (CHANNEL_PIPELINE_DEPTH)
)
input_data_serdes
(
.clk_rxio_io (clk_rxio_io), // High-speed I/O clock for incoming serial data (pins and SERDES)
.clk_rxio_frame (clk_rxio_frame), // Framing pulse for serial-parallel conversion (SERDES)
.rst_rxio_frame_n (rst_rxio_frame_n),
.datain_n (datain_n), // High-speed serial I/O data in
.datain_p (datain_p),
.incdec_p_enable (1'b0), // Enable increment/decrement of delay tap
.incdec_p (1'b0), // Increment (1) or decrement (0) delay tap
.tap_p_current (tap_p_current), // Current value of delay tap
.tap_p_load_value (tap_p_load_value), // New value of delay tap
.tap_p_load (tap_p_load), // Load new delay tap value
.datain_p_bitslip (datain_p_bitslip), // Pulse to shift output word
.datain_p_wordflip (datain_p_wordflip), // Pulse to swap halfwords of the output word
.incdec_n_enable (1'b0), // Enable increment/decrement of delay tap
.incdec_n (1'b0), // Increment (1) or decrement (0) delay tap
.tap_n_current (tap_n_current), // Current value of delay tap
.tap_n_load_value (tap_n_load_value), // New value of delay tap
.tap_n_load (tap_n_load), // Load new delay tap value
.datain_n_bitslip (datain_n_bitslip), // Pulse to shift output word
.datain_n_wordflip (datain_n_wordflip), // Pulse to swap halfwords of the output word
.channel_delay_valid (channel_delay_valid),
.channel_delay_ready (channel_delay_ready),
.channel_delay (channel_delay),
.datain_parallel_valid (datain_parallel_valid_internal), // There is a handshake, but no slack for stalling!
.datain_parallel_ready (datain_parallel_ready_internal), // Must always be ready before valid!
.datain_p_parallel (datain_p_parallel_internal), // Deserialized data, framed by clk_rxio_frame
.datain_n_parallel (datain_n_parallel_internal) // Deserialized data, framed by clk_rxio_frame
);
Feed training module and output FIFO the same deserialized serdes data. Since there is no backpressure possible, this should mostly optimize away.
wire datain_parallel_valid_training;
wire datain_parallel_ready_training;
wire [WORD_WIDTH-1:0] datain_p_parallel_training;
wire [WORD_WIDTH-1:0] datain_n_parallel_training;
wire datain_parallel_valid_fifo;
wire datain_parallel_ready_fifo;
wire [WORD_WIDTH-1:0] datain_p_parallel_fifo;
// verilator lint_off UNUSED
wire [WORD_WIDTH-1:0] datain_n_parallel_fifo;
// verilator lint_on UNUSED
Pipeline_Fork_Lazy
#(
.WORD_WIDTH (WORD_WIDTH + WORD_WIDTH),
.OUTPUT_COUNT (2)
)
training_data_fork
(
.input_valid (datain_parallel_valid_internal),
.input_ready (datain_parallel_ready_internal),
.input_data ({datain_n_parallel_internal, datain_p_parallel_internal}),
.output_valid ({datain_parallel_valid_training, datain_parallel_valid_fifo}),
.output_ready ({datain_parallel_ready_training, datain_parallel_ready_fifo}),
.output_data ({{datain_n_parallel_training, datain_p_parallel_training}, {datain_n_parallel_fifo, datain_p_parallel_fifo}})
);
NOTE: sink this output until the data is correct (after training) and has somewhere to go (after memory calibration is complete, or other logic is ready to receive).
wire datain_parallel_valid_main;
wire datain_parallel_ready_main;
wire [WORD_WIDTH-1:0] datain_parallel_main;
CDC_FIFO_Buffer
#(
.WORD_WIDTH (WORD_WIDTH),
.DEPTH (FIFO_DEPTH),
.RAMSTYLE (FIFO_RAMSTYLE),
.CIRCULAR_BUFFER (0), // non-zero to enable
.CDC_EXTRA_STAGES (0)
)
input_data_fifo
(
.input_clock (clk_rxio_frame),
.input_clear (~rst_rxio_frame_n),
.input_valid (datain_parallel_valid_fifo),
.input_ready (datain_parallel_ready_fifo),
.input_data (datain_p_parallel_fifo),
.output_clock (clk_main),
.output_clear (~rst_main_n),
.output_valid (datain_parallel_valid_main),
.output_ready (datain_parallel_ready_main),
.output_data (datain_parallel_main)
);
Pipeline_Sink
#(
.WORD_WIDTH (WORD_WIDTH),
.IMPLEMENTATION ("AND")
)
input_data_fifo_sink
(
.sink (datain_parallel_enable == 1'b0),
.input_valid (datain_parallel_valid_main),
.input_ready (datain_parallel_ready_main),
.input_data (datain_parallel_main),
.output_valid (datain_parallel_valid),
.output_ready (datain_parallel_ready),
.output_data (datain_parallel)
);
wire datain_parallel_valid_training_pipelined;
wire datain_parallel_ready_training_pipelined;
wire [WORD_WIDTH-1:0] datain_p_parallel_training_pipelined;
wire [WORD_WIDTH-1:0] datain_n_parallel_training_pipelined;
We need a bit of pipelining to break a critical path here between the iserdes data and the training modules using the data. FIXME: this may not be needed after the move to the framing clock
Skid_Buffer_Pipeline
#(
.WORD_WIDTH (WORD_WIDTH + WORD_WIDTH),
.PIPE_DEPTH (1)
)
training_data_pipeline
(
// If PIPE_DEPTH is zero, these are unused
// verilator lint_off UNUSED
.clock (clk_rxio_frame),
.clear (~rst_rxio_frame_n),
// verilator lint_on UNUSED
.input_valid (datain_parallel_valid_training),
.input_ready (datain_parallel_ready_training),
.input_data ({datain_n_parallel_training, datain_p_parallel_training}),
.output_valid (datain_parallel_valid_training_pipelined),
.output_ready (datain_parallel_ready_training_pipelined),
.output_data ({datain_n_parallel_training_pipelined, datain_p_parallel_training_pipelined})
);
iserdes_training
#(
// Bit alignment parameters
.TAP_OFFSET (TAP_OFFSET), // How much the initial N IDELAY lags the P IDELAY, for bit alignment.
.WORD_WIDTH (WORD_WIDTH),
.TRAINING_WORD (TRAINING_WORD),
.BITSLIP_INTERVAL (BITSLIP_INTERVAL),
.CHANNEL_PIPELINE_DEPTH (CHANNEL_PIPELINE_DEPTH)
)
input_serdes_training
(
// clk_rxio_frame domain, for SERDES data and control
.clk_rxio_frame (clk_rxio_frame),
.rst_rxio_frame_n (rst_rxio_frame_n),
.datain_parallel_valid (datain_parallel_valid_training_pipelined), // There is a handshake, but no slack for stalling!
.datain_parallel_ready (datain_parallel_ready_training_pipelined), // Must always be ready before valid!
.datain_p_parallel (datain_p_parallel_training_pipelined), // Deserialized positive data, framed by datain_parallel_valid
.datain_n_parallel (datain_n_parallel_training_pipelined), // Deserialized negative data, framed by datain_parallel_valid
.tap_p_current (tap_p_current), // Current value of delay tap
.tap_p_load_value (tap_p_load_value), // New value of delay tap
.tap_p_load (tap_p_load), // Load new delay tap value
.datain_p_bitslip (datain_p_bitslip), // Pulse to shift output word
.datain_p_wordflip (datain_p_wordflip), // Pulse to swap halfwords of the output word
.tap_n_current (tap_n_current), // Current value of delay tap
.tap_n_load_value (tap_n_load_value), // New value of delay tap
.tap_n_load (tap_n_load), // Load new delay tap value
.datain_n_bitslip (datain_n_bitslip), // Pulse to shift output word
.datain_n_wordflip (datain_n_wordflip), // Pulse to swap halfwords of the output word
.channel_delay_valid (channel_delay_valid), // Load a new channel alignment tap delay
.channel_delay_ready (channel_delay_ready),
.channel_delay (channel_delay),
// System control signals in clk_main domain
.clk_main (clk_main), // General logic clock
.rst_main_n (rst_main_n),
.sync_train (sync_train), // Signal sensor to output training word
.start_bit_word_alignment (start_bit_word_alignment), // Preferably a one-cycle pulse
.done_bit_word_alignment (done_bit_word_alignment), // Pulsed high means serdes data is bit and word-aligned
.start_channel_alignment (start_channel_alignment), // Preferably a one-cycle pulse
.done_channel_alignment (done_channel_alignment) // Pulsed high means serdes data is channel-aligned
);
endmodule