mirror of
https://github.com/Noratrieb/rustv32i.git
synced 2026-01-14 13:25:01 +01:00
A
This commit is contained in:
parent
105706e862
commit
ca565ddeb3
5 changed files with 208 additions and 4 deletions
|
|
@ -6,9 +6,9 @@ A small RISC-V emulator written in Rust.
|
||||||
|
|
||||||
- [x] Base RV32I instruction set
|
- [x] Base RV32I instruction set
|
||||||
- [x] M standard extension
|
- [x] M standard extension
|
||||||
- [ ] A standard extension
|
- [x] A standard extension
|
||||||
- [ ] Zalrsc standard extension
|
- [x] Zalrsc standard extension
|
||||||
- [ ] Zaamo standard extension
|
- [x] Zaamo standard extension
|
||||||
- [ ] F standard extension
|
- [ ] F standard extension
|
||||||
- [ ] D standard extension
|
- [ ] D standard extension
|
||||||
- [ ] C standard extension
|
- [ ] C standard extension
|
||||||
|
|
|
||||||
51
src/emu.rs
51
src/emu.rs
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::inst::{Inst, InstCode};
|
use crate::inst::{AmoOp, Inst, InstCode};
|
||||||
use std::{
|
use std::{
|
||||||
fmt::{Debug, Display},
|
fmt::{Debug, Display},
|
||||||
ops::{Index, IndexMut},
|
ops::{Index, IndexMut},
|
||||||
|
|
@ -82,6 +82,9 @@ pub struct Emulator {
|
||||||
/// Written to insterad of xreg[0].
|
/// Written to insterad of xreg[0].
|
||||||
pub xreg0_value: u32,
|
pub xreg0_value: u32,
|
||||||
pub pc: u32,
|
pub pc: u32,
|
||||||
|
/// We need to store the most recent reservation set for LR/SC
|
||||||
|
/// to make SC fail if it's not to this address.
|
||||||
|
pub reservation_set: Option<u32>,
|
||||||
|
|
||||||
pub debug: bool,
|
pub debug: bool,
|
||||||
|
|
||||||
|
|
@ -356,6 +359,52 @@ impl Emulator {
|
||||||
self[dest] = self[src1] % self[src2];
|
self[dest] = self[src1] % self[src2];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Inst::AmoW {
|
||||||
|
order: _,
|
||||||
|
op,
|
||||||
|
dest,
|
||||||
|
addr,
|
||||||
|
src,
|
||||||
|
} => {
|
||||||
|
let addr = self[addr];
|
||||||
|
self[dest] = self.mem.load_u32(addr)?;
|
||||||
|
let result = match op {
|
||||||
|
AmoOp::Swap => self[src],
|
||||||
|
AmoOp::Add => self[dest].wrapping_add(self[src]),
|
||||||
|
AmoOp::Xor => self[dest] ^ self[src],
|
||||||
|
AmoOp::And => self[dest] & self[src],
|
||||||
|
AmoOp::Or => self[dest] | self[src],
|
||||||
|
AmoOp::Min => (self[dest] as i32).min(self[src] as i32) as u32,
|
||||||
|
AmoOp::Max => (self[dest] as i32).max(self[src] as i32) as u32,
|
||||||
|
AmoOp::Minu => self[dest].min(self[src]),
|
||||||
|
AmoOp::Maxu => self[dest].max(self[src]),
|
||||||
|
};
|
||||||
|
self.mem.store_u32(addr, result)?;
|
||||||
|
}
|
||||||
|
Inst::LrW {
|
||||||
|
order: _,
|
||||||
|
dest,
|
||||||
|
addr,
|
||||||
|
} => {
|
||||||
|
let addr = self[addr];
|
||||||
|
self[dest] = self.mem.load_u32(addr)?;
|
||||||
|
self.reservation_set = Some(addr);
|
||||||
|
}
|
||||||
|
Inst::ScW {
|
||||||
|
order: _,
|
||||||
|
dest,
|
||||||
|
addr,
|
||||||
|
src,
|
||||||
|
} => {
|
||||||
|
let addr = self[addr];
|
||||||
|
self.mem.store_u32(addr, self[src])?;
|
||||||
|
if self.reservation_set != Some(addr) {
|
||||||
|
self[dest] = 1; // error
|
||||||
|
} else {
|
||||||
|
self[dest] = 0; // success
|
||||||
|
}
|
||||||
|
self.reservation_set = None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !jumped {
|
if !jumped {
|
||||||
|
|
|
||||||
151
src/inst.rs
151
src/inst.rs
|
|
@ -3,6 +3,7 @@ use std::fmt::{Debug, Display};
|
||||||
use std::ops::RangeInclusive;
|
use std::ops::RangeInclusive;
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
#[rustfmt::skip]
|
||||||
pub enum Inst {
|
pub enum Inst {
|
||||||
Lui { uimm: u32, dest: Reg },
|
Lui { uimm: u32, dest: Reg },
|
||||||
Auipc { uimm: u32, dest: Reg },
|
Auipc { uimm: u32, dest: Reg },
|
||||||
|
|
@ -62,6 +63,26 @@ pub enum Inst {
|
||||||
Divu { dest: Reg, src1: Reg, src2: Reg },
|
Divu { dest: Reg, src1: Reg, src2: Reg },
|
||||||
Rem { dest: Reg, src1: Reg, src2: Reg },
|
Rem { dest: Reg, src1: Reg, src2: Reg },
|
||||||
Remu { dest: Reg, src1: Reg, src2: Reg },
|
Remu { dest: Reg, src1: Reg, src2: Reg },
|
||||||
|
|
||||||
|
// A
|
||||||
|
LrW {
|
||||||
|
order: AmoOrdering,
|
||||||
|
dest: Reg,
|
||||||
|
addr: Reg,
|
||||||
|
},
|
||||||
|
ScW {
|
||||||
|
order: AmoOrdering,
|
||||||
|
dest: Reg,
|
||||||
|
addr: Reg,
|
||||||
|
src: Reg,
|
||||||
|
},
|
||||||
|
AmoW {
|
||||||
|
order: AmoOrdering,
|
||||||
|
op: AmoOp,
|
||||||
|
dest: Reg,
|
||||||
|
addr: Reg,
|
||||||
|
src: Reg,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
|
@ -81,6 +102,27 @@ pub struct FenceSet {
|
||||||
pub memory_write: bool,
|
pub memory_write: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum AmoOrdering {
|
||||||
|
Relaxed,
|
||||||
|
Acquire,
|
||||||
|
Release,
|
||||||
|
SeqCst,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum AmoOp {
|
||||||
|
Swap,
|
||||||
|
Add,
|
||||||
|
Xor,
|
||||||
|
And,
|
||||||
|
Or,
|
||||||
|
Min,
|
||||||
|
Max,
|
||||||
|
Minu,
|
||||||
|
Maxu,
|
||||||
|
}
|
||||||
|
|
||||||
impl Fence {
|
impl Fence {
|
||||||
pub fn is_pause(&self) -> bool {
|
pub fn is_pause(&self) -> bool {
|
||||||
self.pred
|
self.pred
|
||||||
|
|
@ -102,11 +144,23 @@ impl Fence {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AmoOrdering {
|
||||||
|
pub fn from_ac_rl(ac: bool, rl: bool) -> Self {
|
||||||
|
match (ac, rl) {
|
||||||
|
(false, false) => Self::Relaxed,
|
||||||
|
(true, false) => Self::Acquire,
|
||||||
|
(false, true) => Self::Release,
|
||||||
|
(true, true) => Self::SeqCst,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Debug for Inst {
|
impl Debug for Inst {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
Display::fmt(&self, f)
|
Display::fmt(&self, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Inst {
|
impl Display for Inst {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
|
|
@ -220,6 +274,20 @@ impl Display for Inst {
|
||||||
Inst::Divu { dest, src1, src2 } => write!(f, "divu {dest}, {src1}, {src2}"),
|
Inst::Divu { dest, src1, src2 } => write!(f, "divu {dest}, {src1}, {src2}"),
|
||||||
Inst::Rem { dest, src1, src2 } => write!(f, "rem {dest}, {src1}, {src2}"),
|
Inst::Rem { dest, src1, src2 } => write!(f, "rem {dest}, {src1}, {src2}"),
|
||||||
Inst::Remu { dest, src1, src2 } => write!(f, "remu {dest}, {src1}, {src2}"),
|
Inst::Remu { dest, src1, src2 } => write!(f, "remu {dest}, {src1}, {src2}"),
|
||||||
|
Inst::LrW { order, dest, addr } => write!(f, "lr.w{order} {dest}, ({addr})",),
|
||||||
|
Inst::ScW {
|
||||||
|
order,
|
||||||
|
dest,
|
||||||
|
addr,
|
||||||
|
src,
|
||||||
|
} => write!(f, "sc.w{order} {dest}, {src}, ({addr})"),
|
||||||
|
Inst::AmoW {
|
||||||
|
order,
|
||||||
|
op,
|
||||||
|
dest,
|
||||||
|
addr,
|
||||||
|
src,
|
||||||
|
} => write!(f, "am{op}.w{order} {dest}, {src}, ({addr})",),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -242,6 +310,33 @@ impl Display for FenceSet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for AmoOrdering {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
AmoOrdering::Relaxed => write!(f, ""),
|
||||||
|
AmoOrdering::Acquire => write!(f, ".ac"),
|
||||||
|
AmoOrdering::Release => write!(f, ".rl"),
|
||||||
|
AmoOrdering::SeqCst => write!(f, ".acrl"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for AmoOp {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
AmoOp::Swap => write!(f, "swap"),
|
||||||
|
AmoOp::Add => write!(f, "add"),
|
||||||
|
AmoOp::Xor => write!(f, "xor"),
|
||||||
|
AmoOp::And => write!(f, "and"),
|
||||||
|
AmoOp::Or => write!(f, "or"),
|
||||||
|
AmoOp::Min => write!(f, "min"),
|
||||||
|
AmoOp::Max => write!(f, "max"),
|
||||||
|
AmoOp::Minu => write!(f, "minu"),
|
||||||
|
AmoOp::Maxu => write!(f, "maxu"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn sign_extend(value: u32, size: u32) -> u32 {
|
fn sign_extend(value: u32, size: u32) -> u32 {
|
||||||
assert!(size <= u32::BITS);
|
assert!(size <= u32::BITS);
|
||||||
let sign = value >> (size - 1);
|
let sign = value >> (size - 1);
|
||||||
|
|
@ -554,6 +649,62 @@ impl Inst {
|
||||||
_ => return Err(Error::IllegalInstruction(code, "imm")),
|
_ => return Err(Error::IllegalInstruction(code, "imm")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// AMO
|
||||||
|
00101111 => {
|
||||||
|
// width must be W
|
||||||
|
if code.funct3() != 0b010 {
|
||||||
|
return Err(Error::IllegalInstruction(code, "funct3"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let kind = code.extract(27..=31);
|
||||||
|
let ac = code.extract(26..=26) == 1;
|
||||||
|
let rl = code.extract(25..=25) == 1;
|
||||||
|
|
||||||
|
let order = AmoOrdering::from_ac_rl(ac, rl);
|
||||||
|
|
||||||
|
match kind {
|
||||||
|
// LR
|
||||||
|
0b00010 => {
|
||||||
|
if code.rs2().0 != 0 {
|
||||||
|
return Err(Error::IllegalInstruction(code, "rs2"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Inst::LrW {
|
||||||
|
order,
|
||||||
|
dest: code.rd(),
|
||||||
|
addr: code.rs1(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// SC
|
||||||
|
0b00011 => Inst::ScW {
|
||||||
|
order,
|
||||||
|
dest: code.rd(),
|
||||||
|
addr: code.rs1(),
|
||||||
|
src: code.rs2(),
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
let op = match kind {
|
||||||
|
0b00001 => AmoOp::Swap,
|
||||||
|
0b00000 => AmoOp::Add,
|
||||||
|
0b00100 => AmoOp::Xor,
|
||||||
|
0b01100 => AmoOp::And,
|
||||||
|
0b01000 => AmoOp::Or,
|
||||||
|
0b10000 => AmoOp::Min,
|
||||||
|
0b10100 => AmoOp::Max,
|
||||||
|
0b11000 => AmoOp::Minu,
|
||||||
|
0b11100 => AmoOp::Maxu,
|
||||||
|
_ => return Err(Error::IllegalInstruction(code, "funct7")),
|
||||||
|
};
|
||||||
|
Inst::AmoW {
|
||||||
|
order,
|
||||||
|
op,
|
||||||
|
dest: code.rd(),
|
||||||
|
addr: code.rs1(),
|
||||||
|
src: code.rs2(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => return Err(Error::IllegalInstruction(code, "opcode")),
|
_ => return Err(Error::IllegalInstruction(code, "opcode")),
|
||||||
};
|
};
|
||||||
Ok(inst)
|
Ok(inst)
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,7 @@ fn main() -> eyre::Result<()> {
|
||||||
xreg: [0; 32],
|
xreg: [0; 32],
|
||||||
xreg0_value: 0,
|
xreg0_value: 0,
|
||||||
pc: start,
|
pc: start,
|
||||||
|
reservation_set: None,
|
||||||
|
|
||||||
debug: std::env::args().any(|arg| arg == "--debug"),
|
debug: std::env::args().any(|arg| arg == "--debug"),
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,9 @@ fn input() -> u64 {
|
||||||
fn _start() -> ! {
|
fn _start() -> ! {
|
||||||
let random_number = 45;
|
let random_number = 45;
|
||||||
|
|
||||||
|
let num = core::sync::atomic::AtomicI32::new(0);
|
||||||
|
num.swap(10, core::sync::atomic::Ordering::SeqCst);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let n = input();
|
let n = input();
|
||||||
if n == random_number {
|
if n == random_number {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue