use crate::chip::parallel;
use crate::device::{
ClockChange, DeviceContext, ErasedDevice, InterruptChange, LineChange, PendingAccess,
RegisterFile, RequestChange, Requested, ResetCause, Told,
};
use crate::emulator::{AccessKind, Error as EmulatorError, Memory};
use crate::snapshot::{Reader, Store, Writer};
use crate::soc::{
Bus, Cpu, Error, Fault, NO_REGISTERS, RegisterSpec, SAME_INSTANT_LIMIT, SETTLE_LIMIT, Width,
Window,
};
use crate::time::{EventId, VirtualNanos};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Refused {
pub by: &'static str,
pub address: u64,
pub fault: Fault,
}
pub trait Mcu {
fn bus(&self) -> &Bus;
fn bus_mut(&mut self) -> &mut Bus;
fn reset(&mut self, at: VirtualNanos, cause: ResetCause);
fn poll(&mut self, now: VirtualNanos);
fn split(&mut self, slot: usize) -> (&mut Bus, Option<&mut dyn ErasedDevice>);
}
pub trait Operate: Mcu {
fn reset_every_model(&mut self) {
self.bus_mut().reset();
for slot in 0..self.bus().slots().len() {
if let (_, Some(model)) = self.split(slot) {
model.reset();
}
}
}
fn poll_every_model(&mut self, now: VirtualNanos) {
for slot in 0..self.bus().slots().len() {
let (bus, model) = self.split(slot);
let Some(model) = model else {
continue;
};
let (carried, mut context) = bus.call(now, slot);
model.poll(carried.registers_mut(), &mut context);
}
}
fn check(&mut self) -> Result<(), Error> {
for index in 0..self.bus().slots().len() {
let (bus, model) = self.split(index);
let Some(model) = model else {
continue;
};
let carried = bus.slots()[index].peripheral();
if !std::ptr::eq(model.layout(), carried.registers) {
return Err(Error::LayoutMismatch {
instance: carried.name,
});
}
}
if let Some(router) = self.bus().router()
&& let (bus, None) = self.split(router)
{
return Err(Error::UnheardRouter {
instance: bus.slots()[router].peripheral().name,
});
}
let programmers: Vec<usize> = (self.bus().memory().iter())
.filter_map(|region| self.bus().programmer_of(region.base, Width::W8))
.collect();
for programmer in programmers {
if let (bus, None) = self.split(programmer) {
return Err(Error::UnheardProgrammer {
instance: bus.slots()[programmer].peripheral().name,
});
}
}
Ok(())
}
fn cpu(&self) -> Cpu {
self.bus().cpu()
}
fn next_event(&self) -> Option<VirtualNanos> {
self.bus().next_event()
}
fn read(&mut self, at: VirtualNanos, address: u64, width: Width) -> Result<u64, Fault> {
let located = match self.bus().locate(address, width, AccessKind::Read) {
Err(Fault::Unmapped { .. }) if self.bus().undefined_at(address).is_some() => {
self.bus_mut().answered_undefined(address);
return Ok(0);
}
located => located?,
};
let (bus, model) = self.split(located.slot);
let (slot, context) = bus.call(at, located.slot);
let mut context = context.serving(width);
if let Some(offset) = located.window {
let chip = model.ok_or(Fault::Unmapped { address })?;
return Ok(chip.read_window(offset, width, &mut context) & width.mask());
}
let backed = model.is_some();
let answered =
model.and_then(|model| model.read(located.row, slot.registers_mut(), &mut context));
let value = answered.unwrap_or_else(|| slot.registers_mut().bus_read(located.row));
if !backed {
self.bus_mut().answered_unbacked(located.slot, located.row);
}
Ok((value >> located.lane.shift) & width.mask())
}
fn peek(&mut self, at: VirtualNanos, address: u64, width: Width) -> Result<u64, Fault> {
let located = match self.bus().locate_ungated(address, width, AccessKind::Read) {
Err(Fault::Unmapped { .. }) if self.bus().undefined_at(address).is_some() => {
return Ok(0);
}
located => located?,
};
let (bus, model) = self.split(located.slot);
if let Some(offset) = located.window {
let chip = model.ok_or(Fault::Unmapped { address })?;
return chip
.peek_window(offset, width, at)
.map(|value| value & width.mask())
.ok_or(Fault::Unmapped { address });
}
let registers = bus.slots()[located.slot].registers();
let value = model
.and_then(|model| model.peek(located.row, registers, at))
.unwrap_or_else(|| registers.bus_peek(located.row));
Ok((value >> located.lane.shift) & width.mask())
}
fn write(
&mut self,
at: VirtualNanos,
address: u64,
width: Width,
value: u64,
) -> Result<(), Fault> {
let located = match self.bus().locate(address, width, AccessKind::Write) {
Err(Fault::Unmapped { .. })
if let Some(programmer) = self.bus().programmer_of(address, width) =>
{
let (bus, model) = self.split(programmer);
let (slot, mut context) = bus.call(at, programmer);
let took = model.is_some_and(|model| {
model.write_region(
address,
width,
value & width.mask(),
slot.registers_mut(),
&mut context,
)
});
return took.then_some(()).ok_or(Fault::Unmapped { address });
}
Err(Fault::Unmapped { .. }) if self.bus().undefined_at(address).is_some() => {
self.bus_mut().answered_undefined(address);
return Ok(());
}
located => located?,
};
let (bus, model) = self.split(located.slot);
let (slot, context) = bus.call(at, located.slot);
let mut context = context.serving(width);
if let Some(offset) = located.window {
let chip = model.ok_or(Fault::Unmapped { address })?;
chip.write_window(offset, width, value & width.mask(), &mut context);
return Ok(());
}
let register = &slot.peripheral().registers[located.row];
let neutral = if register.access.is_readable() {
let registers = slot.registers_mut();
registers
.write_rule(located.row)
.neutral(registers.get_row(located.row))
} else {
0
};
let untouched = neutral & !(width.mask() << located.lane.shift);
let written = untouched | ((value & width.mask()) << located.lane.shift);
slot.registers_mut().bus_write(located.row, written);
if let Some(model) = model {
model.write(located.row, written, slot.registers_mut(), &mut context);
} else {
self.bus_mut().answered_unbacked(located.slot, located.row);
}
for held in self.bus().held_in_reset_by(located.slot) {
reset_instance(self, at, held);
}
Ok(())
}
fn advance_to(&mut self, now: VirtualNanos) {
let mut fired_here = 0_u32;
let mut last = None;
while let Some((event, owner, at)) = self.bus_mut().pop_due(now) {
if last == Some(at) {
fired_here += 1;
assert!(
fired_here <= SAME_INSTANT_LIMIT,
"{} scheduled {fired_here} events at {at:?} without advancing the clock",
self.bus().slots()[owner].peripheral().name
);
} else {
last = Some(at);
fired_here = 0;
}
let (bus, model) = self.split(owner);
let (slot, mut context) = bus.call(at, owner);
if let Some(model) = model {
model.on_event(event, slot.registers_mut(), &mut context);
}
}
}
fn settle(&mut self, memory: &mut dyn Memory) -> Result<Vec<Refused>, EmulatorError> {
let mut refused = Vec::new();
for _ in 0..SETTLE_LIMIT {
if self.bus().reset_requested() {
return Ok(refused);
}
if let Some((at, change)) = self.bus_mut().next_clock_change() {
let readers: Vec<usize> = self.bus().readers_of(change).collect();
for reader in readers {
let (bus, model) = self.split(reader);
let (slot, mut context) = bus.call(at, reader);
if let Some(model) = model {
model.on_clock(change.hz, slot.registers_mut(), &mut context);
}
}
continue;
}
if let Some((at, change)) = self.bus_mut().next_request_change() {
for index in 0..self.bus().sinks().len() {
let sink = self.bus().sinks()[index];
let (bus, model) = self.split(sink);
let (slot, mut context) = bus.call(at, sink);
if let Some(model) = model {
model.on_request(change, slot.registers_mut(), &mut context);
}
}
continue;
}
if let Some(router) = self.bus().router()
&& let Some((at, change)) = self.bus_mut().next_interrupt_change()
{
let (bus, model) = self.split(router);
let (slot, mut context) = bus.call(at, router);
if let Some(model) = model {
model.on_interrupt(change, slot.registers_mut(), &mut context);
}
continue;
}
if let Some(answer) = self.bus_mut().next_answer() {
let (bus, model) = self.split(answer.owner);
let (slot, mut context) = bus.call(answer.at, answer.owner);
if let Some(model) = model {
let registers = slot.registers_mut();
if let Some(value) = answer.found {
model.on_access(answer.token, value, registers, &mut context);
} else {
model.on_refused(answer.token, registers, &mut context);
}
}
continue;
}
let Some(access) = self.bus_mut().next_pending() else {
return Ok(refused);
};
match perform(self, access, memory)? {
Ok(value) => self.bus_mut().answer(&access, Some(value)),
Err(fault) => {
refused.push(Refused {
by: self.bus().slots()[access.owner].peripheral().name,
address: access.address,
fault,
});
self.bus_mut().answer(&access, None);
}
}
}
panic!("the models took {SETTLE_LIMIT} steps without settling");
}
fn drain_interrupts(&mut self) -> Vec<InterruptChange> {
self.bus_mut().drain_interrupts().collect()
}
fn drain_lines(&mut self) -> Vec<LineChange> {
self.bus_mut().drain_lines().collect()
}
fn drain_requests(&mut self) -> Vec<RequestChange> {
self.bus_mut().drain_requests().collect()
}
fn drain_clocks(&mut self) -> Vec<ClockChange> {
self.bus_mut().drain_clocks().collect()
}
fn take_reset_request(&mut self) -> Option<ResetCause> {
self.bus_mut().take_reset_request()
}
fn drain_told(&mut self) -> Vec<Told> {
self.bus_mut().drain_told().collect()
}
fn registers(&self, instance: &str) -> Option<&RegisterFile> {
self.bus().registers(instance)
}
}
impl<M: Mcu + ?Sized> Operate for M {}
fn perform<M: Operate + ?Sized>(
mcu: &mut M,
access: PendingAccess,
memory: &mut dyn Memory,
) -> Result<Result<u64, Fault>, EmulatorError> {
let PendingAccess {
at,
kind,
address,
width,
value,
owner,
..
} = access;
let kind = match kind {
Requested::Read => AccessKind::Read,
Requested::Write => AccessKind::Write,
Requested::Program => {
let programmed = mcu.bus().programmer_of(address, width) == Some(owner)
&& memory.program(address, width, value).is_ok();
return Ok(programmed.then_some(0).ok_or(Fault::Unmapped { address }));
}
};
if mcu.bus().region_holding(address, width).is_some() {
return match kind {
AccessKind::Read => memory.read(address, width).map(Ok),
AccessKind::Write => memory.write(address, width, value).map(|()| Ok(0)),
};
}
let served = match kind {
AccessKind::Read => mcu.read(at, address, width),
AccessKind::Write => mcu.write(at, address, width, value).map(|()| 0),
};
mcu.bus_mut().take_undefined();
mcu.bus_mut().take_unbacked();
Ok(served)
}
#[derive(Debug)]
pub struct Single<D> {
pub model: D,
bus: Bus,
slot: usize,
}
impl<D: crate::device::Device + 'static> Single<D> {
pub fn new(
builder: crate::soc::Builder,
instance: &'static str,
model: D,
) -> Result<Self, Error> {
let mut bus = builder.ungated().build()?;
let slot = bus.slot(instance)?;
bus.programmed_only_by(instance);
let mut single = Self { model, bus, slot };
single.check()?;
Ok(single)
}
pub fn registers(&mut self) -> &mut RegisterFile {
self.bus.slot_mut(self.slot).registers_mut()
}
pub fn pair(&mut self) -> (&mut D, &mut RegisterFile) {
let Self { model, bus, slot } = self;
(model, bus.slot_mut(*slot).registers_mut())
}
pub fn drive<T>(
&mut self,
at: VirtualNanos,
input: impl FnOnce(&mut D, &mut RegisterFile, &mut DeviceContext<'_>) -> T,
) -> T {
let Self { model, bus, slot } = self;
let (registers, mut context) = bus.drive(at, *slot);
input(model, registers, &mut context)
}
}
impl<D: crate::device::Device + 'static> Mcu for Single<D> {
fn bus(&self) -> &Bus {
&self.bus
}
fn split(&mut self, slot: usize) -> (&mut Bus, Option<&mut dyn ErasedDevice>) {
let Self {
model,
bus,
slot: mine,
} = self;
let answering: Option<&mut dyn ErasedDevice> = (slot == *mine).then_some(model);
(bus, answering)
}
fn bus_mut(&mut self) -> &mut Bus {
&mut self.bus
}
fn reset(&mut self, _at: VirtualNanos, _cause: ResetCause) {
self.reset_every_model();
}
fn poll(&mut self, now: VirtualNanos) {
self.poll_every_model(now);
}
}
#[derive(Debug)]
pub struct Bare(Bus);
impl Bare {
pub fn new(builder: crate::soc::Builder) -> Result<Self, Error> {
Ok(Self(builder.ungated().build()?))
}
}
impl Mcu for Bare {
fn bus(&self) -> &Bus {
&self.0
}
fn split(&mut self, _slot: usize) -> (&mut Bus, Option<&mut dyn ErasedDevice>) {
(&mut self.0, None)
}
fn bus_mut(&mut self) -> &mut Bus {
&mut self.0
}
fn reset(&mut self, _at: VirtualNanos, _cause: ResetCause) {
self.reset_every_model();
}
fn poll(&mut self, now: VirtualNanos) {
self.poll_every_model(now);
}
}
#[derive(Debug)]
pub struct Wired<M, C> {
pub part: M,
chip: Hung<C>,
slot: usize,
}
impl<M: Mcu, C: parallel::Peer> Wired<M, C> {
pub fn new(mut part: M, window: Window, chip: C) -> Result<Self, Error> {
let slot = part.bus_mut().add_window(window)?;
Ok(Self {
part,
chip: Hung(chip),
slot,
})
}
pub fn chip(&self) -> &C {
&self.chip.0
}
pub fn chip_mut(&mut self) -> &mut C {
&mut self.chip.0
}
}
impl<M: Mcu, C: parallel::Peer> Mcu for Wired<M, C> {
fn bus(&self) -> &Bus {
self.part.bus()
}
fn bus_mut(&mut self) -> &mut Bus {
self.part.bus_mut()
}
fn split(&mut self, slot: usize) -> (&mut Bus, Option<&mut dyn ErasedDevice>) {
if slot == self.slot {
return (self.part.bus_mut(), Some(&mut self.chip));
}
self.part.split(slot)
}
fn reset(&mut self, at: VirtualNanos, cause: ResetCause) {
self.part.reset(at, cause);
self.chip.0.reset();
}
fn poll(&mut self, now: VirtualNanos) {
self.part.poll(now);
let (_, mut context) = self.part.bus_mut().call(now, self.slot);
self.chip.0.poll(&mut context);
}
}
impl<M: Store, C: Store> Store for Wired<M, C> {
fn store(&self, to: &mut Writer) -> Result<(), crate::snapshot::Error> {
self.part.store(to)?;
self.chip.0.store(to)
}
fn load(&mut self, from: &mut Reader) -> Result<(), crate::snapshot::Error> {
self.part.load(from)?;
self.chip.0.load(from)
}
}
#[derive(Debug)]
struct Hung<C>(C);
impl<C: parallel::Peer> ErasedDevice for Hung<C> {
fn layout(&self) -> &'static [RegisterSpec] {
&NO_REGISTERS
}
fn read(&mut self, _: usize, _: &mut RegisterFile, _: &mut DeviceContext<'_>) -> Option<u64> {
None
}
fn write(&mut self, _: usize, _: u64, _: &mut RegisterFile, _: &mut DeviceContext<'_>) {}
fn on_event(&mut self, event: EventId, _: &mut RegisterFile, context: &mut DeviceContext<'_>) {
self.0.on_event(event, context);
}
fn on_request(&mut self, _: RequestChange, _: &mut RegisterFile, _: &mut DeviceContext<'_>) {}
fn on_clock(&mut self, _: u64, _: &mut RegisterFile, _: &mut DeviceContext<'_>) {}
fn poll(&mut self, _: &mut RegisterFile, context: &mut DeviceContext<'_>) {
self.0.poll(context);
}
fn reset(&mut self) {
self.0.reset();
}
fn on_access(&mut self, _: u64, _: u64, _: &mut RegisterFile, _: &mut DeviceContext<'_>) {}
fn on_refused(&mut self, _: u64, _: &mut RegisterFile, _: &mut DeviceContext<'_>) {}
fn read_window(&mut self, offset: u64, width: Width, context: &mut DeviceContext<'_>) -> u64 {
self.0.read(offset, width, context)
}
fn write_window(
&mut self,
offset: u64,
width: Width,
value: u64,
context: &mut DeviceContext<'_>,
) {
self.0.write(offset, width, value, context);
}
fn peek_window(&self, offset: u64, width: Width, now: VirtualNanos) -> Option<u64> {
self.0.peek(offset, width, now)
}
}
fn reset_instance<M: Operate + ?Sized>(mcu: &mut M, at: VirtualNanos, slot: usize) {
mcu.bus_mut().reset_slot(slot);
let (bus, model) = mcu.split(slot);
let (held, mut context) = bus.call(at, slot);
if let Some(model) = model {
model.reset_line(held.registers_mut(), &mut context);
}
for position in 0..held.peripheral().interrupts.len() {
context.lower(position);
}
}