pub mod adc;
pub mod dbg;
pub mod dma;
pub mod flash;
pub mod gpio;
pub mod i2c;
pub mod iwdg;
pub mod otg;
pub mod pwr;
pub mod rcc;
pub mod rtc;
pub mod sdio;
pub mod spi;
pub mod tim;
pub mod usart;
use std::num::NonZeroU32;
use crate::device::{DeviceContext, ErasedDevice, RegisterFile, ResetCause};
use crate::mcu::Mcu;
use crate::snapshot::store;
use crate::soc::{Bus, Error, FieldSpec, Layout};
use crate::time::VirtualNanos;
use crate::cpu::armv7m::Dwt;
use adc::{Adc, AdcCommon};
use dbg::Dbg;
use dma::{Dma, Wire};
use flash::Flash;
use gpio::Gpio;
use i2c::I2c;
use iwdg::Iwdg;
use otg::Otg;
use pwr::Pwr;
use rcc::Rcc;
use rtc::Rtc;
use sdio::Sdio;
use spi::Spi;
use tim::{Advanced, Counter, General, Tim};
use usart::{Kind, Usart};
const RESET_BUS_HZ: u64 = rcc::HSI_HZ;
#[derive(Debug)]
pub struct Stm32f427 {
pub rcc: Rcc,
pub pwr: Pwr,
pub gpio: [Gpio; PORTS.len()],
pub spi: [Spi; SERIAL_INTERFACES.len()],
pub usart: [Usart; SERIAL_PORTS.len()],
pub i2c: [I2c; I2C_INTERFACES.len()],
pub flash: Flash,
pub adc: [Adc; CONVERTERS.len()],
pub adc_common: AdcCommon,
pub tim: [Tim<General>; TIMERS.len()],
pub advanced: [Tim<Advanced>; ADVANCED_TIMERS.len()],
pub dma: [Dma; CONTROLLERS.len()],
pub otg: Otg,
pub sdio: Sdio,
pub rtc: Rtc,
pub dbg: Dbg,
pub iwdg: Iwdg,
pub dwt: Dwt,
bus: Bus,
wiring: Box<[Field]>,
slots: Slots,
}
#[derive(Debug, Clone, Copy)]
struct Slots {
rcc: usize,
pwr: usize,
gpio: [usize; PORTS.len()],
spi: [usize; SERIAL_INTERFACES.len()],
usart: [usize; SERIAL_PORTS.len()],
i2c: [usize; I2C_INTERFACES.len()],
flash: usize,
adc: [usize; CONVERTERS.len()],
adc_common: usize,
tim: [usize; TIMERS.len()],
advanced: [usize; ADVANCED_TIMERS.len()],
dma: [usize; CONTROLLERS.len()],
otg: usize,
sdio: usize,
rtc: usize,
dbg: usize,
iwdg: usize,
dwt: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Field {
None,
Rcc,
Pwr,
Gpio(usize),
Spi(usize),
Usart(usize),
I2c(usize),
Flash,
Adc(usize),
AdcCommon,
Tim(usize),
Advanced(usize),
Dma(usize),
Otg,
Sdio,
Rtc,
Dbg,
Iwdg,
Dwt,
}
impl Field {
fn all() -> Vec<Self> {
let mut all = vec![Self::Rcc, Self::Pwr];
all.extend((0..PORTS.len()).map(Self::Gpio));
all.extend((0..SERIAL_INTERFACES.len()).map(Self::Spi));
all.extend((0..SERIAL_PORTS.len()).map(Self::Usart));
all.extend((0..I2C_INTERFACES.len()).map(Self::I2c));
all.push(Self::Flash);
all.extend((0..CONVERTERS.len()).map(Self::Adc));
all.push(Self::AdcCommon);
all.extend((0..TIMERS.len()).map(Self::Tim));
all.extend((0..ADVANCED_TIMERS.len()).map(Self::Advanced));
all.extend((0..CONTROLLERS.len()).map(Self::Dma));
all.push(Self::Otg);
all.push(Self::Sdio);
all.push(Self::Rtc);
all.push(Self::Dbg);
all.push(Self::Iwdg);
all.push(Self::Dwt);
all
}
fn instance(self) -> &'static str {
match self {
Self::None => unreachable!("the unmodelled slot answers for no instance"),
Self::Rcc => "RCC",
Self::Pwr => "PWR",
Self::Gpio(nth) => PORTS[nth],
Self::Spi(nth) => SERIAL_INTERFACES[nth],
Self::Usart(nth) => SERIAL_PORTS[nth].instance,
Self::I2c(nth) => I2C_INTERFACES[nth],
Self::Flash => "FLASH",
Self::Adc(nth) => CONVERTERS[nth],
Self::AdcCommon => ADC_COMMON,
Self::Tim(nth) => TIMERS[nth].instance,
Self::Advanced(nth) => ADVANCED_TIMERS[nth].instance,
Self::Dma(nth) => CONTROLLERS[nth].instance,
Self::Otg => "OTG_FS_GLOBAL",
Self::Sdio => "SDIO",
Self::Rtc => "RTC",
Self::Dbg => "DBG",
Self::Iwdg => "IWDG",
Self::Dwt => crate::cpu::armv7m::DWT,
}
}
}
pub const SERIAL_INTERFACES: [&str; 6] = ["SPI1", "SPI2", "SPI3", "SPI4", "SPI5", "SPI6"];
pub const I2C_INTERFACES: [&str; 3] = ["I2C1", "I2C2", "I2C3"];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SerialPort {
pub instance: &'static str,
pub kind: Kind,
}
pub const CONVERTERS: [&str; 3] = ["ADC1", "ADC2", "ADC3"];
pub const ADC_COMMON: &str = "C_ADC";
pub const SERIAL_PORTS: [SerialPort; 8] = [
SerialPort {
instance: "USART1",
kind: Kind::Usart,
},
SerialPort {
instance: "USART2",
kind: Kind::Usart,
},
SerialPort {
instance: "USART3",
kind: Kind::Usart,
},
SerialPort {
instance: "USART6",
kind: Kind::Usart,
},
SerialPort {
instance: "UART7",
kind: Kind::Usart,
},
SerialPort {
instance: "UART8",
kind: Kind::Usart,
},
SerialPort {
instance: "UART4",
kind: Kind::Uart,
},
SerialPort {
instance: "UART5",
kind: Kind::Uart,
},
];
#[derive(Debug, Clone, Copy)]
pub struct Timer {
pub instance: &'static str,
pub counter: Counter,
}
pub const TIMERS: [Timer; 4] = [
Timer {
instance: "TIM2",
counter: Counter::Wide,
},
Timer {
instance: "TIM3",
counter: Counter::Narrow,
},
Timer {
instance: "TIM4",
counter: Counter::Narrow,
},
Timer {
instance: "TIM5",
counter: Counter::Wide,
},
];
pub const ADVANCED_TIMERS: [Timer; 2] = [
Timer {
instance: "TIM1",
counter: Counter::Narrow,
},
Timer {
instance: "TIM8",
counter: Counter::Narrow,
},
];
#[derive(Debug)]
pub struct Controller {
pub instance: &'static str,
pub wiring: &'static [Wire],
pub last_data: &'static [Wire],
pub reaches_memory: bool,
}
pub static CONTROLLERS: [Controller; 2] = [
Controller {
instance: "DMA1",
wiring: &DMA1_REQUESTS,
last_data: &[],
reaches_memory: false,
},
Controller {
instance: "DMA2",
wiring: &DMA2_REQUESTS,
last_data: &DMA2_LAST_DATA,
reaches_memory: true,
},
];
static DMA2_LAST_DATA: [Wire; 1] = [Wire {
instance: "SDIO",
request: sdio::LAST_DATA_REQUEST,
channel: 4,
streams: &[3, 6],
}];
static DMA1_REQUESTS: [Wire; 23] = [
Wire {
instance: "I2C1",
request: i2c::RECEIVE_REQUEST,
channel: 1,
streams: &[0, 5],
},
Wire {
instance: "I2C1",
request: i2c::TRANSMIT_REQUEST,
channel: 1,
streams: &[6, 7],
},
Wire {
instance: "I2C3",
request: i2c::RECEIVE_REQUEST,
channel: 3,
streams: &[2],
},
Wire {
instance: "I2C3",
request: i2c::TRANSMIT_REQUEST,
channel: 3,
streams: &[4],
},
Wire {
instance: "I2C2",
request: i2c::RECEIVE_REQUEST,
channel: 7,
streams: &[2, 3],
},
Wire {
instance: "I2C2",
request: i2c::TRANSMIT_REQUEST,
channel: 7,
streams: &[7],
},
Wire {
instance: "UART5",
request: usart::RECEIVE_REQUEST,
channel: 4,
streams: &[0],
},
Wire {
instance: "USART3",
request: usart::RECEIVE_REQUEST,
channel: 4,
streams: &[1],
},
Wire {
instance: "UART4",
request: usart::RECEIVE_REQUEST,
channel: 4,
streams: &[2],
},
Wire {
instance: "USART3",
request: usart::TRANSMIT_REQUEST,
channel: 4,
streams: &[3],
},
Wire {
instance: "UART4",
request: usart::TRANSMIT_REQUEST,
channel: 4,
streams: &[4],
},
Wire {
instance: "USART2",
request: usart::RECEIVE_REQUEST,
channel: 4,
streams: &[5],
},
Wire {
instance: "USART2",
request: usart::TRANSMIT_REQUEST,
channel: 4,
streams: &[6],
},
Wire {
instance: "UART5",
request: usart::TRANSMIT_REQUEST,
channel: 4,
streams: &[7],
},
Wire {
instance: "UART8",
request: usart::TRANSMIT_REQUEST,
channel: 5,
streams: &[0],
},
Wire {
instance: "UART7",
request: usart::TRANSMIT_REQUEST,
channel: 5,
streams: &[1],
},
Wire {
instance: "UART7",
request: usart::RECEIVE_REQUEST,
channel: 5,
streams: &[3],
},
Wire {
instance: "UART8",
request: usart::RECEIVE_REQUEST,
channel: 5,
streams: &[6],
},
Wire {
instance: "USART3",
request: usart::TRANSMIT_REQUEST,
channel: 7,
streams: &[4],
},
Wire {
instance: "SPI3",
request: spi::RECEIVE_REQUEST,
channel: 0,
streams: &[0, 2],
},
Wire {
instance: "SPI3",
request: spi::TRANSMIT_REQUEST,
channel: 0,
streams: &[5, 7],
},
Wire {
instance: "SPI2",
request: spi::RECEIVE_REQUEST,
channel: 0,
streams: &[3],
},
Wire {
instance: "SPI2",
request: spi::TRANSMIT_REQUEST,
channel: 0,
streams: &[4],
},
];
static DMA2_REQUESTS: [Wire; 20] = [
Wire {
instance: "ADC1",
request: adc::DMA_REQUEST,
channel: 0,
streams: &[0, 4],
},
Wire {
instance: "ADC2",
request: adc::DMA_REQUEST,
channel: 1,
streams: &[2, 3],
},
Wire {
instance: "ADC3",
request: adc::DMA_REQUEST,
channel: 2,
streams: &[0, 1],
},
Wire {
instance: "USART1",
request: usart::RECEIVE_REQUEST,
channel: 4,
streams: &[2, 5],
},
Wire {
instance: "USART1",
request: usart::TRANSMIT_REQUEST,
channel: 4,
streams: &[7],
},
Wire {
instance: "USART6",
request: usart::RECEIVE_REQUEST,
channel: 5,
streams: &[1, 2],
},
Wire {
instance: "USART6",
request: usart::TRANSMIT_REQUEST,
channel: 5,
streams: &[6, 7],
},
Wire {
instance: "SDIO",
request: sdio::TRANSFER_REQUEST,
channel: 4,
streams: &[3, 6],
},
Wire {
instance: "SPI6",
request: spi::TRANSMIT_REQUEST,
channel: 1,
streams: &[5],
},
Wire {
instance: "SPI6",
request: spi::RECEIVE_REQUEST,
channel: 1,
streams: &[6],
},
Wire {
instance: "SPI5",
request: spi::RECEIVE_REQUEST,
channel: 2,
streams: &[3],
},
Wire {
instance: "SPI5",
request: spi::TRANSMIT_REQUEST,
channel: 2,
streams: &[4],
},
Wire {
instance: "SPI1",
request: spi::RECEIVE_REQUEST,
channel: 3,
streams: &[0, 2],
},
Wire {
instance: "SPI1",
request: spi::TRANSMIT_REQUEST,
channel: 3,
streams: &[3, 5],
},
Wire {
instance: "SPI4",
request: spi::RECEIVE_REQUEST,
channel: 4,
streams: &[0],
},
Wire {
instance: "SPI4",
request: spi::TRANSMIT_REQUEST,
channel: 4,
streams: &[1],
},
Wire {
instance: "SPI4",
request: spi::RECEIVE_REQUEST,
channel: 5,
streams: &[3],
},
Wire {
instance: "SPI4",
request: spi::TRANSMIT_REQUEST,
channel: 5,
streams: &[4],
},
Wire {
instance: "SPI5",
request: spi::RECEIVE_REQUEST,
channel: 7,
streams: &[5],
},
Wire {
instance: "SPI5",
request: spi::TRANSMIT_REQUEST,
channel: 7,
streams: &[6],
},
];
pub const PORTS: [&str; 11] = [
"GPIOA", "GPIOB", "GPIOC", "GPIOD", "GPIOE", "GPIOF", "GPIOG", "GPIOH", "GPIOI", "GPIOJ",
"GPIOK",
];
pub fn declared_fields() -> impl Iterator<Item = &'static FieldSpec> {
[rcc::DECLARED, pwr::DECLARED, dbg::DECLARED, flash::DECLARED]
.into_iter()
.flatten()
.copied()
}
store!(Stm32f427 {
values: [
rcc, pwr, gpio, flash, adc, adc_common, tim, advanced, otg, rtc, dbg, iwdg, dwt,
],
parts: [spi, usart, i2c, dma, sdio, bus],
built: [wiring, slots],
});
impl Stm32f427 {
pub fn new() -> Result<Self, Error> {
let bus = super::memory_map::builder().build()?;
let mut wiring = vec![Field::None; bus.slots().len()].into_boxed_slice();
let mut slots = Slots {
rcc: 0,
pwr: 0,
gpio: [0; PORTS.len()],
spi: [0; SERIAL_INTERFACES.len()],
usart: [0; SERIAL_PORTS.len()],
i2c: [0; I2C_INTERFACES.len()],
flash: 0,
adc: [0; CONVERTERS.len()],
adc_common: 0,
tim: [0; TIMERS.len()],
advanced: [0; ADVANCED_TIMERS.len()],
dma: [0; CONTROLLERS.len()],
otg: 0,
sdio: 0,
rtc: 0,
dbg: 0,
iwdg: 0,
dwt: 0,
};
for field in Field::all() {
let instance = field.instance();
let slot = bus
.slot_of(instance)
.ok_or(Error::UnknownInstance { instance })?;
wiring[slot] = field;
match field {
Field::Rcc => slots.rcc = slot,
Field::Pwr => slots.pwr = slot,
Field::Gpio(nth) => slots.gpio[nth] = slot,
Field::Spi(nth) => slots.spi[nth] = slot,
Field::Usart(nth) => slots.usart[nth] = slot,
Field::I2c(nth) => slots.i2c[nth] = slot,
Field::Flash => slots.flash = slot,
Field::Adc(nth) => slots.adc[nth] = slot,
Field::AdcCommon => slots.adc_common = slot,
Field::Tim(nth) => slots.tim[nth] = slot,
Field::Advanced(nth) => slots.advanced[nth] = slot,
Field::Dma(nth) => slots.dma[nth] = slot,
Field::Otg => slots.otg = slot,
Field::Sdio => slots.sdio = slot,
Field::Rtc => slots.rtc = slot,
Field::Dbg => slots.dbg = slot,
Field::Iwdg => slots.iwdg = slot,
Field::Dwt => slots.dwt = slot,
Field::None => unreachable!("Field::all names no unmodelled slot"),
}
}
let adc_common = AdcCommon::with_clock(RESET_BUS_HZ);
let mut mcu = Self {
rcc: Rcc::new(),
pwr: Pwr::new(),
gpio: std::array::from_fn(|_| Gpio::default()),
spi: std::array::from_fn(|_| Spi::with_clock(RESET_BUS_HZ)),
usart: std::array::from_fn(|nth| {
Usart::with_clock(SERIAL_PORTS[nth].kind, RESET_BUS_HZ)
}),
i2c: std::array::from_fn(|_| I2c::with_clock(RESET_BUS_HZ)),
flash: Flash::with_clock(RESET_BUS_HZ),
adc: std::array::from_fn(|_| Adc::with_clock(adc_common.driven_hz())),
adc_common,
tim: std::array::from_fn(|nth| Tim::with_clock(RESET_BUS_HZ, TIMERS[nth].counter)),
advanced: std::array::from_fn(|nth| {
Tim::with_clock(RESET_BUS_HZ, ADVANCED_TIMERS[nth].counter)
.breaks_on("RCC", rcc::CLOCK_FAILURE)
}),
dma: std::array::from_fn(|nth| {
Dma::new(
CONTROLLERS[nth].wiring,
CONTROLLERS[nth].last_data,
CONTROLLERS[nth].reaches_memory,
)
}),
otg: Otg,
sdio: Sdio::with_clock(0),
rtc: Rtc,
dbg: Dbg,
iwdg: Iwdg::new(),
dwt: Dwt::with_clock(RESET_BUS_HZ),
bus,
wiring,
slots,
};
mcu.check()?;
Ok(mcu)
}
}
fn with_registers<'a, M>(
model: &'a mut M,
bus: &'a mut Bus,
slot: usize,
) -> (&'a mut M, &'a mut RegisterFile) {
(model, bus.slot_mut(slot).registers_mut())
}
fn driving<M, T>(
model: &mut M,
bus: &mut Bus,
slot: usize,
at: VirtualNanos,
input: impl FnOnce(&mut M, &mut RegisterFile, &mut DeviceContext<'_>) -> T,
) -> T {
let (registers, mut context) = bus.drive(at, slot);
input(model, registers, &mut context)
}
fn keep<L: Layout>(
old: &Bus,
fresh: &mut Bus,
slot: usize,
registers: impl IntoIterator<Item = L>,
) {
for register in registers {
let value = old.slots()[slot].registers().get(register);
fresh.slot_mut(slot).registers_mut().set(register, value);
}
}
impl Stm32f427 {
pub fn clocks(&mut self) -> (&mut Rcc, &mut RegisterFile) {
let Self {
rcc, bus, slots, ..
} = self;
with_registers(rcc, bus, slots.rcc)
}
pub fn drive_clocks<T>(
&mut self,
at: VirtualNanos,
input: impl FnOnce(&mut Rcc, &mut RegisterFile, &mut DeviceContext<'_>) -> T,
) -> T {
let Self {
rcc, bus, slots, ..
} = self;
driving(rcc, bus, slots.rcc, at, input)
}
pub fn power(&mut self) -> (&mut Pwr, &mut RegisterFile) {
let Self {
pwr, bus, slots, ..
} = self;
with_registers(pwr, bus, slots.pwr)
}
pub fn set_supply(&mut self, millivolts: u32) {
let (power, registers) = self.power();
power.set_supply(millivolts, registers);
if let Some(millivolts) = NonZeroU32::new(millivolts) {
self.flash.set_supply(millivolts);
}
}
pub fn drive_power<T>(
&mut self,
at: VirtualNanos,
input: impl FnOnce(&mut Pwr, &mut RegisterFile, &mut DeviceContext<'_>) -> T,
) -> T {
let Self {
pwr, bus, slots, ..
} = self;
driving(pwr, bus, slots.pwr, at, input)
}
pub fn port(&mut self, nth: usize) -> (&mut Gpio, &mut RegisterFile) {
let Self {
gpio, bus, slots, ..
} = self;
with_registers(&mut gpio[nth], bus, slots.gpio[nth])
}
pub fn drive_port<T>(
&mut self,
at: VirtualNanos,
nth: usize,
input: impl FnOnce(&mut Gpio, &mut RegisterFile, &mut DeviceContext<'_>) -> T,
) -> T {
let Self {
gpio, bus, slots, ..
} = self;
driving(&mut gpio[nth], bus, slots.gpio[nth], at, input)
}
pub fn serial(&mut self, nth: usize) -> (&mut Spi, &mut RegisterFile) {
let Self {
spi, bus, slots, ..
} = self;
with_registers(&mut spi[nth], bus, slots.spi[nth])
}
pub fn drive_serial<T>(
&mut self,
at: VirtualNanos,
nth: usize,
input: impl FnOnce(&mut Spi, &mut RegisterFile, &mut DeviceContext<'_>) -> T,
) -> T {
let Self {
spi, bus, slots, ..
} = self;
driving(&mut spi[nth], bus, slots.spi[nth], at, input)
}
pub fn serial_port(&mut self, nth: usize) -> (&mut Usart, &mut RegisterFile) {
let Self {
usart, bus, slots, ..
} = self;
with_registers(&mut usart[nth], bus, slots.usart[nth])
}
pub fn drive_serial_port<T>(
&mut self,
at: VirtualNanos,
nth: usize,
input: impl FnOnce(&mut Usart, &mut RegisterFile, &mut DeviceContext<'_>) -> T,
) -> T {
let Self {
usart, bus, slots, ..
} = self;
driving(&mut usart[nth], bus, slots.usart[nth], at, input)
}
pub fn converter(&mut self, nth: usize) -> (&mut Adc, &mut RegisterFile) {
let Self {
adc, bus, slots, ..
} = self;
with_registers(&mut adc[nth], bus, slots.adc[nth])
}
pub fn timer(&mut self, nth: usize) -> (&mut Tim<General>, &mut RegisterFile) {
let Self {
tim, bus, slots, ..
} = self;
with_registers(&mut tim[nth], bus, slots.tim[nth])
}
pub fn drive_timer<T>(
&mut self,
at: VirtualNanos,
nth: usize,
input: impl FnOnce(&mut Tim<General>, &mut RegisterFile, &mut DeviceContext<'_>) -> T,
) -> T {
let Self {
tim, bus, slots, ..
} = self;
driving(&mut tim[nth], bus, slots.tim[nth], at, input)
}
pub fn advanced_timer(&mut self, nth: usize) -> (&mut Tim<Advanced>, &mut RegisterFile) {
let Self {
advanced,
bus,
slots,
..
} = self;
with_registers(&mut advanced[nth], bus, slots.advanced[nth])
}
pub fn drive_advanced_timer<T>(
&mut self,
at: VirtualNanos,
nth: usize,
input: impl FnOnce(&mut Tim<Advanced>, &mut RegisterFile, &mut DeviceContext<'_>) -> T,
) -> T {
let Self {
advanced,
bus,
slots,
..
} = self;
driving(&mut advanced[nth], bus, slots.advanced[nth], at, input)
}
pub fn controller(&mut self, nth: usize) -> (&mut Dma, &mut RegisterFile) {
let Self {
dma, bus, slots, ..
} = self;
with_registers(&mut dma[nth], bus, slots.dma[nth])
}
pub fn drive_controller<T>(
&mut self,
at: VirtualNanos,
nth: usize,
input: impl FnOnce(&mut Dma, &mut RegisterFile, &mut DeviceContext<'_>) -> T,
) -> T {
let Self {
dma, bus, slots, ..
} = self;
driving(&mut dma[nth], bus, slots.dma[nth], at, input)
}
pub fn usb(&mut self) -> (&mut Otg, &mut RegisterFile) {
let Self {
otg, bus, slots, ..
} = self;
with_registers(otg, bus, slots.otg)
}
pub fn cards(&mut self) -> (&mut Sdio, &mut RegisterFile) {
let Self {
sdio, bus, slots, ..
} = self;
with_registers(sdio, bus, slots.sdio)
}
pub fn program(&mut self) -> (&mut Flash, &mut RegisterFile) {
let Self {
flash, bus, slots, ..
} = self;
with_registers(flash, bus, slots.flash)
}
}
impl Mcu for Stm32f427 {
fn bus(&self) -> &Bus {
&self.bus
}
fn reset(&mut self, cause: ResetCause) {
let mut fresh = Self::new().expect("a part that assembled once assembles again");
let flags = self.bus.slots()[self.slots.rcc]
.registers()
.get(super::generated::rcc::Register::Csr);
let (clocks, registers) = fresh.clocks();
clocks.restarted(cause, flags, registers);
let regulator = self.bus.slots()[self.slots.pwr]
.registers()
.get(super::generated::pwr::Register::Csr);
let (power, registers) = fresh.power();
power.keep_backup_regulator(regulator, registers);
let (hse_hz, lse_hz) = self.rcc.crystals();
fresh.rcc.attach_crystals(hse_hz, lse_hz);
fresh.rcc.calibrate_lsi(self.rcc.lsi_hz());
fresh.set_supply(self.pwr.supply());
for (fresh, old) in fresh.spi.iter_mut().zip(&mut self.spi) {
fresh.inherit(old);
}
for (fresh, old) in fresh.usart.iter_mut().zip(&mut self.usart) {
fresh.inherit(old);
}
for (fresh, old) in fresh.i2c.iter_mut().zip(&mut self.i2c) {
fresh.inherit(old);
}
for (fresh, old) in fresh.adc.iter_mut().zip(&self.adc) {
fresh.inherit(old);
}
fresh.sdio.inherit(&mut self.sdio);
let (optcr, optcr1) = self.flash.committed_options();
let (flash, registers) = fresh.program();
flash.keep_options(optcr, optcr1, registers);
keep(
&self.bus,
&mut fresh.bus,
self.slots.rtc,
super::generated::rtc::Register::ALL,
);
keep(
&self.bus,
&mut fresh.bus,
self.slots.rcc,
[super::generated::rcc::Register::Bdcr],
);
keep(
&self.bus,
&mut fresh.bus,
self.slots.dbg,
[
super::generated::dbg::Register::DbgmcuCr,
super::generated::dbg::Register::DbgmcuApb1Fz,
super::generated::dbg::Register::DbgmcuApb2Fz,
],
);
*self = fresh;
}
fn poll(&mut self, now: VirtualNanos) {
for slot in 0..self.wiring.len() {
let Field::Usart(nth) = self.wiring[slot] else {
continue;
};
let (carried, mut context) = self.bus.call(now, slot);
self.usart[nth].poll(carried.registers_mut(), &mut context);
}
}
fn split(&mut self, slot: usize) -> (&mut Bus, Option<&mut dyn ErasedDevice>) {
let Self {
rcc,
pwr,
gpio,
spi,
usart,
i2c,
flash,
adc,
adc_common,
tim,
advanced,
dma,
otg,
sdio,
rtc,
dbg,
iwdg,
dwt,
bus,
wiring,
..
} = self;
let model: Option<&mut dyn ErasedDevice> = match wiring[slot] {
Field::None => None,
Field::Rcc => Some(rcc),
Field::Pwr => Some(pwr),
Field::Gpio(nth) => Some(&mut gpio[nth]),
Field::Spi(nth) => Some(&mut spi[nth]),
Field::Usart(nth) => Some(&mut usart[nth]),
Field::I2c(nth) => Some(&mut i2c[nth]),
Field::Flash => Some(flash),
Field::Adc(nth) => Some(&mut adc[nth]),
Field::AdcCommon => Some(adc_common),
Field::Tim(nth) => Some(&mut tim[nth]),
Field::Advanced(nth) => Some(&mut advanced[nth]),
Field::Dma(nth) => Some(&mut dma[nth]),
Field::Otg => Some(otg),
Field::Sdio => Some(sdio),
Field::Rtc => Some(rtc),
Field::Dbg => Some(dbg),
Field::Iwdg => Some(iwdg),
Field::Dwt => Some(dwt),
};
(bus, model)
}
fn bus_mut(&mut self) -> &mut Bus {
&mut self.bus
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::BTreeMap;
use crate::soc::{Peripheral, RegisterSpec};
fn instances_of(layout: &'static [RegisterSpec]) -> impl Iterator<Item = &'static Peripheral> {
super::super::generated::PERIPHERALS
.iter()
.filter(move |peripheral| std::ptr::eq(peripheral.registers, layout))
}
fn a_part_with_a_board_on_it() -> Stm32f427 {
use crate::chip::{memory_card, sd, uart};
use crate::soc::stm32f427::generated::dbg::{
Register as DbgRegister, dbgmcu_apb1_fz, dbgmcu_apb2_fz, dbgmcu_cr,
};
use crate::soc::stm32f427::generated::pwr::{
Register as PwrRegister, cr as pwr_cr, csr as pwr_csr,
};
use crate::soc::stm32f427::generated::rcc::{
Register as RccRegister, apb1enr, apb2enr, bdcr,
};
use crate::soc::stm32f427::generated::rtc::Register as RtcRegister;
use crate::soc::stm32f427::generated::tim2::{Register as TimRegister, cr1};
use crate::soc::stm32f427::generated::{DBG, RCC, TIM2};
use crate::soc::{Layout, Width};
let mut mcu = Stm32f427::new().expect("the part assembles");
mcu.rcc.attach_crystals(24_000_000, 32_768);
mcu.rcc.calibrate_lsi(29_000);
mcu.set_supply(3300);
mcu.spi[0].attach(spi::Pattern::default());
mcu.usart[1].attach(usart::Pattern::default());
mcu.usart[6].attach(uart::Host::listen(0).expect("a port is free"));
mcu.sdio
.attach(memory_card::MemoryCard::holding(&[0; sd::BLOCK]));
mcu.adc[0].set_reference(3300);
mcu.adc[0].set_input(adc::Channel::new(2).expect("a pin channel"), 1650);
let at = VirtualNanos(0);
let register = |base: u64, offset: u32| base + u64::from(offset);
let enables = register(RCC.base, RccRegister::Apb2enr.spec().offset);
mcu.write(at, enables, Width::W32, apb2enr::ADC1EN.mask())
.expect("the enable takes the write");
let (rtc, rcc, pwr) = (mcu.slots.rtc, mcu.slots.rcc, mcu.slots.pwr);
mcu.bus_mut()
.slot_mut(pwr)
.registers_mut()
.set(PwrRegister::Cr, pwr_cr::DBP.mask());
mcu.bus_mut()
.slot_mut(pwr)
.registers_mut()
.set(PwrRegister::Csr, pwr_csr::BRE.mask() | pwr_csr::BRR.mask());
mcu.bus_mut()
.slot_mut(rtc)
.registers_mut()
.set(RtcRegister::Bkp19r, 0x5EB0_0755);
mcu.bus_mut()
.slot_mut(rcc)
.registers_mut()
.set(RccRegister::Bdcr, bdcr::RTCEN.mask() | bdcr::LSEON.mask());
mcu.write(
at,
register(RCC.base, RccRegister::Apb1enr.spec().offset),
Width::W32,
apb1enr::TIM2EN.mask(),
)
.expect("the enable takes the write");
mcu.write(
at,
register(TIM2.base, TimRegister::Cr1.spec().offset),
Width::W32,
cr1::CEN.mask(),
)
.expect("the timer takes the write");
assert!(
mcu.next_event().is_some(),
"the running timer scheduled its update"
);
mcu.write(
at,
register(DBG.base, DbgRegister::DbgmcuCr.spec().offset),
Width::W32,
dbgmcu_cr::DBG_STOP.mask(),
)
.expect("the debug component takes the write");
mcu.write(
at,
register(DBG.base, DbgRegister::DbgmcuApb1Fz.spec().offset),
Width::W32,
dbgmcu_apb1_fz::DBG_TIM5_STOP.mask(),
)
.expect("the debug component takes the write");
mcu.write(
at,
register(DBG.base, DbgRegister::DbgmcuApb2Fz.spec().offset),
Width::W32,
dbgmcu_apb2_fz::DBG_TIM1_STOP.mask(),
)
.expect("the debug component takes the write");
mcu
}
#[test]
fn a_reset_from_the_pin_leaves_its_own_flag_and_not_the_softwares() {
use crate::soc::stm32f427::generated::RCC;
use crate::soc::stm32f427::generated::rcc::{Register as RccRegister, csr};
use crate::soc::{Layout, Width};
let at = VirtualNanos(0);
let flags = RCC.base + u64::from(RccRegister::Csr.spec().offset);
let mut mcu = a_part_with_a_board_on_it();
mcu.write(at, flags, Width::W32, csr::RMVF.mask())
.expect("the flags take the write");
mcu.reset(ResetCause::Pin);
let value = mcu.read(at, flags, Width::W32).expect("the flags answer");
assert!(csr::PADRSTF.is_set(value));
assert!(!csr::SFTRSTF.is_set(value) && !csr::WDGRSTF.is_set(value));
mcu.reset(ResetCause::Core);
let value = mcu.read(at, flags, Width::W32).expect("the flags answer");
assert!(csr::PADRSTF.is_set(value) && csr::SFTRSTF.is_set(value));
}
#[test]
fn a_reset_keeps_the_pins_and_the_backup_domain_and_nothing_else() {
use crate::soc::stm32f427::generated::adc1::{Register as AdcRegister, cr2, sqr3, sr};
use crate::soc::stm32f427::generated::dbg::{
Register as DbgRegister, dbgmcu_apb1_fz, dbgmcu_apb2_fz, dbgmcu_cr,
};
use crate::soc::stm32f427::generated::pwr::{Register as PwrRegister, csr as pwr_csr};
use crate::soc::stm32f427::generated::rcc::{Register as RccRegister, apb2enr, bdcr, csr};
use crate::soc::stm32f427::generated::rtc::Register as RtcRegister;
use crate::soc::stm32f427::generated::{ADC1, DBG, RCC, RTC};
use crate::soc::{Layout, Width};
let at = VirtualNanos(0);
let register = |base: u64, offset: u32| base + u64::from(offset);
let enables = register(RCC.base, RccRegister::Apb2enr.spec().offset);
let mut mcu = a_part_with_a_board_on_it();
mcu.reset(ResetCause::Core);
assert_eq!(
mcu.read(at, enables, Width::W32)
.expect("the enables answer"),
0,
"every register is at its reset value"
);
assert_eq!(mcu.next_event(), None);
assert_eq!(mcu.rcc.crystals(), (24_000_000, 32_768));
assert_eq!(
mcu.rcc.lsi_hz(),
29_000,
"a board's LSI calibration is the board's, not a register's"
);
assert_eq!(mcu.pwr.supply(), 3300);
assert!(mcu.spi[0].attached() && mcu.usart[1].attached() && mcu.usart[6].attached());
assert!(!mcu.spi[1].attached());
assert!(mcu.sdio.attached());
mcu.write(at, enables, Width::W32, apb2enr::ADC1EN.mask())
.expect("the enable takes the write");
let adc = |register: AdcRegister| ADC1.base + u64::from(register.spec().offset);
mcu.write(at, adc(AdcRegister::Sqr3), Width::W32, sqr3::SQ1.set(0, 2))
.expect("the converter takes the write");
mcu.write(at, adc(AdcRegister::Cr2), Width::W32, cr2::ADON.mask())
.expect("the converter takes the write");
mcu.write(
at,
adc(AdcRegister::Cr2),
Width::W32,
cr2::ADON.mask() | cr2::SWSTART.mask(),
)
.expect("the converter takes the write");
let converted = VirtualNanos(1_875);
mcu.advance_to(converted);
assert!(
sr::EOC.is_set(
mcu.read(converted, adc(AdcRegister::Sr), Width::W32)
.expect("the converter answers")
)
);
assert_eq!(
mcu.read(converted, adc(AdcRegister::Dr), Width::W32)
.expect("the converter answers"),
2047,
"the pin's voltage is still there"
);
let backup = register(RTC.base, RtcRegister::Bkp19r.spec().offset);
assert_eq!(
mcu.read(at, backup, Width::W32)
.expect("the backup domain answers"),
0x5EB0_0755
);
let domain = mcu
.registers("RCC")
.expect("an instance")
.get(RccRegister::Bdcr);
assert!(bdcr::RTCEN.is_set(domain) && bdcr::LSEON.is_set(domain));
let flags = mcu
.registers("RCC")
.expect("an instance")
.get(RccRegister::Csr);
assert!(
csr::SFTRSTF.is_set(flags) && csr::PADRSTF.is_set(flags) && csr::PORRSTF.is_set(flags),
"the software reset's flag joins the power-on ones the part started with: {flags:#x}"
);
let regulator = mcu
.registers("PWR")
.expect("an instance")
.get(PwrRegister::Csr);
assert!(
pwr_csr::BRE.is_set(regulator) && pwr_csr::BRR.is_set(regulator),
"the backup regulator is still on: {regulator:#x}"
);
let debug_read = |mcu: &mut Stm32f427, offset: u32| {
mcu.read(at, register(DBG.base, offset), Width::W32)
.expect("the debug component answers")
};
assert!(
dbgmcu_cr::DBG_STOP.is_set(debug_read(&mut mcu, DbgRegister::DbgmcuCr.spec().offset)),
"38.16.3: a system reset does not touch the debug component"
);
assert!(
dbgmcu_apb1_fz::DBG_TIM5_STOP.is_set(debug_read(
&mut mcu,
DbgRegister::DbgmcuApb1Fz.spec().offset
)),
"38.16.4: a system reset does not touch the debug component"
);
assert!(
dbgmcu_apb2_fz::DBG_TIM1_STOP.is_set(debug_read(
&mut mcu,
DbgRegister::DbgmcuApb2Fz.spec().offset
)),
"38.16.5: a system reset does not touch the debug component"
);
}
#[test]
fn every_model_that_keeps_time_runs_on_a_clock_the_tree_drives() {
let mcu = Stm32f427::new().unwrap();
for field in Field::all() {
let keeps_time = matches!(
field,
Field::Spi(_)
| Field::Usart(_)
| Field::I2c(_)
| Field::Flash
| Field::Adc(_)
| Field::AdcCommon
| Field::Tim(_)
| Field::Advanced(_)
| Field::Sdio
| Field::Iwdg
| Field::Dwt
);
let clock = mcu.bus().clock_of(field.instance());
assert_eq!(clock.is_some(), keeps_time, "{}", field.instance());
let driver = if matches!(field, Field::Adc(_)) {
ADC_COMMON
} else {
"RCC"
};
if let Some(clock) = clock {
assert_eq!(clock.instance, driver, "{}", field.instance());
}
}
}
#[test]
fn every_field_a_model_declares_is_in_the_list_beside_it() {
const SOURCES: [(&str, &str, &[&FieldSpec]); 3] = [
("rcc", include_str!("peripherals/rcc.rs"), rcc::DECLARED),
("pwr", include_str!("peripherals/pwr.rs"), pwr::DECLARED),
("dbg", include_str!("peripherals/dbg.rs"), dbg::DECLARED),
];
for (model, source, declared) in SOURCES {
let written: Vec<&str> = source
.lines()
.filter_map(|line| line.trim().strip_prefix("pub static "))
.filter_map(|rest| rest.split_once(": FieldSpec"))
.map(|(name, _)| name)
.collect();
assert!(
!written.is_empty(),
"{model} declares none, so this reads nothing"
);
let listed: Vec<&str> = declared.iter().map(|field| field.name).collect();
let forgotten: Vec<&&str> = written
.iter()
.filter(|name| !listed.contains(*name))
.collect();
assert!(
forgotten.is_empty(),
"{model} declares {forgotten:?} and leaves them out of its DECLARED"
);
}
}
#[test]
fn every_test_a_models_table_names_is_a_test_that_exists() {
const WHERE_TESTS_LIVE: [&str; 26] = [
include_str!("../../../tests/advanced_tim.rs"),
include_str!("../../../tests/ak8963.rs"),
include_str!("../../../tests/dbg.rs"),
include_str!("../../../tests/flash.rs"),
include_str!("../../../tests/fram.rs"),
include_str!("../../../tests/i2c.rs"),
include_str!("../../../tests/icm20608.rs"),
include_str!("../../../tests/lis3mdl.rs"),
include_str!("../../../tests/mpu9250.rs"),
include_str!("../../../tests/ms5611.rs"),
include_str!("memory_map.rs"),
include_str!("../../../tests/dwt.rs"),
include_str!("peripherals.rs"),
include_str!("../../../tests/dma.rs"),
include_str!("../../../tests/gpio.rs"),
include_str!("../../../tests/iwdg.rs"),
include_str!("../../../tests/otg.rs"),
include_str!("../../../tests/pwr.rs"),
include_str!("../../../tests/rcc.rs"),
include_str!("../../../tests/rtc.rs"),
include_str!("../../../tests/sdio.rs"),
include_str!("../../../tests/soc.rs"),
include_str!("../../../tests/spi.rs"),
include_str!("../../../tests/tim.rs"),
include_str!("../../../tests/usart.rs"),
include_str!("../../../tests/adc.rs"),
];
const MODELS: [(&str, &str); 24] = [
("adc", include_str!("peripherals/adc.rs")),
("dbg", include_str!("peripherals/dbg.rs")),
("nmea", include_str!("../../chip/nmea.rs")),
("memory_card", include_str!("../../chip/memory_card.rs")),
("ak8963", include_str!("../../chip/ak8963.rs")),
("flash", include_str!("peripherals/flash.rs")),
("fram", include_str!("../../chip/fram.rs")),
("i2c", include_str!("peripherals/i2c.rs")),
("icm20608", include_str!("../../chip/icm20608.rs")),
("lis3mdl", include_str!("../../chip/lis3mdl.rs")),
("mpu9250", include_str!("../../chip/mpu9250.rs")),
("ms5611", include_str!("../../chip/ms5611.rs")),
("dma", include_str!("peripherals/dma.rs")),
("dwt", include_str!("../../cpu/armv7m.rs")),
("gpio", include_str!("peripherals/gpio.rs")),
("iwdg", include_str!("peripherals/iwdg.rs")),
("otg", include_str!("peripherals/otg.rs")),
("pwr", include_str!("peripherals/pwr.rs")),
("rcc", include_str!("peripherals/rcc.rs")),
("rtc", include_str!("peripherals/rtc.rs")),
("sdio", include_str!("peripherals/sdio.rs")),
("spi", include_str!("peripherals/spi.rs")),
("tim", include_str!("peripherals/tim.rs")),
("usart", include_str!("peripherals/usart.rs")),
];
let mut named = 0;
let mut missing = Vec::new();
for (model, source) in MODELS {
let sources: Vec<&str> = WHERE_TESTS_LIVE.into_iter().chain([source]).collect();
let rows = source
.lines()
.filter_map(|line| line.strip_prefix("//! |"))
.filter(|row| !row.starts_with("---"))
.map(|row| row.split('|').collect::<Vec<&str>>())
.filter(|cells| cells.len() == 5);
for cells in rows {
for name in cells[3].split('`').skip(1).step_by(2) {
named += 1;
let defined = format!("fn {name}(");
if !sources.iter().any(|source| source.contains(&defined)) {
missing.push(format!("{model}: {name}"));
}
}
}
}
assert!(named > MODELS.len(), "the tables read as empty");
assert!(
missing.is_empty(),
"a coverage table names a test nothing defines: {}",
missing.join(", ")
);
}
#[test]
fn every_request_the_part_wires_names_a_channel_and_streams_the_controller_has() {
use dma::{CHANNELS, STREAMS};
let soc = super::super::memory_map::builder()
.build()
.expect("the part assembles");
for controller in &CONTROLLERS {
assert!(
soc.peripheral(controller.instance).is_some(),
"{} is not an instance",
controller.instance
);
for wire in controller.wiring {
assert!(
wire.channel < CHANNELS,
"{}'s request {} arrives on channel {}",
wire.instance,
wire.request,
wire.channel
);
assert!(
wire.streams.iter().all(|stream| *stream < STREAMS),
"{}'s request {} reaches {:?}",
wire.instance,
wire.request,
wire.streams
);
assert!(
Field::all()
.into_iter()
.any(|field| field.instance() == wire.instance),
"{} drives a request and is not an instance this part models",
wire.instance
);
}
}
}
#[test]
fn the_part_declares_what_each_of_its_models_declares() {
let names: Vec<&str> = declared_fields().map(|field| field.name).collect();
for model in ["SW", "GPIOKEN", "MRUDS", "LPUDS"] {
assert!(
names.contains(&model),
"{model} is a model's own and the part does not declare it: {names:?}"
);
}
}
fn enable_bit(name: &str) -> u64 {
use crate::soc::stm32f427::generated::rcc;
rcc::REGISTERS
.iter()
.find(|register| register.name == "AHB1ENR")
.expect("the clock controller carries it")
.fields
.iter()
.chain(declared_fields())
.find(|field| field.name == name)
.unwrap_or_else(|| panic!("{name} is a field nothing declares"))
.mask()
}
fn open_every_port_clock(mcu: &mut Stm32f427) {
use crate::soc::stm32f427::generated::{RCC, rcc};
use crate::soc::{Layout, Width};
let at = RCC.base + u64::from(rcc::Register::Ahb1enr.spec().offset);
let enables = PORTS
.iter()
.fold(0, |all, port| all | enable_bit(&format!("{port}EN")));
mcu.write(VirtualNanos(0), at, Width::W32, enables)
.expect("the clock controller holds every enable and nothing gates it");
}
#[test]
fn every_port_has_its_clock_enable_its_reset_and_its_sleep_enable() {
let mcu = Stm32f427::new().expect("the part assembles");
let mut declared: BTreeMap<&str, (u8, u8)> = mcu
.bus()
.peripheral("RCC")
.expect("RCC is an instance")
.registers
.iter()
.flat_map(|register| register.fields)
.map(|field| (field.name, (field.bit, field.width)))
.collect();
declared.extend(declared_fields().map(|field| (field.name, (field.bit, field.width))));
let mut missing = Vec::new();
for (nth, port) in PORTS.iter().enumerate() {
let letter = port
.strip_prefix("GPIO")
.expect("a port is named for its letter");
let bit = u8::try_from(nth).expect("eleven ports");
for suffix in ["EN", "RST", "LPEN"] {
let name = format!("GPIO{letter}{suffix}");
if declared.get(name.as_str()) != Some(&(bit, 1)) {
missing.push(format!(
"{name} at bit {bit}: {:?}",
declared.get(name.as_str())
));
}
}
}
assert!(
missing.is_empty(),
"the part carries {} ports and declares fewer sets of RCC bits: {}",
PORTS.len(),
missing.join(", ")
);
}
#[test]
fn every_modelled_instance_but_the_clock_controller_answers_behind_a_clock() {
use crate::soc::{Fault, Width};
let mut mcu = Stm32f427::new().expect("the part assembles");
for field in Field::all() {
let instance = field.instance();
let base = mcu
.bus()
.peripheral(instance)
.unwrap_or_else(|| panic!("{instance} is an instance"))
.base;
let answered = mcu.read(VirtualNanos(0), base, Width::W32);
if instance == crate::cpu::armv7m::DWT || instance == "DBG" {
assert!(
answered.is_ok(),
"a core-adjacent block is not on the part's clock tree: {answered:?}"
);
} else if instance == "RTC" {
assert!(
answered.is_ok(),
"the backup domain is write-protected, not gated: {answered:?}"
);
} else if instance == "RCC" {
assert!(
answered.is_ok(),
"the clock controller holds every enable, so nothing can gate it: {answered:?}"
);
} else if instance == "IWDG" {
assert!(
answered.is_ok(),
"the independent watchdog has no clock gate: {answered:?}"
);
} else if instance == "FLASH" {
assert!(
answered.is_ok(),
"2.1.11 excepts the flash memory interface as well as the SRAM: {answered:?}"
);
} else {
assert!(
matches!(answered, Err(Fault::Gated { .. })),
"{instance} answers with every clock still off: {answered:?}"
);
}
}
}
#[test]
fn a_port_drives_and_holds_only_its_own() {
use crate::device::Level;
use crate::soc::stm32f427::generated::gpioa::Register;
use crate::soc::{Layout, Width};
use gpio::Pin;
let pin = Pin::new(0).expect("a port has a pin zero");
let idr = u64::from(Register::Idr.spec().offset);
for (nth, driven) in PORTS.iter().enumerate() {
let mut mcu = Stm32f427::new().expect("the part assembles");
open_every_port_clock(&mut mcu);
mcu.drive_port(VirtualNanos(0), nth, |port, _, _| {
port.drive(pin, Some(Level::High));
});
let sentinel = 1 << nth;
mcu.port(nth).1.set(Register::Odr, sentinel);
for (other, name) in PORTS.iter().enumerate() {
let base = mcu
.bus()
.peripheral(name)
.expect("a port is an instance")
.base;
let read = mcu
.read(VirtualNanos(0), base + idr, Width::W32)
.expect("IDR answers at its own offset");
assert_eq!(
read & 1,
u64::from(other == nth),
"{name} reads a pin driven on {driven}"
);
let held = mcu
.registers(name)
.expect("a port is an instance")
.get(Register::Odr);
assert_eq!(
held,
if other == nth { sentinel } else { 0 },
"{name} holds what was stored on {driven}"
);
}
}
}
#[test]
fn every_field_answers_for_the_one_instance_it_is_named_for() {
let mut mcu = Stm32f427::new().expect("the part assembles");
let slots = mcu.bus().slots().len();
let mut answered: Vec<(Field, &'static str)> = (0..slots)
.filter_map(|slot| {
let field = mcu.wiring[slot];
let name = mcu.bus().slots()[slot].peripheral().name;
mcu.split(slot).1.map(|_| (field, name))
})
.collect();
answered.sort_by_key(|(_, name)| *name);
let mut expected: Vec<(Field, &'static str)> = Field::all()
.into_iter()
.map(|field| (field, field.instance()))
.collect();
expected.sort_by_key(|(_, name)| *name);
assert_eq!(
answered, expected,
"every field the part has answers for exactly the instance it is named for"
);
}
#[test]
fn the_part_answers_for_every_serial_interface_and_neither_extended_block() {
let carried: Vec<&str> = instances_of(&super::super::generated::spi1::REGISTERS)
.map(|one| one.name)
.collect();
for instance in SERIAL_INTERFACES {
assert!(
carried.contains(&instance),
"{instance} is not an instance of the serial interface's layout"
);
}
for extended in ["I2S2ext", "I2S3ext"] {
assert!(carried.contains(&extended));
assert!(!SERIAL_INTERFACES.contains(&extended));
}
}
#[test]
fn the_part_says_how_much_of_itself_it_models() {
let mut mcu = Stm32f427::new().expect("the part assembles");
let instances = mcu.bus().slots().len();
let modelled = (0..instances)
.filter(|&slot| mcu.split(slot).1.is_some())
.count();
assert_eq!(modelled, Field::all().len());
assert!(modelled < instances);
println!("{modelled} of {instances} instances carry a model");
}
}