FPGA開発日記

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

自作RISC-Vシミュレータを拡張してシステムレジスタを実装する(途中)

自作RISC-Vシミュレータを作っている。

github.com

基本的な整数命令セットしか備えていなかったが、せっかくシステムレベル制御命令とか、システムレジスタの仕様が公開されたので、命令セットシミュレータに追加してみようと思う。 ただし、まだ発展途上の段階でガチガチに作り込むのはアレなので、ある程度仕様から自動化することを考える。 (命令の生成も同様の方法で、自動化している)

github.com

rubyの配列を用いて、システムレジスタの仕様を記載している。テーブルの1エントリ目がCSRアドレス、2エントリ目は権限(これはもうちょっと表現を考える)、3エントリ目が名前だ。 これを元に、自動的にCSRの読込関数、書き込み関数を自動的にスケルトンを生成することを考える。

github.com

テーブルから、自動的にスケルトンと、読み書きのための関数を生成する。

  • 自動的に生成された関数
#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);

後は、各システムレジスタインスタンスを生成する。このためには、各システムレジスタレジスタフィールドを定義するためのテーブルと、それを元に自動的に関数操作のスケルトンを生成する必要がある。 これについては次回。