Implementing a diplomatic AXI Stream interface in Chisel - BundleMap.cloneType error

391 views Asked by At

I am trying to build a minimal example, of how to generate an AXI4Stream interface using Chisel and diplomacy. I am using the diplomatic interface already available in rocket-chip (freechips.rocketchip.amba.axis). I have some experience with Chisel, but I am still trying to learn diplomacy.

Anyway, I've managed to create a small APB example using the answer provided here: IP block generation/testing when using diplomacy. Possible to give dummy node?

Following that, I tried to create a similar, simple AXI Stream example, but I keep getting errors. Concretely, I get the following error:

[error] (Compile / run) java.lang.Exception: Unable to use BundleMap.cloneType on class freechips.rocketchip.amba.axis.AXISBundleBits, probably because class freechips.rocketchip.amba.axis.AXISBundleBits does not have a constructor accepting BundleFields. Consider overriding cloneType() on class freechips.rocketchip.amba.axis.AXISBundleBits

The code:

package chipyard.example
import chisel3._
import chisel3.internal.sourceinfo.SourceInfo
import chisel3.stage.ChiselStage
import freechips.rocketchip.config.{Config, Parameters}
import freechips.rocketchip.amba.axis._
import freechips.rocketchip.diplomacy.{SimpleNodeImp, ValName, SourceNode, NexusNode, 
                                       SinkNode, LazyModule, LazyModuleImp, TransferSizes,
                                       SimpleDevice, AddressSet}

class MyAxisController(implicit p: Parameters) extends LazyModule {
  val device = new SimpleDevice("my-device", Seq("tutorial,my-device0"))
  val axisParams = AXISSlaveParameters.v1(name = "axisSlave", supportsSizes = TransferSizes(8,8))
  val axisPortParams = AXISSlavePortParameters.v1(slaves = Seq(axisParams))
  val node = AXISSlaveNode(portParams = Seq(axisPortParams))

  lazy val module = new LazyModuleImp(this) {
      val ins = node.in.unzip._1
      val register = RegInit(UInt(8.W), 0.U)
      register := register + ins(0).bits.data
  }
}

class AXISMaster()(implicit p: Parameters) extends LazyModule {
  val axisMasterParams = AXISMasterParameters.v1(
    name = "axisMaster", emitsSizes = TransferSizes(8, 8)
  )

  val axisMasterPortParams = AXISMasterPortParameters.v1(
    masters = Seq(axisMasterParams),
    beatBytes = Option(8)
  )

  val node = AXISMasterNode(
    portParams = Seq(axisMasterPortParams)
  )

  
  lazy val module = new LazyModuleImp(this) {
    //The dontTouch here preserves the interface so logic is generated
    dontTouch(node.out.head._1)
  }
}


class MyAxisWrapper()(implicit p: Parameters) extends LazyModule {
  val master = LazyModule(new AXISMaster)
  val slave  = LazyModule(new MyAxisController()(Parameters.empty))

  slave.node := master.node 

  lazy val module = new LazyModuleImp(this) {
    //nothing???
  }
}

and Main.scala:

package chipyard.example

import chisel3._
import freechips.rocketchip.config.Parameters
import freechips.rocketchip.diplomacy._

import java.io.File
import java.io.FileWriter


/**
 * An object extending App to generate the Verilog code.
 */
object Main {
  def main(args: Array[String]): Unit = {
    //(new chisel3.stage.ChiselStage).execute(args, Seq(ChiselGeneratorAnnotation(() => LazyModule(new MyWrapper()(Parameters.empty)).module)))

    val verilog = (new chisel3.stage.ChiselStage).emitVerilog(
                   LazyModule(new MyAxisWrapper()(Parameters.empty)).module
    )
    //println(s"```verilog\n$verilog```")

    val fileWriter = new FileWriter(new File("./gen/gen.v"))
    fileWriter.write(verilog)
    fileWriter.close()

  }
}

The code is also available at https://github.com/jurevreca12/temp_dspblock_example/tree/axistream2/scala/main.

My question is. Why do I get this error? Or am I doing something wrong in the first place, and is there an easier way to create an AXIStream module?

I appreciate any feedback.

1

There are 1 answers

0
metzkorn On BEST ANSWER

This looks to be an issue with Rocket-Chip's changes to bump to Chisel 3.5. During those changes, AXISBundleBits had its cloneType removed even though it extends off BundleMap (and therefore requires cloneType due to extending off Record).

I don't have all the details of cloneType at this time, but essentially:

  • Records require cloneType
  • Bundles used to require cloneType, but since the compiler plugin was implemented, as of 3.5 they no longer require cloneType.
  • BundleMap is a confusing case because it is a custom Bundle type extending directly off Record and isn't of type Bundle. There's a comment saying it explicitly requires a field constructor in an extended class.
// If you extend this class, you must either redefine cloneType or have a fields constructor
class BundleMap(val fields: Seq[BundleFieldBase]) extends Record with CustomBulkAssignable {
  // All fields must have distinct key.names
  require(fields.map(_.key.name).distinct.size == fields.size)

  val elements: ListMap[String, Data] = ListMap(fields.map { bf => bf.key.name -> chisel3.experimental.DataMirror.internal.chiselTypeClone(bf.data) } :_*)


  override def cloneType: this.type = {
    try {
      this.getClass.getConstructors.head.newInstance(fields).asInstanceOf[this.type]
    } catch {
      case e: java.lang.IllegalArgumentException =>
        throw new Exception("Unable to use BundleMap.cloneType on " +
                       this.getClass + ", probably because " + this.getClass +
                       " does not have a constructor accepting BundleFields.  Consider overriding " +
                       "cloneType() on " + this.getClass, e)
    }
  }

Therefore, it shouldn't have had its cloneType method removed during the 3.5 Chisel bump and that will need to be added back for AXIS in RC's main branch to start working again.

class AXISBundleBits(val params: AXISBundleParameters) extends BundleMap(AXISBundle.keys(params)) {
 override def cloneType: this.type = (new AXISBundleBits(params)).asInstanceOf[this.type] // This line was removed!
  def last = if (params.hasLast) apply(AXISLast) else true.B
  def id   = if (params.hasId)   apply(AXISId)   else 0.U
  def dest = if (params.hasDest) apply(AXISDest) else 0.U

Edit: the cloneType exception issue is now fixed for 3.5 on the main branch :)