stm32f4xx/clocks/pll.rs
1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright OxidOS Automotive SRL.
4//
5// Author: Ioan-Cristian CÎRSTEA <ioan.cirstea@oxidos.io>
6
7//! Main phase-locked loop (PLL) clock driver for the STM32F4xx family. [^doc_ref]
8//!
9//! Many boards of the STM32F4xx family provide several PLL clocks. However, all of them have a
10//! main PLL clock. This driver is designed for the main PLL clock. It will be simply referred as
11//! the PLL clock.
12//!
13//! The PLL clock is composed of two outputs:
14//!
15//! + the main one used for the system clock
16//! + the PLL48CLK used for USB OTG FS, the random number generator and SDIO clocks
17//!
18//! # Implemented features
19//!
20//! - [x] Default configuration of 96MHz with reduced PLL jitter
21//! - [x] 1MHz frequency precision
22//! - [x] Support for 13-216MHz frequency range
23//! - [x] Support for PLL48CLK output
24//!
25//! # Missing features
26//!
27//! - [ ] Precision higher than 1MHz
28//! - [ ] Source selection
29//! - [ ] Precise control over the PLL48CLK frequency
30//!
31//! # Usage
32//!
33//! For the purposes of brevity, any error checking has been removed. In real applications, always
34//! check the return values of the [Pll] methods.
35//!
36//! First, get a reference to the [Pll] struct:
37//! ```rust,ignore
38//! let pll = &peripherals.stm32f4.clocks.pll;
39//! ```
40//!
41//! ## Start the clock with a given frequency
42//!
43//! ```rust,ignore
44//! pll.set_frequency_mhz(PllSource::HSI, HSI_FREQUENCY_MHZ, 100); // 100MHz
45//! pll.enable();
46//! ```
47//!
48//! ## Stop the clock
49//!
50//! ```rust,ignore
51//! pll.disable();
52//! ```
53//!
54//! ## Check whether the PLL clock is running or not
55//! ```rust,ignore
56//! if pll.is_enabled() {
57//! // do something...
58//! } else {
59//! // do something...
60//! }
61//! ```
62//!
63//! ## Check the clock frequency
64//!
65//! ```rust,ignore
66//! let optional_pll_frequency = pll.get_frequency_mhz();
67//! if let None = optional_pll_frequency {
68//! /* Clock stopped */
69//! }
70//! let pll_frequency = optional_pll_frequency.unwrap();
71//! /* Computations based on the PLL frequency */
72//! ```
73//!
74//! ## Reconfigure the clock once started
75//!
76//! ```rust,ignore
77//! pll.disable(); // The PLL clock can't be configured while running
78//! pll.set_frequency_mhz(PllSource::HSI, HSI_FREQUENCY_MHZ, 50); // 50MHz
79//! pll.enable();
80//! ```
81//!
82//! ## Configure the PLL clock so that PLL48CLK output is correctly calibrated
83//! ```rust,ignore
84//! // The frequency of the PLL clock must be 1, 1.5, 2, 2.5, 3, 3.5 or 4 x 48MHz in order to get
85//! // 48MHz output. Otherwise, the driver will attempt to get the closest frequency lower than 48MHz
86//! pll.set_frequency_mhz(PllSource::HSI, HSI_FREQUENCY_MHZ, 72); // 72MHz = 48MHz * 1.5
87//! pll.enable();
88//! ```
89//!
90//! ## Check if the PLL48CLK output is calibrated.
91//! ```rust,ignore
92//! if !pll.is_pll48_calibrated() {
93//! /* Handle the case when it is not calibrated */
94//! }
95//! ```
96//!
97//! ## Get the frequency of the PLL48CLK output
98//!
99//! ```rust,ignore
100//! let optional_pll48_frequency = pll.get_frequency_mhz();
101//! if let None = optional_pll48_frequency {
102//! /* Clock stopped */
103//! }
104//! let pll48_frequency = optional_pll48_frequency.unwrap();
105//! ```
106//!
107//! [^doc_ref]: See 6.2.3 in the documentation.
108
109use crate::chip_specific::clock_constants;
110use crate::clocks::hsi::HSI_FREQUENCY_MHZ;
111use crate::rcc::Rcc;
112use crate::rcc::SysClockSource;
113use crate::rcc::{PllSource, PLLM, PLLP, PLLQ};
114use crate::rcc::{DEFAULT_PLLM_VALUE, DEFAULT_PLLN_VALUE, DEFAULT_PLLP_VALUE, DEFAULT_PLLQ_VALUE};
115
116use kernel::debug;
117use kernel::utilities::cells::OptionalCell;
118use kernel::ErrorCode;
119
120use core::cell::Cell;
121use core::marker::PhantomData;
122
123/// Main PLL clock structure.
124pub struct Pll<'a, PllConstants> {
125 rcc: &'a Rcc,
126 frequency_mhz: OptionalCell<usize>,
127 pll48_frequency_mhz: OptionalCell<usize>,
128 pll48_calibrated: Cell<bool>,
129 _marker: PhantomData<PllConstants>,
130}
131
132impl<'a, PllConstants: clock_constants::PllConstants> Pll<'a, PllConstants> {
133 // Create a new instance of the PLL clock.
134 //
135 // The instance of the PLL clock is configured to run at 96MHz and with minimal PLL jitter
136 // effects.
137 //
138 // # Parameters
139 //
140 // + rcc: an instance of [crate::rcc]
141 //
142 // # Returns
143 //
144 // An instance of the PLL clock.
145 pub(in crate::clocks) fn new(rcc: &'a Rcc) -> Self {
146 const PLLP: usize = match DEFAULT_PLLP_VALUE {
147 PLLP::DivideBy2 => 2,
148 PLLP::DivideBy4 => 4,
149 PLLP::DivideBy6 => 6,
150 PLLP::DivideBy8 => 8,
151 };
152 const PLLM: usize = DEFAULT_PLLM_VALUE as usize;
153 const PLLQ: usize = DEFAULT_PLLQ_VALUE as usize;
154 Self {
155 rcc,
156 frequency_mhz: OptionalCell::new(HSI_FREQUENCY_MHZ / PLLM * DEFAULT_PLLN_VALUE / PLLP),
157 pll48_frequency_mhz: OptionalCell::new(
158 HSI_FREQUENCY_MHZ / PLLM * DEFAULT_PLLN_VALUE / PLLQ,
159 ),
160 pll48_calibrated: Cell::new(true),
161 _marker: PhantomData,
162 }
163 }
164
165 // The caller must ensure the desired frequency lies between MIN_FREQ_MHZ and
166 // MAX_FREQ_MHZ. Otherwise, the return value makes no sense.
167 fn compute_pllp(desired_frequency_mhz: usize) -> PLLP {
168 if desired_frequency_mhz < 55 {
169 PLLP::DivideBy8
170 } else if desired_frequency_mhz < 73 {
171 PLLP::DivideBy6
172 } else if desired_frequency_mhz < 109 {
173 PLLP::DivideBy4
174 } else {
175 PLLP::DivideBy2
176 }
177 }
178
179 // The caller must ensure the desired frequency lies between MIN_FREQ_MHZ and
180 // MAX_FREQ_MHZ. Otherwise, the return value makes no sense.
181 fn compute_plln(
182 desired_frequency_mhz: usize,
183 pll_source_clock_freq: usize,
184 pllp: PLLP,
185 ) -> usize {
186 let vco_input_frequency: usize = pll_source_clock_freq / DEFAULT_PLLM_VALUE as usize;
187 desired_frequency_mhz * Into::<usize>::into(pllp) / vco_input_frequency
188 }
189
190 // The caller must ensure the VCO output frequency lies between 100 and 432MHz. Otherwise, the
191 // return value makes no sense.
192 fn compute_pllq(vco_output_frequency_mhz: usize) -> PLLQ {
193 for pllq in 3..10 {
194 if 48 * pllq >= vco_output_frequency_mhz {
195 return match pllq {
196 3 => PLLQ::DivideBy3,
197 4 => PLLQ::DivideBy4,
198 5 => PLLQ::DivideBy5,
199 6 => PLLQ::DivideBy6,
200 7 => PLLQ::DivideBy7,
201 8 => PLLQ::DivideBy8,
202 _ => PLLQ::DivideBy9,
203 };
204 }
205 }
206 unreachable!("The previous for loop should always return");
207 }
208
209 /// Set the PLL source clock
210 fn set_pll_source_clock(&self, source: PllSource) -> Result<(), ErrorCode> {
211 if self.is_enabled() {
212 Err(ErrorCode::FAIL)
213 } else {
214 self.rcc.set_pll_clocks_source(source);
215 Ok(())
216 }
217 }
218
219 /// Start the PLL clock.
220 ///
221 /// # Errors
222 ///
223 /// + [Err]\([ErrorCode::BUSY]\): if enabling the PLL clock took too long. Recall this method to
224 /// ensure the PLL clock is running.
225 pub fn enable(&self) -> Result<(), ErrorCode> {
226 // Enable the PLL clock
227 self.rcc.enable_pll_clock();
228
229 // Wait until the PLL clock is locked.
230 // 200 was obtained by running tests in release mode
231 for _ in 0..200 {
232 if self.rcc.is_locked_pll_clock() {
233 return Ok(());
234 }
235 }
236
237 // If waiting for the PLL clock took too long, return ErrorCode::BUSY
238 Err(ErrorCode::BUSY)
239 }
240
241 /// Stop the PLL clock.
242 ///
243 /// # Errors
244 ///
245 /// + [Err]\([ErrorCode::FAIL]\): if the PLL clock is configured as the system clock.
246 /// + [Err]\([ErrorCode::BUSY]\): disabling the PLL clock took to long. Retry to ensure it is
247 /// not running.
248 pub fn disable(&self) -> Result<(), ErrorCode> {
249 // Can't disable the PLL clock when it is used as the system clock
250 if self.rcc.get_sys_clock_source() == SysClockSource::PLL {
251 return Err(ErrorCode::FAIL);
252 }
253
254 // Disable the PLL clock
255 self.rcc.disable_pll_clock();
256
257 // Wait to unlock the PLL clock
258 // 10 was obtained by testing in release mode
259 for _ in 0..10 {
260 if !self.rcc.is_locked_pll_clock() {
261 return Ok(());
262 }
263 }
264
265 // If the waiting was too long, return ErrorCode::BUSY
266 Err(ErrorCode::BUSY)
267 }
268
269 /// Check whether the PLL clock is enabled or not.
270 ///
271 /// # Returns
272 ///
273 /// + [false]: the PLL clock is not enabled
274 /// + [true]: the PLL clock is enabled
275 pub fn is_enabled(&self) -> bool {
276 self.rcc.is_enabled_pll_clock()
277 }
278
279 /// Set the frequency of the PLL clock.
280 ///
281 /// The PLL clock has two outputs:
282 ///
283 /// + main output used for configuring the system clock
284 /// + a second output called PLL48CLK used by OTG USB FS (48MHz), the random number generator
285 /// (≤ 48MHz) and the SDIO (≤ 48MHz) clocks.
286 ///
287 /// When calling this method, the given frequency is set for the main output. The method will
288 /// attempt to configure the PLL48CLK output to 48MHz, or to the highest value less than 48MHz
289 /// if it is not possible to get a precise 48MHz. In order to obtain a precise 48MHz frequency
290 /// (for the OTG USB FS peripheral), one should call this method with a frequency of 1, 1.5, 2,
291 /// 2.5 ... 4 x 48MHz.
292 ///
293 /// # Parameters
294 ///
295 /// + pll_source: PLL source clock (HSI or HSE)
296 ///
297 /// + source_frequency: the frequency of the PLL source clock in MHz. For the HSI the frequency
298 /// is fixed to 16MHz. For the HSE, the frequency is hardware-dependent
299 ///
300 /// + desired_frequency_mhz: the desired frequency in MHz. Supported values: 24-216MHz for
301 /// STM32F401 and 13-216MHz for all the other chips
302 ///
303 /// # Errors
304 ///
305 /// + [Err]\([ErrorCode::INVAL]\): if the desired frequency can't be achieved
306 /// + [Err]\([ErrorCode::FAIL]\): if the PLL clock is already enabled. It must be disabled before
307 /// configuring it.
308 pub(super) fn set_frequency_mhz(
309 &self,
310 pll_source: PllSource,
311 source_frequency: usize,
312 desired_frequency_mhz: usize,
313 ) -> Result<(), ErrorCode> {
314 // Check for errors:
315 // + PLL clock running
316 // + invalid frequency
317 if self.rcc.is_enabled_pll_clock() {
318 return Err(ErrorCode::FAIL);
319 } else if desired_frequency_mhz < PllConstants::MIN_FREQ_MHZ
320 || desired_frequency_mhz > PllConstants::MAX_FREQ_MHZ
321 {
322 return Err(ErrorCode::INVAL);
323 }
324
325 // The output frequencies for the PLL clock is computed as following:
326 // Source frequency / PLLM = VCO input frequency (must range from 1MHz to 2MHz)
327 // VCO output frequency = VCO input frequency * PLLN (must range from 100MHz to 432MHz)
328 // PLL output frequency = VCO output frequency / PLLP
329 // PLL48CLK = VCO output frequency / PLLQ
330
331 // Set PLL source (HSI or HSE)
332 if self.set_pll_source_clock(pll_source) != Ok(()) {
333 return Err(ErrorCode::FAIL);
334 }
335
336 // Compute PLLP
337 let pllp = Self::compute_pllp(desired_frequency_mhz);
338 self.rcc.set_pll_clock_p_divider(pllp);
339
340 // Compute PLLN
341 let plln = Self::compute_plln(desired_frequency_mhz, source_frequency, pllp);
342 self.rcc.set_pll_clock_n_multiplier(plln);
343
344 // Compute PLLQ
345 let vco_output_frequency = source_frequency / DEFAULT_PLLM_VALUE as usize * plln;
346 let pllq = Self::compute_pllq(vco_output_frequency);
347 self.rcc.set_pll_clock_q_divider(pllq);
348
349 // Check if PLL48CLK is calibrated, e.g. its frequency is exactly 48MHz
350 let pll48_frequency = vco_output_frequency / pllq as usize;
351 self.pll48_calibrated
352 .set(pll48_frequency == 48 && vco_output_frequency % pllq as usize == 0);
353
354 // Cache the frequency so it is not computed every time a get method is called
355 self.frequency_mhz.set(desired_frequency_mhz);
356 self.pll48_frequency_mhz.set(pll48_frequency);
357
358 Ok(())
359 }
360
361 /// Get the frequency in MHz of the PLL clock.
362 ///
363 /// # Returns
364 ///
365 /// + [Some]\(frequency_mhz\): if the PLL clock is enabled.
366 /// + [None]: if the PLL clock is disabled.
367 pub fn get_frequency_mhz(&self) -> Option<usize> {
368 if self.is_enabled() {
369 self.frequency_mhz.get()
370 } else {
371 None
372 }
373 }
374
375 /// Get the frequency in MHz of the PLL clock from RCC registers instead of using the cached
376 /// value.
377 ///
378 /// # Returns
379 ///
380 /// + [Some]\(frequency_mhz\): if the PLL clock is enabled.
381 /// + [None]: if the PLL clock is disabled.
382 pub fn get_frequency_mhz_no_cache(&self, source_frequency: usize) -> Option<usize> {
383 if self.is_enabled() {
384 let pllm = self.rcc.get_pll_clocks_m_divider() as usize;
385 let plln = self.rcc.get_pll_clock_n_multiplier();
386 let pllp: usize = self.rcc.get_pll_clock_p_divider().into();
387 Some(source_frequency / pllm * plln / pllp)
388 } else {
389 None
390 }
391 }
392
393 /// Get the frequency in MHz of the PLL48 clock.
394 ///
395 /// **NOTE:** If the PLL clock was not configured with a frequency multiple of 48MHz, the
396 /// returned value is inaccurate.
397 ///
398 /// # Returns
399 ///
400 /// + [Some]\(frequency_mhz\): if the PLL clock is enabled.
401 /// + [None]: if the PLL clock is disabled.
402 pub fn get_frequency_mhz_pll48(&self) -> Option<usize> {
403 if self.is_enabled() {
404 self.pll48_frequency_mhz.get()
405 } else {
406 None
407 }
408 }
409
410 /// Check if the PLL48 clock is calibrated (its output is exactly 48MHz).
411 ///
412 /// A frequency of 48MHz is required for USB OTG FS.
413 ///
414 /// # Returns
415 ///
416 /// + [true]: the PLL48 clock frequency is exactly 48MHz.
417 /// + [false]: the PLL48 clock is not exactly 48MHz.
418 pub fn is_pll48_calibrated(&self) -> bool {
419 self.pll48_calibrated.get()
420 }
421}
422
423/// Tests for the PLL clock
424///
425/// This module ensures that the PLL clock works as expected. If changes are brought to the PLL
426/// clock, ensure to run all the tests to see if anything is broken.
427///
428/// # Usage
429///
430/// First, import the [crate::clocks::pll] module inside the board main file:
431///
432/// ```rust,ignore
433/// use stm32f429zi::pll;
434/// ```
435/// To run all the available tests, add this line before **kernel::process::load_processes()**:
436///
437/// ```rust,ignore
438/// pll::tests::run(&peripherals.stm32f4.clocks.pll);
439/// ```
440///
441/// If everything works as expected, the following message should be printed on the kernel console:
442///
443/// ```text
444/// ===============================================
445/// Testing PLL...
446/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
447/// Testing PLL configuration...
448/// Finished testing PLL configuration.
449/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
450/// Testing PLL struct...
451/// Finished testing PLL struct.
452/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
453/// Finished testing PLL. Everything is alright!
454/// ===============================================
455/// ```
456///
457/// There is also the possibility to run a part of the test suite. Check the functions present in
458/// this module for more details.
459///
460/// # Errors
461///
462/// If there are any errors, open an issue ticket at <https://github.com/tock/tock>. Please provide the
463/// output of the test execution.
464pub mod tests {
465 use super::{
466 clock_constants, debug, ErrorCode, Pll, PllSource, DEFAULT_PLLM_VALUE, HSI_FREQUENCY_MHZ,
467 PLLM, PLLP, PLLQ,
468 };
469
470 // Depending on the default PLLM value, the computed PLLN value changes.
471 const MULTIPLIER: usize = match DEFAULT_PLLM_VALUE {
472 PLLM::DivideBy8 => 1,
473 PLLM::DivideBy16 => 2,
474 };
475
476 /// Test if the configuration parameters are correctly computed for a given frequency.
477 ///
478 /// # Usage
479 ///
480 /// ```rust,ignore
481 /// use stm32f429zi::pll; // Import the pll module
482 /// /* Code goes here */
483 /// pll::test::test_pll_config(&peripherals.stm32f4.pll); // Run the tests
484 /// ```
485 pub fn test_pll_config<PllConstants: clock_constants::PllConstants>() {
486 debug!("Testing PLL configuration...");
487
488 // 13 or 24MHz --> minimum value
489 let mut pllp = Pll::<PllConstants>::compute_pllp(PllConstants::MIN_FREQ_MHZ);
490 assert_eq!(PLLP::DivideBy8, pllp);
491 let mut plln =
492 Pll::<PllConstants>::compute_plln(PllConstants::MIN_FREQ_MHZ, HSI_FREQUENCY_MHZ, pllp);
493
494 #[cfg(not(feature = "stm32f401"))]
495 assert_eq!(52 * MULTIPLIER, plln);
496 #[cfg(feature = "stm32f401")]
497 assert_eq!(96 * MULTIPLIER, plln);
498
499 let mut vco_output_frequency_mhz = HSI_FREQUENCY_MHZ / DEFAULT_PLLM_VALUE as usize * plln;
500 let mut pllq = Pll::<PllConstants>::compute_pllq(vco_output_frequency_mhz);
501
502 #[cfg(not(feature = "stm32f401"))]
503 assert_eq!(PLLQ::DivideBy3, pllq);
504 #[cfg(feature = "stm32f401")]
505 assert_eq!(PLLQ::DivideBy4, pllq);
506
507 // 25MHz --> minimum required value for Ethernet devices
508 pllp = Pll::<PllConstants>::compute_pllp(25);
509 assert_eq!(PLLP::DivideBy8, pllp);
510 plln = Pll::<PllConstants>::compute_plln(25, HSI_FREQUENCY_MHZ, pllp);
511 assert_eq!(100 * MULTIPLIER, plln);
512 vco_output_frequency_mhz = HSI_FREQUENCY_MHZ / DEFAULT_PLLM_VALUE as usize * plln;
513 pllq = Pll::<PllConstants>::compute_pllq(vco_output_frequency_mhz);
514 assert_eq!(PLLQ::DivideBy5, pllq);
515
516 // 54MHz --> last frequency before PLLP becomes DivideBy6
517 pllp = Pll::<PllConstants>::compute_pllp(54);
518 assert_eq!(PLLP::DivideBy8, pllp);
519 plln = Pll::<PllConstants>::compute_plln(54, HSI_FREQUENCY_MHZ, pllp);
520 assert_eq!(216 * MULTIPLIER, plln);
521 vco_output_frequency_mhz = HSI_FREQUENCY_MHZ / DEFAULT_PLLM_VALUE as usize * plln;
522 pllq = Pll::<PllConstants>::compute_pllq(vco_output_frequency_mhz);
523 assert_eq!(PLLQ::DivideBy9, pllq);
524
525 // 55MHz --> PLLP becomes DivideBy6
526 pllp = Pll::<PllConstants>::compute_pllp(55);
527 assert_eq!(PLLP::DivideBy6, pllp);
528 plln = Pll::<PllConstants>::compute_plln(55, HSI_FREQUENCY_MHZ, pllp);
529 assert_eq!(165 * MULTIPLIER, plln);
530 vco_output_frequency_mhz = HSI_FREQUENCY_MHZ / DEFAULT_PLLM_VALUE as usize * plln;
531 pllq = Pll::<PllConstants>::compute_pllq(vco_output_frequency_mhz);
532 assert_eq!(PLLQ::DivideBy7, pllq);
533
534 // 70MHz --> Another value for PLLP::DivideBy6
535 pllp = Pll::<PllConstants>::compute_pllp(70);
536 assert_eq!(PLLP::DivideBy6, pllp);
537 plln = Pll::<PllConstants>::compute_plln(70, HSI_FREQUENCY_MHZ, pllp);
538 assert_eq!(210 * MULTIPLIER, plln);
539 vco_output_frequency_mhz = HSI_FREQUENCY_MHZ / DEFAULT_PLLM_VALUE as usize * plln;
540 pllq = Pll::<PllConstants>::compute_pllq(vco_output_frequency_mhz);
541 assert_eq!(PLLQ::DivideBy9, pllq);
542
543 // 72MHz --> last frequency before PLLP becomes DivideBy4
544 pllp = Pll::<PllConstants>::compute_pllp(72);
545 assert_eq!(PLLP::DivideBy6, pllp);
546 plln = Pll::<PllConstants>::compute_plln(72, HSI_FREQUENCY_MHZ, pllp);
547 assert_eq!(216 * MULTIPLIER, plln);
548 vco_output_frequency_mhz = HSI_FREQUENCY_MHZ / DEFAULT_PLLM_VALUE as usize * plln;
549 pllq = Pll::<PllConstants>::compute_pllq(vco_output_frequency_mhz);
550 assert_eq!(PLLQ::DivideBy9, pllq);
551
552 // 73MHz --> PLLP becomes DivideBy4
553 pllp = Pll::<PllConstants>::compute_pllp(73);
554 assert_eq!(PLLP::DivideBy4, pllp);
555 plln = Pll::<PllConstants>::compute_plln(73, HSI_FREQUENCY_MHZ, pllp);
556 assert_eq!(146 * MULTIPLIER, plln);
557 vco_output_frequency_mhz = HSI_FREQUENCY_MHZ / DEFAULT_PLLM_VALUE as usize * plln;
558 pllq = Pll::<PllConstants>::compute_pllq(vco_output_frequency_mhz);
559 assert_eq!(PLLQ::DivideBy7, pllq);
560
561 // 100MHz --> Another value for PLLP::DivideBy4
562 pllp = Pll::<PllConstants>::compute_pllp(100);
563 assert_eq!(PLLP::DivideBy4, pllp);
564 plln = Pll::<PllConstants>::compute_plln(100, HSI_FREQUENCY_MHZ, pllp);
565 assert_eq!(200 * MULTIPLIER, plln);
566 vco_output_frequency_mhz = HSI_FREQUENCY_MHZ / DEFAULT_PLLM_VALUE as usize * plln;
567 pllq = Pll::<PllConstants>::compute_pllq(vco_output_frequency_mhz);
568 assert_eq!(PLLQ::DivideBy9, pllq);
569
570 // 108MHz --> last frequency before PLLP becomes DivideBy2
571 pllp = Pll::<PllConstants>::compute_pllp(108);
572 assert_eq!(PLLP::DivideBy4, pllp);
573 plln = Pll::<PllConstants>::compute_plln(108, HSI_FREQUENCY_MHZ, pllp);
574 assert_eq!(216 * MULTIPLIER, plln);
575 vco_output_frequency_mhz = HSI_FREQUENCY_MHZ / DEFAULT_PLLM_VALUE as usize * plln;
576 pllq = Pll::<PllConstants>::compute_pllq(vco_output_frequency_mhz);
577 assert_eq!(PLLQ::DivideBy9, pllq);
578
579 // 109MHz --> PLLP becomes DivideBy2
580 pllp = Pll::<PllConstants>::compute_pllp(109);
581 assert_eq!(PLLP::DivideBy2, pllp);
582 plln = Pll::<PllConstants>::compute_plln(109, HSI_FREQUENCY_MHZ, pllp);
583 assert_eq!(109 * MULTIPLIER, plln);
584 vco_output_frequency_mhz = HSI_FREQUENCY_MHZ / DEFAULT_PLLM_VALUE as usize * plln;
585 pllq = Pll::<PllConstants>::compute_pllq(vco_output_frequency_mhz);
586 assert_eq!(PLLQ::DivideBy5, pllq);
587
588 // 125MHz --> Another value for PLLP::DivideBy2
589 pllp = Pll::<PllConstants>::compute_pllp(125);
590 assert_eq!(PLLP::DivideBy2, pllp);
591 plln = Pll::<PllConstants>::compute_plln(125, HSI_FREQUENCY_MHZ, pllp);
592 assert_eq!(125 * MULTIPLIER, plln);
593 vco_output_frequency_mhz = HSI_FREQUENCY_MHZ / DEFAULT_PLLM_VALUE as usize * plln;
594 pllq = Pll::<PllConstants>::compute_pllq(vco_output_frequency_mhz);
595 assert_eq!(PLLQ::DivideBy6, pllq);
596
597 // 180MHz --> Max frequency for the CPU
598 pllp = Pll::<PllConstants>::compute_pllp(180);
599 assert_eq!(PLLP::DivideBy2, pllp);
600 plln = Pll::<PllConstants>::compute_plln(180, HSI_FREQUENCY_MHZ, pllp);
601 assert_eq!(180 * MULTIPLIER, plln);
602 vco_output_frequency_mhz = HSI_FREQUENCY_MHZ / DEFAULT_PLLM_VALUE as usize * plln;
603 pllq = Pll::<PllConstants>::compute_pllq(vco_output_frequency_mhz);
604 assert_eq!(PLLQ::DivideBy8, pllq);
605
606 // 216MHz --> Max frequency for the PLL due to the VCO output frequency limit
607 pllp = Pll::<PllConstants>::compute_pllp(216);
608 assert_eq!(PLLP::DivideBy2, pllp);
609 plln = Pll::<PllConstants>::compute_plln(216, HSI_FREQUENCY_MHZ, pllp);
610 assert_eq!(216 * MULTIPLIER, plln);
611 vco_output_frequency_mhz = HSI_FREQUENCY_MHZ / DEFAULT_PLLM_VALUE as usize * plln;
612 pllq = Pll::<PllConstants>::compute_pllq(vco_output_frequency_mhz);
613 assert_eq!(PLLQ::DivideBy9, pllq);
614
615 debug!("Finished testing PLL configuration.");
616 }
617
618 /// Check if the PLL works as expected.
619 ///
620 /// **NOTE:** it is highly recommended to call [test_pll_config]
621 /// first to check whether the configuration parameters are correctly computed.
622 ///
623 /// # Usage
624 ///
625 /// ```rust,ignore
626 /// use stm32f429zi::pll; // Import the PLL module
627 /// /* Code goes here */
628 /// pll::test::test_pll_struct(&peripherals.stm32f4.pll); // Run the tests
629 /// ```
630 pub fn test_pll_struct<'a, PllConstants: clock_constants::PllConstants>(
631 pll: &'a Pll<'a, PllConstants>,
632 ) {
633 debug!("Testing PLL struct...");
634 // Make sure the PLL clock is disabled
635 assert_eq!(Ok(()), pll.disable());
636 assert!(!pll.is_enabled());
637
638 // Attempting to configure the PLL with either too high or too low frequency
639 assert_eq!(
640 Err(ErrorCode::INVAL),
641 pll.set_frequency_mhz(PllSource::HSI, HSI_FREQUENCY_MHZ, 12)
642 );
643 assert_eq!(
644 Err(ErrorCode::INVAL),
645 pll.set_frequency_mhz(PllSource::HSI, HSI_FREQUENCY_MHZ, 217)
646 );
647
648 // Start the PLL with the default configuration.
649 assert_eq!(Ok(()), pll.enable());
650
651 // Make sure the PLL is enabled.
652 assert!(pll.is_enabled());
653
654 // By default, the PLL clock is set to 96MHz
655 assert_eq!(Some(96), pll.get_frequency_mhz());
656
657 // By default, the PLL48 clock is correctly calibrated
658 assert!(pll.is_pll48_calibrated());
659
660 // Impossible to configure the PLL clock once it is enabled.
661 assert_eq!(
662 Err(ErrorCode::FAIL),
663 pll.set_frequency_mhz(PllSource::HSI, HSI_FREQUENCY_MHZ, 50)
664 );
665
666 // Stop the PLL in order to reconfigure it.
667 assert_eq!(Ok(()), pll.disable());
668
669 // Configure the PLL clock to run at 25MHz
670 assert_eq!(
671 Ok(()),
672 pll.set_frequency_mhz(PllSource::HSI, HSI_FREQUENCY_MHZ, 25)
673 );
674
675 // Start the PLL with the new configuration
676 assert_eq!(Ok(()), pll.enable());
677
678 // get_frequency() method should reflect the new change
679 assert_eq!(Some(25), pll.get_frequency_mhz());
680
681 // Since 25 is not a multiple of 48, the PLL48 clock is not correctly calibrated
682 assert!(!pll.is_pll48_calibrated());
683
684 // The expected PLL48 clock value in this case should be approximately 40 MHz.
685 // It is actually exactly 40MHz in this particular case.
686 assert_eq!(Some(40), pll.get_frequency_mhz_pll48());
687
688 // Stop the PLL clock
689 assert_eq!(Ok(()), pll.disable());
690
691 // Attempting to get the frequency of the PLL clock when it is disabled should return None.
692 assert_eq!(None, pll.get_frequency_mhz());
693 // Same for PLL48 clock
694 assert_eq!(None, pll.get_frequency_mhz_pll48());
695
696 // Attempting to configure the PLL clock with a frequency multiple of 48MHz
697 assert_eq!(
698 Ok(()),
699 pll.set_frequency_mhz(PllSource::HSI, HSI_FREQUENCY_MHZ, 144)
700 );
701 assert_eq!(Ok(()), pll.enable());
702 assert_eq!(Some(144), pll.get_frequency_mhz());
703
704 // PLL48 clock output should be correctly calibrated
705 assert!(pll.is_pll48_calibrated());
706 assert_eq!(Some(48), pll.get_frequency_mhz_pll48());
707
708 // Reconfigure the clock for 100MHz
709 assert_eq!(Ok(()), pll.disable());
710 assert_eq!(
711 Ok(()),
712 pll.set_frequency_mhz(PllSource::HSI, HSI_FREQUENCY_MHZ, 100)
713 );
714 assert_eq!(Ok(()), pll.enable());
715 assert_eq!(Some(100), pll.get_frequency_mhz());
716
717 // In this case, the PLL48 clock is not correctly calibrated. Its frequency is
718 // approximately 44MHz.
719 assert!(!pll.is_pll48_calibrated());
720 assert_eq!(Some(44), pll.get_frequency_mhz_pll48());
721
722 // Configure the clock to 72MHz = 48MHz * 1.5
723 assert_eq!(Ok(()), pll.disable());
724 assert_eq!(
725 Ok(()),
726 pll.set_frequency_mhz(PllSource::HSI, HSI_FREQUENCY_MHZ, 72)
727 );
728 assert_eq!(Ok(()), pll.enable());
729 assert_eq!(Some(72), pll.get_frequency_mhz());
730
731 // In this case, the PLL48 clock is correctly calibrated
732 assert!(pll.is_pll48_calibrated());
733 assert_eq!(Some(48), pll.get_frequency_mhz_pll48());
734
735 // Turn off the PLL clock
736 assert_eq!(Ok(()), pll.disable());
737 assert!(!pll.is_enabled());
738
739 debug!("Finished testing PLL struct.");
740 }
741
742 /// Run the entire test suite.
743 ///
744 /// # Usage
745 ///
746 /// ```rust,ignore
747 /// use stm32f429zi::pll; // Import the PLL module
748 /// /* Code goes here */
749 /// pll::test::run(&peripherals.stm32f4.pll); // Run the tests
750 /// ```
751 pub fn run<'a, PllConstants: clock_constants::PllConstants>(pll: &'a Pll<'a, PllConstants>) {
752 debug!("");
753 debug!("===============================================");
754 debug!("Testing PLL...");
755 debug!("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
756 test_pll_config::<PllConstants>();
757 debug!("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
758 test_pll_struct(pll);
759 debug!("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
760 debug!("Finished testing PLL. Everything is alright!");
761 debug!("===============================================");
762 debug!("");
763 }
764}