pub struct PioFifo {
buf: [u32; 8],
head: u8,
tail: u8,
count: u8,
depth: u8,
pub push_success: u64,
pub push_drop: u64,
}
impl PioFifo {
pub fn new(depth: u8) -> Self {
Self {
buf: [0; 8],
head: 0,
tail: 0,
count: 0,
depth,
push_success: 0,
push_drop: 0,
}
}
pub fn push(&mut self, val: u32) -> bool {
if self.count >= self.depth {
self.push_drop = self.push_drop.wrapping_add(1);
return false;
}
self.buf[self.tail as usize] = val;
self.tail = (self.tail + 1) % self.depth;
self.count += 1;
self.push_success = self.push_success.wrapping_add(1);
true
}
pub fn pop(&mut self) -> Option<u32> {
if self.count == 0 {
return None;
}
let val = self.buf[self.head as usize];
self.head = (self.head + 1) % self.depth;
self.count -= 1;
Some(val)
}
pub fn is_full(&self) -> bool {
self.depth > 0 && self.count >= self.depth
}
pub fn is_empty(&self) -> bool {
self.count == 0
}
pub fn level(&self) -> u8 {
self.count
}
pub fn flush(&mut self) {
self.head = 0;
self.tail = 0;
self.count = 0;
}
pub fn set_depth(&mut self, d: u8) {
self.depth = d;
self.flush();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn counters_initialize_to_zero() {
let fifo = PioFifo::new(4);
assert_eq!(fifo.push_success, 0);
assert_eq!(fifo.push_drop, 0);
}
#[test]
fn push_success_count_bumps_on_successful_push() {
let mut fifo = PioFifo::new(4);
assert!(fifo.push(0xAA));
assert!(fifo.push(0xBB));
assert!(fifo.push(0xCC));
assert_eq!(fifo.push_success, 3);
assert_eq!(fifo.push_drop, 0);
}
#[test]
fn push_drop_count_bumps_when_full() {
let mut fifo = PioFifo::new(4);
for v in 0..4u32 {
assert!(fifo.push(v));
}
assert_eq!(fifo.push_success, 4);
assert_eq!(fifo.push_drop, 0);
assert!(!fifo.push(0xDEAD));
assert_eq!(fifo.push_success, 4);
assert_eq!(fifo.push_drop, 1);
for _ in 0..3 {
assert!(!fifo.push(0xBEEF));
}
assert_eq!(fifo.push_success, 4);
assert_eq!(fifo.push_drop, 4);
}
#[test]
fn counters_survive_pop_and_push_mix() {
let mut fifo = PioFifo::new(4);
for v in 0..4u32 {
assert!(fifo.push(v));
}
assert_eq!(fifo.pop(), Some(0));
assert_eq!(fifo.pop(), Some(1));
assert!(fifo.push(0x10));
assert!(fifo.push(0x11));
assert_eq!(fifo.push_success, 6);
assert_eq!(fifo.push_drop, 0);
assert!(!fifo.push(0x12));
assert_eq!(fifo.push_success, 6);
assert_eq!(fifo.push_drop, 1);
}
#[test]
fn is_full_is_false_on_depth_zero_fifo() {
let mut fifo = PioFifo::new(0);
assert!(!fifo.is_full(), "depth=0 FIFO must not report full");
assert!(fifo.is_empty(), "depth=0 FIFO is trivially empty");
assert!(!fifo.push(0xAA));
assert_eq!(fifo.push_drop, 1);
assert_eq!(fifo.push_success, 0);
assert_eq!(fifo.pop(), None);
}
#[test]
fn set_depth_flushes_and_allows_reuse() {
let mut fifo = PioFifo::new(4);
assert!(fifo.push(1));
assert!(fifo.push(2));
assert_eq!(fifo.level(), 2);
fifo.flush();
assert!(fifo.is_empty());
assert_eq!(fifo.level(), 0);
fifo.set_depth(8);
for v in 0..5u32 {
assert!(fifo.push(v));
}
assert_eq!(fifo.level(), 5);
}
}