#[cfg(feature = "stm32f427")]
pub mod stm32f427;
use std::fmt;
use crate::device::{DeviceContext, InterruptChange, RegisterFile};
use crate::time::{EventId, EventQueue, VirtualNanos};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Cpu {
pub name: &'static str,
pub fpu: bool,
pub interrupt_lines: u16,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Peripheral {
pub name: &'static str,
pub base: u64,
pub size: u64,
pub interrupts: &'static [u16],
pub registers: &'static [RegisterSpec],
}
impl Peripheral {
pub const fn contains(&self, address: u64) -> bool {
address >= self.base && address < self.base + self.size
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RegisterSpec {
pub name: &'static str,
pub offset: u32,
pub width: Width,
pub reset: u64,
pub access: Access,
pub read: ReadAction,
pub write: WriteModify,
pub fields: &'static [FieldSpec],
}
impl RegisterSpec {
pub const fn end(&self) -> u32 {
self.offset + self.width.bytes()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FieldSpec {
pub name: &'static str,
pub bit: u8,
pub width: u8,
pub access: Access,
pub read: ReadAction,
pub write: WriteModify,
}
impl FieldSpec {
pub const fn mask(&self) -> u64 {
if self.width >= Width::W64.bits() {
u64::MAX
} else {
((1u64 << self.width) - 1) << self.bit
}
}
pub const fn get(&self, register_value: u64) -> u64 {
(register_value & self.mask()) >> self.bit
}
pub const fn set(&self, register_value: u64, field_value: u64) -> u64 {
(register_value & !self.mask()) | ((field_value << self.bit) & self.mask())
}
pub const fn is_set(&self, register_value: u64) -> bool {
register_value & self.mask() != 0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Width {
W8,
W16,
W32,
W64,
}
impl Width {
pub const fn bits(self) -> u8 {
match self {
Self::W8 => 8,
Self::W16 => 16,
Self::W32 => 32,
Self::W64 => 64,
}
}
pub const fn bytes(self) -> u32 {
self.bits() as u32 / 8
}
pub const fn mask(self) -> u64 {
match self {
Self::W64 => u64::MAX,
narrower => (1u64 << narrower.bits()) - 1,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Access {
ReadOnly,
WriteOnly,
ReadWrite,
WriteOnce,
ReadWriteOnce,
}
impl Access {
pub const fn is_readable(self) -> bool {
!matches!(self, Self::WriteOnly | Self::WriteOnce)
}
pub const fn is_writable(self) -> bool {
!matches!(self, Self::ReadOnly)
}
pub const fn is_write_once(self) -> bool {
matches!(self, Self::WriteOnce | Self::ReadWriteOnce)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ReadAction {
None,
Clear,
Set,
Modify,
ModifyExternal,
}
impl ReadAction {
pub const fn apply(self, value: u64, mask: u64) -> u64 {
match self {
Self::Clear => value & !mask,
Self::Set => value | mask,
Self::None | Self::Modify | Self::ModifyExternal => value,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WriteModify {
None,
OneToClear,
OneToSet,
OneToToggle,
ZeroToClear,
ZeroToSet,
ZeroToToggle,
Clear,
Set,
Modify,
}
impl WriteModify {
pub const fn apply(self, old: u64, written: u64) -> u64 {
match self {
Self::None => written,
Self::OneToClear => old & !written,
Self::OneToSet => old | written,
Self::OneToToggle => old ^ written,
Self::ZeroToClear => old & written,
Self::ZeroToSet => old | !written,
Self::ZeroToToggle => old ^ !written,
Self::Clear => 0,
Self::Set => u64::MAX,
Self::Modify => old,
}
}
pub const fn neutral(self, old: u64) -> u64 {
match self {
Self::OneToClear | Self::OneToSet | Self::OneToToggle => 0,
Self::ZeroToClear | Self::ZeroToSet | Self::ZeroToToggle => u64::MAX,
Self::None | Self::Clear | Self::Set | Self::Modify => old,
}
}
}
pub trait Layout: Copy + fmt::Debug {
const REGISTERS: &'static [RegisterSpec];
fn from_index(index: usize) -> Option<Self>;
fn index(self) -> usize;
fn spec(self) -> &'static RegisterSpec {
&Self::REGISTERS[self.index()]
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RegionKind {
Flash,
Ram,
System,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Region {
pub name: &'static str,
pub base: u64,
pub size: u64,
pub kind: RegionKind,
}
impl Region {
pub const fn flash(name: &'static str, base: u64, size: u64) -> Self {
Self {
name,
base,
size,
kind: RegionKind::Flash,
}
}
pub const fn ram(name: &'static str, base: u64, size: u64) -> Self {
Self {
name,
base,
size,
kind: RegionKind::Ram,
}
}
pub const fn system(name: &'static str, base: u64, size: u64) -> Self {
Self {
name,
base,
size,
kind: RegionKind::System,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error("memory regions {lower} and {upper} overlap")]
OverlappingRegions {
lower: &'static str,
upper: &'static str,
},
#[error("peripheral instances {lower} and {upper} overlap")]
OverlappingInstances {
lower: &'static str,
upper: &'static str,
},
#[error("{instance} raises interrupt {line} but the CPU has {lines} lines")]
InterruptOutOfRange {
instance: &'static str,
line: u16,
lines: u16,
},
#[error("{instance} is not an instance of this SoC")]
UnknownInstance {
instance: &'static str,
},
#[error("{instance} has no register {register}")]
UnknownRegister {
instance: &'static str,
register: &'static str,
},
#[error("reset value {value:#x} does not fit {instance}.{register}")]
ResetTooWide {
instance: &'static str,
register: &'static str,
value: u64,
},
#[error("{instance} does not have the register layout the device was written for")]
LayoutMismatch {
instance: &'static str,
},
#[error("{instance} is bound twice")]
BoundTwice {
instance: &'static str,
},
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[non_exhaustive]
pub enum Fault {
#[error("no register at {address:#x}")]
Unmapped {
address: u64,
},
#[error("a {}-bit access at {address:#x} runs past its register", width.bits())]
Misaligned {
address: u64,
width: Width,
},
}
pub struct Slot {
peripheral: Peripheral,
registers: RegisterFile,
}
impl Slot {
#[must_use]
pub fn peripheral(&self) -> &Peripheral {
&self.peripheral
}
#[must_use]
pub fn registers(&self) -> &RegisterFile {
&self.registers
}
pub fn registers_mut(&mut self) -> &mut RegisterFile {
&mut self.registers
}
}
impl fmt::Debug for Slot {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("Slot")
.field("peripheral", &self.peripheral.name)
.field("registers", &self.registers)
.finish()
}
}
struct Extent {
name: &'static str,
base: u64,
size: u64,
}
impl Extent {
const fn end(&self) -> u64 {
self.base.saturating_add(self.size)
}
}
#[must_use = "a builder does nothing until built"]
pub struct Builder {
cpu: Cpu,
memory: &'static [Region],
peripherals: Vec<Peripheral>,
reset_corrections: Vec<ResetCorrection>,
write_corrections: Vec<WriteCorrection>,
}
struct ResetCorrection {
instance: &'static str,
register: &'static str,
value: u64,
}
struct WriteCorrection {
instance: &'static str,
register: &'static str,
rule: WriteModify,
}
impl fmt::Debug for Builder {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("Builder")
.field("cpu", &self.cpu)
.field("reset_corrections", &self.reset_corrections.len())
.field("write_corrections", &self.write_corrections.len())
.finish_non_exhaustive()
}
}
impl Builder {
pub fn reset(mut self, instance: &'static str, register: &'static str, value: u64) -> Self {
self.reset_corrections.push(ResetCorrection {
instance,
register,
value,
});
self
}
pub fn write_modify(
mut self,
instance: &'static str,
register: &'static str,
rule: WriteModify,
) -> Self {
self.write_corrections.push(WriteCorrection {
instance,
register,
rule,
});
self
}
pub fn build(self) -> Result<Bus, Error> {
let regions = self.memory.iter().map(|region| Extent {
name: region.name,
base: region.base,
size: region.size,
});
if let Some((lower, upper)) = first_overlap(regions) {
return Err(Error::OverlappingRegions { lower, upper });
}
let spans = self.peripherals.iter().map(|peripheral| Extent {
name: peripheral.name,
base: peripheral.base,
size: peripheral.size,
});
if let Some((lower, upper)) = first_overlap(spans) {
return Err(Error::OverlappingInstances { lower, upper });
}
check_interrupts(&self.peripherals, self.cpu.interrupt_lines)?;
let mut slots: Vec<Slot> = self
.peripherals
.into_iter()
.map(|peripheral| Slot {
peripheral,
registers: RegisterFile::new(peripheral.registers),
})
.collect();
slots.sort_by_key(|slot| slot.peripheral.base);
for correction in self.reset_corrections {
let instance = instance_named(&mut slots, correction.instance)?;
let (row, register) = instance
.peripheral
.registers
.iter()
.enumerate()
.find(|(_, register)| register.name == correction.register)
.ok_or(Error::UnknownRegister {
instance: correction.instance,
register: correction.register,
})?;
if correction.value & !register.width.mask() != 0 {
return Err(Error::ResetTooWide {
instance: correction.instance,
register: correction.register,
value: correction.value,
});
}
instance.registers.correct_reset(row, correction.value);
}
for correction in self.write_corrections {
let instance = instance_named(&mut slots, correction.instance)?;
let row = instance
.peripheral
.registers
.iter()
.position(|register| register.name == correction.register)
.ok_or(Error::UnknownRegister {
instance: correction.instance,
register: correction.register,
})?;
instance.registers.correct_write(row, correction.rule);
}
Ok(Bus {
cpu: self.cpu,
memory: self.memory,
slots,
events: EventQueue::default(),
interrupt_changes: Vec::new(),
})
}
}
fn instance_named<'a>(slots: &'a mut [Slot], name: &'static str) -> Result<&'a mut Slot, Error> {
slots
.iter_mut()
.find(|slot| slot.peripheral.name == name)
.ok_or(Error::UnknownInstance { instance: name })
}
fn first_overlap(spans: impl Iterator<Item = Extent>) -> Option<(&'static str, &'static str)> {
let mut sorted: Vec<Extent> = spans.collect();
sorted.sort_by_key(|span| span.base);
sorted
.windows(2)
.find(|pair| pair[1].base < pair[0].end())
.map(|pair| (pair[0].name, pair[1].name))
}
fn check_interrupts(peripherals: &[Peripheral], lines: u16) -> Result<(), Error> {
for peripheral in peripherals {
if let Some(&line) = peripheral.interrupts.iter().find(|&&line| line >= lines) {
return Err(Error::InterruptOutOfRange {
instance: peripheral.name,
line,
lines,
});
}
}
Ok(())
}
#[derive(Debug)]
pub struct Bus {
cpu: Cpu,
memory: &'static [Region],
slots: Vec<Slot>,
events: EventQueue,
interrupt_changes: Vec<InterruptChange>,
}
pub(crate) const SAME_INSTANT_LIMIT: u32 = 1_000;
impl Bus {
pub fn builder(
cpu: Cpu,
memory: &'static [Region],
peripherals: impl IntoIterator<Item = Peripheral>,
) -> Builder {
Builder {
cpu,
memory,
peripherals: peripherals.into_iter().collect(),
reset_corrections: Vec::new(),
write_corrections: Vec::new(),
}
}
#[must_use]
pub fn slots(&self) -> &[Slot] {
&self.slots
}
#[must_use]
pub fn slot_of(&self, name: &str) -> Option<usize> {
self.slots
.iter()
.position(|slot| slot.peripheral.name == name)
}
pub fn cpu(&self) -> Cpu {
self.cpu
}
pub fn memory(&self) -> &'static [Region] {
self.memory
}
pub fn peripherals(&self) -> impl Iterator<Item = &Peripheral> {
self.slots.iter().map(|slot| &slot.peripheral)
}
pub fn peripheral(&self, name: &str) -> Option<&Peripheral> {
self.peripherals()
.find(|peripheral| peripheral.name == name)
}
#[must_use]
pub fn registers(&self, name: &str) -> Option<&RegisterFile> {
self.slots
.iter()
.find(|slot| slot.peripheral.name == name)
.map(|slot| &slot.registers)
}
pub(crate) fn slot_mut(&mut self, index: usize) -> &mut Slot {
&mut self.slots[index]
}
pub(crate) fn locate(
&self,
address: u64,
width: Width,
allows: fn(Access) -> bool,
) -> Result<Located, Fault> {
let candidates = self
.slots
.partition_point(|slot| slot.peripheral.base <= address);
let Some(index) = candidates.checked_sub(1) else {
return Err(Fault::Unmapped { address });
};
let slot = &self.slots[index];
if !slot.peripheral.contains(address) {
return Err(Fault::Unmapped { address });
}
let offset = u32::try_from(address - slot.peripheral.base)
.map_err(|_too_far| Fault::Unmapped { address })?;
let row = slot
.registers
.row_covering(offset, allows)
.ok_or(Fault::Unmapped { address })?;
let register = &slot.peripheral.registers[row];
let byte_in_register = offset - register.offset;
if byte_in_register + width.bytes() > register.width.bytes() {
return Err(Fault::Misaligned { address, width });
}
Ok(Located {
slot: index,
row,
lane: Lane {
shift: byte_in_register * u32::from(Width::W8.bits()),
},
})
}
pub(crate) fn call(
&mut self,
at: VirtualNanos,
index: usize,
) -> (&mut Slot, DeviceContext<'_>) {
let Self {
slots,
events,
interrupt_changes,
..
} = self;
let slot = &mut slots[index];
let context = DeviceContext::new(at, index, slot.peripheral, events, interrupt_changes);
(slot, context)
}
pub(crate) fn pop_due(&mut self, now: VirtualNanos) -> Option<(EventId, usize, VirtualNanos)> {
self.events.pop_due(now)
}
#[must_use]
pub fn next_event(&self) -> Option<VirtualNanos> {
self.events.next_at()
}
pub fn drain_interrupts(&mut self) -> impl Iterator<Item = InterruptChange> + '_ {
self.interrupt_changes.drain(..)
}
}
pub(crate) struct Located {
pub(crate) slot: usize,
pub(crate) row: usize,
pub(crate) lane: Lane,
}
pub(crate) struct Lane {
pub(crate) shift: u32,
}