aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThales Fragoso <[email protected]>2021-07-27 23:09:48 -0300
committerThales Fragoso <[email protected]>2021-07-29 18:43:13 -0300
commit5abaf8e9d6a673bbbf7e900b15a77d6e7d1a2962 (patch)
tree0d67073f748e34c29f560ba6a06be4c2112a16e5
parent1ed65cb9e0f495ede0976f83f87b4a987478d466 (diff)
Start working on the F4 PLL
-rw-r--r--embassy-stm32/src/rcc/f4/mod.rs418
-rw-r--r--embassy-stm32/src/rcc/mod.rs3
2 files changed, 263 insertions, 158 deletions
diff --git a/embassy-stm32/src/rcc/f4/mod.rs b/embassy-stm32/src/rcc/f4/mod.rs
index d47510da7..de330d36d 100644
--- a/embassy-stm32/src/rcc/f4/mod.rs
+++ b/embassy-stm32/src/rcc/f4/mod.rs
@@ -1,115 +1,186 @@
1pub use super::types::*; 1use crate::pac::{FLASH, RCC};
2use crate::pac; 2use crate::peripherals;
3use crate::peripherals::{self, RCC};
4use crate::rcc::{get_freqs, set_freqs, Clocks}; 3use crate::rcc::{get_freqs, set_freqs, Clocks};
5use crate::time::Hertz; 4use crate::time::Hertz;
6use crate::time::U32Ext;
7use core::marker::PhantomData; 5use core::marker::PhantomData;
8use embassy::util::Unborrow; 6use embassy::util::Unborrow;
9use embassy_hal_common::unborrow;
10use pac::rcc::vals::{Hpre, Ppre, Sw};
11 7
12/// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC, 8const HSI: u32 = 16_000_000;
13/// and with the addition of the init function to configure a system clock.
14 9
15/// Only the basic setup using the HSE and HSI clocks are supported as of now. 10// TODO: This is for the F401, find a way to make it compile time configurable
11const SYSCLK_MIN: u32 = 24_000_000;
12const SYSCLK_MAX: u32 = 84_000_000;
13const PCLK2_MAX: u32 = SYSCLK_MAX;
14const PCLK1_MAX: u32 = PCLK2_MAX / 2;
16 15
17/// HSI speed 16/// Clocks configutation
18pub const HSI_FREQ: u32 = 16_000_000; 17#[non_exhaustive]
18#[derive(Default)]
19pub struct Config {
20 pub hse: Option<Hertz>,
21 pub bypass_hse: bool,
22 pub pll48: bool,
23 pub sys_ck: Option<Hertz>,
24 pub hclk: Option<Hertz>,
25 pub pclk1: Option<Hertz>,
26 pub pclk2: Option<Hertz>,
27}
19 28
20/// System clock mux source 29/// RCC peripheral
21#[derive(Clone, Copy)] 30pub struct Rcc<'d> {
22pub enum ClockSrc { 31 config: Config,
23 HSE(Hertz), 32 phantom: PhantomData<&'d mut peripherals::RCC>,
24 HSI16,
25} 33}
26 34
27impl Into<Ppre> for APBPrescaler { 35impl<'d> Rcc<'d> {
28 fn into(self) -> Ppre { 36 pub fn new(_rcc: impl Unborrow<Target = peripherals::RCC> + 'd, config: Config) -> Self {
29 match self { 37 Self {
30 APBPrescaler::NotDivided => Ppre::DIV1, 38 config,
31 APBPrescaler::Div2 => Ppre::DIV2, 39 phantom: PhantomData,
32 APBPrescaler::Div4 => Ppre::DIV4,
33 APBPrescaler::Div8 => Ppre::DIV8,
34 APBPrescaler::Div16 => Ppre::DIV16,
35 } 40 }
36 } 41 }
37}
38 42
39impl Into<Hpre> for AHBPrescaler { 43 fn freeze(mut self) -> Clocks {
40 fn into(self) -> Hpre { 44 use crate::pac::rcc::vals::{Hpre, Hsebyp, Ppre, Sw};
41 match self { 45
42 AHBPrescaler::NotDivided => Hpre::DIV1, 46 let pllsrcclk = self.config.hse.map(|hse| hse.0).unwrap_or(HSI);
43 AHBPrescaler::Div2 => Hpre::DIV2, 47 let sysclk = self.config.sys_ck.map(|sys| sys.0).unwrap_or(pllsrcclk);
44 AHBPrescaler::Div4 => Hpre::DIV4, 48 let sysclk_on_pll = sysclk != pllsrcclk;
45 AHBPrescaler::Div8 => Hpre::DIV8, 49
46 AHBPrescaler::Div16 => Hpre::DIV16, 50 let plls = self.setup_pll(
47 AHBPrescaler::Div64 => Hpre::DIV64, 51 pllsrcclk,
48 AHBPrescaler::Div128 => Hpre::DIV128, 52 self.config.hse.is_some(),
49 AHBPrescaler::Div256 => Hpre::DIV256, 53 if sysclk_on_pll { Some(sysclk) } else { None },
50 AHBPrescaler::Div512 => Hpre::DIV512, 54 self.config.pll48,
55 );
56
57 if self.config.pll48 {
58 assert!(
59 // USB specification allows +-0.25%
60 plls.pll48clk
61 .map(|freq| (48_000_000 - freq as i32).abs() <= 120_000)
62 .unwrap_or(false)
63 );
51 } 64 }
52 }
53}
54 65
55/// Clocks configutation 66 let sysclk = if sysclk_on_pll {
56pub struct Config { 67 plls.pllsysclk.unwrap()
57 mux: ClockSrc, 68 } else {
58 ahb_pre: AHBPrescaler, 69 sysclk
59 apb1_pre: APBPrescaler, 70 };
60 apb2_pre: APBPrescaler, 71 assert!((SYSCLK_MIN..=SYSCLK_MAX).contains(&sysclk));
61} 72
73 let hclk = self.config.hclk.map(|h| h.0).unwrap_or(sysclk);
74 let (hpre_bits, hpre_div) = match (sysclk + hclk - 1) / hclk {
75 0 => unreachable!(),
76 1 => (Hpre::DIV1, 1),
77 2 => (Hpre::DIV2, 2),
78 3..=5 => (Hpre::DIV4, 4),
79 6..=11 => (Hpre::DIV8, 8),
80 12..=39 => (Hpre::DIV16, 16),
81 40..=95 => (Hpre::DIV64, 64),
82 96..=191 => (Hpre::DIV128, 128),
83 192..=383 => (Hpre::DIV256, 256),
84 _ => (Hpre::DIV512, 512),
85 };
86
87 // Calculate real AHB clock
88 let hclk = sysclk / hpre_div;
89
90 let pclk1 = self
91 .config
92 .pclk1
93 .map(|p| p.0)
94 .unwrap_or_else(|| core::cmp::min(PCLK1_MAX, hclk));
95 let (ppre1_bits, ppre1) = match (hclk + pclk1 - 1) / pclk1 {
96 0 => unreachable!(),
97 1 => (0b000, 1),
98 2 => (0b100, 2),
99 3..=5 => (0b101, 4),
100 6..=11 => (0b110, 8),
101 _ => (0b111, 16),
102 };
103 let timer_mul1 = if ppre1 == 1 { 1 } else { 2 };
104
105 // Calculate real APB1 clock
106 let pclk1 = hclk / ppre1;
107 assert!(pclk1 <= PCLK1_MAX);
62 108
63impl Default for Config { 109 let pclk2 = self
64 #[inline] 110 .config
65 fn default() -> Config { 111 .pclk2
66 Config { 112 .map(|p| p.0)
67 mux: ClockSrc::HSI16, 113 .unwrap_or_else(|| core::cmp::min(PCLK2_MAX, hclk));
68 ahb_pre: AHBPrescaler::NotDivided, 114 let (ppre2_bits, ppre2) = match (hclk + pclk2 - 1) / pclk2 {
69 apb1_pre: APBPrescaler::NotDivided, 115 0 => unreachable!(),
70 apb2_pre: APBPrescaler::NotDivided, 116 1 => (0b000, 1),
117 2 => (0b100, 2),
118 3..=5 => (0b101, 4),
119 6..=11 => (0b110, 8),
120 _ => (0b111, 16),
121 };
122 let timer_mul2 = if ppre2 == 1 { 1 } else { 2 };
123
124 // Calculate real APB2 clock
125 let pclk2 = hclk / ppre2;
126 assert!(pclk2 <= PCLK2_MAX);
127
128 Self::flash_setup(sysclk);
129
130 if self.config.hse.is_some() {
131 // NOTE(unsafe) We own the peripheral block
132 unsafe {
133 RCC.cr().modify(|w| {
134 w.set_hsebyp(Hsebyp(self.config.bypass_hse as u8));
135 w.set_hseon(true);
136 });
137 while !RCC.cr().read().hserdy() {}
138 }
71 } 139 }
72 }
73}
74 140
75impl Config { 141 if plls.use_pll {
76 #[inline] 142 unsafe {
77 pub fn clock_src(mut self, mux: ClockSrc) -> Self { 143 RCC.cr().modify(|w| w.set_pllon(true));
78 self.mux = mux; 144 // TODO: PWR setup for HCLK > 168MHz
79 self 145 while !RCC.cr().read().pllrdy() {}
80 } 146 }
147 }
81 148
82 #[inline] 149 unsafe {
83 pub fn ahb_pre(mut self, pre: AHBPrescaler) -> Self { 150 RCC.cfgr().modify(|w| {
84 self.ahb_pre = pre; 151 w.set_ppre2(Ppre(ppre2_bits));
85 self 152 w.set_ppre1(Ppre(ppre1_bits));
86 } 153 w.set_hpre(hpre_bits);
154 });
87 155
88 #[inline] 156 // Wait for the new prescalers to kick in
89 pub fn apb1_pre(mut self, pre: APBPrescaler) -> Self { 157 // "The clocks are divided with the new prescaler factor from 1 to 16 AHB cycles after write"
90 self.apb1_pre = pre; 158 cortex_m::asm::delay(16);
91 self
92 }
93 159
94 #[inline] 160 RCC.cfgr().modify(|w| {
95 pub fn apb2_pre(mut self, pre: APBPrescaler) -> Self { 161 w.set_sw(if sysclk_on_pll {
96 self.apb2_pre = pre; 162 Sw::PLL
97 self 163 } else if self.config.hse.is_some() {
98 } 164 Sw::HSE
99} 165 } else {
166 Sw::HSI
167 })
168 });
169 }
100 170
101/// RCC peripheral 171 Clocks {
102pub struct Rcc<'d> { 172 sys: Hertz(sysclk),
103 _rb: peripherals::RCC, 173 apb1: Hertz(pclk1),
104 phantom: PhantomData<&'d mut peripherals::RCC>, 174 apb2: Hertz(pclk2),
105}
106 175
107impl<'d> Rcc<'d> { 176 apb1_tim: Hertz(pclk1 * timer_mul1),
108 pub fn new(rcc: impl Unborrow<Target = peripherals::RCC> + 'd) -> Self { 177 apb2_tim: Hertz(pclk2 * timer_mul2),
109 unborrow!(rcc); 178
110 Self { 179 ahb1: Hertz(hclk),
111 _rb: rcc, 180 ahb2: Hertz(hclk),
112 phantom: PhantomData, 181 ahb3: Hertz(hclk),
182
183 pll48: plls.pll48clk.map(Hertz),
113 } 184 }
114 } 185 }
115 186
@@ -117,91 +188,122 @@ impl<'d> Rcc<'d> {
117 pub fn clocks(&self) -> &'static Clocks { 188 pub fn clocks(&self) -> &'static Clocks {
118 unsafe { get_freqs() } 189 unsafe { get_freqs() }
119 } 190 }
120}
121 191
122/// Extension trait that freezes the `RCC` peripheral with provided clocks configuration 192 fn setup_pll(
123pub trait RccExt { 193 &mut self,
124 fn freeze(self, config: Config) -> Clocks; 194 pllsrcclk: u32,
125} 195 use_hse: bool,
196 pllsysclk: Option<u32>,
197 pll48clk: bool,
198 ) -> PllResults {
199 use crate::pac::rcc::vals::{Pllp, Pllsrc};
126 200
127impl RccExt for RCC { 201 let sysclk = pllsysclk.unwrap_or(pllsrcclk);
128 #[inline] 202 if pllsysclk.is_none() && !pll48clk {
129 fn freeze(self, cfgr: Config) -> Clocks { 203 // NOTE(unsafe) We have a mutable borrow to the owner of the RegBlock
130 let rcc = pac::RCC; 204 unsafe {
131 let (sys_clk, sw) = match cfgr.mux { 205 RCC.pllcfgr()
132 ClockSrc::HSI16 => { 206 .modify(|w| w.set_pllsrc(Pllsrc(use_hse as u8)));
133 // Enable HSI16
134 unsafe {
135 rcc.cr().modify(|w| w.set_hsion(true));
136 while !rcc.cr().read().hsirdy() {}
137 }
138
139 (HSI_FREQ, Sw::HSI)
140 } 207 }
141 ClockSrc::HSE(freq) => {
142 // Enable HSE
143 unsafe {
144 rcc.cr().modify(|w| w.set_hseon(true));
145 while !rcc.cr().read().hserdy() {}
146 }
147
148 (freq.0, Sw::HSE)
149 }
150 };
151 208
152 unsafe { 209 return PllResults {
153 rcc.cfgr().modify(|w| { 210 use_pll: false,
154 w.set_sw(sw.into()); 211 pllsysclk: None,
155 w.set_hpre(cfgr.ahb_pre.into()); 212 pll48clk: None,
156 w.set_ppre1(cfgr.apb1_pre.into()); 213 };
157 w.set_ppre2(cfgr.apb2_pre.into());
158 });
159 } 214 }
215 // Input divisor from PLL source clock, must result to frequency in
216 // the range from 1 to 2 MHz
217 let pllm_min = (pllsrcclk + 1_999_999) / 2_000_000;
218 let pllm_max = pllsrcclk / 1_000_000;
160 219
161 let ahb_freq: u32 = match cfgr.ahb_pre { 220 // Sysclk output divisor must be one of 2, 4, 6 or 8
162 AHBPrescaler::NotDivided => sys_clk, 221 let sysclk_div = core::cmp::min(8, (432_000_000 / sysclk) & !1);
163 pre => {
164 let pre: Hpre = pre.into();
165 let pre = 1 << (pre.0 as u32 - 7);
166 sys_clk / pre
167 }
168 };
169 222
170 let (apb1_freq, apb1_tim_freq) = match cfgr.apb1_pre { 223 let target_freq = if pll48clk {
171 APBPrescaler::NotDivided => (ahb_freq, ahb_freq), 224 48_000_000
172 pre => { 225 } else {
173 let pre: Ppre = pre.into(); 226 sysclk * sysclk_div
174 let pre: u8 = 1 << (pre.0 - 3);
175 let freq = ahb_freq / pre as u32;
176 (freq, freq * 2)
177 }
178 }; 227 };
179 228
180 let (apb2_freq, apb2_tim_freq) = match cfgr.apb2_pre { 229 // Find the lowest pllm value that minimize the difference between
181 APBPrescaler::NotDivided => (ahb_freq, ahb_freq), 230 // target frequency and the real vco_out frequency.
182 pre => { 231 let pllm = (pllm_min..=pllm_max)
183 let pre: Ppre = pre.into(); 232 .min_by_key(|pllm| {
184 let pre: u8 = 1 << (pre.0 - 3); 233 let vco_in = pllsrcclk / pllm;
185 let freq = ahb_freq / (1 << (pre as u8 - 3)); 234 let plln = target_freq / vco_in;
186 (freq, freq * 2) 235 target_freq - vco_in * plln
187 } 236 })
237 .unwrap();
238
239 let vco_in = pllsrcclk / pllm;
240 assert!((1_000_000..=2_000_000).contains(&vco_in));
241
242 // Main scaler, must result in >= 100MHz (>= 192MHz for F401)
243 // and <= 432MHz, min 50, max 432
244 let plln = if pll48clk {
245 // try the different valid pllq according to the valid
246 // main scaller values, and take the best
247 let pllq = (4..=9)
248 .min_by_key(|pllq| {
249 let plln = 48_000_000 * pllq / vco_in;
250 let pll48_diff = 48_000_000 - vco_in * plln / pllq;
251 let sysclk_diff = (sysclk as i32 - (vco_in * plln / sysclk_div) as i32).abs();
252 (pll48_diff, sysclk_diff)
253 })
254 .unwrap();
255 48_000_000 * pllq / vco_in
256 } else {
257 sysclk * sysclk_div / vco_in
188 }; 258 };
259 assert!((192_000_000..=432_000_000).contains(&(vco_in * plln)));
189 260
190 Clocks { 261 let pllp = (sysclk_div / 2) - 1;
191 sys: sys_clk.hz(), 262
192 ahb1: ahb_freq.hz(), 263 let pllq = (vco_in * plln + 47_999_999) / 48_000_000;
193 ahb2: ahb_freq.hz(), 264 let real_pll48clk = vco_in * plln / pllq;
194 ahb3: ahb_freq.hz(), 265
195 apb1: apb1_freq.hz(), 266 unsafe {
196 apb2: apb2_freq.hz(), 267 RCC.pllcfgr().modify(|w| {
197 apb1_tim: apb1_tim_freq.hz(), 268 w.set_pllm(pllm as u8);
198 apb2_tim: apb2_tim_freq.hz(), 269 w.set_plln(plln as u16);
270 w.set_pllp(Pllp(pllp as u8));
271 w.set_pllq(pllq as u8);
272 w.set_pllsrc(Pllsrc(use_hse as u8));
273 });
274 }
275
276 let real_pllsysclk = vco_in * plln / sysclk_div;
277
278 PllResults {
279 use_pll: true,
280 pllsysclk: Some(real_pllsysclk),
281 pll48clk: if pll48clk { Some(real_pll48clk) } else { None },
199 } 282 }
200 } 283 }
284
285 fn flash_setup(sysclk: u32) {
286 use crate::pac::flash::vals::Latency;
287
288 // Be conservative with voltage ranges
289 const FLASH_LATENCY_STEP: u32 = 30_000_000;
290
291 critical_section::with(|_| unsafe {
292 FLASH
293 .acr()
294 .modify(|w| w.set_latency(Latency(((sysclk - 1) / FLASH_LATENCY_STEP) as u8)));
295 });
296 }
201} 297}
202 298
203pub unsafe fn init(config: Config) { 299pub unsafe fn init(config: Config) {
204 let r = <peripherals::RCC as embassy::util::Steal>::steal(); 300 let r = <peripherals::RCC as embassy::util::Steal>::steal();
205 let clocks = r.freeze(config); 301 let clocks = Rcc::new(r, config).freeze();
206 set_freqs(clocks); 302 set_freqs(clocks);
207} 303}
304
305struct PllResults {
306 use_pll: bool,
307 pllsysclk: Option<u32>,
308 pll48clk: Option<u32>,
309}
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs
index c02db58fe..87be0a5b3 100644
--- a/embassy-stm32/src/rcc/mod.rs
+++ b/embassy-stm32/src/rcc/mod.rs
@@ -31,6 +31,9 @@ pub struct Clocks {
31 31
32 #[cfg(any(rcc_h7))] 32 #[cfg(any(rcc_h7))]
33 pub apb4: Hertz, 33 pub apb4: Hertz,
34
35 #[cfg(rcc_f4)]
36 pub pll48: Option<Hertz>,
34} 37}
35 38
36/// Frozen clock frequencies 39/// Frozen clock frequencies