自作CPUの構成をマルチコアに変更したので、いくつか問題が表面化してしまっている。例えばメモリは1サイクルでアクセスできる仮定にしていたのを、いくつかValid & Readyの方式に実装し直さなければならない。このためには、とりあえずTileLinkにDelayerを挿入したうえで1コアに絞ってデバッグした方がなにかとやりやすい。という訳でコアの数をConfigurableにする。
まずはコア数はTestHarnessでnumCoresとして宣言する。
class TestHarness()(implicit p: Parameters) extends Module { val rv_conf = new RV64IConfig val numCores = 2
これをcore_complexにパラメータとして伝える。
val ldut = LazyModule(new core_complex(rv_conf, numCores = numCores, 4, 5000))
core_complexではCPUのインスタンスを以下のように書き換え、numCoresに基づいて複数のCPUをインスタンス化できるようにしている。
class core_complex[Conf <: RVConfig] (conf: Conf, numCores: Int, ramBeatBytes: Int, txns: Int)(implicit p: Parameters) extends LazyModule { val loader = LazyModule(new loader("loader")) val core = Seq.fill(numCores) { LazyModule(new CoreTop(conf, 0, "core")) }
バスの接続だが、オブジェクトに対するforeachを用いてバスに対する接続を繰り返し記述するようにする。
xbar.node := loader.node
core.foreach { case (core) => {
xbar.node := TLDelayer(0.1) := core.inst_node
xbar.node := TLDelayer(0.1) := core.data_node
}
}
memory.node := xbar.node
デバッグポートだが、これもコアの数だけVecの配列として宣言し、それぞれのコアに対して接続するようにした。
lazy val module = new LazyModuleImp(this) { val io = IO(new Bundle { val req = Input(Bool()) val addr = Input(UInt(32.W)) val data = Input(UInt(32.W)) val ready = Output(Bool()) val cpu_dbg = Output(Vec(numCores, new CpuDebugMonitor(conf))) })
// CPU Core Contorl val cpu_run = Seq.fill(numCores) { RegInit(false.B) } cpu_run.foreach { case(cpu_run) => cpu_run := Mux(io.req && io.req && (io.addr === 0x20000000.U), io.data(0), cpu_run) } core.zip(cpu_run).foreach { case (core, cpu_run) => core.module.io.run := cpu_run } io.cpu_dbg := core.map { case(core) => core.module.io.dbg_monitor }
これにより、コアの数を自由に変更することができるようになった。例えば、numCores=16としてみる。

くそダサいが、一応こんなことができる。16コアの命令バスとデータバスを直接メモリに繋げるとこんなことができる、ということだ。
とりあえず1コアに戻してデバッグしよう。