// Copyright (c) 2026 Rigless Labs.
// SPDX-License-Identifier: MIT OR Apache-2.0
//! The reset register of the USB on-the-go full-speed core, RM0090 rev 22 chapter 34, written against the
//! `otg_fs_global` layout.
//!
//! **This is one register of nineteen, and a deliberate departure from how every other model here
//! is built.** A peripheral the release targets is implemented in full, every behavior citing its
//! section. This one covers `OTG_FS_GRSTCTL` and nothing else. The reason is that a firmware can't
//! get past the core's reset without it and can get everything else it needs from a register file.
//! ChibiOS's `otg_core_reset` spins on this register five times before it goes on, and against a
//! register file nothing it waits forever moves. The rest isn't modeled yet. When the global
//! block is modeled whole, that model takes this register over and this file's second table
//! empties.
//!
//! Nothing enumerates, no endpoint carries a byte, and no host is attached. A firmware brings the console
//! up and writes into it whether or not anyone is listening, which is how the same firmware behaves on a
//! bench with no cable in it.
//!
//! | Register or section | Behavior | Source | Test |
//! |---|---|---|---|
//! | Reset state | `0x8000 0000`: the master idle and nothing being reset. ST's file says `0x2000_0000`, which leaves `AHBIDL` clear, and `memory_map.rs` corrects it. The correction is what ends the first of the driver's five waits | 34.16.2 | `the_core_comes_out_of_reset_with_its_master_idle` |
//! | `AHBIDL` | Reads set and stays set: "Indicates that the AHB master state machine is in the Idle condition", and on this bench it always is, there being no transfer for it to be in the middle of. The field is read-only, so a write cannot clear it and the model does not have to put it back | 34.16.2 | `a_write_cannot_clear_the_idle_flag` |
//! | `CSRST`, `HSRST`, `FCRST`, `RXFFLSH`, `TXFFLSH` | Self-clearing: the core clears each one as the write lands, so a firmware spinning on any of them leaves the loop on its first read. The bit map types all five `rs`, and 34.16.2 says of `CSRST` "this is a self-clearing bit and the core clears this bit", of `HSRST` the same, and of `RXFFLSH` that "the application must wait until the bit is cleared" | 34.16.2 | `the_core_soft_reset_clears_itself`, `every_bit_the_core_clears_for_itself_reads_back_clear` |
//! | `TXFNUM` | Stored, and read back as written. It names the transmit FIFO a flush would empty, and the flush clears itself without one | 34.16.2 | `the_transmit_fifo_number_is_stored` |
//! | The driver's whole sequence | The five waits of ChibiOS's `otg_core_reset`, run in order as a firmware runs them: idle, soft reset, idle again, then the two queue flushes | 34.16.2 | `the_drivers_reset_sequence_leaves_every_one_of_its_waits` |
//! | ES0206 rev 24, 2.14 | Six errata name this core and none touches this register: 2.14.1 is the transmit FIFO's data, 2.14.2 to 2.14.5 are host mode, and 2.14.6 is `OTG_FS_DCFG`, which is the device block's. So there is nothing here to model and nothing left unmodeled | not applicable | |
//!
//! **What the model leaves out, and why.**
//!
//! | Behavior | Source | Reason |
//! |---|---|---|
//! | The other eighteen registers of the global block: `GOTGCTL`, `GOTGINT`, `GAHBCFG`, `GUSBCFG`, `GINTSTS`, `GINTMSK`, `GRXSTSR` and `GNPTXFSIZ` in both their device and host readings, `GRXFSIZ`, `GNPTXSTS`, `GCCFG`, `CID`, `HPTXFSIZ` and the three `DIEPTXF` | 34.16.2 | Register-only: each answers its reset value and keeps what is written, and nothing reacts. The interrupt hierarchy of `GINTSTS` against `GINTMSK` and the FIFO memory `GRXFSIZ` and the `DIEPTXF` registers carve up are the substance of the global block, and modeling either halfway would be worse than not at all |
//! | The device block's thirty-eight registers, the host block's thirty-nine, and the power and clock gating register | 34.16.4, 34.16.3 and 34.16.5, and the `otg_fs_device`, `otg_fs_host` and `otg_fs_pwrclk` layouts | Three more layouts and so three more models, since a model answers for one layout. The device block is where endpoints and transfers live. Host mode is out of scope altogether: nothing on this board attaches a device to the MCU |
//! | The interrupt line | RM0090 rev 22 table 63 lines 42 and 67 | This model raises nothing, because the only register it answers for raises nothing. `GINTSTS` is what drives the line and it is register-only |
//! | `RXFFLSH` taking eight clocks of the slower of the PHY and AHB clocks to clear, and `CSRST` and `HSRST` taking "several clocks, depending on the current state of the core" | 34.16.2 | The model clears all five inside the write that sets them. Only `RXFFLSH` has a duration in the chapter at all. The other two are left to the core's state, and the model doesn't invent one. Clearing at once errs in the direction that can't hang a firmware, and the loss is that a firmware can't observe a bit it set still standing. If one ever needs to, eight clocks is the number the chapter gives |
//! | What a core soft reset actually resets: the interrupts, every CSR bit but the six 34.16.2 lists, all the state machines, and the transmit and receive FIFOs | 34.16.2 | There is no state to reset. The registers this would clear are register-only, and clearing a register file to its reset values on a write is a thing the runtime has no verb for and that nothing yet asks for. It belongs with a model of the whole global block, where there is finally state worth resetting |
//! | The three PHY clocks a firmware must wait after `CSRST` clears before touching the PHY domain, and the synchronization delay that goes with it | 34.16.2 | A delay the application takes, not one the part enforces: the page tells the software to wait, and names no flag that would stop it if it did not. Nothing here can observe the difference |
//! | `FCRST` resetting the frame number counter, so the next `SOF` carries frame zero | 34.16.2 | "Only accessible in host mode", and there is no host mode and no frame counter. The bit still clears itself, because the bit map types it `rs` like the other four and a firmware that writes it in the wrong mode should not hang on it |
use crate::device::{Device, DeviceContext, RegisterFile};
use crate::soc::stm32f427::generated::otg_fs_global::{Register, fs_grstctl};
/// Every bit of `OTG_FS_GRSTCTL` the core clears for itself, 34.16.2: the bit map types all five `rs`,
/// which is a bit the application sets and the core clears. `TXFNUM` beside them is `rw` and stays.
const SELF_CLEARING: u64 = fs_grstctl::CSRST.mask()
| fs_grstctl::HSRST.mask()
| fs_grstctl::FCRST.mask()
| fs_grstctl::RXFFLSH.mask()
| fs_grstctl::TXFFLSH.mask();
/// The USB on-the-go full-speed core, as far as its reset register.
///
/// It keeps nothing: what the one register it answers for does is a function of the write alone,
/// and the register file holds the result. The type is how the part says this instance is answered
/// for rather than read from a file, and that's all it says.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct Otg;
impl Device for Otg {
type Register = Register;
fn write(
&mut self,
register: Register,
_written: u64,
registers: &mut RegisterFile,
_context: &mut DeviceContext<'_>,
) {
if register == Register::FsGrstctl {
// At once, because there are no FIFOs to empty and no state machines to return to idle.
let held = registers.get(Register::FsGrstctl);
registers.set(Register::FsGrstctl, held & !SELF_CLEARING);
}
}
}