use crate::device::{ErasedDevice, InterruptChange, RegisterFile};
use crate::soc::{Access, Bus, Cpu, Error, Fault, Region, SAME_INSTANT_LIMIT, Width};
use crate::time::VirtualNanos;
pub trait Mcu {
fn bus(&self) -> &Bus;
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, Access::is_readable)?;
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 write(
&mut self,
at: VirtualNanos,
address: u64,
width: Width,
value: u64,
) -> Result<(), Fault> {
let located = self.bus().locate(address, width, Access::is_writable)?;
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,
"a model scheduled {fired_here} events at {at:?} without advancing the clock"
);
} 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 drain_interrupts(&mut self) -> Vec<InterruptChange> {
self.bus_mut().drain_interrupts().collect()
}
fn bus_mut(&mut self) -> &mut Bus;
fn registers(&self, instance: &str) -> Option<&RegisterFile> {
self.bus().registers(instance)
}
}
#[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.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 (carried, mut context) = bus.call(at, *slot);
input(model, carried.registers_mut(), &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.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
}
}