RoCC (Rocket Custom Coprocessor) のチュートリアルって意外と少ないので、調査するのに苦労する。
githubにチュートリアルっぽいものがあったので、試行してみようと思った。
これは、RocketCoreのカスタムデザインの中でアキュムレータを内蔵しているデザインがあるので、これをソフトウェアで叩くアプリケーションを作ってみる、という話になっているようだ。
RocketCoreを生成する際に、CONFIGオプションとしてRoccExampleConfig
を指定することで、RoCCにアクセラレータが接続されたデザインを生成することができる。
ちなみに、RoccExampleConfig
を指定することで、以下のConfig.scalaの記述の構成が有効になる。
class RoccExampleConfig extends Config(new WithRoccExample ++ new BaseConfig)
うーん、難しいねえ。WithRoccExample
というのは何だ?
class WithRoccExample extends Config( (pname, site, here) => pname match { case BuildRoCC => Seq( RoccParameters( opcodes = OpcodeSet.custom0, generator = (p: Parameters) => Module(new AccumulatorExample()(p))), RoccParameters( opcodes = OpcodeSet.custom1, generator = (p: Parameters) => Module(new TranslatorExample()(p)), nPTWPorts = 1), RoccParameters( opcodes = OpcodeSet.custom2, generator = (p: Parameters) => Module(new CharacterCountExample()(p)))) case RoccMaxTaggedMemXacts => 1 case _ => throw new CDEMatchError })
なるほど、custom0, custom1, custom2 のカスタム命令に対して、それぞれ AccumulatorExample, TranslatorExample, CharacterCountExampleのデザインを接続しているように見える。それぞれ、
- AccumulatorExample (src/main/scala/rocket/Rocc.scala)
- TranslatorExample (src/main/scala/rocket/Rocc.scala)
- CharacterCountExample (src/main/scala/rocket/Rocc.scala)
class AccumulatorExample(n: Int = 4)(implicit p: Parameters) extends RoCC()(p) { val regfile = Mem(n, UInt(width = xLen)) val busy = Reg(init = Vec.fill(n){Bool(false)}) ... // datapath val addend = cmd.bits.rs1 val accum = regfile(addr) val wdata = Mux(doWrite, addend, accum + addend) when (cmd.fire() && (doWrite || doAccum)) { regfile(addr) := wdata }
本質は↑のような気がしていて、レジスタファイルがアキュムレータとして動作しており、cmd.bits.rs1で指定した数を加算しているように見える。 メモリアクセスを実行すると同じようにレジスタファイルにデータをロードしているようにも見えるね。詳細仕様がないので正直良く分からない。
このRoccExampleConfigを有効にしてRocketCoreをビルドするためには、RocketChipのリポジトリ内でemulatorディレクトリに移動し、以下のようにタイプする。
make CONFIG=RoccExampleConfig
これでビルドが完了する。次に、Accumulatorのテストプログラムを動作させるためにrocket-rocc-exampleリポジトリに格納されているパッチを当てた。
git clone https://github.com/seldridge/rocket-rocc-examples.git cd ../rocket-chip/riscv-tools/riscv-pk git apply ../../../rocket-rocc-examples/patches/riscv-pk.patch mkdir build cd build ../configure --prefix=$RISCV/riscv64-unknown-elf --host=riscv64-unknown-elf make make install
で、環境変数RISCVで指定されているツールセットのディレクトリで、pk(proxy kernel)が更新される。
ちなみに、パッチの内容だが、query-mem()
の設定をすべて取り払っており、メモリサイズを固定している。なんだこりゃ?
diff --git a/machine/configstring.c b/machine/configstring.c index fb2fed7..f3f21d2 100644 --- a/machine/configstring.c +++ b/machine/configstring.c @@ -6,12 +6,7 @@ static void query_mem(const char* config_string) { - query_result res = query_config_string(config_string, "ram{0{addr"); - assert(res.start); - uintptr_t base = get_uint(res); - assert(base == DRAM_BASE); - res = query_config_string(config_string, "ram{0{size"); - mem_size = get_uint(res); + mem_size = 0x10000000; } static void query_rtc(const char* config_string) diff --git a/machine/minit.c b/machine/minit.c index b3f2c86..54ff88e 100644 --- a/machine/minit.c +++ b/machine/minit.c @@ -19,6 +19,7 @@ static void mstatus_init() uintptr_t ms = 0; ms = INSERT_FIELD(ms, MSTATUS_VM, VM_CHOICE); ms = INSERT_FIELD(ms, MSTATUS_FS, 1); + ms = INSERT_FIELD(ms, MSTATUS_XS, 1); write_csr(mstatus, ms); // Make sure the hart actually supports the VM mode we want
次にテストプログラムのビルドを行った。テストプログラムはrocket-rocc-exampleリポジトリに入っているので、そのリポジトリに戻ってmakeを実行するだけでよい。
cd ../../../rocket-rocc-exsamples
make
これで、rocket-rocc-examples/build/test-accumulator
にテストプログラムが作られた。
このテストプログラム、上手く動作するようならば、以下のようにAccumulatorを動かしてログが出力されるはずである。
[INFO] Write R[1] = 0xdead [INFO] Read R[1] [INFO] Received 0xdead [INFO] Accum R[1] with 0xffffffffffffe042 [INFO] Read R[1] [INFO] Received 0xbeef [INFO] Load 0xbad (virt: 0x0xfeefac0, phys: 0x0x8ffffac0) via L1 data cache [INFO] Read R[1] [INFO] Received 0xbad Completed after 5614110 cycles
ところが、今回同様にテストプログラムを走らせると、アサーションエラーとなった。
$ ./emulator-rocketchip-RoccExampleConfig ~/riscv64/riscv64-unknown-elf/riscv64-unknown-elf/bin/pk ../../rocket-rocc-examples/build/test-accumulator ../pk/elf.c:46: assertion failed: !(eh.e_flags & EF_RISCV_RVC)
これは、pk/elf.c
の以下の記述が問題らしい。RVCというのはCompressed Instructionのことで、RISC-Vの16bit命令セットのことだ。これで落ちたということは、__riscv_compressed
が指定されていなかったにもかかわらず、EF_RISCV_RVC
フラグが有効になっていたということかしら。
とりあえず、ここの記述をコメントアウトしてみるとどうだろう。
#ifndef __riscv_compressed assert(!(eh.e_flags & EF_RISCV_RVC)); #endif
mkdir build ../configure --prefix=$RISCV/riscv64-unknown-elf --host=riscv64-unknown-elf && make && make install
これで再度RocketChipを実行してみる。
$ ./emulator-rocketchip-RoccExampleConfig ~/riscv64/riscv64-unknown-elf/riscv64-unknown-elf/bin/pk ../../rocket-rocc-examples/build/test-accumulator [INFO] Write R[1] = 0xdead [INFO] Read R[1] [INFO] Received 0xdead [INFO] Accum R[1] with 0xffffffffffffe042 [INFO] Read R[1] [INFO] Received 0xbeef [INFO] Load 0xbad (virt: 0x0xfeefae0, phys: 0x0x8ffffae0) via L1 data cache [INFO] Read R[1] [INFO] Received 0xbad
おお、所望通りに動作した。次に、このカスタムデザインの中身を読み解いていこう。