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};
|
2022-01-04 23:58:13 +01:00
|
|
|
use crate::rcc::{set_freqs, Clocks};
|
2021-07-30 22:48:13 +02:00
|
|
|
use crate::time::Hertz;
|
|
|
|
use crate::time::U32Ext;
|
|
|
|
|
|
|
|
/// HSI speed
|
|
|
|
pub const HSI_FREQ: u32 = 16_000_000;
|
|
|
|
|
|
|
|
/// LSI speed
|
|
|
|
pub const LSI_FREQ: u32 = 32_000;
|
|
|
|
|
|
|
|
/// 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,
|
|
|
|
}
|
|
|
|
|
2022-03-03 00:35:53 +01:00
|
|
|
impl Into<Hsidiv> for HSI16Prescaler {
|
|
|
|
fn into(self) -> Hsidiv {
|
2021-08-31 07:48:22 +02:00
|
|
|
match self {
|
2022-03-03 00:35:53 +01:00
|
|
|
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,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-28 16:46:08 +01:00
|
|
|
/// 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,
|
|
|
|
}
|
|
|
|
|
2022-03-03 00:35:53 +01:00
|
|
|
impl Into<Ppre> for APBPrescaler {
|
|
|
|
fn into(self) -> Ppre {
|
2021-07-30 22:48:13 +02:00
|
|
|
match self {
|
2022-03-03 00:35:53 +01:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-03 00:35:53 +01:00
|
|
|
impl Into<Hpre> for AHBPrescaler {
|
|
|
|
fn into(self) -> Hpre {
|
2021-07-30 22:48:13 +02:00
|
|
|
match self {
|
2022-03-03 00:35:53 +01:00
|
|
|
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 {
|
2022-01-04 11:18:59 +01:00
|
|
|
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),
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-04 23:58:13 +01:00
|
|
|
pub(crate) unsafe fn init(config: Config) {
|
|
|
|
let (sys_clk, sw) = match config.mux {
|
|
|
|
ClockSrc::HSI16(div) => {
|
|
|
|
// Enable HSI16
|
2022-03-03 00:35:53 +01:00
|
|
|
let div: Hsidiv = div.into();
|
2022-01-04 23:58:13 +01:00
|
|
|
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
|
|
|
|
2022-03-03 00:35:53 +01:00
|
|
|
(HSI_FREQ >> div.0, Sw::HSI)
|
2021-07-30 22:48:13 +02:00
|
|
|
}
|
2022-01-04 23:58:13 +01: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
|
|
|
|
2022-03-03 00:35:53 +01: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)
|
|
|
|
}
|
2022-01-04 23:58:13 +01:00
|
|
|
ClockSrc::LSI => {
|
|
|
|
// Enable LSI
|
|
|
|
RCC.csr().write(|w| w.set_lsion(true));
|
|
|
|
while !RCC.csr().read().lsirdy() {}
|
2022-03-03 00:35:53 +01:00
|
|
|
(LSI_FREQ, Sw::LSI)
|
2021-08-31 07:51:49 +02:00
|
|
|
}
|
2022-01-04 23:58:13 +01: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());
|
2022-01-04 23:58:13 +01:00
|
|
|
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);
|
2022-01-04 23:58:13 +01: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
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2022-03-03 00:35:53 +01:00
|
|
|
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,
|
2022-01-04 23:58:13 +01:00
|
|
|
};
|
2022-03-03 00:35:53 +01:00
|
|
|
let ahb_freq = sys_clk / ahb_div;
|
2022-01-04 23:58:13 +01:00
|
|
|
|
|
|
|
let (apb_freq, apb_tim_freq) = match config.apb_pre {
|
|
|
|
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
|
|
|
pre => {
|
2022-03-03 00:35:53 +01:00
|
|
|
let pre: Ppre = pre.into();
|
|
|
|
let pre: u8 = 1 << (pre.0 - 3);
|
2022-01-04 23:58:13 +01:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2022-01-04 23:58:13 +01: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(),
|
2022-01-04 23:58:13 +01:00
|
|
|
});
|
2021-07-30 22:48:13 +02:00
|
|
|
}
|