I've been studying rocket-chip for utilizing diplomacy and I have a decent grasp on the overall structure of how diplomacy works. (I don't understand it totally, but well enough to create some examples on my own). I would like to develop some IP in which the main objective is to have a regmap
through the use of a *RegisterRouter
.
If I use/modify one of the RegisterNodeExamples from rocket-chip, I get the following:
class MyDeviceController(implicit p: Parameters) extends LazyModule {
val device = new SimpleDevice("my-device", Seq("tutorial,my-device0"))
val node = APBRegisterNode(
//address = Seq(AddressSet(0x10028000, 0xfff)), (Modified since not in APBRegisterNode)
address = AddressSet(0x002000, 0xfff),
//device = device, (Removed since not in APBRegisterNode)
beatBytes = 8)
lazy val module = new LazyModuleImp(this) {
val bigReg = RegInit(0.U(64.W))
val mediumReg = RegInit(0.U(32.W))
val smallReg = RegInit(0.U(16.W))
val tinyReg0 = RegInit(0.U(4.W))
val tinyReg1 = RegInit(0.U(4.W))
node.regmap(
0x00 -> Seq(RegField(64, bigReg)),
0x08 -> Seq(RegField(32, mediumReg)),
0x0C -> Seq(RegField(16, smallReg)),
0x0E -> Seq(
RegField(4, tinyReg0),
RegField(4, tinyReg1)))
}
}
I'm using APB at the moment as I'm very familiar with AMBA protocols and it has the smallest code base under the diplomacy
package. And I could make is so either of the AMBA or TL protocols are used later.
My Question
Is there a way to generate verilog just for MyDeviceController
as a stand alone component?
I have not been able to figure this out if there is. Obviously if I just try to instantiate MyDeviceController
I will get an error for the inward parameters of node
not being connected. I'm not sure if you can give a "dummy" node connection? Or if there is some method that can handle that.
Why I want to do this
It is desirable to test the IP standalone in it's own test environment without a full SoC.
My Current Workaround/Solution
To work around this I essentially created a "wrapper" that creates an APBMasterNode
and connects to the APBRegisterNode
in MyDeviceController
.
class APBMaster()(implicit p: Parameters) extends LazyModule {
val apbMasterParameters = APBMasterParameters(
name = "apbMaster"
)
val apbMasterPortParameters = APBMasterPortParameters(
masters = Seq(apbMasterParameters)
)
val node = APBMasterNode(
portParams = Seq(apbMasterPortParameters)
)
lazy val module = new LazyModuleImp(this) {
val io = IO(new Bundle {
val wtf = Output(Bool())
val start = Input(Bool())
})
val myreg = RegInit(0.U(16.W))
myreg := myreg + 1.U
val prdata = Wire(UInt(64.W))
prdata := node.out.head._1.prdata
//seems to need these things to generate the logic
io.wtf := node.out.head._1.pready && !(node.out.head._1.prdata === 0.U)
node.out.head._1.pstrb := 63.U
node.out.head._1.pprot := 0.U
when(myreg(3,0) === 8.U && io.start) {
node.out.head._1.paddr := myreg
node.out.head._1.psel := true.B
node.out.head._1.penable := false.B
node.out.head._1.pwrite := true.B
node.out.head._1.pwdata := myreg + 1.U
} .elsewhen(myreg(3,0) === 9.U) {
node.out.head._1.paddr := myreg
node.out.head._1.psel := true.B
node.out.head._1.penable := true.B
node.out.head._1.pwrite := true.B
node.out.head._1.pwdata := myreg
} otherwise {
node.out.head._1.paddr := 0.U
node.out.head._1.psel := false.B
node.out.head._1.penable := false.B
node.out.head._1.pwrite := false.B
node.out.head._1.pwdata := 0.U
}
}
}
One issue with this, was that I had to create some controls for each of the APB signals. If I did not, the Chisel/FIRRTL compiler/generator would not create any Verilog for MyDeviceController
. This is what you see above with the myreg
counter being used to do some basic APB transaction.
The wrapper would look like the following:
class APBTop()(implicit p: Parameters) extends LazyModule {
val master = LazyModule(new APBMaster)
val slave = LazyModule(new MyDeviceController()(Parameters.empty))
slave.node := master.node
lazy val module = new LazyModuleImp(this) {
val io = IO(new Bundle {
val busy = Output(Bool())
val wtf = Output(Bool())
val start = Input(Bool())
})
io.busy := true.B
io.wtf := master.module.io.wtf
master.module.io.start := io.start
}
}
I can create this wrapper/master as a typical testing component, then in my testenv just instatiate the MyDeviceController
RTL, however I was wondering if there was another solution. It appears that diplomacy is fairly holistic (which I understand why), but was looking for suggestions on how IP level development is tackled for a Diplomatic infrastructure flow.
Thanks
Edit: Update March 2021
It's been a few months and I've spent more time with RocketChip/Chipyard/Diplomacy. This is a better solution, but leaving the old one below.
There is a makeIOs
method for several Nodes
. Using these we can actually punch out the respective AMBA/TL interface. This allows you to not have to use a wrapper that has nothing but connections.
Here is what it would look like compared to the previous version I suggested
class MyWrapper()(implicit p: Parameters) extends LazyModule {
//val master = LazyModule(new APBMaster)
val ApbPort = APBMasterNode(
portParams = Seq(APBMasterPortParameters(masters = Seq(APBMasterParameters(name = "ApbPort"))))
)
val apbport = InModuleBody {ApbPort.makeIOs()}
This reduces the need for the APBMaster
dummy class as well.
Just to have an answer, I ended up using a combination of what myself and Jack Koenig went back and forth on.
If time permits I'll see if there is a way to make a "template" or LazyModule
wrapper that does this for testing purposes (for each of the main protocols) and submit it to the Chisel repo.
class APBMaster()(implicit p: Parameters) extends LazyModule {
val apbMasterParameters = APBMasterParameters(
name = "apbMaster"
)
val apbMasterPortParameters = APBMasterPortParameters(
masters = Seq(apbMasterParameters)
)
val node = APBMasterNode(
portParams = Seq(apbMasterPortParameters)
)
lazy val module = new LazyModuleImp(this) {
//The dontTouch here preserves the interface so logic is generated
dontTouch(node.out.head._1)
}
}
class MyWrapper()(implicit p: Parameters) extends LazyModule {
val master = LazyModule(new APBMaster)
val slave = LazyModule(new MySlave()(Parameters.empty))
slave.node := master.node
lazy val module = new LazyModuleImp(this) {
//nothing???
}
}
object MyTestextends App {SSVPllFreqDetect)))
(new ChiselStage).execute(args, Seq(ChiselGeneratorAnnotation(() => LazyModule(new MyWrapper()(Parameters.empty)).module)))
}