aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorFelipe Balbi <[email protected]>2025-11-19 09:11:54 -0800
committerGitHub <[email protected]>2025-11-19 09:11:54 -0800
commita8eb124e47e633cd81e0863253d5f6bdd7545260 (patch)
tree526374336411093886915121fea7245efa067c93 /examples
parentffe3e5acae6c0038db4176dc7d031b57f865e07f (diff)
OSTimer updates (#24)
* Initialize OSTIMER0 during HAL initialization Provide the user with a working time driver. Signed-off-by: Felipe Balbi <[email protected]> * Handle time_driver interrupt internally Signed-off-by: Felipe Balbi <[email protected]> * Gate `time-driver` impl behind a `time` flag Also prevents creation of an `Ostimer` instance if the `time` feature is active. * Remove some dead code --------- Signed-off-by: Felipe Balbi <[email protected]> Co-authored-by: James Munns <[email protected]>
Diffstat (limited to 'examples')
-rw-r--r--examples/Cargo.lock1
-rw-r--r--examples/Cargo.toml4
-rw-r--r--examples/src/bin/blinky.rs13
-rw-r--r--examples/src/bin/ostimer_alarm.rs122
-rw-r--r--examples/src/bin/ostimer_async.rs64
-rw-r--r--examples/src/bin/ostimer_counter.rs139
-rw-r--r--examples/src/bin/ostimer_race_test.rs405
7 files changed, 3 insertions, 745 deletions
diff --git a/examples/Cargo.lock b/examples/Cargo.lock
index b2ac9a051..14f472cbf 100644
--- a/examples/Cargo.lock
+++ b/examples/Cargo.lock
@@ -446,6 +446,7 @@ source = "git+https://github.com/OpenDevicePartnership/mcxa-pac?rev=3ab4c868f75a
446dependencies = [ 446dependencies = [
447 "cortex-m", 447 "cortex-m",
448 "cortex-m-rt", 448 "cortex-m-rt",
449 "critical-section",
449 "vcell", 450 "vcell",
450] 451]
451 452
diff --git a/examples/Cargo.toml b/examples/Cargo.toml
index d03d3d95c..d1c6a2071 100644
--- a/examples/Cargo.toml
+++ b/examples/Cargo.toml
@@ -6,13 +6,13 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8cortex-m = { version = "0.7", features = ["critical-section-single-core"] } 8cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
9cortex-m-rt = { version = "0.7", features = ["device"] } 9cortex-m-rt = { version = "0.7" }
10critical-section = "1.2.0" 10critical-section = "1.2.0"
11defmt = "1.0" 11defmt = "1.0"
12defmt-rtt = "1.0" 12defmt-rtt = "1.0"
13embassy-embedded-hal = "0.5.0" 13embassy-embedded-hal = "0.5.0"
14embassy-executor = { version = "0.9.0", features = ["arch-cortex-m", "executor-interrupt", "executor-thread"], default-features = false } 14embassy-executor = { version = "0.9.0", features = ["arch-cortex-m", "executor-interrupt", "executor-thread"], default-features = false }
15embassy-mcxa = { path = "../", features = ["defmt", "rt", "unstable-pac"] } 15embassy-mcxa = { path = "../", features = ["defmt", "rt", "unstable-pac", "time"] }
16embassy-sync = "0.7.2" 16embassy-sync = "0.7.2"
17embassy-time = "0.5.0" 17embassy-time = "0.5.0"
18embassy-time-driver = "0.2.1" 18embassy-time-driver = "0.2.1"
diff --git a/examples/src/bin/blinky.rs b/examples/src/bin/blinky.rs
index 28d83a12e..ab1e59bdb 100644
--- a/examples/src/bin/blinky.rs
+++ b/examples/src/bin/blinky.rs
@@ -2,29 +2,16 @@
2#![no_main] 2#![no_main]
3 3
4use embassy_executor::Spawner; 4use embassy_executor::Spawner;
5use embassy_mcxa::bind_interrupts;
6use embassy_time::Timer; 5use embassy_time::Timer;
7use hal::gpio::{Level, Output}; 6use hal::gpio::{Level, Output};
8use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; 7use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
9 8
10// Bind only OS_EVENT for timer interrupts
11bind_interrupts!(struct Irqs {
12 OS_EVENT => hal::ostimer::time_driver::OsEventHandler;
13});
14
15#[used]
16#[no_mangle]
17static KEEP_OS_EVENT: unsafe extern "C" fn() = OS_EVENT;
18
19#[embassy_executor::main] 9#[embassy_executor::main]
20async fn main(_spawner: Spawner) { 10async fn main(_spawner: Spawner) {
21 let p = hal::init(hal::config::Config::default()); 11 let p = hal::init(hal::config::Config::default());
22 12
23 defmt::info!("Blink example"); 13 defmt::info!("Blink example");
24 14
25 // Initialize embassy-time global driver backed by OSTIMER0
26 hal::ostimer::time_driver::init(hal::config::Config::default().time_interrupt_priority, 1_000_000);
27
28 let mut red = Output::new(p.P3_18, Level::High); 15 let mut red = Output::new(p.P3_18, Level::High);
29 let mut green = Output::new(p.P3_19, Level::High); 16 let mut green = Output::new(p.P3_19, Level::High);
30 let mut blue = Output::new(p.P3_21, Level::High); 17 let mut blue = Output::new(p.P3_21, Level::High);
diff --git a/examples/src/bin/ostimer_alarm.rs b/examples/src/bin/ostimer_alarm.rs
deleted file mode 100644
index 6d38741b7..000000000
--- a/examples/src/bin/ostimer_alarm.rs
+++ /dev/null
@@ -1,122 +0,0 @@
1#![no_std]
2#![no_main]
3
4use core::sync::atomic::{AtomicBool, Ordering};
5
6use embassy_executor::Spawner;
7use embassy_mcxa::bind_interrupts;
8use embassy_mcxa::clocks::periph_helpers::OstimerClockSel;
9use embassy_mcxa::clocks::PoweredClock;
10use embassy_mcxa::lpuart::{Config, Lpuart};
11use embassy_mcxa_examples::init_uart2_pins;
12use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
13
14// Bind only OS_EVENT, and retain the symbol explicitly so it can't be GC'ed.
15bind_interrupts!(struct Irqs {
16 OS_EVENT => hal::ostimer::time_driver::OsEventHandler;
17});
18
19#[used]
20#[no_mangle]
21static KEEP_OS_EVENT: unsafe extern "C" fn() = OS_EVENT;
22
23// Global flag for alarm callback
24static ALARM_FLAG: AtomicBool = AtomicBool::new(false);
25
26// Alarm callback function
27fn alarm_callback() {
28 ALARM_FLAG.store(true, Ordering::Release);
29}
30
31#[embassy_executor::main]
32async fn main(_spawner: Spawner) {
33 let p = hal::init(hal::config::Config::default());
34
35 // Create UART configuration
36 let config = Config {
37 baudrate_bps: 115_200,
38 enable_tx: true,
39 enable_rx: true,
40 ..Default::default()
41 };
42
43 // Create UART instance using LPUART2 with P2_2 as TX and P2_3 as RX
44 unsafe {
45 init_uart2_pins(hal::pac());
46 }
47 let mut uart = Lpuart::new_blocking(
48 p.LPUART2, // Peripheral
49 p.P2_2, // TX pin
50 p.P2_3, // RX pin
51 config,
52 )
53 .unwrap();
54
55 uart.write_str_blocking("OSTIMER Alarm Example\n");
56
57 // Initialize embassy-time global driver backed by OSTIMER0
58 hal::ostimer::time_driver::init(hal::config::Config::default().time_interrupt_priority, 1_000_000);
59
60 // Create OSTIMER instance
61 let config = hal::ostimer::Config {
62 init_match_max: true,
63 power: PoweredClock::NormalEnabledDeepSleepDisabled,
64 source: OstimerClockSel::Clk1M,
65 };
66 let ostimer = hal::ostimer::Ostimer::<hal::ostimer::Ostimer0>::new(p.OSTIMER0, config);
67
68 // Create alarm with callback
69 let alarm = hal::ostimer::Alarm::new()
70 .with_callback(alarm_callback)
71 .with_flag(&ALARM_FLAG);
72
73 uart.write_str_blocking("Scheduling alarm for 2 seconds...\n");
74
75 // Schedule alarm to expire in 2 seconds (2,000,000 microseconds)
76 let scheduled = ostimer.schedule_alarm_delay(&alarm, 2_000_000);
77 if scheduled {
78 uart.write_str_blocking("Alarm scheduled successfully\n");
79 } else {
80 uart.write_str_blocking("Failed to schedule alarm (would exceed timer capacity)\n");
81 return;
82 }
83
84 // Wait for alarm to expire
85 loop {
86 // Check if alarm has expired
87 if ALARM_FLAG.load(Ordering::Acquire) {
88 uart.write_str_blocking("Alarm expired! Callback executed.\n");
89 break;
90 }
91
92 // Busy wait - don't use Timer::after_millis as it interferes with alarm MATCH
93 for _ in 0..100000 {
94 cortex_m::asm::nop();
95 }
96 }
97
98 // Demonstrate canceling an alarm
99 uart.write_str_blocking("Scheduling another alarm for 3 seconds...\n");
100 ALARM_FLAG.store(false, Ordering::Release); // Reset flag
101
102 let scheduled = ostimer.schedule_alarm_delay(&alarm, 3_000_000);
103 if scheduled {
104 uart.write_str_blocking("Alarm scheduled. Waiting 1 second then canceling...\n");
105
106 // Wait 1 second
107 embassy_time::Timer::after_millis(1000).await;
108
109 // Cancel the alarm
110 ostimer.cancel_alarm(&alarm);
111 uart.write_str_blocking("Alarm canceled\n");
112
113 // Check immediately if alarm flag is set
114 if !ALARM_FLAG.load(Ordering::Acquire) {
115 uart.write_str_blocking("Alarm was successfully canceled\n");
116 } else {
117 uart.write_str_blocking("Alarm fired despite cancellation\n");
118 }
119 }
120
121 uart.write_str_blocking("Example complete\n");
122}
diff --git a/examples/src/bin/ostimer_async.rs b/examples/src/bin/ostimer_async.rs
deleted file mode 100644
index f043184e7..000000000
--- a/examples/src/bin/ostimer_async.rs
+++ /dev/null
@@ -1,64 +0,0 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa::bind_interrupts;
6use embassy_mcxa_examples::init_uart2_pins;
7use embassy_time::{Duration, Timer};
8use hal::lpuart::{Config, Lpuart};
9use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
10
11// Bind only OS_EVENT, and retain the symbol explicitly so it can’t be GC’ed.
12bind_interrupts!(struct Irqs {
13 OS_EVENT => hal::ostimer::time_driver::OsEventHandler;
14});
15
16#[used]
17#[no_mangle]
18static KEEP_OS_EVENT: unsafe extern "C" fn() = OS_EVENT;
19
20#[embassy_executor::main]
21async fn main(_spawner: Spawner) {
22 let p = hal::init(hal::config::Config::default());
23
24 // Create UART configuration
25 let config = Config {
26 baudrate_bps: 115_200,
27 enable_tx: true,
28 enable_rx: true,
29 ..Default::default()
30 };
31
32 // Create UART instance using LPUART2 with P2_2 as TX and P2_3 as RX
33 unsafe {
34 init_uart2_pins(hal::pac());
35 }
36 let mut uart = Lpuart::new_blocking(
37 p.LPUART2, // Peripheral
38 p.P2_2, // TX pin
39 p.P2_3, // RX pin
40 config,
41 )
42 .unwrap();
43 uart.blocking_write(b"boot\n").unwrap();
44
45 // Avoid mass NVIC writes here; DefaultHandler now safely returns.
46
47 // Initialize embassy-time global driver backed by OSTIMER0 (re-enables OS_EVENT with priority)
48 // The bind_interrupts! macro handles handler binding automatically
49
50 // Initialize OSTIMER with default 1MHz frequency
51 // Adjust this value to match your actual OSTIMER clock frequency
52 hal::ostimer::time_driver::init(hal::config::Config::default().time_interrupt_priority, 1_000_000);
53
54 // Removed force-pend; rely on real hardware match to trigger OS_EVENT.
55
56 // Log using defmt if enabled
57 defmt::info!("OSTIMER async example starting...");
58
59 loop {
60 defmt::info!("tick");
61 uart.write_str_blocking("tick\n");
62 Timer::after(Duration::from_millis(1000)).await;
63 }
64}
diff --git a/examples/src/bin/ostimer_counter.rs b/examples/src/bin/ostimer_counter.rs
deleted file mode 100644
index f36915ff2..000000000
--- a/examples/src/bin/ostimer_counter.rs
+++ /dev/null
@@ -1,139 +0,0 @@
1//! # OSTIMER Counter Reading and Reset Example
2//!
3//! This example demonstrates the new timer counter reading and reset functionality
4//! of the OSTIMER driver.
5
6#![no_std]
7#![no_main]
8
9use embassy_executor::Spawner;
10use embassy_mcxa::clocks::periph_helpers::OstimerClockSel;
11use embassy_mcxa::clocks::PoweredClock;
12use embassy_mcxa::lpuart::{Blocking, Config, Lpuart};
13use embassy_time::{Duration, Timer};
14use hal::bind_interrupts;
15use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
16
17bind_interrupts!(struct Irqs {
18 OS_EVENT => hal::ostimer::time_driver::OsEventHandler;
19});
20
21#[embassy_executor::main]
22async fn main(_spawner: Spawner) {
23 let p = hal::init(Default::default());
24
25 // Create UART configuration
26 let config = Config {
27 baudrate_bps: 115_200,
28 enable_tx: true,
29 enable_rx: true,
30 ..Default::default()
31 };
32
33 // Create UART instance using LPUART2 with P2_2 as TX and P2_3 as RX
34 unsafe {
35 embassy_mcxa_examples::init_uart2_pins(hal::pac());
36 }
37 let mut uart = Lpuart::new_blocking(
38 p.LPUART2, // Peripheral
39 p.P2_2, // TX pin
40 p.P2_3, // RX pin
41 config,
42 )
43 .unwrap();
44
45 uart.write_str_blocking("OSTIMER Counter Reading and Reset Example\n");
46
47 // Initialize the OSTIMER time driver
48 hal::ostimer::time_driver::init(
49 hal::interrupt::Priority::from(3),
50 1_000_000, // 1MHz clock
51 );
52
53 // Create OSTIMER instance
54 let ostimer = hal::ostimer::Ostimer::<hal::ostimer::Ostimer0>::new(
55 p.OSTIMER0,
56 hal::ostimer::Config {
57 init_match_max: true,
58 power: PoweredClock::NormalEnabledDeepSleepDisabled,
59 source: OstimerClockSel::Clk1M,
60 },
61 );
62
63 // Read initial counter value
64 let initial_counter = ostimer.now();
65 uart.write_str_blocking("Initial counter value: ");
66 write_u64(&mut uart, initial_counter);
67 uart.write_str_blocking("\n");
68
69 // Wait a bit to let counter increment
70 Timer::after(Duration::from_millis(100)).await;
71
72 // Read counter again
73 let counter_after_wait = ostimer.now();
74 uart.write_str_blocking("Counter after 100ms wait: ");
75 write_u64(&mut uart, counter_after_wait);
76 uart.write_str_blocking("\n");
77 uart.write_str_blocking("Difference: ");
78 write_u64(&mut uart, counter_after_wait - initial_counter);
79 uart.write_str_blocking(" ticks\n");
80
81 // Reset the timer
82 uart.write_str_blocking("Resetting timer...\n");
83 ostimer.reset(hal::pac());
84
85 // Read counter after reset
86 let counter_after_reset = ostimer.now();
87 uart.write_str_blocking("Counter after reset: ");
88 write_u64(&mut uart, counter_after_reset);
89 uart.write_str_blocking("\n");
90
91 // Wait again to verify timer is working
92 Timer::after(Duration::from_millis(50)).await;
93
94 let final_counter = ostimer.now();
95 uart.write_str_blocking("Counter after another 50ms: ");
96 write_u64(&mut uart, final_counter);
97 uart.write_str_blocking("\n");
98 uart.write_str_blocking("Difference after reset: ");
99 write_u64(&mut uart, final_counter - counter_after_reset);
100 uart.write_str_blocking(" ticks\n");
101
102 uart.write_str_blocking("Example complete\n");
103}
104
105// Helper function to write a u64 value as decimal string
106fn write_u64(uart: &mut Lpuart<'_, Blocking>, value: u64) {
107 if value == 0 {
108 uart.write_str_blocking("0");
109 return;
110 }
111
112 let mut buffer = [0u8; 20]; // Enough for max u64
113 let mut i = 0;
114 let mut v = value;
115
116 while v > 0 {
117 buffer[i] = b'0' + (v % 10) as u8;
118 v /= 10;
119 i += 1;
120 }
121
122 // Write digits in reverse order
123 while i > 0 {
124 i -= 1;
125 match buffer[i] {
126 b'0' => uart.write_str_blocking("0"),
127 b'1' => uart.write_str_blocking("1"),
128 b'2' => uart.write_str_blocking("2"),
129 b'3' => uart.write_str_blocking("3"),
130 b'4' => uart.write_str_blocking("4"),
131 b'5' => uart.write_str_blocking("5"),
132 b'6' => uart.write_str_blocking("6"),
133 b'7' => uart.write_str_blocking("7"),
134 b'8' => uart.write_str_blocking("8"),
135 b'9' => uart.write_str_blocking("9"),
136 _ => uart.write_str_blocking("?"),
137 }
138 }
139}
diff --git a/examples/src/bin/ostimer_race_test.rs b/examples/src/bin/ostimer_race_test.rs
deleted file mode 100644
index 0106b92a7..000000000
--- a/examples/src/bin/ostimer_race_test.rs
+++ /dev/null
@@ -1,405 +0,0 @@
1//! # OSTIMER Race Condition Test
2//!
3//! This example tests for race conditions in the OSTIMER driver by:
4//! - Scheduling alarms sequentially (hardware limitation: only one at a time)
5//! - Reading the counter during interrupt-heavy periods
6//! - Testing concurrent timer operations
7//! - Stress testing interrupt handling
8
9#![no_std]
10#![no_main]
11
12use core::sync::atomic::{AtomicU32, Ordering};
13
14use embassy_executor::Spawner;
15use embassy_mcxa::clocks::periph_helpers::OstimerClockSel;
16use embassy_mcxa::clocks::PoweredClock;
17use embassy_mcxa::lpuart::{Blocking, Config, Lpuart};
18use embassy_time::{Duration, Timer};
19use hal::bind_interrupts;
20use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
21
22bind_interrupts!(struct Irqs {
23 OS_EVENT => hal::ostimer::time_driver::OsEventHandler;
24});
25
26#[used]
27#[no_mangle]
28static KEEP_OS_EVENT: unsafe extern "C" fn() = OS_EVENT;
29
30// Global counters for race condition detection
31static ALARM_CALLBACK_COUNT: AtomicU32 = AtomicU32::new(0);
32static INTERRUPT_COUNT: AtomicU32 = AtomicU32::new(0);
33static RACE_DETECTED: AtomicU32 = AtomicU32::new(0);
34
35// Alarm callback function
36fn alarm_callback() {
37 let _count = ALARM_CALLBACK_COUNT.fetch_add(1, Ordering::SeqCst);
38 INTERRUPT_COUNT.fetch_add(1, Ordering::SeqCst);
39
40 // Simulate some work in the callback to increase chance of races
41 for _ in 0..10 {
42 cortex_m::asm::nop();
43 }
44}
45
46fn report_default_handler(uart: &mut Lpuart<'_, Blocking>) {
47 let snapshot = hal::interrupt::default_handler_snapshot();
48 if snapshot.count == 0 {
49 return;
50 }
51
52 uart.write_str_blocking("WARNING: DefaultHandler executed ");
53 write_u32(uart, snapshot.count);
54 uart.write_str_blocking(" time(s). Vector=");
55 write_u32(uart, snapshot.vector as u32);
56 uart.write_str_blocking(" CFSR=0x");
57 write_hex32(uart, snapshot.cfsr);
58 uart.write_str_blocking(" HFSR=0x");
59 write_hex32(uart, snapshot.hfsr);
60 uart.write_str_blocking(" PC=0x");
61 write_hex32(uart, snapshot.stacked_pc);
62 uart.write_str_blocking(" LR=0x");
63 write_hex32(uart, snapshot.stacked_lr);
64 uart.write_str_blocking(" SP=0x");
65 write_hex32(uart, snapshot.stacked_sp);
66 uart.write_str_blocking("\n");
67
68 hal::interrupt::clear_default_handler_snapshot();
69}
70
71#[embassy_executor::main]
72async fn main(_spawner: Spawner) {
73 let p = hal::init(Default::default());
74
75 // Create UART configuration
76 let config = Config {
77 baudrate_bps: 115_200,
78 enable_tx: true,
79 enable_rx: true,
80 ..Default::default()
81 };
82
83 // Create UART instance using LPUART2 with P2_2 as TX and P2_3 as RX
84 unsafe {
85 embassy_mcxa_examples::init_uart2_pins(hal::pac());
86 }
87 let mut uart = Lpuart::new_blocking(
88 p.LPUART2, // Peripheral
89 p.P2_2, // TX pin
90 p.P2_3, // RX pin
91 config,
92 )
93 .unwrap();
94
95 uart.write_str_blocking("OSTIMER Race Condition Test Starting...\n");
96
97 // The bind_interrupts! macro handles handler binding automatically
98
99 // Initialize the OSTIMER time driver FIRST
100 hal::ostimer::time_driver::init(
101 hal::interrupt::Priority::from(3),
102 1_000_000, // 1MHz clock
103 );
104
105 uart.write_str_blocking("Time driver initialized\n");
106
107 // Create OSTIMER instance
108 let ostimer = hal::ostimer::Ostimer::<hal::ostimer::Ostimer0>::new(
109 p.OSTIMER0,
110 hal::ostimer::Config {
111 init_match_max: true,
112 power: PoweredClock::NormalEnabledDeepSleepDisabled,
113 source: OstimerClockSel::Clk1M,
114 },
115 );
116
117 uart.write_str_blocking("OSTIMER instance created\n");
118
119 // Test 1: Sequential alarm scheduling (OSTIMER only supports one alarm at a time)
120 uart.write_str_blocking("Test 1: Sequential alarm scheduling...\n");
121 test_rapid_alarms(&ostimer, &mut uart).await;
122 report_default_handler(&mut uart);
123
124 // Test 2: Counter reading during interrupts
125 uart.write_str_blocking("Test 2: Counter reading during interrupts...\n");
126 test_counter_reading_during_interrupts(&ostimer, &mut uart).await;
127 report_default_handler(&mut uart);
128
129 // Test 3: Concurrent timer operations
130 uart.write_str_blocking("Test 3: Concurrent timer operations...\n");
131 test_concurrent_operations(&ostimer, &mut uart).await;
132 report_default_handler(&mut uart);
133
134 // Test 4: Timer reset during operation
135 uart.write_str_blocking("Test 4: Timer reset during operation...\n");
136 test_reset_during_operation(&ostimer, &mut uart, hal::pac()).await;
137 report_default_handler(&mut uart);
138
139 // Report results
140 uart.write_str_blocking("Race condition test complete\n");
141 uart.write_str_blocking("Callback count: ");
142 write_u32(&mut uart, ALARM_CALLBACK_COUNT.load(Ordering::SeqCst));
143 uart.write_str_blocking("\nInterrupt count: ");
144 write_u32(&mut uart, INTERRUPT_COUNT.load(Ordering::SeqCst));
145 uart.write_str_blocking("\nRaces detected: ");
146 write_u32(&mut uart, RACE_DETECTED.load(Ordering::SeqCst));
147 uart.write_str_blocking("\n");
148}
149
150// Test rapid alarm scheduling to stress interrupt handling
151async fn test_rapid_alarms(
152 ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>,
153 uart: &mut Lpuart<'_, Blocking>,
154) {
155 let initial_count = ALARM_CALLBACK_COUNT.load(Ordering::SeqCst);
156
157 // Schedule 10 alarms sequentially (OSTIMER only supports one alarm at a time)
158 for _i in 0..10 {
159 let alarm = hal::ostimer::Alarm::new().with_callback(alarm_callback);
160 let delay_us = 1000; // 1ms delay for each alarm
161 if ostimer.schedule_alarm_delay(&alarm, delay_us) {
162 // Wait for this alarm to complete before scheduling the next
163 Timer::after(Duration::from_micros(delay_us + 100)).await;
164 report_default_handler(uart);
165 } else {
166 RACE_DETECTED.fetch_add(1, Ordering::SeqCst);
167 uart.write_str_blocking("ERROR: Failed to program OSTIMER alarm (match not ready)\n");
168 }
169 }
170
171 // All alarms should have completed by now
172 let final_count = ALARM_CALLBACK_COUNT.load(Ordering::SeqCst);
173 let expected_count = initial_count + 10;
174
175 if final_count != expected_count {
176 RACE_DETECTED.fetch_add(1, Ordering::SeqCst);
177 uart.write_str_blocking("ERROR: Expected ");
178 write_u32(uart, expected_count);
179 uart.write_str_blocking(" callbacks, got ");
180 write_u32(uart, final_count);
181 uart.write_str_blocking("\n");
182 } else {
183 uart.write_str_blocking("PASS: All rapid alarms executed\n");
184 }
185}
186
187// Test reading counter while interrupts are firing
188async fn test_counter_reading_during_interrupts(
189 ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>,
190 uart: &mut Lpuart<'_, Blocking>,
191) {
192 let initial_interrupt_count = INTERRUPT_COUNT.load(Ordering::SeqCst);
193
194 // Schedule an alarm that will fire soon
195 let alarm = hal::ostimer::Alarm::new().with_callback(alarm_callback);
196 if !ostimer.schedule_alarm_delay(&alarm, 500) {
197 RACE_DETECTED.fetch_add(1, Ordering::SeqCst);
198 uart.write_str_blocking("ERROR: Failed to program OSTIMER alarm before counter stress\n");
199 }
200
201 // While alarm is pending, read the counter many times rapidly
202 // This tests if counter reading is atomic and doesn't get corrupted by interrupts
203 let mut last_counter = ostimer.now();
204 let mut consistent_reads = 0;
205 let mut total_reads = 0;
206
207 for _ in 0..1000 {
208 let current_counter = ostimer.now();
209 total_reads += 1;
210
211 // Check if counter is monotonically increasing (basic sanity check)
212 if current_counter >= last_counter {
213 consistent_reads += 1;
214 }
215 last_counter = current_counter;
216
217 // Small delay between reads
218 for _ in 0..10 {
219 cortex_m::asm::nop();
220 }
221
222 report_default_handler(uart);
223 }
224
225 // Wait for alarm to complete
226 Timer::after(Duration::from_millis(1)).await;
227
228 let final_interrupt_count = INTERRUPT_COUNT.load(Ordering::SeqCst);
229
230 if consistent_reads == total_reads {
231 uart.write_str_blocking("PASS: Counter reading consistent during interrupts\n");
232 } else {
233 RACE_DETECTED.fetch_add(1, Ordering::SeqCst);
234 uart.write_str_blocking("ERROR: Counter reading inconsistent: ");
235 write_u32(uart, consistent_reads);
236 uart.write_str_blocking("/");
237 write_u32(uart, total_reads);
238 uart.write_str_blocking(" consistent\n");
239 }
240
241 if final_interrupt_count > initial_interrupt_count {
242 uart.write_str_blocking("PASS: Interrupt fired during counter reading test\n");
243 } else {
244 uart.write_str_blocking("WARNING: No interrupt fired during counter reading test\n");
245 }
246}
247
248// Test concurrent timer operations (embassy-time + alarms)
249async fn test_concurrent_operations(
250 ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>,
251 uart: &mut Lpuart<'_, Blocking>,
252) {
253 let initial_interrupt_count = INTERRUPT_COUNT.load(Ordering::SeqCst);
254
255 // Start an embassy-time timer
256 let timer_future = Timer::after(Duration::from_millis(2));
257
258 // Schedule an alarm that should fire before the timer
259 let alarm = hal::ostimer::Alarm::new().with_callback(alarm_callback);
260 if !ostimer.schedule_alarm_delay(&alarm, 1000) {
261 RACE_DETECTED.fetch_add(1, Ordering::SeqCst);
262 uart.write_str_blocking("ERROR: Failed to program OSTIMER alarm before concurrent operations\n");
263 }
264
265 // Wait for both to complete
266 timer_future.await;
267
268 let final_interrupt_count = INTERRUPT_COUNT.load(Ordering::SeqCst);
269
270 if final_interrupt_count > initial_interrupt_count {
271 uart.write_str_blocking("PASS: Concurrent operations completed\n");
272 } else {
273 uart.write_str_blocking("WARNING: No interrupts during concurrent operations\n");
274 }
275}
276
277// Test timer reset during active operations
278async fn test_reset_during_operation(
279 ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>,
280 uart: &mut Lpuart<'_, Blocking>,
281 peripherals: &hal::pac::Peripherals,
282) {
283 let initial_counter = ostimer.now();
284
285 // Schedule an alarm
286 let alarm = hal::ostimer::Alarm::new().with_callback(alarm_callback);
287 if !ostimer.schedule_alarm_delay(&alarm, 2000) {
288 RACE_DETECTED.fetch_add(1, Ordering::SeqCst);
289 uart.write_str_blocking("ERROR: Failed to program OSTIMER alarm before reset test\n");
290 }
291
292 // Wait a bit then reset the timer
293 Timer::after(Duration::from_millis(1)).await;
294 ostimer.reset(peripherals);
295
296 // Check counter after reset
297 let counter_after_reset = ostimer.now();
298
299 // Wait to see if the alarm still fires (it shouldn't after reset)
300 Timer::after(Duration::from_millis(2)).await;
301
302 let final_counter = ostimer.now();
303
304 if counter_after_reset < initial_counter {
305 uart.write_str_blocking("PASS: Timer reset successful\n");
306 } else {
307 RACE_DETECTED.fetch_add(1, Ordering::SeqCst);
308 uart.write_str_blocking("ERROR: Timer reset may have failed\n");
309 }
310
311 uart.write_str_blocking("Counter progression after reset: ");
312 write_u64(uart, initial_counter);
313 uart.write_str_blocking(" -> ");
314 write_u64(uart, counter_after_reset);
315 uart.write_str_blocking(" -> ");
316 write_u64(uart, final_counter);
317 uart.write_str_blocking("\n");
318}
319
320// Helper function to write a u32 value as decimal string
321fn write_u32(uart: &mut Lpuart<'_, Blocking>, value: u32) {
322 if value == 0 {
323 uart.write_str_blocking("0");
324 return;
325 }
326
327 let mut buffer = [0u8; 10]; // Enough for max u32
328 let mut i = 0;
329 let mut v = value;
330
331 while v > 0 {
332 buffer[i] = b'0' + (v % 10) as u8;
333 v /= 10;
334 i += 1;
335 }
336
337 // Write digits in reverse order
338 while i > 0 {
339 i -= 1;
340 match buffer[i] {
341 b'0' => uart.write_str_blocking("0"),
342 b'1' => uart.write_str_blocking("1"),
343 b'2' => uart.write_str_blocking("2"),
344 b'3' => uart.write_str_blocking("3"),
345 b'4' => uart.write_str_blocking("4"),
346 b'5' => uart.write_str_blocking("5"),
347 b'6' => uart.write_str_blocking("6"),
348 b'7' => uart.write_str_blocking("7"),
349 b'8' => uart.write_str_blocking("8"),
350 b'9' => uart.write_str_blocking("9"),
351 _ => uart.write_str_blocking("?"),
352 }
353 }
354}
355
356fn write_hex32(uart: &mut Lpuart<'_, Blocking>, value: u32) {
357 let mut buf = [b'0'; 8];
358 let mut tmp = value;
359 for i in (0..8).rev() {
360 let digit = (tmp & 0xF) as u8;
361 buf[i] = match digit {
362 0..=9 => b'0' + digit,
363 10..=15 => b'A' + (digit - 10),
364 _ => b'?',
365 };
366 tmp >>= 4;
367 }
368 uart.blocking_write(&buf).unwrap();
369}
370
371// Helper function to write a u64 value as decimal string
372fn write_u64(uart: &mut Lpuart<'_, Blocking>, value: u64) {
373 if value == 0 {
374 uart.blocking_write(b"0").unwrap();
375 return;
376 }
377
378 let mut buffer = [0u8; 20]; // Enough for max u64
379 let mut i = 0;
380 let mut v = value;
381
382 while v > 0 {
383 buffer[i] = b'0' + (v % 10) as u8;
384 v /= 10;
385 i += 1;
386 }
387
388 // Write digits in reverse order
389 while i > 0 {
390 i -= 1;
391 match buffer[i] {
392 b'0' => uart.blocking_write(b"0").unwrap(),
393 b'1' => uart.blocking_write(b"1").unwrap(),
394 b'2' => uart.blocking_write(b"2").unwrap(),
395 b'3' => uart.blocking_write(b"3").unwrap(),
396 b'4' => uart.blocking_write(b"4").unwrap(),
397 b'5' => uart.blocking_write(b"5").unwrap(),
398 b'6' => uart.blocking_write(b"6").unwrap(),
399 b'7' => uart.blocking_write(b"7").unwrap(),
400 b'8' => uart.blocking_write(b"8").unwrap(),
401 b'9' => uart.blocking_write(b"9").unwrap(),
402 _ => uart.blocking_write(b"?").unwrap(),
403 }
404 }
405}