Skip to content

Commit

Permalink
Merge pull request #1 from howjmay/basic
Browse files Browse the repository at this point in the history
feat: Initialize the basic implementation
  • Loading branch information
howjmay committed Feb 9, 2024
2 parents 65f4c3c + 496529d commit c614c7b
Show file tree
Hide file tree
Showing 11 changed files with 732 additions and 9 deletions.
23 changes: 23 additions & 0 deletions .github/workflows/test.yml
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
11 changes: 2 additions & 9 deletions .gitignore
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
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "emurv"
version = "0.1.0"
edition = "2021"

[dependencies]
193 changes: 193 additions & 0 deletions src/cpu.rs
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) {}
4 changes: 4 additions & 0 deletions src/lib.rs
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;
109 changes: 109 additions & 0 deletions src/memory.rs
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;
}
}
Loading

0 comments on commit c614c7b

Please sign in to comment.