/duh-scala

⛏️ DUH component export to Scala

Primary LanguageJavaScriptApache License 2.0Apache-2.0

NPM version Actions Status

DUH component export to Scala

Installation

npm i duh-scala

Exporting a blackbox wrapper and attach method

This is a scala LazyModule that has diplomatic nodes corresponding to the bus interfaces of the duh component.

duh-export-scala <mycomp>.json5 -o <output-dir>

Exporting a RegisterRouter

duh-export-regmap will export a RegisterRouter corresponding the the memoryMaps in a duh component. A RegisterRouter is an abstract Scala description of memory-mapped registers that is generic to any bus interface. Concrete implementations for TileLink and AXI4 are included in the generated Scala.

duh-export-regmap <mycomp>.json5 -o <output-dir>

Exporting a monitor wrapper and attach method

This is a special kind of chisel Module that can be attached to diplomatic edges corresponding to the monitor's bus interface type.

duh-export-monitor <mycomp>.json5 -o <output-dir>

Blackbox wrapper API

This section describes the API of the blackbox wrapper generated by duh-export-scala. The hierarchy of the output modules is looks like

top: N${name}Top
└── imp: L${name}
    └── blackbox: ${name}

The reason for having two layers of wrapping is to separate the purely combinational wrapper logic from the sequential logic. The inner L${name} module only contains combinational wrapper logic while the outer N${name}Top module contains sequential logic

The L${name} module contains the node declarations, blackbox instantiation, and connections between node bundles and blackbox ports. There is also an extraResources method that is called when the DTS data structure is constructed. The fields returned by this method are included in the entry corresponding the component in the DTS.

class L${name}Base(c: ${name}Params)(implicit p: Parameters) extends LazyModule {

  // device declaration
  val device = new SimpleDevice("${name}", Seq("sifive,${name}-${version}")) {
    ...
  }

  // extra fields to include in the entry of this device in the DTS
  def extraResources(resources: ResourceBindings) = Map[String, Seq[ResourceValue]]()

  // node declarations
  val axiNode = ...
  val apbNode = ...
  ...

  // blackbox instantiation and wiring
  lazy val module = new L${name}BaseImp
}

The corresponding user class that extends L${name}Base looks like

class L${name}(c: ${name}Params)(implicit p: Parameters) extends L${name}Base(c)(p)
{

// User code here

}

To add another integer field called data-width and string field called name you would add the following code into the user L${name} class.

  override def extraResources(resources: ResourceBindings) =
      Map("data-width" -> Seq(ResourceInt(dataWidth)),
        "name" -> Seq(ResourceString(name)))

The result should look like

class L${name}(c: ${name}Params)(implicit p: Parameters) extends L${name}Base(c)(p)
{
  override def extraResources(resources: ResourceBindings) =
      Map("data-width" -> Seq(ResourceInt(dataWidth)),
        "name" -> Seq(ResourceString(name)))
}

The N${name} module contains methods to instantate TileLink adapters for the nodes. It also contains a method called userOM that is called when the object model of the component is constructed. The object returned by userOM is included with the base object model. This can be overrided to include arbitrary information in the object model and defaults to empty. The value returned by this method should be an instance of a case class which is why it returns Product with Serializable.

class N${name}TopBase(val c: N${name}TopParams)(implicit p: Parameters) extends SimpleLazyModule
  with BindingScope {

  // userOM method
  def userOM: Product with Serializable = Nil

  // adapter methods
  def getaxilNodeTLAdapter(): TLInwardNode = ...
  def getahblNodeTLAdapter(): TLInwardNode = ...
  ...
}

The corresponding user class that extends N${name}Base looks like

class N${name}Top(c: N${name}TopParams)(implicit p: Parameters) extends N${name}TopBase(c)(p)
{

// User code here

}

To add the following fields to the OMDevice of the component

foo: "foo",
foobar: {
  foo: "foo",
  bar: "bar"
}

add the following code to the user N${name} by defining the following case class

case class MyOM(foo: String, foobar: Map[String, String])

and add the following code to N${name}

  override def userOM = new MyOM("foo", Map("foo" -> "foo", "bar" -> "bar"))

The result should look like

class N${name}Top(c: N${name}TopParams)(implicit p: Parameters) extends N${name}TopBase(c)(p)
{
  override def userOM = new MyOM("foo", Map("foo" -> "foo", "bar" -> "bar"))
}

And the output object model should look like

{
  "foo" : {
    "foo" : "foo",
    "bar" : "bar"
  },
  "memoryRegions" : [ ... ],
  "interrupts" : [ ... ],
  "_types" : [ "OM${name}", "OMDevice", "OMComponent", "OMCompoundType" ]
}

Testing

npm test

License

Apache 2.0 LICENSE.