embassy-rs/embassy · error

pll ref_clk out of range

Error message

pll ref_clk out of range: {} hz

What it means

After the prediv divider, the PLL reference clock (ref_clk) must fall within the hardware-supported ranges: up to 16 MHz, binned into Pllrge ranges (Range1..Range8). A ref_clk above 16 MHz cannot be encoded, so init_pll panics with the offending frequency. This happens before any output-divider math, so the config is rejected outright.

Solutions

  1. Increase config.prediv (DIVM) so in_clk / prediv is <= 16 MHz (ideally in the 2-16 MHz sweet spot).
  2. Recompute pll.mul/divp/divq values after changing prediv to keep the intended output frequencies.
  3. Prefer a reference in 4-8 MHz (Range4) or 8-16 MHz (Range8) for typical VCO configurations.

Example fix

// before
// 48 MHz HSI, prediv Div1 -> ref_clk 48 MHz -> panic
let pll = Pll { source: PllSource::Hsi, prediv: PllPreDiv::Div1, ..Default::default() };
// after
let pll = Pll { source: PllSource::Hsi, prediv: PllPreDiv::Div4, ..Default::default() }; // ref_clk = 12 MHz
Defensive patterns

Strategy: validation

Validate before calling

let ref_hz = in_clk.0 / pll.prediv as u32;
assert!(ref_hz <= 16_000_000, "pll ref_clk {} Hz out of range, increase prediv", ref_hz);

Prevention

When it happens

Trigger: On H7/H5/WL-family RCC init, supplying a Pll whose input frequency divided by prediv (DIVM) exceeds 16 MHz — e.g. 48 MHz HSI with prediv Div1, or a 25 MHz HSE with too small a divider.

Common situations: Forgetting to set prediv/DIVM at all on a fast source; small prediv values with a 25/48 MHz crystal or HSI; porting configs from chips where larger ref clocks were tolerated.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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

Appendix: source

Thrown at embassy-stm32/src/rcc/h.rs:976

    RCC.pllcfgr(num).write(|w| w.set_divm(PllPreDiv::from_bits(0)));
}

fn init_pll(num: usize, config: Pll, input: &PllInput) -> PllOutput {
    let in_clk = match config.source {
        PllSource::Disable => panic!("must not set PllSource::Disable"),
        PllSource::Hsi => unwrap!(input.hsi),
        PllSource::Hse => unwrap!(input.hse),
        PllSource::Csi => unwrap!(input.csi),
    };

    let ref_clk = in_clk / config.prediv as u32;

    let ref_range = match ref_clk.0 {
        ..=1_999_999 => Pllrge::Range1,
        ..=3_999_999 => Pllrge::Range2,
        ..=7_999_999 => Pllrge::Range4,
        ..=16_000_000 => Pllrge::Range8,
        x => panic!("pll ref_clk out of range: {} hz", x),
    };

    // The smaller range (150 to 420 MHz) must
    // be chosen when the reference clock frequency is lower than 2 MHz.
    let wide_allowed = ref_range != Pllrge::Range1;

    #[cfg(any(stm32h743, stm32h730))]
    let vco_clk = match config.fracn {
        Some(fracn) => {
            Hertz::hz((ref_clk.0 as f32 * ((config.mul.to_bits() + 1) as f32 + (fracn as f32 / 8192.0))) as u32)
        }
        None => ref_clk * config.mul,
    };
    #[cfg(not(any(stm32h743, stm32h730)))]
    let vco_clk = ref_clk * config.mul;

    let vco_range = if VCO_RANGE.contains(&vco_clk) {
        Pllvcosel::MediumVco

View on GitHub (pinned to 463a07b963)