玩FPGA的,早晚会碰上网口。如果你从串口转网口,会发现完全是两个世界:串口波特率115200,传一帧640x480的灰度图像要好几秒,网口百兆速率下同样一帧只要几毫秒。所以当你的FPGA工程开始处理图像、采集高速ADC或者跑各种算法时,网口就是一个躲不开的出口。这也是FPGA网络通信设计最典型的动机——把并行处理能力转化成高速的数据回传能力。
这篇part.7面向两类人:一是会点灯、会写状态机、跑过串口但还没碰过以太网的FPGA入门朋友;二是在FPGA上已经实现了图像处理或信号采集、正被回传带宽卡住的开发者。我会从概念、硬件连线、RTL实现到抓包验证全链路讲一遍,把实际踩过的坑和值得注意的地方一起写清楚。
1. 整体思路拆解:FPGA做网络通信到底图什么
1.1 网口在FPGA应用版图里解决什么问题
FPGA的强项从来不是“跑逻辑复杂的事务”,而是“并行处理海量数据”。但数据从FPGA里出来,总得有个出口。在工程上最常见的三个出口是:串口、PCIe、以太网。
串口最简单,但带宽极低,115200波特率算下来每秒也就十几KB,别说图像,哪怕是连续的高速采样数据也顶不住。PCIe带宽高,但要么用自带PCIe硬核的高端芯片,要么用MGT高速串行收发器,硬件布线、驱动、软件配合的复杂度直接拉满,不是零基础阶段该碰的东西。以太网恰恰卡在中间:百兆以太网有效带宽大概在8-11MB/s,千兆能到110MB/s左右,对这个阶段的工程来说基本够用;硬件上用一颗PHY芯片加RJ45座子就能解决,逻辑复杂度也远低于PCIe。
所以这一篇专门讲FPGA网络通信,解决的就是“中速数据回传和指令下发”这个刚需。你可能只是想在FPGA上采个ADC波形上抛给上位机,或者想把板上的图像数据打包发出去,或者想通过网口远程配置滤波器系数,这些都用得上。
1.2 方案选型:为什么是“PHY + FPGA内部逻辑实现MAC”
要做以太网通信,必须面对三个层次的东西:PHY(物理层)、MAC(链路层)、协议栈(IP、ARP、UDP/TCP)。方案上大体有这几条路:
| 方案 | 实现方式 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|---|
| FPGA逻辑实现MAC + 外部PHY | 自己写RTL处理RGMII/MII时序和MAC帧逻辑 | 不依赖厂商IP,灵活性高,能学到协议本质,成本低 | 开发周期长,调试有难度 | 学习、中低速通用场合 |
| 厂商MAC IP核 + 外部PHY | 用Vivado或Quartus里的Tri-Mode Ethernet MAC IP | 时序和MAC功能可靠,接口标准 | 不买授权有功能限制,IP核学习成本不低 | 产品化开发 |
| MCU网口 + FPGA协同 | FMC/SPI/并行总线把数据交给带MAC的MCU(比如STM32H743)处理 | 协议栈成熟,开发最快 | 带宽上限受总线接口限制,两套固件联调麻烦 | 既有MCU主控的产品升级 |
| 带MGT的高速方案 | FPGA的GTP/GTX + SFP光口或SerDes转以太网 | 带宽高,可达万兆 | 布线、时序、功耗难度大 | 高端板卡、数据中心 |
如果单纯从开发速度看,MCU方案可能最快,但这一篇选择的是第一行:用FPGA里自己写的逻辑实现MAC,配合一颗常见的千兆PHY芯片(比如RTL8211、YT8531、88E1512之类)做物理收发。理由很简单,一来零基础阶段自己写一遍MAC对理解以太网协议有不可替代的作用,二来以后要做高速多通道采集,FPGA自己控制链路层往往更灵活,不用依赖MCU的协议栈性能。
关键点在于PHY芯片一般不自带MAC功能,它只负责把MAC交给它的并/串行数据转换成物理线路上的差分信号,反过来再把线路信号还原成数据交给MAC。所以FPGA里必须有一套逻辑去发送和接收符合IEEE 802.3格式的帧,这就是MAC层。
1.3 内容边界:这一篇能做到什么程度
先说明白,在FPGA上完整跑TCP/IP协议栈是不现实的,至少对零基础阶段的工程没有必要。实际工程里绝大多数FPGA网络通信做的是UDP(或者加上ARP),因为UDP无连接、状态简单、实时性好,适合传输采集数据和控制指令。TCP当然也能在FPGA里实现,但状态机极其复杂,一般会交给软核处理器或者MCU侧处理,FPGA侧只管把数据包给出去。
所以这篇的网络通信设计范围是:RGMII接口收发、以太网MAC帧组包/解包、ARP应答、UDP/IP组包和收发。按这个范围,你可实现一个“FPGA当作一个简单网络设备,上位机可以通过网口收命令、收数据”的最小可运行系统。这套东西搞通之后,再往PCIe或者TCP方向走的路会顺很多。
2. 动手前要过的基础概念关
2.1 用生活类比理解协议栈分层
网络通信对FPGA开发者来说,最头疼的往往不是RTL怎么写,而是协议栈那一堆名词。其实你可以把它类比成快递发货:你网购一个包裹,商家打包好货物贴上面单(UDP头、IP头),快递公司给它套上大袋子写上地址(MAC帧头),然后快递员骑着车送过去(PHY物理传输)。每一层只关心自己那一层的事情,数据一层层包起来,收件后再一层层拆开。
放在以太网里就是这个顺序:
- PHY层:物理接头、网线、电压差分信号,负责比特流的实际传输。
- MAC层:负责把数据装成帧(加前导码、目的MAC、源MAC、类型、FCS校验),也负责从网线上收帧、校验、剥壳。
- IP层:负责逻辑地址(IP地址)和跨网络路由,先把数据装进IP报文,然后再塞进MAC帧里。
- UDP层:负责端口,标记这个数据是给哪个应用程序的。
FPGA通信设计里,你至少要处理MAC层和UDP/IP层。ARP属于IP层的地址解析协议,作用是问你“知道某IP对应的MAC是多少吗”,这个也必须处理,否则上位机第一次发IP包时会卡在找不到MAC地址这一步。
2.2 RGMII接口与引脚时序
FPGA和PHY之间现在最通用的是RGMII接口(Reduced Gigabit Media Independent Interface)。它把千兆MAC和PHY之间的数据线从GMII的8位并行降到了4位并行,然后在时钟上下沿都采样,从而在125MHz时钟下跑出1000Mbps。
RGMII的信号不多,按功能分三组:数据、时钟、控制。
| 信号 | 方向(以FPGA侧视角) | 作用 |
|---|---|---|
| eth_rxc / tx_clk | 输入/输出 | 125MHz/25MHz/2.5MHz 时钟,RXC由PHY恢复给MAC |
| eth_rx_ctl / eth_tx_ctl | 输入/输出 | 上下沿分别表示“数据有效”和“数据错误”/“数据有效” |
| eth_rxd[3:0] / eth_txd[3:0] | 输入/输出 | 上下沿各采4bit,拼成8bit数据 |
具体到RGMII时序:发送方向,FPGA在tx_clk上升沿发送低4位(txd[3:0]),下降沿发送高4位(txd[7:4]),同时tx_ctl上升沿表示有数据,下降沿表示数据是有效帧的一部分。接收方向类似,但时钟是PHY恢复出来的rxc,FPGA作为接收端在rxc的上下沿分别取rxd和rx_ctl。
时序上有个最值得注意的点:发送侧的时钟延迟。很多PHY要求RGMII发送数据相对于时钟有一点延迟,否则在高速下建立保持时间不够。Xilinx系的FPGA一般直接在约束里加ODDR来保证源同步时序,Altera系需要在PHY接口侧调整I/O延时。这个细节后面第5章再展开。
2.3 以太网帧格式:收发都得按这个格式来
写FPGA网络通信,MAC帧格式必须烂熟于心。以太网MAC帧基本格式如下:
- 前导码7字节(0x55重复7次)+ 帧起始定界符1字节(0xD5)
- 目的MAC地址6字节
- 源MAC地址6字节
- 类型/长度2字节(0x0800表示IPv4,0x0806表示ARP,0x86DD表示IPv6)
- 数据载荷46-1500字节
- FCS校验4字节(CRC32)
FPGA发送时通常可以不必发前导码和FCS,由PHY或MAC逻辑自己生成,但为了通用性,最好自己组帧时把前导码也补上。这里有一个坑:CRC32的计算范围是从目的MAC到数据结束,不包括前导码和SFD,而且是以太网多项式(0x04C11DB7)的非反射、初始值为全1、结果异或全1的版本。网上CRC生成工具很多默认都用的是反射型CRC32(比如常见的zip crc32),这里直接拿过来用会算错,必须确认采用“Ethernet CRC”的算法形式。
3. 实操:从零搭建最小以太网收发链路
3.1 硬件连接和PHY配置的注意点
选型方面,入门级板卡上比较常见的是RTL8211E、RTL8211F、YT8531这些千兆PHY,芯片基本都支持RGMII模式。拿到原理图先确认几个重要引脚:PHY的时钟来源(通常需要从FPGA或独立晶振给一个25MHz或125MHz参考时钟)、复位引脚极性、RGMII模式配置引脚(mode strap)。
硬件连线上,FPGA与PHY之间就是6组信号:tx_clk、tx_ctl、txd[3:0]、rx_clk、rx_ctl、rxd[3:0]。有些FPGA开发板会用GMII(8位数据+125MHz单沿),但在百兆/千兆通用设计里,RGMII已经是主流,我这里以RGMII为例。
PHY配置这里说一个非常实用的经验:如果只是把网口调通跑UDP,初学阶段完全不需要先写MDIO配置逻辑。大部分PHY的默认strap配置已经能工作在RGMII千兆或百兆模式,上电复位后链路就能用。真正需要MDIO配置的场景是:调速、调整延迟、读取link状态、切换千兆/百兆模式。真到那一步再补一个简单的MDIO主机模块,按PHY寄存器手册逐位写就是了。
如果板卡上有两个或更多PHY,务必注意每个PHY的默认地址可能不同,MDIO地址引脚一般由硬件pull-up/pull-down决定,调试读不到寄存器时先查这个。
3.2 顶层模块划分:不要把协议逻辑全揉在一起
写FPGA网络通信最容易犯的错误是一个模块里既管RGMII时序又管UDP组包还管FIFO调度,最后状态机乱成一锅粥。我建议从一开始就按数据流方向拆成清晰的两条链路,并做独立模块。
接收链路:eth_rgmii_rx(RGMII采样)→ rx_mac(MAC帧解析、CRC校验)→ rx_protocol(ARP应答、UDP解包)→ rx_fifo(数据缓存,输出给应用层)。
发送链路:tx_fifo(应用层数据缓存)→ tx_protocol(UDP/IP/ARP组包)→ tx_mac(MAC帧封装、CRC生成)→ eth_rgmii_tx(RGMII发送时序)。
中间再加一个简单的cmd_parser模块,负责根据收到的命令类型去路由数据。比如收到ARP请求就交给ARP应答逻辑,收到UDP请求就解析端口和载荷,把有效数据写入FIFO,并向发送链路发一个“我有数据要发”的信号。
这样的架构好处很直接:调RGMII时不用关心协议,调协议时不用关心物理时序,出了问题能快速定位是哪一段。另外,这样的模块划分也方便以后换成厂商MAC IP或者加入自定义协议。
3.3 RGMII接收模块的RTL写法
接收方向的关键是把PHY送来的双沿数据转成单沿的8bit数据流。Xilinx FPGA一般用IDDR原语,Altera/国产FPGA也有对应的DDR原语。这里给一个参考性的Verilog写法思路(以Xilinx系为例):
// 伪代码示例:RGMII接收核心逻辑 always @(posedge eth_rxc or negedge eth_rx_rst_n) begin if (!eth_rx_rst_n) begin rx_data_l <= 4'b0; rx_ctl_l <= 1'b0; end else begin rx_data_l <= eth_rxd; rx_ctl_l <= eth_rx_ctl; end end always @(posedge eth_rxc or negedge eth_rx_rst_n) begin if (!eth_rx_rst_n) begin rx_data_h <= 4'b0; rx_ctl_h <= 1'b0; end else begin rx_data_h <= eth_rxd; rx_ctl_h <= eth_rx_ctl; end end // 根据上下沿组合出8bit数据 assign rx_data_byte = {rx_data_h, rx_data_l}; assign rx_data_valid = rx_ctl_l; // 高电平表示数据有效这里必须说明的是:原语的例化方式和你用的是哪家FPGA强相关——Vivado里是IDDR,Quartus里可能是ALTDDIO_IN,国产高云、安路的原语又是另一套。但核心逻辑完全一样:把上下沿采到的4bit拼成8bit,把ctl上下沿的信息解析成data valid和data error,然后交给MAC解析模块。
另一个关键点是异步时钟域。RGMII的rxc和你的用户逻辑时钟(比如125MHz)不是同源时钟,严格来说收到的数据要先做跨时钟域处理。简单做法是把rxc当地址直接采数据,再把同步后的8bit数据用异步FIFO转换到用户时钟域。这一步不做,后续协议解析时偶尔会出现偶发数据错乱,很难查。
3.4 MAC帧解析与组包的状态机设计
MAC接收状态机一般是这样:
- IDLE:等待前导码和SFD,检测到0xD5后进入接收状态
- RECV:按字节收数据,存目的MAC、源MAC、type,数据写入FIFO
- CHECK:数据收完后,比较收到的CRC和本地计算CRC,不一致则丢弃
- DELIVER:CRC正确后,把帧头信息和数据长度交给上层
一个细节:很多以太网帧末尾会有几个字节的填充(pad),为了保证帧长至少64字节。判断一个UDP包是否有效,不能只看数据长度,还得在校验完CRC后,根据IP头里的total_length字段来截取实际UDP数据,多余的部分直接丢掉。
组包状态机则简单很多:IDLE等待发送请求,然后按顺序发送前导码、目的MAC、源MAC、type、数据、CRC。发送数据时注意,CRC必须是整个帧发送完成后再追加的4个字节,不能用“边发边算边输出”的思路,因为CRC需要覆盖整个帧的内容。工程上的做法是:先把数据存进发送FIFO,等FIFO即将读空的最后一个字节发完时,再把计算好的CRC连续发出去。
3.5 最简单的验证方式:先做环回
写完RGMII收和发,先别急着写ARP和UDP。把接收到的任意MAC帧原封不动地发回PHY,然后在电脑上用Wireshark抓包。这样能验证一整条物理链路是否正常,同时还能确认PHY配置、时钟、CRC都在工作。
操作方法是:电脑和FPGA板卡用网线直连(如果电脑千兆口不支持自适应可能有兼容问题,可以在电脑网卡属性里强制百兆模式先试),把FPGA的接收数据直接接到发送数据路径上,注意要把接收到的CRC原样保留一起回发。如果Wireshark里能看到自己连续发的包,到这一步链路就基本通了。之后再去做协议解析就有底气了。
4. 内层协议实现:ARP和UDP/IP的硬逻辑写法
4.1 ARP应答:网络通信里第一个必须写的“对话”
当电脑第一次向FPGA的IP发数据时,会先发一个ARP请求:“谁的IP是192.168.1.10?请告诉我你的MAC地址。”如果FPGA不回答,后续的UDP/IP包永远不会发出来。
ARP请求帧长这样:目的MAC为广播地址FF-FF-FF-FF-FF-FF,类型0x0806,ARP头里硬件类型0x0001、协议类型0x0800、硬件地址长度6、协议地址长度4、操作类型0x0001(请求)。
FPGA收到后要做三件事:检查ARP头里的目标IP是不是自己,如果不是直接丢弃;如果是请求,构造一个ARP应答帧;应答帧的源MAC填自己,目的MAC填请求方的MAC,操作类型改成0x0002,然后把请求里的发送方IP和MAC对调填进去,再封装成MAC帧发出去。
这个模块属于纯组合逻辑+简单状态机就能干的活,在零基础阶段用来练习状态机正合适。注意ARP响应帧不需要CRC计算错——仍然要正确计算并追加FCS,否则电脑网卡会丢弃响应包。
4.2 UDP组包:IP头校验和的计算细节
UDP帧的组装顺序是:MAC帧头(目的MAC、源MAC、type=0x0800)→ IP头 → UDP头 → UDP数据 → CRC。
IP头固定20字节,关键字段需要填:版本号4、头长度5(即20字节)、总长度=20+UDP头8+数据长度、标识可随便填、TTL通常填64、协议号填17(UDP)、源IP、目的IP。IP头校验和是唯一需要算的东西。
IP校验和的算法:把一个16bit序列所有数加起来,进位回卷,取反。为了运算简单,可以在组包时逐字累加IP头里的前10个字(不含校验和字段),最后进位相加取反填入。这里有个常见错误:IP头校验和不受UDP数据影响,但UDP校验和会把伪首部(源IP、目的IP、协议号、UDP长度)也算进去。如果是在FPGA里做纯硬件组包,一个偷懒的办法是把UDP校验和字段填0,在局域网或很多简单上位机场景下对方也能接受,但这属于不规范实现,某些协议栈会丢弃。正规做法是补上UDP校验和计算,好在计算过程和IP校验和类似,只是在前面加上伪首部的12字节一起算。
4.3 CRC32并行计算:别用串行移位寄存器
以太网FCS使用的是CRC32,如果按位串行移位计算,一帧1500字节要算12000拍,严重拖慢吞吐。实际工程中通常用查表法或并行CRC32核心来实现每拍计算8bit甚至32bit。
用LFSR原理推导得到8bit并行CRC32核心后,可以把它封装成一个组合逻辑函数:输入当前CRC寄存器值和新的8bit数据,输出更新后的CRC值。每接收或发送一字节调用一次。具体系数表网上资料很多,但一定要先做验证——用已知的以太网帧测一下,如果CRC结果不匹配就赶紧换参数配置,别等到上板了再抓头。
让我分享一个很实用的验证方法:不要对着复杂的数学式子硬推,先用全0数据帧(目的MAC全0、源MAC全0、type全0、数据全0)算一次,再用全1帧算一次,把结果和某个通用CRC计算工具比对。如果全0和全1都能对上,基本就说明参数没配错。
4.4 缓冲FIFO的深度怎么定
高速收发时,协议组包模块和用户逻辑的频率不一定匹配,必须用FIFO做缓冲。FIFO深度不是随便定的,有几个经验值:
- 发送方向:如果用户逻辑以125MHz写入,发送MAC以125MHz读出,单包数据最多1500字节(不含帧头),最小深度取2048即可。但注意考虑背压:如果PHY或链路暂时繁忙,FIFO要能扛住连续几帧的突发,适当加深到4096更稳。
- 接收方向:UDP解包后数据被应用逻辑消费,如果应用逻辑偶尔停顿,FIFO至少存一整包以上才不会丢包,通常取2048或者更大。
FIFO实现推荐用FPGA内部的Block RAM,Vivado原语用FIFO36E1,Quartus用FIFO IP核,国产厂家也有对应IP。同时务必把读写计数、full、empty信号引出来接调试工具,这一步在调协议栈时能省大量时间。
4.5 一个小型UDP发送时序的参数计算
如果要以百兆以太网连续发送图像数据,需要算好带宽和帧间隔。百兆有效MAC速率算下来是10MB/s左右,但协议头、帧间隔、CRC要吃掉一部分:
| 项目 | 字节数 |
|---|---|
| 以太网帧头+类型 | 14 |
| IP头 | 20 |
| UDP头 | 8 |
| 实际数据(以最大UDP载荷1472为例) | 1472 |
| FCS | 4 |
| 帧间隔+前导码 | 20(IFG 12字节 + 前导码+SFd 8字节) |
所以单帧总开销大约是14+4+20=38字节,有效载荷1472字节,实际最大利用率大约97.5%,百兆下真实有效数据速率最高大概9.75MB/s。如果你要上抛1080P30灰度视频(约62MB/s),百兆明显不够,得上千兆。千兆RGMII时,125MHz时钟、8bit数据宽度,最大有效数据速率约120MB/s,能勉强够1080P30灰度,但如果是彩色RGB888就必须做压缩或者换更高带宽方案。
这个算清楚之后,再做图像上抛类的应用心里就有底了。
5. 常见问题与排查技巧实录
5.1 问题速查表
| 现象 | 可能原因 | 排查/解决思路 |
|---|---|---|
| 网口link灯不亮 | PHY供电、复位、时钟缺失 | 先查复位引脚和参考时钟,用示波器量PHY时钟是否起振,确认PHY模式strap是否处于RGMII模式 |
| ping不通FPGA | ARP没响应、MAC地址不对、FPGA逻辑没跑起来 | 先在PC上用arp -a查看缓存;FPGA侧用ILA抓收到的ARP请求,看是否进了接收状态机;确认目的IP匹配逻辑 |
| Wireshark能看到ARP请求但PC收不到应答帧 | 应答帧CRC计算错误、目的MAC错误、回环测试干扰 | 核对CRC32参数;确认应答帧目的MAC是否等于请求方MAC;确认PHY发送数据是否正常 |
| UDP能发出去但上位机收不到数据 | 端口号对不上、IP校验和错误、帧没对齐 | Wireshark过滤udp.port == 端口号;检查IP/UDP头字段;检查字节序是否大小端颠倒 |
| 数据偶发错误一两个字节 | 跨时钟域没处理、RGMII时序余量不足 | 检查rxc和用户时钟的异步FIFO是否确实存在;确认IO delay或IDDR约束是否违反时序 |
| 高低温或长跑后断链 | RGMII时序裕量不足、PHY配置漂移 | 在时序约束中给RGMII路径加适当输出延迟;用MDIO读取PHY状态寄存器判断link状态 |
5.2 排查手段:ILA和Wireshark配合
FPGA调网络通信,最痛的点是“不能直接看网络包”,所以核心调试手段是:FPGA侧用逻辑分析仪(Vivado里是ILA,Quartus里是SignalTap),电脑侧用Wireshark。
我的建议是验证三步走:第一步,只上RGMII回环,Wireshark里确认有包;第二步,上MAC帧解析,FPGA里收一个PC发的UDP包,用ILA观察MAC帧头是否正确;第三步,上ARP和UDP组包,PC端用Wireshark确认FPGA发出的应答和UDP包格式、校验和都正确。
抓包时有个细节:电脑网卡的ARP缓存会把之前的IP-MAC映射存很久,你改了FPGA里的MAC地址后必须用arp -d命令清理缓存,否则PC会一直往旧MAC发数据,FPGA那边自然收不到。这个问题我见过不少人卡了很久。
5.3 时序约束实践:RGMII路径的set_input_delay
最后必须提一下时序约束。RGMII的收发都是源同步接口,FPGA综合工具并不知道外部PHY的时钟相位关系,所以你必须用set_input_delay和set_output_delay告诉工具外部时序参数,否则工具可能为了满足内部时序而做出不合理的布局布线,板上偶发错数据。
最简单的做法是按照PHY数据手册提供的RGMII延迟参数,给rx_clk到rxd的输入路径设置set_input_delay,给tx输出路径设置set_output_delay。以千兆模式为例,RGMII数据在时钟上升沿之后约1-2ns稳定,输入延迟大致在-0.5到0.5ns之间,具体数值以PHY手册为准。
实际操作中,不少入门板卡为了让RGMII时序稳妥,会选择在PHY侧开启内部延delay(通过strap或MDIO配置),此时FPGA侧的delay可以设置为0,也可以正常约束。这类板卡调起来会省心很多,但也意味着你没法靠FPGA侧的约束去修正所有时序问题,调板前一定先看板卡原理图确认有没有做延迟处理。
6. 学完这一part之后,可以往哪里走
把FPGA的网络通信链路跑通之后,你的FPGA开发能力已经从一个“只会做内部状态机”的阶段,升级到了“能和外接芯片、上位机协同工作”的阶段。接下来的路有好几条可以选择:往系统级走,可以研究在上位机用LabVIEW、Python或者C#写一个实时显示和参数配置界面,这样FPGA图像采集+网络传输+上位机显示就构成一个完整的小项目;往性能走,可以把百兆升级到千兆,并加入多通道UDP切片、Jumbo Frame,甚至把一个图像帧拆成多个UDP包分片发送;往协议深度走,可以去攻坚TCP状态机,或者干脆在FPGA里跑一个软核处理器(MicroBlaze、Nios II,或者国产FPGA里的RISC-V软核),把TCP协议栈跑在软核上,FPGA逻辑专心做数据搬运。
从我个人的经验来看,当初卡我最久的并不是RGMII时序或者CRC计算,而是对整个数据流的全局理解——容易在一个小模块里耗了太久,忘了整个系统是从网线到上位机的一条完整链路。所以如果你是零基础过来的,建议先放下细节,用顶层视角把这条路看通:上位机发一个请求包,经过PHY变成比特流,FPGA里的RGMII模块恢复成字节,MAC模块剥掉帧头,协议模块认出这是ARP还是UDP,该回什么包,然后把回包再一层层套回去。这段流程想明白了,后面的工作其实就是填代码而已。
最后再分享一个小经验:调网络通信时,别只看灯亮没亮,要把Wireshark里每一帧的字段都读到——目的MAC、源MAC、type、IP、UDP端口、校验和,任何一个字段和协议对不上,网络栈都不会把包往上送。先学会“用Wireshark读帧”,再回来写FPGA逻辑,效率会高一倍。这一篇的内容足够搭出一套能用的百兆/千兆UDP数据通信系统,往后的扩展方向,就是你的应用场景说了算了。