From a0792bfaeab5050dc968e1157ef2581b0f1e0295 Mon Sep 17 00:00:00 2001 From: Yang Hau Date: Tue, 20 Feb 2024 21:56:00 +0900 Subject: [PATCH] feat: Add branch instructions --- src/cpu.rs | 43 ++++++++++++-- tests/cpu_test.rs | 142 +++++++++++++++++++++++++++++++++++++++++++--- tests/helper.rs | 36 ++++++++---- 3 files changed, 197 insertions(+), 24 deletions(-) diff --git a/src/cpu.rs b/src/cpu.rs index 74a6b95..efff576 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -132,12 +132,43 @@ pub fn exec_jalr(cpu: &mut CPU, instr: u32) { // ignore the last 1 bit with 0xfffffffe cpu.pc = (cpu.xregs.regs[rs1(instr) as usize] as i32).wrapping_add(imm) as u32 & 0xfffffffe; } -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_beq(cpu: &mut CPU, instr: u32) { + let imm = imm_b(instr) as i32; + if cpu.xregs.regs[rs1(instr) as usize] == cpu.xregs.regs[rs2(instr) as usize] { + cpu.pc = cpu.pc.wrapping_add(imm as u32).wrapping_sub(4); + } +} +pub fn exec_bne(cpu: &mut CPU, instr: u32) { + let imm = imm_b(instr) as i32; + if cpu.xregs.regs[rs1(instr) as usize] != cpu.xregs.regs[rs2(instr) as usize] { + cpu.pc = cpu.pc.wrapping_add(imm as u32).wrapping_sub(4); + } +} +pub fn exec_blt(cpu: &mut CPU, instr: u32) { + let imm = imm_b(instr) as i32; + if (cpu.xregs.regs[rs1(instr) as usize] as i32) < (cpu.xregs.regs[rs2(instr) as usize] as i32) { + cpu.pc = cpu.pc.wrapping_add(imm as u32).wrapping_sub(4); + } +} +pub fn exec_bge(cpu: &mut CPU, instr: u32) { + let imm = imm_b(instr) as i32; + if (cpu.xregs.regs[rs1(instr) as usize] as i32) >= (cpu.xregs.regs[rs2(instr) as usize] as i32) + { + cpu.pc = cpu.pc.wrapping_add(imm as u32).wrapping_sub(4); + } +} +pub fn exec_bltu(cpu: &mut CPU, instr: u32) { + let imm = imm_b(instr) as i32; + if cpu.xregs.regs[rs1(instr) as usize] < cpu.xregs.regs[rs2(instr) as usize] { + cpu.pc = cpu.pc.wrapping_add(imm as u32).wrapping_sub(4); + } +} +pub fn exec_bgeu(cpu: &mut CPU, instr: u32) { + let imm = imm_b(instr) as i32; + if cpu.xregs.regs[rs1(instr) as usize] >= rs2(instr) { + cpu.pc = cpu.pc.wrapping_add(imm as u32).wrapping_sub(4); + } +} 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) {} diff --git a/tests/cpu_test.rs b/tests/cpu_test.rs index cc9753b..03eba45 100644 --- a/tests/cpu_test.rs +++ b/tests/cpu_test.rs @@ -64,17 +64,143 @@ mod tests { assert_eq!(cpu_test.pc, (3 + 12) & 0xfffffffe); } #[test] - fn test_exec_beq() {} + fn test_exec_beq() { + // TODO add test case for imm is a negative number + let mut cpu_test = cpu::CPU::new(); + + cpu_test.pc = 500; + let ori_pc = cpu_test.pc; + // set x7=3 + helper::set_register_val(&mut cpu_test, 7, 3); + // set x8=3 + helper::set_register_val(&mut cpu_test, 8, 3); + // beq x8, x7, 12 + let instr: u32 = helper::set_b_type_instruction(12, 7, 8, BEQ as u8); + cpu::exec_beq(&mut cpu_test, instr); + assert_eq!(cpu_test.pc as i32, (ori_pc as i32) + 12 - 4); + + // set x9=4 + helper::set_register_val(&mut cpu_test, 9, 4); + // beq x9, x7, 12 + let instr: u32 = helper::set_b_type_instruction(12, 7, 9, BEQ as u8); + cpu::exec_beq(&mut cpu_test, instr); + assert_eq!(cpu_test.pc as i32, (ori_pc as i32) + 12 - 4); + } #[test] - fn test_exec_bne() {} + fn test_exec_bne() { + // TODO add test case for imm is a negative number + let mut cpu_test = cpu::CPU::new(); + + cpu_test.pc = 500; + let ori_pc = cpu_test.pc; + // set x7=3 + helper::set_register_val(&mut cpu_test, 7, 3); + // set x8=3 + helper::set_register_val(&mut cpu_test, 8, 3); + // bne x8, x7, 12 + let instr: u32 = helper::set_b_type_instruction(12, 7, 8, BNE as u8); + cpu::exec_bne(&mut cpu_test, instr); + assert_eq!(cpu_test.pc, ori_pc); + + // set x9=4 + helper::set_register_val(&mut cpu_test, 9, 4); + // bne x9, x7, 12 + let instr: u32 = helper::set_b_type_instruction(12, 7, 9, BNE as u8); + cpu::exec_bne(&mut cpu_test, instr); + assert_eq!(cpu_test.pc as i32, (ori_pc as i32) + 12 - 4); + } #[test] - fn test_exec_blt() {} + fn test_exec_blt() { + // TODO add test case for imm is a negative number + let mut cpu_test = cpu::CPU::new(); + + cpu_test.pc = 500; + let ori_pc = cpu_test.pc; + // set x7=3 + helper::set_register_val(&mut cpu_test, 7, 2); + // set x8=3 + helper::set_register_val(&mut cpu_test, 8, 3); + // blt x8, x7, 12 + let instr: u32 = helper::set_b_type_instruction(12, 7, 8, BLT as u8); + cpu::exec_blt(&mut cpu_test, instr); + assert_eq!(cpu_test.pc, ori_pc); + + // set x9=4 + helper::set_register_val(&mut cpu_test, 9, 1); + // blt x9, x7, 12 + let instr: u32 = helper::set_b_type_instruction(12, 7, 9, BLT as u8); + cpu::exec_blt(&mut cpu_test, instr); + assert_eq!(cpu_test.pc as i32, (ori_pc as i32) + 12 - 4); + } #[test] - fn test_exec_bge() {} + fn test_exec_bge() { + // TODO add test case for imm is a negative number + let mut cpu_test = cpu::CPU::new(); + + cpu_test.pc = 500; + let ori_pc = cpu_test.pc; + // set x7=3 + helper::set_register_val(&mut cpu_test, 7, 4); + // set x8=3 + helper::set_register_val(&mut cpu_test, 8, 3); + // bge x8, x7, 12 + let instr: u32 = helper::set_b_type_instruction(12, 7, 8, BGE as u8); + cpu::exec_bge(&mut cpu_test, instr); + assert_eq!(cpu_test.pc, ori_pc); + + // set x9=4 + helper::set_register_val(&mut cpu_test, 9, 5); + // bge x9, x7, 12 + let instr: u32 = helper::set_b_type_instruction(12, 7, 9, BGE as u8); + cpu::exec_bge(&mut cpu_test, instr); + assert_eq!(cpu_test.pc as i32, (ori_pc as i32) + 12 - 4); + } #[test] - fn test_exec_bltu() {} + fn test_exec_bltu() { + // TODO add test case for imm is a negative number + let mut cpu_test = cpu::CPU::new(); + + cpu_test.pc = 500; + let ori_pc = cpu_test.pc; + // set x7=3 + helper::set_register_val(&mut cpu_test, 7, -2); + // set x8=3 + helper::set_register_val(&mut cpu_test, 8, -1); + // bltu x8, x7, 12 + let instr: u32 = helper::set_b_type_instruction(12, 7, 8, BLTU as u8); + cpu::exec_bltu(&mut cpu_test, instr); + assert_eq!(cpu_test.pc, ori_pc); + + // set x9=4 + helper::set_register_val(&mut cpu_test, 9, 3); + // bltu x9, x7, 12 + let instr: u32 = helper::set_b_type_instruction(12, 7, 9, BLTU as u8); + cpu::exec_bltu(&mut cpu_test, instr); + assert_eq!(cpu_test.pc as i32, (ori_pc as i32) + 12 - 4); + } #[test] - fn test_exec_bgeu() {} + fn test_exec_bgeu() { + // TODO add test case for imm is a negative number + let mut cpu_test = cpu::CPU::new(); + + cpu_test.pc = 500; + let ori_pc = cpu_test.pc; + // set x7=3 + helper::set_register_val(&mut cpu_test, 7, -1); + // set x8=3 + helper::set_register_val(&mut cpu_test, 8, 3); + // bgeu x8, x7, 12 + let instr: u32 = helper::set_b_type_instruction(12, 7, 8, BGEU as u8); + cpu::exec_bgeu(&mut cpu_test, instr); + assert_eq!(cpu_test.pc, ori_pc); + + // set x9=4 + helper::set_register_val(&mut cpu_test, 9, -2); + // bgeu x9, x7, 12 + let instr: u32 = helper::set_b_type_instruction(12, 7, 9, BGEU as u8); + cpu::exec_bgeu(&mut cpu_test, instr); + assert_eq!(cpu_test.pc as i32, (ori_pc as i32) + 12 - 4); + } #[test] fn test_exec_lb() {} #[test] @@ -214,7 +340,7 @@ mod tests { // 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); + let instr: u32 = helper::set_r_type_instruction(5, 6, ADD as u8, 31); cpu::exec_add(&mut cpu_test, instr); assert_eq!(cpu_test.xregs.regs[31], 2); } @@ -227,7 +353,7 @@ mod tests { // 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); + let instr: u32 = helper::set_r_type_instruction(6, 5, SUB as u8, 31); cpu::exec_sub(&mut cpu_test, instr); assert_eq!(cpu_test.xregs.regs[31], 0xfffffffa); // 0xfffffffa is -6 } diff --git a/tests/helper.rs b/tests/helper.rs index 6eb37c2..9821c42 100644 --- a/tests/helper.rs +++ b/tests/helper.rs @@ -1,8 +1,17 @@ use emurv::{ cpu, - opcode::{I_TYPE, R_TYPE}, + opcode::{B_TYPE, I_TYPE, R_TYPE}, }; +pub fn set_r_type_instruction(rs2: u8, rs1: u8, funct3: u8, rd: u8) -> u32 { + // |31-20|19-15|14-12|11-7|6-0| + return ((rs2 as u32 & 0x1f) << 20) + | ((rs1 as u32 & 0x1f) << 15) + | (funct3 as u32 & 0x3) + | ((rd as u32 & 0x1f) << 7) + | ((R_TYPE as u32) & 0x7f); +} + 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) @@ -12,8 +21,20 @@ pub fn set_i_type_instruction(imm: i16, rs1: u8, funct3: u8, rd: u8) -> u32 { | ((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_b_type_instruction(imm: i16, rs2: u8, rs1: u8, funct3: u8) -> u32 { + let imm12 = (imm & 0x800) as u32; + let imm11 = (imm & 0x1) as u32; + let imm10_5 = (imm & 0x3f0) as u32; + let imm4_1 = (imm & 0x1e) as u32; + + return (imm12 << 19) + | (imm10_5 << 20) + | ((rs2 as u32 & 0x1f) << 20) + | ((rs1 as u32 & 0x1f) << 15) + | ((funct3 as u32 & 0x7) << 12) + | (imm4_1 << 7) + | (imm11 >> 4) + | ((B_TYPE as u32) & 0x7f); } pub fn set_j_type_instruction(imm: i32, rd: u8, opcode: u8) -> u32 { @@ -26,13 +47,8 @@ pub fn set_j_type_instruction(imm: i32, rd: u8, opcode: u8) -> u32 { 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_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_register_val(cpu: &mut cpu::CPU, rd: u8, val: i16) {