embassy-rs/embassy · error

RNG error persists after reset; check the RCC clock…

Error message

RNG error persists after reset; check the RCC clock configuration

What it means

The blocking RNG read polls for a valid random word; after repeated clock/health failures it resets the RNG peripheral and retries up to MAX_RESET_RETRIES. If errors still persist it panics, because the usual root cause is that the RNG peripheral clock does not meet the required frequency in the RCC configuration.

Solutions

  1. Fix the RCC config so the RNG peripheral clock is enabled and within the datasheet-required frequency range.
  2. Verify the RNG kernel clock source selection (e.g. HSI48/HSE/PLL output) matches your board's clock tree.
  3. If the hardware still errors, replace the chip or fall back to a software RNG / external entropy source.
Defensive patterns

Strategy: validation

Validate before calling

// Before using the RNG, confirm the kernel clock is enabled and fast enough
// e.g. with embassy-stm32 rcc config: ensure rng clock source set and HSI48/PLL enabled.
// let rng_clk = rcc.clocks.rng; // debug-log its value in a probe run

Try / catch

// Panic cannot be caught; wrap blocking reads at a higher level with a fallback:
match std::panic::catch_unwind(|| rng.blocking_next_u32()) {
    Ok(w) => Some(w),
    Err(_) => { log::error!("RNG hardware fault"); None /* use software RNG fallback */ }
}

Prevention

When it happens

Trigger: Calling blocking_next_u32 (or blocking fill via it) when poll_word() repeatedly returns None after full RNG resets — i.e. the RNG keeps signaling seed/clock errors.

Common situations: RNG kernel clock misconfigured (too slow or gated) in RCC init; running on a board where the RNG clock source is wrong; silicon health-error conditions.

Related errors


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

Appendix: source

Thrown at embassy-stm32/src/rng.rs:418

    /// Get a random u32.
    ///
    /// This call is infallible: a seed or clock error is recovered by resetting
    /// the RNG. If the error persists after `MAX_RESET_RETRIES` resets (almost
    /// always a misconfigured RNG clock) it panics, since an infallible API has
    /// no other way to surface the fault. Use [`fill_bytes`](Rng::<Async>::fill_bytes)
    /// for a fallible path that returns [`Error`] instead.
    pub fn blocking_next_u32(&mut self) -> u32 {
        // Only resets are bounded, not poll iterations: a healthy RNG may take
        // many reads to produce the first word, and that must not count as a
        // failure.
        let mut retries = 0;
        loop {
            if let Some(word) = self.poll_word() {
                return word;
            }
            if retries >= MAX_RESET_RETRIES {
                panic!("RNG error persists after reset; check the RCC clock configuration");
            }
            retries += 1;
            self.reset();
        }
    }

    /// Get a random u64
    pub fn blocking_next_u64(&mut self) -> u64 {
        let mut rand = self.blocking_next_u32() as u64;
        rand |= (self.blocking_next_u32() as u64) << 32;
        rand
    }

    /// Fill a slice with random bytes
    pub fn blocking_fill_bytes(&mut self, dest: &mut [u8]) {
        for chunk in dest.chunks_mut(4) {
            let rand = self.blocking_next_u32();
            for (slot, num) in chunk.iter_mut().zip(rand.to_ne_bytes().iter()) {

View on GitHub (pinned to 463a07b963)