// Copyright (c) 2026 Rigless Labs.
// SPDX-License-Identifier: MIT OR Apache-2.0
//! The general-purpose timer of RM0090 rev 22 chapter 18 and the advanced-control timer of chapter 17, one
//! model by its [`Family`]. Written against the `tim2` layout and bound to all four timers chapter 18 covers:
//! TIM2 and TIM5, which count in 32 bits, and TIM3 and TIM4, which count in 16; what separates the four is the
//! counter's width and the option register, and both belong to the instance, ADR-0022. Chapter 17's TIM1 and
//! TIM8 answer from their own layout through [`Advanced`], which puts every register the chapters share at
//! the same offset with the same bits and adds the repetition counter and the break and dead-time register;
//! everything chapter 17 adds is below its own rows.
//!
//! Every functional section 18.3.1 to 18.3.16 and every register 18.4.1 to 18.4.19 is implemented, and every
//! functional section 17.3.1 to 17.3.21 and every register 17.4.1 to 17.4.20. The counter lives on
//! counter-clock cycles: while the internal clock drives it a cycle has an instant on the virtual clock and
//! the model asks to be woken at the next one that matters; while an external clock drives it a cycle is an
//! edge arriving through an input method. Every entry point first settles the timer up to `now`, so the event
//! queue is only a wake-up and the arithmetic is the same whichever clock counts. Nothing here is register-only.
//!
//! | Register or section | Behaviour | Source | Test |
//! |---|---|---|---|
//! | Time-base unit | `PSC + 1` input cycles per count, the prescaler buffered until the update, `ARR` preloaded under `ARPE`, the update at overflow or underflow | 18.3.1, 18.4.10 to 18.4.12 | `the_update_comes_every_period_without_drift`, `arr_preload_and_the_prescaler_take_effect_at_the_update` |
//! | Counter modes | Up, down, and the three centre-aligned modes with `DIR` set by hardware and the flags gated by `CMS`; a match at the crest counts as up and one at the valley as down, and a value above `ARR` flags at the crest only, figure 165 | 18.3.2, 18.4.1, 18.4.5 | `down_counting_underflows_from_arr_to_zero`, `centre_aligned_counting_turns_at_arr_and_zero`, `centre_aligned_flags_follow_figure_165` |
//! | `UDIS`, `URS`, `UG`, `OPM` | The update disabled keeps the shadows, the request source gates the flag, `UG` reinitialises, one-pulse stops at the update | 18.3.2, 18.4.1, 18.4.6 | `ug_reinitialises_and_sets_uif_unless_urs`, `udis_keeps_the_shadows_but_reinitialises`, `one_pulse_mode_stops_at_the_update` |
//! | Clock selection | Internal clock, external clock mode 1 on the trigger input, external clock mode 2 on `ETR`, the internal triggers | 18.3.3, 18.4.3 | `external_clock_mode_1_counts_the_trigger_edges`, `external_clock_mode_2_counts_etr_edges_through_its_prescaler` |
//! | `CK_INT` | The timer clock as the part's tree feeds it, section 6.2's `TIMxCLK`, followed as it changes: what was counted stands, the next cycle comes at the new rate, and a wake-up placed at the old rate is placed again. A clock at nothing stops the counter where it is | 6.2, 18.3.3, ADR-0025 | `the_counter_carries_across_a_change_of_clock`, `a_clock_at_nothing_stops_the_counter` |
//! | Capture/compare channels | A preload and a shadow per channel, `CCxS` choosing the direction and the input, `TRC` as the input under an internal trigger, `CCxS` frozen while enabled | 18.3.4, 18.4.7 to 18.4.9, 18.4.13 to 18.4.16 | `ccr_preload_takes_effect_at_the_update`, `an_input_channel_is_read_only`, `a_channel_on_trc_captures_the_internal_trigger` |
//! | Input capture | The filter, the edge polarity, the prescaler, the capture into `CCRx`, `CCxIF` and `CCxOF`, cleared by reading | 18.3.5, 18.4.5 | `a_capture_latches_the_counter_and_flags_it`, `the_input_filter_drops_a_short_pulse`, `the_capture_prescaler_captures_every_nth_edge` |
//! | PWM input | Two channels on one input with opposite polarity and the reset mode | 18.3.6 | `pwm_input_measures_period_and_duty` |
//! | Forced output | `OCxM` 100 and 101 force the reference; the flag still follows the match | 18.3.7 | `forced_output_holds_its_level_and_the_flag_still_matches` |
//! | Output compare | Frozen, active, inactive, and toggle on match; `CCxIF`; polarity and enable; a value above `ARR` flags at the boundary | 18.3.8, 18.4.5, 18.4.9 | `a_compare_match_toggles_the_output_at_its_instant`, `active_and_inactive_on_match_set_the_reference`, `a_compare_value_above_arr_flags_at_the_overflow` |
//! | PWM | Modes 1 and 2, edge-aligned both ways and centre-aligned, 0% and 100%; the reference is the comparison except while `ETRF` holds it | 18.3.9, figure 165 | `pwm_mode_1_is_high_below_ccr`, `pwm_holds_at_zero_and_above_arr`, `pwm_mode_2_down_counting_inverts_the_comparison`, `centre_aligned_pwm_follows_figure_165` |
//! | The pulse on the pin | [`Tim::pulse`] reads an edge-aligned PWM waveform off the shadows as a high time and a period on the virtual clock, through the polarity, less chapter 17's dead-time where the complementary output is enabled; a level, a capture or a centre-aligned waveform reads as no pulse | 18.3.9, 17.3.11 | `the_pulse_is_the_compare_over_the_period_through_the_polarity`, `a_pulse_is_read_only_off_a_driven_edge_aligned_pwm_pin`, `the_advanced_pulse_needs_moe_and_loses_the_dead_time` |
//! | One-pulse mode | The trigger starts the counter, the pulse spans `CCR` to `ARR`, `OCxFE` acts on the trigger | 18.3.10 | `one_pulse_mode_stops_at_the_update`, `fast_enable_acts_on_the_trigger` |
//! | `OCxREF` clear | A rising `ETRF` clears the reference until the next update under `OCxCE`; a level still high at the update does not clear it again, figure 167 | 18.3.11 | `etrf_clears_the_reference_until_the_update`, `an_etrf_clear_holds_through_a_comparison_that_flips` |
//! | Encoder interface | Modes 1 to 3 counting on one or both inputs with the direction from the other, table 97; `DIR` follows every transition, counted or not; mode 1 counts `TI1FP1` and mode 2 `TI2FP2` as 18.4.3 says, where 18.3.12's first paragraph has the two swapped | 18.3.12, 18.4.3 | `encoder_mode_counts_with_the_phase`, `encoder_modes_1_and_2_count_one_input_and_steer_on_both` |
//! | XOR input | `TI1S` feeds channel 1 with `CH1 ^ CH2 ^ CH3` | 18.3.13, 18.4.2 | `ti1s_xors_the_three_inputs` |
//! | Slave modes | Reset, gated, and trigger on the selected trigger input, `TIF`; both edges under `CCxNP`; the external clock once when modes 1 and 2 both name `ETRF` | 18.3.14, 18.4.3, 18.4.9 | `reset_mode_reinitialises_on_the_trigger`, `gated_mode_counts_while_the_trigger_is_high`, `trigger_mode_starts_on_the_edge`, `trigger_mode_takes_both_edges_under_ccnp`, `etrf_as_both_external_clocks_counts_once` |
//! | Master mode | `TRGO` from reset, `UG` or the slave reset, enable, update, the compare pulse, or a reference | 18.3.15, 18.4.2 | `trgo_follows_the_master_mode`, `trgo_pulses_on_the_compare_and_the_slave_reset` |
//! | Debug mode | The counter holds while the core is halted and the debug module says so | 18.3.16 | `the_counter_holds_while_halted` |
//! | `DIER`, `SR` | One interrupt level: any flag set with its enable set | 18.4.4, 18.4.5 | `the_interrupt_is_the_level_of_flag_and_enable` |
//! | DMA requests | Update, capture/compare, and trigger requests, `CCDS`, the burst through `DCR` and `DMAR` under the target's write rules | 18.4.4, 18.4.17, 18.4.18 | `dma_requests_are_counted_and_the_burst_walks_the_registers`, `the_trigger_request_and_the_burst_into_sr` |
//! | `TIM2_OR` | `ITR1_RMP` held for the board, whose mux puts `TIM8_TRGO`, the PTP, OTG FS, or OTG HS source on `ITR1`; the timer counts whichever arrives | 18.4.19 | `itr1_remap_moves_the_internal_trigger` |
//! | `CNT`, reset state | A write starts the count there; `ARR` is all ones at reset, in the instance's own width | 18.4.10, 18.4.12 | `a_write_to_cnt_sets_the_count`, `a_timer_started_without_an_auto_reload_counts_to_its_own_width` |
//! | The counter's width | 16 bits on TIM3 and TIM4, 32 on TIM2 and TIM5: `CNT`, `ARR` and every `CCRx` count, reload and compare in it, and table 100 leaves their high half reserved on the narrow pair, so a write there does not stand | 18.2, 18.4.10 to 18.4.16, table 100 | `a_narrow_timer_counts_and_reloads_in_sixteen_bits`, `a_wide_timer_counts_and_reloads_in_thirty_two` |
//! | Chapter 17's layout | TIM1 and TIM8 count in sixteen bits and answer from the `tim1` table: the registers the chapters share at the same offsets and bits, which `the_shared_fields_sit_at_the_same_bits_in_both_layouts` holds, `RCR` at `0x30`, `BDTR` at `0x44`, and `ARR` at `0xFFFF` out of reset, where ST's file says zero | 17.2, 17.4.12, 17.4.21 | `an_advanced_timer_answers_from_chapter_17s_layout` |
//! | Repetition counter | The update comes "only when the repetition counter has reached zero", every `N + 1` overflows, or underflows, or both centre-aligned; `UG` and the slave reset bring it at once and reload the counter; a written `RCR` waits "until the next repetition update event" | 17.3.3, 17.4.13 | `the_update_comes_every_repetition_plus_one`, `centre_aligned_the_repetition_counts_both_boundaries` |
//! | `MOE`, `OSSI`, `OSSR`, `OISx`, `OISxN` | Table 95: with `MOE` clear the outputs are not driven, or under `OSSI` held at their idle levels as soon as either enable is set; with it set an output whose enable is clear is not driven, or under `OSSR` driven at its inactive level | 17.3.11, 17.3.12, 17.4.2, 17.4.18 | `moe_gates_the_outputs_and_ossi_picks_the_idle_level`, `off_state_selection_drives_the_inactive_level_of_a_disabled_output` |
//! | Complementary outputs and dead-time | Both enabled, `OCx` is the reference and `OCxN` its opposite, each rising edge delayed by `DTG`'s four ranges of `tDTS`, a pulse narrower than the delay not generated; `OCxN` alone "is not complemented"; each through its own polarity | 17.3.11, 17.4.9, 17.4.18, figures 120 to 122 | `the_complementary_output_is_the_reference_inverted_with_a_dead_time_on_each_rise`, `an_output_alone_on_the_complementary_pin_is_not_inverted` |
//! | The break | The `BKIN` pin under `BKE` and `BKP` clears `MOE` and sets `BIF` and holds both while active; `BG` and the clock security system's pulse do the same and hold nothing; `AOE` sets `MOE` again at the next update while the pin is not active | 17.3.12, 17.4.5, 17.4.6, 17.4.18 | `a_break_on_the_pin_holds_moe_low_and_bif_high_until_the_pin_releases`, `a_software_break_is_a_pulse`, `the_clock_securitys_failure_reaches_the_advanced_timers_break` |
//! | Commutation | Under `CCPC` the enables, polarities and modes of the three channels with a complementary output take effect at `COMG`, or at a rising edge of `TRGI` under `CCUS`; `COMIF`, and a request under `COMDE` | 17.3.14, 17.4.2, 17.4.4, 17.4.6 | `the_commutation_event_applies_the_preloaded_control_bits` |
//! | `LOCK` | Written once after reset; level 1 freezes `DTG`, `OISx`, `BKE`, `BKP` and `AOE`, level 2 those and the polarities of an output channel with `OSSR` and `OSSI`, level 3 those and `OCxM` and `OCxPE` | 17.4.18 | `lock_freezes_what_each_level_says` |
//! | Four lines | The break, the update, the trigger with the commutation, and the capture and compare flags each raise their own line, in table 63's order | 17.4.4, 17.4.5, table 63 | `chapter_17s_flags_raise_four_lines` |
//! | The burst on chapter 17's map | `DBA` twelve is `RCR` and seventeen `BDTR`, where chapter 18's map is reserved | 17.4.19 | `the_dma_burst_reaches_the_repetition_and_break_registers` |
//! | ES0206 rev 24, 2.7.1 | The system break "is not latched": under `AOE` the outputs are re-armed at the following period after a clock failure, where a latched break would hold them | modeled | `erratum_2_7_1_a_clock_failure_re_arms_the_outputs_under_aoe` |
//! | `MSM` | The trigger's resynchronisation delay for slaves | 18.4.3 | not modeled, the delay note below |
//! | Access widths | A 32-bit register written by words, the rest by half-words or words | 18.4 | not modeled: the manual names the widths a write must use and not the effect of a narrower one, so a narrower write lands in its lane |
//! | ES0206 rev 24, 2.7.3 | A second match on the next counter cycle after a `CCR` change, in the three patterns: a toggle output does not toggle again, and centre-aligned the flag does not rise; other modes are unaffected | modeled | `erratum_2_7_3_misses_the_consecutive_match`, `erratum_2_7_3_at_the_valley_and_in_other_modes` |
//! | ES0206 rev 24, 2.7.4 | In reset mode a reference cleared by `ETRF` stays inactive one extra cycle when the reset precedes the compare | modeled | `erratum_2_7_4_holds_the_cleared_reference_one_extra_cycle` |
//! | ES0206 rev 24, 2.7.2 | A trigger pulse is lost when the slave's clock is not yet enabled | pending, the RCC of 0.2, RL-8 | |
//! | `BKE` and `BKP` taking "1 APB clock cycle" to be effective, and the resynchronisation of `MOE` | 17.3.12, 17.4.18 | A write here is effective as it lands and reads back at once; the delay note below |
//! | The dead-time on the way to the idle state, "OCx=OISx and OCxN=OISxN after a dead-time" | 17.3.12, table 95 | The idle levels are driven as `MOE` falls; the two cycles the page gives the way there are the delay note's |
//! | `BDE`, the break's DMA request | 17.3.12 | Named there and given no bit in 17.4.4, so there is nothing to set |
//! | TIM8's own table | 17.4.21 | ST's file gives TIM8 TIM1's layout, and the part answers for it from that; the two chapters give the pair one map |
//! | The option register, on TIM5 | 18.4.19, 18.4.20 | This is written against TIM2's layout and TIM5 is bound to it, so TIM5's option register answers as TIM2's. The two are not the same register: 18.4.19 gives TIM2 `ITR1_RMP` at bits 11:10, remapping internal trigger 1, and 18.4.20 gives TIM5 `TI4_RMP` at bits 7:6, putting the LSI, the LSE or the wake-up on channel 4 to calibrate against. So on TIM5 a write to bits 7:6 is dropped where the part would take it, and bits 11:10 take a write the part would ignore. It is the whole of the difference between the two tables ST's file emits for the wide pair — every other register, field and bit of the two is identical — and it is out of the reach of anything that counts: no clock source, no capture and no compare passes through it. It is one register further than ADR-0022 went, which collapsed layouts differing by reset values alone, and it is the price of one model answering for both | `each_pair_of_timers_answers_from_the_one_layout_it_shares` |
//! | The option register, on TIM3 and TIM4 | table 100 | Table 100 gives the register at `0x50` to TIM2 and TIM5 and to neither of the others, so the narrow pair's table is the wide pair's up to that register and then stops. The two answer from their own table rather than from TIM2's, and `Device::layout` is how the model says which instance it is answering for; no row the model reaches is a row the narrow pair has not | `each_pair_of_timers_answers_from_the_one_layout_it_shares`, `every_timer_the_part_binds_is_the_shape_it_is_bound_to` |
//! | ES0206 rev 24, 2.2.7 | A register write within the clock-enable delay is lost | pending, the RCC of 0.2, RL-8 | |
//!
//! Where the manual gives a resynchronisation delay of a cycle or two for an external edge, `MSM` included, the
//! model applies the edge on the cycle it arrives in. The input filter validates a transition after its `N` samples and drops a
//! transition the input reverts before then; the sampling phase within a cycle is not modeled. Both are said here
//! and nowhere else.
use std::fmt;
use std::marker::PhantomData;
use crate::device::{Device, DeviceContext, Level, RegisterFile, RequestChange};
use crate::edge::Pulse;
use crate::soc::{FieldSpec, Layout, RegisterSpec};
use crate::time::{EventId, VirtualNanos};
use crate::soc::stm32f427::generated::tim1;
use crate::soc::stm32f427::generated::tim2::{
Register, ccer, ccmr1_input, ccmr1_output, ccmr2_input, ccmr2_output, cr1, cr2, dcr, dier, egr,
smcr, sr,
};
const NANOS_PER_SECOND: u128 = 1_000_000_000;
const CHANNEL_COUNT: u8 = 4;
const CHANNELS: usize = CHANNEL_COUNT as usize;
// The interrupt the instance lists first, its global interrupt, RM0090 rev 22 table 63.
const GLOBAL: usize = 0;
const HALF_WORD: u64 = 0xFFFF;
const WORD: u64 = u32::MAX as u64;
// CR1, 18.4.1.
const CMS_EDGE_ALIGNED: u64 = 0b00;
const CMS_FLAGS_DOWN: u64 = 0b01;
const CMS_FLAGS_UP: u64 = 0b10;
// CR2, 18.4.2.
const MMS_RESET: u64 = 0b000;
const MMS_ENABLE: u64 = 0b001;
const MMS_UPDATE: u64 = 0b010;
const MMS_COMPARE_PULSE: u64 = 0b011;
const MMS_FIRST_REFERENCE: u64 = 0b100;
// SMCR, 18.4.3.
const SMS_ENCODER_1: u64 = 0b001;
const SMS_ENCODER_2: u64 = 0b010;
const SMS_ENCODER_3: u64 = 0b011;
const SMS_RESET: u64 = 0b100;
const SMS_GATED: u64 = 0b101;
const SMS_TRIGGER: u64 = 0b110;
const SMS_EXTERNAL_CLOCK_1: u64 = 0b111;
const TS_LAST_INTERNAL: u64 = 0b011;
const TS_TI1_EDGES: u64 = 0b100;
const TS_TI1FP1: u64 = 0b101;
const TS_TI2FP2: u64 = 0b110;
const TS_ETRF: u64 = 0b111;
// CCMRx, 18.4.7 and 18.4.8, per channel: the input and output views of a pair's register share its storage.
const CC_S: [&FieldSpec; CHANNELS] = [
ccmr1_output::CC1S,
ccmr1_output::CC2S,
ccmr2_output::CC3S,
ccmr2_output::CC4S,
];
const IC_PSC: [&FieldSpec; CHANNELS] = [
ccmr1_input::ICPCS,
ccmr1_input::IC2PCS,
ccmr2_input::IC3PSC,
ccmr2_input::IC4PSC,
];
const IC_F: [&FieldSpec; CHANNELS] = [
ccmr1_input::IC1F,
ccmr1_input::IC2F,
ccmr2_input::IC3F,
ccmr2_input::IC4F,
];
const OC_FE: [&FieldSpec; CHANNELS] = [
ccmr1_output::OC1FE,
ccmr1_output::OC2FE,
ccmr2_output::OC3FE,
ccmr2_output::OC4FE,
];
const OC_PE: [&FieldSpec; CHANNELS] = [
ccmr1_output::OC1PE,
ccmr1_output::OC2PE,
ccmr2_output::OC3PE,
ccmr2_output::OC4PE,
];
const OC_M: [&FieldSpec; CHANNELS] = [
ccmr1_output::OC1M,
ccmr1_output::OC2M,
ccmr2_output::OC3M,
ccmr2_output::OC4M,
];
const OC_CE: [&FieldSpec; CHANNELS] = [
ccmr1_output::OC1CE,
ccmr1_output::OC2CE,
ccmr2_output::OC3CE,
ccmr2_output::OC4CE,
];
const CCS_OUTPUT: u64 = 0b00;
const CCS_OWN_INPUT: u64 = 0b01;
const CCS_OTHER_INPUT: u64 = 0b10;
const CCS_TRC: u64 = 0b11;
const OCM_ACTIVE_ON_MATCH: u64 = 0b001;
const OCM_INACTIVE_ON_MATCH: u64 = 0b010;
const OCM_TOGGLE: u64 = 0b011;
const OCM_FORCE_INACTIVE: u64 = 0b100;
const OCM_FORCE_ACTIVE: u64 = 0b101;
const OCM_PWM_1: u64 = 0b110;
const OCM_PWM_2: u64 = 0b111;
// CCER, 18.4.9, per channel.
const CC_E: [&FieldSpec; CHANNELS] = [ccer::CC1E, ccer::CC2E, ccer::CC3E, ccer::CC4E];
const CC_P: [&FieldSpec; CHANNELS] = [ccer::CC1P, ccer::CC2P, ccer::CC3P, ccer::CC4P];
const CC_NP: [&FieldSpec; CHANNELS] = [ccer::CC1NP, ccer::CC2NP, ccer::CC3NP, ccer::CC4NP];
// SR, 18.4.5, per channel.
const CC_IF: [&FieldSpec; CHANNELS] = [sr::CC1IF, sr::CC2IF, sr::CC3IF, sr::CC4IF];
const CC_OF: [&FieldSpec; CHANNELS] = [sr::CC1OF, sr::CC2OF, sr::CC3OF, sr::CC4OF];
// DIER, 18.4.4, per channel.
const CC_DE: [&FieldSpec; CHANNELS] = [dier::CC1DE, dier::CC2DE, dier::CC3DE, dier::CC4DE];
// EGR, 18.4.6, per channel.
const CC_G: [&FieldSpec; CHANNELS] = [egr::CC1G, egr::CC2G, egr::CC3G, egr::CC4G];
// The registers a DMA burst reaches, `DBA` counted in words from `CR1`, 18.4.17; the gaps are reserved.
const BURST_REGISTERS: [Option<Register>; 20] = [
Some(Register::Cr1),
Some(Register::Cr2),
Some(Register::Smcr),
Some(Register::Dier),
Some(Register::Sr),
Some(Register::Egr),
Some(Register::Ccmr1Input),
Some(Register::Ccmr2Input),
Some(Register::Ccer),
Some(Register::Cnt),
Some(Register::Psc),
Some(Register::Arr),
None,
Some(Register::Ccr1),
Some(Register::Ccr2),
Some(Register::Ccr3),
Some(Register::Ccr4),
None,
Some(Register::Dcr),
Some(Register::Dmar),
];
// The digital filter rows of 18.4.3 and 18.4.7: the sampling clock and the consecutive samples a transition needs.
const FILTERS: [(Sampling, u128); 16] = [
(Sampling::Dts(1), 0),
(Sampling::Internal, 2),
(Sampling::Internal, 4),
(Sampling::Internal, 8),
(Sampling::Dts(2), 6),
(Sampling::Dts(2), 8),
(Sampling::Dts(4), 6),
(Sampling::Dts(4), 8),
(Sampling::Dts(8), 6),
(Sampling::Dts(8), 8),
(Sampling::Dts(16), 5),
(Sampling::Dts(16), 6),
(Sampling::Dts(16), 8),
(Sampling::Dts(32), 5),
(Sampling::Dts(32), 6),
(Sampling::Dts(32), 8),
];
#[derive(Debug, Clone, Copy)]
enum Sampling {
// `fCK_INT`.
Internal,
// `fDTS` over the divisor, `fDTS` being `CK_INT` over the `CKD` division of 18.4.1.
Dts(u128),
}
/// A capture/compare channel, 1 to 4.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Channel(u8);
impl Channel {
/// Channel `number`, 1 to 4.
#[must_use]
pub const fn new(number: u8) -> Option<Self> {
if number >= 1 && number <= CHANNEL_COUNT {
Some(Self(number - 1))
} else {
None
}
}
const fn index(self) -> usize {
self.0 as usize
}
fn all() -> impl Iterator<Item = Self> {
(0..CHANNEL_COUNT).map(Self)
}
// The other channel of the pair sharing a `CCMR` register and each other's input, 1 with 2 and 3 with 4.
const fn partner(self) -> Self {
Self(self.0 ^ 1)
}
const fn ccr(self) -> Register {
[
Register::Ccr1,
Register::Ccr2,
Register::Ccr3,
Register::Ccr4,
][self.index()]
}
const fn ccmr(self) -> Register {
if self.0 < 2 {
Register::Ccmr1Input
} else {
Register::Ccmr2Input
}
}
}
// The counting direction, `DIR` of 18.4.1.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Direction {
Up,
Down,
}
// Why an update event happened, which decides the flag under `URS` and the counter's reinitialisation.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum UpdateSource {
Overflow,
Underflow,
Software,
Trigger,
}
// One input stage, 18.3.4: the level on the pin, the level after the filter, and a transition the filter is
// still validating with the clock cycle at which it passes.
#[derive(Debug, Clone, Copy, Default)]
struct Input {
level: Level,
filtered: Level,
pending: Option<(u128, Level)>,
}
// A capture/compare channel's state beyond its registers.
#[derive(Debug, Clone, Copy, Default)]
struct ChannelState {
// The active capture/compare value, the shadow of 18.3.4.
shadow: u64,
// `OCxREF`, active high, 18.3.8, and the `CK_INT` cycle it last changed at, for the dead-time of 17.3.11.
reference: bool,
reference_since: u128,
// How many updates must pass before an `ETRF` clear lifts, 18.3.11 and erratum 2.7.4.
cleared_for_updates: u8,
// Whether a compare event has happened since the `ETRF` clear, erratum 2.7.4.
matched_since_clear: bool,
// Edges the capture prescaler has seen since the last capture, 18.4.7.
prescaled_edges: u64,
// The counter cycle and the value of the last compare match, for erratum 2.7.3 and for not matching twice.
last_match: Option<(u128, u64)>,
// `CCxS` as last accepted, held while the channel is enabled, 18.4.7.
selection: u64,
}
/// DMA requests sent so far, by source, 18.4.4; the DMA model of 0.2 consumes them.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct DmaRequests {
/// `UDE`.
pub update: u64,
/// `CCxDE`, channels 1 to 4.
pub channels: [u64; CHANNELS],
/// `TDE`.
pub trigger: u64,
/// `COMDE`, chapter 17's alone.
pub commutation: u64,
}
/// How wide an instance's counter is, 18.2: "16-bit (TIM3 and TIM4) or 32-bit (TIM2 and TIM5) up, down,
/// up/down auto-reload counter." It is the whole of what separates the four timers of chapter 18, and it
/// belongs to the instance rather than to the layout, ADR-0022: `CNT`, `ARR` and every `CCRx` count,
/// reload and compare in it, and table 100 has their high half "TIM2 and TIM5 only, reserved on the other
/// timers".
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Counter {
/// Sixteen bits: TIM3 and TIM4.
Narrow,
/// Thirty-two bits: TIM2 and TIM5.
Wide,
}
impl Counter {
/// What a count, an auto-reload and a capture/compare value wrap in, and `ARR`'s reset value, 18.4.12.
#[must_use]
pub const fn mask(self) -> u64 {
match self {
Self::Narrow => HALF_WORD,
Self::Wide => WORD,
}
}
/// The vendor's table for an instance that counts in this, which is what the part points it at.
///
/// Table 100 gives the option register at `0x50` to TIM2 and TIM5 and to neither of the others, so the
/// narrow pair's table is the wide pair's up to that register and then stops. This model is written
/// against the wide one and reaches every row of either.
#[must_use]
pub const fn layout(self) -> &'static [RegisterSpec] {
match self {
Self::Narrow => &crate::soc::stm32f427::generated::tim3::REGISTERS,
Self::Wide => &crate::soc::stm32f427::generated::tim2::REGISTERS,
}
}
}
/// Which chapter a timer is: the layout its registers are addressed through, and whether it has chapter 17's
/// repetition counter, break, dead-time and complementary outputs. The model is written against chapter 18's
/// layout; a family says where each of that layout's registers is in its own, and which registers are its own.
pub trait Family: fmt::Debug + Copy + Send + Sync + 'static {
/// The layout an instance of this family is addressed through.
type Register: Layout + PartialEq + Send;
/// Whether the family is chapter 17's.
const ADVANCED: bool;
/// This family's register for one of the chapter 18 layout, or `None` where its own layout has none.
fn own(register: Register) -> Option<Self::Register>;
/// The chapter 18 register one of this family's is, or `None` for a register only chapter 17 has.
fn shared(register: Self::Register) -> Option<Register>;
/// The repetition counter register, chapter 17's alone.
fn repetition() -> Option<Self::Register>;
/// The break and dead-time register, chapter 17's alone.
fn break_and_dead_time() -> Option<Self::Register>;
/// The table an instance counting in `counter` answers from.
fn layout(counter: Counter) -> &'static [RegisterSpec];
/// The register `index` words from `CR1`, which a DMA burst reaches; a gap is reserved.
fn burst(index: u64) -> Option<Self::Register>;
/// The interrupt line a kind of flag raises, by its position in the instance's table.
fn line(flags: Flags) -> usize;
}
/// The kinds of flag, which chapter 17 gives a line each and chapter 18 one line together.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Flags {
/// `BIF`.
Break,
/// `UIF`.
Update,
/// `TIF` and `COMIF`.
TriggerAndCommutation,
/// `CCxIF`.
CaptureCompare,
}
/// The general-purpose timers of chapter 18.
#[derive(Debug, Clone, Copy)]
pub struct General;
/// The advanced-control timers of chapter 17.
#[derive(Debug, Clone, Copy)]
pub struct Advanced;
impl Family for General {
type Register = Register;
const ADVANCED: bool = false;
fn own(register: Register) -> Option<Register> {
Some(register)
}
fn shared(register: Register) -> Option<Register> {
Some(register)
}
fn repetition() -> Option<Register> {
None
}
fn break_and_dead_time() -> Option<Register> {
None
}
fn layout(counter: Counter) -> &'static [RegisterSpec] {
counter.layout()
}
fn burst(index: u64) -> Option<Register> {
BURST_REGISTERS
.get(usize::try_from(index).ok()?)
.copied()
.flatten()
}
fn line(_: Flags) -> usize {
GLOBAL
}
}
impl Family for Advanced {
type Register = tim1::Register;
const ADVANCED: bool = true;
fn own(register: Register) -> Option<tim1::Register> {
Some(match register {
Register::Cr1 => tim1::Register::Cr1,
Register::Cr2 => tim1::Register::Cr2,
Register::Smcr => tim1::Register::Smcr,
Register::Dier => tim1::Register::Dier,
Register::Sr => tim1::Register::Sr,
Register::Egr => tim1::Register::Egr,
Register::Ccmr1Input => tim1::Register::Ccmr1Input,
Register::Ccmr1Output => tim1::Register::Ccmr1Output,
Register::Ccmr2Input => tim1::Register::Ccmr2Input,
Register::Ccmr2Output => tim1::Register::Ccmr2Output,
Register::Ccer => tim1::Register::Ccer,
Register::Cnt => tim1::Register::Cnt,
Register::Psc => tim1::Register::Psc,
Register::Arr => tim1::Register::Arr,
Register::Ccr1 => tim1::Register::Ccr1,
Register::Ccr2 => tim1::Register::Ccr2,
Register::Ccr3 => tim1::Register::Ccr3,
Register::Ccr4 => tim1::Register::Ccr4,
Register::Dcr => tim1::Register::Dcr,
Register::Dmar => tim1::Register::Dmar,
Register::Or => return None,
})
}
fn shared(register: tim1::Register) -> Option<Register> {
Some(match register {
tim1::Register::Cr1 => Register::Cr1,
tim1::Register::Cr2 => Register::Cr2,
tim1::Register::Smcr => Register::Smcr,
tim1::Register::Dier => Register::Dier,
tim1::Register::Sr => Register::Sr,
tim1::Register::Egr => Register::Egr,
tim1::Register::Ccmr1Input => Register::Ccmr1Input,
tim1::Register::Ccmr1Output => Register::Ccmr1Output,
tim1::Register::Ccmr2Input => Register::Ccmr2Input,
tim1::Register::Ccmr2Output => Register::Ccmr2Output,
tim1::Register::Ccer => Register::Ccer,
tim1::Register::Cnt => Register::Cnt,
tim1::Register::Psc => Register::Psc,
tim1::Register::Arr => Register::Arr,
tim1::Register::Ccr1 => Register::Ccr1,
tim1::Register::Ccr2 => Register::Ccr2,
tim1::Register::Ccr3 => Register::Ccr3,
tim1::Register::Ccr4 => Register::Ccr4,
tim1::Register::Dcr => Register::Dcr,
tim1::Register::Dmar => Register::Dmar,
tim1::Register::Rcr | tim1::Register::Bdtr => return None,
})
}
fn repetition() -> Option<tim1::Register> {
Some(tim1::Register::Rcr)
}
fn break_and_dead_time() -> Option<tim1::Register> {
Some(tim1::Register::Bdtr)
}
fn layout(_: Counter) -> &'static [RegisterSpec] {
&tim1::REGISTERS
}
fn burst(index: u64) -> Option<tim1::Register> {
ADVANCED_BURST_REGISTERS
.get(usize::try_from(index).ok()?)
.copied()
}
fn line(flags: Flags) -> usize {
match flags {
Flags::Break => BREAK,
Flags::Update => UPDATE,
Flags::TriggerAndCommutation => TRIGGER_AND_COMMUTATION,
Flags::CaptureCompare => CAPTURE_COMPARE,
}
}
}
// The registers a DMA burst reaches on chapter 17's layout, `DBA` counted in words from `CR1`, 17.4.19.
const ADVANCED_BURST_REGISTERS: [tim1::Register; 20] = [
tim1::Register::Cr1,
tim1::Register::Cr2,
tim1::Register::Smcr,
tim1::Register::Dier,
tim1::Register::Sr,
tim1::Register::Egr,
tim1::Register::Ccmr1Input,
tim1::Register::Ccmr2Input,
tim1::Register::Ccer,
tim1::Register::Cnt,
tim1::Register::Psc,
tim1::Register::Arr,
tim1::Register::Rcr,
tim1::Register::Ccr1,
tim1::Register::Ccr2,
tim1::Register::Ccr3,
tim1::Register::Ccr4,
tim1::Register::Bdtr,
tim1::Register::Dcr,
tim1::Register::Dmar,
];
// Chapter 17's four lines, in the order the instance's table gives them, table 63.
const BREAK: usize = 0;
const UPDATE: usize = 1;
const TRIGGER_AND_COMMUTATION: usize = 2;
const CAPTURE_COMPARE: usize = 3;
const LINES: usize = 4;
// Each flag of `SR` with the enable of `DIER` that lets it raise its line, by the kind of line.
const LINE_FLAGS: [(Flags, &[(&FieldSpec, &FieldSpec)]); 4] = [
(Flags::Break, &[(tim1::sr::BIF, tim1::dier::BIE)]),
(Flags::Update, &[(sr::UIF, dier::UIE)]),
(
Flags::TriggerAndCommutation,
&[(sr::TIF, dier::TIE), (tim1::sr::COMIF, tim1::dier::COMIE)],
),
(
Flags::CaptureCompare,
&[
(sr::CC1IF, dier::CC1IE),
(sr::CC2IF, dier::CC2IE),
(sr::CC3IF, dier::CC3IE),
(sr::CC4IF, dier::CC4IE),
],
),
];
// CCER, 17.4.9: the complementary enables, on the three channels that have one.
const CC_NE: [&FieldSpec; 3] = [tim1::ccer::CC1NE, tim1::ccer::CC2NE, tim1::ccer::CC3NE];
// CR2, 17.4.2: the idle levels.
const OIS: [&FieldSpec; CHANNELS] = [
tim1::cr2::OIS1,
tim1::cr2::OIS2,
tim1::cr2::OIS3,
tim1::cr2::OIS4,
];
const OIS_N: [&FieldSpec; 3] = [tim1::cr2::OIS1N, tim1::cr2::OIS2N, tim1::cr2::OIS3N];
// The control bits `CCPC` preloads, 17.3.14: the enables and polarities of the channels with a
// complementary output, and their output modes.
const PRELOADED_CCER: u64 = tim1::ccer::CC1E.mask()
| tim1::ccer::CC1NE.mask()
| tim1::ccer::CC1P.mask()
| tim1::ccer::CC1NP.mask()
| tim1::ccer::CC2E.mask()
| tim1::ccer::CC2NE.mask()
| tim1::ccer::CC2P.mask()
| tim1::ccer::CC2NP.mask()
| tim1::ccer::CC3E.mask()
| tim1::ccer::CC3NE.mask()
| tim1::ccer::CC3P.mask()
| tim1::ccer::CC3NP.mask();
const PRELOADED_CCMR: [u64; 2] = [
ccmr1_output::OC1M.mask() | ccmr1_output::OC2M.mask(),
ccmr2_output::OC3M.mask(),
];
// BDTR, 17.4.18: what each `LOCK` level freezes.
const LOCK_LEVEL_1: u64 = 1;
const LOCK_LEVEL_2: u64 = 2;
const LOCK_LEVEL_3: u64 = 3;
const LOCKED_AT_1: u64 = tim1::bdtr::DTG.mask()
| tim1::bdtr::BKE.mask()
| tim1::bdtr::BKP.mask()
| tim1::bdtr::AOE.mask();
const LOCKED_AT_2: u64 = tim1::bdtr::OSSR.mask() | tim1::bdtr::OSSI.mask();
const CR2_LOCKED_AT_1: u64 = tim1::cr2::OIS1.mask()
| tim1::cr2::OIS1N.mask()
| tim1::cr2::OIS2.mask()
| tim1::cr2::OIS2N.mask()
| tim1::cr2::OIS3.mask()
| tim1::cr2::OIS3N.mask()
| tim1::cr2::OIS4.mask();
/// The chapter 18 register a family holds at `register`, or zero where it has none.
fn get<F: Family>(registers: &RegisterFile, register: Register) -> u64 {
F::own(register).map_or(0, |own| registers.get(own))
}
fn set<F: Family>(registers: &mut RegisterFile, register: Register, value: u64) {
if let Some(own) = F::own(register) {
registers.set(own, value);
}
}
// The capture/compare control bits in force, 17.3.14: a snapshot of `CCER` and the two `CCMRx` taken at
// the last commutation while `CCPC` holds the written values back.
#[derive(Debug, Clone, Copy)]
struct Controls {
ccer: u64,
ccmr: [u64; 2],
}
/// Where the timer's current clock rate began, on both clocks at once.
#[derive(Debug, Clone, Copy)]
struct Epoch {
at: VirtualNanos,
cycle: u128,
}
impl Default for Epoch {
fn default() -> Self {
Self {
at: VirtualNanos(0),
cycle: 0,
}
}
}
/// A timer of chapter 17 or 18, by its [`Family`].
#[derive(Debug)]
pub struct Tim<F: Family> {
family: PhantomData<F>,
clock_hz: u64,
// Where the current rate began: the instant, and the `CK_INT` cycle it was at. A change of rate moves
// both, so the cycles before it are what they were and the ones after come at the new rate.
epoch: Epoch,
counter: Counter,
// Whether the counter is clocked from an input rather than `CK_INT`, 18.3.3.
external: bool,
// `CNT_EN`: `CEN`, the gate of gated mode, and the debug halt together, 18.3.1.
counting: bool,
// Clock cycles spent not counting, so the counter's cycle count continues where it stopped.
paused: u128,
// The clock cycle counting stopped at, valid while stopped.
stopped_at: u128,
// Cycles an external clock has delivered, edge by edge.
external_cycles: u128,
// The active prescaler and auto-reload, 18.3.1.
psc: u64,
arr: u64,
// The counter's course: the cycle a segment began at, the counter then, and the direction.
segment_cycle: u128,
segment_count: u64,
direction: Direction,
channels: [ChannelState; CHANNELS],
inputs: [Input; CHANNELS],
// The `ETR` pin, `ETR` after `ETP` with its rising edges counted, and `ETRP` after the `ETPS` divider on its
// way through the filter to `ETRF`, 18.3.3.
etr_pin: Level,
etr_polarised: Level,
etr_edges: u64,
etr: Input,
// The trigger input's level, for gated mode, 18.3.14.
trigger: Level,
// `TRGO` and the pulses sent on it, 18.3.15.
trigger_output: Level,
trigger_pulses: u64,
dma_requests: DmaRequests,
burst_index: u64,
halted: bool,
// The level last put on each interrupt line, so a line moves only when it changes.
interrupts: [Level; LINES],
// The instant the model last settled to, which is when its outputs are read as of.
seen: VirtualNanos,
// The wake-up asked of the queue, its instant and id; any other event id is stale.
wakeup: Option<(VirtualNanos, EventId)>,
advanced: Chapter17,
}
// What chapter 17 adds to the timer's state; at rest on chapter 18's.
#[derive(Debug, Clone, Copy)]
struct Chapter17 {
// `REP_CNT`, the repetition down-counter of 17.3.3.
repetition: u64,
// The active capture/compare control bits while `CCPC` preloads them, 17.3.14.
controls: Option<Controls>,
// The `BKIN` pin, and whether the break is held active by it, 17.3.12.
break_pin: Level,
break_active: bool,
// Whether `BDTR` has been written since reset, which freezes `LOCK`, 17.4.18.
lock_frozen: bool,
// What each register held after its last write, by row, for the fields `LOCK` keeps.
kept: [u64; KEPT],
// The instance and request output whose pulse is the system break of 17.3.12, as the part wires it.
system_break: Option<(&'static str, usize)>,
}
// Rows enough for either layout's registers, chapter 17's having the more.
const KEPT: usize = 24;
impl<F: Family> Tim<F> {
/// A timer counting in `counter` whose `CK_INT` runs at `clock_hz` until the part's tree drives it
/// otherwise, 18.3.3.
#[must_use]
pub fn with_clock(clock_hz: u64, counter: Counter) -> Self {
Self {
family: PhantomData,
clock_hz,
epoch: Epoch::default(),
counter,
external: false,
counting: false,
paused: 0,
stopped_at: 0,
external_cycles: 0,
psc: 0,
arr: counter.mask(),
segment_cycle: 0,
segment_count: 0,
direction: Direction::Up,
channels: [ChannelState::default(); CHANNELS],
inputs: [Input::default(); CHANNELS],
etr_pin: Level::Low,
etr_polarised: Level::Low,
etr_edges: 0,
etr: Input::default(),
trigger: Level::Low,
trigger_output: Level::Low,
trigger_pulses: 0,
dma_requests: DmaRequests::default(),
burst_index: 0,
halted: false,
interrupts: [Level::Low; LINES],
seen: VirtualNanos(0),
wakeup: None,
advanced: Chapter17 {
repetition: 0,
controls: None,
break_pin: Level::Low,
break_active: false,
lock_frozen: false,
kept: [0; KEPT],
system_break: None,
},
}
}
/// Takes the pulse on `request` of `instance` as the system break of 17.3.12, "a clock failure event,
/// generated by the Clock Security System", which is the part's to wire.
#[must_use]
pub fn breaks_on(mut self, instance: &'static str, request: usize) -> Self {
self.advanced.system_break = Some((instance, request));
self
}
/// The level on the `TIMx_CHx` pin of `channel`, through its alternate function.
pub fn drive_input(
&mut self,
channel: Channel,
level: Level,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
self.settle(registers, context);
self.inputs[channel.index()].level = level;
self.refilter(channel, registers, context);
if channel.index() < 3 && cr2::TI1S.is_set(get::<F>(registers, Register::Cr2)) {
self.refilter(Channel(0), registers, context);
}
// A filter with no delay passes now, before anything else sees the input.
self.settle(registers, context);
self.finish(registers, context);
}
/// The level on the `TIMx_ETR` pin.
pub fn drive_etr(
&mut self,
level: Level,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
self.settle(registers, context);
self.etr_pin = level;
self.feed_etr(registers, context);
self.settle(registers, context);
self.finish(registers, context);
}
// `ETR` through its path of 18.3.3: `ETP`, then the `ETPS` divider making `ETRP`, then the filter to `ETRF`.
fn feed_etr(&mut self, registers: &RegisterFile, context: &DeviceContext<'_>) {
let smcr_value = get::<F>(registers, Register::Smcr);
let polarised = if smcr::ETP.is_set(smcr_value) {
!self.etr_pin
} else {
self.etr_pin
};
if polarised != self.etr_polarised {
self.etr_polarised = polarised;
let every = 1u64 << smcr::ETPS.get(smcr_value);
self.etr.level = match polarised {
Level::High => {
self.etr_edges += 1;
if self.etr_edges.is_multiple_of(every) {
Level::High
} else {
self.etr.level
}
}
Level::Low => Level::Low,
};
}
let clock = self.clock_cycle(context.now());
let filter = filter_delay::<F>(smcr::ETF.get(smcr_value), registers);
let prescaled = self.etr.level;
start_filter(&mut self.etr, prescaled, clock, filter);
}
/// The level of internal trigger `ITRx`, 0 to 3, another timer's `TRGO`, table 98; a rise is its edge.
pub fn drive_internal_trigger(
&mut self,
trigger: u8,
level: Level,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
self.settle(registers, context);
if smcr::TS.get(get::<F>(registers, Register::Smcr)) == u64::from(trigger) {
if level == Level::High {
self.trigger_edge(registers, context);
}
self.trigger = level;
self.gate(registers, context);
}
self.finish(registers, context);
}
/// Whether the core is halted with the debug module set to stop this timer, 18.3.16.
pub fn halt(
&mut self,
halted: bool,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
self.settle(registers, context);
self.halted = halted;
self.finish(registers, context);
}
/// The `OCx` output of `channel` as of the last instant the timer settled to: its reference through its
/// polarity when enabled, table 99, or `None` while the pin is not driven; on chapter 17's timers table
/// 95, with `MOE`, the off-states, the idle levels and the dead-time.
#[must_use]
pub fn output(&self, channel: Channel, registers: &RegisterFile) -> Option<Level> {
self.outputs(channel, registers).0
}
/// The `OCxN` output of `channel`, table 95, or `None` where the family or the channel has none or
/// the pin is not driven.
#[must_use]
pub fn complementary_output(
&self,
channel: Channel,
registers: &RegisterFile,
) -> Option<Level> {
self.outputs(channel, registers).1
}
/// The pulse `OCx` repeats as the shadows stand, 18.3.9: on a counter running from `CK_INT`, counting
/// edge-aligned in either direction, with `channel` enabled in PWM mode 1 or 2 and its pin driven, the
/// high time and the period on the virtual clock, through the polarity, less the dead-time of 17.3.11
/// where the complementary output is enabled too. `None` for a pin that is not driven or a waveform
/// that is not one of these: a level, a capture and a centre-aligned pulse are not read.
#[must_use]
pub fn pulse(&self, channel: Channel, registers: &RegisterFile) -> Option<Pulse> {
self.output(channel, registers)?;
let index = channel.index();
let ccer_value = self.ccer(registers);
let plain = self.counting
&& !self.external
&& self.clock_hz != 0
&& !Self::centre_aligned(registers)
&& channel_selection::<F>(channel, registers) == CCS_OUTPUT
&& CC_E[index].is_set(ccer_value)
&& (!F::ADVANCED || tim1::bdtr::MOE.is_set(Self::bdtr(registers)));
if !plain {
return None;
}
let period = self.arr + 1;
let compare = self.channels[index].shadow;
let below = match self.direction {
Direction::Up => compare.min(period),
Direction::Down => (compare + 1).min(period),
};
let active = match self.output_mode(channel, registers) {
OCM_PWM_1 => below,
OCM_PWM_2 => period - below,
_ => return None,
};
let per_count = u128::from(self.psc) + 1;
let period_cycles = u128::from(period) * per_count;
let mut active_cycles = u128::from(active) * per_count;
let complementary = CC_NE
.get(index)
.is_some_and(|field| field.is_set(ccer_value));
if complementary {
active_cycles = active_cycles.saturating_sub(Self::dead_time(registers));
}
let high_cycles = if CC_P[index].is_set(ccer_value) {
period_cycles - active_cycles
} else {
active_cycles
};
let nanos = |cycles: u128| {
VirtualNanos(
u64::try_from(cycles * NANOS_PER_SECOND / u128::from(self.clock_hz))
.expect("a period fits the virtual clock"),
)
};
Some(Pulse {
width: nanos(high_cycles),
period: nanos(period_cycles),
})
}
fn outputs(
&self,
channel: Channel,
registers: &RegisterFile,
) -> (Option<Level>, Option<Level>) {
let index = channel.index();
let ccer_value = self.ccer(registers);
let enabled = CC_E[index].is_set(ccer_value);
let active_low = CC_P[index].is_set(ccer_value);
let reference = self.channels[index].reference;
if !F::ADVANCED {
return (enabled.then(|| Level::from(reference != active_low)), None);
}
let complementary = CC_NE
.get(index)
.is_some_and(|field| field.is_set(ccer_value));
let complementary_low = CC_NP[index].is_set(ccer_value);
let bdtr_value = Self::bdtr(registers);
let cr2_value = get::<F>(registers, Register::Cr2);
if !tim1::bdtr::MOE.is_set(bdtr_value) {
if !tim1::bdtr::OSSI.is_set(bdtr_value) || (!enabled && !complementary) {
return (None, None);
}
let idle = Level::from(OIS[index].is_set(cr2_value));
let idle_n = OIS_N
.get(index)
.map(|field| Level::from(field.is_set(cr2_value)));
return (Some(idle), idle_n);
}
let off_state = tim1::bdtr::OSSR.is_set(bdtr_value);
let settled = self
.clock_cycle(self.seen)
.saturating_sub(self.channels[index].reference_since)
>= Self::dead_time(registers);
match (enabled, complementary) {
(false, false) => (None, None),
(false, true) => (
off_state.then(|| Level::from(active_low)),
Some(Level::from(reference != complementary_low)),
),
(true, false) => (
Some(Level::from(reference != active_low)),
(off_state && index < CC_NE.len()).then(|| Level::from(complementary_low)),
),
(true, true) => (
Some(Level::from((reference && settled) != active_low)),
Some(Level::from((!reference && settled) != complementary_low)),
),
}
}
/// The level on the `BKIN` pin, 17.3.12; nothing on chapter 18's timers.
pub fn drive_break(
&mut self,
level: Level,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
self.settle(registers, context);
self.advanced.break_pin = level;
self.finish(registers, context);
}
// `BDTR`, or zero for a family without one.
fn bdtr(registers: &RegisterFile) -> u64 {
F::break_and_dead_time().map_or(0, |register| registers.get(register))
}
// `RCR`, or zero for a family without one.
fn repetitions(registers: &RegisterFile) -> u64 {
F::repetition().map_or(0, |register| registers.get(register))
}
// The dead-time in `CK_INT` cycles, 17.4.18's four ranges of `DTG` in units of `tDTS`, itself `CK_INT`
// divided as `CKD` says.
fn dead_time(registers: &RegisterFile) -> u128 {
let dtg = tim1::bdtr::DTG.get(Self::bdtr(registers));
let units = match dtg >> 5 {
0..=3 => dtg,
4 | 5 => (64 + (dtg & 0x3F)) * 2,
6 => (32 + (dtg & 0x1F)) * 8,
_ => (32 + (dtg & 0x1F)) * 16,
};
u128::from(units) << cr1::CKD.get(get::<F>(registers, Register::Cr1))
}
// `CCER` as it acts: the written value, with the preloaded bits from the last commutation while `CCPC`
// holds them, 17.3.14.
fn ccer(&self, registers: &RegisterFile) -> u64 {
let written = get::<F>(registers, Register::Ccer);
match self.advanced.controls {
Some(controls) => (written & !PRELOADED_CCER) | (controls.ccer & PRELOADED_CCER),
None => written,
}
}
// A channel's `CCMRx` as it acts, the same way.
fn ccmr(&self, channel: Channel, registers: &RegisterFile) -> u64 {
let written = get::<F>(registers, channel.ccmr());
let pair = channel.index() / 2;
match self.advanced.controls {
Some(controls) => {
(written & !PRELOADED_CCMR[pair]) | (controls.ccmr[pair] & PRELOADED_CCMR[pair])
}
None => written,
}
}
fn ccer_field(
&self,
channel: Channel,
fields: &[&FieldSpec; CHANNELS],
registers: &RegisterFile,
) -> bool {
fields[channel.index()].is_set(self.ccer(registers))
}
fn output_mode(&self, channel: Channel, registers: &RegisterFile) -> u64 {
OC_M[channel.index()].get(self.ccmr(channel, registers))
}
// Whether a filtered transition to `new` is an active edge for `channel` under `CCxP` and `CCxNP`, 18.4.9.
fn edge_selected(&self, channel: Channel, new: Level, registers: &RegisterFile) -> bool {
let falling = self.ccer_field(channel, &CC_P, registers);
let both = self.ccer_field(channel, &CC_NP, registers);
both || (new == Level::High) != falling
}
// `OCxREF` takes `active` at `CK_INT` cycle `clock`, which is when a dead-time starts, 17.3.11.
fn set_reference(&mut self, channel: Channel, active: bool, clock: u128) {
let state = &mut self.channels[channel.index()];
if state.reference != active {
state.reference = active;
state.reference_since = clock;
}
}
// The commutation event, 17.3.14: the preloaded control bits take effect, `COMIF`, and the request.
fn commutate(&mut self, registers: &mut RegisterFile, context: &DeviceContext<'_>) {
if !tim1::cr2::CCPC.is_set(get::<F>(registers, Register::Cr2)) {
return;
}
self.advanced.controls = Some(Controls {
ccer: get::<F>(registers, Register::Ccer),
ccmr: [
get::<F>(registers, Register::Ccmr1Input),
get::<F>(registers, Register::Ccmr2Input),
],
});
flag::<F>(registers, tim1::sr::COMIF);
if tim1::dier::COMDE.is_set(get::<F>(registers, Register::Dier)) {
self.dma_requests.commutation += 1;
}
let cycle = self.counter_cycle(context.now());
for channel in Channel::all() {
self.refresh_pwm(channel, cycle, registers);
}
}
// A break, 17.3.12: `MOE` cleared, `BIF` set. From the pin it holds; from software or the clock
// security system it is a pulse, the latter erratum 2.7.1's.
fn take_break(registers: &mut RegisterFile) {
let Some(bdtr) = F::break_and_dead_time() else {
return;
};
let bdtr_value = registers.get(bdtr);
registers.set(bdtr, tim1::bdtr::MOE.set(bdtr_value, 0));
flag::<F>(registers, tim1::sr::BIF);
}
// Whether the `BKIN` pin holds the break, `BKE` and `BKP`, 17.4.18.
fn break_held(&self, registers: &RegisterFile) -> bool {
let bdtr_value = Self::bdtr(registers);
tim1::bdtr::BKE.is_set(bdtr_value)
&& (self.advanced.break_pin == Level::High) == tim1::bdtr::BKP.is_set(bdtr_value)
}
fn refresh_break(&mut self, registers: &mut RegisterFile) {
let held = self.break_held(registers);
if held && !self.advanced.break_active {
Self::take_break(registers);
}
self.advanced.break_active = held;
if held {
if let Some(bdtr) = F::break_and_dead_time() {
let bdtr_value = registers.get(bdtr);
registers.set(bdtr, tim1::bdtr::MOE.set(bdtr_value, 0));
}
flag::<F>(registers, tim1::sr::BIF);
}
}
// `LOCK`, 17.4.18.
fn lock_level(registers: &RegisterFile) -> u64 {
tim1::bdtr::LOCK.get(Self::bdtr(registers))
}
// Puts the bits of `mask` in `register` back to `before`, which is what a locked field does to a write.
fn keep(registers: &mut RegisterFile, register: F::Register, mask: u64, before: u64) {
let written = registers.get(register);
registers.set(register, (written & !mask) | (before & mask));
}
fn write_cr2(
&mut self,
before: u64,
registers: &mut RegisterFile,
context: &DeviceContext<'_>,
) {
if !F::ADVANCED {
return;
}
let own = F::own(Register::Cr2).expect("chapter 17 has CR2");
if Self::lock_level(registers) >= LOCK_LEVEL_1 {
Self::keep(registers, own, CR2_LOCKED_AT_1, before);
}
let preloading = tim1::cr2::CCPC.is_set(registers.get(own));
match (preloading, self.advanced.controls) {
(true, None) => {
self.advanced.controls = Some(Controls {
ccer: get::<F>(registers, Register::Ccer),
ccmr: [
get::<F>(registers, Register::Ccmr1Input),
get::<F>(registers, Register::Ccmr2Input),
],
});
}
(false, Some(_)) => {
self.advanced.controls = None;
let cycle = self.counter_cycle(context.now());
for channel in Channel::all() {
self.refresh_pwm(channel, cycle, registers);
}
}
_ => {}
}
}
fn write_bdtr(&mut self, before: u64, registers: &mut RegisterFile) {
let Some(bdtr) = F::break_and_dead_time() else {
return;
};
if self.advanced.lock_frozen {
Self::keep(registers, bdtr, tim1::bdtr::LOCK.mask(), before);
}
self.advanced.lock_frozen = true;
let level = tim1::bdtr::LOCK.get(before);
if level >= LOCK_LEVEL_1 {
Self::keep(registers, bdtr, LOCKED_AT_1, before);
}
if level >= LOCK_LEVEL_2 {
Self::keep(registers, bdtr, LOCKED_AT_2, before);
}
if self.advanced.break_active {
let bdtr_value = registers.get(bdtr);
registers.set(bdtr, tim1::bdtr::MOE.set(bdtr_value, 0));
}
}
// The counter's value at `now`, what `CNT` reads.
fn counter(&self, now: VirtualNanos) -> u64 {
self.count_at(self.counter_cycle(now))
}
/// `TRGO`: its level for the level modes of `MMS`, and the pulses sent so far for the pulse modes, 18.4.2.
#[must_use]
pub fn trigger_output(&self) -> (Level, u64) {
(self.trigger_output, self.trigger_pulses)
}
/// DMA requests sent so far.
#[must_use]
pub fn dma_requests(&self) -> DmaRequests {
self.dma_requests
}
// `CK_INT` cycles since the virtual clock's zero: those before the epoch, and those since at the rate.
fn clock_cycle(&self, now: VirtualNanos) -> u128 {
let since = u128::from(now.0.saturating_sub(self.epoch.at.0));
self.epoch.cycle + since * u128::from(self.clock_hz) / NANOS_PER_SECOND
}
// The first instant at or after `CK_INT` cycle `cycle`, or never while the clock is stopped.
fn instant_of_clock_cycle(&self, cycle: u128) -> Option<VirtualNanos> {
if self.clock_hz == 0 {
return None;
}
let since = cycle.saturating_sub(self.epoch.cycle);
let nanos = (since * NANOS_PER_SECOND).div_ceil(u128::from(self.clock_hz));
let at = u128::from(self.epoch.at.0) + nanos;
Some(VirtualNanos(
u64::try_from(at).expect("the virtual clock outlasts any run"),
))
}
// The counter clock's cycle at `now`: `CK_INT` cycles less the paused ones, frozen while stopped, or the
// edges an external clock delivered.
fn counter_cycle(&self, now: VirtualNanos) -> u128 {
if self.external {
self.external_cycles
} else if self.counting {
self.clock_cycle(now) - self.paused
} else {
self.stopped_at - self.paused
}
}
fn instant_of_counter_cycle(&self, cycle: u128) -> Option<VirtualNanos> {
self.instant_of_clock_cycle(cycle + self.paused)
}
fn ticks_since_segment(&self, cycle: u128) -> u64 {
let elapsed = cycle.saturating_sub(self.segment_cycle);
u64::try_from(elapsed / (u128::from(self.psc) + 1)).expect("ticks fit the counter's course")
}
fn count_at(&self, cycle: u128) -> u64 {
let ticks = self.ticks_since_segment(cycle);
match self.direction {
Direction::Up => self.segment_count.wrapping_add(ticks) & self.counter.mask(),
Direction::Down => self.segment_count.wrapping_sub(ticks) & self.counter.mask(),
}
}
fn cycle_of_tick(&self, tick: u64) -> u128 {
self.segment_cycle + u128::from(tick) * (u128::from(self.psc) + 1)
}
fn centre_aligned(registers: &RegisterFile) -> bool {
cr1::CMS.get(get::<F>(registers, Register::Cr1)) != CMS_EDGE_ALIGNED
}
// The tick at which the segment ends in an overflow or underflow, 18.3.2, or `None` while `ARR` is zero and
// the counter is blocked, 18.4.12.
fn boundary_tick(&self, registers: &RegisterFile) -> Option<(u64, UpdateSource)> {
if self.arr == 0 {
return None;
}
let centre = Self::centre_aligned(registers);
Some(match self.direction {
Direction::Up if centre => (
self.arr - self.segment_count.min(self.arr),
UpdateSource::Overflow,
),
Direction::Up if self.segment_count <= self.arr => {
(self.arr - self.segment_count + 1, UpdateSource::Overflow)
}
Direction::Up => (
self.counter.mask() - self.segment_count + 1,
UpdateSource::Overflow,
),
Direction::Down if centre => (self.segment_count, UpdateSource::Underflow),
Direction::Down => (self.segment_count + 1, UpdateSource::Underflow),
})
}
// The next compare match of an output channel within the segment, as a tick and the value matched.
fn match_tick(&self, channel: Channel, registers: &RegisterFile) -> Option<(u64, u64)> {
if channel_selection::<F>(channel, registers) != CCS_OUTPUT {
return None;
}
let value = self.channels[channel.index()].shadow;
let tick = match self.direction {
Direction::Up => value.checked_sub(self.segment_count)?,
Direction::Down => self.segment_count.checked_sub(value)?,
};
if tick == 0 {
return None;
}
let cycle = self.cycle_of_tick(tick);
match self.channels[channel.index()].last_match {
Some((last, _)) if cycle <= last => None,
_ => Some((tick, value)),
}
}
// Applies every counter event up to and including `cycle`, in order.
fn advance_counter_to(
&mut self,
cycle: u128,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
while let Some((event_cycle, event)) = self.next_counter_event(registers) {
if event_cycle > cycle {
break;
}
match event {
CounterEvent::Boundary(source) => {
self.boundary(event_cycle, source, registers, context);
}
CounterEvent::Match(found) => {
self.compare_match(found, Some(self.direction), registers);
}
}
}
}
fn next_counter_event(&self, registers: &RegisterFile) -> Option<(u128, CounterEvent)> {
if !self.counting {
return None;
}
let boundary = self
.boundary_tick(registers)
.map(|(tick, source)| (self.cycle_of_tick(tick), CounterEvent::Boundary(source)));
let matches = Channel::all().filter_map(|channel| {
let (tick, value) = self.match_tick(channel, registers)?;
let within = boundary
.is_none_or(|(boundary_cycle, _)| self.cycle_of_tick(tick) < boundary_cycle);
within.then(|| {
let cycle = self.cycle_of_tick(tick);
let found = Match {
channel,
cycle,
value,
};
(cycle, CounterEvent::Match(found))
})
});
boundary
.into_iter()
.chain(matches)
.min_by_key(|(cycle, event)| (*cycle, event.order()))
}
// The counter overflows or underflows at `cycle`: the update, the new segment, the compare values above
// `ARR` that flag here, and the matches at the new count, 18.3.2 and 18.4.5.
fn boundary(
&mut self,
cycle: u128,
source: UpdateSource,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
let centre = Self::centre_aligned(registers);
let old_arr = self.arr;
// 17.3.3: the update comes when the repetition counter is at zero, and reloads it.
if self.advanced.repetition == 0 {
self.update(source, cycle, registers, context);
} else {
self.advanced.repetition -= 1;
}
if centre {
self.direction = match source {
UpdateSource::Overflow => Direction::Down,
_ => Direction::Up,
};
self.segment_count = match self.direction {
Direction::Down => self.arr,
Direction::Up => 0,
};
set_direction_bit::<F>(self.direction, registers);
} else {
self.segment_count = match self.direction {
Direction::Up => 0,
Direction::Down => self.arr,
};
}
self.segment_cycle = cycle;
// A value above `ARR` flags at the overflow, or at the underflow only when counting down edge-aligned,
// 18.4.5 and figure 165.
if source == UpdateSource::Overflow || !centre {
for channel in Channel::all() {
let output = channel_selection::<F>(channel, registers) == CCS_OUTPUT;
if output && self.channels[channel.index()].shadow > old_arr {
self.flag_compare(channel, registers);
}
}
}
self.matches_at_segment_start(None, registers);
}
// The counter reaches a compare value: the flag under the centre-aligned gate, the reference by mode, the
// DMA request, and the compare pulse of `TRGO`, 18.3.8, 18.3.9, 18.4.1.
fn compare_match(
&mut self,
found: Match,
counted: Option<Direction>,
registers: &mut RegisterFile,
) {
let Match {
channel,
cycle,
value,
} = found;
let missed = self.missed_by_erratum_2_7_3(channel, cycle, value, registers);
self.channels[channel.index()].last_match = Some((cycle, value));
let cms = cr1::CMS.get(get::<F>(registers, Register::Cr1));
let gated = matches!(
(cms, counted),
(CMS_FLAGS_DOWN, Some(Direction::Up)) | (CMS_FLAGS_UP, Some(Direction::Down))
);
let lost = missed && Self::centre_aligned(registers);
if !gated && !lost {
self.flag_compare(channel, registers);
}
let mode = self.output_mode(channel, registers);
let state = &mut self.channels[channel.index()];
if !missed {
state.matched_since_clear = true;
}
let reference = state.reference;
let clock = cycle + self.paused;
match mode {
OCM_ACTIVE_ON_MATCH => self.set_reference(channel, true, clock),
OCM_INACTIVE_ON_MATCH => self.set_reference(channel, false, clock),
OCM_TOGGLE if !missed => self.set_reference(channel, !reference, clock),
_ => {}
}
self.refresh_pwm(channel, cycle, registers);
}
fn flag_compare(&mut self, channel: Channel, registers: &mut RegisterFile) {
flag::<F>(registers, CC_IF[channel.index()]);
let dier_value = get::<F>(registers, Register::Dier);
let on_update = cr2::CCDS.is_set(get::<F>(registers, Register::Cr2));
if CC_DE[channel.index()].is_set(dier_value) && !on_update {
self.dma_requests.channels[channel.index()] += 1;
}
if channel.index() == 0
&& cr2::MMS.get(get::<F>(registers, Register::Cr2)) == MMS_COMPARE_PULSE
{
self.trigger_pulses += 1;
}
}
// Erratum ES0206 rev 24, 2.7.3: a match one counter cycle after the previous one, brought by a `CCR` change,
// is missed when the pair is `ARR` then 0 edge-aligned, `ARR - 1` then `ARR` at the crest, or 1 then 0 at the
// valley: a toggle output does not toggle, and centre-aligned the flag does not rise.
fn missed_by_erratum_2_7_3(
&self,
channel: Channel,
cycle: u128,
value: u64,
registers: &RegisterFile,
) -> bool {
let Some((last_cycle, last_value)) = self.channels[channel.index()].last_match else {
return false;
};
if cycle != last_cycle + u128::from(self.psc) + 1 || value == last_value {
return false;
}
// The crest and the valley are boundaries, so the direction has already turned; the values say which.
if Self::centre_aligned(registers) {
(last_value + 1 == self.arr && value == self.arr) || (last_value == 1 && value == 0)
} else {
last_value == self.arr && value == 0
}
}
// The compare values equal to the counter at a segment's first tick match at once.
// `counted` is the direction the count was reached in, or `None` at the crest and the valley, where the flag
// rises whatever `CMS` says, figure 165.
fn matches_at_segment_start(
&mut self,
counted: Option<Direction>,
registers: &mut RegisterFile,
) {
let cycle = self.segment_cycle;
for channel in Channel::all() {
if channel_selection::<F>(channel, registers) != CCS_OUTPUT {
continue;
}
let value = self.channels[channel.index()].shadow;
let already = matches!(self.channels[channel.index()].last_match, Some((last, _)) if last >= cycle);
if value == self.segment_count && !already {
let found = Match {
channel,
cycle,
value,
};
self.compare_match(found, counted, registers);
}
}
for channel in Channel::all() {
self.refresh_pwm(channel, self.segment_cycle, registers);
}
}
// The PWM reference is the comparison of 18.3.9, except while an `ETRF` clear holds it low until the update,
// 18.3.11.
fn refresh_pwm(&mut self, channel: Channel, cycle: u128, registers: &RegisterFile) {
let mode = self.output_mode(channel, registers);
let count = self.count_at(cycle);
let state = &self.channels[channel.index()];
let value = state.shadow;
let active = match (mode, self.direction) {
(OCM_PWM_1, Direction::Up) => count < value,
(OCM_PWM_1, Direction::Down) => count <= value,
(OCM_PWM_2, Direction::Up) => count >= value,
(OCM_PWM_2, Direction::Down) => count > value,
_ => return,
};
if state.cleared_for_updates == 0 {
self.set_reference(channel, active, cycle + self.paused);
}
}
// An update event from `source`: the shadows reload unless `UDIS`, `UIF` unless `URS` and not a boundary,
// the counter reinitialises for `UG` and the slave reset, one-pulse mode stops, and the requests go out,
// 18.3.1, 18.3.2, 18.4.1, 18.4.6.
fn update(
&mut self,
source: UpdateSource,
cycle: u128,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
let cr1_value = get::<F>(registers, Register::Cr1);
let reinitialise = matches!(source, UpdateSource::Software | UpdateSource::Trigger);
let centre = Self::centre_aligned(registers);
if reinitialise {
self.segment_cycle = cycle;
self.segment_count = match self.direction {
Direction::Down if !centre => self.arr,
_ => 0,
};
}
self.advanced.repetition = Self::repetitions(registers);
if cr1::UDIS.is_set(cr1_value) {
return;
}
self.reload_shadows(source, registers);
if let Some(bdtr) = F::break_and_dead_time() {
// 17.4.18 `AOE`: `MOE` is set again at the update; a break still held clears it again after.
let bdtr_value = registers.get(bdtr);
if tim1::bdtr::AOE.is_set(bdtr_value) {
registers.set(bdtr, tim1::bdtr::MOE.set(bdtr_value, 1));
}
}
if reinitialise && self.direction == Direction::Down && !centre {
self.segment_count = self.arr;
}
let flagged = !cr1::URS.is_set(cr1_value)
|| matches!(source, UpdateSource::Overflow | UpdateSource::Underflow);
if flagged {
self.flag_update(registers);
}
if cr2::MMS.get(get::<F>(registers, Register::Cr2)) == MMS_UPDATE {
self.trigger_pulses += 1;
}
if cr1::OPM.is_set(cr1_value) {
set::<F>(registers, Register::Cr1, cr1::CEN.set(cr1_value, 0));
self.set_counting(false, context);
}
}
// The update loads the prescaler, the auto-reload, and the preloaded compare values, 18.3.1 and 18.3.4, and
// counts down the `ETRF` clears, 18.3.11.
fn reload_shadows(&mut self, source: UpdateSource, registers: &RegisterFile) {
self.psc = get::<F>(registers, Register::Psc) & HALF_WORD;
self.arr = get::<F>(registers, Register::Arr) & self.counter.mask();
for channel in Channel::all() {
let output = channel_selection::<F>(channel, registers) == CCS_OUTPUT;
if output && ccmr_field::<F>(channel, &OC_PE, registers) {
self.channels[channel.index()].shadow =
get::<F>(registers, channel.ccr()) & self.counter.mask();
}
let state = &mut self.channels[channel.index()];
if state.cleared_for_updates > 0 {
// Erratum ES0206 rev 24, 2.7.4: a slave reset before the compare leaves the clear one more cycle.
if source == UpdateSource::Trigger && !state.matched_since_clear {
state.cleared_for_updates = 1;
} else {
state.cleared_for_updates -= 1;
}
}
}
}
// `UIF` and the DMA requests of an update: `UDE`, and under `CCDS` the channels' too, 18.4.4.
fn flag_update(&mut self, registers: &mut RegisterFile) {
flag::<F>(registers, sr::UIF);
let dier_value = get::<F>(registers, Register::Dier);
if dier::UDE.is_set(dier_value) {
self.dma_requests.update += 1;
}
if cr2::CCDS.is_set(get::<F>(registers, Register::Cr2)) {
for channel in Channel::all() {
if CC_DE[channel.index()].is_set(dier_value) {
self.dma_requests.channels[channel.index()] += 1;
}
}
}
}
// Starts or stops the counter's clock, keeping the cycle count continuous across the stop.
fn set_counting(&mut self, counting: bool, context: &mut DeviceContext<'_>) {
if counting == self.counting {
return;
}
let clock = self.clock_cycle(context.now());
if counting {
self.paused += clock - self.stopped_at;
} else {
self.stopped_at = clock;
}
self.counting = counting;
}
// Whether `CNT_EN` should be on: `CEN`, the gate, and the debug halt, 18.3.1 and 18.3.14.
fn should_count(&self, registers: &RegisterFile) -> bool {
let cen = cr1::CEN.is_set(get::<F>(registers, Register::Cr1));
let gated = smcr::SMS.get(get::<F>(registers, Register::Smcr)) == SMS_GATED;
cen && !self.halted && (!gated || self.trigger == Level::High)
}
// The level channel `channel`'s input stage sees on its pin: the pin, or the XOR of the first three under
// `TI1S`, 18.3.13.
fn pin_level(&self, channel: Channel, registers: &RegisterFile) -> Level {
if channel.index() == 0 && cr2::TI1S.is_set(get::<F>(registers, Register::Cr2)) {
let bits = self.inputs[..3]
.iter()
.filter(|input| input.level == Level::High)
.count();
Level::from(bits % 2 == 1)
} else {
self.inputs[channel.index()].level
}
}
fn refilter(
&mut self,
channel: Channel,
registers: &RegisterFile,
context: &DeviceContext<'_>,
) {
let level = self.pin_level(channel, registers);
let clock = self.clock_cycle(context.now());
let filter = filter_delay::<F>(ccmr_value_of::<F>(channel, &IC_F, registers), registers);
start_filter(&mut self.inputs[channel.index()], level, clock, filter);
}
// The next filter that passes a transition, as the clock cycle it passes at.
fn next_filter_event(&self) -> Option<(u128, FilterEvent)> {
let inputs = self.inputs.iter().enumerate().filter_map(|(index, input)| {
input
.pending
.map(|(cycle, _)| (cycle, FilterEvent::Input(index)))
});
let etr = self.etr.pending.map(|(cycle, _)| (cycle, FilterEvent::Etr));
inputs.chain(etr).min_by_key(|(cycle, _)| *cycle)
}
// A filtered input changes level: the captures, the trigger, and the clocks that hang on it, 18.3.4.
fn filtered_edge(
&mut self,
event: FilterEvent,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
match event {
FilterEvent::Etr => {
if let Some((_, new)) = self.etr.pending.take() {
self.etr.filtered = new;
self.etrf_level(new, registers, context);
}
}
FilterEvent::Input(index) => {
if let Some((_, new)) = self.inputs[index].pending.take() {
self.inputs[index].filtered = new;
let source = Channel(u8::try_from(index).expect("four channels"));
self.input_level(source, new, registers, context);
}
}
}
}
// `ETRF` changes level: its rising edge is the external clock of mode 2, the trigger when selected, and the
// `OCxREF` clear; its level is the trigger's for gated mode, 18.3.3, 18.3.11, 18.3.14.
fn etrf_level(
&mut self,
new: Level,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
let smcr_value = get::<F>(registers, Register::Smcr);
if new == Level::High {
if smcr::ECE.is_set(smcr_value) {
self.external_tick(registers, context);
}
if smcr::TS.get(smcr_value) == TS_ETRF {
self.trigger_edge(registers, context);
}
self.clear_references(self.clock_cycle(context.now()), registers);
}
if smcr::TS.get(smcr_value) == TS_ETRF {
self.trigger = new;
self.gate(registers, context);
}
}
// A filtered channel input changes level: the captures it is mapped to, the trigger, and the encoder, 18.3.4.
fn input_level(
&mut self,
source: Channel,
new: Level,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
for channel in [source, source.partner()] {
let mapped = match channel_selection::<F>(channel, registers) {
CCS_OWN_INPUT => channel == source,
CCS_OTHER_INPUT => channel.partner() == source,
_ => false,
};
if mapped && self.edge_selected(channel, new, registers) {
self.capture(channel, registers, context);
}
}
self.trigger_from_input(source, new, registers, context);
self.encoder_edge(source, new, registers, context);
}
// `OCxCE`: a high `ETRF` drives the references low until the next update, 18.3.11.
fn clear_references(&mut self, clock: u128, registers: &RegisterFile) {
for channel in Channel::all() {
let clear_enabled = ccmr_field::<F>(channel, &OC_CE, registers);
let mode = self.output_mode(channel, registers);
let forced = matches!(mode, OCM_FORCE_ACTIVE | OCM_FORCE_INACTIVE);
if clear_enabled && !forced && channel_selection::<F>(channel, registers) == CCS_OUTPUT
{
self.set_reference(channel, false, clock);
let state = &mut self.channels[channel.index()];
state.cleared_for_updates = 1;
state.matched_since_clear = false;
}
}
}
// The trigger input `TRGI` drawn from a channel's filtered input as `TS` selects, 18.4.3.
fn trigger_from_input(
&mut self,
source: Channel,
new: Level,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
let ts = smcr::TS.get(get::<F>(registers, Register::Smcr));
let (trigger_level, is_edge) = match (ts, source.index()) {
(TS_TI1_EDGES, 0) => (Level::High, true),
(TS_TI1FP1, 0) | (TS_TI2FP2, 1) => {
let polarised = if self.ccer_field(source, &CC_P, registers) {
!new
} else {
new
};
(polarised, self.edge_selected(source, new, registers))
}
_ => return,
};
if is_edge {
self.trigger_edge(registers, context);
}
self.trigger = if ts == TS_TI1_EDGES {
Level::Low
} else {
trigger_level
};
self.gate(registers, context);
}
// Gated mode starts and stops the counter with the trigger's level and flags both, 18.3.14.
fn gate(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
let counting = self.should_count(registers);
if smcr::SMS.get(get::<F>(registers, Register::Smcr)) == SMS_GATED
&& counting != self.counting
{
flag::<F>(registers, sr::TIF);
self.set_counting(counting, context);
}
}
// An active edge on `TRGI`: the slave mode's action and `TIF`, 18.3.14 and 18.4.5.
fn trigger_edge(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
let smcr_value = get::<F>(registers, Register::Smcr);
if F::ADVANCED && tim1::cr2::CCUS.is_set(get::<F>(registers, Register::Cr2)) {
self.commutate(registers, context);
}
// A channel on `TRC` captures the trigger's edge, under an internal trigger only, 18.4.7.
if smcr::TS.get(smcr_value) <= TS_LAST_INTERNAL {
for channel in Channel::all() {
if channel_selection::<F>(channel, registers) == CCS_TRC {
self.capture(channel, registers, context);
}
}
}
match smcr::SMS.get(smcr_value) {
SMS_RESET => {
let cycle = self.counter_cycle(context.now());
self.update(UpdateSource::Trigger, cycle, registers, context);
self.matches_at_segment_start(Some(self.direction), registers);
// The reset goes out on `TRGO` under `MMS` 000, 18.4.2.
if cr2::MMS.get(get::<F>(registers, Register::Cr2)) == MMS_RESET {
self.trigger_pulses += 1;
}
self.flag_trigger(registers);
}
SMS_TRIGGER => {
let cr1_value = get::<F>(registers, Register::Cr1);
set::<F>(registers, Register::Cr1, cr1::CEN.set(cr1_value, 1));
let counting = self.should_count(registers);
self.set_counting(counting, context);
self.fast_enable(self.clock_cycle(context.now()), registers);
self.flag_trigger(registers);
}
SMS_EXTERNAL_CLOCK_1 => {
// With both external clock modes on, the clock is `ETRF` once, 18.4.3 `ECE` note 3.
if !smcr::ECE.is_set(get::<F>(registers, Register::Smcr)) {
self.external_tick(registers, context);
}
self.flag_trigger(registers);
}
_ => {}
}
}
fn flag_trigger(&mut self, registers: &mut RegisterFile) {
flag::<F>(registers, sr::TIF);
if dier::TDE.is_set(get::<F>(registers, Register::Dier)) {
self.dma_requests.trigger += 1;
}
}
// `OCxFE`: the trigger acts like a compare match on a PWM channel, 18.3.10.
fn fast_enable(&mut self, clock: u128, registers: &RegisterFile) {
for channel in Channel::all() {
let mode = self.output_mode(channel, registers);
if ccmr_field::<F>(channel, &OC_FE, registers) && matches!(mode, OCM_PWM_1 | OCM_PWM_2)
{
self.set_reference(channel, mode == OCM_PWM_2, clock);
}
}
}
// One edge of an external clock: a counter clock cycle, 18.3.3.
fn external_tick(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
if !self.counting {
return;
}
self.external_cycles += 1;
self.advance_counter_to(self.external_cycles, registers, context);
}
// The encoder interface, table 97: a valid edge counts one cycle in the direction the other input's level
// gives.
fn encoder_edge(
&mut self,
source: Channel,
new: Level,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
let sms = smcr::SMS.get(get::<F>(registers, Register::Smcr));
if !matches!(sms, SMS_ENCODER_1 | SMS_ENCODER_2 | SMS_ENCODER_3) {
return;
}
// `DIR` follows every transition; the count follows the input the mode names, 18.3.12 and 18.4.3.
let counted = matches!(
(sms, source.index()),
(SMS_ENCODER_1, 0) | (SMS_ENCODER_2, 1) | (SMS_ENCODER_3, 0 | 1)
);
let polarised = |channel: Channel, level: Level| {
if self.ccer_field(channel, &CC_P, registers) {
!level
} else {
level
}
};
let edge = polarised(source, new);
let other = source.partner();
let other_level = polarised(other, self.inputs[other.index()].filtered);
let rising = edge == Level::High;
let up = match source.index() {
0 => rising != (other_level == Level::High),
_ => rising == (other_level == Level::High),
};
let direction = if up { Direction::Up } else { Direction::Down };
if direction != self.direction {
let cycle = self.counter_cycle(context.now());
let count = self.count_at(cycle);
self.direction = direction;
self.segment_cycle = cycle;
self.segment_count = count;
set_direction_bit::<F>(direction, registers);
}
if counted {
self.external_tick(registers, context);
}
}
// A capture on `channel`: the prescaler, the latch, the flags, and the request, 18.3.5.
fn capture(
&mut self,
channel: Channel,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
if !self.ccer_field(channel, &CC_E, registers) {
return;
}
let every = 1u64 << ccmr_value_of::<F>(channel, &IC_PSC, registers);
let state = &mut self.channels[channel.index()];
state.prescaled_edges += 1;
if !state.prescaled_edges.is_multiple_of(every) {
return;
}
let count = self.count_at(self.counter_cycle(context.now()));
set::<F>(registers, channel.ccr(), count);
self.channels[channel.index()].shadow = count;
self.flag_capture(channel, registers);
}
// A capture flags like a compare, with the overcapture flag when the last one was not read, 18.4.5.
fn flag_capture(&mut self, channel: Channel, registers: &mut RegisterFile) {
if CC_IF[channel.index()].is_set(get::<F>(registers, Register::Sr)) {
flag::<F>(registers, CC_OF[channel.index()]);
}
self.flag_compare(channel, registers);
}
// Applies every event up to `now`, counter events and filter completions in the order of their instants.
fn settle(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
let now = context.now();
self.seen = now;
loop {
let counter = (!self.external)
.then(|| self.next_counter_event(registers))
.flatten()
.and_then(|(cycle, event)| {
self.instant_of_counter_cycle(cycle)
.map(|at| (at, Pending::Counter(cycle, event)))
});
let filter = self.next_filter_event().and_then(|(cycle, event)| {
self.instant_of_clock_cycle(cycle)
.map(|at| (at, Pending::Filter(event)))
});
let Some((instant, pending)) =
counter.into_iter().chain(filter).min_by_key(|(at, _)| *at)
else {
break;
};
if instant > now {
break;
}
match pending {
Pending::Counter(cycle, CounterEvent::Boundary(source)) => {
self.boundary(cycle, source, registers, context);
}
Pending::Counter(_, CounterEvent::Match(found)) => {
self.compare_match(found, Some(self.direction), registers);
}
Pending::Filter(event) => self.filtered_edge(event, registers, context),
}
}
}
// After any change: the clock's state, the level of the interrupt, `TRGO`'s level modes, and the wake-up
// for the next event.
fn finish(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
let counting = self.should_count(registers);
self.set_counting(counting, context);
if F::ADVANCED {
self.refresh_break(registers);
}
self.refresh_interrupt(registers, context);
self.refresh_trigger_output(registers);
self.schedule(registers, context);
}
fn refresh_interrupt(&mut self, registers: &RegisterFile, context: &mut DeviceContext<'_>) {
let status = get::<F>(registers, Register::Sr);
let enables = get::<F>(registers, Register::Dier);
let mut wanted = [Level::Low; LINES];
for (flags, pairs) in LINE_FLAGS {
let raised = pairs
.iter()
.any(|(flag, enable)| flag.is_set(status) && enable.is_set(enables));
if raised {
wanted[F::line(flags)] = Level::High;
}
}
for (line, (level, current)) in wanted.iter().zip(self.interrupts.iter_mut()).enumerate() {
if level != current {
*current = *level;
context.interrupt(line, *level);
}
}
}
fn refresh_trigger_output(&mut self, registers: &RegisterFile) {
let mms = cr2::MMS.get(get::<F>(registers, Register::Cr2));
self.trigger_output = match mms {
MMS_ENABLE => Level::from(self.counting),
MMS_RESET | MMS_UPDATE | MMS_COMPARE_PULSE => Level::Low,
reference => {
let index = usize::try_from(reference - MMS_FIRST_REFERENCE).expect("three bits");
Level::from(self.channels[index].reference)
}
};
}
fn schedule(&mut self, registers: &RegisterFile, context: &mut DeviceContext<'_>) {
let counter = (!self.external)
.then(|| self.next_counter_event(registers))
.flatten()
.and_then(|(cycle, _)| self.instant_of_counter_cycle(cycle));
let filter = self
.next_filter_event()
.and_then(|(cycle, _)| self.instant_of_clock_cycle(cycle));
let next = counter.into_iter().chain(filter).min();
if next != self.wakeup.map(|(at, _)| at) {
self.wakeup = next.map(|at| (at, context.schedule(at)));
}
}
// Starts a new segment at the current cycle with `count`, keeping the prescaler's phase.
fn start_segment(&mut self, count: u64, context: &DeviceContext<'_>) {
let cycle = self.counter_cycle(context.now());
let phase = cycle.saturating_sub(self.segment_cycle) % (u128::from(self.psc) + 1);
self.segment_cycle = cycle - phase;
self.segment_count = count & self.counter.mask();
}
fn set_count(&mut self, count: u64, context: &DeviceContext<'_>) {
self.start_segment(count, context);
for channel in &mut self.channels {
channel.last_match = None;
}
}
// Restarts the segment at the current count before a shadow changes, so nothing is re-timed from the
// segment's start into the past.
fn rebase(&mut self, context: &DeviceContext<'_>) {
let count = self.counter(context.now());
self.start_segment(count, context);
}
fn write_cr1(
&mut self,
value: u64,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
let centre = cr1::CMS.get(value) != CMS_EDGE_ALIGNED;
let encoder = matches!(
smcr::SMS.get(get::<F>(registers, Register::Smcr)),
SMS_ENCODER_1 | SMS_ENCODER_2 | SMS_ENCODER_3
);
if centre || encoder {
// DIR is the hardware's in centre-aligned and encoder modes, 18.4.1.
registers.set(
Register::Cr1,
cr1::DIR.set(value, u64::from(self.direction == Direction::Down)),
);
} else {
let direction = if cr1::DIR.is_set(value) {
Direction::Down
} else {
Direction::Up
};
if direction != self.direction {
let count = self.counter(context.now());
self.direction = direction;
self.set_count(count, context);
}
}
let arr = get::<F>(registers, Register::Arr) & self.counter.mask();
if !cr1::ARPE.is_set(value) && arr != self.arr {
self.rebase(context);
self.arr = arr;
}
}
fn write_smcr(
&mut self,
value: u64,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
let sms = smcr::SMS.get(value);
let external = smcr::ECE.is_set(value)
|| matches!(
sms,
SMS_EXTERNAL_CLOCK_1 | SMS_ENCODER_1 | SMS_ENCODER_2 | SMS_ENCODER_3
);
if external != self.external {
let count = self.counter(context.now());
self.external = external;
self.external_cycles = 0;
self.set_count(count, context);
}
for channel in Channel::all().take(2) {
self.refilter(channel, registers, context);
}
self.feed_etr(registers, context);
}
fn write_egr(
&mut self,
value: u64,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
if egr::UG.is_set(value) {
let cycle = self.counter_cycle(context.now());
self.update(UpdateSource::Software, cycle, registers, context);
self.matches_at_segment_start(Some(self.direction), registers);
if cr2::MMS.get(get::<F>(registers, Register::Cr2)) == MMS_RESET {
self.trigger_pulses += 1;
}
}
for channel in Channel::all() {
if CC_G[channel.index()].is_set(value) {
if channel_selection::<F>(channel, registers) == CCS_OUTPUT {
self.flag_compare(channel, registers);
} else {
let count = self.counter(context.now());
set::<F>(registers, channel.ccr(), count);
self.channels[channel.index()].shadow = count;
self.flag_capture(channel, registers);
}
}
}
if egr::TG.is_set(value) {
self.flag_trigger(registers);
}
if F::ADVANCED {
if tim1::egr::COMG.is_set(value) {
self.commutate(registers, context);
}
if tim1::egr::BG.is_set(value) {
Self::take_break(registers);
}
}
set::<F>(registers, Register::Egr, 0);
}
fn write_ccmr(
&mut self,
register: Register,
value: u64,
registers: &mut RegisterFile,
context: &DeviceContext<'_>,
) {
let before = self.advanced.kept[F::own(register).map_or(0, |own| own.index() % KEPT)];
let first = if register == Register::Ccmr1Input {
Channel(0)
} else {
Channel(2)
};
let pair = [first, first.partner()];
let mut held = value;
let locked = Self::lock_level(registers) >= LOCK_LEVEL_3;
for channel in pair {
if self.ccer_field(channel, &CC_E, registers) {
// CCxS is writable only while the channel is off, 18.4.7.
held = CC_S[channel.index()].set(held, self.channels[channel.index()].selection);
}
if locked && self.channels[channel.index()].selection == CCS_OUTPUT {
let index = channel.index();
held = OC_M[index].set(held, OC_M[index].get(before));
held = OC_PE[index].set(held, OC_PE[index].get(before));
}
}
set::<F>(registers, register, held);
let cycle = self.counter_cycle(context.now());
let clock = self.clock_cycle(context.now());
for channel in pair {
let selection = channel_selection::<F>(channel, registers);
if selection == CCS_OUTPUT && !ccmr_field::<F>(channel, &OC_PE, registers) {
self.channels[channel.index()].shadow =
get::<F>(registers, channel.ccr()) & self.counter.mask();
}
self.channels[channel.index()].selection = selection;
// Frozen leaves the reference where it is, 18.4.7.
match self.output_mode(channel, registers) {
OCM_FORCE_ACTIVE => self.set_reference(channel, true, clock),
OCM_FORCE_INACTIVE => self.set_reference(channel, false, clock),
_ => {}
}
self.refresh_pwm(channel, cycle, registers);
}
}
fn write_ccr(&mut self, channel: Channel, registers: &mut RegisterFile) {
if channel_selection::<F>(channel, registers) != CCS_OUTPUT {
// Read-only in input mode: the last capture stands, 18.4.13.
set::<F>(
registers,
channel.ccr(),
self.channels[channel.index()].shadow,
);
return;
}
if !ccmr_field::<F>(channel, &OC_PE, registers) {
self.channels[channel.index()].shadow =
get::<F>(registers, channel.ccr()) & self.counter.mask();
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum CounterEvent {
Boundary(UpdateSource),
Match(Match),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Match {
channel: Channel,
cycle: u128,
value: u64,
}
impl CounterEvent {
// At one cycle the boundary is applied before a match, so a match at the new count sees the new segment.
const fn order(self) -> u8 {
match self {
Self::Boundary(_) => 0,
Self::Match(..) => 1,
}
}
}
#[derive(Debug, Clone, Copy)]
enum FilterEvent {
Input(usize),
Etr,
}
#[derive(Debug, Clone, Copy)]
enum Pending {
Counter(u128, CounterEvent),
Filter(FilterEvent),
}
impl<F: Family> Device for Tim<F> {
type Register = F::Register;
fn layout(&self) -> &'static [RegisterSpec] {
F::layout(self.counter)
}
fn read(
&mut self,
register: F::Register,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) -> Option<u64> {
self.settle(registers, context);
let answer = match F::shared(register) {
Some(Register::Cnt) => Some(self.counter(context.now())),
Some(shared @ (Register::Ccr1 | Register::Ccr2 | Register::Ccr3 | Register::Ccr4)) => {
let channel = Channel::all().find(|channel| channel.ccr() == shared)?;
if channel_selection::<F>(channel, registers) != CCS_OUTPUT {
// Reading a capture clears its flag, 18.3.5.
let status = get::<F>(registers, Register::Sr);
set::<F>(
registers,
Register::Sr,
CC_IF[channel.index()].set(status, 0),
);
}
None
}
Some(Register::Dmar) => {
let target = self.burst_step(registers)?;
Some(registers.get(target))
}
_ => None,
};
self.finish(registers, context);
answer
}
fn write(
&mut self,
register: F::Register,
value: u64,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
self.settle(registers, context);
let before = self.advanced.kept[register.index() % KEPT];
let Some(shared) = F::shared(register) else {
if Some(register) == F::break_and_dead_time() {
self.write_bdtr(before, registers);
}
self.advanced.kept[register.index() % KEPT] = registers.get(register);
self.finish(registers, context);
return;
};
// A write to the high half of `CNT`, `ARR` or a `CCRx` does not stand on a sixteen-bit timer: table
// 100 leaves it reserved there, and ST's file declares it writable on all four. So the register
// keeps what the part keeps, and everything below reads the instance's own width.
let value = match shared {
Register::Cnt
| Register::Arr
| Register::Ccr1
| Register::Ccr2
| Register::Ccr3
| Register::Ccr4 => {
let narrowed = value & self.counter.mask();
registers.set(register, narrowed);
narrowed
}
_ => value,
};
match shared {
Register::Cr1 => {
let was_counting = self.counting;
self.write_cr1(value, registers, context);
let counting = self.should_count(registers);
self.set_counting(counting, context);
if counting && !was_counting {
self.matches_at_segment_start(Some(self.direction), registers);
}
}
Register::Cr2 => self.write_cr2(before, registers, context),
Register::Smcr => self.write_smcr(value, registers, context),
Register::Egr => self.write_egr(value, registers, context),
Register::Ccmr1Input | Register::Ccmr1Output => {
self.write_ccmr(Register::Ccmr1Input, value, registers, context);
}
Register::Ccmr2Input | Register::Ccmr2Output => {
self.write_ccmr(Register::Ccmr2Input, value, registers, context);
}
Register::Ccer => {
if Self::lock_level(registers) >= LOCK_LEVEL_2 {
// 17.4.18 `LOCK` level 2: the polarities of a channel configured as an output.
let mut mask = 0;
for channel in Channel::all() {
if channel_selection::<F>(channel, registers) == CCS_OUTPUT {
mask |= CC_P[channel.index()].mask() | CC_NP[channel.index()].mask();
}
}
Self::keep(registers, register, mask, before);
}
let cycle = self.counter_cycle(context.now());
for channel in Channel::all() {
if !self.ccer_field(channel, &CC_E, registers) {
// The capture prescaler resets while the channel is off, 18.4.7.
self.channels[channel.index()].prescaled_edges = 0;
}
self.refresh_pwm(channel, cycle, registers);
}
}
Register::Cnt => {
self.set_count(value, context);
self.matches_at_segment_start(Some(self.direction), registers);
}
Register::Arr => {
// Without preload the new auto-reload is live from here on, never from the segment's start.
if !cr1::ARPE.is_set(get::<F>(registers, Register::Cr1)) && value != self.arr {
self.rebase(context);
self.arr = value;
}
}
Register::Ccr1 | Register::Ccr2 | Register::Ccr3 | Register::Ccr4 => {
if let Some(channel) = Channel::all().find(|channel| channel.ccr() == shared) {
self.rebase(context);
self.write_ccr(channel, registers);
self.refresh_pwm(channel, self.counter_cycle(context.now()), registers);
}
}
Register::Dmar => {
if let Some(target) = self.burst_step(registers) {
registers.write_over_bus(target, value);
self.write(target, value, registers, context);
return;
}
}
_ => {}
}
self.advanced.kept[register.index() % KEPT] = registers.get(register);
self.finish(registers, context);
}
/// `CK_INT` has changed rate. The cycles so far stand where they are, the ones from here come at the
/// new rate, and the wake-up asked for at the old rate is asked for again where it now falls.
fn on_clock(&mut self, hz: u64, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
let now = context.now();
self.settle(registers, context);
self.epoch = Epoch {
at: now,
cycle: self.clock_cycle(now),
};
self.clock_hz = hz;
self.schedule(registers, context);
}
fn on_event(
&mut self,
event: EventId,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
if self.wakeup.map(|(_, id)| id) != Some(event) {
return;
}
self.settle(registers, context);
self.finish(registers, context);
}
/// The system break, 17.3.12: a pulse from the clock security system that `BKE` lets through. It is
/// not latched, which is erratum 2.7.1, so `AOE` re-arms the outputs at the next update as after any
/// other break that has passed.
fn on_request(
&mut self,
change: RequestChange,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
let source = (change.instance, change.request);
if self.advanced.system_break != Some(source) || change.level != Level::High {
return;
}
self.settle(registers, context);
if tim1::bdtr::BKE.is_set(Self::bdtr(registers)) {
Self::take_break(registers);
}
self.finish(registers, context);
}
}
impl<F: Family> Tim<F> {
// The register the next `DMAR` access reaches, walking `DBL + 1` registers from `DBA`, 18.4.18.
fn burst_step(&mut self, registers: &RegisterFile) -> Option<F::Register> {
let dcr_value = get::<F>(registers, Register::Dcr);
let base = dcr::DBA.get(dcr_value);
let length = dcr::DBL.get(dcr_value);
let index = base + self.burst_index;
self.burst_index = (self.burst_index + 1) % (length + 1);
F::burst(index)
}
}
// The filter row of `ICxF` or `ETF`: how many `CK_INT` cycles a transition needs to pass, 18.4.3 and 18.4.7.
fn filter_delay<F: Family>(row: u64, registers: &RegisterFile) -> u128 {
let (sampling, samples) = FILTERS[usize::try_from(row).expect("four bits")];
let dts_division = 1u128 << cr1::CKD.get(get::<F>(registers, Register::Cr1));
let period = match sampling {
Sampling::Internal => 1,
Sampling::Dts(divisor) => dts_division * divisor,
};
period * samples
}
fn start_filter(input: &mut Input, level: Level, clock: u128, delay: u128) {
if level == input.filtered {
input.pending = None;
} else {
input.pending = Some((clock + delay, level));
}
}
fn ccmr_value_of<F: Family>(
channel: Channel,
fields: &[&FieldSpec; CHANNELS],
registers: &RegisterFile,
) -> u64 {
fields[channel.index()].get(get::<F>(registers, channel.ccmr()))
}
fn ccmr_field<F: Family>(
channel: Channel,
fields: &[&FieldSpec; CHANNELS],
registers: &RegisterFile,
) -> bool {
fields[channel.index()].is_set(get::<F>(registers, channel.ccmr()))
}
fn flag<F: Family>(registers: &mut RegisterFile, field: &FieldSpec) {
let status = get::<F>(registers, Register::Sr);
set::<F>(registers, Register::Sr, field.set(status, 1));
}
fn channel_selection<F: Family>(channel: Channel, registers: &RegisterFile) -> u64 {
ccmr_value_of::<F>(channel, &CC_S, registers)
}
fn set_direction_bit<F: Family>(direction: Direction, registers: &mut RegisterFile) {
let cr1_value = get::<F>(registers, Register::Cr1);
set::<F>(
registers,
Register::Cr1,
cr1::DIR.set(cr1_value, u64::from(direction == Direction::Down)),
);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::soc::stm32f427::generated::tim2;
/// Every register and field the two layouts share sits at the same offset and the same bits, which is
/// what lets one model written against chapter 18's tables read chapter 17's registers through them.
#[test]
fn the_shared_fields_sit_at_the_same_bits_in_both_layouts() {
let mut shared = 0;
for general in tim2::REGISTERS {
let Some(advanced) = tim1::REGISTERS
.iter()
.find(|candidate| candidate.name == general.name)
else {
continue;
};
assert_eq!(general.offset, advanced.offset, "{}", general.name);
for field in general.fields {
let Some(twin) = advanced
.fields
.iter()
.find(|candidate| candidate.name == field.name)
else {
continue;
};
assert_eq!(field.bit, twin.bit, "{}.{}", general.name, field.name);
assert_eq!(field.width, twin.width, "{}.{}", general.name, field.name);
shared += 1;
}
}
assert!(shared > 80, "{shared} fields shared");
}
}