use std::any::Any;
use std::collections::{BTreeMap, BTreeSet, VecDeque};
use std::fs;
use std::io::{Read, Write};
use std::net::TcpListener;
use super::{
Advance, Frame, Gap, Instance, LinkError, Manifest, Reached, Report, Served, Set, Told,
Triggers, VERSION, WireError, bytes_of, values_of,
};
use crate::chip::{Timeline, uart};
use crate::edge::Coupled;
use crate::emulator::{
self, Driver, Finished, Host, NO_DEADLINE, Settled, Start, Stop, StopReason, WalkThreads,
};
use crate::image::{self, Image};
use crate::machine::Board;
use crate::mcu::{Mcu, Operate};
use crate::session::{Session, Unimplemented, Unmapped};
use crate::snapshot::{self, Reader, Snapshot, Store, Writer, store};
use crate::soc::Fault;
use crate::symbols::{self, Symbols};
use crate::time::VirtualNanos;
use crate::version::RUNTIME;
#[derive(Debug, Default, serde::Serialize, serde::Deserialize)]
pub struct EnginePort {
toward_firmware: VecDeque<u8>,
from_firmware: Vec<u8>,
}
impl EnginePort {
pub fn deliver(&mut self, bytes: &[u8]) {
self.toward_firmware.extend(bytes);
}
pub fn take_sent(&mut self) -> Vec<u8> {
std::mem::take(&mut self.from_firmware)
}
}
store!(EnginePort);
impl uart::Peer for EnginePort {
fn receive(&mut self, word: u16, _context: &mut Timeline<'_, '_>) {
self.from_firmware.push(word.to_le_bytes()[0]);
}
fn transmit(&mut self, _now: VirtualNanos) -> Option<u16> {
self.toward_firmware.pop_front().map(u16::from)
}
}
fn engine_port<'a>(board: &'a mut dyn Board, name: &str) -> Result<&'a mut EnginePort, LinkError> {
let peer: &mut dyn Any = board.host_mut(name).ok_or_else(|| {
LinkError::Misfit(format!("names a port {name} the board has no host on"))
})?;
peer.downcast_mut::<EnginePort>().ok_or_else(|| {
LinkError::Misfit(format!(
"names a port {name} whose far end isn't the engine"
))
})
}
pub fn attach_engine_ports(board: &mut dyn Board) -> Result<Vec<&'static str>, LinkError> {
let ports: Vec<&'static str> = board.hosts().into_iter().map(|wire| wire.name).collect();
for &port in &ports {
if !board.attach_host(port, Box::new(EnginePort::default())) {
return Err(LinkError::Board(format!(
"names a port {port} it won't take"
)));
}
let handed_back = board.host_mut(port).is_some_and(|peer| {
let peer: &dyn Any = peer;
peer.is::<EnginePort>()
});
if !handed_back {
return Err(LinkError::Board(format!(
"takes a port on {port} and doesn't hand it back from host_mut"
)));
}
}
Ok(ports)
}
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct Held {
pub inputs: Vec<i64>,
pub conditions: Vec<i64>,
pub seed: u64,
pub advances: u64,
}
impl Held {
pub fn at_rest(edge: &dyn Coupled, seed: u64) -> Result<Self, WireError> {
Ok(Self {
inputs: values_of(edge.fields(), &edge.rest_wire())?,
conditions: vec![0; edge.conditions().len()],
seed,
advances: 0,
})
}
}
pub fn deliver(
board: &mut dyn Board,
held: &mut Held,
ports: &[&str],
advance: &Advance,
now: VirtualNanos,
) -> Result<(), LinkError> {
if advance.until >= NO_DEADLINE {
return Err(LinkError::Misfit(format!(
"asked to reach {} ns, and the virtual clock stops short of {} ns",
advance.until.0, NO_DEADLINE.0
)));
}
if advance.until < now {
return Err(LinkError::Misfit(format!(
"asked to reach {} ns when the run already stands at {} ns",
advance.until.0, now.0
)));
}
if advance.ports.len() != ports.len() {
return Err(LinkError::Misfit(format!(
"sent bytes for {} ports and the board has {}",
advance.ports.len(),
ports.len()
)));
}
apply(&mut held.inputs, &advance.inputs, "input")?;
apply(&mut held.conditions, &advance.conditions, "condition")?;
match board.edge() {
Some(edge) => {
if !advance.conditions.is_empty() {
edge.condition_wire(&bytes_of(edge.conditions(), &held.conditions)?)?;
}
if !advance.inputs.is_empty() {
edge.sense_wire(&bytes_of(edge.fields(), &held.inputs)?)?;
}
edge.draw(held.seed, held.advances);
}
None if !advance.inputs.is_empty() || !advance.conditions.is_empty() => {
return Err(LinkError::Board(
"has no edge to sense the inputs or the conditions on".to_owned(),
));
}
None => {}
}
held.advances += 1;
for (name, bytes) in ports.iter().zip(&advance.ports) {
if !bytes.is_empty() {
engine_port(board, name)?.deliver(bytes);
}
}
Ok(())
}
pub(super) fn apply(values: &mut [i64], sets: &[Set], what: &str) -> Result<(), LinkError> {
let count = values.len();
for set in sets {
*values.get_mut(usize::from(set.index)).ok_or_else(|| {
LinkError::Misfit(format!(
"set {what} {} and the board has {count}",
set.index
))
})? = set.value;
}
Ok(())
}
pub fn sent(board: &mut dyn Board, ports: &[&str]) -> Result<Vec<Vec<u8>>, LinkError> {
ports
.iter()
.map(|name| Ok(engine_port(board, name)?.take_sent()))
.collect()
}
#[derive(Debug, Default, serde::Serialize, serde::Deserialize)]
#[serde(bound(deserialize = "'de: 'static"))]
pub struct Gaps {
unmapped: BTreeSet<u64>,
refused: BTreeSet<u64>,
unimplemented: BTreeSet<u64>,
unrouted: BTreeSet<(&'static str, u16)>,
told: BTreeSet<(&'static str, &'static str, &'static str)>,
}
impl Gaps {
pub fn fresh<D: Board + ?Sized>(&mut self, session: &Session<D>) -> Vec<Gap> {
self.fresh_in(
&session.unmapped(),
session.refused(),
session.unimplemented(),
session.unrouted(),
)
}
fn fresh_in(
&mut self,
unmapped: &[Unmapped],
refused: &BTreeMap<u64, Fault>,
unimplemented: &BTreeMap<u64, Unimplemented>,
unrouted: &BTreeSet<(&'static str, u16)>,
) -> Vec<Gap> {
let mut gaps = Vec::new();
for entry in unmapped {
if self.unmapped.insert(entry.address) {
gaps.push(Gap::Unmapped {
address: entry.address,
program_counter: entry.program_counter,
});
}
}
let outstanding = refused.iter().filter(|(_, fault)| !fault.is_shut_gate());
for (&address, fault) in outstanding {
if self.refused.insert(address) {
gaps.push(Gap::Refused {
address,
why: fault.to_string(),
});
}
}
for (&address, entry) in unimplemented {
if self.unimplemented.insert(address) {
gaps.push(Gap::Unimplemented {
instance: entry.instance.to_owned(),
register: entry.register.to_owned(),
address,
program_counter: entry.program_counter,
});
}
}
for &(instance, line) in unrouted {
if self.unrouted.insert((instance, line)) {
gaps.push(Gap::Unrouted {
instance: instance.to_owned(),
line,
});
}
}
gaps
}
pub fn told<D: Board + ?Sized>(&mut self, session: &Session<D>) -> Vec<Told> {
self.told_in(session.told())
}
fn told_in<'a>(&mut self, told: impl Iterator<Item = &'a crate::device::Told>) -> Vec<Told> {
told.filter(|told| {
self.told
.insert((told.instance, told.register, told.reason))
})
.map(|told| Told {
instance: told.instance.to_owned(),
register: told.register.to_owned(),
reason: told.reason.to_owned(),
at: told.at,
})
.collect()
}
}
#[must_use]
pub fn report<D: Board + ?Sized>(session: &Session<D>) -> Report {
let count = |entries: usize| u64::try_from(entries).expect("a count fits");
Report {
resets: session.resets().iter().map(|reset| reset.at).collect(),
unmapped: count(session.unmapped().len()),
refused: count(
session
.refused()
.values()
.filter(|fault| !fault.is_shut_gate())
.count(),
),
unimplemented: count(session.unimplemented().len()),
unrouted: count(session.unrouted().len()),
told: count(session.told().count()),
}
}
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
pub struct Launch<'a> {
pub images: &'a [Image],
pub boot: Option<u64>,
pub debugger: Option<&'a TcpListener>,
pub wait_for_debugger: bool,
pub seed: u64,
pub restore: Option<&'a Snapshot>,
pub kernels: &'a [Box<dyn WalkThreads>],
}
impl<'a> Launch<'a> {
#[must_use]
pub fn new(images: &'a [Image]) -> Self {
Self {
images,
boot: None,
debugger: None,
wait_for_debugger: false,
seed: 0,
restore: None,
kernels: &[],
}
}
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum ServeError {
#[error("{machine} has no edge a plant couples to, so it can't be an engine's target")]
NoEdge {
machine: &'static str,
},
#[error("an engine's target runs a firmware, and it was given no image")]
NoFirmware,
#[error("the engine asked for a snapshot before the run had a stop to take it at")]
SnapshotBeforeStop,
#[error(transparent)]
Link(#[from] LinkError),
#[error(transparent)]
Emulator(#[from] emulator::Error),
#[error(transparent)]
Snapshot(#[from] snapshot::Error),
}
impl From<WireError> for ServeError {
fn from(error: WireError) -> Self {
Self::Link(error.into())
}
}
pub fn serve<R, W>(
mut board: Box<dyn Board>,
target: usize,
launch: &Launch<'_>,
served: Served<R, W>,
) -> Result<(), ServeError>
where
R: Read + Send + 'static,
W: Write + Send + 'static,
{
let name = board.name();
let firmware = launch.images.first().ok_or(ServeError::NoFirmware)?;
let Some(edge) = board.edge() else {
return Err(ServeError::NoEdge { machine: name });
};
edge.check().map_err(emulator::Error::from)?;
let (inputs, outputs, conditions, chips) = (
edge.fields(),
edge.output_fields(),
edge.conditions(),
edge.chips(),
);
let held = Held::at_rest(edge, launch.seed)?;
let ports = attach_engine_ports(&mut *board)?;
let code_address: fn(u64) -> u64 = board
.targets()
.get(target)
.map_or(std::convert::identity, |chosen| chosen.core.code_address);
let count = board.target_names().len();
let mcu = board.mcu(target).ok_or(emulator::Error::NoTarget {
machine: name,
target,
count,
})?;
let manifest = Manifest {
version: VERSION,
machine: name.to_owned(),
runtime: RUNTIME.to_owned(),
snapshot: launch.restore.map(Snapshot::hash),
inputs: inputs.to_vec(),
rest: held.inputs.clone(),
outputs: outputs.to_vec(),
ports: ports.iter().map(ToString::to_string).collect(),
conditions: conditions.to_vec(),
instances: instances(mcu),
chips: chips.iter().map(ToString::to_string).collect(),
lines: mcu.cpu().interrupt_lines,
};
let mut driver = Serving {
served,
held,
ports,
gaps: Gaps::default(),
resets_told: 0,
triggers: Triggers::default(),
symbols: Symbols::read(firmware.bytes(), code_address),
firmware: image::digest(launch.images, launch.boot),
failed: None,
};
let mut session = Session::new(board, target, VirtualNanos(0))?;
let resume = match launch.restore {
Some(restored) => {
let digest = driver.firmware;
Some(restored.restore(&mut session, &digest, &mut driver)?)
}
None => None,
};
let standing = launch.restore.map_or(
Stop {
at: VirtualNanos(0),
instruction_count: 0,
reason: StopReason::Deadline,
},
Snapshot::stop,
);
driver.served.open(&manifest)?;
let first = match driver.served.receive()? {
Frame::Advance(first) => first,
Frame::Shutdown => {
driver.served.report(report(&session))?;
return Ok(());
}
_ => return Err(ServeError::SnapshotBeforeStop),
};
deliver(
session.board_mut(),
&mut driver.held,
&driver.ports,
&first,
standing.at,
)?;
if launch.restore.is_none() && !first.armed.is_empty() {
session.pause_at(Some(standing.at));
}
driver
.triggers
.arm(first.armed, &mut session, standing, driver.symbols.as_ref())?;
session.run_until(first.until);
let mut start = Start::new(launch.images);
start.boot = launch.boot;
start.debugger = launch.debugger;
start.wait_for_debugger = launch.wait_for_debugger;
start.resume = resume.as_ref();
start.kernels = launch.kernels;
let Finished {
mut session,
mut driver,
..
} = emulator::run(session, &start, driver)?;
if let Some(error) = driver.failed.take() {
return Err(error);
}
if let Some(error) = session.take_error() {
return Err(error.into());
}
driver.served.report(report(&session))?;
Ok(())
}
fn instances(mcu: &mut dyn Mcu) -> Vec<Instance> {
mcu.bus()
.peripherals()
.map(|peripheral| Instance {
name: peripheral.name.to_owned(),
registers: peripheral
.registers
.iter()
.map(|register| register.name.to_owned())
.collect(),
})
.collect()
}
fn said(error: &dyn std::error::Error) -> String {
let mut said = error.to_string();
let mut beneath = error.source();
while let Some(cause) = beneath {
said.push_str(": ");
said.push_str(&cause.to_string());
beneath = cause.source();
}
said
}
struct Serving<R: Read, W: Write> {
served: Served<R, W>,
held: Held,
ports: Vec<&'static str>,
gaps: Gaps,
resets_told: usize,
triggers: Triggers,
symbols: Result<Symbols, symbols::Error>,
firmware: [u8; 32],
failed: Option<ServeError>,
}
impl<R: Read, W: Write> Serving<R, W> {
fn answer(
&mut self,
session: &mut Session<dyn Board>,
stop: Stop,
host: &mut dyn Host,
) -> Result<Option<VirtualNanos>, ServeError> {
let edge = session
.board_mut()
.edge()
.ok_or_else(|| LinkError::Board("lost its edge in the middle of a run".to_owned()))?;
let outputs = values_of(edge.output_fields(), &edge.drive_wire())?;
let ports = sent(session.board_mut(), &self.ports)?;
let resets = session.resets()[self.resets_told..]
.iter()
.map(|reset| reset.at)
.collect::<Vec<_>>();
self.resets_told = session.resets().len();
let gaps = self.gaps.fresh(session);
let told = self.gaps.told(session);
self.served.reached(Reached {
stop,
outputs,
ports,
resets,
gaps,
told,
fired: self.triggers.take_fired(),
})?;
loop {
match self.served.receive()? {
Frame::Advance(advance) => {
deliver(
session.board_mut(),
&mut self.held,
&self.ports,
&advance,
stop.at,
)?;
self.triggers
.arm(advance.armed, session, stop, self.symbols.as_ref())?;
self.triggers
.settled(session, host, &mut self.held, Settled::Stop(stop))?;
return Ok(Some(advance.until));
}
Frame::Snapshot(asked) => {
let written = snapshot::take(session, host, stop, &self.firmware, self)
.map_err(|error| said(&error))
.and_then(|taken| {
fs::write(&asked.path, &taken.file).map_err(|error| {
format!("writing the snapshot to {}: {}", asked.path, said(&error))
})?;
Ok(taken.hash)
});
match written {
Ok(hash) => self.served.snapshotted(hash)?,
Err(why) => self.served.refused(why)?,
}
}
_ => return Ok(None),
}
}
}
}
impl<R: Read, W: Write> Store for Serving<R, W> {
fn store(&self, to: &mut Writer) -> Result<(), snapshot::Error> {
let Self {
held,
gaps,
resets_told,
triggers,
served: _,
ports: _,
symbols: _,
firmware: _,
failed: _,
} = self;
to.put(held)?;
to.put(gaps)?;
to.put(resets_told)?;
to.put(triggers)
}
fn load(&mut self, from: &mut Reader) -> Result<(), snapshot::Error> {
let Self {
held,
gaps,
resets_told,
triggers,
served: _,
ports: _,
symbols: _,
firmware: _,
failed: _,
} = self;
*held = from.take()?;
*gaps = from.take()?;
*resets_told = from.take()?;
*triggers = from.take()?;
Ok(())
}
}
impl<R: Read + Send, W: Write + Send> Driver<dyn Board> for Serving<R, W> {
fn on_settled(
&mut self,
session: &mut Session<dyn Board>,
host: &mut dyn Host,
settled: Settled,
) {
if self.failed.is_some() {
return;
}
if let Err(error) = self
.triggers
.settled(session, host, &mut self.held, settled)
{
self.failed = Some(error.into());
}
}
fn on_stop(
&mut self,
session: &mut Session<dyn Board>,
stop: Stop,
host: &mut dyn Host,
) -> Option<VirtualNanos> {
if self.failed.is_some() {
return None;
}
self.answer(session, stop, host).unwrap_or_else(|error| {
self.failed = Some(error);
None
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::chip::uart::Peer;
use crate::machine::{HostWire, Target};
use crate::mcu::Mcu;
use crate::soc::Gate;
#[derive(Debug)]
struct Wired {
peer: Option<Box<dyn Peer>>,
hands_back: bool,
}
impl Wired {
fn handing_back(hands_back: bool) -> Self {
Self {
peer: None,
hands_back,
}
}
}
impl Store for Wired {}
impl Board for Wired {
fn name(&self) -> &'static str {
"wired"
}
fn targets(&mut self) -> Vec<Target<'_>> {
Vec::new()
}
fn target_names(&self) -> Vec<&'static str> {
Vec::new()
}
fn mcu(&mut self, _target: usize) -> Option<&mut dyn Mcu> {
None
}
fn hosts(&self) -> Vec<HostWire> {
vec![HostWire {
name: "wire1",
port: 0,
}]
}
fn attach_host(&mut self, name: &str, peer: Box<dyn Peer>) -> bool {
self.peer = Some(peer);
name == "wire1"
}
fn host_mut(&mut self, name: &str) -> Option<&mut dyn Peer> {
match &mut self.peer {
Some(peer) if self.hands_back && name == "wire1" => Some(peer.as_mut()),
_ => None,
}
}
}
fn quiet(until: u64) -> Advance {
Advance {
until: VirtualNanos(until),
inputs: Vec::new(),
ports: vec![Vec::new()],
conditions: Vec::new(),
armed: Vec::new(),
}
}
#[test]
fn a_board_that_does_not_hand_its_port_back_is_refused_before_the_manifest() {
let mut board = Wired::handing_back(true);
assert_eq!(attach_engine_ports(&mut board).unwrap(), ["wire1"]);
let mut board = Wired::handing_back(false);
assert!(matches!(
attach_engine_ports(&mut board),
Err(LinkError::Board(why)) if why.contains("wire1")
));
}
#[test]
fn an_instant_behind_the_run_or_past_the_clock_is_refused_and_the_one_it_stands_at_is_not() {
let mut board = Wired::handing_back(true);
let ports = attach_engine_ports(&mut board).unwrap();
let now = VirtualNanos(10);
let mut deliver_at =
|until| deliver(&mut board, &mut Held::default(), &ports, &quiet(until), now);
assert!(matches!(deliver_at(9), Err(LinkError::Misfit(_))));
assert!(matches!(
deliver_at(NO_DEADLINE.0),
Err(LinkError::Misfit(_))
));
assert!(matches!(deliver_at(u64::MAX), Err(LinkError::Misfit(_))));
assert!(
deliver_at(10).is_ok(),
"a step of no length is still a step"
);
assert!(deliver_at(NO_DEADLINE.0 - 1).is_ok());
}
#[test]
fn an_engine_port_hands_the_controller_its_bytes_in_order_and_keeps_what_the_firmware_sent() {
let mut port = EnginePort::default();
port.deliver(b"ab");
port.deliver(b"c");
let taken: Vec<Option<u16>> = (0..4).map(|_| port.transmit(VirtualNanos(0))).collect();
assert_eq!(
taken,
[
Some(u16::from(b'a')),
Some(u16::from(b'b')),
Some(u16::from(b'c')),
None
]
);
port.from_firmware.extend([1, 2]);
assert_eq!(port.take_sent(), [1, 2]);
assert!(port.take_sent().is_empty(), "each byte is told once");
}
#[test]
fn each_gap_is_told_once_the_first_time_it_is_met_and_a_shut_gate_is_none() {
let mut gaps = Gaps::default();
let unmapped = [Unmapped {
address: 0x1000,
program_counter: 0x20,
}];
let refused = BTreeMap::from([
(0x2000, Fault::Unmapped { address: 0x2000 }),
(
0x2400,
Fault::Gated {
instance: "TIMER",
register: "CONTROL",
by: Gate {
instance: "CLOCKS",
register: "ENABLE",
field: "TIMEREN",
},
},
),
]);
let unimplemented = BTreeMap::from([(
0x3000,
Unimplemented {
instance: "TIMER",
register: "CONTROL",
program_counter: 0x30,
reads: 1,
writes: 0,
written: None,
},
)]);
let unrouted = BTreeSet::from([("TIMER", 7)]);
let first = gaps.fresh_in(&unmapped, &refused, &unimplemented, &unrouted);
assert_eq!(
first,
[
Gap::Unmapped {
address: 0x1000,
program_counter: 0x20
},
Gap::Refused {
address: 0x2000,
why: "no register at 0x2000".to_owned()
},
Gap::Unimplemented {
instance: "TIMER".to_owned(),
register: "CONTROL".to_owned(),
address: 0x3000,
program_counter: 0x30
},
Gap::Unrouted {
instance: "TIMER".to_owned(),
line: 7
},
]
);
assert!(
gaps.fresh_in(&unmapped, &refused, &unimplemented, &unrouted)
.is_empty(),
"nothing new the second time"
);
}
#[test]
fn each_thing_a_model_told_crosses_once() {
let mut gaps = Gaps::default();
let told = |register, at| crate::device::Told {
instance: "BUS",
register,
reason: "the model says no",
at: VirtualNanos(at),
};
let first = [told("CONTROL", 5)];
assert_eq!(
gaps.told_in(first.iter()),
[Told {
instance: "BUS".to_owned(),
register: "CONTROL".to_owned(),
reason: "the model says no".to_owned(),
at: VirtualNanos(5),
}]
);
let later = [told("CONTROL", 5), told("STATUS", 9)];
assert_eq!(
gaps.told_in(later.iter())
.iter()
.map(|told| told.register.as_str())
.collect::<Vec<_>>(),
["STATUS"],
"only what hasn't crossed yet"
);
}
}