在 Rocket Chip 上挂接 TLRAM¶
最近遇到一个需求,需要在 Rocket Chip 里面开辟一块空间,通过 verilog 的 $readmemh 来进行初始化而不是用 BootROM,这样每次修改内容不需要重新跑一次 Chisel -> Verilog 的流程。然后到处研究了一下,找到了解决的方案:
首先是新建一个 TLRAM 然后挂接到 cbus 上:
import freechips.rocketchip.tilelink.TLRAM
import freechips.rocketchip.tilelink.TLFragmenter
import freechips.rocketchip.diplomacy.LazyModule
import freechips.rocketchip.diplomacy.AddressSet
trait HasTestRAM { this: BaseSubsystem =>
val testRAM = LazyModule(
new TLRAM(AddressSet(0x40000000, 0x1FFF), beatBytes = cbus.beatBytes)
)
testRAM.node := cbus.coupleTo("bootrom") { TLFragmenter(cbus) := _ }
}
这里的地址和大小都可以自由定义。然后添加到自己的 Top Module 中:
class TestTop(implicit p:Parameters)
extends RocketSystem
// ...
with HasTestRAM
//...
{
override lazy ...
}
实际上这时候 TLRAM 就已经加入到了 TileLink 总线中。接着,为了让 firrtl 生成 $readmemh 的代码,需要两个步骤:
首先是用 chisel3.util.experimental.loadMemoryFromFile
函数(文档在 https://github.com/freechipsproject/chisel3/wiki/Chisel-Memories):
UPDATE:现在的文档在 Loading Memories for simulation or FPGA initialization 处,并且可以用 loadMemoryFromFileInline。
class TestTopImp(outer: TestTop)
extends RocketSubsystemModuleImp(outer)
// ...
{
loadMemoryFromFile(outer.testRAM.module.mem, "test.hex")
}
这个函数会生成一个 FIRRTL Annotation,记录了在这里需要对这个 mem 生成对应的 readmemh 调用。然后在 firrtl 的调用里传入 .anno.json 和 transform:
$ runMain firrtl.stage.Main -i xxx -o xxx -X verilog -faf /path/to/xxx.anno.json -fct chisel3.util.experimental.LoadMemoryTransform
UPDATE: 现在不需要 -fct chisel3.util.experimental.LoadMemoryTransform
参数。目前这个功能和生成 blackbox memory 有冲突,不能同时使用,需要等 chisel3 后续修复。
这里的 chisel3.util.experimental.LoadMemoryTransform 会找到 anno.json 里面对应的 Annotation,然后生成类似下面这样的 verilog 代码:
这里采用了 Verilog 的 bind 功能,可以在不修改模块代码的时候注入,比如上面,就是注入了一个语句 $readmemh,从而达到目的。