十二 · 2023年08月02日

带丢包的缓存控制

✎ 编 者 按 

但凡牵涉到SmartNIC,都免不了牵涉到MAC IP。在网络设计里,MAC IP Rx端一般都是不接受反压的,随着报文类型对应的处理能力的不一样,在MAC收包处不可避免的存在报文缓存和丢包。

关于缓存与丢包,如何控制

先来谈谈一般像100G Mac 这种类似的IP的Rx的特点:

  • rx接口均为Flow类型(即只有valid而没有ready)
  • 一般带有sop、eop
  • 在eop处会指定报文是否有错误

既然不接受反压(没有ready),那势必需要在流量入口处设置缓存,并当缓存满时允许丢包。

由于在报文eop处可能存在错误,对于错误的报文,往往不会发往数据链路的下游,同时有时候下游会要求报文中间不能存在空拍(即要求整拍输出)。而一提到报文缓存,大多数人想到的就是FIFO,故在很多开源设计里,往往都用FIFO的形式来进行搭建:

image.png

一般会设计两路FIFO,一路用来存放数据,另一路用来存放报文的信息。对于两路FIFO的输入侧,一般大多数的设计思路是:

  1. 在报文SOP首拍时判断Data Fifo是否afull拉起(Msg FIFO一般设计的容量足够满足不会afull先于Data FIFO拉起),若拉起,则这一帧报文认为无法进行缓存,需丢弃,否则则这一拍报文在处理时,可以直接写入Data FIFO,无需再考虑afull信号。
  2. 在报文eop尾拍时,若该数据报文能压入Data FIFO,将报文是否有错误及一些其他信息压入Msg FIFO。

在两路FIFO的输出侧,一般的设计流程是:

等待两路FIFO均不会空

从Msg FIFO中判定要处理的报文是否存在Error,若存在Error,将报文从Data FIFO中取出直接丢掉,否则送往下游。

在报文EOP时,从Msg FIFO中读取出该报文。

流程没问题,用起来也没问题。那么接下来我们来分析下这里的设计是否能优化下。

对于进入的网络报文,Data FIFO从功能上来讲是一个严格的Package FIFO,一帧报文在sop时能缓存那么就意味着这一帧报文就一定能缓存,所以在设计afull时就需要考虑最大报文长度。以9600Byte为最大报文长度而言,当总线位宽是512 bits时,其缓存需要150个entry。那么在设计Data FIFO时其至少要在还剩下150个Entry没有使用时就拉高afull。FIFO本身的作用是吸收瞬时流量,设置如此大的afull阈值是否也就意味着极大的空间浪费?明显afull拉高时我们甚至有可能还能多缓存150个数据报文(如果这段时间恰好下游处理能力不足),而这些报文就这么丢弃掉了(当然你可以说我增大FIFO的深度不就完事儿了么,也没毛病,但空间浪费的情况始终存在)。

其次,为了避免向下游发送错误的报文,我们还需要专门维护一个Msg FIFO(当然也可能存储一些报文长度等类型报文,这里仅考虑存储是否报存在报文错误)。

如果从设计“精益求精”的角度,这里的设计能否“优化下呢?”

异形FIFO

回到上面的问题,之所以设置afull也好还是专门设置一个MsgFifo也好其都是因为写入FIFO的数据不能回退所导致的。当一帧报文在写入过程中写到一半发现FIFO满了这时候会导致有半包数据。当写到最后一拍数据发现有error标志又无法把这一帧报文给回退掉,故而采取上面的那种设计。

那么,如果FIFO能回退是否就解决了这个问题?

关于FIFO的原理,估计大多数面试官都可能会问到过,FIFO状态的维护说白了就是一个读指针(read_ptr)和一个写指针(write_ptr)。之所以不能数据回退,其原因在于没有维护之前状态的写指针所导致。

那么这里我们不妨维护一个能回退状态的指针即可:

image.png

这里我们用write_ptr来维护数一帧数据报文成功写入后的指针,write_ptr_tmp,read_ptr则用于表示正常FIFO的读写指针。只不过write_ptr_tmp相较于普通的FIFO有回退的功能。

对于写操作,为避免一帧数据报文在写操作过程中出现中间有一拍出现full而后消失,我们则需要维护一个wen_flag标志寄存器:

image.png

那么对于数据写使能的处理则可以表示为:

image.png

即当遇到full或者pkg_wen_flag为0或者error时wen均不使能,数据不不再写入RAM。

那么对于write_ptr_tmp,其处理逻辑为:

image.png

在一帧报文进入时,每成功写入一拍数据,则write_ptr_tmp就会加1,一旦出现数据无法成功写入,则其立即回退到write_ptr保存值(此时继以后的报文wen均会拉低),实现回退功能。

而对于write_ptr则更为简单了:

image.png

仅需要在报文的最后一拍判断是否写成功,如果写成功,那么则可以更新write_ptr。

至于读侧,则可以正常根据read_ptr,write_ptr来进行处理,天然的实现了整包输出的特性。

而为方便给用户提示报文是否写入成功,我们可以定义如下:

image.png

即判断最后一拍数据报文能否正常的写入即可。

如此,对于FIFO简单的一点改动,可以使我们充分压榨RAM的资源,避免因为设计而导致的本不必丢失的报文丢失,何乐而不为呢~

代码附录

最后,贴上完整的代码:

image.png

☆ END ☆

作者:玉骐
原文链接:Spinal FPGA
微信公众号:
 title=

推荐阅读

更多SpinalHDL技术干货请关注[Spinal FPGA]欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。
推荐阅读
关注数
1578
内容数
131
用SpinalHDL提升生产力
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息