news 2026/9/18 6:41:16

FPGA实现UDP以太网通信:从verilog-ethernet开源工程到上板回环实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
FPGA实现UDP以太网通信:从verilog-ethernet开源工程到上板回环实战

这个系列写到第10篇了,前面的实验基本都是数码管、串口、IIC、SPI这些基础玩法。这一篇我决定换口味,给自己的目标是:在FPGA里跑通一个能和PC真机通信的UDP以太网通路。刚开始也尝试过自己从零写UDP协议栈,写了几天就卡在IP头校验和、ARP交互这些协议细节上,后来换了思路,先把开源工程verilog-ethernet啃下来,再基于它做裁剪和回环验证。verilog-ethernet是Alex Forencich维护的开源FPGA以太网项目,里面是完整的MAC、IP、ARP、UDP协议栈代码,模块划分非常干净。这篇笔记就按我自己的学习顺序,记录怎么理解PHY/MAC关系、怎么读代码,以及怎么把工程跑上板子并和PC互通的完整过程,适合和我一样处于近似0基础阶段的FPGA学习者。

1. 学这个工程之前,建议先想清楚这三件事

1.1 MAC、PHY、媒体接口,第一课到底该补什么

很多初学者拿到以太网工程,第一反应是去找“以太网帧格式”,然后就盯着目的MAC、源MAC、类型字段看。这些当然重要,但如果你连板子上那颗PHY芯片是干什么的都没搞明白,后面接引脚和调时序会非常痛苦。

FPGA不是直接把网线变压器出来的差分信号接进可编程逻辑里处理的,开发板上一般会有一颗PHY芯片,常见的有瑞昱的RTL8211系列、Marvell的88E1512系列等。这颗PHY负责物理层的活:线路编码、时钟恢复、自协商、载波侦听,它把物理线路上的模拟/串行信号转成MAC层能直接吃的数字接口。FPGA里跑的是MAC层以及MAC之上的IP、ARP、UDP协议逻辑。

MAC和PHY之间的数据接口常见有三种:GMII、RGMII、SGMII。GMII是8位并行数据加时钟,千兆下时钟125MHz;RGMII用DDR双沿采样,引脚更少;SGMII则是通过SerDes串行收发,通常需要FPGA内部的GTP/GTX等高速收发器来实现,比如Xilinx 7系列里的GTX。verilog-ethernet里专门有eth_mac_1g这样的千兆MAC核,也有支持SGMII的示例工程。你把“MAC”理解成负责组帧、算CRC、按包收发,把“PHY”理解成负责把MAC给的数据变成能上线的电信号,这条主线就清晰了。

1.2 为什么FPGA跑协议栈用的是数据流思维而不是回调

写单片机或上位机程序时,网络通信往往是“调用一个socket API,内核去处理,然后回调通知你”。FPGA里没有这种“回调”概念,代码风格几乎全是数据流。你给一个模块喂字节流,它给你吐字节流,中间用AXI-Stream这种接口握手机制贯穿。

AXI-Stream有几个最关键信号:tvalid和tready表示握手,tdata是数据,tlast表示一帧结束,tkeep表示当前拍tdata里哪些字节有效,tuser则用来携带错误标志等附加信息。verilog-ethernet里几乎所有模块边界都是这套接口。比如接收链路,PHY/MAC把一帧数据转成AXI-Stream流,IP层从流里解析出头字段,UDP层再把载荷通过AXI-Stream送给用户逻辑。

所以学这个工程之前,我强烈建议你先写一个简单的AXI-Stream FIFO,把tvalid/tready/tlast/tkeep这套握手玩熟。否则你打开udp_complete_rx源码时,满屏都是if (s_axis_rx_tvalid && s_axis_rx_tready)这类条件,会看得很晕。

1.3 你的板子适合用GMII、RGMII还是SGMII

verilog-ethernet官方示例里常见的是SGMII方向,因为Xilinx开发板很多用GTP/GTX实现SGMII,再接外部PHY或者光模块。但如果你手上的板子是国产或者入门级开发板,PHY接口往往是GMII或RGMII,比如黑金的AX7010、正点原子的开拓者系列,很多板载PHY都是RTL8211系列,走RGMII或GMII。

我建议你先打开自己开发板的原理图,找到PHY那一页,确认FPGA与PHY之间的接口是什么。如果是RGMII,就去看仓库里的rgmii_phy_if相关模块;如果是GMII,就直接用eth_mac_1g搭配GMII接口;如果是SGMII,再去参考官方给Xilinx 7系列做的example。这里最容易踩的坑是:照着别人的SGMII example抄,结果自己板子根本没有GTP/GTX引脚引出,或者参考时钟都不对,导致完全跑不起来。

2. verilog-ethernet这个仓库到底放了什么,该先读哪几个文件

2.1 rtl目录的核心模块分层

打开verilog-ethernet仓库,不要先急着看example,建议先看rtl目录。核心模块大概可以分成下面几层。

模块层级作用
eth_mac_1gMAC千兆MAC核,处理前导码、帧间隙、FCS校验
eth_axis_rx / eth_axis_tx接口适配把MAC收发的数据转成AXI-Stream
eth_arb_mux发送仲裁多路以太网帧发送仲裁
ip_eth_rx / ip_eth_txIP层处理IPv4帧解析和封装
ip_complete_rx / ip_complete_txIP层对用户提供完整的IPv4收发接口
udp_checksum_gen / udp_checksum_calc校验和UDP校验和生成与校验
udp_complete_rx / udp_complete_txUDP层对用户提供完整的UDP收发接口
arp_cache / arp_eth_rx / arp_eth_txARP层ARP缓存与请求/应答处理
axis_fifo / axis_fifo_adapter基础设施AXI-Stream FIFO和位宽适配,在lib/axis下

一开始我也试图从eth_mac_1g开始看,结果发现它要处理CRC32、流控、帧间隔这些东西,细节特别多,很容易陷进去。后来调整顺序,直接从udp_complete_rx和udp_complete_tx入手,因为它们是把UDP收发封装好的“最接近用户”的模块。看完这两个之后再往下钻到ip_complete层,最后再去翻eth_mac,整个层次感会清楚很多。

2.2 建议先读udp_complete,再往下钻

udp_complete_rx和udp_complete_tx本质上是把IP层、UDP层的一些拆包组包细节封装成一个用户友好的接口。用户侧只需要关心AXI-Stream的数据、有效信号、帧结束信号,不需要手动计算IP头长度、UDP长度、校验和。

读udp_complete_rx时建议关注两个点:一是它内部例化了什么子模块,二是它的数据流是怎么走的。你会看到它内部调用了ip_complete_rx、arp缓存、可能的校验和模块等,这些调用关系本身就是一张很好的协议栈结构图。读udp_complete_tx时关注它是如何把用户发来的数据拼成完整IP帧的,以及它为什么需要在发送前查ARP缓存。

如果一上来就对着eth_mac_1g里面的CRC表死磕,可能一个星期后你还在调前导码和CRC,这对以“跑通UDP”为目标的初学者来说性价比很低。

2.3 最小引脚清单:时钟、复位、AXI-Stream、PHY接口

从顶层往下看,一个最小的UDP收发链路,用户侧至少关心这些信号:

  • 时钟clk,一般就是MAC接口时钟或经过PLL处理后的逻辑时钟;复位rst_n,低有效,要确保同步释放。
  • 发送侧用户接口:s_axis_tx_tdata / tvalid / tready / tlast / tkeep,把要发出去的数据送进去。
  • 接收侧用户接口:m_axis_rx_tdata / tvalid / tready / tlast / tkeep,从协议栈读数据。
  • PHY侧接口:根据你用的是GMII还是RGMII,会有TXD、RXD、GTXCLK、RXCLK等引脚。如果走SGMII,还要把GTP/GTX的参考时钟和收发方向引脚接出来。

很多初学者把注意力全放在数据总线上,忽略了PHY接口和时钟复位,结果上板后数据线根本没波形。其实调试时可以用逻辑分析仪或者ILA先抓PHY侧的RXDV和RXD,确认物理链路是不是通的,再往上查IP层和UDP层。

3. 接收链路逐段拆解:从网线到用户手上的UDP数据

3.1 MAC适配层如何把帧变成AXI-Stream

接收方向的第一步,是PHY把线上信号恢复成MAC层数据,然后eth_mac_1g做前导码检测、FCS校验,再通过eth_axis_rx把以太网帧打包成AXI-Stream流。这层要关注几个关键点:eth_axis_rx会把一帧数据以tlast标记结束,如果CRC错误,它会把tuser拉高。这样上层协议模块看到错误帧时可以选择丢弃。

这就是为什么读verilog-ethernet时不能跳过AXI-Stream握手原因。比如IP解析模块可能正在等一整帧数据过去,只有等到tlast才知道帧结束了,然后根据长度字段判断IP头里声明的数据长度和实际收到的帧长度是否一致。如果这一帧中途被CRC错误打断,tuser会告诉上层这个帧无效。

3.2 IP层到底做哪些合法性检查,不是一上来就找UDP

以太网帧里的EtherType字段是0x0800表示IPv4,0x0806表示ARP。ip_eth_rx在拿到一个帧后,第一步是看EtherType,不是IPv4的帧直接丢给其他模块处理。

如果是IPv4,IP层会检查版本号是不是4,IHL是不是标准20字节,目的IP是否等于本机配置的IP,以及是否广播地址。然后它会计算IP头校验和,如果校验和不通过,直接丢弃。接着看protocol字段,如果是17就交给UDP层,如果是1就交给ICMP模块,比如ping用的就是ICMP,这就是为什么“能ping通”和“UDP通”是两个不同层次的验证。

这里有个容易忽略的点:IPv4分片。PC默认情况下如果UDP包太大,IP层会把数据分片,而verilog-ethernet这种硬件协议栈通常不做分片重组。所以我们在PC端发送UDP包时,尽量把数据长度控制在1472字节以内,也就是MTU减掉IP头和UDP头,否则FPGA端可能只会收到第一个分片,后面分片会被丢弃或行为异常。

3.3 UDP层如何匹配端口并输出负载

udp_complete_rx在IP层之上,它做的主要事情有:解析UDP头里的源端口、目的端口、长度、校验和;检查目的端口是不是本模块配置的端口号,如果匹配,就把UDP载荷部分通过AXI-Stream输出给用户。

UDP数据包头固定8字节,用户拿到的数据是从UDP头之后开始的。UDP长度字段的值是8字节头加上载荷长度,校验和计算时用的是伪头部,这部分我在第5节会详细展开。端口匹配不上的包,协议栈一般会直接丢弃,不会给你产生中断或者报警,所以调试时经常出现“明明收到包了,用户接口就是没数据”的情况,这时候先检查端口号配置对不对。

3.4 接收侧的tuser和tlast为什么要处理

我见过一些初学者在回环代码里只接了tdata、tvalid、tready,没接tlast和tuser,结果数据偶发错乱。原因是UDP包数据长度不一定是8字节的整数倍,接收侧最后一拍可能只有几个字节有效,必须通过tkeep判断;而tlast告诉你这一帧到这里就结束了,用户逻辑才能对一帧数据进行处理。如果忽略tlast,回环可能会把两帧数据糊在一起,变成一个大包再发出去,PC端解析就会错乱。

tuser在接收侧通常表示这一帧是否带错误标记,特别是CRC错误,严谨的做法是收到错误帧时把当前FIFO里的数据清掉,不要送去上层的回环发送。verilog-ethernet内部很多模块已经做了错误丢弃,但你自己写的FIFO或用户逻辑不一定做,上板后有时会出现“PC收到错误数据”的诡异现象,原因往往就在这一层。

4. 发送链路逐段拆解:用户数据如何变成以太网帧

4.1 udp_complete_tx的发送顺序与用户接口

发送方向比接收方向更符合直觉:用户只需要把UDP载荷写到发送接口,udp_complete_tx会按照以太网帧的格式,依次生成目的MAC、源MAC、EtherType、IP头、UDP头,然后把用户数据填进去,最后加上FCS,交给MAC层发送。

但有一个点很容易忽略:发送侧不是“你写一个寄存器,它立刻发一帧”,而是你要把一帧完整的数据通过AXI-Stream接口“喂”给它。也就是说,你的用户逻辑要先把数据拼好,然后拉高tvalid,等tready握手,持续输出,直到tlast结束。

我在回环设计里就是先接一个FIFO,接收侧写进来的数据缓存到FIFO里,发送侧则持续读FIFO,读到FIFO末尾时拉出tlast。这样即使PC端发送的数据长度不是4字节或者8字节的整数倍,FIFO加tkeep的逻辑也能正确处理,不至于出现长度字段和实际载荷对不上。

4.2 为什么第一次发包会有“延迟”:ARP缓存

以太网帧的目的MAC地址是要填真实MAC的,但我们的UDP协议栈只知道目的IP,怎么知道PC的MAC地址?答案是ARP。

udp_complete_tx发送前会查ARP缓存,如果缓存里没有对应目的IP的MAC地址,它不会立刻发UDP,而是先发出一个ARP请求,等PC返回ARP应答,学习到MAC地址后,再把缓存的UDP包发出去。所以FPGA侧第一次向PC发包时会出现几十毫秒甚至更长的延迟,这不是死机,是ARP流程。

这也是为什么调试时先ping一下FPGA会对后面的UDP测试有帮助。PC ping FPGA时,先发出ARP请求,FPGA回复ARP应答,同时ARP缓存里就学到了PC的IP和MAC对应关系,之后UDP发送时就不需要再等ARP了。反过来,FPGA先主动发UDP,也会一样触发ARP请求,只是第一次会因为等待ARP而稍微慢一点。

4.3 多模块竞争发送时谁先发:response仲裁

UDP发送、ICMP回复、ARP回复,这些模块最终都要往同一个MAC发送方向上去发数据,所以verilog-ethernet里会有eth_arb_mux这样的模块来做仲裁。它的逻辑并不复杂,本质就是一个优先级判断,同一时刻只允许一路数据占用发送通道。

刚开始学习时不需要太深入仲裁细节,但你需要知道这个模块存在。因为如果你自己在顶层例化了ARP、IP多个发送源,却没有经过仲裁,直接把若干路信号接在一个总线上,FPGA内部会出现多驱动冲突,轻则仿真报错,重则布局布线异常。verilog-ethernet的架构值得学的一点就是“每个发送源先各自保持完整帧,再通过仲裁器决定谁上总线”,这样整个代码结构非常干净。

5. 校验和计算:最容易被小白写错的部分

5.1 IP头校验和与UDP伪头部的区别

IP头校验和只覆盖IP头本身,也就是20字节。每一个16位字相加,高16位溢出回卷,最后取反。而UDP校验和覆盖的范围更广,它要算一个12字节的伪头部,加上UDP头8字节,再加上UDP载荷。

UDP伪头部格式是:源IP地址4字节、目的IP地址4字节、0x00一个字节、协议号0x11一个字节、UDP长度2字节。这个伪头部并不会出现在真实在线路上传输的报文里,它只是用于校验和计算。很多初学者第一次写校验和模块时,只把UDP头和数据加起来算,忘了伪头部,导致协议栈发出的包在PC端Wireshark里显示checksum incorrect。

verilog-ethernet里udp_checksum_gen和udp_checksum_calc这两个模块分别负责生成和校验。读这两个模块的源码,基本就能把校验和的硬件实现逻辑搞明白。

5.2 累加器实现的关键:溢出回卷

计算校验和时,最容易写错的不是“按位取反”,而是“高16位回卷”。简单写法是每次加完一个16位数,如果产生了第17位,就把它再加回低16位,直到没有进位为止。如果直接用sum <= sum + data16然后截断低16位,算出来的校验和是错的。

verilog-ethernet里的实现一般不会写成串行循环,而是用流水线方式处理,因为串行循环做不了高带宽。但对初学者来说,先把“回卷”这个逻辑理解清楚更重要。

// 一段示意代码,仅说明回卷逻辑 reg [16:0] sum_tmp; always @(*) begin sum_tmp = {1'b0, sum} + {1'b0, data16}; if (sum_tmp[16]) begin sum = sum_tmp[15:0] + 16'd1; end else begin sum = sum_tmp[15:0]; end end

这段只是演示思路,真正的流水线实现还要考虑多拍延迟。重点在于理解:校验和的载体是16位,但累加过程要保留进位并回卷。

5.3 校验和为0不是错误,千万分清

IPv4规定UDP校验和可以不计算,把校验和字段填成0x0000。接收端看到0时,会跳过校验,不认为这是一个错误。而IPv6里UDP校验和是强制的,必须计算。

所以调试时如果Wireshark显示“UDP checksum: 0x0000”或者在FPGA端看到收到的UDP包校验和字段为0,不要慌,这是合法的。反过来,如果你希望FPGA端认真校验数据完整性,就需要在udp_complete_rx里配置成强制校验非0校验和;如果你只是做简单回环,可以先接受校验和为0的包,这样能少踩一个坑。

发送端也是一样,如果你图省事不把UDP校验和计算模块接上去,发出去的包校验和就是0,PC端Wireshark会提示“not set”,但PC应用程序仍然能收到数据。不过从学习协议栈的角度,我还是建议把udp_checksum_gen接进去,因为这才是完整的UDP实现。

6. 上板实践:把最小UDP回环跑通并与PC互发

6.1 选官方example还是自己搭:引脚和时钟约束

官方example大多基于Xilinx 7系列,例如Artix-7开发板的SGMII示例。如果你的板子和example用的板子一模一样,可以直接打开Vivado工程生成比特流。但实际上很多人板子不一样,直接跑example基本都会因为引脚约束不对而失败。

我实际走通的路径是:不直接用example的顶层,而是把rtl目录下需要的源码复制到自己的工程里,然后根据自己的开发板引脚重新写约束文件。虽然多花了一点时间,但这样做的好处是你必须把每个引脚的功能搞清楚,而不是“碰运气”式地点个Generate Bitstream。

时钟方面,SGMII示例一般需要GTP参考时钟,比如125MHz的gtrefclk,还要给GTP的复位逻辑足够的时间。如果是GMII/RGMII接口,时钟会更直接,比如GMII的GTXCLK由FPGA给PHY提供125MHz,RXCLK由PHY给FPGA提供125MHz。上板前先用逻辑分析仪确认PHY的RXCLK存在,不然后面都是白调。

6.2 回环逻辑怎么接:接收端到发送端之间必须过FIFO

最简单的回环设计就是:接收侧udp_complete_rx输出的数据,经过一个FIFO,再接到发送侧udp_complete_tx的输入。为什么中间必须加FIFO?因为接收侧输出的节拍和发送侧能接收的节拍不一定完全匹配,比如发送侧可能因为ARP缓存未命中而暂时反压,如果中间没有FIFO缓冲,接收数据就会丢。

FIFO的读侧要正确处理tlast。我的做法是:当FIFO读出一个标记有tlast的数据时,把它作为发送侧的tlast信号一起拉出;同时如果在接收侧看到tuser错误帧,就把FIFO复位,把这一帧扔掉。

端口配置上,入门阶段先固定住源IP、目的IP、源端口、目的端口。比如PC IP设为192.168.1.20,FPGA IP设为192.168.1.10,UDP端口固定为6000。PC发包时源端口也填6000,这样FPGA回环时直接把源端口和目的端口交换,数据就能回来。更通用的动态端口配置可以后面再做,但第一步先固定参数,通路跑通最重要。

6.3 PC端验证:静态IP、Wireshark、Python发包

PC端把有线网卡IP改成和FPGA同网段静态IP,避免DHCP干扰。然后用一条网线直连FPGA开发板和PC,先ping FPGA的IP。如果ping得通,说明ARP、MAC、PHY底层链路都通了。

拿到ping通的结果后,再用Python发一个UDP包测试回环:

import socket s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.settimeout(3) s.sendto(b'hello fpga udp', ('192.168.1.10', 6000)) data, addr = s.recvfrom(1024) print(data, addr)

如果回环正常,recvfrom会把那个UDP包原样收回来。Wireshark可以同时开着,过滤条件写udp.port == 6000 || arp,重点看几件事:有没有ARP请求和应答、UDP包有没有被标记校验和错误、包长度是否正常。

6.4 如果ping通但UDP不通,下一步查什么

我调试过程中最常遇到的场景就是ping通了,但是UDP回环收不到数据。这时候我一般按顺序排查:

  • 检查udp_complete_rx端口匹配逻辑,尤其目的端口是否配置成6000。
  • 检查PC发的UDP包长度,是否超过1472字节导致分片。
  • 检查回环FIFO的读侧是不是忘了把tlast送给发送端,导致发送侧一直等不到帧结束。
  • 检查udp_complete_tx的目的IP和目的MAC是否配置正确。如果先用ping让ARP学到PC MAC,发送效率会高很多;如果没学到,就观察它有没有先发ARP请求。

实际上,UDP不通的问题八成出在“端口没对上”或“FIFO控制逻辑不完整”,不太可能是协议栈本身的问题。开源工程本身是经过大量验证的,先怀疑自己的胶水逻辑,能省很多时间。

7. 我在这个工程里真实踩过的几个坑和排查思路

7.1 复位亚稳态:为什么你的板子总是第一次不通

FPGA里几乎所有模块都用低有效复位rst_n,但复位信号的释放时机如果不同步,器件很容易进入亚稳态,表现出来就是“上电第一次运行不正常,按一下复位键又好了”。

我之前就是直接把按键信号当全局复位用,结果每次上电后PHY那边已经完成配置,而协议栈内部还没稳定,第一次发包必然失败。后来把两级同步器加到复位通路上,并且用一个计数器产生稳定后的复位释放信号,才算解决。verilog-ethernet内部模块大都带有自己的同步逻辑,但你自己写的用户侧复位逻辑同样要处理好,不能只靠模块内部。

这个坑比较隐蔽,因为仿真时复位都是理想信号,上板后真实环境里会有毛刺和亚稳态。如果你也遇到“仿真全对,上板时好时坏”,第一反应先查复位。

7.2 时钟不对:SGMII恢复时钟不能直接当用户逻辑时钟用

SGMII接口下,接收方向的数据时钟来自GTP/GTX的恢复时钟,它的频率和相位跟发送端参考时钟不一定是完全同步的。如果你把所有逻辑都用同一个恢复时钟,后续跨时钟域设计就会出问题。

更合理的做法是:把GTX恢复时钟作为RX侧数据通路时钟,把用户逻辑统一放到另一个稳定时钟域里,中间用异步FIFO过渡。verilog-ethernet示例里通常会在接口处做时钟管理,但你自己写顶层时不要想当然地让所有模块共用一个时钟。GMII/RGMII接口相对好一点,RXCLK由PHY提供,和用户时钟的关系也要根据PHY芯片手册处理。

7.3 仿真过但上板丢包:先查tkeep、tlast、FIFO深度

仿真环境下数据都是规整的,比如你总是发64字节的整数倍数据,tkeep的问题根本暴露不出来。但真实CPC应用里,UDP包长度可能是100字节、500字节这种非8字节整数倍数,最后一拍tkeep就不是全有效,如果用户逻辑没有按tkeep有效字节写入FIFO,最后一个字节会错位。

另外,回环场景看起来简单,其实存在背靠背突发数据的可能。比如PC一下子连续发多个UDP包,如果接收侧和发送侧之间只有一个很浅的FIFO,并且发送侧因为ARP或其他原因反压,就会丢包。后来我把FIFO深度加大到1024,连续突发几百个包也稳定了。所以不要小看FIFO深度,它是一个典型的“仿真永远发现不了,上板压测就露馅”的问题。

7.4 工具链问题:老版ModelSim跑不了SystemVerilog测试平台

verilog-ethernet仓库里的testbench很多是用SystemVerilog写的,语法更现代,接口更简洁。如果你还在用老的ModelSim版本,尤其是某些入门套件自带的ModelSim Intel FPGA Starter Edition,很可能在编译testbench时直接报语法错误。

我当时的做法是直接用Vivado自带的仿真器跑,或者把测试平台里用到的SystemVerilog特性改成Verilog-2001风格的临时测试文件。如果你只是想快速验证自己的回环逻辑,也可以不跑官方的完整tb,而是用自己构造的简化激励,给udp_complete_rx输入一个合法的UDP帧,看用户接口是否有数据输出。这样既避免工具链问题,也更容易定位问题。

最后再分享一个小技巧:调试这种多模块级联的网络工程,一定要养成“分层验证”的习惯。第一步在PC上用Wireshark抓ARP,确认PC和FPGA物理链路通;第二步ping,确认IP层和ICMP回复通;第三步发UDP小包,确认UDP层和用户逻辑通。每一步都只验证一个边界,不要指望一次把整个链路全调通。我在做回环实验时发现,很多网上代码为了省事不处理tkeep和tlast,遇到非8字节整数倍的包就会卡死或长度错误,所以回环逻辑里一定要以tlast作为一帧结束的边界,而不是按固定长度去截。把这条链路走通之后,你会发现后面再做FPGA图像传输、高速接口之类的项目,心里会踏实很多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 6:39:58

海外试玩推广渠道全汇总:Playable Ads投放实操

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 6:37:14

Ferrers图像与整数分拆:组合数学的可视化工具

1. Ferrers 图像与整数分拆的直观理解第一次接触Ferrers图像时&#xff0c;我被这种用点阵表示数字分解的方式惊艳到了。想象你手上有5颗糖果要分给几个小朋友&#xff0c;可以全给一个人&#xff08;5&#xff09;&#xff0c;或者分成23&#xff0c;甚至11111——每种分法对应…

作者头像 李华
网站建设 2026/9/18 6:29:56

肌电图临床判读四层逻辑与神经肌肉诊断决策链

简介&#xff1a;本资源是一份面向神经科医生、康复医师、物理治疗师及医学生等临床与科研人员的《肌电图操作常规》专业指导文档&#xff0c;系统解决肌电图&#xff08;EMG&#xff09;与神经电生理检查标准化实施难题。全文共六章&#xff0c;覆盖检查前申请规范、针极/单纤…

作者头像 李华
网站建设 2026/9/18 6:28:46

HFSM分层有限状态机实战:事件流、优先级与历史恢复

HFSM分层有限状态机这个坑&#xff0c;我是在做第三人称动作游戏的角色控制器时踩进去的。七种角色状态&#xff1a;待机、跑步、攻击、翻滚、受击、死亡、跳跃&#xff0c;用扁平FSM硬写&#xff0c;switch-case堆到五百行之后&#xff0c;加一个新状态就要回改三个旧状态。后…

作者头像 李华
网站建设 2026/9/18 6:28:41

DeFi利率计算的形式化验证与安全防护实践

1. 项目背景与核心价值去年某知名DeFi平台因利率计算漏洞导致上亿美元资产面临风险的事件&#xff0c;让整个行业意识到传统审计手段的局限性。这个项目正是针对DeFi领域最关键的利率计算模块&#xff0c;构建了一套形式化验证的自动化防护体系。我参与过多个DeFi项目的安全审计…

作者头像 李华