use crate::device::{
ClockChange, ErasedDevice, InterruptChange, PendingAccess, RegisterFile, RequestChange,
ResetCause, Told,
};
use crate::emulator::{AccessKind, Error as EmulatorError, Memory};
use crate::soc::{Bus, Cpu, Error, Fault, Region, SAME_INSTANT_LIMIT, SETTLE_LIMIT, Width};
use crate::time::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 reset(&mut self, _cause: ResetCause) {
self.bus_mut().reset();
for slot in 0..self.bus().slots().len() {
if let (_, Some(model)) = self.split(slot) {
model.reset();
}
}
}
fn split(&mut self, slot: usize) -> (&mut Bus, Option<&mut dyn ErasedDevice>);
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,
});
}
}
Ok(())
}
fn cpu(&self) -> Cpu {
self.bus().cpu()
}
fn memory(&self) -> &'static [Region] {
self.bus().memory()
}
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 = self.bus().locate(address, width, AccessKind::Read)?;
let (bus, model) = self.split(located.slot);
let (slot, mut context) = bus.call(at, located.slot);
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));
Ok((value >> located.lane.shift) & width.mask())
}
fn peek(&mut self, at: VirtualNanos, address: u64, width: Width) -> Result<u64, Fault> {
let located = self
.bus()
.locate_ungated(address, width, AccessKind::Read)?;
let (bus, model) = self.split(located.slot);
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 = self.bus().locate(address, width, AccessKind::Write)?;
let (bus, model) = self.split(located.slot);
let (slot, mut context) = bus.call(at, located.slot);
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);
}
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 poll(&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 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(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_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 bus_mut(&mut self) -> &mut Bus;
fn registers(&self, instance: &str) -> Option<&RegisterFile> {
self.bus().registers(instance)
}
}
fn perform<M: Mcu + ?Sized>(
mcu: &mut M,
access: PendingAccess,
memory: &mut dyn Memory,
) -> Result<Result<u64, Fault>, EmulatorError> {
let PendingAccess {
at,
kind,
address,
width,
value,
..
} = access;
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)),
};
}
Ok(match kind {
AccessKind::Read => mcu.read(at, address, width),
AccessKind::Write => mcu.write(at, address, width, value).map(|()| 0),
})
}
#[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 bus = builder.ungated().build()?;
let slot = bus
.slot_of(instance)
.ok_or(Error::UnknownInstance { 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 crate::device::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
}
}
#[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
}
}