Source

License

Index

ISERDES Training

Performs bit alignment first, then word alignment, then channel alignment, on a 1:12 SERDES. All in the clk_rxio domain.

Parameters, Ports, and Initial Conditions

`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

Deserialized data fork

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}})
    );

Bit Alignment.

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
    );

Word Alignment

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
    );

Channel Alignment

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

Back to FPGA Design Elements

fpgacpu.ca