// Copyright (c) 2026 Rigless Labs.
// SPDX-License-Identifier: MIT OR Apache-2.0
//! The MCU debug component of RM0090 rev 22 section 38.16, written against the `dbg` layout.
//!
//! Section 38.16 is `DBGMCU`, ST's own extension of the Cortex-M4's debug infrastructure, mapped on
//! the external private peripheral bus at `0xE004_2000`. Section 38.6.1, in the same
//! chapter, is the ID code the same block answers. Neither section splits by sub-family the way
//! chapters 5 and 6 do, so there is no wrong-chapter trap here, but 38.6.1's device identifier and
//! revision codes are this family's own and are cited as such below.
//!
//! | Register or section | Behavior | Source | Test |
//! |---|---|---|---|
//! | Reset state | Every register at its manual value, `DBGMCU_IDCODE` corrected to this family's, the other three at zero. `memory_map.rs` carries the correction, since a reset value belongs to the instance | 38.16.3, 38.16.4, 38.16.5 | `every_register_answers_its_manual_reset_value` |
//! | `DBGMCU_IDCODE` `DEV_ID` | The device identifier of the STM32F42xxx and STM32F43xxx, read-only. ST's file's reset value is not this family's identifier, corrected in `memory_map.rs` | 38.6.1 | `every_register_answers_its_manual_reset_value`, `software_cannot_write_the_read_only_idcode` |
//! | `DBGMCU_IDCODE` `REV_ID` | The silicon revision, picked and reasoned in `memory_map.rs` since a reset value belongs to the instance. ChibiOS's `stm32_clock_init`, `hal_lld.c` line 308 of ArduPilot's tree, is the reader this part's boot exercises | 38.6.1 | `every_register_answers_its_manual_reset_value` |
//! | `DBGMCU_IDCODE` bits 15:12 | Reserved: 38.6.1 gives them no value and Table 314 prints none either. ST's file puts `6` there, the same nibble both of ArduPilot's own IDCODE constants carry (`hal_lld.c` and `iofirmware.cpp`), so `memory_map.rs` keeps it rather than zeroing what nothing on the shelf says is zero | 38.6.1 | `every_register_answers_its_manual_reset_value` |
//! | `DBGMCU_CR` `DBG_SLEEP`, `DBG_STOP`, `DBG_STANDBY` | Store what a debugger or the firmware writes. What they would do, feed `HCLK`/`FCLK` from the running clock or the internal oscillator while the core sits in a low-power mode, is a mode this model has no way to enter, see the second table | 38.16.1, 38.16.3 | `dbgmcu_cr_stores_every_low_power_and_trace_bit` |
//! | `DBGMCU_CR` `TRACE_IOEN`, `TRACE_MODE` | Store what is written. No trace port answers behind them, see the second table | 38.16.3 | `dbgmcu_cr_stores_every_low_power_and_trace_bit` |
//! | `DBGMCU_APB1_FZ` | Every freeze bit stores what is written, `DBG_RTC_STOP` and ArduPilot's `DBG_TIM5_STOP` among them | 38.16.2, 38.16.4 | `dbgmcu_apb1_fz_stores_every_freeze_bit_the_manual_gives_it` |
//! | `DBGMCU_APB2_FZ` | Every freeze bit stores what is written. What choosing one would do, since a debugger halt already stops every counter this instance could name, is the second table's | 38.16.2, 38.16.5 | `dbgmcu_apb2_fz_stores_every_freeze_bit_the_manual_gives_it` |
//! | System reset | `DBGMCU_CR`, `_APB1_FZ` and `_APB2_FZ` are reset by power-on and not by the system reset the part restarts from: "not reset by system reset" on all three register pages. `Stm32f427::reset` is where a debugger's configuration is kept rather than this file | 38.16.3, 38.16.4, 38.16.5 | `a_reset_keeps_the_pins_and_the_backup_domain_and_nothing_else` |
//!
//! **What the model leaves out, and why.**
//!
//! | Behavior | Source | Reason |
//! |---|---|---|
//! | `DBG_SLEEP`, `DBG_STOP` and `DBG_STANDBY` feeding `HCLK`/`FCLK` while the core is halted in a low-power mode | 38.16.1 | The processor enters Sleep, Stop or Standby inside the engine, which sends the runtime no event for it, the reason `pwr.rs` leaves those modes unmodeled too. The bits are stored and read back |
//! | One freeze bit actually keeping its peripheral counting while an unset one lets another stop, during a debugger halt | 38.16.2, 38.16.4, 38.16.5 | Virtual time is one clock the whole target shares, driven by the instruction count. A debugger halt stops that count for every model at once, so every counter this instance names is frozen for the whole halt whatever the bit says. At power-on every bit is clear, and 38.16.4 and 38.16.5 keep TIM1 to TIM14, both watchdogs and the RTC counting, and bxCAN and the `SMBus` timeouts running, so freezing them anyway is a deviation from the reset default and not only a limit on the set case. Letting an unset bit mean something would need a second, per-instance virtual clock, and the engine has one clock |
//! | An advanced timer's outputs disabled "as if the MOE bit was reset", forced inactive under `OSSI` or released to the GPIO controller, when `DBG_TIMx_STOP` halts its counter | 17.3.21, 38.16.2, 38.16.5 bits 1 and 0 | The same gap as the row above: the halt itself isn't modeled, so nothing this would gate is either. `tim.rs` records the same departure from its own side |
//! | Trace pin assignment, `TRACE_IOEN` and `TRACE_MODE` | 38.16.3, 38.17 | No trace port model exists behind them. ITM, chapter 38.14, and TPIU, chapter 38.17, are both out of scope. The bits are stored |
//! | ES0206 rev 24 2.2.1: with `DBG_STOP` set, an enabled SysTick interrupt wakes the core from Stop debug | ES0206 rev 24 | Needs the low-power mode entry the row above waits on, which isn't modeled here or in `pwr.rs` |
//! | ES0206 rev 24 2.2.2: entering Stop debug with a WFE can miss the instructions sequential to it after wake-up | ES0206 rev 24 | The same gap |
//! | ES0206 rev 24 2.2.3: with the prefetch queue disabled, the flash latency above zero wait states, and a WFE or WFI on a four-byte-aligned address ending in 4, some unreachable instructions can be executed after Sleep or Stop debug | ES0206 rev 24 | The same gap |
//! | ES0206 rev 24 2.9.5: the RTC's subsecond alarm downcounter keeps running under a debugger halt "even when the device is configured to stop the RTC in debug" | ES0206 rev 24 | Names `DBG_RTC_STOP`'s effect, but the flag it corrupts is `RTC_ISR.ALRAF`/`ALRBF`, section 26.6.4, and `rtc.rs` models neither the calendar nor the alarms. Recorded here because the erratum names this instance's field, and again in `rtc.rs` |
//! | No other ES0206 rev 24 item touches this instance | ES0206 rev 24 | The whole errata chapter was read for it. The revision codes are what `memory_map.rs`'s correction already answers through `DEV_ID`/`REV_ID` |
//! | Reserved bits of all three read-write registers reading back whatever was last written instead of the manual's fixed zero | 38.16.3, 38.16.4, 38.16.5 | No field covers a reserved bit in any register of this part, so a register's whole-register write rule is what lands there. The same is true of every reserved bit of every register this SoC models, and ArduPilot writes none of these |
//! | A declared correction for ST's spelling of `DBG_IWDG_STOP` and `DBG_I2C1_SMBUS_TIMEOUT` through `DBG_I2C3_SMBUS_TIMEOUT`, which its file gives as `DBG_IWDEG_STOP` and `DBG_J2C1_SMBUS_TIMEOUT` through `DBG_J2C3SMBUS_TIMEOUT` | 38.16.4 | A model declares a field only where it needs one, and nothing here reads a freeze bit by name beyond `DBG_RTC_STOP`. The three ST misspells still store under its own spelling |
//! | A narrower access than the 32 bits every one of the four register pages requires | 38.6.1, 38.16.3, 38.16.4, 38.16.5 | The register file lands a byte or half-word store in its lane regardless of what a register's own page requires, the same gap `gpio.rs` records for `LCKR` and word-only `IDR` |
//! | `DBGMCU_IDCODE` readable, and `DBGMCU_CR`, `_APB1_FZ` and `_APB2_FZ` writable, "under system reset" | 38.6.1, 38.16.3, 38.16.4, 38.16.5 | Already true here without a line of code: the model answers behind no clock gate, and a rigless reset restarts the core between one access and the next rather than holding it through an interval, so there is no window for a held-in-reset refusal to model |
//! | `DBG_IWDG_STOP`'s access type, blank in 38.16.4's own figure where every other freeze bit is marked `rw` | 38.16.4 | The model takes `rw` from ST's file, the same access every sibling freeze bit has, and 38.16.4's prose gives it the same "stopped when core is halted" meaning as the rest |
//! | Table 314's own printed bit positions: `DBGMCU_CR` fields at bits 20:16, which 38.16.3 reserves, and `DBG_RTC_STOP` at bit 9 | 38.18 table 314 (p. 1700) | Disagrees with the register's own page and with ST's CMSIS header, which the model follows instead: 38.16.3 reserves bits 20:16 and 38.16.4's text and figure agree with the header on bit 10 |
//!
//! **The revision this part reports.** `memory_map.rs` corrects `DBGMCU_IDCODE`'s reset value
//! against section 38.6.1 and gives its reason there, since a reset value belongs to the instance.
use crate::device::Device;
use crate::soc::{Access, FieldSpec, ReadAction, WriteModify};
use crate::soc::stm32f427::generated::dbg::Register;
/// `DBG_RTC_STOP`, RM0090 rev 22 section 38.16.4 bit 10, by the text and the bit figure. ST's file
/// omits it entirely, and Table 314 misprints it at bit 9.
pub static DBG_RTC_STOP: FieldSpec = FieldSpec {
name: "DBG_RTC_STOP",
bit: 10,
width: 1,
access: Access::ReadWrite,
read: ReadAction::None,
write: WriteModify::None,
};
/// Every field this model declares because the vendor's file omits it. See `super::rcc::DECLARED`.
pub static DECLARED: &[&FieldSpec] = &[&DBG_RTC_STOP];
/// The MCU debug component of one STM32F42xxx, RM0090 rev 22 section 38.16.
#[derive(Debug, Default, serde::Serialize, serde::Deserialize)]
pub struct Dbg;
impl Device for Dbg {
type Register = Register;
}