You can use the Scala FIRRTL Compiler's "Replace Sequential Memories" pass to blackbox the memories. This is exactly what is happening with Rocket Chip.
Note that this is limited to only work if the memories have a single read port and a single write port and with read latency 1 and write latency 1.
As an example, consider the following 1r1w (one read, one write) SyncReadMem
:
import chisel3._
class Foo extends MultiIOModule {
val read = IO(new Bundle {
val en = Input(Bool())
val addr = Input(UInt(8.W))
val data = Output(UInt(1.W))
})
val write = IO(new Bundle{
val en = Input(Bool())
val addr = Input(UInt(8.W))
val data = Input(UInt(1.W))
})
val bar = SyncReadMem(256, UInt(1.W))
read.data := bar.read(read.addr, read.en)
when (write.en) {
bar.write(write.addr, write.data)
}
}
If you compile this with a request to run the replace sequential memories pass:
(new ChiselStage)
.emitVerilog(new Foo, Array("--repl-seq-mem", "-c:Foo:-o:Foo.mem.conf"))
The arguments used there are -c:<circuit>
where <circuit>
is the name of the circuit you want to run on and -o:<mem-conf-file>
is the name of a file to generate that will contain information (e.g., name, width, and depth) of the memories that were blackboxed.
You wind up with the memory blackboxed inside a new module bar
called bar_ext
:
module bar(
input [7:0] R0_addr,
input R0_en,
input R0_clk,
output R0_data,
input [7:0] W0_addr,
input W0_en,
input W0_clk,
input W0_data
);
wire [7:0] bar_ext_R0_addr;
wire bar_ext_R0_en;
wire bar_ext_R0_clk;
wire bar_ext_R0_data;
wire [7:0] bar_ext_W0_addr;
wire bar_ext_W0_en;
wire bar_ext_W0_clk;
wire bar_ext_W0_data;
bar_ext bar_ext (
.R0_addr(bar_ext_R0_addr),
.R0_en(bar_ext_R0_en),
.R0_clk(bar_ext_R0_clk),
.R0_data(bar_ext_R0_data),
.W0_addr(bar_ext_W0_addr),
.W0_en(bar_ext_W0_en),
.W0_clk(bar_ext_W0_clk),
.W0_data(bar_ext_W0_data)
);
assign bar_ext_R0_clk = R0_clk;
assign bar_ext_R0_en = R0_en;
assign bar_ext_R0_addr = R0_addr;
assign R0_data = bar_ext_R0_data;
assign bar_ext_W0_clk = W0_clk;
assign bar_ext_W0_en = W0_en;
assign bar_ext_W0_addr = W0_addr;
assign bar_ext_W0_data = W0_data;
endmodule
You can then run a memory compiler to consume the information in the memory configuration file and drop the output in place of bar_ext
.