sim: fix sim.write to struct #9

Merged
programmerjake merged 1 commit from programmerjake/fayalite:fix-sim-write-struct into master 2024-12-19 05:04:18 +00:00
2 changed files with 73 additions and 23 deletions

View file

@ -6429,7 +6429,7 @@ impl_to_sim_value_for_int_value!(SIntValue, SInt, SIntType);
struct SimulationImpl { struct SimulationImpl {
state: interpreter::State, state: interpreter::State,
io: Expr<Bundle>, io: Expr<Bundle>,
uninitialized_inputs: HashSet<Target>, uninitialized_inputs: HashMap<Target, Vec<Target>>,
io_targets: HashMap<Target, CompiledValue<CanonicalType>>, io_targets: HashMap<Target, CompiledValue<CanonicalType>>,
made_initial_step: bool, made_initial_step: bool,
needs_settle: bool, needs_settle: bool,
@ -6483,37 +6483,52 @@ impl SimulationImpl {
.field("clocks_triggered", clocks_triggered) .field("clocks_triggered", clocks_triggered)
.finish_non_exhaustive() .finish_non_exhaustive()
} }
fn parse_io(&mut self, target: Target, value: CompiledValue<CanonicalType>) { /// returns `true` if `target` or any sub-targets are uninitialized inputs
fn parse_io(&mut self, target: Target, value: CompiledValue<CanonicalType>) -> bool {
self.io_targets.insert(target, value); self.io_targets.insert(target, value);
match value.layout.body { match value.layout.body {
CompiledTypeLayoutBody::Scalar => match target.flow() { CompiledTypeLayoutBody::Scalar => match target.flow() {
Flow::Source => {} Flow::Source => false,
Flow::Sink => { Flow::Sink => {
self.uninitialized_inputs.insert(target); self.uninitialized_inputs.insert(target, vec![]);
true
} }
Flow::Duplex => unreachable!(), Flow::Duplex => unreachable!(),
}, },
CompiledTypeLayoutBody::Array { .. } => { CompiledTypeLayoutBody::Array { .. } => {
let value = value.map_ty(Array::from_canonical); let value = value.map_ty(Array::from_canonical);
let mut sub_targets = Vec::new();
for index in 0..value.layout.ty.len() { for index in 0..value.layout.ty.len() {
self.parse_io( let sub_target = target.join(
target.join( TargetPathElement::from(TargetPathArrayElement { index }).intern_sized(),
TargetPathElement::from(TargetPathArrayElement { index })
.intern_sized(),
),
value.element(index),
); );
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 { .. } => { CompiledTypeLayoutBody::Bundle { .. } => {
let value = value.map_ty(Bundle::from_canonical); let value = value.map_ty(Bundle::from_canonical);
let mut sub_targets = Vec::new();
for BundleField { name, .. } in value.layout.ty.fields() { for BundleField { name, .. } in value.layout.ty.fields() {
self.parse_io( let sub_target = target.join(
target.join( TargetPathElement::from(TargetPathBundleField { name }).intern_sized(),
TargetPathElement::from(TargetPathBundleField { name }).intern_sized(),
),
value.field_by_name(name),
); );
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 { let mut retval = Self {
state: State::new(compiled.insns), state: State::new(compiled.insns),
io: compiled.io.to_expr(), io: compiled.io.to_expr(),
uninitialized_inputs: HashSet::new(), uninitialized_inputs: HashMap::new(),
io_targets: HashMap::new(), io_targets: HashMap::new(),
made_initial_step: false, made_initial_step: false,
needs_settle: true, needs_settle: true,
@ -6791,6 +6806,35 @@ impl SimulationImpl {
}; };
panic!("simulator read/write expression must not have dynamic array indexes"); 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, Vec<Target>>,
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] #[track_caller]
fn read_helper(&mut self, io: Expr<CanonicalType>) -> CompiledValue<CanonicalType> { fn read_helper(&mut self, io: Expr<CanonicalType>) -> CompiledValue<CanonicalType> {
let Some(target) = io.target() else { let Some(target) = io.target() else {
@ -6810,7 +6854,7 @@ impl SimulationImpl {
self.settle(); self.settle();
} }
Flow::Sink => { Flow::Sink => {
if self.uninitialized_inputs.contains(&*target) { if self.uninitialized_inputs.contains_key(&*target) {
panic!("can't read from an uninitialized input"); panic!("can't read from an uninitialized input");
} }
} }
@ -6833,7 +6877,7 @@ impl SimulationImpl {
Flow::Duplex => unreachable!(), Flow::Duplex => unreachable!(),
} }
if !self.made_initial_step { if !self.made_initial_step {
self.uninitialized_inputs.remove(&*target); self.mark_target_as_initialized(*target);
} }
self.needs_settle = true; self.needs_settle = true;
compiled_value compiled_value
@ -7136,11 +7180,11 @@ pub struct Simulation<T: BundleType> {
io: Expr<T>, io: Expr<T>,
} }
struct SortedSetDebug<'a, T>(&'a HashSet<T>); struct SortedSetDebug<'a, T, V>(&'a HashMap<T, V>);
impl<T: fmt::Debug> fmt::Debug for SortedSetDebug<'_, T> { impl<T: fmt::Debug, V> fmt::Debug for SortedSetDebug<'_, T, V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 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() { if f.alternate() {
format!("{v:#?}") format!("{v:#?}")
} else { } else {

View file

@ -255,8 +255,14 @@ fn test_shift_register() {
let mut sim = Simulation::new(shift_register()); let mut sim = Simulation::new(shift_register());
let mut writer = RcWriter::default(); let mut writer = RcWriter::default();
sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
sim.write_clock(sim.io().cd.clk, false); sim.write(
sim.write_reset(sim.io().cd.rst, true); sim.io().cd,
#[hdl]
ClockDomain {
clk: false.to_clock(),
rst: true.to_sync_reset(),
},
);
sim.write_bool(sim.io().d, false); sim.write_bool(sim.io().d, false);
sim.advance_time(SimDuration::from_micros(1)); sim.advance_time(SimDuration::from_micros(1));
sim.write_clock(sim.io().cd.clk, true); sim.write_clock(sim.io().cd.clk, true);