use std::any::Any;
use std::collections::BTreeMap;
use std::sync::OnceLock;
use crate::emulator::{Access, Driver, Hit, Host, Settled, Stop, Watch};
use crate::machine::Board;
use crate::session::Session;
use crate::snapshot;
use crate::soc::Width;
use crate::time::VirtualNanos;
pub(super) trait Driven: Send {
fn first_deadline(&mut self) -> VirtualNanos;
fn access(&mut self, access: Access, host: &mut dyn Host) -> (u64, VirtualNanos, bool);
fn refused(&mut self) -> bool;
fn unwatched(&mut self, watch: Watch);
fn stop(&mut self, stop: Stop, host: &mut dyn Host) -> Option<VirtualNanos>;
fn hit(&mut self, hit: Hit, host: &mut dyn Host) -> VirtualNanos;
fn into_any(self: Box<Self>) -> Box<dyn Any + Send>;
fn peek(&mut self, _at: VirtualNanos, _address: u64, _width: Width) -> Option<u64> {
None
}
fn console(&mut self, _at: VirtualNanos, _instruction_count: u64, _bytes: &[u8]) -> bool {
true
}
fn debugger_print(&mut self, _at: VirtualNanos, _instruction_count: u64, _text: &[u8]) {}
fn debugger_attached(&mut self, _at: VirtualNanos, _instruction_count: u64, _attached: bool) {}
fn may_rewind(&self) -> bool {
false
}
fn keep(&self) -> Result<Kept, snapshot::Error> {
Err(snapshot::Error::Failed)
}
fn put_back(&mut self, _kept: &Kept, _host: &mut dyn Host) -> Result<(), snapshot::Error> {
Err(snapshot::Error::Failed)
}
fn retracing(&self) -> bool {
false
}
fn new_way(&mut self, _at: VirtualNanos) {}
}
#[derive(Debug)]
pub(super) struct Kept {
session: Vec<u8>,
last: Option<Stop>,
called: u64,
read: OnceLock<&'static [u8]>,
}
#[cfg(fuzzing)]
impl Kept {
pub(super) fn nothing() -> Self {
Self {
session: Vec::new(),
last: None,
called: 0,
read: OnceLock::new(),
}
}
}
pub(super) struct Run<D: Board + ?Sized, R: Driver<D>> {
pub(super) session: Session<D>,
pub(super) driver: R,
pub(super) last: Option<Stop>,
pub(super) resuming: Option<Stop>,
pub(super) called: u64,
pub(super) furthest: u64,
pub(super) answered: BTreeMap<u64, VirtualNanos>,
}
impl<D: Board + ?Sized, R: Driver<D>> Run<D, R> {
pub(super) fn new(session: Session<D>, driver: R, resuming: Option<Stop>) -> Self {
Self {
session,
driver,
last: None,
resuming,
called: 0,
furthest: 0,
answered: BTreeMap::new(),
}
}
fn next_deadline(&mut self) -> VirtualNanos {
match self.resuming {
Some(taken) => taken.at,
None => self.session.next_deadline(),
}
}
fn heard(&mut self) -> bool {
let fresh = self.called >= self.furthest;
self.called += 1;
self.furthest = self.furthest.max(self.called);
fresh
}
}
impl<D: Board + ?Sized + 'static, R: Driver<D> + 'static> Driven for Run<D, R> {
fn first_deadline(&mut self) -> VirtualNanos {
self.next_deadline()
}
fn access(&mut self, access: Access, host: &mut dyn Host) -> (u64, VirtualNanos, bool) {
let (value, _) = self.session.access(access, host);
let failed = self.session.take_failure();
if self.heard() {
self.driver
.on_settled(&mut self.session, host, Settled::Access { access, value });
}
self.session.settle_watches(host);
(value, self.next_deadline(), failed)
}
fn refused(&mut self) -> bool {
self.session.take_refused()
}
fn unwatched(&mut self, watch: Watch) {
self.session.refuse_watch(watch);
}
fn hit(&mut self, hit: Hit, host: &mut dyn Host) -> VirtualNanos {
if self.heard() {
self.driver
.on_settled(&mut self.session, host, Settled::Hit(hit));
}
self.session.settle_watches(host);
self.next_deadline()
}
fn stop(&mut self, stop: Stop, host: &mut dyn Host) -> Option<VirtualNanos> {
if let Some(taken) = self.resuming.take() {
assert!(
stop.at == taken.at && stop.instruction_count == taken.instruction_count,
"resumed at {} ns after {} instructions, and the snapshot was taken at {} ns after {}",
stop.at.0,
stop.instruction_count,
taken.at.0,
taken.instruction_count
);
self.driver
.on_settled(&mut self.session, host, Settled::Stop(stop));
self.session.settle_watches(host);
let until = self.session.until();
return Some(
self.session
.resume(until, taken.at, taken.instruction_count, host),
);
}
self.session.stop(stop, host);
if self.heard() {
self.driver
.on_settled(&mut self.session, host, Settled::Stop(stop));
}
self.session.settle_watches(host);
if !self.session.reached(&stop) {
return Some(self.session.next_deadline());
}
let call = self.called;
let until = if self.heard() {
let until = self.driver.on_stop(&mut self.session, stop, host);
if let Some(until) = until
&& self.driver.may_rewind()
{
self.answered.insert(call, until);
}
until
} else {
self.answered.get(&call).copied()
};
self.last = until.is_none().then_some(stop);
let until = until?;
self.session.settle_watches(host);
Some(
self.session
.resume(until, stop.at, stop.instruction_count, host),
)
}
fn into_any(self: Box<Self>) -> Box<dyn Any + Send> {
self
}
fn peek(&mut self, at: VirtualNanos, address: u64, width: Width) -> Option<u64> {
self.session.peek(at, address, width)
}
fn console(&mut self, at: VirtualNanos, instruction_count: u64, bytes: &[u8]) -> bool {
let heard = self.heard();
if heard {
self.driver.on_console(at, instruction_count, bytes);
}
heard
}
fn debugger_print(&mut self, at: VirtualNanos, instruction_count: u64, text: &[u8]) {
self.driver.on_debugger_print(at, instruction_count, text);
}
fn debugger_attached(&mut self, at: VirtualNanos, instruction_count: u64, attached: bool) {
self.driver
.on_debugger_attached(at, instruction_count, attached);
}
fn may_rewind(&self) -> bool {
self.driver.may_rewind()
}
fn keep(&self) -> Result<Kept, snapshot::Error> {
if self.resuming.is_some() {
return Err(snapshot::Error::Resuming);
}
Ok(Kept {
session: snapshot::stored(&self.session)?,
last: self.last,
called: self.called,
read: OnceLock::new(),
})
}
fn put_back(&mut self, kept: &Kept, host: &mut dyn Host) -> Result<(), snapshot::Error> {
let read = kept
.read
.get_or_init(|| snapshot::keep(kept.session.clone()));
snapshot::load(&mut self.session, read)?;
self.session.settle_watches(host);
self.last = kept.last;
self.called = kept.called;
Ok(())
}
fn retracing(&self) -> bool {
self.called < self.furthest
}
fn new_way(&mut self, at: VirtualNanos) {
self.furthest = self.called;
self.answered.split_off(&self.called);
self.session.new_way(at);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "pixracer")]
use crate::chip::Timeline;
#[cfg(feature = "pixracer")]
use crate::chip::uart;
use crate::device::Level;
use crate::emulator::{
AccessKind, Error, HitKind, InterruptLine, Memory as GuestMemory, StopReason,
};
use crate::machine::all;
#[cfg(feature = "pixracer")]
use crate::machine::pixracer::Pixracer;
struct Nothing;
impl GuestMemory for Nothing {
fn read(&mut self, address: u64, width: Width) -> Result<u64, Error> {
Err(Error::Memory { address, width })
}
fn write(&mut self, address: u64, width: Width, _value: u64) -> Result<(), Error> {
Err(Error::Memory { address, width })
}
}
impl Host for Nothing {
fn set_interrupt(&mut self, _line: InterruptLine, _level: Level) {}
fn request_reset(&mut self) {}
}
impl<D: Board + ?Sized> Driver<D> for Nothing {
fn on_stop(
&mut self,
_session: &mut Session<D>,
_stop: Stop,
_host: &mut dyn Host,
) -> Option<VirtualNanos> {
None
}
}
#[test]
fn an_access_or_a_hit_before_a_resumed_run_is_back_at_its_stop_keeps_the_stop_armed() {
let Some(&(_, build)) = all().first() else {
return;
};
let taken = Stop {
at: VirtualNanos(5_000_000),
instruction_count: 1_250_000,
reason: StopReason::Deadline,
};
let session = Session::new(build().unwrap(), 0, VirtualNanos(1)).unwrap();
let mut run = Run::new(session, Nothing, Some(taken));
assert_eq!(run.first_deadline(), taken.at);
let read = Access {
kind: AccessKind::Read,
address: 0,
width: Width::W32,
value: 0,
instruction_count: taken.instruction_count,
virtual_time: taken.at,
program_counter: None,
};
assert_eq!(run.access(read, &mut Nothing).1, taken.at);
let hit = Hit {
kind: HitKind::Instruction,
address: 0,
at: taken.at,
instruction_count: taken.instruction_count,
};
assert_eq!(run.hit(hit, &mut Nothing), taken.at);
}
struct Pausing(u32);
impl<D: Board + ?Sized> Driver<D> for Pausing {
fn on_settled(&mut self, session: &mut Session<D>, _host: &mut dyn Host, settled: Settled) {
match settled {
Settled::Access { access, .. } => {
session.pause_at(Some(VirtualNanos(access.virtual_time.0 + 1)));
}
Settled::Stop(_) => self.0 += 1,
Settled::Hit(_) => {}
}
}
fn on_stop(
&mut self,
_session: &mut Session<D>,
_stop: Stop,
_host: &mut dyn Host,
) -> Option<VirtualNanos> {
None
}
}
#[test]
fn an_access_answers_the_pause_the_driver_asked_for_there_and_the_pause_is_the_drivers_alone() {
let Some(&(_, build)) = all().first() else {
return;
};
let session = Session::new(build().unwrap(), 0, VirtualNanos(1_000_000)).unwrap();
let mut run = Run::new(session, Pausing(0), None);
let read = Access {
kind: AccessKind::Read,
address: 0,
width: Width::W32,
value: 0,
instruction_count: 100,
virtual_time: VirtualNanos(400),
program_counter: Some(0x1000),
};
assert_eq!(run.access(read, &mut Nothing).1, VirtualNanos(401));
let paused = Stop {
at: VirtualNanos(401),
instruction_count: 101,
reason: StopReason::Deadline,
};
assert!(run.stop(paused, &mut Nothing).is_some(), "the run goes on");
assert_eq!(run.driver.0, 1);
assert_eq!(run.last, None, "and it wasn't the instant asked for");
}
struct CarriesOn;
impl<D: Board + ?Sized> Driver<D> for CarriesOn {
fn on_stop(
&mut self,
_session: &mut Session<D>,
stop: Stop,
_host: &mut dyn Host,
) -> Option<VirtualNanos> {
Some(VirtualNanos(stop.at.0 + 1_000_000))
}
}
#[test]
fn only_the_stop_the_driver_ends_the_run_at_is_kept() {
let Some(&(_, build)) = all().first() else {
return;
};
let at = VirtualNanos(1_000_000);
let reached = Stop {
at,
instruction_count: 250_000,
reason: StopReason::Deadline,
};
let session = Session::new(build().unwrap(), 0, at).unwrap();
let mut run = Run::new(session, CarriesOn, None);
assert!(run.stop(reached, &mut Nothing).is_some(), "the run goes on");
assert_eq!(run.last, None);
let session = Session::new(build().unwrap(), 0, at).unwrap();
let mut run = Run::new(session, Nothing, None);
assert!(run.stop(reached, &mut Nothing).is_none(), "the run ends");
assert_eq!(run.last, Some(reached));
}
#[test]
fn a_driver_that_never_goes_back_has_no_answer_kept() {
let Some(&(_, build)) = all().first() else {
return;
};
let mut until = VirtualNanos(1_000_000);
let session = Session::new(build().unwrap(), 0, until).unwrap();
let mut run = Run::new(session, CarriesOn, None);
for _ in 0..3 {
let reached = Stop {
at: until,
instruction_count: until.0 / 4,
reason: StopReason::Deadline,
};
until = run.stop(reached, &mut Nothing).expect("the run goes on");
}
assert!(run.answered.is_empty());
}
#[cfg(feature = "pixracer")]
#[derive(Debug, Default)]
struct NewWay(Option<VirtualNanos>);
#[cfg(feature = "pixracer")]
impl snapshot::Store for NewWay {
fn store(&self, _to: &mut snapshot::Writer) -> Result<(), snapshot::Error> {
Ok(())
}
fn load(&mut self, _from: &mut snapshot::Reader) -> Result<(), snapshot::Error> {
Ok(())
}
}
#[cfg(feature = "pixracer")]
impl uart::Peer for NewWay {
fn receive(&mut self, _word: u16, _context: &mut Timeline<'_, '_>) {}
fn transmit(&mut self, _now: VirtualNanos) -> Option<u16> {
None
}
fn forget_after(&mut self, now: VirtualNanos) {
self.0 = Some(now);
}
}
#[test]
#[cfg(feature = "pixracer")]
fn a_new_way_reaches_the_boards_hosts() {
let mut board = Pixracer::new().unwrap();
let wire = board.hosts()[0].name;
assert!(board.attach_host(wire, Box::new(NewWay::default())));
let board: Box<dyn Board> = Box::new(board);
let session = Session::new(board, 0, VirtualNanos(1)).unwrap();
let mut run = Run::new(session, Nothing, None);
run.new_way(VirtualNanos(500));
let host: &mut dyn Any = run.session.board_mut().host_mut(wire).unwrap();
let heard = host.downcast_mut::<NewWay>().unwrap();
assert_eq!(heard.0, Some(VirtualNanos(500)));
}
}