问诸君,何人不曾遇到如此问题
从事工作至今,一直与FPGA打交道。其实无论是FPGA设计还是ASIC设计,数据的缓存总是绕不开的一个问题。而数据的缓存,基本大体上用的最多的就是RAM和FIFO了。
而对于FIFO,搞逻辑的人都不陌生,对于FIFO的使用场景,哪怕是十年的老司机碰到两个信号也是十分惊惧的:Overflow、Underflow。在整个的工程设计里,不管你对自己的设计多么有信心也要老老实实的在DFX中添加所有FIFO的这两个信号。而一旦真的出现了拉高,那不管你的设计经验多么丰富,也得老老实实去review代码和构建测试case。
一定要这样么
为什么经常碰到有人讲FPGA这些不好学?Verilog里可综合的语法就那么多(当然重要的是电路设计的思路)。不同于软件语言,逻辑设计程序里没有入口,而Verilog本身也是一个“低效率”的语言。由于逻辑设计牵涉到时序的概念,相较于软件代码,逻辑设计最大的不同在于除了要考虑功能性的逻辑实现外还要考虑到时序的实现。一个功能的实现往往大量的“无聊”代码都是用在凑时许。举个简单的例子。对于软件设计而言,读取数组中的一个元素只需要一行代码:
![](//file.elecfans.com/web2/M00/4E/E3/pYYBAGK9T8aATJsXAAAcHd8GtYs331.jpg)
而对于逻辑实现呢?对于较大块的数据,我们往往会例化一个RAM,将待读取的数据送到RAM的读地址,拉高读使能。而相较于软件代码的最大不同是我们要考虑时序!!!RAM的读有一拍的延迟,而由此带来的就是我们电路逻辑为了适配时序而不得不去做一些和设计功能“无关”的代码。这也是去阅读别人Verilog代码最痛苦的一点:代码中有太多跟时序相关的设计影响了对于电路设计功能的理解,尤其对于新手而言,去阅读别人的代码真是件“痛苦”的事情。
SpinalHDL中关于RAM的抽象与思考在前文已提到过,这里不再做额外赘述。那么同样,对于FIFO这类电路的出口时序和入口时序,其本质上也都属于握手的一种,这一点也就体现了SpinalHDL对于Stream接口抽象的便捷性。而对于日常的逻辑电路设计而言,出现FIFO Overflow,underflow的常见原因就是我们将ren、wen声明为寄存器信号,而在处理时序上的不当导致Overflow或者Underflow。为此引来的代价就是我们在Fifo中又引入了一个aempty信号和afull信号。这两个信号并不和功能有任何的相关性。你见过哪个软件设计人员在使用Queue时还会再定义一个快要满或快要空的信号标示呢。因为避免一个可能的错误我们引入了多余的信号,又因为引入多余的信号需要处理更近一步的将我们电路设计的初衷给淹没在大量的“凑时序”代码中。而在SpinalHDL中,功能电路和时序电路的分离让我觉得这才是设计逻辑电路的正确姿势。
StreamFIFO
如果你熟悉Axi4总线或者其他类似的总线,有一个概念相信你应该有了解。以Axi4总线AW通道为例,Master端口的aw_valid信号拉高时间节点设计时不应考虑slave端口aw_ready状态。在设计master端口时,当有任务需要向下下发时,我只需要将aw_vaild拉高就行了,而不必去看slave端口aw_ready的脸色。身为Master,就应该有Master的尊严,而不是去跪舔~
那么同样,FIFO是不是也是同样的逻辑。我想往FIFO中写入数据,我只需要告诉FIFO我有数据要给你,等你接受了给我一个标记就行了,何必要眼巴巴的看FIFO的状态,你能接收了我才赶紧给准备一个呢(当真是惯的)~
而上面的这种思想,和软件中的“同步读写”思路是很相像的,均伴随着阻塞的概念。而别忘记,在逻辑设计中由于时序的概念,天然的具备阻塞实现便捷性。
正式基于上面的这些思路(这种设计思想也是深得我心),SpinalHDL中的StreamFifo便由此而来:
无论是出口(pop)还是入口(push),均抽象为Stream接口,push接口为slave、pop接口为Master。而对于Stream接口而言,谁是Master谁当家作主。
这里给出一个example:
![](//file.elecfans.com/web2/M00/4E/E3/pYYBAGK9T8aAXLErAADLxN4F_XU012.jpg)
其时序表现为:
![](//file.elecfans.com/web2/M00/4E/48/poYBAGK9T8aAI-qzAACaljkzIbs539.jpg)
数据从push端口输入到pop端口输出有2个cycle的延迟:一拍写周期、一拍读延时。
StreamFifoLowLatency
在某些场景下,这两拍的lantency是不可接受的:当fifo为空时,若某个cycle中有数据push进fifo,那么同时需要立即显示在Pop接口,那么此时可以选择StreamFifoLowLatency:
![](//file.elecfans.com/web2/M00/4E/48/poYBAGK9T8aAYPKPAADklyvVSlY493.jpg)
其时序模型为:
![](//file.elecfans.com/web2/M00/4E/E3/pYYBAGK9T8aAY-fZAACpycX2dfM931.jpg)
至于里面的设计原理,建议自己去翻看一下源代码,很简单,重要的是将它封装成标准的组建,减少我们真正“业务代码”中“凑时序”的“垃圾”代码。
原作者:玉骐
|