rigless 0.1.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
The STM32F427 as a Rigless SoC: the vendor's own register description turned into Rust a person can implement peripherals against.

## What is here

| Path | Contents |
|---|---|
| `../stm32f427.rs` | The index: what this module is made of, and the surface a board uses. That's `Stm32f427`, the MCU itself, plus `CPU`, `MEMORY`, `PORTS`, `SERIAL_INTERFACES`, and `builder()` for a test that wants the tables alone. The tables a board needs less often stay where they're declared, `peripherals::TIMERS` and `peripherals::CONTROLLERS` among them. The memory map is private to the module |
| `STM32F427.svd` | ST's CMSIS-SVD file for the SoC, committed unchanged |
| `generated.rs`, `generated/` | What `rigless svd generate` emits from it: for each register layout the `Register` enum, a constant per field, and the table. They're never edited by hand |
| `memory_map.rs` | What the SVD can't say: the memory map from RM0090, every correction where the file disagrees with the manual, with its section named, and what joins one instance to another. That's the clock enable each answers behind (section 2.1.11), the bit that unlocks the backup domain (section 5.5.1), which controllers read the requests the peripherals drive (tables 43 and 44), that every serial interface reads them too, because a chip select is a port pin's level arriving as that port's request line, the watchdog's start the clock controller reads (section 6.2.9), the clock security failure the advanced timers read (section 17.3.12), the backup domain reading the clock controller's reset (section 6.3.20), the converters' flags their common block reads and the internal channels it enables on the first converter (sections 13.13.15 and 13.13.16), and which output of the clock tree each instance that keeps time runs on (section 6.2 figure 16) |
| `peripherals.rs`, `peripherals/` | The models, one module per peripheral and each written against one generated layout. Each opens with two tables. The first is what it covers, what it corrects and which erratum of ES0206 it models, and the second is what it leaves out and why. `flash.rs` is the embedded flash memory interface of chapter 3, the STM32F42xxx and STM32F43xxx's own pages rather than the STM32F405's. `gpio.rs` is the GPIO port of RM0090 chapter 8. `pwr.rs` is the power controller of sections 5.5.1 and 5.5.2, which are the STM32F42xxx's and not the STM32F405's in 5.4. `rcc.rs` is the reset and clock controller of chapter 6, which is likewise this part's and not chapter 7. `spi.rs` is the serial peripheral interface of chapter 28, the SPI half of a register block the I2S shares. `tim.rs` is the general-purpose timer of chapter 18, answering for all four of them, and through its `Advanced` family the advanced-control timer of chapter 17, answering for TIM1 and TIM8. `dma.rs` is the direct memory access controller of chapter 10, one model for both of them. `sdio.rs` is the card interface of chapter 31, whose far end is a chip rather than a peripheral and lives under `src/chip`. `usart.rs` is the serial ports of chapter 30, one model answering for all eight from that chapter's two tables. `adc.rs` is the analogue-to-digital converter of chapter 13, answering for all three of them and for the common block they share. `otg.rs` is the reset register of the USB on-the-go full-speed core of chapter 34 and nothing else of it, a deliberate partial that opens by saying so. And `rtc.rs` is the backup domain of chapter 26 as far as the reset that clears it, a partial for the same reason that says so too. `iwdg.rs` is the independent watchdog of chapter 21, whose end of count restarts the part. `i2c.rs` is the inter-integrated-circuit interface of chapter 27 in controller mode, answering for all three of them. `dbg.rs` is the MCU debug component of section 38.16, on the core's own private peripheral bus rather than this part's own, and modelled here rather than beside the core family because the block is ST's own extension of the debug infrastructure and not the architecture's, unlike the cycle counter below. One more model the part binds isn't here and isn't the vendor's: the processor's cycle counter, at `src/cpu/armv7m.rs`, which belongs to the core family rather than to this part |

## The SVD

| | |
|---|---|
| Origin | ST, as published in the Arm STM32F4xx device family pack, tag `v3.1.1`, `CMSIS/SVD/STM32F427.svd` at <https://github.com/Open-CMSIS-Pack/STM32F4xx_DFP> |
| Version | SVD version 1.7, copyright 2024 ST |
| License | Apache-2.0, stated in the file's own header |
| Corrections | Twenty-four to the tables, all in `memory_map.rs`. A field a model needs and ST's file omits or misnames is declared by that model instead, and each says so where it records how it departs from the vendor's file. The file names no interrupt count, so the CPU gets the 91 lines of RM0090 rev 22 table 63. It gets thirteen instances' interrupt lines wrong, and `INTERRUPTS` corrects eleven of them against table 63. The other two are `I2S2ext` and `I2S3ext`, which the file derives from SPI1 and so gives SPI1's line 35, where table 63 gives that line to SPI1 alone and section 28.4.2 pairs the two with I2S2 and I2S3. The part models neither. Nine of the lines in that table are shared, eight by two peripherals and line 18 by all three converters, and the file gives each to one of them and leaves the rest empty, picking inconsistently. SAI1 (line 87) and DMA2D (line 90) it omits outright. And RNG it gives line 81, which is the FPU's, where line 80 `HASH_RNG` is the generator's and the hash processor's both. Table 62 is the STM32F405's vector table and 63 is this part's, which is the same trap chapter 6 sets against chapter 7. The file carries no write rules, so every timer's status register gets the manual's `rc_w0`, cleared by writing zero. And it gets ten reset values wrong. `RCC_PLLI2SCFGR` it gives as `0x2000_3000` where section 6.3.23 gives `0x2400_3000`, dropping a bit of `PLLI2SQ`, which section 6.3.24 confirms for the identical `PLLSAICFGR` the file gets right. The three low-power clock enable registers of sections 6.3.15, 6.3.18, and 6.3.19 come out of reset with every `LPEN` bit set, and the file leaves clear exactly the peripherals the STM32F42xxx adds over the STM32F405: DMA2D, SRAM3, GPIOK, and GPIOJ in the first, UART8 and UART7 in the second, and LTDC, SAI1, SPI6, SPI5, and SPI4 in the third. Section 6.3.19 prints its own value with a typo, `0x0x0477 7F33`, which its bit map settles. `DBGMCU_IDCODE` it gives as `0x1000_6411`, an identifier neither this family's, `0x419`, nor the STM32F405's, `0x413`. Section 38.6.1 leaves the revision product-dependent and the reserved nibble above `DEV_ID` unstated, and the correction picks `0x2003_6419`, revision 4, 5 or B, over-drive included and none of Table 3's seven revision-1-only items, keeping ST's own nibble since nothing on the shelf says it is zero. And `USART_SR` it gives as `0x00C0_0000` on both U(S)ART layouts, which is section 30.6.1's printed `0x000C0 00C0` read with its extra digit intact. That section's own bit map reserves bits 31:10 and puts `TXE` at 7 and `TC` at 6, so the value is `0x0000_00C0` and a firmware's first polled write waits on a flag that's already standing. And `SPI_I2SPR` it gives as `0x000A` where section 28.5.9 gives `0x0002`, on all eight instances of that layout. And the eleven ports are pointed at the one layout they share and each given the reset values sections 8.4.1, 8.4.3, and 8.4.4 print for it, thirty-three in all, written out for every port so that none is right by inheriting port A's. Among them is `GPIOA_OSPEEDR`, which section 8.4.3 gives as `0x0C00_0000` for the debug pins' very high speed and the file gives as zero. And every timer's `TIMx_ARR` it gives as zero, which is the one value 18.4.12 and 17.4.12 both say blocks the counter, so a firmware that starts a timer before programming its reload counts nowhere. Section 18.4.12 and table 100 give all ones, in the width the instance counts in, and section 17.4.12 gives `0xFFFF` to the two timers of chapter 17. The four of chapter 18 and the two of chapter 17 the part binds are corrected, and the other eight aren't yet. And `OTG_FS_GRSTCTL` it gives as `0x2000_0000`, a bit section 34.16.2 reserves, where that section gives `0x8000 0000`: bit 31 is `AHBIDL`, the AHB master idle flag, and a USB driver waits on it before it does anything else, so the vendor's value is a boot that never starts. Every other reset value checked against RM0090 rev 22 matched, `RCC_CSR`'s `0x0E00_0000` included. And `FLASH_ACR`'s `LATENCY` it gives three bits, transcribing the STM32F405's section 3.9.1, where this family's section 3.9.2 gives four. `FLASH_SR` it gives with no `RDERR` at all, the same transcription, where section 3.9.6 gives this family bit 8. And `FLASH_CR` `LOCK`/`STRT` and `FLASH_OPTCR` `OPTLOCK`/`OPTSTRT` it types plain read-write where their own pages type them write-one-to-set, cleared only by hardware. And every I2C interface's `SR1` it types plain read-write, where section 27.6.6 types its error flags `rc_w0`, cleared by writing zero. And `FLASH_SR` it gives no write rule, where section 3.9.6 clears every flag but `BSY` on a written one. And `RCC_AHB1ENR` it gives nine port enables where section 6.3.10 gives eleven, so `GPIOJEN` and `GPIOKEN` are declared for ports J and K. And it stops each instance of five of section 2.3's Table 1 blocks short of the next, at its last register or `0x400` on, where Table 1 gives the converters `0x4001 2000` to `0x4001 23FF`, the Ethernet MAC `0x4002 8000` to `0x4002 93FF`, USB OTG HS `0x4004 0000` to `0x4007 FFFF`, USB OTG FS `0x5000 0000` to `0x5003 FFFF` and the FMC's registers `0xA000 0000` to `0xA000 0FFF`, so `BLOCKS` runs each instance on to the next one's base and the last to the block's end, and a debugger's memory map holds the whole block |

The file needs no special treatment from the generator: no clusters, no arrays, every register 32 bits wide, and 33 of its 83 peripherals derive their layout from a sibling. Interrupt lines aren't unique: RM0090 rev 22 table 63 gives position 24 to `TIM1_BRK_TIM9` and position 80 to `HASH_RNG`, one vector for two peripherals each. ST's file doesn't say so, and gets both wrong in its own way. It leaves TIM9 no interrupt at all, and gives RNG position 81, which table 63 assigns to the FPU. Thirty-four of its instances carry no interrupt where the manual gives several of them one. Nine of them are corrected in `INTERRUPTS`, with the wrong lines TIM8 and RNG carry, against table 63 rather than against the file. A correction is a struct update or a builder call in `memory_map.rs`, never an edit to the file or the generated tables.

To bump the file, replace it, regenerate, and review the diff of the tables:

```text
rigless svd generate src/soc/stm32f427
```

CI runs the same command and fails when the committed tables differ from the result.

## Writing a model against it

```rust
use rigless::device::{Device, DeviceContext, RegisterFile};
use rigless::time::VirtualNanos;
use rigless::soc::Width;
use rigless::soc::stm32f427::generated::spi1::{cr1, Register};

#[derive(Default)]
struct Spi { pub enabled: bool }
impl Device for Spi {
    type Register = Register;
    fn write(&mut self, register: Register, value: u64, _registers: &mut RegisterFile, _context: &mut DeviceContext<'_>) {
        if register == Register::Cr1 { self.enabled = cr1::SPE.is_set(value); }
    }
}

let mut soc = rigless::mcu::Single::new(
    rigless::soc::stm32f427::builder(),
    "SPI1",
    Spi::default(),
)?;
rigless::mcu::Mcu::write(&mut soc, VirtualNanos(0), rigless::soc::stm32f427::generated::SPI1.base, Width::W32, cr1::SPE.mask())?;
assert!(soc.model.enabled);
# Ok::<(), Box<dyn std::error::Error>>(())
```

`Single` puts one model on a bus of its own, with the part's gates dropped so the model answers alone, and checks it was written for that instance's layout. That's what a test wants when it puts a model under the microscope. The model is then `soc.model`, a field. `Stm32f427::new()` is the whole MCU, with a model on every instance this part has one for, each its own field. An instance the part has no model for answers from its register file alone, and a run reports each register the firmware reaches there as an unimplemented gap.

How far a model reaches is therefore how far the vendor's file lets it. `spi.rs` reaches six of the eight instances that carry its layout. `I2S2ext` and `I2S3ext` are the two section 28.4.2 gives another job, and the part answers for neither. `gpio.rs` reaches all eleven ports. ST's file splits them across three layouts, one for port A, one for port B and one the other nine share, because sections 8.4.1, 8.4.3, and 8.4.4 give ports A and B reset values of their own, and a reset value belongs to the instance, so the memory map points every port at the one layout and states each one's values as corrections. `dma.rs` reaches both controllers, which are one layout and differ only in what the part wires to them. `tim.rs` reaches all four timers of chapter 18 across two layouts, the same rule a second time. Section 18.2 gives TIM2 and TIM5 a thirty-two-bit counter where TIM3 and TIM4 have sixteen, and table 100 gives the wide pair an option register the narrow pair hasn't, so the counter's width is a property of the instance and each pair answers from its own table. Through its `Advanced` family it also reaches both timers of chapter 17, TIM1 and TIM8, from the `tim1` layout ST's file gives the pair.

`usart.rs` reaches all eight serial ports across the two layouts ST's file gives them. UART4 and UART5 answer from the shorter one, which drops the modes table 148 marks not applicable to them. `i2c.rs` reaches all three interfaces, which ST's file gives the one `i2c3` layout. `adc.rs` reaches all three converters and their common block. `rcc.rs`, `pwr.rs`, `flash.rs`, `sdio.rs`, `iwdg.rs` and `dbg.rs` each reach the one instance their layout has.

`otg.rs` reaches one instance and one of its nineteen registers, which is a different thing entirely. It's a deliberate partial whose second table names every register it leaves out. The rest isn't modelled yet. `rtc.rs` is the other partial: it reaches one instance and answers for the twenty backup registers of section 26.6.20 and the reset that clears them, and the other nineteen registers of chapter 26 are one row of its second table. Binding a model claims the whole instance either way, so both are walked whole by the bench and both account for every register they don't answer for.

`src/cpu/armv7m.rs` is the one model the part binds that isn't under `peripherals/`. The cycle counter is at the same address on every ARMv7-M core, so it belongs to the family and not to this part, and neither ST manual describes it. ARM DDI 0403E.b and the Cortex-M4 technical reference manual do. It reaches the part because `memory_map.rs` places the instance beside the vendor's, and it reaches the runtime at all because the machine description claims a window of the processor's own peripheral bus for it.