Merge pull request #2015 from willglynn/stm32u5_faster_clocks

stm32: u5: implement >55 MHz clock speeds
This commit is contained in:
Dario Nieuwenhuis 2023-10-06 23:38:15 +00:00 committed by GitHub
commit 3bf8e4de5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 274 additions and 93 deletions

View File

@ -1,7 +1,7 @@
use stm32_metapac::rcc::vals::{Msirange, Msirgsel, Pllm, Pllsrc, Sw}; use stm32_metapac::rcc::vals::{Msirange, Msirgsel, Pllm, Pllmboost, Pllrge, Pllsrc, Sw};
pub use super::bus::{AHBPrescaler, APBPrescaler}; pub use super::bus::{AHBPrescaler, APBPrescaler};
use crate::pac::{FLASH, RCC}; use crate::pac::{FLASH, PWR, RCC};
use crate::rcc::{set_freqs, Clocks}; use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz; use crate::time::Hertz;
@ -15,23 +15,86 @@ pub use crate::pac::pwr::vals::Vos as VoltageScale;
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub enum ClockSrc { pub enum ClockSrc {
/// Use an internal medium speed oscillator (MSIS) as the system clock.
MSI(MSIRange), MSI(MSIRange),
/// Use the external high speed clock as the system clock.
///
/// HSE clocks faster than 25 MHz require at least `VoltageScale::RANGE3`, and HSE clocks must
/// never exceed 50 MHz.
HSE(Hertz), HSE(Hertz),
/// Use the 16 MHz internal high speed oscillator as the system clock.
HSI16, HSI16,
PLL1R(PllSrc, PllM, PllN, PllClkDiv), /// Use PLL1 as the system clock.
PLL1R(PllConfig),
}
impl Default for ClockSrc {
fn default() -> Self {
// The default system clock source is MSIS @ 4 MHz, per RM0456 § 11.4.9
ClockSrc::MSI(MSIRange::Range4mhz)
}
}
#[derive(Clone, Copy, Debug)]
pub struct PllConfig {
/// The clock source for the PLL.
pub source: PllSrc,
/// The PLL prescaler.
///
/// The clock speed of the `source` divided by `m` must be between 4 and 16 MHz.
pub m: PllM,
/// The PLL multiplier.
///
/// The multiplied clock `source` divided by `m` times `n` must be between 128 and 544
/// MHz. The upper limit may be lower depending on the `Config { voltage_range }`.
pub n: PllN,
/// The divider for the R output.
///
/// When used to drive the system clock, `source` divided by `m` times `n` divided by `r`
/// must not exceed 160 MHz. System clocks above 55 MHz require a non-default
/// `Config { voltage_range }`.
pub r: PllClkDiv,
}
impl PllConfig {
/// A configuration for HSI16 / 1 * 10 / 1 = 160 MHz
pub const fn hsi16_160mhz() -> Self {
PllConfig {
source: PllSrc::HSI16,
m: PllM::NotDivided,
n: PllN::Mul10,
r: PllClkDiv::NotDivided,
}
}
/// A configuration for MSIS @ 48 MHz / 3 * 10 / 1 = 160 MHz
pub const fn msis_160mhz() -> Self {
PllConfig {
source: PllSrc::MSIS(MSIRange::Range48mhz),
m: PllM::Div3,
n: PllN::Mul10,
r: PllClkDiv::NotDivided,
}
}
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub enum PllSrc { pub enum PllSrc {
MSI(MSIRange), /// Use an internal medium speed oscillator as the PLL source.
MSIS(MSIRange),
/// Use the external high speed clock as the system PLL source.
///
/// HSE clocks faster than 25 MHz require at least `VoltageScale::RANGE3`, and HSE clocks must
/// never exceed 50 MHz.
HSE(Hertz), HSE(Hertz),
/// Use the 16 MHz internal high speed oscillator as the PLL source.
HSI16, HSI16,
} }
impl Into<Pllsrc> for PllSrc { impl Into<Pllsrc> for PllSrc {
fn into(self) -> Pllsrc { fn into(self) -> Pllsrc {
match self { match self {
PllSrc::MSI(..) => Pllsrc::MSIS, PllSrc::MSIS(..) => Pllsrc::MSIS,
PllSrc::HSE(..) => Pllsrc::HSE, PllSrc::HSE(..) => Pllsrc::HSE,
PllSrc::HSI16 => Pllsrc::HSI16, PllSrc::HSI16 => Pllsrc::HSI16,
} }
@ -41,57 +104,45 @@ impl Into<Pllsrc> for PllSrc {
seq_macro::seq!(N in 2..=128 { seq_macro::seq!(N in 2..=128 {
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub enum PllClkDiv { pub enum PllClkDiv {
NotDivided, NotDivided = 1,
#( #(
Div~N = (N-1), Div~N = N,
)* )*
} }
impl PllClkDiv { impl PllClkDiv {
fn to_div(&self) -> u8 { fn to_div(&self) -> u8 {
match self { match self {
PllClkDiv::NotDivided => 1, PllClkDiv::NotDivided => 0,
#( #(
PllClkDiv::Div~N => N + 1, PllClkDiv::Div~N => N - 1,
)* )*
} }
} }
} }
}); });
impl Into<u8> for PllClkDiv {
fn into(self) -> u8 {
(self as u8) + 1
}
}
seq_macro::seq!(N in 4..=512 { seq_macro::seq!(N in 4..=512 {
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub enum PllN { pub enum PllN {
NotMultiplied, NotMultiplied = 1,
#( #(
Mul~N = N-1, Mul~N = N,
)* )*
} }
impl PllN { impl PllN {
fn to_mul(&self) -> u16 { fn to_mul(&self) -> u16 {
match self { match self {
PllN::NotMultiplied => 1, PllN::NotMultiplied => 0,
#( #(
PllN::Mul~N => N + 1, PllN::Mul~N => N - 1,
)* )*
} }
} }
} }
}); });
impl Into<u16> for PllN {
fn into(self) -> u16 {
(self as u16) + 1
}
}
// Pre-division // Pre-division
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub enum PllM { pub enum PllM {
@ -132,6 +183,7 @@ impl Into<Sw> for ClockSrc {
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum MSIRange { pub enum MSIRange {
/// The 48 MHz MSI speed is unavailable in `VoltageScale::RANGE4`.
Range48mhz = 48_000_000, Range48mhz = 48_000_000,
Range24mhz = 24_000_000, Range24mhz = 24_000_000,
Range16mhz = 16_000_000, Range16mhz = 16_000_000,
@ -179,12 +231,6 @@ impl Into<Msirange> for MSIRange {
} }
} }
impl Default for MSIRange {
fn default() -> Self {
MSIRange::Range4mhz
}
}
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct Config { pub struct Config {
pub mux: ClockSrc, pub mux: ClockSrc,
@ -193,24 +239,57 @@ pub struct Config {
pub apb2_pre: APBPrescaler, pub apb2_pre: APBPrescaler,
pub apb3_pre: APBPrescaler, pub apb3_pre: APBPrescaler,
pub hsi48: bool, pub hsi48: bool,
/// The voltage range influences the maximum clock frequencies for different parts of the
/// device. In particular, system clocks exceeding 110 MHz require `RANGE1`, and system clocks
/// exceeding 55 MHz require at least `RANGE2`.
///
/// See RM0456 § 10.5.4 for a general overview and § 11.4.10 for clock source frequency limits.
pub voltage_range: VoltageScale,
} }
impl Default for Config { impl Config {
fn default() -> Self { unsafe fn init_hsi16(&self) -> Hertz {
Self { RCC.cr().write(|w| w.set_hsion(true));
mux: ClockSrc::MSI(MSIRange::default()), while !RCC.cr().read().hsirdy() {}
ahb_pre: AHBPrescaler::DIV1,
apb1_pre: APBPrescaler::DIV1, HSI_FREQ
apb2_pre: APBPrescaler::DIV1, }
apb3_pre: APBPrescaler::DIV1,
hsi48: false, unsafe fn init_hse(&self, frequency: Hertz) -> Hertz {
} // Check frequency limits per RM456 § 11.4.10
} match self.voltage_range {
} VoltageScale::RANGE1 | VoltageScale::RANGE2 | VoltageScale::RANGE3 => {
assert!(frequency.0 <= 50_000_000);
}
VoltageScale::RANGE4 => {
assert!(frequency.0 <= 25_000_000);
}
}
// Enable HSE, and wait for it to stabilize
RCC.cr().write(|w| w.set_hseon(true));
while !RCC.cr().read().hserdy() {}
frequency
}
unsafe fn init_msis(&self, range: MSIRange) -> Hertz {
// Check MSI output per RM0456 § 11.4.10
match self.voltage_range {
VoltageScale::RANGE4 => {
assert!(range as u32 <= 24_000_000);
}
_ => {}
}
// RM0456 § 11.8.2: spin until MSIS is off or MSIS is ready before setting its range
loop {
let cr = RCC.cr().read();
if cr.msison() == false || cr.msisrdy() == true {
break;
}
}
pub(crate) unsafe fn init(config: Config) {
let sys_clk = match config.mux {
ClockSrc::MSI(range) => {
RCC.icscr1().modify(|w| { RCC.icscr1().modify(|w| {
let bits: Msirange = range.into(); let bits: Msirange = range.into();
w.set_msisrange(bits); w.set_msisrange(bits);
@ -221,75 +300,159 @@ pub(crate) unsafe fn init(config: Config) {
w.set_msison(true); w.set_msison(true);
}); });
while !RCC.cr().read().msisrdy() {} while !RCC.cr().read().msisrdy() {}
Hertz(range as u32)
}
}
range.into() impl Default for Config {
fn default() -> Self {
Self {
mux: ClockSrc::default(),
ahb_pre: AHBPrescaler::DIV1,
apb1_pre: APBPrescaler::DIV1,
apb2_pre: APBPrescaler::DIV1,
apb3_pre: APBPrescaler::DIV1,
hsi48: false,
voltage_range: VoltageScale::RANGE3,
} }
ClockSrc::HSE(freq) => { }
RCC.cr().write(|w| w.set_hseon(true)); }
while !RCC.cr().read().hserdy() {}
freq.0 pub(crate) unsafe fn init(config: Config) {
} // Ensure PWR peripheral clock is enabled
ClockSrc::HSI16 => { RCC.ahb3enr().modify(|w| {
RCC.cr().write(|w| w.set_hsion(true)); w.set_pwren(true);
while !RCC.cr().read().hsirdy() {} });
RCC.ahb3enr().read(); // synchronize
HSI_FREQ.0 // Set the requested power mode
} PWR.vosr().modify(|w| {
ClockSrc::PLL1R(src, m, n, div) => { w.set_vos(config.voltage_range);
let freq = match src { });
PllSrc::MSI(_) => { while !PWR.vosr().read().vosrdy() {}
// TODO: enable MSI
MSIRange::default().into()
}
PllSrc::HSE(hertz) => {
// TODO: enable HSE
hertz.0
}
PllSrc::HSI16 => {
RCC.cr().write(|w| w.set_hsion(true));
while !RCC.cr().read().hsirdy() {}
HSI_FREQ.0 let sys_clk = match config.mux {
} ClockSrc::MSI(range) => config.init_msis(range),
ClockSrc::HSE(freq) => config.init_hse(freq),
ClockSrc::HSI16 => config.init_hsi16(),
ClockSrc::PLL1R(pll) => {
// Configure the PLL source
let source_clk = match pll.source {
PllSrc::MSIS(range) => config.init_msis(range),
PllSrc::HSE(hertz) => config.init_hse(hertz),
PllSrc::HSI16 => config.init_hsi16(),
}; };
// disable // Calculate the reference clock, which is the source divided by m
let reference_clk = source_clk / (pll.m as u8 as u32 + 1);
// Check limits per RM0456 § 11.4.6
assert!(Hertz::mhz(4) <= reference_clk && reference_clk <= Hertz::mhz(16));
// Calculate the PLL1 VCO clock and PLL1 R output clock
let pll1_clk = reference_clk * (pll.n as u8 as u32);
let pll1r_clk = pll1_clk / (pll.r as u8 as u32);
// Check system clock per RM0456 § 11.4.9
assert!(pll1r_clk <= Hertz::mhz(160));
// Check PLL clocks per RM0456 § 11.4.10
match config.voltage_range {
VoltageScale::RANGE1 => {
assert!(pll1_clk >= Hertz::mhz(128) && pll1_clk <= Hertz::mhz(544));
assert!(pll1r_clk <= Hertz::mhz(208));
}
VoltageScale::RANGE2 => {
assert!(pll1_clk >= Hertz::mhz(128) && pll1_clk <= Hertz::mhz(544));
assert!(pll1r_clk <= Hertz::mhz(110));
}
VoltageScale::RANGE3 => {
assert!(pll1_clk >= Hertz::mhz(128) && pll1_clk <= Hertz::mhz(330));
assert!(pll1r_clk <= Hertz::mhz(55));
}
VoltageScale::RANGE4 => {
panic!("PLL is unavailable in voltage range 4");
}
}
// § 10.5.4: if we're targeting >= 55 MHz, we must configure PLL1MBOOST to a prescaler
// value that results in an output between 4 and 16 MHz for the PWR EPOD boost
let mboost = if pll1r_clk >= Hertz::mhz(55) {
// source_clk can be up to 50 MHz, so there's just a few cases:
if source_clk > Hertz::mhz(32) {
// Divide by 4, giving EPOD 8-12.5 MHz
Pllmboost::DIV4
} else if source_clk > Hertz::mhz(16) {
// Divide by 2, giving EPOD 8-16 MHz
Pllmboost::DIV2
} else {
// Bypass, giving EPOD 4-16 MHz
Pllmboost::BYPASS
}
} else {
// Nothing to do
Pllmboost::BYPASS
};
// Disable the PLL, and wait for it to disable
RCC.cr().modify(|w| w.set_pllon(0, false)); RCC.cr().modify(|w| w.set_pllon(0, false));
while RCC.cr().read().pllrdy(0) {} while RCC.cr().read().pllrdy(0) {}
let vco = freq * n as u8 as u32; // Configure the PLL
let pll_ck = vco / (div as u8 as u32 + 1);
RCC.pll1cfgr().write(|w| { RCC.pll1cfgr().write(|w| {
w.set_pllm(m.into()); // Configure PLL1 source and prescaler
w.set_pllsrc(src.into()); w.set_pllsrc(pll.source.into());
w.set_pllm(pll.m.into());
// Configure PLL1 input frequncy range
let input_range = if reference_clk <= Hertz::mhz(8) {
Pllrge::FREQ_4TO8MHZ
} else {
Pllrge::FREQ_8TO16MHZ
};
w.set_pllrge(input_range);
// Set the prescaler for PWR EPOD
w.set_pllmboost(mboost);
// Enable PLL1R output
w.set_pllren(true); w.set_pllren(true);
}); });
// Configure the PLL divisors
RCC.pll1divr().modify(|w| { RCC.pll1divr().modify(|w| {
w.set_pllr(div.to_div()); // Set the VCO multiplier
w.set_plln(n.to_mul()); w.set_plln(pll.n.to_mul());
// Set the R output divisor
w.set_pllr(pll.r.to_div());
}); });
// Enable PLL // Do we need the EPOD booster to reach the target clock speed per § 10.5.4?
if pll1r_clk >= Hertz::mhz(55) {
// Enable the booster
PWR.vosr().modify(|w| {
w.set_boosten(true);
});
while !PWR.vosr().read().boostrdy() {}
}
// Enable the PLL
RCC.cr().modify(|w| w.set_pllon(0, true)); RCC.cr().modify(|w| w.set_pllon(0, true));
while !RCC.cr().read().pllrdy(0) {} while !RCC.cr().read().pllrdy(0) {}
pll_ck pll1r_clk
} }
}; }
.0;
if config.hsi48 { if config.hsi48 {
RCC.cr().modify(|w| w.set_hsi48on(true)); RCC.cr().modify(|w| w.set_hsi48on(true));
while !RCC.cr().read().hsi48rdy() {} while !RCC.cr().read().hsi48rdy() {}
} }
// TODO make configurable // The clock source is ready
let power_vos = VoltageScale::RANGE3; // Calculate and set the flash wait states
let wait_states = match config.voltage_range {
// states and programming delay
let wait_states = match power_vos {
// VOS 1 range VCORE 1.26V - 1.40V // VOS 1 range VCORE 1.26V - 1.40V
VoltageScale::RANGE1 => { VoltageScale::RANGE1 => {
if sys_clk < 32_000_000 { if sys_clk < 32_000_000 {
@ -335,21 +498,34 @@ pub(crate) unsafe fn init(config: Config) {
} }
} }
}; };
FLASH.acr().modify(|w| { FLASH.acr().modify(|w| {
w.set_latency(wait_states); w.set_latency(wait_states);
}); });
// Switch the system clock source
RCC.cfgr1().modify(|w| { RCC.cfgr1().modify(|w| {
w.set_sw(config.mux.into()); w.set_sw(config.mux.into());
}); });
// RM0456 § 11.4.9 specifies maximum bus frequencies per voltage range, but the maximum bus
// frequency for each voltage range exactly matches the maximum permitted PLL output frequency.
// Given that:
//
// 1. Any bus frequency can never exceed the system clock frequency;
// 2. We checked the PLL output frequency if we're using it as a system clock;
// 3. The maximum HSE frequencies at each voltage range are lower than the bus limits, and
// we checked the HSE frequency if configured as a system clock; and
// 4. The maximum frequencies from the other clock sources are lower than the lowest bus
// frequency limit
//
// ...then we do not need to perform additional bus-related frequency checks.
// Configure the bus prescalers
RCC.cfgr2().modify(|w| { RCC.cfgr2().modify(|w| {
w.set_hpre(config.ahb_pre.into()); w.set_hpre(config.ahb_pre.into());
w.set_ppre1(config.apb1_pre.into()); w.set_ppre1(config.apb1_pre.into());
w.set_ppre2(config.apb2_pre.into()); w.set_ppre2(config.apb2_pre.into());
}); });
RCC.cfgr3().modify(|w| { RCC.cfgr3().modify(|w| {
w.set_ppre3(config.apb3_pre.into()); w.set_ppre3(config.apb3_pre.into());
}); });

View File

@ -23,7 +23,12 @@ async fn main(_spawner: Spawner) {
info!("Hello World!"); info!("Hello World!");
let mut config = Config::default(); let mut config = Config::default();
config.rcc.mux = ClockSrc::PLL1R(PllSrc::HSI16, PllM::Div2, PllN::Mul10, PllClkDiv::NotDivided); config.rcc.mux = ClockSrc::PLL1R(PllConfig {
source: PllSrc::HSI16,
m: PllM::Div2,
n: PllN::Mul10,
r: PllClkDiv::NotDivided,
});
//config.rcc.mux = ClockSrc::MSI(MSIRange::Range48mhz); //config.rcc.mux = ClockSrc::MSI(MSIRange::Range48mhz);
config.rcc.hsi48 = true; config.rcc.hsi48 = true;