From 2f269f32560ab178f62d08730909745f49b8eaef Mon Sep 17 00:00:00 2001 From: Carl St-Laurent Date: Sat, 3 Jun 2023 22:05:24 -0400 Subject: [PATCH 1/7] stm32/rcc: Implement basic PLL support for STM32G4 series --- embassy-stm32/src/rcc/g4.rs | 163 +++++++++++++++++++++++++++++++++++- 1 file changed, 162 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index 7e748c7b5..13dced73d 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -1,4 +1,4 @@ -use stm32_metapac::rcc::vals::{Hpre, Ppre, Sw}; +use stm32_metapac::rcc::vals::{Hpre, Pllsrc, Ppre, Sw}; use crate::pac::{PWR, RCC}; use crate::rcc::{set_freqs, Clocks}; @@ -15,6 +15,7 @@ pub const LSI_FREQ: Hertz = Hertz(32_000); pub enum ClockSrc { HSE(Hertz), HSI16, + PLL(PllSrc, PllM, PllN, PllClkDiv), } /// AHB prescaler @@ -41,6 +42,128 @@ pub enum APBPrescaler { Div16, } +/// PLL clock input source +#[derive(Clone, Copy, Debug)] +pub enum PllSrc { + HSI16, + HSE(Hertz), +} + +impl Into for PllSrc { + fn into(self) -> Pllsrc { + match self { + PllSrc::HSE(..) => Pllsrc::HSE, + PllSrc::HSI16 => Pllsrc::HSI16, + } + } +} + +#[derive(Clone, Copy)] +pub enum PllClkDiv { + Div2, + Div4, + Div6, + Div8, +} + +impl PllClkDiv { + pub fn to_div(self) -> u32 { + let val: u8 = self.into(); + (val as u32 + 1) * 2 + } +} + +impl From for u8 { + fn from(val: PllClkDiv) -> u8 { + match val { + PllClkDiv::Div2 => 0b00, + PllClkDiv::Div4 => 0b01, + PllClkDiv::Div6 => 0b10, + PllClkDiv::Div8 => 0b11, + } + } +} + +seq_macro::seq!(N in 8..=127 { + #[derive(Clone, Copy)] + pub enum PllN { + #( + Mul~N, + )* + } + + impl From for u8 { + fn from(val: PllN) -> u8 { + match val { + #( + PllN::Mul~N => N, + )* + } + } + } + + impl PllN { + pub fn to_mul(self) -> u32 { + match self { + #( + PllN::Mul~N => N, + )* + } + } + } +}); + +// Pre-division +#[derive(Copy, Clone)] +pub enum PllM { + Div1, + Div2, + Div3, + Div4, + Div5, + Div6, + Div7, + Div8, + Div9, + Div10, + Div11, + Div12, + Div13, + Div14, + Div15, + Div16, +} + +impl PllM { + pub fn to_div(self) -> u32 { + let val: u8 = self.into(); + val as u32 + 1 + } +} + +impl From for u8 { + fn from(val: PllM) -> u8 { + match val { + PllM::Div1 => 0b0000, + PllM::Div2 => 0b0001, + PllM::Div3 => 0b0010, + PllM::Div4 => 0b0011, + PllM::Div5 => 0b0100, + PllM::Div6 => 0b0101, + PllM::Div7 => 0b0110, + PllM::Div8 => 0b0111, + PllM::Div9 => 0b1000, + PllM::Div10 => 0b1001, + PllM::Div11 => 0b1010, + PllM::Div12 => 0b1011, + PllM::Div13 => 0b1100, + PllM::Div14 => 0b1101, + PllM::Div15 => 0b1110, + PllM::Div16 => 0b1111, + } + } +} + impl AHBPrescaler { const fn div(self) -> u32 { match self { @@ -135,6 +258,44 @@ pub(crate) unsafe fn init(config: Config) { (freq.0, Sw::HSE) } + ClockSrc::PLL(src, prediv, mul, div) => { + let src_freq = match src { + PllSrc::HSI16 => { + // Enable HSI16 + RCC.cr().write(|w| w.set_hsion(true)); + while !RCC.cr().read().hsirdy() {} + + HSI_FREQ.0 + } + PllSrc::HSE(freq) => { + // Enable HSE + RCC.cr().write(|w| w.set_hseon(true)); + while !RCC.cr().read().hserdy() {} + + freq.0 + } + }; + + // Disable PLL + RCC.cr().modify(|w| w.set_pllon(false)); + while RCC.cr().read().pllrdy() {} + + let freq = src_freq / prediv.to_div() * mul.to_mul() / div.to_div(); + assert!(freq <= 170_000_000); + + RCC.pllcfgr().write(move |w| { + w.set_plln(mul.into()); + w.set_pllm(prediv.into()); + w.set_pllr(div.into()); + w.set_pllsrc(src.into()); + }); + + RCC.cr().modify(|w| w.set_pllon(true)); + while !RCC.cr().read().pllrdy() {} + RCC.pllcfgr().modify(|w| w.set_pllren(true)); + + (freq, Sw::PLLRCLK) + } }; RCC.cfgr().modify(|w| { From 675499449fa3e348d27aebc5aae2b7f736648609 Mon Sep 17 00:00:00 2001 From: Carl St-Laurent Date: Sat, 3 Jun 2023 22:10:43 -0400 Subject: [PATCH 2/7] Example using PLL --- examples/stm32g4/src/bin/pll.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 examples/stm32g4/src/bin/pll.rs diff --git a/examples/stm32g4/src/bin/pll.rs b/examples/stm32g4/src/bin/pll.rs new file mode 100644 index 000000000..5dcdf4e53 --- /dev/null +++ b/examples/stm32g4/src/bin/pll.rs @@ -0,0 +1,27 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::rcc::{ClockSrc, PllClkDiv, PllM, PllN, PllSrc}; +use embassy_stm32::time::mhz; +use embassy_stm32::Config; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = Config::default(); + + // Configure PLL to max frequency of 170 MHz + config.rcc.mux = ClockSrc::PLL(PllSrc::HSE(mhz(16)), PllM::Div4, PllN::Mul85, PllClkDiv::Div2); + + let _p = embassy_stm32::init(config); + info!("Hello World!"); + + loop { + Timer::after(Duration::from_millis(1000)).await; + info!("1s elapsed"); + } +} From 6fe853a7d353edcc8b0fba8773915c4269599179 Mon Sep 17 00:00:00 2001 From: Carl St-Laurent Date: Sun, 4 Jun 2023 10:58:44 -0400 Subject: [PATCH 3/7] Better comments --- embassy-stm32/src/rcc/g4.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index 13dced73d..7d061192b 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -261,14 +261,14 @@ pub(crate) unsafe fn init(config: Config) { ClockSrc::PLL(src, prediv, mul, div) => { let src_freq = match src { PllSrc::HSI16 => { - // Enable HSI16 + // Enable HSI16 as clock source for PLL RCC.cr().write(|w| w.set_hsion(true)); while !RCC.cr().read().hsirdy() {} HSI_FREQ.0 } PllSrc::HSE(freq) => { - // Enable HSE + // Enable HSE as clock source for PLL RCC.cr().write(|w| w.set_hseon(true)); while !RCC.cr().read().hserdy() {} @@ -276,7 +276,7 @@ pub(crate) unsafe fn init(config: Config) { } }; - // Disable PLL + // Make sure PLL is disabled while we configure it RCC.cr().modify(|w| w.set_pllon(false)); while RCC.cr().read().pllrdy() {} @@ -290,6 +290,7 @@ pub(crate) unsafe fn init(config: Config) { w.set_pllsrc(src.into()); }); + // Enable PLL RCC.cr().modify(|w| w.set_pllon(true)); while !RCC.cr().read().pllrdy() {} RCC.pllcfgr().modify(|w| w.set_pllren(true)); From e83762e9790d79f0716af5b3f1fcfd730f6fab35 Mon Sep 17 00:00:00 2001 From: Carl St-Laurent Date: Sun, 4 Jun 2023 11:05:13 -0400 Subject: [PATCH 4/7] Use HSI16 for exemple since HSE might have a different value depending on board --- examples/stm32g4/src/bin/pll.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/stm32g4/src/bin/pll.rs b/examples/stm32g4/src/bin/pll.rs index 5dcdf4e53..580afe03d 100644 --- a/examples/stm32g4/src/bin/pll.rs +++ b/examples/stm32g4/src/bin/pll.rs @@ -5,7 +5,6 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::rcc::{ClockSrc, PllClkDiv, PllM, PllN, PllSrc}; -use embassy_stm32::time::mhz; use embassy_stm32::Config; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -14,8 +13,8 @@ use {defmt_rtt as _, panic_probe as _}; async fn main(_spawner: Spawner) { let mut config = Config::default(); - // Configure PLL to max frequency of 170 MHz - config.rcc.mux = ClockSrc::PLL(PllSrc::HSE(mhz(16)), PllM::Div4, PllN::Mul85, PllClkDiv::Div2); + // Configure PLL to 128Mhz frequency + config.rcc.mux = ClockSrc::PLL(PllSrc::HSI16, PllM::Div4, PllN::Mul64, PllClkDiv::Div2); let _p = embassy_stm32::init(config); info!("Hello World!"); From ade46489f190df685b5a81e0ccc267efc05b2de6 Mon Sep 17 00:00:00 2001 From: Carl St-Laurent Date: Sun, 4 Jun 2023 11:57:42 -0400 Subject: [PATCH 5/7] Added Vcore boost mode and Flash wait state --- embassy-stm32/src/rcc/g4.rs | 35 +++++++++++++++++++++++++++++++++ examples/stm32g4/src/bin/pll.rs | 4 ++-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index 7d061192b..6b1206c6a 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -1,4 +1,6 @@ +use stm32_metapac::flash::vals::Latency; use stm32_metapac::rcc::vals::{Hpre, Pllsrc, Ppre, Sw}; +use stm32_metapac::FLASH; use crate::pac::{PWR, RCC}; use crate::rcc::{set_freqs, Clocks}; @@ -283,6 +285,39 @@ pub(crate) unsafe fn init(config: Config) { let freq = src_freq / prediv.to_div() * mul.to_mul() / div.to_div(); assert!(freq <= 170_000_000); + if freq >= 150_000_000 { + // Enable Core Boost mode ([RM0440] p234) + PWR.cr5() + .modify(|w: &mut stm32_metapac::pwr::regs::Cr5| w.set_r1mode(false)); + // Set flash wait state in boost mode based on frequency ([RM0440] p191) + if freq <= 36_000_000 { + FLASH.acr().modify(|w| w.set_latency(Latency::WS0)); + } else if freq <= 68_000_000 { + FLASH.acr().modify(|w| w.set_latency(Latency::WS1)); + } else if freq <= 102_000_000 { + FLASH.acr().modify(|w| w.set_latency(Latency::WS2)); + } else if freq <= 136_000_000 { + FLASH.acr().modify(|w| w.set_latency(Latency::WS3)); + } else { + FLASH.acr().modify(|w| w.set_latency(Latency::WS4)); + } + } else { + PWR.cr5() + .modify(|w: &mut stm32_metapac::pwr::regs::Cr5| w.set_r1mode(true)); + // Set flash wait state in normal mode based on frequency ([RM0440] p191) + if freq <= 30_000_000 { + FLASH.acr().modify(|w| w.set_latency(Latency::WS0)); + } else if freq <= 60_000_000 { + FLASH.acr().modify(|w| w.set_latency(Latency::WS1)); + } else if freq <= 80_000_000 { + FLASH.acr().modify(|w| w.set_latency(Latency::WS2)); + } else if freq <= 120_000_000 { + FLASH.acr().modify(|w| w.set_latency(Latency::WS3)); + } else { + FLASH.acr().modify(|w| w.set_latency(Latency::WS4)); + } + } + RCC.pllcfgr().write(move |w| { w.set_plln(mul.into()); w.set_pllm(prediv.into()); diff --git a/examples/stm32g4/src/bin/pll.rs b/examples/stm32g4/src/bin/pll.rs index 580afe03d..bde30c284 100644 --- a/examples/stm32g4/src/bin/pll.rs +++ b/examples/stm32g4/src/bin/pll.rs @@ -13,8 +13,8 @@ use {defmt_rtt as _, panic_probe as _}; async fn main(_spawner: Spawner) { let mut config = Config::default(); - // Configure PLL to 128Mhz frequency - config.rcc.mux = ClockSrc::PLL(PllSrc::HSI16, PllM::Div4, PllN::Mul64, PllClkDiv::Div2); + // Configure PLL to max frequency of 170 MHz + config.rcc.mux = ClockSrc::PLL(PllSrc::HSI16, PllM::Div4, PllN::Mul85, PllClkDiv::Div2); let _p = embassy_stm32::init(config); info!("Hello World!"); From 4185c10bf83b96f015ef8d861b37555fff241061 Mon Sep 17 00:00:00 2001 From: Carl St-Laurent Date: Sun, 4 Jun 2023 12:09:03 -0400 Subject: [PATCH 6/7] Cleanup --- embassy-stm32/src/rcc/g4.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index 6b1206c6a..3ba9e7eb0 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -286,9 +286,8 @@ pub(crate) unsafe fn init(config: Config) { assert!(freq <= 170_000_000); if freq >= 150_000_000 { - // Enable Core Boost mode ([RM0440] p234) - PWR.cr5() - .modify(|w: &mut stm32_metapac::pwr::regs::Cr5| w.set_r1mode(false)); + // Enable Core Boost mode on freq >= 150Mhz ([RM0440] p234) + PWR.cr5().modify(|w| w.set_r1mode(false)); // Set flash wait state in boost mode based on frequency ([RM0440] p191) if freq <= 36_000_000 { FLASH.acr().modify(|w| w.set_latency(Latency::WS0)); @@ -302,8 +301,7 @@ pub(crate) unsafe fn init(config: Config) { FLASH.acr().modify(|w| w.set_latency(Latency::WS4)); } } else { - PWR.cr5() - .modify(|w: &mut stm32_metapac::pwr::regs::Cr5| w.set_r1mode(true)); + PWR.cr5().modify(|w| w.set_r1mode(true)); // Set flash wait state in normal mode based on frequency ([RM0440] p191) if freq <= 30_000_000 { FLASH.acr().modify(|w| w.set_latency(Latency::WS0)); From 8ddeaddc674871db2125a7462c5b18eef938f497 Mon Sep 17 00:00:00 2001 From: Carl St-Laurent Date: Thu, 8 Jun 2023 20:46:48 -0400 Subject: [PATCH 7/7] Rename to follow ref manual and CubeIDE --- embassy-stm32/src/rcc/g4.rs | 20 ++++++++++---------- examples/stm32g4/src/bin/pll.rs | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index 3ba9e7eb0..2b52416b2 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -17,7 +17,7 @@ pub const LSI_FREQ: Hertz = Hertz(32_000); pub enum ClockSrc { HSE(Hertz), HSI16, - PLL(PllSrc, PllM, PllN, PllClkDiv), + PLLCLK(PllSrc, PllM, PllN, PllR), } /// AHB prescaler @@ -61,27 +61,27 @@ impl Into for PllSrc { } #[derive(Clone, Copy)] -pub enum PllClkDiv { +pub enum PllR { Div2, Div4, Div6, Div8, } -impl PllClkDiv { +impl PllR { pub fn to_div(self) -> u32 { let val: u8 = self.into(); (val as u32 + 1) * 2 } } -impl From for u8 { - fn from(val: PllClkDiv) -> u8 { +impl From for u8 { + fn from(val: PllR) -> u8 { match val { - PllClkDiv::Div2 => 0b00, - PllClkDiv::Div4 => 0b01, - PllClkDiv::Div6 => 0b10, - PllClkDiv::Div8 => 0b11, + PllR::Div2 => 0b00, + PllR::Div4 => 0b01, + PllR::Div6 => 0b10, + PllR::Div8 => 0b11, } } } @@ -260,7 +260,7 @@ pub(crate) unsafe fn init(config: Config) { (freq.0, Sw::HSE) } - ClockSrc::PLL(src, prediv, mul, div) => { + ClockSrc::PLLCLK(src, prediv, mul, div) => { let src_freq = match src { PllSrc::HSI16 => { // Enable HSI16 as clock source for PLL diff --git a/examples/stm32g4/src/bin/pll.rs b/examples/stm32g4/src/bin/pll.rs index bde30c284..8cee41e9b 100644 --- a/examples/stm32g4/src/bin/pll.rs +++ b/examples/stm32g4/src/bin/pll.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::rcc::{ClockSrc, PllClkDiv, PllM, PllN, PllSrc}; +use embassy_stm32::rcc::{ClockSrc, PllM, PllN, PllR, PllSrc}; use embassy_stm32::Config; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { let mut config = Config::default(); // Configure PLL to max frequency of 170 MHz - config.rcc.mux = ClockSrc::PLL(PllSrc::HSI16, PllM::Div4, PllN::Mul85, PllClkDiv::Div2); + config.rcc.mux = ClockSrc::PLLCLK(PllSrc::HSI16, PllM::Div4, PllN::Mul85, PllR::Div2); let _p = embassy_stm32::init(config); info!("Hello World!");