//! K406: real PL011 input proves exact-capacity acceptance and sticky overflow rejection. use super::*; use turkos_kernel::serial_rx::{BoundedLine, BoundedLineEvent}; const RXFE: u32 = 1 << 4; const DATA_ERROR: u32 = 0x0f00; const INPUT_COUNT: usize = 57; fn fail(reason: &str) -> ! { write_str("TURKOS/SERIAL-LIMIT-ERROR: "); write_line(reason); park() } fn ticks() -> u64 { let value: u64; unsafe { asm!("mrs {value}, CNTPCT_EL0", value = out(reg) value, options(nomem, nostack, preserves_flags)) }; value } pub(super) fn smoke_test() -> ! { let base = UART_BASE.load(Ordering::Relaxed); let frequency: u64; unsafe { asm!("mrs {value}, CNTFRQ_EL0", value = out(reg) frequency, options(nomem, nostack, preserves_flags)) }; if frequency == 0 { fail("zero counter frequency"); } let mut line = BoundedLine::new(); let mut accepted = 0usize; let mut rejected = 0usize; let mut overflows = 0usize; let mut discarded = 0usize; let mut suppressed = 0usize; write_line("TURKOS/SERIAL-LIMIT: READY EXPECTED=0x39 CAPACITY=0x10"); for index in 0..INPUT_COUNT { let deadline = ticks() .checked_add(frequency.saturating_mul(5)) .unwrap_or(u64::MAX); while unsafe { read_volatile((base + 0x18) as *const u32) } & RXFE != 0 { if ticks() >= deadline { fail("input timeout"); } core::hint::spin_loop(); } let data = unsafe { read_volatile(base as *const u32) }; if data & DATA_ERROR != 0 { fail("PL011 data error"); } let byte = data as u8; let label = match line.feed(byte) { BoundedLineEvent::Inserted => "INSERT", BoundedLineEvent::Erased => "ERASE", BoundedLineEvent::Empty => "EMPTY", BoundedLineEvent::OverflowStarted => { overflows += 1; write_line("TURKOS/SERIAL-LIMIT-ERROR-REPORTED: LINE_TOO_LONG MAX=0x10"); "OVERFLOW" } BoundedLineEvent::Discarded => { discarded += 1; "DISCARD" } BoundedLineEvent::TooLong => { rejected += 1; write_str("TURKOS/SERIAL-LIMIT-REJECT: NUMBER=0x"); write_hex(rejected); write_line(" REASON=LINE_TOO_LONG"); "REJECT" } BoundedLineEvent::SuppressedLf => { suppressed += 1; "SUPPRESS" } BoundedLineEvent::Complete(bytes, len) => { let expected = match accepted { 0 => b"AAAAAAAAAAAAAAA".as_slice(), 1 => b"BBBBBBBBBBBBBBBB".as_slice(), 2 => b"OK".as_slice(), _ => fail("too many accepted lines"), }; if &bytes[..len] != expected { fail("line content"); } accepted += 1; write_str("TURKOS/SERIAL-LIMIT-LINE: NUMBER=0x"); write_hex(accepted); write_str(" LENGTH=0x"); write_hex(len); write_str(" TEXT="); for value in &bytes[..len] { write_byte(*value); } write_str("\r\n"); "ACCEPT" } }; write_str("TURKOS/SERIAL-LIMIT-BYTE: INDEX=0x"); write_hex(index); write_str(" VALUE=0x"); write_hex(byte as usize); write_str(" EVENT="); write_str(label); write_str(" LEN=0x"); write_hex(line.len()); write_str(" ACCEPTED=0x"); write_hex(accepted); write_str(" REJECTED=0x"); write_hex(rejected); write_str(" OVERFLOWS=0x"); write_hex(overflows); write_str(" DISCARDED=0x"); write_hex(discarded); write_str(" SUPPRESSED=0x"); write_hex(suppressed); write_str("\r\n"); } if accepted != 3 || rejected != 1 || overflows != 1 || discarded != 2 || suppressed != 1 || line.len() != 0 { fail("final line accounting"); } write_line( "TURKOS/SERIAL-LIMIT: PASS ACCEPTED=0x3 REJECTED=0x1 OVERFLOWS=0x1 DISCARDED=0x2 SUPPRESSED=0x1 BYTES=0x39", ); park() }