Chipyardの環境ではScalaで実装しているRocket-ChipやBOOMコアもインスタンス化することができるが、それ以外にSystemVerilogで実装されているArianeもシミュレーションを実行することができる。
つまり、SystemVerilogで実装した独自CPUコアに対してもChipyardの環境を構築することができるということだ。この方法について調査しようと思う。
ArianeのChipyard用Wrapper
ArianeはSystemVerilogで記述されているため、これをそのままChiselベースのChipyardに載せることは出来ない。このため、Chiselで書いたSystemVerilog用のWrapperを用いることになる。

ここで大事なのはArinaneCoreBlackBoxで、ここにはSystemVerilogのデザインをラップするための機能が入っている。
src/main/scala/ArianeCoreBlackbox.scala
class ArianeCoreBlackbox( traceportEnabled: Boolean, traceportSz: Int, xLen: Int, rasEntries: Int, btbEntries: Int, bhtEntries: Int, execRegAvail: Int = 5, ... extends BlackBox( Map( "TRACEPORT_SZ" -> IntParam(traceportSz), "XLEN" -> IntParam(xLen), "RAS_ENTRIES" -> IntParam(rasEntries), "BTB_ENTRIES" -> IntParam(btbEntries), "BHT_ENTRIES" -> IntParam(bhtEntries), "EXEC_REG_CNT" -> IntParam(exeRegCnt), "CACHE_REG_CNT" -> IntParam(cacheRegCnt),
このデザインを見て初めて知ったのだが、Chiselの変数をパラメータとして伝搬させることもできるのか。知らなかった。
I/Oの接続は、I/Oポートを宣言してこれをTileで接続するようになっていた。
src/main/scala/ArianeCoreBlackbox.scala
with HasBlackBoxResource { val io = IO(new Bundle { val clk_i = Input(Clock()) val rst_ni = Input(Bool()) val boot_addr_i = Input(UInt(64.W)) val hart_id_i = Input(UInt(64.W)) val irq_i = Input(UInt(2.W)) val ipi_i = Input(Bool()) val time_irq_i = Input(Bool()) val debug_req_i = Input(Bool()) val trace_o = Output(UInt(traceportSz.W)) val axi_resp_i_aw_ready = Input(Bool()) val axi_req_o_aw_valid = Output(Bool()) val axi_req_o_aw_bits_id = Output(UInt(axiIdWidth.W)) ...
最終的なI/Oの接続はTile上で行われている。これはArianeのI/OがAXIのようなのでこれをそのままつなげたようだ。
src/main/scala/ArianeTile.scala
// connect the axi interface outer.memAXI4Node.out foreach { case (out, edgeOut) => core.io.axi_resp_i_aw_ready := out.aw.ready out.aw.valid := core.io.axi_req_o_aw_valid out.aw.bits.id := core.io.axi_req_o_aw_bits_id out.aw.bits.addr := core.io.axi_req_o_aw_bits_addr out.aw.bits.len := core.io.axi_req_o_aw_bits_len out.aw.bits.size := core.io.axi_req_o_aw_bits_size
このように、ポートの規格を統一すれば独自コアでもChipyardの環境を構築することができる。今度はこれに挑戦してみよう。