其實RISC-V官方也有開發了一個instruction accurate等級的模擬器Spike,只是主要以C++撰寫。從提交紀錄看起來,這個模擬器已經開發超過十年,支援的指令集也相當豐富,可以作為後續驗證的golden。這次嘗試從原始碼編譯整個模擬器,並且實際執行一個簡單的RISC-V程式,看看Spike可以提供給我們那些資訊。
官方已經提供了執行檔,下載後將其加入PATH即可。
# Setup RISC-V toolchain
wget https://github.com/riscv/riscv-gnu-toolchain/releases/download/2021.06.26/riscv32-elf-ubuntu-20.04-nightly-2021.06.26-nightly.tar.gz
tar -xvf riscv32-elf-ubuntu-20.04-nightly-2021.06.26-nightly.tar.gz
mv riscv riscv-toolchain
export PATH=$(realpath riscv-toolchain/bin):$PATH
官方說明如下:
It is designed to support tethered RISC-V implementations with limited I/O capability and thus handles I/O-related system calls by proxying them to a host computer.
簡單來說proxy kernel能將Spike的I/O系統呼叫重新導向host。由於Spike本身沒有模擬外部的I/O裝置,因此需要proxy kernel才能讓這些I/O操作能正常運作。
# Compile the proxy kernel
git clone https://github.com/riscv/riscv-pk.git
cd riscv-pk
mkdir build
cd build
../configure --prefix=$(realpath ../../riscv-toolchain) --host=riscv32-unknown-elf
make
cd ../..
終於可以編譯模擬器本身了,過程與編譯proxy kernel類似,只是需要額外安裝device-tree-compiler。
# Compile Spike simulator
sudo apt install device-tree-compiler
git clone https://github.com/riscv/riscv-isa-sim.git
cd riscv-isa-sim
mkdir build
cd build
../configure
make
cd ../..
int main() {
return 0;
}
./riscv-toolchain/bin/riscv32-unknown-elf-gcc -o main.elf main.c
加上-l
選項可以看到執行結果,並且指定isa為RV32,才能執行剛剛編譯出來的ELF檔。
./riscv-isa-sim/build/spike -l --isa=RV32 ./riscv-pk/build/pk main.elf
可以看到以下輸出:
core 0: 0x00001000 (0x00000297) auipc t0, 0x0
core 0: 0x00001004 (0x02028593) addi a1, t0, 32
core 0: 0x00001008 (0xf1402573) csrr a0, mhartid
core 0: 0x0000100c (0x0182a283) lw t0, 24(t0)
core 0: 0x00001010 (0x00028067) jr t0
core 0: 0x80000000 (0x1f80006f) j pc + 0x1f8
core 0: 0x800001f8 (0x00000093) li ra, 0
core 0: 0x800001fc (0x00000113) li sp, 0
core 0: 0x80000200 (0x00000193) li gp, 0
core 0: 0x80000204 (0x00000213) li tp, 0
core 0: 0x80000208 (0x00000293) li t0, 0
...
每行的格式固定,依序為core ID、目前PC address、指令hex、以及相對應的反組譯結果。當我們實作完指令decoder之後,就可以使用這個PC trace,來驗證我們實作的正確性。