FPGA開発日記

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

Binary Translation型エミュレータを作る(システムレジスタアクセスの実装)

Binary Translation型エミュレータにおいてシステムレジスタアクセスを実装している。システムレジスタアクセスのためのCSR命令は、Helper関数を呼び出してRustの関数側で複雑な処理を実行する。前回は関数の呼び出し側を実装し、Rustの簡単な関数を呼び出すことに成功したので、今回はこの複雑な処理を実装する。

CSRをRustで実装する

まず、RISC-VのCSRレジスタを実装する。Rustで実装する必要があるのだが、これは依然作ったインタプリタ型のRISC-Vシミュレータから実装を拝借する。

  • src/riscv_csr.rs
pub struct RiscvCsrBase<W> {
    pub m_csr: W,
}

impl RiscvCsrBase<i64> {
    pub fn new() -> RiscvCsrBase<i64> {
        RiscvCsrBase { m_csr: 0x0 }
    }

    fn csrrw(&mut self, imm: i64) -> i64 {
        let ret_val: i64 = self.m_csr;
        self.m_csr = imm;
        return ret_val;
    }

    fn csrrs(&mut self, imm: i64) -> i64 {
        let ret_val: i64 = self.m_csr;
        self.m_csr = self.m_csr | imm;
        return ret_val;
    }

    fn csrrc(&mut self, imm: i64) -> i64 {
        let ret_val: i64 = self.m_csr;
        self.m_csr = self.m_csr & !imm;
        return ret_val;
    }
}

impl CsrAddr {
    pub fn from_u64(n: u64) -> CsrAddr {
        match n {
            0x001 => CsrAddr::FFlags,
            // 0xc00 => CsrAddr::Cycle        ,
            // 0xc02 => CsrAddr::Instret      ,
            // 0xc03 => CsrAddr::Hpmcounter3  ,
            // 0xc04 => CsrAddr::Hpmcounter4  ,
            // 0xc05 => CsrAddr::Hpmcounter5  ,
...
            
impl RiscvCsr<i64> {
    pub fn new() -> RiscvCsr<i64> {
        RiscvCsr {
            m_fflags: RiscvCsrBase::<i64>::new(),
            m_mcycle: RiscvCsrBase::<i64>::new(),
            m_minstret: RiscvCsrBase::<i64>::new(),
            m_mimpid: RiscvCsrBase::<i64>::new(),
            m_marchid: RiscvCsrBase::<i64>::new(),
...

複数のヘルパー関数をサポートする

前回のヘルパー関数は1つだったのだが、これを複数定義してhelper_csrrw配列に登録した。

impl EmuEnv {
    pub fn new() -> EmuEnv {
        EmuEnv {
            head: [0xdeadbeef; 1],
            m_regs: [0; 32],
            m_pc: [0x0; 1],
            m_csr: RiscvCsr::new(),

            helper_csrrw: [
                Self::dummy_helper_csrrw,
                Self::dummy_helper_csrrs,
                Self::dummy_helper_csrrc,
                Self::dummy_helper_csrrwi,
                Self::dummy_helper_csrrsi,
                Self::dummy_helper_csrrci,
            ],
...

各ヘルパー関数のオフセット計算は以下のように改造した。引数によって取得できるヘルパー関数のアドレスが取得できる。

    pub fn calc_helper_func_relat_address(&self, csr_helper_idx: usize) -> isize {
        let csr_helper_func_ptr =
            unsafe { self.helper_csrrw.as_ptr().offset(csr_helper_idx as isize) as *const u8 };
        let self_ptr = self.head.as_ptr() as *const u8;
        let diff = unsafe { csr_helper_func_ptr.offset_from(self_ptr) };
        diff
    }

dummy_helper_csrrw()は以下のように拡張した。

    fn dummy_helper_csrrw(emu: &mut EmuEnv, dest: u32, source: u32, csr_addr: u32) -> usize {
        println!(
            "helper_csrrw(emu, {:}, {:}, 0x{:03x}) is called!",
            dest, source, csr_addr
        );
        let data = emu.m_regs[source as usize];
        let reg_data = emu
            .m_csr
            .csrrw(CsrAddr::from_u64(csr_addr as u64), data as i64);
        emu.m_regs[dest as usize] = reg_data as u64;
        emu.dump_gpr();
        return 0;
    }

上記で定義したm_csr.csrrw()を呼び出すことでCSRレジスタの操作を行う。

実行結果

以下のようなテストコードをコンパイルしてテストを実行する。

  • csr_test.S
    lui     x10, 0x12345
    addi    x10, x10, 0x678
    csrrw   x11, mtvec, x10

    lui     x10, 0xabcde
    addi    x10, x10, 0x3ef
    csrrs   x12, mtvec, x10

    lui     x10, 0x76543
    addi    x10, x10, 0x210
    csrrc   x13, mtvec, x10
    
    csrrw   x14, mtvec, x0

x10には0x12345678が設定されている。x11にはmtvecの値が格納され(現在は0)、mtvecに0x12345678が設定される。

helper_csrrw(emu, 11, 10, 0x305) is called!
x00 = 0000000000000000  x01 = 0000000000000000  x02 = 0000000000000000  x03 = 0000000000000000
x04 = 0000000000000000  x05 = 0000000000000000  x06 = 0000000000000000  x07 = 0000000000000000
x08 = 0000000000000000  x09 = 0000000000000000  x10 = 0000000012345678  x11 = 0000000000000000
x12 = 0000000000000000  x13 = 0000000000000000  x14 = 0000000000000000  x15 = 0000000000000000
x16 = 0000000000000000  x17 = 0000000000000000  x18 = 0000000000000000  x19 = 0000000000000000
x20 = 0000000000000000  x21 = 0000000000000000  x22 = 0000000000000000  x23 = 0000000000000000
x24 = 0000000000000000  x25 = 0000000000000000  x26 = 0000000000000000  x27 = 0000000000000000
x28 = 0000000000000000  x29 = 0000000000000000  x30 = 0000000000000000  x31 = 0000000000000000
PC = 0000000000000000

次に、x12にmtvecの値を格納し、mtvecにx10の値を再設定する。mtvecには0x12345678 | 0xabcde3ef = 0xbbfdf7ffが設定される。

helper_csrrs(emu, 12, 10, 0x305) is called!
x00 = 0000000000000000  x01 = 0000000000000000  x02 = 0000000000000000  x03 = 0000000000000000
x04 = 0000000000000000  x05 = 0000000000000000  x06 = 0000000000000000  x07 = 0000000000000000
x08 = 0000000000000000  x09 = 0000000000000000  x10 = 00000000abcde3ef  x11 = 0000000000000000
x12 = 0000000012345678  x13 = 0000000000000000  x14 = 0000000000000000  x15 = 0000000000000000
x16 = 0000000000000000  x17 = 0000000000000000  x18 = 0000000000000000  x19 = 0000000000000000
x20 = 0000000000000000  x21 = 0000000000000000  x22 = 0000000000000000  x23 = 0000000000000000
x24 = 0000000000000000  x25 = 0000000000000000  x26 = 0000000000000000  x27 = 0000000000000000
x28 = 0000000000000000  x29 = 0000000000000000  x30 = 0000000000000000  x31 = 0000000000000000
PC = 0000000000000000

最後に、x13にmtvecの値を格納し、mtvecにx10の値を再設定する。mtvecには0xbbfdf7ff & ~0x76543210 = 0x89a9c5efが設定される。

helper_csrrc(emu, 13, 10, 0x305) is called!
x00 = 0000000000000000  x01 = 0000000000000000  x02 = 0000000000000000  x03 = 0000000000000000
x04 = 0000000000000000  x05 = 0000000000000000  x06 = 0000000000000000  x07 = 0000000000000000
x08 = 0000000000000000  x09 = 0000000000000000  x10 = 0000000076543210  x11 = 0000000000000000
x12 = 0000000012345678  x13 = 00000000bbfdf7ff  x14 = 0000000000000000  x15 = 0000000000000000
x16 = 0000000000000000  x17 = 0000000000000000  x18 = 0000000000000000  x19 = 0000000000000000
x20 = 0000000000000000  x21 = 0000000000000000  x22 = 0000000000000000  x23 = 0000000000000000
x24 = 0000000000000000  x25 = 0000000000000000  x26 = 0000000000000000  x27 = 0000000000000000
x28 = 0000000000000000  x29 = 0000000000000000  x30 = 0000000000000000  x31 = 0000000000000000
PC = 0000000000000000

最後のcsrrwx14にmtvecの値が格納される。0x89a9c5efが設定される。

x00 = 0000000000000000  x01 = 0000000000000000  x02 = 0000000000000000  x03 = 0000000000000000
x04 = 0000000000000000  x05 = 0000000000000000  x06 = 0000000000000000  x07 = 0000000000000000
x08 = 0000000000000000  x09 = 0000000000000000  x10 = 0000000076543210  x11 = 0000000000000000
x12 = 0000000012345678  x13 = 00000000bbfdf7ff  x14 = 0000000089a9c5ef  x15 = 0000000000000000
x16 = 0000000000000000  x17 = 0000000000000000  x18 = 0000000000000000  x19 = 0000000000000000
x20 = 0000000000000000  x21 = 0000000000000000  x22 = 0000000000000000  x23 = 0000000000000000
x24 = 0000000000000000  x25 = 0000000000000000  x26 = 0000000000000000  x27 = 0000000000000000
x28 = 0000000000000000  x29 = 0000000000000000  x30 = 0000000000000000  x31 = 0000000000000000
PC = 0000000000000000