Differences between LazyModule and LazyModuleImp

548 views Asked by At

What are the differences between LazyModule and LazyModuleImp? Like the diplomacy demo under rocket-chip/doc says: The desired hardware for the module must be written inside LazyModuleImp. But considering following codes:

class A(implicit p: Parameters) extends LazyModule {
  val b = LazyModule(new Leaf)
  val c = LazyModule(new Leaf)

  val input = b.input
  val output = c.output

  val bOutput = b.output.makeSink
  val cInput = BundleBridgeSource[Bool](() => Bool())
  c.input := cInput
  lazy val module = new LazyModuleImp(this) {
    cInput.bundle := bOutput.bundle
  }
}

The := is a hardware operation, it appears both inside and outside of the LazyModuleImp, so which code should place in LazyModuleImp ?

1

There are 1 answers

0
l Steveo l On BEST ANSWER

The best way (IMO) to think about LazyModule is that there are essentially two parts to a LazyModule

  1. The non-hardware implementation (everything outside of LazyModuleImp) This is where we define Nodes, resolve parameters, do anything that one would think of as "software"
  2. The hardware implementation (everything inside LazyModuleImp) This is where we create the actual hardware. We generally use the parameters we resolved outside to build some hardware.

In the case of diplomacy, := is actually a Node connection. In which you are connecting two diplomatic nodes. When this connection is performed, you are essentially "drawing" a path from one node to another. In the case of the diplomacy demo, you would be connecting the AdderDriverNode to the AdderNode. This is performed outside of the LazyModuleImp as we need to define this node graph BEFORE we can build the hardware.

For example, if you look at the AdderTestHarness, you can see the following outside of the LazyModuleImp

  // create edges via binding operators between nodes in order to define a complete graph
  drivers.foreach{ driver => adder.node := driver.node }

  drivers.zip(monitor.nodeSeq).foreach { case (driver, monitorNode) => monitorNode := driver.node }
  monitor.nodeSum := adder.node

Here we are connecting our drivers to the adder, the drivers to the monitor, then the adder to the monitor. These connections create the hardware based on the Bundle generation that was defined for each node.

So I tend to think about LazyModules, and diplomacy in general, as a two pass method for creating hardware. The first pass is defining the hardware topology. This is done outside of LazyModuleImp. I will describe how each piece is connected, and resolve parameters such as widths, addresses, etc. The second pass is the actual hardware generation. In here, all of my parameters should be resolved and the hardware I have defined is now created.

Diplomacy is quite impressive, however it can be quite difficult to grasp. Particularly if coming from a strictly hardware background, or strictly software background. It requires a firm understanding of how certain software paradigms work to grasp the architecture of parameter/edge resolution, then you have the hurdle of using that to actually create some real hardware.