embassy-rs/embassy · error
Enabling OsTimer clock should not fail
Error message
Enabling OsTimer clock should not fail
What it means
embassy-mcxa's OsTimer driver init calls enable_and_reset::<OSTIMER0> and unwraps the result with the message "Enabling OsTimer clock should not fail". The expect assumes the 1 MHz always-on clock (Clk1M) is always available; if the clock enable operation returns an error (e.g. the power/clock controller rejects the request or the config's clock source is gated), the driver panics during time-driver initialization.
Solutions
- Update embassy-mcxa to the latest version; failures in this path are typically library bugs fixed upstream
- Verify your chip/feature selection matches your actual MCX part so the correct OSTIMER0 instance and clock config are used
- Check that nothing earlier in boot disabled the always-on/1 MHz clock domain or the OSTIMER0 power domain
- If it persists, file an upstream issue with the returned error from enable_and_reset (temporarily replace the expect with a match to log it)
Example fix
// before
.enable_and_reset::<OSTIMER0>(&OsTimerConfig { .. })
.expect("Enabling OsTimer clock should not fail");
// after (debug)
let parts = unsafe { enable_and_reset::<OSTIMER0>(&cfg) };
if let Err(e) = &parts { defmt::error!("ostimer enable failed: {:?}", e); }
let parts = parts.expect("Enabling OsTimer clock should not fail"); Defensive patterns
Strategy: try-catch
Validate before calling
// Probe the clock before handing control to the time driver
// (no_std: log the concrete error instead of an opaque panic)
match unsafe { enable_and_reset::<OSTIMER0>(&OsTimerConfig::default()) } {
Ok(_) => {},
Err(e) => defmt::error!("OSTIMER0 enable failed: {:?}", e),
} Try / catch
// Replace the library's expect during bring-up to surface the root cause
let parts = unsafe { enable_and_reset::<OSTIMER0>(&cfg) };
let parts = match parts {
Ok(p) => p,
Err(e) => { defmt::error!("ostimer init failed: {:?}", e); defmt::panic!(); }
}; Prevention
- Keep embassy-mcxa and related HAL crates on versions tested together
- Initialize the time driver first, before any code can gate clocks
- Verify your target/feature selection matches the exact MCX part number on your board
- During bring-up, surface enable_and_reset errors with logging rather than bare expects
When it happens
Trigger: Initializing the embassy time driver (embassy_mcxa::ostimer::OsTimer::init, usually reached via embassy executor/time driver setup) when enable_and_reset::<OSTIMER0> with PoweredClock::AlwaysEnabled and OstimerClockSel::Clk1M returns Err.
Common situations: Boot issues on MCX A/N series boards where the always-on 1 MHz clock domain or the OSTIMER0 peripheral clock gate is unavailable or the clock configuration layer has a bug/regression.
Related errors
- MCLK frequency < 9.5 MHz is not compatible with the TRNG
- AHB frequency is too low
- must not select PLL source as DISABLE
- LSE is not configured, but selected for time_driver!!!
- IC source was set to PLL , but it is not currently enabled
AI-assisted analysis of embassy-rs/embassy@463a07b963 (2026-09-10).
Data as JSON: /api/errors/5e2877c3fa46c2a1.
Report an issue: GitHub.
Appendix: source
Thrown at embassy-mcxa/src/ostimer.rs:83
/// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.
alarms: Mutex<CriticalSectionRawMutex, AlarmState>,
queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>,
}
impl OsTimer {
fn init(&'static self, irq_prio: crate::interrupt::Priority) {
// init alarms
critical_section::with(|cs| {
let alarm = DRIVER.alarms.borrow(cs);
alarm.timestamp.set(u64::MAX);
});
let parts = unsafe {
enable_and_reset::<OSTIMER0>(&OsTimerConfig {
power: PoweredClock::AlwaysEnabled,
source: OstimerClockSel::Clk1M,
})
.expect("Enabling OsTimer clock should not fail")
};
// Currently does nothing as Clk1M is always enabled anyway, this is here
// to make sure that doesn't change in a refactoring.
core::mem::forget(parts.wake_guard);
interrupt::OS_EVENT.disable();
// Make sure interrupt is masked
OSTIMER0.osevent_ctrl().modify(|w| w.set_ostimer_intena(false));
// Default to the end of time
OSTIMER0.match_l().write(|w| w.set_match_value(u32::MAX));
OSTIMER0.match_h().write(|w| w.set_match_value(u16::MAX));
interrupt::OS_EVENT.unpend();
interrupt::OS_EVENT.set_priority(irq_prio);
unsafe { interrupt::OS_EVENT.enable() };View on GitHub (pinned to 463a07b963)