mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-09-29 21:30:39 +00:00
esp-hal: Add HIL test for embassy timer and executor (#1886)
* esp_hal: Add embassy timers and executors HIL test * Add timeout to each test * cfg-gate out systimer tests for ESP32
This commit is contained in:
parent
0c29c4381c
commit
c5ea0e9149
@ -114,6 +114,10 @@ harness = false
|
||||
name = "uart_tx_rx_async"
|
||||
harness = false
|
||||
|
||||
[[test]]
|
||||
name = "embassy_timers_executors"
|
||||
harness = false
|
||||
required-features = ["async", "embassy"]
|
||||
|
||||
[dependencies]
|
||||
cfg-if = "1.0.0"
|
||||
|
494
hil-test/tests/embassy_timers_executors.rs
Normal file
494
hil-test/tests/embassy_timers_executors.rs
Normal file
@ -0,0 +1,494 @@
|
||||
//! 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
|
||||
//! immediatelly 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
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt_rtt as _;
|
||||
use embassy_time::{Duration, Ticker, Timer};
|
||||
use esp_backtrace as _;
|
||||
use esp_hal::{
|
||||
clock::ClockControl,
|
||||
peripherals::Peripherals,
|
||||
prelude::*,
|
||||
system::SystemControl,
|
||||
timer::{timg::TimerGroup, ErasedTimer, OneShotTimer, PeriodicTimer},
|
||||
};
|
||||
#[cfg(not(feature = "esp32"))]
|
||||
use esp_hal::{interrupt::Priority, timer::systimer::SystemTimer};
|
||||
#[cfg(not(feature = "esp32"))]
|
||||
use esp_hal_embassy::InterruptExecutor;
|
||||
#[cfg(not(feature = "esp32"))]
|
||||
use static_cell::StaticCell;
|
||||
|
||||
macro_rules! mk_static {
|
||||
($t:ty,$val:expr) => {{
|
||||
static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new();
|
||||
#[deny(unused_attributes)]
|
||||
let x = STATIC_CELL.uninit().write(($val));
|
||||
x
|
||||
}};
|
||||
}
|
||||
|
||||
unsafe fn __make_static<T>(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 crate::*;
|
||||
#[embassy_executor::task]
|
||||
pub async fn test_one_shot_timg_invoker() {
|
||||
let outcome;
|
||||
{
|
||||
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;
|
||||
{
|
||||
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;
|
||||
{
|
||||
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;
|
||||
{
|
||||
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;
|
||||
{
|
||||
outcome = test_interrupt_executor().await;
|
||||
}
|
||||
embedded_test::export::check_outcome(outcome);
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
pub async fn test_tick_and_increment_invoker() {
|
||||
let outcome;
|
||||
{
|
||||
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 crate::*;
|
||||
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<ErasedTimer>; 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<ErasedTimer>; 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<ErasedTimer>; 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<ErasedTimer>; 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));
|
||||
|
||||
loop {
|
||||
ticker.next().await;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn task500ms() {
|
||||
Timer::after_millis(500).await;
|
||||
}
|
||||
|
||||
pub async fn task300ms() {
|
||||
Timer::after_millis(300).await;
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
pub async fn e_task300ms() {
|
||||
task300ms().await;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[embedded_test::tests(executor = esp_hal_embassy::Executor::new())]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::{task_invokers::*, 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();
|
||||
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;
|
||||
}
|
||||
assert!(t2 > t1);
|
||||
assert!((t2 - t1).to_millis() >= 1_000u64);
|
||||
}
|
||||
|
||||
#[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());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[timeout(3)]
|
||||
#[cfg(not(feature = "esp32"))]
|
||||
fn run_test_periodic_systimer() {
|
||||
let peripherals = Peripherals::take();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#[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();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[timeout(3)]
|
||||
#[cfg(not(feature = "esp32"))]
|
||||
fn run_test_periodic_oneshot_systimer() {
|
||||
let mut peripherals = Peripherals::take();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#[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());
|
||||
});
|
||||
}
|
||||
|
||||
#[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());
|
||||
});
|
||||
}
|
||||
|
||||
#[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);
|
||||
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 timers = [timer0, timer1];
|
||||
let timers = mk_static!([OneShotTimer<ErasedTimer>; 2], timers);
|
||||
esp_hal_embassy::init(&clocks, timers);
|
||||
|
||||
static EXECUTOR: StaticCell<InterruptExecutor<2>> = StaticCell::new();
|
||||
let executor =
|
||||
InterruptExecutor::new(system.software_interrupt_control.software_interrupt2);
|
||||
let executor = EXECUTOR.init(executor);
|
||||
|
||||
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;
|
||||
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[timeout(3)]
|
||||
async fn run_tick_test_timg() {
|
||||
let spawner = embassy_executor::Spawner::for_current_executor().await;
|
||||
|
||||
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<ErasedTimer>; 1], timers);
|
||||
esp_hal_embassy::init(&clocks, timers);
|
||||
|
||||
spawner.must_spawn(tick());
|
||||
tick_and_increment().await;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user