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]; |         let bitslice = &BitSlice::<u8, Lsb0>::from_slice(&bytes)[..width]; | ||||||
|         bits.clone_from_bitslice(bitslice); |         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 { |     fn bits_to_bigint(bits: &BitSlice) -> BigInt { | ||||||
|         let sign_byte = if Self::Signed::VALUE && bits.last().as_deref().copied().unwrap_or(false) { |         let sign_byte = if Self::Signed::VALUE && bits.last().as_deref().copied().unwrap_or(false) { | ||||||
|             0xFF |             0xFF | ||||||
|  |  | ||||||
|  | @ -5904,12 +5904,21 @@ impl SimTraceKind { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Clone, PartialEq, Eq, Hash, Debug)] | #[derive(Clone, PartialEq, Eq, Hash)] | ||||||
| pub struct SimValue<T: Type> { | pub struct SimValue<T: Type> { | ||||||
|     ty: T, |     ty: T, | ||||||
|     bits: BitVec, |     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> { | impl SimValue<CanonicalType> { | ||||||
|     #[track_caller] |     #[track_caller] | ||||||
|     fn to_expr_impl(ty: CanonicalType, bits: &BitSlice) -> Expr<CanonicalType> { |     fn to_expr_impl(ty: CanonicalType, bits: &BitSlice) -> Expr<CanonicalType> { | ||||||
|  | @ -6795,35 +6804,149 @@ impl SimulationModuleState { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Copy, Clone, Debug, Eq, PartialEq)] | #[derive(Copy, Clone, Debug)] | ||||||
| enum WaitTarget { | enum WaitTarget<ChangeKey, ChangeValue> { | ||||||
|     /// Settle is less than Instant
 |  | ||||||
|     Settle, |     Settle, | ||||||
|     Instant(SimInstant), |     Instant(SimInstant), | ||||||
|  |     Change { key: ChangeKey, value: ChangeValue }, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl PartialOrd for WaitTarget { | #[derive(Clone)] | ||||||
|     fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { | struct EarliestWaitTargets { | ||||||
|         Some(self.cmp(other)) |     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 { | impl Default for EarliestWaitTargets { | ||||||
|     fn cmp(&self, other: &Self) -> std::cmp::Ordering { |     fn default() -> Self { | ||||||
|         match (self, other) { |         Self { | ||||||
|             (WaitTarget::Settle, WaitTarget::Settle) => std::cmp::Ordering::Equal, |             settle: false, | ||||||
|             (WaitTarget::Settle, WaitTarget::Instant(_)) => std::cmp::Ordering::Less, |             instant: None, | ||||||
|             (WaitTarget::Instant(_), WaitTarget::Settle) => std::cmp::Ordering::Greater, |             changes: HashMap::new(), | ||||||
|             (WaitTarget::Instant(l), WaitTarget::Instant(r)) => l.cmp(r), |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | 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 { | struct SimulationExternModuleState { | ||||||
|     module_state: SimulationModuleState, |     module_state: SimulationModuleState, | ||||||
|     sim: ExternModuleSimulation, |     sim: ExternModuleSimulation, | ||||||
|     running_generator: Option<Pin<Box<dyn Future<Output = ()> + 'static>>>, |     running_generator: Option<Pin<Box<dyn Future<Output = ()> + 'static>>>, | ||||||
|     wait_target: Option<WaitTarget>, |     wait_targets: EarliestWaitTargets, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl fmt::Debug for SimulationExternModuleState { | impl fmt::Debug for SimulationExternModuleState { | ||||||
|  | @ -6832,7 +6955,7 @@ impl fmt::Debug for SimulationExternModuleState { | ||||||
|             module_state, |             module_state, | ||||||
|             sim, |             sim, | ||||||
|             running_generator, |             running_generator, | ||||||
|             wait_target, |             wait_targets, | ||||||
|         } = self; |         } = self; | ||||||
|         f.debug_struct("SimulationExternModuleState") |         f.debug_struct("SimulationExternModuleState") | ||||||
|             .field("module_state", module_state) |             .field("module_state", module_state) | ||||||
|  | @ -6841,7 +6964,7 @@ impl fmt::Debug for SimulationExternModuleState { | ||||||
|                 "running_generator", |                 "running_generator", | ||||||
|                 &running_generator.as_ref().map(|_| DebugAsDisplay("...")), |                 &running_generator.as_ref().map(|_| DebugAsDisplay("...")), | ||||||
|             ) |             ) | ||||||
|             .field("wait_target", wait_target) |             .field("wait_targets", wait_targets) | ||||||
|             .finish() |             .finish() | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -6896,29 +7019,19 @@ impl<I: BoolOrIntType> MaybeNeedsSettleFn<&'_ mut interpreter::State> for ReadBo | ||||||
| struct ReadFn { | struct ReadFn { | ||||||
|     compiled_value: CompiledValue<CanonicalType>, |     compiled_value: CompiledValue<CanonicalType>, | ||||||
|     io: Expr<CanonicalType>, |     io: Expr<CanonicalType>, | ||||||
|  |     bits: BitVec, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl MaybeNeedsSettleFn<&'_ mut interpreter::State> for ReadFn { | impl MaybeNeedsSettleFn<&'_ mut interpreter::State> for ReadFn { | ||||||
|     type Output = SimValue<CanonicalType>; |     type Output = SimValue<CanonicalType>; | ||||||
| 
 | 
 | ||||||
|     fn call(self, state: &mut interpreter::State) -> Self::Output { |     fn call(self, state: &mut interpreter::State) -> Self::Output { | ||||||
|         let Self { compiled_value, io } = self; |         let Self { | ||||||
|         let mut bits = BitVec::repeat(false, compiled_value.layout.ty.bit_width()); |  | ||||||
|         SimulationImpl::read_write_sim_value_helper( |  | ||||||
|             state, |  | ||||||
|             compiled_value, |             compiled_value, | ||||||
|             &mut bits, |             io, | ||||||
|             |_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), |  | ||||||
|             bits, |             bits, | ||||||
|         } |         } = self; | ||||||
|  |         SimulationImpl::read_no_settle_helper(state, io, compiled_value, bits) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -7017,7 +7130,7 @@ impl SimulationImpl { | ||||||
|                     ), |                     ), | ||||||
|                     sim: simulation, |                     sim: simulation, | ||||||
|                     running_generator: None, |                     running_generator: None, | ||||||
|                     wait_target: Some(WaitTarget::Settle), |                     wait_targets: EarliestWaitTargets::settle(), | ||||||
|                 } |                 } | ||||||
|             }, |             }, | ||||||
|         )); |         )); | ||||||
|  | @ -7200,22 +7313,23 @@ impl SimulationImpl { | ||||||
|     } |     } | ||||||
|     #[track_caller] |     #[track_caller] | ||||||
|     fn advance_time(this_ref: &Rc<RefCell<Self>>, duration: SimDuration) { |     fn advance_time(this_ref: &Rc<RefCell<Self>>, duration: SimDuration) { | ||||||
|         let instant = this_ref.borrow().instant + duration; |         let run_target = this_ref.borrow().instant + duration; | ||||||
|         Self::run_until(this_ref, WaitTarget::Instant(instant)); |         Self::run_until(this_ref, run_target); | ||||||
|     } |     } | ||||||
|  |     /// clears `targets`
 | ||||||
|     #[must_use] |     #[must_use] | ||||||
|     fn yield_advance_time_or_settle( |     fn yield_wait<'a>( | ||||||
|         this: Rc<RefCell<Self>>, |         this: Rc<RefCell<Self>>, | ||||||
|         module_index: usize, |         module_index: usize, | ||||||
|         duration: Option<SimDuration>, |         targets: &'a mut EarliestWaitTargets, | ||||||
|     ) -> impl Future<Output = ()> + 'static { |     ) -> impl Future<Output = ()> + 'a { | ||||||
|         struct MyGenerator { |         struct MyGenerator<'a> { | ||||||
|             sim: Rc<RefCell<SimulationImpl>>, |             sim: Rc<RefCell<SimulationImpl>>, | ||||||
|             yielded_at_all: bool, |             yielded_at_all: bool, | ||||||
|             module_index: usize, |             module_index: usize, | ||||||
|             target: WaitTarget, |             targets: &'a mut EarliestWaitTargets, | ||||||
|         } |         } | ||||||
|         impl Future for MyGenerator { |         impl Future for MyGenerator<'_> { | ||||||
|             type Output = (); |             type Output = (); | ||||||
| 
 | 
 | ||||||
|             fn poll( |             fn poll( | ||||||
|  | @ -7223,65 +7337,92 @@ impl SimulationImpl { | ||||||
|                 cx: &mut std::task::Context<'_>, |                 cx: &mut std::task::Context<'_>, | ||||||
|             ) -> Poll<Self::Output> { |             ) -> Poll<Self::Output> { | ||||||
|                 let this = &mut *self; |                 let this = &mut *self; | ||||||
|                 let yielded_at_all = mem::replace(&mut this.yielded_at_all, true); |  | ||||||
|                 let mut sim = this.sim.borrow_mut(); |                 let mut sim = this.sim.borrow_mut(); | ||||||
|                 let sim = &mut *sim; |                 let sim = &mut *sim; | ||||||
|                 assert!(cx.waker().will_wake(&sim.generator_waker), "can't use ExternModuleSimulationState's methods outside of ExternModuleSimulation"); |                 assert!(cx.waker().will_wake(&sim.generator_waker), "can't use ExternModuleSimulationState's methods outside of ExternModuleSimulation"); | ||||||
|                 if let WaitTarget::Instant(target) = this.target { |                 this.targets.convert_earlier_instants_to_settle(sim.instant); | ||||||
|                     if target < sim.instant { |                 if this.targets.is_empty() { | ||||||
|                         this.target = WaitTarget::Settle; |                     this.targets.settle = true; | ||||||
|                     } else if yielded_at_all && target == sim.instant { |  | ||||||
|                         this.target = WaitTarget::Settle; |  | ||||||
|                     } |  | ||||||
|                 } |                 } | ||||||
|                 if let WaitTarget::Settle = this.target { |                 if this.targets.settle { | ||||||
|                     if yielded_at_all { |                     if this.yielded_at_all { | ||||||
|  |                         this.targets.clear(); | ||||||
|                         return Poll::Ready(()); |                         return Poll::Ready(()); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|                 let wait_target = sim.extern_modules[this.module_index] |                 sim.extern_modules[this.module_index] | ||||||
|                     .wait_target |                     .wait_targets | ||||||
|                     .get_or_insert(this.target); |                     .extend(this.targets.iter()); | ||||||
|                 *wait_target = (*wait_target).min(this.target); |                 this.targets.clear(); | ||||||
|  |                 this.yielded_at_all = true; | ||||||
|                 Poll::Pending |                 Poll::Pending | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         let target = duration.map_or(WaitTarget::Settle, |duration| { |  | ||||||
|             WaitTarget::Instant(this.borrow().instant + duration) |  | ||||||
|         }); |  | ||||||
|         MyGenerator { |         MyGenerator { | ||||||
|             sim: this, |             sim: this, | ||||||
|             yielded_at_all: false, |             yielded_at_all: false, | ||||||
|             module_index, |             module_index, | ||||||
|             target, |             targets, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     /// returns the next `WaitTarget` and the set of things ready to run then.
 |     async fn yield_advance_time_or_settle( | ||||||
|     fn get_ready_to_run_set(&self, ready_to_run_set: &mut ReadyToRunSet) -> Option<WaitTarget> { |         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(); |         ready_to_run_set.clear(); | ||||||
|         let mut wait_target = None; |         let mut retval = None; | ||||||
|         if self.state_ready_to_run { |         if self.state_ready_to_run { | ||||||
|             ready_to_run_set.state_ready_to_run = true; |             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() { |         for module_index in 0..self.extern_modules.len() { | ||||||
|             let Some(extern_module_wait_target) = extern_module.wait_target else { |             let Some(instant) = self.is_extern_module_ready_to_run(module_index) else { | ||||||
|                 continue; |                 continue; | ||||||
|             }; |             }; | ||||||
|             if let Some(wait_target) = &mut wait_target { |             if let Some(retval) = &mut retval { | ||||||
|                 match extern_module_wait_target.cmp(wait_target) { |                 match instant.cmp(retval) { | ||||||
|                     std::cmp::Ordering::Less => ready_to_run_set.clear(), |                     std::cmp::Ordering::Less => ready_to_run_set.clear(), | ||||||
|                     std::cmp::Ordering::Equal => {} |                     std::cmp::Ordering::Equal => {} | ||||||
|                     std::cmp::Ordering::Greater => continue, |                     std::cmp::Ordering::Greater => continue, | ||||||
|                 } |                 } | ||||||
|             } else { |             } else { | ||||||
|                 wait_target = Some(extern_module_wait_target); |                 retval = Some(instant); | ||||||
|             } |             } | ||||||
|             ready_to_run_set |             ready_to_run_set | ||||||
|                 .extern_modules_ready_to_run |                 .extern_modules_ready_to_run | ||||||
|                 .push(module_index); |                 .push(module_index); | ||||||
|         } |         } | ||||||
|         wait_target |         retval | ||||||
|     } |     } | ||||||
|     fn set_instant_no_sim(&mut self, instant: SimInstant) { |     fn set_instant_no_sim(&mut self, instant: SimInstant) { | ||||||
|         self.instant = instant; |         self.instant = instant; | ||||||
|  | @ -7296,8 +7437,98 @@ impl SimulationImpl { | ||||||
|             Ok(trace_writer_state) |             Ok(trace_writer_state) | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |     #[must_use] | ||||||
|     #[track_caller] |     #[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 this = this_ref.borrow_mut(); | ||||||
|         let mut ready_to_run_set = ReadyToRunSet::default(); |         let mut ready_to_run_set = ReadyToRunSet::default(); | ||||||
|         let generator_waker = this.generator_waker.clone(); |         let generator_waker = this.generator_waker.clone(); | ||||||
|  | @ -7305,10 +7536,7 @@ impl SimulationImpl { | ||||||
|             this.main_module.uninitialized_ios.is_empty(), |             this.main_module.uninitialized_ios.is_empty(), | ||||||
|             "didn't initialize all inputs", |             "didn't initialize all inputs", | ||||||
|         ); |         ); | ||||||
|         match run_target { |         let run_target = run_target.max(this.instant); | ||||||
|             WaitTarget::Settle => {} |  | ||||||
|             WaitTarget::Instant(run_target) => assert!(run_target >= this.instant), |  | ||||||
|         } |  | ||||||
|         let mut settle_cycle = 0; |         let mut settle_cycle = 0; | ||||||
|         let mut run_extern_modules = true; |         let mut run_extern_modules = true; | ||||||
|         loop { |         loop { | ||||||
|  | @ -7318,92 +7546,25 @@ impl SimulationImpl { | ||||||
|                 Some(next_wait_target) if next_wait_target <= run_target => next_wait_target, |                 Some(next_wait_target) if next_wait_target <= run_target => next_wait_target, | ||||||
|                 _ => break, |                 _ => break, | ||||||
|             }; |             }; | ||||||
|             match next_wait_target { |             if next_wait_target > this.instant { | ||||||
|                 WaitTarget::Settle => {} |                 settle_cycle = 0; | ||||||
|                 WaitTarget::Instant(instant) => { |                 this.set_instant_no_sim(next_wait_target); | ||||||
|                     settle_cycle = 0; |  | ||||||
|                     this.set_instant_no_sim(instant); |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
|             if run_extern_modules { |             if run_extern_modules { | ||||||
|                 for module_index in ready_to_run_set.extern_modules_ready_to_run.drain(..) { |                 drop(this); | ||||||
|                     let extern_module = &mut this.extern_modules[module_index]; |                 Self::run_extern_modules_cycle( | ||||||
|                     extern_module.wait_target = None; |                     this_ref, | ||||||
|                     let mut generator = if !extern_module.module_state.did_initial_settle { |                     &generator_waker, | ||||||
|                         let sim = extern_module.sim; |                     &ready_to_run_set.extern_modules_ready_to_run, | ||||||
|                         drop(this); |                 ); | ||||||
|                         Box::into_pin(sim.run(ExternModuleSimulationState { |                 this = this_ref.borrow_mut(); | ||||||
|                             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; |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
|             if ready_to_run_set.state_ready_to_run { |             if ready_to_run_set.state_ready_to_run { | ||||||
|                 this.state_ready_to_run = false; |                 if this.run_state_settle_cycle() { | ||||||
|                 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; |  | ||||||
|                     // wait for clocks to settle before running extern modules again
 |                     // wait for clocks to settle before running extern modules again
 | ||||||
|                     run_extern_modules = false; |                     run_extern_modules = false; | ||||||
|  |                 } else { | ||||||
|  |                     run_extern_modules = true; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             if this.main_module.did_initial_settle { |             if this.main_module.did_initial_settle { | ||||||
|  | @ -7434,14 +7595,14 @@ impl SimulationImpl { | ||||||
|             }); |             }); | ||||||
|             this.state.memory_write_log.clear(); |             this.state.memory_write_log.clear(); | ||||||
|         } |         } | ||||||
|         match run_target { |         if run_target > this.instant { | ||||||
|             WaitTarget::Settle => {} |             this.set_instant_no_sim(run_target); | ||||||
|             WaitTarget::Instant(instant) => this.set_instant_no_sim(instant), |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     #[track_caller] |     #[track_caller] | ||||||
|     fn settle(this_ref: &Rc<RefCell<Self>>) { |     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 { |     fn get_module(&self, which_module: WhichModule) -> &SimulationModuleState { | ||||||
|         match which_module { |         match which_module { | ||||||
|  | @ -7522,12 +7683,13 @@ impl SimulationImpl { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     #[track_caller] |     #[track_caller] | ||||||
|     fn read_write_sim_value_helper( |     fn read_write_sim_value_helper<Bits>( | ||||||
|         state: &mut interpreter::State, |         state: &mut interpreter::State, | ||||||
|         compiled_value: CompiledValue<CanonicalType>, |         compiled_value: CompiledValue<CanonicalType>, | ||||||
|         bits: &mut BitSlice, |         start_bit_index: usize, | ||||||
|         read_write_big_scalar: impl Fn(bool, &mut BitSlice, &mut BigInt) + Copy, |         bits: &mut Bits, | ||||||
|         read_write_small_scalar: impl Fn(bool, &mut BitSlice, &mut SmallUInt) + Copy, |         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 { |         match compiled_value.layout.body { | ||||||
|             CompiledTypeLayoutBody::Scalar => { |             CompiledTypeLayoutBody::Scalar => { | ||||||
|  | @ -7544,14 +7706,18 @@ impl SimulationImpl { | ||||||
|                     CanonicalType::Clock(_) => false, |                     CanonicalType::Clock(_) => false, | ||||||
|                     CanonicalType::PhantomConst(_) => unreachable!(), |                     CanonicalType::PhantomConst(_) => unreachable!(), | ||||||
|                 }; |                 }; | ||||||
|  |                 let bit_indexes = | ||||||
|  |                     start_bit_index..start_bit_index + compiled_value.layout.ty.bit_width(); | ||||||
|                 match compiled_value.range.len() { |                 match compiled_value.range.len() { | ||||||
|                     TypeLen::A_SMALL_SLOT => read_write_small_scalar( |                     TypeLen::A_SMALL_SLOT => read_write_small_scalar( | ||||||
|                         signed, |                         signed, | ||||||
|  |                         bit_indexes, | ||||||
|                         bits, |                         bits, | ||||||
|                         &mut state.small_slots[compiled_value.range.small_slots.start], |                         &mut state.small_slots[compiled_value.range.small_slots.start], | ||||||
|                     ), |                     ), | ||||||
|                     TypeLen::A_BIG_SLOT => read_write_big_scalar( |                     TypeLen::A_BIG_SLOT => read_write_big_scalar( | ||||||
|                         signed, |                         signed, | ||||||
|  |                         bit_indexes, | ||||||
|                         bits, |                         bits, | ||||||
|                         &mut state.big_slots[compiled_value.range.big_slots.start], |                         &mut state.big_slots[compiled_value.range.big_slots.start], | ||||||
|                     ), |                     ), | ||||||
|  | @ -7571,7 +7737,8 @@ impl SimulationImpl { | ||||||
|                                 .index_array(element.layout.len(), element_index), |                                 .index_array(element.layout.len(), element_index), | ||||||
|                             write: None, |                             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_big_scalar, | ||||||
|                         read_write_small_scalar, |                         read_write_small_scalar, | ||||||
|                     ); |                     ); | ||||||
|  | @ -7580,7 +7747,7 @@ impl SimulationImpl { | ||||||
|             CompiledTypeLayoutBody::Bundle { fields } => { |             CompiledTypeLayoutBody::Bundle { fields } => { | ||||||
|                 let ty = Bundle::from_canonical(compiled_value.layout.ty); |                 let ty = Bundle::from_canonical(compiled_value.layout.ty); | ||||||
|                 for ( |                 for ( | ||||||
|                     (field, offset), |                     (_field, offset), | ||||||
|                     CompiledBundleField { |                     CompiledBundleField { | ||||||
|                         offset: layout_offset, |                         offset: layout_offset, | ||||||
|                         ty: field_layout, |                         ty: field_layout, | ||||||
|  | @ -7597,7 +7764,8 @@ impl SimulationImpl { | ||||||
|                             )), |                             )), | ||||||
|                             write: None, |                             write: None, | ||||||
|                         }, |                         }, | ||||||
|                         &mut bits[offset..][..field.ty.bit_width()], |                         start_bit_index + offset, | ||||||
|  |                         bits, | ||||||
|                         read_write_big_scalar, |                         read_write_big_scalar, | ||||||
|                         read_write_small_scalar, |                         read_write_small_scalar, | ||||||
|                     ); |                     ); | ||||||
|  | @ -7606,21 +7774,89 @@ impl SimulationImpl { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     #[track_caller] |     #[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( |     fn read( | ||||||
|         &mut self, |         &mut self, | ||||||
|         io: Expr<CanonicalType>, |         io: Expr<CanonicalType>, | ||||||
|         which_module: WhichModule, |         which_module: WhichModule, | ||||||
|     ) -> MaybeNeedsSettle<ReadFn, SimValue<CanonicalType>> { |     ) -> ( | ||||||
|         self.get_module(which_module) |         CompiledValue<CanonicalType>, | ||||||
|             .read_helper(io, which_module) |         MaybeNeedsSettle<ReadFn, SimValue<CanonicalType>>, | ||||||
|             .map(|compiled_value| ReadFn { compiled_value, io }) |     ) { | ||||||
|             .apply_no_settle(&mut self.state) |         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] |     #[track_caller] | ||||||
|     fn write( |     fn write( | ||||||
|         &mut self, |         &mut self, | ||||||
|         io: Expr<CanonicalType>, |         io: Expr<CanonicalType>, | ||||||
|         value: SimValue<CanonicalType>, |         value: &SimValue<CanonicalType>, | ||||||
|         which_module: WhichModule, |         which_module: WhichModule, | ||||||
|     ) { |     ) { | ||||||
|         let compiled_value = self |         let compiled_value = self | ||||||
|  | @ -7631,20 +7867,22 @@ impl SimulationImpl { | ||||||
|         Self::read_write_sim_value_helper( |         Self::read_write_sim_value_helper( | ||||||
|             &mut self.state, |             &mut self.state, | ||||||
|             compiled_value, |             compiled_value, | ||||||
|             &mut value.into_bits(), |             0, | ||||||
|             |signed, bits, value| { |             &mut value.bits(), | ||||||
|  |             |signed, bit_range, bits, value| { | ||||||
|                 if signed { |                 if signed { | ||||||
|                     *value = SInt::bits_to_bigint(bits); |                     *value = SInt::bits_to_bigint(&bits[bit_range]); | ||||||
|                 } else { |                 } 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>()]; |                 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.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); |                 *value = SmallUInt::from_le_bytes(small_value); | ||||||
|             }, |             }, | ||||||
|         ); |         ); | ||||||
|  | @ -7920,14 +8158,14 @@ macro_rules! impl_simulation_methods { | ||||||
|             let retval = $self |             let retval = $self | ||||||
|                 .sim_impl |                 .sim_impl | ||||||
|                 .borrow_mut() |                 .borrow_mut() | ||||||
|                 .read(Expr::canonical(io), $which_module); |                 .read(Expr::canonical(io), $which_module).1; | ||||||
|             SimValue::from_canonical($self.settle_if_needed(retval)$(.$await)?) |             SimValue::from_canonical($self.settle_if_needed(retval)$(.$await)?) | ||||||
|         } |         } | ||||||
|         $(#[$track_caller])? |         $(#[$track_caller])? | ||||||
|         pub $($async)? fn write<IO: Type, V: ToSimValue<IO>>(&mut $self, io: Expr<IO>, value: V) { |         pub $($async)? fn write<IO: Type, V: ToSimValue<IO>>(&mut $self, io: Expr<IO>, value: V) { | ||||||
|             $self.sim_impl.borrow_mut().write( |             $self.sim_impl.borrow_mut().write( | ||||||
|                 Expr::canonical(io), |                 Expr::canonical(io), | ||||||
|                 value.into_sim_value(Expr::ty(io)).into_canonical(), |                 &value.into_sim_value(Expr::ty(io)).into_canonical(), | ||||||
|                 $which_module, |                 $which_module, | ||||||
|             ); |             ); | ||||||
|         } |         } | ||||||
|  | @ -8008,6 +8246,7 @@ impl<T: BundleType> Simulation<T> { | ||||||
| pub struct ExternModuleSimulationState { | pub struct ExternModuleSimulationState { | ||||||
|     sim_impl: Rc<RefCell<SimulationImpl>>, |     sim_impl: Rc<RefCell<SimulationImpl>>, | ||||||
|     module_index: usize, |     module_index: usize, | ||||||
|  |     wait_for_changes_wait_targets: EarliestWaitTargets, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl fmt::Debug for ExternModuleSimulationState { | impl fmt::Debug for ExternModuleSimulationState { | ||||||
|  | @ -8015,11 +8254,12 @@ impl fmt::Debug for ExternModuleSimulationState { | ||||||
|         let Self { |         let Self { | ||||||
|             sim_impl: _, |             sim_impl: _, | ||||||
|             module_index, |             module_index, | ||||||
|  |             wait_for_changes_wait_targets: _, | ||||||
|         } = self; |         } = self; | ||||||
|         f.debug_struct("ExternModuleSimulationState") |         f.debug_struct("ExternModuleSimulationState") | ||||||
|             .field("sim_impl", &DebugAsDisplay("...")) |             .field("sim_impl", &DebugAsDisplay("...")) | ||||||
|             .field("module_index", module_index) |             .field("module_index", module_index) | ||||||
|             .finish() |             .finish_non_exhaustive() | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -8036,6 +8276,42 @@ impl ExternModuleSimulationState { | ||||||
|         ) |         ) | ||||||
|         .await |         .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 |     async fn settle_if_needed<F, O>(&mut self, v: MaybeNeedsSettle<F, O>) -> O | ||||||
|     where |     where | ||||||
|         for<'a> F: MaybeNeedsSettleFn<&'a mut interpreter::State, Output = O>, |         for<'a> F: MaybeNeedsSettleFn<&'a mut interpreter::State, Output = O>, | ||||||
|  |  | ||||||
|  | @ -1485,3 +1485,50 @@ fn test_extern_module() { | ||||||
|         panic!(); |         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( |             running_generator: Some( | ||||||
|                 ..., |                 ..., | ||||||
|             ), |             ), | ||||||
|             wait_target: Some( |             wait_targets: { | ||||||
|                 Instant( |                 Instant( | ||||||
|                     20.500000000000 μs, |                     20.500000000000 μs, | ||||||
|                 ), |                 ), | ||||||
|             ), |             }, | ||||||
|         }, |         }, | ||||||
|     ], |     ], | ||||||
|     state_ready_to_run: false, |     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