embassy-rs/embassy · error

MCLK frequency < 9.5 MHz is not compatible with the TRNG

Error message

MCLK frequency {} < 9.5 MHz is not compatible with the TRNG

What it means

The TRNG driver's clock setup (set_div, called from init) requires the MCLK system clock to be at least 9.5 MHz; below that no valid divider ratio exists, so the driver panics. The library throws this because the TRNG peripheral cannot operate reliably (sampling/decimation constraints) below its minimum input frequency. It is a compile-time-unknowable configuration constraint that can only be validated at runtime.

Solutions

  1. Raise MCLK to at least 9.5 MHz (e.g. 20/40/80 MHz from PLL) before constructing/initializing the TRNG.
  2. Call set_div only with freq >= 9_500_000; assert the clock frequency in your own init code before touching the TRNG.
  3. If low power is required, temporarily boost MCLK only when random data is needed, then re-derive entropy asynchronously.
  4. Check the ClockConfig passed to your RCC/clock setup and confirm the MCLK divider yields >= 9.5 MHz.

Example fix

// before (MCLK from default ~4MHz source)
let p = embassy_mspm0::init(Default::default());
let mut trng = Trng::new(p.TRNG, p.PAxx);
// after (configure 80 MHz MCLK first)
let config = Config { ... }; // clock config yielding MCLK >= 20 MHz
let p = embassy_mspm0::init(config);
assert!(mclk_hz() >= 9_500_000, "MCLK too low for TRNG");
let mut trng = Trng::new(p.TRNG, p.PAxx);
Defensive patterns

Strategy: validation

Validate before calling

// Rust (no-std): assert before constructing the TRNG
fn ensure_trng_clock_ok(mclk_hz: u32) {
    assert!(mclk_hz >= 9_500_000, "MCLK {} Hz too low for TRNG (min 9.5 MHz)", mclk_hz);
}
ensure_trng_clock_ok(clock_config.mclk_hz());

Type guard

// Rust: validate the frequency before any division
fn is_trng_compatible(mclk_hz: u32) -> bool { mclk_hz >= 9_500_000 }

if !is_trng_compatible(mclk_hz) { /* reconfigure clocks or skip TRNG */ }

Prevention

When it happens

Trigger: Calling embassy_mspm0::trng::Trng::init (which calls set_div) while MCLK is configured below 9,500,000 Hz — e.g. running the MCU from a low-power clock configuration (default ~4 MHz or 32.768 kHz) instead of the full-speed PLL/DFLL setup.

Common situations: Developers using a minimal clock_init / default power-on clock tree before initializing the TRNG; low-power applications that drop MCLK below 9.5 MHz and later need random numbers; boards where the clock config was changed for power reasons but the TRNG min-frequency requirement was overlooked.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of embassy-rs/embassy@463a07b963 (2026-09-10). Data as JSON: /api/errors/f3f934cf26e9083c. Report an issue: GitHub.

Appendix: source

Thrown at embassy-mspm0/src/trng.rs:358

    }

    fn set_div(&mut self) {
        // L-series TRM 13.2.2: The TRNG is derived from MCLK. Datasheets specify 9.5-20 MHz range.
        let freq = crate::sysctl::mclk_frequency();
        let ratio = if freq > 160_000_000 {
            panic!("MCLK frequency {} > 160 MHz is not compatible with the TRNG", freq)
        } else if freq >= 80_000_000 {
            Ratio::DivBy8
        } else if freq >= 60_000_000 {
            Ratio::DivBy6
        } else if freq >= 40_000_000 {
            Ratio::DivBy4
        } else if freq >= 20_000_000 {
            Ratio::DivBy2
        } else if freq >= 9_500_000 {
            Ratio::DivBy1
        } else {
            panic!("MCLK frequency {} < 9.5 MHz is not compatible with the TRNG", freq)
        };
        regs().clkdiv().write(|w| w.set_ratio(ratio));
    }

    fn set_decim_rate(&mut self) {
        regs().ctl().modify(|w| w.set_decim_rate(self.decim_rate));
    }

    fn clr_rdy(&mut self) {
        regs().iclr().write(|w| w.set_irq_captured_rdy(true));
    }

    fn set_cmd(&mut self, cmd: vals::Cmd) {
        regs().iclr().write(|w| w.set_irq_cmd_done(true));
        regs().ctl().modify(|w| w.set_cmd(cmd));
        while !regs().ris().read().irq_cmd_done() {}
    }

View on GitHub (pinned to 463a07b963)