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
This commit is contained in:
Barnaby Walters 2024-02-17 00:30:16 +01:00
parent a24087c36c
commit 6d7458dac7
2 changed files with 33 additions and 35 deletions

View File

@ -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. // 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!) // 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, its necessary to divide if config.boost {
// SYSCLK by 2 using the AHB prescaler, set boost and flash read latency, switch system frequency, wait 1us and // RM0440 p235
// reconfigure the AHB prescaler as desired. Unclear whether this is always necessary. // “The sequence to switch from Range1 normal mode to Range1 boost mode is:
PWR.cr5().modify(|w| w.set_r1mode(!config.boost)); // 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) // Configure flash read access latency based on boost mode and frequency (RM0440 p98)
FLASH.acr().modify(|w| { 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, ..=34_000_000) => Latency::WS0,
(true, ..=68_000_000) => Latency::WS1, (true, ..=68_000_000) => Latency::WS1,
(true, ..=102_000_000) => Latency::WS2, (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 // Now that boost mode and flash read access latency are configured, set up SYSCLK
RCC.cfgr().modify(|w| { RCC.cfgr().modify(|w| {
w.set_sw(sw); w.set_sw(sw);
@ -275,30 +288,16 @@ pub(crate) unsafe fn init(config: Config) {
w.set_ppre2(config.apb2_pre); w.set_ppre2(config.apb2_pre);
}); });
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre { let (apb1_freq, apb1_tim_freq) = super::util::calc_pclk(hclk, config.apb1_pre);
APBPrescaler::DIV1 => (ahb_freq, ahb_freq), let (apb2_freq, apb2_tim_freq) = super::util::calc_pclk(hclk, config.apb2_pre);
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)
}
};
// Configure the 48MHz clock source for USB and RNG peripherals. // Configure the 48MHz clock source for USB and RNG peripherals.
{ RCC.ccipr().modify(|w| {
let source = match config.clk48_src { w.set_clk48sel(match config.clk48_src {
Clk48Src::PLL1_Q => { Clk48Src::PLL1_Q => {
// Make sure the PLLQ is enabled and running at 48Mhz // Not checking that PLL1_Q is 48MHz here so as not to require the user to have a 48MHz clock.
let pllq_freq = pll_freq.as_ref().and_then(|f| f.pll_q); // Peripherals which require one (USB, RNG) should check that theyre driven by a valid 48MHz
assert!(pllq_freq.is_some() && pllq_freq.unwrap().0 == 48_000_000); // clock at init.
crate::pac::rcc::vals::Clk48sel::PLL1_Q crate::pac::rcc::vals::Clk48sel::PLL1_Q
} }
Clk48Src::HSI48 => { Clk48Src::HSI48 => {
@ -307,10 +306,8 @@ pub(crate) unsafe fn init(config: Config) {
crate::pac::rcc::vals::Clk48sel::HSI48 crate::pac::rcc::vals::Clk48sel::HSI48
} }
_ => unreachable!(), _ => 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_adc12sel(config.adc12_clock_source));
RCC.ccipr().modify(|w| w.set_adc345sel(config.adc345_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!( set_clocks!(
sys: Some(sys_clk), sys: Some(sys_clk),
hclk1: Some(ahb_freq), hclk1: Some(hclk),
hclk2: Some(ahb_freq), hclk2: Some(hclk),
hclk3: Some(ahb_freq), hclk3: Some(hclk),
pclk1: Some(apb1_freq), pclk1: Some(apb1_freq),
pclk1_tim: Some(apb1_tim_freq), pclk1_tim: Some(apb1_tim_freq),
pclk2: Some(apb2_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 { mod max {
use core::ops::RangeInclusive; use core::ops::RangeInclusive;

View File

@ -44,6 +44,7 @@ async fn main(_spawner: Spawner) {
}); });
config.rcc.sys = Sysclk::PLL1_R; config.rcc.sys = Sysclk::PLL1_R;
config.rcc.boost = true; // BOOST!
if USE_HSI48 { if USE_HSI48 {
// Sets up the Clock Recovery System (CRS) to use the USB SOF to trim the HSI48 oscillator. // Sets up the Clock Recovery System (CRS) to use the USB SOF to trim the HSI48 oscillator.