led_client/rp-hal/on-target-tests/tests/i2c_tests/blocking.rs
2024-12-21 00:48:45 -05:00

531 lines
16 KiB
Rust

use core::{cell::RefCell, ops::RangeInclusive};
use critical_section::Mutex;
use fugit::{HertzU32, RateExtU32};
use rp2040_hal::{
self as hal,
clocks::init_clocks_and_plls,
gpio::{FunctionI2C, Pin, PullUp},
i2c::{Error, ValidAddress},
pac,
watchdog::Watchdog,
Clock, Timer,
};
use super::{Controller, FIFOBuffer, Generator, MutexCell, Target, TargetState};
pub struct State {
controller: Option<Controller>,
timer: hal::Timer,
resets: hal::pac::RESETS,
ref_clock_freq: HertzU32,
}
static TARGET: MutexCell<Option<Target>> = Mutex::new(RefCell::new(None));
static PAYLOAD: MutexCell<TargetState> = MutexCell::new(RefCell::new(TargetState::new()));
static TIMER: MutexCell<Option<Timer>> = MutexCell::new(RefCell::new(None));
macro_rules! assert_vec_eq {
($e:expr) => {
critical_section::with(|cs| {
let v = &mut PAYLOAD.borrow_ref_mut(cs).vec;
assert_eq!(*v, $e, "FIFO");
v.clear();
});
};
}
macro_rules! assert_restart_count {
($e:expr) => {{
let restart_cnt: u32 = critical_section::with(|cs| PAYLOAD.borrow_ref(cs).restart_cnt);
defmt::assert!(
$e.contains(&restart_cnt),
"restart count out of range {} ∉ {}",
restart_cnt,
$e
);
}};
}
pub fn setup<T: ValidAddress>(xtal_freq_hz: u32, addr: T) -> State {
unsafe {
hal::sio::spinlock_reset();
}
let mut pac = pac::Peripherals::take().unwrap();
let mut watchdog = Watchdog::new(pac.WATCHDOG);
let clocks = init_clocks_and_plls(
xtal_freq_hz,
pac.XOSC,
pac.CLOCKS,
pac.PLL_SYS,
pac.PLL_USB,
&mut pac.RESETS,
&mut watchdog,
)
.ok()
.unwrap();
let timer = hal::Timer::new(pac.TIMER, &mut pac.RESETS, &clocks);
// The single-cycle I/O block controls our GPIO pins
let mut sio = hal::Sio::new(pac.SIO);
let pins = hal::gpio::Pins::new(
pac.IO_BANK0,
pac.PADS_BANK0,
sio.gpio_bank0,
&mut pac.RESETS,
);
// Configure two pins as being I²C, not GPIO
let ctrl_sda_pin: Pin<_, FunctionI2C, PullUp> = pins.gpio0.reconfigure();
let ctrl_scl_pin: Pin<_, FunctionI2C, PullUp> = pins.gpio1.reconfigure();
let trg_sda_pin: Pin<_, FunctionI2C, PullUp> = pins.gpio2.reconfigure();
let trg_scl_pin: Pin<_, FunctionI2C, PullUp> = pins.gpio3.reconfigure();
let i2c_ctrl = hal::I2C::new_controller(
pac.I2C0,
ctrl_sda_pin,
ctrl_scl_pin,
400.kHz(),
&mut pac.RESETS,
clocks.system_clock.freq(),
);
let i2c_target = hal::I2C::new_peripheral_event_iterator(
pac.I2C1,
trg_sda_pin,
trg_scl_pin,
&mut pac.RESETS,
addr,
);
critical_section::with(|cs| TARGET.replace(cs, Some(i2c_target)));
static mut STACK: rp2040_hal::multicore::Stack<10240> = rp2040_hal::multicore::Stack::new();
unsafe {
// delegate I2C1 irqs to core 1
hal::multicore::Multicore::new(&mut pac.PSM, &mut pac.PPB, &mut sio.fifo)
.cores()
.get_mut(1)
.expect("core 1 is not available")
.spawn(&mut STACK.mem, || {
pac::NVIC::unpend(hal::pac::Interrupt::I2C1_IRQ);
pac::NVIC::unmask(hal::pac::Interrupt::I2C1_IRQ);
loop {
cortex_m::asm::wfi()
}
})
.expect("failed to start second core.");
}
State {
controller: Some(i2c_ctrl),
timer,
resets: pac.RESETS,
ref_clock_freq: clocks.system_clock.freq(),
}
}
pub fn reset<T: ValidAddress>(state: &mut State, addr: T, throttling: bool) -> &mut Controller {
// reset controller
let (i2c, (sda, scl)) = state
.controller
.take()
.expect("State contains a controller")
.free(&mut state.resets);
// TARGET is shared with core1. Therefore this needs to happen in a cross-core
// critical-section.
critical_section::with(|cs| {
// reset peripheral
let (i2c, (sda, scl)) = TARGET
.replace(cs, None)
.expect("State contains a target")
.free(&mut state.resets);
// reset payload storage
PAYLOAD.replace_with(cs, |_| TargetState::new());
// remove timer/disable throttling
TIMER.replace(cs, throttling.then_some(state.timer));
//
TARGET.replace(
cs,
Some(hal::I2C::new_peripheral_event_iterator(
i2c,
sda,
scl,
&mut state.resets,
addr,
)),
);
});
state.controller = Some(hal::I2C::new_controller(
i2c,
sda,
scl,
400.kHz(),
&mut state.resets,
state.ref_clock_freq,
));
state
.controller
.as_mut()
.expect("State contains a controller")
}
/// Wait for the expected count of Stop event to ensure the target side has finished processing
/// requests.
///
/// If a test ends with a write command, there is a risk that the test will check the content of
/// the shared buffer while the target handler hasn't finished processing its fifo.
pub fn wait_stop_count(stop_cnt: u32) {
while critical_section::with(|cs| PAYLOAD.borrow_ref(cs).stop_cnt) < stop_cnt {
cortex_m::asm::wfe();
}
defmt::flush();
}
pub fn peripheral_handler() {
critical_section::with(|cs| {
let Some(ref mut target) = *TARGET.borrow_ref_mut(cs) else {
return;
};
let mut timer = TIMER.borrow_ref_mut(cs);
while let Some(evt) = target.next_event() {
if let Some(t) = timer.as_mut() {
use embedded_hal_0_2::blocking::delay::DelayUs;
t.delay_us(50);
}
super::target_handler(
target,
evt,
&mut *PAYLOAD.borrow_ref_mut(cs),
timer.is_some(),
);
}
})
}
pub fn write<T: ValidAddress>(state: &mut State, addr: T) {
use embedded_hal_0_2::blocking::i2c::Write;
let controller = reset(state, addr, false);
let samples: FIFOBuffer = Generator::seq().take(25).collect();
assert_eq!(controller.write(addr, &samples).is_ok(), true);
wait_stop_count(1);
assert_restart_count!((0..=0));
assert_vec_eq!(samples);
}
pub fn write_iter<T: ValidAddress>(state: &mut State, addr: T) {
let controller = reset(state, addr, false);
let samples: FIFOBuffer = Generator::seq().take(25).collect();
controller
.write_iter(addr, samples.iter().cloned())
.expect("Successful write_iter");
wait_stop_count(1);
assert_restart_count!((0..=0));
assert_vec_eq!(samples);
}
pub fn write_iter_read<T: ValidAddress>(
state: &mut State,
addr: T,
restart_count: RangeInclusive<u32>,
) {
let controller = reset(state, addr, false);
let samples_seq: FIFOBuffer = Generator::seq().take(25).collect();
let samples_fib: FIFOBuffer = Generator::fib().take(25).collect();
let mut v = [0u8; 25];
controller
.write_iter_read(addr, samples_fib.iter().cloned(), &mut v)
.expect("Successful write_iter_read");
wait_stop_count(1);
assert_restart_count!(restart_count);
assert_eq!(v, samples_seq);
assert_vec_eq!(samples_fib);
}
pub fn write_read<T: ValidAddress>(state: &mut State, addr: T, restart_count: RangeInclusive<u32>) {
use embedded_hal_0_2::blocking::i2c::WriteRead;
let controller = reset(state, addr, false);
let samples_seq: FIFOBuffer = Generator::seq().take(25).collect();
let samples_fib: FIFOBuffer = Generator::fib().take(25).collect();
let mut v = [0u8; 25];
controller
.write_read(addr, &samples_fib, &mut v)
.expect("successfully write_read");
wait_stop_count(1);
assert_restart_count!(restart_count);
assert_eq!(v, samples_seq);
assert_vec_eq!(samples_fib);
}
pub fn read<T: ValidAddress>(state: &mut State, addr: T, restart_count: RangeInclusive<u32>) {
use embedded_hal_0_2::blocking::i2c::Read;
let controller = reset(state, addr, false);
let mut v = [0u8; 25];
controller.read(addr, &mut v).expect("successfully read");
wait_stop_count(1);
let samples: FIFOBuffer = Generator::fib().take(25).collect();
assert_restart_count!(restart_count);
assert_eq!(v, samples);
assert_vec_eq!([]);
}
pub fn transactions_read<T: ValidAddress>(
state: &mut State,
addr: T,
restart_count: RangeInclusive<u32>,
) {
use embedded_hal::i2c::{I2c, Operation};
let controller = reset(state, addr, false);
let mut v = [0u8; 25];
controller
.transaction(addr, &mut [Operation::Read(&mut v)])
.expect("successfully write_read");
wait_stop_count(1);
let samples: FIFOBuffer = Generator::fib().take(25).collect();
assert_restart_count!(restart_count);
assert_eq!(v, samples);
assert_vec_eq!([]);
}
pub fn transactions_write<T: ValidAddress>(state: &mut State, addr: T) {
use embedded_hal::i2c::{I2c, Operation};
let controller = reset(state, addr, false);
let samples: FIFOBuffer = Generator::seq().take(25).collect();
controller
.transaction(addr, &mut [Operation::Write(&samples)])
.expect("successfully write_read");
wait_stop_count(1);
assert_restart_count!((0..=0));
assert_vec_eq!(samples);
}
pub fn transactions_read_write<T: ValidAddress>(
state: &mut State,
addr: T,
restart_count: RangeInclusive<u32>,
) {
use embedded_hal::i2c::{I2c, Operation};
let controller = reset(state, addr, true);
let samples_seq: FIFOBuffer = Generator::seq().take(25).collect();
let samples_fib: FIFOBuffer = Generator::fib().take(25).collect();
let mut v = [0u8; 25];
controller
.transaction(
addr,
&mut [Operation::Read(&mut v), Operation::Write(&samples_seq)],
)
.expect("successfully write_read");
wait_stop_count(1);
assert_restart_count!(restart_count);
assert_eq!(v, samples_fib);
assert_vec_eq!(samples_seq);
}
pub fn transactions_write_read<T: ValidAddress>(
state: &mut State,
addr: T,
restart_count: RangeInclusive<u32>,
) {
use embedded_hal::i2c::{I2c, Operation};
let controller = reset(state, addr, false);
let samples_seq: FIFOBuffer = Generator::seq().take(25).collect();
let mut v = [0u8; 25];
controller
.transaction(
addr,
&mut [Operation::Write(&samples_seq), Operation::Read(&mut v)],
)
.expect("successfully write_read");
wait_stop_count(1);
assert_restart_count!(restart_count);
assert_eq!(v, samples_seq);
assert_vec_eq!(samples_seq);
}
pub fn transaction<T: ValidAddress>(
state: &mut State,
addr: T,
restart_count: RangeInclusive<u32>,
) {
// Throttling is important for this test as it also ensures that the Target implementation
// does not "waste" bytes that would be discarded otherwise.
//
// One down side of this is that the Target implementation is unable to detect restarts
// between consicutive write operations
use embedded_hal::i2c::{I2c, Operation};
let controller = reset(state, addr, true);
let mut v = ([0u8; 14], [0u8; 25], [0u8; 25], [0u8; 14], [0u8; 14]);
let samples: FIFOBuffer = Generator::seq().take(25).collect();
controller
.transaction(
addr,
&mut [
Operation::Write(&samples), // goes to v2
Operation::Read(&mut v.0),
Operation::Read(&mut v.1),
Operation::Read(&mut v.2),
Operation::Write(&samples), // goes to v3
Operation::Read(&mut v.3),
Operation::Write(&samples), // goes to v4
Operation::Write(&samples), // remains in buffer
Operation::Write(&samples), // remains in buffer
Operation::Read(&mut v.4),
],
)
.expect("successfully write_read");
wait_stop_count(1);
// There are 14restarts in this sequence but because of latency in the target handling, it
// may only detect 7.
assert_restart_count!(restart_count);
// assert writes
let e: FIFOBuffer = itertools::chain!(
samples.iter(),
samples.iter(),
samples.iter(),
samples.iter(),
samples.iter(),
)
.cloned()
.collect();
assert_vec_eq!(e);
// assert reads
let g: FIFOBuffer = Generator::seq().take(92).collect();
let h: FIFOBuffer = itertools::chain!(
v.0.into_iter(),
v.1.into_iter(),
v.2.into_iter(),
v.3.into_iter(),
v.4.into_iter()
)
.collect();
assert_eq!(g, h);
}
pub fn transactions_iter<T: ValidAddress>(
state: &mut State,
addr: T,
restart_count: RangeInclusive<u32>,
) {
use embedded_hal::i2c::{I2c, Operation};
let controller = reset(state, addr, false);
let samples: FIFOBuffer = Generator::seq().take(25).collect();
let mut v = [0u8; 25];
controller
.transaction(
addr,
&mut [Operation::Write(&samples), Operation::Read(&mut v)],
)
.expect("successfully write_read");
wait_stop_count(1);
assert_restart_count!(restart_count);
assert_eq!(v, samples);
assert_vec_eq!(samples);
}
pub fn embedded_hal<T: ValidAddress>(
state: &mut State,
addr: T,
restart_count: RangeInclusive<u32>,
) {
// Throttling is important for this test as it also ensures that the Target implementation
// does not "waste" bytes that would be discarded otherwise.
//
// One down side of this is that the Target implementation is unable to detect restarts
// between consicutive write operations
use embedded_hal::i2c::I2c;
let controller = reset(state, addr, true);
let samples1: FIFOBuffer = Generator::seq().take(25).collect();
let samples2: FIFOBuffer = Generator::fib().take(14).collect();
let mut v = ([0; 14], [0; 25], [0; 25], [0; 14], [0; 14]);
let mut case = || {
controller.write(addr, &samples1)?;
wait_stop_count(1);
controller.read(addr, &mut v.0)?;
wait_stop_count(2);
controller.read(addr, &mut v.1)?;
wait_stop_count(3);
controller.read(addr, &mut v.2)?;
wait_stop_count(4);
controller.write_read(addr, &samples2, &mut v.3)?;
wait_stop_count(5);
controller.write(addr, &samples2)?;
wait_stop_count(6);
controller.write(addr, &samples1)?;
wait_stop_count(7);
controller.write_read(addr, &samples1, &mut v.4)?;
wait_stop_count(8);
Ok::<(), Error>(())
};
case().expect("Successful test");
// There are 14restarts in this sequence but because of latency in the target handling, it
// may only detect 7.
assert_restart_count!(restart_count);
// assert writes
let e: FIFOBuffer = itertools::chain!(
Generator::seq().take(25),
Generator::fib().take(14),
Generator::fib().take(14),
Generator::seq().take(25),
Generator::seq().take(25),
)
.collect();
assert_vec_eq!(e);
// assert reads
let g: FIFOBuffer = itertools::chain!(
Generator::seq().take(64),
Generator::fib().take(14),
Generator::seq().take(14)
)
.collect();
let h: FIFOBuffer = itertools::chain!(
v.0.into_iter(),
v.1.into_iter(),
v.2.into_iter(),
v.3.into_iter(),
v.4.into_iter()
)
.collect();
assert_eq!(g, h);
}