简介:FPGA SRIO例程是一份面向FPGA开发者的Serial RapidIO接口设计与回环验证资源,适用于学习高速串行通信协议、Verilog HDL编程以及FPGA工程调试的工程师。资源围绕SRIO回环传输机制展开,可帮助理解发送接收链路、CRC校验、错误处理及仿真测试流程。压缩包共452个文件,约80.63MB,主要包含Verilog/VHDL源码、Xilinx工程文件(xpr/xci/dcp)、约束文件(xdc)、仿真与测试脚本(do/tcl/bat)、逻辑分析仪配置(wcfg/ltx)以及bit文件和文档说明,覆盖从代码编写、综合实现到板级调试的完整流程。已有3075人学习下载。通过该例程,读者可获得可运行的FPGA SRIO工程模板、回环测试实现思路、仿真脚本与调试方法,适合作为自主设计SRIO接口或项目开发前的参考起点。
1. 为什么SRIO例程这么难跑通:先看清它到底在干什么
做FPGA开发这几年,我见过太多同行在SRIO上卡住。倒不是这个协议有多高深,而是它的调试链路太长:从IP核配置、参考时钟、链路训练,到事务格式、缓冲管理,任何一个环节出问题,表现都是"链路起不来"或者"数据对不上"。你手里拿到一个"fpga srio例程",如果只是打开工程点一下Run Simulation,大概率什么都看不明白。
先把这个协议的本质说清楚。Serial RapidIO(SRIO)是一种高性能、低引脚数的包交换互连协议,主要用在嵌入式系统里做板间或者芯片间通信。它跟PCIe、以太网最大的区别在于:SRIO是为嵌入式场景设计的,强调低延迟、低开销、确定性的传输行为。打个比方,PCIe像城市间的高速公路,带宽大、规则复杂;SRIO更像园区里的专用物流通道,专门为固定路线、固定货物设计,跑起来简单直接。
一个完整的SRIO例程,通常包含三部分:SRIO IP核、用户业务逻辑、测试激励或调试观测逻辑。IP核负责物理层、传输层和逻辑层的协议处理,用户逻辑负责构造事务、收发数据。很多初学者拿到例程后,一上来就盯着HELLO包格式和LSU寄存器,结果连基本的数据通路都没摸清。我建议你先不要管那些细节,先把以下三件事搞明白:
- 这个例程用的是IP核的哪个版本,是SRIO Gen2还是Gen3,线速率和通道数配的是多少
- 用户逻辑和IP核之间走的是哪几类接口,是AXI4-Stream还是专用的srio接口
- 例程默认是自发自收(回环模式),还是真的面向两个FPGA芯片通信
搞清楚这三件事,你才算真正"看懂"了一个例程。接下来我以Xilinx SRIO IP核为例,按照从配置到上板验证的完整路径,把每个关键节点的决策逻辑和实操方法讲一遍。
2. 配置IP核时的几个关键决策点:线速率、通道数和参考时钟
SRIO IP核的配置界面看起来复杂,但真正需要你拍板的核心参数其实就那么几个。配置错了后面全盘皆输,所以我把每个参数背后的取舍逻辑都讲清楚。
2.1 线速率和通道数:先从链路带宽需求倒推
线速率(Line Rate)直接影响物理层的PCS/PMA配置,常见档位从1.25Gbps到6.25Gbps不等。SRIO Gen2协议支持1.25、2.5、3.125、5.0、6.25Gbps几种速率。通道数(Lane Width)则支持1x、2x、4x三种模式。带宽需求决定了速率和通道数的组合方式:如果你的业务带宽是2Gbps,可以选择2.5Gbps的4x,也可以选择6.25Gbps的1x——但后者对PCB走线质量要求更高,前者更稳妥。
我的建议是:第一版设计尽量选保守配置。速率选2.5Gbps或3.125Gbps,通道选4x或2x。原因有两个。第一,低速率的眼图裕量更大,链路训练成功率更高,排错时更容易定位是协议问题还是物理层问题。第二,多数SRIO IP核在低速档位下的参考时钟选择更灵活,不容易出现PLL锁定失败的问题。等你把数据通路跑通了,再逐步提高速率也不迟。
2.2 参考时钟:最容易被忽视的"隐形杀手"
SRIO IP核对参考时钟极其敏感。它需要两类时钟:一类是GT参考时钟(gt_refclk),用于驱动高速收发器内部的PLL;另一类是AXI接口时钟(axi_aclk),用于逻辑侧事务处理。很多例程跑不通,问题就出在GT参考时钟上。
GT参考时钟的频率由线速率和IP核配置共同决定。要么选线速率除以某个整数,要么直接用协议规定的标准值。举例来说,线速率是5.0Gbps、PCS位宽是32bit时,GT参考时钟通常配125MHz或156.25MHz。具体选哪个,要看你用的FPGA型号和IP核版本——有的器件要求156.25MHz,有的则两种都可以。请务必去查对应IP核的手册里的"Reference Clock Requirements"部分,不要凭感觉配。
此外,参考时钟的质量直接决定链路能否稳定训练。时钟抖动过大、压摆率不足、甚至PCB走线过长,都会导致phy_lock一直拉不起来。用频谱仪或示波器测一下参考时钟的抖动指标,这个工作值得做。
2.3 Device ID和地址映射:SRIO世界里的门牌号
SRIO是基于Device ID寻址的,每个端点的Device ID就像门牌号。发送端在构造包时,会填写目标Device ID;接收端收到包后,会判断这个ID是不是自己的,决定接受还是丢弃。IP核配置界面里会让你填"Device ID Width"和"Device ID",这个必须和通信对端约定一致。
地址映射则是另一个容易踩坑的地方。SRIO支持内存映射访问,你可以通过对端的内存地址发起NREAD/NWRITE事务。但FPGA逻辑侧访问的地址空间和IP核内部的地址窗口之间,存在一套映射规则。使用例程时要注意:默认情况下,IP核把接收到的NREAD/NWRITE请求映射到某个地址范围,如果你改动过IP核的地址窗口配置,但用户逻辑里的地址偏移没跟着动,数据就会写到错误的地方,这是典型的问题场景。
3. 例程工程结构的拆解思路:Xilinx SRIO Example Design到底怎么用
3.1 先学会"抄作业":Generate Example Design
Xilinx的SRIO IP核自带Example Design,这是官方提供的参考工程,包含了完整的初始化、链路训练、回环测试逻辑。在Vivado里生成IP后,右键选"Open IP Example Design"就能得到一个完整的工程。这是我强烈建议的第一步——不要着急自己从零搭工程,先把这个官方例程跑通。
打开这个例程,你会看到几个关键文件:
srio_gen2_0_example_top.v:顶层模块,例化了IP核和用户逻辑srio_gen2_0_init_srio.v:负责SRIO链路的初始化和链路训练状态机srio_gen2_0_example_user_logic.v:用户逻辑,包含了LSU读写、门铃事务等示例srio_gen2_0_phy_loopback.v:物理层回环测试模块
最核心的是init_srio模块。它通过维护事务(Maintenance事务)往IP核的寄存器写配置,完成链路初始化。链路训练的关键信号是phy_lock和link_initialized,前者表示物理层PLL锁定和通道对齐完成,后者表示链路层握手成功。跑仿真时,你先看这两个信号有没有拉高。
3.2 用户逻辑侧需要关心的几个关键接口
SRIO IP核对用户暴露的接口主要有几类:
lsu_*接口:LSU(Load/Store Unit)用于发起NWRITE、SWRITE、NREAD等事务。你需要往LSU寄存器里写目标Device ID、地址和长度,然后触发发送。每个LSU同时只能处理一个事务,多个事务需要排队或者用多个LSU(典型IP核提供4到8个LSU)。
nread_*和nwrite_*接口:这些是IP核收到的对端发来的读写请求。比如对端发一个NWRITE请求,数据就会出现在nwrite_*接口上,用户逻辑要负责把数据接走(写入BRAM或FIFO)。
doorbell_*接口:门铃事务,本质上是一个16bit的消息中断,不携带数据负载,常用来做控制信令或者通知对端"数据已经准备好"。这是调试时最好用的信号,因为你可以通过它快速确认链路是否通了。
maintenance_*接口:维护事务,用来访问端点自身的配置空间,也能访问对端的维护寄存器。
例程里的example_user_logic把这几个接口都演示了一遍。你读懂它之后,后续开发就是替换它内部的数据处理逻辑,比如把NWRITE收到的数据从FIFO改成DDR写控制,或者把LSU发送的数据源从固定数改成业务数据。
4. 从例程到项目:修改回环设计,构建真正的双端通信链路
官方例程默认跑的是物理层回环或内部回环,数据发出去又从自己这里收回来。这在验证IP核配置是否正确时很有用,但它模拟不了真实的应用场景。真正做项目时,你需要的是两个FPGA(或者FPGA和DSP、交换芯片)之间通信。所以下一步,是把回环模式改掉,构建一个真正的一端发、一端收的链路。
4.1 回环模式的类型与关闭方法
SRIO IP核支持三种回环:物理层回环(PHY Loopback)、链路层回环(Link Loopback)、远端回环。官方例程通过一个参数选择回环类型,通常也支持从外部引脚控制。做双端通信时,必须把回环关闭,让数据从GT引脚真实地发出去。
如果你用的是开发板自带的例程,设备上可能有一个回环开关或者拨码开关,但多数情况下你需要改代码:把顶层模块中控制回环模式的信号固定为不回环。然后观察phy_loopback_mode或类似的信号,确保它被置成无回环模式。
这个改动的意义在于:回环模式下,GT收发器的TX和RX在FPGA内部短接,很多物理层问题被掩盖了。关闭回环后,信号要经过PCB走线、连接器、线缆到达对端,再返回——参考时钟抖动、信号完整性、电平标准等问题都会暴露出来。不要跳过这一步,直接在上板阶段去验证双端通信。
4.2 双方约定参数:不止是速率和通道数
两个FPGA要通信,除了速率和通道数一致,还有几项参数必须提前对齐:
- Device ID宽度和具体值,比如端A用0x00,端B用0x01
- 地址映射规则,比如端A想读端B的某段空间,地址如何翻译
- 事务类型,是NWRITE还是SWRITE,SWRITE的地址是递增8字节对齐的,NWRITE则支持更多字节数
- 流控和重传机制,SRIO协议本身有重传机制,但你要确认IP核的流控配置和处理策略是否匹配
双方参数不一致时,最常见的表现是:链路层训练成功(link_initialized拉高),但一发起事务就超时或者收到错误响应。因为你发过去的包,对端可能认为Device ID不匹配直接丢弃了,或者地址越界返回错误。
4.3 逻辑设计的改法:用状态机替代例程里的固定序列
官方例程的LSU发送逻辑是固定死的,跑一次就结束。实际项目中,你需要自己写一个状态机,控制LSU什么时候发、发什么、收到响应后下一步做什么。我常用的做法是:
- 业务数据先写入DDR或者BRAM
- 状态机判断数据量达到某个阈值后,写LSU寄存器触发一次SWRITE发送
- 发送完成后产生一个完成中断,状态机进入下一次触发等待
- 对端通过门铃通知"数据已收到"或者通过轮询状态寄存器确认
这个过程中,你可以借助IP核提供的中断信号irq来减少轮询开销。但要注意,LSU完成中断和门铃中断对应的中断源不同,需要在IP核的中断寄存器里分别使能。例程里通常没有把这些中断全部打开,你需要对照寄存器手册逐个使能。
5. 仿真和上板时怎么确认链路是健康的:几个必看的信号和排查手段
很多初学者跑完仿真,看到波形就以为例程通了。这个"通"要打一个问号——仿真通过只能说明逻辑功能正确,物理层的信号质量和时序对齐是仿真正不了的。所以我把仿真和上板两阶段的验证要点分开讲。
5.1 仿真阶段:看关键标志信号的时序
仿真时,建议重点关注以下几种信号。
gt_rxrecclk或者gt_txoutclk:这两个时钟要稳定输出。如果收发器PLL没锁定,这两个时钟就不稳定。
phy_lock:物理层锁定信号。它拉高说明GT收发器的CDR已经锁定,通道字节对齐完成。如果在仿真里这个信号一直在抖(反复拉高拉低),优先怀疑线速率和参考时钟的比例关系是否与IP核配置匹配。
link_initialized:链路层训练完成信号。它拉高说明SRIO链路层的握手协议(训练控制符号交互)已经完成。
fft_irq或irq:IP核的中断输出。链路错误、格式错误、缓冲溢出都会触发中断。仿真时看到这个信号拉高,要停下来查看是哪种错误。
用自定义的计数器回环是最直接的功能验证方式。比如在用户逻辑里写一个发送计数器,每次事务加1,对端收到后回传,本端再校验。如果数据全对,说明从物理层到逻辑层的整条通路都没问题。官方例程里通常用data_gen模块生成一个递增序列,用来做这种完整性校验——你可以直接沿用。
5.2 上板阶段:ILA抓到比仿真正十倍的真相
上板之后,物理层的问题就藏不住了。教大家一套我自己常用的排查链路,按这个顺序走,定位问题效率会高得多。
第一步,查电源和时钟。用示波器量一下GT收发器的供电电压,确认电压跌落不超过额定的5%。再量GT参考时钟的频率和抖动。这一步能排除80%的物理层问题。
第二步,查复位信号。SRIO IP核的复位逻辑比较复杂,有全局复位、GT复位、链路复位等多级复位。确认复位时序符合IP核手册的要求——特别注意,gt_pll_lock没有拉高之前,不能释放GT复位。
第三步,查链路训练状态。上板后先用ILA或者Vivado的Hardware Manager抓取phy_lock和link_initialized信号。如果phy_lock拉不起来,检查GT参考时钟、线速率配置和PCB走线;如果phy_lock稳定但link_initialized不拉高,检查对端的SRIO链路配置,特别是速率是不是相同、宽度是不是匹配。
第四步,查数据通路。链路训练成功后,发起一次简单的NWRITE事务,在对端用ILA抓nwrite_*接口,确认数据真的到了用户逻辑。再到对端发起一次NREAD,确认读数据能正确返回。两次都通过,你的SRIO链路就算真正打通了。
注意:ILA的采样时钟最好用IP核输出的
axi_aclk,不要用系统时钟,因为SRIO的事务时序都是跟axi_aclk对齐的,用错时钟可能导致采样到的信号毛刺或错位。
6. 实际项目里最常踩的坑和根因定位过程
6.1 坑一:GT参考时钟配置与实际时钟频率不匹配
现象是phy_lock一直在抖动,或者干脆拉不起来。排查后你会发现,IP核里选的参考时钟频率和开发板上的实际晶振频率不一致。
我之前用过一块板子,标称GT参考时钟是125MHz,实际焊的是100MHz晶体。IP核配置界面里选了125MHz,结果PLL锁不住,链路怎么都训练不起来。后来示波器一量才发现问题。所以拿到板子的第一件事,就是核对原理图上的GT参考时钟晶振频率,把它和IP核配置一字不差地对上。
6.2 坑二:两台设备的速率和通道数不匹配导致训练失败
SRIO链路训练是双方协商的过程,一方发起训练序列,另一方回应。如果两端速率不同,训练永远完成不了。
这个坑的特点很隐蔽:因为每端的IP核配置都是合法的,仿真都正常,但连起来就是不通。排查办法是,用维护事务读取对端的端点信息寄存器,查它的速率能力位,和本端的配置做对比。此外,很多SRIO IP核支持速率协商,可以自动降速到双方都支持的速率,但前提是你在IP核配置里开启了这一功能。
6.3 坑三:数据格式错误——NREAD和NWRITE的size字段没对齐
SRIO的事务包格式中,size字段表示数据负载的字节数。FPGA侧的AXI接口位宽通常是64bit或者128bit,如果你发起NREAD请求时size字段和AXI接口位宽对不上,对端返回的数据可能会错位或者被截断。
具体来说,AXI接口位宽64bit时,一个NREAD请求的size应该是8字节的倍数;如果你写的是4字节,IP核会通过内部逻辑把数据拼接,但有些旧版本IP核处理这个场景有bug,表现是读回的4字节数据落在了错误的字节通道上。解决方法是,统一事务size和AXI接口位宽的关系,不要出现混搭。
6.4 坑四:缓冲溢出导致的事务重传风暴
SRIO接收端有缓冲区管理机制。如果对端发送数据的速度超过了本端FIFO的消费速度,缓冲区就会满,IP核会发送重传请求,发送端缓存所有未确认的数据。如果这种情况持续发生,会形成重传风暴,链路吞吐量急剧下降。
排查方法:抓取IP核的错误寄存器,看有没有buf_overflow或retry_stopped这类标志。解决思路通常是两种:增加接收侧FIFO的深度,或者优化用户逻辑消费数据的速率,比如从CPU轮询改成DMA突发传输。
7. 最后再分享一点个人经验
SRIO例程这东西,看起来只是一个参考工程,但你真的把它吃透了,等于把协议栈的物理层、传输层、逻辑层都过了一遍。我自己带过几个新人,发现一个规律:凡是老老实实把官方例程从仿真到上板跑通、再自己动手做一遍双端通信的人,后面写SRIO业务逻辑基本不费劲;凡是急着上手改业务代码、跳过了链路验证的人,最后都要回头补课。
如果你现在刚开始接触,我给你三个小建议。第一,不要自己从头搭工程,先把官方Example Design跑通,这是性价比最高的学习路径。第二,改代码时一次只改一处,比如先把用户逻辑的发送数据源改掉,验证通过后再动IP核参数,不要同时改多个地方,否则出了问题根本没法定位。第三,把日志和信号观测做扎实,ILA探针和上板脚本这些工具,前期花时间搭好,后期排查问题能省掉大量重复劳动。
另外提一句,不同厂商(Xilinx、Intel、Microchip)的SRIO IP核接口差异很大,连事务类型和寄存器定义都完全不同。你在Xilinx例程里学会的知识,换到另一家平台时要重新看手册,但协议层的基本概念是相通的。学的是自己的理解,而不是某个厂商的接口。
本文还有配套的精品资源,点击获取