Skip to content

Commit

Permalink
Make freeze() fallible
Browse files Browse the repository at this point in the history
  • Loading branch information
Sh3Rm4n committed Oct 14, 2020
1 parent 54e8261 commit c4c48df
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 50 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

### Breaking changes

- `rcc.cfgr.freeze(...)` is now fallible
- As the clock tree can be configured in many wrong ways,
this change will make it obvious, that the clock is misconfigured.
No more nasty hidden asserts in `rcc`

### Added

- Support for 16-bit words with SPI ([#107](https://github.com/stm32-rs/stm32f3xx-hal/pull/107))
Expand Down
2 changes: 1 addition & 1 deletion examples/adc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ fn main() -> ! {
// Get peripherals, clocks and freeze them
let mut dp = pac::Peripherals::take().unwrap();
let mut rcc = dp.RCC.constrain();
let clocks = rcc.cfgr.freeze(&mut dp.FLASH.constrain().acr);
let clocks = rcc.cfgr.freeze(&mut dp.FLASH.constrain().acr).unwrap();

// set up adc1
let mut adc1 = adc::Adc::adc1(
Expand Down
6 changes: 5 additions & 1 deletion examples/pwm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ fn main() -> ! {
// Configure our clocks
let mut flash = dp.FLASH.constrain();
let mut rcc = dp.RCC.constrain();
let clocks = rcc.cfgr.sysclk(16.mhz()).freeze(&mut flash.acr);
let clocks = rcc
.cfgr
.sysclk(16.mhz())
.freeze(&mut flash.acr)
.unwrap_or_default();

// Prep the pins we need in their correct alternate function
let mut gpioa = dp.GPIOA.split(&mut rcc.ahb);
Expand Down
2 changes: 1 addition & 1 deletion examples/serial_dma.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ fn main() -> ! {

let mut flash = dp.FLASH.constrain();
let mut rcc = dp.RCC.constrain();
let clocks = rcc.cfgr.freeze(&mut flash.acr);
let clocks = rcc.cfgr.freeze(&mut flash.acr).unwrap();

let mut gpioa = dp.GPIOA.split(&mut rcc.ahb);

Expand Down
3 changes: 2 additions & 1 deletion examples/spi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ fn main() -> ! {
.use_hse(8.mhz())
.sysclk(48.mhz())
.pclk1(24.mhz())
.freeze(&mut flash.acr);
.freeze(&mut flash.acr)
.unwrap();

// Configure pins for SPI
let sck = gpioa.pa5.into_af5(&mut gpioa.moder, &mut gpioa.afrl);
Expand Down
3 changes: 2 additions & 1 deletion examples/usb_serial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ fn main() -> ! {
.sysclk(48.mhz())
.pclk1(24.mhz())
.pclk2(24.mhz())
.freeze(&mut flash.acr);
.freeze(&mut flash.acr)
.unwrap();

assert!(clocks.usbclk_valid());

Expand Down
132 changes: 87 additions & 45 deletions src/rcc.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
//! Reset and Clock Control
use core::default;

use crate::pac::{
rcc::{self, cfgr, cfgr2},
RCC,
};

use crate::flash::ACR;
use crate::time::Hertz;
use crate::time::U32Ext;

#[derive(Copy, Clone, Debug)]
pub enum RccError {
PllMaxExceeded,
Pclk1MaxExceeded,
Pclk2MaxExceeded,
SysclkMaxExceeded,
HclkMaxExceeded,
}

/// Extension trait that constrains the `RCC` peripheral
pub trait RccExt {
Expand Down Expand Up @@ -357,7 +369,7 @@ impl CFGR {
feature = "stm32f303xe",
feature = "stm32f398"
)))]
fn calc_pll(&self, sysclk: u32) -> (u32, PllConfig) {
fn calc_pll(&self, sysclk: u32) -> Result<(u32, PllConfig), RccError> {
let pllsrcclk = self.hse.unwrap_or(HSI / 2);
// Get the optimal value for the pll divisor (PLL_DIV) and multiplier (PLL_MUL)
// Only for HSE PLL_DIV can be changed
Expand All @@ -377,22 +389,26 @@ impl CFGR {
divisor *= 2;
}

// PLL_MUL maximal value is 16
assert!(divisor <= 16);
// PRE_DIV maximal value is 16
assert!(multiplier <= 16);
// PLL_MUL and PLLD_DIV maximal values are 16
if multiplier <= 16 || divisor <= 16 {
return Err(RccError::PllMaxExceeded);
}

(multiplier, Some(divisor))
}
// HSI division is always divided by 2 and has no adjustable division
else {
let pll_mul = sysclk / pllsrcclk;
assert!(pll_mul <= 16);
if pll_mul <= 16 {
return Err(RccError::PllMaxExceeded);
}
(pll_mul, None)
};

let sysclk = (pllsrcclk / pll_div.unwrap_or(1)) * pll_mul;
assert!(sysclk <= 72_000_000);
if sysclk <= 72_000_000 {
return Err(RccError::SysclkMaxExceeded);
}

let pll_src = if self.hse.is_some() {
cfgr::PLLSRC_A::HSE_DIV_PREDIV
Expand All @@ -404,14 +420,14 @@ impl CFGR {
let pll_mul_bits = into_pll_mul(pll_mul as u8);
let pll_div_bits = pll_div.map(|pll_div| into_pre_div(pll_div as u8));

(
Ok((
sysclk,
PllConfig {
src: pll_src,
mul: pll_mul_bits,
div: pll_div_bits,
},
)
))
}

/// Calculate the values for the pll multiplier (PLLMUL) and the pll divisor (PLLDIV).
Expand All @@ -423,7 +439,7 @@ impl CFGR {
/// the external oscillator (HSE).
/// After this the system clock frequency (SYSCLK) can be changed via a division and a
/// multiplication block.
/// It can be divided from with values 1..16 and multiplied from 2..16.
/// It can be divided from with values `1..16` and multiplied from `2..16`.
///
/// To determine the optimal values, the greatest common divisor is calculated and the
/// limitations of the possible values are taken into considiration.
Expand All @@ -434,7 +450,7 @@ impl CFGR {
feature = "stm32f303xe",
feature = "stm32f398",
))]
fn calc_pll(&self, sysclk: u32) -> (u32, PllConfig) {
fn calc_pll(&self, sysclk: u32) -> Result<(u32, PllConfig), RccError> {
let pllsrcclk = self.hse.unwrap_or(HSI);

let (pll_mul, pll_div) = {
Expand All @@ -451,17 +467,18 @@ impl CFGR {
divisor *= 2;
}

// PLL_MUL maximal value is 16
assert!(multiplier <= 16);

// PRE_DIV maximal value is 16
assert!(divisor <= 16);
// PLL_MUL and PLLD_DIV maximal values are 16
if multiplier <= 16 || divisor <= 16 {
return Err(RccError::PllMaxExceeded);
}

(multiplier, divisor)
};

let sysclk = (pllsrcclk / pll_div) * pll_mul;
assert!(sysclk <= 72_000_000);
if sysclk <= 72_000_000 {
return Err(RccError::SysclkMaxExceeded);
}

// Select hardware clock source of the PLL
// TODO Check whether HSI_DIV2 could be useful
Expand All @@ -475,25 +492,25 @@ impl CFGR {
let pll_mul_bits = into_pll_mul(pll_mul as u8);
let pll_div_bits = into_pre_div(pll_div as u8);

(
Ok((
sysclk,
PllConfig {
src: pll_src,
mul: pll_mul_bits,
div: Some(pll_div_bits),
},
)
))
}

/// Get the system clock, the system clock source and the pll_options, if needed.
///
/// The system clock source is determined by the chosen system clock and the provided hardware
/// clock.
/// This function does only chose the PLL if needed, otherwise it will use the oscillator clock as system clock.
fn get_sysclk(&self) -> (u32, cfgr::SW_A, Option<PllConfig>) {
fn get_sysclk(&self) -> Result<(u32, cfgr::SW_A, Option<PllConfig>), RccError> {
// If a sysclk is given, check if the PLL has to be used,
// else select the system clock source, which is either HSI or HSE.
match (self.sysclk, self.hse) {
Ok(match (self.sysclk, self.hse) {
// No need to use the PLL
// PLL is needed for USB, but we can make this assumption, to not use PLL here,
// because the two valid USB clocks, 72 Mhz and 48 Mhz, can't be generated
Expand All @@ -503,23 +520,22 @@ impl CFGR {
// No need to use the PLL
(Some(sysclk), None) if sysclk == HSI => (HSI, cfgr::SW_A::HSI, None),
(Some(sysclk), _) => {
let (sysclk, pll_config) = self.calc_pll(sysclk);
let (sysclk, pll_config) = self.calc_pll(sysclk)?;
(sysclk, cfgr::SW_A::PLL, Some(pll_config))
}
// Use HSE as system clock
(None, Some(hse)) => (hse, cfgr::SW_A::HSE, None),
// Use HSI as system clock
(None, None) => (HSI, cfgr::SW_A::HSI, None),
}
})
}

/// Freezes the clock configuration, making it effective
pub fn freeze(self, acr: &mut ACR) -> Clocks {
let (sysclk, sysclk_source, pll_config) = self.get_sysclk();
pub fn freeze(self, acr: &mut ACR) -> Result<Clocks, RccError> {
let (sysclk, sysclk_source, pll_config) = self.get_sysclk()?;

let (hpre_bits, hpre) = self
.hclk
.map(|hclk| match sysclk / hclk {
let (hpre_bits, hpre) = if let Some(hclk) = self.hclk {
match sysclk.checked_div(hclk).ok_or(RccError::HclkMaxExceeded)? {
0 => unreachable!(),
1 => (cfgr::HPRE_A::DIV1, 1),
2 => (cfgr::HPRE_A::DIV2, 2),
Expand All @@ -529,45 +545,56 @@ impl CFGR {
40..=95 => (cfgr::HPRE_A::DIV64, 64),
96..=191 => (cfgr::HPRE_A::DIV128, 128),
192..=383 => (cfgr::HPRE_A::DIV256, 256),
// TODO: Add Error case, if result is to high
_ => (cfgr::HPRE_A::DIV512, 512),
})
.unwrap_or((cfgr::HPRE_A::DIV1, 1));
}
} else {
(cfgr::HPRE_A::DIV1, 1)
};

let hclk: u32 = sysclk / hpre;

assert!(hclk <= 72_000_000);
if hclk <= 72_000_000 {
return Err(RccError::HclkMaxExceeded);
}

let (ppre1_bits, ppre1) = self
.pclk1
.map(|pclk1| match hclk / pclk1 {
let (ppre1_bits, ppre1) = if let Some(pclk1) = self.pclk1 {
match hclk.checked_div(pclk1).ok_or(RccError::Pclk1MaxExceeded)? {
0 => unreachable!(),
1 => (cfgr::PPRE1_A::DIV1, 1),
2 => (cfgr::PPRE1_A::DIV2, 2),
3..=5 => (cfgr::PPRE1_A::DIV4, 4),
6..=11 => (cfgr::PPRE1_A::DIV8, 8),
_ => (cfgr::PPRE1_A::DIV16, 16),
})
.unwrap_or((cfgr::PPRE1_A::DIV1, 1));
}
} else {
(cfgr::PPRE1_A::DIV1, 1)
};

let pclk1 = hclk / u32::from(ppre1);

assert!(pclk1 <= 36_000_000);
if pclk1 <= 36_000_000 {
return Err(RccError::Pclk1MaxExceeded);
}

let (ppre2_bits, ppre2) = self
.pclk2
.map(|pclk2| match hclk / pclk2 {
let (ppre2_bits, ppre2) = if let Some(pclk2) = self.pclk2 {
match hclk.checked_div(pclk2).ok_or(RccError::Pclk2MaxExceeded)? {
0 => unreachable!(),
1 => (cfgr::PPRE2_A::DIV1, 1),
2 => (cfgr::PPRE2_A::DIV2, 2),
3..=5 => (cfgr::PPRE2_A::DIV4, 4),
6..=11 => (cfgr::PPRE2_A::DIV8, 8),
_ => (cfgr::PPRE2_A::DIV16, 16),
})
.unwrap_or((cfgr::PPRE2_A::DIV1, 1));
}
} else {
(cfgr::PPRE2_A::DIV1, 1)
};

let pclk2 = hclk / u32::from(ppre2);

assert!(pclk2 <= 72_000_000);
if pclk2 <= 72_000_000 {
return Err(RccError::Pclk2MaxExceeded);
}

// Adjust flash wait states according to the
// HCLK frequency (cpu core clock)
Expand Down Expand Up @@ -624,15 +651,15 @@ impl CFGR {
.variant(sysclk_source)
});

Clocks {
Ok(Clocks {
hclk: Hertz(hclk),
pclk1: Hertz(pclk1),
pclk2: Hertz(pclk2),
ppre1,
ppre2,
sysclk: Hertz(sysclk),
usbclk_valid,
}
})
}
}

Expand Down Expand Up @@ -686,3 +713,18 @@ impl Clocks {
self.usbclk_valid
}
}

// FIXME: Meh this is a public contructor now :(
impl default::Default for Clocks {
fn default() -> Self {
Self {
hclk: 8.mhz().into(),
pclk1: 8.mhz().into(),
pclk2: 8.mhz().into(),
ppre1: 1,
ppre2: 1,
sysclk: 8.mhz().into(),
usbclk_valid: false,
}
}
}

0 comments on commit c4c48df

Please sign in to comment.