fix next_pc::Queue and add test

This commit is contained in:
Jacob Lifshay 2025-12-14 20:39:15 -08:00
parent cbd52c60a8
commit f39f40ce1f
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ

View file

@ -2899,11 +2899,11 @@ impl ResetSteps for BranchTargetBuffer {
struct Queue<T, Capacity: Size> { struct Queue<T, Capacity: Size> {
data: ArrayType<T, Capacity>, data: ArrayType<T, Capacity>,
/// inclusive /// inclusive
head: UIntInRangeType<ConstUsize<0>, Capacity>, start: UIntInRangeType<ConstUsize<0>, Capacity>,
/// exclusive /// exclusive
tail: UIntInRangeType<ConstUsize<0>, Capacity>, end: UIntInRangeType<ConstUsize<0>, Capacity>,
/// used to disambiguate between a full and an empty queue /// used to disambiguate between a full and an empty queue
eq_head_tail_means_full: Bool, eq_start_end_means_full: Bool,
} }
impl<T: Type, Capacity: Size> Queue<T, Capacity> { impl<T: Type, Capacity: Size> Queue<T, Capacity> {
@ -2923,37 +2923,36 @@ impl<T: Type, Capacity: Size> Queue<T, Capacity> {
(pos + self.capacity() - 1) % self.capacity() (pos + self.capacity() - 1) % self.capacity()
} }
fn is_empty(this: &SimValue<Self>) -> bool { fn is_empty(this: &SimValue<Self>) -> bool {
this.head == this.tail && !*this.eq_head_tail_means_full this.start == this.end && !*this.eq_start_end_means_full
} }
fn is_full(this: &SimValue<Self>) -> bool { fn is_full(this: &SimValue<Self>) -> bool {
this.head == this.tail && *this.eq_head_tail_means_full this.start == this.end && *this.eq_start_end_means_full
} }
fn len(this: &SimValue<Self>) -> usize { fn len(this: &SimValue<Self>) -> usize {
let capacity = this.ty().capacity(); let capacity = this.ty().capacity();
if Self::is_full(this) { if Self::is_full(this) {
capacity capacity
} else { } else {
(*this.tail + capacity - *this.head) % capacity (*this.end + capacity - *this.start) % capacity
} }
} }
fn space_left(this: &SimValue<Self>) -> usize { fn space_left(this: &SimValue<Self>) -> usize {
this.ty().capacity() - Self::len(this) this.ty().capacity() - Self::len(this)
} }
fn clear(this: &mut SimValue<Self>) { fn clear(this: &mut SimValue<Self>) {
*this.head = 0; *this.start = 0;
*this.tail = 0; *this.end = 0;
*this.eq_head_tail_means_full = false; *this.eq_start_end_means_full = false;
} }
fn try_push(this: &mut SimValue<Self>, value: impl ToSimValueWithType<T>) -> Result<(), ()> { fn try_push(this: &mut SimValue<Self>, value: impl ToSimValueWithType<T>) -> Result<(), ()> {
if Self::is_full(this) { if Self::is_full(this) {
Err(()) Err(())
} else { } else {
let head = *this.head; let end = *this.end;
let head = this.ty().next_pos(head); *this.end = this.ty().next_pos(end);
*this.head = head; *this.eq_start_end_means_full = true;
*this.eq_head_tail_means_full = true; let data = &mut this.data[end];
let data = &mut this.data[head]; *data = dbg!(value.to_sim_value_with_type(data.ty()));
*data = value.to_sim_value_with_type(data.ty());
Ok(()) Ok(())
} }
} }
@ -2961,11 +2960,10 @@ impl<T: Type, Capacity: Size> Queue<T, Capacity> {
if Self::is_empty(this) { if Self::is_empty(this) {
None None
} else { } else {
let head = *this.head; let end = this.ty().prev_pos(*this.end);
let data = this.data[head].clone(); *this.end = end;
let head = this.ty().prev_pos(head); let data = this.data[end].clone();
*this.head = head; *this.eq_start_end_means_full = false;
*this.eq_head_tail_means_full = false;
Some(data) Some(data)
} }
} }
@ -2973,22 +2971,23 @@ impl<T: Type, Capacity: Size> Queue<T, Capacity> {
if Self::is_empty(this) { if Self::is_empty(this) {
None None
} else { } else {
Some(this.data[*this.tail].clone()) Some(this.data[*this.start].clone())
} }
} }
fn peek_iter( fn peek_iter(
this: &SimValue<Self>, this: &SimValue<Self>,
) -> impl Clone + DoubleEndedIterator<Item = SimValue<T>> + ExactSizeIterator { ) -> impl Clone + DoubleEndedIterator<Item = SimValue<T>> + ExactSizeIterator {
(0..Self::len(this)).map(|nth| this.data[this.ty().nth_pos_after(*this.tail, nth)].clone()) (0..Self::len(this))
.map(|nth| dbg!(this.data[this.ty().nth_pos_after(*this.start, nth)].clone()))
} }
fn pop(this: &mut SimValue<Self>) -> Option<SimValue<T>> { fn pop(this: &mut SimValue<Self>) -> Option<SimValue<T>> {
if Self::is_empty(this) { if Self::is_empty(this) {
None None
} else { } else {
let tail = *this.tail; let start = *this.start;
let data = this.data[tail].clone(); *this.start = this.ty().next_pos(start);
*this.tail = this.ty().next_pos(tail); let data = this.data[start].clone();
*this.eq_head_tail_means_full = false; *this.eq_start_end_means_full = false;
Some(data) Some(data)
} }
} }
@ -2999,9 +2998,9 @@ impl<T: SimValueDefault, Capacity: Size> SimValueDefault for Queue<T, Capacity>
fn sim_value_default(self) -> SimValue<Self> { fn sim_value_default(self) -> SimValue<Self> {
let Self { let Self {
data, data,
head, start,
tail, end,
eq_head_tail_means_full: _, eq_start_end_means_full: _,
} = self; } = self;
#[hdl(sim)] #[hdl(sim)]
Queue::<T, Capacity> { Queue::<T, Capacity> {
@ -3009,9 +3008,9 @@ impl<T: SimValueDefault, Capacity: Size> SimValueDefault for Queue<T, Capacity>
data.element().sim_value_default(), data.element().sim_value_default(),
Capacity::from_usize(data.len()), Capacity::from_usize(data.len()),
), ),
head: 0usize.to_sim_value_with_type(head), start: 0usize.to_sim_value_with_type(start),
tail: 0usize.to_sim_value_with_type(tail), end: 0usize.to_sim_value_with_type(end),
eq_head_tail_means_full: false, eq_start_end_means_full: false,
} }
} }
} }
@ -3022,13 +3021,13 @@ impl<T: SimValueDefault, Capacity: Size> ResetSteps for Queue<T, Capacity> {
#[hdl(sim)] #[hdl(sim)]
let Queue::<T, Capacity> { let Queue::<T, Capacity> {
data, data,
head, start,
tail, end,
eq_head_tail_means_full, eq_start_end_means_full,
} = this; } = this;
**head = 0; **start = 0;
**tail = 0; **end = 0;
**eq_head_tail_means_full = false; **eq_start_end_means_full = false;
ResetSteps::reset_step(data, step) ResetSteps::reset_step(data, step)
} }
} }
@ -4141,3 +4140,52 @@ pub fn next_pc(config: PhantomConst<CpuConfig>) {
}, },
); );
} }
#[cfg(test)]
mod tests {
use super::*;
use std::collections::VecDeque;
#[test]
fn test_queue() {
let mut queue: SimValue<Queue<UInt<8>, ConstUsize<8>>> = Queue::TYPE.sim_value_default();
let mut reference_queue = VecDeque::new();
let mut tested_full = false;
let mut tested_empty = false;
for i in 0..0x1000u32 {
let expected_full = reference_queue.len() >= queue.ty().capacity();
let full = Queue::is_full(&queue);
assert_eq!(expected_full, full, "{queue:?}");
let expected_empty = reference_queue.is_empty();
let empty = Queue::is_empty(&queue);
assert_eq!(expected_empty, empty, "{queue:?}");
tested_full |= full;
if tested_full {
tested_empty |= empty;
}
let rand = i
.wrapping_mul(0xED5E3831) // a random prime
.rotate_left(16)
.wrapping_mul(0x2287F1BD) // a random prime
.rotate_left(16);
if ((rand >> 8) & 1) == 0 {
let popped = Queue::pop(&mut queue).map(|v| v.as_int());
let expected_popped = reference_queue.pop_front();
dbg!(expected_popped);
assert_eq!(popped, expected_popped);
} else if !full {
let push_value = rand as u8;
dbg!(push_value);
Queue::try_push(&mut queue, push_value).expect("known to be not full");
reference_queue.push_back(push_value);
}
dbg!(&queue);
dbg!(&reference_queue);
let queue_contents = Vec::from_iter(Queue::peek_iter(&queue).map(|v| v.as_int()));
let reference_queue_contents = Vec::from_iter(reference_queue.iter().copied());
assert_eq!(queue_contents, reference_queue_contents);
}
assert!(tested_full);
assert!(tested_empty);
}
}