FPGA纯逻辑实现UDP协议栈:verilog-ethernet工程实战解析
2026/9/15 3:14:36 网站建设 项目流程

我自己这个FPGA学习系列写到第10篇,前面折腾完数码管、串口、DDR、跨时钟域这些基本功之后,终于要碰一块真正硬核的东西:verilog-ethernet开源UDP协议栈工程。简单说,这个工程能让FPGA以纯逻辑方式把以太网数据包收进来、解析出UDP内容、再按要求发回去,全程不依赖软核CPU,纯硬件逻辑干活。对于想做FPGA图像采集回传、高速数据记录、仪器控制、TDC直方图抓取这类应用的人来说,这是一个绕不开的节点。

这篇内容适合两类读者:一类是像我一样从近似0基础自学FPGA、想把网络能力加进来的开发爱好者;另一类是用软核调过网口、现在想搞明白底层以太网协议到底怎么在硬件上转起来的人。我会按"先懂原理、再读代码、然后上板、最后扩展"的顺序,把整个学习过程和我踩过的坑完整过一遍。

1. 为什么是verilog-ethernet:先解决"要不要自己写协议栈"的问题

很多FPGA初学者碰到网络需求时,第一反应是"我从零写一个UDP协议栈"。这个想法很热血,但我不建议在没有基础的时候直接干。原因不是写不出来,而是以太网链路本身的水太深,会消耗掉你大量本该花在业务逻辑上的时间。

1.1 自己写UDP协议栈要面对哪些坎

先列一下完整链路上躲不开的模块:

  • 物理层接口时序,比如GMII的8位数据在125MHz时钟下采样,或者RGMII在时钟上下沿各采4位;
  • MAC层帧格式,包括前导码、帧起始符、目的MAC、源MAC、EtherType、数据、FCS校验;
  • ARP协议,负责把IP地址解析成MAC地址,没有它,PC端甚至不会把数据包真正发出来;
  • IP层协议,至少要做20字节IP头的组包和解析,得计算和校验头部校验和;
  • UDP层协议,8字节UDP头,源端口、目的端口、长度、校验和。

这些如果全部自己造轮子,学到的知识确实很扎实,但项目进度会拖得很长。你最后会发现,你真正关心的是"怎么把采集到的图像数据从FPGA传回PC",而不是一遍遍调CRC和帧间隙。

1.2 verilog-ethernet到底帮你做了什么

verilog-ethernet在GitHub上是很老牌的开源以太网IP核项目,作者是Alex Forencich,纯Verilog实现,MIT协议可以放心用在个人学习和商业项目里。它把上面说的完整MAC+ARP+IP+UDP链路全部实现好了,模块之间用类似AXI-Stream的握手信号连接,跟你自己写的用户逻辑对接非常方便。

更关键的是它有完整仿真example,你可以先在电脑上把回环测试跑通,再往板子上移植。对初学者来说,这种"先仿真后上板"的路径能省掉大量排查接线问题和PHY配置的时间。我自己的学习方式是先把example完整跑一遍,再用ILA抓实际总线数据对照理解,效果比对着源码硬啃好太多了。

1.3 一个需要明确的前提:这个工程不是黑盒子

Arteris、Xilinx这些厂商也有现成的Ethernet IP核,但很多是加密的,你只能当黑盒子用。verilog-ethernet的优势在于所有RTL代码都是开放的,你可以一层层往下看,直到看到MAC层怎么处理FCS,也能自己加过滤规则、改缓存策略。这种"透明性"对学习来说是巨大的价值。我一直觉得,FPGA工程师和软件工程师最大的不同是,你手上的每个逻辑门都是可见的、可修改的,利用好这种可见性才能进步快。

2. 学这个工程前,需要补哪些网络基础

verilog-ethernet写得很规整,但如果你完全不了解网络包结构就直接翻代码,很容易被一堆tdata、tvalid、tlast信号淹没。所以在打开工程之前,我建议至少把这几个概念吃透。

2.1 一帧数据从PC到FPGA,要拆多少层

以太网帧、IP报文、UDP报文这三个概念是最底层的骨架。你在PC上用网络调试助手发一个UDP包,数据实际在网线上是长这样的:

  • 最外层是MAC帧,包含目的MAC地址(6字节)、源MAC地址(6字节)、EtherType(2字节)。
  • 如果EtherType是0x0800,说明里面装的是IPv4报文;如果是0x0806,说明里面装的是ARP报文。
  • IPv4报文里有一个Protocol字段,值为17就表示上层是UDP。
  • UDP报文里有源端口、目的端口,以及真正的用户数据。

我常用一个快递的类比:MAC地址是"快递柜的物理格口号",IP地址是"你家小区地址",端口是"家里具体哪个人签收"。FPGA的MAC模块负责拆快递柜,IP模块负责按小区地址派送,UDP模块负责把包裹递到对应的人手上。

2.2 为什么ARP是网络通不通的关键

很多人在第一次调UDP时遇到的现象是:PC端显示"发送成功",但FPGA完全没反应。八成原因不是UDP模块问题,而是ARP没通。

这里讲一下原因:PC要把IP包发给FPGA,底层必须知道FPGA网卡的MAC地址。PC会先发一个ARP广播包,内容是"谁的IP是192.168.1.10,请把你的MAC地址告诉我"。FPGA收到后需要回一个ARP应答,PC才能拿到MAC地址,随后才会真正发送UDP数据包。

verilog-ethernet里的eth_arp模块就是干这个事的。它不但会自动回应对端发来的ARP请求,还会维护一张ARP缓存表,记录"哪个IP对应哪个MAC"。这个缓存表的作用是,当FPGA主动往外发包时,需要先查表找到目的MAC地址,查不到就发ARP请求去问。

2.3 你需要认识的几个关键字段

读代码时,下面这些字段你会反复看到,提前背下来能省很多功夫:

字段长度作用
目的MAC6字节帧的接收方网卡地址
EtherType2字节0x0800=IPv4,0x0806=ARP
IP头中的Protocol1字节17=UDP,1=ICMP
UDP源端口2字节发送方端口号
UDP目的端口2字节接收方端口号
UDP长度2字节UDP头+数据的总长度

做FPGA的时候,你不需要关心整个完整五元组,通常只需要关注"目的端口"来做数据分发,比如端口5001给图像通道、端口5002给命令通道。这个思路在verilog-ethernet上很容易实现,因为接收端的UDP头会被解析成独立信号,你直接对端口号做比较即可。

3. 工程骨架与代码阅读路线:从哪里看起才能不迷路

verilog-ethernet仓库里目录结构比较清晰,rtl目录下面有大量的模块,第一次进去很容易懵。我给出的建议是:不要从头到尾线性读,而是按数据流向去读,并且先从最高层的eth_udp模块入手。

3.1 先建立一个模块地图

从功能上划分,整个工程可以分成三大板块:

  • 用户接口层:eth_udp模块,它把UDP、IP、ARP打包成一个整体,对上层提供收发stream接口;
  • 协议处理层:eth_udp_tx、eth_udp_rx、eth_ip、eth_arp等,负责处理网络协议;
  • 物理层适配层:eth_mac、eth_phy_rgmii、eth_phy_gmii等,负责对接具体板上的PHY芯片。

我画不出复杂的层次图,但你可以想象成一个工厂流水线:用户逻辑是"货主",UDP/IP/ARP是"仓库分拣员",MAC和PHY是"运输车队"。货主把货物交给分拣员,分拣员打包并贴上地址标签,运输车队按标签运出去;接收的时候流程反过来。

3.2 我推荐的阅读顺序

如果照着rtl目录里的字母顺序读,你可能很快就在eth_mac的FCS逻辑里失去了耐心。我自己的顺序是这样:

  1. 先读顶层eth_udp,搞清楚对外接口有哪些信号,明确哪些信号是配置用的、哪些是收发数据用的。
  2. 再读eth_udp_tx,沿着发送路径走一遍,看UDP头、IP头是怎么在发送时拼上去的。
  3. 再读eth_udp_rx,沿接收路径走一遍,看数据包是怎么被解析的。
  4. 之后读eth_arp,理解ARP请求和应答的处理逻辑。
  5. 最后才是eth_mac和PHY接口层,因为这一层最琐碎,但理解了它才能把板子调通。

这个顺序的核心逻辑是:先看"对外长什么样",再看"内部怎么处理",最后看"怎么跟真实世界对接"。跟学FPGA本身一样,先搭框架,再填细节。

3.3 和前面学的AXI-Stream握手信号衔接

verilog-ethernet内部的用户侧接口大量使用valid/ready握手,这个如果你之前学过AXI-Stream总线,会觉得很亲切。握手规则就三条:valid表示数据有效,ready表示接收方可以接收,只有当valid和ready同时为高时,数据才算被成功传输。tlast表示这是包的最后一个数据,tuser多半用来表示包的长度、地址或错误标志。

很多初学者在这里犯的错是把valid当成"写使能"直接用,忽略了ready信号可能会导致数据丢失。丢包在仿真里不一定能发现,因为仿真模型对时序不敏感,一上板就原形毕露。这个我后面专门讲。

4. 数据通路拆解:一个UDP包从PC到逻辑再到PC的完整旅程

这一章我打算贴着verilog-ethernet的实际信号和流程,把一条UDP报文在FPGA里的完整旅程走一遍。你以后自己改代码,最终也必须落到这条通路上。

4.1 发送路径:用户数据如何变成网线上的字节流

当用户逻辑想通过UDP往外发数据时,会把数据放到udp_tx_axis_tdata总线上,然后拉高udp_tx_axis_tvalid,等待udp_tx_axis_tready应答。等到握手成功后,eth_udp_tx模块内部的状态机会做这么几件事:

  • 组装UDP头,把配置好的源端口、目的端口、长度填进去;
  • 组装IP头,把源IP、目的IP、协议号17填进去,同时计算IP头校验和;
  • 查ARP缓存表,得到目的MAC地址;
  • 组装MAC头,填目的MAC、源MAC、EtherType=0x0800;
  • 把整个帧交给MAC层,补前导码、填充位、计算FCS。

发送路径最需要注意的是长度字段。UDP头里的长度是"UDP头8字节+数据长度",IP头里的总长度是"IP头20字节+UDP头8字节+数据长度",MAC帧则要求整帧最小64字节,不够要在数据后面补0。verilog-ethernet的eth_udp_tx会自动做填充,但如果你要自己改发送逻辑,这个坑极其容易踩。

4.2 接收路径:网线上的字节流如何变成用户数据

接收路径是发送的逆过程:

  • MAC层先做前导码检测、FCS校验,剥掉MAC头;
  • eth_ip_rx检查IP头,确认目的IP是本地IP,同时校验头部校验和;
  • eth_udp_rx检查UDP口和长度,把数据载荷部分搬到udp_rx_axis_tdata上。

接收方向最容易忽略的是"过滤"。eth_ip_rx其实承担了很重要的过滤职责:不是发给本地IP的IP包会被直接丢掉,不是本地端口的数据也不会出现在用户接口上。这个过滤逻辑如果你要支持多个端口,就得自己改。

4.3 ARP核心流程在代码里长什么样

在eth_arp模块里,每收到一个ARP请求,它都会做一次目的IP匹配。如果请求的IP等于本地local_ip,就自动回一个ARP应答,应答包的内容是"本地MAC地址是xxx,对应IP是yyy"。同时它把对端的IP和MAC组合写入缓存表。

ARP缓存表在我的经验里是非常重要的一个部件。FPGA主动发包时,如果缓存表里查不到目的IP对应的MAC,tx路径会触发一次ARP请求,并且要等对端回应后,才能把真正的数据包发出去。这个等待过程在仿真里没问题,上板后如果PC防火墙或者驱动不回ARP,体验就是"FPGA发不出来包"。

5. 在自己板子上例化:时钟、复位、物理层接口和信号对接

代码读得差不多了,下一步就是往自己的工程里移植。这一步涉及很多跟平台相关的细节,我把自己踩过的坑在这里集中说一下。

5.1 先解决物理层接口:你的PHY芯片是什么接口

市面上常见的开发板网口PHY芯片有三类接口:GMII、RGMII、SGMII。

  • 如果你的板子是RGMII接口,需要用到eth_phy_rgmii这个适配模块,它负责把RGMII的双沿4位数据转成GMII的单沿8位数据;
  • 如果是老式GMII接口,mac可以直接对接,但要注意GTX_CLK的相位;
  • 如果是SGMII,那通常还要借助Xilinx或Intel的高速串行收发器,复杂程度会上一个台阶。

我自己的板子用的是RGMII接口,第一次上板时经常出现偶发丢包,后来发现是input delay约束没写对。RGMII在千兆模式下的时钟和数据之间的相位关系有明确要求,必须在XDC里通过set_input_delay/set_output_delay来约束,不能只靠时钟约束。这个工程里如果自带example,它的XDC文件是最好的范本,直接拿自己的引脚号替换就行。

5.2 时钟和复位的处理

verilog-ethernet的模块是在同一个时钟域下工作的,通常就是以太网的125MHz时钟。如果你板子上有独立的用户逻辑时钟,比如200MHz,那两边之间必须用异步FIFO做缓冲,不能直接把不同时钟域的信号接到eth_udp模块上。

复位要注意的是释放同步。异步复位、同步释放是FPGA的基本功,特别是网络这种高速接口,复位毛刺会导致PHY状态错乱。有条件的板子,最好让PHY的复位引脚和FPGA内部复位都由同一个复位芯片或逻辑控制。

5.3 例化eth_udp时那些绕不开的信号

例化时,除了收发数据信号,还有几个配置类信号必须接好:

  • local_mac:本机MAC地址,最好设成一个固定的、不会和局域网内其他设备冲突的地址;
  • local_ip:本机IP地址,你PC端的IP必须和它在同一个网段;
  • gateway_ip:如果只是直连或同一个局域网,这个值不一定用到,但建议填对。

以你clone到的具体版本为准,信号名可能会有小差异,但原理是通用的。例化时尽量照着example里的wrapper改,不要自己凭空写,因为漏掉某个tuser信号会导致仿真时看着正常,上板完全不发数据。

下面是一个简化的例化示意:

eth_udp #( .TARGET ("XILINX"), .MACSIZE (1), .ARP_CACHE_SIZE(8) ) u_eth_udp ( .rst (rst_n), .clk (clk_125m), .local_mac (local_mac), .local_ip (local_ip), .gateway_ip (gateway_ip), .udp_tx_axis_tdata (udp_tx_tdata), .udp_tx_axis_tvalid(udp_tx_tvalid), .udp_tx_axis_tready(udp_tx_tready), .udp_tx_axis_tlast (udp_tx_tlast), .udp_tx_axis_tuser (udp_tx_tuser), .udp_rx_axis_tdata (udp_rx_tdata), .udp_rx_axis_tvalid(udp_rx_tvalid), .udp_rx_axis_tready(udp_rx_tready), .udp_rx_axis_tlast (udp_rx_tlast), .udp_rx_axis_tuser (udp_rx_tuser), .mac_rx_axis_tdata (mac_rx_tdata), .mac_rx_axis_tvalid(mac_rx_tvalid), .mac_rx_axis_tlast (mac_rx_tlast), .mac_tx_axis_tdata (mac_tx_tdata), .mac_tx_axis_tvalid(mac_tx_tvalid), .mac_tx_axis_tready(mac_tx_tready), .mac_tx_axis_tlast (mac_tx_tlast) );

注意mac侧信号如果不需要直接操作,可以连到eth_mac模块,或者照example接法来。上板之前先打开原版example,把所有信号名过一遍,别凭记忆硬写。

5.4 用户数据接口怎么对接常见FIFO

我自己在项目里习惯在用户逻辑和eth_udp之间放FIFO。发送方向,用户逻辑把数据写进FIFO,再由发送状态机把FIFO数据搬到udp_tx_axis;接收方向,udp_rx_axis的数据搬运到FIFO,用户的处理逻辑再读FIFO。

这里建议重点检查FIFO的状态信号。如果FIFO的半满、空、满信号接错了,会造成反压不及时。理论上tready为低时发送方就不能再发起传输,但如果FIFO已经满了而上一拍tready还是高,那就溢出丢包了。实际处理时要在udp_tx_axis_tready为高时才能从FIFO里pop数据,这个判断不能省。

6. 仿真与上板验证:怎么确认协议栈真的在干活

移植完成后,别急着直接上板模拟。先把仿真跑通,再上板用Wireshark抓包验证。这个顺序能帮你隔离很多问题。

6.1 先用工程自带的example做回环仿真

verilog-ethernet的example目录下有现成的回环示例,通常设计了从用户侧发出一个UDP包,然后从MAC接口收到的逻辑,或者把收到的数据再发回去。用Vivado的XSim或ModelSim仿真都能跑,但我更推荐Verilator,因为这个项目作者的开发环境就是Verilator,运行速度快,生成波形也方便。

仿真时你会看到完整的握手过程:FSM从IDLE跳到组装UDP头,再组装IP头,最后组装MAC头。刚开始看波形时可以先不看数据,只看valid和ready的握手节奏,理解状态机的时序布局,然后再进阶看数据内容是否符合预期。

6.2 自己写一个最小Testbench的思路

如果想自己验证特定行为,只需要搭一个最小Testbench:产生125MHz时钟,拉高复位释放,设置local_mac、local_ip,然后送一个UDP包到udp_rx_axis或从udp_tx_axis收包。比如验证发送方向,你从用户侧发出一个payload,检查MAC接口的EtherType是不是0x0800,目的MAC是不是PC的MAC。

要验证ARP,可以在Testbench里模拟一个ARP请求发进来,观察FPGA是否会自动回一个ARP应答,应答里的MAC地址是不是local_mac。这个测试通过后,上板成功的概率就有一半了。

6.3 上板验证的完整流程

上板后建议按以下步骤来:

  1. 先用网线把FPGA板子和PC直连,不要经过交换机,减少外部干扰。
  2. PC端设置一个静态IP,比如192.168.1.100,掩码255.255.255.0,和FPGA的local_ip(192.168.1.10)同网段。
  3. 在PC上用Wireshark监听网口,观察是否有ARP广播。
  4. 用ping命令ping 192.168.1.10,如果能通,说明ARP和ICMP的部分已经被协议栈处理了。
  5. 再用网络调试助手发送UDP数据到192.168.1.10:某个端口,FPGA内做一个简单回环,看PC能不能收到同样的数据。

到最后一步时,PC防火墙很可能会拦截UDP,发出去的数据能到FPGA,但回包PC收不到。我自己的习惯是先临时关闭防火墙或者添加ICMP和UDP入站规则,验证通过后再把安全策略改回来。

6.4 用ILA加探针做内部观察

Wireshark能看到外部结果,但看不到FPGA内部状态。遇到问题时,在Vivado里用ILA抓udp_tx_axis_tvalid、udp_tx_axis_tready、udp_tx_axis_tdata这些信号。如果你配置的ILA采样深度够大,还能看到完整的ARP交互时序。

注意ILA本身也会占资源、影响时序,抓高频信号时注意采样时钟选择。125MHz下ILA通常没问题,但如果你内部还跑了DDR4这类高速接口,要注意避免在走线拥塞路径上插探针后导致时序违例。

7. 实测中的典型问题与排查链路

这里把我在实际调试中遇到过的问题按现象分类,每个都给出完整的排查思路,不是直接给答案,而是告诉你我会怎么一步步下手。

7.1 现象一:PC ping不通FPGA

排查链路:

  1. 先用Wireshark抓包,确认PC是否发出了ARP请求。没发出,检查PC网卡和防火墙设置。
  2. ARP请求发出去了但FPGA没回。先用ILA抓MAC层rx信号,看数据有没有进入FPGA。
  3. rx有数据,但eth_arp没有响应。核对local_ip是否配置正确,ARP请求里的目标IP是否等于local_ip。
  4. 如果一切匹配但不回包,查一下PHY的link状态,百兆和千兆模式不要搞错,有时候PHY协商为百兆,但FPGA侧还是千兆收发逻辑。

有一次我花了一下午,最后发现是PHY芯片的复位引脚绑定错了,芯片一直处于复位状态,网口link灯都没亮。这种事很常见,先确认硬件层面的link状态,永远比在代码里苦找高效。

7.2 现象二:UDP能发不能收,或者能收不能发

先说"能发不能收"。这类问题多半在接收方向,重点检查udp_rx_axis_tready信号。如果你用户逻辑没有及时拉高ready,接收方向就会自动反压,暂时不接收新数据。长时间不拉高,FIFO一旦满,新的包就会被丢。还有一种可能是PC发出的UDP帧目的端口和FPGA内部配置的端口不一致,eth_udp_rx直接把包丢了。

再说"能收不能发"。重点查udp_tx_axis_tready是否被卡死,以及ARP缓存表是否查得到目的MAC。如果PC是刚换过IP或MAC,ARP缓存表里的旧条目会导致FPGA把数据发给一个已经不存在的MAC地址。把ARP缓存条目改小,或者每次发送前强制刷新缓存,可以缓解。

7.3 现象二加强版:回环数据差几个字节

这种情况经常发生在tlast的处理上。回环工程在接收端收到一个UDP包后,会原封不动地搬到发送端。如果接收状态机在tlast上提前停止搬运,或者发送状态机没有把最后一个字节当作tlast发出,就会出现数据差几个字节、甚至PC端报UDP长度不对。

解决方法是仔细核对收发两条路径上的tlast产生条件。接收方向应该是当eth_udp_rx输出tlast时,就认为一个完整的UDP载荷结束了;发送方向则应该把tlast和最后的数据字节对齐,不能把tlast时序拖后一拍。

7.4 现象三:时序收敛不过

网络接口的125MHz时钟本身不算快,但如果你把协议栈和用户逻辑直接揉在一起,组合逻辑路径很容易变长,导致时序违例。我见过有人在用户逻辑里做复杂判断,然后把结果直接接到udp_tx_axis_tvalid上,结果这一整条路径都参与了时序收敛,最后频率上不去。

建议是协议栈和用户逻辑之间务必加FIFO或寄存器级缓冲,把长组合逻辑控制在用户逻辑内部,不把关键路径暴露给125MHz网络时钟域。必要时看Vivado的时序报告,找到最差的路径,再决定要不要pipeline插一拍。

7.5 现象四:长时间运行后网络断流

这种问题最隐蔽,往往是FIFO溢出或ARP缓存过期导致的。最简单有效的办法是在设计里加统计计数器:收到多少包、发出多少包、CRC错误包多少包、FIFO上溢多少次、ARP缓存命中多少次。一旦出现断流,看计数器就能定位在哪一环丢包。

我之前在一个采集项目里,FPGA持续高速上传,运行十几分钟后上位机就收不到数据了。查计数器发现是接收端FIFO上溢次数在增长,原因是PC端应用程序处理慢,导致UDP包堆积。加了一个暂停发送的控制报文之后问题解决。这类问题不是说协议栈不稳定,而是整体流控没有做好,需要在FPGA侧设计"对端来不及处理就先停一下"的反压机制。

8. 跑通之后怎么扩展:从回环到自己的业务

能跑通回环,意味着你已经会用一个成熟的UDP协议栈IP核。这时候真正的项目才算开始。下面几个方向是我觉得从"跑通"到"会改"最值得尝试的扩展点。

8.1 自定义应用层帧格式

UDP的payload里怎么组织数据,完全由你说了算。我常用的做法是设计一个简单帧头:2字节帧类型、2字节数据长度、4字节帧序号、后面跟真正的数据。这个设计可以让接收端在没有额外传输层信息的情况下,也能完成数据分包和丢包检测。

比如你要做FPGA图像处理,把摄像头采集的一行图像数据用这种自定义格式打包,然后通过UDP发给PC。PC端只要根据帧序号拼接,就能把图像恢复出来。加上重传或请求机制之后,就变成了一个轻量级的可靠传输方案。

8.2 多端口与过滤规则

verilog-ethernet的接收端是支持端口过滤的,但如果你需要同时处理多个端口,最方便的做法是把udp_rx_axis的端口号信号引出来,在用户逻辑里做一个分发器。比如目的端口是5001时,数据进入图像缓存模块;目的端口是5002时,数据进入命令解析模块。

发送方向也类似,可以用一个简单的仲裁器,让多个用户模块共用udp_tx_axis,但要注意同一时刻只能有一个模块发起发送,否则会包内容错乱。仲裁最简单的实现就是轮询或优先级固定优先。

8.3 和DDR、图像处理、TDC这些项目结合时的注意点

当你把抓取的MIPI图像或TDC直方图数据塞进UDP时,带宽很容易成为瓶颈。千兆以太网的理论有效载荷带宽大约是950Mbps左右,扣掉帧开销、IP/UDP头之后,实际能用的带宽大约是940Mbps,但很多设计中实际只能稳定跑到700Mbps到800Mbps。如果图像分辨率高、帧率高,就要考虑压缩、ROI裁剪,或者直接上10G/25G以太网。

verilog-ethernet也包含10G、25G的MAC实现,但跑高速以太网通常还需要外接光模块和高速串行收发器,复杂度会显著提升。如果你之前看过我写的DDR4工程,会发现网络和DDR结合时最容易出现的问题是DDR带宽分配:写入是摄像头,读出是网络,两边抢DDR带宽。解决办法是给两段分配不同的缓存区,并且用轮询或优先级仲裁来决定DDR的读写调度。

8.4 继续深入的学习方向

把这个UDP工程跑通之后,我个人建议下一步不要急着做复杂的TCP协议栈,而是先做三件事:

  • 把回环例子里的每个状态机画成时序图,真正做到自己能在纸上推演状态跳转;
  • 自己动手改一个端口过滤规则,或者改ARP缓存表深度,再仿真验证行为变化;
  • 把工程从一个厂商芯片换到另一个厂商芯片上,比如从Xilinx移植到高云或易灵思,体验跨平台适配的坑,这能帮你更深刻地理解哪些逻辑是与厂商无关的、哪些是必须用原语替代的。

等这三件事做完,你对"FPGA怎么处理一个网络包"的理解会达到一个完全不同的层次。网络协议既不是天书,也不是玄学,它就是一堆状态机在处理字节流,而你现在已经亲手打开了这扇门。

最后再补充一句我自己的心得:FPGA学习最容易卡住的地方往往不是某个逻辑不会写,而是缺少一条贯穿的完整视角。从UART到DDR到以太网,每一次都是把这条线拉长一点。verilog-ethernet这个工程就是那个帮你把"用户逻辑"和"物理网口"完整串联起来的关键节点,啃下它,你对FPGA的信心会明显不一样。

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

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

立即咨询