rigless 0.0.4

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; needs the rigless-sidecar package from rigless-labs/qemu
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 — `Stm32f427`, the MCU itself, plus `CPU`, `MEMORY`, `SERIAL_INTERFACES`, and `builder()` for a test that wants the tables alone. 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 `Reg` enum, a constant per field, and the table; never edited by hand |
| `memory_map.rs` | What the SVD cannot say: the memory map from RM0090, and every correction where the file disagrees with the manual, with its section named |
| `peripherals.rs`, `peripherals/` | The models of release 0.1, one module per register layout, each opening with a table of what it covers, what it corrects, and which erratum of ES0206 it models or why it does not: `gpio.rs`, the GPIO port of RM0090 chapter 8; `pwr.rs`, 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`, the reset and clock controller of chapter 6, which is likewise this part's and not chapter 7; `spi.rs`, the serial peripheral interface of chapter 28, which is the SPI half of a register block the I2S shares; and `tim.rs`, the general-purpose timer of chapter 18 |

## 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 | Eleven, all in `memory_map.rs`: the file names no interrupt count, so the CPU gets the 91 lines of RM0090 rev 22 table 63; it names no interrupt for SAI1 (line 87) or DMA2D (line 90), so those instances get theirs; it carries no write rules, so every timer's status register gets the manual's `rc_w0`, cleared by writing zero; and it gets five 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; UART8 and UART7; and LTDC, SAI1, SPI6, SPI5, and SPI4. 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`, the STM32F405's device identifier with a reserved nibble set, where section 38.6.1 gives this family `0x419`; the revision that section leaves product-dependent, and the correction picks `0x1007`, revision 1, with its reason. 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 is already standing. And `SPI_I2SPR` it gives as `0x000A` where section 28.5.9 gives `0x0002`, on all eight instances of that layout. Every other reset value checked against RM0090 rev 22 matched, `RCC_CSR`'s `0x0E00_0000` included |

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 are not unique; HASH and RNG share one, as do TIM1 and TIM9. A correction is a struct update 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, 1 << 6)?;
assert!(soc.model.enabled);
# Ok::<(), Box<dyn std::error::Error>>(())
```

`Single` puts one model on a bus of its own, and checks it was written for that instance's layout. That is 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.

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 until RL-116. `gpio.rs` and `tim.rs` reach one instance each, because ST's file gives every port and every timer a layout of its own where the manual gives one chapter; that is RL-131, and closing it is what takes the GPIO from one port to eleven without a line of model changing.