more unit tests

fifo input/output from host with back-pressure works
gpio loopback input testing works

framework for automated assertion testing is in place;
all tests from this point forward will endeavor to include
some asserts that will cause the test to fail, so we don't require
manual inspection to check test results.
This commit is contained in:
bunnie 2024-03-03 18:33:30 +08:00
parent ca3a3bba19
commit 0844cfbc52
3 changed files with 144 additions and 48 deletions

View file

@ -1,5 +1,14 @@
pub mod units;
// masks for utra::main::WDATA, which is abused to control GPIO register states for testing this module
// these signals are "pulled through" the hierarchy and only exist in the test configuration, thus
// they don't have a utralib entry
pub const TEST_I2C_MASK: u32 = 0b00001;
pub const TEST_FORCE_MASK: u32 = 0b00010;
pub const TEST_LOOP_OE_MASK: u32 = 0b00100;
pub const TEST_INVERT_MASK: u32 = 0b01000;
pub const TEST_FORCE_OFFSET: usize = 16; // top 16 bits
#[cfg(not(any(target_os = "xous")))]
mod duart {
pub const UART_DOUT: utralib::Register = utralib::Register::new(0, 0xff);
@ -81,22 +90,23 @@ pub fn bio_tests() {
units::hello_world();
units::hello_multiverse();
units::fifo_basic();
units::host_fifo_tests();
}
// Test plan:
// Unit tests:
// -[x] Basic FIFO stall test. Two cores writing to each other to unlock.
// -[ ] Host FIFO stall on empty test. Core stalls until host provides data.
// -[ ] Host FIFO stall on full test. Core stall until host reads data.
// -[ ] GPIO input path test
// -[x] Host FIFO stall on empty test. Core stalls until host provides data.
// -[x] Host FIFO stall on full test. Core stall until host reads data.
// -[x] GPIO input path test
// -[ ] Extclk as x20 stall source
// -[ ] Check Extclk:gpio pin mapping (make sure bit ordering is not swapped)
// -[ ] GPIO direction control test
// -[ ] FIFO level trigger test - eq, gt, lt on various channels, at various fullness levels
// -[ ] Stall on event - register bit test, between cores
// -[ ] Stall on event - register bit test, to host
// -[ ] Stall on event - FIFO level test
// -[ ] Host IRQ generation test - some combination with event tests above to confirm IRQ generation
// -[ ] Extclk as x20 stall source
// -[ ] Check Extclk:gpio pin mapping (make sure bit ordering is not swapped)
// -[ ] Core ID read test
// -[ ] Core aclk counter test
// Application tests:

View file

@ -1,10 +1,10 @@
use super::report_api;
use super::{report_api, TEST_INVERT_MASK};
use crate::*;
pub fn hello_world() {
report_api(0x1310_0000);
let mut bio_ss = BioSharedState::new();
let simple_test_code = fn_to_slice(simple_test, simple_test_endcap);
let simple_test_code = hello_world_code();
// copy code to reset vector for 0th machine
bio_ss.load_code(simple_test_code, 0);
@ -16,24 +16,15 @@ pub fn hello_world() {
report_api(0x1310_00FF);
}
pub unsafe fn simple_test() {
core::arch::asm!(
"add x1, zero, 0x10",
"0:",
"add x1, x1, 0x1",
"mv x21, x1",
"mv x20, zero",
"j 0b",
"nop"
);
}
// this marks the "end address" of simple_test
pub unsafe fn simple_test_endcap() {
core::arch::asm!(
"nop"
);
}
bio_code!(hello_world_code, HELLO_START, HELLO_END,
"add x1, zero, 0x10",
"0:",
"add x1, x1, 0x1",
"mv x21, x1",
"mv x20, zero",
"j 0b",
"nop"
);
pub fn hello_multiverse() {
report_api(0x1310_1000);
@ -51,8 +42,8 @@ pub fn hello_multiverse() {
bio_ss.bio.wo(utra::bio::SFR_QDIV3, 0x20_0000);
// snap GPIO outputs to the quantum
bio_ss.bio.wo(utra::bio::SFR_CONFIG,
bio_ss.bio.ms(utra::bio::SFR_CONFIG_SNAP_TO_QUANTUM, 1)
| bio_ss.bio.ms(utra::bio::SFR_CONFIG_SNAP_TO_QUANTUM, 2) // arbitrary choice, they should all be the same
bio_ss.bio.ms(utra::bio::SFR_CONFIG_SNAP_OUTPUT_TO_QUANTUM, 1)
| bio_ss.bio.ms(utra::bio::SFR_CONFIG_SNAP_OUTPUT_TO_QUANTUM, 2) // arbitrary choice, they should all be the same
);
// start all the machines, all at once
bio_ss.bio.wo(utra::bio::SFR_CTRL, 0xfff);
@ -130,6 +121,9 @@ bio_code!(multiverse_code, MULTIVERSE_START, MULTIVERSE_END,
pub fn fifo_basic() {
report_api(0x1310_2000);
report_api(0xf1f0_0000);
// clear any prior test config state
let mut test_cfg = CSR::new(utra::main::HW_MAIN_BASE as *mut u32);
test_cfg.wo(utra::main::WDATA, 0);
let mut bio_ss = BioSharedState::new();
// stop all the machines, so that code can be loaded
@ -140,12 +134,11 @@ pub fn fifo_basic() {
// configure & run the 0th machine
// / 16. clock
bio_ss.bio.wo(utra::bio::SFR_QDIV0, 0x23_BE00);
bio_ss.bio.wo(utra::bio::SFR_QDIV3, 0x23_BE00);
bio_ss.bio.wo(utra::bio::SFR_QDIV1, 0x33_1200);
// snap GPIO outputs to the quantum
bio_ss.bio.wo(utra::bio::SFR_CONFIG,
bio_ss.bio.ms(utra::bio::SFR_CONFIG_SNAP_TO_QUANTUM, 1)
| bio_ss.bio.ms(utra::bio::SFR_CONFIG_SNAP_TO_QUANTUM, 2) // arbitrary choice, they should all be the same
);
bio_ss.bio.wo(utra::bio::SFR_QDIV2, 0x33_1200);
// don't snap GPIO outputs
bio_ss.bio.wo(utra::bio::SFR_CONFIG, 0);
// start all the machines, all at once
bio_ss.bio.wo(utra::bio::SFR_CTRL, 0xfff);
@ -166,12 +159,13 @@ bio_code!(fifo_basic_code, FIFO_BASIC_START, FIFO_BASIC_END,
"li x2, 0xFFFF",
"mv x26, x2",
"li x1, 0x10000000",
"li x2, 0x10000",
"11:",
"mv x16, x1",
"mv x21, x17",
"add x1, x1, x2",
// pass to mach 3 to update the loop counter
"mv x19, x1",
"mv x20, zero",
"mv x1, x19",
"j 11b",
// mach 1 code
"91:",
@ -181,14 +175,89 @@ bio_code!(fifo_basic_code, FIFO_BASIC_START, FIFO_BASIC_END,
"21:",
"mv x17, x1",
"mv x21, x16",
"addi x1, x1, 1",
// pass to mach 2 to update the loop counter
"mv x18, x1",
"mv x20, zero",
"mv x1, x18",
"j 21b",
// mach 2 code
"92:",
"addi x18, x18, 2", // increment the value in fifo by 2
"mv x20, zero",
"j 92b",
// mach 3 code
"93:",
"j 93b",
"li x2, 0x40000",
"23:",
"add x19, x19, x2", // increment the value in fifo by 0x4_0000
"mv x20, zero",
"j 23b",
"nop"
);
pub fn host_fifo_tests() {
report_api(0x1310_3000);
report_api(0xf1f0_0001);
// clear prior test config state
let mut test_cfg = CSR::new(utra::main::HW_MAIN_BASE as *mut u32);
test_cfg.wo(utra::main::WDATA, 0);
let mut bio_ss = BioSharedState::new();
// stop all the machines, so that code can be loaded
bio_ss.bio.wo(utra::bio::SFR_CTRL, 0x0);
let code = fifo_host_bitbang();
bio_ss.load_code(code, 0);
// configure & run the 0th machine
// clock it slowly, so the fifo builds up back pressure
bio_ss.bio.wo(utra::bio::SFR_QDIV0, 0x400_0000);
bio_ss.bio.wo(utra::bio::SFR_QDIV1, 0x400_0000);
// don't snap GPIO outputs
bio_ss.bio.wo(utra::bio::SFR_CONFIG, 0);
// invert readbacks via I/O
test_cfg.wo(utra::main::WDATA, TEST_INVERT_MASK);
// start cores 1 & 2
bio_ss.bio.wo(utra::bio::SFR_CTRL, 0x333);
// clock some values into the bitbang fifo
for i in 0..16 {
bio_ss.bio.wo(utra::bio::SFR_TXF0, i + 0xF1F0_0000);
while bio_ss.bio.rf(utra::bio::SFR_FLEVEL_PCLK_REGFIFO_LEVEL0) >= 8 {}
}
// wait for the write FIFO to drain
while bio_ss.bio.rf(utra::bio::SFR_FLEVEL_PCLK_REGFIFO_LEVEL0) != 0 {}
// read back some fifo values, and check that back-pressure worked
for i in 0..16 {
let rbk = bio_ss.bio.r(utra::bio::SFR_RXF1);
// we get indices 0-9: we can capture up to 8+1 entries before backpressure stops captures
// and there is 1 extra value stuck in the CPU itself at the time of the stall.
//
// finally, we're pegged at 15, because, backpressure caused us to miss the rest of
// the entries, and we are stuck at the final written value of the output test
if i <= 9 {
assert!(rbk == !(0xF1F0_0000 + i));
} else {
assert!(rbk == !(0xF1F0_0000 + 15));
}
report_api(rbk);
}
report_api(0x1310_30FF);
}
bio_code!(fifo_host_bitbang, FIFO_HOST_BITBANG_START, FIFO_HOST_BITBANG_END,
"j 90f",
"nop",
"j 91f",
"nop",
"90:",
"mv x21, x16",
"mv x20, zero",
"j 90b",
"91:",
"mv x20, zero",
"mv x17, x21",
"j 91b"
);

View file

@ -1,9 +1,38 @@
//! FIFO - 8-deep fifo head/tail access. Cores halt on overflow/underflow.
//! - x16 r/w fifo[0]
//! - x17 r/w fifo[1]
//! - x18 r/w fifo[2]
//! - x19 r/w fifo[3]
//!
//! Quantum - core will halt until host-configured clock divider pules occurs,
//! or an external event comes in on a host-specified GPIO pin.
//! - x20 -/w halt to quantum
//!
//! GPIO - note clear-on-0 semantics for bit-clear ops
//! - x21 r/w write: (x26 & x21) -> gpio pins; read: gpio pins -> x21
//! - x22 -/w (x26 & x22) -> `1` will set corresponding pin on gpio
//! - x23 -/w (x26 & x23) -> `0` will clear corresponding pin on gpio
//! - x24 -/w (x26 & x24) -> `1` will make corresponding gpio pin an output
//! - x25 -/w (x26 & x25) -> `0` will make corresponding gpio pin an input
//! - x26 r/w mask GPIO action outputs
//!
//! Events - operate on a shared event register. Bits [7:0] are hard-wired to FIFO
//! level flags, configured by the host; writes to bits [7:0] are ignored.
//! - x27 -/w mask event sensitivity bits
//! - x28 -/w `1` will set the corresponding event bit. Only [31:8] are wired up.
//! - x29 -/w `1` will clear the corresponding event bit Only [31:8] are wired up.
//! - x30 r/- halt until ((x27 & events) == x27), and return unmasked `events` value
//!
//! Core ID & debug:
//! - x31 r/- [31:30] -> core ID; [29:0] -> cpu clocks since reset
#![cfg_attr(feature = "baremetal", no_std)]
use utralib::generated::*;
#[cfg(feature = "tests")]
pub mod bio_tests;
#[derive(Debug)]
pub enum BioError {
/// specified state machine is not valid
@ -29,18 +58,6 @@ pub fn lfsr_next(state: u16) -> u16 {
((state << 1) + bit) & 0x1_FF
}
type BioRustFn = unsafe fn();
pub fn fn_to_slice(target_fn: BioRustFn, endcap_fn: BioRustFn) -> &'static [u8] {
let start_ptr = target_fn as *const u8;
unsafe {
core::slice::from_raw_parts(start_ptr,
(endcap_fn as *const u8) as usize - start_ptr as usize
)
}
}
pub struct BioSharedState {
pub bio: CSR<u32>,
pub imem_slice: &'static mut [u32],