FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

RISC-Vのptパタン(timer付きパタン)の調査

http://bar.eecs.berkeley.edu/projects/images/projects/2010-riscv.png

画像: UCB-BAR: RISC-V Instruction Set Architecture より。

ISSRISC-Vのタイマー機能を搭載したら、pt系のパタンが軒並み落ちるようになった。 もともとあまりカバーしていなかった部分なので、これを機にパタンが通るように修正しようとしたのだが、だいぶ詰まってしまっている。

ptパタンとは

RISC-VのISAパタンセットにはいくつかの種類が存在する。このなかでptパタンは、「仮想アドレスモードを使用しない、何サイクルかに一回タイマ割り込みが発生する」というパタンだ。

その他にも、以下のようなパタンセットが用意されている。

GitHub - riscv/riscv-tests at c9022d2f63f50388b2ab1192966f30dbe7819a59 より抜粋。

f:id:msyksphinz:20160513002704p:plain

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システムレジスタを比較し、値が一致したらタイマ割り込みが発生するという仕組みだ。

f:id:msyksphinz:20160513003020p:plain

したがって、ある特定サイクル後にタイマを発生したいとなれば、現在のタイマのカウント値を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;                                            \