FPGA開発日記

FPGAというより、コンピュータアーキテクチャかもね! カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages

RustでRISC-V命令セットシミュレータを作ろう (13. RV32仮想アドレスモードの実装)

f:id:msyksphinz:20190224185310p:plain:w400

Rustで作る自作命令セットシミュレータの続き。RV32のテストは動くようになってきたので、次はRV32の仮想アドレスモードの対応について考える。 これも昔作ったC++RISC-Vシミュレータですでに対応しているので簡単なのだが、いくつかRV32とRV64で切り替えなければならない点がある。

まずはRV32とRV64で動作する仮想アドレスモードが違うということ。RV32ではMBare(物理アドレスモード)とSv32が使用できるあが、RV64ではMBareとSv39, Sv48, Sv57, Sv64が使用できる(Sv57, Sv64はまだ仕様としては未定義)。

f:id:msyksphinz:20200216121758p:plain
RISC-Vの仮想アドレスモード・RV32とRV64でサポートする仮想アドレスモードの違い

さらにSATPシステムレジスタの定義も少し異なる。これもRV64とRV32で区別しなければならない。

f:id:msyksphinz:20200216121909p:plain
SATPレジスタのRV32とRV64での仕様の違い。ビットフィールドが異なる。

したがって、まずはページテーブルウォークの際のSATP参照の実装を変更する必要がある。

  • swimmer_rust/src/riscv_mmu.rs
        let pte_base: Addr64T = match self.m_xlen {
            32 => Self::extract_bit_field(satp, 21, 0) as Addr64T,
            64 => Self::extract_bit_field(satp, 43, 0) as Addr64T,
            _ => panic!("Internal Error: XLEN should either 32 or 64"),
        };
  • swimmer_rust/src/riscv_core.rs
    fn get_vm_mode(&mut self) -> VMMode {
        let satp_val = self.m_csr.csrrs(CsrAddr::Satp, 0) as Xlen64T; // SATP
        let mode = match self.m_xlen {
            32 => Self::extract_bit_field(satp_val, 31, 31),
            64 => Self::extract_bit_field(satp_val, 63, 60),
            _ => panic!("Internal Error: XLEN should either 32 or 64"),
        };

XLENの値に応じて変更を行う。ビットフィールドの定義がRV32とRV64で異なっているためだ。

それ以外の所でケアしなければならないことはあまりない。Sv32, Sv39でビットフィールドの定義を変えつつ、実装を追加する。

  • swimmer_rust/src/riscv_mmu.rs
        if self.get_vm_mode() == VMMode::Sv39
            && (priv_mode == PrivMode::Supervisor || priv_mode == PrivMode::User)
        {
            let ppn_idx: Vec<u8> = vec![12, 21, 30];
            let pte_len: Vec<u8> = vec![9, 9, 26];
            let pte_idx: Vec<u8> = vec![10, 19, 28];
            let vpn_len: Vec<u8> = vec![9, 9, 9];
            let vpn_idx: Vec<u8> = vec![12, 21, 30];
            let pagesize: u32 = 4096; // num::pow(2, 12);
            let ptesize: u32 = 8;

            return self.walk_page_table(
                vaddr, acc_type, 3, ppn_idx, pte_len, pte_idx, vpn_len, vpn_idx, pagesize, ptesize,
            );
        } else if self.get_vm_mode() == VMMode::Sv32
            && (priv_mode == PrivMode::Supervisor || priv_mode == PrivMode::User)
        {
            let ppn_idx: Vec<u8> = vec![12, 22];
            let pte_len: Vec<u8> = vec![10, 12];
            let pte_idx: Vec<u8> = vec![10, 20];
            let vpn_len: Vec<u8> = vec![10, 10];
            let vpn_idx: Vec<u8> = vec![12, 22];
            let pagesize: u32 = 4096; // num::pow(2, 12);
            let ptesize: u32 = 4;

            return self.walk_page_table(
                vaddr, acc_type, 2, ppn_idx, pte_len, pte_idx, vpn_len, vpn_idx, pagesize, ptesize,
            );
        } else {
            return Ok(vaddr);
        }

動作確認を行う。

cargo run -- --arch rv32 riscv-tests/isa/rv32ui-v-add.bin | tee rv32ui-v-add.log
     20650:S:Sv32:ffffffffffc02234:0046a783:lw         x15,0x004(x13)     :x13=>ffffffffffc01000 (ffffffffffc01004)=>00000000 x15<=0000000000000000
     20651:S:Sv32:ffffffffffc02238:00050813:addi       x16,x10,0x000      :x10=>0000000000000001 x16<=0000000000000001
     20652:S:Sv32:ffffffffffc0223c:41f55893:srai       x17,x10,0x1f       :x10=>0000000000000001 x17<=0000000000000000
     20653:S:Sv32:ffffffffffc02240:00f76733:or         x14,x14,x15        :x14=>0000000000000000 x15=>0000000000000000 x14<=0000000000000000
     20654:S:Sv32:ffffffffffc02244:02070663:beq        x14,x00,0x2c        :x14=>0000000000000000 x00=>0000000000000000
     20655:S:Sv32:ffffffffffc02270:0106a023:sw         x16,0x0000000(x13) :x16=>0000000000000001 x13=>ffffffffffc01000 (ffffffffffc01000)<=00000001
PASS : riscv-tests/isa/rv32ui-v-add.bin

Passできたようだ。リグレッションテストで確認していこう。