
简介这份 PDF 面向使用 Xilinx FPGA 进行高速串行接口开发的工程师与学习者聚焦 Aurora 8B/10B IP 核的配置、仿真与工程落地适合已具备一定 Vivado 和 GTX 收发器基础、希望厘清协议细节与实操流程的读者。压缩包仅含 1 个 PDF 文件大小约 8.74MB内容围绕 IP 核参数设置、接口速率与数据宽度选择、GTX 引脚分配等易混淆点展开并对官方文档中的编码机制、控制字符和错误检测概念作出解析。作者结合仿真流程说明如何搭建测试平台、验证编解码与错误检测还简要介绍修改源码和约束文件以适配实际工程的方法涉及时钟、数据、控制信号连接、链路稳定性检查以及仿真中时序与信号完整性问题的排查思路。目前已有 3748 人学习可作为配置核对、仿真排错、引脚约束理解和工程移植的参考材料。1. 上板第二天lane_up 里只有一个 bit 是 1有个做图像采集的同行找我说 Xilinx FPGA 上的高速串口卡住了GTX 的参考时钟在示波器上非常干净复位放了两百毫秒Aurora 8B/10B 例化出来的核channel_up一直是 0lane_up四个 bit 里只有一个为 1。代码翻了两天没找到问题最后发现是参考时钟接在 Quad 1 的时钟脚上收发器却配在 X0Y0。这种错在文档里只是一行脚注在板子上能烧掉两天。这份《Aurora 8B/10B IP 核详解和仿真》就是冲着这类地方写的。它把 IP 核的配置项逐条拆开讲重点落在 GTX 引脚选择、参考时钟怎么配对、仿真怎么跑以及生成的源码和约束文件要改哪几处才能塞进自己的工程。适合第一次碰高速收发器、手上有现成 Aurora 工程却跑不通的人也适合已经跑通但说不清user_clk是怎么算出来的人。2. IP 核配置逐项拆解Lane 宽度、线速率和参考时钟怎么算配置页面上真正会让人卡住的其实只有三组数Lane 宽度、线速率、参考时钟。其余选项大多有默认值选错了顶多功能少一点这三组数对不上链路连训练都进不去。下面按我一般配置的顺序拆。2.1 三个必须一起算的数Aurora 8B/10B 每个 lane 的用户数据宽度固定是 16 bit所以 4 lane 就是 64 bit2 lane 就是 32 bit这个不用自己推。真正要推的是user_clk线速率乘 0.8 是编码后的有效带宽再除以用户侧位宽就是用户时钟频率。把位宽 lane 数 × 16 代进去公式可以简化成一条user_clk line_rate(Gbps) x 0.8 / 16 (GHz) line_rate(Gbps) / 20 (GHz)3.125 Gbps 算出来是 156.25 MHz6.25 Gbps 是 312.5 MHz10.3125 Gbps 是 515.625 MHz。这条式子决定了后面所有时序约束的基准也决定了你的下游逻辑比如 DDR 写控制器、图像打包模块能不能在这个频率上收敛。我见过有人在 4 lane、10.3125 Gbps 的配置下按 300 MHz 写用户逻辑综合能过上板一跑就偶发丢帧原因就是用户时钟其实是 515 MHz时序余量早没了。参考时钟不能随便给。GTX 的 Quad PLL 在参考时钟和线速率之间只能做整数倍分频常见的清爽组合是 156.25 MHz 出 3.125 Gbps×20和 6.25 Gbps×40125 MHz 出 3.125 Gbps×25。你在 IP 配置页上填了线速率之后参考时钟那一栏如果给出不能整除的频率界面会直接变红或者干脆锁住不让选很多人第一次配置卡十分钟就卡在这。稳妥做法是先定线速率再从板子上已有的晶振反推能不能整除实在不行换一个晶振别指望 IP 帮你兜。提示同一块板子上多个 Quad 共用一路参考时钟很常见但收发器实例必须属于这路时钟能驱动的 Quad 范围跨 Quad 之前先翻器件手册的时钟布线表别凭感觉连。2.2 数据流模式Duplex、TX-only 和 Framing、Streaming第二个要拍板的是数据流模式和接口类型这两项一旦生成再改等于重做一遍工程。数据流模式有 Duplex、TX-only、RX-only 三选。只做单向采集比如相机往 FPGA 灌数据选 RX-onlyIP 面积小、时序好收敛还省掉一半的复位逻辑。我一般先问清楚业务是不是真的需要回传很多项目明明只要接收却顺手选了 Duplex结果 TX 侧的tready一直悬空。接口类型有 Framing 和 Streaming 两种。Framing 模式由硬件自动插入 SCP帧起始、PAD填充、ECP帧结束三种控制字符用户侧只需要在最后一个数据拍上把tlast拉高接收侧 IP 会把tlast还给你同时tkeep标出最后一拍里哪几个字节有效。Streaming 模式不组帧全靠用户自己往数据流里塞控制字符灵活但调试成本高。第一次做项目建议先用 Framing 跑通等链路稳定了再考虑 Streaming。流控Flow Control三个选项None、UFC、NFC。None 最简单靠tready反压链路对端来不及收就自然丢吞吐不会丢数据。UFC 是用户流控通过一组ufc_req_tx/ufc_tx之类的握手信号让远端暂停NFC 是原生流控由 IP 自动发。除非你有明确的带宽仲裁需求否则选 None反压信号本来就是 AXI4-Stream 的常规玩法。2.3 生成之后马上要改的两处IP 生成完Vivado 会给你一个 wrapper里头是核的例化模板。这份 PDF 里比较实用的地方是把用户侧信号和时钟复位分了组照着抄不会漏。我一般会把自己的顶层例化写成这样// 4 lane、64bit 用户接口、Framing 模式的 Aurora 8B/10B 核 aurora_8b10b_0 u_aurora ( // 用户发送接口AXI4-Stream 风格 .tx_tdata (tx_tdata), // 64bit 4 lane x 16bit .tx_tvalid (tx_tvalid), .tx_tready (tx_tready), // 核输出反压用 .tx_tlast (tx_tlast), // Framing 模式下标记帧尾 .tx_tkeep (tx_tkeep), // 8bit一 bit 对应一个字节 .tx_tuser (1b0), // 用户接收接口 .rx_tdata (rx_tdata), .rx_tvalid (rx_tvalid), .rx_tready (rx_tready), .rx_tlast (rx_tlast), .rx_tkeep (rx_tkeep), // 状态与时钟 .channel_up (channel_up), .lane_up (lane_up), // 每 lane 一位全 1 才算正常 .user_clk (user_clk), // 由上面公式算出用户逻辑跑这个域 .sync_clk (sync_clk), .reset_pb (aurora_reset), // 用户侧复位低有效与否看 IP 配置 .pma_init (pma_init), // PMA 初始化需保持足够周期 .gt_refclk_p (gt_refclk_p), .gt_refclk_n (gt_refclk_n), .init_clk (init_clk) // 自由运行时钟DRP 和初始化用 );参数说明几个容易搞错的tx_tkeep在 4 lane 下只有 8 bit因为一拍 8 个字节一位管一个字节全有效就是8hFF最后一拍按实际字节数填就行pma_init不是普通复位手册要求保持的时间比reset_pb长得多我一般按 2 ms 给宁可多不能少init_clk必须是自由运行的不能挂在user_clk上否则user_clk没起来的时候初始化逻辑自己也没时钟就成死锁了。第二处是约束文件。GT 的参考时钟脚和收发器位置是硬绑定PIN 和 LOC 都写错就等着lane_up不动# 参考时钟差分对必须落在 MGTREFCLK 专用引脚上 set_property PACKAGE_PIN AF6 [get_ports gt_refclk_p] set_property PACKAGE_PIN AF5 [get_ports gt_refclk_n] set_property IOSTANDARD LVDS [get_ports {gt_refclk_p gt_refclk_n}] # 156.25MHz 对应周期 6.4ns频率改了这个数必须跟着改 create_clock -name gt_refclk -period 6.4 [get_ports gt_refclk_p] # 收发器通道位置路径按生成的实际层级改 set_property LOC GTXE2_CHANNEL_X0Y3 [get_cells -hier -filter {NAME ~ *gt_wrapper*/gt_inst*}] # 异步时钟域用户时钟和 init_clk 之间不需要做时序分析 set_clock_groups -asynchronous -group [get_clocks user_clk] -group [get_clocks init_clk]create_clock的周期一定要和实际晶振对上见过改板子换了 125 MHz 晶振、约束还写着 6.4 ns 的综合不报错上板后user_clk频率对不上预期整个下游逻辑都在错误频率下工作非常隐蔽。set_clock_groups也别省user_clk和init_clk之间本来就异步不声明异步组工具会给你一堆跨时钟域路径报告里的违例多到没法看。3. 目录与文件清单一份 Aurora 工程里到底有哪些东西IP 生成完之后目录会多出一大片文件第一次看很容易懵。这一章的目录结构按常见的 IP 生成方式给实际名字会随你填的 IP 名变化但层级和每个文件的分工是稳定的。3.1 生成目录长什么样example_design 里哪些能直接跑aurora_8b10b_0_ex/ ├── aurora_8b10b_0.xci # IP 核本体所有配置都锁在这里 ├── aurora_8b10b_0/ │ ├── synth/ # 综合用网表与 wrapper │ ├── sim/ # 仿真用行为模型 │ └── example_design/ │ ├── aurora_8b10b_0_exdes.v # 示例工程顶层 │ ├── aurora_8b10b_0_frame_gen.v # 发送激励发生器 │ ├── aurora_8b10b_0_frame_check.v # 接收侧比对与错误统计 │ ├── aurora_8b10b_0_ll_to_framing.v # 链路层到组帧层的适配 │ ├── aurora_8b10b_0_support.v # 时钟、复位、GT 的捆扎层 │ ├── aurora_8b10b_0_clocks.v # MMCM 与时钟分发 │ ├── aurora_8b10b_0_reset_logic.v # 复位状态机 │ ├── aurora_8b10b_0_gt.v # 收发器例化 │ ├── aurora_8b10b_0_exdes_tb.v # 仿真用 testbench │ └── aurora_8b10b_0_exdes.xdc # 示例约束.xci是整套东西的源头改配置只能改它再重新生成手改生成出来的.v文件下次重新生成全被覆盖。example_design直接是一个能综合、能仿真的完整工程frame_gen按设定的帧长和帧数造数据frame_check在旁边比对跑通之后你会看到frame_check打印的通过计数在涨这是最省事的一次自证。我一般拿到新的板子先用这套 example design 原样跑一遍确认收发器和时钟没问题再开始动自己的逻辑。顺序反了出问题时你分不清是板子的事还是代码的事。3.2 核心 RTL 文件逐个点名从 gt_wrapper 到 frame_gensupport层是整个例子的骨架。它把user_clk、sync_clk、reset_pb、pma_init这些时钟复位收拢在一个模块里对外只暴露少量端口。这层最常见的改动是把复位输入换成你自己的复位源别的不用动。clocks.v负责从gt_refclk产生user_clk和sync_clk里面是一个 MMCM 或 PLL 实例加一堆 BUFG。如果 IP 配置时选了「由 IP 内部产生用户时钟」改这里意义不大如果选了外部提供那user_clk就得你自己从别处引进来这也是很多工程时钟连不通的根源。判断方法很简单看clocks.v里有没有MMCME2_ADV例化。reset_logic.v是一个状态机内部大概走「等时钟稳定 → 释放pma_init→ 等pma_init计数到 → 释放reset_pb→ 等channel_up」这么一条链。它的关键价值是把几个复位之间的先后顺序固定死了你要调整复位时长改的就是这个文件里的计数器位宽别去改 IP 的.xci。gt.v是收发器例化引脚、参考时钟、环回模式都在这里。做环回测试时只要把 IP 配置里的环回选项改成 Near-end PCS 或 Near-end PMA重新生成后这个文件会跟着变不用手改。frame_gen.v和frame_check.v是示例逻辑不是协议的一部分上真项目时基本都要删掉或替换。删之前先看清它们和ll_to_framing之间的握手关系直接删会留下悬空的tvalid。3.3 接口时序对照channel_up 前后用户侧该做什么channel_up是整条链路可用的唯一标志。它没起来之前用户侧绝对不能往tx_tvalid上送数据因为核此时还没完成通道初始化发出去的东西会被丢弃rx_tvalid也不会有效。我一般这么写用户侧的门控// channel_up 之前保持复位之后才允许用户逻辑出数据 always (posedge user_clk) begin if (!channel_up) begin user_tx_tvalid 1b0; user_tx_tlast 1b0; end else begin user_tx_tvalid fifo_rd_en; user_tx_tlast fifo_rd_last; end endlane_up是过程量四个 bit 全 1 才说明四个 lane 都完成了对齐只有lane_up全 1 之后channel_up才会拉高。调试时先看lane_up是最快的定位手段全 0 说明参考时钟或收发器没起来部分为 1 说明 lane 间偏斜或某个通道物理连接有问题全 1 但channel_up不起来则要怀疑复位时序或者对端没上线。4. 仿真跑通从 Testbench 到波形上的握手时序仿真在这个项目里的作用不是「证明设计正确」而是让你在没板子、没示波器的情况下把初始化流程和握手时序看清楚一遍。Aurora 的初始化过程有好几百个时钟周期上板时用 ILA 抓一次成本很高仿真里随便看。4.1 用 Vivado 自带仿真跑通 example design生成完 IP 之后示例工程一般会把 testbench 设成仿真顶层。最简单的方式是直接在 GUI 里点 Run Simulation但工程多了之后我更习惯用脚本一是能复现二是方便挂到 CI 上跑回归。# 打开示例工程切仿真顶层并跑完整个仿真 open_project ./aurora_8b10b_0_ex/aurora_8b10b_0_ex.xpr set_property top aurora_8b10b_0_exdes_tb [get_filesets sim_1] set_property -name {xsim.simulate.runtime} -value {all} -objects [get_filesets sim_1] launch_simulation -mode behavioral run allruntime设成all表示跑到$finish示例 testbench 一般自带一个超时保护跑不到结束会自己停。仿真时间会很长因为 Aurora 初始化加上几十帧数据的传输在行为级模型下可能要跑几百万个时钟周期我一般先只跑到channel_up拉高确认初始化没问题再放开跑完整数据。testbench 顶部通常放了一组参数或宏用来把复位计数和超时计数按比例缩短方便在合理时间内跑完。想改仿真时长、帧数、帧长改这几个值就行别去改 RTL。4.2 看波形要盯的四件事第一件是参考时钟和user_clk的频率比例。在波形里量一下user_clk的周期对照第 2 章那条公式如果对不上说明 MMCM 配置算错了后面全部白搭。第二件是lane_up的拉高顺序。正常情况下几个 lane 差不多同时起来如果某个 lane 比其他晚很多多半是初始化的对齐序列在那一路上丢了仿真里能看到重传。第三件是channel_up拉高之后tx_tvalid和tx_tready的握手。AXI4-Stream 的规则是tvalid拉高后必须等tready中间不能撤仿真里如果看到tvalid拉高又掉下去那就是用户侧逻辑写错了上板后表现为偶发丢帧极难查。第四件是tlast和tkeep的配合。Framing 模式下最后一拍的tkeep决定这一帧有多少字节有效tlast和tkeep不对应的话接收侧算出来的帧长就是错的。这个错误在仿真里表现为frame_check的错帧计数缓慢增长不是一次全错很容易被忽略。4.3 自己写激励把 frame_gen 换成自建数据源示例的frame_gen只会造递增数验证不了你自己的业务格式。我一般保留它的骨架把数据源换成一个 BRAM 或 FIFO 的读侧这样能直接喂真实数据。// 用 FIFO 读侧替代 frame_gen 的递增数据源 always (posedge user_clk) begin if (!channel_up) begin m_axis_tvalid 1b0; m_axis_tlast 1b0; end else if (fifo_rd_en) begin m_axis_tvalid 1b1; m_axis_tdata fifo_dout; // 64bit 一拍 m_axis_tlast (fifo_left 8d1); // 读完最后一个字节拉 tlast m_axis_tkeep (fifo_left 8) ? 8hFF : (8hFF (8 - fifo_left)); end else begin m_axis_tvalid 1b0; end endfifo_left是这一帧还剩多少字节tkeep用右移的方式按剩余字节数生成掩码这样最后一拍不满 8 字节也能正确表达。tvalid拉高后不能再撤回所以fifo_rd_en只在tready有效时才给这个条件漏掉就会违反 AXI4-Stream 协议。改完激励之后接收侧要么用frame_check的比对逻辑要么自己写一个把rx_tdata写回 BRAM 再和源数据比。我倾向后者因为能直接定位到第几个字节出错比看一个通过计数有用得多。5. 避坑与排查GTX 引脚、参考时钟和复位那些坑这一章的四条都是从「仿真好好的、上板就出事」的场景里抠出来的每条按现象、原因、解决写遇到同类问题可以先对号入座。5.1 现象参考时钟有波形lane_up 死活不起来示波器上gt_refclk幅度正常、频率也对但lane_up全 0channel_up一直是 0ILA 里看pma_init已经释放了。原因通常有两个参考时钟的差分对接到了非 MGTREFCLK 专用引脚上普通 IO 引脚进不了 Quad 的时钟网络或者收發器实例所在的 Quad 和参考时钟能驱动的 Quad 范围对不上跨了不止一个 Quad。解决方式是把引脚约束和 IP 配置里的 Quad 编号放在一起核对PACKAGE_PIN查板卡原理图LOC查器件手册的收发器位置表两边必须能对上。另外注意参考时钟的 P/N 极性原理图上标反了在约束里也要跟着反否则时钟进不来。这一条是最容易翻车的地方我第一次碰的时候在这上面耗了整整两天。5.2 现象仿真能过上板 channel_up 拉起来又反复掉channel_up短暂拉高然后周期性掉下去ILA 里能看到lane_up也跟着掉。常见原因是复位时序不满足pma_init保持时间不够或者reset_pb在user_clk还没稳定的时候就释放了。GTX 的 PMA 初始化有一个最小保持时间短了会导致 PLL 没锁稳就被放行链路起来之后又会失锁。解决办法是把reset_logic.v里的计数器加长pma_init至少保持 2 ms 量级reset_pb在pma_init释放后再多等几百个user_clk。另外检查init_clk是不是真的自由运行如果它依赖user_clk而user_clk又依赖链路状态就构成了环形依赖表现就是这种来回抖。5.3 现象数据能通但偶发错帧frame_check 缓慢报错大部分帧是对的每隔几百帧出一帧错误frame_check的错帧计数缓慢增长。原因多半在tkeep上Framing 模式下最后一拍的有效字节数由tkeep决定如果发端把tkeep固定写成全 FF接收侧就会把无效字节也算进帧长帧长一错后面的帧边界就跟着错位。还有一种情况是 CRC 打开发送、接收侧没打开或者两侧的字节序设置不一致。Aurora 8B/10B 有 Little-Endian 和 Big-Endian 的选项两侧必须一致选错了数据每个字节都是反的但帧结构是对的所以只表现为数据错、不表现为链路断很有迷惑性。处理方式是在发送侧严格按剩余字节数生成tkeep接收侧用rx_tkeep去写存储器别按tvalid的数量全写。CRC 和字节序这两项在 IP 配置里改一次就要重新生成改完记得把接收侧配置同步一遍。注意错帧计数是一帧一帧慢慢涨的不是一下全错很多人扫一眼「基本都通」就放过去了等量产才发现误码率不达标。5.4 现象改了 IP 参数重新生成工程编译报模块重复定义改完.xci里的 Lane 数或接口模式重新生成综合时报一堆模块重复定义或者报某个端口找不到。原因有两个旧的生成目录残留比如aurora_8b10b_0.gen或.srcs下的旧文件还挂在工程里或者 IP 例化名和工程里另一个同名实例冲突。解决办法是重新生成之前先把工程里旧的 IP 文件从文件列表中移除删掉.gen、.srcs下对应的目录再重新生成。改完配置之后接口位宽会变顶层例化也要跟着改tdata从 64 bit 变成 32 bit 的时候下游的 FIFO 位宽、tkeep位宽都得同步这类连锁改动漏一处就是编译错误反倒好查。5.5 现象ILA 抓不到任何收发器内部信号想看 GT 内部状态把 ILA 挂到gt_wrapper里的信号上结果一个都抓不到或者工具直接报不能探测。原因是 GT 的内部信号大多在X0Y的硬核里Vivado 不允许直接对收发器内部信号插 ILA。可行的替代方式是打开 IP 配置里的 DRP 接口通过 DRP 读 PLL 锁定状态、眼图扫描结果和错误计数或者用 IP 自带的状态端口hard_err、soft_err引到顶层再挂 ILA。这两个错误标志很值钱hard_err一般是链路层硬错误soft_err是 8B/10B 解码里的非法字符或 disparity 错误链路质量不好时它会先动。6. 二次开发改造把示例工程改成自己的数据通路示例工程跑通之后改造的核心就一件事把frame_gen和frame_check换成你的业务逻辑其余部分尽量别动。我的做法是先做减法。把exdes.v里的frame_gen、frame_check两个实例连同相关连线一起删掉顶层的用户接口直接引出成端口然后综合一次确认工程在没有这两个模块的情况下能过。这一步会暴露所有隐式依赖比如某个复位信号原本是从frame_check里引出来的删掉之后就悬空了。先让它编译过再往里加东西。接着是加自己的数据源。如果是采集类应用接收侧用 FIFO 缓存rx_tdatarx_tlast一到就把这一帧的长度和 CRC 状态写进一个描述符 FIFO发送侧同理把要发的数据打成帧按第 4 章那段tkeep生成逻辑驱动tx接口。业务逻辑全部放在user_clk域里别跨到sync_clk上去跨域就要加异步 FIFO复杂度立刻上一个台阶。验证上我坚持两条一是仿真里必须有错帧注入人为把某一帧的tkeep改错看接收侧能不能检出来二是上板后先跑长时间的环回把soft_err和hard_err计数引到 ILA 或者寄存器里读出来跑几个小时看计数是不是稳定在 0。误码这种东西很玄学短时间测不出来不代表没有。改造点建议动作验证方式数据源删frame_gen接自己的 FIFO 或 BRAM 读侧仿真比对源数据与回读数据数据接收用rx_tkeep控制写使能别按tvalid全写注入短帧检查帧长复位保留reset_logic只改计数器位宽上板看channel_up是否稳定不掉约束只改 PIN、LOC 和create_clock周期综合后看时序报告里 GT 相关路径最后提醒一句.xci一旦定版就别随手改改一次要重新生成、重新约束、重新仿真成本比想象中高。我现在的习惯是每次动 IP 配置之前先把整个工程目录复制一份出问题直接回退比在 git 里翻半天 diff 靠谱。希望帮到你。本文还有配套的精品资源点击获取