自作RISC-Vシミュレータを作っている。
基本的な整数命令セットしか備えていなかったが、せっかくシステムレベル制御命令とか、システムレジスタの仕様が公開されたので、命令セットシミュレータに追加してみようと思う。 ただし、まだ発展途上の段階でガチガチに作り込むのはアレなので、ある程度仕様から自動化することを考える。 (命令の生成も同様の方法で、自動化している)
rubyの配列を用いて、システムレジスタの仕様を記載している。テーブルの1エントリ目がCSRアドレス、2エントリ目は権限(これはもうちょっと表現を考える)、3エントリ目が名前だ。 これを元に、自動的にCSRの読込関数、書き込み関数を自動的にスケルトンを生成することを考える。
テーブルから、自動的にスケルトンと、読み書きのための関数を生成する。
- 自動的に生成された関数
#include <stdint.h> uint32_t RISCV_Read_CSR (uint32_t addr) { switch (addr) { case 0x001 : return Read_FFLAGS (); case 0x002 : return Read_FRM (); case 0x003 : return Read_FSCR (); case 0xc00 : return Read_CYCLE (); case 0xc01 : return Read_TIME (); case 0xc02 : return Read_INSTRET (); case 0xc80 : return Read_CYCLEH (); case 0xc81 : return Read_TIMEH (); case 0xc82 : return Read_INSTRETH (); case 0x100 : return Read_SSTATUS (); case 0x101 : return Read_STVEC (); case 0x104 : return Read_SIE (); case 0x121 : return Read_STIMECMP (); case 0xd01 : return Read_STIME (); case 0xd81 : return Read_STIMEH (); case 0x140 : return Read_SSCRATCH (); case 0x141 : return Read_SEPC (); case 0xd42 : return Read_SCAUSE (); case 0xd43 : return Read_SBADADDR (); case 0x144 : return Read_SIP (); case 0x180 : return Read_SPTBR (); case 0x181 : return Read_SASID (); case 0x900 : return Read_CYCLEW (); case 0x901 : return Read_TIMEW (); case 0x902 : return Read_INSTRETW (); case 0x980 : return Read_CYCLEHW (); case 0x981 : return Read_TIMEHW (); case 0x982 : return Read_INSTRETHW (); case 0x200 : return Read_HSTATUS (); case 0x201 : return Read_HTVEC (); case 0x202 : return Read_HTDELEG (); case 0x221 : return Read_HTIMECMP (); case 0xe01 : return Read_HTIME (); case 0xe81 : return Read_HTIMEH (); case 0x240 : return Read_HSCRATCH (); case 0x241 : return Read_HEPC (); case 0x242 : return Read_HCAUSE (); ...
- 自動的に生成された関数テンプレート
#include <stdint.h> uint32_t Read_FFLAGS (uint32_t addr); uint32_t Read_FRM (uint32_t addr); uint32_t Read_FSCR (uint32_t addr); uint32_t Read_CYCLE (uint32_t addr); uint32_t Read_TIME (uint32_t addr); uint32_t Read_INSTRET (uint32_t addr); uint32_t Read_CYCLEH (uint32_t addr); uint32_t Read_TIMEH (uint32_t addr); uint32_t Read_INSTRETH (uint32_t addr); uint32_t Read_SSTATUS (uint32_t addr); uint32_t Read_STVEC (uint32_t addr); uint32_t Read_SIE (uint32_t addr); uint32_t Read_STIMECMP (uint32_t addr); uint32_t Read_STIME (uint32_t addr); uint32_t Read_STIMEH (uint32_t addr); uint32_t Read_SSCRATCH (uint32_t addr); uint32_t Read_SEPC (uint32_t addr); uint32_t Read_SCAUSE (uint32_t addr); uint32_t Read_SBADADDR (uint32_t addr); uint32_t Read_SIP (uint32_t addr); uint32_t Read_SPTBR (uint32_t addr); uint32_t Read_SASID (uint32_t addr); uint32_t Read_CYCLEW (uint32_t addr); uint32_t Read_TIMEW (uint32_t addr); uint32_t Read_INSTRETW (uint32_t addr); uint32_t Read_CYCLEHW (uint32_t addr); uint32_t Read_TIMEHW (uint32_t addr);
後は、各システムレジスタのインスタンスを生成する。このためには、各システムレジスタのレジスタフィールドを定義するためのテーブルと、それを元に自動的に関数操作のスケルトンを生成する必要がある。 これについては次回。