pub mod gpio;
pub mod pwr;
pub mod rcc;
pub mod spi;
pub mod tim;
use crate::device::{DeviceContext, ErasedDevice, RegisterFile};
use crate::mcu::Mcu;
use crate::soc::{Bus, Error};
use crate::time::VirtualNanos;
use gpio::Gpio;
use pwr::Pwr;
use rcc::Rcc;
use spi::Spi;
use tim::Tim;
const RESET_BUS_HZ: std::num::NonZeroU64 =
std::num::NonZeroU64::new(16_000_000).expect("sixteen million");
#[derive(Debug)]
pub struct Stm32f427 {
pub rcc: Rcc,
pub pwr: Pwr,
pub gpioa: Gpio,
pub spi: [Spi; SERIAL_INTERFACES.len()],
pub tim2: Tim,
bus: Bus,
wiring: Box<[Field]>,
slots: Slots,
}
#[derive(Debug, Clone, Copy)]
struct Slots {
rcc: usize,
pwr: usize,
gpioa: usize,
spi: [usize; SERIAL_INTERFACES.len()],
tim2: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Field {
None,
Rcc,
Pwr,
Gpioa,
Spi(usize),
Tim2,
}
impl Field {
fn all() -> Vec<Self> {
let mut all = vec![Self::Rcc, Self::Pwr, Self::Gpioa, Self::Tim2];
all.extend((0..SERIAL_INTERFACES.len()).map(Self::Spi));
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::Gpioa => "GPIOA",
Self::Spi(nth) => SERIAL_INTERFACES[nth],
Self::Tim2 => "TIM2",
}
}
}
pub const SERIAL_INTERFACES: [&str; 6] = ["SPI1", "SPI2", "SPI3", "SPI4", "SPI5", "SPI6"];
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,
gpioa: 0,
spi: [0; SERIAL_INTERFACES.len()],
tim2: 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::Gpioa => slots.gpioa = slot,
Field::Spi(nth) => slots.spi[nth] = slot,
Field::Tim2 => slots.tim2 = slot,
Field::None => unreachable!("Field::all names no unmodelled slot"),
}
}
let mut mcu = Self {
rcc: Rcc::new(),
pwr: Pwr::new(),
gpioa: Gpio::default(),
spi: std::array::from_fn(|_| Spi::with_clock(RESET_BUS_HZ)),
tim2: Tim::with_clock(RESET_BUS_HZ),
bus,
wiring,
slots,
};
mcu.check()?;
Ok(mcu)
}
}
macro_rules! paired {
($name:ident, $driving:ident, $field:ident, $model:ty, $what:literal) => {
#[doc = concat!("The ", $what, " and the registers it keeps its state in.")]
pub fn $name(&mut self) -> (&mut $model, &mut RegisterFile) {
let Self {
$field, bus, slots, ..
} = self;
($field, bus.slot_mut(slots.$field).registers_mut())
}
#[doc = concat!("Drives an input of the ", $what, " at `at`, an input the bus does not carry: it")]
pub fn $driving<T>(
&mut self,
at: VirtualNanos,
input: impl FnOnce(&mut $model, &mut RegisterFile, &mut DeviceContext<'_>) -> T,
) -> T {
let Self {
$field, bus, slots, ..
} = self;
let (carried, mut context) = bus.call(at, slots.$field);
input($field, carried.registers_mut(), &mut context)
}
};
}
impl Stm32f427 {
paired!(clocks, drive_clocks, rcc, Rcc, "clock tree");
paired!(power, drive_power, pwr, Pwr, "regulator");
paired!(port_a, drive_port_a, gpioa, Gpio, "port A");
paired!(timer, drive_timer, tim2, Tim, "timer");
pub fn serial(&mut self, nth: usize) -> (&mut Spi, &mut RegisterFile) {
let Self {
spi, bus, slots, ..
} = self;
(&mut spi[nth], bus.slot_mut(slots.spi[nth]).registers_mut())
}
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;
let (carried, mut context) = bus.call(at, slots.spi[nth]);
input(&mut spi[nth], carried.registers_mut(), &mut context)
}
}
impl Mcu for Stm32f427 {
fn bus(&self) -> &Bus {
&self.bus
}
fn split(&mut self, slot: usize) -> (&mut Bus, Option<&mut dyn ErasedDevice>) {
let Self {
rcc,
pwr,
gpioa,
spi,
tim2,
bus,
wiring,
..
} = self;
let model: Option<&mut dyn ErasedDevice> = match wiring[slot] {
Field::None => None,
Field::Rcc => Some(rcc),
Field::Pwr => Some(pwr),
Field::Gpioa => Some(gpioa),
Field::Spi(nth) => Some(&mut spi[nth]),
Field::Tim2 => Some(tim2),
};
(bus, model)
}
fn bus_mut(&mut self) -> &mut Bus {
&mut self.bus
}
}
#[cfg(test)]
mod tests {
use super::*;
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))
}
#[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");
}
}