ARTICLE DETAIL

资讯详情

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

FPGA UDP回环验证全攻略:从假通过到真实链路调试

FPGA UDP回环验证全攻略:从假通过到真实链路调试 简介一套面向FPGA开发者的UDP通信回环工程基于Quartus开发环境实现UDP报文收发、自环返回和在线验证适合网络通信学习、接口调试及协议栈正确性测试。资源包共297个文件压缩后约23.93MB包含Verilog/VHDL与AHDL硬件描述语言文件、Quartus工程配置文件、C辅助源码、可下载的sof配置比特流以及PDF设计文档其中v、vhd、tdf文件构成收发与回环核心逻辑qpf、qsf、qdb等文件用于工程管理、综合布局和引脚约束cpp、h文件实现上位机或测试辅助功能说明文档则梳理了设计思路与使用步骤。目前已有722人学习下载工程展示了UDP报文头组装、MAC层封装、校验和计算、接收解封装以及数据回环重发等关键流程并附带UDP摄像头数据接收示例便于对照源码理解FPGA与以太网交互的完整链路同时提供完整的目录结构和测试脚本可帮助读者快速掌握FPGA实现UDP通信的方法。与纯软件模拟不同FPGA实现还需关注时钟同步、位宽匹配和MAC接口时序本工程在这些方面均给出可参考设计。1. 为什么说 UDP 回环是 FPGA 通信链路里最容易“假通过”的一环把 FPGA 的 UDP 收发做到回环是绝大多数人验证以太网链路的第一步但这步恰恰最容易踩空数据确实回来了但回来的可能不是“你发给它的那一段”而是硬件自发自收、MAC 核没复位好、或者 VIP 测试激励里同一份数据被读了两遍。标题里的 v4 指的是 IPv4不是某个软件版本整条链路要处理的是 IPv4 报文和 UDP 数据报。回环测的不是“有没有通”而是“收进来和发出去的路径是否是同一份数据”。适合谁做刚把三速 MAC IP 核调到不报错的 FPGA 入门者以及要在自测板上把 UDP 通道打通再交给软件同事联调的工程师。下面这套方案是我在 Artix-7 上用双 FIFO 加一个回环状态机做完的逻辑开销很小但能挡住多数“看起来通了”的坑。2. 先把回环模式定下来L2 回帧、L3 回包还是 L4 回数据2.1 在协议栈的哪一层做回环决定了你要写多少逻辑UDP 回环不等于“原样把以太网帧丢回去”。原样丢回的是 L2 回帧MAC 地址不用改CRC 也不用重算MAC IP 核会自动处理逻辑最少但验证价值也最低它只能证明 PHY 和 MAC 收发通路是活的证明不了你写的 UDP 解析和封装逻辑。真正要验证的是 L4 回数据——把 UDP 载荷解出来、缓存、再重新封装成 IPv4/UDP 报文发出去MAC 地址、IP 地址、端口、校验和全都要重填。回环层级做法逻辑开销能验证什么适合场景L2 回帧收帧直接转发出 MAC几乎为零PHY/MAC 通路、时钟恢复首次上电点亮链路L3 回 IP 包解析 IP 头后整包转发低IP 头解析、长度字段处理验证 IP 层重组L4 回 UDP 数据提取载荷、重封装、回发中UDP 头、端口过滤、校验和上位机联调、业务前自测我一般建议直接做到 L4因为回环测试的目的本来就是给后续业务逻辑铺路。L2 回帧虽然能在一个小时内跑通但等软件同事拿 UDP 工具连上来发现不通排查成本反而更高。标题里的“udp收发”两个方向都必须覆盖接收方向要做端口与地址过滤发送方向要把缓存的数据按序取出来重新组包。2.2 FPGA UDP 协议栈选型MAC IP 核加开源栈是多数人的做法FPGA 厂商没有免费的完整 UDP/IP 协议栈 IP 核可以用Vivado 里的三速以太网 MAC 只解决到 MAC 层IPv4 和 UDP 的解析需要自己写或者集成开源 Verilog 以太网 IP 核。常见做法是 MAC 用厂商 IPUDP 协议栈用开源方案GitHub 上引用较多的 verilog-ethernet 项目提供了完整的 AXI-Stream 接口 UDP 栈包含 IPv4、ARP、ICMP 回显接收和发送路径分离回环设计只需要把它的 rx_udp_payload 接到 tx_udp_payload。不推荐自己从头写完整的 IPv4 分片重组多数 UDP 业务包不会超过 MTU 1500 字节回环测试更不需要分片。需要把握的是 UDP 数据报格式8 字节 UDP 头源端口、目的端口、长度、校验和加上载荷IPv4 头 20 字节。回环里真正要动的是 IPv4 头的源/目的 IP 互换、UDP 头的源/目的端口互换以及重新计算首部校验和。这些字段在代码里都是固定偏移量用状态机按字节计数处理最直接。2.3 地址过滤怎么设目标 MAC、目标 IP 与 UDP 端口是一组“与”关系回环收包前必须先做过滤否则板卡会把广播帧、ARP 请求以及发给别的主机的报文都收进来。过滤条件是 AND 关系目的 MAC 等于本机 MAC目的 IP 等于本机 IP目的 UDP 端口等于本机端口三者同时命中才认为这是送往本 UDP 应用的报文。端口过滤的坑在字节序。UDP 头里端口是网络字节序大端而 FPGA 代码里经常直接用 parameter 定义一个 16 位常量比如 16‘d5001 表示十进制 5001在 wire 上和报文里的字节排列正好相反。没有经验的工程师常在这里调到怀疑人生。一个可靠的写法是定义端口常量时写成 16’h1389 这种十六进制并在注释里标注端口号或者用一个交换字节序的宏wire [15:0] dst_port_swapped {rx_data[7:0], rx_data[15:8]}; if (dst_port_swapped 16‘d5001) // 注意这里赋的是主机序的十进制度上面的写法能避免字节序混乱。源 IP 不需要过滤回环回发时只用保存这个 IP 值。建议把 MAC 地址、IP 地址、端口号都放在模块顶部的 parameter 里并且只定义一套“本机参数”后续跨板联调时只需要改这四个常量。3. Verilog 实现一个最小 UDP 回环状态机接收解析、缓存载荷、重新封装3.1 回环状态机的基本骨架IDLE、解析、转发整个回环逻辑用三段式状态机实现接收侧按字节流解析以太网/IPv4/UDP 头命中过滤条件后把载荷写入缓存帧尾tlast到来后启动发送路径先重发头部再读缓存回发载荷。下面是一个可参考的骨架接口对齐三速 MAC IP 的 AXI-Stream 从口和主口。module udp_loopback #( parameter [47:0] MY_MAC 48‘h00_11_22_33_44_55, parameter [31:0] MY_IP 32’hC0_A8_01_0A, // 192.168.1.10 parameter [15:0] MY_PORT 16‘d5001 )( input wire clk, input wire rst_n, // 接收侧 AXI-Stream 从口 input wire [7:0] rx_tdata, input wire rx_tvalid, output wire rx_tready, input wire rx_tlast, // 发送侧 AXI-Stream 主口 output reg [7:0] tx_tdata, output reg tx_tvalid, input wire tx_tready, output reg tx_tlast ); localparam S_IDLE 3‘d0; localparam S_PARSE 3’d1; localparam S_RX_PAY 3‘d2; localparam S_TX_HDR 3’d3; localparam S_TX_PAY 3‘d4; reg [2:0] state; reg [10:0] byte_cnt; reg [47:0] peer_mac; reg [31:0] peer_ip; reg [15:0] peer_port; reg [15:0] udp_len; reg frm_hit; // 帧接收控制IDLE 进入解析tlast 结束回到 IDLE always (posedge clk or negedge rst_n) begin if (!rst_n) begin state S_IDLE; byte_cnt 0; end else begin case (state) S_IDLE: if (rx_tvalid rx_tready) begin byte_cnt 0; state S_PARSE; end S_PARSE: begin if (rx_tvalid rx_tready) begin byte_cnt byte_cnt 1; // 0-3 字节目的MAC4-9 源MAC12-15源IP16-19目的IP // 20-21任意22-23协议号26-27源端口28-29目的端口 if (byte_cnt 29) begin // 所有过滤与字段采集在这里完成 if (frm_hit) state S_RX_PAY; else state S_IDLE; end end end S_RX_PAY: begin if (rx_tvalid rx_tready) begin wr_en rx_tvalid; wr_data rx_tdata; if (rx_tlast) begin state S_TX_HDR; byte_cnt 0; end end end S_TX_HDR: // 重发头部14 字节 MAC 20 字节 IP 8 字节 UDP S_TX_PAY: // 从缓存读载荷发到 tlast endcase end end endmodule这段骨架抓住一个关键设计解析阶段不缓存整帧而是边收边判断到第 30 个字节时已经能确定 5 元组过滤结果决定继续收载荷还是直接丢帧。这样对无效帧只多花了 30 个时钟周期不会占满缓存。frm_hit 的判定逻辑需要提前在每个字节处并行比较用寄存器暂存已收到的部分字段最后一个字节到达时用组合逻辑产生命中信号。回发时头部不是存下来的原始字节而是按格式重新填写的。以太网头部 14 字节目的 MAC 填收到的源 MAC源 MAC 填 MY_MAC类型字段填 0x0800IPv4 头 20 字节版本/服务类型/Total Length 按捕获后的值改写源 IP 填收到的目的 IP目的 IP 填收到的源 IP头部校验和需要重算UDP 头 8 字节源端口填收到的目的端口目的端口填收到的源端口长度就是 udp_len。这个重新组包过程用 ROM 查表或 case 语句按 byte_cnt 输出即可不必引入复杂逻辑。3.2 用双端口 RAM 做载荷缓存收和发不抢带宽载荷缓存建议用双端口 RAM 而不是 FIFO原因是回环的收和发在时间上是错开的先完整收完一帧再开始发送。双端口 RAM 的 A 口接接收侧写B 口接发送侧读读写指针完全独立不需要关心空满标志只要在发完前不从 RX 侧新写入即可。缓存宽度选 32 位还是 8 位取决于 MAC IP 核的接口配置。如果 AXI-Stream 从口是 8 位就配一个 8 位宽的简单双端口 RAM地址从 0 写到 udp_len 的大小。注意 UDP 载荷长度不一定是 4 的整数倍发送时要用 udp_len 控制读取字节数不能用“地址为 0 就停”的常见做法。有的开源 UDP 协议栈内部已经做了数据重组把缓存地址和长度索引暴露出来了这时你只需要在 IP 核的接口上做映射连状态机都可以省掉。回环之所以经常卡在这一步是因为忽略了“索引”和“重组”这两个动作UDP 载荷不是连续的字节流它前面有固定长度的协议头你的写地址必须从偏移量 4214 字节 MAC 20 字节 IP 8 字节 UDP 头处开始清零计数而不是从帧头第 0 字节开始。用 byte_cnt 减去 42 作为写地址是最直观的写法但要注意减法后的地址不要越过 RAM 深度。IP 核缓存索引重组的机制也是一样的思路用长度字段做边界所有数据都按偏移量落位。3.3 单时钟域回环与反压这里最容易被“应该没问题”带沟里UDP 回环在自测环境里通常只有一个用户时钟MAC 核的 RX 和 TX 都跑同一个频率很多人因此认为不存在跨时钟域问题时序一定收敛。这只是表象即使时钟同源接收路径和发送路径通过 RAM 交换数据时依然存在读指针滞后于写指针一个周期的判断问题。真正的坑在反压。回环状态下RX 侧 MAC 持续送数如果 TX 侧 tready 拉低S_TX_PAY 状态就不能推进但 RAM 的读指针也要停。另一个隐藏问题是 tlast 的处理MAC 核发送时要求 tx_tlast 伴随最后一个有效字节同时出现滞后一拍会导致上一帧的尾巴和下一帧的头粘连而 ILA 里看单帧波形完全看不出这个错误。处理反压的标准做法是给 RX 侧加回压当 RAM 写满或正在回发时把 rx_tready 拉低让 MAC 核暂停上送新数据。三速 MAC IP 的 AXI-Stream 接口本身支持 tready 反压但时序上要求 tready 必须在 tvalid 拉高之前至少一个周期有效这个约束在回环状态机里容易漏。经验是S_TX_HDR 和 S_TX_PAY 期间无条件拉低 rx_tready保证不会出现“正在发、又收新帧”的双写冲突。3.4 校验和与 CRC 到底抄不抄近路以太网帧的 CRC 由 MAC IP 核在发送时自动追加接收时自动校验不需要用户逻辑处理这一层可以不操心。需要决定的是 IPv4 头校验和与 UDP 校验和要不要计算。IPv4 头校验和是必须的很多网卡和抓包工具会丢弃或标记校验失败的 IP 包。但这个校验和只覆盖 20 字节 IPv4 头不含数据实现起来非常便宜把 20 字节按 16 位累加再加回进位取反。UDP 校验和则有两条路可以选RFC 768 规定 UDP 校验和可以填 0 表示不校验很多轻量 FPGA UDP 栈为了省逻辑直接填 0Wireshark 也不会报错但如果上层软件对数据完整性有要求或者要经过某些严格检查的交换机就必须算真校验和。回环场景的推荐是 IPv4 头校验和必算UDP 校验和填 0。原因有两个回环测的是连通性不是链路误码UDP 校验和伪包头里要拼上源 IP、目标 IP、协议号、UDP 长度跨时钟域用 16 位累加器会增加判断逻辑等回环通了再补也来得及。等真正做业务时再把校验和模块打开那是一个独立于回环状态机的纯组合逻辑模块。4. 上板验证Vivado 连 ILA用 Wireshark 和 iperf3 确认回环真通4.1 最小 Vivado 工程怎么连三速 MAC、时钟、ILA 三样就够不要在第一次回环验证时就引入大型数据通路。最小工程包含四个部分Clock Wizard 产生用户时钟和 MAC 参考时钟三速以太 MAC IP 核配置 GMII/RGMII 接口你的回环模块以及一个 ILA 调试核。用 Vivado 自带的回环测试按键连在回环模块的复位端即可不需要 PDMA 或 AXI 总线。Vivado 工程里要确认的一个参数是 MAC IP 的“Support non-incrementing addresses”选项如果你的 AXI-Stream 数据接口是连续推送的字节流这个选项不需要开但如果你打算后续在数据通路里做 DMA 或分组缓存这里先选上能减少之后 IP 核配置的改动。时钟频率方面1GbE 使用 125 MHz 用户时钟100MbE 是 25 MHzRGMII 的 IDELAY 在 1G 模式下必须开启具体数值由 Vivado 根据器件自动生成不要手动调。工程综合布局布线通过后先别急着烧板。把 ILA 的触发条件设为 rx_tvalid 上升沿采样深度 1024这样可以第一时间看到接收路径是否真的在进数。上板后按下复位按键再查看 ILA 波形里 rx_tlast 是否周期性出现——如果 rx_tvalid 一直有效但 rx_tlast 从不出现问题几乎都在 MAC IP 的配置上而不是回环逻辑。4.2 用 ILA 抓时序里最容易翻车的复位和 tlast回环设计最容易翻车的两个信号是复位和 tlast。先看复位MAC IP 核的复位要求保持至少若干个用户时钟周期并且复位释放后要等待内部 PHY 配置完成。如果回环模块与 MAC IP 共用一个异步复位按键释放时复位取消和时钟沿的相对关系不确定会导致状态机初始化异常。规范做法是把 MAC 核的复位完成信号通过同步器打两拍后作为自己模块的复位释放条件。tlast 的检查放在第二个通道。ILA 的触发条件设成tx_tlast 1’b1抓一次发送过程重点看 tx_tvalid 与 tx_tlast 是否在同一拍tlast 比 tvalid 晚一拍就说明你的计数器多计了一个字节下一帧的第一个字节会被当成上一帧的填充字节发出去。这个错误在连续回环时表现为偶发坏帧单次测试很难复现最好的验证方式是用后面讲的 iperf3 持续打流观察丢包率是否稳定为 0。ILA 的采样深度建议设置 16384只保留 BRAM不用分布式 RAM。回环包的 UDP 载荷如果大于几百字节太浅的 ILA 只能抓到帧头看不到整帧数据无法确认载荷缓存的读写地址是否连续。4.3 用 Wireshark 和 iperf3 做收发包比对ILA 只能证明 RTL 内部行为正确还要从上位机侧确认整条链路。先给开发板配置固定 IP 192.168.1.10电脑网卡配置 192.168.1.2用网线直连避免交换机干扰。打开 Wireshark 抓包过滤条件写udp ip.src 192.168.1.2 ip.dst 192.168.1.10这个过滤器能排除 ARP 和 LLDP 等其他广播帧。用网络调试助手向开发板 5001 端口发送一串递增数据然后在 Wireshark 里看回包回包的 ip.src 应该是 192.168.1.10udp.srcport 是 5001载荷应该和发送内容完全一致。如果只能看到请求没有回包直接看回环模块的 ILA 波形多数是地址过滤条件没命中frm_hit 一直为低。打流量用 iperf3UDP 模式看带宽和丢包iperf3 -u -c 192.168.1.10 -p 5001 -b 100M -t 10 -l 1400这里-b 100M是目标速率-l 1400是 UDP 载荷大小把载荷设成接近 MTU 可以顺带验证长包回环。UDP 测试要同时看客户端sender和服务端receiver的输出iperf3 的输出里 Sender 栏显示的是本机发出的速率Receiver 栏显示的是对端实际收到的速率。因为 FPGA 回环会把包原路打回iperf3 的 server 端在同一台电脑上会同时收到回环数据包实际观察两组数据的一致性比只看某一端更有意义。正常回环测试应该是 0% lossiperf3 的 jitter 列有一个非零抖动值属于正常现象不必在意。如果电脑没有 iperf3也可以用 tshark 做更粗暴的统计抓包一分钟统计抓到的回环包数量与发送的 UDP 包数量是否一致命令如下tshark -i eth0 -Y udp.srcport 5001 ip.src 192.168.1.10 -T fields -e udp.length | wc -l这条命令统计的是 FPGA 回传给电脑的 UDP 包数和 iperf3 报告里的发送包数比对即可。5. 回环通过后把“环”拆掉之前要补的三件事5.1 跨板联调时优先改的 4 个地址参数回环代码里地址和端口都是参数化定义的跨板联调要改的是四个常量MY_MAC、MY_IP、MY_PORT 以及你预期对端使用的目标端口。一个常见错误是改完 MY_IP 后 ARP 缓存没清电脑上arp -d清一下再 ping 一次。UDP 端口号建议避开 5000、8000 之类常用端口选一个不容易被通信库默认占用的高位端口比如 37000 附近。5.2 加一个 CRC 错误计数防止“假通过”回环测试看着包全回来了但可能是 MAC 核收了坏帧后仍然向上抛。给回环模块加两个计数器rx_frame_cnt 在每收到一个完整帧且 tlast 出现时加一rx_crc_err_cnt 在 MAC 核输出的 rx_error 信号拉高时加一。这两个计数器的值读到 ILA 里比对如果 CRC 错误帧占比超过万分之一说明物理链路或时钟有问题。这个习惯从回环阶段就建立后面接业务时才能快速定位问题在哪一段。CAN 通信里经常出现“数据帧回环测试没问题但标准模式无法发送”的现象本质就是只验证了本地收发路径没有验证对端确认与链路仲裁UDP 回环同理别把“自己收到自己发的帧”等同于“对端能收到”。5.3 换个带背压的数据通路才能从回环走向真实吞吐回环状态机验证完协议栈之后要回发的数据不再来自 RX 侧而是来自你的业务模块图像、采集数据或 AXI DMA背压方向就反过来了TX 侧 MAC 核的 tready 决定业务模块能不能继续送数据。一个直接的方案是把双端口 RAM 换成异步 FIFO写时钟用业务时钟读时钟用 MAC 用户时钟FIFO 的 prog_full 信号作为业务模块的暂停信号。到这里UDP 收发链路的骨架已经完整了剩下的就是填充业务逻辑再回头去补 UDP 校验和与分片重组。本文还有配套的精品资源点击获取
返回列表