Source

License

Index

Sensor Data Receive All Channels

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

IDELAYCTRL (automatic calibration module for IDELAY taps). One per IO Bank.

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

Input SERDES for each channel.

    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

Join all the channel data

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

Coordinate channel training

    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

Back to FPGA Design Elements

fpgacpu.ca