☰
SRIO回环测试与数据流控制:从物理层到事务层的工程实践
2026/9/29 18:27:49 网站建设 项目流程

做SRIO接口调试头一年,我干过一件蠢事:两颗FPGA的SERDES直连,中间没有任何转接芯片,我信心满满地把SRIO IP配置好,结果链路训练协议状态机卡在某个状态就是上不去。我对着原理图查了整整一下午,最后发现是IP核里那个Loopback选项被我不小心选成了内部环回,数据根本没送到对端去。从那次之后,我养成一个习惯:凡是调SRIO,第一件事不是接对端设备,而是先在自己的板子上跑通回环测试,把发送路径、接收路径、流控状态全部独立验证过一遍,再谈互联。

这篇文章我不打算讲太多理论,重点放在SRIO回环测试的工程实现和数据流控制的观测方法上。我会把我实际调试过程中用到的环回路径选择、IP配置、测试序列设计、寄存器观测点、以及踩过的坑都整理出来。无论是刚接触SRIO的FPGA工程师,还是已经调通但遇到数据丢失、链路不稳定等问题的同行,应该都能从里面找到能直接用的东西。

1. 回环测试放在SRIO第一步的真正原因

SRIO接口的问题从来不是单点的。物理层要看SERDES时钟恢复、均衡、极性,链路层要看训练状态机和流控,事务层要看请求和响应是否匹配。如果第一次调试就把两片FPGA连在一起,出现异常时你根本无法判断是哪一侧、哪一层出了问题。回环测试的价值在于:人为构造一条只有发送端和接收端、没有外界干扰的短路径,把问题范围缩小到你能控制的位置。

1.1 三种回环模式,分别验证到哪一层

SRIO IP核里常见的回环模式有三种,我在不同阶段会切换使用。

  • 物理层PMA回环:发送端的串行数据在SERDES收发器内部直接回到接收端,不经过线路。这个模式下,SERDES的时钟恢复、CDR、8B/10B编解码都会被测到,适合确认GT收发通道本身没有物理损伤。
  • 物理层PCS回环:回环点位于协议编解码逻辑之后,也就是说数据经过对齐、字节重排等处理后绕回,能够覆盖更深一层的链路逻辑。
  • 外部线缆回环:把发送差分线对用短电缆或PCB走线连接到接收差分线对,验证从引脚到引脚的真实物理链路。这种方式最接近真实互联,但要求板卡上预留了可操作的回环路径。

我个人的建议是:板卡调试初期先用PMA内部回环做链路训练,报文收发基本正常后再切换到外部线缆回环做交叉验证。不要一上来就外部环回,否则一旦环境噪声大或者时钟不稳定,很容易误判成逻辑问题。

1.2 回环测试能提前暴露哪些真实问题

回环测试最常见的结果是“链路修练成功了,但是收发数据对不上”。这个现象背后往往藏着几类问题。

第一,SERDES极性反转。PCB布线时差分对正负接反,链路训练可能仍然能完成,但接收到的数据字节序会错乱。回环测试时如果只发固定数,或者只用CRC做判断,很容易漏掉这种问题。

第二,参考时钟频率不匹配。SRIO IP期望的参考时钟是125MHz、156.25MHz、250MHz等,如果给错频率,链路训练不一定立刻失败,但后续传输会频繁出现CRC错误。这类问题用回环测试能快速暴露,因为内部回环时链路距离极短,正常情况下不应该出现任何物理层错误。

第三,恢复时钟抖动过大导致链路间歇性断开。这在外部回环里比较常见,尤其是板卡走线质量差时。通过观察链路up状态寄存器和错误计数器,可以判断是间歇故障还是稳定故障。

把这些潜在问题在回环阶段解决掉,后面做端到端互联时才不会手忙脚乱。

2. SRIO数据流控制到底在控制什么

数据流控制这个名词看起来抽象,实际落到代码和波形上,就是一系列缓冲区和握手信号的管理过程。SRIO的数据流,按逻辑层事务类型分成不同的“类”,每一类事务都有独立的处理规则和缓冲约束。你把数据从用户逻辑侧送入SRIO IP核时,IP核会根据对端缓冲区的可用空间来决定是否接收你的数据。对端缓冲区满的时候,IP核会通过流控机制告知本端停止发送,本端的用户接口就会拉低ready,你的状态机就只能等在那里。

2.1 数据流不是一个包,而是一组不同类型的事务

SRIO逻辑层事务,对应的就是用户逻辑里要发起的一次次操作。最常用到的有这么几类。

  • NWRITE:普通写操作,不需要响应,适合批量数据搬运。
  • NWRITE_R:带响应的写操作,对端收到后必须回一个响应事务,适合需要确认的关键数据。
  • NREAD:读操作,请求方发出读请求,对端把数据以响应事务返回。
  • SWRITE:流写,专门用于大数据块连续搬运,payload可以很大,效率高。
  • DOORBELL:门铃事务,没有数据负载,只有16bit的信息字段,通常用来做中断通知。
  • MESSAGE:消息事务,携带数据负载,但格式和普通写不同,适合多处理器通信场景。

回环测试里,我一般会按照“读写事务、门铃事务、批量流写事务”三类分别测试。因为这三类事务在IP核内部走的数据路径不完全一样,单独测试才能定位是公共路径的问题还是某类事务特有的问题。

2.2 credit机制:数据流控制的底层逻辑

SRIO在物理层和逻辑层都用了信用量机制管理缓冲区。你可以把credit理解成电影院座位:发送端每发一个数据分组,就消耗掉一个座位;接收端处理完成一个分组,就通过控制符号通知发送端“这个座位空了”。当发送端的可用credit归零,就必须停止发送。

在回环测试中观察credit机制最直观的方式,是看用户接口上的流控信号。IP核的发送侧用户接口通常是一组AXI4-Stream形式的信号,其中tready信号的变化就反映了对端credit状态。当IP核内部缓冲计数耗尽或检测到对端BUF_STAT显示没有空间时,tready会被拉低,用户逻辑必须暂停tvalid。很多第一次写SRIO用户逻辑的工程师会忽视tready,在tready低电平期间继续把数据放在总线上,最后导致数据丢失。

2.3 门铃与消息:控制信息怎么插进数据流

在纯数据回环测试里,我们往往会忽略门铃事务,但实际工程中门铃恰恰是流控的关键。比如一个数据包从FPGA A发到FPGA B,FPGA B需要知道“数据已经写入我的哪个地址范围”,这时A可以通过门铃事务发送一个16bit的描述值触发B的中断。门铃事务本身只有16bit信息,不会占用太多缓冲区,但它的到达和确认必须可靠。

我测试门铃回环时,习惯在接收侧写一个门铃接收状态机,每收到一个门铃事务就更新一次寄存器并拉高中断。发送侧则循环发送门铃,同时记录发送次数。然后去比较发送次数、接收次数、中断次数三者是否一致。这个测试通过后,再去做数据搬运,整个系统的握手逻辑才会更让人放心。

3. 搭建一套可复现的SRIO回环测试环境

回环测试的环境搭建,不是简单把IP配置拉出来跑一下就完事。很多细节会影响结果,而且这些问题通常在回环测试阶段暴露得最明显。

3.1 硬件链路:参考时钟、复位与回环连线

硬件上第一件事是确认参考时钟。SRIO核需要的参考时钟频率由IP核配置决定,通常是125MHz或156.25MHz。对SERDES来说,参考时钟的质量极其重要,我不建议使用FPGA内部PLL分频出来的时钟做参考源,哪怕它在普通逻辑里工作正常。最好是从板上专用时钟芯片引一路时钟到SERDES参考时钟引脚。

复位也有讲究。很多SRIO IP核有独立的复位启动流程,外部复位释放后,IP核内部的复位状态机要经过一段时间才能进入正常操作。如果上电后马上操作寄存器,往往会读到全F或全0。回环测试中我会等复位释放后再加延时,比如等待20ms以上,再去读取设备ID寄存器和端口状态寄存器,确认IP核完成初始化。

回环链路的话,如果板上有SMA连接器或者调试排针,可以准备一根短的同轴电缆或差分对线做外部环回。内部PMA环回则不需要任何外部连线,直接用IP配置或寄存器设置开启。

3.2 IP配置里真正影响数据流的选项

以Xilinx的SRIO IP核为例,配置界面里有一堆选项,但下面这几个和数据流直接相关,必须认真对待。

  • 链路速率和通道数:速率影响吞吐,通道数影响AXI数据位宽和时钟频率。回环测试建议先用单通道最低速率,比如2.5Gbps x1,调通后再提高。
  • 端口类型和设备ID:SRIO是点对点互联,两端设备ID需要互相匹配。配置时要把本端ID和对端ID都填对,否则对端会直接丢弃你的事务请求。
  • 回环模式选项:在IP配置阶段可以直接选内部回环,但运行时也可以通过维护事务读写寄存器来切换。如果调试过程中回环选项没有生效,多半是字节偏移或总线位宽配置和实际不符。
  • 用户接口的tdata位宽:常见的有64bit、128bit、256bit。宽度越大,单拍能塞的数据越多,但用户逻辑判定头包和尾包的逻辑也要相应调整。回环测试初期建议把位宽设小一点,方便抓内部信号。
  • 流控的维护响应能力:如果配置成不支持维护事务,寄存器窗口和调试通道会受影响。

3.3 用户逻辑端如何发起一次真实的事务

发起一次SRIO事务,在用户逻辑侧看起来就是向AXI4-Stream接口写入一段符合规范的“分组”。里面包含转发头(route header)和载荷数据。这里我不贴完整代码,只说一说状态机的关键状态。

一个最简单的NWRITE_R写请求状态机可以这样设计:

  1. 空闲态:等待启动信号。
  2. 发送头状态:把构造好的SRIO头组合成对应总线位宽的数据,拉高tvalid,等待tready。
  3. 发送数据状态:把用户数据按顺序送入tdata,在最后一个数据拍拉高tlast。
  4. 等待响应状态:NWRITE_R要求对端返回响应事务,所以状态机要等待接收侧出现对应响应,或者由专门的响应处理模块记录完成标志。
  5. 返回空闲态或进入下一次发送。

头包的构造,需要填transaction type、destID、srcID、address、byte count等字段。我一开始手工构造时经常把地址偏移算错,后来统一用脚本生成宏定义,从源头避免错误。

特别注意:NWRITE_R的响应事务在接收路径上回来,但IP核不会帮你判断哪笔响应对应哪笔请求,需要用户逻辑自己维护一个tag列表。回环测试里为了省事,可以固定tag值为某个常数,这样一旦看到响应回来,就能直接判定成功。

4. 回环测试中怎么观察和验证数据流

链路训练通过只是第一步,数据流是否真正可控,还需要通过寄存器和计数器去验证。回环测试的好处是,所有数据都从本端发出、本端收回,所以任何环节出错都能在内部观测点上抓到。

4.1 靠寄存器与计数器锁定各环节

SRIO IP核会提供一组状态寄存器,回环调试时我常用的观测点有这些:

  • 端口状态寄存器:确认PORT_OK、RETE_ERR等状态位,判断链路是否稳定。
  • 错误计数器:CRC错误计数、超时错误计数,只要这些计数在增长,物理层一定有问题。
  • 接收缓冲区可用空间状态:看接收端还能承接多少数据,用来判断流控是否合理。
  • 发送完成计数:用户逻辑每发出一笔事务,计数器加一。
  • 接收完成计数:每收到一笔完整事务,计数器加一。

在回环测试中,最理想的状况是发送计数等于接收计数,且响应计数也匹配。如果发送大于接收,就意味着有事务在传输过程中被丢弃或接收侧没有完整抓取。如果接收大于发送,那说明可能是回环数据里混入了旧的缓冲数据,这种情况偶尔会在内部回环切换后出现,需要先做一次软复位再开始统计。

4.2 测试数据生成:递增模式、PRBS还是全0全1

很多初学者做回环测试只会发全0或者递增数据。说实话,这两种模式在SRIO里都不是最优的。

全0全1只能看出链路“通不通”,很难发现总线位宽错位、字节序错误的问题。递增数据可以检查到一部分字节错位,但连续递增在数据链路上也有固定模式,某些错位场景可能恰好测不出来。

我建议至少准备三种测试数据模式:

  • 递增模式:每个字节依次加一,判断发送端和接收端解包后的字节序是否一致。
  • 伪随机序列PRBS:用于压力测试,数据模式更接近真实业务。
  • 带边界的特殊数据:比如全0、全1、0xAAAA、0x5555交替,专门用来暴露链路级的信号完整性问题。

回环测试中判断数据对错有两种做法。一种是接收侧做实时比较,接收完一包后和数据模型比对;另一种是接收侧把数据存入RAM,测试结束后用上位机或脚本回读比对。前者适合长时间压力测试,后者适合精确诊断。

4.3 从回环测试里读出真实吞吐率的细节

回环测试还可以用来估算链路吞吐率。很多人以为把SRIO配置成x4 5Gbps就能跑到20Gbps线速,实际根本不是这么回事。影响吞吐率的因素很多:

  • 事务头本身占用的时钟周期
  • 每次事务的payload大小
  • 流控机制导致的等待周期
  • 用户接口到IP核之间的握手效率

我测吞吐率时,会用内部计数器统计发送的payload字节数,同时用32bit定时器记录运行时间。在发送逻辑里连续发出大量SWRITE事务,每个事务的payload尽量设计成最大长度,然后计算实际吞吐。测量的结果一般来说会低于理论线速,但差距如果超过20%,就要去检查流控等待时间是否过长,或者发送状态机是否在每笔事务之间多等了多余的周期。

这里有个容易忽略的点:SRIO的发送端口对连续事务之间有最小间隔要求,IP核会按内部协议强制插空拍。用户逻辑侧看到的tready会周期性拉低,这属于正常现象,不代表链路有问题。关键是确认这个拉低节奏是否符合设定速率下的预期。

5. 回环不通时的完整排查链路

这部分内容来自于我真实调SRIO的经历。回环测试如果一开始就不通,不要急着改代码,先按下面的顺序排查。

5.1 PORT_OK不置位:先查链路层

链路训练完成、PORT_OK置位,是一切数据传输的前提。如果PORT_OK不置位,首先要查IP核有没有处于回环模式。

我踩过的坑是:在IP配置里选了内部回环,但软件初始化时又通过维护事务往端口寄存器写了非回环配置,结果IP核实际拿到的模式是外部正连。这种事很隐蔽,因为两个地方配置冲突时,寄存器最终生效的是后写的值。解决方法是统一把回环配置放在一个初始化流程里,读取寄存器确认实际生效值,而不是只看配置界面的下拉框。

PORT_OK不置位还要看参考时钟是否稳定。用示波器或频率计测量参考时钟频率,同时确认GT复位信号在训练期间没有被意外拉低。链路层错误计数如果一直在涨,优先怀疑SERDES端接电阻、参考时钟质量,而不是逻辑代码。

5.2 数据对不上:先分清字节序和时序问题

链路训练成功,但是收发数据不一致。这种情况先不要怀疑IP核,大概率是用户逻辑对SRIO头字段解析不对。

我处理这类问题时,会在接收侧把收到的头字段和发送侧发出去的寄存器值逐项打印出来比对。常见问题包括:

  • destID和srcID写反,对端把包丢弃,而回环模式下本端自己收自己,情况会更加隐蔽——因为回环存在空口走线延迟和不同的字节序。
  • 地址字段的字节偏移没算对。SRIO地址按字节寻址,但用户接口的数据位宽是128bit时,地址低4bit可能被IP核忽略,需要用户逻辑做对齐。
  • tdata位宽和SRIO头字段的字节顺序在FPGA端是little-endian还是big-endian。Xilinx IP核内部有一个属性叫header_data_big_endian可以调整字节序。我第一次遇到数据解析不出来时,就是这个设置没有跟上用户逻辑的习惯。

如果数据接口的手握状态机在tready低电平期间仍然发送了数据,也会导致数据错位。这种情况回环测试能查出来,但波形上不明显,需要用ILA抓取tvalid和tready信号,确认它们重叠时数据是否有效。

5.3 流控失效的边界场景:多包连续发送时丢包

还有一种很难查的问题:单包回环正常,多包连续发送时会丢包。我遇到过连续发100包,其中第37包、第88包丢了,而且不是每次都丢,看起来很随机。

这种丢包现象,首先怀疑的是发送侧没有正确处理tready。我的代码里只判断了tready为高时发送,忽视了tready可能在发送过程中被拉低。AXI4-Stream协议要求tvalid一旦拉高且tready拉低时,tdata必须保持不变,直到这一拍完成握手。如果tready拉低后我仍然改变了tdata,IP核取到的数据就会错。

解决方法是把发送状态机改成标准AXI握手逻辑:tvalid拉高后,必须等到tready为高才算发送完成;如果tready为低,则保持tdata和tvalid不变。这不是SRIO特有的问题,但在高速率下更容易暴露。

另外,多个VC(虚拟通道)并发时,流控是按VC独立进行的。如果用户逻辑把高优先级事务全部塞进同一个VC,该VC缓冲耗尽后整个发送侧都可能受阻塞。回环测试时可以刻意让不同VC同时发数据,观察是否存在一个VC占满缓冲后影响另一个VC的情况。


我实际调完一轮回环测试后最大的感受是:SRIO调试不要迷信波形,也不要只靠逻辑分析。回环测试把物理层、链路层、传输层剥离开,每一层都有对应的寄存器和计数器可以观测。你只要把观测点设置得足够细,问题最终都能定位到某个具体的位或某个信号。尤其是数据流控制部分,多花时间看tready的行为、看credit变化的节奏,比盲目改代码可靠得多。这个经验,在我后续做多设备互联时节省了非常多的时间。

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

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

立即咨询