stm32f4xx/clocks/
clocks.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//! STM32F4xx clock driver
8//!
9//! This crate provides drivers for various clocks: HSI, PLL, system, AHB, APB1 and APB2.
10//! This documentation applies to the system clock, AHB, APB1 and APB2. For in-detail documentation
11//! for HSI and PLL, check their documentation.
12//!
13//! # Features
14//!
15//! - [x] Dynamic system source
16//! - [x] Hardware limits verification for AHB, APB1 and APB2.
17//! - [x] Prescaler configuration for AHB, APB1 and APB2.
18//! - [x] Support for MCO1
19//!
20//! # Limitations
21//!
22//! - [ ] Precision of 1MHz
23//! - [ ] No support for MCO2
24//!
25//! # Usage [^usage_note]
26//!
27//! First, import the following enums:
28//!
29//! ```rust,ignore
30//! // Assuming a STM32F429 chip. Change this to correspond to the chip model.
31//! use stm32f429zi::rcc::APBPrescaler;
32//! use stm32f429zi::rcc::AHBPrescaler;
33//! use stm32f429zi::rcc::SysClockSource;
34//! ```
35//!
36//! A reference to the [crate::clocks::Clocks] is needed:
37//!
38//! ```rust,ignore
39//! // Add this in board main.rs
40//! let clocks = &peripherals.stm32f4.clocks;
41//! ```
42//!
43//! ## Retrieve the AHB frequency:
44//!
45//! ```rust,ignore
46//! let ahb_frequency = clocks.get_ahb_frequency_mhz();
47//! debug!("Current AHB frequency is {}MHz", ahb_frequency);
48//! ```
49//!
50//! ## Retrieve the AHB prescaler:
51//!
52//! ```rust,ignore
53//! let ahb_prescaler = clocks.get_ahb_prescaler();
54//! debug!("Current AHB prescaler is {:?}", ahb_prescaler);
55//! ```
56//!
57//! NOTE: If one wishes to get the usize equivalent value of [crate::clocks::Clocks::get_ahb_prescaler], to use in
58//! computations for example, they must use [crate::rcc::AHBPrescaler].into() method:
59//!
60//! ```rust,ignore
61//! let ahb_prescaler_usize: usize = clocks.get_ahb_prescaler().into();
62//! if ahb_prescaler_usize > 8 {
63//!     /* Do something */
64//! }
65//! ```
66//!
67//! ## Set the AHB prescaler
68//!
69//! ```rust,ignore
70//! clocks.set_ahb_prescaler(AHBPrescaler::DivideBy4);
71//! ```
72//!
73//! ## APB1 and APB2 prescalers
74//!
75//! APB1 and APB2 prescalers are configured in a similar way as AHB prescaler, except that the
76//! corresponding enum is APBPrescaler.
77//!
78//! ## Retrieve the system clock frequency:
79//!
80//! ```rust,ignore
81//! let sys_frequency = clocks.get_sys_clock_frequency_mhz();
82//! debug!("Current system clock frequency is {}MHz", sys_frequency);
83//! ```
84//!
85//! ## Retrieve the system clock source:
86//!
87//! ```rust,ignore
88//! let sys_source = clocks.get_sys_clock_source();
89//! debug!("Current system clock source is {:?}", sys_source);
90//! ```
91//!
92//! ## Change the system clock source to PLL:
93//!
94//! Changing the system clock source is a fastidious task because of AHB, APB1 and APB2 limits,
95//! which are chip-dependent. This example assumes a STM32F429 chip.
96//!
97//! First, get a reference to the PLL
98//!
99//! ```rust,ignore
100//! let pll = &peripherals.stm32f4.clocks.pll;
101//! ```
102//!
103//! Then, configure its frequency and enable it
104//! ```rust,ignore
105//! pll.set_frequency_mhz(50);
106//! pll.enable();
107//! ```
108//!
109//! STM32F429 maximum APB1 frequency is 45MHz, which is computed as following:
110//! freq_APB1 = freq_sys / AHB_prescaler / APB1_prescaler
111//! Default prescaler values are 1, which gives an frequency of 50MHz without modifying the
112//! APB1 prescaler. As such, the APB1 prescaler must be changed.
113//!
114//! ```rust,ignore
115//! clocks.set_apb1_prescaler(APBPrescaler::DivideBy2);
116//! ```
117//!
118//! Since the APB1 frequency limit is satisfied now, the system clock source can be safely changed.
119//!
120//! ```rust,ignore
121//! clocks.set_sys_clock_source(SysClockSource::PLL);
122//! ```
123//!
124//! ## Another example of changing the system clock to PLL for STM32F429:
125//!
126//! As before, Pll clock is configured and enabled.
127//!
128//! ```rust,ignore
129//! pll.set_frequency_mhz(100);
130//! pll.enable();
131//! ```
132//!
133//! Because of the high frequency of the PLL clock, both APB1 and APB2 prescalers must be
134//! configured.
135//!
136//! ```rust,ignore
137//! clocks.set_apb1_prescaler(APBPrescaler::DivideBy4);
138//! clocks.set_apb2_prescaler(APBPrescaler::DivideBy2);
139//! ```
140//!
141//! As an alternative, the AHB prescaler could be configured to change both APB1 and APB2
142//! frequencies.
143//!
144//! ```rust,ignore
145//! // Changing it to 2 wouldn't work, because it would give a frequency of 50MHz for the APB1.
146//! clocks.set_ahb_prescaler(APBPrescaler::DivideBy4);
147//! ```
148//!
149//! Now, it's safe to change the system clock source:
150//!
151//! ```rust,ignore
152//! clocks.set_sys_clock_source(SysClockSource::PLL);
153//! ```
154//!
155//! [^usage_note]: For the purpose of brevity, any error checking has been removed.
156
157use crate::chip_specific::ChipSpecs as ChipSpecsTrait;
158use crate::clocks::hse::Hse;
159use crate::clocks::hsi::Hsi;
160use crate::clocks::hsi::HSI_FREQUENCY_MHZ;
161use crate::clocks::pll::Pll;
162use crate::flash::Flash;
163use crate::rcc::AHBPrescaler;
164use crate::rcc::APBPrescaler;
165use crate::rcc::MCO1Divider;
166use crate::rcc::MCO1Source;
167use crate::rcc::PllSource;
168use crate::rcc::Rcc;
169use crate::rcc::SysClockSource;
170
171use kernel::debug;
172use kernel::utilities::cells::OptionalCell;
173use kernel::ErrorCode;
174
175/// Main struct for configuring on-board clocks.
176pub struct Clocks<'a, ChipSpecs> {
177    rcc: &'a Rcc,
178    flash: OptionalCell<&'a Flash<ChipSpecs>>,
179    /// High speed internal clock
180    pub hsi: Hsi<'a>,
181    /// High speed external clock
182    pub hse: Hse<'a>,
183    /// Main phase loop-lock clock
184    pub pll: Pll<'a, ChipSpecs>,
185}
186
187impl<'a, ChipSpecs: ChipSpecsTrait> Clocks<'a, ChipSpecs> {
188    // The constructor must be called when the default peripherals are created
189    pub fn new(rcc: &'a Rcc) -> Self {
190        Self {
191            rcc,
192            flash: OptionalCell::empty(),
193            hsi: Hsi::new(rcc),
194            hse: Hse::new(rcc),
195            pll: Pll::new(rcc),
196        }
197    }
198
199    // This method should be called when the dependencies are resolved
200    pub(crate) fn set_flash(&self, flash: &'a Flash<ChipSpecs>) {
201        self.flash.set(flash);
202    }
203
204    /// Set the AHB prescaler
205    ///
206    /// AHB bus, core, memory, DMA, Cortex System timer and FCLK Cortex free-running clock
207    /// frequencies are equal to the system clock frequency divided by the AHB prescaler.
208    ///
209    /// # Errors:
210    ///
211    /// + [Err]\([ErrorCode::FAIL]\) if changing the AHB prescaler doesn't preserve APB frequency
212    /// constraints
213    /// + [Err]\([ErrorCode::BUSY]\) if changing the AHB prescaler took too long. Retry.
214    pub fn set_ahb_prescaler(&self, prescaler: AHBPrescaler) -> Result<(), ErrorCode> {
215        // Changing the AHB prescaler affects the APB frequencies. A check must be done to ensure
216        // that the constraints are still valid
217        let divider: usize = prescaler.into();
218        let new_ahb_frequency = self.get_sys_clock_frequency_mhz() / divider;
219        if !self.check_apb1_frequency_limit(new_ahb_frequency)
220            || !self.check_apb2_frequency_limit(new_ahb_frequency)
221        {
222            return Err(ErrorCode::FAIL);
223        }
224
225        self.rcc.set_ahb_prescaler(prescaler);
226
227        for _ in 0..16 {
228            if self.get_ahb_prescaler() == prescaler {
229                return Ok(());
230            }
231        }
232
233        Err(ErrorCode::BUSY)
234    }
235
236    /// Get the current configured AHB prescaler
237    pub fn get_ahb_prescaler(&self) -> AHBPrescaler {
238        self.rcc.get_ahb_prescaler()
239    }
240
241    /// Get the frequency of the AHB
242    pub fn get_ahb_frequency_mhz(&self) -> usize {
243        let ahb_divider: usize = self.get_ahb_prescaler().into();
244        self.get_sys_clock_frequency_mhz() / ahb_divider
245    }
246
247    // APB1 frequency must not be higher than the maximum allowable frequency. This method is
248    // called when the system clock source is changed. The ahb_frequency_mhz is the
249    // hypothetical future frequency.
250    fn check_apb1_frequency_limit(&self, ahb_frequency_mhz: usize) -> bool {
251        ahb_frequency_mhz
252            <= ChipSpecs::APB1_FREQUENCY_LIMIT_MHZ
253                * Into::<usize>::into(self.rcc.get_apb1_prescaler())
254    }
255
256    /// Set the APB1 prescaler.
257    ///
258    /// The APB1 peripheral clock frequency is equal to the AHB frequency divided by the APB1
259    /// prescaler.
260    ///
261    /// # Errors:
262    ///
263    /// + [Err]\([ErrorCode::FAIL]\) if the desired prescaler would break the APB1 frequency limit
264    /// + [Err]\([ErrorCode::BUSY]\) if setting the prescaler took too long. Retry.
265    pub fn set_apb1_prescaler(&self, prescaler: APBPrescaler) -> Result<(), ErrorCode> {
266        let ahb_frequency = self.get_ahb_frequency_mhz();
267        let divider: usize = prescaler.into();
268        if ahb_frequency / divider > ChipSpecs::APB1_FREQUENCY_LIMIT_MHZ {
269            return Err(ErrorCode::FAIL);
270        }
271
272        self.rcc.set_apb1_prescaler(prescaler);
273
274        for _ in 0..16 {
275            if self.rcc.get_apb1_prescaler() == prescaler {
276                return Ok(());
277            }
278        }
279
280        Err(ErrorCode::BUSY)
281    }
282
283    /// Get the current configured APB1 prescaler
284    pub fn get_apb1_prescaler(&self) -> APBPrescaler {
285        self.rcc.get_apb1_prescaler()
286    }
287
288    /// Get the current APB1 frequency
289    pub fn get_apb1_frequency_mhz(&self) -> usize {
290        // Every enum variant can be converted into a usize
291        let divider: usize = self.rcc.get_apb1_prescaler().into();
292        self.get_ahb_frequency_mhz() / divider
293    }
294
295    // Same as for APB1, APB2 has a frequency limit that must be enforced by software
296    fn check_apb2_frequency_limit(&self, ahb_frequency_mhz: usize) -> bool {
297        ahb_frequency_mhz
298            <= ChipSpecs::APB2_FREQUENCY_LIMIT_MHZ
299                * Into::<usize>::into(self.rcc.get_apb2_prescaler())
300    }
301
302    /// Set the APB2 prescaler.
303    ///
304    /// The APB2 peripheral clock frequency is equal to the AHB frequency divided by the APB2
305    /// prescaler.
306    ///
307    /// # Errors:
308    ///
309    /// + [Err]\([ErrorCode::FAIL]\) if the desired prescaler would break the APB2 frequency limit
310    /// + [Err]\([ErrorCode::BUSY]\) if setting the prescaler took too long. Retry.
311    pub fn set_apb2_prescaler(&self, prescaler: APBPrescaler) -> Result<(), ErrorCode> {
312        let current_ahb_frequency = self.get_ahb_frequency_mhz();
313        let divider: usize = prescaler.into();
314        if current_ahb_frequency / divider > ChipSpecs::APB2_FREQUENCY_LIMIT_MHZ {
315            return Err(ErrorCode::FAIL);
316        }
317
318        self.rcc.set_apb2_prescaler(prescaler);
319
320        for _ in 0..16 {
321            if self.rcc.get_apb2_prescaler() == prescaler {
322                return Ok(());
323            }
324        }
325
326        Err(ErrorCode::BUSY)
327    }
328
329    /// Get the current configured APB2 prescaler
330    pub fn get_apb2_prescaler(&self) -> APBPrescaler {
331        self.rcc.get_apb2_prescaler()
332    }
333
334    /// Get the current APB2 frequency
335    pub fn get_apb2_frequency_mhz(&self) -> usize {
336        // Every enum variant can be converted into a usize
337        let divider: usize = self.rcc.get_apb2_prescaler().into();
338        self.get_ahb_frequency_mhz() / divider
339    }
340
341    /// Set the system clock source
342    ///
343    /// # Errors:
344    ///
345    /// + [Err]\([ErrorCode::FAIL]\) if the source is not enabled.
346    /// + [Err]\([ErrorCode::SIZE]\) if the source frequency surpasses the system clock frequency
347    /// limit, or the APB1 and APB2 limits are not satisfied.
348    /// + [Err]\([ErrorCode::BUSY]\) if the source switching took too long. Retry.
349    pub fn set_sys_clock_source(&self, source: SysClockSource) -> Result<(), ErrorCode> {
350        // Immediately return if the required source is already configured as the system clock
351        // source. Should this maybe be Err(ErrorCode::ALREADY)?
352        if source == self.get_sys_clock_source() {
353            return Ok(());
354        }
355
356        // Ensure the source is enabled before configuring it as the system clock source
357        if let false = match source {
358            SysClockSource::HSI => self.hsi.is_enabled(),
359            SysClockSource::HSE => self.hse.is_enabled(),
360            SysClockSource::PLL => self.pll.is_enabled(),
361        } {
362            return Err(ErrorCode::FAIL);
363        }
364
365        let current_frequency = self.get_sys_clock_frequency_mhz();
366
367        // Get the frequency of the source to be configured
368        let alternate_frequency = match source {
369            // The unwrap can't fail because the source clock status was checked before
370            SysClockSource::HSI => self.hsi.get_frequency_mhz().unwrap(),
371            SysClockSource::HSE => self.hse.get_frequency_mhz().unwrap(),
372            SysClockSource::PLL => self.pll.get_frequency_mhz().unwrap(),
373        };
374
375        // Check the alternate frequency is not higher than the system clock limit
376        if alternate_frequency > ChipSpecs::SYS_CLOCK_FREQUENCY_LIMIT_MHZ {
377            return Err(ErrorCode::SIZE);
378        }
379
380        // Retrieve the currently configured AHB prescaler
381        let ahb_divider: usize = self.get_ahb_prescaler().into();
382        // Compute the possible future AHB frequency
383        let ahb_frequency = alternate_frequency / ahb_divider;
384
385        // APB1 frequency must not exceed APB1_FREQUENCY_LIMIT_MHZ
386        if !self.check_apb1_frequency_limit(ahb_frequency) {
387            return Err(ErrorCode::SIZE);
388        }
389
390        // APB2 frequency must not exceed APB2_FREQUENCY_LIMIT_MHZ
391        if !self.check_apb2_frequency_limit(ahb_frequency) {
392            return Err(ErrorCode::SIZE);
393        }
394
395        // The documentation recommends the following sequence when changing the system clock
396        // frequency:
397        //
398        // + if the desired frequency is higher than the current frequency, first change flash
399        // latency, then set the new system clock source.
400        // + if the desired frequency is lower than the current frequency, first change the system
401        // clock source, then set the flash latency
402        if alternate_frequency > current_frequency {
403            self.flash
404                .unwrap_or_panic()
405                .set_latency(alternate_frequency)?;
406        }
407        self.rcc.set_sys_clock_source(source);
408        if alternate_frequency < current_frequency {
409            self.flash
410                .unwrap_or_panic()
411                .set_latency(alternate_frequency)?;
412        }
413
414        // If this point is reached, everything worked as expected
415        Ok(())
416    }
417
418    /// Get the current system clock source
419    pub fn get_sys_clock_source(&self) -> SysClockSource {
420        self.rcc.get_sys_clock_source()
421    }
422
423    /// Get the current system clock frequency in MHz
424    pub fn get_sys_clock_frequency_mhz(&self) -> usize {
425        match self.get_sys_clock_source() {
426            // These unwraps can't panic because set_sys_clock_frequency ensures that the source is
427            // enabled. Also, Hsi and Pll structs ensure that the clocks can't be disabled when
428            // they are configured as the system clock
429            SysClockSource::HSI => self.hsi.get_frequency_mhz().unwrap(),
430            SysClockSource::HSE => self.hse.get_frequency_mhz().unwrap(),
431            SysClockSource::PLL => self.pll.get_frequency_mhz().unwrap(),
432        }
433    }
434
435    /// Get the current system clock frequency in MHz from RCC registers instead of the cached
436    /// value. Used for debug only.
437    pub fn _get_sys_clock_frequency_mhz_no_cache(&self) -> usize {
438        match self.get_sys_clock_source() {
439            // These unwraps can't panic because set_sys_clock_frequency ensures that the source is
440            // enabled. Also, Hsi and Pll structs ensure that the clocks can't be disabled when
441            // they are configured as the system clock
442            SysClockSource::HSI => self.hsi.get_frequency_mhz().unwrap(),
443            SysClockSource::HSE => self.hse.get_frequency_mhz().unwrap(),
444            SysClockSource::PLL => {
445                let pll_source_frequency = match self.rcc.get_pll_clocks_source() {
446                    PllSource::HSI => self.hsi.get_frequency_mhz().unwrap(),
447                    PllSource::HSE => self.hse.get_frequency_mhz().unwrap(),
448                };
449                self.pll
450                    .get_frequency_mhz_no_cache(pll_source_frequency)
451                    .unwrap()
452            }
453        }
454    }
455
456    /// Set the frequency of the PLL clock.
457    ///
458    /// # Parameters
459    ///
460    /// + pll_source: PLL source clock (HSI or HSE)
461    ///
462    /// + desired_frequency_mhz: the desired frequency in MHz. Supported values: 24-216MHz for
463    /// STM32F401 and 13-216MHz for all the other chips
464    ///
465    /// # Errors
466    ///
467    /// + [Err]\([ErrorCode::INVAL]\): if the desired frequency can't be achieved
468    /// + [Err]\([ErrorCode::FAIL]\): if the PLL clock is already enabled. It must be disabled before
469    pub fn set_pll_frequency_mhz(
470        &self,
471        pll_source: PllSource,
472        desired_frequency_mhz: usize,
473    ) -> Result<(), ErrorCode> {
474        let source_frequency = match pll_source {
475            PllSource::HSI => HSI_FREQUENCY_MHZ,
476            PllSource::HSE => self.hse.get_frequency_mhz().unwrap(),
477        };
478        self.pll
479            .set_frequency_mhz(pll_source, source_frequency, desired_frequency_mhz)
480    }
481
482    /// Set the clock source for the microcontroller clock output 1 (MCO1)
483    ///
484    /// # Errors:
485    ///
486    /// + [Err]\([ErrorCode::FAIL]\) if the source apart from HSI is already enabled.
487    pub fn set_mco1_clock_source(&self, source: MCO1Source) -> Result<(), ErrorCode> {
488        match source {
489            MCO1Source::HSE => {
490                if !self.hse.is_enabled() {
491                    return Err(ErrorCode::FAIL);
492                }
493            }
494            MCO1Source::PLL => {
495                if self.pll.is_enabled() {
496                    return Err(ErrorCode::FAIL);
497                }
498            }
499            _ => (),
500        }
501
502        self.rcc.set_mco1_clock_source(source);
503
504        Ok(())
505    }
506
507    /// Get the clock source of the MCO1
508    pub fn get_mco1_clock_source(&self) -> MCO1Source {
509        self.rcc.get_mco1_clock_source()
510    }
511
512    /// Set MCO1 divider
513    ///
514    /// # Errors:
515    ///
516    /// + [Err]\([ErrorCode::FAIL]\) if the configured source apart from HSI is already enabled.
517    pub fn set_mco1_clock_divider(&self, divider: MCO1Divider) -> Result<(), ErrorCode> {
518        match self.get_mco1_clock_source() {
519            MCO1Source::PLL => {
520                if self.pll.is_enabled() {
521                    return Err(ErrorCode::FAIL);
522                }
523            }
524            MCO1Source::HSI => (),
525            MCO1Source::HSE => (),
526        }
527
528        self.rcc.set_mco1_clock_divider(divider);
529
530        Ok(())
531    }
532
533    /// Get MCO1 divider
534    pub fn get_mco1_clock_divider(&self) -> MCO1Divider {
535        self.rcc.get_mco1_clock_divider()
536    }
537}
538
539/// Stm32f4Clocks trait
540///
541/// This can be used to control clocks without the need to keep a reference of the chip specific
542/// Clocks struct, for instance by peripherals
543pub trait Stm32f4Clocks {
544    /// Get RCC instance
545    fn get_rcc(&self) -> &Rcc;
546
547    /// Get current AHB clock (HCLK) frequency in Hz
548    fn get_ahb_frequency(&self) -> usize;
549
550    // Extend this to expose additional clock resources
551}
552
553impl<'a, ChipSpecs: ChipSpecsTrait> Stm32f4Clocks for Clocks<'a, ChipSpecs> {
554    fn get_rcc(&self) -> &'a Rcc {
555        self.rcc
556    }
557
558    fn get_ahb_frequency(&self) -> usize {
559        self.get_ahb_frequency_mhz() * 1_000_000
560    }
561}
562
563/// Tests for clocks functionalities
564///
565/// These tests ensure the clocks are properly working. If any changes are made to the clock
566/// module, make sure to run these tests.
567///
568/// # Usage
569///
570/// First, import the [crate::clocks] module inside the board main file:
571///
572/// ```rust,ignore
573/// // This example assumes a STM32F429 chip
574/// use stm32f429zi::clocks;
575/// ```
576///
577/// To run all the available tests, add this line before **kernel::process::load_processes()**:
578///
579/// ```rust,ignore
580/// clocks::tests::run_all(&peripherals.stm32f4.clocks);
581/// ```
582///
583/// If everything works as expected, the following message should be printed on the kernel console:
584///
585/// ```text
586/// ===============================================
587/// Testing clocks...
588///
589/// ===============================================
590/// Testing HSI...
591/// Finished testing HSI. Everything is alright!
592/// ===============================================
593///
594///
595/// ===============================================
596/// Testing PLL...
597/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
598/// Testing PLL configuration...
599/// Finished testing PLL configuration.
600/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
601/// Testing PLL struct...
602/// Finished testing PLL struct.
603/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
604/// Finished testing PLL. Everything is alright!
605/// ===============================================
606///
607///
608/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
609/// Testing clocks struct...
610/// Finished testing clocks struct. Everything is alright!
611/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
612///
613/// Finished testing clocks. Everything is alright!
614/// ===============================================
615/// ```
616///
617/// There is also the possibility to run a part of the test suite. Check the functions present in
618/// this module for more details.
619///
620/// # Errors
621///
622/// If there are any errors, open an issue ticket at <https://github.com/tock/tock>. Please provide the
623/// output of the test execution.
624pub mod tests {
625    use super::{
626        debug, AHBPrescaler, APBPrescaler, ChipSpecsTrait, Clocks, ErrorCode, MCO1Divider,
627        MCO1Source, PllSource, SysClockSource, HSI_FREQUENCY_MHZ,
628    };
629
630    const LOW_FREQUENCY: usize = 25;
631    #[cfg(not(any(
632        feature = "stm32f401",
633        feature = "stm32f410",
634        feature = "stm32f411",
635        feature = "stm32f412",
636        feature = "stm32f413",
637        feature = "stm32f423"
638    )))]
639    const HIGH_FREQUENCY: usize = 112;
640    #[cfg(any(
641        feature = "stm32f401",
642        feature = "stm32f410",
643        feature = "stm32f411",
644        feature = "stm32f412",
645        feature = "stm32f413",
646        feature = "stm32f423"
647    ))]
648    const HIGH_FREQUENCY: usize = 80;
649
650    fn set_default_configuration<ChipSpecs: ChipSpecsTrait>(clocks: &Clocks<ChipSpecs>) {
651        assert_eq!(Ok(()), clocks.set_sys_clock_source(SysClockSource::HSI));
652        assert_eq!(Ok(()), clocks.pll.disable());
653        assert_eq!(Ok(()), clocks.set_ahb_prescaler(AHBPrescaler::DivideBy1));
654        assert_eq!(Ok(()), clocks.set_apb1_prescaler(APBPrescaler::DivideBy1));
655        assert_eq!(Ok(()), clocks.set_apb2_prescaler(APBPrescaler::DivideBy1));
656        assert_eq!(HSI_FREQUENCY_MHZ, clocks.get_sys_clock_frequency_mhz());
657        assert_eq!(HSI_FREQUENCY_MHZ, clocks.get_apb1_frequency_mhz());
658        assert_eq!(HSI_FREQUENCY_MHZ, clocks.get_apb2_frequency_mhz());
659    }
660
661    // This macro ensure that the system clock frequency goes back to the default value to prevent
662    // changing the UART baud rate
663    macro_rules! check_and_panic {
664        ($left:expr, $right:expr, $clocks: ident) => {
665            match (&$left, &$right) {
666                (left_val, right_val) => {
667                    if *left_val != *right_val {
668                        set_default_configuration($clocks);
669                        assert_eq!($left, $right);
670                    }
671                }
672            };
673        };
674    }
675
676    /// Test for the AHB and APB prescalers
677    ///
678    /// # Usage
679    ///
680    /// First, import the clock module:
681    ///
682    /// ```rust,ignore
683    /// // This test assumes a STM32F429 chip
684    /// use stm32f429zi::clocks;
685    /// ```
686    ///
687    /// Then run the test:
688    ///
689    /// ```rust,ignore
690    /// clocks::test::test_prescalers(&peripherals.stm32f4.clocks);
691    /// ```
692    pub fn test_prescalers<ChipSpecs: ChipSpecsTrait>(clocks: &Clocks<ChipSpecs>) {
693        // This test requires a bit of setup. A system clock running at HIGH_FREQUENCY is configured.
694        check_and_panic!(
695            Ok(()),
696            clocks
697                .pll
698                .set_frequency_mhz(PllSource::HSI, HSI_FREQUENCY_MHZ, HIGH_FREQUENCY),
699            clocks
700        );
701        check_and_panic!(Ok(()), clocks.pll.enable(), clocks);
702        check_and_panic!(
703            Ok(()),
704            clocks.set_apb1_prescaler(APBPrescaler::DivideBy4),
705            clocks
706        );
707        check_and_panic!(
708            Ok(()),
709            clocks.set_apb2_prescaler(APBPrescaler::DivideBy2),
710            clocks
711        );
712        check_and_panic!(
713            Ok(()),
714            clocks.set_sys_clock_source(SysClockSource::PLL),
715            clocks
716        );
717
718        // Trying to reduce the APB scaler to an invalid value should fail
719        check_and_panic!(
720            Err(ErrorCode::FAIL),
721            clocks.set_apb1_prescaler(APBPrescaler::DivideBy1),
722            clocks
723        );
724        // The following assert will pass on these models because of the low system clock
725        // frequency limit
726        #[cfg(not(any(
727            feature = "stm32f401",
728            feature = "stm32f410",
729            feature = "stm32f411",
730            feature = "stm32f412",
731            feature = "stm32f413",
732            feature = "stm32f423"
733        )))]
734        check_and_panic!(
735            Err(ErrorCode::FAIL),
736            clocks.set_apb2_prescaler(APBPrescaler::DivideBy1),
737            clocks
738        );
739        // Any failure in changing the APB prescalers must preserve their values
740        check_and_panic!(APBPrescaler::DivideBy4, clocks.get_apb1_prescaler(), clocks);
741        check_and_panic!(APBPrescaler::DivideBy2, clocks.get_apb2_prescaler(), clocks);
742
743        // Increasing the AHB prescaler should allow decreasing APB prescalers
744        check_and_panic!(
745            Ok(()),
746            clocks.set_ahb_prescaler(AHBPrescaler::DivideBy4),
747            clocks
748        );
749        check_and_panic!(
750            Ok(()),
751            clocks.set_apb1_prescaler(APBPrescaler::DivideBy1),
752            clocks
753        );
754        check_and_panic!(
755            Ok(()),
756            clocks.set_apb2_prescaler(APBPrescaler::DivideBy1),
757            clocks
758        );
759
760        // Now, decreasing the AHB prescaler would result in the violation of APB constraints
761        check_and_panic!(
762            Err(ErrorCode::FAIL),
763            clocks.set_ahb_prescaler(AHBPrescaler::DivideBy1),
764            clocks
765        );
766        // Any failure in changing the AHB prescaler must preserve its value
767        check_and_panic!(AHBPrescaler::DivideBy4, clocks.get_ahb_prescaler(), clocks);
768
769        // Revert to default configuration
770        set_default_configuration(clocks);
771    }
772
773    /// Test for the [crate::clocks::Clocks] struct
774    ///
775    /// # Usage
776    ///
777    /// First, import the clock module:
778    ///
779    /// ```rust,ignore
780    /// // This test assumes a STM32F429 chip
781    /// use stm32f429zi::clocks;
782    /// ```
783    ///
784    /// Then run the test:
785    ///
786    /// ```rust,ignore
787    /// clocks::test::test_clocks_struct(&peripherals.stm32f4.clocks);
788    /// ```
789    pub fn test_clocks_struct<ChipSpecs: ChipSpecsTrait>(clocks: &Clocks<ChipSpecs>) {
790        debug!("");
791        debug!("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
792        debug!("Testing clocks struct...");
793
794        // By default, the HSI clock is the system clock
795        check_and_panic!(SysClockSource::HSI, clocks.get_sys_clock_source(), clocks);
796
797        // HSI frequency is 16MHz
798        check_and_panic!(
799            HSI_FREQUENCY_MHZ,
800            clocks.get_sys_clock_frequency_mhz(),
801            clocks
802        );
803
804        // APB1 default prescaler is 1
805        check_and_panic!(APBPrescaler::DivideBy1, clocks.get_apb1_prescaler(), clocks);
806
807        // APB1 default frequency is 16MHz
808        check_and_panic!(HSI_FREQUENCY_MHZ, clocks.get_apb1_frequency_mhz(), clocks);
809
810        // APB2 default prescaler is 1
811        check_and_panic!(APBPrescaler::DivideBy1, clocks.get_apb1_prescaler(), clocks);
812
813        // APB2 default frequency is 16MHz
814        check_and_panic!(HSI_FREQUENCY_MHZ, clocks.get_apb2_frequency_mhz(), clocks);
815
816        // Attempting to change the system clock source with a disabled source
817        check_and_panic!(
818            Err(ErrorCode::FAIL),
819            clocks.set_sys_clock_source(SysClockSource::PLL),
820            clocks
821        );
822
823        // Attempting to set twice the same system clock source is fine
824        check_and_panic!(
825            Ok(()),
826            clocks.set_sys_clock_source(SysClockSource::HSI),
827            clocks
828        );
829
830        // Change the system clock source to a low frequency so that APB prescalers don't need to be
831        // changed
832        check_and_panic!(
833            Ok(()),
834            clocks
835                .pll
836                .set_frequency_mhz(PllSource::HSI, HSI_FREQUENCY_MHZ, LOW_FREQUENCY),
837            clocks
838        );
839        check_and_panic!(Ok(()), clocks.pll.enable(), clocks);
840        check_and_panic!(
841            Ok(()),
842            clocks.set_sys_clock_source(SysClockSource::PLL),
843            clocks
844        );
845        check_and_panic!(SysClockSource::PLL, clocks.get_sys_clock_source(), clocks);
846
847        // Now the system clock frequency is equal to 25MHz
848        check_and_panic!(LOW_FREQUENCY, clocks.get_sys_clock_frequency_mhz(), clocks);
849
850        // APB1 and APB2 frequencies must also be 25MHz
851        check_and_panic!(LOW_FREQUENCY, clocks.get_apb1_frequency_mhz(), clocks);
852        check_and_panic!(LOW_FREQUENCY, clocks.get_apb2_frequency_mhz(), clocks);
853
854        // Attempting to disable PLL when it is configured as the system clock must fail
855        check_and_panic!(Err(ErrorCode::FAIL), clocks.pll.disable(), clocks);
856        // Same for the HSI since it is used indirectly as a system clock through PLL
857        check_and_panic!(Err(ErrorCode::FAIL), clocks.hsi.disable(), clocks);
858
859        // Revert to default system clock configuration
860        set_default_configuration(clocks);
861
862        // Attempting to change the system clock frequency without correctly configuring the APB1
863        // prescaler (freq_APB1 <= APB1_FREQUENCY_LIMIT_MHZ) and APB2 prescaler
864        // (freq_APB2 <= APB2_FREQUENCY_LIMIT_MHZ) must fail
865        check_and_panic!(Ok(()), clocks.pll.disable(), clocks);
866        check_and_panic!(
867            Ok(()),
868            clocks
869                .pll
870                .set_frequency_mhz(PllSource::HSI, HSI_FREQUENCY_MHZ, HIGH_FREQUENCY),
871            clocks
872        );
873        check_and_panic!(Ok(()), clocks.pll.enable(), clocks);
874        check_and_panic!(
875            Err(ErrorCode::SIZE),
876            clocks.set_sys_clock_source(SysClockSource::PLL),
877            clocks
878        );
879
880        // Even if the APB1 prescaler is changed to 2, it must fail
881        // (HIGH_FREQUENCY / 2 > APB1_FREQUENCY_LIMIT_MHZ)
882        check_and_panic!(
883            Ok(()),
884            clocks.set_apb1_prescaler(APBPrescaler::DivideBy2),
885            clocks
886        );
887        #[cfg(not(any(
888            feature = "stm32f401",
889            feature = "stm32f410",
890            feature = "stm32f411",
891            feature = "stm32f412",
892            feature = "stm32f413",
893            feature = "stm32f423"
894        )))]
895        check_and_panic!(
896            Err(ErrorCode::SIZE),
897            clocks.set_sys_clock_source(SysClockSource::PLL),
898            clocks
899        );
900
901        // Configuring APB1 prescaler to 4 is fine, but APB2 prescaler is still wrong
902        check_and_panic!(
903            Ok(()),
904            clocks.set_apb1_prescaler(APBPrescaler::DivideBy4),
905            clocks
906        );
907        #[cfg(not(any(
908            feature = "stm32f401",
909            feature = "stm32f410",
910            feature = "stm32f411",
911            feature = "stm32f412",
912            feature = "stm32f413",
913            feature = "stm32f423"
914        )))]
915        check_and_panic!(
916            Err(ErrorCode::SIZE),
917            clocks.set_sys_clock_source(SysClockSource::PLL),
918            clocks
919        );
920
921        // Configuring APB2 prescaler to 2
922        check_and_panic!(
923            Ok(()),
924            clocks.set_apb2_prescaler(APBPrescaler::DivideBy2),
925            clocks
926        );
927
928        // Now the system clock source can be changed
929        check_and_panic!(
930            Ok(()),
931            clocks.set_sys_clock_source(SysClockSource::PLL),
932            clocks
933        );
934        check_and_panic!(HIGH_FREQUENCY / 4, clocks.get_apb1_frequency_mhz(), clocks);
935        check_and_panic!(HIGH_FREQUENCY / 2, clocks.get_apb2_frequency_mhz(), clocks);
936
937        // Revert to default system clock configuration
938        set_default_configuration(clocks);
939
940        // This time, configure the AHB prescaler instead of APB prescalers
941        check_and_panic!(
942            Ok(()),
943            clocks.set_ahb_prescaler(AHBPrescaler::DivideBy4),
944            clocks
945        );
946        check_and_panic!(Ok(()), clocks.pll.enable(), clocks);
947        check_and_panic!(
948            Ok(()),
949            clocks.set_sys_clock_source(SysClockSource::PLL),
950            clocks
951        );
952        check_and_panic!(HIGH_FREQUENCY / 4, clocks.get_ahb_frequency_mhz(), clocks);
953        check_and_panic!(HIGH_FREQUENCY / 4, clocks.get_apb1_frequency_mhz(), clocks);
954        check_and_panic!(HIGH_FREQUENCY / 4, clocks.get_apb2_frequency_mhz(), clocks);
955
956        // Revert to default configuration
957        set_default_configuration(clocks);
958
959        debug!("Finished testing clocks struct. Everything is alright!");
960        debug!("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
961        debug!("");
962    }
963
964    /// Test for the microcontroller clock outputs
965    ///
966    /// # Usage
967    ///
968    /// First, import the clock module:
969    ///
970    /// ```rust,ignore
971    /// // This test assumes a STM32F429 chip
972    /// use stm32f429zi::clocks;
973    /// ```
974    ///
975    /// Then run the test:
976    ///
977    /// ```rust,ignore
978    /// clocks::test::test_mco(&peripherals.stm32f4.clocks);
979    /// ```
980    pub fn test_mco<ChipSpecs: ChipSpecsTrait>(clocks: &Clocks<ChipSpecs>) {
981        debug!("");
982        debug!("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
983        debug!("Testing MCOs...");
984
985        // Set MCO1 source to PLL
986        assert_eq!(Ok(()), clocks.set_mco1_clock_source(MCO1Source::PLL));
987
988        // Set MCO1 divider to 3
989        assert_eq!(
990            Ok(()),
991            clocks.set_mco1_clock_divider(MCO1Divider::DivideBy3)
992        );
993
994        // Enable PLL
995        assert_eq!(Ok(()), clocks.pll.enable());
996
997        // Attempting to change the divider while the PLL is running must fail
998        assert_eq!(
999            Err(ErrorCode::FAIL),
1000            clocks.set_mco1_clock_divider(MCO1Divider::DivideBy2)
1001        );
1002
1003        // Switch back to HSI
1004        assert_eq!(Ok(()), clocks.set_mco1_clock_source(MCO1Source::HSI));
1005
1006        // Attempting to change the source to PLL when it is already enabled must fail
1007        assert_eq!(
1008            Err(ErrorCode::FAIL),
1009            clocks.set_mco1_clock_source(MCO1Source::PLL)
1010        );
1011
1012        debug!("Finished testing MCOs. Everything is alright!");
1013        debug!("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
1014        debug!("");
1015    }
1016
1017    /// Run the entire test suite for all clocks
1018    pub fn run_all<ChipSpecs: ChipSpecsTrait>(clocks: &Clocks<ChipSpecs>) {
1019        debug!("");
1020        debug!("===============================================");
1021        debug!("Testing clocks...");
1022
1023        crate::clocks::hsi::tests::run(&clocks.hsi);
1024        crate::clocks::pll::tests::run(&clocks.pll);
1025        test_prescalers(clocks);
1026        test_clocks_struct(clocks);
1027        test_mco(clocks);
1028
1029        debug!("Finished testing clocks. Everything is alright!");
1030        debug!("===============================================");
1031        debug!("");
1032    }
1033}