rigless 0.2.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
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
// Copyright (c) 2026 Rigless Labs.
// SPDX-License-Identifier: MIT OR Apache-2.0
//! Reset and clock control of RM0090 rev 22 chapter 6, written against the `rcc` layout.
//!
//! Chapter 6 is the RCC of the STM32F42xxx and STM32F43xxx, which is this SoC. Chapter 7 is the
//! same peripheral for the STM32F405/407/415/417. Its sections are numbered alike and read almost
//! the same, but it's the wrong part. Every citation here is chapter 6.
//!
//! | Register or section | Behavior | Source | Test |
//! |---|---|---|---|
//! | Reset state | HSI on and ready, everything else off, HSI the system clock | 6.2.6, 6.3.1 | `reset_runs_on_the_ready_hsi` |
//! | `CR` oscillators | Each enable raises its ready flag after that oscillator's startup, and clearing the enable drops it | 6.3.1 | `an_oscillator_becomes_ready_after_its_startup`, `clearing_an_enable_drops_its_ready_flag` |
//! | `CR` `HSEBYP` | Writable only while the HSE is off | 6.3.1 | `hsebyp_is_writable_only_while_the_hse_is_off` |
//! | `CR` in-use rule | The enable of an oscillator that clocks the system, directly or through the PLL, can't be cleared | 6.3.1, 6.2.6 | `the_running_system_clock_cannot_be_switched_off` |
//! | `CR` ready flags | Hardware's. A write can't set one, so a firmware can't report an oscillator locked that never started | 6.3.1 | `software_cannot_set_a_flag_hardware_owns` |
//! | `CR` startup, canceled | An enable cleared before its startup fires leaves no stale event behind | 6.3.1 | `a_startup_canceled_before_it_fires_leaves_nothing_behind` |
//! | `PLLCFGR` | `f(VCO) = f(input) x PLLN / PLLM`, `f(PLL) = f(VCO) / PLLP`, `f(48 MHz) = f(VCO) / PLLQ` | 6.3.2 | `the_pll_multiplies_and_divides_as_the_formula_says`, `a_factor_of_zero_yields_no_clock_rather_than_a_division_by_zero` |
//! | `PLLCFGR` while locked | A write is ignored while `PLLON` is set, and `PLLM` and `PLLSRC`, which the PLLI2S shares, while either is on | 6.2.3, 6.3.2 | `a_locked_pll_ignores_its_configuration`, `the_shared_pll_input_is_frozen_by_either_pll` |
//! | `CR` in-use rule through the PLL | Neither the PLL nor the oscillator feeding it can be stopped while the PLL clocks the system, and a PLL whose oscillator does stop is no longer ready | 6.3.1, 6.2.6 | `the_pll_and_its_source_cannot_be_stopped_while_they_clock_the_system`, `a_pll_whose_oscillator_stops_is_no_longer_ready` |
//! | `CFGR` `SW`, `SWS` | The switch happens only when the target is ready, and is deferred until it becomes ready | 6.2.6, 6.3.3 | `the_switch_waits_for_the_target_to_be_ready` |
//! | `CFGR` `HPRE` | The AHB prescaler, whose encoding has no divide by 32 | 6.3.3 | `the_ahb_prescaler_skips_thirty_two` |
//! | `CFGR` `PPRE1`, `PPRE2` | The two APB prescalers | 6.3.3 | `the_apb_prescalers_divide_the_ahb_clock` |
//! | `AHBxENR`, `APBxENR` as clocks | Each instance that keeps time runs on a clock of its own: its source's rate while its enable is set, and none while it's clear | 2.1.11, figure 16, 6.3.10 to 6.3.14 | `a_port_whose_enable_is_clear_takes_nothing_a_peer_sends` |
//! | `CFGR` `RTCPRE` | The HSE division for the RTC, where 0 and 1 are no clock | 6.3.3 | `the_rtc_clock_is_the_hse_divided_or_nothing`, `rtcpre_of_zero_is_no_clock` |
//! | `CFGR` `MCO1`, `MCO2` | Both outputs, each of the four sources of either, and their prescalers | 6.3.3 | `the_clock_outputs_carry_their_source_divided`, `every_clock_output_source_is_what_the_encoding_says` |
//! | Clock outputs | Seven rates the tree drives as clocks the runtime carries, numbered [`HCLK`] to [`IWDG_CLK`], and after them one for each instance that keeps time, through its enable. Each is driven at the write, the ready event or the clock failure that changes it, and at no other. The part's description says which instance runs on which | 6.2 figure 16 | `the_tree_drives_its_clocks_when_the_switch_lands`, `a_clock_is_driven_when_it_changes_and_not_otherwise`, `the_48_mhz_domain_is_nothing_until_the_pll_is_ready` |
//! | Timer clocks | `TIMPRE` clear: `PCLKx` at a prescaler of 1, else twice it. `TIMPRE` set: `HCLK` at 1, 2, or 4, else four times `PCLKx` | 6.2, 6.3.25 | `the_timer_clock_follows_the_doubling_rule`, `timpre_gives_the_timers_hclk` |
//! | `CIR` | A ready flag rises only when that source's `RDYIE` already stands, and then it's what raises the interrupt. `RDYC` clears it. Each source has its own triplet | 6.3.4 | `a_ready_flag_rises_only_for_a_source_whose_interrupt_is_enabled`, `a_ready_event_sets_its_flag_and_an_enabled_one_interrupts`, `the_clear_bits_clear_and_read_zero`, `every_source_has_its_own_flag_enable_and_clear` |
//! | `CIR` flags | Bits 7:0 are hardware's. A write can't raise one, bit 6 included, which ST's file omits | 6.3.4 | `software_cannot_set_a_flag_hardware_owns` |
//! | `BDCR` LSE | Its ready flag, and a bypass writable only while the LSE is off | 6.3.20 | `the_lse_becomes_ready_and_clocks_the_rtc`, `lsebyp_is_writable_only_while_the_lse_is_off` |
//! | `BDCR` RTC | `RTCEN` gates `RTCCLK`, and `RTCSEL` can't be changed once chosen unless the domain is reset | 6.2.8, 6.3.20 | `the_rtc_source_is_chosen_once_and_the_enable_gates_it` |
//! | `BDCR` `BDRST` | Set and cleared by software: it stands until the firmware clears it and holds the domain at its reset value while it does | 6.3.20 | `the_backup_reset_holds_the_domain_until_it_is_cleared` |
//! | Watchdog clock | Starting the independent watchdog is a request line to this model: the LSI oscillator is forced running from there on, whatever a write leaves in `LSION`, and `IWDG_CLK` carries its rate, nothing until it's ready | 6.2.9, 6.3.21 | `starting_the_watchdog_keeps_the_lsi_ready_even_once_lsion_reads_cleared`, `iwdg_clk_is_the_lsi_once_it_is_ready` |
//! | `CSR` | The LSI with its ready flag, and the reset flags, which only `RMVF` clears and a plain write leaves alone | 6.3.21 | `the_lsi_becomes_ready`, `rmvf_clears_the_reset_flags`, `a_plain_write_to_csr_keeps_the_reset_flags` |
//! | The LSI's own rate | A board's calibration through [`Rcc::calibrate_lsi`], refused outside DS9405 rev 13 table 42's 17 to 47 kHz, 32 kHz typical until then, and kept across a system reset | 6.2.5, 6.2.11 | `calibrating_the_lsi_moves_the_watchdogs_timeout`, `calibrating_the_lsi_outside_its_range_is_refused` |
//! | `DCKCFGR` | `TIMPRE`, and the SAI1-A source and divisors, which [`Rcc::sai_clock`] answers. `SAI1BSRC` is stored: no model consumes it | 6.3.25 | `the_sai_clock_follows_its_source_and_divisor` |
//! | `PLLI2SCFGR`, `PLLSAICFGR` | `N` on the VCO, `R` for the PLLI2S output that MCO2 and the I2S see, and `Q` for the SAI. Ignored while the PLL they configure is on | 6.2.3, 6.3.23, 6.3.24 | `the_audio_plls_multiply_and_divide`, `an_audio_pll_with_a_factor_of_zero_gives_no_clock`, `a_locked_audio_pll_ignores_its_configuration` |
//! | Clock security | With `CSSON` set and the HSE running, a failure disables it, sets `CSSF`, and pulses [`CLOCK_FAILURE`] to the break inputs of TIM1 and TIM8. Only where the HSE clocked the system does it force the HSI and stop the PLL and PLLI2S | 6.2.7, 17.3.12 | `a_clock_failure_forces_the_hsi_and_flags_it`, `a_clock_failure_leaves_a_system_it_does_not_clock_alone`, `a_clock_failure_starts_the_oscillator_it_switches_to`, `a_detector_that_is_off_or_has_nothing_to_watch_reports_nothing`, `the_clock_securitys_failure_reaches_the_advanced_timers_break` |
//! | `AHBxRSTR`, `APBxRSTR`, and every enable no modeled instance answers behind | Stored and readable, and nothing acts on them. The enables that gate a modeled instance are the row further down | 6.3.5 to 6.3.14 | `an_enable_bit_is_stored`, `a_reset_bit_is_stored` |
//! | `AHBxLPENR`, `APBxLPENR` | Stored. The low-power modes they qualify aren't modeled, and all three registers come out of reset with every bit set, which ST's file gets wrong | 6.3.15 to 6.3.19 | `a_low_power_enable_is_stored`, `the_low_power_enables_reset_with_every_peripheral_set` |
//! | `SSCGR` | Stored. Spread spectrum modulates the PLL to spread its emissions. It has no observable effect on a clock a deterministic emulator counts in whole nanoseconds | 6.3.22 | `spread_spectrum_is_stored_and_changes_nothing` |
//! | 6.1.1, 6.1.2, 6.1.3 | The three resets, as the flags they leave in `CSR` and the domain `BDRST` clears. A software reset the part restarts from leaves `SFTRSTF` beside the flags it had, and one from the pin `PADRSTF` alone | 6.1, 6.3.20, 6.3.21 | `rmvf_clears_the_reset_flags`, `the_backup_reset_holds_the_domain_until_it_is_cleared`, `a_reset_keeps_the_pins_and_the_backup_domain_and_nothing_else`, `a_reset_from_the_pin_leaves_its_own_flag_and_not_the_softwares` |
//! | `AHB1ENR`, `APB1ENR`, `APB2ENR` | Every bit here is a gate on the instance it names: an instance whose enable is clear doesn't answer its registers at all. The bits are the model's, and the part's binding is what consults them | 2.1.11, 6.3.10, 6.3.13, 6.3.14 | `a_register_answers_its_reset_value_once_its_clock_stands_and_not_before`, `every_clock_gate_is_named_for_the_instance_it_gates` |
//! | `BDCR` | The backup domain takes no write while `PWR_CR`'s `DBP` is clear, and answers a read throughout. It's the same mechanism, over one register and in one direction | 5.5.1, 6.3.20 | `the_backup_domain_refuses_a_write_and_answers_a_read` |
//!
//! **What the model leaves out, and why.**
//!
//! | Behavior | Source | Reason |
//! |---|---|---|
//! | How long a source takes to become ready | 6.3.1, DS9405 rev 13 | Section 6.3.1 fixes none of them, so every figure is the model's own, and a ready flag rises one microsecond after its enable. The datasheet's typical times are characteristics, not a programming contract, and modeling them would cost a firmware what it doesn't cost the part. The instruction rate is fixed whatever the clock tree says, so the 2 ms HSE is 500 000 instructions of a wait loop against the 32 000 the part retires on its 16 MHz HSI, and the 2 s LSE is half a billion. What the flag keeps is the part's order: hardware raises it, not the write. That's what tells a firmware that polls from one that doesn't. A machine that wants the datasheet's figure has no way to say so yet |
//! | A cleared enable drops its ready flag at once, where the part takes 6 oscillator cycles: 250 ns on a 24 MHz HSE, 183 us on the LSE | 6.3.1, 6.3.20 | A simplification, and the model's own. A firmware polls a ready flag to see it rise, not to watch it fall, and the direction this errs in is the one that can't hang a wait loop |
//! | The NMI a clock failure raises | 6.2.7 | A core exception, which the interrupt bitmap can't reach. A firmware sees `CSSF` and the forced switch |
//! | An HSE that stops while `CSSON` is clear | 6.2.7 | The detector is what makes a failure observable. With it off, nothing in this chapter reports one |
//! | An oscillator enabled with nothing on its pins reaching ready at no frequency | 6.3.1, 6.3.20 | The flag rises after the startup whatever is fitted, and every clock it drives reads zero |
//! | The remaining branches of figure 16: the I2S clock, whose `I2SSRC` is stored and not answered, SAI1-B, LTDC, the Ethernet and USB taps, and the core timer's `HCLK/8` | 6.2 | The seven outputs, and each instance's own clock through its enable, are driven as clocks. These are answered by method for a firmware that configures them, and driven to nobody, because nothing consumes them. The core's timer is the core's own model |
//! | Measuring the HSI, the LSE, or the LSI (6.2.11's own reason for the LSI: "to compute the IWDG timeout") against TIM5 or TIM11 | 6.2.11 | The wiring between a clock and a timer's input isn't built. `Rcc::calibrate_lsi` gives a board the number that measurement would answer, without the timer wiring itself |
//! | Section 6.2.3's broader reading of two rules the model takes from the register sections instead | 6.2.3, 6.2.7, 6.3.2 | Chapter 6 states each twice, and not alike. Section 6.2.3 says `PLLM` and `PLLSRC` are "common to both PLLs" and names all three, where section 6.3.2's own bit text says they "can be written only when the PLL and PLLI2S are disabled". Section 6.2.3 also says the PLLs are disabled "when HSE or PLL (clocked by HSE) are used as system clock", where section 6.2.7 says "the HSE oscillator clock was the clock source of PLL used as the system clock". The model follows the register section in the first case and section 6.2.7 in the second, because each is the narrower rule. Refusing a write or stopping a PLL that the part would have allowed is the failure a firmware can't work around |
//! | Holding a peripheral in reset through `RCC_AHBxRSTR` or `RCC_APBxRSTR`, so that it answers its reset values while the bit stands | 6.3.5 to 6.3.9 | The bits are stored. Being held at reset is a third behavior beside the gate and the request line the runtime gives the part, and it isn't one of them: a gated instance doesn't answer at all, where one held at reset answers its reset values |
//! | ES0206 rev 24 2.2.7: a peripheral doesn't answer for two AHB cycles, or `1 + (AHB/APB prescaler)` APB cycles, after its clock is enabled | ES0206 rev 24 | A gate opens at the instant the bit is written: the runtime takes performance over cycle fidelity, and a delayed gate would mean holding an access back. Both firmwares apply the dummy-read workaround anyway |
//! | ES0206 rev 24 2.2.9: the backup domain's protection takes effect some APB1 cycles after `DBP` is programmed | ES0206 rev 24 | The same as 2.2.7 and for the same reason: the gate over `BDCR` opens at the write |
//! | ES0206 rev 24 2.2.10: PC13 transitions disturb the LSE crystal | ES0206 rev 24 | An analog effect with no register behavior |
//! | ES0206 rev 24 2.2.11: a backup domain reset missed after its supply drops into a window a few mV wide | ES0206 rev 24 | The model has no VBAT rail and no power cycle, so the domain always reaches its reset value |
//! | ES0206 rev 24 2.2.12: over-drive and under-drive unavailable on some revisions | ES0206 rev 24 | A power mode of `PWR`, which owns the row that settles it. What belongs here is the ceiling it would impose: the model applies no upper bound to any frequency, so a firmware may configure a tree the part couldn't run |
//!
//! **Where this model departs from the vendor's file.** ST's SVD splits `SW`, `SWS`, and the
//! `PLLCFGR` factors one field per bit, and omits `PLLI2SQ` and `CIR`'s three `PLLSAI` bits. So the
//! model declares them as sections 6.3.2, 6.3.3, 6.3.4, and 6.3.23 give them. The SVD types three
//! flags read-write that those sections type `r` (`CR` bit 29, `CIR` bit 6, and `CSR` bits 31:25),
//! and the model puts those back on every write. The register file enforces the rest from the
//! table, which stays the vendor's.
//!
use crate::device::{Device, DeviceContext, Level, RegisterFile, RequestChange, ResetCause};
use crate::soc::{Access, FieldSpec, Layout, ReadAction, WriteModify};
use crate::time::{EventId, VirtualNanos};

use crate::soc::stm32f427::generated::rcc::{
    REGISTERS, Register, bdcr, cfgr, cir, cr, csr, dckcfgr, pllcfgr, plli2scfgr, pllsaicfgr,
};

/// The RCC's one interrupt, position zero of its instance's table. RM0090 rev 22 table 63 gives it
/// line 5.
const GLOBAL: usize = 0;

/// The internal oscillator that clocks the SoC out of reset, 16 MHz, RM0090 rev 22 section 6.2.2.
/// With `RCC_CFGR` at its reset value, every bus and both timer clocks run at this rate,
/// section 6.3.3. It's what a model that keeps time is built with until the tree drives it
/// otherwise.
pub const HSI_HZ: u64 = 16_000_000;

/// The clock outputs the tree drives, numbered as this model's own, RM0090 rev 22 section 6.2
/// figure 16. The part's description says which instance runs on which, and the model drives each
/// one whenever a write or a ready event changes what it comes to.
///
/// `HCLK` is the AHB clock, "to AHB bus, core, memory and DMA", so it's what the processor's own
/// cycle counter counts.
pub const HCLK: usize = 0;
/// The low-speed peripheral clock, `PCLK1`.
pub const PCLK1: usize = 1;
/// The high-speed peripheral clock, `PCLK2`.
pub const PCLK2: usize = 2;
/// What the timers on APB1 count, [`Rcc::timer_clock`] with [`Apb::One`].
pub const APB1_TIMERS: usize = 3;
/// What the timers on APB2 count, [`Rcc::timer_clock`] with [`Apb::Two`].
pub const APB2_TIMERS: usize = 4;
/// `PLL48CLK`, the 48 MHz domain the card interface, the USB core and the random number generator
/// run on, [`Rcc::pll48`].
pub const PLL48: usize = 5;
/// `IWDGCLK`, the LSI behind the watchdog's enable gate, RM0090 rev 22 figure 16 and section 6.2.9.
pub const IWDG_CLK: usize = 6;

/// The clock security system's failure, driven as a pulse on this instance's request output one.
/// It's the break input section 17.3.12 gives the advanced-control timers, "a clock failure event,
/// generated by the Clock Security System". Output zero is the backup domain's reset,
/// [`super::rtc::BACKUP_DOMAIN_RESET`].
pub const CLOCK_FAILURE: usize = 1;
/// How many clock outputs the model drives for the tree's own rates. The clock of each instance that
/// keeps time is one more after them, [`Rcc::gated`].
const OUTPUTS: usize = 7;

// `on_request`'s `change.instance` this model matches, section 6.2.9. `iwdg::STARTED` is the number.
const IWDG_STARTED_BY: &str = "IWDG";
/// The internal low-speed oscillator's typical rate, DS9405 rev 13 table 42, sections 6.2.5 and
/// 6.3.21 disagreeing between 32 and 40 kHz. What a board calibrates the model to before it, since
/// the oscillator is untrimmed silicon and 6.2.11 names measuring it against a timer for exactly
/// this: table 42's own 17 to 47 kHz range is what [`Rcc::calibrate_lsi`] refuses outside of.
const LSI_HZ_TYPICAL: u64 = 32_000;
const LSI_HZ_MIN: u64 = 17_000;
const LSI_HZ_MAX: u64 = 47_000;

const fn field(name: &'static str, bit: u8, width: u8, access: Access) -> FieldSpec {
    FieldSpec {
        name,
        bit,
        width,
        access,
        read: ReadAction::None,
        write: WriteModify::None,
    }
}

/// `SW`, the system clock switch, RM0090 rev 22 section 6.3.3. ST's file splits it into `SW0` and
/// `SW1`.
pub static SW: FieldSpec = field("SW", 0, 2, Access::ReadWrite);
/// `SWS`, the switch status, RM0090 rev 22 section 6.3.3. It's read-only, and ST's file splits it.
pub static SWS: FieldSpec = field("SWS", 2, 2, Access::ReadOnly);
/// `PLLM`, RM0090 rev 22 section 6.3.2. ST's file splits it into `PLLM0` to `PLLM5`.
pub static PLLM: FieldSpec = field("PLLM", 0, 6, Access::ReadWrite);
/// `PLLN`, RM0090 rev 22 section 6.3.2. ST's file splits it into `PLLN0` to `PLLN8`.
pub static PLLN: FieldSpec = field("PLLN", 6, 9, Access::ReadWrite);
/// `PLLP`, RM0090 rev 22 section 6.3.2. ST's file splits it into `PLLP0` and `PLLP1`.
pub static PLLP: FieldSpec = field("PLLP", 16, 2, Access::ReadWrite);
/// `PLLQ`, RM0090 rev 22 section 6.3.2. ST's file splits it into `PLLQ0` to `PLLQ3`.
pub static PLLQ: FieldSpec = field("PLLQ", 24, 4, Access::ReadWrite);
/// `PLLI2SQ`, RM0090 rev 22 section 6.3.23 bits 27:24, the PLLI2S divider for the SAI1 clock. ST's
/// file omits it, though it carries `PLLSAIQ` of the sibling register.
pub static PLLI2SQ: FieldSpec = field("PLLI2SQ", 24, 4, Access::ReadWrite);
/// `PLLSAIRDYF`, RM0090 rev 22 section 6.3.4 bit 6. ST's file omits it.
pub static PLLSAIRDYF: FieldSpec = field("PLLSAIRDYF", 6, 1, Access::ReadOnly);
/// `PLLSAIRDYIE`, RM0090 rev 22 section 6.3.4 bit 14. ST's file omits it.
pub static PLLSAIRDYIE: FieldSpec = field("PLLSAIRDYIE", 14, 1, Access::ReadWrite);
/// `PLLSAIRDYC`, RM0090 rev 22 section 6.3.4 bit 22. ST's file omits it.
pub static PLLSAIRDYC: FieldSpec = field("PLLSAIRDYC", 22, 1, Access::WriteOnly);

/// Port J's clock enable, RM0090 rev 22 section 6.3.10 bit 9. ST's file declares only bits 0 to 8.
pub static GPIOJEN: FieldSpec = field("GPIOJEN", 9, 1, Access::ReadWrite);
/// Port K's clock enable, RM0090 rev 22 section 6.3.10 bit 10. ST's file omits it.
pub static GPIOKEN: FieldSpec = field("GPIOKEN", 10, 1, Access::ReadWrite);
/// Port J's reset, RM0090 rev 22 section 6.3.5 bit 9. ST's file omits it.
pub static GPIOJRST: FieldSpec = field("GPIOJRST", 9, 1, Access::ReadWrite);
/// Port K's reset, RM0090 rev 22 section 6.3.5 bit 10. ST's file omits it.
pub static GPIOKRST: FieldSpec = field("GPIOKRST", 10, 1, Access::ReadWrite);
/// Port J's clock enable in sleep, RM0090 rev 22 section 6.3.15 bit 9. ST's file omits it.
pub static GPIOJLPEN: FieldSpec = field("GPIOJLPEN", 9, 1, Access::ReadWrite);
/// Port K's clock enable in sleep, RM0090 rev 22 section 6.3.15 bit 10. ST's file omits it.
pub static GPIOKLPEN: FieldSpec = field("GPIOKLPEN", 10, 1, Access::ReadWrite);

/// Every field this model declares because the vendor's file doesn't, or doesn't declare whole.
///
/// A generated table is the vendor's file transcribed, so a field ST omits or splits can't be added
/// to one. It's declared here instead. That lets a check ask the part what it declares
/// rather than only what ST did, and it's why none of the above needs an exemption anywhere.
pub static DECLARED: &[&FieldSpec] = &[
    &SW,
    &SWS,
    &PLLM,
    &PLLN,
    &PLLP,
    &PLLQ,
    &PLLI2SQ,
    &PLLSAIRDYF,
    &PLLSAIRDYIE,
    &PLLSAIRDYC,
    &GPIOJEN,
    &GPIOKEN,
    &GPIOJRST,
    &GPIOKRST,
    &GPIOJLPEN,
    &GPIOKLPEN,
];

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Source {
    Hsi,
    Hse,
    Pll,
    PllI2s,
    PllSai,
    Lse,
    Lsi,
}

impl Source {
    /// Every source, in the order their flags sit in `CIR`, RM0090 rev 22 section 6.3.4.
    const ALL: [Self; 7] = [
        Self::Lsi,
        Self::Lse,
        Self::Hsi,
        Self::Hse,
        Self::Pll,
        Self::PllI2s,
        Self::PllSai,
    ];

    fn control(self) -> (Register, &'static FieldSpec, &'static FieldSpec) {
        match self {
            Self::Hsi => (Register::Cr, cr::HSION, cr::HSIRDY),
            Self::Hse => (Register::Cr, cr::HSEON, cr::HSERDY),
            Self::Pll => (Register::Cr, cr::PLLON, cr::PLLRDY),
            Self::PllI2s => (Register::Cr, cr::PLLI2SON, cr::PLLI2SRDY),
            Self::PllSai => (Register::Cr, cr::PLLSAION, cr::PLLSAIRDY),
            Self::Lse => (Register::Bdcr, bdcr::LSEON, bdcr::LSERDY),
            Self::Lsi => (Register::Csr, csr::LSION, csr::LSIRDY),
        }
    }

    fn is_pll(self) -> bool {
        matches!(self, Self::Pll | Self::PllI2s | Self::PllSai)
    }

    /// The `CIR` flag, enable, and clear bits of this source, RM0090 rev 22 section 6.3.4.
    fn interrupt(self) -> (&'static FieldSpec, &'static FieldSpec, &'static FieldSpec) {
        match self {
            Self::Hsi => (cir::HSIRDYF, cir::HSIRDYIE, cir::HSIRDYC),
            Self::Hse => (cir::HSERDYF, cir::HSERDYIE, cir::HSERDYC),
            Self::Pll => (cir::PLLRDYF, cir::PLLRDYIE, cir::PLLRDYC),
            Self::PllI2s => (cir::PLLI2SRDYF, cir::PLLI2SRDYIE, cir::PLLI2SRDYC),
            Self::PllSai => (&PLLSAIRDYF, &PLLSAIRDYIE, &PLLSAIRDYC),
            Self::Lse => (cir::LSERDYF, cir::LSERDYIE, cir::LSERDYC),
            Self::Lsi => (cir::LSIRDYF, cir::LSIRDYIE, cir::LSIRDYC),
        }
    }
}

/// How long a source takes to become ready. RM0090 rev 22 section 6.3.1 fixes none of them. It says
/// only that hardware sets the flag once the oscillator is stable. DS9405 rev 13 tables 39 to 43
/// give characteristics, not a programming contract, so any figure here is the model's own. It's
/// one microsecond for every source, and deliberately short: the instruction rate is fixed
/// whatever the clock tree says, so the datasheet's 2 ms HSE costs a wait loop 500 000 instructions
/// against the 32 000 the part retires on its 16 MHz HSI, and its 2 s LSE costs half a billion. A
/// microsecond is 250. That's long enough that a firmware can still clear an enable inside it,
/// which is what section 6.3.1's canceled startup and its re-arm rule are about. A figure shorter
/// than one instruction would put both out of every firmware's reach.
const STARTUP: VirtualNanos = VirtualNanos(1_000);

/// The reset and clock control of one STM32F42xxx, RM0090 rev 22 chapter 6.
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct Rcc {
    hse_hz: u64,
    lse_hz: u64,
    pending: [Option<EventId>; Source::ALL.len()],
    shadow: Shadow,
    interrupt: Level,
    /// The rate last driven on each clock output, or `None` before the first, so a rate is driven
    /// when it changes and not on every write that leaves it alone.
    driven: Vec<Option<u64>>,
    /// Each gated instance's clock, in the order of the outputs after the tree's own: the output its
    /// rate comes from, and the register and field of its enable, as indices into the tables.
    gated: Vec<(usize, usize, usize)>,
    /// Whether the independent watchdog has asked for the LSI, RM0090 rev 22 section 6.2.9: once set,
    /// a write can no longer clear `LSION`.
    iwdg_forces_lsi: bool,
    /// The LSI's own rate, a board's to calibrate through [`Rcc::calibrate_lsi`].
    lsi_hz: u64,
}

impl Default for Rcc {
    fn default() -> Self {
        Self::new()
    }
}

impl Rcc {
    /// The RCC of a bare part: nothing on either oscillator pin until a board fits a crystal
    /// through [`Rcc::attach_crystals`]. A pin with nothing on it still reaches ready, at no
    /// frequency, and whether it should is an open question.
    ///
    /// Each instance that keeps time runs on a clock of its own: its source's rate while its enable
    /// is set, and none while it's clear. RM0090 rev 22 figure 16 draws each peripheral's clock
    /// through the enable bit sections 6.3.10 to 6.3.14 give it, so a peripheral whose enable is
    /// clear has no clock and does nothing, even with its own registers set up to run.
    #[must_use]
    pub fn new() -> Self {
        let gated: Vec<(usize, usize, usize)> = crate::soc::stm32f427::memory_map::clock_enables()
            .into_iter()
            .map(|(source, register, field)| {
                let (index, spec) = REGISTERS
                    .iter()
                    .enumerate()
                    .find(|(_, spec)| spec.name == register)
                    .unwrap_or_else(|| panic!("the clock controller has no register {register}"));
                let bit = spec
                    .fields
                    .iter()
                    .position(|spec| spec.name == field)
                    .unwrap_or_else(|| panic!("{register} has no field {field}"));
                (source, index, bit)
            })
            .collect();
        Self {
            hse_hz: 0,
            lse_hz: 0,
            pending: [None; Source::ALL.len()],
            shadow: Shadow::default(),
            interrupt: Level::Low,
            driven: vec![None; OUTPUTS + gated.len()],
            gated,
            iwdg_forces_lsi: false,
            lsi_hz: LSI_HZ_TYPICAL,
        }
    }

    /// The output the `n`th instance that keeps time runs on, in the part's order.
    #[must_use]
    pub(crate) const fn gated(n: usize) -> usize {
        OUTPUTS + n
    }

    /// The crystals a board fits, in hertz, with zero for a pin it leaves empty.
    /// RM0090 rev 22 section 6.2.1 takes the high-speed one between 4 and 26 MHz, and 6.2.4 the
    /// low-speed one at 32.768 kHz.
    ///
    /// A crystal is soldered on, so this is for before the first instruction and takes no register
    /// file. What a running oscillator does when its frequency changes underneath it isn't a thing
    /// boards do, and it isn't a thing this models. Whether an empty pin should reach ready at all
    /// is an open question.
    pub fn attach_crystals(&mut self, hse_hz: u64, lse_hz: u64) {
        self.hse_hz = hse_hz;
        self.lse_hz = lse_hz;
    }

    /// A board's calibration of the LSI, which panics outside DS9405 rev 13 table 42's 17 to 47 kHz.
    pub fn calibrate_lsi(&mut self, hz: u64) {
        assert!(
            (LSI_HZ_MIN..=LSI_HZ_MAX).contains(&hz),
            "DS9405 rev 13 table 42 gives the LSI 17 to 47 kHz, and {hz} Hz is outside it"
        );
        self.lsi_hz = hz;
    }

    /// The LSI's rate, a board's calibration or the typical 32 kHz.
    #[must_use]
    pub const fn lsi_hz(&self) -> u64 {
        self.lsi_hz
    }

    /// The crystals a board fitted, in hertz.
    #[must_use]
    pub const fn crystals(&self) -> (u64, u64) {
        (self.hse_hz, self.lse_hz)
    }
}

impl Rcc {
    fn source_hz(&self, registers: &RegisterFile, source: Source) -> u64 {
        let (register, _, ready) = source.control();
        if !ready.is_set(registers.get(register)) {
            return 0;
        }
        match source {
            Source::Hsi => HSI_HZ,
            Source::Hse => self.hse_hz,
            Source::Lse => self.lse_hz,
            Source::Lsi => self.lsi_hz,
            Source::Pll => self.pll_p(registers),
            // The audio PLLs have no single output: the VCO is what their Q and R taps divide, and
            // the model answers it here so that [`Rcc::plli2s_clock`] and [`Rcc::sai_clock`] can.
            Source::PllI2s | Source::PllSai => self.vco(registers, source),
        }
    }

    /// What clocks the three PLLs: `PLLSRC` picks the HSE or the HSI, RM0090 rev 22 section 6.3.2.
    fn pll_input_hz(&self, registers: &RegisterFile) -> u64 {
        if cr::HSERDY.is_set(registers.get(Register::Cr)) && Self::pll_source_is_hse(registers) {
            self.hse_hz
        } else if cr::HSIRDY.is_set(registers.get(Register::Cr))
            && !Self::pll_source_is_hse(registers)
        {
            HSI_HZ
        } else {
            0
        }
    }

    fn pll_source_is_hse(registers: &RegisterFile) -> bool {
        pllcfgr::PLLSRC.is_set(registers.get(Register::Pllcfgr))
    }

    /// `f(VCO) = f(input) x PLLN / PLLM`, RM0090 rev 22 section 6.3.2. The audio PLLs share `PLLM`.
    fn vco(&self, registers: &RegisterFile, which: Source) -> u64 {
        let pllcfgr = registers.get(Register::Pllcfgr);
        let m = PLLM.get(pllcfgr);
        if m == 0 {
            return 0;
        }
        let n = match which {
            Source::Pll => PLLN.get(pllcfgr),
            Source::PllI2s => plli2scfgr::PLLI2SNX.get(registers.get(Register::Plli2scfgr)),
            Source::PllSai => pllsaicfgr::PLLSAIN.get(registers.get(Register::Pllsaicfgr)),
            _ => return 0,
        };
        // The manual writes the formula as f(in) x (N / M), and the division has to come second.
        // Otherwise an input that PLLM doesn't divide evenly loses up to N - 1 hertz of the VCO.
        self.pll_input_hz(registers).saturating_mul(n) / m
    }

    /// `f(PLL) = f(VCO) / PLLP`, RM0090 rev 22 section 6.3.2, with `PLLP` encoding 2, 4, 6, 8.
    fn pll_p(&self, registers: &RegisterFile) -> u64 {
        let p = (PLLP.get(registers.get(Register::Pllcfgr)) + 1) * 2;
        self.vco(registers, Source::Pll) / p
    }

    /// The 48 MHz domain: `f(VCO) / PLLQ`, RM0090 rev 22 section 6.3.2, and nothing until the PLL
    /// is ready, since it's that PLL's VCO the tap divides.
    #[must_use]
    pub fn pll48(&self, registers: &RegisterFile) -> u64 {
        let (register, _, ready) = Source::Pll.control();
        let q = PLLQ.get(registers.get(Register::Pllcfgr));
        if q == 0 || !ready.is_set(registers.get(register)) {
            return 0;
        }
        self.vco(registers, Source::Pll) / q
    }

    /// `IWDGCLK`, RM0090 rev 22 figure 16 (p.153): the LSI through the watchdog's own enable gate,
    /// nothing until the independent watchdog has started, section 6.2.9 (p.159).
    fn iwdg_clk(&self, registers: &RegisterFile) -> u64 {
        if !self.iwdg_forces_lsi {
            return 0;
        }
        self.source_hz(registers, Source::Lsi)
    }

    /// Drives every clock output whose rate has changed. It's every entry point's last act: a write
    /// to the tree, a source becoming ready and a clock failure are the three things that move one.
    fn drive_clocks(&mut self, registers: &RegisterFile, context: &mut DeviceContext<'_>) {
        let rates = [
            self.hclk(registers),
            self.pclk1(registers),
            self.pclk2(registers),
            self.timer_clock(registers, Apb::One),
            self.timer_clock(registers, Apb::Two),
            self.pll48(registers),
            self.iwdg_clk(registers),
        ];
        let gated: Vec<u64> = self
            .gated
            .iter()
            .map(|&(source, register, field)| {
                let enable = &REGISTERS[register].fields[field];
                let register = Register::from_index(register).expect("found in the same table");
                if enable.is_set(registers.get(register)) {
                    rates[source]
                } else {
                    0
                }
            })
            .collect();
        for (output, hz) in rates.into_iter().chain(gated).enumerate() {
            if self.driven[output] != Some(hz) {
                self.driven[output] = Some(hz);
                context.clock(output, hz);
            }
        }
    }

    /// The system clock, whichever source `SWS` says is driving it, RM0090 rev 22 section 6.3.3.
    #[must_use]
    pub fn sysclk(&self, registers: &RegisterFile) -> u64 {
        match SWS.get(registers.get(Register::Cfgr)) {
            0 => self.source_hz(registers, Source::Hsi),
            1 => self.source_hz(registers, Source::Hse),
            2 => self.source_hz(registers, Source::Pll),
            _ => 0,
        }
    }

    /// The AHB clock, `SYSCLK / HPRE`. The encoding has no divide by 32:
    /// RM0090 rev 22 section 6.3.3.
    #[must_use]
    pub fn hclk(&self, registers: &RegisterFile) -> u64 {
        self.sysclk(registers) / ahb_divider(cfgr::HPRE.get(registers.get(Register::Cfgr)))
    }

    /// The APB1 clock, `HCLK / PPRE1`, RM0090 rev 22 section 6.3.3.
    #[must_use]
    pub fn pclk1(&self, registers: &RegisterFile) -> u64 {
        self.hclk(registers) / apb_divider(cfgr::PPRE1.get(registers.get(Register::Cfgr)))
    }

    /// The APB2 clock, `HCLK / PPRE2`, RM0090 rev 22 section 6.3.3.
    #[must_use]
    pub fn pclk2(&self, registers: &RegisterFile) -> u64 {
        self.hclk(registers) / apb_divider(cfgr::PPRE2.get(registers.get(Register::Cfgr)))
    }

    /// What the timers on one APB domain count, RM0090 rev 22 section 6.2. That section names
    /// `TIMPRE` in `RCC_CFGR` in one sentence and in `RCC_DKCFGR` in the next two. It's in
    /// `DCKCFGR`, section 6.3.25.
    #[must_use]
    pub fn timer_clock(&self, registers: &RegisterFile, apb: Apb) -> u64 {
        let field = match apb {
            Apb::One => cfgr::PPRE1,
            Apb::Two => cfgr::PPRE2,
        };
        let divider = apb_divider(field.get(registers.get(Register::Cfgr)));
        let pclk = self.hclk(registers) / divider;
        if dckcfgr::TIMPRE.is_set(registers.get(Register::Dckcfgr)) {
            // Section 6.2 words this as 1, 2, or 4 giving HCLK and anything else four times PCLKx.
            // At a prescaler of exactly 4 the two are the same number, so only 1 and 2 tell the
            // arms apart.
            if divider <= 4 {
                self.hclk(registers)
            } else {
                pclk.saturating_mul(4)
            }
        } else if divider == 1 {
            pclk
        } else {
            pclk.saturating_mul(2)
        }
    }

    /// The RTC clock: the LSE, the LSI, or the HSE divided by `RTCPRE`,
    /// RM0090 rev 22 sections 6.2.8, 6.3.3, and 6.3.20. An `RTCPRE` of 0 or 1 is no clock, and so
    /// is an unselected source.
    #[must_use]
    pub fn rtc_clock(&self, registers: &RegisterFile) -> u64 {
        let bdcr_value = registers.get(Register::Bdcr);
        // RM0090 rev 22 section 6.3.20 bit 15: RTCEN gates RTCCLK, and figure 16 puts that gate in
        // front of everything downstream of the selection.
        if !bdcr::RTCEN.is_set(bdcr_value) {
            return 0;
        }
        let select = bdcr::RTCSEL0.get(bdcr_value) | (bdcr::RTCSEL1.get(bdcr_value) << 1);
        match select {
            1 => self.source_hz(registers, Source::Lse),
            2 => self.source_hz(registers, Source::Lsi),
            3 => {
                let prescaler = cfgr::RTCPRE.get(registers.get(Register::Cfgr));
                if prescaler < 2 {
                    0
                } else {
                    self.source_hz(registers, Source::Hse) / prescaler
                }
            }
            _ => 0,
        }
    }

    /// The first clock output, its source divided by its prescaler, RM0090 rev 22 section 6.3.3.
    #[must_use]
    pub fn mco1(&self, registers: &RegisterFile) -> u64 {
        let value = registers.get(Register::Cfgr);
        let source = match cfgr::MCO1.get(value) {
            0 => self.source_hz(registers, Source::Hsi),
            1 => self.source_hz(registers, Source::Lse),
            2 => self.source_hz(registers, Source::Hse),
            _ => self.source_hz(registers, Source::Pll),
        };
        source / mco_divider(cfgr::MCO1PRE.get(value))
    }

    /// The PLLI2S clock output, `f(VCO) / PLLI2SR`, RM0090 rev 22 section 6.3.23. It's what the I2S
    /// peripherals and MCO2 see. The SAI takes the Q tap instead.
    #[must_use]
    pub fn plli2s_clock(&self, registers: &RegisterFile) -> u64 {
        let r = plli2scfgr::PLLI2SRX.get(registers.get(Register::Plli2scfgr));
        if r == 0 {
            return 0;
        }
        self.source_hz(registers, Source::PllI2s) / r
    }

    /// The second clock output, RM0090 rev 22 section 6.3.3.
    #[must_use]
    pub fn mco2(&self, registers: &RegisterFile) -> u64 {
        let value = registers.get(Register::Cfgr);
        let source = match cfgr::MCO2.get(value) {
            0 => self.sysclk(registers),
            1 => self.plli2s_clock(registers),
            2 => self.source_hz(registers, Source::Hse),
            _ => self.source_hz(registers, Source::Pll),
        };
        source / mco_divider(cfgr::MCO2PRE.get(value))
    }

    /// The SAI1-A clock: the PLLSAI's or the PLLI2S's Q output over its divisor, or an outside
    /// clock. RM0090 rev 22 section 6.3.25. A divisor field of `n` divides by `n + 1`.
    #[must_use]
    pub fn sai_clock(&self, registers: &RegisterFile) -> u64 {
        let dckcfgr_value = registers.get(Register::Dckcfgr);
        match dckcfgr::SAI1ASRC.get(dckcfgr_value) {
            0 => {
                let q = pllsaicfgr::PLLSAIQ.get(registers.get(Register::Pllsaicfgr));
                if q == 0 {
                    return 0;
                }
                self.source_hz(registers, Source::PllSai)
                    / q
                    / (dckcfgr::PLLSAIDIVQ.get(dckcfgr_value) + 1)
            }
            1 => {
                let q = PLLI2SQ.get(registers.get(Register::Plli2scfgr));
                if q == 0 {
                    return 0;
                }
                self.source_hz(registers, Source::PllI2s)
                    / q
                    / (dckcfgr::PLLI2SDIVQ.get(dckcfgr_value) + 1)
            }
            // 10 is an outside clock on the alternate function pin, which no wire reaches. 11 is
            // what the manual calls a wrong configuration.
            _ => 0,
        }
    }
}

/// Which APB domain a timer sits on.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Apb {
    /// The low-speed domain, `PCLK1`.
    One,
    /// The high-speed domain, `PCLK2`.
    Two,
}

/// `HPRE`: `0xxx` is no division, then 2, 4, 8, 16, **64**, 128, 256, 512.
/// RM0090 rev 22 section 6.3.3 leaves 32 out of the encoding, so `1100` is 64 and not 32.
fn ahb_divider(encoding: u64) -> u64 {
    match encoding {
        0b1000 => 2,
        0b1001 => 4,
        0b1010 => 8,
        0b1011 => 16,
        0b1100 => 64,
        0b1101 => 128,
        0b1110 => 256,
        0b1111 => 512,
        _ => 1,
    }
}

/// `PPRE1` and `PPRE2`: `0xx` is no division, then 2, 4, 8, 16. RM0090 rev 22 section 6.3.3.
fn apb_divider(encoding: u64) -> u64 {
    match encoding {
        0b100 => 2,
        0b101 => 4,
        0b110 => 8,
        0b111 => 16,
        _ => 1,
    }
}

/// `MCO1PRE` and `MCO2PRE`: `0xx` is no division, then 2, 3, 4, 5. RM0090 rev 22 section 6.3.3.
fn mco_divider(encoding: u64) -> u64 {
    match encoding {
        0b100 => 2,
        0b101 => 3,
        0b110 => 4,
        0b111 => 5,
        _ => 1,
    }
}

/// `PLLSAIRDY`, which RM0090 rev 22 section 6.3.1 types `r` and ST's file types read-write. Every
/// other ready flag of `CR`, and the HSI's factory calibration, are read-only in that file, so the
/// register file already refuses a write to them.
const CR_HARDWARE: u64 = cr::PLLSAIRDY.mask();

/// `PLLSAIRDYF`, which RM0090 rev 22 section 6.3.4 gives as bit 6 and ST's file omits, so the
/// register's own read-write access applies to it. The other six flags and `CSSF` are read-only in
/// that file.
const CIR_HARDWARE: u64 = PLLSAIRDYF.mask();

/// The seven reset flags of `CSR` bits 31:25, `r` in RM0090 rev 22 section 6.3.21 and read-write in
/// ST's file. Only `RMVF` clears them, so without this a plain write of `LSION` would erase the
/// record of why the part restarted. `LSIRDY` beside them is read-only in that file and needs
/// nothing.
const CSR_HARDWARE: u64 = csr::BORRSTF.mask()
    | csr::PADRSTF.mask()
    | csr::PORRSTF.mask()
    | csr::SFTRSTF.mask()
    | csr::WDGRSTF.mask()
    | csr::WWDGRSTF.mask()
    | csr::LPWRRSTF.mask();

/// What a write must not change, kept because a model sees the value written and not the one before
/// it. It starts at the manual's reset values, which `the_shadow_starts_at_the_reset_values` holds
/// against the register file. Section 6.3.1 leaves `CR`'s `HSICAL` byte undefined, and zero is the
/// model's choice.
#[derive(Debug, serde::Serialize, serde::Deserialize)]
struct Shadow {
    cr: u64,
    cir: u64,
    csr: u64,
    bdcr: u64,
    pllcfgr: u64,
    plli2scfgr: u64,
    pllsaicfgr: u64,
}

impl Default for Shadow {
    fn default() -> Self {
        Self {
            cr: 0x0000_0083,
            cir: 0,
            csr: 0x0E00_0000,
            bdcr: 0,
            pllcfgr: 0x2400_3010,
            plli2scfgr: 0x2400_3000,
            pllsaicfgr: 0x2400_3000,
        }
    }
}

impl Rcc {
    /// Where a source's pending startup sits in the table.
    fn index_of(source: Source) -> usize {
        Source::ALL
            .iter()
            .position(|candidate| *candidate == source)
            .expect("a source of the table")
    }

    fn requested(registers: &RegisterFile) -> Option<Source> {
        match SW.get(registers.get(Register::Cfgr)) {
            0 => Some(Source::Hsi),
            1 => Some(Source::Hse),
            2 => Some(Source::Pll),
            _ => None,
        }
    }

    fn is_ready(registers: &RegisterFile, source: Source) -> bool {
        let (register, _, ready) = source.control();
        ready.is_set(registers.get(register))
    }

    /// The switch of RM0090 rev 22 section 6.2.6. It happens only when the target is ready, and a
    /// request for a source that isn't ready yet is honored the moment it becomes ready.
    fn try_switch(registers: &mut RegisterFile) {
        let Some(target) = Self::requested(registers) else {
            return;
        };
        if !Self::is_ready(registers, target) {
            return;
        }
        let want = SW.get(registers.get(Register::Cfgr));
        let cfgr = registers.get(Register::Cfgr);
        registers.set(Register::Cfgr, SWS.set(cfgr, want));
    }

    /// Whether the system runs on this source, directly or through the PLL.
    /// RM0090 rev 22 section 6.3.1 forbids clearing the enable of one that does.
    fn clocks_the_system(registers: &RegisterFile, source: Source) -> bool {
        match SWS.get(registers.get(Register::Cfgr)) {
            0 => source == Source::Hsi,
            1 => source == Source::Hse,
            2 => {
                source == Source::Pll
                    || (source == Source::Hse && Self::pll_source_is_hse(registers))
                    || (source == Source::Hsi && !Self::pll_source_is_hse(registers))
            }
            _ => false,
        }
    }

    /// Raises the RCC interrupt when any enabled ready flag stands, RM0090 rev 22 section 6.3.4.
    ///
    /// The model keeps the level it last drove, so a write that changes nothing reports nothing.
    fn refresh_interrupt(&mut self, registers: &RegisterFile, context: &mut DeviceContext<'_>) {
        let cir_value = registers.get(Register::Cir);
        let wanted = if Source::ALL.iter().any(|source| {
            let (flag, enable, _) = source.interrupt();
            flag.is_set(cir_value) && enable.is_set(cir_value)
        }) {
            Level::High
        } else {
            Level::Low
        };
        if wanted == self.interrupt {
            return;
        }
        self.interrupt = wanted;
        context.interrupt(GLOBAL, wanted);
    }

    /// The HSE has stopped and the clock security system notices, RM0090 rev 22 section 6.2.7.
    /// Every clause of that section is conditional on what the oscillator was clocking, and the
    /// model's two tables say which of them it carries.
    pub fn fail_hse(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
        let cr_before = registers.get(Register::Cr);
        if !cr::CSSON.is_set(cr_before) || !cr::HSERDY.is_set(cr_before) {
            return;
        }
        let feeds_the_system = Self::clocks_the_system(registers, Source::Hse);
        let through_the_pll = feeds_the_system && SWS.get(registers.get(Register::Cfgr)) == 2;
        let mut cr_value = cr::HSEON.set(cr_before, 0);
        cr_value = cr::HSERDY.set(cr_value, 0);
        self.pending[Self::index_of(Source::Hse)] = None;
        if through_the_pll {
            for source in [Source::Pll, Source::PllI2s] {
                self.pending[Self::index_of(source)] = None;
            }
            cr_value = cr::PLLON.set(cr_value, 0);
            cr_value = cr::PLLRDY.set(cr_value, 0);
            cr_value = cr::PLLI2SON.set(cr_value, 0);
            cr_value = cr::PLLI2SRDY.set(cr_value, 0);
        }
        if feeds_the_system {
            // Hardware starts the oscillator it switches to. A firmware may have left it off.
            cr_value = cr::HSION.set(cr_value, 1);
            cr_value = cr::HSIRDY.set(cr_value, 1);
        }
        registers.set(Register::Cr, cr_value);
        if feeds_the_system {
            let mut cfgr_value = SW.set(registers.get(Register::Cfgr), 0);
            cfgr_value = SWS.set(cfgr_value, 0);
            registers.set(Register::Cfgr, cfgr_value);
        }
        let cir_value = registers.get(Register::Cir);
        registers.set(Register::Cir, cir::CSSF.set(cir_value, 1));
        context.request(CLOCK_FAILURE, Level::High);
        context.request(CLOCK_FAILURE, Level::Low);
        self.store_shadow(registers);
        self.refresh_interrupt(registers, context);
        self.drive_clocks(registers, context);
    }
}

impl Device for Rcc {
    type Register = Register;

    fn write(
        &mut self,
        register: Register,
        value: u64,
        registers: &mut RegisterFile,
        context: &mut DeviceContext<'_>,
    ) {
        match register {
            Register::Cr => self.write_cr(registers, context),
            Register::Cfgr => {
                Self::try_switch(registers);
            }
            Register::Cir => self.write_cir(value, registers, context),
            Register::Bdcr => self.write_bdcr(registers, context),
            Register::Csr => self.write_csr(value, registers, context),
            Register::Pllcfgr | Register::Plli2scfgr | Register::Pllsaicfgr => {
                self.guard_pll_configuration(register, registers);
            }
            _ => {}
        }
        self.store_shadow(registers);
        self.drive_clocks(registers, context);
    }

    fn on_event(
        &mut self,
        event: EventId,
        registers: &mut RegisterFile,
        context: &mut DeviceContext<'_>,
    ) {
        // An instant fires once per instance, so one event names every source started then.
        let named: Vec<usize> = (0..self.pending.len())
            .filter(|&index| self.pending[index] == Some(event))
            .collect();
        for index in named {
            self.pending[index] = None;
            let source = Source::ALL[index];
            let (register, _, ready) = source.control();
            let value = registers.get(register);
            registers.set(register, ready.set(value, 1));
            // RM0090 rev 22 section 6.3.4: each flag "is set by hardware when ... and xRDYIE is
            // set". A firmware that polls a ready flag with its interrupt disabled is polling a bit
            // hardware never raises. The ready flag of section 6.3.1 is the one to poll, and that's
            // the one the line above sets.
            let (flag, enable, _) = source.interrupt();
            let cir_value = registers.get(Register::Cir);
            if enable.is_set(cir_value) {
                registers.set(Register::Cir, flag.set(cir_value, 1));
            }
            self.follow_cr_enables(registers, context);
            Self::try_switch(registers);
            self.store_shadow(registers);
            self.refresh_interrupt(registers, context);
            self.drive_clocks(registers, context);
        }
    }

    /// The independent watchdog has started, RM0090 rev 22 section 6.2.9: the LSI is forced on from
    /// here on, the same in-use rule `write_cr` already gives the oscillators of `CR`, over a request
    /// line rather than a call into another instance.
    fn on_request(
        &mut self,
        change: RequestChange,
        registers: &mut RegisterFile,
        context: &mut DeviceContext<'_>,
    ) {
        if change.instance != IWDG_STARTED_BY || change.request != super::iwdg::STARTED {
            return;
        }
        self.iwdg_forces_lsi = true;
        self.write_csr(0, registers, context);
        self.store_shadow(registers);
        self.drive_clocks(registers, context);
    }
}

impl Rcc {
    /// The part has restarted, RM0090 rev 22 sections 6.1.1 and 6.3.21. `cause`'s own flag joins the
    /// reset flags the part had, which only `RMVF` clears, and figure 15 (p.152) routes every
    /// internal reset source through the same pulse generator onto NRST as `PADRSTF`'s own power-on
    /// reset value: this instance sets that flag too, whatever the cause. A low level on the pin
    /// itself, section 6.1.1's external reset, is that flag alone.
    ///
    /// # Panics
    ///
    /// Panics on a model this part carries no watchdog for: bit 28 is the core's own request, and a
    /// model asking under an instance name this row does not know would otherwise pass as one.
    pub fn restarted(
        &mut self,
        cause: ResetCause,
        flags_before: u64,
        registers: &mut RegisterFile,
    ) {
        let flag = match cause {
            ResetCause::Core => csr::SFTRSTF,
            ResetCause::Model("IWDG") => csr::WDGRSTF,
            ResetCause::Model(other) => panic!("{other} has no reset flag this part carries"),
            ResetCause::Pin => csr::PADRSTF,
        };
        let flags = flags_before | flag.mask() | csr::PADRSTF.mask();
        registers.restore(Register::Csr, CSR_HARDWARE, flags);
        self.store_shadow(registers);
    }

    fn store_shadow(&mut self, registers: &RegisterFile) {
        self.shadow = Shadow {
            cr: registers.get(Register::Cr),
            cir: registers.get(Register::Cir),
            csr: registers.get(Register::Csr),
            bdcr: registers.get(Register::Bdcr),
            pllcfgr: registers.get(Register::Pllcfgr),
            plli2scfgr: registers.get(Register::Plli2scfgr),
            pllsaicfgr: registers.get(Register::Pllsaicfgr),
        };
    }

    /// `CR`: the five enables and their ready flags, the bypass rule, and the in-use rule.
    fn write_cr(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
        registers.restore(Register::Cr, CR_HARDWARE, self.shadow.cr);
        // RM0090 rev 22 section 6.3.1: HSEBYP is writable only while the HSE oscillator is off.
        if cr::HSEON.is_set(self.shadow.cr) {
            registers.restore(Register::Cr, cr::HSEBYP.mask(), self.shadow.cr);
        }
        self.follow_cr_enables(registers, context);
        self.refresh_interrupt(registers, context);
    }

    /// Arms, holds, or drops the startup of every source whose enable lives in `CR`,
    /// RM0090 rev 22 section 6.3.1. A ready event runs it as well as a write, because an oscillator
    /// becoming ready is what lets a PLL already enabled against it start locking.
    fn follow_cr_enables(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
        for (index, source) in Source::ALL.iter().enumerate() {
            let (register, enable, ready) = source.control();
            if register != Register::Cr {
                continue;
            }
            let value = registers.get(register);
            if !enable.is_set(value) {
                if Self::clocks_the_system(registers, *source) {
                    registers.set(register, enable.set(value, 1));
                } else {
                    registers.set(register, ready.set(value, 0));
                    self.pending[index] = None;
                }
                continue;
            }
            // Section 6.3.1 forbids stopping an oscillator that feeds a PLL clocking the system,
            // and says nothing about one that stops while the system runs elsewhere. Holding the
            // flag there would let section 6.2.6's switch accept a PLL at no frequency, so the
            // model drops it.
            if source.is_pll() && self.pll_input_hz(registers) == 0 {
                registers.set(register, ready.set(value, 0));
                self.pending[index] = None;
                continue;
            }
            if !ready.is_set(value) && self.pending[index].is_none() {
                self.pending[index] =
                    Some(context.schedule(VirtualNanos(context.now().0 + STARTUP.0)));
            }
        }
    }

    /// `CIR`: the clear bits clear their flags and read zero, RM0090 rev 22 section 6.3.4.
    fn write_cir(
        &mut self,
        written: u64,
        registers: &mut RegisterFile,
        context: &mut DeviceContext<'_>,
    ) {
        registers.restore(Register::Cir, CIR_HARDWARE, self.shadow.cir);
        let mut value = registers.get(Register::Cir);
        for source in Source::ALL {
            let (flag, _, clear) = source.interrupt();
            if clear.is_set(written) {
                value = flag.set(value, 0);
            }
            value = clear.set(value, 0);
        }
        if cir::CSSC.is_set(written) {
            value = cir::CSSF.set(value, 0);
        }
        value = cir::CSSC.set(value, 0);
        registers.set(Register::Cir, value);
        self.refresh_interrupt(registers, context);
    }

    /// `BDCR`: the LSE, the RTC source, and the backup reset, RM0090 rev 22 section 6.3.20.
    fn write_bdcr(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
        // RM0090 rev 22 section 6.3.20: BDRST is set and cleared by software, so it stands until
        // the firmware clears it and holds the rest of the domain at its reset value while it does.
        if bdcr::BDRST.is_set(registers.get(Register::Bdcr)) {
            registers.set(Register::Bdcr, bdcr::BDRST.mask());
            self.pending[Self::index_of(Source::Lse)] = None;
            // The rest of the domain is the RTC's, and a reset is a line rather than a reach into
            // another model's registers. It's held high for as long as the firmware holds
            // the bit.
            context.request(super::rtc::BACKUP_DOMAIN_RESET, Level::High);
            self.refresh_interrupt(registers, context);
            return;
        }
        context.request(super::rtc::BACKUP_DOMAIN_RESET, Level::Low);
        // RM0090 rev 22 section 6.3.20 bit 2: LSEBYP is writable only while the LSE clock is
        // disabled, which is the rule section 6.3.1 gives HSEBYP.
        if bdcr::LSEON.is_set(self.shadow.bdcr) {
            registers.restore(Register::Bdcr, bdcr::LSEBYP.mask(), self.shadow.bdcr);
        }
        // RM0090 rev 22 section 6.3.20 bits 9:8 and section 6.2.8: once the RTC source has been
        // chosen it can't be changed again unless the backup domain is reset, and BDRST above is
        // what resets it.
        let selection = bdcr::RTCSEL0.mask() | bdcr::RTCSEL1.mask();
        if self.shadow.bdcr & selection != 0 {
            registers.restore(Register::Bdcr, selection, self.shadow.bdcr);
        }
        self.follow_enable(Source::Lse, registers, context);
    }

    /// `CSR`: the LSI, and `RMVF` clearing the reset flags, RM0090 rev 22 section 6.3.21.
    fn write_csr(
        &mut self,
        written: u64,
        registers: &mut RegisterFile,
        context: &mut DeviceContext<'_>,
    ) {
        registers.restore(Register::Csr, CSR_HARDWARE, self.shadow.csr);
        if csr::RMVF.is_set(written) {
            let mut value = registers.get(Register::Csr);
            for flag in [
                csr::BORRSTF,
                csr::PADRSTF,
                csr::PORRSTF,
                csr::SFTRSTF,
                csr::WDGRSTF,
                csr::WWDGRSTF,
                csr::LPWRRSTF,
            ] {
                value = flag.set(value, 0);
            }
            registers.set(Register::Csr, csr::RMVF.set(value, 0));
        }
        self.follow_enable(Source::Lsi, registers, context);
    }

    /// One oscillator outside `CR`: schedule its ready when enabled, drop it when not.
    ///
    /// RM0090 rev 22 section 6.3.21 types `LSION` "set and cleared by software", so a write here
    /// lands as written. Section 6.2.9 forces only the oscillator once the independent watchdog has
    /// started, not the bit, so `iwdg_forces_lsi` joins `LSION` in the enable this reads rather than
    /// overwriting what the write left in the register.
    fn follow_enable(
        &mut self,
        source: Source,
        registers: &mut RegisterFile,
        context: &mut DeviceContext<'_>,
    ) {
        let index = Self::index_of(source);
        let (register, enable, ready) = source.control();
        let value = registers.get(register);
        let enabled = enable.is_set(value) || (source == Source::Lsi && self.iwdg_forces_lsi);
        if enabled {
            if !ready.is_set(value) && self.pending[index].is_none() {
                self.pending[index] =
                    Some(context.schedule(VirtualNanos(context.now().0 + STARTUP.0)));
            }
        } else {
            registers.set(register, ready.set(value, 0));
            self.pending[index] = None;
        }
        self.refresh_interrupt(registers, context);
    }

    /// RM0090 rev 22 section 6.2.3: a PLL's factors can't change while it's enabled, so the write
    /// is ignored and the register keeps what it had.
    fn guard_pll_configuration(&mut self, register: Register, registers: &mut RegisterFile) {
        let (locked, previous) = match register {
            Register::Pllcfgr => (cr::PLLON, self.shadow.pllcfgr),
            Register::Plli2scfgr => (cr::PLLI2SON, self.shadow.plli2scfgr),
            Register::Pllsaicfgr => (cr::PLLSAION, self.shadow.pllsaicfgr),
            _ => return,
        };
        let cr_value = registers.get(Register::Cr);
        if locked.is_set(cr_value) {
            registers.set(register, previous);
            return;
        }
        // RM0090 rev 22 section 6.3.2 bits 5:0 and 22: PLLM and PLLSRC feed the PLLI2S too, so each
        // can be written only while both are disabled. The rest of PLLCFGR is the PLL's, and the
        // guard above has it. So a firmware that writes the whole word while the PLLI2S runs gets
        // the factors it asked for and the input it had. That's what a per-field interlock does on
        // the part: the bits that may not be written don't latch, and the ones beside them do.
        if register == Register::Pllcfgr && cr::PLLI2SON.is_set(cr_value) {
            let value = registers.get(register);
            let kept = PLLM.set(value, PLLM.get(previous));
            registers.set(
                register,
                pllcfgr::PLLSRC.set(kept, pllcfgr::PLLSRC.get(previous)),
            );
        }
    }
}

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

    #[test]
    fn the_shadow_starts_at_the_reset_values() {
        let mut soc = crate::mcu::Single::new(stm32f427::builder(), "RCC", Rcc::new()).unwrap();
        let registers = soc.registers();
        let shadow = Shadow::default();
        for (name, seeded, register) in [
            ("CR", shadow.cr, Register::Cr),
            ("CIR", shadow.cir, Register::Cir),
            ("CSR", shadow.csr, Register::Csr),
            ("BDCR", shadow.bdcr, Register::Bdcr),
            ("PLLCFGR", shadow.pllcfgr, Register::Pllcfgr),
            ("PLLI2SCFGR", shadow.plli2scfgr, Register::Plli2scfgr),
            ("PLLSAICFGR", shadow.pllsaicfgr, Register::Pllsaicfgr),
        ] {
            assert_eq!(seeded, registers.get(register), "{name} out of reset");
        }
    }
}