diff --git a/hil-test/tests/embassy_timers_executors.rs b/hil-test/tests/embassy_timers_executors.rs index 9fca42ac2..301a71d5d 100644 --- a/hil-test/tests/embassy_timers_executors.rs +++ b/hil-test/tests/embassy_timers_executors.rs @@ -1,23 +1,4 @@ //! Embassy timer and executor Test -//! -//! Description of implemented tests: -//! - run_test_one_shot_timg(): Tests Timg configured as OneShotTimer -//! - run_test_periodic_timg(): Tests Timg configured as PeriodicTimer -//! - run_test_one_shot_systimer(): Tests systimer configured as OneShotTimer -//! - run_test_periodic_systimer(): Tests systimer configured as PeriodicTimer -//! - run_test_periodic_oneshot_timg(): Tests Timg configured as PeriodicTimer -//! and then reconfigured as OneShotTimer -//! - run_test_periodic_oneshot_systimer(): Tests systimer configured as -//! PeriodicTimer and then reconfigured as OneShotTimer -//! - run_test_join_timg(): Tests Timg configured as OneShotTimer and wait on -//! two different timeouts via join -//! - run_test_join_systimer(): Tests systimer configured as OneShotTimer and -//! wait on two different timeouts via join -//! - run_test_interrupt_executor(): Tests InterruptExecutor and Thread -//! (default) executor in parallel -//! - run_tick_test_timg(): Tests Timg configured as OneShotTimer if it fires -//! immediately in the case of the time scheduling was already in the past -//! (timestamp being too big) // esp32c2 is disabled currently as it fails //% CHIPS: esp32 esp32c3 esp32c6 esp32h2 esp32s3 @@ -29,7 +10,7 @@ use defmt_rtt as _; use embassy_time::{Duration, Ticker, Timer}; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, + clock::{ClockControl, Clocks}, peripherals::Peripherals, prelude::*, system::SystemControl, @@ -49,207 +30,104 @@ macro_rules! mk_static { }}; } -unsafe fn __make_static(t: &mut T) -> &'static mut T { - ::core::mem::transmute(t) -} - -// we need to tell the test framework somehow, if the async test passed or -// failed, this mod is the list of functions that are spawned as an actual tests -mod task_invokers { - use test_helpers::*; - - use super::*; - - #[embassy_executor::task] - pub async fn test_one_shot_timg_invoker() { - let outcome = test_helpers::test_one_shot_timg().await; - - embedded_test::export::check_outcome(outcome); - } - - #[embassy_executor::task] - #[cfg(not(feature = "esp32"))] - pub async fn test_one_shot_systimer_invoker() { - let outcome = task_invokers::test_one_shot_systimer().await; - - embedded_test::export::check_outcome(outcome); - } - - #[embassy_executor::task] - pub async fn test_join_timg_invoker() { - let outcome = test_join_timg().await; - - embedded_test::export::check_outcome(outcome); - } - - #[embassy_executor::task] - #[cfg(not(feature = "esp32"))] - pub async fn test_join_systimer_invoker() { - let outcome = test_join_systimer().await; - - embedded_test::export::check_outcome(outcome); - } - - #[embassy_executor::task] - #[cfg(not(feature = "esp32"))] - pub async fn test_interrupt_executor_invoker() { - let outcome = test_interrupt_executor().await; - - embedded_test::export::check_outcome(outcome); - } - - #[embassy_executor::task] - pub async fn test_tick_and_increment_invoker() { - let outcome = tick_and_increment().await; - - embedded_test::export::check_outcome(outcome); - } -} - // List of the functions that are ACTUALLY TESTS but are called in the invokers mod test_helpers { use super::*; - pub async fn test_one_shot_timg() { - let peripherals = unsafe { Peripherals::steal() }; - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timers = [OneShotTimer::new(timer0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); - - let t1 = esp_hal::time::current_time(); - Timer::after_millis(500).await; - task300ms().await; - let t2 = esp_hal::time::current_time(); - - assert!(t2 > t1); - assert!((t2 - t1).to_millis() >= 500u64); - } - - #[cfg(not(feature = "esp32"))] - pub async fn test_one_shot_systimer() { - let peripherals = unsafe { Peripherals::steal() }; - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - - let systimer = SystemTimer::new(peripherals.SYSTIMER); - let alarm0: ErasedTimer = systimer.alarm0.into(); - let timers = [OneShotTimer::new(alarm0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); - - let t1 = esp_hal::time::current_time(); - Timer::after_millis(500).await; - task300ms().await; - let t2 = esp_hal::time::current_time(); - - assert!(t2 > t1); - assert!((t2 - t1).to_millis() >= 500u64); - } - - pub async fn test_join_timg() { - let peripherals = unsafe { Peripherals::steal() }; - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timers = [OneShotTimer::new(timer0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); - - let t1 = esp_hal::time::current_time(); - embassy_futures::join::join(task500ms(), task300ms()).await; - task500ms().await; - let t2 = esp_hal::time::current_time(); - - assert!(t2 > t1); - assert!((t2 - t1).to_millis() >= 1_000u64); - } - - #[cfg(not(feature = "esp32"))] - pub async fn test_join_systimer() { - let peripherals = unsafe { Peripherals::steal() }; - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - - let systimer = SystemTimer::new(peripherals.SYSTIMER); - let alarm0: ErasedTimer = systimer.alarm0.into(); - let timers = [OneShotTimer::new(alarm0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); - - let t1 = esp_hal::time::current_time(); - embassy_futures::join::join(task500ms(), task300ms()).await; - task500ms().await; - let t2 = esp_hal::time::current_time(); - - assert!(t2 > t1); - assert!((t2 - t1).to_millis() >= 1_000u64); - } - - #[cfg(not(feature = "esp32"))] - pub async fn test_interrupt_executor() { - let mut ticker = Ticker::every(Duration::from_millis(300)); - - let t1 = esp_hal::time::current_time(); - ticker.next().await; - ticker.next().await; - ticker.next().await; - let t2 = esp_hal::time::current_time(); - - assert!(t2 > t1); - assert!((t2 - t1).to_millis() >= 900u64); - } - - pub async fn tick_and_increment() { - const HZ: u64 = 100_000u64; - let mut counter = 0; - let mut ticker = Ticker::every(Duration::from_hz(HZ)); - - let t1 = esp_hal::time::current_time(); - let t2; - - loop { - ticker.next().await; - counter += 1; - - if counter > 100_000 { - t2 = esp_hal::time::current_time(); - break; - } - } - - assert!(t2 > t1); - assert!((t2 - t1).to_millis() >= 1000u64); - assert!((t2 - t1).to_millis() <= 1300u64); - } - #[embassy_executor::task] - pub async fn tick() { - const HZ: u64 = 1000u64; - let mut ticker = Ticker::every(Duration::from_hz(HZ)); + pub async fn e_task30ms() { + Timer::after_millis(30).await; + } +} - loop { - ticker.next().await; - } +mod test_cases { + use esp_hal::peripheral::Peripheral; + + use super::*; + + pub async fn run_test_one_shot_async() { + let t1 = esp_hal::time::current_time(); + Timer::after_millis(50).await; + Timer::after_millis(30).await; + let t2 = esp_hal::time::current_time(); + + assert!(t2 > t1, "t2: {:?}, t1: {:?}", t2, t1); + assert!( + (t2 - t1).to_millis() >= 80u64, + "diff: {:?}", + (t2 - t1).to_millis() + ); } - pub async fn task500ms() { - Timer::after_millis(500).await; + pub fn run_test_periodic_timer(timer: impl Peripheral

) { + let mut periodic = PeriodicTimer::new(timer); + + let t1 = esp_hal::time::current_time(); + periodic.start(100.millis()).unwrap(); + + nb::block!(periodic.wait()).unwrap(); + let t2 = esp_hal::time::current_time(); + + assert!(t2 > t1, "t2: {:?}, t1: {:?}", t2, t1); + assert!( + (t2 - t1).to_millis() >= 100u64, + "diff: {:?}", + (t2 - t1).to_millis() + ); } - pub async fn task300ms() { - Timer::after_millis(300).await; + pub fn run_test_oneshot_timer(timer: impl Peripheral

) { + let timer = OneShotTimer::new(timer); + + let t1 = esp_hal::time::current_time(); + timer.delay_millis(50); + let t2 = esp_hal::time::current_time(); + + assert!(t2 > t1, "t2: {:?}, t1: {:?}", t2, t1); + assert!( + (t2 - t1).to_millis() >= 50u64, + "diff: {:?}", + (t2 - t1).to_millis() + ); } - #[embassy_executor::task] - pub async fn e_task300ms() { - task300ms().await; + pub async fn run_join_test() { + let t1 = esp_hal::time::current_time(); + embassy_futures::join::join(Timer::after_millis(50), Timer::after_millis(30)).await; + Timer::after_millis(50).await; + let t2 = esp_hal::time::current_time(); + + assert!(t2 > t1, "t2: {:?}, t1: {:?}", t2, t1); + assert!( + (t2 - t1).to_millis() >= 100u64, + "diff: {:?}", + (t2 - t1).to_millis() + ); + } +} + +struct Resources { + clocks: Clocks<'static>, + timg0: esp_hal::peripherals::TIMG0, + #[cfg(not(feature = "esp32"))] + systimer: esp_hal::peripherals::SYSTIMER, + software_interrupt_control: esp_hal::system::SoftwareInterruptControl, +} + +impl Resources { + fn set_up_embassy_with_timg0(self) { + let timg0 = TimerGroup::new(self.timg0, &self.clocks); + let timer0: ErasedTimer = timg0.timer0.into(); + let timers = mk_static!(OneShotTimer, OneShotTimer::new(timer0)); + esp_hal_embassy::init(&self.clocks, core::slice::from_mut(timers)); + } + + #[cfg(not(feature = "esp32"))] + fn set_up_embassy_with_systimer(self) { + let systimer = SystemTimer::new(self.systimer); + let alarm0: ErasedTimer = systimer.alarm0.into(); + let timers = mk_static!(OneShotTimer, OneShotTimer::new(alarm0)); + esp_hal_embassy::init(&self.clocks, core::slice::from_mut(timers)); } } @@ -257,226 +135,174 @@ mod test_helpers { #[embedded_test::tests(executor = esp_hal_embassy::Executor::new())] mod test { use super::*; - use crate::{task_invokers::*, test_helpers::*}; + use crate::{test_cases::*, test_helpers::*}; - #[test] - #[timeout(3)] - fn run_test_one_shot_timg() { - let mut executor = esp_hal_embassy::Executor::new(); - let executor = unsafe { __make_static(&mut executor) }; - executor.run(|spawner| { - spawner.must_spawn(task_invokers::test_one_shot_timg_invoker()); - }); - } - - #[test] - #[timeout(3)] - fn run_test_periodic_timg() { - let peripherals = Peripherals::take(); + #[init] + fn init() -> Resources { + let peripherals = unsafe { Peripherals::steal() }; let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - - let mut periodic = PeriodicTimer::new(timg0.timer0); - - let t1 = esp_hal::time::current_time(); - periodic.start(1.secs()).unwrap(); - - let t2; - loop { - nb::block!(periodic.wait()).unwrap(); - t2 = esp_hal::time::current_time(); - break; + Resources { + clocks: ClockControl::boot_defaults(system.clock_control).freeze(), + timg0: peripherals.TIMG0, + #[cfg(not(feature = "esp32"))] + systimer: peripherals.SYSTIMER, + software_interrupt_control: system.software_interrupt_control, } - assert!(t2 > t1); - assert!((t2 - t1).to_millis() >= 1_000u64); + } + + #[test] + #[timeout(3)] + async fn test_one_shot_timg(resources: Resources) { + resources.set_up_embassy_with_timg0(); + + run_test_one_shot_async().await; } #[test] #[timeout(3)] #[cfg(not(feature = "esp32"))] - fn run_test_one_shot_systimer() { - let mut executor = esp_hal_embassy::Executor::new(); - let executor = unsafe { __make_static(&mut executor) }; - executor.run(|spawner| { - spawner.must_spawn(test_one_shot_systimer_invoker()); - }); + async fn test_one_shot_systimer(resources: Resources) { + resources.set_up_embassy_with_systimer(); + + run_test_one_shot_async().await; + } + + #[test] + #[timeout(3)] + fn test_periodic_timg(resources: Resources) { + let timg0 = TimerGroup::new(resources.timg0, &resources.clocks); + + run_test_periodic_timer(timg0.timer0); } #[test] #[timeout(3)] #[cfg(not(feature = "esp32"))] - fn run_test_periodic_systimer() { - let peripherals = Peripherals::take(); + fn test_periodic_systimer(resources: Resources) { + let systimer = SystemTimer::new(resources.systimer); - let systimer = SystemTimer::new(peripherals.SYSTIMER); - - let mut periodic = PeriodicTimer::new(systimer.alarm0); - - let t1 = esp_hal::time::current_time(); - periodic.start(1.secs()).unwrap(); - - let t2; - loop { - nb::block!(periodic.wait()).unwrap(); - t2 = esp_hal::time::current_time(); - break; - } - assert!(t2 > t1); - assert!((t2 - t1).to_millis() >= 1_000u64); + run_test_periodic_timer(systimer.alarm0); } #[test] #[timeout(3)] - fn run_test_periodic_oneshot_timg() { - let mut peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + fn test_periodic_oneshot_timg(mut resources: Resources) { + let mut timg0 = TimerGroup::new(&mut resources.timg0, &resources.clocks); + run_test_periodic_timer(&mut timg0.timer0); - let mut timg0 = TimerGroup::new(&mut peripherals.TIMG0, &clocks); - - let mut periodic = PeriodicTimer::new(&mut timg0.timer0); - - let t1 = esp_hal::time::current_time(); - periodic.start(1.secs()).unwrap(); - - let t2; - loop { - nb::block!(periodic.wait()).unwrap(); - t2 = esp_hal::time::current_time(); - break; - } - assert!(t2 > t1); - assert!((t2 - t1).to_millis() >= 1_000u64); - - core::mem::drop(periodic); - - let timg0 = TimerGroup::new(&mut peripherals.TIMG0, &clocks); - - let timer0 = OneShotTimer::new(timg0.timer0); - - let t1 = esp_hal::time::current_time(); - timer0.delay_millis(500); - let t2 = esp_hal::time::current_time(); - - assert!(t2 > t1); - assert!((t2 - t1).to_millis() >= 500u64); + let mut timg0 = TimerGroup::new(&mut resources.timg0, &resources.clocks); + run_test_oneshot_timer(&mut timg0.timer0); } #[test] #[timeout(3)] #[cfg(not(feature = "esp32"))] - fn run_test_periodic_oneshot_systimer() { - let mut peripherals = Peripherals::take(); + fn test_periodic_oneshot_systimer(mut resources: Resources) { + let mut systimer = SystemTimer::new(&mut resources.systimer); + run_test_periodic_timer(&mut systimer.alarm0); - let mut systimer = SystemTimer::new(&mut peripherals.SYSTIMER); - - let mut periodic = PeriodicTimer::new(&mut systimer.alarm0); - - let t1 = esp_hal::time::current_time(); - periodic.start(1.secs()).unwrap(); - - let t2; - loop { - nb::block!(periodic.wait()).unwrap(); - t2 = esp_hal::time::current_time(); - break; - } - assert!(t2 > t1); - assert!((t2 - t1).to_millis() >= 1_000u64); - - core::mem::drop(periodic); - - let systimer = SystemTimer::new(&mut peripherals.SYSTIMER); - - let timer0 = OneShotTimer::new(systimer.alarm0); - - let t1 = esp_hal::time::current_time(); - timer0.delay_millis(500); - let t2 = esp_hal::time::current_time(); - - assert!(t2 > t1); - assert!((t2 - t1).to_millis() >= 500u64); + let mut systimer = SystemTimer::new(&mut resources.systimer); + run_test_oneshot_timer(&mut systimer.alarm0); } #[test] #[timeout(3)] - fn run_test_join_timg() { - let mut executor = esp_hal_embassy::Executor::new(); - let executor = unsafe { __make_static(&mut executor) }; - executor.run(|spawner| { - spawner.must_spawn(test_join_timg_invoker()); - }); + async fn test_join_timg(resources: Resources) { + resources.set_up_embassy_with_timg0(); + + run_join_test().await; } #[test] #[timeout(3)] #[cfg(not(feature = "esp32"))] - fn run_test_join_systimer() { - let mut executor = esp_hal_embassy::Executor::new(); - let executor = unsafe { __make_static(&mut executor) }; - executor.run(|spawner| { - spawner.must_spawn(test_join_systimer_invoker()); - }); + async fn test_join_systimer(resources: Resources) { + resources.set_up_embassy_with_systimer(); + + run_join_test().await; } + /// Test that the ticker works in tasks ran by the interrupt executors. #[test] #[timeout(3)] #[cfg(not(feature = "esp32"))] - async fn run_test_interrupt_executor() { - let spawner = embassy_executor::Spawner::for_current_executor().await; - - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); + async fn test_interrupt_executor(resources: Resources) { + let timg0 = TimerGroup::new(resources.timg0, &resources.clocks); let timer0: ErasedTimer = timg0.timer0.into(); let timer0 = OneShotTimer::new(timer0); - let timer1 = { - let systimer = esp_hal::timer::systimer::SystemTimer::new(peripherals.SYSTIMER); - let alarm0: ErasedTimer = systimer.alarm0.into(); - OneShotTimer::new(alarm0) - }; + let systimer = SystemTimer::new(resources.systimer); + let alarm0: ErasedTimer = systimer.alarm0.into(); + let timer1 = OneShotTimer::new(alarm0); - let timers = [timer0, timer1]; - let timers = mk_static!([OneShotTimer; 2], timers); - esp_hal_embassy::init(&clocks, timers); + let timers = mk_static!([OneShotTimer; 2], [timer0, timer1]); + esp_hal_embassy::init(&resources.clocks, timers); let executor = mk_static!( InterruptExecutor<2>, - InterruptExecutor::new(system.software_interrupt_control.software_interrupt2) + InterruptExecutor::new(resources.software_interrupt_control.software_interrupt2) ); + #[embassy_executor::task] + #[cfg(not(feature = "esp32"))] + async fn test_interrupt_executor_invoker() { + let outcome = async { + let mut ticker = Ticker::every(Duration::from_millis(30)); + + let t1 = esp_hal::time::current_time(); + ticker.next().await; + ticker.next().await; + ticker.next().await; + let t2 = esp_hal::time::current_time(); + + assert!(t2 > t1, "t2: {:?}, t1: {:?}", t2, t1); + assert!( + (t2 - t1).to_micros() >= 85000u64, + "diff: {:?}", + (t2 - t1).to_micros() + ); + }; + + embedded_test::export::check_outcome(outcome.await); + } + let spawner_int = executor.start(Priority::Priority3); spawner_int.must_spawn(test_interrupt_executor_invoker()); - spawner.must_spawn(e_task300ms()); - - // we need to delay so the e_task300ms() could be spawned - task500ms().await; + let spawner = embassy_executor::Spawner::for_current_executor().await; + spawner.must_spawn(e_task30ms()); + // The test ends once the interrupt executor's task has finished loop {} } + /// Test that timg0 and systimer don't have vastly different tick rates. #[test] #[timeout(3)] - async fn run_tick_test_timg() { - let spawner = embassy_executor::Spawner::for_current_executor().await; + async fn tick_test_timer_tick_rates(resources: Resources) { + resources.set_up_embassy_with_timg0(); - let peripherals = unsafe { Peripherals::steal() }; - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + // We are retrying 5 times because probe-rs polling RTT may introduce some + // jitter. + for _ in 0..5 { + let t1 = esp_hal::time::current_time(); - let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); - let timer0: ErasedTimer = timg0.timer0.into(); - let timers = [OneShotTimer::new(timer0)]; - let timers = mk_static!([OneShotTimer; 1], timers); - esp_hal_embassy::init(&clocks, timers); + let mut ticker = Ticker::every(Duration::from_hz(100_000)); + for _ in 0..2000 { + ticker.next().await; + } + let t2 = esp_hal::time::current_time(); - spawner.must_spawn(tick()); - tick_and_increment().await; + assert!(t2 > t1, "t2: {:?}, t1: {:?}", t2, t1); + let duration = (t2 - t1).to_micros(); + + assert!(duration >= 19000, "diff: {:?}", (t2 - t1).to_micros()); + if duration <= 21000 { + return; + } + } + + assert!(false, "Test failed after 5 retries"); } }