FPGA教程系列-Vivado Aurora 8B/10B 例程修改

FPGA教程系列-Vivado Aurora 8B/10B 例程修改

之前的Aurora 8b/10b的例程,是通过LL接口转换成AXI接口的,既然AXI接口也了解的差不多了,可以尝试把这个接口转换的功能去掉了。

也就是说要去掉两个接口文件。

FRAME_GEN修改

verilog 复制代码
`timescale 1 ns / 1 ps
`define DLY #1

module aurora_8b10b_0_AXI_FRAME_GEN #
(
    parameter DATA_WIDTH = 16,
    parameter STRB_WIDTH = DATA_WIDTH / 8
)
(
    // User Interface (AXI4-Stream Master)
    output  [DATA_WIDTH-1:0]    m_axis_tdata,
    output  [STRB_WIDTH-1:0]    m_axis_tkeep,
    output                      m_axis_tlast,
    output  reg                 m_axis_tvalid,
    input                       m_axis_tready,

    // System Interface
    input                       USER_CLK,      
    input                       RESET,
    input                       CHANNEL_UP
);

//***************************Internal Register Declarations***************************

    reg     [DATA_WIDTH-1:0]   data_lfsr_r;    
    wire                       reset_c;
    wire                       dly_data_xfer;
    reg     [4:0]              channel_up_cnt;

//*********************************Main Body of Code**********************************

  // --------------------------------------------------------------------------
  // Delay Logic: Wait for Channel Up to stabilize
  // --------------------------------------------------------------------------
  always @ (posedge USER_CLK)
  begin
    if(RESET)
        channel_up_cnt <= `DLY 5'd0;
    else if(CHANNEL_UP)
      if(&channel_up_cnt)
        channel_up_cnt <= `DLY channel_up_cnt;
      else 
        channel_up_cnt <= `DLY channel_up_cnt + 1'b1;
    else
      channel_up_cnt <= `DLY 5'd0;
  end

  assign dly_data_xfer = (&channel_up_cnt);

  // Generate RESET signal when Aurora channel is not ready
  assign reset_c = RESET || !dly_data_xfer;

  // --------------------------------------------------------------------------
  // Transmit Data Generation (LFSR)
  // --------------------------------------------------------------------------
  // Transmit data when m_axis_tready is high.
  // Random data is generated using XNOR feedback LFSR.
  // m_axis_tvalid is asserted continuously once the channel is up.

  always @(posedge USER_CLK)
  begin
      if(reset_c)
      begin
          // Reset seed value (16'hABCD)
          data_lfsr_r    <=  `DLY    16'hABCD;  
          m_axis_tvalid  <=  `DLY    1'b0;   
      end
      else if(m_axis_tready) // Only update data if the slave (Aurora) is ready
      begin
          // LFSR Feedback Logic adapted for [15:0] bus
          // Original: {!{r[3]^r[12]^r[14]^r[15]}, r[0:14]} (Right shift relative to [0:15])
          // New: Shift right, insert new bit at MSB [15].
          // Mapping indices from old [0:15] to new [15:0]: 
          // 3->12, 12->3, 14->1, 15->0.
          data_lfsr_r    <=  `DLY    {
                                      !{data_lfsr_r[12] ^ data_lfsr_r[3] ^ data_lfsr_r[1] ^ data_lfsr_r[0]},
                                      data_lfsr_r[15:1]
                                     };
          m_axis_tvalid  <=  `DLY    1'b1;
      end
      else
      begin
          // If not ready, hold the data and valid signal
           m_axis_tvalid  <=  `DLY    1'b1;
      end
  end
   
    // --------------------------------------------------------------------------
    // AXI Output Assignments
    // --------------------------------------------------------------------------
    assign m_axis_tdata = data_lfsr_r;
    
    // Continuous streaming mode:
    // TKEEP is always all 1s (all bytes valid)
    assign m_axis_tkeep = {STRB_WIDTH{1'b1}};
    // TLAST is always 0 (infinite frame / continuous stream)
    assign m_axis_tlast = 1'b0;

endmodule

稍微解读一下:

接口定义 (AXI4-Stream)

verilog 复制代码
output  [DATA_WIDTH-1:0]    m_axis_tdata,   // 数据总线 (16位)
output  [STRB_WIDTH-1:0]    m_axis_tkeep,   // 数据有效字节指示 (Strobe/Keep)
output                      m_axis_tlast,   // 帧结束信号
output  reg                 m_axis_tvalid,  // 数据有效指示 (Master -> Slave)
input                       m_axis_tready,  // 准备好接收指示 (Slave -> Master)
  • DATA_WIDTH 默认为 16位。
  • USER_CLK, RESET, CHANNEL_UP 是系统控制信号。

A. 启动延时与复位逻辑 (Channel Up Delay)

verilog 复制代码
always @ (posedge USER_CLK)
begin
  if(RESET)
      channel_up_cnt <= `DLY 5'd0;
  else if(CHANNEL_UP)
    // 计数器逻辑:如果 Channel 建立,开始计数
    if(&channel_up_cnt) // 如果计数器全为1 (31)
      channel_up_cnt <= `DLY channel_up_cnt; // 保持最大值
    else 
      channel_up_cnt <= `DLY channel_up_cnt + 1'b1; // 递增
  else
    channel_up_cnt <= `DLY 5'd0; // 如果 Channel 断开,重置计数
end

assign dly_data_xfer = (&channel_up_cnt); // 只有计数满时,允许传输
assign reset_c = RESET || !dly_data_xfer; // 内部复位信号
  • Aurora 链路刚建立 (CHANNEL_UP 变高) 时可能还需要几十个时钟周期才能完全稳定。
  • 使用一个 5-bit 计数器,在 CHANNEL_UP 拉高后等待 31 个时钟周期,才释放内部复位 reset_c,开始发送数据。

B. 伪随机数生成 (LFSR) 与流控

verilog 复制代码
always @(posedge USER_CLK)
begin
    if(reset_c)
    begin
        data_lfsr_r    <=  `DLY    16'hABCD;  // 1. 初始化种子 (Seed)
        m_axis_tvalid  <=  `DLY    1'b0;      // 复位时 Valid 拉低
    end
    else if(m_axis_tready) // 2. AXI 握手成功 (Slave 准备好接收)
    begin
        // 3. 生成下一个随机数
        // 算法:XNOR 反馈,左移/右移调整适配 Little Endian
        data_lfsr_r    <=  `DLY    {
                                    !{data_lfsr_r[12] ^ data_lfsr_r[3] ^ data_lfsr_r[1] ^ data_lfsr_r[0]}, // 新的 MSB
                                    data_lfsr_r[15:1] //其余位右移
                                   };
        m_axis_tvalid  <=  `DLY    1'b1; // 数据有效,拉高 Valid
    end
    else
    begin
        // 4. 背压 (Backpressure) 处理
        // 如果 Slave (m_axis_tready) 为低,保持当前数据不变
        // m_axis_tvalid 保持为高,等待 Slave 准备好
         m_axis_tvalid  <=  `DLY    1'b1;
    end
end
  • 流控 (Flow Control) :这是 AXI 协议的关键。

    • 只有当 m_axis_tready == 1 时,LFSR 才会更新数值(发送下一个数据)。
    • 如果 m_axis_tready == 0(下游拥塞),寄存器值保持不变,tvalid 保持为 1,实现了背压机制,不会丢失数据。
  • LFSR 算法

    • 这是一个 16位 的 XNOR 反馈移位寄存器。
    • 为了适配 AXI 的 [15:0] (Little Endian) 格式,相比原代码(Big Endian [0:15]),反馈位的索引做了镜像映射(例如原代码的 bit 3 变成了现在的 bit 12)。

C. 输出赋值

verilog 复制代码
assign m_axis_tdata = data_lfsr_r;     // 输出寄存器值
assign m_axis_tkeep = {STRB_WIDTH{1'b1}}; // 设置为全1 (例如 2'b11),表示16bit数据全部有效
assign m_axis_tlast = 1'b0;            // 设置为0,表示永远没有"最后一包",是连续流

FRAME_CHECK修改

verilog 复制代码
`timescale 1 ns / 1 ps
`define DLY #1

module aurora_8b10b_0_AXI_FRAME_CHECK #
(
    parameter DATA_WIDTH = 16,
    parameter STRB_WIDTH = DATA_WIDTH / 8
)
(
    // User Interface (AXI4-Stream Slave)
    input  [DATA_WIDTH-1:0]    s_axis_tdata,
    input  [STRB_WIDTH-1:0]    s_axis_tkeep,
    input                      s_axis_tlast,
    input                      s_axis_tvalid,
    output                     s_axis_tready,

    // System Interface
    input                      USER_CLK,      
    input                      RESET,
    input                      CHANNEL_UP,

    output [7:0]               ERR_COUNT
);

//***************************Internal Register Declarations***************************
    
    // Slack registers (Pipeline stage)
    reg     [DATA_WIDTH-1:0]   s_axis_tdata_r;
    reg                        s_axis_tvalid_r;

    reg     [8:0]              err_count_r = 9'd0;
    
    // Expected Data Generation (LFSR)
    reg     [DATA_WIDTH-1:0]   data_lfsr_r;

//*********************************Wire Declarations**********************************
  
    wire                       reset_c;
    wire    [DATA_WIDTH-1:0]   data_lfsr_concat_w;
    wire                       data_valid_c;
    wire                       data_err_detected_c;
    reg                        data_err_detected_r;

//*********************************Main Body of Code**********************************

    // AXI4-Stream Sink is always ready to receive data for checking
    assign s_axis_tready = 1'b1;

    // Generate RESET signal
    assign reset_c = RESET;

    // --------------------------------------------------------------------------
    // Capture / Slack Pipeline
    // --------------------------------------------------------------------------
    always @ (posedge USER_CLK)
    begin
        s_axis_tdata_r  <= `DLY s_axis_tdata;
        s_axis_tvalid_r <= `DLY s_axis_tvalid;
    end

    // Data is valid to check when pipeline valid is high
    assign  data_valid_c = s_axis_tvalid_r;

    // --------------------------------------------------------------------------
    // Generate Expected Data (LFSR)
    // --------------------------------------------------------------------------
    // This logic must match the AXI_FRAME_GEN module exactly.
    // 16-bit LFSR with taps at logic indices matching the Generator.
    
    always @(posedge USER_CLK)
    begin
        if(reset_c || !CHANNEL_UP)
        begin
            data_lfsr_r   <=  `DLY  16'hD5E6;  // Initial Seed value (Must match TX side start)
        end
        else if(data_valid_c)
        begin
            // LFSR Feedback Logic adapted for [15:0] Little Endian
            // Corresponds to legacy [0:15] taps: 3, 12, 14, 15
            // Mapped to [15:0]: 12, 3, 1, 0
            data_lfsr_r   <=  `DLY  {
                                     !{data_lfsr_r[12] ^ data_lfsr_r[3] ^ data_lfsr_r[1] ^ data_lfsr_r[0]},
                                     data_lfsr_r[15:1]
                                    };
        end
    end

    assign data_lfsr_concat_w = data_lfsr_r;

    // --------------------------------------------------------------------------
    // Check Incoming Data
    // --------------------------------------------------------------------------
        
    // An error is detected when LFSR generated expected data 
    // does not match valid data from the pipeline register
    assign  data_err_detected_c = (data_valid_c && (s_axis_tdata_r != data_lfsr_concat_w));

    // Register the error signal for timing
    always @(posedge USER_CLK)
        data_err_detected_r <= `DLY data_err_detected_c; 

    // --------------------------------------------------------------------------
    // Error Counter
    // --------------------------------------------------------------------------
    // Compare and count. Stop at max value (255).
    always @(posedge USER_CLK)
    begin
        if(CHANNEL_UP)
        begin
            if(&err_count_r) // Saturation check (if all bits are 1)
                err_count_r <= `DLY err_count_r;
            else if(data_err_detected_r)
                err_count_r <= `DLY err_count_r + 1'b1;
        end
        else
        begin           
            err_count_r <= `DLY 9'd0;
        end
    end

    // Output the lower 8 bits (MSB used for saturation logic only)
    assign ERR_COUNT = err_count_r[7:0];

endmodule

简单解释一下:

接口定义 (AXI4-Stream Slave)

verilog 复制代码
input  [DATA_WIDTH-1:0]    s_axis_tdata,   // 接收到的数据
input  [STRB_WIDTH-1:0]    s_axis_tkeep,   // (在此模块中未用于逻辑,仅作为接口占位)
input                      s_axis_tlast,   // (在此模块中未用于逻辑)
input                      s_axis_tvalid,  // 数据有效指示 (Master -> Slave)
output                     s_axis_tready,  // 准备好接收指示 (Slave -> Master)
  • Slave 模式:作为数据的终点。
  • ERR_COUNT:输出给用户的 8-bit 错误统计值,通常连接到 LED 或 VIO 以便观察链路质量。

A. 接收流控 (Always Ready)

verilog 复制代码
assign s_axis_tready = 1'b1;

B. 输入打拍 (Pipeline / Slack Registers)

verilog 复制代码
always @ (posedge USER_CLK)
begin
    s_axis_tdata_r  <= `DLY s_axis_tdata;
    s_axis_tvalid_r <= `DLY s_axis_tvalid;
end
assign  data_valid_c = s_axis_tvalid_r;

时序优化(Timing Closure)。数据进入模块后,先在寄存器里存一拍。这样做可以切断 Aurora IP 接收逻辑与校验逻辑之间的长组合逻辑路径,有助于在高频时钟下满足时序要求。

C. 预期数据生成 (LFSR)

verilog 复制代码
always @(posedge USER_CLK)
begin
    if(reset_c || !CHANNEL_UP)
    begin
        data_lfsr_r   <=  `DLY  16'hD5E6;  // <--- 注意这里的种子 (Seed)
    end
    else if(data_valid_c)
    begin
        // LFSR 反馈逻辑,必须与 FRAME_GEN 完全一致
        data_lfsr_r   <=  `DLY  {
                                 !{data_lfsr_r[12] ^ data_lfsr_r[3] ^ data_lfsr_r[1] ^ data_lfsr_r[0]},
                                 data_lfsr_r[15:1]
                                };
    end
end

CHANNEL_UP​ 为低(链路未建立)时,LFSR 复位到初始种子 16'hD5E6​。关键点 :这意味着发送端(FRAME_GEN)发送的第一个数据必须16'hD5E6​,否则校验一开始就会报错。当 data_valid_c​ 有效时(即收到一个数据),LFSR 计算下一个周期应该收到的数据。

D. 错误检测与计数

verilog 复制代码
// 1. 比较逻辑
assign  data_err_detected_c = (data_valid_c && (s_axis_tdata_r != data_lfsr_concat_w));

// 2. 错误计数器 (饱和计数)
always @(posedge USER_CLK)
begin
    if(CHANNEL_UP)
    begin
        if(&err_count_r) // 如果计数器全为1 (达到 511,9位计数器)
            err_count_r <= `DLY err_count_r; // 保持最大值,不再增加 (饱和)
        else if(data_err_detected_r)
            err_count_r <= `DLY err_count_r + 1'b1; // 发现错误,计数+1
    end
    // ...
end
  • 比较:比较的是"打了一拍的接收数据"和"当前 LFSR 的值"。
  • 饱和计数器 :为了防止错误计数溢出回零导致误判,计数器设计为"饱和"模式。一旦达到最大值(这里用了 9 位计数器的高位判断,约 255 以上),就锁定最大值。这意味着如果你看到 ERR_COUNT 为 255,说明错误非常多,而不仅仅是 255 个。

仿真分析

中间还有一步要修改例化,自行解决吧。仿真前看下综合后的模块

可以发现,只有3个模块了,与预期相符。

程序需要运行一段时间。先看一下发射的数据:

再看下接收与校验:

相关推荐
Wishell201519 小时前
FPGA教程系列-Vivado AXI4-Full 仿真测试
仿真
Wishell20152 天前
日拱一卒之FPGA学习计划
仿真
Wishell20153 天前
日拱一卒之quartus芯片移植查看
仿真
Wishell20155 天前
FPGA教程系列-Vivado AXI4-Full接口
仿真
云雾J视界6 天前
超越Proteus:AI时代驱动电路仿真的新范式——以LTspice双脉冲测试为例
proteus·仿真·ltspice·驱动电路·ai时代·闭环验证
Wishell20156 天前
FPGA教程系列-Vivado AXI4-Lite master 测试
仿真
Wishell20157 天前
FPGA教程系列-Vivado AXI4-Lite slave 测试
仿真
Wishell20158 天前
FPGA教程系列-Vivado AXI4-Lite接口
仿真
民乐团扒谱机11 天前
【微实验】仿AU音频编辑器开发实践:从零构建音频可视化工具
算法·c#·仿真·audio·fft·频谱