Skip to content

Commit

Permalink
Merge pull request #2 from howjmay/basic
Browse files Browse the repository at this point in the history
feat: Add instructions
  • Loading branch information
howjmay authored Feb 12, 2024
2 parents c614c7b + c708837 commit c340474
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 40 deletions.
44 changes: 33 additions & 11 deletions src/cpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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) {}
Expand Down Expand Up @@ -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) {}
Expand Down
13 changes: 7 additions & 6 deletions src/opcode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
}
94 changes: 75 additions & 19 deletions tests/cpu_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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]
Expand Down
34 changes: 30 additions & 4 deletions tests/helper.rs
Original file line number Diff line number Diff line change
@@ -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);
}

0 comments on commit c340474

Please sign in to comment.