三两行代码实现一个模块,让工作轻松一点儿不好么。
小伙伴的问题
下午微信群里有个小伙伴问了这么一道题:
- 将一个为UInt(128 bits)的Stream接口连接到一个UInt(32 bits)的StreamFiFo上,在SpinalHDL里有没有什么好的方式实现。
熟悉数字逻辑电路的小伙伴想必都不陌生,其本质上是一个接口位宽转换,在实现时无非用位宽转换FIFO或者自己在入口做一个简单的位宽转换,进来一拍有效数据分成4拍放入FIFO里~
若自己的工作里遇到这个需求,熟悉Verilog的小伙伴们是否已经迫不及待的开始动手写了呢~
你先写着,我让子弹飞一会儿.
三行代码实现一个功能
这个需求在SpinalHDL里只需要三行代码(接口的声明不算~):
case class example5() extends Component{
val io=new Bundle{
val dataIn=slave(Stream(UInt(128 bits)))
val dataOut=master(Stream(UInt(32 bits)))
}
val dataAdapt=Stream(UInt(32 bits))
StreamWidthAdapter(io.dataIn,dataAdapt)
io.dataOut<>dataAdapt.queue(512)
}
仿真波形:
这里的实现调用了两个库函数:StreamWidthAdapter,queue。而你的接口列表写完了否?
StreamWidthAdapter
StreamWidthAdapter,顾名思义,其用途是进行Stream接口的位宽转换。看下其源代码:
其输入参数列表为:
- input:输入Stream接口
- output:输出Stream接口
- endianness:选择转换方式为大尾端还是小尾端(LITTLE,BIG)
- padding:当输入输出端口位宽不是整数倍时进行数据填充(默认为false,但如果位宽不能整除则要设置为true)。
其内部设计实现思路并不难,位宽匹配时输入输出直接相连,不匹配时则通过增加一个counter来实现接口时序调整和数据填充。
除此之外,StreamWidthAdapter还提供了一个make方法:
def make[T <: Data, T2 <: Data](input : Stream[T], outputPayloadType : HardType[T2], endianness: Endianness = LITTLE, padding : Boolean = false) : Stream[T2] = {
val ret = Stream(outputPayloadType())
StreamWidthAdapter(input,ret,endianness,padding)
ret
}
可以看出,其内部封装了对StreamWidthAdapter的实现并返回一个Stream接口,通过这个函数,我们上面的代码甚至可以简化为一行:
case class example5() extends Component{
val io=new Bundle{
val dataIn=slave(Stream(UInt(128 bits)))
val dataOut=master(Stream(UInt(32 bits)))
}
io.dataOut<>StreamWidthAdapter.make(io.dataIn,UInt(32 bits)).queue(512)
}
queue
SpinalHDL在Stream里提供了一个queue方法:
/** Connect this to a fifo and return its pop stream
*/
def queue(size: Int): Stream[T] = {
val fifo = new StreamFifo(payloadType, size).setCompositeName(this,"queue", true)
fifo.setPartialName(this,"fifo")
fifo.io.push << this
fifo.io.pop
}
注释很明晰:将Stream接口连接到FIFO上,并返回FIFO的POP端口。
小结
SpinalHDL本质上和我们写Verilog并无差别,只不过借助于Scala语言及将常见基础组件封装起来便于快捷的调用。当然我们在使用的同时还是要去仔细分析下其源代码,理解设计的本质。
☆ END ☆
作者:玉骐
原文链接:https://mp.weixin.qq.com/s/MAxApMnOM2WmyjyJTkykpQ
微信公众号:
推荐阅读
更多SpinalHDL技术干货请关注Spinal FPGA专栏。