如何使用 ScalaBlackBox 编写累加器?

How to writing a accumulator by using ScalaBlackBox?

我想为 dsptools 创建一些类似于 DspReal 的新数字类型,例如 DspPosit 和 DspQuire。 DspPosit 基于我有一些 Java 代码的 posit,而 DspQuire 基于 quire,它是一种用于 posit 的累加器。因为我现在就是想模拟一下,所以写了很多ScalaBlackBox来像DspReal一样对它们进行运算。但是,我发现ScalaBlackBox 不能构建时序逻辑。例如,quire 累加器的当前输出取决于它的输入和最后的输出。但是 ScalaBlackBox 无法获取输出的值。此外,step(n) 也会影响输出。因为累加器将在每个时钟周期读取其输入。

我发现了踏板的一些系统问题。首先,ScalaBlackBox的twoOp和oneOp等函数,会被多次调用。我不知道为什么。其次,step(n)是PeekPokeTester的函数,不能被ScalaBlackBox访问。第三,我尝试读取当前输出但系统出错。

trait DspBlackBlackBoxImpl extends BlackBoxImplementation with ScalaBlackBox

abstract class DspQuireAccumulator extends DspBlackBlackBoxImpl {

  lazy val accValue = Quire32() // initial value

  /**
    * sub-classes must implement this two argument function
    *
    * @param posit  accumulate element
    * @return       quire operation result
    */
  def accOp(posit: Posit32): Unit

  def outputDependencies(outputName: String): Seq[(String)] = {
    outputName match {
      case "out" => Seq("in") // Seq("out", "in") gives errors
      case _ => Seq.empty
    }
  }

  def cycle(): Unit = {}

  def execute(inputValues: Seq[Concrete], tpe: Type, outputName: String): Concrete = {
    val arg1 :: _ = inputValues
    val positArg = Posit32(arg1.value)
    accOp(positArg)
    val result = quire32ToBigInt(accValue)
    ConcreteSInt(result, DspQuire.underlyingWidth, arg1.poisoned).asUInt
  }

  def getOutput(inputValues: Seq[BigInt], tpe: Type, outputName: String): BigInt = {
    val arg1 :: _ = inputValues
    val positArg = Posit32(arg1)
    accOp(positArg)
    quire32ToBigInt(accValue)
  }
}
class DspQuireAddAcc(val name: String) extends DspQuireAccumulator {
  def accOp(posit: Posit32): Unit = accValue += posit
}
class QuireBlackboxAccOperand extends BlackBox {
  val io = IO(new Bundle() {
    val in = Input(UInt(DspPosit.underlyingWidth.W))
    val out = Output(UInt(DspQuire.underlyingWidth.W))
  })
}

class BBQAddAcc extends QuireBlackboxAccOperand

class TreadleDspQuireFactory extends ScalaBlackBoxFactory {
  def createInstance(instanceName: String, blackBoxName: String): Option[ScalaBlackBox] = {
    blackBoxName match {
      case "BBQAddAcc" => Some(add(new DspQuireAddAcc(instanceName)))
...

accOp 将被多次调用。所以,如果我想累加 List(1, 2, 3),结果可能是 0 + 1 + 1 + 2 + 2 + ... 而且peek函数会再次调用accOp一次,这也让我很困惑。

我相信此时您的大部分问题都是由于混合使用两种不同的方法造成的。我认为你不应该使用 BlackBoxImplmentation 因为它是与 firrtl-interpreter. Just use the ScalaBlackBox and implement the methods as described in the wiki page Black Boxes and Treadle and shown in the TreadleTest BlackBoxWithState.

一起使用的旧方案

不要使用 outputDependencies,而是使用 getDependencies 指定输入和输出之间的任何依赖关系。 inputChanged 将在输入 IO 更改时调用。所以在那个方法中你想记录或更新你的黑盒子的内部状态。 clockChange 将在时钟更改时调用,并将提供转换信息,以便您可以决定接下来会发生什么。 Treadle 会在需要黑匣子输出时调用 getOutput,因为您不会使用 outputDependencies,所以您可以忽略输入,只根据您的内部状态提供输出值。

我仍在尝试在此处重现您的代码的 运行 版本,但如果您可以尝试我上面的建议并告诉我进展如何,我将需要一点时间将其组合在一起那会很有帮助。我有兴趣让 Treadle 的这个功能更好、更容易使用,因此欢迎所有反馈。