#![no_std]
#![no_main]
use core::ptr::{read_volatile, write_volatile};
use et_kernel::{MsgBuf, fence, hart_id, kernel_entry, scp_shire_base, shire_id, trace_str};
kernel_entry!();
const PRODUCER_HART: u32 = 0;
const CONSUMER_HART: u32 = 1;
const N: u32 = 4096;
const CAP: u32 = 256;
const GO: u32 = 0x600D_600D;
const SPIN_MAX: u64 = 2_000_000;
const TAIL_OFF: usize = 0; const HEAD_OFF: usize = 64; const GO_OFF: usize = 128; const RING_OFF: usize = 192;
const USE_SCRATCHPAD: bool = false;
const SHARED_WORDS: usize = RING_OFF / 4 + CAP as usize;
static mut SHARED: [u32; SHARED_WORDS] = [0xDEAD_BEEF; SHARED_WORDS];
#[inline(always)]
fn payload(i: u32) -> u32 {
i.wrapping_add(1)
}
#[unsafe(no_mangle)]
pub extern "C" fn entry_point() -> i64 {
let hid = hart_id();
if hid != PRODUCER_HART && hid != CONSUMER_HART {
return 0;
}
let base: *mut u8 = if USE_SCRATCHPAD {
scp_shire_base(shire_id()) as *mut u8
} else {
&raw mut SHARED as *mut u8
};
let tail_ptr = unsafe { base.add(TAIL_OFF) } as *mut u32;
let head_ptr = unsafe { base.add(HEAD_OFF) } as *mut u32;
let go_ptr = unsafe { base.add(GO_OFF) } as *mut u32;
let ring = unsafe { base.add(RING_OFF) } as *mut u32;
if hid == PRODUCER_HART {
produce(tail_ptr, head_ptr, go_ptr, ring);
} else {
consume(tail_ptr, head_ptr, go_ptr, ring);
}
0
}
fn produce(tail_ptr: *mut u32, head_ptr: *mut u32, go_ptr: *mut u32, ring: *mut u32) {
unsafe {
write_volatile(tail_ptr, 0);
write_volatile(head_ptr, 0);
}
fence();
unsafe { write_volatile(go_ptr, GO) };
let mut tail: u32 = 0;
let mut spins: u64 = 0;
let mut ok = true;
let mut i: u32 = 0;
while i < N {
loop {
let h = unsafe { read_volatile(head_ptr) };
if tail.wrapping_sub(h) < CAP {
break;
}
spins += 1;
if spins > SPIN_MAX {
ok = false;
break;
}
core::hint::spin_loop();
}
if !ok {
break;
}
unsafe { write_volatile(ring.add((tail % CAP) as usize), payload(i)) };
fence();
tail = tail.wrapping_add(1);
unsafe { write_volatile(tail_ptr, tail) };
i += 1;
}
let mut m = MsgBuf::new();
m.str(b"SPSC producer hart ")
.u64(PRODUCER_HART as u64)
.str(b" sent ")
.u64(i as u64)
.str(if ok { b" items OK" } else { b" items TIMEOUT" });
trace_str(m.as_slice());
}
fn consume(tail_ptr: *mut u32, head_ptr: *mut u32, go_ptr: *mut u32, ring: *mut u32) {
let mut spins: u64 = 0;
let mut ok = true;
loop {
if unsafe { read_volatile(go_ptr) } == GO {
break;
}
spins += 1;
if spins > SPIN_MAX {
ok = false;
break;
}
core::hint::spin_loop();
}
fence();
let mut head: u32 = 0;
let mut sum: u64 = 0;
let mut errors: u32 = 0;
let mut i: u32 = 0;
while ok && i < N {
loop {
let t = unsafe { read_volatile(tail_ptr) };
if t != head {
break;
}
spins += 1;
if spins > SPIN_MAX {
ok = false;
break;
}
core::hint::spin_loop();
}
if !ok {
break;
}
fence();
let v = unsafe { read_volatile(ring.add((head % CAP) as usize)) };
if v != payload(i) {
errors += 1;
}
sum = sum.wrapping_add(v as u64);
head = head.wrapping_add(1);
unsafe { write_volatile(head_ptr, head) };
i += 1;
}
let expected = (N as u64) * (N as u64 + 1) / 2;
let pass = ok && errors == 0 && i == N && sum == expected;
let mut m = MsgBuf::new();
m.str(b"SPSC consumer hart ")
.u64(CONSUMER_HART as u64)
.str(b" got ")
.u64(i as u64)
.str(b" items, sum ")
.u64(sum)
.str(b" (expected ")
.u64(expected)
.str(b"), errors ")
.u64(errors as u64)
.str(if pass {
b" -> RESULT PASS"
} else {
b" -> RESULT FAIL"
});
trace_str(m.as_slice());
}
#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! {
loop {
core::hint::spin_loop();
}
}