diff --git a/src/cpu.rs b/src/cpu.rs index 0f489b8..da7b3bb 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -10,7 +10,7 @@ pub struct CPU { pub xregs: registers::XREGS, pc: u64, - bus: memory::BUS, + pub bus: memory::BUS, } impl CPU { @@ -50,7 +50,7 @@ impl CPU { BGEU => exec_bgeu(self, instr), _ => panic!(), }, - LOAD => match (funct3) { + LOAD => match funct3 { LB => exec_lb(self, instr), LH => exec_lh(self, instr), LW => exec_lw(self, instr), @@ -60,20 +60,20 @@ impl CPU { LWU => exec_lwu(self, instr), _ => panic!(), }, - S_TYPE => match (funct3) { + 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) { + 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) { + SRI => match funct7 { SRLI => exec_srli(self, instr), SRAI => exec_srai(self, instr), _ => panic!(), @@ -84,8 +84,8 @@ impl CPU { panic!("malformed I type instruction"); } }, - R_TYPE => match (funct3) { - ADDSUB => match (funct7) { + R_TYPE => match funct3 { + ADDSUB => match funct7 { ADD => exec_add(self, instr), SUB => exec_sub(self, instr), _ => (), @@ -94,7 +94,7 @@ impl CPU { SLT => exec_slt(self, instr), SLTU => exec_sltu(self, instr), XOR => exec_xor(self, instr), - SR => match (funct7) { + SR => match funct7 { SRL => exec_srl(self, instr), SRA => exec_sra(self, instr), _ => (), @@ -111,107 +111,83 @@ impl CPU { } } -fn rd(instr: u32) -> u32 { - return (instr >> 7) & 0x1f; // rd in bits 11..7 -} -fn rs1(instr: u32) -> u32 { - return (instr >> 15) & 0x1f; // rs1 in bits 19..15 -} -fn rs2(instr: u32) -> u32 { - return (instr >> 20) & 0x1f; // rs2 in bits 24..20 -} - -fn imm_I(instr: u32) -> u32 { - return (instr & 0xfff00000) >> 20; -} - -// uint64_t imm_S(uint32_t inst) { -// // imm[11:5] = inst[31:25], imm[4:0] = inst[11:7] -// return ((int64_t)(int32_t)(inst & 0xfe000000) >> 20) -// | ((inst >> 7) & 0x1f); -// } -// uint64_t imm_B(uint32_t inst) { -// // imm[12|10:5|4:1|11] = inst[31|30:25|11:8|7] -// return ((int64_t)(int32_t)(inst & 0x80000000) >> 19) -// | ((inst & 0x80) << 4) // imm[11] -// | ((inst >> 20) & 0x7e0) // imm[10:5] -// | ((inst >> 7) & 0x1e); // imm[4:1] -// } -// uint64_t imm_U(uint32_t inst) { -// // imm[31:12] = inst[31:12] -// return (int64_t)(int32_t)(inst & 0xfffff999); -// } -// uint64_t imm_J(uint32_t inst) { -// // imm[20|10:1|11|19:12] = inst[31|30:21|20|19:12] -// return (uint64_t)((int64_t)(int32_t)(inst & 0x80000000) >> 11) -// | (inst & 0xff000) // imm[19:12] -// | ((inst >> 9) & 0x800) // imm[11] -// | ((inst >> 20) & 0x7fe); // imm[10:1] -// } - -// uint32_t shamt(uint32_t inst) { -// // shamt(shift amount) only required for immediate shift instructions -// // shamt[4:5] = imm[5:0] -// return (uint32_t) (imm_I(inst) & 0x1f); // TODO: 0x1f / 0x3f ? -// } - -// uint64_t csr(uint32_t inst) { -// // csr[11:0] = inst[31:20] -// return ((inst & 0xfff00000) >> 20); -// } - // RV32I // see page 64 at https://riscv.org/wp-content/uploads/2016/06/riscv-spec-v2.1.pdf -pub fn exec_lui(cpu: &CPU, instr: u32) {} -pub fn exec_auipc(cpu: &CPU, instr: u32) {} -pub fn exec_jal(cpu: &CPU, instr: u32) {} -pub fn exec_jalr(cpu: &CPU, instr: u32) {} -pub fn exec_beq(cpu: &CPU, instr: u32) {} -pub fn exec_bne(cpu: &CPU, instr: u32) {} -pub fn exec_blt(cpu: &CPU, instr: u32) {} -pub fn exec_bge(cpu: &CPU, instr: u32) {} -pub fn exec_bltu(cpu: &CPU, instr: u32) {} -pub fn exec_bgeu(cpu: &CPU, instr: u32) {} -pub fn exec_lb(cpu: &CPU, instr: u32) {} -pub fn exec_lh(cpu: &CPU, instr: u32) {} -pub fn exec_lw(cpu: &CPU, instr: u32) {} -pub fn exec_ld(cpu: &CPU, instr: u32) {} -pub fn exec_lbu(cpu: &CPU, instr: u32) {} -pub fn exec_lhu(cpu: &CPU, instr: u32) {} -pub fn exec_lwu(cpu: &CPU, instr: u32) {} -pub fn exec_sb(cpu: &CPU, instr: u32) {} -pub fn exec_sh(cpu: &CPU, instr: u32) {} -pub fn exec_sw(cpu: &CPU, instr: u32) {} -pub fn exec_sd(cpu: &CPU, instr: u32) {} +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: &CPU, instr: u32) {} -pub fn exec_sltiu(cpu: &CPU, instr: u32) {} -pub fn exec_xori(cpu: &CPU, instr: u32) {} -pub fn exec_ori(cpu: &CPU, instr: u32) {} -pub fn exec_andi(cpu: &CPU, instr: u32) {} -pub fn exec_slli(cpu: &CPU, instr: u32) {} -pub fn exec_srli(cpu: &CPU, instr: u32) {} -pub fn exec_srai(cpu: &CPU, instr: u32) {} -pub fn exec_add(cpu: &CPU, instr: u32) {} -pub fn exec_sub(cpu: &CPU, instr: u32) {} -pub fn exec_sll(cpu: &CPU, instr: u32) {} -pub fn exec_slt(cpu: &CPU, instr: u32) {} -pub fn exec_sltu(cpu: &CPU, instr: u32) {} -pub fn exec_xor(cpu: &CPU, instr: u32) {} -pub fn exec_srl(cpu: &CPU, instr: u32) {} -pub fn exec_sra(cpu: &CPU, instr: u32) {} -pub fn exec_or(cpu: &CPU, instr: u32) {} -pub fn exec_and(cpu: &CPU, instr: u32) {} -pub fn exec_fence(cpu: &CPU, instr: u32) {} -pub fn exec_fence_i(cpu: &CPU, instr: u32) {} -pub fn exec_ecall(cpu: &CPU, instr: u32) {} -pub fn exec_ebreak(cpu: &CPU, instr: u32) {} -pub fn exec_csrrw(cpu: &CPU, instr: u32) {} -pub fn exec_csrrs(cpu: &CPU, instr: u32) {} -pub fn exec_csrrc(cpu: &CPU, instr: u32) {} -pub fn exec_csrrwi(cpu: &CPU, instr: u32) {} -pub fn exec_csrrsi(cpu: &CPU, instr: u32) {} -pub fn exec_csrrci(cpu: &CPU, instr: u32) {} +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/opcode.rs b/src/opcode.rs index a7a85b2..9485e29 100644 --- a/src/opcode.rs +++ b/src/opcode.rs @@ -87,3 +87,53 @@ 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/tests/cpu_test.rs b/tests/cpu_test.rs index 69f2cec..876c75d 100644 --- a/tests/cpu_test.rs +++ b/tests/cpu_test.rs @@ -1,13 +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(); - let instr: Vec = vec![ - 0x93, 0x0F, 0x40, 0x00, // addi x31, x0, 4 - ]; - cpu::exec_addi(&mut cpu_test, u32::from_le_bytes(instr.try_into().unwrap())); + + // 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); +}