use crate::common::{HartId, PhysAddr};
use crate::isa::op::AtomicOp;
use crate::sim::memory::{GlobalMemory, Ram};
use crate::sim::packet::{AccessSize, MemOp, MemRespData};
const H0: HartId = HartId::new(0);
const H1: HartId = HartId::new(1);
const RAM_BYTES: usize = 0x1000;
const WORD: PhysAddr = PhysAddr::new(0x100);
fn memory() -> GlobalMemory {
GlobalMemory::new(Some(Ram::new(0, RAM_BYTES)), 2, 64)
}
fn value(response: &MemRespData) -> u64 {
match response {
MemRespData::Performed { value, .. } => *value,
other => panic!("expected a performed access, got {other:?}"),
}
}
fn amo(op: AtomicOp, data: u64, hart: HartId) -> MemOp {
MemOp::Atomic { op, data, hart }
}
#[test]
fn an_amo_returns_the_old_value_and_leaves_the_result() {
let mut memory = memory();
let _ = memory.perform(WORD, AccessSize::B8, &amo(AtomicOp::Swap, 5, H0));
let old = memory.perform(WORD, AccessSize::B8, &amo(AtomicOp::Add, 3, H1));
assert_eq!(value(&old), 5);
assert_eq!(memory.read(WORD, 8), Some(8));
}
#[test]
fn a_word_amo_writes_only_its_word() {
let mut memory = memory();
memory.load(WORD, &[0xFF; 8]);
let _ = memory.perform(WORD, AccessSize::B4, &amo(AtomicOp::Swap, 0, H0));
assert_eq!(memory.read(WORD, 8), Some(0xFFFF_FFFF_0000_0000));
}
#[test]
fn a_store_conditional_with_its_reservation_writes_and_returns_zero() {
let mut memory = memory();
memory.reservations_mut().set(H0, WORD);
let result = memory.perform(WORD, AccessSize::B8, &amo(AtomicOp::Sc, 9, H0));
assert_eq!(value(&result), 0);
assert_eq!(memory.read(WORD, 8), Some(9));
assert!(!memory.reservations().check(H0, WORD), "an SC clears its reservation");
}
#[test]
fn a_store_conditional_whose_reservation_another_hart_broke_fails() {
let mut memory = memory();
memory.reservations_mut().set(H0, WORD);
let _ = memory.perform(WORD, AccessSize::B8, &amo(AtomicOp::Swap, 1, H1));
let result = memory.perform(WORD, AccessSize::B8, &amo(AtomicOp::Sc, 9, H0));
assert_eq!(value(&result), 1);
assert_eq!(memory.read(WORD, 8), Some(1));
}
#[test]
fn a_span_read_returns_its_bytes_in_address_order() {
let mut memory = memory();
memory.load(PhysAddr::new(0x200), &(1..=0x20).collect::<Vec<u8>>());
let response = memory.perform(PhysAddr::new(0x208), AccessSize::Span(24), &MemOp::Read);
let MemRespData::PerformedBytes { bytes, observed } = response else {
panic!("expected the span's bytes, got {response:?}");
};
assert_eq!(&*bytes, &(9..=32).collect::<Vec<u8>>()[..]);
assert!(observed.is_some(), "a two-hart read carries its write order");
}
#[test]
fn a_span_read_takes_effect_where_it_is_served() {
assert!(MemOp::Read.takes_effect_when_served(AccessSize::Span(64)));
assert!(!MemOp::Read.takes_effect_when_served(AccessSize::Line));
}