diff --git a/src/cpu.rs b/src/cpu.rs index da7b3bb..25049cc 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -8,7 +8,7 @@ use crate::registers; pub struct CPU { // integer registers pub xregs: registers::XREGS, - pc: u64, + pub pc: u64, pub bus: memory::BUS, } @@ -37,10 +37,10 @@ impl CPU { self.xregs.regs[0] = 0; // x0 hardwired to 0 at each cycle match instr { - LUI => (), - AUIPC => (), - JAL => (), - JALR => (), + LUI => exec_lui(self, instr), + AUIPC => exec_auipc(self, instr), + JAL => exec_jal(self, instr), + JALR => exec_jalr(self, instr), B_TYPE => match funct7 { BEQ => exec_beq(self, instr), BNE => exec_bne(self, instr), @@ -113,10 +113,24 @@ impl CPU { // 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_lui(cpu: &mut CPU, instr: u32) { + let imm = (imm_U(instr) as i32) as u64; + cpu.xregs.regs[rd(instr) as usize] = imm; +} +pub fn exec_auipc(cpu: &mut CPU, instr: u32) { + let imm = (imm_U(instr) as i32) as i64; + cpu.xregs.regs[rd(instr) as usize] = (cpu.pc as i64 + imm) as u64; +} +pub fn exec_jal(cpu: &mut CPU, instr: u32) { + let imm = (imm_J(instr) as i32) as i64; + cpu.xregs.regs[rd(instr) as usize] = cpu.pc + 4; + cpu.pc = (cpu.pc as i64 + imm) as u64; +} +pub fn exec_jalr(cpu: &mut CPU, instr: u32) { + let imm = (imm_J(instr) as i32) as i64; + cpu.xregs.regs[rd(instr) as usize] = cpu.pc + 4; + cpu.pc = (cpu.pc as i64 + imm) as u64; +} 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) {} @@ -171,8 +185,16 @@ 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_add(cpu: &mut CPU, instr: u32) { + cpu.xregs.regs[rd(instr) as usize] = (cpu.xregs.regs[rs1(instr) as usize] as i64 + + cpu.xregs.regs[rs2(instr) as usize] as i64) + as u64; +} +pub fn exec_sub(cpu: &mut CPU, instr: u32) { + cpu.xregs.regs[rd(instr) as usize] = (cpu.xregs.regs[rs1(instr) as usize] as i64 + - cpu.xregs.regs[rs2(instr) as usize] as i64) + as u64; +} 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) {} diff --git a/src/opcode.rs b/src/opcode.rs index 9485e29..2d34b77 100644 --- a/src/opcode.rs +++ b/src/opcode.rs @@ -122,18 +122,19 @@ pub fn imm_S(instr: u32) -> u64 { } pub fn imm_I(instr: u32) -> i32 { - return ((instr & 0xfff00000) as i32 >> 20); + // imm[11:0] = inst[31:20] + return (instr & 0xfff00000) as i32 >> 20; } pub fn imm_U(instr: u32) -> u64 { // imm[31:12] = inst[31:12] - return (instr & 0xfffff999) as u64; + return (instr & 0xfffff000) 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] + return (((instr & 0x80000000) as i32 as i64 >> 11) as u64)// imm[20] + | ((instr & 0x3ff00000) >> 20) as u64 // imm[10:1] + | ((instr & 0x80000) >> 9) as u64 // imm[11] + | (instr & 0xff000) as u64; // imm[19:12] } diff --git a/tests/cpu_test.rs b/tests/cpu_test.rs index 876c75d..d0af4df 100644 --- a/tests/cpu_test.rs +++ b/tests/cpu_test.rs @@ -3,14 +3,48 @@ mod helper; #[cfg(test)] mod tests { use crate::helper; - use emurv::cpu; + use emurv::{cpu, opcode::*}; #[test] - fn test_exec_lui() {} + fn test_exec_lui() { + let mut cpu_test = cpu::CPU::new(); + + // lui x5, 4 + let instr: u32 = helper::set_u_type_instruction(4 << 12, 5, LUI as u8); + cpu::exec_lui(&mut cpu_test, instr); + assert_eq!(cpu_test.xregs.regs[5], 4 << 12); + + // lui x5, -4 + let instr: u32 = helper::set_u_type_instruction(-(4 << 12), 5, LUI as u8); + cpu::exec_lui(&mut cpu_test, instr); + assert_eq!(cpu_test.xregs.regs[5], -(4 << 12) as u64); + } #[test] - fn test_exec_auipc() {} + fn test_exec_auipc() { + let mut cpu_test = cpu::CPU::new(); + + let ori_pc = cpu_test.pc; + // auipc x5, 4 + let instr: u32 = helper::set_u_type_instruction(4 << 12, 5, AUIPC as u8); + cpu::exec_auipc(&mut cpu_test, instr); + assert_eq!(cpu_test.xregs.regs[5], ori_pc + (4 << 12)); + + // auipc x5, -4 + let instr: u32 = helper::set_u_type_instruction(-(4 << 12), 5, AUIPC as u8); + cpu::exec_auipc(&mut cpu_test, instr); + assert_eq!(cpu_test.xregs.regs[5], (ori_pc as i64 + (-4 << 12)) as u64); + } #[test] - fn test_exec_jal() {} + fn test_exec_jal() { + let mut cpu_test = cpu::CPU::new(); + + let ori_pc = cpu_test.pc; + // jal x5, 12 + let instr: u32 = helper::set_j_type_instruction(12, 5, JAL as u8); + cpu::exec_jal(&mut cpu_test, instr); + assert_eq!(cpu_test.xregs.regs[5], ori_pc + 4); + assert_eq!(cpu_test.pc, ori_pc + 12); + } #[test] fn test_exec_jalr() {} #[test] @@ -52,7 +86,7 @@ mod tests { let mut cpu_test = cpu::CPU::new(); // addi x31, x0, 4 - let instr: u32 = helper::set_i_type_instruction(4, 0, 0x0, 31, 0x13); + let instr: u32 = helper::set_i_type_instruction(4, 0, ADDI as u8, 31); cpu::exec_addi(&mut cpu_test, instr); assert_eq!(cpu_test.xregs.regs[31], 4); } @@ -63,15 +97,15 @@ mod tests { // 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); + let instr: u32 = helper::set_i_type_instruction(2, 1, SLTI as u8, 31); 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); + let instr: u32 = helper::set_i_type_instruction(4, 1, SLTI as u8, 31); 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 + let instr: u32 = helper::set_i_type_instruction(-2, 1, SLTI as u8, 31); // 254 == -2 cpu::exec_slti(&mut cpu_test, instr); assert_eq!(cpu_test.xregs.regs[31], 0); } @@ -82,15 +116,15 @@ mod tests { // 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); + let instr: u32 = helper::set_i_type_instruction(2, 1, SLTIU as u8, 31); 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); + let instr: u32 = helper::set_i_type_instruction(4, 1, SLTIU as u8, 31); 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); + let instr: u32 = helper::set_i_type_instruction(-2, 1, SLTIU as u8, 31); cpu::exec_sltiu(&mut cpu_test, instr); assert_eq!(cpu_test.xregs.regs[31], 1); } @@ -99,7 +133,7 @@ mod tests { let mut cpu_test = cpu::CPU::new(); // xori x31, x0, 4 - let instr: u32 = helper::set_i_type_instruction(4, 0, 0x4, 31, 0x13); + let instr: u32 = helper::set_i_type_instruction(4, 0, XORI as u8, 31); cpu::exec_xori(&mut cpu_test, instr); assert_eq!(cpu_test.xregs.regs[31], 4); } @@ -108,7 +142,7 @@ mod tests { let mut cpu_test = cpu::CPU::new(); // ori x31, x0, 4 - let instr: u32 = helper::set_i_type_instruction(4, 0, 0x6, 31, 0x13); + let instr: u32 = helper::set_i_type_instruction(4, 0, ORI as u8, 31); cpu::exec_ori(&mut cpu_test, instr); assert_eq!(cpu_test.xregs.regs[31], 4); } @@ -117,7 +151,7 @@ mod tests { let mut cpu_test = cpu::CPU::new(); // andi x31, x0, 4 - let instr: u32 = helper::set_i_type_instruction(4, 0, 0x7, 31, 0x13); + let instr: u32 = helper::set_i_type_instruction(4, 0, ANDI as u8, 31); cpu::exec_andi(&mut cpu_test, instr); assert_eq!(cpu_test.xregs.regs[31], 0); } @@ -128,7 +162,7 @@ mod tests { // 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); + let instr: u32 = helper::set_i_type_instruction(2, 1, SLLI as u8, 31); cpu::exec_slli(&mut cpu_test, instr); assert_eq!(cpu_test.xregs.regs[31], 12); } @@ -139,7 +173,7 @@ mod tests { // 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); + let instr: u32 = helper::set_i_type_instruction(2, 1, SRLI as u8, 31); cpu::exec_srli(&mut cpu_test, instr); assert_eq!(cpu_test.xregs.regs[31], 63); } @@ -150,15 +184,37 @@ mod tests { // 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); + let instr: u32 = helper::set_i_type_instruction(2, 1, SRAI as u8, 31); 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() {} + fn test_exec_add() { + let mut cpu_test = cpu::CPU::new(); + + // set x5=-2 + helper::set_register_val(&mut cpu_test, 5, -2); + // set x6=4 + helper::set_register_val(&mut cpu_test, 6, 4); + // add x31, x5, x6 + let instr: u32 = helper::set_r_type_instruction(ADD as u8, 5, 6, 31); + cpu::exec_add(&mut cpu_test, instr); + assert_eq!(cpu_test.xregs.regs[31], 2); + } #[test] - fn test_exec_sub() {} + fn test_exec_sub() { + let mut cpu_test = cpu::CPU::new(); + + // set x5=-2 + helper::set_register_val(&mut cpu_test, 5, -2); + // set x6=4 + helper::set_register_val(&mut cpu_test, 6, 4); + // sub x31, x5, x6 + let instr: u32 = helper::set_r_type_instruction(SUB as u8, 6, 5, 31); + cpu::exec_sub(&mut cpu_test, instr); + assert_eq!(cpu_test.xregs.regs[31], 18446744073709551610); // 18446744073709551610 is -6 + } #[test] fn test_exec_sll() {} #[test] diff --git a/tests/helper.rs b/tests/helper.rs index 470af50..6eb37c2 100644 --- a/tests/helper.rs +++ b/tests/helper.rs @@ -1,15 +1,41 @@ -use emurv::cpu; +use emurv::{ + cpu, + opcode::{I_TYPE, R_TYPE}, +}; -pub fn set_i_type_instruction(imm: i16, rs1: u8, funct3: u8, rd: u8, opcode: u8) -> u32 { +pub fn set_i_type_instruction(imm: i16, rs1: u8, funct3: u8, rd: 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); + | ((I_TYPE as u32) & 0x7f); +} + +pub fn set_u_type_instruction(imm: i32, rd: u8, opcode: u8) -> u32 { + return (imm as u32 & 0xfffff000) as u32 | ((rd as u32 & 0x1f) << 7) | ((opcode as u32) & 0x7f); +} + +pub fn set_j_type_instruction(imm: i32, rd: u8, opcode: u8) -> u32 { + // |31-12|11-7|6-0| + // imm[20|10:1|11|19:12] = instr[31|30:21|20|19:12] + let instr_imm = (((imm as i64) << 11) & 0x80000000) + | (((imm as i64) << 20) & 0x3ff00000) + | (((imm as i64) << 9) & 0x80000) + | ((imm as i64) & 0xff000); + return (instr_imm) as u32 | ((rd as u32 & 0x1f) << 7) | ((opcode as u32) & 0x7f); +} + +pub fn set_r_type_instruction(funct7: u8, rs2: u8, rs1: u8, rd: u8) -> u32 { + // |31-20|19-15|14-12|11-7|6-0| + return ((funct7 as u32 & 0x7f) << 25) + | ((rs2 as u32 & 0x1f) << 20) + | ((rs1 as u32 & 0x1f) << 15) + | ((rd as u32 & 0x1f) << 7) + | ((R_TYPE 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); + let instr = set_i_type_instruction(val, 0x0, 0x0, rd); cpu::exec_addi(cpu, instr); }