// Copyright (c) 2026 Rigless Labs.
// SPDX-License-Identifier: MIT OR Apache-2.0
//! Reset and clock control of RM0090 rev 22 chapter 6, written against the `rcc` layout.
//!
//! Chapter 6 is the RCC of the STM32F42xxx and STM32F43xxx, which is this SoC. Chapter 7 is the
//! same peripheral for the STM32F405/407/415/417. Its sections are numbered alike and read almost
//! the same, but it's the wrong part. Every citation here is chapter 6.
//!
//! | Register or section | Behaviour | Source | Test |
//! |---|---|---|---|
//! | Reset state | HSI on and ready, everything else off, HSI the system clock | 6.2.6, 6.3.1 | `reset_runs_on_the_ready_hsi` |
//! | `CR` oscillators | Each enable raises its ready flag after that oscillator's startup, and clearing the enable drops it | 6.3.1 | `an_oscillator_becomes_ready_after_its_startup`, `clearing_an_enable_drops_its_ready_flag` |
//! | `CR` `HSEBYP` | Writable only while the HSE is off | 6.3.1 | `hsebyp_is_writable_only_while_the_hse_is_off` |
//! | `CR` in-use rule | The enable of an oscillator that clocks the system, directly or through the PLL, can't be cleared | 6.3.1, 6.2.6 | `the_running_system_clock_cannot_be_switched_off` |
//! | `CR` ready flags | Hardware's. A write can't set one, so a firmware can't report an oscillator locked that never started | 6.3.1 | `software_cannot_set_a_flag_hardware_owns` |
//! | `CR` startup, cancelled | An enable cleared before its startup fires leaves no stale event behind | 6.3.1 | `a_startup_cancelled_before_it_fires_leaves_nothing_behind` |
//! | `PLLCFGR` | `f(VCO) = f(input) x PLLN / PLLM`, `f(PLL) = f(VCO) / PLLP`, `f(48 MHz) = f(VCO) / PLLQ` | 6.3.2 | `the_pll_multiplies_and_divides_as_the_formula_says`, `a_factor_of_zero_yields_no_clock_rather_than_a_division_by_zero` |
//! | `PLLCFGR` while locked | A write is ignored while `PLLON` is set, and `PLLM` and `PLLSRC`, which the PLLI2S shares, while either is on | 6.2.3, 6.3.2 | `a_locked_pll_ignores_its_configuration`, `the_shared_pll_input_is_frozen_by_either_pll` |
//! | `CR` in-use rule through the PLL | Neither the PLL nor the oscillator feeding it can be stopped while the PLL clocks the system, and a PLL whose oscillator does stop is no longer ready | 6.3.1, 6.2.6 | `the_pll_and_its_source_cannot_be_stopped_while_they_clock_the_system`, `a_pll_whose_oscillator_stops_is_no_longer_ready` |
//! | `CFGR` `SW`, `SWS` | The switch happens only when the target is ready, and is deferred until it becomes ready | 6.2.6, 6.3.3 | `the_switch_waits_for_the_target_to_be_ready` |
//! | `CFGR` `HPRE` | The AHB prescaler, whose encoding has no divide by 32 | 6.3.3 | `the_ahb_prescaler_skips_thirty_two` |
//! | `CFGR` `PPRE1`, `PPRE2` | The two APB prescalers | 6.3.3 | `the_apb_prescalers_divide_the_ahb_clock` |
//! | `AHBxENR`, `APBxENR` as clocks | Each instance that keeps time runs on a clock of its own: its source's rate while its enable is set, and none while it's clear | 2.1.11, figure 16, 6.3.10 to 6.3.14 | `a_port_whose_enable_is_clear_takes_nothing_a_peer_sends` |
//! | `CFGR` `RTCPRE` | The HSE division for the RTC, where 0 and 1 are no clock | 6.3.3 | `the_rtc_clock_is_the_hse_divided_or_nothing`, `rtcpre_of_zero_is_no_clock` |
//! | `CFGR` `MCO1`, `MCO2` | Both outputs, each of the four sources of either, and their prescalers | 6.3.3 | `the_clock_outputs_carry_their_source_divided`, `every_clock_output_source_is_what_the_encoding_says` |
//! | Clock outputs | Seven rates the tree drives as clocks the runtime carries, numbered [`HCLK`] to [`IWDG_CLK`], and after them one for each instance that keeps time, through its enable. Each is driven at the write, the ready event or the clock failure that changes it, and at no other. The part's description says which instance runs on which | 6.2 figure 16 | `the_tree_drives_its_clocks_when_the_switch_lands`, `a_clock_is_driven_when_it_changes_and_not_otherwise`, `the_48_mhz_domain_is_nothing_until_the_pll_is_ready` |
//! | Timer clocks | `TIMPRE` clear: `PCLKx` at a prescaler of 1, else twice it. `TIMPRE` set: `HCLK` at 1, 2, or 4, else four times `PCLKx` | 6.2, 6.3.25 | `the_timer_clock_follows_the_doubling_rule`, `timpre_gives_the_timers_hclk` |
//! | `CIR` | A ready flag rises only when that source's `RDYIE` already stands, and then it's what raises the interrupt. `RDYC` clears it. Each source has its own triplet | 6.3.4 | `a_ready_flag_rises_only_for_a_source_whose_interrupt_is_enabled`, `a_ready_event_sets_its_flag_and_an_enabled_one_interrupts`, `the_clear_bits_clear_and_read_zero`, `every_source_has_its_own_flag_enable_and_clear` |
//! | `CIR` flags | Bits 7:0 are hardware's. A write can't raise one, bit 6 included, which ST's file omits | 6.3.4 | `software_cannot_set_a_flag_hardware_owns` |
//! | `BDCR` LSE | Its ready flag, and a bypass writable only while the LSE is off | 6.3.20 | `the_lse_becomes_ready_and_clocks_the_rtc`, `lsebyp_is_writable_only_while_the_lse_is_off` |
//! | `BDCR` RTC | `RTCEN` gates `RTCCLK`, and `RTCSEL` can't be changed once chosen unless the domain is reset | 6.2.8, 6.3.20 | `the_rtc_source_is_chosen_once_and_the_enable_gates_it` |
//! | `BDCR` `BDRST` | Set and cleared by software: it stands until the firmware clears it and holds the domain at its reset value while it does | 6.3.20 | `the_backup_reset_holds_the_domain_until_it_is_cleared` |
//! | Watchdog clock | Starting the independent watchdog is a request line to this model: the LSI oscillator is forced running from there on, whatever a write leaves in `LSION`, and `IWDG_CLK` carries its rate, nothing until it's ready | 6.2.9, 6.3.21 | `starting_the_watchdog_keeps_the_lsi_ready_even_once_lsion_reads_cleared`, `iwdg_clk_is_the_lsi_once_it_is_ready` |
//! | `CSR` | The LSI with its ready flag, and the reset flags, which only `RMVF` clears and a plain write leaves alone | 6.3.21 | `the_lsi_becomes_ready`, `rmvf_clears_the_reset_flags`, `a_plain_write_to_csr_keeps_the_reset_flags` |
//! | The LSI's own rate | A board's calibration through [`Rcc::calibrate_lsi`], refused outside DS9405 rev 13 table 42's 17 to 47 kHz, 32 kHz typical until then, and kept across a system reset | 6.2.5, 6.2.11 | `calibrating_the_lsi_moves_the_watchdogs_timeout`, `calibrating_the_lsi_outside_its_range_is_refused` |
//! | `DCKCFGR` | `TIMPRE`, and the SAI1-A source and divisors, which [`Rcc::sai_clock`] answers. `SAI1BSRC` is stored: no model consumes it | 6.3.25 | `the_sai_clock_follows_its_source_and_divisor` |
//! | `PLLI2SCFGR`, `PLLSAICFGR` | `N` on the VCO, `R` for the PLLI2S output that MCO2 and the I2S see, and `Q` for the SAI. Ignored while the PLL they configure is on | 6.2.3, 6.3.23, 6.3.24 | `the_audio_plls_multiply_and_divide`, `an_audio_pll_with_a_factor_of_zero_gives_no_clock`, `a_locked_audio_pll_ignores_its_configuration` |
//! | Clock security | With `CSSON` set and the HSE running, a failure disables it, sets `CSSF`, and pulses [`CLOCK_FAILURE`] to the break inputs of TIM1 and TIM8. Only where the HSE clocked the system does it force the HSI and stop the PLL and PLLI2S | 6.2.7, 17.3.12 | `a_clock_failure_forces_the_hsi_and_flags_it`, `a_clock_failure_leaves_a_system_it_does_not_clock_alone`, `a_clock_failure_starts_the_oscillator_it_switches_to`, `a_detector_that_is_off_or_has_nothing_to_watch_reports_nothing`, `the_clock_securitys_failure_reaches_the_advanced_timers_break` |
//! | `AHBxRSTR`, `APBxRSTR`, and every enable no modelled instance answers behind | Stored and readable, and nothing acts on them. The enables that gate a modelled instance are the row further down | 6.3.5 to 6.3.14 | `an_enable_bit_is_stored`, `a_reset_bit_is_stored` |
//! | `AHBxLPENR`, `APBxLPENR` | Stored. The low-power modes they qualify aren't modeled, and all three registers come out of reset with every bit set, which ST's file gets wrong | 6.3.15 to 6.3.19 | `a_low_power_enable_is_stored`, `the_low_power_enables_reset_with_every_peripheral_set` |
//! | `SSCGR` | Stored. Spread spectrum modulates the PLL to spread its emissions. It has no observable effect on a clock a deterministic emulator counts in whole nanoseconds | 6.3.22 | `spread_spectrum_is_stored_and_changes_nothing` |
//! | 6.1.1, 6.1.2, 6.1.3 | The three resets, as the flags they leave in `CSR` and the domain `BDRST` clears. A software reset the part restarts from leaves `SFTRSTF` beside the flags it had, and one from the pin `PADRSTF` alone | 6.1, 6.3.20, 6.3.21 | `rmvf_clears_the_reset_flags`, `the_backup_reset_holds_the_domain_until_it_is_cleared`, `a_reset_keeps_the_pins_and_the_backup_domain_and_nothing_else`, `a_reset_from_the_pin_leaves_its_own_flag_and_not_the_softwares` |
//! | `AHB1ENR`, `APB1ENR`, `APB2ENR` | Every bit here is a gate on the instance it names: an instance whose enable is clear doesn't answer its registers at all. The bits are the model's, and the part's binding is what consults them | 2.1.11, 6.3.10, 6.3.13, 6.3.14 | `a_register_answers_its_reset_value_once_its_clock_stands_and_not_before`, `every_clock_gate_is_named_for_the_instance_it_gates` |
//! | `BDCR` | The backup domain takes no write while `PWR_CR`'s `DBP` is clear, and answers a read throughout. It's the same mechanism, over one register and in one direction | 5.5.1, 6.3.20 | `the_backup_domain_refuses_a_write_and_answers_a_read` |
//!
//! **What the model leaves out, and why.**
//!
//! | Behaviour | Source | Reason |
//! |---|---|---|
//! | How long a source takes to become ready | 6.3.1, DS9405 rev 13 | Section 6.3.1 fixes none of them, so every figure is the model's own, and a ready flag rises one microsecond after its enable. The datasheet's typical times are characteristics, not a programming contract, and modelling them would cost a firmware what it doesn't cost the part. The instruction rate is fixed whatever the clock tree says, so the 2 ms HSE is 500 000 instructions of a wait loop against the 32 000 the part retires on its 16 MHz HSI, and the 2 s LSE is half a billion. What the flag keeps is the part's order: hardware raises it, not the write. That's what tells a firmware that polls from one that doesn't. A machine that wants the datasheet's figure has no way to say so yet |
//! | A cleared enable drops its ready flag at once, where the part takes 6 oscillator cycles: 250 ns on a 24 MHz HSE, 183 us on the LSE | 6.3.1, 6.3.20 | A simplification, and the model's own. A firmware polls a ready flag to see it rise, not to watch it fall, and the direction this errs in is the one that can't hang a wait loop |
//! | The NMI a clock failure raises | 6.2.7 | A core exception, which the interrupt bitmap can't reach. A firmware sees `CSSF` and the forced switch |
//! | An HSE that stops while `CSSON` is clear | 6.2.7 | The detector is what makes a failure observable. With it off, nothing in this chapter reports one |
//! | An oscillator enabled with nothing on its pins reaching ready at no frequency | 6.3.1, 6.3.20 | The flag rises after the startup whatever is fitted, and every clock it drives reads zero |
//! | The remaining branches of figure 16: the I2S clock, whose `I2SSRC` is stored and not answered, SAI1-B, LTDC, the Ethernet and USB taps, and the core timer's `HCLK/8` | 6.2 | The seven outputs, and each instance's own clock through its enable, are driven as clocks. These are answered by method for a firmware that configures them, and driven to nobody, because nothing consumes them. The core's timer is the core's own model |
//! | Measuring the HSI, the LSE, or the LSI (6.2.11's own reason for the LSI: "to compute the IWDG timeout") against TIM5 or TIM11 | 6.2.11 | The wiring between a clock and a timer's input isn't built. `Rcc::calibrate_lsi` gives a board the number that measurement would answer, without the timer wiring itself |
//! | Section 6.2.3's broader reading of two rules the model takes from the register sections instead | 6.2.3, 6.2.7, 6.3.2 | Chapter 6 states each twice, and not alike. Section 6.2.3 says `PLLM` and `PLLSRC` are "common to both PLLs" and names all three, where section 6.3.2's own bit text says they "can be written only when the PLL and PLLI2S are disabled". Section 6.2.3 also says the PLLs are disabled "when HSE or PLL (clocked by HSE) are used as system clock", where section 6.2.7 says "the HSE oscillator clock was the clock source of PLL used as the system clock". The model follows the register section in the first case and section 6.2.7 in the second, because each is the narrower rule. Refusing a write or stopping a PLL that the part would have allowed is the failure a firmware can't work around |
//! | Holding a peripheral in reset through `RCC_AHBxRSTR` or `RCC_APBxRSTR`, so that it answers its reset values while the bit stands | 6.3.5 to 6.3.9 | The bits are stored. Being held at reset is a third behaviour beside the gate and the request line the runtime gives the part, and it isn't one of them: a gated instance doesn't answer at all, where one held at reset answers its reset values |
//! | ES0206 rev 24 2.2.7: a peripheral doesn't answer for two AHB cycles, or `1 + (AHB/APB prescaler)` APB cycles, after its clock is enabled | ES0206 rev 24 | A gate opens at the instant the bit is written: the runtime takes performance over cycle fidelity, and a delayed gate would mean holding an access back. Both firmwares apply the dummy-read workaround anyway |
//! | ES0206 rev 24 2.2.9: the backup domain's protection takes effect some APB1 cycles after `DBP` is programmed | ES0206 rev 24 | The same as 2.2.7 and for the same reason: the gate over `BDCR` opens at the write |
//! | ES0206 rev 24 2.2.10: PC13 transitions disturb the LSE crystal | ES0206 rev 24 | An analogue effect with no register behaviour |
//! | ES0206 rev 24 2.2.11: a backup domain reset missed after its supply drops into a window a few mV wide | ES0206 rev 24 | The model has no VBAT rail and no power cycle, so the domain always reaches its reset value |
//! | ES0206 rev 24 2.2.12: over-drive and under-drive unavailable on some revisions | ES0206 rev 24 | A power mode of `PWR`, which owns the row that settles it. What belongs here is the ceiling it would impose: the model applies no upper bound to any frequency, so a firmware may configure a tree the part couldn't run |
//!
//! **Where this model departs from the vendor's file.** ST's SVD splits `SW`, `SWS`, and the
//! `PLLCFGR` factors one field per bit, and omits `PLLI2SQ` and `CIR`'s three `PLLSAI` bits. So the
//! model declares them as sections 6.3.2, 6.3.3, 6.3.4, and 6.3.23 give them. The SVD types three
//! flags read-write that those sections type `r` (`CR` bit 29, `CIR` bit 6, and `CSR` bits 31:25),
//! and the model puts those back on every write. The register file enforces the rest from the
//! table, which stays the vendor's.
//!
use crate::device::{Device, DeviceContext, Level, RegisterFile, RequestChange, ResetCause};
use crate::soc::{Access, FieldSpec, Layout, ReadAction, WriteModify};
use crate::time::{EventId, VirtualNanos};
use crate::soc::stm32f427::generated::rcc::{
REGISTERS, Register, bdcr, cfgr, cir, cr, csr, dckcfgr, pllcfgr, plli2scfgr, pllsaicfgr,
};
/// The RCC's one interrupt, position zero of its instance's table. RM0090 rev 22 table 63 gives it
/// line 5.
const GLOBAL: usize = 0;
/// The internal oscillator that clocks the SoC out of reset, 16 MHz, RM0090 rev 22 section 6.2.2.
/// With `RCC_CFGR` at its reset value, every bus and both timer clocks run at this rate,
/// section 6.3.3. It's what a model that keeps time is built with until the tree drives it
/// otherwise.
pub const HSI_HZ: u64 = 16_000_000;
/// The clock outputs the tree drives, numbered as this model's own, RM0090 rev 22 section 6.2
/// figure 16. The part's description says which instance runs on which, and the model drives each
/// one whenever a write or a ready event changes what it comes to.
///
/// `HCLK` is the AHB clock, "to AHB bus, core, memory and DMA", so it's what the processor's own
/// cycle counter counts.
pub const HCLK: usize = 0;
/// The low-speed peripheral clock, `PCLK1`.
pub const PCLK1: usize = 1;
/// The high-speed peripheral clock, `PCLK2`.
pub const PCLK2: usize = 2;
/// What the timers on APB1 count, [`Rcc::timer_clock`] with [`Apb::One`].
pub const APB1_TIMERS: usize = 3;
/// What the timers on APB2 count, [`Rcc::timer_clock`] with [`Apb::Two`].
pub const APB2_TIMERS: usize = 4;
/// `PLL48CLK`, the 48 MHz domain the card interface, the USB core and the random number generator
/// run on, [`Rcc::pll48`].
pub const PLL48: usize = 5;
/// `IWDGCLK`, the LSI behind the watchdog's enable gate, RM0090 rev 22 figure 16 and section 6.2.9.
pub const IWDG_CLK: usize = 6;
/// The clock security system's failure, driven as a pulse on this instance's request output one.
/// It's the break input section 17.3.12 gives the advanced-control timers, "a clock failure event,
/// generated by the Clock Security System". Output zero is the backup domain's reset,
/// [`super::rtc::BACKUP_DOMAIN_RESET`].
pub const CLOCK_FAILURE: usize = 1;
/// How many clock outputs the model drives for the tree's own rates. The clock of each instance that
/// keeps time is one more after them, [`Rcc::gated`].
const OUTPUTS: usize = 7;
// `on_request`'s `change.instance` this model matches, section 6.2.9. `iwdg::STARTED` is the number.
const IWDG_STARTED_BY: &str = "IWDG";
/// The internal low-speed oscillator's typical rate, DS9405 rev 13 table 42, sections 6.2.5 and
/// 6.3.21 disagreeing between 32 and 40 kHz. What a board calibrates the model to before it, since
/// the oscillator is untrimmed silicon and 6.2.11 names measuring it against a timer for exactly
/// this: table 42's own 17 to 47 kHz range is what [`Rcc::calibrate_lsi`] refuses outside of.
const LSI_HZ_TYPICAL: u64 = 32_000;
const LSI_HZ_MIN: u64 = 17_000;
const LSI_HZ_MAX: u64 = 47_000;
const fn field(name: &'static str, bit: u8, width: u8, access: Access) -> FieldSpec {
FieldSpec {
name,
bit,
width,
access,
read: ReadAction::None,
write: WriteModify::None,
}
}
/// `SW`, the system clock switch, RM0090 rev 22 section 6.3.3. ST's file splits it into `SW0` and
/// `SW1`.
pub static SW: FieldSpec = field("SW", 0, 2, Access::ReadWrite);
/// `SWS`, the switch status, RM0090 rev 22 section 6.3.3. It's read-only, and ST's file splits it.
pub static SWS: FieldSpec = field("SWS", 2, 2, Access::ReadOnly);
/// `PLLM`, RM0090 rev 22 section 6.3.2. ST's file splits it into `PLLM0` to `PLLM5`.
pub static PLLM: FieldSpec = field("PLLM", 0, 6, Access::ReadWrite);
/// `PLLN`, RM0090 rev 22 section 6.3.2. ST's file splits it into `PLLN0` to `PLLN8`.
pub static PLLN: FieldSpec = field("PLLN", 6, 9, Access::ReadWrite);
/// `PLLP`, RM0090 rev 22 section 6.3.2. ST's file splits it into `PLLP0` and `PLLP1`.
pub static PLLP: FieldSpec = field("PLLP", 16, 2, Access::ReadWrite);
/// `PLLQ`, RM0090 rev 22 section 6.3.2. ST's file splits it into `PLLQ0` to `PLLQ3`.
pub static PLLQ: FieldSpec = field("PLLQ", 24, 4, Access::ReadWrite);
/// `PLLI2SQ`, RM0090 rev 22 section 6.3.23 bits 27:24, the PLLI2S divider for the SAI1 clock. ST's
/// file omits it, though it carries `PLLSAIQ` of the sibling register.
pub static PLLI2SQ: FieldSpec = field("PLLI2SQ", 24, 4, Access::ReadWrite);
/// `PLLSAIRDYF`, RM0090 rev 22 section 6.3.4 bit 6. ST's file omits it.
pub static PLLSAIRDYF: FieldSpec = field("PLLSAIRDYF", 6, 1, Access::ReadOnly);
/// `PLLSAIRDYIE`, RM0090 rev 22 section 6.3.4 bit 14. ST's file omits it.
pub static PLLSAIRDYIE: FieldSpec = field("PLLSAIRDYIE", 14, 1, Access::ReadWrite);
/// `PLLSAIRDYC`, RM0090 rev 22 section 6.3.4 bit 22. ST's file omits it.
pub static PLLSAIRDYC: FieldSpec = field("PLLSAIRDYC", 22, 1, Access::WriteOnly);
/// Port J's clock enable, RM0090 rev 22 section 6.3.10 bit 9. ST's file declares only bits 0 to 8.
pub static GPIOJEN: FieldSpec = field("GPIOJEN", 9, 1, Access::ReadWrite);
/// Port K's clock enable, RM0090 rev 22 section 6.3.10 bit 10. ST's file omits it.
pub static GPIOKEN: FieldSpec = field("GPIOKEN", 10, 1, Access::ReadWrite);
/// Port J's reset, RM0090 rev 22 section 6.3.5 bit 9. ST's file omits it.
pub static GPIOJRST: FieldSpec = field("GPIOJRST", 9, 1, Access::ReadWrite);
/// Port K's reset, RM0090 rev 22 section 6.3.5 bit 10. ST's file omits it.
pub static GPIOKRST: FieldSpec = field("GPIOKRST", 10, 1, Access::ReadWrite);
/// Port J's clock enable in sleep, RM0090 rev 22 section 6.3.15 bit 9. ST's file omits it.
pub static GPIOJLPEN: FieldSpec = field("GPIOJLPEN", 9, 1, Access::ReadWrite);
/// Port K's clock enable in sleep, RM0090 rev 22 section 6.3.15 bit 10. ST's file omits it.
pub static GPIOKLPEN: FieldSpec = field("GPIOKLPEN", 10, 1, Access::ReadWrite);
/// Every field this model declares because the vendor's file doesn't, or doesn't declare whole.
///
/// A generated table is the vendor's file transcribed, so a field ST omits or splits can't be added
/// to one. It's declared here instead. That lets a check ask the part what it declares
/// rather than only what ST did, and it's why none of the above needs an exemption anywhere.
pub static DECLARED: &[&FieldSpec] = &[
&SW,
&SWS,
&PLLM,
&PLLN,
&PLLP,
&PLLQ,
&PLLI2SQ,
&PLLSAIRDYF,
&PLLSAIRDYIE,
&PLLSAIRDYC,
&GPIOJEN,
&GPIOKEN,
&GPIOJRST,
&GPIOKRST,
&GPIOJLPEN,
&GPIOKLPEN,
];
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Source {
Hsi,
Hse,
Pll,
PllI2s,
PllSai,
Lse,
Lsi,
}
impl Source {
/// Every source, in the order their flags sit in `CIR`, RM0090 rev 22 section 6.3.4.
const ALL: [Self; 7] = [
Self::Lsi,
Self::Lse,
Self::Hsi,
Self::Hse,
Self::Pll,
Self::PllI2s,
Self::PllSai,
];
fn control(self) -> (Register, &'static FieldSpec, &'static FieldSpec) {
match self {
Self::Hsi => (Register::Cr, cr::HSION, cr::HSIRDY),
Self::Hse => (Register::Cr, cr::HSEON, cr::HSERDY),
Self::Pll => (Register::Cr, cr::PLLON, cr::PLLRDY),
Self::PllI2s => (Register::Cr, cr::PLLI2SON, cr::PLLI2SRDY),
Self::PllSai => (Register::Cr, cr::PLLSAION, cr::PLLSAIRDY),
Self::Lse => (Register::Bdcr, bdcr::LSEON, bdcr::LSERDY),
Self::Lsi => (Register::Csr, csr::LSION, csr::LSIRDY),
}
}
fn is_pll(self) -> bool {
matches!(self, Self::Pll | Self::PllI2s | Self::PllSai)
}
/// The `CIR` flag, enable, and clear bits of this source, RM0090 rev 22 section 6.3.4.
fn interrupt(self) -> (&'static FieldSpec, &'static FieldSpec, &'static FieldSpec) {
match self {
Self::Hsi => (cir::HSIRDYF, cir::HSIRDYIE, cir::HSIRDYC),
Self::Hse => (cir::HSERDYF, cir::HSERDYIE, cir::HSERDYC),
Self::Pll => (cir::PLLRDYF, cir::PLLRDYIE, cir::PLLRDYC),
Self::PllI2s => (cir::PLLI2SRDYF, cir::PLLI2SRDYIE, cir::PLLI2SRDYC),
Self::PllSai => (&PLLSAIRDYF, &PLLSAIRDYIE, &PLLSAIRDYC),
Self::Lse => (cir::LSERDYF, cir::LSERDYIE, cir::LSERDYC),
Self::Lsi => (cir::LSIRDYF, cir::LSIRDYIE, cir::LSIRDYC),
}
}
}
/// How long a source takes to become ready. RM0090 rev 22 section 6.3.1 fixes none of them. It says
/// only that hardware sets the flag once the oscillator is stable. DS9405 rev 13 tables 39 to 43
/// give characteristics, not a programming contract, so any figure here is the model's own. It's
/// one microsecond for every source, and deliberately short: the instruction rate is fixed
/// whatever the clock tree says, so the datasheet's 2 ms HSE costs a wait loop 500 000 instructions
/// against the 32 000 the part retires on its 16 MHz HSI, and its 2 s LSE costs half a billion. A
/// microsecond is 250. That's long enough that a firmware can still clear an enable inside it,
/// which is what section 6.3.1's cancelled startup and its re-arm rule are about. A figure shorter
/// than one instruction would put both out of every firmware's reach.
const STARTUP: VirtualNanos = VirtualNanos(1_000);
/// The reset and clock control of one STM32F42xxx, RM0090 rev 22 chapter 6.
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct Rcc {
hse_hz: u64,
lse_hz: u64,
pending: [Option<EventId>; Source::ALL.len()],
shadow: Shadow,
interrupt: Level,
/// The rate last driven on each clock output, or `None` before the first, so a rate is driven
/// when it changes and not on every write that leaves it alone.
driven: Vec<Option<u64>>,
/// Each gated instance's clock, in the order of the outputs after the tree's own: the output its
/// rate comes from, and the register and field of its enable, as indices into the tables.
gated: Vec<(usize, usize, usize)>,
/// Whether the independent watchdog has asked for the LSI, RM0090 rev 22 section 6.2.9: once set,
/// a write can no longer clear `LSION`.
iwdg_forces_lsi: bool,
/// The LSI's own rate, a board's to calibrate through [`Rcc::calibrate_lsi`].
lsi_hz: u64,
}
impl Default for Rcc {
fn default() -> Self {
Self::new()
}
}
impl Rcc {
/// The RCC of a bare part: nothing on either oscillator pin until a board fits a crystal
/// through [`Rcc::attach_crystals`]. A pin with nothing on it still reaches ready, at no
/// frequency, and whether it should is an open question.
///
/// Each instance that keeps time runs on a clock of its own: its source's rate while its enable
/// is set, and none while it's clear. RM0090 rev 22 figure 16 draws each peripheral's clock
/// through the enable bit sections 6.3.10 to 6.3.14 give it, so a peripheral whose enable is
/// clear has no clock and does nothing, even with its own registers set up to run.
#[must_use]
pub fn new() -> Self {
let gated: Vec<(usize, usize, usize)> = crate::soc::stm32f427::memory_map::clock_enables()
.into_iter()
.map(|(source, register, field)| {
let (index, spec) = REGISTERS
.iter()
.enumerate()
.find(|(_, spec)| spec.name == register)
.unwrap_or_else(|| panic!("the clock controller has no register {register}"));
let bit = spec
.fields
.iter()
.position(|spec| spec.name == field)
.unwrap_or_else(|| panic!("{register} has no field {field}"));
(source, index, bit)
})
.collect();
Self {
hse_hz: 0,
lse_hz: 0,
pending: [None; Source::ALL.len()],
shadow: Shadow::default(),
interrupt: Level::Low,
driven: vec![None; OUTPUTS + gated.len()],
gated,
iwdg_forces_lsi: false,
lsi_hz: LSI_HZ_TYPICAL,
}
}
/// The output the `n`th instance that keeps time runs on, in the part's order.
#[must_use]
pub(crate) const fn gated(n: usize) -> usize {
OUTPUTS + n
}
/// The crystals a board fits, in hertz, with zero for a pin it leaves empty.
/// RM0090 rev 22 section 6.2.1 takes the high-speed one between 4 and 26 MHz, and 6.2.4 the
/// low-speed one at 32.768 kHz.
///
/// A crystal is soldered on, so this is for before the first instruction and takes no register
/// file. What a running oscillator does when its frequency changes underneath it isn't a thing
/// boards do, and it isn't a thing this models. Whether an empty pin should reach ready at all
/// is an open question.
pub fn attach_crystals(&mut self, hse_hz: u64, lse_hz: u64) {
self.hse_hz = hse_hz;
self.lse_hz = lse_hz;
}
/// A board's calibration of the LSI, which panics outside DS9405 rev 13 table 42's 17 to 47 kHz.
pub fn calibrate_lsi(&mut self, hz: u64) {
assert!(
(LSI_HZ_MIN..=LSI_HZ_MAX).contains(&hz),
"DS9405 rev 13 table 42 gives the LSI 17 to 47 kHz, and {hz} Hz is outside it"
);
self.lsi_hz = hz;
}
/// The LSI's rate, a board's calibration or the typical 32 kHz.
#[must_use]
pub const fn lsi_hz(&self) -> u64 {
self.lsi_hz
}
/// The crystals a board fitted, in hertz.
#[must_use]
pub const fn crystals(&self) -> (u64, u64) {
(self.hse_hz, self.lse_hz)
}
}
impl Rcc {
fn source_hz(&self, registers: &RegisterFile, source: Source) -> u64 {
let (register, _, ready) = source.control();
if !ready.is_set(registers.get(register)) {
return 0;
}
match source {
Source::Hsi => HSI_HZ,
Source::Hse => self.hse_hz,
Source::Lse => self.lse_hz,
Source::Lsi => self.lsi_hz,
Source::Pll => self.pll_p(registers),
// The audio PLLs have no single output: the VCO is what their Q and R taps divide, and
// the model answers it here so that [`Rcc::plli2s_clock`] and [`Rcc::sai_clock`] can.
Source::PllI2s | Source::PllSai => self.vco(registers, source),
}
}
/// What clocks the three PLLs: `PLLSRC` picks the HSE or the HSI, RM0090 rev 22 section 6.3.2.
fn pll_input_hz(&self, registers: &RegisterFile) -> u64 {
if cr::HSERDY.is_set(registers.get(Register::Cr)) && Self::pll_source_is_hse(registers) {
self.hse_hz
} else if cr::HSIRDY.is_set(registers.get(Register::Cr))
&& !Self::pll_source_is_hse(registers)
{
HSI_HZ
} else {
0
}
}
fn pll_source_is_hse(registers: &RegisterFile) -> bool {
pllcfgr::PLLSRC.is_set(registers.get(Register::Pllcfgr))
}
/// `f(VCO) = f(input) x PLLN / PLLM`, RM0090 rev 22 section 6.3.2. The audio PLLs share `PLLM`.
fn vco(&self, registers: &RegisterFile, which: Source) -> u64 {
let pllcfgr = registers.get(Register::Pllcfgr);
let m = PLLM.get(pllcfgr);
if m == 0 {
return 0;
}
let n = match which {
Source::Pll => PLLN.get(pllcfgr),
Source::PllI2s => plli2scfgr::PLLI2SNX.get(registers.get(Register::Plli2scfgr)),
Source::PllSai => pllsaicfgr::PLLSAIN.get(registers.get(Register::Pllsaicfgr)),
_ => return 0,
};
// The manual writes the formula as f(in) x (N / M), and the division has to come second.
// Otherwise an input that PLLM doesn't divide evenly loses up to N - 1 hertz of the VCO.
self.pll_input_hz(registers).saturating_mul(n) / m
}
/// `f(PLL) = f(VCO) / PLLP`, RM0090 rev 22 section 6.3.2, with `PLLP` encoding 2, 4, 6, 8.
fn pll_p(&self, registers: &RegisterFile) -> u64 {
let p = (PLLP.get(registers.get(Register::Pllcfgr)) + 1) * 2;
self.vco(registers, Source::Pll) / p
}
/// The 48 MHz domain: `f(VCO) / PLLQ`, RM0090 rev 22 section 6.3.2, and nothing until the PLL
/// is ready, since it's that PLL's VCO the tap divides.
#[must_use]
pub fn pll48(&self, registers: &RegisterFile) -> u64 {
let (register, _, ready) = Source::Pll.control();
let q = PLLQ.get(registers.get(Register::Pllcfgr));
if q == 0 || !ready.is_set(registers.get(register)) {
return 0;
}
self.vco(registers, Source::Pll) / q
}
/// `IWDGCLK`, RM0090 rev 22 figure 16 (p.153): the LSI through the watchdog's own enable gate,
/// nothing until the independent watchdog has started, section 6.2.9 (p.159).
fn iwdg_clk(&self, registers: &RegisterFile) -> u64 {
if !self.iwdg_forces_lsi {
return 0;
}
self.source_hz(registers, Source::Lsi)
}
/// Drives every clock output whose rate has changed. It's every entry point's last act: a write
/// to the tree, a source becoming ready and a clock failure are the three things that move one.
fn drive_clocks(&mut self, registers: &RegisterFile, context: &mut DeviceContext<'_>) {
let rates = [
self.hclk(registers),
self.pclk1(registers),
self.pclk2(registers),
self.timer_clock(registers, Apb::One),
self.timer_clock(registers, Apb::Two),
self.pll48(registers),
self.iwdg_clk(registers),
];
let gated: Vec<u64> = self
.gated
.iter()
.map(|&(source, register, field)| {
let enable = ®ISTERS[register].fields[field];
let register = Register::from_index(register).expect("found in the same table");
if enable.is_set(registers.get(register)) {
rates[source]
} else {
0
}
})
.collect();
for (output, hz) in rates.into_iter().chain(gated).enumerate() {
if self.driven[output] != Some(hz) {
self.driven[output] = Some(hz);
context.clock(output, hz);
}
}
}
/// The system clock, whichever source `SWS` says is driving it, RM0090 rev 22 section 6.3.3.
#[must_use]
pub fn sysclk(&self, registers: &RegisterFile) -> u64 {
match SWS.get(registers.get(Register::Cfgr)) {
0 => self.source_hz(registers, Source::Hsi),
1 => self.source_hz(registers, Source::Hse),
2 => self.source_hz(registers, Source::Pll),
_ => 0,
}
}
/// The AHB clock, `SYSCLK / HPRE`. The encoding has no divide by 32:
/// RM0090 rev 22 section 6.3.3.
#[must_use]
pub fn hclk(&self, registers: &RegisterFile) -> u64 {
self.sysclk(registers) / ahb_divider(cfgr::HPRE.get(registers.get(Register::Cfgr)))
}
/// The APB1 clock, `HCLK / PPRE1`, RM0090 rev 22 section 6.3.3.
#[must_use]
pub fn pclk1(&self, registers: &RegisterFile) -> u64 {
self.hclk(registers) / apb_divider(cfgr::PPRE1.get(registers.get(Register::Cfgr)))
}
/// The APB2 clock, `HCLK / PPRE2`, RM0090 rev 22 section 6.3.3.
#[must_use]
pub fn pclk2(&self, registers: &RegisterFile) -> u64 {
self.hclk(registers) / apb_divider(cfgr::PPRE2.get(registers.get(Register::Cfgr)))
}
/// What the timers on one APB domain count, RM0090 rev 22 section 6.2. That section names
/// `TIMPRE` in `RCC_CFGR` in one sentence and in `RCC_DKCFGR` in the next two. It's in
/// `DCKCFGR`, section 6.3.25.
#[must_use]
pub fn timer_clock(&self, registers: &RegisterFile, apb: Apb) -> u64 {
let field = match apb {
Apb::One => cfgr::PPRE1,
Apb::Two => cfgr::PPRE2,
};
let divider = apb_divider(field.get(registers.get(Register::Cfgr)));
let pclk = self.hclk(registers) / divider;
if dckcfgr::TIMPRE.is_set(registers.get(Register::Dckcfgr)) {
// Section 6.2 words this as 1, 2, or 4 giving HCLK and anything else four times PCLKx.
// At a prescaler of exactly 4 the two are the same number, so only 1 and 2 tell the
// arms apart.
if divider <= 4 {
self.hclk(registers)
} else {
pclk.saturating_mul(4)
}
} else if divider == 1 {
pclk
} else {
pclk.saturating_mul(2)
}
}
/// The RTC clock: the LSE, the LSI, or the HSE divided by `RTCPRE`,
/// RM0090 rev 22 sections 6.2.8, 6.3.3, and 6.3.20. An `RTCPRE` of 0 or 1 is no clock, and so
/// is an unselected source.
#[must_use]
pub fn rtc_clock(&self, registers: &RegisterFile) -> u64 {
let bdcr_value = registers.get(Register::Bdcr);
// RM0090 rev 22 section 6.3.20 bit 15: RTCEN gates RTCCLK, and figure 16 puts that gate in
// front of everything downstream of the selection.
if !bdcr::RTCEN.is_set(bdcr_value) {
return 0;
}
let select = bdcr::RTCSEL0.get(bdcr_value) | (bdcr::RTCSEL1.get(bdcr_value) << 1);
match select {
1 => self.source_hz(registers, Source::Lse),
2 => self.source_hz(registers, Source::Lsi),
3 => {
let prescaler = cfgr::RTCPRE.get(registers.get(Register::Cfgr));
if prescaler < 2 {
0
} else {
self.source_hz(registers, Source::Hse) / prescaler
}
}
_ => 0,
}
}
/// The first clock output, its source divided by its prescaler, RM0090 rev 22 section 6.3.3.
#[must_use]
pub fn mco1(&self, registers: &RegisterFile) -> u64 {
let value = registers.get(Register::Cfgr);
let source = match cfgr::MCO1.get(value) {
0 => self.source_hz(registers, Source::Hsi),
1 => self.source_hz(registers, Source::Lse),
2 => self.source_hz(registers, Source::Hse),
_ => self.source_hz(registers, Source::Pll),
};
source / mco_divider(cfgr::MCO1PRE.get(value))
}
/// The PLLI2S clock output, `f(VCO) / PLLI2SR`, RM0090 rev 22 section 6.3.23. It's what the I2S
/// peripherals and MCO2 see. The SAI takes the Q tap instead.
#[must_use]
pub fn plli2s_clock(&self, registers: &RegisterFile) -> u64 {
let r = plli2scfgr::PLLI2SRX.get(registers.get(Register::Plli2scfgr));
if r == 0 {
return 0;
}
self.source_hz(registers, Source::PllI2s) / r
}
/// The second clock output, RM0090 rev 22 section 6.3.3.
#[must_use]
pub fn mco2(&self, registers: &RegisterFile) -> u64 {
let value = registers.get(Register::Cfgr);
let source = match cfgr::MCO2.get(value) {
0 => self.sysclk(registers),
1 => self.plli2s_clock(registers),
2 => self.source_hz(registers, Source::Hse),
_ => self.source_hz(registers, Source::Pll),
};
source / mco_divider(cfgr::MCO2PRE.get(value))
}
/// The SAI1-A clock: the PLLSAI's or the PLLI2S's Q output over its divisor, or an outside
/// clock. RM0090 rev 22 section 6.3.25. A divisor field of `n` divides by `n + 1`.
#[must_use]
pub fn sai_clock(&self, registers: &RegisterFile) -> u64 {
let dckcfgr_value = registers.get(Register::Dckcfgr);
match dckcfgr::SAI1ASRC.get(dckcfgr_value) {
0 => {
let q = pllsaicfgr::PLLSAIQ.get(registers.get(Register::Pllsaicfgr));
if q == 0 {
return 0;
}
self.source_hz(registers, Source::PllSai)
/ q
/ (dckcfgr::PLLSAIDIVQ.get(dckcfgr_value) + 1)
}
1 => {
let q = PLLI2SQ.get(registers.get(Register::Plli2scfgr));
if q == 0 {
return 0;
}
self.source_hz(registers, Source::PllI2s)
/ q
/ (dckcfgr::PLLI2SDIVQ.get(dckcfgr_value) + 1)
}
// 10 is an outside clock on the alternate function pin, which no wire reaches. 11 is
// what the manual calls a wrong configuration.
_ => 0,
}
}
}
/// Which APB domain a timer sits on.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Apb {
/// The low-speed domain, `PCLK1`.
One,
/// The high-speed domain, `PCLK2`.
Two,
}
/// `HPRE`: `0xxx` is no division, then 2, 4, 8, 16, **64**, 128, 256, 512.
/// RM0090 rev 22 section 6.3.3 leaves 32 out of the encoding, so `1100` is 64 and not 32.
fn ahb_divider(encoding: u64) -> u64 {
match encoding {
0b1000 => 2,
0b1001 => 4,
0b1010 => 8,
0b1011 => 16,
0b1100 => 64,
0b1101 => 128,
0b1110 => 256,
0b1111 => 512,
_ => 1,
}
}
/// `PPRE1` and `PPRE2`: `0xx` is no division, then 2, 4, 8, 16. RM0090 rev 22 section 6.3.3.
fn apb_divider(encoding: u64) -> u64 {
match encoding {
0b100 => 2,
0b101 => 4,
0b110 => 8,
0b111 => 16,
_ => 1,
}
}
/// `MCO1PRE` and `MCO2PRE`: `0xx` is no division, then 2, 3, 4, 5. RM0090 rev 22 section 6.3.3.
fn mco_divider(encoding: u64) -> u64 {
match encoding {
0b100 => 2,
0b101 => 3,
0b110 => 4,
0b111 => 5,
_ => 1,
}
}
/// `PLLSAIRDY`, which RM0090 rev 22 section 6.3.1 types `r` and ST's file types read-write. Every
/// other ready flag of `CR`, and the HSI's factory calibration, are read-only in that file, so the
/// register file already refuses a write to them.
const CR_HARDWARE: u64 = cr::PLLSAIRDY.mask();
/// `PLLSAIRDYF`, which RM0090 rev 22 section 6.3.4 gives as bit 6 and ST's file omits, so the
/// register's own read-write access applies to it. The other six flags and `CSSF` are read-only in
/// that file.
const CIR_HARDWARE: u64 = PLLSAIRDYF.mask();
/// The seven reset flags of `CSR` bits 31:25, `r` in RM0090 rev 22 section 6.3.21 and read-write in
/// ST's file. Only `RMVF` clears them, so without this a plain write of `LSION` would erase the
/// record of why the part restarted. `LSIRDY` beside them is read-only in that file and needs
/// nothing.
const CSR_HARDWARE: u64 = csr::BORRSTF.mask()
| csr::PADRSTF.mask()
| csr::PORRSTF.mask()
| csr::SFTRSTF.mask()
| csr::WDGRSTF.mask()
| csr::WWDGRSTF.mask()
| csr::LPWRRSTF.mask();
/// What a write must not change, kept because a model sees the value written and not the one before
/// it. It starts at the manual's reset values, which `the_shadow_starts_at_the_reset_values` holds
/// against the register file. Section 6.3.1 leaves `CR`'s `HSICAL` byte undefined, and zero is the
/// model's choice.
#[derive(Debug, serde::Serialize, serde::Deserialize)]
struct Shadow {
cr: u64,
cir: u64,
csr: u64,
bdcr: u64,
pllcfgr: u64,
plli2scfgr: u64,
pllsaicfgr: u64,
}
impl Default for Shadow {
fn default() -> Self {
Self {
cr: 0x0000_0083,
cir: 0,
csr: 0x0E00_0000,
bdcr: 0,
pllcfgr: 0x2400_3010,
plli2scfgr: 0x2400_3000,
pllsaicfgr: 0x2400_3000,
}
}
}
impl Rcc {
/// Where a source's pending startup sits in the table.
fn index_of(source: Source) -> usize {
Source::ALL
.iter()
.position(|candidate| *candidate == source)
.expect("a source of the table")
}
fn requested(registers: &RegisterFile) -> Option<Source> {
match SW.get(registers.get(Register::Cfgr)) {
0 => Some(Source::Hsi),
1 => Some(Source::Hse),
2 => Some(Source::Pll),
_ => None,
}
}
fn is_ready(registers: &RegisterFile, source: Source) -> bool {
let (register, _, ready) = source.control();
ready.is_set(registers.get(register))
}
/// The switch of RM0090 rev 22 section 6.2.6. It happens only when the target is ready, and a
/// request for a source that isn't ready yet is honoured the moment it becomes ready.
fn try_switch(registers: &mut RegisterFile) {
let Some(target) = Self::requested(registers) else {
return;
};
if !Self::is_ready(registers, target) {
return;
}
let want = SW.get(registers.get(Register::Cfgr));
let cfgr = registers.get(Register::Cfgr);
registers.set(Register::Cfgr, SWS.set(cfgr, want));
}
/// Whether the system runs on this source, directly or through the PLL.
/// RM0090 rev 22 section 6.3.1 forbids clearing the enable of one that does.
fn clocks_the_system(registers: &RegisterFile, source: Source) -> bool {
match SWS.get(registers.get(Register::Cfgr)) {
0 => source == Source::Hsi,
1 => source == Source::Hse,
2 => {
source == Source::Pll
|| (source == Source::Hse && Self::pll_source_is_hse(registers))
|| (source == Source::Hsi && !Self::pll_source_is_hse(registers))
}
_ => false,
}
}
/// Raises the RCC interrupt when any enabled ready flag stands, RM0090 rev 22 section 6.3.4.
///
/// The model keeps the level it last drove, so a write that changes nothing reports nothing.
fn refresh_interrupt(&mut self, registers: &RegisterFile, context: &mut DeviceContext<'_>) {
let cir_value = registers.get(Register::Cir);
let wanted = if Source::ALL.iter().any(|source| {
let (flag, enable, _) = source.interrupt();
flag.is_set(cir_value) && enable.is_set(cir_value)
}) {
Level::High
} else {
Level::Low
};
if wanted == self.interrupt {
return;
}
self.interrupt = wanted;
context.interrupt(GLOBAL, wanted);
}
/// The HSE has stopped and the clock security system notices, RM0090 rev 22 section 6.2.7.
/// Every clause of that section is conditional on what the oscillator was clocking, and the
/// model's two tables say which of them it carries.
pub fn fail_hse(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
let cr_before = registers.get(Register::Cr);
if !cr::CSSON.is_set(cr_before) || !cr::HSERDY.is_set(cr_before) {
return;
}
let feeds_the_system = Self::clocks_the_system(registers, Source::Hse);
let through_the_pll = feeds_the_system && SWS.get(registers.get(Register::Cfgr)) == 2;
let mut cr_value = cr::HSEON.set(cr_before, 0);
cr_value = cr::HSERDY.set(cr_value, 0);
self.pending[Self::index_of(Source::Hse)] = None;
if through_the_pll {
for source in [Source::Pll, Source::PllI2s] {
self.pending[Self::index_of(source)] = None;
}
cr_value = cr::PLLON.set(cr_value, 0);
cr_value = cr::PLLRDY.set(cr_value, 0);
cr_value = cr::PLLI2SON.set(cr_value, 0);
cr_value = cr::PLLI2SRDY.set(cr_value, 0);
}
if feeds_the_system {
// Hardware starts the oscillator it switches to. A firmware may have left it off.
cr_value = cr::HSION.set(cr_value, 1);
cr_value = cr::HSIRDY.set(cr_value, 1);
}
registers.set(Register::Cr, cr_value);
if feeds_the_system {
let mut cfgr_value = SW.set(registers.get(Register::Cfgr), 0);
cfgr_value = SWS.set(cfgr_value, 0);
registers.set(Register::Cfgr, cfgr_value);
}
let cir_value = registers.get(Register::Cir);
registers.set(Register::Cir, cir::CSSF.set(cir_value, 1));
context.request(CLOCK_FAILURE, Level::High);
context.request(CLOCK_FAILURE, Level::Low);
self.store_shadow(registers);
self.refresh_interrupt(registers, context);
self.drive_clocks(registers, context);
}
}
impl Device for Rcc {
type Register = Register;
fn write(
&mut self,
register: Register,
value: u64,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
match register {
Register::Cr => self.write_cr(registers, context),
Register::Cfgr => {
Self::try_switch(registers);
}
Register::Cir => self.write_cir(value, registers, context),
Register::Bdcr => self.write_bdcr(registers, context),
Register::Csr => self.write_csr(value, registers, context),
Register::Pllcfgr | Register::Plli2scfgr | Register::Pllsaicfgr => {
self.guard_pll_configuration(register, registers);
}
_ => {}
}
self.store_shadow(registers);
self.drive_clocks(registers, context);
}
fn on_event(
&mut self,
event: EventId,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
// An instant fires once per instance, so one event names every source started then.
let named: Vec<usize> = (0..self.pending.len())
.filter(|&index| self.pending[index] == Some(event))
.collect();
for index in named {
self.pending[index] = None;
let source = Source::ALL[index];
let (register, _, ready) = source.control();
let value = registers.get(register);
registers.set(register, ready.set(value, 1));
// RM0090 rev 22 section 6.3.4: each flag "is set by hardware when ... and xRDYIE is
// set". A firmware that polls a ready flag with its interrupt disabled is polling a bit
// hardware never raises. The ready flag of section 6.3.1 is the one to poll, and that's
// the one the line above sets.
let (flag, enable, _) = source.interrupt();
let cir_value = registers.get(Register::Cir);
if enable.is_set(cir_value) {
registers.set(Register::Cir, flag.set(cir_value, 1));
}
self.follow_cr_enables(registers, context);
Self::try_switch(registers);
self.store_shadow(registers);
self.refresh_interrupt(registers, context);
self.drive_clocks(registers, context);
}
}
/// The independent watchdog has started, RM0090 rev 22 section 6.2.9: the LSI is forced on from
/// here on, the same in-use rule `write_cr` already gives the oscillators of `CR`, over a request
/// line rather than a call into another instance.
fn on_request(
&mut self,
change: RequestChange,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
if change.instance != IWDG_STARTED_BY || change.request != super::iwdg::STARTED {
return;
}
self.iwdg_forces_lsi = true;
self.write_csr(0, registers, context);
self.store_shadow(registers);
self.drive_clocks(registers, context);
}
}
impl Rcc {
/// The part has restarted, RM0090 rev 22 sections 6.1.1 and 6.3.21. `cause`'s own flag joins the
/// reset flags the part had, which only `RMVF` clears, and figure 15 (p.152) routes every
/// internal reset source through the same pulse generator onto NRST as `PADRSTF`'s own power-on
/// reset value: this instance sets that flag too, whatever the cause. A low level on the pin
/// itself, section 6.1.1's external reset, is that flag alone.
///
/// # Panics
///
/// Panics on a model this part carries no watchdog for: bit 28 is the core's own request, and a
/// model asking under an instance name this row does not know would otherwise pass as one.
pub fn restarted(
&mut self,
cause: ResetCause,
flags_before: u64,
registers: &mut RegisterFile,
) {
let flag = match cause {
ResetCause::Core => csr::SFTRSTF,
ResetCause::Model("IWDG") => csr::WDGRSTF,
ResetCause::Model(other) => panic!("{other} has no reset flag this part carries"),
ResetCause::Pin => csr::PADRSTF,
};
let flags = flags_before | flag.mask() | csr::PADRSTF.mask();
registers.restore(Register::Csr, CSR_HARDWARE, flags);
self.store_shadow(registers);
}
fn store_shadow(&mut self, registers: &RegisterFile) {
self.shadow = Shadow {
cr: registers.get(Register::Cr),
cir: registers.get(Register::Cir),
csr: registers.get(Register::Csr),
bdcr: registers.get(Register::Bdcr),
pllcfgr: registers.get(Register::Pllcfgr),
plli2scfgr: registers.get(Register::Plli2scfgr),
pllsaicfgr: registers.get(Register::Pllsaicfgr),
};
}
/// `CR`: the five enables and their ready flags, the bypass rule, and the in-use rule.
fn write_cr(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
registers.restore(Register::Cr, CR_HARDWARE, self.shadow.cr);
// RM0090 rev 22 section 6.3.1: HSEBYP is writable only while the HSE oscillator is off.
if cr::HSEON.is_set(self.shadow.cr) {
registers.restore(Register::Cr, cr::HSEBYP.mask(), self.shadow.cr);
}
self.follow_cr_enables(registers, context);
self.refresh_interrupt(registers, context);
}
/// Arms, holds, or drops the startup of every source whose enable lives in `CR`,
/// RM0090 rev 22 section 6.3.1. A ready event runs it as well as a write, because an oscillator
/// becoming ready is what lets a PLL already enabled against it start locking.
fn follow_cr_enables(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
for (index, source) in Source::ALL.iter().enumerate() {
let (register, enable, ready) = source.control();
if register != Register::Cr {
continue;
}
let value = registers.get(register);
if !enable.is_set(value) {
if Self::clocks_the_system(registers, *source) {
registers.set(register, enable.set(value, 1));
} else {
registers.set(register, ready.set(value, 0));
self.pending[index] = None;
}
continue;
}
// Section 6.3.1 forbids stopping an oscillator that feeds a PLL clocking the system,
// and says nothing about one that stops while the system runs elsewhere. Holding the
// flag there would let section 6.2.6's switch accept a PLL at no frequency, so the
// model drops it.
if source.is_pll() && self.pll_input_hz(registers) == 0 {
registers.set(register, ready.set(value, 0));
self.pending[index] = None;
continue;
}
if !ready.is_set(value) && self.pending[index].is_none() {
self.pending[index] =
Some(context.schedule(VirtualNanos(context.now().0 + STARTUP.0)));
}
}
}
/// `CIR`: the clear bits clear their flags and read zero, RM0090 rev 22 section 6.3.4.
fn write_cir(
&mut self,
written: u64,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
registers.restore(Register::Cir, CIR_HARDWARE, self.shadow.cir);
let mut value = registers.get(Register::Cir);
for source in Source::ALL {
let (flag, _, clear) = source.interrupt();
if clear.is_set(written) {
value = flag.set(value, 0);
}
value = clear.set(value, 0);
}
if cir::CSSC.is_set(written) {
value = cir::CSSF.set(value, 0);
}
value = cir::CSSC.set(value, 0);
registers.set(Register::Cir, value);
self.refresh_interrupt(registers, context);
}
/// `BDCR`: the LSE, the RTC source, and the backup reset, RM0090 rev 22 section 6.3.20.
fn write_bdcr(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
// RM0090 rev 22 section 6.3.20: BDRST is set and cleared by software, so it stands until
// the firmware clears it and holds the rest of the domain at its reset value while it does.
if bdcr::BDRST.is_set(registers.get(Register::Bdcr)) {
registers.set(Register::Bdcr, bdcr::BDRST.mask());
self.pending[Self::index_of(Source::Lse)] = None;
// The rest of the domain is the RTC's, and a reset is a line rather than a reach into
// another model's registers. It's held high for as long as the firmware holds
// the bit.
context.request(super::rtc::BACKUP_DOMAIN_RESET, Level::High);
self.refresh_interrupt(registers, context);
return;
}
context.request(super::rtc::BACKUP_DOMAIN_RESET, Level::Low);
// RM0090 rev 22 section 6.3.20 bit 2: LSEBYP is writable only while the LSE clock is
// disabled, which is the rule section 6.3.1 gives HSEBYP.
if bdcr::LSEON.is_set(self.shadow.bdcr) {
registers.restore(Register::Bdcr, bdcr::LSEBYP.mask(), self.shadow.bdcr);
}
// RM0090 rev 22 section 6.3.20 bits 9:8 and section 6.2.8: once the RTC source has been
// chosen it can't be changed again unless the backup domain is reset, and BDRST above is
// what resets it.
let selection = bdcr::RTCSEL0.mask() | bdcr::RTCSEL1.mask();
if self.shadow.bdcr & selection != 0 {
registers.restore(Register::Bdcr, selection, self.shadow.bdcr);
}
self.follow_enable(Source::Lse, registers, context);
}
/// `CSR`: the LSI, and `RMVF` clearing the reset flags, RM0090 rev 22 section 6.3.21.
fn write_csr(
&mut self,
written: u64,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
registers.restore(Register::Csr, CSR_HARDWARE, self.shadow.csr);
if csr::RMVF.is_set(written) {
let mut value = registers.get(Register::Csr);
for flag in [
csr::BORRSTF,
csr::PADRSTF,
csr::PORRSTF,
csr::SFTRSTF,
csr::WDGRSTF,
csr::WWDGRSTF,
csr::LPWRRSTF,
] {
value = flag.set(value, 0);
}
registers.set(Register::Csr, csr::RMVF.set(value, 0));
}
self.follow_enable(Source::Lsi, registers, context);
}
/// One oscillator outside `CR`: schedule its ready when enabled, drop it when not.
///
/// RM0090 rev 22 section 6.3.21 types `LSION` "set and cleared by software", so a write here
/// lands as written. Section 6.2.9 forces only the oscillator once the independent watchdog has
/// started, not the bit, so `iwdg_forces_lsi` joins `LSION` in the enable this reads rather than
/// overwriting what the write left in the register.
fn follow_enable(
&mut self,
source: Source,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
let index = Self::index_of(source);
let (register, enable, ready) = source.control();
let value = registers.get(register);
let enabled = enable.is_set(value) || (source == Source::Lsi && self.iwdg_forces_lsi);
if enabled {
if !ready.is_set(value) && self.pending[index].is_none() {
self.pending[index] =
Some(context.schedule(VirtualNanos(context.now().0 + STARTUP.0)));
}
} else {
registers.set(register, ready.set(value, 0));
self.pending[index] = None;
}
self.refresh_interrupt(registers, context);
}
/// RM0090 rev 22 section 6.2.3: a PLL's factors can't change while it's enabled, so the write
/// is ignored and the register keeps what it had.
fn guard_pll_configuration(&mut self, register: Register, registers: &mut RegisterFile) {
let (locked, previous) = match register {
Register::Pllcfgr => (cr::PLLON, self.shadow.pllcfgr),
Register::Plli2scfgr => (cr::PLLI2SON, self.shadow.plli2scfgr),
Register::Pllsaicfgr => (cr::PLLSAION, self.shadow.pllsaicfgr),
_ => return,
};
let cr_value = registers.get(Register::Cr);
if locked.is_set(cr_value) {
registers.set(register, previous);
return;
}
// RM0090 rev 22 section 6.3.2 bits 5:0 and 22: PLLM and PLLSRC feed the PLLI2S too, so each
// can be written only while both are disabled. The rest of PLLCFGR is the PLL's, and the
// guard above has it. So a firmware that writes the whole word while the PLLI2S runs gets
// the factors it asked for and the input it had. That's what a per-field interlock does on
// the part: the bits that may not be written don't latch, and the ones beside them do.
if register == Register::Pllcfgr && cr::PLLI2SON.is_set(cr_value) {
let value = registers.get(register);
let kept = PLLM.set(value, PLLM.get(previous));
registers.set(
register,
pllcfgr::PLLSRC.set(kept, pllcfgr::PLLSRC.get(previous)),
);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::soc::stm32f427;
#[test]
fn the_shadow_starts_at_the_reset_values() {
let mut soc = crate::mcu::Single::new(stm32f427::builder(), "RCC", Rcc::new()).unwrap();
let registers = soc.registers();
let shadow = Shadow::default();
for (name, seeded, register) in [
("CR", shadow.cr, Register::Cr),
("CIR", shadow.cir, Register::Cir),
("CSR", shadow.csr, Register::Csr),
("BDCR", shadow.bdcr, Register::Bdcr),
("PLLCFGR", shadow.pllcfgr, Register::Pllcfgr),
("PLLI2SCFGR", shadow.plli2scfgr, Register::Plli2scfgr),
("PLLSAICFGR", shadow.pllsaicfgr, Register::Pllsaicfgr),
] {
assert_eq!(seeded, registers.get(register), "{name} out of reset");
}
}
}