FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

Chisel + Diplomacyで自作SoCを設計する検討

Chiselを使ったDiplomacyのデザイン、久しぶりに再会した。いくつか例を作ってみたいと思うので、大昔に作った自作のRISC-V Chisel CPUコアをDiplomacyで載せ替えて、簡単にバス接続が実現できる様子を観察してみたい。

まず、大昔に用意したCPUコアは以下のインタフェースを持っている。簡単な命令バスとデータバスが並んでいる。

class CpuIo [Conf <: RVConfig](conf: Conf) extends Bundle {
  val run      = Input(Bool())

  val inst_bus = new InstBus(conf)
  val data_bus = new DataBus(conf)

  val dbg_monitor = new CpuDebugMonitor(conf)
}

これをTileLinkに乗せ換えるのがとりあえず手っ取り早そうだ。まずはDiplomacyを使ったTileLinkのラッパーモジュールを作った。

class CoreTop(name: String)(implicit p: Parameters) extends LazyModule {
  val inst_node = TLClientNode(Seq(TLClientPortParameters(Seq(TLClientParameters(name = name + "_inst")))))
  val data_node = TLClientNode(Seq(TLClientPortParameters(Seq(TLClientParameters(name = name + "_name")))))

  lazy val module = new LazyModuleImp(this) {
    val io = IO(new Bundle {
      val run = Input(Bool())

    val cpu = Module(new Cpu(new RV64IConfig))

    cpu.io.run := true.B
...

CoreTopモジュール内でLazyModuleImpを作成し、その中でcpuインスタンス化している。このcpuモジュールに対してTileLinkのI/Oを接続していくという訳だ。

    inst_out.a.valid := cpu.io.inst_bus.req
    inst_out.a.bits.address := cpu.io.inst_bus.addr
    inst_out.a.bits.opcode := TLMessages.Get
    inst_out.a.bits.param := 0.U
    cpu.io.inst_bus.ack := inst_out.d.valid
    cpu.io.inst_bus.rddata := inst_out.d.bits.data.asSInt

    data_out.a.valid := cpu.io.data_bus.req
    data_out.a.bits.address := cpu.io.data_bus.addr
    data_out.a.bits.opcode := TLMessages.Get
    data_out.a.bits.param := 0.U
    cpu.io.data_bus.ack := data_out.d.valid
    cpu.io.data_bus.rddata := data_out.d.bits.data.asSInt

このようにして作成したCoreTopモジュールは、命令バスとデータバスの2つのノードが存在していることになる。これらをSoC内で接続するためには以下のようにする。

class core_complex(ramBeatBytes: Int, txns: Int)(implicit p: Parameters) extends LazyModule {
  val loader = LazyModule(new loader("loader"))

  // val ifu    = LazyModule(new ifu("ifu"))
  val core   = LazyModule(new CoreTop("core"))
  val xbar   = LazyModule(new TLXbar)
  val memory = LazyModule(new TLRAM(AddressSet(0x02000, 0x0fff), beatBytes = ramBeatBytes))

  xbar.node := loader.node
  xbar.node := TLDelayer(0.0001) := core.inst_node
  xbar.node := TLDelayer(0.0001) := core.data_node
  memory.node := xbar.node

CoreTopLazyModuleインスタンス化し、core.inst_nodecore.data_nodeクロスバーに接続している。

  xbar.node := TLDelayer(0.0001) := core.inst_node
  xbar.node := TLDelayer(0.0001) := core.data_node

この構成について、GraphMLによるノード関係を出力してみると以下のようになった。coreから2つのノードが出ており、最終的にxbarを経由してmemoryに繋がっている。上手く行っているようだ。

f:id:msyksphinz:20201121013824p:plain