diff --git a/crates/fayalite/src/int.rs b/crates/fayalite/src/int.rs index 5d10b29..236f240 100644 --- a/crates/fayalite/src/int.rs +++ b/crates/fayalite/src/int.rs @@ -621,6 +621,12 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed { let bitslice = &BitSlice::::from_slice(&bytes)[..width]; bits.clone_from_bitslice(bitslice); } + fn bits_equal_bigint_wrapping(v: &BigInt, bits: &BitSlice) -> bool { + bits.iter() + .by_vals() + .enumerate() + .all(|(bit_index, bit): (usize, bool)| v.bit(bit_index as u64) == bit) + } fn bits_to_bigint(bits: &BitSlice) -> BigInt { let sign_byte = if Self::Signed::VALUE && bits.last().as_deref().copied().unwrap_or(false) { 0xFF diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index b12e9a8..275b106 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -5904,12 +5904,21 @@ impl SimTraceKind { } } -#[derive(Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Clone, PartialEq, Eq, Hash)] pub struct SimValue { ty: T, bits: BitVec, } +impl fmt::Debug for SimValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SimValue") + .field("ty", &self.ty) + .field("bits", &BitSliceWriteWithBase(&self.bits)) + .finish() + } +} + impl SimValue { #[track_caller] fn to_expr_impl(ty: CanonicalType, bits: &BitSlice) -> Expr { @@ -6795,35 +6804,149 @@ impl SimulationModuleState { } } -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -enum WaitTarget { - /// Settle is less than Instant +#[derive(Copy, Clone, Debug)] +enum WaitTarget { Settle, Instant(SimInstant), + Change { key: ChangeKey, value: ChangeValue }, } -impl PartialOrd for WaitTarget { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) +#[derive(Clone)] +struct EarliestWaitTargets { + settle: bool, + instant: Option, + changes: HashMap, SimValue>, +} + +impl fmt::Debug for EarliestWaitTargets { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_set().entries(self.iter()).finish() } } -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), +impl Default for EarliestWaitTargets { + fn default() -> Self { + Self { + settle: false, + instant: None, + changes: HashMap::new(), } } } +impl EarliestWaitTargets { + fn settle() -> Self { + Self { + settle: true, + instant: None, + changes: HashMap::new(), + } + } + fn instant(instant: SimInstant) -> Self { + Self { + settle: false, + instant: Some(instant), + changes: HashMap::new(), + } + } + fn len(&self) -> usize { + self.settle as usize + self.instant.is_some() as usize + self.changes.len() + } + fn is_empty(&self) -> bool { + self.len() == 0 + } + fn clear(&mut self) { + let Self { + settle, + instant, + changes, + } = self; + *settle = false; + *instant = None; + changes.clear(); + } + fn insert( + &mut self, + value: impl std::borrow::Borrow, ChangeValue>>, + ) where + ChangeValue: std::borrow::Borrow>, + { + let value = value.borrow(); + match value { + WaitTarget::Settle => self.settle = true, + WaitTarget::Instant(instant) => { + if self.instant.is_none_or(|v| v > *instant) { + self.instant = Some(*instant); + } + } + WaitTarget::Change { key, value } => { + self.changes + .entry(*key) + .or_insert_with(|| value.borrow().clone()); + } + } + } + fn convert_earlier_instants_to_settle(&mut self, instant: SimInstant) { + if self.instant.is_some_and(|v| v <= instant) { + self.settle = true; + self.instant = None; + } + } + fn iter<'a>( + &'a self, + ) -> impl Clone + + Iterator, &'a SimValue>> + + 'a { + self.settle + .then_some(WaitTarget::Settle) + .into_iter() + .chain(self.instant.map(|instant| WaitTarget::Instant(instant))) + .chain( + self.changes + .iter() + .map(|(&key, value)| WaitTarget::Change { key, value }), + ) + } +} + +impl>> + Extend, ChangeValue>> for EarliestWaitTargets +{ + fn extend, ChangeValue>>>( + &mut self, + iter: T, + ) { + iter.into_iter().for_each(|v| self.insert(v)) + } +} + +impl<'a, ChangeValue: std::borrow::Borrow>> + Extend<&'a WaitTarget, ChangeValue>> for EarliestWaitTargets +{ + fn extend, ChangeValue>>>( + &mut self, + iter: T, + ) { + iter.into_iter().for_each(|v| self.insert(v)) + } +} + +impl FromIterator for EarliestWaitTargets +where + Self: Extend, +{ + fn from_iter>(iter: T) -> Self { + let mut retval = Self::default(); + retval.extend(iter); + retval + } +} + struct SimulationExternModuleState { module_state: SimulationModuleState, sim: ExternModuleSimulation, running_generator: Option + 'static>>>, - wait_target: Option, + wait_targets: EarliestWaitTargets, } impl fmt::Debug for SimulationExternModuleState { @@ -6832,7 +6955,7 @@ impl fmt::Debug for SimulationExternModuleState { module_state, sim, running_generator, - wait_target, + wait_targets, } = self; f.debug_struct("SimulationExternModuleState") .field("module_state", module_state) @@ -6841,7 +6964,7 @@ impl fmt::Debug for SimulationExternModuleState { "running_generator", &running_generator.as_ref().map(|_| DebugAsDisplay("...")), ) - .field("wait_target", wait_target) + .field("wait_targets", wait_targets) .finish() } } @@ -6896,29 +7019,19 @@ impl MaybeNeedsSettleFn<&'_ mut interpreter::State> for ReadBo struct ReadFn { compiled_value: CompiledValue, io: Expr, + bits: BitVec, } impl MaybeNeedsSettleFn<&'_ mut interpreter::State> for ReadFn { type Output = SimValue; fn call(self, state: &mut interpreter::State) -> Self::Output { - let Self { compiled_value, io } = self; - let mut bits = BitVec::repeat(false, compiled_value.layout.ty.bit_width()); - SimulationImpl::read_write_sim_value_helper( - state, + let Self { compiled_value, - &mut bits, - |_signed, bits, value| ::copy_bits_from_bigint_wrapping(value, bits), - |_signed, bits, value| { - let bytes = value.to_le_bytes(); - let bitslice = BitSlice::::from_slice(&bytes); - bits.clone_from_bitslice(&bitslice[..bits.len()]); - }, - ); - SimValue { - ty: Expr::ty(io), + io, bits, - } + } = self; + SimulationImpl::read_no_settle_helper(state, io, compiled_value, bits) } } @@ -7017,7 +7130,7 @@ impl SimulationImpl { ), sim: simulation, running_generator: None, - wait_target: Some(WaitTarget::Settle), + wait_targets: EarliestWaitTargets::settle(), } }, )); @@ -7200,22 +7313,23 @@ impl SimulationImpl { } #[track_caller] fn advance_time(this_ref: &Rc>, duration: SimDuration) { - let instant = this_ref.borrow().instant + duration; - Self::run_until(this_ref, WaitTarget::Instant(instant)); + let run_target = this_ref.borrow().instant + duration; + Self::run_until(this_ref, run_target); } + /// clears `targets` #[must_use] - fn yield_advance_time_or_settle( + fn yield_wait<'a>( this: Rc>, module_index: usize, - duration: Option, - ) -> impl Future + 'static { - struct MyGenerator { + targets: &'a mut EarliestWaitTargets, + ) -> impl Future + 'a { + struct MyGenerator<'a> { sim: Rc>, yielded_at_all: bool, module_index: usize, - target: WaitTarget, + targets: &'a mut EarliestWaitTargets, } - impl Future for MyGenerator { + impl Future for MyGenerator<'_> { type Output = (); fn poll( @@ -7223,65 +7337,92 @@ impl SimulationImpl { 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; - } + this.targets.convert_earlier_instants_to_settle(sim.instant); + if this.targets.is_empty() { + this.targets.settle = true; } - if let WaitTarget::Settle = this.target { - if yielded_at_all { + if this.targets.settle { + if this.yielded_at_all { + this.targets.clear(); 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); + sim.extern_modules[this.module_index] + .wait_targets + .extend(this.targets.iter()); + this.targets.clear(); + this.yielded_at_all = true; 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, + targets, } } - /// returns the next `WaitTarget` and the set of things ready to run then. - fn get_ready_to_run_set(&self, ready_to_run_set: &mut ReadyToRunSet) -> Option { + async fn yield_advance_time_or_settle( + this: Rc>, + module_index: usize, + duration: Option, + ) { + let mut targets = duration.map_or(EarliestWaitTargets::settle(), |duration| { + EarliestWaitTargets::instant(this.borrow().instant + duration) + }); + Self::yield_wait(this, module_index, &mut targets).await; + } + fn is_extern_module_ready_to_run(&mut self, module_index: usize) -> Option { + let module = &self.extern_modules[module_index]; + let mut retval = None; + for wait_target in module.wait_targets.iter() { + retval = match (wait_target, retval) { + (WaitTarget::Settle, _) => Some(self.instant), + (WaitTarget::Instant(instant), _) if instant <= self.instant => Some(self.instant), + (WaitTarget::Instant(instant), None) => Some(instant), + (WaitTarget::Instant(instant), Some(retval)) => Some(instant.min(retval)), + (WaitTarget::Change { key, value }, retval) => { + if Self::value_changed(&mut self.state, key, &value.bits) { + Some(self.instant) + } else { + retval + } + } + }; + if retval == Some(self.instant) { + break; + } + } + retval + } + fn get_ready_to_run_set(&mut self, ready_to_run_set: &mut ReadyToRunSet) -> Option { ready_to_run_set.clear(); - let mut wait_target = None; + let mut retval = None; if self.state_ready_to_run { ready_to_run_set.state_ready_to_run = true; - wait_target = Some(WaitTarget::Settle); + retval = Some(self.instant); } - for (module_index, extern_module) in self.extern_modules.iter().enumerate() { - let Some(extern_module_wait_target) = extern_module.wait_target else { + for module_index in 0..self.extern_modules.len() { + let Some(instant) = self.is_extern_module_ready_to_run(module_index) else { continue; }; - if let Some(wait_target) = &mut wait_target { - match extern_module_wait_target.cmp(wait_target) { + if let Some(retval) = &mut retval { + match instant.cmp(retval) { std::cmp::Ordering::Less => ready_to_run_set.clear(), std::cmp::Ordering::Equal => {} std::cmp::Ordering::Greater => continue, } } else { - wait_target = Some(extern_module_wait_target); + retval = Some(instant); } ready_to_run_set .extern_modules_ready_to_run .push(module_index); } - wait_target + retval } fn set_instant_no_sim(&mut self, instant: SimInstant) { self.instant = instant; @@ -7296,8 +7437,98 @@ impl SimulationImpl { Ok(trace_writer_state) }); } + #[must_use] #[track_caller] - fn run_until(this_ref: &Rc>, run_target: WaitTarget) { + fn run_state_settle_cycle(&mut self) -> bool { + self.state_ready_to_run = false; + self.state.setup_call(0); + if self.breakpoints.is_some() { + loop { + match self + .state + .run(self.breakpoints.as_mut().expect("just checked")) + { + RunResult::Break(break_action) => { + println!( + "hit breakpoint at:\n{:?}", + self.state.debug_insn_at(self.state.pc), + ); + match break_action { + BreakAction::DumpStateAndContinue => { + println!("{self:#?}"); + } + BreakAction::Continue => {} + } + } + RunResult::Return(()) => break, + } + } + } else { + let RunResult::Return(()) = self.state.run(()); + } + if self + .clocks_triggered + .iter() + .any(|i| self.state.small_slots[*i] != 0) + { + self.state_ready_to_run = true; + true + } else { + false + } + } + #[track_caller] + fn run_extern_modules_cycle( + this_ref: &Rc>, + generator_waker: &std::task::Waker, + extern_modules_ready_to_run: &[usize], + ) { + let mut this = this_ref.borrow_mut(); + for module_index in extern_modules_ready_to_run.iter().copied() { + let extern_module = &mut this.extern_modules[module_index]; + extern_module.wait_targets.clear(); + let mut generator = if !extern_module.module_state.did_initial_settle { + let sim = extern_module.sim; + drop(this); + Box::into_pin(sim.run(ExternModuleSimulationState { + sim_impl: this_ref.clone(), + module_index, + wait_for_changes_wait_targets: EarliestWaitTargets::default(), + })) + } else if let Some(generator) = extern_module.running_generator.take() { + drop(this); + generator + } else { + continue; + }; + 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] + .module_state + .did_initial_settle = true; + if !this.extern_modules[module_index] + .module_state + .uninitialized_ios + .is_empty() + { + panic!( + "extern module didn't initialize all outputs before \ + waiting, settling, or reading any inputs: {}", + this.extern_modules[module_index].sim.source_location + ); + } + this.extern_modules[module_index].running_generator = generator; + } + } + /// clears `targets` + #[track_caller] + fn run_until(this_ref: &Rc>, run_target: SimInstant) { let mut this = this_ref.borrow_mut(); let mut ready_to_run_set = ReadyToRunSet::default(); let generator_waker = this.generator_waker.clone(); @@ -7305,10 +7536,7 @@ impl SimulationImpl { this.main_module.uninitialized_ios.is_empty(), "didn't initialize all inputs", ); - match run_target { - WaitTarget::Settle => {} - WaitTarget::Instant(run_target) => assert!(run_target >= this.instant), - } + let run_target = run_target.max(this.instant); let mut settle_cycle = 0; let mut run_extern_modules = true; loop { @@ -7318,92 +7546,25 @@ impl SimulationImpl { Some(next_wait_target) if next_wait_target <= run_target => next_wait_target, _ => break, }; - match next_wait_target { - WaitTarget::Settle => {} - WaitTarget::Instant(instant) => { - settle_cycle = 0; - this.set_instant_no_sim(instant); - } + if next_wait_target > this.instant { + settle_cycle = 0; + this.set_instant_no_sim(next_wait_target); } if run_extern_modules { - for module_index in ready_to_run_set.extern_modules_ready_to_run.drain(..) { - let extern_module = &mut this.extern_modules[module_index]; - extern_module.wait_target = None; - let mut generator = if !extern_module.module_state.did_initial_settle { - let sim = extern_module.sim; - drop(this); - Box::into_pin(sim.run(ExternModuleSimulationState { - sim_impl: this_ref.clone(), - module_index, - })) - } else if let Some(generator) = extern_module.running_generator.take() { - drop(this); - generator - } else { - continue; - }; - 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] - .module_state - .did_initial_settle = true; - if !this.extern_modules[module_index] - .module_state - .uninitialized_ios - .is_empty() - { - panic!( - "extern module didn't initialize all outputs before \ - waiting, settling, or reading any inputs: {}", - this.extern_modules[module_index].sim.source_location - ); - } - this.extern_modules[module_index].running_generator = generator; - } + drop(this); + Self::run_extern_modules_cycle( + this_ref, + &generator_waker, + &ready_to_run_set.extern_modules_ready_to_run, + ); + this = this_ref.borrow_mut(); } if ready_to_run_set.state_ready_to_run { - this.state_ready_to_run = false; - run_extern_modules = true; - this.state.setup_call(0); - if this.breakpoints.is_some() { - loop { - let this = &mut *this; - match this - .state - .run(this.breakpoints.as_mut().expect("just checked")) - { - RunResult::Break(break_action) => { - println!( - "hit breakpoint at:\n{:?}", - this.state.debug_insn_at(this.state.pc), - ); - match break_action { - BreakAction::DumpStateAndContinue => { - println!("{this:#?}"); - } - BreakAction::Continue => {} - } - } - RunResult::Return(()) => break, - } - } - } else { - let RunResult::Return(()) = this.state.run(()); - } - if this - .clocks_triggered - .iter() - .any(|i| this.state.small_slots[*i] != 0) - { - this.state_ready_to_run = true; + if this.run_state_settle_cycle() { // wait for clocks to settle before running extern modules again run_extern_modules = false; + } else { + run_extern_modules = true; } } if this.main_module.did_initial_settle { @@ -7434,14 +7595,14 @@ impl SimulationImpl { }); this.state.memory_write_log.clear(); } - match run_target { - WaitTarget::Settle => {} - WaitTarget::Instant(instant) => this.set_instant_no_sim(instant), + if run_target > this.instant { + this.set_instant_no_sim(run_target); } } #[track_caller] fn settle(this_ref: &Rc>) { - Self::run_until(this_ref, WaitTarget::Settle); + let run_target = this_ref.borrow().instant; + Self::run_until(this_ref, run_target); } fn get_module(&self, which_module: WhichModule) -> &SimulationModuleState { match which_module { @@ -7522,12 +7683,13 @@ impl SimulationImpl { } } #[track_caller] - fn read_write_sim_value_helper( + fn read_write_sim_value_helper( state: &mut interpreter::State, compiled_value: CompiledValue, - bits: &mut BitSlice, - read_write_big_scalar: impl Fn(bool, &mut BitSlice, &mut BigInt) + Copy, - read_write_small_scalar: impl Fn(bool, &mut BitSlice, &mut SmallUInt) + Copy, + start_bit_index: usize, + bits: &mut Bits, + read_write_big_scalar: impl Fn(bool, std::ops::Range, &mut Bits, &mut BigInt) + Copy, + read_write_small_scalar: impl Fn(bool, std::ops::Range, &mut Bits, &mut SmallUInt) + Copy, ) { match compiled_value.layout.body { CompiledTypeLayoutBody::Scalar => { @@ -7544,14 +7706,18 @@ impl SimulationImpl { CanonicalType::Clock(_) => false, CanonicalType::PhantomConst(_) => unreachable!(), }; + let bit_indexes = + start_bit_index..start_bit_index + compiled_value.layout.ty.bit_width(); match compiled_value.range.len() { TypeLen::A_SMALL_SLOT => read_write_small_scalar( signed, + bit_indexes, bits, &mut state.small_slots[compiled_value.range.small_slots.start], ), TypeLen::A_BIG_SLOT => read_write_big_scalar( signed, + bit_indexes, bits, &mut state.big_slots[compiled_value.range.big_slots.start], ), @@ -7571,7 +7737,8 @@ impl SimulationImpl { .index_array(element.layout.len(), element_index), write: None, }, - &mut bits[element_index * element_bit_width..][..element_bit_width], + start_bit_index + element_index * element_bit_width, + bits, read_write_big_scalar, read_write_small_scalar, ); @@ -7580,7 +7747,7 @@ impl SimulationImpl { CompiledTypeLayoutBody::Bundle { fields } => { let ty = Bundle::from_canonical(compiled_value.layout.ty); for ( - (field, offset), + (_field, offset), CompiledBundleField { offset: layout_offset, ty: field_layout, @@ -7597,7 +7764,8 @@ impl SimulationImpl { )), write: None, }, - &mut bits[offset..][..field.ty.bit_width()], + start_bit_index + offset, + bits, read_write_big_scalar, read_write_small_scalar, ); @@ -7606,21 +7774,89 @@ impl SimulationImpl { } } #[track_caller] + fn read_no_settle_helper( + state: &mut interpreter::State, + io: Expr, + compiled_value: CompiledValue, + mut bits: BitVec, + ) -> SimValue { + bits.clear(); + bits.resize(compiled_value.layout.ty.bit_width(), false); + SimulationImpl::read_write_sim_value_helper( + state, + compiled_value, + 0, + &mut bits, + |_signed, bit_range, bits, value| { + ::copy_bits_from_bigint_wrapping(value, &mut bits[bit_range]); + }, + |_signed, bit_range, bits, value| { + let bytes = value.to_le_bytes(); + let bitslice = BitSlice::::from_slice(&bytes); + let bitslice = &bitslice[..bit_range.len()]; + bits[bit_range].clone_from_bitslice(bitslice); + }, + ); + SimValue { + ty: Expr::ty(io), + bits, + } + } + /// doesn't modify `bits` + fn value_changed( + state: &mut interpreter::State, + compiled_value: CompiledValue, + mut bits: &BitSlice, + ) -> bool { + assert_eq!(bits.len(), compiled_value.layout.ty.bit_width()); + let any_change = std::cell::Cell::new(false); + SimulationImpl::read_write_sim_value_helper( + state, + compiled_value, + 0, + &mut bits, + |_signed, bit_range, bits, value| { + if !::bits_equal_bigint_wrapping(value, &bits[bit_range]) { + any_change.set(true); + } + }, + |_signed, bit_range, bits, value| { + let bytes = value.to_le_bytes(); + let bitslice = BitSlice::::from_slice(&bytes); + let bitslice = &bitslice[..bit_range.len()]; + if bits[bit_range] != *bitslice { + any_change.set(true); + } + }, + ); + any_change.get() + } + #[track_caller] fn read( &mut self, io: Expr, which_module: WhichModule, - ) -> MaybeNeedsSettle> { - self.get_module(which_module) - .read_helper(io, which_module) - .map(|compiled_value| ReadFn { compiled_value, io }) - .apply_no_settle(&mut self.state) + ) -> ( + CompiledValue, + MaybeNeedsSettle>, + ) { + let compiled_value = self.get_module(which_module).read_helper(io, which_module); + let value = compiled_value + .map(|compiled_value| ReadFn { + compiled_value, + io, + bits: BitVec::new(), + }) + .apply_no_settle(&mut self.state); + let (MaybeNeedsSettle::NeedsSettle(compiled_value) + | MaybeNeedsSettle::NoSettleNeeded(compiled_value)) = compiled_value; + (compiled_value, value) } #[track_caller] fn write( &mut self, io: Expr, - value: SimValue, + value: &SimValue, which_module: WhichModule, ) { let compiled_value = self @@ -7631,20 +7867,22 @@ impl SimulationImpl { Self::read_write_sim_value_helper( &mut self.state, compiled_value, - &mut value.into_bits(), - |signed, bits, value| { + 0, + &mut value.bits(), + |signed, bit_range, bits, value| { if signed { - *value = SInt::bits_to_bigint(bits); + *value = SInt::bits_to_bigint(&bits[bit_range]); } else { - *value = UInt::bits_to_bigint(bits); + *value = UInt::bits_to_bigint(&bits[bit_range]); } }, - |signed, bits, value| { + |signed, bit_range, bits, value| { let mut small_value = [0; mem::size_of::()]; - if signed && bits.last().as_deref().copied() == Some(true) { + if signed && bits[bit_range.clone()].last().as_deref().copied() == Some(true) { small_value.fill(u8::MAX); } - small_value.view_bits_mut::()[0..bits.len()].clone_from_bitslice(bits); + small_value.view_bits_mut::()[0..bit_range.len()] + .clone_from_bitslice(&bits[bit_range]); *value = SmallUInt::from_le_bytes(small_value); }, ); @@ -7920,14 +8158,14 @@ macro_rules! impl_simulation_methods { let retval = $self .sim_impl .borrow_mut() - .read(Expr::canonical(io), $which_module); + .read(Expr::canonical(io), $which_module).1; 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(), + &value.into_sim_value(Expr::ty(io)).into_canonical(), $which_module, ); } @@ -8008,6 +8246,7 @@ impl Simulation { pub struct ExternModuleSimulationState { sim_impl: Rc>, module_index: usize, + wait_for_changes_wait_targets: EarliestWaitTargets, } impl fmt::Debug for ExternModuleSimulationState { @@ -8015,11 +8254,12 @@ impl fmt::Debug for ExternModuleSimulationState { let Self { sim_impl: _, module_index, + wait_for_changes_wait_targets: _, } = self; f.debug_struct("ExternModuleSimulationState") .field("sim_impl", &DebugAsDisplay("...")) .field("module_index", module_index) - .finish() + .finish_non_exhaustive() } } @@ -8036,6 +8276,42 @@ impl ExternModuleSimulationState { ) .await } + pub async fn wait_for_changes>( + &mut self, + iter: I, + timeout: Option, + ) { + self.wait_for_changes_wait_targets.clear(); + let which_module = WhichModule::Extern { + module_index: self.module_index, + }; + for io in iter { + let io = Expr::canonical(io.to_expr()); + let (key, value) = self.sim_impl.borrow_mut().read(io, which_module); + let value = self.settle_if_needed(value).await; + self.wait_for_changes_wait_targets + .insert(WaitTarget::Change { key, value }); + } + if let Some(timeout) = timeout { + self.wait_for_changes_wait_targets.instant = + Some(self.sim_impl.borrow().instant + timeout); + } + SimulationImpl::yield_wait( + self.sim_impl.clone(), + self.module_index, + &mut self.wait_for_changes_wait_targets, + ) + .await; + } + pub async fn wait_for_clock_edge(&mut self, clk: impl ToExpr) { + let clk = clk.to_expr(); + while self.read_clock(clk).await { + self.wait_for_changes([clk], None).await; + } + while !self.read_clock(clk).await { + self.wait_for_changes([clk], None).await; + } + } async fn settle_if_needed(&mut self, v: MaybeNeedsSettle) -> O where for<'a> F: MaybeNeedsSettleFn<&'a mut interpreter::State, Output = O>, diff --git a/crates/fayalite/tests/sim.rs b/crates/fayalite/tests/sim.rs index e516f22..6d20715 100644 --- a/crates/fayalite/tests/sim.rs +++ b/crates/fayalite/tests/sim.rs @@ -1485,3 +1485,50 @@ fn test_extern_module() { panic!(); } } + +#[hdl_module(outline_generated, extern)] +pub fn extern_module2() { + #[hdl] + let en: Bool = m.input(); + #[hdl] + let clk: Clock = m.input(); + #[hdl] + let o: UInt<8> = m.output(); + m.extern_module_simulation_fn((en, clk, o), |(en, clk, o), mut sim| async move { + for b in "Hello, World!\n".bytes().cycle() { + sim.write(o, b).await; + loop { + sim.wait_for_clock_edge(clk).await; + if sim.read_bool(en).await { + break; + } + } + } + }); +} + +#[test] +fn test_extern_module2() { + let _n = SourceLocation::normalize_files_for_tests(); + let mut sim = Simulation::new(extern_module2()); + let mut writer = RcWriter::default(); + sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); + for i in 0..30 { + sim.write(sim.io().en, i % 10 < 5); + sim.write(sim.io().clk, false); + sim.advance_time(SimDuration::from_micros(1)); + sim.write(sim.io().clk, true); + sim.advance_time(SimDuration::from_micros(1)); + } + sim.flush_traces().unwrap(); + let vcd = String::from_utf8(writer.take()).unwrap(); + println!("####### VCD:\n{vcd}\n#######"); + if vcd != include_str!("sim/expected/extern_module2.vcd") { + panic!(); + } + let sim_debug = format!("{sim:#?}"); + println!("#######\n{sim_debug}\n#######"); + if sim_debug != include_str!("sim/expected/extern_module2.txt") { + panic!(); + } +} diff --git a/crates/fayalite/tests/sim/expected/extern_module.txt b/crates/fayalite/tests/sim/expected/extern_module.txt index 23af0b2..6cce70b 100644 --- a/crates/fayalite/tests/sim/expected/extern_module.txt +++ b/crates/fayalite/tests/sim/expected/extern_module.txt @@ -153,11 +153,11 @@ Simulation { running_generator: Some( ..., ), - wait_target: Some( + wait_targets: { Instant( 20.500000000000 μs, ), - ), + }, }, ], state_ready_to_run: false, diff --git a/crates/fayalite/tests/sim/expected/extern_module2.txt b/crates/fayalite/tests/sim/expected/extern_module2.txt new file mode 100644 index 0000000..96710fb --- /dev/null +++ b/crates/fayalite/tests/sim/expected/extern_module2.txt @@ -0,0 +1,308 @@ +Simulation { + state: State { + insns: Insns { + state_layout: StateLayout { + ty: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(extern_module2: extern_module2).extern_module2::en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(extern_module2: extern_module2).extern_module2::clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(extern_module2: extern_module2).extern_module2::o", + ty: UInt<8>, + }, + ], + .. + }, + }, + memories: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + insns: [ + // at: module-XXXXXXXXXX.rs:1:1 + 0: Return, + ], + .. + }, + pc: 0, + memory_write_log: [], + memories: StatePart { + value: [], + }, + small_slots: StatePart { + value: [], + }, + big_slots: StatePart { + value: [ + 0, + 1, + 101, + ], + }, + }, + io: Instance { + name: ::extern_module2, + instantiated: Module { + name: extern_module2, + .. + }, + }, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::extern_module2, + instantiated: Module { + name: extern_module2, + .. + }, + }.en, + Instance { + name: ::extern_module2, + instantiated: Module { + name: extern_module2, + .. + }, + }.clk, + Instance { + name: ::extern_module2, + instantiated: Module { + name: extern_module2, + .. + }, + }.o, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::extern_module2, + instantiated: Module { + name: extern_module2, + .. + }, + }.clk, + Instance { + name: ::extern_module2, + instantiated: Module { + name: extern_module2, + .. + }, + }.en, + Instance { + name: ::extern_module2, + instantiated: Module { + name: extern_module2, + .. + }, + }.o, + }, + did_initial_settle: true, + }, + extern_modules: [ + SimulationExternModuleState { + module_state: SimulationModuleState { + base_targets: [ + ModuleIO { + name: extern_module2::en, + is_input: true, + ty: Bool, + .. + }, + ModuleIO { + name: extern_module2::clk, + is_input: true, + ty: Clock, + .. + }, + ModuleIO { + name: extern_module2::o, + is_input: false, + ty: UInt<8>, + .. + }, + ], + uninitialized_ios: {}, + io_targets: { + ModuleIO { + name: extern_module2::clk, + is_input: true, + ty: Clock, + .. + }, + ModuleIO { + name: extern_module2::en, + is_input: true, + ty: Bool, + .. + }, + ModuleIO { + name: extern_module2::o, + is_input: false, + ty: UInt<8>, + .. + }, + }, + did_initial_settle: true, + }, + sim: ExternModuleSimulation { + generator: SimGeneratorFn { + args: ( + ModuleIO { + name: extern_module2::en, + is_input: true, + ty: Bool, + .. + }, + ModuleIO { + name: extern_module2::clk, + is_input: true, + ty: Clock, + .. + }, + ModuleIO { + name: extern_module2::o, + is_input: false, + ty: UInt<8>, + .. + }, + ), + f: ..., + }, + source_location: SourceLocation( + module-XXXXXXXXXX.rs:5:1, + ), + }, + running_generator: Some( + ..., + ), + wait_targets: { + Change { + key: CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(extern_module2: extern_module2).extern_module2::clk", + ty: Clock, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 1, len: 1 }, + }, + write: None, + }, + value: SimValue { + ty: Clock, + bits: 0x1, + }, + }, + }, + }, + ], + state_ready_to_run: false, + trace_decls: TraceModule { + name: "extern_module2", + children: [ + TraceModuleIO { + name: "en", + child: TraceBool { + location: TraceScalarId(0), + name: "en", + flow: Source, + }, + ty: Bool, + flow: Source, + }, + TraceModuleIO { + name: "clk", + child: TraceClock { + location: TraceScalarId(1), + name: "clk", + flow: Source, + }, + ty: Clock, + flow: Source, + }, + TraceModuleIO { + name: "o", + child: TraceUInt { + location: TraceScalarId(2), + name: "o", + ty: UInt<8>, + flow: Sink, + }, + ty: UInt<8>, + flow: Sink, + }, + ], + }, + traces: [ + SimTrace { + id: TraceScalarId(0), + kind: BigBool { + index: StatePartIndex(0), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(1), + kind: BigClock { + index: StatePartIndex(1), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(2), + kind: BigUInt { + index: StatePartIndex(2), + ty: UInt<8>, + }, + state: 0x65, + last_state: 0x65, + }, + ], + trace_memories: {}, + trace_writers: [ + Running( + VcdWriter { + finished_init: true, + timescale: 1 ps, + .. + }, + ), + ], + instant: 60 μs, + clocks_triggered: [], + .. +} \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/extern_module2.vcd b/crates/fayalite/tests/sim/expected/extern_module2.vcd new file mode 100644 index 0000000..464f4bd --- /dev/null +++ b/crates/fayalite/tests/sim/expected/extern_module2.vcd @@ -0,0 +1,150 @@ +$timescale 1 ps $end +$scope module extern_module2 $end +$var wire 1 ! en $end +$var wire 1 " clk $end +$var wire 8 # o $end +$upscope $end +$enddefinitions $end +$dumpvars +1! +0" +b1001000 # +$end +#1000000 +1" +b1100101 # +#2000000 +0" +#3000000 +1" +b1101100 # +#4000000 +0" +#5000000 +1" +#6000000 +0" +#7000000 +1" +b1101111 # +#8000000 +0" +#9000000 +1" +b101100 # +#10000000 +0! +0" +#11000000 +1" +#12000000 +0" +#13000000 +1" +#14000000 +0" +#15000000 +1" +#16000000 +0" +#17000000 +1" +#18000000 +0" +#19000000 +1" +#20000000 +1! +0" +#21000000 +1" +b100000 # +#22000000 +0" +#23000000 +1" +b1010111 # +#24000000 +0" +#25000000 +1" +b1101111 # +#26000000 +0" +#27000000 +1" +b1110010 # +#28000000 +0" +#29000000 +1" +b1101100 # +#30000000 +0! +0" +#31000000 +1" +#32000000 +0" +#33000000 +1" +#34000000 +0" +#35000000 +1" +#36000000 +0" +#37000000 +1" +#38000000 +0" +#39000000 +1" +#40000000 +1! +0" +#41000000 +1" +b1100100 # +#42000000 +0" +#43000000 +1" +b100001 # +#44000000 +0" +#45000000 +1" +b1010 # +#46000000 +0" +#47000000 +1" +b1001000 # +#48000000 +0" +#49000000 +1" +b1100101 # +#50000000 +0! +0" +#51000000 +1" +#52000000 +0" +#53000000 +1" +#54000000 +0" +#55000000 +1" +#56000000 +0" +#57000000 +1" +#58000000 +0" +#59000000 +1" +#60000000