diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index c2cbea4..4c20644 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -55,13 +55,17 @@ use petgraph::{ use std::{ any::Any, borrow::Cow, + cell::RefCell, collections::BTreeSet, fmt, future::{Future, IntoFuture}, marker::PhantomData, mem, ops::IndexMut, + pin::Pin, + rc::Rc, sync::Arc, + task::Poll, }; mod interpreter; @@ -6755,10 +6759,35 @@ impl SimulationModuleState { } } +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +enum WaitTarget { + /// Settle is less than Instant + Settle, + Instant(SimInstant), +} + +impl PartialOrd for WaitTarget { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for WaitTarget { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + match (self, other) { + (WaitTarget::Settle, WaitTarget::Settle) => std::cmp::Ordering::Equal, + (WaitTarget::Settle, WaitTarget::Instant(_)) => std::cmp::Ordering::Less, + (WaitTarget::Instant(_), WaitTarget::Settle) => std::cmp::Ordering::Greater, + (WaitTarget::Instant(l), WaitTarget::Instant(r)) => l.cmp(r), + } + } +} + struct SimulationExternModuleState { module_state: SimulationModuleState, sim: ExternModuleSimulation, - running_generator: Option + 'static>>, + running_generator: Option + 'static>>>, + wait_target: Option, } impl fmt::Debug for SimulationExternModuleState { @@ -6767,6 +6796,7 @@ impl fmt::Debug for SimulationExternModuleState { module_state, sim, running_generator, + wait_target, } = self; f.debug_struct("SimulationExternModuleState") .field("module_state", module_state) @@ -6775,6 +6805,7 @@ impl fmt::Debug for SimulationExternModuleState { "running_generator", &running_generator.as_ref().map(|_| DebugAsDisplay("...")), ) + .field("wait_target", wait_target) .finish() } } @@ -6782,7 +6813,7 @@ impl fmt::Debug for SimulationExternModuleState { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] enum WhichModule { Main, - Extern { index: usize }, + Extern { module_index: usize }, } struct ReadBitFn { @@ -6855,11 +6886,19 @@ impl MaybeNeedsSettleFn<&'_ mut interpreter::State> for ReadFn { } } +struct GeneratorWaker; + +impl std::task::Wake for GeneratorWaker { + fn wake(self: Arc) { + panic!("can't await other kinds of futures in function passed to ExternalModuleSimulation"); + } +} + struct SimulationImpl { state: interpreter::State, io: Expr, main_module: SimulationModuleState, - extern_modules: Vec, + extern_modules: Box<[SimulationExternModuleState]>, needs_settle: bool, trace_decls: TraceModule, traces: SimTraces]>>, @@ -6868,6 +6907,7 @@ struct SimulationImpl { instant: SimInstant, clocks_triggered: Interned<[StatePartIndex]>, breakpoints: Option, + generator_waker: std::task::Waker, } impl fmt::Debug for SimulationImpl { @@ -6891,6 +6931,7 @@ impl SimulationImpl { instant, clocks_triggered, breakpoints: _, + generator_waker: _, } = self; f.debug_struct("Simulation") .field("state", state) @@ -6908,6 +6949,8 @@ impl SimulationImpl { } fn new(compiled: Compiled) -> Self { let io_target = Target::from(compiled.io); + // TODO: add extern_modules + let extern_modules: Box<[SimulationExternModuleState]> = Box::new([]); Self { state: State::new(compiled.insns), io: compiled.io.to_expr(), @@ -6928,8 +6971,7 @@ impl SimulationImpl { ) }), ), - // TODO: add extern_modules - extern_modules: vec![], + extern_modules, needs_settle: true, trace_decls: compiled.base_module.trace_decls, traces: SimTraces(Box::from_iter(compiled.traces.0.iter().map( @@ -6948,6 +6990,7 @@ impl SimulationImpl { instant: SimInstant::START, clocks_triggered: compiled.clocks_triggered, breakpoints: None, + generator_waker: Arc::new(GeneratorWaker).into(), } } fn write_traces( @@ -7086,10 +7129,11 @@ impl SimulationImpl { } } #[track_caller] - fn advance_time(&mut self, duration: SimDuration) { - self.settle(); - self.instant += duration; - self.for_each_trace_writer_storing_error(|this, mut trace_writer_state| { + fn advance_time(this: &Rc>, duration: SimDuration) { + Self::settle(this); + let mut this = this.borrow_mut(); + this.instant += duration; + this.for_each_trace_writer_storing_error(|this, mut trace_writer_state| { match &mut trace_writer_state { TraceWriterState::Decls(_) | TraceWriterState::Init(_) => unreachable!(), TraceWriterState::Running(trace_writer) => { @@ -7100,31 +7144,130 @@ impl SimulationImpl { Ok(trace_writer_state) }); } + #[must_use] + fn yield_advance_time_or_settle( + this: Rc>, + module_index: usize, + duration: Option, + ) -> impl Future + 'static { + struct MyGenerator { + sim: Rc>, + yielded_at_all: bool, + module_index: usize, + target: WaitTarget, + } + impl Future for MyGenerator { + type Output = (); + + fn poll( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> Poll { + let this = &mut *self; + let yielded_at_all = mem::replace(&mut this.yielded_at_all, true); + let mut sim = this.sim.borrow_mut(); + let sim = &mut *sim; + assert!(cx.waker().will_wake(&sim.generator_waker), "can't use ExternModuleSimulationState's methods outside of ExternModuleSimulation"); + if let WaitTarget::Instant(target) = this.target { + if target < sim.instant { + this.target = WaitTarget::Settle; + } else if yielded_at_all && target == sim.instant { + this.target = WaitTarget::Settle; + } + } + if let WaitTarget::Settle = this.target { + if yielded_at_all { + return Poll::Ready(()); + } + } + let wait_target = sim.extern_modules[this.module_index] + .wait_target + .get_or_insert(this.target); + *wait_target = (*wait_target).min(this.target); + Poll::Pending + } + } + let target = duration.map_or(WaitTarget::Settle, |duration| { + WaitTarget::Instant(this.borrow().instant + duration) + }); + MyGenerator { + sim: this, + yielded_at_all: false, + module_index, + target, + } + } + fn get_ready_external_modules(&mut self, run_list: &mut Vec) -> Option { + run_list.clear(); + let mut iter = self.extern_modules.iter_mut().enumerate().filter_map( + |(module_index, extern_module)| Some((module_index, extern_module.wait_target?)), + ); + let (first_module_index, mut wait_target) = iter.next()?; + run_list.push(first_module_index); + for (next_module_index, next_wait_target) in iter { + match next_wait_target.cmp(&wait_target) { + std::cmp::Ordering::Less => run_list.clear(), + std::cmp::Ordering::Equal => {} + std::cmp::Ordering::Greater => continue, + } + run_list.push(next_module_index); + wait_target = next_wait_target; + } + Some(wait_target) + } #[track_caller] - fn settle(&mut self) { + fn settle(this_ref: &Rc>) { + let mut this = this_ref.borrow_mut(); assert!( - self.main_module.uninitialized_ios.is_empty(), + this.main_module.uninitialized_ios.is_empty(), "didn't initialize all inputs", ); + let mut run_list = Vec::new(); + let generator_waker = this.generator_waker.clone(); for _ in 0..100000 { - if !self.needs_settle { + if !this.needs_settle { + if let Some(wait_target) = this.get_ready_external_modules(&mut run_list) { + if wait_target > WaitTarget::Instant(this.instant) { + return; + } + for module_index in run_list.drain(..) { + this.extern_modules[module_index].wait_target = None; + let Some(mut generator) = + this.extern_modules[module_index].running_generator.take() + else { + continue; + }; + drop(this); + let generator = match generator + .as_mut() + .poll(&mut std::task::Context::from_waker(&generator_waker)) + { + Poll::Ready(()) => None, + Poll::Pending => Some(generator), + }; + this = this_ref.borrow_mut(); + this.extern_modules[module_index].running_generator = generator; + } + continue; + } return; } - self.state.setup_call(0); - if self.breakpoints.is_some() { + this.state.setup_call(0); + if this.breakpoints.is_some() { loop { - match self + let this = &mut *this; + match this .state - .run(self.breakpoints.as_mut().expect("just checked")) + .run(this.breakpoints.as_mut().expect("just checked")) { RunResult::Break(break_action) => { println!( "hit breakpoint at:\n{:?}", - self.state.debug_insn_at(self.state.pc), + this.state.debug_insn_at(this.state.pc), ); match break_action { BreakAction::DumpStateAndContinue => { - println!("{self:#?}"); + println!("{this:#?}"); } BreakAction::Continue => {} } @@ -7133,21 +7276,21 @@ impl SimulationImpl { } } } else { - let RunResult::Return(()) = self.state.run(()); + let RunResult::Return(()) = this.state.run(()); } - if self.main_module.did_initial_settle { - self.read_traces::(); + if this.main_module.did_initial_settle { + this.read_traces::(); } else { - self.read_traces::(); + this.read_traces::(); } - self.state.memory_write_log.sort_unstable(); - self.state.memory_write_log.dedup(); - self.main_module.did_initial_settle = true; - self.needs_settle = self + this.state.memory_write_log.sort_unstable(); + this.state.memory_write_log.dedup(); + this.main_module.did_initial_settle = true; + this.needs_settle = this .clocks_triggered .iter() - .any(|i| self.state.small_slots[*i] != 0); - self.for_each_trace_writer_storing_error(|this, trace_writer_state| { + .any(|i| this.state.small_slots[*i] != 0); + this.for_each_trace_writer_storing_error(|this, trace_writer_state| { Ok(match trace_writer_state { TraceWriterState::Decls(trace_writer_decls) => TraceWriterState::Running( this.init_trace_writer(trace_writer_decls.write_decls( @@ -7165,20 +7308,22 @@ impl SimulationImpl { TraceWriterState::Errored(e) => TraceWriterState::Errored(e), }) }); - self.state.memory_write_log.clear(); + this.state.memory_write_log.clear(); } panic!("settle(): took too many steps"); } fn get_module(&self, which_module: WhichModule) -> &SimulationModuleState { match which_module { WhichModule::Main => &self.main_module, - WhichModule::Extern { index } => &self.extern_modules[index].module_state, + WhichModule::Extern { module_index } => &self.extern_modules[module_index].module_state, } } fn get_module_mut(&mut self, which_module: WhichModule) -> &mut SimulationModuleState { match which_module { WhichModule::Main => &mut self.main_module, - WhichModule::Extern { index } => &mut self.extern_modules[index].module_state, + WhichModule::Extern { module_index } => { + &mut self.extern_modules[module_index].module_state + } } } #[track_caller] @@ -7374,14 +7519,31 @@ impl SimulationImpl { ); } #[track_caller] - fn settle_if_needed(&mut self, v: MaybeNeedsSettle) -> O + fn settle_if_needed(this_ref: &Rc>, v: MaybeNeedsSettle) -> O where for<'a> F: MaybeNeedsSettleFn<&'a mut interpreter::State, Output = O>, { match v { MaybeNeedsSettle::NeedsSettle(v) => { - self.settle(); - v.call(&mut self.state) + Self::settle(this_ref); + v.call(&mut this_ref.borrow_mut().state) + } + MaybeNeedsSettle::NoSettleNeeded(v) => v, + } + } + #[track_caller] + async fn yield_settle_if_needed( + this_ref: &Rc>, + module_index: usize, + v: MaybeNeedsSettle, + ) -> O + where + for<'a> F: MaybeNeedsSettleFn<&'a mut interpreter::State, Output = O>, + { + match v { + MaybeNeedsSettle::NeedsSettle(v) => { + Self::yield_advance_time_or_settle(this_ref.clone(), module_index, None).await; + v.call(&mut this_ref.borrow_mut().state) } MaybeNeedsSettle::NoSettleNeeded(v) => v, } @@ -7455,17 +7617,17 @@ impl SimulationImpl { self.trace_writers = trace_writers; retval } - fn close(mut self) -> std::io::Result<()> { - if self.main_module.did_initial_settle { - self.settle(); + fn close(this: Rc>) -> std::io::Result<()> { + if this.borrow().main_module.did_initial_settle { + Self::settle(&this); } - self.close_all_trace_writers() + this.borrow_mut().close_all_trace_writers() } - fn flush_traces(&mut self) -> std::io::Result<()> { - if self.main_module.did_initial_settle { - self.settle(); + fn flush_traces(this_ref: &Rc>) -> std::io::Result<()> { + if this_ref.borrow().main_module.did_initial_settle { + Self::settle(this_ref); } - self.for_each_trace_writer_getting_error( + this_ref.borrow_mut().for_each_trace_writer_getting_error( |this, trace_writer: TraceWriterState| match trace_writer { TraceWriterState::Decls(v) => { let mut v = v.write_decls( @@ -7499,7 +7661,7 @@ impl Drop for SimulationImpl { } pub struct Simulation { - sim_impl: SimulationImpl, + sim_impl: Rc>, io: Expr, } @@ -7546,24 +7708,113 @@ impl fmt::Debug for SortedMapDebug<'_, K, V> { impl fmt::Debug for Simulation { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Self { sim_impl, io } = self; - sim_impl.debug_fmt(Some(io), f) + sim_impl.borrow().debug_fmt(Some(io), f) } } +macro_rules! impl_simulation_methods { + (async_await = ($($async:tt, $await:tt)?)) => { + #[track_caller] + pub $($async)? fn read_bool_or_int(&mut self, io: Expr) -> I::Value { + let retval = self + .sim_impl + .borrow_mut() + .read_bool_or_int(io, WhichModule::Main); + self.settle_if_needed(retval)$(.$await)? + } + #[track_caller] + pub $($async)? fn write_bool_or_int( + &mut self, + io: Expr, + value: impl ToExpr, + ) { + let value = value.to_expr(); + assert_eq!(Expr::ty(io), Expr::ty(value), "type mismatch"); + let value = value + .to_literal_bits() + .expect("the value that is being written to an input must be a literal"); + self.sim_impl.borrow_mut().write_bool_or_int( + io, + I::bits_to_value(Cow::Borrowed(&value)), + WhichModule::Main, + ); + } + #[track_caller] + pub $($async)? fn write_clock(&mut self, io: Expr, value: bool) { + self.sim_impl + .borrow_mut() + .write_bit(Expr::canonical(io), value, WhichModule::Main); + } + #[track_caller] + pub $($async)? fn read_clock(&mut self, io: Expr) -> bool { + let retval = self + .sim_impl + .borrow_mut() + .read_bit(Expr::canonical(io), WhichModule::Main); + self.settle_if_needed(retval)$(.$await)? + } + #[track_caller] + pub $($async)? fn write_bool(&mut self, io: Expr, value: bool) { + self.sim_impl + .borrow_mut() + .write_bit(Expr::canonical(io), value, WhichModule::Main); + } + #[track_caller] + pub $($async)? fn read_bool(&mut self, io: Expr) -> bool { + let retval = self + .sim_impl + .borrow_mut() + .read_bit(Expr::canonical(io), WhichModule::Main); + self.settle_if_needed(retval)$(.$await)? + } + #[track_caller] + pub $($async)? fn write_reset(&mut self, io: Expr, value: bool) { + self.sim_impl + .borrow_mut() + .write_bit(Expr::canonical(io), value, WhichModule::Main); + } + #[track_caller] + pub $($async)? fn read_reset(&mut self, io: Expr) -> bool { + let retval = self + .sim_impl + .borrow_mut() + .read_bit(Expr::canonical(io), WhichModule::Main); + self.settle_if_needed(retval)$(.$await)? + } + #[track_caller] + pub $($async)? fn read(&mut self, io: Expr) -> SimValue { + let retval = self + .sim_impl + .borrow_mut() + .read(Expr::canonical(io), WhichModule::Main); + SimValue::from_canonical(self.settle_if_needed(retval)$(.$await)?) + } + #[track_caller] + pub $($async)? fn write>(&mut self, io: Expr, value: V) { + self.sim_impl.borrow_mut().write( + Expr::canonical(io), + value.into_sim_value(Expr::ty(io)).into_canonical(), + WhichModule::Main, + ); + } + }; +} + impl Simulation { pub fn new(module: Interned>) -> Self { Self::from_compiled(Compiled::new(module)) } pub fn add_trace_writer(&mut self, writer: W) { self.sim_impl + .borrow_mut() .trace_writers .push(TraceWriterState::Decls(DynTraceWriterDecls::new(writer))); } pub fn flush_traces(&mut self) -> std::io::Result<()> { - self.sim_impl.flush_traces() + SimulationImpl::flush_traces(&self.sim_impl) } pub fn close(self) -> std::io::Result<()> { - self.sim_impl.close() + SimulationImpl::close(self.sim_impl) } pub fn canonical(self) -> Simulation { let Self { sim_impl, io } = self; @@ -7586,92 +7837,29 @@ impl Simulation { let sim_impl = SimulationImpl::new(compiled.canonical()); Self { io: Expr::from_bundle(sim_impl.io), - sim_impl, + sim_impl: Rc::new(RefCell::new(sim_impl)), } } #[track_caller] pub fn settle(&mut self) { - self.sim_impl.settle(); + SimulationImpl::settle(&self.sim_impl); } #[track_caller] pub fn advance_time(&mut self, duration: SimDuration) { - self.sim_impl.advance_time(duration); + SimulationImpl::advance_time(&self.sim_impl, duration); } #[track_caller] - pub fn read_bool_or_int(&mut self, io: Expr) -> I::Value { - let retval = self.sim_impl.read_bool_or_int(io, WhichModule::Main); - self.sim_impl.settle_if_needed(retval) - } - #[track_caller] - pub fn write_bool_or_int( - &mut self, - io: Expr, - value: impl ToExpr, - ) { - let value = value.to_expr(); - assert_eq!(Expr::ty(io), Expr::ty(value), "type mismatch"); - let value = value - .to_literal_bits() - .expect("the value that is being written to an input must be a literal"); - self.sim_impl.write_bool_or_int( - io, - I::bits_to_value(Cow::Borrowed(&value)), - WhichModule::Main, - ); - } - #[track_caller] - pub fn write_clock(&mut self, io: Expr, value: bool) { - self.sim_impl - .write_bit(Expr::canonical(io), value, WhichModule::Main); - } - #[track_caller] - pub fn read_clock(&mut self, io: Expr) -> bool { - let retval = self - .sim_impl - .read_bit(Expr::canonical(io), WhichModule::Main); - self.sim_impl.settle_if_needed(retval) - } - #[track_caller] - pub fn write_bool(&mut self, io: Expr, value: bool) { - self.sim_impl - .write_bit(Expr::canonical(io), value, WhichModule::Main); - } - #[track_caller] - pub fn read_bool(&mut self, io: Expr) -> bool { - let retval = self - .sim_impl - .read_bit(Expr::canonical(io), WhichModule::Main); - self.sim_impl.settle_if_needed(retval) - } - #[track_caller] - pub fn write_reset(&mut self, io: Expr, value: bool) { - self.sim_impl - .write_bit(Expr::canonical(io), value, WhichModule::Main); - } - #[track_caller] - pub fn read_reset(&mut self, io: Expr) -> bool { - let retval = self - .sim_impl - .read_bit(Expr::canonical(io), WhichModule::Main); - self.sim_impl.settle_if_needed(retval) - } - #[track_caller] - pub fn read(&mut self, io: Expr) -> SimValue { - let retval = self.sim_impl.read(Expr::canonical(io), WhichModule::Main); - SimValue::from_canonical(self.sim_impl.settle_if_needed(retval)) - } - #[track_caller] - pub fn write>(&mut self, io: Expr, value: V) { - self.sim_impl.write( - Expr::canonical(io), - value.into_sim_value(Expr::ty(io)).into_canonical(), - WhichModule::Main, - ); + fn settle_if_needed(&mut self, v: MaybeNeedsSettle) -> O + where + for<'a> F: MaybeNeedsSettleFn<&'a mut interpreter::State, Output = O>, + { + SimulationImpl::settle_if_needed(&self.sim_impl, v) } + impl_simulation_methods!(async_await = ()); #[doc(hidden)] /// This is explicitly unstable and may be changed/removed at any time pub fn set_breakpoints_unstable(&mut self, pcs: HashSet, trace: bool) { - self.sim_impl.breakpoints = Some(BreakpointsSet { + self.sim_impl.borrow_mut().breakpoints = Some(BreakpointsSet { last_was_break: false, set: pcs, trace, @@ -7679,22 +7867,74 @@ impl Simulation { } } -#[derive(Debug)] pub struct ExternModuleSimulationState { + sim_impl: Rc>, + module_index: usize, io_ty: T, } +impl fmt::Debug for ExternModuleSimulationState { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + sim_impl: _, + module_index, + io_ty, + } = self; + f.debug_struct("ExternModuleSimulationState") + .field("sim_impl", &DebugAsDisplay("...")) + .field("module_index", module_index) + .field("io_ty", io_ty) + .finish() + } +} + impl ExternModuleSimulationState { pub fn canonical(self) -> ExternModuleSimulationState { + let Self { + sim_impl, + module_index, + io_ty, + } = self; ExternModuleSimulationState { - io_ty: Bundle::from_canonical(self.io_ty.canonical()), + sim_impl, + module_index, + io_ty: Bundle::from_canonical(io_ty.canonical()), } } pub fn from_canonical(sim: ExternModuleSimulationState) -> Self { + let ExternModuleSimulationState { + sim_impl, + module_index, + io_ty, + } = sim; Self { - io_ty: T::from_canonical(sim.io_ty.canonical()), + sim_impl, + module_index, + io_ty: T::from_canonical(io_ty.canonical()), } } + #[track_caller] + pub async fn settle(&mut self) { + SimulationImpl::yield_advance_time_or_settle(self.sim_impl.clone(), self.module_index, None) + .await + } + #[track_caller] + pub async fn advance_time(&mut self, duration: SimDuration) { + SimulationImpl::yield_advance_time_or_settle( + self.sim_impl.clone(), + self.module_index, + Some(duration), + ) + .await + } + #[track_caller] + async fn settle_if_needed(&mut self, v: MaybeNeedsSettle) -> O + where + for<'a> F: MaybeNeedsSettleFn<&'a mut interpreter::State, Output = O>, + { + SimulationImpl::yield_settle_if_needed(&self.sim_impl, self.module_index, v).await + } + impl_simulation_methods!(async_await = (async, await)); } pub trait ExternModuleSimGenerator: