// Copyright (c) 2026 Rigless Labs.
// SPDX-License-Identifier: MIT OR Apache-2.0
//! The universal synchronous asynchronous receiver transmitter of RM0090 rev 22 chapter 30, written
//! against the `usart6` layout. It answers for all eight instances: USART1, USART2, USART3, USART6,
//! UART7 and UART8 from that table, and UART4 and UART5 from the `uart4` table. That's the same
//! registers at the same offsets and bits, less the synchronous, smartcard and flow-control fields
//! and the guard-time register, table 148. [`Kind`] says which table an instance answers from.
//!
//! **What's on the far end is the caller's business.** The model sends and receives frames through
//! [`Peer`], hands the peer each word as its stop bit ends, and asks it for the next word whenever
//! the receiver is free: at the end of each frame, and at every stop of the target through
//! [`Device::poll`]. That's how a byte a host wrote lands at the stop it was noticed at. A chip, a
//! host on a socket and a test's stand-in all hang there the same way, and the model keeps its own
//! time against `BRR` and the peripheral clock whatever is behind it.
//!
//! | Register or section | Behaviour | Source | Test |
//! |---|---|---|---|
//! | Reset state | `TXE` and `TC` set and nothing else, `BRR` and the control registers clear. It's the value `memory_map.rs` corrects ST's file to | 30.6.1, 30.6.8 | `reset_leaves_the_transmitter_empty_and_complete` |
//! | The frame | A start bit, eight or nine data bits by `M`, and 1, 0.5, 2 or 1.5 stop bits by `STOP`, each bit `8 × (2 − OVER8) × USARTDIV` cycles of the peripheral clock, `USARTDIV` from `BRR`'s mantissa and fraction. A run of frames is timed from where it began, so the rounding can't accumulate | 30.3.1, 30.3.2, 30.3.4, 30.6.3, 30.6.5 | `the_frame_time_follows_brr_over8_m_and_stop`, `a_run_of_frames_does_not_drift` |
//! | Transmitter | A write to `DR` clears `TXE`. With nothing on the wire the word starts at once and `TXE` is set again. With a frame on the wire it waits in `TDR` until that frame ends. The peer hears each word as its stop bit ends. `TC` rises when a frame ends with `TXE` set | 30.3.2, 30.6.1 | `a_word_written_goes_out_after_the_idle_frame_and_the_peer_hears_it_at_its_stop_bit`, `a_second_word_waits_in_tdr_and_tc_rises_after_the_last` |
//! | The idle frame | Setting `TE` sends one frame of ones before the first word, which a word written meanwhile waits behind | 30.3.2, 30.6.4 | `a_word_written_goes_out_after_the_idle_frame_and_the_peer_hears_it_at_its_stop_bit` |
//! | `SBK` | A break of ten or eleven low bits by `M` and the stop bits, after the frame on the wire and before the word in `TDR`. The bit clears as the break ends | 30.3.2, 30.6.4 | `a_break_goes_out_after_the_current_word_and_sbk_clears_at_its_end` |
//! | Receiver | With `RE` set the model asks the peer. A word it offers takes a frame to arrive and then lands in `RDR` with `RXNE`. A read of `DR` clears `RXNE` and answers the word | 30.3.3, 30.6.1 | `a_word_the_peer_offers_lands_in_rdr_after_its_frame_and_raises_rxne` |
//! | `ORE` | A frame ending with `RXNE` still set sets `ORE`, keeps `RDR`, and loses the word. A read of `SR` then of `DR` clears it | 30.3.3, 30.6.1 | `a_second_word_with_rxne_standing_sets_ore_and_keeps_rdr` |
//! | `NF` and `FE` | A garbled line, a board condition, is [`Errors`]: how often a word arrives with noise or a framing error, drawn as each frame ends. An errored word lands with `RXNE` and its flag, and its data bits are flipped by the draw, since "the invalid data is transferred from the Shift register to the `USART_DR` register" and the page says no more of what it holds. `ONEBIT` disables `NF`. A word that overruns is lost, as 30.3.3 lists, so it raises only `ORE` whatever it came with, which is how the model reads 30.6.1's framing error that overruns | 30.3.3, 30.6.1, 30.6.6 | `a_garbled_word_lands_flipped_with_its_flag_and_onebit_hides_the_noise`, `an_errored_word_that_overruns_sets_only_ore` |
//! | The DMA against the error interrupt | With `DMAR` and `EIE` on, an errored word raises the request and the line together. The request's read of `DR` takes the word and leaves the flag, since no read of `SR` saw it rise, so the handler that runs next still finds it | 30.3.13, 30.6.1, table 147 | `the_dma_takes_an_errored_word_and_leaves_the_flag_for_the_handler` |
//! | `IDLE` | One frame with nothing arriving after a received word sets it, once, until `RXNE` has risen again. A read of `SR` then of `DR` clears it | 30.3.1, 30.3.3, 30.6.1 | `the_line_goes_idle_one_frame_after_the_last_word_and_not_again_until_the_next` |
//! | The far end between stops | A word the peer has by the instant the session polls at lands then, so a host's byte reaches a sleeping firmware | not applicable | `a_hosts_bytes_land_in_the_round_they_are_noticed_in`, `a_host_on_a_socket_hears_what_the_controller_sends_and_its_bytes_come_back` |
//! | A silent far end | A far end the board has silenced, a board condition, is a cut line. It hears none of what the firmware sends, and what it says at each look goes nowhere, so the line rests as it does behind a far end with nothing to say and nothing stale arrives once it answers again | not applicable | `a_silent_far_end_hears_nothing_and_what_it_says_is_lost` |
//! | `M`, `PCE`, `PS` | Nine data bits under `M`. Under `PCE` the most significant data bit is the parity of the rest, even or odd by `PS`, on the word sent and the word received | 30.3.7, 30.6.2, 30.6.4 | `the_data_register_takes_nine_bits_under_m_and_the_parity_bit_replaces_the_msb_under_pce` |
//! | The clearing sequences | `TC` by a read of `SR` then a write of `DR`, or a written zero. `RXNE` by a read of `DR` or a written zero. `IDLE`, `ORE`, `NF`, `FE` by a read of `SR` then a read of `DR`. `PE` by a read of `SR` then either access to `DR`. `ORE`, `NF` and `FE` clear only when that read of `SR` answered them set, so an error that rose after it stands: 30.3.3 has a word arrive between the two reads, and a DMA reads `DR` without reading `SR` at all. `CTS` and `LBD` by a written zero. A written one leaves every flag, and `TXE` is hardware's alone | 30.6.1 | `a_write_of_zero_to_sr_clears_only_the_bits_the_page_lets_it`, `a_second_word_waits_in_tdr_and_tc_rises_after_the_last`, `a_read_of_sr_before_a_flag_rose_does_not_clear_it` |
//! | The interrupt | One line, table 147: `TXE` under `TXEIE`, `TC` under `TCIE`, `RXNE` and `ORE` under `RXNEIE`, `IDLE` under `IDLEIE`, `PE` under `PEIE`, `LBD` under `LBDIE`, `CTS` under `CTSIE`, and `FE`, `ORE` or `NF` under `EIE` with `DMAR` | 30.4 | `each_enable_raises_the_line_for_its_own_flag` |
//! | DMA | `DMAR` drives the receive request off `RXNE` and `DMAT` the transmit request off `TXE`, so a controller wired to this instance is asked for one word at a time | 30.3.13 | `dmar_and_dmat_drive_the_two_requests_off_rxne_and_txe` |
//! | `TE` and `RE` cleared | The frame on the wire is lost when `TE` clears and the one arriving when `RE` clears | 30.3.2, 30.3.3 | `clearing_te_drops_the_word_in_flight_and_clearing_re_the_one_arriving` |
//! | `UE` cleared | Nothing new starts and the frame on the wire finishes | 30.6.4 | `a_disabled_usart_starts_nothing_and_finishes_what_it_had` |
//! | `fPCLK` | The peripheral clock as the part's tree feeds it, followed as it changes: the next frame goes at the new rate, and the fraction a run was carrying is dropped with the rate it was a fraction of | 30.3.4, 6.2 | `a_change_of_clock_applies_from_the_next_frame` |
//! | A stopped clock | The port's enable cleared: no frame on the wire ends, so neither `RXNE` nor `TC` rises, and a word a peer sends meanwhile is lost | 2.1.11, 6.2 figure 16, 6.3.13, 6.3.14 | `a_port_whose_enable_is_clear_takes_nothing_a_peer_sends` |
//! | The `uart4` table | UART4 and UART5 answer from their own six registers, with the fields the two tables share at the same bits, which `the_shared_fields_sit_at_the_same_bits_in_both_layouts` holds | 30.6.8, table 148 | `the_uart_layout_answers_for_uart4_and_uart5` |
//!
//! **What the model leaves out, and why.**
//!
//! | Behaviour | Source | Reason |
//! |---|---|---|
//! | Synchronous mode, `CLKEN`, `CPOL`, `CPHA`, `LBCL` | 30.3.9 | A clock on the `CK` pin is a waveform, and a peer here is a word at a time. The bits are stored and the frames cross as they would asynchronously |
//! | Hardware flow control, `CTSE`, `RTSE`, the `CTS` flag | 30.3.14 | No pin carries `CTS` or `RTS` to a peer. The bits are stored, a frame is never held for `CTS`, and the flag never rises. A host on a socket is always ready |
//! | LIN, smartcard, `IrDA` and half-duplex, `LINEN`, `LBD`, `SCEN`, `NACK`, `IREN`, `IRLP`, `HDSEL`, `GTPR` | 30.3.8, 30.3.10 to 30.3.12 | Four protocols on the same registers, and the first board wires none of them. The bits are stored and the model frames as in asynchronous mode. `LBD` never rises |
//! | Mute mode, `RWU`, `WAKE`, `ADD` | 30.3.6 | A network of USARTs on one line, and every line the first board has is point to point. `RWU` is stored and never set by hardware |
//! | `PE` as an error received | 30.3.7 | A peer hands over a word, not bits, and the model computes the parity bit of what it receives, so there's no wrong parity to receive and `PE` never rises. A garbled word's flipped bits are data bits, never the parity bit. A break arriving, which the page frames as `FE`, has no way to arrive |
//! | Start bit detection and the sampling of 30.3.3 | 30.3.3 | A word arrives whole and takes a frame to do so. Noise and framing errors are drawn per word rather than sampled per bit |
//! | Noise on the start bit under `ONEBIT` | 30.3.3, 30.6.6 | `ONEBIT` applies only to the data bits, so on the part a noisy start bit still raises `NF`. The model draws noise per word, so under `ONEBIT` it never raises `NF` |
//! | A frame on the wire when the peripheral clock changes | 30.3.4 | It finishes at the rate it began at, where the part would clock its remaining bits at the new one: a difference of less than one frame, once, and a tree isn't retuned with a frame in flight |
//! | Being held at reset by `RCC_APBxRSTR` | 6.3.8, 6.3.9 | The enable half of this is the part's gate. The reset half isn't one of the joins the runtime gives the part |
//! | The one bit time between `TE` and the first bit | 30.6.4 | Folded into the idle frame that follows it, which is a full frame of ones |
//! | Two breaks back to back, and `SBK` cleared before its break begins | 30.3.2 | The bit is read when the frame on the wire ends, so a second set before that is one break |
//! | ES0206 rev 24 2.11.1: the idle frame not detected under a clock deviation | ES0206 rev 24 | No clock deviates. A peer's word takes exactly a frame |
//! | ES0206 rev 24 2.11.2: `PE` cleared by a write to `DR` after a read of `SR` | ES0206 rev 24 | Unreachable: `PE` never rises here, the row on `PE` above. The clearing sequence the erratum meets by accident, a read of `SR` then either access to `DR`, is the one the model keeps |
//! | ES0206 rev 24 2.11.3, 2.11.6, 2.11.8: mute mode and smartcard | ES0206 rev 24 | The modes aren't modelled |
//! | ES0206 rev 24 2.11.4, 2.11.5, 2.11.7, 2.11.9: `CTS`, `RTS` and the break | ES0206 rev 24 | Flow control isn't modelled, so there's no `CTS` to ignore and no `RTS` to drive |
//!
//! **Where the model departs from the vendor's file.** ST types every flag of `SR` read-write with
//! no write rule, where 30.6.1 types `CTS`, `LBD`, `TC` and `RXNE` `rc_w0` and the rest read-only.
//! The model keeps the flags itself and puts them back after every write. ST gives `SR` a reset
//! value with a digit too many, which `memory_map.rs` corrects for every instance.
use std::any::Any;
use std::collections::VecDeque;
use crate::chip::uart::{Errors, Peer};
use crate::chip::{Place, Placed};
use crate::device::{Device, DeviceContext, Level, RegisterFile};
use crate::snapshot::store;
use crate::soc::RegisterSpec;
use crate::soc::stm32f427::generated::{uart4, usart6};
use crate::time::{EventId, VirtualNanos};
use usart6::{Register, cr1, cr2, cr3, sr};
/// The request output `DMAR` drives, RM0090 rev 22 section 30.3.13.
pub const RECEIVE_REQUEST: usize = 0;
/// The request output `DMAT` drives.
pub const TRANSMIT_REQUEST: usize = 1;
/// The instance's one interrupt line, table 147.
const GLOBAL: usize = 0;
const NANOS_PER_SECOND: u128 = 1_000_000_000;
/// How many words the model remembers sending, oldest dropped first.
const REMEMBERED: usize = 64;
const EIGHT_BITS: u16 = 0xFF;
const NINE_BITS: u16 = 0x1FF;
/// The flags a written zero clears, 30.6.1: `CTS`, `LBD`, `TC` and `RXNE`.
const CLEARED_BY_ZERO: u64 = sr::CTS.mask() | sr::LBD.mask() | sr::TC.mask() | sr::RXNE.mask();
/// The flags a read of `SR` then a read of `DR` clears, whatever that read of `SR` answered.
const CLEARED_BY_READING: u64 = sr::IDLE.mask() | sr::PE.mask();
/// The errors a read of `SR` then a read of `DR` clears, and only those that read of `SR` answered set.
const ERRORS_CLEARED_WHEN_SEEN: u64 = sr::ORE.mask() | sr::NF.mask() | sr::FE.mask();
/// The flags a read of `SR` then a write of `DR` clears.
const CLEARED_BY_WRITING: u64 = sr::TC.mask() | sr::PE.mask();
/// `STOP`, 30.6.5, in half bits: 1, 0.5, 2 and 1.5 stop bits.
const STOP_HALF_BITS: [u64; 4] = [2, 1, 4, 3];
/// Which of the two tables an instance answers from, table 148 and 30.6.8.
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum Kind {
/// USART1, USART2, USART3, USART6, UART7 and UART8: the `usart6` table, every register and
/// field.
Usart,
/// UART4 and UART5: the `uart4` table, six registers and the fields the chapter gives them.
Uart,
}
impl Kind {
/// The instance's own table, which the `uart4` one is a prefix of.
#[must_use]
pub const fn layout(self) -> &'static [RegisterSpec] {
match self {
Self::Usart => &usart6::REGISTERS,
Self::Uart => &uart4::REGISTERS,
}
}
}
/// A stand-in for the far end: it answers a known pattern, one word per ask, and records every word
/// it was sent, so the bench can prove a controller reached the far end rather than its own buffer.
#[derive(Debug, Default, serde::Serialize, serde::Deserialize)]
pub struct Pattern {
answers: VecDeque<u16>,
received: Vec<u16>,
}
impl Pattern {
/// A peer answering `words`, oldest first, then nothing.
#[must_use]
pub fn answering(words: impl IntoIterator<Item = u16>) -> Self {
Self {
answers: words.into_iter().collect(),
received: Vec::new(),
}
}
}
store!(Pattern);
impl Peer for Pattern {
fn receive(&mut self, word: u16, _context: &DeviceContext<'_>) {
self.received.push(word);
}
fn transmit(&mut self, _now: VirtualNanos) -> Option<u16> {
self.answers.pop_front()
}
fn received(&self) -> &[u16] {
&self.received
}
}
/// A frame on the wire, ending at its event.
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
struct Frame {
event: EventId,
kind: FrameKind,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
enum FrameKind {
Data(u16),
Idle,
Break,
}
/// A run of frames in one direction: where the last ends, and the fraction of a nanosecond carried.
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
struct Run {
ends: VirtualNanos,
carry: u64,
}
/// One serial port of an STM32F42xxx, RM0090 rev 22 chapter 30.
#[derive(Debug)]
pub struct Usart {
kind: Kind,
clock_hz: u64,
peer: Option<Place<dyn Peer>>,
status: u64,
holding: Option<u16>,
sending: Option<Frame>,
sent: VecDeque<u16>,
receiving: Option<Frame>,
received: u16,
idle: Option<EventId>,
idle_armed: bool,
/// What the last read of `SR` answered, until the access to `DR` that completes its sequence.
seen: Option<u64>,
errors: Errors,
outgoing: Option<Run>,
incoming: Option<Run>,
interrupt: Level,
requests: [Level; 2],
control: u64,
}
store!(Usart {
values: [
clock_hz, status, holding, sending, sent, receiving, received, idle, idle_armed, seen,
errors, outgoing, incoming, interrupt, requests, control,
],
parts: [peer],
built: [kind],
});
impl Usart {
/// The port of `kind` whose peripheral bus runs at `clock_hz`, which is what `BRR` divides,
/// until the part's tree drives it otherwise.
#[must_use]
pub fn with_clock(kind: Kind, clock_hz: u64) -> Self {
Self {
kind,
clock_hz,
peer: None,
status: sr::TXE.mask() | sr::TC.mask(),
holding: None,
sending: None,
sent: VecDeque::new(),
receiving: None,
received: 0,
idle: None,
idle_armed: false,
seen: None,
errors: Errors::default(),
outgoing: None,
incoming: None,
interrupt: Level::Low,
requests: [Level::Low; 2],
control: 0,
}
}
/// Hangs `peer` on the far end of the line, in place of whatever was there.
pub fn attach(&mut self, peer: impl Peer) {
self.attach_boxed(Box::new(peer));
}
/// Hangs a boxed peer, which is what a caller holding `dyn Peer` has.
pub fn attach_boxed(&mut self, peer: Box<dyn Peer>) {
self.peer = Some(Place::new(peer));
}
/// Takes the far end off `from`'s wire and hangs it on this one, silent and garbled if it was.
pub fn inherit(&mut self, from: &mut Self) {
self.peer = from.peer.take();
self.errors = from.errors;
}
/// How the line garbles what arrives on it, a board condition, for the board to set.
pub fn errors_mut(&mut self) -> &mut Errors {
&mut self.errors
}
/// Stops the far end answering, or lets it answer again: a board condition, [`Place::silence`].
/// It hears none of what the firmware sends, and what it says goes nowhere, as down a cut line.
/// Answers whether anything is on the far end.
#[must_use]
pub fn silence(&mut self, silent: bool) -> bool {
self.peer
.as_mut()
.map(|place| place.silence(silent))
.is_some()
}
/// The far end's place on the line, for a trigger to count and a one-shot fault to reach. Each
/// word it sends is a transaction.
pub fn place_mut(&mut self) -> Option<&mut dyn Placed> {
self.peer.as_mut().map(|place| -> &mut dyn Placed { place })
}
/// Whether anything is on the far end.
#[must_use]
pub fn attached(&self) -> bool {
self.peer.is_some()
}
/// The far end, to ask what it saw.
#[must_use]
pub fn peer(&self) -> Option<&dyn Peer> {
self.peer.as_ref().map(Place::chip)
}
/// The far end.
pub fn peer_mut(&mut self) -> Option<&mut dyn Peer> {
self.peer.as_mut().map(Place::chip_mut)
}
/// The far end as the type it was hung as, to drive what it measures. `None` where nothing
/// hangs there or another peer does. The board that hung the chip is what asks for it back.
pub fn chip_mut<P: Peer>(&mut self) -> Option<&mut P> {
let peer: &mut dyn Any = self.peer.as_mut()?.chip_mut();
peer.downcast_mut()
}
/// The words the port has most recently sent, oldest first.
#[must_use]
pub fn sent(&self) -> Vec<u16> {
self.sent.iter().copied().collect()
}
/// The words on the line from the far end, for a test that stands in for one without a peer:
/// what's written here is what the peer would have offered.
pub fn drive_rx(&mut self, words: impl IntoIterator<Item = u16>) {
self.attach(Pattern::answering(words));
}
/// The transmitter's and the receiver's enables, 30.6.4.
fn enabled(registers: &RegisterFile) -> (bool, bool) {
let value = registers.get(Register::Cr1);
let on = cr1::UE.is_set(value);
(on && cr1::TE.is_set(value), on && cr1::RE.is_set(value))
}
/// One bit in peripheral clock cycles, 30.3.4: `8 × (2 − OVER8) × USARTDIV`, which `BRR`
/// codes as a mantissa and a fraction of sixteenths or eighths. Zero is a baud generator with
/// nothing to divide by.
fn bit_cycles(registers: &RegisterFile) -> u64 {
let brr = registers.get(Register::Brr);
let mantissa = usart6::brr::DIV_MANTISSA.get(brr);
let fraction = usart6::brr::DIV_FRACTION.get(brr);
if cr1::OVER8.is_set(registers.get(Register::Cr1)) {
(mantissa << 3) | (fraction & 0b111)
} else {
(mantissa << 4) | fraction
}
}
/// The data bits of a frame, 30.6.4.
fn data_bits(registers: &RegisterFile) -> u32 {
if cr1::M.is_set(registers.get(Register::Cr1)) {
9
} else {
8
}
}
fn data_mask(registers: &RegisterFile) -> u16 {
if cr1::M.is_set(registers.get(Register::Cr1)) {
NINE_BITS
} else {
EIGHT_BITS
}
}
/// A frame in half bits: the start bit, the data, and `STOP`'s share. A break is its low bits
/// and the stop bits, 30.3.2.
fn half_bits(kind: FrameKind, registers: &RegisterFile) -> u64 {
let stop = STOP_HALF_BITS
[usize::try_from(cr2::STOP.get(registers.get(Register::Cr2))).expect("two bits")];
let data = u64::from(Self::data_bits(registers));
match kind {
FrameKind::Break => 2 * (data + 2) + stop,
FrameKind::Data(_) | FrameKind::Idle => 2 + 2 * data + stop,
}
}
/// When a frame of `kind` starting no earlier than `from` ends, continuing `run` where it
/// stands, and the run as it then stands.
fn frame_end(
&self,
run: Option<Run>,
from: VirtualNanos,
kind: FrameKind,
registers: &RegisterFile,
) -> Option<(VirtualNanos, Run)> {
let cycles = Self::bit_cycles(registers);
if cycles == 0 || self.clock_hz == 0 {
return None;
}
let (start, carry) = match run {
Some(run) if run.ends >= from => (run.ends, run.carry),
_ => (from, 0),
};
let numerator =
u128::from(Self::half_bits(kind, registers)) * u128::from(cycles) * NANOS_PER_SECOND
+ u128::from(carry);
let denominator = 2 * u128::from(self.clock_hz);
let nanos = u64::try_from(numerator / denominator).expect("a frame fits the virtual clock");
let carry = u64::try_from(numerator % denominator).expect("less than the denominator");
let ends = VirtualNanos(start.0.saturating_add(nanos));
Some((ends, Run { ends, carry }))
}
/// The word as it goes on the wire: `M`'s bits, and under `PCE` the parity in the most
/// significant one, even or odd by `PS`, 30.3.7.
fn framed(word: u16, registers: &RegisterFile) -> u16 {
let control = registers.get(Register::Cr1);
let mask = Self::data_mask(registers);
let word = word & mask;
if !cr1::PCE.is_set(control) {
return word;
}
let top = mask ^ (mask >> 1);
let ones = (word & (mask >> 1)).count_ones();
let odd = cr1::PS.is_set(control);
let parity = (ones % 2 == 1) != odd;
if parity { word | top } else { word & !top }
}
/// Puts a frame of `kind` on the wire, if the transmitter is enabled and the wire is free.
fn send(
&mut self,
kind: FrameKind,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) -> bool {
if self.sending.is_some() || !Self::enabled(registers).0 {
return false;
}
let Some((at, run)) = self.frame_end(self.outgoing, context.now(), kind, registers) else {
return false;
};
self.outgoing = Some(run);
self.sending = Some(Frame {
event: context.schedule(at),
kind,
});
true
}
/// Moves the word in `TDR`, or the break `SBK` asks for, onto a free wire, 30.3.2.
fn start_sending(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
if self.sending.is_some() {
return;
}
if cr1::SBK.is_set(registers.get(Register::Cr1))
&& self.send(FrameKind::Break, registers, context)
{
return;
}
let Some(word) = self.holding else {
return;
};
if self.send(FrameKind::Data(word), registers, context) {
self.holding = None;
self.status = sr::TXE.set(self.status, 1);
}
}
/// The frame on the wire has ended.
fn finish_sending(
&mut self,
kind: FrameKind,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
self.sending = None;
match kind {
FrameKind::Data(word) => {
if self.sent.len() == REMEMBERED {
self.sent.pop_front();
}
self.sent.push_back(word);
if let Some(peer) = self.peer.as_mut().and_then(Place::answering) {
peer.receive(word, context);
}
}
FrameKind::Break => {
let value = registers.get(Register::Cr1);
registers.set(Register::Cr1, cr1::SBK.set(value, 0));
}
FrameKind::Idle => {}
}
self.start_sending(registers, context);
if self.sending.is_none() {
self.outgoing = None;
if kind != FrameKind::Idle && sr::TXE.is_set(self.status) {
self.status = sr::TC.set(self.status, 1);
}
}
}
/// Asks the far end for a word to put on the wire, if the receiver is enabled and free, 30.3.3.
/// A silent far end's words are lost at every look, whatever the receiver is doing.
fn receive_next(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
let now = context.now();
if let Some(place) = &mut self.peer {
place.lose_while_silent(now);
}
if self.receiving.is_some() || !Self::enabled(registers).1 {
return;
}
// A word a drop takes is gone, and the one after it crosses in its place.
let Some(word) = self.peer.as_mut().and_then(|place| {
loop {
let word = place.answering()?.transmit(now)?;
if let Some(crossed) = place.sent(u32::from(word)) {
let crossed = crossed & u32::from(u16::MAX);
break Some(u16::try_from(crossed).expect("masked to a word"));
}
}
}) else {
self.arm_idle(registers, context);
return;
};
let Some((at, run)) = self.frame_end(self.incoming, now, FrameKind::Data(word), registers)
else {
return;
};
self.incoming = Some(run);
self.idle = None;
self.receiving = Some(Frame {
event: context.schedule(at),
kind: FrameKind::Data(word),
});
}
/// One idle frame after the last word with nothing arriving sets `IDLE` once per burst, 30.3.3.
fn arm_idle(&mut self, registers: &RegisterFile, context: &mut DeviceContext<'_>) {
if !self.idle_armed || self.idle.is_some() {
return;
}
let Some((at, _)) =
self.frame_end(self.incoming, context.now(), FrameKind::Idle, registers)
else {
return;
};
self.idle = Some(context.schedule(at));
}
/// The frame arriving has ended: `RDR` and `RXNE` with any error it came with, or `ORE` if the last
/// word is still there. How it came is drawn before the overrun is, so the pattern of errors
/// doesn't move with when the firmware reads.
fn finish_receiving(
&mut self,
word: u16,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
self.receiving = None;
let parity = cr1::PCE.is_set(registers.get(Register::Cr1));
let arrival = self
.errors
.next(Self::data_bits(registers) - u32::from(parity));
if sr::RXNE.is_set(self.status) {
self.status = sr::ORE.set(self.status, 1);
} else {
let one_sample = cr3::ONEBIT.is_set(registers.get(Register::Cr3));
self.received = Self::framed(word ^ arrival.flipped, registers);
self.status = sr::RXNE.set(self.status, 1);
if arrival.noise && !one_sample {
self.status |= sr::NF.mask();
}
if arrival.framing {
self.status |= sr::FE.mask();
}
self.idle_armed = true;
}
self.receive_next(registers, context);
if self.receiving.is_none() {
self.incoming = self.incoming.filter(|run| run.ends >= context.now());
}
}
/// `CR1`: the transmitter's enable and the idle frame it begins with, the receiver's enable,
/// and the break, 30.3.2, 30.3.3 and 30.6.4.
fn write_cr1(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
let value = registers.get(Register::Cr1);
let was = self.control;
self.control = value;
let (transmits, receives) = Self::enabled(registers);
let did = cr1::UE.is_set(was);
let transmitted = did && cr1::TE.is_set(was);
let received = did && cr1::RE.is_set(was);
if transmits && !cr1::TE.is_set(was) {
self.send(FrameKind::Idle, registers, context);
}
if !transmits && transmitted && cr1::UE.is_set(value) {
self.sending = None;
self.outgoing = None;
}
if !receives && received && cr1::UE.is_set(value) {
self.receiving = None;
self.incoming = None;
}
self.start_sending(registers, context);
self.receive_next(registers, context);
}
/// `SR`, 30.6.1: a written zero clears `CTS`, `LBD`, `TC` and `RXNE`. The rest are hardware's.
fn write_sr(&mut self, written: u64) {
self.status &= !(CLEARED_BY_ZERO & !written);
self.seen = None;
}
/// The line of table 147 and the two requests of 30.3.13, each following the flags that feed
/// it. The model keeps the level it last drove, so a change that changes nothing reports
/// nothing.
fn refresh_signals(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
registers.set(Register::Sr, self.status);
let status = self.status;
let control = registers.get(Register::Cr1);
let control2 = registers.get(Register::Cr2);
let control3 = registers.get(Register::Cr3);
for (output, enable, flag) in [
(RECEIVE_REQUEST, cr3::DMAR, sr::RXNE),
(TRANSMIT_REQUEST, cr3::DMAT, sr::TXE),
] {
let wanted = Level::from(enable.is_set(control3) && flag.is_set(status));
if wanted != self.requests[output] {
self.requests[output] = wanted;
context.request(output, wanted);
}
}
let errors = sr::FE.mask() | sr::ORE.mask() | sr::NF.mask();
let wanted = (cr1::TXEIE.is_set(control) && sr::TXE.is_set(status))
|| (cr1::TCIE.is_set(control) && sr::TC.is_set(status))
|| (cr1::RXNEIE.is_set(control) && (sr::RXNE.is_set(status) || sr::ORE.is_set(status)))
|| (cr1::IDLEIE.is_set(control) && sr::IDLE.is_set(status))
|| (cr1::PEIE.is_set(control) && sr::PE.is_set(status))
|| (cr2::LBDIE.is_set(control2) && sr::LBD.is_set(status))
|| (cr3::CTSIE.is_set(control3) && sr::CTS.is_set(status))
|| (cr3::EIE.is_set(control3) && cr3::DMAR.is_set(control3) && status & errors != 0);
let wanted = Level::from(wanted);
if wanted != self.interrupt {
self.interrupt = wanted;
context.interrupt(GLOBAL, wanted);
}
}
}
impl Device for Usart {
type Register = Register;
fn layout(&self) -> &'static [RegisterSpec] {
self.kind.layout()
}
/// The peripheral clock has changed. A frame on the wire keeps its end. The runs' carries were
/// a fraction of the old rate and mean nothing at the new one.
fn on_clock(&mut self, hz: u64, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
self.clock_hz = hz;
// ceiling: a frame on the wire when the clock stops is dropped, not frozen; upgrade when a
// firmware gates a port mid-frame and waits for the rest of the word.
if hz == 0 {
self.sending = None;
self.receiving = None;
self.idle = None;
self.outgoing = None;
self.incoming = None;
return;
}
for run in [&mut self.outgoing, &mut self.incoming]
.into_iter()
.flatten()
{
run.carry = 0;
}
// A word waiting in `TDR` goes out once the clock is back.
self.start_sending(registers, context);
self.refresh_signals(registers, context);
}
/// `SR` answers the flags and arms the sequences a read of `DR` completes. `DR` answers `RDR`,
/// clears `RXNE`, and completes them, 30.6.1 and 30.6.2.
fn read(
&mut self,
register: Register,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) -> Option<u64> {
match register {
Register::Sr => {
self.seen = Some(self.status);
Some(self.status)
}
Register::Dr => {
self.status = sr::RXNE.set(self.status, 0);
if let Some(seen) = self.seen.take() {
self.status &= !(CLEARED_BY_READING | (ERRORS_CLEARED_WHEN_SEEN & seen));
}
self.receive_next(registers, context);
self.refresh_signals(registers, context);
Some(u64::from(self.received))
}
_ => None,
}
}
/// `DR` is the word a read would take. `SR` reads as it stands, with no sequence armed.
fn peek(
&self,
register: Register,
_registers: &RegisterFile,
_now: VirtualNanos,
) -> Option<u64> {
(register == Register::Dr).then(|| u64::from(self.received))
}
fn write(
&mut self,
register: Register,
value: u64,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
match register {
Register::Dr => {
self.status = sr::TXE.set(self.status, 0);
if self.seen.take().is_some() {
self.status &= !CLEARED_BY_WRITING;
}
let word = u16::try_from(value & u64::from(NINE_BITS)).expect("nine bits");
self.holding = Some(Self::framed(word, registers));
self.start_sending(registers, context);
}
Register::Sr => self.write_sr(value),
Register::Cr1 => self.write_cr1(registers, context),
_ => {}
}
self.refresh_signals(registers, context);
}
fn on_event(
&mut self,
event: EventId,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
// An instant fires once per instance, so one event can end both frames and the idle line.
let mut ended = false;
if let Some(frame) = self.sending.filter(|frame| frame.event == event) {
self.finish_sending(frame.kind, registers, context);
ended = true;
}
if let Some(frame) = self.receiving.filter(|frame| frame.event == event)
&& let FrameKind::Data(word) = frame.kind
{
self.finish_receiving(word, registers, context);
ended = true;
}
if self.idle == Some(event) {
self.idle = None;
self.idle_armed = false;
self.status = sr::IDLE.set(self.status, 1);
ended = true;
}
if ended {
self.refresh_signals(registers, context);
}
}
/// At every stop the far end is asked again, which is how a host's byte arrives.
fn poll(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
if self.peer.is_none() || self.receiving.is_some() {
return;
}
self.receive_next(registers, context);
self.refresh_signals(registers, context);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::soc::Layout;
/// The `uart4` table is the `usart6` table without the fields chapter 30 leaves out of UART4
/// and UART5, so a field the model reads through the one is at the same bit in the other.
#[test]
fn the_shared_fields_sit_at_the_same_bits_in_both_layouts() {
for (row, register) in uart4::REGISTERS.iter().enumerate() {
let full = &usart6::REGISTERS[row];
assert_eq!(register.name, full.name);
assert_eq!(register.offset, full.offset);
for field in register.fields {
let shared = full
.fields
.iter()
.find(|candidate| candidate.name == field.name)
.unwrap_or_else(|| panic!("{} has no {}", full.name, field.name));
assert_eq!(field.bit, shared.bit, "{}.{}", register.name, field.name);
assert_eq!(
field.width, shared.width,
"{}.{}",
register.name, field.name
);
}
}
assert_eq!(uart4::Register::Cr3.index(), Register::Cr3.index());
assert_eq!(usart6::REGISTERS.len(), uart4::REGISTERS.len() + 1);
}
}