在FPGA高速数据流设计中,FIFO是最常用的缓冲单元。但传统FIFO有个致命盲区:它只管"先进先出",却不管"包完不完整"。想象一下,网络数据包写到一半,读取端就开始往外吐数据——下游接收到的只是个"碎包",协议解析直接崩溃。
今天我们来实现一个带包边界保护的AXI-Stream FIFO模块(axis_fifo_packet),用硬件逻辑来确保每一个数据包的完整性。
一、为什么需要包边界保护?
在AXI-Stream总线中,数据以"包(Packet)"为单位传输,用tlast信号标记包尾。但在实际场景中,上游可能因为流量突发只发了半个包,或者下游背压导致数据滞留。如果FIFO不具备包边界感知能力,就可能把不完整的数据包暴露给下游,造成:
协议解析错误:以太网帧头还在FIFO里,帧尾没写完,下游已经开始解析
数据混淆:两个包的数据在边界处粘连,无法区分
系统崩溃:视频流的一帧图像被截断,解码器直接花屏
这个模块的核心设计目标就是:在tlast到达之前,读取端绝不输出该包的任何数据。
二、架构解析
模块采用经典的"块RAM + 输出寄存器"两级流水线结构:
第一层:块RAM存储
数据写入时,连同tlast一起打包存入RAM。这里用了一个关键技巧——包计数器(Packet Counter)。
每次写入端出现tlast,写包计数器packet_wr_cnt_r加1;
每次从RAM读取数据到输出寄存器时,如果旧数据的tlast为1,读包计数器packet_rd_cnt_r加1。
第二层:输出寄存器(Look-ahead)
输出寄存器提前从RAM预取数据,当下游握手时直接输出,消除RAM读取延迟。但这里有一道"门禁"——内部有效信号inter_s_axis_tvalid必须同时满足三个条件:
FIFO非空(有数据可读)
有至少一个完整包(写包计数 ≠ 读包计数)
不是"最后一个包的包尾"(防止多读)
条件2实现包边界保护,只要上游还没发完一个包(没给tlast),写包计数就不会增加,读取端就会被牢牢锁死。
三、应用场景
这个模块特别适合以下场景:
网络通信
处理以太网帧、IP数据包时,必须保证帧完整性,防止MAC层收到残帧。
视频/图像传输
一帧图像由多行数据组成,tlast标记帧结束。如果FIFO输出半帧,显示控制器会直接罢工。
雷达/通信基带
突发脉冲数据包必须原子性传输,任何截断都会导致信号处理算法失效。
DMA与存储控制器
在数据块搬移时,确保每个事务(Transaction)完整交付,避免地址越界。
四、仿真验证
测试平台设计了9个严苛的测试用例,覆盖了实际工程中可能遇到的各种 corner case:
Test 1 & 2:基础与边界保护
先写一个12拍的大包,验证数据和tlast时序正确;再故意写15拍不带tlast的"碎包",确认读取端零输出——直到补全第16拍tlast后,整包才允许读出。这是包边界保护的直接证据。
Test 3:多包连续传输
连续写入3个8拍的数据包,验证包与包之间不混淆,每个包的tlast都精准出现在末尾。
Test 4:FIFO满保护
连续灌入32个单拍小包填满FIFO,第33个写入被tready=0拒绝,读出时数据零丢失。
Test 5 & 6:流水与长包
边写边读18拍数据包,验证流水不中断;25拍长包持续传输,确认tlast只在最后一拍出现,中途绝不提前拉高。
Test 7:背压测试
写入20拍数据后,故意拉低下游ready 5个周期。FIFO像弹簧一样吸收压力,恢复后数据完整输出,没有任何 beat 丢失。
Test 8:超大包极限
30拍超大包接近FIFO深度32的极限,验证在容量边界处的正确性。
Test 9:混合包长
10拍和15拍两种长度包混合写入,测试不同长度包共存时的边界保护能力。
五、如何获取?
关注公众号,我是小灰灰的FPGA,关注我,带你从入门到精通数字逻辑设计。
后台回复(建议复制粘贴):axis_fifo_packet
140