[Tutorial] Chisel simulation with the hierarchical BlackBox Module
- Chisel Backbox Parameterization
- Chisel Simulation with Blackbox
- Chisel Simulation with Hierarchical Blackbox
Chisel Backbox Parameterization
For basic introduction, you can refer to Chisel Official Document: BlackBoxes.
Parameters can be passed into the Verilog module by passing a Map()
to the params
argument of the BlackBox
constructor. The keys of the map are the parameter names, and the values are the parameter values.
For example, in the following code, the CW_add
module has a parameter wA
which is passed into the Verilog module by Map("wA" -> wA)
.
class CW_add(val wA: Int = 4) extends BlackBox(Map("wA" -> wA)) with HasBlackBoxPath {
require(wA >= 1, "wA must be >= 1")
val io = IO(new Bundle {
val A: UInt = Input(UInt(wA.W))
val B: UInt = Input(UInt(wA.W))
val CI: UInt = Input(UInt(1.W))
val Z: UInt = Output(UInt(wA.W))
val CO: UInt = Output(UInt(1.W))
})
}
Chisel Simulation with Blackbox
If we want to do simulation with Blackbox modules, we need to provide the Verilog file of the Blackbox module.
This can be done by using the addResource
method of the HasBlackBoxResource
trait.
Besides, the trait HasBlackBoxPath
provides a addPath
method that can be used to specify the Verilog file(s) of the Blackbox module.
This method is suggested when the Verilog file is larger than 1MB.
Chisel Simulation with Hierarchical Blackbox
However, sometimes, the Blackbox module is not a single Verilog file, but a hierarchical Verilog module.
In this case, we need to provide the Verilog files of the hierarchical Blackbox module.
This can be done by calling the addPath
method multiple times.
Below is an example of using hierarchical Blackbox module in Chisel simulation.
Line 24 calls the addPath
method of the CW_add
module to specify the Verilog file of the hierarchical Blackbox module multiple times.
import chisel3._
import chiseltest._
import chiseltest.simulator.VerilatorFlags
import firrtl2.annotations.NoTargetAnnotation
import utest._
class addWithBlackBox(val wA: Int = 4) extends Module {
require(wA >= 1, "wA must be >= 1")
val io = IO(new Bundle {
val A: UInt = Input(UInt(wA.W))
val B: UInt = Input(UInt(wA.W))
val CI: UInt = Input(UInt(1.W))
val Z: UInt = Output(UInt(wA.W))
val CO: UInt = Output(UInt(1.W))
})
protected val U1: CW_add = Module(new CW_add(wA))
U1.io.A := io.A
U1.io.B := io.B
U1.io.CI := io.CI
io.Z := U1.io.Z
io.CO := U1.io.CO
def addPath(blackBoxFileSeq: Seq[String]): Unit = {
blackBoxFileSeq.foreach(blackBoxPath => U1.addPath(blackBoxPath))
}
}
object addWithBlackBox {
def simWithBBoxVerilog(blackBoxFileSeq: Seq[String]): addWithBlackBox = {
val addWithBlackBox = new addWithBlackBox
addWithBlackBox.addPath(blackBoxFileSeq)
addWithBlackBox
}
}
object DemoTest extends TestSuite with ChiselUtestTester {
val bboxFileSeq: Seq[String] = Seq(
"chipware/test/resources/CW_add_demo.v"
)
val annotationSeq: Seq[NoTargetAnnotation] = Seq(
WriteVcdAnnotation,
VerilatorBackendAnnotation,
// Disable all lint warnings
VerilatorFlags(Seq("-Wno-lint"))
)
val tests: Tests = Tests {
test("add should work with blackbox") {
testCircuit(addWithBlackBox.simWithBBoxVerilog(bboxFileSeq), annotationSeq = annotationSeq) { dut =>
dut.io.A.poke(1.U)
dut.io.B.poke(2.U)
dut.io.CI.poke(0.U)
dut.io.Z.expect(3.U)
dut.io.CO.expect(0.U)
}
}
}
}