I am trying to simulate a system using chisel 3. The system has a blackbox that has a verilog. The verilog code is not behavioural, it simply instantiate a module that the synthesizer configures.I know the behaviour of the module and want to write a code in chisel to simulate the behaviour.
So basically how to extend a blackbox in chisel 3 with a behaviour that could be used in simulation.
Currently there is no built-in way to directly provide a behavioral model to substitute a blackbox when testing chisel3 code. However, there are some options that could work in your situation:
SyncReadMem
and substitute it with --repl-seq-mem
For memories in particular, you can used the built-in SyncReadMem
which will work fine in all simulation and even formal verification backends. Then for your "tape-out" / FPGA synthesis you replace all synchronous read memories with Verilog code that uses the Vendor provided memory. This flow is used by the open-source chipyard project for ASIC tapeout. You essentially need to pass the following flags to the firrtl compiler: --infer-rw --repl-seq-mem
which will then automatically blackbox all SyncReadMem
instances and generate description files for them. From these files you can write Verilog implementations using the vendor provided RTL.
Hint: You can use --gen-mem-verilog
to get a blueprint for the Verilog modules you need to implement in terms of the Xilinx block.
SyncReadMem
and try to get BRAM interference workingYou should be able to get Chisel SyncReadMem
to be correctly inferred as BRAM by the Xilinx tool. This options is afaik used by the open-source firesim project to generate RTL for Xilinx FPGAs. The flags you would want to pass to the firrtl compiler are: --infer-rw --target:fpga
This option is the most versatile, but also requires the most work. Here is a quick draft of what that may look like:
import chisel3._
class MemIO extends Bundle {
val addr = Input(UInt(4.W))
val doWrite = Input(Bool())
val dataIn = Input(UInt(8.W))
val dataOut = Output(UInt(8.W))
}
class MemBlackBox extends BlackBox {
val io = IO(new MemIO)
// ...
}
class MemBehavioral extends Module {
val io = IO(new MemIO)
// ...
io <> DontCare // to make things compile
}
class Memory(simulation: Boolean) extends Module {
val io = IO(new MemIO)
if(simulation) {
val inner = Module(new MemBehavioral) ; inner.io <> io
} else {
val inner = Module(new MemBlackBox) ; inner.io <> io
}
}
val pretty = Array(
"--emission-options", "disableMemRandomization,disableRegisterRandomization"
)
println("Behavioral")
println(getVerilogString(new Memory(simulation = true), pretty))
println("\n\nSynthesizable")
println(getVerilogString(new Memory(simulation = false), pretty))
You can see the output on scasti.