跳转至

在 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 代码:

module xxx(
    // ...
);
  // ...
    $readmemh(path, mem_xxx);
endmodule

bind TLRAM xxx xxx(.*);

这里采用了 Verilog 的 bind 功能,可以在不修改模块代码的时候注入,比如上面,就是注入了一个语句 $readmemh,从而达到目的。

评论