本篇讲解一个小的example,带你一起看点儿SpinalHDL里有趣地玩儿法。
不讲理—总线插队
先来看一个波形图:
这里有一组输入Stream总线接口、一组输出Stream总线,它的电路规则描述是:
- 输出总线与输入总线之间有一拍的延迟
- 输出总线在输出输入总线第一拍结果之前先输出输入总线第一拍有效数据的高4bit与低4bit之和。
简而言之,输出总线io_dataOut原本只需要将输入总线io_dataIn 做一拍总线握手延迟即可但现在需要在输出io_dataIn* 之前插入一拍数据先输出。
insertHeader
在SpinalHDL里,这种类似AXI4的Stream总线可以表述成Stream(fragment
):
val io=new Bundle{
val dataIn=slave Stream(Fragment(UInt(8 bits)))
val dataOut=master Stream(Fragment(UInt(8 bits)))
}
而针对Fragment类型,SpinalHDL里内置了insertHeader函数:
通过insertHeader函数,我们可以在输出第一拍结果之前插入一个指定的值或者一组指定的值先输出。这里我们拿insertHeader(Header:T)函数来分析:
在这里声明了一个寄存器waitPacket,初始值为高电平,上游信号pimped总线的ready信号为下游总线ready信号为高,waitPacket信号为低时方拉高。当waitPacket信号为高时,下游总线ret信号的fragment为header值,为低时则为上游信号值。当下游总线发射一拍信号后waitPacket信号拉低,当发送一帧总线的最后一拍时重新拉高waitPacket。
可以看出,整体的设计思路并不难,我们用Verilog也可以很容易实现,但SpinalHDL好处在于能够将功能封装抽象成库函数供我们快速调用而不是每次都要实现这些。
对于初学数字电路设计的小伙伴来讲,还是自己写一个比较好,先别想着去调库函数,要先培养自己的数字逻辑思维。
开始优雅地插队
这里真正的代码实现只有两行:
- 第9行代码声明一个sum变量。
- 第十行代码计算输入总线dragment域高4位与低4位的和。之所以调用sundivideIn函数在于方便调整(若需求改了按两比特依次求和这里也很容易去修改)。第十行完成输入总线header插入(insertHeader),总线打拍握手交互(m2sPipe)。
至此,插队完成!而开篇的波形图即使这段代码的仿真。
END
作者:玉骐
原文链接:https://mp.weixin.qq.com/s/dshAPmS53HGEGmMsZVK6oQ
微信公众号:
推荐阅读
更多SpinalHDL技术干货请关注Spinal FPGA专栏。