#[cfg(feature = "stm32f427")]
pub mod stm32f427;
use std::fmt;
use std::num::{NonZeroU32, NonZeroU64};
use crate::device::{
Answer, ClockChange, DeviceContext, Effects, InterruptChange, PendingAccess, RegisterFile,
RequestChange, ResetCause, Told,
};
use crate::emulator::AccessKind;
use crate::snapshot::store;
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,
pub endian: Endian,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum Endian {
#[default]
Little,
Big,
}
#[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, serde::Serialize, serde::Deserialize)]
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,
}
impl RegionKind {
#[must_use]
pub const fn is_writable(self) -> bool {
matches!(self, Self::Ram)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub struct Region {
pub name: &'static str,
pub base: u64,
pub size: u64,
pub kind: RegionKind,
pub blocks: &'static [EraseBlocks],
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EraseBlocks {
pub size: NonZeroU64,
pub count: NonZeroU32,
}
impl Region {
pub const fn flash(name: &'static str, base: u64, size: u64) -> Self {
Self {
name,
base,
size,
kind: RegionKind::Flash,
blocks: &[],
}
}
pub const fn ram(name: &'static str, base: u64, size: u64) -> Self {
Self {
name,
base,
size,
kind: RegionKind::Ram,
blocks: &[],
}
}
#[must_use]
pub const fn erased_in(self, blocks: &'static [EraseBlocks]) -> Self {
Self { blocks, ..self }
}
#[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,
blocks: &[],
}
}
}
#[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("the erase blocks of memory region {region} don't add up to its size")]
EraseBlocksMismatch {
region: &'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, and 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("{instance}.{register}.{field} does not fit its register")]
FieldOutOfRange {
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, serde::Serialize, serde::Deserialize)]
#[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,
},
}
impl Fault {
#[must_use]
pub fn is_shut_gate(&self) -> bool {
matches!(self, Self::Gated { .. })
}
}
struct Declaration {
instance: &'static str,
register: &'static str,
field: &'static FieldSpec,
}
fn row_named(
table: &'static [RegisterSpec],
instance: &'static str,
register: &'static str,
) -> Result<usize, Error> {
table
.iter()
.position(|candidate| candidate.name == register)
.ok_or(Error::UnknownRegister { instance, register })
}
fn correct_field(slots: &mut [Slot], correction: &FieldCorrection) -> Result<(), Error> {
let slot = slot_named_mut(slots, correction.instance)?;
let row = row_named(
slot.peripheral.registers,
correction.instance,
correction.register,
)?;
let register = &slot.peripheral.registers[row];
let past = u32::from(correction.corrected.bit) + u32::from(correction.corrected.width);
if past > u32::from(register.width.bits()) {
return Err(Error::FieldOutOfRange {
instance: correction.instance,
register: correction.register,
field: correction.field,
});
}
if !register
.fields
.iter()
.any(|field| field.name == correction.field)
{
return Err(Error::UnknownField {
instance: correction.instance,
register: correction.register,
field: correction.field,
});
}
slot.registers
.correct_field(row, correction.field, correction.corrected);
Ok(())
}
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 = row_named(
slots[by_slot].peripheral.registers,
gate.instance,
gate.register,
)?;
let mask = slots[by_slot]
.registers
.fields(by_row)
.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| row_named(slots[gated].peripheral.registers, spec.instance, 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, serde::Serialize, serde::Deserialize)]
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,
}
store!(Slot {
values: [],
parts: [registers],
built: [peripheral, gates],
});
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>,
field_corrections: Vec<FieldCorrection>,
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,
}
struct FieldCorrection {
instance: &'static str,
register: &'static str,
field: &'static str,
corrected: FieldSpec,
}
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 field(
mut self,
instance: &'static str,
register: &'static str,
field: &'static str,
corrected: FieldSpec,
) -> Self {
self.field_corrections.push(FieldCorrection {
instance,
register,
field,
corrected,
});
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 });
}
check_erase_blocks(self.memory)?;
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 = row_named(
instance.peripheral.registers,
correction.instance,
correction.register,
)?;
let register = &instance.peripheral.registers[row];
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 = row_named(
instance.peripheral.registers,
correction.instance,
correction.register,
)?;
instance.registers.correct_write(row, correction.rule);
}
for correction in &self.field_corrections {
correct_field(&mut slots, correction)?;
}
for declaration in &self.declarations {
let instance = slot_named_mut(&mut slots, declaration.instance)?;
let row = row_named(
instance.peripheral.registers,
declaration.instance,
declaration.register,
)?;
let past = u32::from(declaration.field.bit) + u32::from(declaration.field.width);
if past > u32::from(instance.peripheral.registers[row].width.bits()) {
return Err(Error::FieldOutOfRange {
instance: declaration.instance,
register: declaration.register,
field: declaration.field.name,
});
}
instance.registers.declare_field(row, declaration.field);
}
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();
}
let spans = slots
.iter()
.map(|slot| (slot.peripheral.base, slot.peripheral.size))
.collect();
Ok(Bus {
cpu: self.cpu,
memory: self.memory,
spans,
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_erase_blocks(memory: &[Region]) -> Result<(), Error> {
let covered = |region: &Region| {
region.blocks.iter().try_fold(0_u64, |covered, blocks| {
blocks
.size
.get()
.checked_mul(u64::from(blocks.count.get()))
.and_then(|bytes| covered.checked_add(bytes))
})
};
match memory
.iter()
.find(|region| !region.blocks.is_empty() && covered(region) != Some(region.size))
{
Some(region) => Err(Error::EraseBlocksMismatch {
region: region.name,
}),
None => Ok(()),
}
}
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>,
spans: Box<[(u64, u64)]>,
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);
store!(Bus {
values: [effects],
parts: [slots],
built: [cpu, memory, spans, sinks, clocks],
});
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(),
field_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();
}
let mut events = std::mem::take(&mut self.effects.events);
events.clear();
self.effects = Effects {
events,
..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.spans.partition_point(|&(base, _)| base <= address);
let index = candidates.checked_sub(1)?;
let (base, size) = self.spans[index];
(address - base < size).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 fn drive(
&mut self,
at: VirtualNanos,
slot: usize,
) -> (&mut RegisterFile, DeviceContext<'_>) {
let (slot, context) = self.call(at, slot);
(slot.registers_mut(), context)
}
pub 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> {
self.land::<true>(address, width, kind)
}
pub(crate) fn locate_ungated(
&self,
address: u64,
width: Width,
kind: AccessKind,
) -> Result<Located, Fault> {
self.land::<false>(address, width, kind)
}
fn land<const GATED: bool>(
&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 GATED
&& 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,
});
}
let lane = match self.cpu.endian {
Endian::Little => byte_in_register,
Endian::Big => register.width.bytes() - byte_in_register - width.bytes(),
};
Ok(Located {
slot: index,
row,
lane: Lane {
shift: lane * 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 fn drain_told(&mut self) -> impl Iterator<Item = Told> + '_ {
self.effects.told.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 reset_requested(&self) -> bool {
self.effects.reset.is_some()
}
pub fn take_reset_request(&mut self) -> Option<ResetCause> {
self.effects.reset.take()
}
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, width: Width) -> Option<&'static Region> {
let last = address.checked_add(u64::from(width.bytes()) - 1)?;
self.memory
.iter()
.find(|region| region.contains(address) && region.contains(last))
}
}
pub(crate) struct Located {
pub(crate) slot: usize,
pub(crate) row: usize,
pub(crate) lane: Lane,
}
pub(crate) struct Lane {
pub(crate) shift: u32,
}
#[cfg(test)]
mod tests {
use super::*;
const ENABLE: FieldSpec = FieldSpec {
name: "EN",
bit: 0,
width: 1,
access: Access::ReadWrite,
read: ReadAction::None,
write: WriteModify::None,
};
const fn register(
name: &'static str,
offset: u32,
fields: &'static [FieldSpec],
) -> RegisterSpec {
RegisterSpec {
name,
offset,
width: Width::W32,
reset: 0,
access: Access::ReadWrite,
read: ReadAction::None,
write: WriteModify::None,
fields,
}
}
static CLOCKS: [RegisterSpec; 1] = [register("ENR", 0x0, &[ENABLE])];
static PORT: [RegisterSpec; 2] = [register("DR", 0x0, &[]), register("SR", 0x4, &[])];
static MEMORY: [Region; 1] = [Region::ram("sram", 0x2000_0000, 4 << 10)];
const fn blocks(size: u64, count: u32) -> EraseBlocks {
match (NonZeroU64::new(size), NonZeroU32::new(count)) {
(Some(size), Some(count)) => EraseBlocks { size, count },
_ => panic!("a block has bytes"),
}
}
static WHOLE: [Region; 2] = [
Region::flash("flash", 0x0800_0000, 0x1_8000)
.erased_in(&[blocks(0x4000, 2), blocks(0x1_0000, 1)]),
Region::flash("plain", 0x0900_0000, 0x1000),
];
static SHORT: [Region; 1] =
[Region::flash("short", 0x0800_0000, 0x1_8000).erased_in(&[blocks(0x4000, 2)])];
static OVER: [Region; 1] = [Region::flash("over", 0x0800_0000, 0x1_8000)
.erased_in(&[blocks(0x4000, 2), blocks(0x1_0000, 2)])];
static WRAPS: [Region; 1] = [Region::flash("wraps", 0x0800_0000, 0x1_8000)
.erased_in(&[blocks(u64::MAX, u32::MAX), blocks(0x1_8001, 1)])];
#[test]
fn erase_blocks_that_dont_cover_their_region_are_refused() {
let cpu = Cpu {
name: "core",
fpu: false,
interrupt_lines: 16,
endian: Endian::Little,
};
let build = |memory: &'static [Region]| Bus::builder(cpu, memory, []).build().map(drop);
assert_eq!(build(&WHOLE), Ok(()));
for (memory, region) in [(&SHORT, "short"), (&OVER, "over"), (&WRAPS, "wraps")] {
assert_eq!(build(memory), Err(Error::EraseBlocksMismatch { region }));
}
}
fn gated() -> Bus {
let cpu = Cpu {
name: "core",
fpu: false,
interrupt_lines: 16,
endian: Endian::Little,
};
let instance = |name, base, registers: &'static [RegisterSpec]| Peripheral {
name,
base,
size: 0x100,
interrupts: &[],
registers,
};
Bus::builder(
cpu,
&MEMORY,
[
instance("CLOCKS", 0x4000_0000, &CLOCKS),
instance("PORT", 0x4000_1000, &PORT),
],
)
.gate("PORT", Gate::by("CLOCKS", "ENR", "EN"))
.build()
.unwrap()
}
#[test]
fn locate_ungated_finds_what_locate_finds_and_skips_the_gate() {
let mut bus = gated();
let landing = |located: Result<Located, Fault>| {
located.map(|located| (located.slot, located.row, located.lane.shift))
};
let status_high_half = (0x4000_1006, Width::W16);
for kind in [AccessKind::Read, AccessKind::Write] {
let (address, width) = status_high_half;
assert!(
matches!(bus.locate(address, width, kind), Err(Fault::Gated { .. })),
"{kind:?}"
);
assert_eq!(
landing(bus.locate_ungated(address, width, kind)),
Ok((1, 1, 16))
);
}
assert_eq!(
landing(bus.locate_ungated(0x4000_1006, Width::W32, AccessKind::Read)),
Err(Fault::Misaligned {
address: 0x4000_1006,
width: Width::W32
})
);
assert_eq!(
landing(bus.locate_ungated(0x4000_1008, Width::W32, AccessKind::Read)),
Err(Fault::Unmapped {
address: 0x4000_1008
})
);
bus.slot_mut(0).registers_mut().bus_write(0, ENABLE.mask());
let (address, width) = status_high_half;
assert_eq!(
landing(bus.locate(address, width, AccessKind::Read)),
landing(bus.locate_ungated(address, width, AccessKind::Read)),
"the gate open"
);
}
}