#[cfg(feature = "stm32f427")]
pub mod stm32f427;
use std::fmt;
use crate::device::{
Answer, ClockChange, DeviceContext, Effects, InterruptChange, PendingAccess, RegisterFile,
RequestChange,
};
use crate::emulator::AccessKind;
use crate::time::{EventId, 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,
}
}
#[must_use]
pub const fn contains(&self, address: u64) -> bool {
address >= self.base && address - self.base < self.size
}
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} is clocked twice; an instance runs on one clock")]
ClockedTwice {
instance: &'static str,
},
#[error("{instance} has no register {register}")]
UnknownRegister {
instance: &'static str,
register: &'static str,
},
#[error("{instance}.{register} has no field {field}")]
UnknownField {
instance: &'static str,
register: &'static str,
field: &'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("{instance}.{register} is gated by {}.{}.{}", by.instance, by.register, by.field)]
Gated {
instance: &'static str,
register: &'static str,
by: Gate,
},
#[error("a {}-bit access at {address:#x} runs past its register", width.bits())]
Misaligned {
address: u64,
width: Width,
},
}
struct Declaration {
instance: &'static str,
register: &'static str,
field: &'static FieldSpec,
}
fn slot_named(slots: &[Slot], instance: &'static str) -> Result<usize, Error> {
slots
.iter()
.position(|slot| slot.peripheral.name == instance)
.ok_or(Error::UnknownInstance { instance })
}
fn resolve_gate(
slots: &[Slot],
declarations: &[Declaration],
spec: &GateSpec,
) -> Result<(usize, ResolvedGate), Error> {
let gate = spec.gate;
let by_slot = slot_named(slots, gate.instance)?;
let by_row = slots[by_slot]
.peripheral
.registers
.iter()
.position(|register| register.name == gate.register)
.ok_or(Error::UnknownRegister {
instance: gate.instance,
register: gate.register,
})?;
let mask = slots[by_slot].peripheral.registers[by_row]
.fields
.iter()
.find(|field| field.name == gate.field)
.or_else(|| {
declarations
.iter()
.find(|declared| {
declared.instance == gate.instance
&& declared.register == gate.register
&& declared.field.name == gate.field
})
.map(|declared| declared.field)
})
.map(FieldSpec::mask)
.ok_or(Error::UnknownField {
instance: gate.instance,
register: gate.register,
field: gate.field,
})?;
let gated = slot_named(slots, spec.instance)?;
let rows = spec
.registers
.iter()
.map(|name| {
slots[gated]
.peripheral
.registers
.iter()
.position(|register| register.name == *name)
.ok_or(Error::UnknownRegister {
instance: spec.instance,
register: name,
})
})
.collect::<Result<Vec<usize>, Error>>()?;
Ok((
gated,
ResolvedGate {
by_slot,
by_row,
mask,
writes_only: spec.writes_only,
rows: rows.into_boxed_slice(),
gate,
},
))
}
pub struct Slot {
peripheral: Peripheral,
registers: RegisterFile,
gates: Box<[ResolvedGate]>,
}
#[derive(Debug, Clone)]
struct ResolvedGate {
by_slot: usize,
by_row: usize,
mask: u64,
writes_only: bool,
rows: Box<[usize]>,
gate: Gate,
}
impl ResolvedGate {
fn covers(&self, row: usize, writing: bool) -> bool {
(writing || !self.writes_only) && (self.rows.is_empty() || self.rows.contains(&row))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Gate {
pub instance: &'static str,
pub register: &'static str,
pub field: &'static str,
}
impl Gate {
#[must_use]
pub const fn by(instance: &'static str, register: &'static str, field: &'static str) -> Self {
Self {
instance,
register,
field,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Clock {
pub instance: &'static str,
pub output: usize,
}
impl Clock {
#[must_use]
pub const fn output(instance: &'static str, output: usize) -> Self {
Self { instance, output }
}
}
#[derive(Debug, Clone, Copy)]
struct ResolvedClock {
source: usize,
output: usize,
sink: usize,
}
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)
.field("gates", &self.gates)
.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>,
gates: Vec<GateSpec>,
sinks: Vec<&'static str>,
clocks: Vec<(&'static str, Clock)>,
declarations: Vec<Declaration>,
}
struct GateSpec {
instance: &'static str,
registers: &'static [&'static str],
writes_only: bool,
gate: Gate,
}
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 declare(
mut self,
instance: &'static str,
register: &'static str,
field: &'static FieldSpec,
) -> Self {
self.declarations.push(Declaration {
instance,
register,
field,
});
self
}
pub fn gate(mut self, instance: &'static str, gate: Gate) -> Self {
self.gates.push(GateSpec {
instance,
registers: &[],
writes_only: false,
gate,
});
self
}
pub fn gate_writes(
mut self,
instance: &'static str,
registers: &'static [&'static str],
gate: Gate,
) -> Self {
self.gates.push(GateSpec {
instance,
registers,
writes_only: true,
gate,
});
self
}
pub(crate) fn ungated(mut self) -> Self {
self.gates.clear();
self
}
pub fn reads_requests(mut self, instance: &'static str) -> Self {
self.sinks.push(instance);
self
}
pub fn clocked_by(mut self, instance: &'static str, clock: Clock) -> Self {
self.clocks.push((instance, clock));
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 {
gates: Box::default(),
peripheral,
registers: RegisterFile::new(peripheral.registers),
})
.collect();
slots.sort_by_key(|slot| slot.peripheral.base);
for correction in self.reset_corrections {
let instance = slot_named_mut(&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 = slot_named_mut(&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);
}
let sinks = self
.sinks
.iter()
.map(|name| slot_named(&slots, name))
.collect::<Result<Vec<usize>, Error>>()?;
let mut clocks = Vec::with_capacity(self.clocks.len());
for (instance, clock) in &self.clocks {
let sink = slot_named(&slots, instance)?;
if clocks
.iter()
.any(|wired: &ResolvedClock| wired.sink == sink)
{
return Err(Error::ClockedTwice { instance });
}
clocks.push(ResolvedClock {
source: slot_named(&slots, clock.instance)?,
output: clock.output,
sink,
});
}
for spec in self.gates {
let (gated, resolved) = resolve_gate(&slots, &self.declarations, &spec)?;
let mut gates = std::mem::take(&mut slots[gated].gates).into_vec();
gates.push(resolved);
slots[gated].gates = gates.into_boxed_slice();
}
Ok(Bus {
cpu: self.cpu,
memory: self.memory,
slots,
effects: Effects::default(),
sinks,
clocks,
})
}
}
fn slot_named_mut<'a>(slots: &'a mut [Slot], name: &'static str) -> Result<&'a mut Slot, Error> {
let index = slot_named(slots, name)?;
Ok(&mut slots[index])
}
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>,
effects: Effects,
sinks: Vec<usize>,
clocks: Vec<ResolvedClock>,
}
pub(crate) const SAME_INSTANT_LIMIT: u32 = 1_000;
pub(crate) const SETTLE_LIMIT: u32 = 2 * 2 * (65_535 + 4 * 65_535);
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(),
gates: Vec::new(),
sinks: Vec::new(),
clocks: Vec::new(),
declarations: Vec::new(),
}
}
#[must_use]
pub fn slots(&self) -> &[Slot] {
&self.slots
}
pub fn reset(&mut self) {
for slot in &mut self.slots {
slot.registers.reset();
}
self.effects = Effects::default();
}
#[must_use]
pub fn slot_of(&self, name: &str) -> Option<usize> {
self.slots
.iter()
.position(|slot| slot.peripheral.name == name)
}
#[must_use]
pub fn slot_at(&self, address: u64) -> Option<usize> {
let candidates = self
.slots
.partition_point(|slot| slot.peripheral.base <= address);
let index = candidates.checked_sub(1)?;
self.slots[index]
.peripheral
.contains(address)
.then_some(index)
}
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)
}
#[must_use]
pub fn peripheral(&self, name: &str) -> Option<&Peripheral> {
self.slot_of(name).map(|slot| &self.slots[slot].peripheral)
}
#[must_use]
pub fn registers(&self, name: &str) -> Option<&RegisterFile> {
self.slot_of(name).map(|slot| &self.slots[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,
kind: AccessKind,
) -> Result<Located, Fault> {
let allows: fn(Access) -> bool = match kind {
AccessKind::Read => Access::is_readable,
AccessKind::Write => Access::is_writable,
};
let index = self.slot_at(address).ok_or(Fault::Unmapped { address })?;
let slot = &self.slots[index];
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 });
}
if let Some(shut) = slot
.gates
.iter()
.find(|gate| gate.covers(row, kind == AccessKind::Write) && !self.gate_is_open(gate))
{
return Err(Fault::Gated {
instance: slot.peripheral.name,
register: register.name,
by: shut.gate,
});
}
Ok(Located {
slot: index,
row,
lane: Lane {
shift: byte_in_register * u32::from(Width::W8.bits()),
},
})
}
fn gate_is_open(&self, gate: &ResolvedGate) -> bool {
self.slots[gate.by_slot].registers.get_row(gate.by_row) & gate.mask != 0
}
pub(crate) fn call(
&mut self,
at: VirtualNanos,
index: usize,
) -> (&mut Slot, DeviceContext<'_>) {
let Self { slots, effects, .. } = self;
let slot = &mut slots[index];
let context = DeviceContext::new(at, index, slot.peripheral, effects);
(slot, context)
}
pub(crate) fn pop_due(&mut self, now: VirtualNanos) -> Option<(EventId, usize, VirtualNanos)> {
self.effects.events.pop_due(now)
}
#[must_use]
pub fn next_event(&self) -> Option<VirtualNanos> {
self.effects.events.next_at()
}
pub fn drain_interrupts(&mut self) -> impl Iterator<Item = InterruptChange> + '_ {
self.effects.interrupts.drain(..)
}
pub(crate) fn sinks(&self) -> &[usize] {
&self.sinks
}
pub(crate) fn next_request_change(&mut self) -> Option<(VirtualNanos, RequestChange)> {
self.effects.requests.pop_front()
}
pub fn drain_requests(&mut self) -> impl Iterator<Item = RequestChange> + '_ {
self.effects.requests.drain(..).map(|(_at, change)| change)
}
pub(crate) fn next_clock_change(&mut self) -> Option<(VirtualNanos, ClockChange)> {
self.effects.clocks.pop_front()
}
pub(crate) fn readers_of(&self, change: ClockChange) -> impl Iterator<Item = usize> + '_ {
let source = self.slot_of(change.instance);
self.clocks
.iter()
.filter(move |wired| Some(wired.source) == source && wired.output == change.output)
.map(|wired| wired.sink)
}
pub fn clock_of(&self, instance: &str) -> Option<Clock> {
let sink = self.slot_of(instance)?;
self.clocks
.iter()
.find(|wired| wired.sink == sink)
.map(|wired| Clock {
instance: self.slots[wired.source].peripheral.name,
output: wired.output,
})
}
pub fn drain_clocks(&mut self) -> impl Iterator<Item = ClockChange> + '_ {
self.effects.clocks.drain(..).map(|(_at, change)| change)
}
pub(crate) fn next_pending(&mut self) -> Option<PendingAccess> {
self.effects.accesses.pop_front()
}
pub(crate) fn next_answer(&mut self) -> Option<Answer> {
self.effects.answers.pop_front()
}
pub(crate) fn answer(&mut self, access: &PendingAccess, found: Option<u64>) {
self.effects.answers.push_back(Answer {
at: access.at,
owner: access.owner,
token: access.token,
found,
});
}
pub(crate) fn region_holding(&self, address: u64) -> Option<&'static Region> {
self.memory.iter().find(|region| region.contains(address))
}
}
pub(crate) struct Located {
pub(crate) slot: usize,
pub(crate) row: usize,
pub(crate) lane: Lane,
}
pub(crate) struct Lane {
pub(crate) shift: u32,
}