forked from libre-chip/fayalite
		
	simulator: allow external module generators to wait for value changes and/or clock edges
This commit is contained in:
		
							parent
							
								
									ab9ff4f2db
								
							
						
					
					
						commit
						a115585d5a
					
				
					 6 changed files with 969 additions and 182 deletions
				
			
		|  | @ -621,6 +621,12 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed { | |||
|         let bitslice = &BitSlice::<u8, Lsb0>::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 | ||||
|  |  | |||
|  | @ -5904,12 +5904,21 @@ impl SimTraceKind { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, PartialEq, Eq, Hash, Debug)] | ||||
| #[derive(Clone, PartialEq, Eq, Hash)] | ||||
| pub struct SimValue<T: Type> { | ||||
|     ty: T, | ||||
|     bits: BitVec, | ||||
| } | ||||
| 
 | ||||
| impl<T: Type> fmt::Debug for SimValue<T> { | ||||
|     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<CanonicalType> { | ||||
|     #[track_caller] | ||||
|     fn to_expr_impl(ty: CanonicalType, bits: &BitSlice) -> Expr<CanonicalType> { | ||||
|  | @ -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<ChangeKey, ChangeValue> { | ||||
|     Settle, | ||||
|     Instant(SimInstant), | ||||
|     Change { key: ChangeKey, value: ChangeValue }, | ||||
| } | ||||
| 
 | ||||
| impl PartialOrd for WaitTarget { | ||||
|     fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { | ||||
|         Some(self.cmp(other)) | ||||
| #[derive(Clone)] | ||||
| struct EarliestWaitTargets { | ||||
|     settle: bool, | ||||
|     instant: Option<SimInstant>, | ||||
|     changes: HashMap<CompiledValue<CanonicalType>, SimValue<CanonicalType>>, | ||||
| } | ||||
| 
 | ||||
| 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<ChangeValue>( | ||||
|         &mut self, | ||||
|         value: impl std::borrow::Borrow<WaitTarget<CompiledValue<CanonicalType>, ChangeValue>>, | ||||
|     ) where | ||||
|         ChangeValue: std::borrow::Borrow<SimValue<CanonicalType>>, | ||||
|     { | ||||
|         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<Item = WaitTarget<CompiledValue<CanonicalType>, &'a SimValue<CanonicalType>>> | ||||
|            + '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<ChangeValue: std::borrow::Borrow<SimValue<CanonicalType>>> | ||||
|     Extend<WaitTarget<CompiledValue<CanonicalType>, ChangeValue>> for EarliestWaitTargets | ||||
| { | ||||
|     fn extend<T: IntoIterator<Item = WaitTarget<CompiledValue<CanonicalType>, ChangeValue>>>( | ||||
|         &mut self, | ||||
|         iter: T, | ||||
|     ) { | ||||
|         iter.into_iter().for_each(|v| self.insert(v)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'a, ChangeValue: std::borrow::Borrow<SimValue<CanonicalType>>> | ||||
|     Extend<&'a WaitTarget<CompiledValue<CanonicalType>, ChangeValue>> for EarliestWaitTargets | ||||
| { | ||||
|     fn extend<T: IntoIterator<Item = &'a WaitTarget<CompiledValue<CanonicalType>, ChangeValue>>>( | ||||
|         &mut self, | ||||
|         iter: T, | ||||
|     ) { | ||||
|         iter.into_iter().for_each(|v| self.insert(v)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<A> FromIterator<A> for EarliestWaitTargets | ||||
| where | ||||
|     Self: Extend<A>, | ||||
| { | ||||
|     fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self { | ||||
|         let mut retval = Self::default(); | ||||
|         retval.extend(iter); | ||||
|         retval | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| struct SimulationExternModuleState { | ||||
|     module_state: SimulationModuleState, | ||||
|     sim: ExternModuleSimulation, | ||||
|     running_generator: Option<Pin<Box<dyn Future<Output = ()> + 'static>>>, | ||||
|     wait_target: Option<WaitTarget>, | ||||
|     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<I: BoolOrIntType> MaybeNeedsSettleFn<&'_ mut interpreter::State> for ReadBo | |||
| struct ReadFn { | ||||
|     compiled_value: CompiledValue<CanonicalType>, | ||||
|     io: Expr<CanonicalType>, | ||||
|     bits: BitVec, | ||||
| } | ||||
| 
 | ||||
| impl MaybeNeedsSettleFn<&'_ mut interpreter::State> for ReadFn { | ||||
|     type Output = SimValue<CanonicalType>; | ||||
| 
 | ||||
|     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| <UInt>::copy_bits_from_bigint_wrapping(value, bits), | ||||
|             |_signed, bits, value| { | ||||
|                 let bytes = value.to_le_bytes(); | ||||
|                 let bitslice = BitSlice::<u8, Lsb0>::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<RefCell<Self>>, 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<RefCell<Self>>, | ||||
|         module_index: usize, | ||||
|         duration: Option<SimDuration>, | ||||
|     ) -> impl Future<Output = ()> + 'static { | ||||
|         struct MyGenerator { | ||||
|         targets: &'a mut EarliestWaitTargets, | ||||
|     ) -> impl Future<Output = ()> + 'a { | ||||
|         struct MyGenerator<'a> { | ||||
|             sim: Rc<RefCell<SimulationImpl>>, | ||||
|             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<Self::Output> { | ||||
|                 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<WaitTarget> { | ||||
|     async fn yield_advance_time_or_settle( | ||||
|         this: Rc<RefCell<Self>>, | ||||
|         module_index: usize, | ||||
|         duration: Option<SimDuration>, | ||||
|     ) { | ||||
|         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<SimInstant> { | ||||
|         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<SimInstant> { | ||||
|         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<RefCell<Self>>, 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<RefCell<Self>>, | ||||
|         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<RefCell<Self>>, 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<RefCell<Self>>) { | ||||
|         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<Bits>( | ||||
|         state: &mut interpreter::State, | ||||
|         compiled_value: CompiledValue<CanonicalType>, | ||||
|         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<usize>, &mut Bits, &mut BigInt) + Copy, | ||||
|         read_write_small_scalar: impl Fn(bool, std::ops::Range<usize>, &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<CanonicalType>, | ||||
|         compiled_value: CompiledValue<CanonicalType>, | ||||
|         mut bits: BitVec, | ||||
|     ) -> SimValue<CanonicalType> { | ||||
|         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| { | ||||
|                 <UInt>::copy_bits_from_bigint_wrapping(value, &mut bits[bit_range]); | ||||
|             }, | ||||
|             |_signed, bit_range, bits, value| { | ||||
|                 let bytes = value.to_le_bytes(); | ||||
|                 let bitslice = BitSlice::<u8, Lsb0>::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<CanonicalType>, | ||||
|         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 !<UInt>::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::<u8, Lsb0>::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<CanonicalType>, | ||||
|         which_module: WhichModule, | ||||
|     ) -> MaybeNeedsSettle<ReadFn, SimValue<CanonicalType>> { | ||||
|         self.get_module(which_module) | ||||
|             .read_helper(io, which_module) | ||||
|             .map(|compiled_value| ReadFn { compiled_value, io }) | ||||
|             .apply_no_settle(&mut self.state) | ||||
|     ) -> ( | ||||
|         CompiledValue<CanonicalType>, | ||||
|         MaybeNeedsSettle<ReadFn, SimValue<CanonicalType>>, | ||||
|     ) { | ||||
|         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<CanonicalType>, | ||||
|         value: SimValue<CanonicalType>, | ||||
|         value: &SimValue<CanonicalType>, | ||||
|         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::<SmallUInt>()]; | ||||
|                 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::<Lsb0>()[0..bits.len()].clone_from_bitslice(bits); | ||||
|                 small_value.view_bits_mut::<Lsb0>()[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<IO: Type, V: ToSimValue<IO>>(&mut $self, io: Expr<IO>, 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<T: BundleType> Simulation<T> { | |||
| pub struct ExternModuleSimulationState { | ||||
|     sim_impl: Rc<RefCell<SimulationImpl>>, | ||||
|     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<I: IntoIterator<Item: ToExpr>>( | ||||
|         &mut self, | ||||
|         iter: I, | ||||
|         timeout: Option<SimDuration>, | ||||
|     ) { | ||||
|         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<Type = Clock>) { | ||||
|         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<F, O>(&mut self, v: MaybeNeedsSettle<F, O>) -> O | ||||
|     where | ||||
|         for<'a> F: MaybeNeedsSettleFn<&'a mut interpreter::State, Output = O>, | ||||
|  |  | |||
|  | @ -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!(); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -153,11 +153,11 @@ Simulation { | |||
|             running_generator: Some( | ||||
|                 ..., | ||||
|             ), | ||||
|             wait_target: Some( | ||||
|             wait_targets: { | ||||
|                 Instant( | ||||
|                     20.500000000000 μs, | ||||
|                 ), | ||||
|             ), | ||||
|             }, | ||||
|         }, | ||||
|     ], | ||||
|     state_ready_to_run: false, | ||||
|  |  | |||
							
								
								
									
										308
									
								
								crates/fayalite/tests/sim/expected/extern_module2.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										308
									
								
								crates/fayalite/tests/sim/expected/extern_module2.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,308 @@ | |||
| Simulation { | ||||
|     state: State { | ||||
|         insns: Insns { | ||||
|             state_layout: StateLayout { | ||||
|                 ty: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|                     }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         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<Memories> { | ||||
|                     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: <simulator>::extern_module2, | ||||
|         instantiated: Module { | ||||
|             name: extern_module2, | ||||
|             .. | ||||
|         }, | ||||
|     }, | ||||
|     main_module: SimulationModuleState { | ||||
|         base_targets: [ | ||||
|             Instance { | ||||
|                 name: <simulator>::extern_module2, | ||||
|                 instantiated: Module { | ||||
|                     name: extern_module2, | ||||
|                     .. | ||||
|                 }, | ||||
|             }.en, | ||||
|             Instance { | ||||
|                 name: <simulator>::extern_module2, | ||||
|                 instantiated: Module { | ||||
|                     name: extern_module2, | ||||
|                     .. | ||||
|                 }, | ||||
|             }.clk, | ||||
|             Instance { | ||||
|                 name: <simulator>::extern_module2, | ||||
|                 instantiated: Module { | ||||
|                     name: extern_module2, | ||||
|                     .. | ||||
|                 }, | ||||
|             }.o, | ||||
|         ], | ||||
|         uninitialized_ios: {}, | ||||
|         io_targets: { | ||||
|             Instance { | ||||
|                 name: <simulator>::extern_module2, | ||||
|                 instantiated: Module { | ||||
|                     name: extern_module2, | ||||
|                     .. | ||||
|                 }, | ||||
|             }.clk, | ||||
|             Instance { | ||||
|                 name: <simulator>::extern_module2, | ||||
|                 instantiated: Module { | ||||
|                     name: extern_module2, | ||||
|                     .. | ||||
|                 }, | ||||
|             }.en, | ||||
|             Instance { | ||||
|                 name: <simulator>::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<SmallSlots> { | ||||
|                                     len: 0, | ||||
|                                     debug_data: [], | ||||
|                                     .. | ||||
|                                 }, | ||||
|                                 big_slots: StatePartLayout<BigSlots> { | ||||
|                                     len: 1, | ||||
|                                     debug_data: [ | ||||
|                                         SlotDebugData { | ||||
|                                             name: "InstantiatedModule(extern_module2: extern_module2).extern_module2::clk", | ||||
|                                             ty: Clock, | ||||
|                                         }, | ||||
|                                     ], | ||||
|                                     .. | ||||
|                                 }, | ||||
|                             }, | ||||
|                             body: Scalar, | ||||
|                         }, | ||||
|                         range: TypeIndexRange { | ||||
|                             small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, | ||||
|                             big_slots: StatePartIndexRange<BigSlots> { 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<BigSlots>(0), | ||||
|             }, | ||||
|             state: 0x0, | ||||
|             last_state: 0x0, | ||||
|         }, | ||||
|         SimTrace { | ||||
|             id: TraceScalarId(1), | ||||
|             kind: BigClock { | ||||
|                 index: StatePartIndex<BigSlots>(1), | ||||
|             }, | ||||
|             state: 0x1, | ||||
|             last_state: 0x1, | ||||
|         }, | ||||
|         SimTrace { | ||||
|             id: TraceScalarId(2), | ||||
|             kind: BigUInt { | ||||
|                 index: StatePartIndex<BigSlots>(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: [], | ||||
|     .. | ||||
| } | ||||
							
								
								
									
										150
									
								
								crates/fayalite/tests/sim/expected/extern_module2.vcd
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								crates/fayalite/tests/sim/expected/extern_module2.vcd
									
										
									
									
									
										Normal file
									
								
							|  | @ -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 | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue