diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index 10fbb92..988afca 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -6429,7 +6429,7 @@ impl_to_sim_value_for_int_value!(SIntValue, SInt, SIntType); struct SimulationImpl { state: interpreter::State, io: Expr, - uninitialized_inputs: HashSet, + uninitialized_inputs: HashMap>, io_targets: HashMap>, made_initial_step: bool, needs_settle: bool, @@ -6483,37 +6483,52 @@ impl SimulationImpl { .field("clocks_triggered", clocks_triggered) .finish_non_exhaustive() } - fn parse_io(&mut self, target: Target, value: CompiledValue) { + /// returns `true` if `target` or any sub-targets are uninitialized inputs + fn parse_io(&mut self, target: Target, value: CompiledValue) -> bool { self.io_targets.insert(target, value); match value.layout.body { CompiledTypeLayoutBody::Scalar => match target.flow() { - Flow::Source => {} + Flow::Source => false, Flow::Sink => { - self.uninitialized_inputs.insert(target); + self.uninitialized_inputs.insert(target, vec![]); + true } Flow::Duplex => unreachable!(), }, CompiledTypeLayoutBody::Array { .. } => { let value = value.map_ty(Array::from_canonical); + let mut sub_targets = Vec::new(); for index in 0..value.layout.ty.len() { - self.parse_io( - target.join( - TargetPathElement::from(TargetPathArrayElement { index }) - .intern_sized(), - ), - value.element(index), + let sub_target = target.join( + TargetPathElement::from(TargetPathArrayElement { index }).intern_sized(), ); + if self.parse_io(sub_target, value.element(index)) { + sub_targets.push(sub_target); + } + } + if sub_targets.is_empty() { + false + } else { + self.uninitialized_inputs.insert(target, sub_targets); + true } } CompiledTypeLayoutBody::Bundle { .. } => { let value = value.map_ty(Bundle::from_canonical); + let mut sub_targets = Vec::new(); for BundleField { name, .. } in value.layout.ty.fields() { - self.parse_io( - target.join( - TargetPathElement::from(TargetPathBundleField { name }).intern_sized(), - ), - value.field_by_name(name), + let sub_target = target.join( + TargetPathElement::from(TargetPathBundleField { name }).intern_sized(), ); + if self.parse_io(sub_target, value.field_by_name(name)) { + sub_targets.push(sub_target); + } + } + if sub_targets.is_empty() { + false + } else { + self.uninitialized_inputs.insert(target, sub_targets); + true } } } @@ -6522,7 +6537,7 @@ impl SimulationImpl { let mut retval = Self { state: State::new(compiled.insns), io: compiled.io.to_expr(), - uninitialized_inputs: HashSet::new(), + uninitialized_inputs: HashMap::new(), io_targets: HashMap::new(), made_initial_step: false, needs_settle: true, @@ -6791,6 +6806,35 @@ impl SimulationImpl { }; panic!("simulator read/write expression must not have dynamic array indexes"); } + fn mark_target_as_initialized(&mut self, mut target: Target) { + fn remove_target_and_children( + uninitialized_inputs: &mut HashMap>, + target: Target, + ) { + let Some(children) = uninitialized_inputs.remove(&target) else { + return; + }; + for child in children { + remove_target_and_children(uninitialized_inputs, child); + } + } + remove_target_and_children(&mut self.uninitialized_inputs, target); + while let Some(target_child) = target.child() { + let parent = target_child.parent(); + for child in self + .uninitialized_inputs + .get(&*parent) + .map(|v| &**v) + .unwrap_or(&[]) + { + if self.uninitialized_inputs.contains_key(child) { + return; + } + } + target = *parent; + self.uninitialized_inputs.remove(&target); + } + } #[track_caller] fn read_helper(&mut self, io: Expr) -> CompiledValue { let Some(target) = io.target() else { @@ -6810,7 +6854,7 @@ impl SimulationImpl { self.settle(); } Flow::Sink => { - if self.uninitialized_inputs.contains(&*target) { + if self.uninitialized_inputs.contains_key(&*target) { panic!("can't read from an uninitialized input"); } } @@ -6833,7 +6877,7 @@ impl SimulationImpl { Flow::Duplex => unreachable!(), } if !self.made_initial_step { - self.uninitialized_inputs.remove(&*target); + self.mark_target_as_initialized(*target); } self.needs_settle = true; compiled_value @@ -7136,11 +7180,11 @@ pub struct Simulation { io: Expr, } -struct SortedSetDebug<'a, T>(&'a HashSet); +struct SortedSetDebug<'a, T, V>(&'a HashMap); -impl fmt::Debug for SortedSetDebug<'_, T> { +impl fmt::Debug for SortedSetDebug<'_, T, V> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut entries = Vec::from_iter(self.0.iter().map(|v| { + let mut entries = Vec::from_iter(self.0.iter().map(|(v, _)| { if f.alternate() { format!("{v:#?}") } else { diff --git a/crates/fayalite/tests/sim.rs b/crates/fayalite/tests/sim.rs index bea7d93..a249235 100644 --- a/crates/fayalite/tests/sim.rs +++ b/crates/fayalite/tests/sim.rs @@ -255,8 +255,14 @@ fn test_shift_register() { let mut sim = Simulation::new(shift_register()); let mut writer = RcWriter::default(); sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); - sim.write_clock(sim.io().cd.clk, false); - sim.write_reset(sim.io().cd.rst, true); + sim.write( + sim.io().cd, + #[hdl] + ClockDomain { + clk: false.to_clock(), + rst: true.to_sync_reset(), + }, + ); sim.write_bool(sim.io().d, false); sim.advance_time(SimDuration::from_micros(1)); sim.write_clock(sim.io().cd.clk, true);