aboutsummaryrefslogtreecommitdiff
path: root/src/ostimer.rs
diff options
context:
space:
mode:
authorFelipe Balbi <[email protected]>2025-11-07 11:00:15 -0800
committerGitHub <[email protected]>2025-11-07 11:00:15 -0800
commit5632acec18cc5906b1625a8facf530db56c73300 (patch)
treeabf2897f4b2f9814069c64611896be2d42cf2ce8 /src/ostimer.rs
parent47e383545f4aac3bfaec0563429cc721540e665a (diff)
parent9590d94ee9ba016f65a13100c429fc56ffe58e40 (diff)
Merge pull request #1 from bogdan-petru/import/mcxa276-initial
feat(mcxa276): initial HAL import
Diffstat (limited to 'src/ostimer.rs')
-rw-r--r--src/ostimer.rs678
1 files changed, 678 insertions, 0 deletions
diff --git a/src/ostimer.rs b/src/ostimer.rs
new file mode 100644
index 000000000..a4cab6970
--- /dev/null
+++ b/src/ostimer.rs
@@ -0,0 +1,678 @@
1//! # OSTIMER Driver with Robustness Features
2//!
3//! This module provides an async timer driver for the NXP MCXA276 OSTIMER peripheral
4//! with protection against race conditions and timer rollover issues.
5//!
6//! ## Features
7//!
8//! - Async timing with embassy-time integration
9//! - Gray code counter handling (42-bit counter)
10//! - Interrupt-driven wakeups
11//! - Configurable interrupt priority
12//! - **Race condition protection**: Critical sections and atomic operations
13//! - **Timer rollover handling**: Bounds checking and rollover prevention
14//!
15//! ## Clock Frequency Configuration
16//!
17//! The OSTIMER frequency depends on your system's clock configuration. You must provide
18//! the actual frequency when calling `time_driver::init()`.
19//!
20//! ## Race Condition Protection
21//! - Critical sections in interrupt handlers prevent concurrent access
22//! - Atomic register operations with memory barriers
23//! - Proper interrupt flag clearing and validation
24//!
25//! ## Timer Rollover Handling
26//! - Bounds checking prevents scheduling beyond timer capacity
27//! - Immediate wake for timestamps that would cause rollover issues
28#![allow(dead_code)]
29
30use core::sync::atomic::{AtomicBool, Ordering};
31
32use crate::interrupt::InterruptExt;
33use crate::pac;
34
35// PAC defines the shared RegisterBlock under `ostimer0`.
36type Regs = pac::ostimer0::RegisterBlock;
37
38// OSTIMER EVTIMER register layout constants
39/// Total width of the EVTIMER counter in bits (42 bits total)
40const EVTIMER_TOTAL_BITS: u32 = 42;
41/// Width of the low part of EVTIMER (bits 31:0)
42const EVTIMER_LO_BITS: u32 = 32;
43/// Width of the high part of EVTIMER (bits 41:32)
44const EVTIMER_HI_BITS: u32 = 10;
45/// Bit position where high part starts in the combined 64-bit value
46const EVTIMER_HI_SHIFT: u32 = 32;
47
48/// Bit mask for the high part of EVTIMER
49const EVTIMER_HI_MASK: u16 = (1 << EVTIMER_HI_BITS) - 1;
50
51/// Maximum value for MATCH_L register (32-bit)
52const MATCH_L_MAX: u32 = u32::MAX;
53/// Maximum value for MATCH_H register (10-bit)
54const MATCH_H_MAX: u16 = EVTIMER_HI_MASK;
55
56/// Bit mask for extracting the low 32 bits from a 64-bit value
57const LOW_32_BIT_MASK: u64 = u32::MAX as u64;
58
59/// Gray code conversion bit shifts (most significant to least)
60const GRAY_CONVERSION_SHIFTS: [u32; 6] = [32, 16, 8, 4, 2, 1];
61
62/// Maximum timer value before rollover (2^42 - 1 ticks)
63/// Actual rollover time depends on the configured clock frequency
64const TIMER_MAX_VALUE: u64 = (1u64 << EVTIMER_TOTAL_BITS) - 1;
65
66/// Threshold for detecting timer rollover in comparisons (1 second at 1MHz)
67const TIMER_ROLLOVER_THRESHOLD: u64 = 1_000_000;
68
69/// Common default interrupt priority for OSTIMER
70const DEFAULT_INTERRUPT_PRIORITY: u8 = 3;
71
72// Global alarm state for interrupt handling
73static ALARM_ACTIVE: AtomicBool = AtomicBool::new(false);
74static mut ALARM_CALLBACK: Option<fn()> = None;
75static mut ALARM_FLAG: Option<*const AtomicBool> = None;
76static mut ALARM_TARGET_TIME: u64 = 0;
77
78/// Number of tight spin iterations between elapsed time checks while waiting for MATCH writes to return to the idle (0) state.
79const MATCH_WRITE_READY_SPINS: usize = 512;
80/// Maximum time (in OSTIMER ticks) to wait for MATCH registers to become writable (~5 ms at 1 MHz).
81const MATCH_WRITE_READY_TIMEOUT_TICKS: u64 = 5_000;
82/// Short stabilization delay executed after toggling the MRCC reset line to let the OSTIMER bus interface settle.
83const RESET_STABILIZE_SPINS: usize = 512;
84
85pub(super) fn wait_for_match_write_ready(r: &Regs) -> bool {
86 let start = now_ticks_read();
87 let mut spin_budget = 0usize;
88
89 loop {
90 if r.osevent_ctrl().read().match_wr_rdy().bit_is_clear() {
91 return true;
92 }
93
94 cortex_m::asm::nop();
95 spin_budget += 1;
96
97 if spin_budget >= MATCH_WRITE_READY_SPINS {
98 spin_budget = 0;
99
100 let elapsed = now_ticks_read().wrapping_sub(start);
101 if elapsed >= MATCH_WRITE_READY_TIMEOUT_TICKS {
102 return false;
103 }
104 }
105 }
106}
107
108pub(super) fn wait_for_match_write_complete(r: &Regs) -> bool {
109 let start = now_ticks_read();
110 let mut spin_budget = 0usize;
111
112 loop {
113 if r.osevent_ctrl().read().match_wr_rdy().bit_is_clear() {
114 return true;
115 }
116
117 cortex_m::asm::nop();
118 spin_budget += 1;
119
120 if spin_budget >= MATCH_WRITE_READY_SPINS {
121 spin_budget = 0;
122
123 let elapsed = now_ticks_read().wrapping_sub(start);
124 if elapsed >= MATCH_WRITE_READY_TIMEOUT_TICKS {
125 return false;
126 }
127 }
128 }
129}
130
131fn prime_match_registers(r: &Regs) {
132 // Disable the interrupt, clear any pending flag, then wait until the MATCH registers are writable.
133 r.osevent_ctrl()
134 .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit());
135
136 if wait_for_match_write_ready(r) {
137 r.match_l().write(|w| unsafe { w.match_value().bits(MATCH_L_MAX) });
138 r.match_h().write(|w| unsafe { w.match_value().bits(MATCH_H_MAX) });
139 let _ = wait_for_match_write_complete(r);
140 }
141}
142
143/// Single-shot alarm functionality for OSTIMER
144pub struct Alarm<'d> {
145 /// Whether the alarm is currently active
146 active: AtomicBool,
147 /// Callback to execute when alarm expires (optional)
148 callback: Option<fn()>,
149 /// Flag that gets set when alarm expires (optional)
150 flag: Option<&'d AtomicBool>,
151 _phantom: core::marker::PhantomData<&'d mut ()>,
152}
153
154impl<'d> Alarm<'d> {
155 /// Create a new alarm instance
156 pub fn new() -> Self {
157 Self {
158 active: AtomicBool::new(false),
159 callback: None,
160 flag: None,
161 _phantom: core::marker::PhantomData,
162 }
163 }
164
165 /// Set a callback that will be executed when the alarm expires
166 /// Note: Due to interrupt handler constraints, callbacks must be static function pointers
167 pub fn with_callback(mut self, callback: fn()) -> Self {
168 self.callback = Some(callback);
169 self
170 }
171
172 /// Set a flag that will be set to true when the alarm expires
173 pub fn with_flag(mut self, flag: &'d AtomicBool) -> Self {
174 self.flag = Some(flag);
175 self
176 }
177
178 /// Check if the alarm is currently active
179 pub fn is_active(&self) -> bool {
180 self.active.load(Ordering::Acquire)
181 }
182
183 /// Cancel the alarm if it's active
184 pub fn cancel(&self) {
185 self.active.store(false, Ordering::Release);
186 }
187}
188
189/// Configuration for Ostimer::new()
190#[derive(Copy, Clone)]
191pub struct Config {
192 /// Initialize MATCH registers to their max values and mask/clear the interrupt flag.
193 pub init_match_max: bool,
194 /// OSTIMER clock frequency in Hz (must match the actual hardware clock)
195 pub clock_frequency_hz: u64,
196}
197
198impl Default for Config {
199 fn default() -> Self {
200 Self {
201 init_match_max: true,
202 // Default to 1MHz - user should override this with actual frequency
203 clock_frequency_hz: 1_000_000,
204 }
205 }
206}
207
208/// OSTIMER peripheral instance
209pub struct Ostimer<'d, I: Instance> {
210 _inst: core::marker::PhantomData<I>,
211 clock_frequency_hz: u64,
212 _phantom: core::marker::PhantomData<&'d mut ()>,
213}
214
215impl<'d, I: Instance> Ostimer<'d, I> {
216 /// Construct OSTIMER handle.
217 /// Requires clocks for the instance to be enabled by the board before calling.
218 /// Does not enable NVIC or INTENA; use time_driver::init() for async operation.
219 pub fn new(_inst: impl Instance, cfg: Config, _p: &'d crate::pac::Peripherals) -> Self {
220 assert!(cfg.clock_frequency_hz > 0, "OSTIMER frequency must be greater than 0");
221
222 if cfg.init_match_max {
223 let r: &Regs = unsafe { &*I::ptr() };
224 // Mask INTENA, clear pending flag, and set MATCH to max so no spurious IRQ fires.
225 prime_match_registers(r);
226 }
227
228 Self {
229 _inst: core::marker::PhantomData,
230 clock_frequency_hz: cfg.clock_frequency_hz,
231 _phantom: core::marker::PhantomData,
232 }
233 }
234
235 /// Get the configured clock frequency in Hz
236 pub fn clock_frequency_hz(&self) -> u64 {
237 self.clock_frequency_hz
238 }
239
240 /// Read the current timer counter value in timer ticks
241 ///
242 /// # Returns
243 /// Current timer counter value as a 64-bit unsigned integer
244 pub fn now(&self) -> u64 {
245 now_ticks_read()
246 }
247
248 /// Reset the timer counter to zero
249 ///
250 /// This performs a hardware reset of the OSTIMER peripheral, which will reset
251 /// the counter to zero and clear any pending interrupts. Note that this will
252 /// affect all timer operations including embassy-time.
253 ///
254 /// # Safety
255 /// This operation will reset the entire OSTIMER peripheral. Any active alarms
256 /// or time_driver operations will be disrupted. Use with caution.
257 pub fn reset(&self, peripherals: &crate::pac::Peripherals) {
258 critical_section::with(|_| {
259 let r: &Regs = unsafe { &*I::ptr() };
260
261 // Mask the peripheral interrupt flag before we toggle the reset line so that
262 // no new NVIC activity races with the reset sequence.
263 r.osevent_ctrl()
264 .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit());
265
266 unsafe {
267 crate::reset::assert::<crate::reset::line::Ostimer0>(peripherals);
268 }
269
270 for _ in 0..RESET_STABILIZE_SPINS {
271 cortex_m::asm::nop();
272 }
273
274 unsafe {
275 crate::reset::release::<crate::reset::line::Ostimer0>(peripherals);
276 }
277
278 while !<crate::reset::line::Ostimer0 as crate::reset::ResetLine>::is_released(&peripherals.mrcc0) {
279 cortex_m::asm::nop();
280 }
281
282 for _ in 0..RESET_STABILIZE_SPINS {
283 cortex_m::asm::nop();
284 }
285
286 // Clear alarm bookkeeping before re-arming MATCH registers.
287 ALARM_ACTIVE.store(false, Ordering::Release);
288 unsafe {
289 ALARM_TARGET_TIME = 0;
290 ALARM_CALLBACK = None;
291 ALARM_FLAG = None;
292 }
293
294 prime_match_registers(r);
295 });
296
297 // Ensure no stale OS_EVENT request remains pending after the reset sequence.
298 crate::interrupt::OS_EVENT.unpend();
299 }
300
301 /// Schedule a single-shot alarm to expire after the specified delay in microseconds
302 ///
303 /// # Parameters
304 /// * `alarm` - The alarm instance to schedule
305 /// * `delay_us` - Delay in microseconds from now
306 ///
307 /// # Returns
308 /// `true` if the alarm was scheduled successfully, `false` if it would exceed timer capacity
309 pub fn schedule_alarm_delay(&self, alarm: &Alarm, delay_us: u64) -> bool {
310 let delay_ticks = (delay_us * self.clock_frequency_hz) / 1_000_000;
311 let target_time = now_ticks_read() + delay_ticks;
312 self.schedule_alarm_at(alarm, target_time)
313 }
314
315 /// Schedule a single-shot alarm to expire at the specified absolute time in timer ticks
316 ///
317 /// # Parameters
318 /// * `alarm` - The alarm instance to schedule
319 /// * `target_ticks` - Absolute time in timer ticks when the alarm should expire
320 ///
321 /// # Returns
322 /// `true` if the alarm was scheduled successfully, `false` if it would exceed timer capacity
323 pub fn schedule_alarm_at(&self, alarm: &Alarm, target_ticks: u64) -> bool {
324 let now = now_ticks_read();
325
326 // Check if target time is in the past
327 if target_ticks <= now {
328 // Execute callback immediately if alarm was supposed to be active
329 if alarm.active.load(Ordering::Acquire) {
330 alarm.active.store(false, Ordering::Release);
331 if let Some(callback) = alarm.callback {
332 callback();
333 }
334 if let Some(flag) = &alarm.flag {
335 flag.store(true, Ordering::Release);
336 }
337 }
338 return true;
339 }
340
341 // Check for timer rollover
342 let max_future = now + TIMER_MAX_VALUE;
343 if target_ticks > max_future {
344 return false; // Would exceed timer capacity
345 }
346
347 // Program the timer
348 let r: &Regs = unsafe { &*I::ptr() };
349
350 critical_section::with(|_| {
351 // Disable interrupt and clear flag
352 r.osevent_ctrl()
353 .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit());
354
355 if !wait_for_match_write_ready(r) {
356 prime_match_registers(r);
357
358 if !wait_for_match_write_ready(r) {
359 alarm.active.store(false, Ordering::Release);
360 ALARM_ACTIVE.store(false, Ordering::Release);
361 unsafe {
362 ALARM_TARGET_TIME = 0;
363 ALARM_CALLBACK = None;
364 ALARM_FLAG = None;
365 }
366 return false;
367 }
368 }
369
370 // Mark alarm as active now that we know the MATCH registers are writable
371 alarm.active.store(true, Ordering::Release);
372
373 // Set global alarm state for interrupt handler
374 ALARM_ACTIVE.store(true, Ordering::Release);
375 unsafe {
376 ALARM_TARGET_TIME = target_ticks;
377 ALARM_CALLBACK = alarm.callback;
378 ALARM_FLAG = alarm.flag.map(|f| f as *const AtomicBool);
379 }
380
381 // Program MATCH registers (Gray-coded)
382 let gray = bin_to_gray(target_ticks);
383 let l = (gray & LOW_32_BIT_MASK) as u32;
384 let h = (((gray >> EVTIMER_HI_SHIFT) as u16) & EVTIMER_HI_MASK) as u16;
385
386 r.match_l().write(|w| unsafe { w.match_value().bits(l) });
387 r.match_h().write(|w| unsafe { w.match_value().bits(h) });
388
389 if !wait_for_match_write_complete(r) {
390 alarm.active.store(false, Ordering::Release);
391 ALARM_ACTIVE.store(false, Ordering::Release);
392 unsafe {
393 ALARM_TARGET_TIME = 0;
394 ALARM_CALLBACK = None;
395 ALARM_FLAG = None;
396 }
397 return false;
398 }
399
400 let now_after_program = now_ticks_read();
401 let intrflag_set = r.osevent_ctrl().read().ostimer_intrflag().bit_is_set();
402 if now_after_program >= target_ticks && !intrflag_set {
403 alarm.active.store(false, Ordering::Release);
404 ALARM_ACTIVE.store(false, Ordering::Release);
405 unsafe {
406 ALARM_TARGET_TIME = 0;
407 ALARM_CALLBACK = None;
408 ALARM_FLAG = None;
409 }
410 return false;
411 }
412
413 // Enable interrupt
414 r.osevent_ctrl().write(|w| w.ostimer_intena().set_bit());
415
416 true
417 })
418 }
419
420 /// Cancel any active alarm
421 pub fn cancel_alarm(&self, alarm: &Alarm) {
422 critical_section::with(|_| {
423 alarm.cancel();
424
425 // Clear global alarm state
426 ALARM_ACTIVE.store(false, Ordering::Release);
427 unsafe { ALARM_TARGET_TIME = 0 };
428
429 // Reset MATCH registers to maximum values to prevent spurious interrupts
430 let r: &Regs = unsafe { &*I::ptr() };
431 prime_match_registers(r);
432 });
433 }
434
435 /// Check if an alarm has expired (call this from your interrupt handler)
436 /// Returns true if the alarm was active and has now expired
437 pub fn check_alarm_expired(&self, alarm: &Alarm) -> bool {
438 if alarm.active.load(Ordering::Acquire) {
439 alarm.active.store(false, Ordering::Release);
440
441 // Execute callback
442 if let Some(callback) = alarm.callback {
443 callback();
444 }
445
446 // Set flag
447 if let Some(flag) = &alarm.flag {
448 flag.store(true, Ordering::Release);
449 }
450
451 true
452 } else {
453 false
454 }
455 }
456}
457
458/// Read current EVTIMER (Gray-coded) and convert to binary ticks.
459#[inline(always)]
460fn now_ticks_read() -> u64 {
461 let r: &Regs = unsafe { &*pac::Ostimer0::ptr() };
462
463 // Read high then low to minimize incoherent snapshots
464 let hi = (r.evtimerh().read().evtimer_count_value().bits() as u64) & (EVTIMER_HI_MASK as u64);
465 let lo = r.evtimerl().read().evtimer_count_value().bits() as u64;
466
467 // Combine and convert from Gray code to binary
468 let gray = lo | (hi << EVTIMER_HI_SHIFT);
469 gray_to_bin(gray)
470}
471
472// Instance trait like other drivers, providing a PAC pointer for this OSTIMER instance
473pub trait Instance {
474 fn ptr() -> *const Regs;
475}
476
477// Token for OSTIMER0 provided by embassy-hal-internal peripherals macro.
478pub type Ostimer0 = crate::peripherals::OSTIMER0;
479
480impl Instance for crate::peripherals::OSTIMER0 {
481 #[inline(always)]
482 fn ptr() -> *const Regs {
483 pac::Ostimer0::ptr()
484 }
485}
486
487// Also implement Instance for the Peri wrapper type
488impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::OSTIMER0> {
489 #[inline(always)]
490 fn ptr() -> *const Regs {
491 pac::Ostimer0::ptr()
492 }
493}
494
495#[inline(always)]
496fn bin_to_gray(x: u64) -> u64 {
497 x ^ (x >> 1)
498}
499
500#[inline(always)]
501fn gray_to_bin(gray: u64) -> u64 {
502 // More efficient iterative conversion using predefined shifts
503 let mut bin = gray;
504 for &shift in &GRAY_CONVERSION_SHIFTS {
505 bin ^= bin >> shift;
506 }
507 bin
508}
509
510pub mod time_driver {
511 use core::sync::atomic::Ordering;
512 use core::task::Waker;
513
514 use embassy_sync::waitqueue::AtomicWaker;
515 use embassy_time_driver as etd;
516
517 use super::{
518 bin_to_gray, now_ticks_read, Regs, ALARM_ACTIVE, ALARM_CALLBACK, ALARM_FLAG, ALARM_TARGET_TIME,
519 EVTIMER_HI_MASK, EVTIMER_HI_SHIFT, LOW_32_BIT_MASK,
520 };
521 use crate::pac;
522 pub struct Driver;
523 static TIMER_WAKER: AtomicWaker = AtomicWaker::new();
524
525 impl etd::Driver for Driver {
526 fn now(&self) -> u64 {
527 // Use the hardware counter (frequency configured in init)
528 super::now_ticks_read()
529 }
530
531 fn schedule_wake(&self, timestamp: u64, waker: &Waker) {
532 let now = self.now();
533
534 // If timestamp is in the past or very close to now, wake immediately
535 if timestamp <= now {
536 waker.wake_by_ref();
537 return;
538 }
539
540 // Prevent scheduling too far in the future (beyond timer rollover)
541 // This prevents wraparound issues
542 let max_future = now + super::TIMER_MAX_VALUE;
543 if timestamp > max_future {
544 // For very long timeouts, wake immediately to avoid rollover issues
545 waker.wake_by_ref();
546 return;
547 }
548
549 // Register the waker first so any immediate wake below is observed by the executor.
550 TIMER_WAKER.register(waker);
551
552 let r: &Regs = unsafe { &*pac::Ostimer0::ptr() };
553
554 critical_section::with(|_| {
555 // Mask INTENA and clear flag
556 r.osevent_ctrl()
557 .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit());
558
559 // Read back to ensure W1C took effect on hardware
560 let _ = r.osevent_ctrl().read().ostimer_intrflag().bit();
561
562 if !super::wait_for_match_write_ready(r) {
563 super::prime_match_registers(r);
564
565 if !super::wait_for_match_write_ready(r) {
566 // If we can't safely program MATCH, wake immediately and leave INTENA masked.
567 waker.wake_by_ref();
568 return;
569 }
570 }
571
572 // Program MATCH (Gray-coded). Write low then high, then fence.
573 let gray = bin_to_gray(timestamp);
574 let l = (gray & LOW_32_BIT_MASK) as u32;
575
576 let h = (((gray >> EVTIMER_HI_SHIFT) as u16) & EVTIMER_HI_MASK) as u16;
577
578 r.match_l().write(|w| unsafe { w.match_value().bits(l) });
579 r.match_h().write(|w| unsafe { w.match_value().bits(h) });
580
581 if !super::wait_for_match_write_complete(r) {
582 waker.wake_by_ref();
583 return;
584 }
585
586 let now_after_program = super::now_ticks_read();
587 let intrflag_set = r.osevent_ctrl().read().ostimer_intrflag().bit_is_set();
588 if now_after_program >= timestamp && !intrflag_set {
589 waker.wake_by_ref();
590 return;
591 }
592
593 // Enable peripheral interrupt
594 r.osevent_ctrl().write(|w| w.ostimer_intena().set_bit());
595 });
596 }
597 }
598
599 /// Install the global embassy-time driver and configure NVIC priority for OS_EVENT.
600 ///
601 /// # Parameters
602 /// * `priority` - Interrupt priority for the OSTIMER interrupt
603 /// * `frequency_hz` - Actual OSTIMER clock frequency in Hz (stored for future use)
604 ///
605 /// Note: The frequency parameter is currently accepted for API compatibility.
606 /// The embassy_time_driver macro handles driver registration automatically.
607 pub fn init(priority: crate::interrupt::Priority, frequency_hz: u64) {
608 // Mask/clear at peripheral and set default MATCH
609 let r: &Regs = unsafe { &*pac::Ostimer0::ptr() };
610 super::prime_match_registers(r);
611
612 // Configure NVIC for timer operation
613 crate::interrupt::OS_EVENT.configure_for_timer(priority);
614
615 // Note: The embassy_time_driver macro automatically registers the driver
616 // The frequency parameter is accepted for future compatibility
617 let _ = frequency_hz; // Suppress unused parameter warning
618 }
619
620 // Export the global time driver expected by embassy-time
621 embassy_time_driver::time_driver_impl!(static DRIVER: Driver = Driver);
622
623 /// To be called from the OS_EVENT IRQ.
624 pub fn on_interrupt() {
625 let r: &Regs = unsafe { &*pac::Ostimer0::ptr() };
626
627 // Critical section to prevent races with schedule_wake
628 critical_section::with(|_| {
629 // Check if interrupt is actually pending and handle it atomically
630 if r.osevent_ctrl().read().ostimer_intrflag().bit_is_set() {
631 // Clear flag and disable interrupt atomically
632 r.osevent_ctrl().write(|w| {
633 w.ostimer_intrflag()
634 .clear_bit_by_one() // Write-1-to-clear using safe helper
635 .ostimer_intena()
636 .clear_bit()
637 });
638
639 // Wake the waiting task
640 TIMER_WAKER.wake();
641
642 // Handle alarm callback if active and this interrupt is for the alarm
643 if ALARM_ACTIVE.load(Ordering::SeqCst) {
644 let current_time = now_ticks_read();
645 let target_time = unsafe { ALARM_TARGET_TIME };
646
647 // Check if current time is close to alarm target time (within 1000 ticks for timing variations)
648 if current_time >= target_time && current_time <= target_time + 1000 {
649 ALARM_ACTIVE.store(false, Ordering::SeqCst);
650 unsafe { ALARM_TARGET_TIME = 0 };
651
652 // Execute callback if set
653 unsafe {
654 if let Some(callback) = ALARM_CALLBACK {
655 callback();
656 }
657 }
658
659 // Set flag if provided
660 unsafe {
661 if let Some(flag) = ALARM_FLAG {
662 (*flag).store(true, Ordering::SeqCst);
663 }
664 }
665 }
666 }
667 }
668 });
669 }
670
671 /// Type-level handler to be used with bind_interrupts! for OS_EVENT.
672 pub struct OsEventHandler;
673 impl crate::interrupt::typelevel::Handler<crate::interrupt::typelevel::OS_EVENT> for OsEventHandler {
674 unsafe fn on_interrupt() {
675 on_interrupt();
676 }
677 }
678}