当前位置: 首页 > news >正文

Aurora 8B/10B协议实战——你的板间互联为什么总是掉线?

做FPGA板间互联,很多人第一反应是“用光纤啊,SFP+插上就行”。

但真正把两块板子用光纤连起来跑通Aurora,很多人第一版都会遇到这几个问题:

  • 链路起来了,但数据传一会就断

  • 两边时钟不同源,误码率飙升

  • 帧边界对不上,上层协议解析全乱

  • ILA抓到的数据跟发出去的不一样

这篇文章把Aurora 8B/10B在GTX上的实战经验全部拆开,每个坑都给你可复现的代码和排查方法。


痛苦点一:链路起来了但传一会就断——你没处理Lane Up/Down事件

❌ 错误做法

verilog

// 很多人这样写——只要lane_up就认为链路可用

always @(posedge user_clk) begin

if (lane_up) begin

tx_valid <= 1'b1; // 直接开始发数据

// ... 往Aurora写数据

end

end

现象:刚上电正常,跑几分钟数据突然全断,ILA看lane_up已经拉低了,但你的发送逻辑还在拼命写——数据丢失+链路无法自动恢复。

✅ 正确做法

verilog

// 必须同时检测 lane_up + channel_up

reg [1:0] lane_up_dly;

reg [1:0] channel_up_dly;

wire lane_up_rising, lane_up_falling;

wire channel_up_rising, channel_up_falling;

always @(posedge user_clk or negedge user_rst_n) begin

if (!user_rst_n) begin

lane_up_dly <= 2'b00;

channel_up_dly <= 2'b00;

end else begin

lane_up_dly <= {lane_up_dly[0], lane_up};

channel_up_dly <= {channel_up_dly[0], channel_up};

end

end

assign lane_up_rising = ~lane_up_dly[1] & lane_up_dly[0];

assign lane_up_falling = lane_up_dly[1] & ~lane_up_dly[0];

assign channel_up_rising = ~channel_up_dly[1] & channel_up_dly[0];

assign channel_up_falling= channel_up_dly[1] & ~channel_up_dly[0];

// 只有channel_up为高时才发送数据

always @(posedge user_clk or negedge user_rst_n) begin

if (!user_rst_n) begin

tx_valid <= 1'b0;

tx_data <= 64'h0;

end else if (channel_up) begin

tx_valid <= s_axis_tvalid; // 来自上层AXI Stream

tx_data <= s_axis_tdata;

end else begin

tx_valid <= 1'b0; // channel_up掉了,停止发送

tx_data <= 64'h0;

end

end

// 检测到链路掉线 → 自动触发重训

always @(posedge user_clk or negedge user_rst_n) begin

if (!user_rst_n) begin

do_retrain <= 1'b0;

end else if (lane_up_falling || channel_up_falling) begin

do_retrain <= 1'b1; // 触发Aurora重训

end else if (channel_up_rising) begin

do_retrain <= 1'b0; // 链路恢复,清除重训标志

end

end

📌 原理解释

Aurora协议有两个状态信号,必须同时关注:

信号含义什么时候为高
lane_up物理Lane训练完成GT收发器8B/10B对齐完成
channel_up逻辑信道建立完成两端Aurora核完成握手,可以传数据

只检测lane_up是不够的——物理层通了,但逻辑层可能因为时钟偏差、缓冲区溢出等原因还没准备好。

正确顺序

text

GT训练完成 → lane_up=1 → Aurora握手 → channel_up=1 → 开始传数据

链路异常 → channel_up=0 → 停止发送 → 触发重训 → lane_up可能也掉


痛苦点二:两边时钟不同源,误码率居高不下

问题描述

两块板子各有一个独立的100MHz晶振,分别给各自的GTX提供参考时钟。看似没问题,但实际跑起来误码率(BER)高达10⁻⁶ ~ 10⁻⁹——对于Aurora这种没有CRC重传的协议来说,这个误码率足以让链路频繁重训。(注:工业级优秀链路BER应≤10⁻¹²,时钟不同源会导致劣化到10⁻⁶~10⁻⁹)

根本原因

GTX接收端对参考时钟的容忍度是±200ppm,但两个独立晶振的频差可能超过这个值,特别是在温度变化时。更关键的是:Aurora 8B/10B没有CDR之后的软件层时钟补偿——时钟偏差会直接累积成采样误差。

✅ 正确做法:使用Aurora的时钟补偿功能

verilog

// Aurora IP核配置时,必须使能时钟补偿

// 在Vivado IP核配置界面:

// Shared Logic: Include Shared Logic in Core ← 推荐

// Clock Compensation: Enabled

// Clock Compensation Period: 6 (适配GTX标准12.5Gb/s速率,速率变更时需同步调整)

// 同时,在约束文件里保证参考时钟质量

create_clock -name gt_refclk -period 10.000 [get_ports gt_refclk_p]

set_input_jitter gt_refclk 0.005 // 参考时钟抖动≤5ps

如果无法共享参考时钟(板间距离太远),怎么办?

verilog

// 方案:使用Aurora的"Clock Compensation" + 更大的弹性缓冲区

// IP核配置里把RX弹性缓冲区深度调大

// (在Advanced Configuration里,RX Buffer Depth设为最大1024)

// 同时在FPGA内部做跨时钟域处理

// Aurora输出的user_clk跟你本地逻辑时钟不同源!

// 必须用异步FIFO隔离

aurora_rx_fifo u_fifo (

.wr_clk (user_clk), // 写时钟:Aurora恢复时钟(异步)

.rd_clk (local_clk), // 读时钟:本地逻辑时钟

.wr_en (aurora_rx_valid),

.wr_data (aurora_rx_data),

.rd_en (fifo_rd_en),

.rd_data (local_rx_data),

.empty (fifo_empty),

.full (fifo_full)

);


痛苦点三:帧边界对不上,上层协议解析全乱

问题描述

Aurora是流式协议,本身不保证帧边界对齐。你发了一帧100字节的数据,接收端拿到的可能是从第50字节开始的——帧头对不上。

❌ 错误做法

verilog

// 直接把Aurora接收数据送给上层,假设帧边界对齐

always @(posedge local_clk) begin

if (aurora_rx_valid) begin

rx_buffer[wr_ptr] <= aurora_rx_data; // 直接写,不检测帧头

wr_ptr <= wr_ptr + 1'b1;

end

end

// 结果:帧头可能在任意位置,上层解析全乱

✅ 正确做法:在Aurora之上封装帧格式

verilog

// 发送侧:每帧数据前面加帧头和长度字段

localparam FRAME_SOF = 16'hFBFC; // 帧起始标志(避开8B/10B控制字符)

reg [15:0] tx_frame_cnt;

reg tx_in_frame;

always @(posedge user_clk) begin

if (!channel_up) begin

tx_in_frame <= 1'b0;

end else if (s_axis_tvalid && !tx_in_frame) begin

// 发送帧头 + 长度

tx_valid <= 1'b1;

tx_data <= {16'h0, FRAME_SOF, s_axis_tdata[15:0]};

tx_in_frame <= 1'b1;

end else if (tx_in_frame) begin

tx_valid <= s_axis_tvalid;

tx_data <= s_axis_tdata;

if (s_axis_tlast) tx_in_frame <= 1'b0;

end

end

// 接收侧:帧头对齐 + 长度校验

reg [15:0] rx_hdr;

reg rx_got_sof;

reg [15:0] rx_frame_len, rx_byte_cnt;

always @(posedge local_clk) begin

if (fifo_rd_en && !fifo_empty) begin

rx_hdr <= local_rx_data[63:48];

if (rx_hdr == FRAME_SOF && !rx_got_sof) begin

rx_got_sof <= 1'b1;

rx_frame_len <= local_rx_data[47:32];

rx_byte_cnt <= 16'h0;

end else if (rx_got_sof) begin

rx_byte_cnt <= rx_byte_cnt + 16'd8;

if (rx_byte_cnt >= rx_frame_len) rx_got_sof <= 1'b0;

end

end

end

更简单的方案:直接用Aurora的User Flow Control(UFC)来传递帧边界信息,在IP核配置中使能即可。


痛苦点四:ILA抓到的数据跟发出去的不一样——你没有处理Aurora的空闲字符

问题描述

你在发送端ILA里看到数据是0x12345678_ABCDEF01,但在接收端ILA里看到的是0xBCDEF011_2345678——数据被Aurora插入的空闲字符(/I//K/字符)打乱了。

根本原因

Aurora 8B/10B在链路空闲时会自动插入/I/(Idle)字符来维持链路同步。如果你用ILA直接抓rx_data,看到的数据流里会混有这些空闲字符。你的逻辑必须能识别并丢弃这些字符。

✅ 正确做法

verilog

// 接收侧必须过滤Aurora的空闲和控制字符

// Aurora 8B/10B使用的K字符:

// /K28.5/ = 8'hBC(Comma,对齐用)

// /I0/~/I3/ = 空闲字符

reg rx_is_kchar;

reg [7:0] rx_kchar_value;

always @(posedge user_clk) begin

rx_kchar_value <= rx_data[7:0];

rx_is_kchar <= rx_is_k; // Aurora输出的K字符标志

if (rx_is_kchar) begin

// 控制字符,丢弃

case (rx_kchar_value)

8'hBC: ; // Comma,忽略

default: ; // 其他K字符,忽略

endcase

end else begin

// 有效数据,往上传递

app_rx_valid <= 1'b1;

app_rx_data <= rx_data;

end

end

ILA调试技巧:

verilog

// 同时抓rx_data和rx_is_k,过滤控制字符后再分析

wire [63:0] ila_rx_filtered = rx_is_k ? 64'h0 : rx_data;

ila_aurora u_ila (

.clk (user_clk),

.probe0 (aurora_lane_up),

.probe1 (aurora_channel_up),

.probe2 (ila_rx_filtered), // 只看有效数据

.probe3 (aurora_rx_valid && !aurora_rx_is_k), // 有效数据valid

.probe4 (tx_data),

.probe5 (aurora_err_count)

);


⚠️ 五大注意事项(必读)

注意事项一:user_clk不是自由运行的时钟

user_clk是Aurora IP核输出的时钟,它只在lane_up为高时才稳定输出。如果链路训练失败或掉线,user_clk可能停止翻转或输出不稳定时钟。

verilog

// ❌ 错误:用user_clk驱动需要持续运行的逻辑

always @(posedge user_clk) begin

heartbeat_counter <= heartbeat_counter + 1'b1; // 链路掉线后心跳停了!

end

// ✅ 正确:需要持续运行的逻辑用本地时钟

always @(posedge local_clk) begin // local_clk来自FPGA本地晶振,永远稳定

if (!channel_up) begin

heartbeat_counter <= 32'h0;

end else begin

heartbeat_counter <= heartbeat_counter + 1'b1;

end

end

注意事项二:复位时序必须满足最小脉宽

Aurora IP核的gt_reset必须保持至少10个init_clk周期。

verilog

// ✅ 正确的复位时序

reg [7:0] rst_cnt;

reg gt_reset_i;

always @(posedge init_clk) begin // init_clk通常≤50MHz

if (rst_cnt < 8'd10) begin

rst_cnt <= rst_cnt + 1'b1;

gt_reset_i <= 1'b1;

end else begin

gt_reset_i <= 1'b0;

end

end

// gt_reset必须保持至少10个init_clk周期!仅拉高1个周期无效

另外,user_reset_n是Aurora输出给用户逻辑的复位信号,不是输入。

注意事项三:AXI-Stream禁止依赖Aurora反压

Aurora IP核的TX接口tready常态为高,内部FIFO深度有限。上游持续灌入数据会直接溢出丢包,严禁依赖tready做流量控制

verilog

// ❌ 错误:试图用tready反压Aurora

always @(posedge user_clk) begin

if (aurora_tx_tready) begin

tx_valid <= s_axis_tvalid; // 以为在等ready,实际Aurora几乎永远ready

end

end

// 结果:当Aurora内部缓冲区满时,数据直接被丢弃,不报错!

// ✅ 正确:用异步FIFO做流量控制

assign s_axis_tready = !fifo_almost_full; // 用FIFO反压,不是Aurora

注意事项四:多Lane链路务必等待通道完全对齐

多Lane Aurora(x4/x8)在所有Lane训练完成(lane_up=1)之后,还需要额外时间做Lane对齐,PCB走线偏斜会直接影响对齐时间。

工程规范:差分对内偏斜 < 5mil,Lane间偏斜 < 20mil。超标会大幅拉长对齐时间,甚至导致channel_up无法拉高。

调试技巧:用ILA同时抓所有Lane的lane_up和最终的channel_up,测量对齐时间。实测4-Lane Aurora通常需要额外100μs ~ 1ms。如果对齐时间异常长(>10ms),优先检查PCB走线等长。

注意事项五:Vivado版本与IP核兼容性

不同Vivado版本生成的Aurora IP核信号名可能变化,跨版本移植时务必对照IP手册。

Vivado版本Aurora IP版本注意事项
2018.3及之前v11.x不推荐,BUG较多
2019.1 ~ 2020.2v12.x稳定,推荐老项目维护
2021.1 ~ 2022.2v13.x信号名变更(如*_lane_up*_lane_up_i),还有其他状态/错误信号同步变更
2023.1及之后v14.x支持UltraScale+,IP核界面较大改动

💡升级建议:升级Vivado版本后,务必重新生成Aurora IP核,不要直接复用旧工程的.xci文件。建议先跑Example Design验证IP核兼容性。


Aurora 8B/10B 完整调试清单

检查项方法期望结果
GT参考时钟频率示波器测晶振输出100MHz ±50ppm
链路是否训练完成ILA看lane_up持续为高
信道是否建立ILA看channel_up持续为高
误码率读取aurora_err_count寄存器不增长或增长极慢(BER<1e-12)
发送侧是否在channel_up前发数据ILA看tx_validchannel_up时序tx_validchannel_up之后才拉高
接收数据是否有K字符混入ILA看rx_is_k有效数据时段rx_is_k=0
时钟域是否隔离检查user_clk和本地时钟是否有路径跨越必须经过异步FIFO
帧边界是否对齐接收侧搜索FRAME_SOF能稳定搜到帧头
user_clk是否用于持续运行逻辑代码审查持续运行逻辑必须用本地时钟
复位时长是否≥10个init_clk周期ILA看gt_reset脉冲宽度实测≥10周期
多Lane对齐时间ILA测lane_upchannel_up延迟<1ms为正常;若>10ms检查PCB走线偏斜

常见问题 FAQ

Q1:Aurora 8B/10B和64B/66B有什么区别?我该选哪个?

对比项8B/10B64B/66B
编码开销20%(8→10bit)~3.125%(64→66bit)
最大线速率(GTX)12.5 Gb/sGTX不支持
最大线速率(GTH/GTY)10.3125 Gb/s16.375 Gb/s
时钟补偿需要(插入/K/字符)不需要(靠SYNC头)
兼容性GTX/GTH/GTY全支持GTH/GTY支持,GTX不支持
推荐场景GTX器件、<10Gb/sGTH/GTY器件、>10Gb/s

💡 8B/10B也可用于GTH/GTY,但高速场景(>10Gb/s)官方强烈推荐64B/66B以获得更高效率。

Q2:单Lane Aurora可以用吗?还是必须x4/x8?

可以用单Lane。Aurora支持1/2/4/8 Lane配置。

  • 单Lane优势:省引脚(1对收发只用4根差分线)、延迟更低(无需Lane对齐)、适合低速(<10Gb/s)

  • 单Lane劣势:带宽上限低、无冗余(一Lane坏则全链路挂)

  • 推荐:板间距离短、带宽<10Gb/s用单Lane;需高带宽/冗余用x4。

Q3:Aurora的实际吞吐量能达到线速率的多少?

8B/10B编码理论有效带宽公式(以12.5Gb/s线速率为例):

  • 纯编码开销后:12.5 Gb/s × 8/10 = 10 Gb/s

  • 叠加时钟补偿(约3%额外开销):约9.7 Gb/s

  • 实测满载:约9.5 Gb/s(扣除空闲字符、协议头等)

Q4:为什么我的Aurora链路err_count一直在涨?

按优先级排查:

  1. 参考时钟质量差→ 示波器测晶振相位噪声,jitter >50ps影响大

  2. PCB走线问题→ SFP+到FPGA走线是否100Ω差分阻抗匹配?

  3. 光模块问题→ 换一个光模块试试(10G光模块便宜,先排除)

  4. 温度问题→ 高温下晶振频偏增大,降温后再测

  5. IP核配置错误→ 检查Line Rate和Reference Clock是否与硬件匹配

快速定位:用ILA抓aurora_err_count的增长速度。突发式增长(几分钟涨100)→时钟或光模块问题;持续缓慢增长 → PCB信号完整性问题。

Q5:Aurora和PCIe、以太网相比,我该选哪个?

协议优势劣势典型场景
Aurora简单、延迟低、FPGA间专用需专用光纤、距离受限板间高速互联(<100m)
PCIe生态成熟、主机可直接访问协议复杂、需主机支持FPGA ↔ 主机
以太网距离不限、交换机扩展协议栈复杂、延迟较高远距离、多节点

结论:FPGA板间互联(同一机架内),Aurora是最简单选择;需与主机通信用PCIe;需远距离或多节点用以太网。

Q6:Vivado仿真里怎么验证Aurora?

Xilinx提供了Aurora的仿真模型,在IP核生成后的example_design目录中。

步骤

  1. 生成Aurora IP核后,右键 →Open Example Design

  2. Vivado自动创建完整仿真工程

  3. 运行仿真,观察关键信号时序

关键仿真节点

  • lane_up拉高 → 物理层训练完成

  • channel_up拉高 → 逻辑层握手完成

  • 之后可发送测试数据

常见错误lane_up一直为0 → 检查GT参考时钟频率是否正确(仿真模型需输入正确的时钟频率)。


总结

痛苦点根因解法
链路传一会就断只检测lane_up,没处理channel_up掉线检测channel_up+掉线自动重训
误码率高两板时钟不同源,无补偿使能时钟补偿+异步FIFO隔离
帧边界对不上Aurora是流式协议,不保证帧对齐自定义帧头+长度字段封装
ILA数据对不上空闲字符/K字符混入有效数据过滤rx_is_k,只看有效数据

核心设计原则

物理层看lane_up,逻辑层看channel_up;时钟域必隔离,控制逻辑优先本地时钟。

一句话:Aurora 8B/10B上手简单,但要做到工业级稳定,这四个坑+五个注意事项一个都绕不开。关注我,FPGA实战系列持续更新。

http://www.jsqmd.com/news/1152431/

相关文章:

  • 企业微信机器人如何打破限制?官方Webhook与第三方RPA接口对比实现
  • 大数据计算机毕设之基于 Django + 大数据的租车客源分析系统的设计与实现 基于分布式计算的租车业务数据分析系统(完整前后端代码+说明文档+LW,调试定制等)
  • S7-200 SMART 存储卡应用:3分钟完成程序复制与密码清除操作
  • 【大数据毕业设计】基于 Hadoop 的外卖用户消费行为分析系统设计与实现 基于 Django 的外卖配送大数据可视化平台(源码+文档+远程调试,全bao定制等)
  • 虚拟机CentOS 网络配置【实操】
  • 从零搭建内网渗透单域环境:Active Directory攻防实战指南
  • NVIDIA A800/A100 服务器环境配置:Ubuntu 22.04 + CUDA 12.1 + PyTorch 2.1 完整避坑指南
  • Cytoscape 3.10 下载安装教程 专业生物科学数据分析与蛋白互作网络可视化软件下载安装步骤
  • 专家系统(ES)与ChatGPT对比:3个维度解析符号AI与神经网络的差异与融合
  • 说说java中hashMap的原理
  • Ubuntu 24.04 彻底关闭自动更新(桌面+服务器通用)
  • TI DLP4710 完整产品线及应用场景
  • GPT-4 API 成本优化实战:3种调用策略对比,Token消耗降低40%
  • 软件 License 防破解策略对比:3 种常见攻击手段与 5 种加固方案实测
  • Android BLE 开发实战:连接 GATT 服务器与 20 字节数据分包传输
  • 【大数据课程设计/毕业设计】基于 Hadoop 的外卖配送用户评价挖掘系统设计与实现 基于智能数据分析的外卖配送运营决策系统【附源码、数据库、万字文档】
  • Go HTTP Client 连接池:推理调用别忘了调 MaxIdleConns
  • 射频混频器测试——变频损耗、隔离度、1dB压缩点一次讲透
  • YOLOv6模型高效转换昇腾OM格式实战指南
  • 工业负载控制方案:TPD2017FN与STM32F401RB应用指南
  • LLM创意写作能力深度解析:技术瓶颈与优化策略
  • 2026年,这家切削液净化专家如何让工厂废液变“黄金”?
  • YOLO11超参数优化实战:提升目标检测性能的关键方法
  • C++详细分析讲解引用的概念与使用
  • CVPR 2025 | FEFM:频域穷尽融合机制,CRM+DRM双分支协同挖掘跨场共性与差异特征!
  • CRU 数据与 ArcGIS 集成:3 步完成站点气候值批量提取与重采样
  • 经销商管理系统适合哪些行业使用?2026年好用的经销商管理系统服务商推荐
  • 开发者“复活”Windows Movie Maker 视频编辑工具,内存占用比微软 Clipchamp 低 97%
  • 靠谱的EMBA院校对比|2026内地及境外头部EMBA综合实力榜单
  • Multi-Raft 分片架构的实现细节:数据放置策略与 Leader 均衡的工程权衡