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
最後のcsrrw
でx14
に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