rigless 0.2.2

Virtual hardware-in-the-loop runtime: run unmodified firmware on an emulated MCU with every peripheral, chip model and unit of virtual time in Rust, and a CPU core of its own that translates the firmware's instructions to host code
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
// Copyright (c) 2026 Rigless Labs.
// SPDX-License-Identifier: MIT OR Apache-2.0
//! The timers of RM0433 rev 9 chapters 38 to 42, one model by its [`Family`], the layout it is addressed
//! through, and its [`Kind`], the timer it is. Fourteen timers share it: the advanced-control TIM1 and TIM8
//! (chapter 38), the general-purpose TIM2 to TIM5 (39), TIM12 to TIM14 (40), TIM15 to TIM17 (41), and the basic
//! TIM6 and TIM7 (42). The registers sit at the same offsets in every layout, so the model names one by its
//! slot and finds its row once, and a timer answers for the registers its kind has. A register a kind lacks
//! but its layout carries, as TIM12 does of TIM2's table, stores what is written and does nothing.
//!
//! **What the model handles and what falls to the register file.** Every register the chapters give has an
//! arm: `CNT`, `ARR`, `PSC` and the `CCRx` keep their width, `CR1`, `CR2`, `SMCR`, `EGR`, the capture/compare
//! mode and enable registers, `BDTR`, `AF1`, `AF2`, `TISEL`, `DCR` and `DMAR` act, and `SR` and `DIER` are
//! read for the flags and the lines. `RCR` is read at the update. The register file holds the stored bits and
//! applies `SR`'s `rc_w0` that the part corrects.
//!
//! | Register or section | Behavior | Source | Test |
//! |---|---|---|---|
//! | Time base | `PSC + 1` cycles a count, the prescaler buffered to the update, `ARR` preloaded under `ARPE`, a counter blocked while `ARR` is zero | 38.3.1, 39.3.1, 40.3.1, 41.4.1, 42.3.1, 39.4.15 | `the_update_comes_every_period_without_drift`, `arr_preload_and_the_prescaler_take_effect_at_the_update`, `a_null_auto_reload_blocks_the_counter` |
//! | Counter modes | Up, down, center-aligned with `DIR` the hardware's and the flags gated by `CMS` | 38.3.2, 39.3.2 | `down_counting_underflows_from_arr_to_zero`, `center_aligned_counting_turns_at_arr_and_zero`, `center_aligned_flags_follow_figure_435` |
//! | `UDIS`, `URS`, `UG`, `OPM` | Update gates and one pulse | 38.4.1, 38.4.6 | `ug_reinitializes_and_sets_uif_unless_urs`, `udis_keeps_the_shadows_but_reinitializes`, `one_pulse_mode_stops_at_the_update` |
//! | Counter width | 32 bits on TIM2 and TIM5, 16 on the rest | 39.2 | `a_narrow_timer_counts_and_reloads_in_sixteen_bits`, `a_wide_timer_counts_and_reloads_in_thirty_two` |
//! | Repetition counter | An update every `REP + 1` overflows, sixteen bits on TIM1 and TIM8 and eight on TIM15 to TIM17 | 38.3.3, 38.4.15, 41.5.13 | `the_update_comes_every_repetition_plus_one`, `tim15_has_a_dead_time_on_channel_1_and_an_eight_bit_repetition_counter` |
//! | Clock | `CK_INT` followed as the tree changes it, 64 MHz out of reset ([`RESET_CLOCK_HZ`]), external clock modes 1 and 2, the encoder | 38.3.5, 8.5.6 | `the_counter_carries_across_a_change_of_clock`, `a_timer_built_with_no_clock_runs_at_the_rate_it_wakes_up_with`, `external_clock_mode_1_counts_the_trigger_edges`, `encoder_mode_counts_with_the_phase` |
//! | `ETR` | `ETP`, `ETPS`, `ETF`, and the source `ETRSEL` chooses | 38.3.4, 39.4.22 | `etp_inverts_and_etf_filters_etr`, `etrsel_and_tisel_choose_the_source_the_timer_sees` |
//! | Input capture | Filter, edge, prescaler, `CCxIF` and `CCxOF`, the input `TISEL` chooses, PWM input | 38.3.6 to 38.3.8, 38.4.30 | `a_capture_latches_the_counter_and_flags_it`, `the_input_filter_drops_a_short_pulse`, `pwm_input_measures_period_and_duty` |
//! | Output compare | Frozen, active, inactive, toggle, forced, flags at the match | 38.3.9, 38.3.10 | `a_compare_match_toggles_the_output_at_its_instant`, `forced_output_holds_its_level_and_the_flag_still_matches` |
//! | PWM | Modes 1 and 2, preloads, and [`Tim::pulse`] and [`Tim::complementary_pulse`] reading the waveform | 38.3.11 | `pwm_mode_1_is_high_below_ccr`, `the_pulse_is_the_compare_over_the_period_through_the_polarity`, `the_motor_outputs_on_tim5_and_tim4_make_the_pulse_the_driver_asks_for` |
//! | Asymmetric, combined and 3-phase PWM | The four-bit `OCxM`, `OCxREFC`, channels 5 and 6, `GC5Cx` | 38.3.12 to 38.3.14 | `asymmetric_pwm_takes_the_first_reference_up_and_the_second_down`, `combined_pwm_gives_the_or_or_the_and_of_a_pairs_two_references`, `channel_5_groups_into_channel_1_and_flags_its_own_match` |
//! | Complementary outputs, dead-time | Table 337, `DTG`, the off-states | 38.3.15 | `the_complementary_output_is_the_reference_inverted_with_a_dead_time_on_each_rise`, `the_motor_outputs_on_tim8s_complementary_pins_make_their_pulse` |
//! | Break | `BRK` and `BRK2` with their sources, polarities, filters, `MOE`, `AOE`, `BIF`, `B2IF`, `SBIF`, system breaks | 38.3.16, 38.3.17, 38.4.20 | `a_break_on_the_pin_holds_moe_low_and_bif_high_until_the_pin_releases`, `break_2_leaves_the_outputs_at_their_inactive_level_and_flags_b2if`, `a_break_gathers_its_enabled_sources_each_through_its_own_polarity`, `the_break_filter_needs_its_samples_before_the_break_acts`, `a_system_break_flags_sbif_and_clears_moe_when_the_break_is_enabled` |
//! | `OCxREF` clear, 6-step, `LOCK` | `OCxCE` on `ETRF`, commutation, three lock levels | 38.3.18, 38.3.19, 38.4.20 | `etrf_clears_the_reference_until_the_update`, `the_commutation_event_applies_the_preloaded_control_bits`, `lock_freezes_what_each_level_says` |
//! | One pulse, retriggerable | `OCxFE`, `SMS` 1000 with `OCxM` 1000 and 1001 | 38.3.20, 38.3.21 | `fast_enable_acts_on_the_trigger`, `a_retriggerable_pulse_starts_on_the_trigger_and_is_extended_by_another` |
//! | `UIFREMAP` | `UIF` in `CNT` bit 31, hiding the wide counter's | 38.3.23, 39.3.16 | `uifremap_copies_the_update_flag_into_bit_31_of_the_counter`, `uifremap_hides_the_wide_counters_top_bit_from_reads` |
//! | `TI1S` | `XOR` of two or three pins | 38.3.24 | `ti1s_xors_the_three_inputs` |
//! | Slave modes, `TRGO`, `TRGO2` | Reset, gated, trigger, combined, `MMS`, `MMS2` | 38.3.26, 38.3.27 | `reset_mode_reinitializes_on_the_trigger`, `gated_mode_counts_while_the_trigger_is_high`, `trgo_follows_the_master_mode`, `trgo2_carries_the_event_mms2_selects` |
//! | DMA | A request stands from its event until the transfer serves it, "pending until served by the DMA controller": by the last access of the `DBL + 1` to `DMAR` for update, trigger and commutation, by the access to `CCRx` for a channel, or by [`Tim::acknowledge`] where the data goes elsewhere. The burst walks the registers through `DCR` and `DMAR`, and a read of `DMAR` answers as the register it reaches | 17.1, 38.3.28, Table 103 | `an_update_request_stands_until_the_burst_has_been_served`, `a_request_raised_before_its_controller_is_enabled_is_standing_when_it_looks`, `a_dshot_burst_loads_the_four_compare_values_an_update`, `a_dmar_read_answers_as_the_register_it_reaches_does`, `the_request_outputs_are_the_numbers_table_103_inputs_are_wired_to` |
//! | Settling | The counter is settled at each access and woken for only when an interrupt, request, pin, trigger output or slave could see its events, and a write is applied after the events due before it | | `reading_the_counter_over_and_over_is_a_pure_function_of_the_instant`, `center_aligned_flags_follow_figure_435` |
//! | `LOCK` and the reset of `AF1` | The lock compares a write with the register as reset left it | 38.4.26, 38.4.20 | `lock_level_1_keeps_the_alternate_function_registers_at_their_reset_value` |
//! | Lines | One for most, four on TIM1 and TIM8 | Table 147 | `chapter_38s_flags_raise_four_lines`, `tim8_raises_its_own_four_lines` |
//! | The ChibiOS tick | TIM12 and TIM5 free-running with an alarm in `CCR1` | 40.4, 39.4 | `an_alarm_through_ccr1_interrupts_at_its_microsecond_exactly`, `an_alarm_past_the_wrap_waits_for_the_counter_to_come_round`, `the_bootloaders_tick_on_tim5_counts_in_thirty_two_bits` |
//! | TIM12 to TIM17, TIM6, TIM7 | Their channels and modes by [`Kind`] | 40, 41, 42 | `tim12_has_two_channels_and_counts_up_only`, `tim13_is_one_channel_and_its_oc1_is_the_pin_a_slave_can_trigger_on`, `tim15_has_one_break_and_flags_a_system_break_in_bif`, `tim16_and_tim17_are_one_channel_with_a_break_and_only_the_plain_modes`, `a_basic_timer_counts_to_its_auto_reload_and_raises_its_line` |
//! | Resets and corrections | `ARR` and `AF1`/`AF2` reset values, `SR` `rc_w0` | 38.4.14, 38.4.26, 38.4.5 | `every_timer_comes_out_of_reset_as_the_manual_gives_it`, `every_timers_status_register_clears_on_a_zero_and_ignores_a_one` |
//! | Snapshot, reset | The state serializes, a reset restarts the timer | | `the_model_state_round_trips_through_a_snapshot`, `a_reset_stops_the_counter_and_keeps_the_clock`, `two_runs_of_one_script_are_the_same_run` |
//! | ES0392 rev 15 2.16.1 | In combined reset and trigger with `MSM` and one pulse, a trigger at `ARR` starts no pulse. Present on revision V | modeled | `erratum_2_16_1_loses_a_trigger_that_arrives_at_the_auto_reload` |
//! | ES0392 rev 15 2.16.2 | A consecutive compare one cycle later is missed | modeled | `erratum_2_16_2_misses_the_consecutive_match`, `erratum_2_16_2_at_the_valley_and_in_other_modes` |
//! | ES0392 rev 15 2.16.3 | The output clear with an external reset holds one extra cycle | modeled | `erratum_2_16_3_holds_the_cleared_reference_one_extra_cycle` |
//!
//! **What the model leaves out, and why.**
//!
//! | Behavior | Source | Reason |
//! |---|---|---|
//! | Debug mode: the counter holding while the core is halted | 38.3.29, 42.3.5 | A debugger halt already stops the virtual clock |
//! | `MSM`, the trigger's resynchronization delay, and the 1 APB cycle on `BKE`/`BKP` writes | 38.4.3, 38.4.20 | The manual gives a cycle or two and the model applies the edge as it arrives. Only erratum 2.16.1 reads `MSM` |
//! | The bidirectional break's output drive | 38.3.17 | The comparator's open-drain output belongs to the comparator and the pin |
//! | Which timer feeds which `ITRx`, and the converters' triggers | Tables 336, 341, 344, 349 | The part wires [`Tim::trigger_output`] to [`Tim::drive_internal_trigger`] |
//! | The Hall sensor interface | 38.3.25 | A configuration of rows above across two timers |
//! | The dead-time on the way to the idle state | 38.3.16 | The idle levels are driven as `MOE` falls |
//! | Combined and retriggerable waveforms in [`Tim::pulse`] | 38.3.12 to 38.3.14 | The pin is read with [`Tim::output`] and [`Tim::reference`] |
//! | ES0392 rev 15 2.2.2, timer system breaks do not work | | Absent on revision V, this part's: the system breaks act |
//! | ES0392 rev 15 2.16.4, unexpected output when `ocref_clr` is used in combined or asymmetric modes | | Present on revision V with no workaround, and the sheet gives no waveform to reproduce |
//! | Wiring `TIMx_CCMR3` and other holes | 39.4.30 | ChibiOS writes the word at `0x54` of every timer. Where the layout has no register there, the part answers it as undefined space in a peripheral, which reads zero and drops the write, Table 8 note 1, and the session is told |
//!
//! **What the part must wire.** Clocks: TIM2 to TIM7 and TIM12 to TIM14 run on `rcc_timx_ker_ck`, TIM1, TIM8,
//! TIM15, TIM16 and TIM17 on `rcc_timy_ker_ck`, Table 57. Requests: [`UPDATE_REQUEST`], [`CHANNEL_REQUESTS`],
//! [`TRIGGER_REQUEST`] and [`COMMUTATION_REQUEST`] for the multiplexer of Table 103, levels it reads, and
//! [`Tim::acknowledge`] from the controller for a transfer that doesn't touch the timer. Interrupts by position in
//! the instance's table. System breaks by [`Tim::breaks_on`].
//!
//! **Where this model departs from the vendor's file.** `SR` is read-write there where the manual types every
//! flag `rc_w0`, `ARR` resets to zero, `AF1` and `AF2` to zero, and `RCR`'s `REP` is eight bits on TIM1 where
//! 38.4.15 gives sixteen. The part corrects the first three in `memory_map.rs`, and the model reads sixteen.
//! ST's file misspells `OC4CE` and `OC4M[3]`, and the model reads their bits.
//!
//! **Determinism.** The model reads no clock and draws no number. `two_runs_of_one_script_are_the_same_run`
//! drives two instances through one script and compares their interrupts, registers and state.

use std::marker::PhantomData;

use crate::device::{Level, RegisterFile};
use crate::soc::stm32h743::generated::tim1::{cnt, cr1, sr};
use crate::soc::{FieldSpec, Layout};
use crate::time::{EventId, VirtualNanos};

mod access;
mod breaks;
mod count;
mod family;
mod fields;
mod inputs;
mod outputs;

pub use family::{
    ADVANCED_TIMERS, Advanced, BASIC_TIMERS, Basic, CHANNEL_REQUESTS, COMMUTATION_REQUEST, Counter,
    Family, Flags, General, Kind, TIMER_15, TIMER_16, TIMER_17, TIMERS, TRIGGER_REQUEST, Tim15,
    Tim16, Tim17, Timer, UPDATE_REQUEST,
};

use family::{Map, SLOTS, Slot};
use fields::{CHANNEL_COUNT, CHANNELS, INPUTS};

/// The rate every timer runs at out of reset: 64 MHz. `RCC_CR` resets with the 64 MHz internal oscillator
/// as the system clock, `HSIDIV` at "Division by 1, hsi(_ker)_ck = 64 MHz (default after reset)", RM0433 rev 9
/// section 8.7.2. `RCC_D1CFGR`, `RCC_D2CFGR` and `RCC_CFGR` reset to zero, sections 8.7.7 to 8.7.9, which
/// divides the system clock by nothing on the way to the bus, and with `D2PPRE1` and `D2PPRE2` below
/// `0b100` and `TIMPRE` clear, Table 57 in section 8.5.6 has the timer clock "equal to the bus clock".
pub const RESET_CLOCK_HZ: u64 = 64_000_000;

/// The reset value of `AF1` and `AF2` on the timers with a break: `BKINE` set, the `BKIN` pin enabled as a
/// break source, RM0433 rev 9 sections 38.4.26 to 38.4.29, 41.5.19, 41.6.17 and 41.6.19.
pub const BREAK_INPUT_ENABLED: u64 = 0x0000_0001;

const NANOS_PER_SECOND: u128 = 1_000_000_000;
const HALF_WORD: u64 = 0xFFFF;
const WORD: u64 = u32::MAX as u64;
// 38.3.6
const TI_SOURCES: usize = 16;
// 38.4.26
const ETR_SOURCES: usize = 16;
const BREAKS: usize = 2;
const BREAK_SOURCES: usize = 4;
const MAX_LINES: usize = 4;
const REQUESTS: usize = 7;

/// A capture/compare channel, 1 to 6. Channels 5 and 6 of the advanced timers have no pin.
#[derive(
    Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
)]
pub struct Channel(u8);

impl Channel {
    /// Channel `number`, 1 to 6.
    #[must_use]
    pub const fn new(number: u8) -> Option<Self> {
        if number >= 1 && number <= CHANNEL_COUNT {
            Some(Self(number - 1))
        } else {
            None
        }
    }

    /// The channel's number, 1 to 6.
    #[must_use]
    pub const fn number(self) -> u8 {
        self.0 + 1
    }

    const fn index(self) -> usize {
        self.0 as usize
    }

    const fn partner(self) -> Self {
        Self(self.0 ^ 1)
    }

    const fn has_input(self) -> bool {
        self.index() < INPUTS
    }

    const fn ccr(self) -> Slot {
        [
            Slot::Ccr1,
            Slot::Ccr2,
            Slot::Ccr3,
            Slot::Ccr4,
            Slot::Ccr5,
            Slot::Ccr6,
        ][self.index()]
    }

    const fn ccmr(self) -> Slot {
        [Slot::Ccmr1, Slot::Ccmr2, Slot::Ccmr3][self.index() / 2]
    }
}

fn present(count: u8) -> impl Iterator<Item = Channel> {
    (0..count).map(Channel)
}

/// Which of a timer's break inputs a level is driven on.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BreakInput {
    /// `BRK`, which every timer with a break has.
    First,
    /// `BRK2`, the advanced timers' second.
    Second,
}

impl BreakInput {
    const fn index(self) -> usize {
        self as usize
    }
}

/// What a break gathers, section 38.3.16: the `BKIN` pin, either comparator and the digital filter's
/// analog watchdog.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BreakSource {
    /// The `BKIN` pin through the alternate function controller.
    Pin,
    /// The first comparator's output.
    Comparator1,
    /// The second comparator's output.
    Comparator2,
    /// The digital filter's break output.
    Dfsdm,
}

impl BreakSource {
    const fn index(self) -> usize {
        self as usize
    }
}

// 38.4.1
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
enum Direction {
    Up,
    Down,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
enum UpdateSource {
    Overflow,
    Underflow,
    Software,
    Trigger,
}

// 38.3.6
#[derive(Debug, Clone, Copy, Default, serde::Serialize, serde::Deserialize)]
struct Input {
    level: Level,
    filtered: Level,
    pending: Option<(u128, Level)>,
}

#[derive(Debug, Clone, Copy, Default, serde::Serialize, serde::Deserialize)]
struct ChannelState {
    shadow: u64,
    reference: bool,
    pin: Level,
    pin_since: u128,
    cleared_for_updates: u8,
    matched_since_clear: bool,
    armed: bool,
    prescaled_edges: u64,
    last_match: Option<(u128, u64)>,
    selection: u64,
}

/// DMA requests made so far, by source, 38.4.4. The same events pulse the request outputs of
/// [`UPDATE_REQUEST`], [`CHANNEL_REQUESTS`], [`TRIGGER_REQUEST`] and [`COMMUTATION_REQUEST`].
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct DmaRequests {
    /// `UDE`.
    pub update: u64,
    /// `CCxDE`, channels 1 to 4.
    pub channels: [u64; INPUTS],
    /// `TDE`.
    pub trigger: u64,
    /// `COMDE`.
    pub commutation: u64,
}

// 38.3.19
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
struct Controls {
    ccer: u64,
    ccmr: [u64; 2],
}

#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
struct Epoch {
    at: VirtualNanos,
    cycle: u128,
}

impl Default for Epoch {
    fn default() -> Self {
        Self {
            at: VirtualNanos(0),
            cycle: 0,
        }
    }
}

#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
struct Sources {
    ti: [[Level; TI_SOURCES]; INPUTS],
    etr: [Level; ETR_SOURCES],
    breaks: [[Option<Level>; BREAK_SOURCES]; BREAKS],
}

impl Default for Sources {
    fn default() -> Self {
        Self {
            ti: [[Level::Low; TI_SOURCES]; INPUTS],
            etr: [Level::Low; ETR_SOURCES],
            breaks: [[None; BREAK_SOURCES]; BREAKS],
        }
    }
}

// 38.3.3 and 38.3.15 to 38.3.19
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
struct PowerStage {
    repetition: u64,
    controls: Option<Controls>,
    filters: [Input; BREAKS],
    active: [bool; BREAKS],
    lock_frozen: bool,
    kept: [u64; SLOTS],
}

#[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 {
    const fn order(self) -> u8 {
        match self {
            Self::Boundary(_) => 0,
            Self::Match(..) => 1,
        }
    }
}

#[derive(Debug, Clone, Copy)]
enum FilterEvent {
    Input(usize),
    Etr,
    Break(usize),
}

#[derive(Debug, Clone, Copy)]
enum Pending {
    Counter(u128, CounterEvent),
    Filter(FilterEvent),
}

/// A timer of RM0433 rev 9 chapters 38 to 42, by its [`Family`] and [`Kind`].
#[derive(Debug, serde::Serialize, serde::Deserialize)]
#[serde(bound(deserialize = "'de: 'static"))]
pub struct Tim<F: Family> {
    family: PhantomData<F>,
    width: Counter,
    kind: Kind,
    map: Map,
    clock_hz: u64,
    epoch: Epoch,
    external: bool,
    counting: bool,
    paused: u128,
    stopped_at: u128,
    external_cycles: u128,
    psc: u64,
    arr: u64,
    segment_cycle: u128,
    segment_count: u64,
    direction: Direction,
    channels: [ChannelState; CHANNELS],
    group: u64,
    inputs: [Input; INPUTS],
    sources: Sources,
    etr_polarized: Level,
    etr_edges: u64,
    etr: Input,
    trigger: Level,
    trigger_output: Level,
    trigger_pulses: u64,
    trigger2_output: Level,
    trigger2_pulses: u64,
    dma_requests: DmaRequests,
    // The requests standing, until served, and the level last driven on each output.
    requested: [bool; REQUESTS],
    driven: [Level; REQUESTS],
    burst_index: u64,
    interrupts: [Level; MAX_LINES],
    seen: VirtualNanos,
    wakeup: Option<(VirtualNanos, EventId)>,
    power: PowerStage,
    system_breaks: Vec<(&'static str, usize)>,
    // Boxed in an array so serde doesn't borrow the field whole.
    name: [&'static str; 1],
}

impl<F: Family> Tim<F> {
    /// The timer `timer` whose `CK_INT` runs at `clock_hz` until the part's clock tree drives it
    /// otherwise.
    ///
    /// # Panics
    ///
    /// Panics when the timer's kind isn't one the family carries, which is a wrong row in the part's
    /// table.
    #[must_use]
    pub fn with_clock(clock_hz: u64, timer: Timer) -> Self {
        assert!(
            F::KINDS.contains(&timer.kind),
            "{} is a {:?} timer, which {} does not carry",
            timer.instance,
            timer.kind,
            std::any::type_name::<F>(),
        );
        let mut tim = Self::build(clock_hz, timer.counter, timer.kind);
        tim.name = [timer.instance];
        tim
    }

    /// The timer `timer` at the rate it wakes up with, [`RESET_CLOCK_HZ`], until the part's clock tree
    /// drives it otherwise.
    #[must_use]
    pub fn new(timer: Timer) -> Self {
        Self::with_clock(RESET_CLOCK_HZ, timer)
    }

    fn build(clock_hz: u64, width: Counter, kind: Kind) -> Self {
        Self {
            family: PhantomData,
            width,
            kind,
            map: Map::of::<F::Register>(),
            clock_hz,
            epoch: Epoch::default(),
            external: false,
            counting: false,
            paused: 0,
            stopped_at: 0,
            external_cycles: 0,
            psc: 0,
            arr: width.mask(),
            segment_cycle: 0,
            segment_count: 0,
            direction: Direction::Up,
            channels: [ChannelState::default(); CHANNELS],
            group: 0,
            inputs: [Input::default(); INPUTS],
            sources: Sources::default(),
            etr_polarized: Level::Low,
            etr_edges: 0,
            etr: Input::default(),
            trigger: Level::Low,
            trigger_output: Level::Low,
            trigger_pulses: 0,
            trigger2_output: Level::Low,
            trigger2_pulses: 0,
            dma_requests: DmaRequests::default(),
            requested: [false; REQUESTS],
            driven: [Level::Low; REQUESTS],
            burst_index: 0,
            interrupts: [Level::Low; MAX_LINES],
            seen: VirtualNanos(0),
            wakeup: None,
            power: PowerStage {
                repetition: 0,
                controls: None,
                filters: [Input::default(); BREAKS],
                active: [false; BREAKS],
                lock_frozen: false,
                kept: Self::reset_values(kind, width),
            },
            system_breaks: Vec::new(),
            name: [""],
        }
    }

    // What the registers `LOCK` compares a write with hold out of reset.
    fn reset_values(kind: Kind, width: Counter) -> [u64; SLOTS] {
        let mut kept = [0; SLOTS];
        kept[Slot::Arr.index()] = width.mask();
        if kind.breaks() > 0 {
            kept[Slot::Af1.index()] = BREAK_INPUT_ENABLED;
            kept[Slot::Af2.index()] = BREAK_INPUT_ENABLED;
        }
        kept
    }

    /// Takes the pulse on `request` of `instance` as a system break, section 38.3.16, of which a
    /// clock failure the clock security system detects is one. The part wires each source it has, so
    /// this can be called once for each.
    #[must_use]
    pub fn breaks_on(mut self, instance: &'static str, request: usize) -> Self {
        self.system_breaks.push((instance, request));
        self
    }

    fn own(&self, slot: Slot) -> Option<F::Register> {
        self.map
            .row(slot)
            .and_then(<F::Register as Layout>::from_index)
    }

    fn reg(&self, registers: &RegisterFile, slot: Slot) -> u64 {
        self.own(slot).map_or(0, |own| registers.get(own))
    }

    fn put(&self, registers: &mut RegisterFile, slot: Slot, value: u64) {
        if let Some(own) = self.own(slot) {
            registers.set(own, value);
        }
    }

    fn flag(&self, registers: &mut RegisterFile, field: &FieldSpec) {
        let status = self.reg(registers, Slot::Sr);
        self.put(registers, Slot::Sr, field.set(status, 1));
    }

    fn count_register(&self, registers: &RegisterFile, now: VirtualNanos) -> u64 {
        let count = self.count_now(now);
        if cr1::UIFREMAP.is_set(self.reg(registers, Slot::Cr1)) {
            let copy = u64::from(sr::UIF.is_set(self.reg(registers, Slot::Sr)));
            cnt::UIFCPY.set(count, copy)
        } else {
            count
        }
    }

    /// `TRGO`: its level for the level modes of `MMS`, and the pulses sent so far for the pulse
    /// modes, 38.4.2.
    #[must_use]
    pub fn trigger_output(&self) -> (Level, u64) {
        (self.trigger_output, self.trigger_pulses)
    }

    /// `TRGO2`, which the advanced timers send the converters: its level for the level modes of
    /// `MMS2`, and the pulses sent so far for the pulse modes, 38.4.2.
    #[must_use]
    pub fn trigger2_output(&self) -> (Level, u64) {
        (self.trigger2_output, self.trigger2_pulses)
    }

    /// DMA requests made so far.
    #[must_use]
    pub fn dma_requests(&self) -> DmaRequests {
        self.dma_requests
    }
}