☰
FPGA高速通信实战:Chip2Chip IP配置与仿真调试避坑指南
2026/10/7 9:04:15 网站建设 项目流程

我在一个项目里需要在两块FPGA之间做点对点高速视频流传输,需求净带宽大概3Gbps。刚开始我打算直接用Aurora 8B/10B IP核自己搭协议栈,结果从GT复位到链路初始化再到数据稳定传输,一路踩坑,光是确认“链路up”的信号就折腾了两天。后来切到Xilinx Chip2Chip IP核,它把Aurora PHY、链路层、流控和AXI4-Stream接口全部封装好,配置界面里只需要关心线速率、数据宽度和参考时钟。这篇文章我会把Chip2Chip IP核在配置和仿真阶段遇到的关键问题整理一遍,重点讲Aurora PHY通信配置时的决策逻辑、仿真启动时的复位序列,以及几个让波形变红的隐形根源。内容适合正在用Vivado做FPGA间通信设计和仿真的朋友,尤其踩过“GT起不来”和“channel_up永远不拉高”这类坑的人。

1. Chip2Chip与Aurora PHY:这套组合到底在做什么

1.1 Chip2Chip IP核的本质

Chip2Chip IP核是Xilinx提供的一个面向“芯片到芯片”点对点通信的解决方案。它底层跑的是Aurora 8B/10B协议,但用户不需要关心Aurora的通道绑定、时钟补偿、帧格式这些细节。IP核对外暴露的是干净的AXI4-Stream接口,发送侧只管把数据流推进去,接收侧只管把数据流读出来,中间的高速串行传输全部由IP核自己打理。

用一句话概括:Chip2Chip是Aurora IP核的上层简化封装,专门为了“两个FPGA之间无脑传数据”这种场景。在Vivado里配置Chip2Chip时,很多选项和Aurora IP完全一样,因为底层就是同一个GT transceiver和同一套Aurora链路层逻辑。只不过Chip2Chip帮你把用户侧的握手状态机写好了,这对项目进度紧张的场景非常友好。

1.2 Aurora协议在Chip2Chip里的角色

Aurora协议是Xilinx私有的轻量级高速串行协议,它定义了物理层和链路层的分工。物理层负责用FPGA内部的GTX/GTH/GTY transceiver做高速串行收发,包括8B/10B编解码、串并转换、时钟恢复;链路层则负责初始化、通道对齐、错误检测、时钟补偿序列的插入与移除。

Chip2Chip IP核直接使用Aurora作为传输管道,所以你在配置界面里看到的“Line Rate”“GT Reference Clock”“Data Width”这些参数,本质上是Aurora PHY的参数。你把它配对了,Chip2Chip才能正常工作。

有些朋友会问:那我直接例化Aurora IP核不就行了?确实可行,但直接例化Aurora IP时,你需要自己处理用户接口的流控、帧协议、初始化握手。比如发送端什么时候可以发数据、接收端怎么判断一个帧结束了、通道绑定失败怎么办。这些东西在Aurora IP核里面只给了最底层的端口,用户侧的状态机完全要自己写。Chip2Chip就把这些脏活累活全包了。

1.3 为什么选Chip2Chip而不是直接撸Aurora

我当时的决策很简单:项目周期紧,我不想在链路层协议上花太多时间。用Chip2Chip之后,我的RTL代码里只需要维护一个AXI4-Stream接口的状态机,发送侧检测tready,接收侧处理tvalid,核心业务逻辑很快就跑起来了。

代价是灵活性降低。Chip2Chip只支持单通道点对点,不支持多通道绑定,也不支持Aurora的广播、多播这些高级功能。如果你的应用是两个FPGA之间固定的一对一高速流传输,Chip2Chip是最省心的选择。如果要做复杂网络拓扑,还是得回归Aurora IP甚至更底层的手写GT。

2. 配置前的三个计算题:线速率、用户时钟和GT参考时钟

很多人的Chip2Chip仿真起不来,问题往往出在配置阶段拍脑袋填参数。这一节讲清楚三个关键计算,填完再动手。

2.1 线速率怎么定:从吞吐量倒推

线速率不是随便选的,要从实际有效带宽倒推。Aurora 8B/10B编码每个字节要额外传2bit,所以链路有效数据率只有线速率的80%。假如你需要净荷3Gbps,那GT线速率至少是:

3Gbps ÷ 0.8 = 3.75Gbps

选线速率时不能只按理论值选,还要留出余量。因为Aurora链路会周期性插入时钟补偿序列,这也会吃掉一部分带宽。我的习惯是至少留10%的余量,例如需求3Gbps,我选4Gbps线速率,这样即使有少量重传也不至于断流。

Vivado的Chip2Chip配置界面里,线速率选项一般从几百Mbps到6Gbps以上都有,具体上限取决于你使用的FPGA器件和GT transceiver类型。7系列里GTX普遍支持到6.6Gbps,Ultrascale+的GTH/GTY会更高。选型时先查器件手册,别等到综合报错再回头改。

2.2 用户接口时钟和数据宽度的关系

数据宽度决定了用户侧AXI4-Stream接口的时钟频率。Chip2Chip IP核的数据宽度常见选项是2字节、4字节、8字节。用户时钟和线速率、数据宽度的关系是:

用户时钟频率 = (线速率 × 0.8) / (数据宽度 × 8)

举个例子:线速率4Gbps,数据宽度4字节(32bit),用户时钟就是:

4Gbps × 0.8 / 32bit = 100MHz

这个时钟是用户逻辑工作的时钟,也是复位、握手信号的时间基准。配置IP核时一定要把这个时钟算准,否则后续状态机时序分析跑不过。

我当时算完用户时钟后,又专门确认了一下FPGA内部的时钟资源能不能生成这个频率。如果要用MMCM/PLL分频,还要看输入时钟范围是否满足。有些同学配置时没注意,用户时钟设成250MHz,但FPGA内部逻辑根本跑不到250MHz,导致时序收敛失败。

2.3 参考时钟选择与GT位置映射

GT参考时钟是另一个大头。Chip2Chip IP核需要外部提供一个参考时钟给GT transceiver的PLL。这个参考时钟频率通常等于线速率除以一个系数,常见的是线速率/2或线速率/4。例如线速率4Gbps,参考时钟可以用125MHz(4GHz/32?这里需要查手册)——实际上不同GT型号支持的参考时钟频率范围不同。

实际操作中,Vivado的IP配置界面会让你选择参考时钟的来源,比如refclk是来自某个专用时钟引脚还是来自GT Quad内部。关键点是:参考时钟引脚必须落在你使用的GT所在的Quad上。不同Quad的高速时钟引脚物理位置不同,选错了要么编译时布线困难,要么干脆产生不了时钟。

我栽过一次:芯片上选了GTH Bank 224,但在配置界面里把参考时钟引脚选成了Bank 225的时钟引脚,结果综合过了,布局布线时报错说参考时钟无法驱动目标GT的PLL。后来按照Vivado的"GT Placement"提示重新选了参考时钟,问题才解决。

除此之外,power_down端口必须在用户逻辑中显式拉低。Chip2Chip IP核会有gt_power_down或类似的端口,仿真时如果悬空,GT永远处于power down状态,链路自然起不来。这个不起眼的问题最容易让人怀疑人生。

3. 仿真启动的细节:复位、初始化与链路建立的正确姿势

仿真阶段最容易出问题的就是复位时序。Chip2Chip底层是Aurora,而Aurora链路初始化有一套严格的握手流程。在testbench里随便来个“上电后拉高复位再拉低”的节奏,十个有九个会卡在初始化。

3.1 复位信号的拉低顺序和时长

Chip2Chip IP核通常有一个整体复位信号,可能叫axi_reset或者gt_reset。这个信号不是简单的一上电就解除就可以。Aurora链路要求GT的TX和RX在复位释放后完成各自的PLL锁定、TX复位完成、RX复位完成,然后才进入通道握手。

正确做法是:

  1. 上电后先拉低复位信号,保持至少10个用户时钟周期。
  2. 等待IP核输出的gt_tx_reset_done和gt_rx_reset_done拉高。
  3. 两个都拉高之后,再释放用户侧的AXI4-Stream复位。

在testbench里,可以用一个简单的状态机来跟踪这个流程。伪代码类似:

initial begin reset_n = 0; #100; wait(tx_reset_done && rx_reset_done); @(posedge user_clk); reset_n = 1; end

注意wait语句前面最好加一个超时保护,防止GT一直不完成复位时仿真卡死。

3.2 如何确认Aurora链路初始化完成

Chip2Chip IP核通常会输出一个链路状态信号,比如channel_up或者link_up。只有这个信号拉高,才表示Aurora PHY链路初始化完成,可以发送用户数据。

在仿真平台里,我习惯写一个等待任务:

task wait_link_up; begin while (!link_up) begin @(posedge user_clk); if ($time > 1_000_000) begin $error("Link up timeout!"); $finish; end end end endtask

这个超时时间要根据参考时钟和线速率估算。Aurora初始化通常需要交换若干帧,在仿真中可能需要几十到几百微秒。如果你的testbench只跑几个微秒就断言失败,那多半是超时时间不够,不是逻辑错。

3.3 仿真时常见的超时陷阱

我见过一种经典场景:testbench里给了参考时钟,也给了复位,但channel_up就是一直不拉高。后来发现是init_clk没有配置。Chip2Chip IP核需要用户提供一个慢速初始化时钟(通常几十MHz,比如50MHz)用于GTPLL的配置和链路初始化。如果这个时钟没有给,或者给了之后频率不对,链路就会一直卡在复位状态。

另一个常见陷阱是GT的复位完成信号在仿真中保持低电平。这是因为仿真模型里GT参考时钟需要先跑一段时间,PLL才能锁定。有些GT仿真模型要求参考时钟运行至少10微秒后,gt_pll_lock才会拉高。所以testbench里不要急着检测channel_up,先让时钟和复位稳定跑一段。

还有的朋友用ModelSim仿真时波形显示红色X,排除逻辑后才发现是glbl模块没有编译进去。Xilinx的仿真库要求编译glbl模块用来初始化全局复位、配置高阻等。忘了把这个文件加到仿真工程里,GT模型就会输出一堆X,整个波形看起来就跟最上面的红线一样。

4. 实战调试中的避坑清单:从波形红线到数据错位

仿真通过不等于板级没问题。但仿真阶段埋下的定时错误,后面板级排查会更痛苦。这里把我在多个项目里遇到的高频坑列一下。

4.1 AXIS接口的信号时序坑

Chip2Chip的用户接口是AXI4-Stream。发送侧,你需要等待tready为高才能拉高tvalid并给出有效数据。有些朋友写状态机时只关心tvalid,不管tready,结果仿真里数据看起来全发出去了,但接收端根本收不齐,因为数据在握手不完整时被丢掉了。

接收侧也有坑。Chip2Chip会把tkeep和tdata对齐地输出。如果接收逻辑没解析tkeep,认为每个周期数据都是完整的,就会在最后一拍数据不完整时产生错位。尤其数据宽度超过4字节后,tkeep的组合情况更多,必须按AXI4-Stream规范解析。

我习惯在testbench里做一个数据比对模块:发送端带上递增的序列号字段,接收端校验序列号是否连续。一旦跳号,立刻打印错误并结束仿真。这样能快速暴露握手和被解析错误,而不是淹没在长长的波形里。

4.2 共享逻辑与example design的坑

Vivado的GT类IP都有“Shared Logic”选项,Chip2Chip也一样。可以选择Include、Explicit或None。这个选项控制GT_COMMON(PLL、时钟分频等公共电路)放在哪里。

如果一个工程里同时用了多个Chip2Chip或Aurora IP,把它们都设置成Include Shared Logic,综合时会报错,说GT_COMMON原语被多个模块驱动。正确做法是:只让其中一个IP Include共享逻辑,其他IP选择Explicit方式,在顶层模块中手动例化共享逻辑,或者使用IP自带的Example Design封装。

我踩过一次更隐蔽的:两个Chip2Chip IP共享同一个GT参考时钟,共享逻辑放得离其中一个IP比较远,结果时钟偏斜导致误码率升高。这个在仿真中根本看不出来,只能靠板级眼图测量。后来我把两个IP放在同一个Quad,共享逻辑放在中间位置,问题才解决。

4.3 仿真不收敛/波形为X的排查链路

如果波形里大量信号是红色X或高阻Z,按这个顺序排查:

  1. 检查参考时钟是否有效。在GT参考时钟引脚上添加一个断言,确认时钟频率接近预期。
  2. 检查init_clk是否存在且运行。
  3. 检查复位信号是否已释放,以及释放时序是否满足IP核要求。
  4. 检查gt_power_down是否拉低。
  5. 检查glbl模块是否编译进仿真工程。
  6. 检查IP核输出的所有时钟信号(user_clk、init_clk_out等)是否已经产生。

大部分情况下,波形为X的根源就是某个基础时钟或复位没给到位。不要一头扎进状态机分析,先把时钟复位树检查一遍。

4.4 真实项目中的一次链路不稳定调试

有一块板子,两片Kintex-7通过Chip2Chip通信,仿真全通,但连续跑半小时后偶尔出现CRC错误。用ILA抓soft_err信号,发现会偶发拉高一个周期。查了Aurora协议,soft_err通常表示接收端检测到8B/10B编码错误或帧校验错误。

用IBERT做误码率测试,发现在某一组GT差分对上误码率偏高。最终定位是PCB上这一组差分信号的过孔阻抗不连续,加上参考层被电源隔断,导致信号质量恶化。换了一张改版的PCB后问题消失。

这个经历说明一个残酷现实:Chip2Chip的逻辑和仿真做对了,不代表物理链路就一定稳定。FPGA高速串行通信的上限往往取决于PCB和电源设计,而不是RTL逻辑。

5. 一套可复用的调试流程与检查表

5.1 链路状态寄存器速查

调试Chip2Chip时,以下信号是必须盯着的:

信号/寄存器作用正常状态
channel_upAurora通道建立标志高电平
gt_tx_reset_doneGT发送端复位完成高电平
gt_rx_reset_doneGT接收端复位完成高电平
gt_pll_lockGT PLL锁定高电平
hard_err硬错误标志(通道丢失等)低电平
soft_err软错误标志(编码错误等)低电平
user_clk用户逻辑参考时钟运行
init_clk初始化时钟运行

这些信号不一定全都有精确名称,但Vivado生成的Chip2Chip example design里都能找到对应端口。拿到一个新IP,第一件事就是打开example design的仿真工程,把信号名抄下来。

5.2 从仿真到板级调试的检查清单

仿真阶段:

  • 参考时钟频率是否按配置设置。
  • init_clk是否运行。
  • 复位释放顺序是否符合要求。
  • channel_up是否在规定超时时间内拉高。
  • 用户数据发送是否在channel_up之后才开始。
  • 有没有检查tready握手的完整性。

板级阶段:

  • 使用ILA观察channel_up和soft_err。
  • 使用IBERT对每一对GT差分线做误码率测试。
  • 用示波器检查参考时钟波形质量。
  • 如果出现周期性错误,检查时钟补偿序列是否被异常删除。
  • PCB上高速差分对距离、过孔数量、参考层连续性都要核查。

5.3 我的几句实在话

用了两年多Chip2Chip,我的体会是:它确实把Aurora的门槛降低了大半,但并不是有了IP就能躺平。真正决定项目成败的,还是配置时的计算、复位时序的严谨、仿真的充分覆盖,以及板级信号完整性的基本功。

小事上建议:拿到Vivado生成的example design,别急着往自己工程里套,先跑一遍example的仿真,确认IP在当前器件和版本下能work。然后自己写testbench时,把超时断言写充分。一个自带超时检测、数据比对、错误上报的testbench,能帮你在后续版本迭代里省下大量排查时间。

另外一个小技巧:在testbench里故意把GT参考时钟往上拨动一点,或者加一些随机抖动,看看Chip2Chip的时钟恢复能力如何。虽然这不能完全模拟真实PCB噪声,但能提前暴露出一些过于紧的时序假设。我靠这个提前拦截过一次因为参考时钟容限不够导致的链路不稳定问题,算是成本最低的“预实验”了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询