自作CPUでのCLINTの実装をやりたくて、仕様を調査している。
一つは以下のドキュメントを確認している。
定義しないといけないレジスタは、MSIP, MTIME, MTIMECMPだ。
まずはソフトウェア割込みに関するレジスタはMSIPで、
Machine-mode software interrupts are generated by writing to the memory-mapped control register msip . The msip register is a 32-bit wide WARL register where the upper 31 bits are tied to 0. The least significant bit can be used to drive the MSIP bit of the mip CSR of a RISC-V hart. Other bits in the msip register are hardwired to zero. On reset, the msip register is cleared to zero.
マシンモードのソフトウェア割り込みは、メモリマップドコントロールレジスタ msip に書き込むことで生成されます。msipレジスタは32ビット幅のWARLレジスタで、上位31ビットは0に固定されています。最下位ビットはRISC-V Hartのmip CSRのMSIPビットを駆動するために使用されます。msipレジスタの他のビットは、0にハードワイヤされています。リセット時、msipレジスタは0にクリアされます。
このメモリマップI/Oを書き込むことでソフトウェア割込みが生成されるということになる。これはマルチコアの場合はMSIPの各コアに対応するビットに書き込むことでIPI(Inter Processor Interrupt)を生成できるということだと思われる。
さらにRTL実装もPULP Projectにより公開されている。これは便利だ。
さらに、Spikeでの動作確認をしておく。riscv/clint.cc
に以下の記述を入れて、動作を確認してみる。
diff --git a/riscv/clint.cc b/riscv/clint.cc index aee995bf..083cdc80 100644 --- a/riscv/clint.cc +++ b/riscv/clint.cc @@ -71,6 +71,7 @@ bool clint_t::store(reg_t addr, size_t len, const uint8_t* bytes) void clint_t::increment(reg_t inc) { + std::cerr << "clint_t::increment(" << inc << ") called, mtime = " << std::hex << mtime << ", mtimecmp[0]" << mtimecmp[0] << "\n"; if (real_time) { struct timeval now; uint64_t diff_usecs;
./riscv-isa-sim/spike -l --log-commits --isa=rv64imafdc --extlib=./libserialdevice.so --device=serialdevice,1409286144,uart --dtb=../dts/rv64imafdc.dtb --kernel=../tests/linux/Image --initrd=../tests/linux/spike_rootfs.cpio ../tests/linux/fw_jump.elf >& linux_hifive.spike.log
なるほど、CLINTが動作していることが確認できる。