rigless 0.0.5

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; a fork of QEMU is built in and executes the instructions
Documentation
// Copyright (c) 2026 Rigless Labs.
// SPDX-License-Identifier: MIT OR Apache-2.0
//! The backup domain of chapter 26, as far as the reset that clears it.
//!
//! The twenty backup registers are eighty bytes of application data in the one part of the chip that a
//! reset does not touch, which is what a firmware uses them for: ArduPilot reads all twenty before it has
//! written any, to learn whether the reboot it just took was asked for. What makes them worth a model is
//! not that they store bits — a register file does that — but the two ways they stop storing them.
//!
//! | Register or section | Behaviour | Source | Test |
//! |---|---|---|---|
//! | `RTC_BKP0R` to `RTC_BKP19R` | Twenty 32-bit registers of application data, read and written whole | 26.6.20 | `every_backup_register_holds_what_was_written` |
//! | Backup domain reset | `RCC_BDCR.BDRST` "resets the entire Backup domain", so every register of this instance returns to its own reset value. The clock controller drives it as a line rather than reaching in here, ADR-0023, and `rcc.rs` is the other end | 6.3.20 bit 16 | `a_backup_domain_reset_clears_the_backup_registers`, `the_domain_is_held_in_reset_while_the_bit_stands` |
//! | Write protection | Writes are refused until `PWR_CR.DBP` stands: "the `RCC_BDCR` register, the RTC registers (including the backup registers) ... are protected against parasitic write access". Declared in `memory_map.rs`, where the part's bindings live | 5.5.1 bit 8, 26.3.5 | `a_write_without_the_backup_domain_open_is_refused` |
//! | Read access | Not gated. 26.3.5 protects against *write* access and says nothing about reads, and the part is the witness: a firmware reads the backup registers with `RTCEN` still clear | 26.3.5 | `the_backup_registers_read_without_the_rtc_clock` |
//! | `RTC_WPR` | Not needed for these registers. 26.3.5 lists the key sequence and then excepts them by name: it unlocks "all the RTC registers except for `RTC_ISR[13:8]`, `RTC_TAFCR`, and `RTC_BKPxR`" | 26.3.5 | `the_key_sequence_is_not_what_stands_between_a_firmware_and_these` |
//! | ES0206 rev 24, 2.9 | Three errata name the RTC. 2.9.2 is the calendar registers and 2.9.3 the interrupt mask, neither of which is here. 2.9.1 is a spurious tamper detection, and a tamper event is one of the two things that clears these registers — it is unreachable without the tamper unit, and the row below says so | not applicable | |
//!
//! # What is not, and why
//!
//! | Departure | Reason |
//! |---|---|
//! | A system reset does not preserve them | 26.6.20 gives the registers two reset lines, and the second — "System reset: not affected" — is the whole reason a firmware trusts them. The runtime's reset is the bus's, every register to its reset value, and nothing yet keeps the backup domain apart from the rest of the part; a check of this row needs that first |
//! | A tamper event does not clear them | 26.3.13's tamper detection is not modelled, so the event cannot fire. The clearing itself is the same mechanism this module already answers, so it is the tamper unit that is missing rather than this |
//! | The calendar, the alarms, the wake-up timer, the timestamp, the calibration and the shift control | The rest of chapter 26. Nothing has asked for them, and the backup registers do not depend on them — they are in the domain, not on the clock |

use crate::device::{Device, DeviceContext, Level, RegisterFile, RequestChange};
use crate::soc::stm32f427::generated::rtc::Register;

/// The clock controller's own numbering of the line it drives the backup domain's reset on.
///
/// A request is named by the instance driving it and that instance's number for it, so this is the
/// clock controller's to define and this module's to recognise; `rcc.rs` drives it from `BDCR`.
pub const BACKUP_DOMAIN_RESET: usize = 0;

/// The instance that drives the backup domain's reset.
const RESET_BY: &str = "RCC";

/// The backup domain.
///
/// It holds nothing of its own: what the domain stores is the register file, and what this model adds is
/// the one line that empties it. A reset is level-driven rather than an edge, so the domain is put back
/// every time the line is seen high — which is what holding `BDRST` does on the part.
#[derive(Debug, Default)]
pub struct Rtc;

impl Device for Rtc {
    type Register = Register;

    fn on_request(
        &mut self,
        change: RequestChange,
        registers: &mut RegisterFile,
        _context: &mut DeviceContext<'_>,
    ) {
        if change.instance == RESET_BY
            && change.request == BACKUP_DOMAIN_RESET
            && change.level == Level::High
        {
            registers.reset();
        }
    }
}