画像: UCB-BAR: RISC-V Instruction Set Architecture より。
ISSにRISC-Vのタイマー機能を搭載したら、pt系のパタンが軒並み落ちるようになった。 もともとあまりカバーしていなかった部分なので、これを機にパタンが通るように修正しようとしたのだが、だいぶ詰まってしまっている。
ptパタンとは
RISC-VのISAパタンセットにはいくつかの種類が存在する。このなかでptパタンは、「仮想アドレスモードを使用しない、何サイクルかに一回タイマ割り込みが発生する」というパタンだ。
その他にも、以下のようなパタンセットが用意されている。
GitHub - riscv/riscv-tests at c9022d2f63f50388b2ab1192966f30dbe7819a59 より抜粋。
ptパタンの処理の流れ
ptパタンはまずmie.mtipビットを有効化して、タイマ割り込みの発生を有効にする。
#define ENABLE_TIMER_INTERRUPT \ li a0, MIP_MTIP; \ csrs mie, a0; \ csrr a0, mtime; \ addi a0, a0, TIMER_INTERVAL; \ csrw mtimecmp, a0; \
RISC-Vのタイマ割り込みの発生基準はどのようになっているかというと、mtimeシステムレジスタとmtimecmpシステムレジスタを比較し、値が一致したらタイマ割り込みが発生するという仕組みだ。
したがって、ある特定サイクル後にタイマを発生したいとなれば、現在のタイマのカウント値をmtimeから取得し、それにいくつかの値を加算してmtimecmpを更新するという流れだ。
csrr a0, mtime; \ addi a0, a0, TIMER_INTERVAL; \ csrw mtimecmp, a0; \
ここで、riscv-testパタンにおいてTIMER_INTERVALの値はどうなっているかというと、
#define TIMER_INTERVAL 2
短かすぎじゃないかね!?さすがに2サイクルで更新は無理な気がするので、ちょっと伸ばしてみた。
#define TIMER_INTERVAL 100
すると、プログラムを開始するとまずはユーザモードに移る。ユーザモードでプログラム実行中にタイマ割り込みが入る仕組みだ。 ユーザモード中に割り込みが発生するとtvec_userに飛ぶのだが、
tvec_user: \ EXTRA_TVEC_USER; \ /* test whether the test came from pass/fail */ \ la t5, ecall; \ csrr t6, mepc; \ beq t5, t6, write_tohost; \ /* test whether the stvec_handler target exists */ \ la t5, stvec_handler; \ bnez t5, mrts_routine; \ /* test whether the mtvec_handler target exists */ \ la t5, mtvec_handler; \ bnez t5, mtvec_handler; \ /* some other exception occurred */ \ j other_exception; \ .align 6; \ ... #undef EXTRA_TVEC_USER #define EXTRA_TVEC_USER \ csrw mscratch, a0; \ csrr a0, mcause; \ bltz a0, _interrupt_handler; \ _skip: \
この時によく分かっていないのが、mtvec_handler, stvec_handlerは定義してもしなくても良いことになっている。 これらの変数はweak宣言がされており、ptテストパタンでは定義されていなかった。 その場合、mtvec_handlerの値はゼロになり、bnez命令でジャンプせずother_exceptionに飛んでしまう。 このあたりは、本来はどのような動作をすべきなんだろう?まだ調査不足でPASSには至っていない。
#define RVTEST_CODE_BEGIN \ .text; \ .align 6; \ .weak stvec_handler; \ .weak mtvec_handler; \