reduceBalancedTree的使用
两种电路描述的差异
考虑下下面两种电路的差异:
sum=data_0+data_1+data_2+data_3
其综合电路为:
sum=(data_0+data_1)+(data_2+data_3)
其综合电路为:
第一种直接相加的方式会生成一个较长的加法链,而第二种加法方式相当于实现了一个加法树,其在时序收敛上相较于第一种方式更加友好,也往往采用较多。
第一种电路的实现方式在《软为硬用,如是而已》中提到过,采用reduce即可:
case class balanceTree() extends Component {
val io=new Bundle{
val dataIn=in Vec(UInt(8 bits),4)
val sum=out UInt(8 bits)
}
noIoPrefix()
io.sum:=io.dataIn.reduce(_+_)
}
而类似第二种欲实现一个树形电路的描述则需要调用reduceBalancedTree方法:
case class balanceTree() extends Component {
val io=new Bundle{
val dataIn=in Vec(UInt(8 bits),4)
val sum=out UInt(8 bits)
}
noIoPrefix()
io.sum:=io.dataIn.reduceBalancedTree(_+_)
}
reduceBalancedTree
SpinalHDL对于reduceBalancedTree提供了两个方法调用:
def reduceBalancedTree(op: (T, T) => T): T
def reduceBalancedTree(op: (T, T) => T, levelBridge: (T, Int) => T): T
Vec类型和Seq类型均可以调用该方法,该方法实现为:
从源代码里可以看出,该方法最终的实现依赖两个参数:
- op: (T, T) => T 树的每一层级的操作,上面的加法树里我们定义的是将两个电路对象相加,当然你也可以用来实现其他的方法,只要符合op方法定义即可。
- levelBridge: (T, Int) => T 树层级间的调用方法,该方法依赖两个参数,第一个参数为带操作的电路对象,第二个参数为树的level层级(level从零开始)。
分析源代码不难看出,其通过递归实现一个树形结构,而stage将在生成RTL代码前会完成电路树形结构的生成(可参照《SpinalHDL—if向左、when向右》)。
当调用reduceBalancedTree(op: (T, T) => T)时,其实现里将levelBridge实现为(s,l)=>s,即对树层级间不做任何的操作。上面的加法树中我们在加法树的每个层级添加寄存器:
case class balanceTree() extends Component {
val io=new Bundle{
val dataIn=in Vec(UInt(8 bits),4)
val sum=out UInt(8 bits)
}
noIoPrefix()
io.sum:=io.dataIn.reduceBalancedTree(_+_,(s,l)=>RegNext(s))
}
仅在奇数级插入寄存器(level是从零开始):
case class balanceTree() extends Component {
val io=new Bundle{
val dataIn=in Vec(UInt(8 bits),4)
val sum=out UInt(8 bits)
}
noIoPrefix()
io.sum:=io.dataIn.reduceBalancedTree(_+_,(s,l)=>RegNext(s))
}
写在最后
用Verilog/SystemVerilog实现一个类似reduceBalancedTree功能的模版你可还能接受么~
☆ END ☆
作者:玉骐
原文链接:https://mp.weixin.qq.com/s/aueJXGImPrRfer4\_kBkT9w
微信公众号:
推荐阅读
更多SpinalHDL技术干货请关注Spinal FPGA专栏。