Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Initialize the basic implementation #1

Merged
merged 3 commits into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading