embassy/embassy-stm32/src/rcc/g0.rs

463 lines
12 KiB
Rust
Raw Normal View History

embassy-stm32: g0: add PLL clock source STM32G0 SYSCLK can be sourced from PLLRCLK. Given that the HSI runs at 16 MHz and the HSE range is 4-48 MHz, the PLL is the only way to reach 64 MHz. This commit adds `ClockSrc::PLL`. The PLL sources from either HSI16 or HSE, divides it by `m`, and locks its VCO to multiple `n`. It then divides the VCO by `r`, `p`, and `q` to produce up to three associated clock signals: * PLLRCLK is one of the inputs on the SYSCLK mux. This is the main reason the user will configure the PLL, so `r` is mandatory and the output is enabled unconditionally. * PLLPCLK is available as a clock source for the ADC and I2S peripherals, so `p` is optional and the output is conditional. * PLLQCLK exists only on STM32G0B0xx, and exists only to feed the MCO and MCO2 peripherals, so `q` is optional and the output is conditional. When the user specifies `ClockSrc::PLL(PllConfig)`, `rcc::init()` calls `PllConfig::init()` which initializes the PLL per [RM0454]. It disables the PLL, waits for it to stop, enables the source oscillator, configures the PLL, waits for it to lock, and then enables the appropriate outputs. `rcc::init()` then switches the clock source to PLLRCLK. `rcc::init()` is now also resonsible for calculating and setting flash wait states. SYSCLCK < 24 MHz is fine in the reset state, but 24-48 MHz requires waiting 1 cycle and 48-64 MHz requires waiting 2 cycles. (This was likely a blocker for anyone using HSE >= 24 MHz, with or without the PLL.) Flash accesses are now automatically slowed down as needed before changing the clock source, and sped up as permitted after changing the clock source. The number of flash wait states also determines if flash prefetching will be profitable, so that is now handled automatically too. [RM0454]: https://www.st.com/resource/en/reference_manual/rm0454-stm32g0x0-advanced-armbased-32bit-mcus-stmicroelectronics.pdf
2022-05-28 04:15:38 +02:00
use crate::pac::flash::vals::Latency;
use crate::pac::rcc::vals::{self, Hpre, Hsidiv, Ppre, Sw};
use crate::pac::{FLASH, PWR, RCC};
use crate::rcc::{set_freqs, Clocks};
2022-06-12 22:15:44 +02:00
use crate::time::{Hertz, U32Ext};
2021-07-30 22:48:13 +02:00
/// HSI speed
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
2021-07-30 22:48:13 +02:00
/// LSI speed
pub const LSI_FREQ: Hertz = Hertz(32_000);
2021-07-30 22:48:13 +02:00
/// System clock mux source
#[derive(Clone, Copy)]
pub enum ClockSrc {
HSE(Hertz),
2021-08-31 07:48:22 +02:00
HSI16(HSI16Prescaler),
embassy-stm32: g0: add PLL clock source STM32G0 SYSCLK can be sourced from PLLRCLK. Given that the HSI runs at 16 MHz and the HSE range is 4-48 MHz, the PLL is the only way to reach 64 MHz. This commit adds `ClockSrc::PLL`. The PLL sources from either HSI16 or HSE, divides it by `m`, and locks its VCO to multiple `n`. It then divides the VCO by `r`, `p`, and `q` to produce up to three associated clock signals: * PLLRCLK is one of the inputs on the SYSCLK mux. This is the main reason the user will configure the PLL, so `r` is mandatory and the output is enabled unconditionally. * PLLPCLK is available as a clock source for the ADC and I2S peripherals, so `p` is optional and the output is conditional. * PLLQCLK exists only on STM32G0B0xx, and exists only to feed the MCO and MCO2 peripherals, so `q` is optional and the output is conditional. When the user specifies `ClockSrc::PLL(PllConfig)`, `rcc::init()` calls `PllConfig::init()` which initializes the PLL per [RM0454]. It disables the PLL, waits for it to stop, enables the source oscillator, configures the PLL, waits for it to lock, and then enables the appropriate outputs. `rcc::init()` then switches the clock source to PLLRCLK. `rcc::init()` is now also resonsible for calculating and setting flash wait states. SYSCLCK < 24 MHz is fine in the reset state, but 24-48 MHz requires waiting 1 cycle and 48-64 MHz requires waiting 2 cycles. (This was likely a blocker for anyone using HSE >= 24 MHz, with or without the PLL.) Flash accesses are now automatically slowed down as needed before changing the clock source, and sped up as permitted after changing the clock source. The number of flash wait states also determines if flash prefetching will be profitable, so that is now handled automatically too. [RM0454]: https://www.st.com/resource/en/reference_manual/rm0454-stm32g0x0-advanced-armbased-32bit-mcus-stmicroelectronics.pdf
2022-05-28 04:15:38 +02:00
PLL(PllConfig),
2021-07-30 22:48:13 +02:00
LSI,
}
2021-08-31 07:48:22 +02:00
#[derive(Clone, Copy)]
pub enum HSI16Prescaler {
NotDivided,
Div2,
Div4,
Div8,
Div16,
Div32,
Div64,
Div128,
}
impl Into<Hsidiv> for HSI16Prescaler {
fn into(self) -> Hsidiv {
2021-08-31 07:48:22 +02:00
match self {
HSI16Prescaler::NotDivided => Hsidiv::DIV1,
HSI16Prescaler::Div2 => Hsidiv::DIV2,
HSI16Prescaler::Div4 => Hsidiv::DIV4,
HSI16Prescaler::Div8 => Hsidiv::DIV8,
HSI16Prescaler::Div16 => Hsidiv::DIV16,
HSI16Prescaler::Div32 => Hsidiv::DIV32,
HSI16Prescaler::Div64 => Hsidiv::DIV64,
HSI16Prescaler::Div128 => Hsidiv::DIV128,
2021-08-31 07:48:22 +02:00
}
}
}
embassy-stm32: g0: add PLL clock source STM32G0 SYSCLK can be sourced from PLLRCLK. Given that the HSI runs at 16 MHz and the HSE range is 4-48 MHz, the PLL is the only way to reach 64 MHz. This commit adds `ClockSrc::PLL`. The PLL sources from either HSI16 or HSE, divides it by `m`, and locks its VCO to multiple `n`. It then divides the VCO by `r`, `p`, and `q` to produce up to three associated clock signals: * PLLRCLK is one of the inputs on the SYSCLK mux. This is the main reason the user will configure the PLL, so `r` is mandatory and the output is enabled unconditionally. * PLLPCLK is available as a clock source for the ADC and I2S peripherals, so `p` is optional and the output is conditional. * PLLQCLK exists only on STM32G0B0xx, and exists only to feed the MCO and MCO2 peripherals, so `q` is optional and the output is conditional. When the user specifies `ClockSrc::PLL(PllConfig)`, `rcc::init()` calls `PllConfig::init()` which initializes the PLL per [RM0454]. It disables the PLL, waits for it to stop, enables the source oscillator, configures the PLL, waits for it to lock, and then enables the appropriate outputs. `rcc::init()` then switches the clock source to PLLRCLK. `rcc::init()` is now also resonsible for calculating and setting flash wait states. SYSCLCK < 24 MHz is fine in the reset state, but 24-48 MHz requires waiting 1 cycle and 48-64 MHz requires waiting 2 cycles. (This was likely a blocker for anyone using HSE >= 24 MHz, with or without the PLL.) Flash accesses are now automatically slowed down as needed before changing the clock source, and sped up as permitted after changing the clock source. The number of flash wait states also determines if flash prefetching will be profitable, so that is now handled automatically too. [RM0454]: https://www.st.com/resource/en/reference_manual/rm0454-stm32g0x0-advanced-armbased-32bit-mcus-stmicroelectronics.pdf
2022-05-28 04:15:38 +02:00
/// The PLL configuration.
///
/// * `VCOCLK = source / m * n`
/// * `PLLRCLK = VCOCLK / r`
/// * `PLLQCLK = VCOCLK / q`
/// * `PLLPCLK = VCOCLK / p`
#[derive(Clone, Copy)]
pub struct PllConfig {
/// The source from which the PLL receives a clock signal
pub source: PllSrc,
/// The initial divisor of that clock signal
pub m: Pllm,
/// The PLL VCO multiplier, which must be in the range `8..=86`.
pub n: u8,
/// The final divisor for `PLLRCLK` output which drives the system clock
pub r: Pllr,
/// The divisor for the `PLLQCLK` output, if desired
pub q: Option<Pllr>,
/// The divisor for the `PLLPCLK` output, if desired
pub p: Option<Pllr>,
}
impl Default for PllConfig {
#[inline]
fn default() -> PllConfig {
// HSI16 / 1 * 8 / 2 = 64 MHz
PllConfig {
source: PllSrc::HSI16,
m: Pllm::Div1,
n: 8,
r: Pllr::Div2,
q: None,
p: None,
}
}
}
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum PllSrc {
HSI16,
HSE(Hertz),
}
#[derive(Clone, Copy)]
pub enum Pllm {
Div1,
Div2,
Div3,
Div4,
Div5,
Div6,
Div7,
Div8,
}
impl From<Pllm> for u8 {
fn from(v: Pllm) -> Self {
match v {
Pllm::Div1 => 0b000,
Pllm::Div2 => 0b001,
Pllm::Div3 => 0b010,
Pllm::Div4 => 0b011,
Pllm::Div5 => 0b100,
Pllm::Div6 => 0b101,
Pllm::Div7 => 0b110,
Pllm::Div8 => 0b111,
}
}
}
impl From<Pllm> for u32 {
fn from(v: Pllm) -> Self {
match v {
Pllm::Div1 => 1,
Pllm::Div2 => 2,
Pllm::Div3 => 3,
Pllm::Div4 => 4,
Pllm::Div5 => 5,
Pllm::Div6 => 6,
Pllm::Div7 => 7,
Pllm::Div8 => 8,
}
}
}
#[derive(Clone, Copy)]
pub enum Pllr {
Div2,
Div3,
Div4,
Div5,
Div6,
Div7,
Div8,
}
impl From<Pllr> for u8 {
fn from(v: Pllr) -> Self {
match v {
Pllr::Div2 => 0b000,
Pllr::Div3 => 0b001,
Pllr::Div4 => 0b010,
Pllr::Div5 => 0b011,
Pllr::Div6 => 0b101,
Pllr::Div7 => 0b110,
Pllr::Div8 => 0b111,
}
}
}
impl From<Pllr> for u32 {
fn from(v: Pllr) -> Self {
match v {
Pllr::Div2 => 2,
Pllr::Div3 => 3,
Pllr::Div4 => 4,
Pllr::Div5 => 5,
Pllr::Div6 => 6,
Pllr::Div7 => 7,
Pllr::Div8 => 8,
}
}
}
/// AHB prescaler
#[derive(Clone, Copy, PartialEq)]
pub enum AHBPrescaler {
NotDivided,
Div2,
Div4,
Div8,
Div16,
Div64,
Div128,
Div256,
Div512,
}
/// APB prescaler
#[derive(Clone, Copy)]
pub enum APBPrescaler {
NotDivided,
Div2,
Div4,
Div8,
Div16,
}
impl Into<Ppre> for APBPrescaler {
fn into(self) -> Ppre {
2021-07-30 22:48:13 +02:00
match self {
APBPrescaler::NotDivided => Ppre::DIV1,
APBPrescaler::Div2 => Ppre::DIV2,
APBPrescaler::Div4 => Ppre::DIV4,
APBPrescaler::Div8 => Ppre::DIV8,
APBPrescaler::Div16 => Ppre::DIV16,
2021-07-30 22:48:13 +02:00
}
}
}
impl Into<Hpre> for AHBPrescaler {
fn into(self) -> Hpre {
2021-07-30 22:48:13 +02:00
match self {
AHBPrescaler::NotDivided => Hpre::DIV1,
AHBPrescaler::Div2 => Hpre::DIV2,
AHBPrescaler::Div4 => Hpre::DIV4,
AHBPrescaler::Div8 => Hpre::DIV8,
AHBPrescaler::Div16 => Hpre::DIV16,
AHBPrescaler::Div64 => Hpre::DIV64,
AHBPrescaler::Div128 => Hpre::DIV128,
AHBPrescaler::Div256 => Hpre::DIV256,
AHBPrescaler::Div512 => Hpre::DIV512,
2021-07-30 22:48:13 +02:00
}
}
}
/// Clocks configutation
pub struct Config {
pub mux: ClockSrc,
pub ahb_pre: AHBPrescaler,
pub apb_pre: APBPrescaler,
pub low_power_run: bool,
2021-07-30 22:48:13 +02:00
}
impl Default for Config {
#[inline]
fn default() -> Config {
Config {
2021-08-31 07:48:22 +02:00
mux: ClockSrc::HSI16(HSI16Prescaler::NotDivided),
2021-07-30 22:48:13 +02:00
ahb_pre: AHBPrescaler::NotDivided,
apb_pre: APBPrescaler::NotDivided,
2021-08-31 07:51:49 +02:00
low_power_run: false,
2021-07-30 22:48:13 +02:00
}
}
}
embassy-stm32: g0: add PLL clock source STM32G0 SYSCLK can be sourced from PLLRCLK. Given that the HSI runs at 16 MHz and the HSE range is 4-48 MHz, the PLL is the only way to reach 64 MHz. This commit adds `ClockSrc::PLL`. The PLL sources from either HSI16 or HSE, divides it by `m`, and locks its VCO to multiple `n`. It then divides the VCO by `r`, `p`, and `q` to produce up to three associated clock signals: * PLLRCLK is one of the inputs on the SYSCLK mux. This is the main reason the user will configure the PLL, so `r` is mandatory and the output is enabled unconditionally. * PLLPCLK is available as a clock source for the ADC and I2S peripherals, so `p` is optional and the output is conditional. * PLLQCLK exists only on STM32G0B0xx, and exists only to feed the MCO and MCO2 peripherals, so `q` is optional and the output is conditional. When the user specifies `ClockSrc::PLL(PllConfig)`, `rcc::init()` calls `PllConfig::init()` which initializes the PLL per [RM0454]. It disables the PLL, waits for it to stop, enables the source oscillator, configures the PLL, waits for it to lock, and then enables the appropriate outputs. `rcc::init()` then switches the clock source to PLLRCLK. `rcc::init()` is now also resonsible for calculating and setting flash wait states. SYSCLCK < 24 MHz is fine in the reset state, but 24-48 MHz requires waiting 1 cycle and 48-64 MHz requires waiting 2 cycles. (This was likely a blocker for anyone using HSE >= 24 MHz, with or without the PLL.) Flash accesses are now automatically slowed down as needed before changing the clock source, and sped up as permitted after changing the clock source. The number of flash wait states also determines if flash prefetching will be profitable, so that is now handled automatically too. [RM0454]: https://www.st.com/resource/en/reference_manual/rm0454-stm32g0x0-advanced-armbased-32bit-mcus-stmicroelectronics.pdf
2022-05-28 04:15:38 +02:00
impl PllConfig {
pub(crate) unsafe fn init(self) -> u32 {
assert!(self.n >= 8 && self.n <= 86);
let (src, input_freq) = match self.source {
PllSrc::HSI16 => (vals::Pllsrc::HSI16, HSI_FREQ.0),
embassy-stm32: g0: add PLL clock source STM32G0 SYSCLK can be sourced from PLLRCLK. Given that the HSI runs at 16 MHz and the HSE range is 4-48 MHz, the PLL is the only way to reach 64 MHz. This commit adds `ClockSrc::PLL`. The PLL sources from either HSI16 or HSE, divides it by `m`, and locks its VCO to multiple `n`. It then divides the VCO by `r`, `p`, and `q` to produce up to three associated clock signals: * PLLRCLK is one of the inputs on the SYSCLK mux. This is the main reason the user will configure the PLL, so `r` is mandatory and the output is enabled unconditionally. * PLLPCLK is available as a clock source for the ADC and I2S peripherals, so `p` is optional and the output is conditional. * PLLQCLK exists only on STM32G0B0xx, and exists only to feed the MCO and MCO2 peripherals, so `q` is optional and the output is conditional. When the user specifies `ClockSrc::PLL(PllConfig)`, `rcc::init()` calls `PllConfig::init()` which initializes the PLL per [RM0454]. It disables the PLL, waits for it to stop, enables the source oscillator, configures the PLL, waits for it to lock, and then enables the appropriate outputs. `rcc::init()` then switches the clock source to PLLRCLK. `rcc::init()` is now also resonsible for calculating and setting flash wait states. SYSCLCK < 24 MHz is fine in the reset state, but 24-48 MHz requires waiting 1 cycle and 48-64 MHz requires waiting 2 cycles. (This was likely a blocker for anyone using HSE >= 24 MHz, with or without the PLL.) Flash accesses are now automatically slowed down as needed before changing the clock source, and sped up as permitted after changing the clock source. The number of flash wait states also determines if flash prefetching will be profitable, so that is now handled automatically too. [RM0454]: https://www.st.com/resource/en/reference_manual/rm0454-stm32g0x0-advanced-armbased-32bit-mcus-stmicroelectronics.pdf
2022-05-28 04:15:38 +02:00
PllSrc::HSE(freq) => (vals::Pllsrc::HSE, freq.0),
};
let m_freq = input_freq / u32::from(self.m);
// RM0454 § 5.4.4:
// > Caution: The software must set these bits so that the PLL input frequency after the
// > /M divider is between 2.66 and 16 MHz.
debug_assert!(m_freq >= 2_660_000 && m_freq <= 16_000_000);
let n_freq = m_freq * self.n as u32;
// RM0454 § 5.4.4:
// > Caution: The software must set these bits so that the VCO output frequency is between
// > 64 and 344 MHz.
debug_assert!(n_freq >= 64_000_000 && n_freq <= 344_000_000);
let r_freq = n_freq / u32::from(self.r);
// RM0454 § 5.4.4:
// > Caution: The software must set this bitfield so as not to exceed 64 MHz on this clock.
debug_assert!(r_freq <= 64_000_000);
// RM0454 § 5.2.3:
// > To modify the PLL configuration, proceed as follows:
// > 1. Disable the PLL by setting PLLON to 0 in Clock control register (RCC_CR).
RCC.cr().modify(|w| w.set_pllon(false));
// > 2. Wait until PLLRDY is cleared. The PLL is now fully stopped.
while RCC.cr().read().pllrdy() {}
// > 3. Change the desired parameter.
// Enable whichever clock source we're using, and wait for it to become ready
match self.source {
PllSrc::HSI16 => {
RCC.cr().write(|w| w.set_hsion(true));
while !RCC.cr().read().hsirdy() {}
}
PllSrc::HSE(_) => {
RCC.cr().write(|w| w.set_hseon(true));
while !RCC.cr().read().hserdy() {}
}
}
// Configure PLLSYSCFGR
RCC.pllsyscfgr().modify(|w| {
w.set_pllr(u8::from(self.r));
w.set_pllren(false);
if let Some(q) = self.q {
w.set_pllq(u8::from(q));
}
w.set_pllqen(false);
if let Some(p) = self.p {
w.set_pllp(u8::from(p));
}
w.set_pllpen(false);
w.set_plln(self.n);
w.set_pllm(self.m as u8);
w.set_pllsrc(src)
});
// > 4. Enable the PLL again by setting PLLON to 1.
RCC.cr().modify(|w| w.set_pllon(true));
// Wait for the PLL to become ready
while !RCC.cr().read().pllrdy() {}
// > 5. Enable the desired PLL outputs by configuring PLLPEN, PLLQEN, and PLLREN in PLL
// > configuration register (RCC_PLLCFGR).
RCC.pllsyscfgr().modify(|w| {
// We'll use R for system clock, so enable that unconditionally
w.set_pllren(true);
// We may also use Q or P
w.set_pllqen(self.q.is_some());
w.set_pllpen(self.p.is_some());
});
r_freq
}
}
pub(crate) unsafe fn init(config: Config) {
let (sys_clk, sw) = match config.mux {
ClockSrc::HSI16(div) => {
// Enable HSI16
let div: Hsidiv = div.into();
RCC.cr().write(|w| {
w.set_hsidiv(div);
w.set_hsion(true)
});
while !RCC.cr().read().hsirdy() {}
2021-07-30 22:48:13 +02:00
(HSI_FREQ.0 >> div.0, Sw::HSI)
2021-07-30 22:48:13 +02:00
}
ClockSrc::HSE(freq) => {
// Enable HSE
RCC.cr().write(|w| w.set_hseon(true));
while !RCC.cr().read().hserdy() {}
2021-07-30 22:48:13 +02:00
(freq.0, Sw::HSE)
2021-07-30 22:48:13 +02:00
}
embassy-stm32: g0: add PLL clock source STM32G0 SYSCLK can be sourced from PLLRCLK. Given that the HSI runs at 16 MHz and the HSE range is 4-48 MHz, the PLL is the only way to reach 64 MHz. This commit adds `ClockSrc::PLL`. The PLL sources from either HSI16 or HSE, divides it by `m`, and locks its VCO to multiple `n`. It then divides the VCO by `r`, `p`, and `q` to produce up to three associated clock signals: * PLLRCLK is one of the inputs on the SYSCLK mux. This is the main reason the user will configure the PLL, so `r` is mandatory and the output is enabled unconditionally. * PLLPCLK is available as a clock source for the ADC and I2S peripherals, so `p` is optional and the output is conditional. * PLLQCLK exists only on STM32G0B0xx, and exists only to feed the MCO and MCO2 peripherals, so `q` is optional and the output is conditional. When the user specifies `ClockSrc::PLL(PllConfig)`, `rcc::init()` calls `PllConfig::init()` which initializes the PLL per [RM0454]. It disables the PLL, waits for it to stop, enables the source oscillator, configures the PLL, waits for it to lock, and then enables the appropriate outputs. `rcc::init()` then switches the clock source to PLLRCLK. `rcc::init()` is now also resonsible for calculating and setting flash wait states. SYSCLCK < 24 MHz is fine in the reset state, but 24-48 MHz requires waiting 1 cycle and 48-64 MHz requires waiting 2 cycles. (This was likely a blocker for anyone using HSE >= 24 MHz, with or without the PLL.) Flash accesses are now automatically slowed down as needed before changing the clock source, and sped up as permitted after changing the clock source. The number of flash wait states also determines if flash prefetching will be profitable, so that is now handled automatically too. [RM0454]: https://www.st.com/resource/en/reference_manual/rm0454-stm32g0x0-advanced-armbased-32bit-mcus-stmicroelectronics.pdf
2022-05-28 04:15:38 +02:00
ClockSrc::PLL(pll) => {
let freq = pll.init();
(freq, Sw::PLLRCLK)
}
ClockSrc::LSI => {
// Enable LSI
RCC.csr().write(|w| w.set_lsion(true));
while !RCC.csr().read().lsirdy() {}
(LSI_FREQ, Sw::LSI)
2021-08-31 07:51:49 +02:00
}
};
embassy-stm32: g0: add PLL clock source STM32G0 SYSCLK can be sourced from PLLRCLK. Given that the HSI runs at 16 MHz and the HSE range is 4-48 MHz, the PLL is the only way to reach 64 MHz. This commit adds `ClockSrc::PLL`. The PLL sources from either HSI16 or HSE, divides it by `m`, and locks its VCO to multiple `n`. It then divides the VCO by `r`, `p`, and `q` to produce up to three associated clock signals: * PLLRCLK is one of the inputs on the SYSCLK mux. This is the main reason the user will configure the PLL, so `r` is mandatory and the output is enabled unconditionally. * PLLPCLK is available as a clock source for the ADC and I2S peripherals, so `p` is optional and the output is conditional. * PLLQCLK exists only on STM32G0B0xx, and exists only to feed the MCO and MCO2 peripherals, so `q` is optional and the output is conditional. When the user specifies `ClockSrc::PLL(PllConfig)`, `rcc::init()` calls `PllConfig::init()` which initializes the PLL per [RM0454]. It disables the PLL, waits for it to stop, enables the source oscillator, configures the PLL, waits for it to lock, and then enables the appropriate outputs. `rcc::init()` then switches the clock source to PLLRCLK. `rcc::init()` is now also resonsible for calculating and setting flash wait states. SYSCLCK < 24 MHz is fine in the reset state, but 24-48 MHz requires waiting 1 cycle and 48-64 MHz requires waiting 2 cycles. (This was likely a blocker for anyone using HSE >= 24 MHz, with or without the PLL.) Flash accesses are now automatically slowed down as needed before changing the clock source, and sped up as permitted after changing the clock source. The number of flash wait states also determines if flash prefetching will be profitable, so that is now handled automatically too. [RM0454]: https://www.st.com/resource/en/reference_manual/rm0454-stm32g0x0-advanced-armbased-32bit-mcus-stmicroelectronics.pdf
2022-05-28 04:15:38 +02:00
// Determine the flash latency implied by the target clock speed
// RM0454 § 3.3.4:
let target_flash_latency = if sys_clk <= 24_000_000 {
Latency::WS0
} else if sys_clk <= 48_000_000 {
Latency::WS1
} else {
Latency::WS2
};
// Increase the number of cycles we wait for flash if the new value is higher
// There's no harm in waiting a little too much before the clock change, but we'll
// crash immediately if we don't wait enough after the clock change
let mut set_flash_latency_after = false;
FLASH.acr().modify(|w| {
// Is the current flash latency less than what we need at the new SYSCLK?
if w.latency().0 <= target_flash_latency.0 {
// We must increase the number of wait states now
w.set_latency(target_flash_latency)
} else {
// We may decrease the number of wait states later
set_flash_latency_after = true;
}
// RM0454 § 3.3.5:
// > Prefetch is enabled by setting the PRFTEN bit of the FLASH access control register
// > (FLASH_ACR). This feature is useful if at least one wait state is needed to access the
// > Flash memory.
//
// Enable flash prefetching if we have at least one wait state, and disable it otherwise.
w.set_prften(target_flash_latency.0 > 0);
});
if !set_flash_latency_after {
// Spin until the effective flash latency is compatible with the clock change
while FLASH.acr().read().latency().0 < target_flash_latency.0 {}
}
// Configure SYSCLK source, HCLK divisor, and PCLK divisor all at once
let (sw, hpre, ppre) = (sw.into(), config.ahb_pre.into(), config.apb_pre.into());
RCC.cfgr().modify(|w| {
embassy-stm32: g0: add PLL clock source STM32G0 SYSCLK can be sourced from PLLRCLK. Given that the HSI runs at 16 MHz and the HSE range is 4-48 MHz, the PLL is the only way to reach 64 MHz. This commit adds `ClockSrc::PLL`. The PLL sources from either HSI16 or HSE, divides it by `m`, and locks its VCO to multiple `n`. It then divides the VCO by `r`, `p`, and `q` to produce up to three associated clock signals: * PLLRCLK is one of the inputs on the SYSCLK mux. This is the main reason the user will configure the PLL, so `r` is mandatory and the output is enabled unconditionally. * PLLPCLK is available as a clock source for the ADC and I2S peripherals, so `p` is optional and the output is conditional. * PLLQCLK exists only on STM32G0B0xx, and exists only to feed the MCO and MCO2 peripherals, so `q` is optional and the output is conditional. When the user specifies `ClockSrc::PLL(PllConfig)`, `rcc::init()` calls `PllConfig::init()` which initializes the PLL per [RM0454]. It disables the PLL, waits for it to stop, enables the source oscillator, configures the PLL, waits for it to lock, and then enables the appropriate outputs. `rcc::init()` then switches the clock source to PLLRCLK. `rcc::init()` is now also resonsible for calculating and setting flash wait states. SYSCLCK < 24 MHz is fine in the reset state, but 24-48 MHz requires waiting 1 cycle and 48-64 MHz requires waiting 2 cycles. (This was likely a blocker for anyone using HSE >= 24 MHz, with or without the PLL.) Flash accesses are now automatically slowed down as needed before changing the clock source, and sped up as permitted after changing the clock source. The number of flash wait states also determines if flash prefetching will be profitable, so that is now handled automatically too. [RM0454]: https://www.st.com/resource/en/reference_manual/rm0454-stm32g0x0-advanced-armbased-32bit-mcus-stmicroelectronics.pdf
2022-05-28 04:15:38 +02:00
w.set_sw(sw);
w.set_hpre(hpre);
w.set_ppre(ppre);
});
embassy-stm32: g0: add PLL clock source STM32G0 SYSCLK can be sourced from PLLRCLK. Given that the HSI runs at 16 MHz and the HSE range is 4-48 MHz, the PLL is the only way to reach 64 MHz. This commit adds `ClockSrc::PLL`. The PLL sources from either HSI16 or HSE, divides it by `m`, and locks its VCO to multiple `n`. It then divides the VCO by `r`, `p`, and `q` to produce up to three associated clock signals: * PLLRCLK is one of the inputs on the SYSCLK mux. This is the main reason the user will configure the PLL, so `r` is mandatory and the output is enabled unconditionally. * PLLPCLK is available as a clock source for the ADC and I2S peripherals, so `p` is optional and the output is conditional. * PLLQCLK exists only on STM32G0B0xx, and exists only to feed the MCO and MCO2 peripherals, so `q` is optional and the output is conditional. When the user specifies `ClockSrc::PLL(PllConfig)`, `rcc::init()` calls `PllConfig::init()` which initializes the PLL per [RM0454]. It disables the PLL, waits for it to stop, enables the source oscillator, configures the PLL, waits for it to lock, and then enables the appropriate outputs. `rcc::init()` then switches the clock source to PLLRCLK. `rcc::init()` is now also resonsible for calculating and setting flash wait states. SYSCLCK < 24 MHz is fine in the reset state, but 24-48 MHz requires waiting 1 cycle and 48-64 MHz requires waiting 2 cycles. (This was likely a blocker for anyone using HSE >= 24 MHz, with or without the PLL.) Flash accesses are now automatically slowed down as needed before changing the clock source, and sped up as permitted after changing the clock source. The number of flash wait states also determines if flash prefetching will be profitable, so that is now handled automatically too. [RM0454]: https://www.st.com/resource/en/reference_manual/rm0454-stm32g0x0-advanced-armbased-32bit-mcus-stmicroelectronics.pdf
2022-05-28 04:15:38 +02:00
if set_flash_latency_after {
// We can make the flash require fewer wait states
// Spin until the SYSCLK changes have taken effect
loop {
let cfgr = RCC.cfgr().read();
if cfgr.sw() == sw && cfgr.hpre() == hpre && cfgr.ppre() == ppre {
break;
}
}
// Set the flash latency to require fewer wait states
FLASH.acr().modify(|w| w.set_latency(target_flash_latency));
}
let ahb_div = match config.ahb_pre {
AHBPrescaler::NotDivided => 1,
AHBPrescaler::Div2 => 2,
AHBPrescaler::Div4 => 4,
AHBPrescaler::Div8 => 8,
AHBPrescaler::Div16 => 16,
AHBPrescaler::Div64 => 64,
AHBPrescaler::Div128 => 128,
AHBPrescaler::Div256 => 256,
AHBPrescaler::Div512 => 512,
};
let ahb_freq = sys_clk / ahb_div;
let (apb_freq, apb_tim_freq) = match config.apb_pre {
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
pre => {
let pre: Ppre = pre.into();
let pre: u8 = 1 << (pre.0 - 3);
let freq = ahb_freq / pre as u32;
(freq, freq * 2)
}
};
if config.low_power_run {
assert!(sys_clk.hz() <= 2_000_000.hz());
PWR.cr1().modify(|w| w.set_lpr(true));
2021-07-30 22:48:13 +02:00
}
set_freqs(Clocks {
sys: sys_clk.hz(),
2022-02-14 02:12:06 +01:00
ahb1: ahb_freq.hz(),
apb1: apb_freq.hz(),
apb1_tim: apb_tim_freq.hz(),
});
2021-07-30 22:48:13 +02:00
}