聊一聊如何动态选取变量中指定宽度的数据。
+:与-:
在Verilog/SystemVerilog中,当我们从一个数据中根据另一个变量动态选取指定位宽时用到的表达式为“+:”和“-:”。+:和 -:适用于那些 [MSB : LSB]都想使用变量的情况。
[BASE +: WIDTH] 代表 [BASE+WIDTH : BASE],BASE是变量,WIDTH是常量。
[BASE -: WIDTH] 代表 [BASE : BASE-WIDTH]。
data[0 +: 8] <--等价于--> data[7:0]
data[15 +: 2] <--等价于--> data[16:15]
data[7 -: 8] <--等价于--> data[7:0]
data[15 -: 2] <--等价于--> data[15:14]
这也算是一个常用的功能。条条大路通罗马,在SpinalHDL里,有两种写法可实现相同的功能。
x(offset, width)
无论是对于UInt/SInt还是Bits,其都提供了一个x(offset, width)方法,该方法用于从变量中指定偏移处选取指定位宽。offset为UInt类型,用于指定偏移,width为BitCount类型(SpinalHDL-Doc中有误,width类型不可为Int):
case class example6() extends Component{
val io=new Bundle{
val dataIn=in UInt(512 bits)
val sel=in UInt(9 bits)
val dataOut=out UInt(8 bits)
}
io.dataOut:=io.dataIn(io.sel,8 bits)
}
其对应的RTL代码为:
module example6 (
input [511:0] io_dataIn,
input [9:0] io_sel,
output [7:0] io_dataOut
);
assign io_dataOut = io_dataIn[io_sel +: 8];
endmodule
可以看出,x(offset, width)方法对应的实现是+:,而对于-:则并没有支持,若想实现只能这么来写:
io.dataOut:=io.dataIn(io.sel,8 bits)
subdivideIn
相较于x(offset, width),subdivideIn则方法则是将变量按指定方式进行切割形成一个Vec数组,其语法为:
//Subdivide x into y slices, y: Int
x.subdivideIn(y slices)
//Subdivide x into multiple slices of y bits, y: IntSubdivide x into multiple slices of y bits, y: Int
x.subdivideIn(y bits)
而在《起承转合,Vec数组的使用》中提到,Vec中有提供read方法,能够让我们读取指定的范围,因而可以这么来写:
io.dataOut:=io.dataIn.subdivideIn(8 bits).read(io.sel)
其生成的Verilog代码中会是一个case语句。
one more thing
无论是Verilog/SystemVerilog,还是SpinalHDL,向量的选取均只能取固定位宽的数据,这也是为什么x(offset, width)中width必须是一个固定值。我们描述的仍旧是电路,所以电路对象在进行电路描述时便是固定下来的。
无他,我们甚至可以认为SpinalHDL是一个用来写Verilog的工具脚本而已~
END
作者:玉骐
原文链接:https://mp.weixin.qq.com/s/O6RNZ1G5Oy5UTwG5WEBjEw
微信公众号:
推荐阅读
更多SpinalHDL技术干货请关注Spinal FPGA专栏。