rigless 0.3.0

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
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
// Copyright (c) 2026 Rigless Labs.
// SPDX-License-Identifier: MIT OR Apache-2.0
//! The STM32H743: its peripheral models, one module per generated register layout, and the MCU that
//! is made of them.
//!
//! A model is a building block. It's written against one register layout and knows nothing of any
//! part or board. [`Stm32h743`] is the whole microcontroller, and it holds its models as fields:
//! `mcu.flash`, `mcu.gpio[0]`. Reaching one is a field access that can't fail. It runs on its own, with
//! no board: [`Stm32h743::new`] gives a part fresh out of reset, running on its internal
//! oscillator, with nothing on its pins.
//!
//! An instance with no field answers from its register file alone, at the reset values the bus
//! description gives it.

pub mod adc;
pub mod bdma;
pub mod dbg;
pub mod dma;
pub mod dmamux;
pub mod exti;
pub mod flash;
pub mod gpio;
pub mod i2c;
pub mod otg;
pub mod pwr;
pub mod rcc;
pub mod rng;
pub mod sdmmc;
pub mod spi;
pub mod syscfg;
pub mod tim;
pub mod usart;

use crate::cpu::armv7m::Dwt;
use crate::device::{DeviceContext, ErasedDevice, RegisterFile, ResetCause};
use crate::mcu::{Mcu, Operate};
use crate::snapshot::store;
use crate::soc::{Bus, Error};
use crate::time::VirtualNanos;

use super::generated::rtc;

use adc::{Adc, AdcCommon, Block, Converter};
use bdma::Bdma;
use dbg::Dbg;
use dma::{DMA1_FIRST_CHANNEL, DMA2_FIRST_CHANNEL, Dma};
use dmamux::{Dmamux1, Dmamux2};
use exti::Exti;
use flash::Flash;
use gpio::{Gpio, PORTS};
use i2c::I2c;
use otg::Otg;
use pwr::Pwr;
use rcc::Rcc;
use rng::Rng;
use sdmmc::Sdmmc;
use spi::Spi;
use syscfg::Syscfg;
use tim::{
    ADVANCED_TIMERS, Advanced, BASIC_TIMERS, Basic, General, TIMER_15, TIMER_16, TIMER_17, TIMERS,
    Tim, Tim15, Tim16, Tim17,
};
use usart::Usart;

/// What the core runs at until a firmware touches the clock tree: the 64 MHz HSI, undivided, RM0433
/// rev 9 section 8.5.2 and figure 49.
const RESET_CORE_HZ: u64 = 64_000_000;

/// One whole STM32H743: the bus, and a model for every instance this part has one for.
///
/// The fields are the part's inventory. Everything else about it (where each answers, what its
/// registers reset to, the corrections against RM0433) is the bus, built by
/// [`memory_map::builder`](super::memory_map::builder).
#[derive(Debug)]
pub struct Stm32h743 {
    /// Reset and clock control, RM0433 rev 9 chapter 8.
    pub rcc: Rcc,
    /// The power controller, chapter 6.
    pub pwr: Pwr,
    /// The embedded flash memory interface of chapter 4, which programs the flash region.
    pub flash: Flash,
    /// The eleven ports of chapter 11, in the order of [`PORTS`].
    pub gpio: [Gpio; PORTS.len()],
    /// The system configuration controller of chapter 12.
    pub syscfg: Syscfg,
    /// The extended interrupt and event controller of chapter 20.
    pub exti: Exti,
    /// The random number generator of chapter 34.
    pub rng: Rng,
    /// The microcontroller debug unit of chapter 60.
    pub dbg: Dbg,
    /// The general-purpose timers of chapters 39 and 40, in the order of [`TIMERS`].
    pub tim: [Tim<General>; TIMERS.len()],
    /// The advanced-control timers of chapter 38, in the order of [`ADVANCED_TIMERS`].
    pub advanced: [Tim<Advanced>; ADVANCED_TIMERS.len()],
    /// The basic timers of chapter 42, in the order of [`BASIC_TIMERS`].
    pub basic: [Tim<Basic>; BASIC_TIMERS.len()],
    /// TIM15, chapter 41.
    pub tim15: Tim<Tim15>,
    /// TIM16, chapter 41.
    pub tim16: Tim<Tim16>,
    /// TIM17, chapter 41.
    pub tim17: Tim<Tim17>,
    /// The serial ports of chapter 48, in the order of [`usart::INSTANCES`].
    pub usart: [Usart; usart::INSTANCES.len()],
    /// The serial peripheral interfaces of chapter 50, in the order of [`spi::INSTANCES`].
    pub spi: [Spi; spi::INSTANCES.len()],
    /// The inter-integrated circuit interfaces of chapter 47, in the order of [`i2c::INSTANCES`].
    pub i2c: [I2c; i2c::INSTANCES.len()],
    /// The two direct memory access controllers of chapter 15, DMA1 then DMA2.
    pub dma: [Dma; 2],
    /// The request multiplexer of chapter 17 the two controllers take their requests from.
    pub dmamux1: Dmamux1,
    /// The two USB on-the-go cores of chapter 57, in the order of [`otg::INSTANCES`], as far as their
    /// reset registers.
    pub otg: [Otg; otg::INSTANCES.len()],
    /// The two SD and MMC card interfaces of chapter 55, in the order of [`sdmmc::INSTANCES`],
    /// with nothing in either slot.
    pub sdmmc: [Sdmmc; sdmmc::INSTANCES.len()],
    /// The three converters of chapter 25, in the order of [`Converter::ALL`].
    pub adc: [Adc; Converter::ALL.len()],
    /// The converters' two common blocks, in the order of [`Block::ALL`].
    pub adc_common: [AdcCommon; Block::ALL.len()],
    /// The basic controller of chapter 16, which serves the third domain.
    pub bdma: Bdma,
    /// The request multiplexer the basic controller takes its requests from.
    pub dmamux2: Dmamux2,
    /// The cycle counter the processor brings with it, which is Arm's rather than ST's.
    pub dwt: Dwt,
    bus: Bus,
    /// Which field answers for each slot of the bus, resolved once in [`Stm32h743::new`] so that
    /// nothing after it looks a peripheral up by name.
    wiring: Box<[Field]>,
    /// Where each field's register file lives, resolved once for the same reason.
    slots: Slots,
}

/// The slot each model's register file is in.
#[derive(Debug, Clone, Copy)]
struct Slots {
    rcc: usize,
    pwr: usize,
    flash: usize,
    gpio: [usize; PORTS.len()],
    dbg: usize,
    usart: [usize; usart::INSTANCES.len()],
    spi: [usize; spi::INSTANCES.len()],
    i2c: [usize; i2c::INSTANCES.len()],
    tim: [usize; TIMERS.len()],
    advanced: [usize; ADVANCED_TIMERS.len()],
    tim15: usize,
    /// The backup domain's real-time clock, which has no model and keeps its registers through a
    /// system reset.
    rtc: usize,
}

/// Which of the MCU's fields answers for one slot of its bus.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Field {
    /// A slot this part has no model for: its register file answers alone.
    None,
    /// The clock controller, [`Stm32h743::rcc`].
    Rcc,
    /// The power controller, [`Stm32h743::pwr`].
    Pwr,
    /// The flash interface, [`Stm32h743::flash`].
    Flash,
    /// The nth port, indexing [`Stm32h743::gpio`].
    Gpio(usize),
    /// The system configuration controller, [`Stm32h743::syscfg`].
    Syscfg,
    /// The interrupt and event controller, [`Stm32h743::exti`].
    Exti,
    /// The random number generator, [`Stm32h743::rng`].
    Rng,
    /// The debug unit, [`Stm32h743::dbg`].
    Dbg,
    /// The nth general-purpose timer, indexing [`Stm32h743::tim`].
    Tim(usize),
    /// The nth advanced-control timer, indexing [`Stm32h743::advanced`].
    Advanced(usize),
    /// The nth basic timer, indexing [`Stm32h743::basic`].
    Basic(usize),
    /// [`Stm32h743::tim15`].
    Tim15,
    /// [`Stm32h743::tim16`].
    Tim16,
    /// [`Stm32h743::tim17`].
    Tim17,
    /// The nth serial port, indexing [`Stm32h743::usart`].
    Usart(usize),
    /// The nth serial peripheral interface, indexing [`Stm32h743::spi`].
    Spi(usize),
    /// The nth inter-integrated circuit interface, indexing [`Stm32h743::i2c`].
    I2c(usize),
    /// The nth controller, indexing [`Stm32h743::dma`].
    Dma(usize),
    /// [`Stm32h743::dmamux1`].
    Dmamux1,
    /// The nth USB core, indexing [`Stm32h743::otg`].
    Otg(usize),
    /// The nth card interface, indexing [`Stm32h743::sdmmc`].
    Sdmmc(usize),
    /// The nth converter, indexing [`Stm32h743::adc`].
    Adc(usize),
    /// The nth common block, indexing [`Stm32h743::adc_common`].
    AdcCommon(usize),
    /// [`Stm32h743::bdma`].
    Bdma,
    /// [`Stm32h743::dmamux2`].
    Dmamux2,
    /// The processor's cycle counter, [`Stm32h743::dwt`].
    Dwt,
}

impl Field {
    /// Every field this part has, which is what [`Stm32h743::new`] walks. [`Field::instance`] is an
    /// exhaustive match, so a field with no instance to answer for doesn't compile.
    fn all() -> Vec<Self> {
        let mut all = vec![Self::Rcc, Self::Pwr, Self::Flash];
        all.extend((0..PORTS.len()).map(Self::Gpio));
        all.extend([Self::Syscfg, Self::Exti, Self::Rng, Self::Dbg]);
        all.extend((0..TIMERS.len()).map(Self::Tim));
        all.extend((0..ADVANCED_TIMERS.len()).map(Self::Advanced));
        all.extend((0..BASIC_TIMERS.len()).map(Self::Basic));
        all.extend([Self::Tim15, Self::Tim16, Self::Tim17]);
        all.extend((0..usart::INSTANCES.len()).map(Self::Usart));
        all.extend((0..spi::INSTANCES.len()).map(Self::Spi));
        all.extend((0..i2c::INSTANCES.len()).map(Self::I2c));
        all.extend([
            Self::Dma(0),
            Self::Dma(1),
            Self::Dmamux1,
            Self::Bdma,
            Self::Dmamux2,
        ]);
        all.extend((0..otg::INSTANCES.len()).map(Self::Otg));
        all.extend((0..sdmmc::INSTANCES.len()).map(Self::Sdmmc));
        all.extend((0..Converter::ALL.len()).map(Self::Adc));
        all.extend((0..Block::ALL.len()).map(Self::AdcCommon));
        all.push(Self::Dwt);
        all
    }

    /// The instance this field answers for.
    fn instance(self) -> &'static str {
        match self {
            Self::None => unreachable!("the unmodeled slot answers for no instance"),
            Self::Rcc => super::generated::RCC.name,
            Self::Pwr => super::generated::PWR.name,
            Self::Flash => super::generated::FLASH.name,
            Self::Gpio(nth) => PORTS[nth],
            Self::Syscfg => super::generated::SYSCFG.name,
            Self::Exti => super::generated::EXTI.name,
            Self::Rng => super::generated::RNG.name,
            Self::Dbg => super::generated::DBGMCU.name,
            Self::Tim(nth) => TIMERS[nth].instance,
            Self::Advanced(nth) => ADVANCED_TIMERS[nth].instance,
            Self::Basic(nth) => BASIC_TIMERS[nth].instance,
            Self::Tim15 => TIMER_15.instance,
            Self::Tim16 => TIMER_16.instance,
            Self::Tim17 => TIMER_17.instance,
            Self::Usart(nth) => usart::INSTANCES[nth],
            Self::Spi(nth) => spi::INSTANCES[nth].name,
            Self::I2c(nth) => i2c::INSTANCES[nth].name,
            Self::Dma(nth) => [super::generated::DMA1.name, super::generated::DMA2.name][nth],
            Self::Dmamux1 => super::generated::DMAMUX1.name,
            Self::Otg(nth) => otg::INSTANCES[nth],
            Self::Sdmmc(nth) => sdmmc::INSTANCES[nth],
            Self::Adc(nth) => Converter::ALL[nth].instance(),
            Self::AdcCommon(nth) => Block::ALL[nth].instance(),
            Self::Bdma => super::generated::BDMA.name,
            Self::Dmamux2 => super::generated::DMAMUX2.name,
            Self::Dwt => crate::cpu::armv7m::DWT,
        }
    }
}

store!(Stm32h743 {
    values: [
        rcc, pwr, otg, sdmmc, adc, adc_common, flash, gpio, syscfg, exti, rng, dbg, tim, advanced,
        basic, tim15, tim16, tim17, dwt,
    ],
    parts: [usart, spi, i2c, dma, dmamux1, bdma, dmamux2, bus],
    built: [wiring, slots],
});

impl Stm32h743 {
    /// The part out of reset: every model fresh, running on the internal oscillator, nothing on its
    /// pins.
    ///
    /// Fails only if the part's own description is wrong: a field whose instance the tables don't
    /// carry, or a field wired to a slot whose layout it wasn't written for.
    pub fn new() -> Result<Self, Error> {
        let bus = super::memory_map::builder().build()?;
        let mut wiring = vec![Field::None; bus.slots().len()].into_boxed_slice();
        let mut slots = Slots {
            rcc: 0,
            pwr: 0,
            flash: 0,
            gpio: [0; PORTS.len()],
            dbg: 0,
            usart: [0; usart::INSTANCES.len()],
            spi: [0; spi::INSTANCES.len()],
            i2c: [0; i2c::INSTANCES.len()],
            tim: [0; TIMERS.len()],
            advanced: [0; ADVANCED_TIMERS.len()],
            tim15: 0,
            rtc: bus.slot("RTC")?,
        };
        for field in Field::all() {
            let instance = field.instance();
            let slot = bus.slot(instance)?;
            wiring[slot] = field;
            match field {
                Field::Rcc => slots.rcc = slot,
                Field::Pwr => slots.pwr = slot,
                Field::Flash => slots.flash = slot,
                Field::Gpio(nth) => slots.gpio[nth] = slot,
                Field::Dbg => slots.dbg = slot,
                Field::Usart(nth) => slots.usart[nth] = slot,
                Field::Spi(nth) => slots.spi[nth] = slot,
                Field::I2c(nth) => slots.i2c[nth] = slot,
                Field::Tim(nth) => slots.tim[nth] = slot,
                Field::Advanced(nth) => slots.advanced[nth] = slot,
                Field::Tim15 => slots.tim15 = slot,
                Field::None => unreachable!("Field::all names no unmodeled slot"),
                _ => {}
            }
        }
        let mut mcu = Self {
            rcc: Rcc::new(),
            pwr: Pwr::new(),
            flash: Flash::new(),
            gpio: std::array::from_fn(|_| Gpio::default()),
            syscfg: Syscfg,
            exti: Exti::new(super::generated::SYSCFG.base),
            rng: Rng::new(rng::RESET_AHB_HZ, rng::KERNEL_HZ),
            dbg: Dbg,
            tim: std::array::from_fn(|nth| Tim::new(TIMERS[nth])),
            advanced: std::array::from_fn(|nth| Tim::new(ADVANCED_TIMERS[nth])),
            basic: std::array::from_fn(|nth| Tim::new(BASIC_TIMERS[nth])),
            tim15: Tim::new(TIMER_15),
            tim16: Tim::new(TIMER_16),
            tim17: Tim::new(TIMER_17),
            usart: std::array::from_fn(|_| Usart::new()),
            spi: std::array::from_fn(|nth| Spi::new(spi::INSTANCES[nth].kind)),
            i2c: std::array::from_fn(|_| I2c::default()),
            dma: [Dma::new(DMA1_FIRST_CHANNEL), Dma::new(DMA2_FIRST_CHANNEL)],
            dmamux1: Dmamux1::new(),
            otg: [Otg; otg::INSTANCES.len()],
            sdmmc: [Sdmmc::default(); sdmmc::INSTANCES.len()],
            // Out of reset `ADCSEL` selects `pll2_p_ck` and PLL2 is off, so the kernel clock stands
            // still, RM0433 rev 9 section 8.7.22.
            adc: Converter::ALL.map(|converter| Adc::new(converter, 0)),
            adc_common: Block::ALL.map(|block| AdcCommon::new(block, 0)),
            bdma: Bdma::new(),
            dmamux2: Dmamux2::new(),
            dwt: Dwt::with_clock(RESET_CORE_HZ),
            bus,
            wiring,
            slots,
        };
        mcu.check()?;
        Ok(mcu)
    }

    /// The clock tree and the registers it keeps its state in.
    pub fn clocks(&mut self) -> (&mut Rcc, &mut RegisterFile) {
        with_registers(&mut self.rcc, &mut self.bus, self.slots.rcc)
    }

    /// The power controller and the registers it keeps its state in.
    pub fn power(&mut self) -> (&mut Pwr, &mut RegisterFile) {
        with_registers(&mut self.pwr, &mut self.bus, self.slots.pwr)
    }

    /// Sets the rail the board runs the part from, in millivolts.
    pub fn set_supply(&mut self, millivolts: u32) {
        let (power, registers) = self.power();
        power.set_supply(millivolts, registers);
    }

    /// Sets the analog rail, `VDDA`, the board gives the part, in millivolts.
    pub fn set_analog_supply(&mut self, millivolts: u32) {
        let (power, registers) = self.power();
        power.set_analog_supply(millivolts, registers);
    }

    /// The flash interface and the registers it keeps its state in.
    pub fn program(&mut self) -> (&mut Flash, &mut RegisterFile) {
        with_registers(&mut self.flash, &mut self.bus, self.slots.flash)
    }

    /// The `nth` general-purpose timer of [`TIMERS`] and its registers, to read what its pins
    /// carry.
    pub fn timer(&mut self, nth: usize) -> (&mut Tim<General>, &mut RegisterFile) {
        with_registers(&mut self.tim[nth], &mut self.bus, self.slots.tim[nth])
    }

    /// The `nth` advanced-control timer of [`ADVANCED_TIMERS`] and its registers.
    pub fn advanced_timer(&mut self, nth: usize) -> (&mut Tim<Advanced>, &mut RegisterFile) {
        with_registers(
            &mut self.advanced[nth],
            &mut self.bus,
            self.slots.advanced[nth],
        )
    }

    /// TIM15 and its registers.
    pub fn timer_15(&mut self) -> (&mut Tim<Tim15>, &mut RegisterFile) {
        with_registers(&mut self.tim15, &mut self.bus, self.slots.tim15)
    }

    /// Drives an input of the `nth` port at `at`, the way a board drives its pins.
    pub fn drive_port<T>(
        &mut self,
        at: VirtualNanos,
        nth: usize,
        input: impl FnOnce(&mut Gpio, &mut RegisterFile, &mut DeviceContext<'_>) -> T,
    ) -> T {
        driving(
            &mut self.gpio[nth],
            &mut self.bus,
            self.slots.gpio[nth],
            at,
            input,
        )
    }
}

/// What the real-time clock holds of `register` after a system reset, or `None` where the reset
/// gives it back its reset value, RM0433 rev 9 section 46.3.10. The calendar's shadow registers
/// reset unless `BYPSHAD` bypasses them, sections 46.6.1, 46.6.2 and 46.6.10. `RTC_ISR` keeps all but
/// `INIT`, `INITF` and `RSF`, section 46.6.4. The write protection key, section 46.6.9, starts over.
/// Every other register is the backup domain's, "not affected by a system reset".
fn kept_by_the_clock(register: rtc::Register, clock: &RegisterFile) -> Option<u64> {
    let value = clock.get(register);
    match register {
        rtc::Register::RtcTr | rtc::Register::RtcDr | rtc::Register::RtcSsr => rtc::rtc_cr::BYPSHAD
            .is_set(clock.get(rtc::Register::RtcCr))
            .then_some(value),
        rtc::Register::RtcIsr => Some(
            value
                & !(rtc::rtc_isr::INIT.mask()
                    | rtc::rtc_isr::INITF.mask()
                    | rtc::rtc_isr::RSF.mask()),
        ),
        rtc::Register::RtcWpr => None,
        _ => Some(value),
    }
}

/// A model and the register file it keeps half its state in, paired by the slot resolved at
/// construction.
fn with_registers<'a, M>(
    model: &'a mut M,
    bus: &'a mut Bus,
    slot: usize,
) -> (&'a mut M, &'a mut RegisterFile) {
    (model, bus.slot_mut(slot).registers_mut())
}

/// Drives an input of one model at `at`, an input the bus doesn't carry: it gets its registers and a
/// context as on a bus access, so it may schedule and raise.
fn driving<M, T>(
    model: &mut M,
    bus: &mut Bus,
    slot: usize,
    at: VirtualNanos,
    input: impl FnOnce(&mut M, &mut RegisterFile, &mut DeviceContext<'_>) -> T,
) -> T {
    let (registers, mut context) = bus.drive(at, slot);
    input(model, registers, &mut context)
}

impl Stm32h743 {
    /// Tells every chip a serial port carried through a reset at `at` that its part restarted, since
    /// the events it scheduled went with the old bus.
    fn chips_restarted(&mut self, at: VirtualNanos) {
        let Self {
            usart,
            spi,
            i2c,
            bus,
            slots,
            ..
        } = self;
        for (usart, &slot) in usart.iter_mut().zip(&slots.usart) {
            driving(usart, bus, slot, at, |usart, _, context| {
                usart.chips_restarted(context);
            });
        }
        for (spi, &slot) in spi.iter_mut().zip(&slots.spi) {
            driving(spi, bus, slot, at, |spi, _, context| {
                spi.chips_restarted(context);
            });
        }
        for (i2c, &slot) in i2c.iter_mut().zip(&slots.i2c) {
            driving(i2c, bus, slot, at, |i2c, _, context| {
                i2c.chips_restarted(context);
            });
        }
    }
}

impl Mcu for Stm32h743 {
    fn bus(&self) -> &Bus {
        &self.bus
    }

    /// A system reset, RM0433 rev 9 section 8.4.2: a fresh part with what the board put on its pins
    /// still there. The flash interface keeps the option bytes the last completed commit left,
    /// section 4.3.14, the debug unit its registers, section 60.5.8, and the real-time clock what
    /// section 46.3.10 says a system reset leaves it. Regions and windows a board added to the bus
    /// stay.
    fn reset(&mut self, at: VirtualNanos, cause: ResetCause) {
        let mut fresh = Self::new().expect("a part that assembled once assembles again");
        // The clock controller and the power controller keep what a system reset leaves them, the
        // backup domain's control and the supply configuration among it, and the crystals and rail
        // the board fitted.
        std::mem::swap(&mut fresh.rcc, &mut self.rcc);
        crate::device::Device::reset(&mut fresh.rcc);
        std::mem::swap(&mut fresh.pwr, &mut self.pwr);
        crate::device::Device::reset(&mut fresh.pwr);
        let flags = self.bus.slots()[self.slots.rcc]
            .registers()
            .get(super::generated::rcc::Register::Rsr);
        let (clocks, registers) = fresh.clocks();
        clocks.restarted(cause, flags, registers);
        for (fresh, old) in fresh.spi.iter_mut().zip(&mut self.spi) {
            fresh.inherit(old);
        }
        for (fresh, old) in fresh.i2c.iter_mut().zip(&mut self.i2c) {
            fresh.inherit(old);
        }
        let options = self.flash.committed_options();
        let (flash, registers) = fresh.program();
        flash.keep_options(options, registers);
        for (fresh, old) in fresh.usart.iter_mut().zip(&mut self.usart) {
            fresh.inherit(old);
        }
        for (fresh, old) in fresh.adc.iter_mut().zip(&mut self.adc) {
            std::mem::swap(fresh, old);
            crate::device::Device::reset(fresh);
        }
        for (fresh, old) in fresh.gpio.iter_mut().zip(&mut self.gpio) {
            *fresh = std::mem::take(old);
            crate::device::Device::reset(fresh);
        }
        for register in dbg::KEPT_BY_A_SYSTEM_RESET {
            let value = self.bus.slots()[self.slots.dbg].registers().get(register);
            fresh
                .bus
                .slot_mut(fresh.slots.dbg)
                .registers_mut()
                .set(register, value);
        }
        let clock = self.bus.slots()[self.slots.rtc].registers();
        let fresh_clock = fresh.bus.slot_mut(fresh.slots.rtc).registers_mut();
        for register in rtc::Register::ALL {
            if let Some(value) = kept_by_the_clock(register, clock) {
                fresh_clock.set(register, value);
            }
        }
        fresh
            .bus
            .keep_added(&self.bus)
            .expect("a part rebuilt from its own description");
        *self = fresh;
        self.chips_restarted(at);
    }

    fn poll(&mut self, now: VirtualNanos) {
        self.poll_every_model(now);
    }

    fn split(&mut self, slot: usize) -> (&mut Bus, Option<&mut dyn ErasedDevice>) {
        let Self {
            rcc,
            pwr,
            flash,
            gpio,
            syscfg,
            exti,
            rng,
            dbg,
            tim,
            advanced,
            basic,
            tim15,
            tim16,
            tim17,
            usart,
            spi,
            i2c,
            dma,
            dmamux1,
            otg,
            sdmmc,
            adc,
            adc_common,
            bdma,
            dmamux2,
            dwt,
            bus,
            wiring,
            ..
        } = self;
        // A window a board added has a slot past the part's own, and the board's model for it.
        let field = wiring.get(slot).copied().unwrap_or(Field::None);
        let model: Option<&mut dyn ErasedDevice> = match field {
            Field::None => None,
            Field::Rcc => Some(rcc),
            Field::Pwr => Some(pwr),
            Field::Flash => Some(flash),
            Field::Gpio(nth) => Some(&mut gpio[nth]),
            Field::Syscfg => Some(syscfg),
            Field::Exti => Some(exti),
            Field::Rng => Some(rng),
            Field::Dbg => Some(dbg),
            Field::Tim(nth) => Some(&mut tim[nth]),
            Field::Advanced(nth) => Some(&mut advanced[nth]),
            Field::Basic(nth) => Some(&mut basic[nth]),
            Field::Tim15 => Some(tim15),
            Field::Tim16 => Some(tim16),
            Field::Tim17 => Some(tim17),
            Field::Usart(nth) => Some(&mut usart[nth]),
            Field::Spi(nth) => Some(&mut spi[nth]),
            Field::I2c(nth) => Some(&mut i2c[nth]),
            Field::Dma(nth) => Some(&mut dma[nth]),
            Field::Dmamux1 => Some(dmamux1),
            Field::Otg(nth) => Some(&mut otg[nth]),
            Field::Sdmmc(nth) => Some(&mut sdmmc[nth]),
            Field::Adc(nth) => Some(&mut adc[nth]),
            Field::AdcCommon(nth) => Some(&mut adc_common[nth]),
            Field::Bdma => Some(bdma),
            Field::Dmamux2 => Some(dmamux2),
            Field::Dwt => Some(dwt),
        };
        (bus, model)
    }

    fn bus_mut(&mut self) -> &mut Bus {
        &mut self.bus
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    /// Every field answers for the one instance it's named for, and every other slot answers from its
    /// register file alone.
    #[test]
    fn every_field_answers_for_the_one_instance_it_is_named_for() {
        let mut mcu = Stm32h743::new().unwrap();
        for field in Field::all() {
            let slot = mcu.bus.slot_of(field.instance()).unwrap();
            assert_eq!(mcu.wiring[slot], field);
            assert!(mcu.split(slot).1.is_some(), "{}", field.instance());
        }
        let modeled = mcu.wiring.iter().filter(|&&field| field != Field::None);
        assert_eq!(modeled.count(), Field::all().len());
    }

    /// A store into the flash reaches the interface, which takes it, and a reset leaves the part
    /// fresh with the same bus.
    #[test]
    fn the_part_hands_flash_stores_to_its_interface_and_comes_back_from_a_reset() {
        let mut mcu = Stm32h743::new().unwrap();
        let at = VirtualNanos(0);
        assert_eq!(
            mcu.write(at, 0x0800_0000, crate::soc::Width::W32, 0),
            Ok(())
        );
        let slots = mcu.bus.slots().len();
        mcu.reset(at, ResetCause::Pin);
        assert_eq!(mcu.bus.slots().len(), slots);
    }
}