From 6d7458dac7e768425342910e04a75c85e667cb82 Mon Sep 17 00:00:00 2001 From: Barnaby Walters Date: Sat, 17 Feb 2024 00:30:16 +0100 Subject: [PATCH] Refinements * Implemented boost mode dance (RM0440 p234-245, 6.5.1) * Enabled boost mode in usb_serial example, tested on hardware * Removed hard requirement of a valid 48MHz source (HSI48 is checked if requested, PLL passed through as-is and assumed to be valid) * Used calc_pclk to calculate APB frequencies * Refactored 48MHz configuration code to remove unnecessary let and block * Renamed ahb_freq to hclk for clarity and consistency --- embassy-stm32/src/rcc/g4.rs | 67 ++++++++++++-------------- examples/stm32g4/src/bin/usb_serial.rs | 1 + 2 files changed, 33 insertions(+), 35 deletions(-) diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index 0c1a1e4b1..382ffbead 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -242,17 +242,25 @@ pub(crate) unsafe fn init(config: Config) { }; // Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency. - let ahb_freq = sys_clk / config.ahb_pre; + let hclk = sys_clk / config.ahb_pre; // Configure Core Boost mode ([RM0440] p234 – inverted because setting r1mode to 0 enables boost mode!) - // TODO: according to RM0440 p235, when switching from range1-normal to range1-boost, it’s necessary to divide - // SYSCLK by 2 using the AHB prescaler, set boost and flash read latency, switch system frequency, wait 1us and - // reconfigure the AHB prescaler as desired. Unclear whether this is always necessary. - PWR.cr5().modify(|w| w.set_r1mode(!config.boost)); + if config.boost { + // RM0440 p235 + // “The sequence to switch from Range1 normal mode to Range1 boost mode is: + // 1. The system clock must be divided by 2 using the AHB prescaler before switching to a higher system frequency. + RCC.cfgr().modify(|w| w.set_hpre(AHBPrescaler::DIV2)); + // 2. Clear the R1MODE bit in the PWR_CR5 register. (enables boost mode) + PWR.cr5().modify(|w| w.set_r1mode(false)); + + // Below: + // 3. Adjust wait states according to new freq target + // 4. Configure and switch to new frequency + } // Configure flash read access latency based on boost mode and frequency (RM0440 p98) FLASH.acr().modify(|w| { - w.set_latency(match (config.boost, ahb_freq.0) { + w.set_latency(match (config.boost, hclk.0) { (true, ..=34_000_000) => Latency::WS0, (true, ..=68_000_000) => Latency::WS1, (true, ..=102_000_000) => Latency::WS2, @@ -267,6 +275,11 @@ pub(crate) unsafe fn init(config: Config) { }) }); + if config.boost { + // 5. Wait for at least 1us and then reconfigure the AHB prescaler to get the needed HCLK clock frequency. + cortex_m::asm::delay(16); + } + // Now that boost mode and flash read access latency are configured, set up SYSCLK RCC.cfgr().modify(|w| { w.set_sw(sw); @@ -275,30 +288,16 @@ pub(crate) unsafe fn init(config: Config) { w.set_ppre2(config.apb2_pre); }); - let (apb1_freq, apb1_tim_freq) = match config.apb1_pre { - APBPrescaler::DIV1 => (ahb_freq, ahb_freq), - pre => { - let freq = ahb_freq / pre; - (freq, freq * 2u32) - } - }; - - let (apb2_freq, apb2_tim_freq) = match config.apb2_pre { - APBPrescaler::DIV1 => (ahb_freq, ahb_freq), - pre => { - let freq = ahb_freq / pre; - (freq, freq * 2u32) - } - }; + let (apb1_freq, apb1_tim_freq) = super::util::calc_pclk(hclk, config.apb1_pre); + let (apb2_freq, apb2_tim_freq) = super::util::calc_pclk(hclk, config.apb2_pre); // Configure the 48MHz clock source for USB and RNG peripherals. - { - let source = match config.clk48_src { + RCC.ccipr().modify(|w| { + w.set_clk48sel(match config.clk48_src { Clk48Src::PLL1_Q => { - // Make sure the PLLQ is enabled and running at 48Mhz - let pllq_freq = pll_freq.as_ref().and_then(|f| f.pll_q); - assert!(pllq_freq.is_some() && pllq_freq.unwrap().0 == 48_000_000); - + // Not checking that PLL1_Q is 48MHz here so as not to require the user to have a 48MHz clock. + // Peripherals which require one (USB, RNG) should check that they‘re driven by a valid 48MHz + // clock at init. crate::pac::rcc::vals::Clk48sel::PLL1_Q } Clk48Src::HSI48 => { @@ -307,10 +306,8 @@ pub(crate) unsafe fn init(config: Config) { crate::pac::rcc::vals::Clk48sel::HSI48 } _ => unreachable!(), - }; - - RCC.ccipr().modify(|w| w.set_clk48sel(source)); - } + }) + }); RCC.ccipr().modify(|w| w.set_adc12sel(config.adc12_clock_source)); RCC.ccipr().modify(|w| w.set_adc345sel(config.adc345_clock_source)); @@ -339,9 +336,9 @@ pub(crate) unsafe fn init(config: Config) { set_clocks!( sys: Some(sys_clk), - hclk1: Some(ahb_freq), - hclk2: Some(ahb_freq), - hclk3: Some(ahb_freq), + hclk1: Some(hclk), + hclk2: Some(hclk), + hclk3: Some(hclk), pclk1: Some(apb1_freq), pclk1_tim: Some(apb1_tim_freq), pclk2: Some(apb2_freq), @@ -355,7 +352,7 @@ pub(crate) unsafe fn init(config: Config) { ); } -// TODO: if necessary, make more of these gated behind cfg attrs +// TODO: if necessary, make more of these, gated behind cfg attrs mod max { use core::ops::RangeInclusive; diff --git a/examples/stm32g4/src/bin/usb_serial.rs b/examples/stm32g4/src/bin/usb_serial.rs index 353ac1799..989fef5b0 100644 --- a/examples/stm32g4/src/bin/usb_serial.rs +++ b/examples/stm32g4/src/bin/usb_serial.rs @@ -44,6 +44,7 @@ async fn main(_spawner: Spawner) { }); config.rcc.sys = Sysclk::PLL1_R; + config.rcc.boost = true; // BOOST! if USE_HSI48 { // Sets up the Clock Recovery System (CRS) to use the USB SOF to trim the HSI48 oscillator.