diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..5717481 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,23 @@ +name: Github Actions + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + cup_test: + runs-on: ubuntu-22.04 + steps: + - name: checkout code + uses: actions/checkout@v3.2.0 + - name: setup Rust Cargo + run: | + sudo apt install cargo + + - name: run tests + run: | + cargo test --package emurv --test cpu_test -- tests --nocapture + + \ No newline at end of file diff --git a/.gitignore b/.gitignore index 6985cf1..c36e1af 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,7 @@ -# Generated by Cargo -# will have compiled files and executables -debug/ -target/ - -# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries -# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html -Cargo.lock - # These are backup files generated by rustfmt **/*.rs.bk # MSVC Windows builds of rustc generate these, which store debugging information *.pdb + +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..843544f --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "emurv" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..e42eb09 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "emurv" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/src/cpu.rs b/src/cpu.rs new file mode 100644 index 0000000..da7b3bb --- /dev/null +++ b/src/cpu.rs @@ -0,0 +1,193 @@ +// pub mod cpu; + +use crate::memory; +use crate::opcode::*; +use crate::registers; + +#[derive(Debug)] +pub struct CPU { + // integer registers + pub xregs: registers::XREGS, + pc: u64, + + pub bus: memory::BUS, +} + +impl CPU { + pub fn new() -> Self { + let mut cpu: CPU = CPU { + xregs: registers::XREGS::new(), + pc: memory::MEM_BASE, + bus: memory::BUS::new(), + }; + cpu.xregs.regs[2] = memory::MEM_BASE + memory::MEM_SIZE; // Set stack pointer + cpu.pc = memory::MEM_BASE; + return cpu; + } + + fn fetch(self) -> u32 { + let instr: u32 = self.bus.load(self.pc, 32); + return instr; + } + + fn execute(&mut self, instr: u32) { + let opcode = instr & 0x7f; + let funct3 = (instr >> 12) & 0x7; + let funct7 = (instr >> 25) & 0x7f; + self.xregs.regs[0] = 0; // x0 hardwired to 0 at each cycle + + match instr { + LUI => (), + AUIPC => (), + JAL => (), + JALR => (), + B_TYPE => match funct7 { + BEQ => exec_beq(self, instr), + BNE => exec_bne(self, instr), + BLT => exec_blt(self, instr), + BGE => exec_bge(self, instr), + BLTU => exec_bltu(self, instr), + BGEU => exec_bgeu(self, instr), + _ => panic!(), + }, + LOAD => match funct3 { + LB => exec_lb(self, instr), + LH => exec_lh(self, instr), + LW => exec_lw(self, instr), + LD => exec_ld(self, instr), + LBU => exec_lbu(self, instr), + LHU => exec_lhu(self, instr), + LWU => exec_lwu(self, instr), + _ => panic!(), + }, + S_TYPE => match funct3 { + SB => exec_sb(self, instr), + SH => exec_sh(self, instr), + SW => exec_sw(self, instr), + SD => exec_sd(self, instr), + _ => panic!(), + }, + I_TYPE => match funct3 { + ADDI => exec_addi(self, instr), + SLLI => exec_slli(self, instr), + SLTI => exec_slti(self, instr), + SLTIU => exec_sltiu(self, instr), + XORI => exec_xori(self, instr), + SRI => match funct7 { + SRLI => exec_srli(self, instr), + SRAI => exec_srai(self, instr), + _ => panic!(), + }, + ORI => exec_ori(self, instr), + ANDI => exec_andi(self, instr), + _ => { + panic!("malformed I type instruction"); + } + }, + R_TYPE => match funct3 { + ADDSUB => match funct7 { + ADD => exec_add(self, instr), + SUB => exec_sub(self, instr), + _ => (), + }, + SLL => exec_sll(self, instr), + SLT => exec_slt(self, instr), + SLTU => exec_sltu(self, instr), + XOR => exec_xor(self, instr), + SR => match funct7 { + SRL => exec_srl(self, instr), + SRA => exec_sra(self, instr), + _ => (), + }, + OR => exec_or(self, instr), + AND => exec_and(self, instr), + _ => { + panic!("malformed I type instruction"); + } + }, + FENCE => exec_fence(self, instr), + _ => panic!(), + } + } +} + +// RV32I +// see page 64 at https://riscv.org/wp-content/uploads/2016/06/riscv-spec-v2.1.pdf +pub fn exec_lui(cpu: &mut CPU, instr: u32) {} +pub fn exec_auipc(cpu: &mut CPU, instr: u32) {} +pub fn exec_jal(cpu: &mut CPU, instr: u32) {} +pub fn exec_jalr(cpu: &mut CPU, instr: u32) {} +pub fn exec_beq(cpu: &mut CPU, instr: u32) {} +pub fn exec_bne(cpu: &mut CPU, instr: u32) {} +pub fn exec_blt(cpu: &mut CPU, instr: u32) {} +pub fn exec_bge(cpu: &mut CPU, instr: u32) {} +pub fn exec_bltu(cpu: &mut CPU, instr: u32) {} +pub fn exec_bgeu(cpu: &mut CPU, instr: u32) {} +pub fn exec_lb(cpu: &mut CPU, instr: u32) {} +pub fn exec_lh(cpu: &mut CPU, instr: u32) {} +pub fn exec_lw(cpu: &mut CPU, instr: u32) {} +pub fn exec_ld(cpu: &mut CPU, instr: u32) {} +pub fn exec_lbu(cpu: &mut CPU, instr: u32) {} +pub fn exec_lhu(cpu: &mut CPU, instr: u32) {} +pub fn exec_lwu(cpu: &mut CPU, instr: u32) {} +pub fn exec_sb(cpu: &mut CPU, instr: u32) {} +pub fn exec_sh(cpu: &mut CPU, instr: u32) {} +pub fn exec_sw(cpu: &mut CPU, instr: u32) {} +pub fn exec_sd(cpu: &mut CPU, instr: u32) {} +pub fn exec_addi(cpu: &mut CPU, instr: u32) { + let imm = imm_I(instr); + cpu.xregs.regs[rd(instr) as usize] = cpu.xregs.regs[rs1(instr) as usize] + imm as u64; +} +pub fn exec_slti(cpu: &mut CPU, instr: u32) { + let imm = imm_I(instr); + cpu.xregs.regs[rd(instr) as usize] = + ((cpu.xregs.regs[rs1(instr) as usize] as i64) < (imm as i64)) as u64; +} +pub fn exec_sltiu(cpu: &mut CPU, instr: u32) { + let imm = imm_I(instr); + cpu.xregs.regs[rd(instr) as usize] = (cpu.xregs.regs[rs1(instr) as usize] < imm as u64) as u64; +} +pub fn exec_xori(cpu: &mut CPU, instr: u32) { + let imm = imm_I(instr); + cpu.xregs.regs[rd(instr) as usize] = cpu.xregs.regs[rs1(instr) as usize] ^ imm as u64; +} +pub fn exec_ori(cpu: &mut CPU, instr: u32) { + let imm = imm_I(instr); + cpu.xregs.regs[rd(instr) as usize] = cpu.xregs.regs[rs1(instr) as usize] | imm as u64; +} +pub fn exec_andi(cpu: &mut CPU, instr: u32) { + let imm = imm_I(instr); + cpu.xregs.regs[rd(instr) as usize] = cpu.xregs.regs[rs1(instr) as usize] & imm as u64; +} +pub fn exec_slli(cpu: &mut CPU, instr: u32) { + let imm = imm_I(instr); + cpu.xregs.regs[rd(instr) as usize] = cpu.xregs.regs[rs1(instr) as usize] << imm as u64; +} +pub fn exec_srli(cpu: &mut CPU, instr: u32) { + let imm = imm_I(instr); + cpu.xregs.regs[rd(instr) as usize] = cpu.xregs.regs[rs1(instr) as usize] >> imm as u64; +} +pub fn exec_srai(cpu: &mut CPU, instr: u32) { + let imm = imm_I(instr); + cpu.xregs.regs[rd(instr) as usize] = (cpu.xregs.regs[rs1(instr) as usize] as i64 >> imm) as u64; +} +pub fn exec_add(cpu: &mut CPU, instr: u32) {} +pub fn exec_sub(cpu: &mut CPU, instr: u32) {} +pub fn exec_sll(cpu: &mut CPU, instr: u32) {} +pub fn exec_slt(cpu: &mut CPU, instr: u32) {} +pub fn exec_sltu(cpu: &mut CPU, instr: u32) {} +pub fn exec_xor(cpu: &mut CPU, instr: u32) {} +pub fn exec_srl(cpu: &mut CPU, instr: u32) {} +pub fn exec_sra(cpu: &mut CPU, instr: u32) {} +pub fn exec_or(cpu: &mut CPU, instr: u32) {} +pub fn exec_and(cpu: &mut CPU, instr: u32) {} +pub fn exec_fence(cpu: &mut CPU, instr: u32) {} +pub fn exec_fence_i(cpu: &mut CPU, instr: u32) {} +pub fn exec_ecall(cpu: &mut CPU, instr: u32) {} +pub fn exec_ebreak(cpu: &mut CPU, instr: u32) {} +pub fn exec_csrrw(cpu: &mut CPU, instr: u32) {} +pub fn exec_csrrs(cpu: &mut CPU, instr: u32) {} +pub fn exec_csrrc(cpu: &mut CPU, instr: u32) {} +pub fn exec_csrrwi(cpu: &mut CPU, instr: u32) {} +pub fn exec_csrrsi(cpu: &mut CPU, instr: u32) {} +pub fn exec_csrrci(cpu: &mut CPU, instr: u32) {} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..eaba5d3 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,4 @@ +pub mod cpu; +pub mod memory; +pub mod opcode; +pub mod registers; diff --git a/src/memory.rs b/src/memory.rs new file mode 100644 index 0000000..0031ea8 --- /dev/null +++ b/src/memory.rs @@ -0,0 +1,109 @@ +// pub mod memory; + +pub const MEM_BASE: u64 = 0x80000000; // defined in QEMU +pub const MEM_SIZE: u64 = 1024; + +#[derive(Debug)] +pub struct BUS { + mem: MEMORY, +} + +impl BUS { + pub fn new() -> Self { + BUS { mem: MEMORY::new() } + } + pub fn load(self, addr: u64, size: u64) -> u32 { + return self.mem.load(addr, size) as u32; + } + pub fn store(&mut self, addr: u64, size: u64, value: u64) { + self.mem.store(addr, size, value); + } +} + +#[derive(Debug)] +pub struct MEMORY { + mem: [u8; MEM_SIZE as usize], +} + +impl MEMORY { + fn new() -> Self { + MEMORY { + mem: [0; MEM_SIZE as usize], + } + } + + fn load(self, addr: u64, size: u64) -> u64 { + match size { + 8 => return self.load8(addr), + 16 => return self.load16(addr), + 32 => return self.load32(addr), + 64 => return self.load64(addr), + _ => panic!("wrong load size"), + } + } + fn store(&mut self, addr: u64, size: u64, value: u64) { + match size { + 8 => self.store8(addr, value), + 16 => self.store16(addr, value), + 32 => self.store32(addr, value), + 64 => self.store64(addr, value), + _ => panic!("wrong store size"), + } + } + + // load funcs + fn load8(self, addr: u64) -> u64 { + let index = (addr - MEM_BASE) as usize; + return self.mem[index] as u64; + } + fn load16(self, addr: u64) -> u64 { + let index = (addr - MEM_BASE) as usize; + return self.mem[index] as u64 | ((self.mem[index + 1] as u64) << 8); + } + fn load32(self, addr: u64) -> u64 { + let index = (addr - MEM_BASE) as usize; + return self.mem[index] as u64 + | ((self.mem[index + 1] as u64) << 8) + | ((self.mem[index + 2] as u64) << 16) + | ((self.mem[index + 3] as u64) << 24); + } + fn load64(self, addr: u64) -> u64 { + let index = (addr - MEM_BASE) as usize; + return self.mem[index] as u64 + | ((self.mem[index + 1] as u64) << 8) + | ((self.mem[index + 2] as u64) << 16) + | ((self.mem[index + 3] as u64) << 24) + | ((self.mem[index + 4] as u64) << 32) + | ((self.mem[index + 5] as u64) << 40) + | ((self.mem[index + 6] as u64) << 48) + | ((self.mem[index + 7] as u64) << 56); + } + + // store funcs + fn store8(&mut self, addr: u64, value: u64) { + let index = (addr - MEM_BASE) as usize; + self.mem[index] = (value & (std::u8::MAX as u64)) as u8; + } + fn store16(&mut self, addr: u64, value: u64) { + let index = (addr - MEM_BASE) as usize; + self.mem[index] = (value & (std::u8::MAX as u64)) as u8; + self.mem[index + 1] = ((value >> 8) & (std::u8::MAX as u64)) as u8; + } + fn store32(&mut self, addr: u64, value: u64) { + let index = (addr - MEM_BASE) as usize; + self.mem[index] = (value & (std::u8::MAX as u64)) as u8; + self.mem[index + 1] = ((value >> 8) & (std::u8::MAX as u64)) as u8; + self.mem[index + 2] = ((value >> 16) & (std::u8::MAX as u64)) as u8; + } + fn store64(&mut self, addr: u64, value: u64) { + let index = (addr - MEM_BASE) as usize; + self.mem[index] = (value & (std::u8::MAX as u64)) as u8; + self.mem[index + 1] = ((value >> 8) & (std::u8::MAX as u64)) as u8; + self.mem[index + 2] = ((value >> 16) & (std::u8::MAX as u64)) as u8; + self.mem[index + 3] = ((value >> 24) & (std::u8::MAX as u64)) as u8; + self.mem[index + 4] = ((value >> 32) & (std::u8::MAX as u64)) as u8; + self.mem[index + 5] = ((value >> 40) & (std::u8::MAX as u64)) as u8; + self.mem[index + 6] = ((value >> 48) & (std::u8::MAX as u64)) as u8; + self.mem[index + 7] = ((value >> 56) & (std::u8::MAX as u64)) as u8; + } +} diff --git a/src/opcode.rs b/src/opcode.rs new file mode 100644 index 0000000..9485e29 --- /dev/null +++ b/src/opcode.rs @@ -0,0 +1,139 @@ +// pub mod opcode; + +pub const LUI: u32 = 0x37; +pub const AUIPC: u32 = 0x17; + +pub const JAL: u32 = 0x6f; +pub const JALR: u32 = 0x67; + +pub const B_TYPE: u32 = 0x63; +pub const BEQ: u32 = 0x0; +pub const BNE: u32 = 0x1; +pub const BLT: u32 = 0x4; +pub const BGE: u32 = 0x5; +pub const BLTU: u32 = 0x6; +pub const BGEU: u32 = 0x7; + +pub const LOAD: u32 = 0x03; +pub const LB: u32 = 0x0; +pub const LH: u32 = 0x1; +pub const LW: u32 = 0x2; +pub const LD: u32 = 0x3; +pub const LBU: u32 = 0x4; +pub const LHU: u32 = 0x5; +pub const LWU: u32 = 0x6; + +pub const S_TYPE: u32 = 0x23; +pub const SB: u32 = 0x0; +pub const SH: u32 = 0x1; +pub const SW: u32 = 0x2; +pub const SD: u32 = 0x3; + +pub const I_TYPE: u32 = 0x13; +pub const ADDI: u32 = 0x0; +pub const SLLI: u32 = 0x1; +pub const SLTI: u32 = 0x2; +pub const SLTIU: u32 = 0x3; +pub const XORI: u32 = 0x4; +pub const SRI: u32 = 0x5; +pub const SRLI: u32 = 0x00; +pub const SRAI: u32 = 0x20; +pub const ORI: u32 = 0x6; +pub const ANDI: u32 = 0x7; + +pub const R_TYPE: u32 = 0x33; +pub const ADDSUB: u32 = 0x0; +pub const ADD: u32 = 0x00; +pub const SUB: u32 = 0x20; +pub const SLL: u32 = 0x1; +pub const SLT: u32 = 0x2; +pub const SLTU: u32 = 0x3; +pub const XOR: u32 = 0x4; +pub const SR: u32 = 0x5; +pub const SRL: u32 = 0x00; +pub const SRA: u32 = 0x20; +pub const OR: u32 = 0x6; +pub const AND: u32 = 0x7; + +pub const FENCE: u32 = 0x0f; + +// pub const I_TYPE_64: u32 = 0x1b; +// pub const ADDIW: u32 = 0x0; +// pub const SLLIW: u32 = 0x1; +// pub const SRIW: u32 = 0x5; +// pub const SRLIW: u32 = 0x00; +// pub const SRAIW: u32 = 0x20; + +// pub const R_TYPE_64: u32 = 0x3b; +// pub const ADDSUB: u32 = 0x0; +// pub const ADDW: u32 = 0x00; +// pub const MULW: u32 = 0x01; +// pub const SUBW: u32 = 0x20; +// pub const DIVW: u32 = 0x4; +// pub const SLLW: u32 = 0x1; +// pub const SRW: u32 = 0x5; +// pub const SRLW: u32 = 0x00; +// pub const DIVUW: u32 = 0x01; +// pub const SRAW: u32 = 0x20; +// pub const REMW: u32 = 0x6; +// pub const REMUW: u32 = 0x7; + +pub const CSR: u32 = 0x73; +pub const ECALL: u32 = 0x00; +pub const EBREAK: u32 = 0x00; +pub const CSRRW: u32 = 0x01; +pub const CSRRS: u32 = 0x02; +pub const CSRRC: u32 = 0x03; +pub const CSRRWI: u32 = 0x05; +pub const CSRRSI: u32 = 0x06; +pub const CSRRCI: u32 = 0x07; + +pub fn rd(instr: u32) -> u32 { + return (instr >> 7) & 0x1f; // rd in bits 11..7 +} +pub fn rs1(instr: u32) -> u32 { + return (instr >> 15) & 0x1f; // rs1 in bits 19..15 +} +pub fn rs2(instr: u32) -> u32 { + return (instr >> 20) & 0x1f; // rs2 in bits 24..20 +} + +pub fn shamt(instr: u32) -> u32 { + // shamt[4:5] = imm[5:0] + return (imm_I(instr) & 0x1f) as u32; +} + +pub fn csr(instr: u32) -> u64 { + // csr[11:0] = inst[31:20] + return ((instr & 0xfff00000) >> 20) as u64; +} + +pub fn imm_B(instr: u32) -> u64 { + // imm[12|10:5|4:1|11] = inst[31|30:25|11:8|7] + return ((instr & 0x80000000) >> 19) as u64 + | ((instr & 0x80) << 4) as u64 // imm[11] + | ((instr >> 20) & 0x7e0) as u64 // imm[10:5] + | ((instr >> 7) & 0x1e) as u64; // imm[4:1] +} + +pub fn imm_S(instr: u32) -> u64 { + // imm[11:5] = inst[31:25], imm[4:0] = inst[11:7] + return ((instr & 0xfe000000) >> 20) as u64 | ((instr >> 7) & 0x1f) as u64; +} + +pub fn imm_I(instr: u32) -> i32 { + return ((instr & 0xfff00000) as i32 >> 20); +} + +pub fn imm_U(instr: u32) -> u64 { + // imm[31:12] = inst[31:12] + return (instr & 0xfffff999) as u64; +} + +pub fn imm_J(instr: u32) -> u64 { + // imm[20|10:1|11|19:12] = inst[31|30:21|20|19:12] + return ((instr & 0x80000000) >> 11) as u64 + | (instr & 0xff000) as u64 // imm[19:12] + | ((instr >> 9) & 0x800) as u64 // imm[11] + | ((instr >> 20) & 0x7fe) as u64; // imm[10:1] +} diff --git a/src/registers.rs b/src/registers.rs new file mode 100644 index 0000000..f068579 --- /dev/null +++ b/src/registers.rs @@ -0,0 +1,36 @@ +// pub mod registers; + +use core::fmt; + +pub struct XREGS { + pub regs: [u64; 32], +} + +impl XREGS { + pub fn new() -> Self { + XREGS { regs: [0; 32] } + } +} + +impl fmt::Debug for XREGS { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut s = f.debug_struct("xregs"); + for i in 0..32 { + s.field(format!("xreg[{i}]").as_str(), &self.regs[i]); + } + s.finish() + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn xregs_debug() { + let mut xregs = XREGS::new(); + for i in 0..32 { + xregs.regs[i] = (i * 11) as u64; + } + println!("{xregs:#?}") + } +} diff --git a/tests/cpu_test.rs b/tests/cpu_test.rs new file mode 100644 index 0000000..876c75d --- /dev/null +++ b/tests/cpu_test.rs @@ -0,0 +1,198 @@ +mod helper; + +#[cfg(test)] +mod tests { + use crate::helper; + use emurv::cpu; + + #[test] + fn test_exec_lui() {} + #[test] + fn test_exec_auipc() {} + #[test] + fn test_exec_jal() {} + #[test] + fn test_exec_jalr() {} + #[test] + fn test_exec_beq() {} + #[test] + fn test_exec_bne() {} + #[test] + fn test_exec_blt() {} + #[test] + fn test_exec_bge() {} + #[test] + fn test_exec_bltu() {} + #[test] + fn test_exec_bgeu() {} + #[test] + fn test_exec_lb() {} + #[test] + fn test_exec_lh() {} + #[test] + fn test_exec_lw() {} + #[test] + fn test_exec_ld() {} + #[test] + fn test_exec_lbu() {} + #[test] + fn test_exec_lhu() {} + #[test] + fn test_exec_lwu() {} + #[test] + fn test_exec_sb() {} + #[test] + fn test_exec_sh() {} + #[test] + fn test_exec_sw() {} + #[test] + fn test_exec_sd() {} + #[test] + fn test_exec_addi() { + let mut cpu_test = cpu::CPU::new(); + + // addi x31, x0, 4 + let instr: u32 = helper::set_i_type_instruction(4, 0, 0x0, 31, 0x13); + cpu::exec_addi(&mut cpu_test, instr); + assert_eq!(cpu_test.xregs.regs[31], 4); + } + #[test] + fn test_exec_slti() { + let mut cpu_test = cpu::CPU::new(); + + // set x1=3 + helper::set_register_val(&mut cpu_test, 1, 3); + // slti x31, x1, 2 + let instr: u32 = helper::set_i_type_instruction(2, 1, 0x2, 31, 0x13); + cpu::exec_slti(&mut cpu_test, instr); + assert_eq!(cpu_test.xregs.regs[31], 0); + // slti x31, x1, 4 + let instr: u32 = helper::set_i_type_instruction(4, 1, 0x2, 31, 0x13); + cpu::exec_slti(&mut cpu_test, instr); + assert_eq!(cpu_test.xregs.regs[31], 1); + // slti x31, x1, -2 + let instr: u32 = helper::set_i_type_instruction(-2, 1, 0x2, 31, 0x13); // 254 == -2 + cpu::exec_slti(&mut cpu_test, instr); + assert_eq!(cpu_test.xregs.regs[31], 0); + } + #[test] + fn test_exec_sltiu() { + let mut cpu_test = cpu::CPU::new(); + + // set x1=3 + helper::set_register_val(&mut cpu_test, 1, 3); + // sltiu x31, x1, 2 + let instr: u32 = helper::set_i_type_instruction(2, 1, 0x3, 31, 0x13); + cpu::exec_sltiu(&mut cpu_test, instr); + assert_eq!(cpu_test.xregs.regs[31], 0); + // sltiu x31, x1, 4 + let instr: u32 = helper::set_i_type_instruction(4, 1, 0x3, 31, 0x13); + cpu::exec_sltiu(&mut cpu_test, instr); + assert_eq!(cpu_test.xregs.regs[31], 1); + // sltiu x31, x1, 254 + let instr: u32 = helper::set_i_type_instruction(-2, 1, 0x3, 31, 0x13); + cpu::exec_sltiu(&mut cpu_test, instr); + assert_eq!(cpu_test.xregs.regs[31], 1); + } + #[test] + fn test_exec_xori() { + let mut cpu_test = cpu::CPU::new(); + + // xori x31, x0, 4 + let instr: u32 = helper::set_i_type_instruction(4, 0, 0x4, 31, 0x13); + cpu::exec_xori(&mut cpu_test, instr); + assert_eq!(cpu_test.xregs.regs[31], 4); + } + #[test] + fn test_exec_ori() { + let mut cpu_test = cpu::CPU::new(); + + // ori x31, x0, 4 + let instr: u32 = helper::set_i_type_instruction(4, 0, 0x6, 31, 0x13); + cpu::exec_ori(&mut cpu_test, instr); + assert_eq!(cpu_test.xregs.regs[31], 4); + } + #[test] + fn test_exec_andi() { + let mut cpu_test = cpu::CPU::new(); + + // andi x31, x0, 4 + let instr: u32 = helper::set_i_type_instruction(4, 0, 0x7, 31, 0x13); + cpu::exec_andi(&mut cpu_test, instr); + assert_eq!(cpu_test.xregs.regs[31], 0); + } + #[test] + fn test_exec_slli() { + let mut cpu_test = cpu::CPU::new(); + + // set x1=3 + helper::set_register_val(&mut cpu_test, 1, 3); + // slli x31, x0, 2 + let instr: u32 = helper::set_i_type_instruction(2, 1, 0x1, 31, 0x13); + cpu::exec_slli(&mut cpu_test, instr); + assert_eq!(cpu_test.xregs.regs[31], 12); + } + #[test] + fn test_exec_srli() { + let mut cpu_test = cpu::CPU::new(); + + // set x1=254 + helper::set_register_val(&mut cpu_test, 1, 254); + // srli x31, x0, 2 + let instr: u32 = helper::set_i_type_instruction(2, 1, 0x1, 31, 0x13); + cpu::exec_srli(&mut cpu_test, instr); + assert_eq!(cpu_test.xregs.regs[31], 63); + } + #[test] + fn test_exec_srai() { + let mut cpu_test = cpu::CPU::new(); + + // set x1=-2 + helper::set_register_val(&mut cpu_test, 1, -2); + // srli x31, x0, 2 + let instr: u32 = helper::set_i_type_instruction(2, 1, 0x5, 31, 0x13); + cpu::exec_srai(&mut cpu_test, instr); + // -2 >> 2 = -1 + assert_eq!(cpu_test.xregs.regs[31], std::u64::MAX); + } + #[test] + fn test_exec_add() {} + #[test] + fn test_exec_sub() {} + #[test] + fn test_exec_sll() {} + #[test] + fn test_exec_slt() {} + #[test] + fn test_exec_sltu() {} + #[test] + fn test_exec_xor() {} + #[test] + fn test_exec_srl() {} + #[test] + fn test_exec_sra() {} + #[test] + fn test_exec_or() {} + #[test] + fn test_exec_and() {} + #[test] + fn test_exec_fence() {} + #[test] + fn test_exec_fence_i() {} + #[test] + fn test_exec_ecall() {} + #[test] + fn test_exec_ebreak() {} + #[test] + fn test_exec_csrrw() {} + #[test] + fn test_exec_csrrs() {} + #[test] + fn test_exec_csrrc() {} + #[test] + fn test_exec_csrrwi() {} + #[test] + fn test_exec_csrrsi() {} + #[test] + fn test_exec_csrrci() {} +} diff --git a/tests/helper.rs b/tests/helper.rs new file mode 100644 index 0000000..470af50 --- /dev/null +++ b/tests/helper.rs @@ -0,0 +1,15 @@ +use emurv::cpu; + +pub fn set_i_type_instruction(imm: i16, rs1: u8, funct3: u8, rd: u8, opcode: u8) -> u32 { + // |31-20|19-15|14-12|11-7|6-0| + return ((imm as u32 & 0xfff) << 20) + | ((rs1 as u32 & 0x1f) << 15) + | ((funct3 as u32 & 0x7) << 12) + | ((rd as u32 & 0x1f) << 7) + | ((opcode as u32) & 0x7f); +} + +pub fn set_register_val(cpu: &mut cpu::CPU, rd: u8, val: i16) { + let instr = set_i_type_instruction(val, 0x0, 0x0, rd, 0x13); + cpu::exec_addi(cpu, instr); +}