Files
stm32u575-hal/src/rcc/rcc.rs
2025-10-19 13:18:08 +02:00

757 lines
22 KiB
Rust

// stm32u5xx-hal/src/rcc.rs
//! Reset and Clock Control for STM32U575 (initial/minimal port)
//!
//! Scope: only features that clearly overlap between stm32l4xx-hal rcc.rs
//! and the STM32U5 HAL C (stm32u5xx_hal_rcc.c/.h).
//!
//! Skipped (present in U5 HAL C, not here yet):
//! - MSIK (kernel MSI), SHSI (secure HSI)
//! - PLL2 / PLL3
//! - PLL FRACN and MBOOST/EPOD handling
//! - LSE glitch filter, LSE CSS, LSESYS
//! - LSI divider (LSIPREDIV)
//! - STOP wake-up clock selection (STOPWUCK / STOPKERWUCK)
//! - Security/privileged attributes
//! - APB3 (PCLK3) prescaler & clocks
//! - MCO configuration
//! - Per-peripheral Enable/Reset tables (normally in rcc/enable.rs)
use crate::pac::{rcc, RCC};
use crate::flash::ACR;
use crate::pwr::Pwr;
use crate::time::Hertz;
use fugit::RateExtU32;
// Uncomment when you add bus-peripheral enable/reset mapping like in L4
// mod enable;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MsiFreq {
// Order and names reflect STM32U5 MSIRANGE mapping (RCC_MSIRANGE_x):
// 0..15 encode the specific frequencies per stm32u5xx_hal_rcc.h
// We provide exact frequencies in Hz.
MSI48, // 48_000_000
MSI24, // 24_000_000
MSI16, // 16_000_000
MSI12, // 12_000_000
MSI4, // 4_000_000
MSI2, // 2_000_000
MSI1_33, // 1_330_000 (approx 1.33 MHz)
MSI1, // 1_000_000
MSI3_072, // 3_072_000
MSI1_536, // 1_536_000
MSI1_024, // 1_024_000
MSI_768K, // 768_000
MSI400K, // 400_000
MSI200K, // 200_000
MSI133K, // 133_000
MSI100K, // 100_000
}
impl MsiFreq {
fn to_hertz(self) -> Hertz {
let hz = match self {
Self::MSI48 => 48_000_000,
Self::MSI24 => 24_000_000,
Self::MSI16 => 16_000_000,
Self::MSI12 => 12_000_000,
Self::MSI4 => 4_000_000,
Self::MSI2 => 2_000_000,
Self::MSI1_33 => 1_330_000,
Self::MSI1 => 1_000_000,
Self::MSI3_072 => 3_072_000,
Self::MSI1_536 => 1_536_000,
Self::MSI1_024 => 1_024_000,
Self::MSI_768K => 768_000,
Self::MSI400K => 400_000,
Self::MSI200K => 200_000,
Self::MSI133K => 133_000,
Self::MSI100K => 100_000,
};
hz.Hz()
}
// Map to ICSCR1.MSISRANGE field encoding (0..15)
fn to_range_bits(self) -> u8 {
match self {
Self::MSI48 => 0,
Self::MSI24 => 1,
Self::MSI16 => 2,
Self::MSI12 => 3,
Self::MSI4 => 4,
Self::MSI2 => 5,
Self::MSI1_33 => 6,
Self::MSI1 => 7,
Self::MSI3_072 => 8,
Self::MSI1_536 => 9,
Self::MSI1_024 => 10,
Self::MSI_768K => 11,
Self::MSI400K => 12,
Self::MSI200K => 13,
Self::MSI133K => 14,
Self::MSI100K => 15,
}
}
}
/// Extension trait that constrains the `RCC` peripheral
pub trait RccExt {
/// Constrains the `RCC` peripheral so it plays nicely with the other abstractions
fn constrain(self) -> Rcc;
}
impl RccExt for RCC {
fn constrain(self) -> Rcc {
Rcc {
ahb1: AHB1::new(),
ahb2_1: AHB2_1::new(),
ahb2_2: AHB2_2::new(),
ahb3: AHB3::new(),
apb1r1: APB1R1::new(),
apb1r2: APB1R2::new(),
apb2: APB2::new(),
apb3: APB3::new(),
cfgr: CFGR {
hse: None,
lse: None,
msi: None,
hsi48: false,
lsi: false,
hclk: None,
pclk1: None,
pclk2: None,
sysclk: None,
pll_source: None,
pll_config: None,
},
}
}
}
/// Constrained RCC peripheral
pub struct Rcc {
pub ahb1: AHB1,
pub ahb2_1: AHB2_1,
pub ahb2_2: AHB2_2,
pub ahb3: AHB3,
pub apb1r1: APB1R1,
pub apb1r2: APB1R2,
pub apb2: APB2,
pub apb3: APB3,
pub cfgr: CFGR,
}
/// Backup domain control register proxy
pub struct BDCR {
_0: (),
}
impl BDCR {
#[allow(dead_code)]
pub(crate) fn bdcr(&mut self) -> &rcc::BDCR {
unsafe { &(*RCC::ptr()).bdcr }
}
}
/// Control/Status Register proxy
pub struct CSR {
_0: (),
}
impl CSR {
#[allow(dead_code)]
pub(crate) fn csr(&mut self) -> &rcc::CSR {
unsafe { &(*RCC::ptr()).csr }
}
}
macro_rules! bus_struct_1 {
($busX:ident => ($EN:ident, $en:ident, $SMEN:ident, $smen:ident, $RST:ident, $rst:ident, $doc:literal),) => {
#[doc = $doc]
pub struct $busX {
_0: (),
}
impl $busX {
pub(crate) fn new() -> Self {
Self { _0: () }
}
#[allow(unused)]
pub(crate) fn enr(&self) -> &rcc::$EN {
unsafe { &(*RCC::ptr()).$en }
}
#[allow(unused)]
pub(crate) fn smenr(&self) -> &rcc::$SMEN {
unsafe { &(*RCC::ptr()).$smen }
}
#[allow(unused)]
pub(crate) fn rstr(&self) -> &rcc::$RST {
unsafe { &(*RCC::ptr()).$rst }
}
}
};
}
// U5 splits AHB2/APB1 into *1/*2 register banks; we model both.
bus_struct_1! {
AHB1 => (AHB1ENR, ahb1enr, AHB1SMENR, ahb1smenr, AHB1RSTR, ahb1rstr, "AHB1 registers"),
}
bus_struct_1! {
AHB2_1 => (AHB2ENR1, ahb2enr1, AHB2SMENR1, ahb2smenr1, AHB2RSTR1, ahb2rstr1, "AHB2 part 1 registers"),
}
bus_struct_1! {
AHB2_2 => (AHB2ENR2, ahb2enr2, AHB2SMENR2, ahb2smenr2, AHB2RSTR2, ahb2rstr2, "AHB2 part 2 registers"),
}
bus_struct_1! {
AHB3 => (AHB3ENR, ahb3enr, AHB3SMENR, ahb3smenr, AHB3RSTR, ahb3rstr, "AHB3 registers"),
}
bus_struct_1! {
APB1R1 => (APB1ENR1, apb1enr1, APB1SMENR1, apb1smenr1, APB1RSTR1, apb1rstr1, "APB1 register set 1"),
}
bus_struct_1! {
APB1R2 => (APB1ENR2, apb1enr2, APB1SMENR2, apb1smenr2, APB1RSTR2, apb1rstr2, "APB1 register set 2"),
}
bus_struct_1! {
APB2 => (APB2ENR, apb2enr, APB2SMENR, apb2smenr, APB2RSTR, apb2rstr, "APB2 registers"),
}
bus_struct_1! {
APB3 => (APB3ENR, apb3enr, APB3SMENR, apb3smenr, APB3RSTR, apb3rstr, "APB3 registers"),
}
/// Bus associated to peripheral
pub trait RccBus: crate::Sealed {
type Bus;
}
/// Enable/disable peripheral
pub trait Enable: RccBus {
fn enable(bus: &mut Self::Bus);
fn disable(bus: &mut Self::Bus);
fn is_enabled() -> bool;
fn is_disabled() -> bool;
unsafe fn enable_unchecked();
unsafe fn disable_unchecked();
}
/// Enable/disable peripheral in sleep mode
pub trait SMEnable: RccBus {
fn enable_in_sleep_mode(bus: &mut Self::Bus);
fn disable_in_sleep_mode(bus: &mut Self::Bus);
fn is_enabled_in_sleep_mode() -> bool;
fn is_disabled_in_sleep_mode() -> bool;
unsafe fn enable_in_sleep_mode_unchecked();
unsafe fn disable_in_sleep_mode_unchecked();
}
/// Reset peripheral
pub trait Reset: RccBus {
fn reset(bus: &mut Self::Bus);
unsafe fn reset_unchecked();
}
#[derive(Debug, PartialEq)]
struct HseConfig {
speed: u32,
bypass: CrystalBypass,
css: ClockSecuritySystem,
}
#[derive(Debug, PartialEq)]
struct LseConfig {
bypass: CrystalBypass,
// Skipped: LSE CSS / glitch filter / LSESYS
css: ClockSecuritySystem, // kept in struct for future, not applied
}
/// Crystal bypass selector
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum CrystalBypass {
Enable,
Disable,
}
/// Clock Security System
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ClockSecuritySystem {
Enable,
Disable,
}
const HSI: u32 = 16_000_000; // Hz (HSI16)
/// PLL Source (PLL1)
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PllSource {
MSI,
HSI16,
HSE,
}
impl PllSource {
// Map to RCC_PLL1CFGR PLL1SRC field bits (00: none, 01: MSI, 10: HSI, 11: HSE)
fn to_pllsrc_bits(self) -> u8 {
match self {
Self::MSI => 0b01,
Self::HSI16 => 0b10,
Self::HSE => 0b11,
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct PllConfig {
// Main PLL division factor M (1..16) -> register stores (M-1)
m: u8,
// Main PLL multiplication factor N (4..512) -> register stores (N-1)
n: u16,
// Main PLL division factor for PLLR (system clock) (1..128) -> reg stores (R-1)
r: u8,
// Skipped: P, Q outputs, MBOOST, FRACN, RGE beyond minimal
}
impl PllConfig {
pub fn new(m: u8, n: u16, r: u8) -> Self {
assert!(m >= 1 && m <= 16);
assert!(n >= 4 && n <= 512);
assert!(r >= 1 && r <= 128);
Self { m, n, r }
}
}
/// Clock configuration builder
pub struct CFGR {
hse: Option<HseConfig>,
lse: Option<LseConfig>,
msi: Option<MsiFreq>,
hsi48: bool,
lsi: bool,
hclk: Option<u32>,
pclk1: Option<u32>,
pclk2: Option<u32>,
sysclk: Option<u32>,
pll_source: Option<PllSource>,
pll_config: Option<PllConfig>,
}
impl CFGR {
pub fn hse(mut self, freq: Hertz, bypass: CrystalBypass, css: ClockSecuritySystem) -> Self {
self.hse = Some(HseConfig {
speed: freq.raw(),
bypass,
css,
});
self
}
pub fn lse(mut self, bypass: CrystalBypass, css: ClockSecuritySystem) -> Self {
self.lse = Some(LseConfig { bypass, css });
self
}
pub fn msi(mut self, range: MsiFreq) -> Self {
self.msi = Some(range);
self
}
pub fn lsi(mut self, on: bool) -> Self {
self.lsi = on;
self
}
pub fn hsi48(mut self, on: bool) -> Self {
self.hsi48 = on;
self
}
pub fn hclk(mut self, freq: Hertz) -> Self {
self.hclk = Some(freq.raw());
self
}
pub fn pclk1(mut self, freq: Hertz) -> Self {
self.pclk1 = Some(freq.raw());
self
}
pub fn pclk2(mut self, freq: Hertz) -> Self {
self.pclk2 = Some(freq.raw());
self
}
pub fn sysclk(mut self, freq: Hertz) -> Self {
self.sysclk = Some(freq.raw());
self
}
pub fn sysclk_with_pll(mut self, freq: Hertz, cfg: PllConfig) -> Self {
self.sysclk = Some(freq.raw());
self.pll_config = Some(cfg);
self
}
pub fn pll_source(mut self, source: PllSource) -> Self {
self.pll_source = Some(source);
self
}
pub fn freeze(&self, acr: &mut ACR, pwr: &mut Pwr) -> Clocks {
let rcc = unsafe { &*RCC::ptr() };
// Ensure MSI is ON and selected first (safe base)
if rcc.cr.read().msison().bit_is_clear() {
rcc.cr.modify(|_, w| w.msison().set_bit());
while rcc.cr.read().msisrdy().bit_is_clear() {}
}
// Program MSI to 4 MHz as default base (MSIRGSEL=1, MSISRANGE=4MHz)
unsafe {
rcc.icscr1.modify(|_, w| {
w.msirgsel().set_bit();
w.msisrange().bits(MsiFreq::MSI4.to_range_bits());
w
});
}
// If SYSCLK not MSI, force switch to MSI (SW=00) and wait
if rcc.cfgr1.read().sws().bits() != 0 {
// Reset CFGR1.SW to MSI
rcc.cfgr1.modify(|_, w| unsafe { w.sw().bits(0b00) });
while rcc.cfgr1.read().sws().bits() != 0b00 {}
}
//
// 1) Oscillators
//
// LSI if requested (or if we needed by LSE CSS in future)
let lsi_used = if self.lsi {
rcc.bdcr.modify(|_, w| w.lsion().set_bit());
while rcc.bdcr.read().lsirdy().bit_is_clear() {}
true
} else {
false
};
// LSE if requested
if let Some(lse_cfg) = &self.lse {
// Enable backup domain write access
// NOTE: depends on your Pwr wrapper; mirrors L4 HAL style
pwr.cr1.reg().modify(|_, w| w.dbp().set_bit());
// Enable LSE (bypass if set)
if lse_cfg.bypass == CrystalBypass::Enable {
rcc.bdcr.modify(|_, w| w.lsebyp().set_bit());
}
rcc.bdcr.modify(|_, w| w.lseon().set_bit());
while rcc.bdcr.read().lserdy().bit_is_clear() {}
// Skipped: LSESYS and LSE CSS/glitch filter
}
// HSE if requested
if let Some(hse_cfg) = &self.hse {
rcc.cr.modify(|_, w| {
if hse_cfg.bypass == CrystalBypass::Enable {
w.hsebyp().set_bit();
}
w.hseon().set_bit()
});
while rcc.cr.read().hserdy().bit_is_clear() {}
if hse_cfg.css == ClockSecuritySystem::Enable {
// Enable CSS on HSE (CR.CSSON)
rcc.cr.modify(|_, w| w.csson().set_bit());
}
}
// MSI range if set explicitly
if let Some(msi) = self.msi {
unsafe {
rcc.icscr1.modify(|_, w| {
w.msirgsel().set_bit();
w.msisrange().bits(msi.to_range_bits());
w
})
};
while rcc.cr.read().msisrdy().bit_is_clear() {}
}
// HSI48
if self.hsi48 {
rcc.cr.modify(|_, w| w.hsi48on().set_bit());
while rcc.cr.read().hsi48rdy().bit_is_clear() {}
}
//
// 2) PLL1 setup (optional)
//
// Decide PLL1 source
let (src_freq, pll_src) = if let Some(src) = self.pll_source {
match src {
PllSource::HSE => {
let hse = self
.hse
.as_ref()
.expect("HSE selected as PLL source, but not enabled");
(hse.speed, src)
}
PllSource::HSI16 => (HSI, src),
PllSource::MSI => {
let msi = self.msi.expect("MSI selected as PLL source, but not enabled");
(msi.to_hertz().raw(), src)
}
}
} else {
if let Some(hse) = &self.hse {
(hse.speed, PllSource::HSE)
} else if let Some(msi) = self.msi {
(msi.to_hertz().raw(), PllSource::MSI)
} else {
(HSI, PllSource::HSI16)
}
};
// If we need HSI for PLL1 and it's not on, turn it on
if pll_src == PllSource::HSI16 {
rcc.cr.modify(|_, w| w.hsion().set_bit());
while rcc.cr.read().hsirdy().bit_is_clear() {}
}
let want_sysclk = match (self.sysclk, self.msi) {
(Some(sysclk), _) => sysclk,
(None, Some(msi)) => msi.to_hertz().raw(),
(None, None) => MsiFreq::MSI4.to_hertz().raw(),
};
assert!(want_sysclk <= 80_000_000);
// Auto PLL config if not provided (simple best-effort: M=1, R=2)
let pllconf = if self.pll_config.is_none() {
if self.sysclk.is_some() {
let m = 1u8;
let r = 2u8;
// want_sysclk = (src/m)*N / r => N = want_sysclk * r / (src/m)
let n = ((want_sysclk as u64) * (r as u64) / (src_freq as u64)) as u16;
Some(PllConfig::new(m, n, r))
} else {
None
}
} else {
self.pll_config
};
// Compute prescalers from requested hclk/pclk1/pclk2
// HPRE (CFGR2.HPRE), PPRE1/PPRE2 (CFGR2.PPRE1/PPRE2)
let (hpre_bits, hpre_div) = self
.hclk
.map(|hclk| match want_sysclk / hclk {
0 => unreachable!(),
1 => (0b0000, 1),
2 => (0b1000, 2),
3..=5 => (0b1001, 4),
6..=11 => (0b1010, 8),
12..=39 => (0b1011, 16),
40..=95 => (0b1100, 64),
96..=191 => (0b1101, 128),
192..=383 => (0b1110, 256),
_ => (0b1111, 512),
})
.unwrap_or((0b0000, 1));
let hclk = want_sysclk / hpre_div;
let (ppre1_bits, ppre1_div) = self
.pclk1
.map(|pclk1| match hclk / pclk1 {
0 => unreachable!(),
1 => (0b000, 1),
2 => (0b100, 2),
3..=5 => (0b101, 4),
6..=11 => (0b110, 8),
_ => (0b111, 16),
})
.unwrap_or((0b000, 1));
let pclk1 = hclk / (ppre1_div as u32);
let (ppre2_bits, ppre2_div) = self
.pclk2
.map(|pclk2| match hclk / pclk2 {
0 => unreachable!(),
1 => (0b000, 1),
2 => (0b100, 2),
3..=5 => (0b101, 4),
6..=11 => (0b110, 8),
_ => (0b111, 16),
})
.unwrap_or((0b000, 1));
let pclk2 = hclk / (ppre2_div as u32);
// Timer clocks double if APB prescaler != 1
let timclk1 = if ppre1_div == 1 { pclk1 } else { 2 * pclk1 };
let timclk2 = if ppre2_div == 1 { pclk2 } else { 2 * pclk2 };
// Adjust Flash latency (simple L4-like thresholds for <=80 MHz)
unsafe {
acr.acr().write(|w| {
w.latency().bits(if hclk <= 16_000_000 {
0b000
} else if hclk <= 32_000_000 {
0b001
} else if hclk <= 48_000_000 {
0b010
} else if hclk <= 64_000_000 {
0b011
} else {
0b100
})
})
}
// Program prescalers first (CFGR2: HPRE/PPRE1/PPRE2)
rcc.cfgr2.modify(|_, w| unsafe {
w.hpre().bits(hpre_bits);
w.ppre1().bits(ppre1_bits);
w.ppre2().bits(ppre2_bits)
});
let mut used_msi = self.msi;
let sysclk_src_bits;
if let Some(pll) = pllconf {
// Basic checks for VCO ranges (very simplified; FRACN/MBOOST ignored)
let vco_in = (src_freq as u32) / (pll.m as u32);
assert!(vco_in >= 1_000_000 && vco_in <= 16_000_000);
let vco = (vco_in as u64) * (pll.n as u64);
assert!(vco >= 128_000_000 && vco <= 544_000_000);
let sysclk_calc = (vco / (pll.r as u64)) as u32;
assert!(sysclk_calc <= 80_000_000);
// Disable PLL1 before reconfig
rcc.cr.modify(|_, w| w.pll1on().clear_bit());
while rcc.cr.read().pll1rdy().bit_is_set() {}
// Set PLL source
let src_bits = pll_src.to_pllsrc_bits();
rcc.pll1cfgr.modify(|_, w| unsafe {
w.pll1src().bits(src_bits);
// VCI range selection (very rough): 4..8 => range0, 8..16 => range1
let rge = if vco_in < 8_000_000 { 0b00 } else { 0b11 };
w.pll1rge().bits(rge);
// M
w.pll1m().bits(pll.m - 1);
// Enable only R output
w.pll1ren().clear_bit(); // clear first; will set after lock
w
});
// Program N, R in PLL1DIVR
rcc.pll1divr.modify(|_, w| unsafe {
w.pll1n().bits((pll.n - 1) as u16);
w.pll1r().bits(pll.r - 1);
// Leave P/Q untouched/disabled (skipped)
w
});
// Enable PLL1
rcc.cr.modify(|_, w| w.pll1on().set_bit());
while rcc.cr.read().pll1rdy().bit_is_clear() {}
// Enable PLL1 R output
rcc.pll1cfgr.modify(|_, w| w.pll1ren().set_bit());
// Switch SYSCLK to PLL1 (CFGR1.SW = 11)
sysclk_src_bits = 0b11;
rcc.cfgr1.modify(|_, w| unsafe { w.sw().bits(sysclk_src_bits) });
} else {
// Keep MSI as SYSCLK (CFGR1.SW = 00)
sysclk_src_bits = 0b00;
if used_msi.is_none() {
used_msi = Some(MsiFreq::MSI4);
}
rcc.cfgr1.modify(|_, w| unsafe { w.sw().bits(sysclk_src_bits) });
}
// Wait for SWS
while rcc.cfgr1.read().sws().bits() != sysclk_src_bits {}
// If we ended up on PLL, and MSI wasn't requested for other purposes, we can switch MSI off
if used_msi.is_none() && sysclk_src_bits == 0b11 {
rcc.cr.modify(|_, w| w.msison().clear_bit());
}
Clocks {
hclk: hclk.Hz(),
lsi: lsi_used,
lse: self.lse.is_some(),
msi: used_msi,
hsi48: self.hsi48,
pclk1: pclk1.Hz(),
pclk2: pclk2.Hz(),
ppre1: ppre1_div as u8,
ppre2: ppre2_div as u8,
sysclk: want_sysclk.Hz(),
timclk1: timclk1.Hz(),
timclk2: timclk2.Hz(),
pll_source: if pllconf.is_some() { Some(pll_src) } else { None },
}
}
}
/// Frozen clock frequencies (indicate RCC config is locked)
#[derive(Clone, Copy, Debug)]
pub struct Clocks {
hclk: Hertz,
hsi48: bool,
msi: Option<MsiFreq>,
lsi: bool,
lse: bool,
pclk1: Hertz,
pclk2: Hertz,
ppre1: u8,
ppre2: u8,
sysclk: Hertz,
timclk1: Hertz,
timclk2: Hertz,
pll_source: Option<PllSource>,
}
impl Clocks {
pub fn hclk(&self) -> Hertz {
self.hclk
}
pub fn hsi48(&self) -> bool {
self.hsi48
}
pub fn msi(&self) -> Option<MsiFreq> {
self.msi
}
pub fn lsi(&self) -> bool {
self.lsi
}
pub fn lse(&self) -> bool {
self.lse
}
pub fn pclk1(&self) -> Hertz {
self.pclk1
}
pub fn pclk2(&self) -> Hertz {
self.pclk2
}
pub fn pll_source(&self) -> Option<PllSource> {
self.pll_source
}
#[allow(dead_code)]
pub(crate) fn ppre1(&self) -> u8 {
self.ppre1
}
#[allow(dead_code)]
pub(crate) fn ppre2(&self) -> u8 {
self.ppre2
}
pub fn sysclk(&self) -> Hertz {
self.sysclk
}
pub fn timclk1(&self) -> Hertz {
self.timclk1
}
pub fn timclk2(&self) -> Hertz {
self.timclk2
}
}