Performs bit alignment first, then word alignment, then channel alignment, on a 1:12 SERDES. All in the clk_rxio domain.
`default_nettype none
module iserdes_training
#(
// Bit and Word alignment parameters
parameter TAP_OFFSET = 2, // How much the initial N IDELAY lags the P IDELAY, for bit alignment.
parameter WORD_WIDTH = 12,
parameter [WORD_WIDTH-1:0] TRAINING_WORD = 'b011110001101,
parameter BITSLIP_INTERVAL = 5,
parameter CHANNEL_PIPELINE_DEPTH = 16, // Must match iserdes_1_to_12_data_diff.v parameter
// Do not set at instantiation, except in Vivado IPI
parameter TAP_COUNTER_WIDTH = 5,
parameter CHANNEL_DELAY_WIDTH = CHANNEL_PIPELINE_DEPTH >= 2 ? clog2(CHANNEL_PIPELINE_DEPTH) : 1
)
(
// clk_rxio_frame domain, for SERDES data and control
input wire clk_rxio_frame, // High-speed I/O clock for incoming serial data
input wire rst_rxio_frame_n,
input wire datain_parallel_valid, // There is a handshake, but no slack for stalling!
output wire datain_parallel_ready, // Must always be ready before valid!
input wire [WORD_WIDTH-1:0] datain_p_parallel, // Deserialized positive data, framed by datain_parallel_valid
input wire [WORD_WIDTH-1:0] datain_n_parallel, // Deserialized negative data, framed by datain_parallel_valid
input wire [TAP_COUNTER_WIDTH-1:0] tap_p_current, // Current value of delay tap
output wire [TAP_COUNTER_WIDTH-1:0] tap_p_load_value, // New value of delay tap
output wire tap_p_load, // Load new delay tap value
output reg datain_p_bitslip, // Pulse to shift output word
output reg datain_p_wordflip, // Pulse to swap halfwords of the output word
input wire [TAP_COUNTER_WIDTH-1:0] tap_n_current, // Current value of delay tap
output wire [TAP_COUNTER_WIDTH-1:0] tap_n_load_value, // New value of delay tap
output wire tap_n_load, // Load new delay tap value
output reg datain_n_bitslip, // Pulse to shift output word
output reg datain_n_wordflip, // Pulse to swap halfwords of the output word
output wire channel_delay_valid, // Load a new channel alignment tap delay
input wire channel_delay_ready,
output wire [CHANNEL_DELAY_WIDTH-1:0] channel_delay,
// System control signals in clk_main domain
input wire clk_main, // General logic clock
input wire rst_main_n,
output reg 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"
initial begin
datain_p_bitslip = 1'b0;
datain_n_bitslip = 1'b0;
datain_p_wordflip = 1'b0;
datain_n_wordflip = 1'b0;
sync_train = 1'b0;
end
Feed all training modules the same deserialized serdes data. Since there is no backpressure possible, this should mostly optimize away. Nor do we have to worry about one output stalling all the others.
wire datain_parallel_valid_bit;
wire datain_parallel_ready_bit;
wire [WORD_WIDTH-1:0] datain_p_parallel_bit;
wire [WORD_WIDTH-1:0] datain_n_parallel_bit;
wire datain_parallel_valid_word;
wire datain_parallel_ready_word;
wire [WORD_WIDTH-1:0] datain_p_parallel_word;
// verilator lint_off UNUSED
wire [WORD_WIDTH-1:0] datain_n_parallel_word;
// verilator lint_on UNUSED
wire datain_parallel_valid_channel;
wire datain_parallel_ready_channel;
wire [WORD_WIDTH-1:0] datain_p_parallel_channel;
// verilator lint_off UNUSED
wire [WORD_WIDTH-1:0] datain_n_parallel_channel;
// verilator lint_on UNUSED
Pipeline_Fork_Lazy
#(
.WORD_WIDTH (WORD_WIDTH + WORD_WIDTH),
.OUTPUT_COUNT (3)
)
training_data_fork
(
.input_valid (datain_parallel_valid),
.input_ready (datain_parallel_ready),
.input_data ({datain_n_parallel, datain_p_parallel}),
.output_valid ({datain_parallel_valid_channel, datain_parallel_valid_word, datain_parallel_valid_bit}),
.output_ready ({datain_parallel_ready_channel, datain_parallel_ready_word, datain_parallel_ready_bit}),
.output_data ({{datain_n_parallel_channel, datain_p_parallel_channel}, {datain_n_parallel_word, datain_p_parallel_word}, {datain_n_parallel_bit, datain_p_parallel_bit}})
);
The bit alignment done output triggers the start input of the word alignment.
wire sync_train_bit_alignment;
wire done_bit_alignment;
iserdes_bit_alignment
#(
.TAP_OFFSET (TAP_OFFSET), // How much the initial N IDELAY lags the P IDELAY, for bit alignment.
.WORD_WIDTH (WORD_WIDTH)
)
iserdes_bit_alignment
(
// 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_bit), // There is a handshake, but no slack for stalling!
.datain_parallel_ready (datain_parallel_ready_bit), // Must always be ready before valid!
.datain_p_parallel (datain_p_parallel_bit), // Deserialized positive data, framed by datain_parallel_valid
.datain_n_parallel (datain_n_parallel_bit), // 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
.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
// System control signals in clk_main domain
.clk_main (clk_main), // General logic clock
.sync_train (sync_train_bit_alignment),
.start_alignment (start_bit_word_alignment), // Preferably a one-cycle pulse
.done_alignment (done_bit_alignment) // Pulsed high means serdes data is bit-aligned
);
Started by the completion of the bit alignment.
wire sync_train_word_alignment;
wire datain_bitslip;
wire datain_wordflip;
iserdes_word_alignment
#(
.WORD_WIDTH (WORD_WIDTH),
.TRAINING_WORD (TRAINING_WORD),
.BITSLIP_INTERVAL (BITSLIP_INTERVAL)
)
iserdes_word_alignment
(
// 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_word), // There is a handshake, but no slack for stalling!
.datain_parallel_ready (datain_parallel_ready_word), // Must always be ready before valid!
.datain_p_parallel (datain_p_parallel_word), // Deserialized positive data, framed by datain_parallel_valid
// We don't need the negative polarity data here.
.datain_bitslip (datain_bitslip), // Pulse to shift output word (apply to both P and N SERDES)
.datain_wordflip (datain_wordflip), // Pulse to swap halfwords of the output word (apply to both P and N SERDES)
// System control signals in clk_main domain
.clk_main (clk_main), // General logic clock and reset
.sync_train (sync_train_word_alignment),
.start_alignment (done_bit_alignment), // Preferably a one-cycle pulse
.done_alignment (done_bit_word_alignment) // Pulsed high means serdes data is word-aligned
);
Started by the completion of the word alignment. The channel alignment done signal is the end of training.
wire sync_train_channel_alignment;
iserdes_channel_alignment
#(
.WORD_WIDTH (WORD_WIDTH),
.TRAINING_WORD (TRAINING_WORD),
.CHANNEL_PIPELINE_DEPTH (CHANNEL_PIPELINE_DEPTH) // Must match iserdes_1_to_12_data_diff.v parameter
)
iserdes_channel_alignment
(
// 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_channel), // There is a handshake, but no slack for stalling!
.datain_parallel_ready (datain_parallel_ready_channel), // Must always be ready before valid!
.datain_p_parallel (datain_p_parallel_channel), // Deserialized positive data, framed by datain_parallel_valid
// We don't need the negative polarity data here.
.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 and reset
.rst_main_n (rst_main_n),
.sync_train (sync_train_channel_alignment), // Signal sensor to output training word
.start_alignment (start_channel_alignment), // Preferably a one-cycle pulse
.done_alignment (done_channel_alignment) // Pulsed high means serdes data is word-aligned
);
Both N and P ISERDES get the same bitslip and wordflip, so they end up outputting complementary words. It's not strictly necessary here, but in the future, it could be useful when re-training on the fly, and to check correct operation when debugging.
always @(*) begin
datain_p_bitslip = datain_bitslip;
datain_n_bitslip = datain_bitslip;
datain_p_wordflip = datain_wordflip;
datain_n_wordflip = datain_wordflip;
end
The training modules never work in tandem, so we can simply OR-reduce their
sync_train signals. The individual outputs should rise and fall so as to
create no gaps in the final signal, but a gap might be ok.
always @(*) begin
sync_train = sync_train_bit_alignment || sync_train_word_alignment || sync_train_channel_alignment;
end
endmodule