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
// Copyright (c) 2026 Rigless Labs.
// SPDX-License-Identifier: MIT OR Apache-2.0
//! The memory map of the STM32H743 and every correction of the vendor's file against RM0433 rev 9.

use std::num::{NonZeroU32, NonZeroU64};

use crate::soc::{
    Builder, Bus, Clock, Cpu, EraseBlocks, Gate, Layout, Peripheral, Region, WriteModify,
};

use super::generated;
use super::peripherals;

/// The memory map from RM0433 rev 9: the TCMs and the system SRAM of section 2.4, and the flash of
/// section 4.3.4's Table 15, two banks of eight sectors with a system memory each.
///
/// Erased flash reads all ones. Section 4.3.9 programs a 256-bit flash word into erased cells, and
/// only an erase sets a bit back. The firmware cannot write the flash. Its stores go to the flash
/// interface, `Flash`, which programs the array.
///
/// The first system memory holds the unique device ID at `0x1FF1_E800`, section 61.1, and the
/// flash size word ChibiOS reads beside it.
pub static MEMORY: [Region; 11] = [
    Region::ram("itcm", 0x0000_0000, 64 << 10),
    Region::flash("flash", 0x0800_0000, 2 << 20)
        .erased_in(&SECTORS)
        .erased_to(0xFF)
        .programmed_by("Flash"),
    Region::system("system1", 0x1FF0_0000, 128 << 10),
    Region::system("system2", 0x1FF4_0000, 128 << 10),
    Region::ram("dtcm", 0x2000_0000, 128 << 10),
    Region::ram("axi_sram", 0x2400_0000, 512 << 10),
    Region::ram("sram1", 0x3000_0000, 128 << 10),
    Region::ram("sram2", 0x3002_0000, 128 << 10),
    Region::ram("sram3", 0x3004_0000, 32 << 10),
    Region::ram("sram4", 0x3800_0000, 64 << 10),
    Region::ram("backup_sram", 0x3880_0000, 4 << 10),
];

/// The flash's sectors in address order, RM0433 rev 9 Table 15: eight of 128 Kbytes in each bank.
pub(crate) static SECTORS: [EraseBlocks; 1] = [EraseBlocks {
    size: NonZeroU64::new(128 << 10).expect("a sector has bytes"),
    count: NonZeroU32::new(16).expect("two banks of eight"),
}];

/// The processor as ST's file describes it. Its 150 interrupt lines are section 20.1's "up to 150
/// maskable interrupt channels".
pub static CPU: Cpu = generated::CPU;

/// The three registers a port resets differently, in the order of [`PORT_RESETS`]'s values: RM0433
/// rev 9 sections 11.4.1, 11.4.3 and 11.4.4. Every other register of section 11.4 resets alike on
/// every port.
const CONFIGURED: [&str; 3] = ["MODER", "OSPEEDR", "PUPDR"];

/// What each port resets [`CONFIGURED`] to, sections 11.4.1, 11.4.3 and 11.4.4, read from the page.
///
/// Port A wakes up with the debug pins in their alternate function, at very high speed with their
/// pulls, and port B with two more of them. Every other port wakes up with every pin analog, the
/// mode the part draws least current in, and with no speed or pull.
///
/// **ST's file gives every port port A's three values, and port A's `PUPDR` as `0x1210_0000`**,
/// where section 11.4.4 gives `0x6400_0000`. The ten other ports derive from port A in the file, so
/// they take its reset values with its layout.
///
/// Written for all eleven, so that a port is never right by inheriting a value belonging to port A.
/// `every_port_comes_out_of_reset_as_the_manual_gives_it` reads all thirty-three back.
const PORT_RESETS: [(&str, [u64; CONFIGURED.len()]); 11] = [
    ("GPIOA", [0xABFF_FFFF, 0x0C00_0000, 0x6400_0000]),
    ("GPIOB", [0xFFFF_FEBF, 0x0000_00C0, 0x0000_0100]),
    ("GPIOC", [0xFFFF_FFFF, 0, 0]),
    ("GPIOD", [0xFFFF_FFFF, 0, 0]),
    ("GPIOE", [0xFFFF_FFFF, 0, 0]),
    ("GPIOF", [0xFFFF_FFFF, 0, 0]),
    ("GPIOG", [0xFFFF_FFFF, 0, 0]),
    ("GPIOH", [0xFFFF_FFFF, 0, 0]),
    ("GPIOI", [0xFFFF_FFFF, 0, 0]),
    ("GPIOJ", [0xFFFF_FFFF, 0, 0]),
    ("GPIOK", [0xFFFF_FFFF, 0, 0]),
];

/// The clock controller's registers whose reset value ST's file gets wrong, with the value RM0433 rev
/// 9 gives, each under the name of its copy for the processor too, `C1_`, which section 8.7 gives
/// the same value. ST's file gives every one of them zero.
///
/// Section 8.7.39's Table 65 gives `RCC_RSR` the power-on reset's flags. Section 8.7.48's Table 74
/// gives `RCC_APB4ENR` bit 16 set, `RTCAPBEN`, the RTC's register interface "enabled (default after
/// reset)". Sections 8.7.49 to 8.7.57, Tables 75 to 83, give every low-power enable its own set bits,
/// the peripherals the part keeps clocked through `CSleep` unless told otherwise.
const CLOCK_RESETS: [(&str, &str, u64); 11] = [
    ("RSR", "C1_RSR", 0x00FE_0000),
    ("APB4ENR", "C1_APB4ENR", 0x0001_0000),
    ("AHB3LPENR", "C1_AHB3LPENR", 0xF001_5131),
    ("AHB1LPENR", "C1_AHB1LPENR", 0x1E03_C023),
    ("AHB2LPENR", "C1_AHB2LPENR", 0xE000_0271),
    ("AHB4LPENR", "C1_AHB4LPENR", 0x3128_07FF),
    ("APB3LPENR", "C1_APB3LPENR", 0x0000_0058),
    ("APB1LLPENR", "C1_APB1LLPENR", 0xE8FF_CBFF),
    ("APB1HLPENR", "C1_APB1HLPENR", 0x0000_0136),
    ("APB2LPENR", "C1_APB2LPENR", 0x31D7_3033),
    ("APB4LPENR", "C1_APB4LPENR", 0x0421_DEAA),
];

/// Every other register whose reset value ST's file gets wrong, with the value RM0433 rev 9 gives.
///
/// | Register | Value | Source | ST's file |
/// |---|---|---|---|
/// | `RCC_CR` | `0x0000 0001`, HSI on and nothing else | 8.7.2 | `0x83`, which sets `HSIKERON` and `CSION` too, where each bit's description says off after reset |
/// | `PWR_CR3` | `0x0000 0046`, `SCUEN` set, the supply configuration update the controller waits on before its first voltage scaling | 6.8.4, revision V | `0x0000 0006`, which the section gives revision Y. This part is revision V, as `DBGMCU_IDC` says |
/// | `FLASH_CR1` | `0x0000 0031`, bank 1 locked and programmed a double word at a time | 4.9.4 | Zero, a bank unlocked out of reset. It gives `FLASH_CR2` 4.9.25's `0x0000 0031` |
/// | `FLASH_OPTCR` at both mappings | `0x0000 0001`, the option bytes locked | 4.9.7 | Zero. The section's `X` is bit 31, `SWAP_BANK`, loaded from the option bytes, which don't swap the banks out of the factory |
/// | `FLASH_CRCCR1`, `FLASH_CRCCR2` | `0x001C 0000` | 4.9.19, 4.9.34 | Zero |
/// | `DBGMCU_IDC` | `0x2003 6450`, revision V | 60.5.8 and its `REV_ID` table | `0x1000_6450`, a revision the table doesn't name. Revision V runs at 480 MHz, ES0392 rev 15 Table 2, and it's the one current flight controllers carry |
/// | `OTG_GRSTCTL` of both USB cores | `0x8000 0000`, `AHBIDL` set, the master idle | 57.14.5 | `0x2000_0000`, which sets reserved bit 29 and leaves `AHBIDL` clear |
const RESETS: [(&str, &str, u64); 10] = [
    ("RCC", "CR", 0x0000_0001),
    ("PWR", "CR3", 0x0000_0046),
    ("Flash", "CR1", 0x0000_0031),
    ("Flash", "OPTCR", 0x0000_0001),
    ("Flash", "OPTCR_", 0x0000_0001),
    ("Flash", "CRCCR1", 0x001C_0000),
    ("Flash", "CRCCR2", 0x001C_0000),
    ("DBGMCU", "IDC", 0x2003_6450),
    (
        peripherals::otg::INSTANCES[0],
        "OTG_HS_GRSTCTL",
        peripherals::otg::GRSTCTL_RESET,
    ),
    (
        peripherals::otg::INSTANCES[1],
        "OTG_HS_GRSTCTL",
        peripherals::otg::GRSTCTL_RESET,
    ),
];

/// Every instance whose interrupt lines ST's file gets wrong, with the lines RM0433 rev 9 section
/// 19.1.2's Table 147 gives it, read from the page.
///
/// | Instances | Table 147 | ST's file |
/// |---|---|---|
/// | TIM12, TIM13, TIM14 | 43, 44, 45, which each shares with TIM8's break, update and trigger | 28 to all three, which is TIM2's |
/// | ADC2 | 18, `ADC1_2`, shared with ADC1 | 127, which is ADC3's |
/// | DMA1, DMA2 | DMA1's streams 0 to 6 on 11 to 17 and stream 7 on 47, and DMA2's on 56 to 60 and 68 to 70 | DMA1's streams 2 to 6 to DMA2 |
/// | EXTI | 1, 6 to 10, 23 and 40 | Those, and 81, `cpu_fpu_it`, which is the processor's own |
/// | HRTIM's master, five timers and common block | 103 to 109 in that order, the fault on 109 | Each one place on: the master nothing, timer A 103 and 109, and the common block 108 |
/// | The two USB controllers' host blocks, `OTG1_HS_HOST` and `OTG2_HS_HOST` | None of their own: 101 is `usb2_gbl_it`, the second controller's global line, which [`OTG2_HS_GLOBAL`](generated::OTG2_HS_GLOBAL) holds with its other three | 101 to both |
/// | SDMMC1, SDMMC2 | 49 and 124, one each | Both to each, since SDMMC2 derives from SDMMC1 |
/// | The three delay blocks | None: section 25 gives the block no interrupt | 149 to each, which is `WKUP`, the wakeup pins' |
/// | PWR | 149, `WKUP`, the wakeup pins its `PWR_WKUPCR` arms | Nothing |
/// | DAC | 54, `TIM6_DAC`, its underrun shared with TIM6 | Nothing |
/// | RAMECC1, RAMECC2, RAMECC3 | 145, the three domains' ECC diagnostics on one line | Nothing |
const INTERRUPTS: [(&str, &[u16]); 27] = [
    ("TIM12", &[43]),
    ("TIM13", &[44]),
    ("TIM14", &[45]),
    ("ADC2", &[18]),
    ("DMA1", &[11, 12, 13, 14, 15, 16, 17, 47]),
    ("DMA2", &[56, 57, 58, 59, 60, 68, 69, 70]),
    ("EXTI", &[1, 6, 7, 8, 9, 10, 23, 40]),
    ("HRTIM_Master", &[103]),
    ("HRTIM_TIMA", &[104]),
    ("HRTIM_TIMB", &[105]),
    ("HRTIM_TIMC", &[106]),
    ("HRTIM_TIMD", &[107]),
    ("HRTIM_TIME", &[108]),
    ("HRTIM_Common", &[109]),
    ("OTG1_HS_HOST", &[]),
    ("OTG2_HS_HOST", &[]),
    ("OTG2_HS_GLOBAL", &[98, 99, 100, 101]),
    ("SDMMC1", &[49]),
    ("SDMMC2", &[124]),
    ("DELAY_Block_SDMMC1", &[]),
    ("DELAY_Block_SDMMC2", &[]),
    ("DELAY_Block_QUADSPI", &[]),
    ("PWR", &[149]),
    ("DAC", &[54]),
    ("RAMECC1", &[145]),
    ("RAMECC2", &[145]),
    ("RAMECC3", &[145]),
];

/// Every instance ST's file describes, with the lines of [`INTERRUPTS`], and the processor's own
/// blocks after them.
///
/// ST's file describes what ST built around the core and stops at the core's boundary, so the data
/// watchpoint unit is in neither the file nor the reference manual. It's the part's all the same,
/// since this part carries a Cortex-M7 and that core carries the unit, so it's placed here from
/// `cpu::armv7m`.
fn instances() -> impl Iterator<Item = Peripheral> {
    generated::PERIPHERALS
        .iter()
        .map(|&peripheral| {
            INTERRUPTS
                .iter()
                .find(|(name, _)| *name == peripheral.name)
                .map_or(peripheral, |&(_, interrupts)| Peripheral {
                    interrupts,
                    ..peripheral
                })
        })
        .chain(std::iter::once(crate::cpu::armv7m::PERIPHERAL))
}

/// The flash interface's other corrections of ST's file against RM0433 rev 9 chapter 4, besides the
/// reset values in [`RESETS`]. The model in [`peripherals::flash`] holds the fields and the lists.
///
/// | Registers | RM0433 rev 9 | ST's file |
/// |---|---|---|
/// | The twenty option registers: `FLASH_OPTSR_CUR` and `FLASH_OPTSR_PRG`, `FLASH_BOOT_CURR` and `FLASH_BOOT_PRGR` under both their addresses, and each bank's current and programming `FLASH_PRAR`, `FLASH_SCAR` and `FLASH_WPSN` | The default factory values of Table 21, in [`peripherals::flash::OptionBytes::FACTORY`]. Section 4.9.38's Table 28 gives every one a reset value of `X`, the value loaded from the option bytes | Zero |
/// | `LOCK1`, `LOCK2` and `OPTLOCK`, which `FLASH_OPTCR` has under both its addresses | Type `rs`, written one to set, cleared by the unlock sequence: 4.9.4, 4.9.25, 4.9.7 | Plain read-write |
/// | `SWAP_BANK`, under both addresses of `FLASH_OPTCR` | Type `r`, loaded from the option bytes at reset: 4.9.7 | Plain read-write |
/// | `FLASH_SR1`, `FLASH_SR2`, `FLASH_OPTSR_CUR`, `FLASH_SCAR_CUR1`, `FLASH_SCAR_CUR2` and `FLASH_CRCDATAR`, which have two addresses where the manual gives them | Every bit type `r`: 4.9.5, 4.9.26, 4.9.8, 4.9.13, 4.9.30, 4.9.22 | Read-write, so a write would land |
///
/// It types some registers the wrong direction in a way no correction here reaches, since a correction
/// changes a field and not a register's access, and the model reads the write anyway. `FLASH_KEYR1`,
/// `FLASH_OPTKEYR` at both its addresses and the two `FLASH_CCRx` it types read-write, where
/// sections 4.9.2, 4.9.3, 4.9.6 and 4.9.27 type them write-only, so the model clears each after a
/// write and it reads zero. `FLASH_KEYR2` it types read-only, where 4.9.24 types it write-only, and
/// `FLASH_BOOT_PRGR` read-only at both its addresses, where 4.9.18 types it read-write. The register
/// file drops a write to either, and the model takes the write it is shown.
fn flash_corrections(mut builder: Builder) -> Builder {
    for register in peripherals::flash::OPTION_REGISTERS {
        let value = peripherals::flash::OptionBytes::FACTORY.word(register);
        builder = builder.reset("Flash", register.spec().name, value);
    }
    builder = builder
        .field("Flash", "CR1", "LOCK1", peripherals::flash::LOCK1)
        .field("Flash", "CR2", "LOCK2", peripherals::flash::LOCK2);
    for register in ["OPTCR", "OPTCR_"] {
        builder = builder
            .field("Flash", register, "OPTLOCK", peripherals::flash::OPTLOCK)
            .field(
                "Flash",
                register,
                "SWAP_BANK",
                peripherals::flash::SWAP_BANK,
            );
    }
    for register in peripherals::flash::READ_ONLY {
        builder = builder.write_modify("Flash", register.spec().name, WriteModify::Modify);
    }
    builder
}

/// The corrections of ST's file against RM0433 rev 9 for the system configuration controller, the
/// extended interrupt and event controller and the random number generator. The models in
/// [`peripherals::syscfg`], [`peripherals::exti`] and [`peripherals::rng`] hold the fields and the lists.
///
/// | Registers | Value | Source | ST's file |
/// |---|---|---|---|
/// | `EXTI_CPUIMR2` | `0xFFF5 FFFF`, the direct lines unmasked and the configurable ones masked | 20.6.22 | Zero |
/// | `EXTI_CPUIMR3` | `0x018B FFFF` | 20.6.25 | Zero |
/// | `EXTI_CPUPR1`, `EXTI_CPUPR2`, `EXTI_CPUPR3` | Type `rc1`: a written one clears the bit | 20.6.21, 20.6.24, 20.6.27 | Plain read-write, and the second and third read-only |
/// | `SYSCFG_PWRCR` `ODEN` | One bit, bit 0 | 12.3.10 | Four bits wide |
/// | `SYSCFG_CCCSR` `READY` | Type `r` | 12.3.7 | Read-write |
/// | `RNG_SR` `SEIS` and `CEIS` | Type `rc_w0`: a written zero clears the bit, and a written one does nothing | 34.7.2 | Plain read-write |
fn syscfg_exti_rng_corrections(mut builder: Builder) -> Builder {
    builder = builder
        .reset("EXTI", "CPUIMR2", 0xFFF5_FFFF)
        .reset("EXTI", "CPUIMR3", 0x018B_FFFF);
    for register in peripherals::exti::PENDING {
        builder = builder.write_modify("EXTI", register.spec().name, WriteModify::OneToClear);
    }
    builder
        .field("SYSCFG", "PWRCR", "ODEN", peripherals::syscfg::ODEN)
        .field("SYSCFG", "CCCSR", "READY", peripherals::syscfg::READY)
        .field("RNG", "SR", "SEIS", peripherals::rng::SEIS)
        .field("RNG", "SR", "CEIS", peripherals::rng::CEIS)
}

/// Every timer's corrections of ST's file against RM0433 rev 9 chapters 38 to 42. The model in
/// [`peripherals::tim`] holds the behavior.
///
/// | Register | RM0433 rev 9 | ST's file |
/// |---|---|---|
/// | `TIMx_SR` of all fourteen timers | Every flag is type `rc_w0`: cleared by writing zero and left alone by writing one. Sections 38.4.5, 39.4.5, 40.4.5, 40.5.3, 41.5.5, 41.6.4 and 42.4.4 | Plain read-write, with no write rules at all |
/// | `TIMx_ARR` | `0xFFFF` on every timer but TIM2 and TIM5, whose 32 bits reset to `0xFFFF_FFFF`. TIM3 and TIM4 narrow it to `0xFFFF` because 39.4.15 gives "Bits 31:16 ARR[31:16]: High auto-reload value (TIM2 and TIM5)" to those two alone. Sections 38.4.14, 39.4.15, 40.4.12, 40.5.10, 41.5.12, 41.6.11 and 42.4.8 | Zero, which is the one value that blocks the counter |
/// | `TIMx_AF1` of TIM1, TIM8, TIM15, TIM16 and TIM17, and `TIMx_AF2` of TIM1 and TIM8 | `0x0000_0001`: the `BKIN` pin enabled as a break source, sections 38.4.26 to 38.4.29, 41.5.19, 41.6.17 and 41.6.19 | Zero |
fn timer_corrections(mut builder: Builder) -> Builder {
    let every_timer = peripherals::tim::TIMERS
        .iter()
        .chain(&peripherals::tim::ADVANCED_TIMERS)
        .chain(&peripherals::tim::BASIC_TIMERS)
        .chain([
            &peripherals::tim::TIMER_15,
            &peripherals::tim::TIMER_16,
            &peripherals::tim::TIMER_17,
        ]);
    for timer in every_timer {
        builder = builder
            .write_modify(timer.instance, "SR", WriteModify::ZeroToClear)
            .reset(timer.instance, "ARR", timer.counter.mask());
    }
    for timer in &peripherals::tim::ADVANCED_TIMERS {
        builder = builder
            .reset(timer.instance, "AF1", peripherals::tim::BREAK_INPUT_ENABLED)
            .reset(timer.instance, "AF2", peripherals::tim::BREAK_INPUT_ENABLED);
    }
    builder
        .reset("TIM15", "AF1", peripherals::tim::BREAK_INPUT_ENABLED)
        .reset("TIM16", "TIM16_AF1", peripherals::tim::BREAK_INPUT_ENABLED)
        .reset("TIM17", "TIM17_AF1", peripherals::tim::BREAK_INPUT_ENABLED)
}

/// The clock controller's other corrections of ST's file against RM0433 rev 9 chapter 8, besides the
/// reset values in [`CLOCK_RESETS`] and [`RESETS`]. The model in [`peripherals::rcc`] holds the lists.
///
/// | What | RM0433 rev 9 | ST's file |
/// |---|---|---|
/// | The ready flags of `RCC_CR`, `LSERDY`, `LSIRDY`, `LSECSSD`, the divider flag, the two domain flags, `SWS`, and the reset flags of `RCC_RSR` and `RCC_C1_RSR` | Type `r`: 8.7.2, 8.7.7, 8.7.26, 8.7.27, 8.7.39 | Read-write, so a write would land |
/// | `RCC_CIFR`, every bit | Type `r`: 8.7.24, so a write leaves the register as it was | Read-write |
/// | `HSECSSON`, `WW1RSC` | Type `rs` and `rw1`: set by software, cleared by a system reset: 8.7.2, 8.7.37 | Plain read-write |
/// | A gate on each instance's clock enable | "When the bus interface clock is not active, read or write accesses to the peripheral registers are not supported. A read access will return invalid data. A write access will be ignored": 8.5.10 | No such rule, a file describes registers and not their clocks |
/// | The clock each instance that keeps time runs on | Figures 49 to 59 and Table 57 | |
/// | Writes to `RCC_BDCR` | Protected until `DBP` is set: 8.7.26, and 6.8.1 bit 8 for the RTC as well | |
///
/// The part also reads the requests the clock controller needs from the power controller and the
/// independent watchdog.
fn rcc_corrections(mut builder: Builder) -> Builder {
    for (register, field) in peripherals::rcc::field_corrections() {
        builder = builder.field("RCC", register, field.name, field);
    }
    builder = builder.write_modify("RCC", "CIFR", WriteModify::Modify);
    for (instance, register, field) in peripherals::rcc::CLOCK_GATES {
        builder = builder.gate(instance, Gate::by("RCC", register, field));
    }
    for (instance, register, field) in peripherals::rcc::RESET_LINES {
        builder = builder.reset_by(instance, Gate::by("RCC", register, field));
    }
    for (instance, output) in peripherals::rcc::UNGATED {
        builder = builder.clocked_by(instance, Clock::output("RCC", output));
    }
    for (nth, (instance, _)) in peripherals::rcc::GATED_CLOCKS.iter().enumerate() {
        builder = builder.clocked_by(
            instance,
            Clock::output("RCC", peripherals::rcc::Rcc::gated(nth)),
        );
    }
    builder.reads_requests("RCC")
}

/// The power controller's corrections of ST's file against RM0433 rev 9 chapter 6.
///
/// | What | RM0433 rev 9 | ST's file |
/// |---|---|---|
/// | `PWR_D3CR` | `0x0000 4000`, "Following reset VOSRDY will be read 1 by software": 6.8.6 | `0x0000_4000`, which reads `VOSRDY` clear |
/// | The six wake-up flags of `PWR_WKUPFR` | Type `r`: 6.8.8 | Read-write, so a write would land |
/// | Writes to `RCC_BDCR` and the RTC | "In reset state, the `RCC_BDCR` register, the RTC registers (including the backup registers), BREN and MONEN bits in `PWR_CR2` register, are protected against parasitic write access. This bit must be set to enable write access to these registers": 6.8.1 bit 8 | No such rule |
///
/// The write protection is on writes alone: a firmware reads `LSERDY` back while the domain is still
/// shut.
fn pwr_corrections(mut builder: Builder) -> Builder {
    builder = builder.reset("PWR", "D3CR", peripherals::pwr::D3CR_RESET);
    for field in &peripherals::pwr::WKUP_FLAGS {
        builder = builder.field("PWR", "WKUPFR", field.name, *field);
    }
    builder
        .gate_writes("RCC", &["BDCR"], Gate::by("PWR", "CR1", "DBP"))
        .gate_writes("RTC", &[], Gate::by("PWR", "CR1", "DBP"))
}

/// The serial peripheral interfaces' corrections of ST's file against RM0433 rev 9 chapter 50, for all
/// six instances of the layout. The model in [`peripherals::spi`] holds the fields.
///
/// | Register and field | RM0433 rev 9 | ST's file |
/// |---|---|---|
/// | `CR1` `CSTART`, `IOLOCK` | Type `rs`: written one sets it, hardware clears it. 50.11.1 | Read-only, so the firmware could never start a transfer |
/// | `CR2` `TSER` | Type `rs`: set by software while it holds zero, cleared by hardware at the reload. 50.11.2 | Read-only |
/// | `IER` `TXPIE`, `DXPIE` | Set by software, cleared by software or by the `TXTF` event. 50.11.5 types them `rs`, and ES0392 rev 15 item 2.22.4's workaround needs software to clear them | Read-only, and the second spelled `DPXPIE` |
/// | `TXCRC`, `RXCRC` | Every bit type `r`. 50.11.11, 50.11.12 | Read-write, so a write would land |
///
/// The file also spells `FTHLV` as `FTHVL` and calls the I2S configuration register `CGFR`, where section
/// 50.11.14 names it `SPI_I2SCFGR`. Neither changes a bit, so they stay as the file has them.
fn spi_corrections(mut builder: Builder) -> Builder {
    for instance in peripherals::spi::INSTANCES {
        for (register, corrected) in peripherals::spi::FIELD_CORRECTIONS {
            builder = builder.field(
                instance.name,
                register.spec().name,
                corrected.name,
                *corrected,
            );
        }
        for register in peripherals::spi::READ_ONLY {
            builder =
                builder.write_modify(instance.name, register.spec().name, WriteModify::Modify);
        }
    }
    builder
}

/// The inter-integrated circuit interfaces' corrections of ST's file against RM0433 rev 9 section
/// 47.9, the same on all four instances.
///
/// | Fields | RM0433 rev 9 | ST's file |
/// |---|---|---|
/// | `CR2` `START`, `STOP`, `NACK` and `PECBYTE` | Type `rs`: 47.9.2. Software sets them, hardware clears them, and a written zero does nothing | Plain read-write, so a written zero would clear a request hardware has not carried out |
/// | `ISR` `TXE` and `TXIS` | Type `rs`: 47.9.7. Software can set `TXE` to flush `TXDR`, and `TXIS` to raise a transmit event | Plain read-write, so a written zero would clear the flag |
///
/// It also splits `SADD[9:0]` into ten one-bit fields, which the model reads whole through a field
/// of its own and which needs no correction here.
fn i2c_corrections(mut builder: Builder) -> Builder {
    for instance in &peripherals::i2c::INSTANCES {
        builder = builder
            .field(instance.name, "CR2", "START", peripherals::i2c::START)
            .field(instance.name, "CR2", "STOP", peripherals::i2c::STOP)
            .field(instance.name, "CR2", "NACK", peripherals::i2c::NACK)
            .field(instance.name, "CR2", "PECBYTE", peripherals::i2c::PECBYTE)
            .field(instance.name, "ISR", "TXE", peripherals::i2c::TXE)
            .field(instance.name, "ISR", "TXIS", peripherals::i2c::TXIS);
    }
    builder
}

/// Names the readers of requests the DMA controllers and their multiplexers need, RM0433 rev 9 chapters 15
/// to 17: each multiplexer hears the peripherals, the controllers' acknowledges and its own events, each
/// controller hears its multiplexer, and every timer hears the multiplexer's acknowledge of its request,
/// sections 17.1 and 17.4.4. The models in [`peripherals::dmamux`] document the numbering.
fn dma_corrections(mut builder: Builder) -> Builder {
    for reader in ["DMAMUX1", "DMAMUX2", "DMA1", "DMA2", "BDMA"] {
        builder = builder.reads_requests(reader);
    }
    let every_timer = peripherals::tim::TIMERS
        .iter()
        .chain(&peripherals::tim::ADVANCED_TIMERS)
        .chain(&peripherals::tim::BASIC_TIMERS)
        .chain([
            &peripherals::tim::TIMER_15,
            &peripherals::tim::TIMER_16,
            &peripherals::tim::TIMER_17,
        ]);
    for timer in every_timer {
        builder = builder.reads_requests(timer.instance);
    }
    builder
}

/// The converters' corrections of ST's file against RM0433 rev 9 chapter 25, the same on all three
/// instances. The model in [`peripherals::adc`] holds the fields and the values.
///
/// | What | RM0433 rev 9 | ST's file |
/// |---|---|---|
/// | `ADC_CR` reset | `0x2000 0000`, the converter in deep-power-down: 25.6.3 | Zero |
/// | `ADC_CFGR` reset | `0x8000 0000`, the injected queue disabled: 25.6.4 | Zero |
/// | `ADC_LTR1` reset | Zero: 25.6.9 | `0x0FFF 0000` |
/// | `ADC_HTR1`, which the file calls `LHTR1`, `ADC_HTR2`, `ADC_HTR3` resets | `0x03FF FFFF`: 25.6.10, 25.6.22, 25.6.24 | `0x0FFF 0000` for the first and zero for the others |
/// | The command bits of `ADC_CR`: `ADEN`, `ADDIS`, `ADSTART`, `JADSTART`, `ADSTP`, `JADSTP`, `ADCAL` | Type `rs`: 25.6.3 | Plain read-write, so a written zero would clear a command hardware has not carried out |
/// | `BOOST` | Two bits, 9:8, for revision V: 25.6.3 | One bit |
/// | `LDORDY`, bit 12 of `ADC_ISR` | Revision V's regulator status, type `r`: 25.6.1 | Not in the file |
/// | `SMP0`, bits 2:0 of `ADC_SMPR1` | The sampling time of channel 0: 25.6.6 | The file's register starts at `SMP1` |
fn adc_corrections(mut builder: Builder) -> Builder {
    for converter in peripherals::adc::Converter::ALL {
        let instance = converter.instance();
        for (register, value) in peripherals::adc::RESETS {
            builder = builder.reset(instance, register, value);
        }
        for command in &peripherals::adc::CR_COMMANDS {
            builder = builder.field(instance, "CR", command.name, *command);
        }
        builder = builder
            .field(instance, "CR", "BOOST", peripherals::adc::BOOST)
            .declare(instance, "ISR", &peripherals::adc::LDORDY)
            .declare(instance, "SMPR1", &peripherals::adc::SMP0);
    }
    // Figure 136: the kernel clock reaches each common block, which divides it into the analog
    // clock of its converters, so a converter runs on its block's output and not the tree's.
    for (converter, block) in [
        ("ADC1", peripherals::adc::Block::Adc12),
        ("ADC2", peripherals::adc::Block::Adc12),
        ("ADC3", peripherals::adc::Block::Adc3),
    ] {
        builder = builder.clocked_by(
            converter,
            Clock::output(block.instance(), peripherals::adc::ANALOG_CLOCK),
        );
    }
    // Only the third converter reads a request, the common block's enables of its internal
    // channels. The first two have no internal channel and discard every request.
    builder = builder.reads_requests(peripherals::adc::Converter::Adc3.instance());
    for block in peripherals::adc::Block::ALL {
        builder = builder.reads_requests(block.instance());
    }
    builder
}

/// Each USB core's device block from `0x800` to the end of OUT endpoint 8's `0x20` at `0xC1F`. Sections
/// 57.14.65, 57.14.62, 57.14.66 and 57.14.64 place OUT endpoint x's control, interrupt, transfer size
/// and DMA address registers at `0xB00`, `0xB08`, `0xB10` and `0xB14 + 0x20 * x`, for x up to 8, so
/// endpoint 8's run from `0xC00` to `0xC17`. ST's file ends each core's device block at `0xBFF`.
const OTG_DEVICE_BLOCK: u64 = 0x420;

/// The part's tables, with every reset value RM0433 rev 9 corrects in place of the vendor's. The
/// module's README lists them.
pub fn builder() -> Builder {
    // Table 8, note 1: "Accessing undefined memory space in a peripheral returns zeros." ChibiOS
    // writes `TIMx_CCMR3` at `0x54` of every timer, which only TIM1 and TIM8 have.
    // ceiling: OUT endpoint 8's four registers answer as undefined space, zero and no write; upgrade
    // when a model of the USB core's device block takes them
    let mut builder = Bus::builder(CPU, &MEMORY, instances())
        .undefined_reads_zero()
        .grow_block("OTG1_HS_DEVICE", OTG_DEVICE_BLOCK)
        .grow_block("OTG2_HS_DEVICE", OTG_DEVICE_BLOCK);
    for (instance, register, value) in RESETS {
        builder = builder.reset(instance, register, value);
    }
    for (register, copy, value) in CLOCK_RESETS {
        builder = builder
            .reset("RCC", register, value)
            .reset("RCC", copy, value);
    }
    for (port, values) in PORT_RESETS {
        for (register, value) in CONFIGURED.iter().zip(values) {
            builder = builder.reset(port, register, value);
        }
    }
    builder = timer_corrections(builder);
    builder = rcc_corrections(builder);
    builder = pwr_corrections(builder);
    builder = dma_corrections(builder);
    let builder = flash_corrections(builder);
    let builder = spi_corrections(builder);
    let builder = i2c_corrections(builder);
    let builder = adc_corrections(builder);
    let builder = syscfg_exti_rng_corrections(builder);
    wiring(builder)
}

/// How the part's instances reach each other, past what each one's own corrections say.
fn wiring(mut builder: Builder) -> Builder {
    // Section 20.3: the ports' pins reach the interrupt controller's configurable lines. The ports
    // announce each pin's level as a request line of their own, and the controller reads them all.
    builder = builder.reads_requests("EXTI");
    // A serial interface reads the pins the board hangs its chips' selects on the same way.
    for instance in &peripherals::spi::INSTANCES {
        builder = builder.reads_requests(instance.name);
    }
    builder
}

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

    #[test]
    fn the_bus_carries_every_instance_and_the_memory_of_section_2_4() {
        let soc = builder().build().unwrap();
        assert_eq!(
            soc.peripherals().count(),
            generated::PERIPHERALS.len() + 1,
            "every instance of the file, and the data watchpoint unit"
        );
        assert!(soc.peripheral(crate::cpu::armv7m::DWT).is_some());
        assert_eq!(soc.cpu().interrupt_lines, 150);
        let ram: u64 = MEMORY
            .iter()
            .filter(|region| region.name.contains("sram") && region.name != "backup_sram")
            .map(|region| region.size)
            .sum();
        assert_eq!(ram, 864 << 10, "section 2.4's 864 Kbytes of system SRAM");
        let sectors: u64 = SECTORS
            .iter()
            .map(|blocks| blocks.size.get() * u64::from(blocks.count.get()))
            .sum();
        assert_eq!(sectors, MEMORY[1].size, "the sectors cover the flash");
    }

    #[test]
    fn the_flash_array_is_programmed_by_the_flash_interface_and_by_nothing_else() {
        let programmed: Vec<&str> = MEMORY
            .iter()
            .filter_map(|region| region.programmer.map(|_| region.name))
            .collect();
        assert_eq!(programmed, ["flash"], "the user flash and nothing more");
        assert_eq!(MEMORY[1].programmer, Some("Flash"));
        let soc = builder().build().unwrap();
        assert!(
            soc.peripheral("Flash").is_some(),
            "the instance the region names"
        );
    }

    #[test]
    fn every_corrected_instance_carries_table_147s_lines_and_no_two_share_one_the_table_does_not() {
        /// The lines Table 147 gives more than one instance.
        const SHARED: [u16; 6] = [18, 43, 44, 45, 54, 145];

        let soc = builder().build().unwrap();
        for (name, lines) in INTERRUPTS {
            let instance = soc
                .peripheral(name)
                .unwrap_or_else(|| panic!("{name} is an instance"));
            assert_eq!(instance.interrupts, lines, "{name}");
        }
        let mut holders: std::collections::BTreeMap<u16, Vec<&str>> =
            std::collections::BTreeMap::new();
        for instance in soc.peripherals() {
            for &line in instance.interrupts {
                holders.entry(line).or_default().push(instance.name);
            }
        }
        for (line, names) in holders {
            assert!(
                names.len() == 1 || SHARED.contains(&line),
                "line {line} is held by {names:?}"
            );
        }
    }

    #[test]
    fn every_port_comes_out_of_reset_as_the_manual_gives_it() {
        use super::generated::gpioa::Register;
        use crate::soc::Layout;

        /// Sections 11.4.1, 11.4.3 and 11.4.4, from the page.
        const NAMED_BY_THE_MANUAL: [(&str, [u64; 3]); 2] = [
            ("GPIOA", [0xABFF_FFFF, 0x0C00_0000, 0x6400_0000]),
            ("GPIOB", [0xFFFF_FEBF, 0x0000_00C0, 0x0000_0100]),
        ];
        const OTHER_PORTS: [u64; 3] = [0xFFFF_FFFF, 0, 0];

        let configured = [Register::Moder, Register::Ospeedr, Register::Pupdr];
        let named: Vec<&str> = configured
            .iter()
            .map(|register| register.spec().name)
            .collect();
        assert_eq!(
            named, CONFIGURED,
            "the corrections name these three, in this order"
        );

        let soc = builder().build().unwrap();
        for (port, _) in PORT_RESETS {
            let values = NAMED_BY_THE_MANUAL
                .iter()
                .find(|(named, _)| *named == port)
                .map_or(OTHER_PORTS, |(_, values)| *values);
            let registers = soc
                .registers(port)
                .unwrap_or_else(|| panic!("{port} is an instance"));
            for (register, expected) in configured.iter().zip(values) {
                assert_eq!(
                    registers.get(*register),
                    expected,
                    "{port} {}",
                    register.spec().name
                );
            }
        }
    }

    #[test]
    fn the_clock_power_flash_and_debug_registers_come_out_of_reset_as_the_manual_gives_them() {
        use super::generated::{dbgmcu, flash, pwr, rcc};

        let soc = builder().build().unwrap();
        let rcc_registers = soc.registers("RCC").unwrap();
        assert_eq!(rcc_registers.get(rcc::Register::Cr), 0x0000_0001);
        assert_eq!(rcc_registers.get(rcc::Register::Rsr), 0x00FE_0000);
        assert_eq!(rcc_registers.get(rcc::Register::C1Rsr), 0x00FE_0000);
        assert_eq!(rcc_registers.get(rcc::Register::Apb4enr), 0x0001_0000);
        assert_eq!(rcc_registers.get(rcc::Register::Apb4lpenr), 0x0421_DEAA);
        assert_eq!(rcc_registers.get(rcc::Register::C1Apb2lpenr), 0x31D7_3033);
        assert_eq!(
            soc.registers("PWR").unwrap().get(pwr::Register::Cr3),
            0x0000_0046
        );
        let flash_registers = soc.registers("Flash").unwrap();
        assert_eq!(flash_registers.get(flash::Register::Cr1), 0x0000_0031);
        assert_eq!(flash_registers.get(flash::Register::Cr2), 0x0000_0031);
        assert_eq!(flash_registers.get(flash::Register::Optcr), 0x0000_0001);
        assert_eq!(flash_registers.get(flash::Register::Optcr_), 0x0000_0001);
        assert_eq!(flash_registers.get(flash::Register::Crccr2), 0x001C_0000);
        assert_eq!(
            soc.registers("DBGMCU").unwrap().get(dbgmcu::Register::Idc),
            0x2003_6450
        );
    }
}