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个模块了,与预期相符。
程序需要运行一段时间。先看一下发射的数据:

再看下接收与校验:
