Wednesday, May 3, 2017

Gentle introduction to diplomacy

In a previous post, I was looking at how to use the diplomacy and uncore libraries (in Rocket Chip Generator) to hook up a simple AHB master module to AHB slave module.  To my knowledge, none of the library code is documented.  IDEA's debugger came in handy to grow my understanding of the code base.  I can confirm the wiring portion now works.  May come back to this in the future.

Master module


class Master()(implicit p: Parameters) extends LazyModule
{
  val ahbMasterParameters = AHBMasterParameters(
    // nothing for now ..
  )

  val ahbMasterPortParameters = AHBMasterPortParameters(
    masters = Seq(ahbMasterParameters)
  )

  val node = AHBMasterNode(
    portParams = Seq(ahbMasterPortParameters)
  )

  lazy val module = new LazyModuleImp(this) {
    val io = new Bundle {
      val out = node.bundleOut
    }
  }
}

Slave module

class Slave()(implicit p: Parameters) extends LazyModule
{
  val ahbSlaveParameters = AHBSlaveParameters(
    address = Seq(AddressSet(0x0, 0xFFFF)),
    regionType    = RegionType.UNCACHED,
    executable    = true,
    supportsRead  = TransferSizes(1, 4),
    supportsWrite = TransferSizes(1, 4)
  )

  val ahbSlavePortParameters = AHBSlavePortParameters(
    slaves = Seq(ahbSlaveParameters),
    beatBytes = 4
  )

  val node = AHBSlaveNode(
    portParams = Seq(ahbSlavePortParameters)
  )

  lazy val module = new LazyModuleImp(this) {
    val io = new Bundle {
      val in = node.bundleIn
    }
  }
}

Connect master to slave

class Slave()(implicit p: Parameters) extends LazyModule
{
  val ahbSlaveParameters = AHBSlaveParameters(
    address = Seq(AddressSet(0x0, 0xFFFF)),
    regionType    = RegionType.UNCACHED,
    executable    = true,
    supportsRead  = TransferSizes(1, 4),
    supportsWrite = TransferSizes(1, 4)
  )

  val ahbSlavePortParameters = AHBSlavePortParameters(
    slaves = Seq(ahbSlaveParameters),
    beatBytes = 4
  )

  val node = AHBSlaveNode(
    portParams = Seq(ahbSlavePortParameters)
  )

  lazy val module = new LazyModuleImp(this) {
    val io = new Bundle {
      val in = node.bundleIn
    }
  }
}

Top level hookup

class ChiselTopTest extends ChiselFlatSpec {

  class ChiselTopTester(c: ChiselTopWrapper) extends PeekPokeTester(c) {
    println("Hello World!")
  }

  // https://github.com/ucb-bar/rocket-chip/issues/359
  class ChiselTopWrapper(p: Parameters) extends Module {
    // must wrap Module into Module() ..
    val top = Module(LazyModule(new ChiselTop()(p)).module)
    // must wrap IO into IO(), thought it wasn't necessary with Chisel3 ..
    val io = IO(top.io.cloneType)
    io <> top.io
  }

  implicit val p: Parameters = new BlankConfig
  chisel3.iotesters.Driver(() => new ChiselTopWrapper(p)) {
    c => new ChiselTopTester(c)
  }
}

Generated verilog (abridged), woo!

module ChiselTop(
  input   clock,
  input   reset,
  input   io_ddrClock
);
  Master_master master (
    .io_out_0_hwdata(master_io_out_0_hwdata),
    .io_out_0_hrdata(master_io_out_0_hrdata) //--TODO: check naming
  );
  Slave_slave slave (
    .io_in_0_hwdata(slave_io_in_0_hwdata),
    .io_in_0_hrdata(slave_io_in_0_hrdata)
  );
  // slave to master for read data
  assign master_io_out_0_hrdata = slave_io_in_0_hrdata;
  // master to slave for write data
  assign slave_io_in_0_hwdata = master_io_out_0_hwdata;
endmodule

... there might be a bug in naming of ports for hrdata.