RISC-Vのシミュレータは、シミュレーション対象のプログラムのElfファイル以外に、いくつかの外部ライブラリをロードしている。
- RISC-V の Device Tree (SpikeのビルドにDevice Tree Compiler が必要なのはこのためだ)
- RISC-V の Proxy Kernel (I/Oなどの本来OSが行う仕事を肩代わりしている)
図. RISC-V Spike Instruction Set Simulatorの入出力ファイル
まず、Proxy Kernelについては libpk.a
ではなく ${RISCV}/riscv-unknown-elf/bin/pk
を観察する。
Proxy Kernel のエントリポイントは0x8000_0000 に設定されているため、この場所からプログラムの実行がスタートする。
の前に、本当に最初に始まるのはデバッグコードの実行だ。これはSpikeに以下のように記述されている。
見てわかる通り、このコードは最初にstart_pcのコードにジャンプする。これが上記の${RISCV}/riscv-unknown-elf/bin/pk
に相当する。
- ./riscv-isa-sim/riscv/sim.cc
uint32_t reset_vec[reset_vec_size] = { 0x297, // auipc t0,0x0 0x28593 + (reset_vec_size * 4 << 20), // addi a1, t0, &dtb 0xf1402573, // csrr a0, mhartid get_core(0)->get_xlen() == 32 ? 0x0182a283u : // lw t0,24(t0) 0x0182b283u, // ld t0,24(t0) 0x28067, // jr t0 0, (uint32_t) (start_pc & 0xffffffff), (uint32_t) (start_pc >> 32) };
次にDevice Treeだが、これは何とSpikeが自身でDevice Tree Compilerを動作させて生成するようになっている。
例えば、デフォルトのSpikeを立ち上げたときに生成されるDTSは以下のようになる。これはSpike の --dump-dts
で確認できる。
$ spike --dump-dts test_output_c /dts-v1/; / { #address-cells = <2>; #size-cells = <2>; compatible = "ucbbar,spike-bare-dev"; model = "ucbbar,spike-bare"; cpus { #address-cells = <1>; #size-cells = <0>; timebase-frequency = <10000000>; CPU0: cpu@0 { device_type = "cpu"; reg = <0>; status = "okay"; compatible = "riscv"; riscv,isa = "rv64imafdc"; mmu-type = "riscv,sv48"; clock-frequency = <1000000000>; CPU0_intc: interrupt-controller { #interrupt-cells = <1>; interrupt-controller; compatible = "riscv,cpu-intc"; }; }; }; memory@80000000 { device_type = "memory"; reg = <0x0 0x80000000 0x0 0x80000000>; }; soc { #address-cells = <2>; #size-cells = <2>; compatible = "ucbbar,spike-bare-soc", "simple-bus"; ranges; clint@2000000 { compatible = "riscv,clint0"; interrupts-extended = <&CPU0_intc 3 &CPU0_intc 7 >; reg = <0x0 0x2000000 0x0 0xc0000>; }; }; htif { compatible = "ucb,htif0"; }; };
これをコンパイルしてDTBを作成し、これをブートコードの後ろに挿入している。
- ./riscv-isa-sim/riscv/sim.cc
dts = s.str(); std::string dtb = dts_compile(dts); rom.insert(rom.end(), dtb.begin(), dtb.end()); const int align = 0x1000; rom.resize((rom.size() + align - 1) / align * align); boot_rom.reset(new rom_device_t(rom)); bus.add_device(DEFAULT_RSTVEC, boot_rom.get());