This commit is contained in:
nora 2025-03-09 16:02:15 +01:00
parent 105706e862
commit ca565ddeb3
5 changed files with 208 additions and 4 deletions

View file

@ -1,4 +1,4 @@
use crate::inst::{Inst, InstCode};
use crate::inst::{AmoOp, Inst, InstCode};
use std::{
fmt::{Debug, Display},
ops::{Index, IndexMut},
@ -82,6 +82,9 @@ pub struct Emulator {
/// Written to insterad of xreg[0].
pub xreg0_value: 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,
@ -356,6 +359,52 @@ impl Emulator {
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 {

View file

@ -3,6 +3,7 @@ use std::fmt::{Debug, Display};
use std::ops::RangeInclusive;
#[derive(Clone, Copy)]
#[rustfmt::skip]
pub enum Inst {
Lui { uimm: u32, dest: Reg },
Auipc { uimm: u32, dest: Reg },
@ -62,6 +63,26 @@ pub enum Inst {
Divu { dest: Reg, src1: Reg, src2: Reg },
Rem { 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)]
@ -81,6 +102,27 @@ pub struct FenceSet {
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 {
pub fn is_pause(&self) -> bool {
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 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(&self, f)
}
}
impl Display for Inst {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
@ -220,6 +274,20 @@ impl Display for Inst {
Inst::Divu { dest, src1, src2 } => write!(f, "divu {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::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 {
assert!(size <= u32::BITS);
let sign = value >> (size - 1);
@ -554,6 +649,62 @@ impl Inst {
_ => 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")),
};
Ok(inst)

View file

@ -64,6 +64,7 @@ fn main() -> eyre::Result<()> {
xreg: [0; 32],
xreg0_value: 0,
pc: start,
reservation_set: None,
debug: std::env::args().any(|arg| arg == "--debug"),