news 2026/10/6 1:28:54

ZYNQ双板Aurora 8b/10b光口通信实战:从IP配置到性能优化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ZYNQ双板Aurora 8b/10b光口通信实战:从IP配置到性能优化

1. 项目缘起与整体方案设计

两块ZYNQ板子之间用光口互传数据,这个需求在工业采集、多板卡级联、雷达信号处理这类场景里非常常见。我这次拿到的任务很直接:两块ZYNQ-7020板子,各自挂一个SFP光模块,通过光纤对接,实现双向数据互传,速率跑在3.125Gbps,用Aurora 8b/10b协议。选Aurora而不是自己撸一个SerDes收发逻辑,原因很简单——Xilinx已经把GT收发器的物理层、通道绑定、时钟校正、CRC校验这些脏活累活全封装好了,你只需要关心用户接口的AXI4-Stream握手就行。

Aurora 8b/10b这个IP核本质上是一个链路层协议引擎。它把GT收发器的串行差分信号转换成并行数据,做8b/10b编解码,管理通道初始化握手,处理时钟补偿序列,还支持帧模式和流模式两种数据传输方式。我这次用的是帧模式(Framing Mode),因为需要传输不定长数据包,帧模式自带CRC校验和帧边界标识,接收端能直接判断一帧数据是否完整。

整体架构上,每块ZYNQ里例化一个Aurora 8b/10b IP核,配置成Full Duplex、Framing Mode、2字节数据位宽(对应3.125Gbps线速率)。用户逻辑这边用一个简单的发送状态机,从BRAM里读数据打包成AXI4-Stream帧发出去;接收端把Aurora输出的AXI4-Stream数据写入另一个BRAM,PL通过AXI-Lite寄存器通知PS读取。PS端跑一个裸机程序,负责初始化、配置寄存器、搬运数据。

为什么选2字节位宽而不是4字节?因为3.125Gbps线速率下,8b/10b编码后有效数据率是2.5Gbps,2字节位宽对应125MHz的用户时钟,时序压力小,布线容易收敛。4字节位宽虽然吞吐更高,但用户时钟要跑到62.5MHz的双沿或者125MHz的DDR,对PL逻辑的时序要求更苛刻。对于大多数中低速采集场景,2字节位宽完全够用。

注意:Aurora IP的线速率和参考时钟必须匹配。3.125Gbps对应125MHz参考时钟,如果你板子上是156.25MHz的差分晶振,需要在IP配置里选对应的线速率档位,否则GT根本起不来。

2. Aurora 8b/10b IP核配置详解与避坑指南

2.1 IP核关键参数逐项拆解

打开Vivado的IP Catalog,搜Aurora 8B/10B,双击进去。第一页是Physical Layer配置,这里有几个参数必须和硬件严格对应:

  • Lane Width:选2 Bytes。这个决定了用户接口的数据位宽,也影响GT的内部数据路径宽度。
  • Line Rate:填3.125 Gbps。这个值必须和你的参考时钟频率成整数倍关系,否则PLL锁不住。
  • GT Refclk:选125 MHz。这是GT收发器的参考时钟输入,来自板子上的差分晶振。
  • INIT Clk:选50 MHz。这是Aurora内部逻辑的初始化时钟,可以用PL的普通时钟。
  • Data Decoding:选8B/10B。这个不用改,Aurora 8b/10b只支持这一种。
  • Interface:选Framing。流模式没有帧边界,接收端不知道一包数据从哪开始到哪结束,不适合我的场景。
  • Flow Control:选None。我不需要背压,发送端只管发,接收端FIFO深度够大就行。
  • Little Endian:勾上。ZYNQ是小端架构,数据字节序要一致。

第二页是GT Selection,这里要选具体的GT Quad和Channel。ZYNQ-7020的PL里通常有4个GTX收发器,分布在两个Quad里。你得根据原理图确认SFP光模块连的是哪个GT通道。我板子上SFP接的是GTX Quad 0的Channel 0,所以这里选Quad 0、Channel 0。

第三页是Core Options,这里有几个容易踩坑的地方:

  • Column Used:选Left或Right,取决于你的GT在芯片的哪一侧。选错了布局布线会报错。
  • DRP Mode:选AXI4-Lite。这样PS可以通过AXI总线动态改GT参数,调试时很方便。
  • Vivado Lab Tools:不勾。我们不需要ILA在线调试GT内部信号,省点资源。

配置完点OK,IP核就例化好了。这时候你会看到Aurora IP对外暴露了几组接口:用户收发AXI4-Stream、GT差分对、参考时钟输入、初始化时钟、复位、以及一堆状态信号。

2.2 复位逻辑与时钟域处理

Aurora IP的复位逻辑是新手最容易翻车的地方。它有三个复位相关信号:gt_reset、reset、power_down。这三个信号的时序关系必须严格遵守数据手册。

gt_reset是给GT收发器的硬复位,必须保持至少6个init_clk周期。reset是给Aurora逻辑的软复位,必须在gt_reset释放之后、GT时钟稳定之后再释放。power_down正常工作时拉低,低功耗模式才拉高。

我实测下来,最稳妥的复位顺序是这样的:

// 复位状态机 localparam IDLE = 0, GT_RST = 1, WAIT_LOCK = 2, LOGIC_RST = 3, READY = 4; reg [2:0] rst_state = IDLE; reg gt_reset_r = 1'b1; reg reset_r = 1'b1; reg power_down_r = 1'b0; always @(posedge init_clk) begin case(rst_state) IDLE: begin if (user_reset) begin gt_reset_r <= 1'b1; rst_state <= GT_RST; end end GT_RST: begin if (cnt_gt_rst == 6'd63) begin // 保持64个周期 gt_reset_r <= 1'b0; rst_state <= WAIT_LOCK; end end WAIT_LOCK: begin if (gt_pll_lock && !gt_reset_done) begin rst_state <= LOGIC_RST; end end LOGIC_RST: begin reset_r <= 1'b0; rst_state <= READY; end READY: begin // 正常工作 end endcase end

提示:gt_pll_lock信号来自Aurora IP,表示GT的PLL已经锁定。如果这个信号一直不拉高,先检查参考时钟有没有输入,再检查线速率配置对不对。

时钟域方面,Aurora IP的用户接口时钟user_clk是从GT恢复出来的,频率等于线速率除以20(8b/10b编码开销)再除以位宽。3.125Gbps、2字节位宽下,user_clk是125MHz。你的发送逻辑和接收逻辑都必须在这个时钟域下工作,或者做好跨时钟域处理。

3. 发送与接收逻辑的Verilog实现

3.1 发送端状态机设计

发送端的任务很简单:从BRAM里读出一包数据,加上帧头,通过AXI4-Stream发给Aurora IP。AXI4-Stream的握手协议是tvalid和tready同时为高时数据传输有效。

我设计了一个四状态的状态机:

localparam S_IDLE = 0, S_SEND_HEAD = 1, S_SEND_DATA = 2, S_SEND_TAIL = 3; reg [1:0] tx_state = S_IDLE; reg [15:0] tx_cnt = 0; reg [31:0] pkt_len = 0; always @(posedge user_clk) begin case(tx_state) S_IDLE: begin if (tx_start) begin pkt_len <= data_len; tx_state <= S_SEND_HEAD; end end S_SEND_HEAD: begin if (m_axis_tready) begin m_axis_tdata <= 16'h55AA; // 帧头 m_axis_tvalid <= 1'b1; m_axis_tlast <= 1'b0; tx_state <= S_SEND_DATA; end end S_SEND_DATA: begin if (m_axis_tready && m_axis_tvalid) begin m_axis_tdata <= bram_data[tx_cnt]; tx_cnt <= tx_cnt + 1; if (tx_cnt == pkt_len - 1) begin m_axis_tlast <= 1'b1; tx_state <= S_SEND_TAIL; end end end S_SEND_TAIL: begin if (m_axis_tready) begin m_axis_tvalid <= 1'b0; m_axis_tlast <= 1'b0; tx_state <= S_IDLE; end end endcase end

这里有个细节:m_axis_tlast必须在最后一个数据字的同一拍拉高,不能提前也不能延后。Aurora IP靠这个信号判断帧结束,然后自动插入CRC和帧尾序列。

3.2 接收端数据解析与BRAM写入

接收端的状态机更简单,因为Aurora IP已经把帧边界解析好了,你只需要在tvalid和tready同时为高时把数据写进BRAM。

always @(posedge user_clk) begin if (s_axis_tvalid && s_axis_tready) begin if (s_axis_tlast) begin // 一帧结束,产生中断通知PS rx_done <= 1'b1; rx_len <= rx_cnt; end else begin bram[wr_addr] <= s_axis_tdata; wr_addr <= wr_addr + 1; rx_cnt <= rx_cnt + 1; end end end

注意:BRAM的写地址必须在帧开始时清零,否则第二帧数据会覆盖到第一帧后面。我一开始忘了清零,结果PS读到的数据长度一直累加,查了半天才发现是地址没复位。

3.3 中断与PS-PL交互

PL收到一帧数据后,通过AXI-Lite寄存器产生一个中断给PS。PS端在裸机程序里注册中断服务函数,收到中断后从BRAM里把数据读走。

AXI-Lite寄存器映射我定义了四个:

寄存器偏移名称功能
0x00CTRLbit0: 发送启动,bit1: 接收使能
0x04STATUSbit0: 发送完成,bit1: 接收完成
0x08TX_LEN发送数据长度
0x0CRX_LEN接收数据长度

PS端的中断服务函数大概长这样:

void aurora_isr(void *CallbackRef) { u32 status = Xil_In32(AURORA_BASE + 0x04); if (status & 0x02) { u32 len = Xil_In32(AURORA_BASE + 0x0C); // 从BRAM读数据 for (int i = 0; i < len; i++) { rx_buf[i] = Xil_In32(BRAM_BASE + i * 4); } // 清中断 Xil_Out32(AURORA_BASE + 0x04, 0x02); } }

4. 板级调试与常见问题排查实录

4.1 GT收发器不锁定的排查思路

两块板子上电、加载bit流之后,第一件事是看gt_pll_lock和channel_up信号。如果gt_pll_lock不拉高,说明GT的PLL没锁住,问题一定出在参考时钟或线速率配置上。

我遇到过两种情况:一是参考时钟晶振没起振,用示波器量差分对没有波形;二是线速率填错了,3.125Gbps填成了3.125MHz,差了1000倍。排查顺序是先用示波器确认参考时钟频率和幅度,再核对IP配置里的线速率和参考时钟是否匹配。

如果gt_pll_lock拉高了但channel_up不拉高,说明GT的物理层通了但Aurora的通道初始化没完成。这时候要检查光纤是不是接反了——TX接RX、RX接TX,这是最基本的。另外,两块板子的Aurora IP配置必须完全一致,线速率、位宽、参考时钟任何一个不一样都握不上手。

4.2 数据错位的典型原因

数据能收到但内容不对,最常见的原因是字节序问题。Aurora IP默认是小端模式,但如果你在IP配置里没勾Little Endian,或者PS端读BRAM时按大端解析,数据就会错位。

另一个原因是帧头对齐。我在发送端插入了16'h55AA作为帧头,接收端如果直接从第一个数据字开始存BRAM,帧头也会被存进去。PS读数据时要从第二个字开始读,或者接收端状态机跳过帧头。

还有一种情况是时钟补偿序列被误当成数据。Aurora在空闲时会发送时钟补偿序列,接收端如果没正确过滤,这些序列会混进BRAM。不过Aurora IP内部已经处理了时钟补偿,用户接口只会输出有效数据,所以这个问题一般不会出现。

4.3 常见问题速查表

现象可能原因排查方法
gt_pll_lock不拉高参考时钟缺失或频率错误示波器量差分时钟,核对IP配置
channel_up不拉高光纤接反或两端配置不一致交换TX/RX,核对两端IP参数
数据错位字节序不一致检查Little Endian配置和PS解析方式
接收数据长度累加BRAM写地址未复位在帧开始时清零写地址
中断不触发AXI-Lite寄存器未使能检查CTRL寄存器bit1和GIC配置
误码率高光纤质量差或GT预加重不足换光纤,调GT的TX Diff Swing

提示:调试Aurora时,Vivado的ILA是神器。把gt_pll_lock、channel_up、m_axis_tvalid、m_axis_tready、s_axis_tvalid、s_axis_tready这几个信号抓出来,一眼就能看出问题出在哪个环节。

5. 性能实测与优化经验

5.1 实测吞吐与延迟

两块ZYNQ板子通过1米光纤对接,Aurora跑3.125Gbps,2字节位宽,用户时钟125MHz。实测单向吞吐能跑到250MB/s左右,接近理论值312.5MB/s的80%。瓶颈在BRAM的读写带宽和PS端的中断响应延迟。

延迟方面,从发送端写入BRAM到接收端PS读出数据,端到端延迟大约15微秒。其中GT传输延迟约1微秒,PL逻辑处理约2微秒,PS中断响应约10微秒。如果对延迟敏感,可以把PS中断改成轮询,能降到5微秒以内。

5.2 提升吞吐的几种手段

如果250MB/s不够用,有几个方向可以优化:

  • 加大位宽:把Lane Width从2字节改成4字节,用户时钟不变的情况下吞吐翻倍。但时序收敛难度增加,需要仔细约束。
  • 提高线速率:从3.125Gbps提到6.25Gbps,参考时钟也要相应提高到250MHz。ZYNQ-7020的GTX支持到6.6Gbps,但PCB走线质量要够好。
  • 用DMA搬运:PS端用DMA从BRAM搬数据到DDR,比CPU逐字读取快得多。ZYNQ的DMAC支持AXI4-Stream到Memory的搬运,配置好描述符就行。
  • 流模式替代帧模式:流模式没有CRC和帧边界开销,有效数据率更高,但需要你自己管理数据包边界。

5.3 长时间运行的稳定性

我让这套系统连续跑了72小时,每秒钟发一包1KB的数据,总共发了约2.6亿包。误码率为零,没有出现通道断开或数据错乱。关键措施是:

  • GT的预加重和均衡参数用默认值,没有乱调。
  • 光纤用工业级单模光纤,接头用LC/PC打磨过的。
  • 电源用低纹波的LDO给GT供电,开关电源的纹波会耦合到GT的模拟电路里。
  • 板子放在恒温箱里,温度控制在25±5℃。

注意:GT收发器对电源纹波非常敏感。如果误码率偏高,先用示波器量一下GT电源的纹波,超过50mVpp就要考虑换LDO或加滤波电容。

6. 工程文件组织与复现建议

6.1 Vivado工程目录结构

我的工程目录是这样组织的:

aurora_loopback/ ├── src/ │ ├── rtl/ │ │ ├── aurora_top.v │ │ ├── tx_fsm.v │ │ ├── rx_fsm.v │ │ └── axi_lite_reg.v │ ├── ip/ │ │ └── aurora_8b10b_0/ │ └── constr/ │ └── aurora_pins.xdc ├── sdk/ │ └── aurora_test/ │ └── src/ │ └── main.c └── scripts/ └── build.tcl

约束文件里最关键的是GT差分对的引脚约束和参考时钟约束:

# GT差分对 set_property PACKAGE_PIN F6 [get_ports gt_txp_out] set_property PACKAGE_PIN E6 [get_ports gt_txn_out] set_property PACKAGE_PIN D6 [get_ports gt_rxp_in] set_property PACKAGE_PIN C6 [get_ports gt_rxn_in] # 参考时钟 set_property PACKAGE_PIN F10 [get_ports gt_refclk_p] set_property PACKAGE_PIN E10 [get_ports gt_refclk_n] create_clock -period 8.000 [get_ports gt_refclk_p]

6.2 复现步骤清单

如果你想复现这套系统,按这个顺序来:

  1. 在Vivado里新建工程,选对ZYNQ型号。
  2. 例化Aurora 8B/10B IP,按第2节的参数配置。
  3. 写发送和接收状态机,例化BRAM和AXI-Lite寄存器。
  4. 写约束文件,绑定GT引脚和参考时钟。
  5. 综合、实现、生成bit流。
  6. 导出硬件到SDK,写裸机程序。
  7. 两块板子都加载bit流,光纤对接,跑测试程序。

提示:两块板子的bit流可以完全一样,Aurora IP会自动协商主从。不需要一块配成Master一块配成Slave,IP内部有自动协商机制。

6.3 后续扩展方向

这套框架搭好之后,可以往几个方向扩展:一是加一个DDR缓存,把接收到的数据先存DDR再慢慢处理;二是把Aurora封装成自定义IP,用户接口改成AXI4-Stream,方便集成到更大的系统里;三是加一个简单的协议层,在帧头里加源地址、目的地址、包序号,实现多板卡路由。

我个人在实际操作中的体会是,Aurora 8b/10b这个IP核本身很稳定,大部分问题都出在复位时序、时钟配置和引脚约束上。把这三样搞对了,剩下的就是写状态机搬数据的事。第一次调通可能需要两三天,后面再复现就是半天的事。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/6 1:28:39

STM32 FMAC实战:用硬件加速器卸载FIR滤波,释放CPU性能

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 1:28:24

Orbbec深度相机ROS2部署实战:从单设备调通到多相机配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 1:27:28

ADXL355高精度加速度计寄存器配置与工业级数据采集实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 1:26:05

FPGA实战:基于XC7A50T从零打造LED控制器

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 1:25:41

PCB过孔工艺全解析:通孔、盲孔、埋孔选型与成本控制指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 1:24:11

XC6206P332MR LDO芯片详解:从丝印识别到应用选型

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华