ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

FPGA高速接口SRIO回环测试与时序优化实战

FPGA高速接口SRIO回环测试与时序优化实战 做FPGA高速接口这一年多SRIOSerial RapidIO是我觉得最值得花时间啃的一块硬骨头。不少朋友在群里问我SRIO回环测试到底怎么搭、时序报红怎么查、IP核配置那么多选项到底怎么选。这期就把我实际调试SRIO的经验完整复盘一遍从协议基础到回环测试代码再到时序优化的具体手法全部摊开来讲。1. 为什么是SRIO和PCIe、Aurora、以太网放在一起怎么选先说结论如果你在多个DSP、FPGA之间要传大量数据且对延迟和实时性有硬要求SRIO几乎是绕不开的选择。我这几年经手的项目从雷达信号处理到图像采集传输片间通信首选就是它。拿它跟另外几个常见高速接口做对比接口典型速率协议开销应用场景实现难度SRIO Gen25Gbps/lane低事务层精简多处理器互联、基带板卡内部通信中高PCIe Gen38Gbps/lane中TLP开销不小CPU与外设、GPU互联高Aurora最高10Gbps极低纯通道协议无协议要求的裸数据传输低以太网10G/25G高TCP/IP栈开销跨设备、长距离传输中这里要注意一个最常见的选型误区很多人看Aurora延迟低、实现简单就直接拿来传关键业务数据。Aurora本质上只保证物理层和链路层的可靠传输没有事务层的概念你需要在FPGA内部自己定义包格式、自己做重传机制、自己处理多节点寻址。而SRIO把这些都定义好了load/store、消息传递、全局共享存储协议栈替你把大部分脏活累活干了。我做个比喻Aurora像一条专用高速公路路况好但你得自己配车队和调度中心SRIO像成熟的高速铁路网车次、信号、调度规则都定死了你要做的只是把货物装上车。SRIO的协议栈也值得先理清楚后面做时序优化时你会反复跟它打交道逻辑层定义了读写事务、消息、维护操作以及包格式。回环测试中主要操作的就是这一层通过发起NREAD/NWRITE事务来验证链路。传输层负责路由和寻址里面有device ID的概念类似MAC地址。多设备互联时这部分决定了包能不能到达正确的目标。物理层包含串行链路训练、8B/10B编解码、时钟恢复。我们说的时序优化一大半落在这层。理解这个分层很关键。很多新手报时序问题时拿了一堆物理层的报告来找我但真正的问题出在逻辑层的缓冲配置上也有人反过来链路没训练成功还在死磕逻辑层的代码——层级定位错了后面全白搭。2. SRIO IP核配置细节每个选项背后的取舍逻辑我用Xilinx家的SRIO Gen2 IP核做例子其他厂商的虽然界面不同但核心概念是相通的。配置界面里有几个关键区域每个选项都不是随便填的。2.1 物理层配置链路宽度选择上项目用4x还是1x/2x不是越高越好。4x链路带宽当然大但PCB走线压力、功耗、FPGA引脚资源占用都是成倍增加。我的经验是板级互联首选4x板内短距离传输2x足够如果只是调试验证1x就能跑通协议先把链路折腾起来再说。线速率方面Gen2标准支持1.25G、2.5G、3.125G、5G等几个档位。5G是SRIO Gen2最常见的工作点包括TI的C6678 DSP、Xilinx的很多FPGA都支持。但这里有个坑5G速率下信号完整性SI要求明显提高PCB板材、连接器、走线长度都会影响链路稳定性。如果PCB设计水平一般不如先从3.125G跑起。参考时钟这里值得多写几句。SRIO IP核的参考时钟频率跟线速率有一一对应关系线速率参考时钟频率内部PLL倍频关系1.25G62.5MHz 或 125MHz1.25G refclk × 202.5G125MHz2.5G refclk × 203.125G125MHz3.125G refclk × 255G125MHz 或 156.25MHz5G refclk × 40我碰到过一种情况有人把125MHz参考时钟用在了需要156.25MHz的配置上结果IP核初始化一直失败报的错误还很隐蔽不看综合报告根本发现不了。参考时钟的jitter性能也要重视SRIO协议对参考时钟的jitter有严格要求如果使用普通的板载晶振而不是专用的时钟芯片碰到偶发性的链路训练失败概率会高很多。2.2 事务层配置Buffer深度是个容易被低估的选项。SRIO协议本身有流控机制接收端的buffer满了会通过物理层的buffer status信息通知对端暂停发送。理论上buffer小也能工作但吞吐量会掉得很难看。我实测过同样在5G 4x链路上接收buffer从32个包增加到128个包实际吞吐提升了将近20%。原因很简单协议一直在处理暂停/恢复的流控包有效数据占比自然就低了。对于需要高吞吐、低延迟的应用我建议接收buffer开大一些发送buffer保持默认即可。发送端一般不需要很深的缓冲因为数据往往是FPGA内部逻辑主动发起写入的你可以通过逻辑自己控制发送节奏。门铃doorbell机制也建议打开。门铃是SRIO消息传递的一种轻量级形式对端发来一个门铃事务时接收端会置一个中断标志。在回环测试里门铃常被用来做“数据发送完成”的握手信号——对端收到完数据后发一个门铃回来发送端就知道上一次传输已经结束了。2.3 高级选项IP核生成时还能选是否包含共享存储单元、是否使能虚拟通道等。调试阶段这些先关掉减少不必要的逻辑复杂性。特别是虚拟通道需要额外的缓冲区资源还会引入额外的时序路径实际项目用到的场景不多。Vivado的IP配置界面右上角会实时显示当前配置的资源占用和时钟结构我习惯先把配置导出成文件存档再去生成IP核。这样后面如果配置需要调整对比差异时非常清晰不用凭记忆回溯之前选了哪些选项。3. 回环测试全流程从链路训练到数据校验回环测试是SRIO接口调试的第一步目的是在不依赖外部设备的情况下验证FPGA内部的物理层、链路层和事务层是否正常工作。3.1 选择合适的回环方式SRIO IP核支持多个回环模式不同模式的测试意义不同硬件回环Hardware Loopback外部用线缆或PCB走线把发送端差分信号直接连到接收端。这种模式最接近真实链路把GTX的TX和RX都验证了包括PCB走线、连接器、焊盘这些物理链路因素。板级调试完成之后我建议把这一步作为必测项。近端PMA回环Near-End PMA Loopback在FPGA内部信号从发送器输出前直接环回到接收器的模拟前端。不经过物理外部走线主要验证GTX的收发器本身。远端PCS回环Far-End PCS Loopback在PCS层做回环信号经过编解码逻辑但不出FPGA。调试顺序上我每次都是先PMA回环、再PCS回环、再外部硬件回环。这样出了问题能快速定位是GTX收发器本身有毛病还是PCB链路有问题。3.2 初始化与链路训练最容易被卡住的一关链路训练这个阶段新手最容易卡住。SRIO物理层有完整的状态机无训练→训练A→训练B→链路OK。IP核会输出链路初始化状态信号比如port_initialized拉高才代表链路完全建立。我碰到过一个典型场景外部回环线缆都接好了port_initialized一直不拉高。排查了很久最后发现是GTX的参考时钟没有完全稳定就复位了收发器——硬件复位信号和时钟稳定标志之间的先后顺序没有对。后来在逻辑里加了时钟锁定检测等gt_rxresetdone和gt_txresetdone都拉高之后再释放复位问题马上就解决了。这里记录一下我实际用的初始化顺序1. 等待参考时钟稳定通过时钟芯片的LOCK输出或MMCM/PLL锁定信号 2. 复位SRIO IP核 3. 等待gt_rxresetdone、gt_txresetdone拉高 4. 再等待port_initialized拉高 5. 发起维护事务读取对端设备ID寄存器验证链路可用第5步很关键这是正式读写之前的一次“握手”能提前暴露很多问题。维护事务是SRIO协议里专门用来做配置和状态读取的不需要复杂的应用逻辑IP核的原语接口里就有寄存器读写端口。3.3 回环测试的Verilog代码骨架核心思路是应用逻辑发起一个NWRITE事务把数据写入FPGA自己的接收端然后通过接收端接口把数据读回来再做比对。因为是从自己发到自己收调试起来很方便。// 简化版SRIO回环测试逻辑框架 module srio_loopback_test ( input wire clk, // 用户时钟 input wire rst_n, output reg test_done, output reg [31:0] error_count ); // SRIO IP核的AXI4-Stream接口信号 wire s_axis_tvalid; wire s_axis_tready; wire [63:0] s_axis_tdata; wire [7:0] s_axis_tkeep; wire s_axis_tlast; wire s_axis_tuser; wire m_axis_tvalid; wire m_axis_tready; wire [63:0] m_axis_tdata; wire [7:0] m_axis_tkeep; wire m_axis_tlast; wire m_axis_tuser; // 发送数据生成模块 reg [63:0] tx_data; reg tx_valid; reg tx_last; always (posedge clk or negedge rst_n) begin if (!rst_n) begin tx_data 64d0; tx_valid 1b0; end else if (s_axis_tready tx_valid) begin tx_data tx_data 64d1; if (tx_data 64hFFFF_FFFF_FFFF_FFFE) begin tx_valid 1b0; end end else if (test_start) begin tx_valid 1b1; tx_data 64d0; end end // 接收数据校验模块 reg [63:0] expected_data; always (posedge clk or negedge rst_n) begin if (!rst_n) begin expected_data 64d0; error_count 32d0; end else if (m_axis_tvalid m_axis_tready) begin if (m_axis_tdata ! expected_data) begin error_count error_count 1b1; end expected_data m_axis_tdata 64d1; end end // 发送完成与测试完成标志 always (posedge clk or negedge rst_n) begin if (!rst_n) begin test_done 1b0; end else if (tx_last s_axis_tready s_axis_tvalid) begin test_done 1b1; end end endmodule写这段逻辑的时候有几个细节要注意AXI4-Stream的tready和tvalid握手时序必须严格按协议来tvalid拉高之后不能因为tready为低就拉低否则属于协议违规。SRIO IP核的用户接口基本都遵循AXI4-Stream这个点错了协议层会直接报错。tlast是包结束标志SRIO的事务包通常以tlast标记边界。如果配置了tuser信号里面包含了事务类型、目标地址等信息需要跟tdata同一个时钟周期对齐拉高。数据校验如果只是单纯比较碰到两个设备初始化时的对齐问题可能整包数据都错位了还在那里傻等。我通常在包头发一个特定的模式比如固定值包序号接收端先同步包头再逐字节校验数据体这样能快速定位是整包开始位置错了还是数据内容错了。3.4 常见的回环测试失败现象现象可能原因排查方法port_initialized一直为低参考时钟不稳、复位出错、GTX初始化失败检查resetdone信号、时钟锁定、逻辑复位时序链路OK但读不到数据目标地址配置错误、对齐没建立检查维护事务读寄存器、对照地址映射数据大量出错编解码失步、PPM偏差、信号完整性差改用PRBS模式测试物理层误码率偶发错误跑几分钟才错一次参考时钟jitter偏大、电源纹波检查clocking wizard配置、电源质量PRBS测试值得单独说。SRIO IP核内部集成了PRBS生成和校验逻辑不用写任何应用代码就能测物理层误码率。我每次回环测试之前都会先用PRBS模式跑半小时以上确认误码率为零再去做业务数据回环。这样可以先把物理层问题排除掉后续逻辑层出错时能更快定位。4. 时序优化方法论从时序报告反推布线布局SRIO接口的时序优化是我在实际调试中花时间最多的地方。很多人以为SRIO这么高速的接口时序问题一定出在GTX收发器本身的TX/RX路径上——这个理解对了一半。实际上真正让我调到头大的往往是用户逻辑和IP核之间的接口时序以及多时钟域之间的交互。4.1 GTX收发器自身的时钟结构GTX收发器的时钟结构要搞清楚每个GTX channel内部有独立的TX和RX时钟域分别由各自的PLL驱动。TX的并行时钟频率取决于线速率和FPGA内部数据位宽并行时钟频率 线速率 / 内部数据位宽以5G线速率、内部32bit位宽为例并行时钟频率 5000Mbps / 32bit 156.25MHz如果你的用户逻辑工作在156.25MHz这个频率上时序就比较好收敛。但很多时候IP核配置出来的内部数据位宽是64bit并行时钟就成了78.125MHz这时用户逻辑如果还是按156.25MHz去跑就要做跨时钟域CDC处理。4.2 用户逻辑的频率匹配和跨时钟域SRIO IP核的AXI4-Stream接口时钟频率是user_clk由IP核内部根据线速率自动生成。我在一个项目里遇到过这样的情况逻辑工程师习惯了把所有模块都跑在200MHz的全局时钟下SRIO的user_clk是125MHz两个时钟域之间直接用同一个FIFO读写结果偶发丢包。解决思路是在用户逻辑和SRIO IP核之间插入一个异步FIFO做缓存和时钟域转换。数据写入侧用用户全局时钟读出侧用SRIO的user_clkFIFO本身处理了跨时钟域的同步问题。这里还有一个经验FIFO的深度不要只按一包数据的大小来算。SRIO的接收端可能会背靠背收到多包数据特别是做了多包合并、一个大事务拆成多个包如果FIFO深度不够数据会溢出。我按最大突发长度 × 2来设置FIFO深度留出足够余量。4.3 时序报告的解读方法Vivado综合实现后时序报告里高频出现三类违例每种的处理思路完全不同第一类是SRIO IP核内部的时序违例。这类问题很少见但一旦出现多半是IP核例化时某个时钟没有正确连接。检查IP核的输入时钟是否和约束一致比如IP核要求GT_REFCLK是125MHz你的约束文件却写成了150MHz后端布局布线就会产生错误路径。这种几乎无解只能改正约束后重新实现。第二类是用户逻辑到SRIO接口的跨时钟路径违例。这是最常见的。user_clk是125MHz用户逻辑跑在250MHz两边直连的话路径延迟可能会超过一个user_clk周期。处理方法有三个从简单到复杂依次是加流水寄存器Pipeline Register打断长路径。异步FIFO隔离时钟域。调整综合属性把关键路径上某些逻辑块复制多份提升布线自由度。第三类是复位网络的时序违例。SRIO IP核的复位信号有自己的时序要求最好跟IP核的user_clk同步后再释放。有些偷懒的做法是把异步复位直接接到IP核上复位信号释放时如果刚好落在时钟上升沿附近可能引发亚稳态链路初始化行为就变得不确定。复位释放同步器的结构很简单// 复位同步释放模块 module reset_sync ( input wire clk, input wire rst_n_in, output reg rst_n_out ); reg rst_n_r1; always (posedge clk or negedge rst_n_in) begin if (!rst_n_in) rst_n_r1 1b0; else rst_n_r1 1b1; end always (posedge clk or negedge rst_n_in) begin if (!rst_n_in) begin rst_n_out 1b0; end else begin rst_n_out rst_n_r1; end end endmodule这个两级触发器结构的延迟非常小但对亚稳态的抑制效果立竿见影。我一直建议凡是连到IP核上的任何控制信号只要来自异步时钟域一律先过两级触发器同步再说。4.4 多die FPGA的约束技巧现在我们用的很多高端FPGA是multi-die架构比如Xilinx的VU9P内部有4个die通过die-to-die接口互联。SRIO这种高速接口逻辑落在哪个die上直接决定了时序能不能收敛。关键技巧是约束文件里用Pblock把SRIO IP核连同它的GTX收发器约束在同一个die上。如果IP核的逻辑分散在两个die之间die-to-die互联路径的延迟是片内的好几倍时序很难跑收敛。我在一个项目上就吃过这个亏。6G SRIO接口的资源自动布局时被分散到两个die上时序报告里少了大概0.3ns的裕量怎么优化都收不回来。后来用Pblock手动指定了位置时序一次通过。这个经验对使用Xilinx VU/UP系列、Intel的Agilex等multi-die架构的FPGA工程师特别有用。Pblock约束写法如下create_pblock pblock_srio add_cells_to_pblock [get_pblocks pblock_srio] [get_cells -hier -filter {NAME ~ *srio_inst*}] resize_pblock [get_pblocks pblock_srio] -add SLICE_X*Y*具体坐标要根据你工程里GTX的物理位置来定在某个die范围内画一块区域把SRIO逻辑框进去即可。约束完后重新布局布线看时序报告确认所有逻辑都在目标die内。4.5 一个实战时序优化案例在这个项目里SRIO用户时钟125MHz数据位宽64bit逻辑要同时处理两路独立的4K图像数据流。图像数据速率接近物理极限导致s_axis_tvalid到tdata这条路径经常报时序违例。一开始我把s_axis上的信号直接接到图像处理模块的输出端时序报告中该路径WNSWorst Negative Slack是-0.2ns。这个负数看着不大但高速接口的时序余量本来是越宽裕越好任何温度和电压波动都可能把-0.2ns变成-0.5ns导致实际误码。处理方法分两步第一步在图像模块和SRIO IP核之间插入一个小的寄存器组把发送数据先打拍到SRIO IP核的时钟域内。第二步把图像模块中产生这些数据的那段组合逻辑做了拆分原来一个时钟周期内完成的运算拆成两个周期多出一级流水。优化后WNS从-0.2ns变成0.35ns整个设计稳定不少。这个案例说明一个高频场景SRIO吞吐高用户逻辑常常以接近接口速率在跑这时候用户逻辑本身的时序设计才是难点。组合逻辑能拆就拆寄存器能加就加不要怕那一两个周期的延迟——对高速数据流来说延迟1-2个周期完全无所谓稳定不出错才是第一优先级。5. 实测中的高频问题与排查策略最后把这几个月来被问得最多的、以及我自己实测中反复踩过的坑集中写一下。每个问题都是真实场景希望能帮大家少走弯路。5.1 链路能训练成功但吞吐量上不去这种问题隐蔽性很强。直观上看链路训练通过了数据收发也正常但吞吐量就是只有理论值的60%-70%。我在一个4x 5G链路上跑出了2.1Gbps的实际吞吐理论上应该是5Gbps × 4 20Gbps怎么算都不对。排查思路是检查NWRITE请求的突发长度——如果每包只有64字节包头和协议开销占比太大吞吐自然上不去。建议单包数据长度提到256字节以上。检查接收端FIFO的读速率——接收FIFO读得太慢流控频繁触发吞吐就下降。看看接收侧逻辑是不是有其他地方在处理数据时卡顿。检查是否有大量写响应Response占用了带宽——在不需要确认的场景可以用NWRITE_with_response关闭响应省去一半控制报文。我的经验是大多数吞吐上不去的情况把单包长度加长、关闭不必要的响应机制之后吞吐马上就能提升不少。5.2 外部回环线缆的连接顺序外部回环测试时很多人直接拿一根SMA线缆把TX和RX连起来结果链路死活起不来。原因在于SRIO是差分对TX由TXP/TXN组成RX由RXP/RXN组成。外部连接时一定要交叉连接——TXP接RXNTXN接RXP。因为FPGA内部的接收端通常是反相处理的直接同相连的话差分信号的极性反了8B/10B解码必然失败。这个坑我见过好几个同事踩过。括线缆标签看不清的情况下最靠谱的办法是用IP核的gt_rxbyteisaligned信号来辅助判断——连对了这个信号会拉高连反了怎么训练都过不去。5.3 参考时钟相位噪声的影响SRIO对参考时钟的质量要求比很多人想象中高得多。我做过一次对比实验同一个SRIO IP核用普通晶振做参考时钟时链路偶尔出现误码换成专用的低抖动时钟芯片比如TI的LMK系列同样的PCB和逻辑跑48小时零误码。数据上SRIO Gen2的5G速率要求参考时钟的随机抖动一般控制在1ps RMS以内。普通晶振出厂规格可能标称3-5ps RMS实际工作时还会更差。如果PCB布线时参考时钟没有单独包地隔离或者和数字信号靠得太近抖动还会进一步恶化。建议参考时钟电路要远离高速数字信号时钟芯片输出经过RC滤波后再送GTX的REFCLK引脚。5.4 LVDS接口和SRIO同时使用时IO Bank分配混乱在资源紧张的时候人容易把片内所有接口混排LVDS、SRIO、DDR接口挤在一个区域。我在一个项目里就遇到过LVDS采集接口占用了靠近GTX的IO Bank导致SRIO的参考时钟输入和GTX收发器之间的走线被迫绕了很远时序余量掉了将近0.4ns。如果板上资源和引脚允许建议把高速串行接口和并行LVDS接口分配在距离足够远的IO Bank。实在分配不开就要在综合阶段手动设置区域约束让两种接口的逻辑不要相互穿插。5.5 FPGA和DSP之间的SRIO互联常见的对齐问题SRIO在FPGA-DSP互联场景中的应用非常多。TI的C6678 DSP和Xilinx FPGA之间的SRIO互联我反复调过多次。几乎所有初次联调的人都会碰到同一个问题设备ID没对齐。SRIO协议规定每个设备有唯一的device ID。FPGA侧发送的请求包里带有目标设备IDDSP侧接收时要检查这个ID是否和自己配置的一致。如果DSP的boot配置里把device ID设成了0x01FPGA一直往0x00发数据链路层通信正常但数据永远到不了DSP。看起来像是链路有问题实际上只是ID映射没配。排查方法很简单用维护事务读取对端的device ID寄存器确认双方看到的ID一致后再跑业务。这里写个小技巧FPGA侧SRIO IP核通常有寄存器访问接口可以直接通过IP核自带的配置端口发起维护读不需要额外写逻辑代码。5.6 复位亚稳态的修复记录最后聊一个最容易被忽视的问题——复位。热词里都提到了“fpga复位信号亚稳态”可见这是高频痛点。SRIO IP核的复位信号有几个不同的用途gt_resetGTX收发器复位、port_reset链路层复位、user_reset用户接口复位。这些复位信号不是随便拉一下就行的它们之间有严格的时序关系。我在一个项目里出现了一个诡异现象回环测试偶尔在一上电的前几秒报错之后恢复正常。后来抓了信号发现是gt_reset释放后用户逻辑的复位信号在异步时钟域下被释放和user_clk的上升沿撞在一起进入了亚稳态导致最初几个控制信号处于不定态。最直接的修复方式就是前面提到的复位同步释放结构所有的复位信号经过两级触发器同步到各自的目标时钟域之后再接到功能模块上。这个代码一共就十几行但解决的是看似不可捉摸的偶发问题。回环测试完成的判断标准写完这么多最后说说怎么才算一次成功的回环测试。不仅仅是test_done拉高、error_count保持为零就够了。我给自己定的标准是PRBS模式跑30分钟以上误码率为零。业务数据回环跑完预设的所有测试包error_count始终为零。用ILA集成逻辑分析仪抓到完整的链路初始化过程确认port_initialized在所有条件下都能正常拉高。断电重启、温度循环之后重复测试结果稳定。可能有人觉得PRBS跑30分钟太浪费时间但高速接口的偶发错误本来就很难复现跑长一点能提前暴露电源和时钟质量问题。我在一个项目里就是靠长时间的PRBS测试发现了一个电容选型错误导致的电源纹波问题问题发现时整板还没拿去烤机改起来成本要低得多。SRIO接口的调试就是这样问题往往不太直观。协议本身不复杂复杂的是把物理层、链路层、事务层、用户逻辑层层打通还要让时序收敛到稳定状态。希望这篇复盘能让大家少走一段弯路。如果有其他SRIO相关的坑也欢迎来交流。
返回列表