资讯动态

FPGA SRIO例程详解:从Verilog高速接口到DMA传输实践

发布时间:2026/10/19 5:24:08 来源:尧图企业网站定制
简介面向数字逻辑与高速串行接口设计者的FPGA SRIO回环通信例程演示如何用Verilog在FPGA中实现Serial RapidIO协议并通过回环方式将发送数据经SRIO接口返回自身校验收发一致性以验证链路硬件和协议层是否正常。资源共452个文件、约80.63MB主要包含Vivado工程与IP核配置xpr/xci、Verilog/VHDL源码、仿真批处理及Tcl脚本、约束文件与bit下载文件等覆盖从综合、仿真到上板调试的完整流程。目前已有3097人学习下载。借助该例程可理解SRIO帧结构、流量控制、CRC校验、时钟管理与数据同步等关键机制掌握回环测试路径的搭建方法与调试技巧工程内的脚本和文档也能辅助快速复现实验。对于需要入门高速串行接口或验证链路可靠性的开发者这份例程提供了可直接运行的工程结构和清晰的验证思路适合进一步扩展为自定义SRIO应用。1. 这个FPGA SRIO例程到底在解决什么问题某开发者向导师接了一个高速数据处理板卡FPGA 要把 2GB/s 的实时采样数据送给后级 DSP试了几种总线方案都卡在 PCB 走线带宽上。最后在某个开源仓库里找到了这套 Verilog SRIO 例程从串行 RapidIO 协议栈到用户接口逻辑一次拢齐。链路跑起来后一对差分线最高能拉到 10Gbps工程里的代码直接把协议细节封装成了可例化的模块适合拿来即用的场景。如果你正想把 FPGA 挂到处理器的高速互联网络上或是在做基带数据处理、高速采集、板间光纤传输这个例程能让你少走不少弯路。它不只是一个 IP 核配置记录还包含完整的读写状态机、门铃中断、仿真平台和调试指引。哪怕你第一次听到 SRIO 这个词照着也能把链路打通。2. 架构设计拆解SRIO 例程的工程结构与时序链路2.1 SRIO 协议栈在 FPGA 中的位置Serial RapidIO 是一种面向嵌入式系统的分组交换互连协议分为逻辑层、传输层和物理层。逻辑层定义读写操作、消息传递和门铃传输层负责路由和事务标识物理层处理串行链路的字节编解码、链路训练和错误重传。FPGA 设计里这三层大多由厂商的 IP 核承担用户真正要面对的只是 IP 核抛出来的用户事务接口。这套例程走的是端点模式也就是你把 FPGA 当作 SRIO 网络中的一个终端节点另一个节点通常是一块 DSP、ARM 或者另一片 FPGA。用户逻辑只需要往 IP 核的请求接口上发起 NREAD、NWRITE、DOORBELL 等事务IP 核底层会自动把请求包装成 SRIO 包经过高速收发器SerDes发送到远端。协议里的维护事务、错误恢复都被 IP 核消化掉了用户所能看到的黑匣子就是一组握手信号。链路层跑没跑起来一般看 IP 核给出的链路状态寄存器比如 port OK、链路速率、链路宽度。例程在顶层把那些状态位拉了出来方便在线调试时直接观察。这个设计思路很好排查问题不用去翻 IP 核内部波形。2.2 例程目录结构与关键文件拿到工程后可以先按目录结构快速建立一个全局认识。例程采用的是常见 FPGA 工程组织方式代码和约束文件分层放好。目录清单如下目录路径作用src/rtl/顶层模块、SRIO 用户逻辑、异步 FIFO 和数据校验模块src/ip/SRIO IP 核的配置工程文件在开发工具中可直接打开src/sim/Testbench 仿真环境包含发送节点和接收节点模型constraints/引脚约束、XDC/SDC 时序约束文件docs/说明文档记录链路速率、内存映射和寄存器定义顶层模块名一般是 srio_top.v内部例化了厂商 SRIO IP 核和用户状态机。用户状态机是例程的灵魂它实现了 NREAD 和 NWRITE 的请求应答时序。testbench 里搭了一个“虚拟远端”用来模拟对端节点的维护响应和数据回包这让没有硬件的人也能先把功能验证跑通。约束文件里除了差分时钟和收发引脚绑定还写了对 SerDes 参考时钟的时序约束。这一步在例程里做得很完整如果你想移植到另一块板卡最需要改的也是这两个文件。2.3 数据通路寄存器模式和 DMA 模式的取舍例程提供两条数据通路一条是低速控制通路用于主机通过 SRIO 读写 FPGA 内部的配置寄存器另一条是高速数据通路用轻量级 DMA 把连续数据块搬进 SRIO 的发送接口。控制通路走 NREAD/NWRITE事务长度短但主机侧访问简单数据通路则是一连串 NWRITE 突发减少握手开销。工程默认把两条通路都接了出来。寄存器模式通常映射到一个地址窗口主机发起一个 32 位读请求FPGA 把对应地址的数据指向返回。DMA 模式则更贴近真实业务主机在 FPGA 内部描述符表里填入源地址、目的地址和长度FPGA 上的 DMA 状态机自动发起大批量写事务。如果只需要基本通信用寄存器模式足够了如果后面做图像传输建议直接绕到 DMA 通路上。为什么这样做隔离因为 SRIO 的事务层对长突发支持相对有限如果每个 256 字节的数据块都靠 CPU 逐个发起协议开销会吃掉大量带宽。例程把 DMA 独立成模块就是为了让实际项目能直接沿用这种设计思路。3. 核心模块拆解Verilog 代码逐段讲解3.1 SRIO IP 核的例化模板工程顶层把 IP 核的引脚和用户逻辑拆开了。底层 IP 核负责物理层用户只需要关心请求接口和响应接口。一个典型例化模板如下module srio_top #( parameter C_SRIO_LANES 1, // 链路宽度: 1x/2x/4x parameter C_LINK_RATE 5.0, // 每通道线速率单位 Gbps parameter C_REFCLK_FREQ 125.0, // 参考时钟频率单位 MHz parameter C_DEVICE_ID 16h0001 // 本端设备 ID ) ( input wire sys_clk_p, // 系统时钟差分脚 input wire sys_clk_n, input wire ref_clk_p, // SerDes 参考时钟差分脚 input wire ref_clk_n, input wire reset_n, output wire srio_txn, // SRIO TX 差分对 output wire srio_txp, input wire srio_rxn, // SRIO RX 差分对 input wire srio_rxp, // 用户请求接口 output wire [63:0] ireq_addr, output wire [63:0] ireq_data, output wire [7:0] ireq_be, output wire [3:0] ireq_type, input wire ireq_ready, output wire ireq_valid, // 用户响应接口 input wire [63:0] iresp_data, input wire iresp_valid, output wire iresp_ready, output wire port_ok, // 链路训练完成 output wire [3:0] port_speed // 实际链路速率指示 ); wire user_clk; wire reset_n_sync; sync_reset #( .RESET_DELAY_CYCLES(64) ) u_reset_sync ( .clk (user_clk), .async_rst(~reset_n), .sync_rst (reset_n_sync) ); srio_ip u_srio_ip ( .sys_clk_p (sys_clk_p), .sys_clk_n (sys_clk_n), .ref_clk_p (ref_clk_p), .ref_clk_n (ref_clk_n), .sys_rst_n (reset_n_sync), .srio_rxn (srio_rxn), .srio_rxp (srio_rxp), .srio_txn (srio_txn), .srio_txp (srio_txp), .ireq_addr (ireq_addr[38:0]), .ireq_data (ireq_data[63:0]), .ireq_be (ireq_be), .ireq_type (ireq_type), .ireq_ready (ireq_ready), .ireq_valid (ireq_valid), .iresp_data (iresp_data[63:0]), .iresp_valid (iresp_valid), .iresp_ready (iresp_ready), .port_ok (port_ok), .port_speed (port_speed) ); endmodule这段代码把端口分成了四组系统时钟、参考时钟、差分数据线、请求/响应接口。ireq_type 的定义要对照协议NREAD 是 2 位的某个编码NWRITE 是另一个编码具体值在 IP 核手册里有对照表。ireq_be 是字节使能写数据时用于标识哪些字节有效高电平表示对应字节参与这次写操作。参考时钟的频率在这里是个关键参数。链路速率一旦选成 5.0 Gbps参考时钟通常需要跑 125 MHz。选错了IP 核内部 PLL 锁定不住链路训练永远无法完成现象就是 port_ok 一直拉低。所以例程把 ref_clk 和 link rate 集中成参数改板子时不容易漏。3.2 请求发起状态机NREAD 和 NWRITE 时序例程里的用户状态机是整个逻辑层的核心它把读请求、写请求从等待循环里解耦出来。代码段简化如下localparam [2:0] IDLE 3d0; localparam [2:0] WAIT_REQ 3d1; localparam [2:0] WAIT_RESP 3d2; localparam [2:0] SEND_WRITE 3d3; localparam [2:0] COMPLETE 3d4; reg [2:0] state; reg [63:0] read_data; reg busy; always (posedge user_clk or negedge reset_n) begin if (!reset_n) begin state IDLE; busy 1b0; end else begin case (state) IDLE: if (cmd_valid) begin busy 1b1; if (cmd_is_read) state WAIT_REQ; else state SEND_WRITE; end WAIT_REQ: begin // 发起 NREAD 请求 ireq_valid 1b1; ireq_type 4b0001; // NREAD 编码示例 ireq_addr cmd_addr; if (ireq_ready ireq_valid) begin ireq_valid 1b0; state WAIT_RESP; end end WAIT_RESP: begin if (iresp_valid) begin read_data iresp_data; state COMPLETE; end end SEND_WRITE: begin // 连续发送 NWRITE 数据 ireq_valid 1b1; ireq_type 4b0010; // NWRITE 编码示例 ireq_addr cmd_addr; ireq_data cmd_data; if (ireq_ready) begin ireq_valid 1b0; state COMPLETE; end end COMPLETE: begin busy 1b0; if (!cmd_valid) state IDLE; end endcase end end看这段代码要注意握手顺序ireq_valid 和 ireq_ready 同时拉高才算事务真正被发出。有些新手只看 valid 就以为已经传入 IP 核结果下一个状态提前跳走等到对端没有响应才发觉请求根本没进发送队列。SRIO IP 核的请求接口大多使用这种 valid/ready 机制当 valid 拉高且 ready 拉高时当前事务在同一拍被锁存之后必须撤销 valid否则 IP 核会当成一次新请求。NREAD 是请求响应模式发起后要等 iresp_valid 回来读到数据才进完成状态。NWRITE 则属于 posted 事务不需要响应例程里会把 COMPLETE 状态直接跳回 IDLE。另外一种 NWRITE_R 需要响应适合需要确认写入成功的场景例程没有把它做进状态机留给了二次开发。3.3 门铃与本地中断处理逻辑门铃Doorbell在 SRIO 里是轻量级事务用于发送中断信号不带数据负载。主机想通知 FPGA 某个缓冲区已就绪会往 FPGA 设备发一个门铃事务。例程在接收侧单独拉出了一个模块处理门铃代码如下reg irq_pulse; reg [31:0] doorbell_info; always (posedge user_clk or negedge reset_n) begin if (!reset_n) begin irq_pulse 1b0; doorbell_info 32d0; end else begin if (iresp_valid iresp_type DOORBELL) begin doorbell_info iresp_data[31:0]; irq_pulse 1b1; // 本地中断脉冲 end else begin irq_pulse 1b0; end end end这里 DOORBELL 是一个局部定义的编码常量实际值要和 IP 核手册一致。网上常见的一个失误是直接把 NREAD 的响应当成门铃包处理。NREAD 的远端点可能返回带数据负载的响应也可能返回门铃格式的事件两者在类型字段上不同。这个例程把门铃单独检测就是为了避免业务数据和中断信息混在一起。门铃数据里通常还携带目标信箱 ID。例程的简化版只抓了低 32 位真做多核通信时还要根据信箱 ID 决定把中断送往哪个 CPU 核的映射寄存器。3.4 跨时钟域和复位同步处理SRIO IP 核的用户接口时钟和系统时钟往往不是同一个域。例程在所有跨时钟路径上补了同步逻辑其中复位同步最简单module sync_reset #( parameter RESET_DELAY_CYCLES 64 ) ( input wire clk, input wire async_rst, output wire sync_rst ); reg [($clog2(RESET_DELAY_CYCLES))-1:0] rst_counter; reg sync_rst_reg; always (posedge clk) begin if (async_rst) rst_counter b0; else if (rst_counter ! RESET_DELAY_CYCLES - 1) rst_counter rst_counter 1; sync_rst_reg (rst_counter RESET_DELAY_CYCLES - 1); end assign sync_rst !sync_rst_reg; endmodule在 FPGA 工程里直接把外部复位信号接到 SRIO IP 核上是个高频“踩坑”点。外部复位通常是按键信号上升沿不干净和时钟不同步IP 核内部状态机会误判复位时序。同步器先把复位信号拉长几十拍再输出稳定复位链路训练成功率明显提高。例程里的这些细节都值得保留。4. 从搭建到跑通仿真验证与上板调试步骤4.1 创建工程和配置 SRIO IP 核拿到例程后先在自己的开发环境里新建工程把 src/ip 下的 IP 核配置文件加进来。配置 SRIO IP 时重点看六个参数协议模式选择“Serial RapidIO”而非“RapidIO”同族其他模式设备类型Endpoint链路速率5.0 Gbps 是比较稳健的起步值链路宽度1x 或 2x首次验证建议用 1x参考时钟务必与板卡上实际接的晶振频率一致设备 ID本端 ID 和远端 ID 要区分开例程的 docs 文档里已经把推荐的参数组合写清楚了照着选就行。不匹配会导致链路训练失败或者传输错误。配置完成之后先跑一遍语法检查把例程里的 rtl 文件全部加入工程再综合一次确认无残留错误。4.2 Testbench 里的环回验证例程仿真环境搭了一个双节点模型一个 master 节点和一个 slave 节点。它们的 SRIO 收发端口在仿真里直接对接用线网相连模拟差分链路。testbench 首先按顺序发起维护读请求读到链路状态寄存器确认链路 OK然后发起 NREAD 和 NWRITE 测试数据通路的正确性。initial begin reset_n 1b0; #1000 reset_n 1b1; #2000 // 发起一次 32 位读请求 cmd_valid 1b1; cmd_is_read 1b1; cmd_addr 64h0000_0000_0000_0100; #500 cmd_valid 1b0; // 等待响应 wait (busy 1b0); if (read_data 32hA5A5_1234) $display(READ OK: data %h, read_data); else $error(READ FAIL: data %h, read_data); // 写请求 cmd_valid 1b1; cmd_is_read 1b0; cmd_addr 64h0000_0000_0000_0200; cmd_data 64hCAFE_FOOD; #500 cmd_valid 1b0; end仿真里最容易忽略的是复位时序。例程的复位同步器需要几百拍才能释放复位testbench 里如果复位释放过早SRIO IP 核会一直在初始化循环里。通常 100 微秒后再发起事务比较稳妥如果仿真快速运行等待时间在 2000 纳秒左右也可以但要把复位同步器的时钟周期数算进去。跑完仿真后打开波形重点看三块链路状态寄存器是否变成 OK、门铃事件是否产生脉冲、响应数据是否与激励数据一致。这三张波形在手基本功能就算验证干净了。4.3 上板验证从链路训练到数据校验上板调试前先把逻辑分析仪探针插到例程预留的观测引脚上。例程把 port_ok、port_speed、门铃脉冲、写响应计数都引了出来作为 debug 信号。建议先观察链路训练结果再用最简单的主机读操作确认 SRIO 点对点通路。上板第一步是烧录工程检查电源和参考时钟灯是否正常。然后用主机侧工具发起维护读操作读取端口 0 的链路状态确认 port_ok 拉高。第二步发起一次 32 位写事务向 FPGA 内部测试寄存器写入固定特征值再读回来比对。如果读写一致说明请求响应队列工作正常如果读回全零或全一大概率是字节顺序或地址映射问题。高速链路可以用 loopback 模式做自测让 SRIO IP 核把发送数据环回到接收端。例程提供一个配置寄存器控制内部回环开启后不接外部线缆就能验证收发通路。回环测试通过之后再接入真实光模块或板间差分线此时只需要排查物理连接上的问题。5. 避坑指南SRIO 例程调试中常见的问题排查5.1 链路反复“重新训练”port_ok 死活不拉高现象上电后 port_ok 一直在抖动链路状态寄存器不断切回检查状态类型训练不成功。原因最常见的是参考时钟质量差。板卡上随手用了普通有源晶振抖动指标满足不了 5Gbps SerDes 的需求。另外参考时钟的引脚分配也可能没有走到专用时钟输入导致时钟偏斜。解决换用低相噪的差分振荡器频率严格匹配 IP 核配置。把 ref_clk_p/ref_clk_n 放到专用 GTP/GTY 参考时钟引脚上同时检查 PCB 等长约束。例程约束文件里专门把参考时钟引脚加了注释照着搬就不会踩到。5.2 读回数据全是 0xFF 或 0x00偶发正确现象主机发起 NREAD 读 FPGA 内部寄存器返回的数据统一是全 F 或者全 0但多试几次偶尔能读到正确值。原因这不是逻辑错误而是布局布线后数据通道的时序收敛出问题。SRIO 响应接口时钟域较复杂如果响应数据输入到用户 FIFO 时没有做跨时钟域采样亚稳态一拍就全 F。另一个常见原因是读写地址的高低位反了导致数据从错误地址读出。解决先在仿真里排除逻辑错误确认地址映射表没有错位。上板后打开时序报告检查所有跨时钟域路径的保持时间裕量必要的时候在响应数据路径上加两级触发器。这里的关键是不要急着换代码先看时序报告。5.3 门铃发出去了FPGA 侧中断不触发现象主机侧软件显示门铃已经发送成功FPGA 这边终端标志没有任何反应门铃脉冲信号从来没拉高。原因回看代码会发现门铃事件躲在响应数据通道里。其实门铃不是普通数据响应它是 IP 核的一类独立事务类型必须用 iresp_valid 配合特定的类型字段识别。例程里已经做了类型判断但如果 IP 核配置里关闭了门铃接收能力IP 核会在内部直接丢弃门铃包。解决检查 IP 核配置中“Transaction Type Enable”是否勾选了 Doorbell 接收。同时确认远端发送门铃时目标设备 ID 写的是本端的 ID而不是广播地址。这个坑在例程文档里专门备注了第一次上板最容易翻车。5.4 大数据块传输时 CRC 错误链路似乎“自动降速”现象传输小数据块没问题一旦开始搬运 2MB 以上的数据块就出现 CRC 错误链路经常重训练。原因这通常不是 SRIO 协议本身的问题而是用户逻辑侧背压做得不好。发送 FIFO 写满之后IP 核的缓冲队列不再接收数据底层链路开始重传。如果重传次数超过阈值链路质量下降甚至重新训练。解决给发送方向的 FIFO 留足深度至少能容纳两个最大突发包。同时检查例程的流控信号确保当 IP 核 ready 拉低时用户状态机立刻停止下一拍数据写入。这个坑通常看不见摸不着只能靠在线逻辑分析仪抓 ready 信号波形看到每包后半段的低电平就能定位。6. 二次开发把 SRIO 例程改造成高速 DMA 传输器6.1 改造思路从轮询式读写到描述符搬运例程的寄存器模式适合调试但做实际业务时主机不应该逐个去做读请求。一种更通用的做法是增加一个 DMA 寄存器组FPGA 内部维护描述符队列主机只配置源地址、目标地址和数据长度FPGA 自动搬运。你可以把例程里的 NWRITE 状态机扩展一下让它从 DMA 缓冲区连续搬数据不需要主机干预。具体改造时先固定一个帧长比如 256 字节。让 DMA 状态机在发送完一帧之后马上跳到下一帧直到剩余长度清零。在描述符末尾增加一个“传输完成”计数器主机通过 NREAD 读取这个计数器确认搬运进度。例程原本的门铃模块也可以复用最后一帧发送完时触发一个门铃通知远端整块传输结束。6.2 验证方法连续传输 64MB 数据比对改造完成后先跑行为级仿真生成随机测试向量模拟 DMA 描述符更新过程。上板后建议用主机连续发送 64MB 随机数据FPGA 侧回读比对。比对通过率在 99.999% 以上才算稳定。还可以利用例程的 FPGA 内部自检模式把同一份数据同时写入发送 FIFO 和对照组 RAM随后从远端读回的数据与对照 RAM 对比这能快速区分问题出在 SRIO 链路还是外部主机软件。吞吐率验证可以用计数器统计每秒发送完成的事务数量。以 5Gbps 链路宽度 1x 计算有效开销约三成最高毛带宽约 3.5Gbps。如果实测远低于这个值大概率是事务间留下了空泡可以检查 NWRITE 请求之间是否连续 framing。6.3 从这例程里带走的开发习惯我在看完这套例程后每次调试 SRIO 工程都会把仿真流程强制走一遍先在线看链路状态寄存器、门铃脉冲和读写数据波形再综合上板。SRIO 这类高速接口有太多玄学问题链路训练不成功时最容易浪费时间先确认参考时钟和复位释放再检查用户逻辑比什么都重要。从那以后我每回新建工程都会把例程的模拟节点保留下来作为回归测试的一部分所有二次改动只要仿真通过上板成功率就高不少。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑