如何将运算符作为参数传递

How to pass a operator as a parameter

我正在尝试将运算符传递给模块,以便可以通用地构建模块。我传递了一个双输入运算符参数,然后在缩减操作中使用它。如果我用具体的运算符替换传递的参数,这就可以了。

将 Chisel/UInt/Data 运算符作为模块参数传递的正确方法是什么?

  val io = IO(new Bundle {
    val a = Vec(n, Flipped(Decoupled(UInt(width.W))))
    val z = Decoupled(UInt(width.W))
  })
  val a_int = for (n <- 0 until n) yield DCInput(io.a(n))
  val z_int = Wire(Decoupled(UInt(width.W)))

  val all_valid = a_int.map(_.valid).reduce(_ & _)
  z_int.bits := a_int.map(_.bits).reduce(_ op _)
...

这是一种奇特的 Scala 实现方式

import chisel3._
import chisel3.tester._
import chiseltest.ChiselScalatestTester
import org.scalatest.{FreeSpec, Matchers}

class ChiselFuncParam(mathFunc: UInt => UInt => UInt) extends Module {
  val io = IO(new Bundle {
    val a = Input(UInt(8.W))
    val b = Input(UInt(8.W))
    val out = Output(UInt(8.W))
  })

  io.out := mathFunc(io.a)(io.b)
}

class CFPTest extends FreeSpec with ChiselScalatestTester with Matchers {
  def add(a: UInt)(b: UInt): UInt = a + b
  def sub(a: UInt)(b: UInt): UInt = a - b

  "add works" in {
    test(new ChiselFuncParam(add)) { c =>
      c.io.a.poke(9.U)
      c.io.b.poke(5.U)
      c.io.out.expect(14.U)
    }
  }
  "sub works" in {
    test(new ChiselFuncParam(sub)) { c =>
      c.io.a.poke(9.U)
      c.io.b.poke(2.U)
      c.io.out.expect(7.U)
    }
  }
}

虽然只传入运算符的字符串形式,然后使用简单的 Scala ifs 来控制适当的代码生成可能会更清楚。像

class MathOp(code: String) extends Module {
  val io = IO(new Bundle {
    val a = Input(UInt(8.W))
    val b = Input(UInt(8.W))
    val out = Output(UInt(8.W))
  })

  io.out := (code match {
    case "+" => io.a + io.b
    case "-" => io.a - io.b
    // ...
  })
}

Chick 已经提供了一个很好的答案,但我想提供另一个例子来说明和解释 Chisel 和 Scala 在硬件设计方面的一些真正强大的功能。我知道你 (Guy) 可能知道大部分内容,但我想为遇到这个问题的其他人提供详细的答案。

我将从完整示例开始,然后突出显示正在使用的一些功能。

class MyModule[T <: Data](n: Int, gen: T)(op: (T, T) => T) extends Module {
  require(n > 0, "reduce only works on non-empty Vecs")
  val io = IO(new Bundle {
    val in  = Input(Vec(n, gen))
    val out = Output(gen)
  })
  io.out := io.in.reduce(op)
}

[T <: Data] 这叫做Type Parameter (T) with an Upper Type Bound (<: Data). This allows us to make the Module generic to the hardware type with which we parameterize it. We give T an upper bound of Data (which is a type from Chisel) to tell Scala that this is a hardware type we can use to generate hardware with Chisel. The upper-bound means it must be a subtype of Data, which includes all of the Chisel hardware types (eg. UInt, SInt, Vec, Bundle and user classes that extend Bundle). This is the exact same way that the Chisel constructors like Reg(...) are parameterized

您会注意到有 multiple parameter lists(n: Int, gen: T)(op: (T, T) => T)。第一个参数 n: Int 是一个简单的整数参数。第二个参数 gen: T 是我们的通用类型 T,因此是 Data 的子类型,用作我们将在模块内生成的硬件的模板。

第二个参数列表(op: (T, T) => T)是一个函数。作为函数式编程语言,functions are values in Scala, and thus can be used as arguments just like our Int argument. (T, T) => T reads as a function of two arguments, both of type T, that returns a T. Remember that T is our hardware type that is a subclass of Data. Because op is in a second parameter list, this is telling Scala that it should infer T from gen, and then use the same T for op. For example, if gen is UInt(8.W), Scala infers T as UInt. This then constrains op to be a function of type (UInt, UInt) => UInt. Bitwise AND is such a function, so we can pass an anonymous function 到 AND 两个 UInt(_ & _).

现在我们已经有了参数化的抽象类型 MyModule class,我们如何实际使用它?上面我给出了如何将它与 UInts 一起使用的片段,但让我们看看如何获​​得一些实际的 Verilog:

object MyMain extends App {
  println(chisel3.Driver.emitVerilog(new MyModule(4, UInt(8.W))(_ & _)))
}

或者,我们可以用更复杂的类型参数化 MyModule

class MyBundle extends Bundle {
  val bar = Bool()
  val baz = Bool()
}

object MyMain extends App {
  def combineMyBundle(a: MyBundle, b: MyBundle): MyBundle = {
    val w = Wire(new MyBundle)
    w.bar := a.bar && b.bar
    w.baz := a.baz && b.baz
    w
  }
  println(chisel3.Driver.emitVerilog(new MyModule(4, new MyBundle)(combineMyBundle)))
}

我们还必须定义一个 (MyBundle, MyBundle) => MyBundle 类型的函数,我们用 combineMyBundle.

您可以在 Scastie.

上看到我上面提供的代码的完整、可运行版本

我希望有人觉得这个例子有用!