ちょっと自作CPUしてみる
- Chiselを使って自作CPUして、それにTileLinkを接続して簡単なマルチコアシステムを作ってみる。
作ったCPUコア: RV64の簡単な5段パイプライン
まともに検証していないけどとりあえずriscv-tests
程度は動くCPUコアを作った。
- フェッチバス・データバスは非常に単純なもの(非TileLink / 非AXIバス)
自作CPUをどのようにしてTileLinkに組み込んでSoCを構築するか?
- 自作CPUをLazyModuleでラップする
TileLink
のノードを定義する- TileLinkのノードと自作CPUのノードを接続する
図. 自作CPUのインタフェース構成
- TileLinkのノードは2つ用意している。
- 命令フェッチ用
- データアクセス用
- どちらもTileLinkだが、自作CPUは独自バスプロトコルなので無理やり置き換えている。
- これで、
CoreTop
モジュールはTileLinkのインタフェースを2つ持ったモジュールに置き換わる
- これで、
// TileLinkで定義した命令・データバスのClientNodeを内部に引っ張ってくる val (inst_out, inst_edge) = inst_node.out(0) val (data_out, data_edge) = data_node.out(0) val baseEnd = 0 val (sizeEnd, sizeOff) = (inst_edge.bundle.sizeBits + baseEnd, baseEnd) val (sourceEnd, sourceOff) = (inst_edge.bundle.sourceBits + sizeEnd, sizeEnd) val beatBytes = inst_edge.bundle.dataBits // 自作CPUのインスタンス定義 val cpu = Module(new Cpu(conf, hartid)) // TileLinkのフェッチポートの信号と、自作CPUのフェッチポート(独自プロトコル)を接続 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 ... cpu.io.inst_bus.ready := inst_out.a.ready cpu.io.inst_bus.ack := inst_out.d.valid cpu.io.inst_bus.rddata := inst_out.d.bits.data.asSInt // TileLinkのデータアクセスポートの信号と、自作CPUのデータアクセスポート(独自プロトコル)を接続 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 ... cpu.io.data_bus.ack := data_out.d.valid cpu.io.data_bus.rddata := data_out.d.bits.data.asSInt
TileLinkモジュールを持っCoreTopを使ってSoCを作る
CoreTop
とメモリモジュールを持つCoreComplex
モジュールを作成してこれらを接続する。
class core_complex[Conf <: RVConfig] (conf: Conf, numCores: Int, ramBeatBytes: Int, txns: Int)(implicit p: Parameters) extends LazyModule { // 外部からメモリにデータアクセスするためのデータローダ val loader = LazyModule(new loader("loader")) // CPUコア(numCore分だけ複数個インスタンス化する) val core = Seq.tabulate(numCores) { case i => LazyModule(new CoreTop(conf, i, "core" + i.toString)) } // TileLinkのクロスバ val xbar = LazyModule(new TLXbar) // TileLinkインタフェースを持つメモリ val memory = LazyModule(new TLRAM(AddressSet(0x80000000L, 0x0ffff), beatBytes = ramBeatBytes)) // クロスバにデータローダを接続する xbar.node := loader.node core.foreach { case (core) => { // クラスバにCPUコアの命令フェッチポートとデータポートを接続する xbar.node := TLDelayer(0.5) := core.inst_node xbar.node := TLDelayer(0.5) := core.data_node } } // クロスバをメモリに接続する memory.node := xbar.node
図. 自作CPUをTileLinkを通じてメモリに接続し、簡単なSoCを構成する
numCores
を>1にして簡単にマルチコア構成を実現
上記のChiselの記述ならば、簡単にマルチコアにできる。nCores
に2, 3, ... を設定するだけで自動的にクロスバに複数のCPUが接続されるようになる。
図. 自作CPUをTileLinkを通じてメモリに接続する。コア数=4の場合