-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from howjmay/basic
feat: Initialize the basic implementation
- Loading branch information
Showing
11 changed files
with
732 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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/[email protected] | ||
- name: setup Rust Cargo | ||
run: | | ||
sudo apt install cargo | ||
- name: run tests | ||
run: | | ||
cargo test --package emurv --test cpu_test -- tests --nocapture | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
[package] | ||
name = "emurv" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
pub mod cpu; | ||
pub mod memory; | ||
pub mod opcode; | ||
pub mod registers; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} |
Oops, something went wrong.