Guga Kupradze
Published

Aurora 64B/66B Bring-Up Guide for Kria KR260

A practical step-by-step guide to creating and validating a high-speed Aurora serial link on KR260 hardware.

BeginnerProtip3 hours386
Aurora 64B/66B Bring-Up Guide for Kria KR260

Story

Read more

Code

AXI_PKT_CHECK

Verilog
module aurora_stream_rx_check #(
  parameter integer DATA_W = 64,
  parameter [DATA_W-1:0] SEED = {DATA_W{1'b0}}
)(
  input  wire                  clk,        // aurora user_clk
  input  wire                  rst,        // sync, active-high
  input  wire                  run_level,  // <-- tie to channel_up
  input  wire [DATA_W-1:0]     tdata,
  input  wire                  tvalid,
  output reg  [31:0]           beat_cnt,
  output reg  [31:0]           err_cnt
);
  // edge detect to reseed when channel comes up
  reg [1:0] rl_sync;
  always @(posedge clk) rl_sync <= {rl_sync[0], run_level};
  wire run_rise = rl_sync[0] & ~rl_sync[1];

  reg [DATA_W-1:0] expected;

  always @(posedge clk) begin
    if (!rst) begin
      expected <= SEED;
      beat_cnt <= 32'd0;
      err_cnt  <= 32'd0;
    end else begin
      if (run_rise) begin
        expected <= SEED;
        beat_cnt <= 32'd0;
        // keep err_cnt sticky so you can see mismatches; clear if you prefer.
      end
      if (tvalid && run_level) begin
        if (tdata !== expected) err_cnt <= err_cnt + 1'b1;
        expected <= expected + {{(DATA_W-1){1'b0}},1'b1};
        beat_cnt <= beat_cnt + 1'b1;
      end
    end
  end
endmodule

AXI_PKT_CHECK

Verilog
module aurora_stream_tx_gen #(
  parameter integer DATA_W          = 64,
  parameter [DATA_W-1:0] SEED       = {DATA_W{1'b0}},
  parameter integer UPDATE_INTERVAL = 200,   // cycles to wait between bursts
  parameter integer BURST_LEN       = 4      // beats per burst
)(
  input  wire                  clk,        // aurora user_clk
  input  wire                  rst_n,      // sync, ACTIVE-LOW reset
  input  wire                  run_level,  // tie to channel_up
  // AXIS to Aurora TX
  output reg  [DATA_W-1:0]     tdata,
  output reg                   tvalid,
  output reg                   tlast,
  input  wire                  tready,
  // optional stats
  output reg  [31:0]           beat_cnt
);

  // -------- local clog2 (Verilog-2001) --------
  function integer clog2;
    input integer value;
    integer i;
    begin
      clog2 = 0;
      for (i = value-1; i > 0; i = i >> 1)
        clog2 = clog2 + 1;
    end
  endfunction

  localparam integer WAIT_W  = (UPDATE_INTERVAL > 1) ? clog2(UPDATE_INTERVAL) : 1;
  localparam integer BURST_W = (BURST_LEN       > 1) ? clog2(BURST_LEN+1)     : 1;

  // Edge-detect run_level (channel_up) in this clock domain
  reg [1:0] rl_sync;
  always @(posedge clk) begin
    if (!rst_n) rl_sync <= 2'b00; else rl_sync <= {rl_sync[0], run_level};
  end
  wire run_rise = rl_sync[0] & ~rl_sync[1];

  wire fire = tvalid && tready;

  reg [WAIT_W-1:0]  wait_cnt;
  reg [BURST_W-1:0] burst_sent;

  // FSM encodings
  localparam S_WAIT  = 1'b0;
  localparam S_BURST = 1'b1;
  reg        state;

  always @(posedge clk) begin
    if (!rst_n) begin
      // ACTIVE-LOW reset asserted
      tvalid     <= 1'b0;
      tlast      <= 1'b0;
      tdata      <= SEED;
      beat_cnt   <= 32'd0;
      wait_cnt   <= {WAIT_W{1'b0}};
      burst_sent <= {BURST_W{1'b0}};
      state      <= S_WAIT;   // wait until channel_up rises, then start bursting
    end else begin
      // defaults each cycle
      tlast <= 1'b0;

      // (re)seed exactly once when channel_up rises
      if (run_rise) begin
        tdata      <= SEED;
        beat_cnt   <= 32'd0;
        wait_cnt   <= {WAIT_W{1'b0}};
        burst_sent <= {BURST_W{1'b0}};
        state      <= S_BURST;
      end

      case (state)
        S_BURST: begin
          // Drive valid only when the link is up
          tvalid <= run_level;

          if (run_level && fire) begin
            // successful beat
            tdata      <= tdata + {{(DATA_W-1){1'b0}},1'b1};
            beat_cnt   <= beat_cnt + 1'b1;
            burst_sent <= burst_sent + 1'b1;

            if (burst_sent == (BURST_LEN-1)) begin
              // last beat of burst
              tlast      <= 1'b1;
              state      <= S_WAIT;
              wait_cnt   <= {WAIT_W{1'b0}};
              burst_sent <= {BURST_W{1'b0}};
              tvalid     <= 1'b0; // deassert next cycle in WAIT
            end
          end
        end

        S_WAIT: begin
          tvalid <= 1'b0; // idle between bursts
          if (run_level) begin
            if (wait_cnt == (UPDATE_INTERVAL-1)) begin
              state    <= S_BURST;
              wait_cnt <= {WAIT_W{1'b0}};
            end else begin
              wait_cnt <= wait_cnt + 1'b1;
            end
          end
          // if run_level drops, we just hold here; next run_rise will reseed and start a burst
        end
      endcase
    end
  end
endmodule

Credits

Guga Kupradze
2 projects • 2 followers
Embedded Systems | FPGA & SoC (Zynq UltraScale+) | Real-Time & High-Speed Communication | PL/PS Co-Design

Comments