如何让Chisel的Module类实现硬件生成函数式的IO宽度自动推断?
Chisel中Module实现IO宽度自动推断的方案
背景:硬件函数与Module的差异
在Chisel中,硬件生成函数可让编译器自动推断Vec等类型的宽度,无需显式传参:
def hw_func(x: Vec[UInt]): Vec[UInt] = x
不同宽度的Vec能直接适配调用:
val x6 = Wire(Vec(6, UInt())) val y6 = Wire(Vec(6, UInt())) y6 := hw_func(x6) val x32 = Wire(Vec(32, UInt())) val y32 = Wire(Vec(32, UInt())) y32 := hw_func(x32)
但传统Module需要通过构造参数指定IO宽度,写法繁琐:
class hw_module(widthX: Int, widthY: Int) extends Module { val io = IO(new Bundle { val x = Input(Vec(widthX, UInt)) val y = Output(Vec(widthY, UInt)) }) io.y := io.x }
实例化必须显式传入宽度,无法自动推断:
val x6 = Wire(Vec(6, UInt())) val y6 = Wire(Vec(6, UInt())) val inst6 = Module(new hw_module(6, 6)) inst6.io.x := x6 y6 := inst6.io.y val x32 = Wire(Vec(32, UInt())) val y32 = Wire(Vec(32, UInt())) val inst32 = Module(new hw_module(32, 32)) inst32.io.x := x32 y32 := inst32.io.y
针对这个问题,以下是几种实现自动推断的实用方案:
方案1:Scala隐式参数推导
利用Scala隐式机制,让编译器自动提取Vec的长度参数:
// 定义提取Vec长度的类型类 trait VecLength[A] { def length: Int } object VecLength { implicit def forVec[T](vec: Vec[T]): VecLength[Vec[T]] = new VecLength[Vec[T]] { override def length: Int = vec.length } } // 改进后的Module,依赖隐式参数获取宽度 class AutoWidthModule[T <: Data](implicit lenX: VecLength[Vec[T]], lenY: VecLength[Vec[T]]) extends Module { val io = IO(new Bundle { val x = Input(Vec(lenX.length, T)) val y = Output(Vec(lenY.length, T)) }) io.y := io.x } // 辅助工厂方法,自动推导隐式参数 object AutoWidthModule { def apply[T <: Data](x: Vec[T], y: Vec[T]): AutoWidthModule[T] = { implicit val lenX = VecLength.forVec(x) implicit val lenY = VecLength.forVec(y) Module(new AutoWidthModule[T]) } }
使用时无需手动传宽度,编译器自动匹配:
val x6 = Wire(Vec(6, UInt())) val y6 = Wire(Vec(6, UInt())) val inst6 = AutoWidthModule(x6, y6) inst6.io.x := x6 y6 := inst6.io.y val x32 = Wire(Vec(32, UInt())) val y32 = Wire(Vec(32, UInt())) val inst32 = AutoWidthModule(x32, y32) inst32.io.x := x32 y32 := inst32.io.y
方案2:基于Data类型克隆的工厂方法
直接利用Chisel的cloneOf方法,通过传入IO模板实例自动推导类型与宽度:
class GenericModule[T <: Data](xType: T, yType: T) extends Module { val io = IO(new Bundle { val x = Input(cloneOf(xType)) val y = Output(cloneOf(yType)) }) io.y := io.x } object GenericModule { def apply[T <: Data](x: T, y: T): GenericModule[T] = { Module(new GenericModule(cloneOf(x), cloneOf(y))) } }
调用时直接传入目标Wire实例即可:
val x6 = Wire(Vec(6, UInt())) val y6 = Wire(Vec(6, UInt())) val inst6 = GenericModule(x6, y6) inst6.io.x := x6 y6 := inst6.io.y val x32 = Wire(Vec(32, UInt())) val y32 = Wire(Vec(32, UInt())) val inst32 = GenericModule(x32, y32) inst32.io.x := x32 y32 := inst32.io.y
方案3:参数化Bundle+隐式推导(适用于复杂IO)
如果IO是自定义Bundle,可结合参数化Bundle与隐式转换实现通用推导:
// 定义参数化IO Bundle trait MyIOBundle extends Bundle { def widthX: Int def widthY: Int val x = Input(Vec(widthX, UInt())) val y = Output(Vec(widthY, UInt())) } class AutoMyIOBundle(val widthX: Int, val widthY: Int) extends MyIOBundle object AutoMyIOBundle { implicit def fromVecs(x: Vec[UInt], y: Vec[UInt]): AutoMyIOBundle = new AutoMyIOBundle(x.length, y.length) } // 接收Bundle实例的Module class BundleBasedModule(ioBundle: MyIOBundle) extends Module { val io = IO(ioBundle) io.y := io.x } object BundleBasedModule { def apply(x: Vec[UInt], y: Vec[UInt]): BundleBasedModule = { Module(new BundleBasedModule(AutoMyIOBundle.fromVecs(x, y))) } }
使用方式同样简洁:
val x6 = Wire(Vec(6, UInt())) val y6 = Wire(Vec(6, UInt())) val inst6 = BundleBasedModule(x6, y6) inst6.io.x := x6 y6 := inst6.io.y
内容的提问来源于stack exchange,提问作者Guilty
相关产品推荐
相关产品推荐

