embassy-rs/embassy · error

PLL is unavailable in voltage range 4

Error message

PLL is unavailable in voltage range 4

What it means

Per RM0456 §11.4.10, the STM32U5 PLL cannot run when the core voltage scaling is Range 4 (lowest power, lowest max frequency). embassy-stm32's init_pll enforces this and panics if a PLL is requested while voltage_range is VoltageScale::Range4.

Solutions

  1. Change config.voltage_range to Range1, Range2, or Range3 (checking VCO 128–544/330 MHz and output limits for the range)
  2. Or remove/disable all PLL configurations to run legitimately in Range4
  3. Verify VCO and PLL output frequencies against the selected range's limits (vco 128–544 MHz R1/R2, 128–330 MHz R3; out max 208/110/55 MHz)

Example fix

// before
config.voltage_range = VoltageScale::Range4;
config.pll1 = Some(PllConfig { source: PllSource::Hsi, ... }); // panic
// after
config.voltage_range = VoltageScale::Range1; // or Range2/Range3 as power budget allows
config.pll1 = Some(PllConfig { source: PllSource::Hsi, ... });
Defensive patterns

Strategy: validation

Validate before calling

if config.pll1.is_some() || config.pll2.is_some() || config.pll3.is_some() {
    assert!(config.voltage_range != VoltageScale::Range4, "PLL unavailable in Range 4");
}
// also check VCO/output limits per range:
// Range1/2: VCO 128-544 MHz, out <=208/110 MHz; Range3: VCO 128-330 MHz, out <=55 MHz

Type guard

fn pll_ok_in_range(range: VoltageScale, has_pll: bool) -> bool {
    !has_pll || !matches!(range, VoltageScale::Range4)
}

Prevention

When it happens

Trigger: rcc init with config.voltage_range = VoltageScale::Range4 and any PLL1/PLL2/PLL3 configured (PLL source Some).

Common situations: Choosing Range4 for low-power operation while still needing a PLL-derived clock (e.g. for USB or a high sysclk); copying a config that sets Range4 but leaves PLLs enabled.

Related errors


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

Appendix: source

Thrown at embassy-stm32/src/rcc/u5.rs:633

    let src_freq = match pll.source {
        PllSource::Disable => panic!("must not select PLL source as DISABLE"),
        PllSource::Hse => unwrap!(input.hse),
        PllSource::Hsi => unwrap!(input.hsi),
        PllSource::Msis => unwrap!(input.msi),
    };

    // Calculate the reference clock, which is the source divided by m
    let ref_freq = src_freq / pll.prediv;
    // Check limits per RM0456 § 11.4.6
    assert!(Hertz::mhz(4) <= ref_freq && ref_freq <= Hertz::mhz(16));

    // Check PLL clocks per RM0456 § 11.4.10
    let (vco_min, vco_max, out_max) = match voltage_range {
        VoltageScale::Range1 => (Hertz::mhz(128), Hertz::mhz(544), Hertz::mhz(208)),
        VoltageScale::Range2 => (Hertz::mhz(128), Hertz::mhz(544), Hertz::mhz(110)),
        VoltageScale::Range3 => (Hertz::mhz(128), Hertz::mhz(330), Hertz::mhz(55)),
        VoltageScale::Range4 => panic!("PLL is unavailable in voltage range 4"),
    };

    // Calculate the PLL VCO clock
    let vco_freq = ref_freq * pll.mul;
    assert!(vco_freq >= vco_min && vco_freq <= vco_max);

    // Calculate output clocks.
    let p = pll.divp.map(|div| vco_freq / div);
    let q = pll.divq.map(|div| vco_freq / div);
    let r = pll.divr.map(|div| vco_freq / div);
    for freq in [p, q, r] {
        if let Some(freq) = freq {
            assert!(freq <= out_max);
        }
    }

    let divr = match instance {
        PllInstance::Pll1 => RCC.pll1divr(),

View on GitHub (pinned to 463a07b963)