diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index ab5b7b7..731f469 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -1567,9 +1567,37 @@ struct MemoryPortWriteInsns<'a> { struct Memory { mem: Mem, memory: StatePartIndex, + trace: TraceMem, ports: Vec, } +#[derive(Copy, Clone)] +enum MakeTraceDeclTarget { + Expr(Expr), + Memory { + id: TraceMemoryId, + depth: usize, + stride: usize, + start: usize, + ty: CanonicalType, + }, +} + +impl MakeTraceDeclTarget { + fn flow(self) -> Flow { + match self { + MakeTraceDeclTarget::Expr(expr) => Expr::flow(expr), + MakeTraceDeclTarget::Memory { .. } => Flow::Duplex, + } + } + fn ty(self) -> CanonicalType { + match self { + MakeTraceDeclTarget::Expr(expr) => Expr::ty(expr), + MakeTraceDeclTarget::Memory { ty, .. } => ty, + } + } +} + #[derive(Debug)] pub struct Compiler { insns: Insns, @@ -1589,7 +1617,7 @@ pub struct Compiler { compiled_value_to_clock_trigger_map: HashMap, ClockTrigger>, enum_discriminants: HashMap, StatePartIndex>, registers: Vec, - traces: Vec>, + traces: SimTraces>>, memories: Vec, } @@ -1614,14 +1642,13 @@ impl Compiler { compiled_value_to_clock_trigger_map: HashMap::new(), enum_discriminants: HashMap::new(), registers: Vec::new(), - traces: Vec::new(), + traces: SimTraces(Vec::new()), memories: Vec::new(), } } fn new_sim_trace(&mut self, kind: SimTraceKind) -> TraceScalarId { - let id = TraceScalarId(self.traces.len()); - self.traces.push(SimTrace { - id, + let id = TraceScalarId(self.traces.0.len()); + self.traces.0.push(SimTrace { kind, state: (), last_state: (), @@ -1631,30 +1658,47 @@ impl Compiler { fn make_trace_scalar_helper( &mut self, instantiated_module: InstantiatedModule, - target: Expr, + target: MakeTraceDeclTarget, source_location: SourceLocation, small_kind: impl FnOnce(StatePartIndex) -> SimTraceKind, big_kind: impl FnOnce(StatePartIndex) -> SimTraceKind, - ) -> TraceScalarId { - let compiled_value = self.compile_expr(instantiated_module, target); - let compiled_value = self.compiled_expr_to_value(compiled_value, source_location); - self.new_sim_trace(match compiled_value.range.len() { - TypeLen::A_SMALL_SLOT => small_kind(compiled_value.range.small_slots.start), - TypeLen::A_BIG_SLOT => big_kind(compiled_value.range.big_slots.start), - _ => unreachable!(), - }) + ) -> TraceLocation { + match target { + MakeTraceDeclTarget::Expr(target) => { + let compiled_value = self.compile_expr(instantiated_module, target); + let compiled_value = self.compiled_expr_to_value(compiled_value, source_location); + TraceLocation::Scalar(self.new_sim_trace(match compiled_value.range.len() { + TypeLen::A_SMALL_SLOT => small_kind(compiled_value.range.small_slots.start), + TypeLen::A_BIG_SLOT => big_kind(compiled_value.range.big_slots.start), + _ => unreachable!(), + })) + } + MakeTraceDeclTarget::Memory { + id, + depth, + stride, + start, + ty, + } => TraceLocation::Memory(TraceMemoryLocation { + id, + depth, + stride, + start, + len: ty.bit_width(), + }), + } } fn make_trace_scalar( &mut self, instantiated_module: InstantiatedModule, - target: Expr, + target: MakeTraceDeclTarget, name: Interned, source_location: SourceLocation, ) -> TraceDecl { - let flow = Expr::flow(target); - match Expr::ty(target) { + let flow = target.flow(); + match target.ty() { CanonicalType::UInt(ty) => TraceUInt { - id: self.make_trace_scalar_helper( + location: self.make_trace_scalar_helper( instantiated_module, target, source_location, @@ -1667,7 +1711,7 @@ impl Compiler { } .into(), CanonicalType::SInt(ty) => TraceSInt { - id: self.make_trace_scalar_helper( + location: self.make_trace_scalar_helper( instantiated_module, target, source_location, @@ -1680,7 +1724,7 @@ impl Compiler { } .into(), CanonicalType::Bool(_) => TraceBool { - id: self.make_trace_scalar_helper( + location: self.make_trace_scalar_helper( instantiated_module, target, source_location, @@ -1694,17 +1738,36 @@ impl Compiler { CanonicalType::Array(_) => unreachable!(), CanonicalType::Enum(ty) => { assert_eq!(ty.discriminant_bit_width(), ty.type_properties().bit_width); - let compiled_value = self.compile_expr(instantiated_module, target); - let compiled_value = self.compiled_expr_to_value(compiled_value, source_location); - let discriminant = self.compile_enum_discriminant( - compiled_value.map_ty(Enum::from_canonical), - source_location, - ); - TraceFieldlessEnum { - id: self.new_sim_trace(SimTraceKind::EnumDiscriminant { - index: discriminant, - ty, + let location = match target { + MakeTraceDeclTarget::Expr(target) => { + let compiled_value = self.compile_expr(instantiated_module, target); + let compiled_value = + self.compiled_expr_to_value(compiled_value, source_location); + let discriminant = self.compile_enum_discriminant( + compiled_value.map_ty(Enum::from_canonical), + source_location, + ); + TraceLocation::Scalar(self.new_sim_trace(SimTraceKind::EnumDiscriminant { + index: discriminant, + ty, + })) + } + MakeTraceDeclTarget::Memory { + id, + depth, + stride, + start, + ty: _, + } => TraceLocation::Memory(TraceMemoryLocation { + id, + depth, + stride, + start, + len: ty.type_properties().bit_width, }), + }; + TraceFieldlessEnum { + location, name, ty, flow, @@ -1713,7 +1776,7 @@ impl Compiler { } CanonicalType::Bundle(_) => unreachable!(), CanonicalType::AsyncReset(_) => TraceAsyncReset { - id: self.make_trace_scalar_helper( + location: self.make_trace_scalar_helper( instantiated_module, target, source_location, @@ -1725,7 +1788,7 @@ impl Compiler { } .into(), CanonicalType::SyncReset(_) => TraceSyncReset { - id: self.make_trace_scalar_helper( + location: self.make_trace_scalar_helper( instantiated_module, target, source_location, @@ -1738,7 +1801,7 @@ impl Compiler { .into(), CanonicalType::Reset(_) => unreachable!(), CanonicalType::Clock(_) => TraceClock { - id: self.make_trace_scalar_helper( + location: self.make_trace_scalar_helper( instantiated_module, target, source_location, @@ -1754,16 +1817,33 @@ impl Compiler { fn make_trace_decl_child( &mut self, instantiated_module: InstantiatedModule, - target: Expr, + target: MakeTraceDeclTarget, name: Interned, source_location: SourceLocation, ) -> TraceDecl { - match Expr::ty(target) { + match target.ty() { CanonicalType::Array(ty) => { let elements = Interned::from_iter((0..ty.len()).map(|index| { self.make_trace_decl_child( instantiated_module, - Expr::::from_canonical(target)[index], + match target { + MakeTraceDeclTarget::Expr(target) => MakeTraceDeclTarget::Expr( + Expr::::from_canonical(target)[index], + ), + MakeTraceDeclTarget::Memory { + id, + depth, + stride, + start, + ty: _, + } => MakeTraceDeclTarget::Memory { + id, + depth, + stride, + start: start + ty.element().bit_width() * index, + ty: ty.element(), + }, + }, Intern::intern_owned(format!("[{index}]")), source_location, ) @@ -1772,7 +1852,7 @@ impl Compiler { name, elements, ty, - flow: Expr::flow(target), + flow: target.flow(), } .into() } @@ -1780,32 +1860,73 @@ impl Compiler { if ty.variants().iter().all(|v| v.ty.is_none()) { self.make_trace_scalar(instantiated_module, target, name, source_location) } else { - let flow = Expr::flow(target); - let compiled_value = self.compile_expr(instantiated_module, target); - let compiled_value = - self.compiled_expr_to_value(compiled_value, source_location); - let discriminant = self.compile_enum_discriminant( - compiled_value.map_ty(Enum::from_canonical), - source_location, - ); - let discriminant = TraceEnumDiscriminant { - id: self.new_sim_trace(SimTraceKind::EnumDiscriminant { - index: discriminant, - ty, + let flow = target.flow(); + let location = match target { + MakeTraceDeclTarget::Expr(target) => { + let compiled_value = self.compile_expr(instantiated_module, target); + let compiled_value = + self.compiled_expr_to_value(compiled_value, source_location); + let discriminant = self.compile_enum_discriminant( + compiled_value.map_ty(Enum::from_canonical), + source_location, + ); + TraceLocation::Scalar(self.new_sim_trace( + SimTraceKind::EnumDiscriminant { + index: discriminant, + ty, + }, + )) + } + MakeTraceDeclTarget::Memory { + id, + depth, + stride, + start, + ty: _, + } => TraceLocation::Memory(TraceMemoryLocation { + id, + depth, + stride, + start, + len: ty.discriminant_bit_width(), }), + }; + let discriminant = TraceEnumDiscriminant { + location, name: "$tag".intern(), ty, flow, }; - let base = Expr::::from_canonical(target); let non_empty_fields = Interned::from_iter(ty.variants().into_iter().enumerate().flat_map( |(variant_index, variant)| { - variant.ty.map(|_| { + variant.ty.map(|variant_ty| { self.make_trace_decl_child( instantiated_module, - ops::VariantAccess::new_by_index(base, variant_index) - .to_expr(), + match target { + MakeTraceDeclTarget::Expr(target) => { + MakeTraceDeclTarget::Expr( + ops::VariantAccess::new_by_index( + Expr::::from_canonical(target), + variant_index, + ) + .to_expr(), + ) + } + MakeTraceDeclTarget::Memory { + id, + depth, + stride, + start, + ty: _, + } => MakeTraceDeclTarget::Memory { + id, + depth, + stride, + start: start + ty.discriminant_bit_width(), + ty: variant_ty, + }, + }, variant.name, source_location, ) @@ -1823,19 +1944,41 @@ impl Compiler { } } CanonicalType::Bundle(ty) => { - let fields = Interned::from_iter(ty.fields().iter().map(|field| { - self.make_trace_decl_child( - instantiated_module, - Expr::field(Expr::::from_canonical(target), &field.name), - field.name, - source_location, - ) - })); + let fields = Interned::from_iter(ty.fields().iter().zip(ty.field_offsets()).map( + |(field, field_offset)| { + self.make_trace_decl_child( + instantiated_module, + match target { + MakeTraceDeclTarget::Expr(target) => { + MakeTraceDeclTarget::Expr(Expr::field( + Expr::::from_canonical(target), + &field.name, + )) + } + MakeTraceDeclTarget::Memory { + id, + depth, + stride, + start, + ty: _, + } => MakeTraceDeclTarget::Memory { + id, + depth, + stride, + start: start + field_offset, + ty: field.ty, + }, + }, + field.name, + source_location, + ) + }, + )); TraceBundle { name, fields, ty, - flow: Expr::flow(target), + flow: target.flow(), } .into() } @@ -1855,7 +1998,7 @@ impl Compiler { instantiated_module: InstantiatedModule, target_base: TargetBase, ) -> TraceDecl { - let target = target_base.to_expr(); + let target = MakeTraceDeclTarget::Expr(target_base.to_expr()); match target_base { TargetBase::ModuleIO(module_io) => TraceModuleIO { name: module_io.name(), @@ -3810,10 +3953,10 @@ impl Compiler { insns.push( match data.len() { TypeLen::A_BIG_SLOT => { - let dest = data.big_slots.start; + let value = data.big_slots.start; if signed { - Insn::MemoryReadSInt { - dest, + Insn::MemoryWriteSInt { + value, memory, addr, stride, @@ -3821,8 +3964,8 @@ impl Compiler { width, } } else { - Insn::MemoryReadUInt { - dest, + Insn::MemoryWriteUInt { + value, memory, addr, stride, @@ -3832,7 +3975,7 @@ impl Compiler { } } TypeLen::A_SMALL_SLOT => { - let _dest = data.small_slots.start; + let _value = data.small_slots.start; todo!("memory ports' data are always big for now"); } _ => unreachable!(), @@ -4139,7 +4282,7 @@ impl Compiler { }), )) .start; - let ports = mem + let (ports, trace_ports) = mem .ports() .iter() .map(|&port| { @@ -4149,7 +4292,11 @@ impl Compiler { target: target_base.into(), }; self.decl_conditions.insert(target, conditions); - trace_decls.push(self.make_trace_decl(instantiated_module, target_base)); + let TraceDecl::Scope(TraceScope::MemPort(trace_port)) = + self.make_trace_decl(instantiated_module, target_base) + else { + unreachable!(); + }; let clk = Expr::field(port.to_expr(), "clk"); let clk = self.compile_expr(instantiated_module, clk); let clk = self.compiled_expr_to_value(clk, mem.source_location()); @@ -4318,20 +4465,53 @@ impl Compiler { }), ); self.add_assignment(Interned::default(), read_insns, mem.source_location()); - MemoryPort { - clk_triggered, - addr_delayed, - en_delayed, - data_layout, - read_data_delayed, - write_data_delayed, - write_mask_delayed, - write_mode_delayed, - write_insns, - } + ( + MemoryPort { + clk_triggered, + addr_delayed, + en_delayed, + data_layout, + read_data_delayed, + write_data_delayed, + write_mask_delayed, + write_mode_delayed, + write_insns, + }, + trace_port, + ) }) - .collect(); - self.memories.push(Memory { mem, memory, ports }); + .unzip(); + let name = mem.scoped_name().1 .0; + let id = TraceMemoryId(self.memories.len()); + let stride = mem.array_type().element().bit_width(); + let trace = TraceMem { + id, + name, + stride, + element_type: self + .make_trace_decl_child( + instantiated_module, + MakeTraceDeclTarget::Memory { + id, + depth: mem.array_type().len(), + stride, + start: 0, + ty: mem.array_type().element(), + }, + name, + mem.source_location(), + ) + .intern_sized(), + ports: Intern::intern_owned(trace_ports), + array_type: mem.array_type(), + }; + trace_decls.push(trace.into()); + self.memories.push(Memory { + mem, + memory, + trace, + ports, + }); } fn compile_block( &mut self, @@ -4657,12 +4837,13 @@ impl Compiler { } } fn process_memories(&mut self) { - for Memory { - mem, - memory: _, - ports, - } in mem::take(&mut self.memories) - { + for memory_index in 0..self.memories.len() { + let Memory { + mem, + memory: _, + trace: _, + ref mut ports, + } = self.memories[memory_index]; for MemoryPort { clk_triggered, addr_delayed, @@ -4673,7 +4854,7 @@ impl Compiler { write_mask_delayed, write_mode_delayed, write_insns, - } in ports + } in mem::take(ports) { let port_end = self.insns.new_label(); self.insns.push( @@ -4732,7 +4913,15 @@ impl Compiler { self.original_base_module, self.original_base_module.source_location(), ), - traces: Intern::intern_owned(self.traces), + traces: SimTraces(Intern::intern_owned(self.traces.0)), + trace_memories: Interned::from_iter(self.memories.iter().map( + |&Memory { + mem: _, + memory, + trace, + ports: _, + }| (memory, trace), + )), clocks_triggered, } } @@ -4749,7 +4938,8 @@ pub struct Compiled { insns: Interned>, base_module: CompiledModule, io: Instance, - traces: Interned<[SimTrace<()>]>, + traces: SimTraces]>>, + trace_memories: Interned<[(StatePartIndex, TraceMem)]>, clocks_triggered: Interned<[StatePartIndex]>, } @@ -4763,6 +4953,7 @@ impl Compiled { base_module, io, traces, + trace_memories, clocks_triggered, } = self; Compiled { @@ -4770,6 +4961,7 @@ impl Compiled { base_module, io: Instance::from_canonical(io.canonical()), traces, + trace_memories, clocks_triggered, } } @@ -4779,6 +4971,7 @@ impl Compiled { base_module, io, traces, + trace_memories, clocks_triggered, } = canonical; Self { @@ -4786,6 +4979,7 @@ impl Compiled { base_module, io: Instance::from_canonical(io.canonical()), traces, + trace_memories, clocks_triggered, } } @@ -4806,6 +5000,45 @@ impl TraceScalarId { } } +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct TraceMemoryId(usize); + +impl fmt::Debug for TraceMemoryId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "TraceMemoryId({})", self.0) + } +} + +impl TraceMemoryId { + pub fn as_usize(self) -> usize { + self.0 + } +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct TraceMemoryLocation { + pub id: TraceMemoryId, + pub depth: usize, + pub stride: usize, + pub start: usize, + pub len: usize, +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum TraceLocation { + Scalar(TraceScalarId), + Memory(TraceMemoryLocation), +} + +impl fmt::Debug for TraceLocation { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Scalar(v) => v.fmt(f), + Self::Memory(v) => v.fmt(f), + } + } +} + macro_rules! impl_trace_decl { ( $( @@ -4956,6 +5189,17 @@ impl_trace_decl! { module: TraceModule, ty: Bundle, }), + Mem(TraceMem { + fn children(self) -> _ { + Interned::from_iter([*self.element_type].into_iter().chain(self.ports.iter().map(|&v| v.into()))) + } + id: TraceMemoryId, + name: Interned, + stride: usize, + element_type: Interned, + ports: Interned<[TraceMemPort]>, + array_type: Array, + }), MemPort(TraceMemPort { fn children(self) -> _ { [self.bundle.into()][..].intern() @@ -5020,72 +5264,72 @@ impl_trace_decl! { }), #[kind = TraceScalarKind] Scalar(TraceScalar { - fn id(self) -> TraceScalarId; + fn location(self) -> TraceLocation; UInt(TraceUInt { - fn id(self) -> _ { - self.id + fn location(self) -> _ { + self.location } - id: TraceScalarId, + location: TraceLocation, name: Interned, ty: UInt, flow: Flow, }), SInt(TraceSInt { - fn id(self) -> _ { - self.id + fn location(self) -> _ { + self.location } - id: TraceScalarId, + location: TraceLocation, name: Interned, ty: SInt, flow: Flow, }), Bool(TraceBool { - fn id(self) -> _ { - self.id + fn location(self) -> _ { + self.location } - id: TraceScalarId, + location: TraceLocation, name: Interned, flow: Flow, }), FieldlessEnum(TraceFieldlessEnum { - fn id(self) -> _ { - self.id + fn location(self) -> _ { + self.location } - id: TraceScalarId, + location: TraceLocation, name: Interned, ty: Enum, flow: Flow, }), EnumDiscriminant(TraceEnumDiscriminant { - fn id(self) -> _ { - self.id + fn location(self) -> _ { + self.location } - id: TraceScalarId, + location: TraceLocation, name: Interned, ty: Enum, flow: Flow, }), Clock(TraceClock { - fn id(self) -> _ { - self.id + fn location(self) -> _ { + self.location } - id: TraceScalarId, + location: TraceLocation, name: Interned, flow: Flow, }), SyncReset(TraceSyncReset { - fn id(self) -> _ { - self.id + fn location(self) -> _ { + self.location } - id: TraceScalarId, + location: TraceLocation, name: Interned, flow: Flow, }), AsyncReset(TraceAsyncReset { - fn id(self) -> _ { - self.id + fn location(self) -> _ { + self.location } - id: TraceScalarId, + location: TraceLocation, name: Interned, flow: Flow, }), @@ -5095,11 +5339,21 @@ impl_trace_decl! { pub trait TraceWriterDecls: fmt::Debug + 'static + Sized { type Error: std::error::Error + Send + Sync + 'static; type TraceWriter: TraceWriter; - fn write_decls(self, module: TraceModule) -> Result; + fn write_decls( + self, + module: TraceModule, + trace_scalar_id_count: usize, + trace_memory_id_count: usize, + ) -> Result; } trait TraceWriterDeclsDynTrait: fmt::Debug { - fn write_decls_dyn(self: Box, module: TraceModule) -> std::io::Result; + fn write_decls_dyn( + self: Box, + module: TraceModule, + trace_scalar_id_count: usize, + trace_memory_id_count: usize, + ) -> std::io::Result; } fn err_into_io(e: E) -> std::io::Error { @@ -5110,9 +5364,20 @@ fn err_into_io(e: E) -> std::io::E } impl TraceWriterDeclsDynTrait for T { - fn write_decls_dyn(self: Box, module: TraceModule) -> std::io::Result { + fn write_decls_dyn( + self: Box, + module: TraceModule, + trace_scalar_id_count: usize, + trace_memory_id_count: usize, + ) -> std::io::Result { Ok(DynTraceWriter(Box::new( - TraceWriterDecls::write_decls(*self, module).map_err(err_into_io)?, + TraceWriterDecls::write_decls( + *self, + module, + trace_scalar_id_count, + trace_memory_id_count, + ) + .map_err(err_into_io)?, ))) } } @@ -5135,6 +5400,12 @@ pub trait TraceWriter: fmt::Debug + 'static { { Ok(()) } + fn set_memory_element( + &mut self, + memory: TraceMemoryId, + element_index: usize, + element_data: &BitSlice, + ) -> Result<(), Self::Error>; fn set_signal_uint(&mut self, id: TraceScalarId, value: &BitSlice) -> Result<(), Self::Error>; fn set_signal_sint(&mut self, id: TraceScalarId, value: &BitSlice) -> Result<(), Self::Error>; fn set_signal_bool(&mut self, id: TraceScalarId, value: bool) -> Result<(), Self::Error> { @@ -5182,8 +5453,14 @@ impl fmt::Debug for DynTraceWriterDecls { impl TraceWriterDecls for DynTraceWriterDecls { type Error = std::io::Error; type TraceWriter = DynTraceWriter; - fn write_decls(self, module: TraceModule) -> Result { - self.0.write_decls_dyn(module) + fn write_decls( + self, + module: TraceModule, + trace_scalar_id_count: usize, + trace_memory_id_count: usize, + ) -> Result { + self.0 + .write_decls_dyn(module, trace_scalar_id_count, trace_memory_id_count) } } @@ -5192,6 +5469,12 @@ trait TraceWriterDynTrait: fmt::Debug + 'static { fn change_time_to_dyn(&mut self, instant: SimInstant) -> std::io::Result<()>; fn flush_dyn(&mut self) -> std::io::Result<()>; fn close_dyn(self: Box) -> std::io::Result<()>; + fn set_memory_element_dyn( + &mut self, + memory: TraceMemoryId, + element_index: usize, + element_data: &BitSlice, + ) -> std::io::Result<()>; fn set_signal_uint_dyn(&mut self, id: TraceScalarId, value: &BitSlice) -> std::io::Result<()>; fn set_signal_sint_dyn(&mut self, id: TraceScalarId, value: &BitSlice) -> std::io::Result<()>; fn set_signal_bool_dyn(&mut self, id: TraceScalarId, value: bool) -> std::io::Result<()>; @@ -5220,6 +5503,17 @@ impl TraceWriterDynTrait for T { fn close_dyn(self: Box) -> std::io::Result<()> { Ok(TraceWriter::close(*self).map_err(err_into_io)?) } + fn set_memory_element_dyn( + &mut self, + memory: TraceMemoryId, + element_index: usize, + element_data: &BitSlice, + ) -> std::io::Result<()> { + Ok( + TraceWriter::set_memory_element(self, memory, element_index, element_data) + .map_err(err_into_io)?, + ) + } fn set_signal_uint_dyn(&mut self, id: TraceScalarId, value: &BitSlice) -> std::io::Result<()> { Ok(TraceWriter::set_signal_uint(self, id, value).map_err(err_into_io)?) } @@ -5274,6 +5568,15 @@ impl TraceWriter for DynTraceWriter { fn close(self) -> Result<(), Self::Error> { self.0.close_dyn() } + fn set_memory_element( + &mut self, + memory: TraceMemoryId, + element_index: usize, + element_data: &BitSlice, + ) -> Result<(), Self::Error> { + self.0 + .set_memory_element_dyn(memory, element_index, element_data) + } fn change_time_to(&mut self, instant: SimInstant) -> Result<(), Self::Error> { self.0.change_time_to_dyn(instant) } @@ -5318,24 +5621,91 @@ enum TraceWriterState { Errored(Option), } +trait SimTraceDebug { + fn fmt(&self, id: I, f: &mut fmt::Formatter<'_>) -> fmt::Result; +} + +struct SimTraceDebugAsDebug(T, I); + +impl fmt::Debug for SimTraceDebugAsDebug<&'_ T, I> +where + T: SimTraceDebug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(self.1, f) + } +} + +impl SimTraceDebug for Vec +where + [T]: SimTraceDebug, +{ + fn fmt(&self, id: I, f: &mut fmt::Formatter<'_>) -> fmt::Result { + <[T]>::fmt(&**self, id, f) + } +} + +impl SimTraceDebug for Interned +where + T: SimTraceDebug, +{ + fn fmt(&self, id: I, f: &mut fmt::Formatter<'_>) -> fmt::Result { + T::fmt(&**self, id, f) + } +} + +impl SimTraceDebug for Box +where + T: SimTraceDebug, +{ + fn fmt(&self, id: I, f: &mut fmt::Formatter<'_>) -> fmt::Result { + T::fmt(&**self, id, f) + } +} + +impl SimTraceDebug<()> for [T] +where + T: SimTraceDebug, +{ + fn fmt(&self, _id: (), f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list() + .entries( + self.iter() + .enumerate() + .map(|(id, v)| SimTraceDebugAsDebug(v, TraceScalarId(id))), + ) + .finish() + } +} + #[derive(Clone, PartialEq, Eq, Hash)] -struct SimTrace { - id: TraceScalarId, - kind: SimTraceKind, +struct SimTrace { + kind: K, state: S, last_state: S, } -impl fmt::Debug for SimTrace<()> { +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +struct SimTraces(T); + +impl fmt::Debug for SimTraces +where + T: SimTraceDebug<()>, +{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt((), f) + } +} + +impl SimTraceDebug for SimTrace { + fn fmt(&self, id: TraceScalarId, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Self { - id, kind, state, last_state, } = self; f.debug_struct("SimTrace") - .field("id", id) + .field("id", &id) .field("kind", kind) .field("state", state) .field("last_state", last_state) @@ -5343,16 +5713,15 @@ impl fmt::Debug for SimTrace<()> { } } -impl fmt::Debug for SimTrace { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl SimTraceDebug for SimTrace { + fn fmt(&self, id: TraceScalarId, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Self { - id, kind, state, last_state, } = self; f.debug_struct("SimTrace") - .field("id", id) + .field("id", &id) .field("kind", kind) .field("state", &BitSliceWriteWithBase(state)) .field("last_state", &BitSliceWriteWithBase(last_state)) @@ -5440,7 +5809,8 @@ struct SimulationImpl { made_initial_step: bool, needs_settle: bool, trace_decls: TraceModule, - traces: Box<[SimTrace]>, + traces: SimTraces]>>, + trace_memories: HashMap, TraceMem>, trace_writers: Vec>, instant: SimInstant, clocks_triggered: Interned<[StatePartIndex]>, @@ -5491,19 +5861,18 @@ impl SimulationImpl { made_initial_step: false, needs_settle: true, trace_decls: compiled.base_module.trace_decls, - traces: Box::from_iter(compiled.traces.iter().map( + traces: SimTraces(Box::from_iter(compiled.traces.0.iter().map( |&SimTrace { - id, kind, state: _, last_state: _, }| SimTrace { - id, kind, state: kind.make_state(), last_state: kind.make_state(), }, - )), + ))), + trace_memories: HashMap::from_iter(compiled.trace_memories.iter().copied()), trace_writers: vec![], instant: SimInstant::START, clocks_triggered: compiled.clocks_triggered, @@ -5528,16 +5897,41 @@ impl SimulationImpl { &mut self, mut trace_writer: DynTraceWriter, ) -> std::io::Result { - for &SimTrace { + let mut set_memory_element = |memory: StatePartIndex, + trace_mem: &TraceMem, + element_index: usize| { + let start = trace_mem.stride * element_index; + let end = start + trace_mem.stride; + trace_writer.set_memory_element( + self.trace_memories[&memory].id, + element_index, + &self.state.memories[memory][start..end], + ) + }; + if ONLY_IF_CHANGED { + for &(memory, element_index) in &self.state.memory_write_log { + set_memory_element(memory, &self.trace_memories[&memory], element_index)?; + } + } else { + for (&memory, trace_mem) in &self.trace_memories { + for element_index in 0..trace_mem.array_type.len() { + set_memory_element(memory, trace_mem, element_index)?; + } + } + } + for ( id, - kind, - ref state, - ref last_state, - } in &self.traces + &SimTrace { + kind, + ref state, + ref last_state, + }, + ) in self.traces.0.iter().enumerate() { if ONLY_IF_CHANGED && state == last_state { continue; } + let id = TraceScalarId(id); match kind { SimTraceKind::BigUInt { .. } | SimTraceKind::SmallUInt { .. } => { trace_writer.set_signal_uint(id, state)?; @@ -5588,11 +5982,10 @@ impl SimulationImpl { #[inline(never)] fn read_traces(&mut self) { for &mut SimTrace { - id: _, kind, ref mut state, ref mut last_state, - } in &mut self.traces + } in &mut self.traces.0 { if !IS_INITIAL_STEP { mem::swap(state, last_state); @@ -5667,6 +6060,8 @@ impl SimulationImpl { } else { self.read_traces::(); } + self.state.memory_write_log.sort_unstable(); + self.state.memory_write_log.dedup(); self.made_initial_step = true; self.needs_settle = self .clocks_triggered @@ -5675,7 +6070,11 @@ impl SimulationImpl { self.for_each_trace_writer_storing_error(|this, trace_writer_state| { Ok(match trace_writer_state { TraceWriterState::Decls(trace_writer_decls) => TraceWriterState::Running( - this.init_trace_writer(trace_writer_decls.write_decls(this.trace_decls)?)?, + this.init_trace_writer(trace_writer_decls.write_decls( + this.trace_decls, + this.traces.0.len(), + this.trace_memories.len(), + )?)?, ), TraceWriterState::Init(trace_writer) => { TraceWriterState::Running(this.init_trace_writer(trace_writer)?) @@ -5686,6 +6085,7 @@ impl SimulationImpl { TraceWriterState::Errored(e) => TraceWriterState::Errored(e), }) }); + self.state.memory_write_log.clear(); } panic!("settle(): took too many steps"); } @@ -5712,7 +6112,12 @@ impl SimulationImpl { } else { match target.flow() { Flow::Source => { - panic!("can't read from an output before the simulation has made any steps"); + if !self.uninitialized_inputs.is_empty() { + panic!( + "can't read from an output before the simulation has made any steps" + ); + } + self.settle(); } Flow::Sink => { if self.uninitialized_inputs.contains(&*target) { @@ -5802,7 +6207,13 @@ impl SimulationImpl { let mut retval = Ok(()); let close_trace_writer = |trace_writer: TraceWriterState| match trace_writer { - TraceWriterState::Decls(v) => v.write_decls(self.trace_decls)?.close(), + TraceWriterState::Decls(v) => v + .write_decls( + self.trace_decls, + self.traces.0.len(), + self.trace_memories.len(), + )? + .close(), TraceWriterState::Init(v) => v.close(), TraceWriterState::Running(v) => v.close(), TraceWriterState::Errored(Some(e)) => Err(e), @@ -5873,7 +6284,11 @@ impl SimulationImpl { self.for_each_trace_writer_getting_error( |this, trace_writer: TraceWriterState| match trace_writer { TraceWriterState::Decls(v) => { - let mut v = v.write_decls(this.trace_decls)?; + let mut v = v.write_decls( + this.trace_decls, + this.traces.0.len(), + this.trace_memories.len(), + )?; v.flush()?; Ok(TraceWriterState::Init(v)) } @@ -5957,6 +6372,7 @@ impl fmt::Debug for Simulation { needs_settle, trace_decls, traces, + trace_memories, trace_writers, instant, clocks_triggered, @@ -5975,6 +6391,7 @@ impl fmt::Debug for Simulation { .field("needs_settle", needs_settle) .field("trace_decls", trace_decls) .field("traces", traces) + .field("trace_memories", trace_memories) .field("trace_writers", trace_writers) .field("instant", instant) .field("clocks_triggered", clocks_triggered) diff --git a/crates/fayalite/src/sim/interpreter.rs b/crates/fayalite/src/sim/interpreter.rs index aabf6e2..e4a0570 100644 --- a/crates/fayalite/src/sim/interpreter.rs +++ b/crates/fayalite/src/sim/interpreter.rs @@ -990,6 +990,7 @@ macro_rules! make_state_part_kinds { pub(crate) struct State { pub(crate) insns: Interned>, pub(crate) pc: usize, + pub(crate) memory_write_log: Vec<(StatePartIndex, usize)>, $(pub(crate) $state_field: StatePart<$StateKind>,)* $(pub(crate) $type_field: StatePart<$TypeKind>,)* } @@ -999,6 +1000,7 @@ macro_rules! make_state_part_kinds { Self { insns, pc: 0, + memory_write_log: Vec::with_capacity(32), $($state_field: StatePart::new(&insns.state_layout.$state_field.layout_data),)* $($type_field: StatePart::new(&insns.state_layout.ty.$type_field.layout_data),)* } @@ -1009,6 +1011,7 @@ macro_rules! make_state_part_kinds { insns: &self.insns.insns, pc: self.pc, orig_pc: &mut self.pc, + memory_write_log: &mut self.memory_write_log, $($state_field: self.$state_field.borrow(),)* $($type_field: self.$type_field.borrow(),)* } @@ -1021,6 +1024,7 @@ macro_rules! make_state_part_kinds { pub(crate) insns: &'a [Insn], pub(crate) orig_pc: &'a mut usize, pub(crate) pc: usize, + pub(crate) memory_write_log: &'a mut Vec<(StatePartIndex, usize)>, $(pub(crate) $state_field: BorrowedStatePart<'a, $StateKind>,)* $(pub(crate) $type_field: BorrowedStatePart<'a, $TypeKind>,)* } @@ -2146,6 +2150,7 @@ impl State { let Self { insns: _, pc, + memory_write_log: _, memories: _, small_slots: _, big_slots: _, @@ -2172,6 +2177,18 @@ impl BorrowedState<'_> { } Some(retval) } + fn log_memory_write(&mut self, memory: StatePartIndex, addr: SmallUInt) { + let Ok(addr) = usize::try_from(addr) else { + return; + }; + if addr < self.memories.value.len() { + let log_entry = (memory, addr); + if self.memory_write_log.last().copied() == Some(log_entry) { + return; + } + self.memory_write_log.push(log_entry); + } + } } impl TypeIndexRange { @@ -2898,6 +2915,7 @@ impl_insns! { } => { let addr = state.small_slots[addr]; memory_write_big::(&mut state.memories[memory], addr, stride, start, width, &mut state.big_slots[value]); + state.log_memory_write(memory, addr); next!(); } MemoryWriteSInt { @@ -2916,6 +2934,7 @@ impl_insns! { } => { let addr = state.small_slots[addr]; memory_write_big::(&mut state.memories[memory], addr, stride, start, width, &mut state.big_slots[value]); + state.log_memory_write(memory, addr); next!(); } Return => { diff --git a/crates/fayalite/src/sim/vcd.rs b/crates/fayalite/src/sim/vcd.rs index 0779b5c..b8248e3 100644 --- a/crates/fayalite/src/sim/vcd.rs +++ b/crates/fayalite/src/sim/vcd.rs @@ -9,13 +9,14 @@ use crate::{ time::{SimDuration, SimInstant}, TraceArray, TraceAsyncReset, TraceBool, TraceBundle, TraceClock, TraceDecl, TraceEnumDiscriminant, TraceEnumWithFields, TraceFieldlessEnum, TraceInstance, - TraceMemPort, TraceModule, TraceModuleIO, TraceReg, TraceSInt, TraceScalar, TraceScalarId, - TraceScope, TraceSyncReset, TraceUInt, TraceWire, TraceWriter, TraceWriterDecls, + TraceLocation, TraceMem, TraceMemPort, TraceMemoryId, TraceMemoryLocation, TraceModule, + TraceModuleIO, TraceReg, TraceSInt, TraceScalar, TraceScalarId, TraceScope, TraceSyncReset, + TraceUInt, TraceWire, TraceWriter, TraceWriterDecls, }, }; -use bitvec::slice::BitSlice; +use bitvec::{order::Lsb0, slice::BitSlice}; use std::{ - fmt::{self, Display}, + fmt, io::{self, Write}, mem, }; @@ -112,20 +113,20 @@ macro_rules! trait_arg { ( trait $Arg:ident { $( - fn $fn:ident(self) -> $ty:ty; + fn $fn:ident(&mut self) -> $ty:ty; )* } ) => { trait $Arg: Sized { - $(fn $fn(self) -> $ty { + $(fn $fn(&mut self) -> $ty { unreachable!() })* } $( impl $Arg for $ty { - fn $fn(self) -> $ty { - self + fn $fn(&mut self) -> $ty { + self.reborrow() } } )* @@ -134,21 +135,52 @@ macro_rules! trait_arg { trait_arg! { trait Arg { - fn module(self) -> ArgModule; - fn module_body(self) -> ArgModuleBody; - fn in_type(self) -> ArgInType; + fn module(&mut self) -> ArgModule<'_>; + fn module_body(&mut self) -> ArgModuleBody<'_>; + fn in_type(&mut self) -> ArgInType<'_>; } } -struct ArgModule {} +struct ArgModule<'a> { + properties: &'a mut VcdWriterProperties, +} -struct ArgModuleBody {} +impl<'a> ArgModule<'a> { + fn reborrow(&mut self) -> ArgModule<'_> { + ArgModule { + properties: self.properties, + } + } +} -#[derive(Clone, Copy)] -struct ArgInType { +struct ArgModuleBody<'a> { + properties: &'a mut VcdWriterProperties, +} + +impl<'a> ArgModuleBody<'a> { + fn reborrow(&mut self) -> ArgModuleBody<'_> { + ArgModuleBody { + properties: self.properties, + } + } +} + +struct ArgInType<'a> { source_var_type: &'static str, sink_var_type: &'static str, duplex_var_type: &'static str, + properties: &'a mut VcdWriterProperties, +} + +impl<'a> ArgInType<'a> { + fn reborrow(&mut self) -> ArgInType<'_> { + ArgInType { + source_var_type: self.source_var_type, + sink_var_type: self.sink_var_type, + duplex_var_type: self.duplex_var_type, + properties: self.properties, + } + } } trait WriteTrace: Copy { @@ -179,11 +211,10 @@ impl WriteTrace for TraceScalar { } } -fn write_scalar_id(writer: &mut W, id: TraceScalarId) -> io::Result<()> { +fn write_vcd_id(writer: &mut W, mut id: usize) -> io::Result<()> { let min_char = b'!'; let max_char = b'~'; let base = (max_char - min_char + 1) as usize; - let mut id = id.as_usize(); loop { let digit = (id % base) as u8 + min_char; id /= base; @@ -195,7 +226,7 @@ fn write_scalar_id(writer: &mut W, id: TraceScalarId) -> io::Resul Ok(()) } -fn write_escaped(writer: &mut W, value: impl Display) -> io::Result<()> { +fn write_escaped(writer: &mut W, value: impl fmt::Display) -> io::Result<()> { // escaping rules from function GTKWave uses to decode VCD strings: // https://github.com/gtkwave/gtkwave/blob/491f24d7e8619cfc1fcc65704ee5c967d1083c18/lib/libfst/fstapi.c#L7090 struct Wrapper(W); @@ -247,14 +278,47 @@ fn is_unescaped_verilog_identifier(ident: &str) -> bool { } fn write_vcd_var( + properties: &mut VcdWriterProperties, + memory_element_part_body: MemoryElementPartBody, writer: &mut W, var_type: &str, size: usize, - id: TraceScalarId, + location: TraceLocation, name: &str, ) -> io::Result<()> { + let id = match location { + TraceLocation::Scalar(id) => id.as_usize(), + TraceLocation::Memory(TraceMemoryLocation { + id, + depth, + stride: _, + start, + len, + }) => { + let MemoryProperties { + element_parts, + element_part_index, + element_index, + } = &mut properties.memory_properties[id.as_usize()]; + let first_id; + if let Some(element_part) = element_parts.get(*element_part_index) { + first_id = element_part.first_id; + } else { + first_id = properties.next_scalar_id; + properties.next_scalar_id += depth; + element_parts.push(MemoryElementPart { + first_id, + start, + len, + body: memory_element_part_body, + }); + } + *element_part_index += 1; + first_id + *element_index + } + }; write!(writer, "$var {var_type} {size} ")?; - write_scalar_id(writer, id)?; + write_vcd_id(writer, id)?; writer.write_all(b" ")?; if !is_unescaped_verilog_identifier(name) { writer.write_all(b"\\")?; @@ -264,31 +328,49 @@ fn write_vcd_var( } impl WriteTrace for TraceUInt { - fn write_trace(self, writer: &mut W, arg: A) -> io::Result<()> { + fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { let ArgInType { source_var_type, sink_var_type, duplex_var_type, + properties, } = arg.in_type(); - let Self { id, name, ty, flow } = self; - let var_type = match flow { + let Self { + location, + name, + ty, + flow, + } = self; + let mut var_type = match flow { Flow::Source => source_var_type, Flow::Sink => sink_var_type, Flow::Duplex => duplex_var_type, }; if ty.width() == 0 { - write_vcd_var(writer, "string", ty.width(), id, &name) - } else { - write_vcd_var(writer, var_type, ty.width(), id, &name) + var_type = "string"; } + write_vcd_var( + properties, + MemoryElementPartBody::Scalar, + writer, + var_type, + ty.width(), + location, + &name, + ) } } impl WriteTrace for TraceSInt { fn write_trace(self, writer: &mut W, arg: A) -> io::Result<()> { - let Self { id, name, ty, flow } = self; + let Self { + location, + name, + ty, + flow, + } = self; TraceUInt { - id, + location, name, ty: UInt::new_dyn(ty.width()), flow, @@ -299,9 +381,13 @@ impl WriteTrace for TraceSInt { impl WriteTrace for TraceBool { fn write_trace(self, writer: &mut W, arg: A) -> io::Result<()> { - let Self { id, name, flow } = self; + let Self { + location, + name, + flow, + } = self; TraceUInt { - id, + location, name, flow, ty: UInt::new_dyn(1), @@ -312,46 +398,93 @@ impl WriteTrace for TraceBool { impl WriteTrace for TraceFieldlessEnum { fn write_trace(self, writer: &mut W, arg: A) -> io::Result<()> { - let Self { id, name, ty, flow } = self; - TraceEnumDiscriminant { id, name, ty, flow }.write_trace(writer, arg) + let Self { + location, + name, + ty, + flow, + } = self; + TraceEnumDiscriminant { + location, + name, + ty, + flow, + } + .write_trace(writer, arg) } } impl WriteTrace for TraceEnumDiscriminant { - fn write_trace(self, writer: &mut W, arg: A) -> io::Result<()> { + fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { let ArgInType { source_var_type: _, sink_var_type: _, duplex_var_type: _, + properties, } = arg.in_type(); let Self { - id, + location, name, - ty: _, + ty, flow: _, } = self; - write_vcd_var(writer, "string", 1, id, &name) + write_vcd_var( + properties, + MemoryElementPartBody::EnumDiscriminant { ty }, + writer, + "string", + 1, + location, + &name, + ) } } impl WriteTrace for TraceClock { fn write_trace(self, writer: &mut W, arg: A) -> io::Result<()> { - let Self { id, name, flow } = self; - TraceBool { id, name, flow }.write_trace(writer, arg) + let Self { + location, + name, + flow, + } = self; + TraceBool { + location, + name, + flow, + } + .write_trace(writer, arg) } } impl WriteTrace for TraceSyncReset { fn write_trace(self, writer: &mut W, arg: A) -> io::Result<()> { - let Self { id, name, flow } = self; - TraceBool { id, name, flow }.write_trace(writer, arg) + let Self { + location, + name, + flow, + } = self; + TraceBool { + location, + name, + flow, + } + .write_trace(writer, arg) } } impl WriteTrace for TraceAsyncReset { fn write_trace(self, writer: &mut W, arg: A) -> io::Result<()> { - let Self { id, name, flow } = self; - TraceBool { id, name, flow }.write_trace(writer, arg) + let Self { + location, + name, + flow, + } = self; + TraceBool { + location, + name, + flow, + } + .write_trace(writer, arg) } } @@ -360,6 +493,7 @@ impl WriteTrace for TraceScope { match self { Self::Module(v) => v.write_trace(writer, arg), Self::Instance(v) => v.write_trace(writer, arg), + Self::Mem(v) => v.write_trace(writer, arg), Self::MemPort(v) => v.write_trace(writer, arg), Self::Wire(v) => v.write_trace(writer, arg), Self::Reg(v) => v.write_trace(writer, arg), @@ -372,12 +506,12 @@ impl WriteTrace for TraceScope { } impl WriteTrace for TraceModule { - fn write_trace(self, writer: &mut W, arg: A) -> io::Result<()> { - let ArgModule {} = arg.module(); + fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { + let ArgModule { properties } = arg.module(); let Self { name, children } = self; write_vcd_scope(writer, "module", &name, |writer| { for child in children { - child.write_trace(writer, ArgModuleBody {})?; + child.write_trace(writer, ArgModuleBody { properties })?; } Ok(()) }) @@ -385,8 +519,8 @@ impl WriteTrace for TraceModule { } impl WriteTrace for TraceInstance { - fn write_trace(self, writer: &mut W, arg: A) -> io::Result<()> { - let ArgModuleBody {} = arg.module_body(); + fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { + let ArgModuleBody { properties } = arg.module_body(); let Self { name: _, instance_io, @@ -399,21 +533,74 @@ impl WriteTrace for TraceInstance { source_var_type: "wire", sink_var_type: "wire", duplex_var_type: "wire", + properties, }, )?; - module.write_trace(writer, ArgModule {}) + module.write_trace(writer, ArgModule { properties }) + } +} + +impl WriteTrace for TraceMem { + fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { + let ArgModuleBody { properties } = arg.module_body(); + let Self { + id, + name, + stride: _, + element_type, + ports, + array_type, + } = self; + write_vcd_scope(writer, "struct", &*name, |writer| { + write_vcd_scope(writer, "struct", "contents", |writer| { + for element_index in 0..array_type.len() { + write_vcd_scope(writer, "struct", &format!("[{element_index}]"), |writer| { + properties.memory_properties[id.as_usize()].element_index = element_index; + properties.memory_properties[id.as_usize()].element_part_index = 0; + element_type.write_trace( + writer, + ArgInType { + source_var_type: "reg", + sink_var_type: "reg", + duplex_var_type: "reg", + properties, + }, + ) + })?; + } + Ok(()) + })?; + for port in ports { + port.write_trace(writer, ArgModuleBody { properties })?; + } + Ok(()) + }) } } impl WriteTrace for TraceMemPort { - fn write_trace(self, writer: &mut W, arg: A) -> io::Result<()> { - todo!() + fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { + let ArgModuleBody { properties } = arg.module_body(); + let Self { + name: _, + bundle, + ty: _, + } = self; + bundle.write_trace( + writer, + ArgInType { + source_var_type: "wire", + sink_var_type: "wire", + duplex_var_type: "wire", + properties, + }, + ) } } impl WriteTrace for TraceWire { - fn write_trace(self, writer: &mut W, arg: A) -> io::Result<()> { - let ArgModuleBody {} = arg.module_body(); + fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { + let ArgModuleBody { properties } = arg.module_body(); let Self { name: _, child, @@ -425,14 +612,15 @@ impl WriteTrace for TraceWire { source_var_type: "wire", sink_var_type: "wire", duplex_var_type: "wire", + properties, }, ) } } impl WriteTrace for TraceReg { - fn write_trace(self, writer: &mut W, arg: A) -> io::Result<()> { - let ArgModuleBody {} = arg.module_body(); + fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { + let ArgModuleBody { properties } = arg.module_body(); let Self { name: _, child, @@ -444,14 +632,15 @@ impl WriteTrace for TraceReg { source_var_type: "reg", sink_var_type: "reg", duplex_var_type: "reg", + properties, }, ) } } impl WriteTrace for TraceModuleIO { - fn write_trace(self, writer: &mut W, arg: A) -> io::Result<()> { - let ArgModuleBody {} = arg.module_body(); + fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { + let ArgModuleBody { properties } = arg.module_body(); let Self { name: _, child, @@ -464,14 +653,15 @@ impl WriteTrace for TraceModuleIO { source_var_type: "wire", sink_var_type: "wire", duplex_var_type: "wire", + properties, }, ) } } impl WriteTrace for TraceBundle { - fn write_trace(self, writer: &mut W, arg: A) -> io::Result<()> { - let arg = arg.in_type(); + fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { + let mut arg = arg.in_type(); let Self { name, fields, @@ -480,7 +670,7 @@ impl WriteTrace for TraceBundle { } = self; write_vcd_scope(writer, "struct", &name, |writer| { for field in fields { - field.write_trace(writer, arg)?; + field.write_trace(writer, arg.reborrow())?; } Ok(()) }) @@ -488,8 +678,8 @@ impl WriteTrace for TraceBundle { } impl WriteTrace for TraceArray { - fn write_trace(self, writer: &mut W, arg: A) -> io::Result<()> { - let arg = arg.in_type(); + fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { + let mut arg = arg.in_type(); let Self { name, elements, @@ -498,7 +688,7 @@ impl WriteTrace for TraceArray { } = self; write_vcd_scope(writer, "struct", &name, |writer| { for element in elements { - element.write_trace(writer, arg)?; + element.write_trace(writer, arg.reborrow())?; } Ok(()) }) @@ -506,8 +696,8 @@ impl WriteTrace for TraceArray { } impl WriteTrace for TraceEnumWithFields { - fn write_trace(self, writer: &mut W, arg: A) -> io::Result<()> { - let arg = arg.in_type(); + fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { + let mut arg = arg.in_type(); let Self { name, discriminant, @@ -516,9 +706,9 @@ impl WriteTrace for TraceEnumWithFields { flow: _, } = self; write_vcd_scope(writer, "struct", &name, |writer| { - discriminant.write_trace(writer, arg)?; + discriminant.write_trace(writer, arg.reborrow())?; for field in non_empty_fields { - field.write_trace(writer, arg)?; + field.write_trace(writer, arg.reborrow())?; } Ok(()) }) @@ -529,76 +719,184 @@ impl TraceWriterDecls for VcdWriterDecls { type Error = io::Error; type TraceWriter = VcdWriter; - fn write_decls(self, module: TraceModule) -> Result { + fn write_decls( + self, + module: TraceModule, + trace_scalar_id_count: usize, + trace_memory_id_count: usize, + ) -> Result { let Self { mut writer, timescale, } = self; writeln!(writer, "$timescale {} $end", vcd_timescale(timescale))?; - module.write_trace(&mut writer, ArgModule {})?; + let mut properties = VcdWriterProperties { + next_scalar_id: trace_scalar_id_count, + memory_properties: (0..trace_memory_id_count) + .map(|_| MemoryProperties { + element_parts: Vec::with_capacity(8), + element_part_index: 0, + element_index: 0, + }) + .collect(), + }; + module.write_trace( + &mut writer, + ArgModule { + properties: &mut properties, + }, + )?; writeln!(writer, "$enddefinitions $end")?; writeln!(writer, "$dumpvars")?; Ok(VcdWriter { writer, finished_init: false, timescale, + properties, }) } } +enum MemoryElementPartBody { + Scalar, + EnumDiscriminant { ty: Enum }, +} + +struct MemoryElementPart { + first_id: usize, + start: usize, + len: usize, + body: MemoryElementPartBody, +} + +struct MemoryProperties { + element_parts: Vec, + element_part_index: usize, + element_index: usize, +} + +struct VcdWriterProperties { + next_scalar_id: usize, + memory_properties: Box<[MemoryProperties]>, +} + pub struct VcdWriter { writer: W, finished_init: bool, timescale: SimDuration, + properties: VcdWriterProperties, } impl VcdWriter { pub fn timescale(&self) -> SimDuration { self.timescale } - fn write_string_value_change( - &mut self, - value: impl Display, - id: TraceScalarId, - ) -> io::Result<()> { - self.writer.write_all(b"s")?; - write_escaped(&mut self.writer, value)?; - self.writer.write_all(b" ")?; - write_scalar_id(&mut self.writer, id)?; - self.writer.write_all(b"\n") +} + +fn write_string_value_change( + writer: &mut impl io::Write, + value: impl fmt::Display, + id: usize, +) -> io::Result<()> { + writer.write_all(b"s")?; + write_escaped(writer, value)?; + writer.write_all(b" ")?; + write_vcd_id(writer, id)?; + writer.write_all(b"\n") +} + +fn write_bits_value_change( + writer: &mut impl io::Write, + value: &BitSlice, + id: usize, +) -> io::Result<()> { + match value.len() { + 0 => writer.write_all(b"s0 ")?, + 1 => writer.write_all(if value[0] { b"1" } else { b"0" })?, + _ => { + writer.write_all(b"b")?; + let mut any_ones = false; + for bit in value.iter().rev() { + if *bit { + any_ones = true; + writer.write_all(b"1")?; + } else if any_ones { + writer.write_all(b"0")?; + } + } + if !any_ones { + writer.write_all(b"0")?; + } + writer.write_all(b" ")?; + } } + write_vcd_id(writer, id)?; + writer.write_all(b"\n") +} + +fn write_enum_discriminant_value_change( + writer: &mut impl io::Write, + variant_index: usize, + ty: Enum, + id: usize, +) -> io::Result<()> { + write_string_value_change( + writer, + format_args!( + "{} ({variant_index})", + ty.variants() + .get(variant_index) + .map(|v| &*v.name) + .unwrap_or(""), + ), + id, + ) } impl TraceWriter for VcdWriter { type Error = io::Error; - fn set_signal_uint(&mut self, id: TraceScalarId, value: &BitSlice) -> Result<(), Self::Error> { - match value.len() { - 0 => self.writer.write_all(b"s0 ")?, - 1 => self.writer.write_all(if value[0] { b"1" } else { b"0" })?, - _ => { - self.writer.write_all(b"b")?; - let mut any_ones = false; - for bit in value.iter().rev() { - if *bit { - any_ones = true; - self.writer.write_all(b"1")?; - } else if any_ones { - self.writer.write_all(b"0")?; - } + fn set_memory_element( + &mut self, + memory: TraceMemoryId, + element_index: usize, + element_data: &BitSlice, + ) -> Result<(), Self::Error> { + for &MemoryElementPart { + first_id, + start, + len, + ref body, + } in &self.properties.memory_properties[memory.as_usize()].element_parts + { + match body { + MemoryElementPartBody::Scalar => write_bits_value_change( + &mut self.writer, + &element_data[start..start + len], + first_id + element_index, + )?, + MemoryElementPartBody::EnumDiscriminant { ty } => { + let mut variant_index = 0; + BitSlice::::from_element_mut(&mut variant_index)[..len] + .clone_from_bitslice(&element_data[start..start + len]); + write_enum_discriminant_value_change( + &mut self.writer, + variant_index, + *ty, + first_id + element_index, + )? } - if !any_ones { - self.writer.write_all(b"0")?; - } - self.writer.write_all(b" ")?; } } - write_scalar_id(&mut self.writer, id)?; - self.writer.write_all(b"\n") + Ok(()) + } + + fn set_signal_uint(&mut self, id: TraceScalarId, value: &BitSlice) -> Result<(), Self::Error> { + write_bits_value_change(&mut self.writer, value, id.as_usize()) } fn set_signal_sint(&mut self, id: TraceScalarId, value: &BitSlice) -> Result<(), Self::Error> { - self.set_signal_uint(id, value) + write_bits_value_change(&mut self.writer, value, id.as_usize()) } fn finish_init(&mut self) -> Result<(), Self::Error> { @@ -630,16 +928,7 @@ impl TraceWriter for VcdWriter { variant_index: usize, ty: Enum, ) -> Result<(), Self::Error> { - self.write_string_value_change( - format_args!( - "{} ({variant_index})", - ty.variants() - .get(variant_index) - .map(|v| &*v.name) - .unwrap_or(""), - ), - id, - ) + write_enum_discriminant_value_change(&mut self.writer, variant_index, ty, id.as_usize()) } } @@ -649,6 +938,7 @@ impl fmt::Debug for VcdWriter { writer: _, finished_init, timescale, + properties: _, } = self; f.debug_struct("VcdWriter") .field("finished_init", finished_init) diff --git a/crates/fayalite/tests/sim.rs b/crates/fayalite/tests/sim.rs index a8b23f2..41d966b 100644 --- a/crates/fayalite/tests/sim.rs +++ b/crates/fayalite/tests/sim.rs @@ -1,5 +1,6 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information + use fayalite::{ int::UIntValue, prelude::*, @@ -7,6 +8,7 @@ use fayalite::{ sim::{time::SimDuration, vcd::VcdWriterDecls, Simulation}, util::RcWriter, }; +use std::num::NonZeroUsize; #[hdl_module(outline_generated)] pub fn connect_const() { @@ -492,4 +494,113 @@ fn test_enums() { } } -// TODO: add tests for memories +#[hdl_module(outline_generated)] +pub fn memories() { + #[hdl] + let r: fayalite::memory::ReadStruct<(UInt<8>, SInt<8>), ConstUsize<4>> = m.input(); + #[hdl] + let w: fayalite::memory::WriteStruct<(UInt<8>, SInt<8>), ConstUsize<4>> = m.input(); + #[hdl] + let mut mem = memory_with_init([(0x01u8, 0x23i8); 16]); + mem.read_latency(0); + mem.write_latency(NonZeroUsize::new(1).unwrap()); + mem.read_under_write(ReadUnderWrite::Old); + connect_any(mem.new_read_port(), r); + connect_any(mem.new_write_port(), w); +} + +#[cfg(todo)] // TODO: finish +#[hdl] +#[test] +fn test_memories() { + let _n = SourceLocation::normalize_files_for_tests(); + let mut sim = Simulation::new(memories()); + let mut writer = RcWriter::default(); + sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); + sim.write_clock(sim.io().r.clk, false); + sim.write_clock(sim.io().w.clk, false); + #[derive(Debug, PartialEq, Eq)] + struct IO { + r_addr: u8, + r_en: bool, + r_data: (u8, i8), + w_addr: u8, + w_en: bool, + w_data: (u8, i8), + w_mask: (bool, bool), + } + let io_cycles = [IO { + r_addr: 0, + r_en: false, + r_data: (0, 0), + w_addr: 0, + w_en: false, + w_data: (0, 0), + w_mask: (false, false), + }]; + for ( + cycle, + expected @ IO { + r_addr, + r_en, + r_data: _, + w_addr, + w_en, + w_data, + w_mask, + }, + ) in io_cycles.into_iter().enumerate() + { + sim.write_bool_or_int(sim.io().r.addr, r_addr.cast_to_static()); + sim.write_bool(sim.io().r.en, r_en); + sim.write_bool_or_int(sim.io().w.addr, w_addr.cast_to_static()); + sim.write_bool(sim.io().w.en, w_en); + sim.write_bool_or_int(sim.io().w.data.0, w_data.0); + sim.write_bool_or_int(sim.io().w.data.1, w_data.1); + sim.write_bool(sim.io().w.mask.0, w_mask.0); + sim.write_bool(sim.io().w.mask.1, w_mask.1); + let io = IO { + r_addr, + r_en, + r_data: ( + sim.read_bool_or_int(sim.io().r.data.0) + .to_bigint() + .try_into() + .expect("known to be in range"), + sim.read_bool_or_int(sim.io().r.data.1) + .to_bigint() + .try_into() + .expect("known to be in range"), + ), + w_addr, + w_en, + w_data, + w_mask, + }; + assert_eq!( + expected, + io, + "cycle: {cycle}\nvcd:\n{}", + String::from_utf8(writer.take()).unwrap(), + ); + sim.advance_time(SimDuration::from_micros(1)); + sim.write_clock(sim.io().r.clk, true); + sim.write_clock(sim.io().w.clk, true); + sim.advance_time(SimDuration::from_micros(1)); + sim.write_clock(sim.io().r.clk, false); + sim.write_clock(sim.io().w.clk, false); + } + sim.flush_traces().unwrap(); + let vcd = String::from_utf8(writer.take()).unwrap(); + println!("####### VCD:\n{vcd}\n#######"); + if vcd != include_str!("sim/expected/memories.vcd") { + panic!(); + } + let sim_debug = format!("{sim:#?}"); + println!("#######\n{sim_debug}\n#######"); + if sim_debug != include_str!("sim/expected/memories.txt") { + panic!(); + } +} + +// TODO: add more tests for memories diff --git a/crates/fayalite/tests/sim/expected/connect_const.txt b/crates/fayalite/tests/sim/expected/connect_const.txt index f361cc3..228e4a0 100644 --- a/crates/fayalite/tests/sim/expected/connect_const.txt +++ b/crates/fayalite/tests/sim/expected/connect_const.txt @@ -47,6 +47,7 @@ Simulation { .. }, pc: 2, + memory_write_log: [], memories: StatePart { value: [], }, @@ -112,7 +113,7 @@ Simulation { TraceModuleIO { name: "o", child: TraceUInt { - id: TraceScalarId(0), + location: TraceScalarId(0), name: "o", ty: UInt<8>, flow: Sink, @@ -133,6 +134,7 @@ Simulation { last_state: 0x05, }, ], + trace_memories: {}, trace_writers: [], instant: 0 s, clocks_triggered: [], diff --git a/crates/fayalite/tests/sim/expected/connect_const_reset.txt b/crates/fayalite/tests/sim/expected/connect_const_reset.txt index 0b0c043..2421c14 100644 --- a/crates/fayalite/tests/sim/expected/connect_const_reset.txt +++ b/crates/fayalite/tests/sim/expected/connect_const_reset.txt @@ -73,6 +73,7 @@ Simulation { .. }, pc: 5, + memory_write_log: [], memories: StatePart { value: [], }, @@ -175,7 +176,7 @@ Simulation { TraceModuleIO { name: "reset_out", child: TraceAsyncReset { - id: TraceScalarId(0), + location: TraceScalarId(0), name: "reset_out", flow: Sink, }, @@ -185,7 +186,7 @@ Simulation { TraceModuleIO { name: "bit_out", child: TraceBool { - id: TraceScalarId(1), + location: TraceScalarId(1), name: "bit_out", flow: Sink, }, @@ -212,6 +213,7 @@ Simulation { last_state: 0x1, }, ], + trace_memories: {}, trace_writers: [ Running( VcdWriter { diff --git a/crates/fayalite/tests/sim/expected/counter_async.txt b/crates/fayalite/tests/sim/expected/counter_async.txt index 0f49e80..104ddeb 100644 --- a/crates/fayalite/tests/sim/expected/counter_async.txt +++ b/crates/fayalite/tests/sim/expected/counter_async.txt @@ -168,6 +168,7 @@ Simulation { .. }, pc: 18, + memory_write_log: [], memories: StatePart { value: [], }, @@ -417,12 +418,12 @@ Simulation { name: "cd", fields: [ TraceClock { - id: TraceScalarId(0), + location: TraceScalarId(0), name: "clk", flow: Source, }, TraceAsyncReset { - id: TraceScalarId(1), + location: TraceScalarId(1), name: "rst", flow: Source, }, @@ -446,7 +447,7 @@ Simulation { TraceModuleIO { name: "count", child: TraceUInt { - id: TraceScalarId(2), + location: TraceScalarId(2), name: "count", ty: UInt<4>, flow: Sink, @@ -457,7 +458,7 @@ Simulation { TraceReg { name: "count_reg", child: TraceUInt { - id: TraceScalarId(3), + location: TraceScalarId(3), name: "count_reg", ty: UInt<4>, flow: Duplex, @@ -502,6 +503,7 @@ Simulation { last_state: 0x3, }, ], + trace_memories: {}, trace_writers: [ Running( VcdWriter { diff --git a/crates/fayalite/tests/sim/expected/counter_sync.txt b/crates/fayalite/tests/sim/expected/counter_sync.txt index 30b84b2..cf425e0 100644 --- a/crates/fayalite/tests/sim/expected/counter_sync.txt +++ b/crates/fayalite/tests/sim/expected/counter_sync.txt @@ -150,6 +150,7 @@ Simulation { .. }, pc: 15, + memory_write_log: [], memories: StatePart { value: [], }, @@ -398,12 +399,12 @@ Simulation { name: "cd", fields: [ TraceClock { - id: TraceScalarId(0), + location: TraceScalarId(0), name: "clk", flow: Source, }, TraceSyncReset { - id: TraceScalarId(1), + location: TraceScalarId(1), name: "rst", flow: Source, }, @@ -427,7 +428,7 @@ Simulation { TraceModuleIO { name: "count", child: TraceUInt { - id: TraceScalarId(2), + location: TraceScalarId(2), name: "count", ty: UInt<4>, flow: Sink, @@ -438,7 +439,7 @@ Simulation { TraceReg { name: "count_reg", child: TraceUInt { - id: TraceScalarId(3), + location: TraceScalarId(3), name: "count_reg", ty: UInt<4>, flow: Duplex, @@ -483,6 +484,7 @@ Simulation { last_state: 0x3, }, ], + trace_memories: {}, trace_writers: [ Running( VcdWriter { diff --git a/crates/fayalite/tests/sim/expected/enums.txt b/crates/fayalite/tests/sim/expected/enums.txt index 69c94dd..8b7f172 100644 --- a/crates/fayalite/tests/sim/expected/enums.txt +++ b/crates/fayalite/tests/sim/expected/enums.txt @@ -875,6 +875,7 @@ Simulation { .. }, pc: 100, + memory_write_log: [], memories: StatePart { value: [], }, @@ -1333,12 +1334,12 @@ Simulation { name: "cd", fields: [ TraceClock { - id: TraceScalarId(0), + location: TraceScalarId(0), name: "clk", flow: Source, }, TraceSyncReset { - id: TraceScalarId(1), + location: TraceScalarId(1), name: "rst", flow: Source, }, @@ -1362,7 +1363,7 @@ Simulation { TraceModuleIO { name: "en", child: TraceBool { - id: TraceScalarId(2), + location: TraceScalarId(2), name: "en", flow: Source, }, @@ -1372,7 +1373,7 @@ Simulation { TraceModuleIO { name: "which_in", child: TraceUInt { - id: TraceScalarId(3), + location: TraceScalarId(3), name: "which_in", ty: UInt<2>, flow: Source, @@ -1383,7 +1384,7 @@ Simulation { TraceModuleIO { name: "data_in", child: TraceUInt { - id: TraceScalarId(4), + location: TraceScalarId(4), name: "data_in", ty: UInt<4>, flow: Source, @@ -1394,7 +1395,7 @@ Simulation { TraceModuleIO { name: "which_out", child: TraceUInt { - id: TraceScalarId(5), + location: TraceScalarId(5), name: "which_out", ty: UInt<2>, flow: Sink, @@ -1405,7 +1406,7 @@ Simulation { TraceModuleIO { name: "data_out", child: TraceUInt { - id: TraceScalarId(6), + location: TraceScalarId(6), name: "data_out", ty: UInt<4>, flow: Sink, @@ -1418,7 +1419,7 @@ Simulation { child: TraceEnumWithFields { name: "the_reg", discriminant: TraceEnumDiscriminant { - id: TraceScalarId(7), + location: TraceScalarId(7), name: "$tag", ty: Enum { A, @@ -1432,13 +1433,13 @@ Simulation { name: "B", fields: [ TraceUInt { - id: TraceScalarId(8), + location: TraceScalarId(8), name: "0", ty: UInt<1>, flow: Source, }, TraceBool { - id: TraceScalarId(9), + location: TraceScalarId(9), name: "1", flow: Source, }, @@ -1458,13 +1459,13 @@ Simulation { name: "a", elements: [ TraceUInt { - id: TraceScalarId(10), + location: TraceScalarId(10), name: "[0]", ty: UInt<1>, flow: Source, }, TraceUInt { - id: TraceScalarId(11), + location: TraceScalarId(11), name: "[1]", ty: UInt<1>, flow: Source, @@ -1474,7 +1475,7 @@ Simulation { flow: Source, }, TraceSInt { - id: TraceScalarId(12), + location: TraceScalarId(12), name: "b", ty: SInt<2>, flow: Source, @@ -1623,6 +1624,7 @@ Simulation { last_state: 0x3, }, ], + trace_memories: {}, trace_writers: [ Running( VcdWriter { diff --git a/crates/fayalite/tests/sim/expected/mod1.txt b/crates/fayalite/tests/sim/expected/mod1.txt index cf15b07..82ec9ce 100644 --- a/crates/fayalite/tests/sim/expected/mod1.txt +++ b/crates/fayalite/tests/sim/expected/mod1.txt @@ -180,6 +180,7 @@ Simulation { .. }, pc: 17, + memory_write_log: [], memories: StatePart { value: [], }, @@ -531,25 +532,25 @@ Simulation { name: "o", fields: [ TraceUInt { - id: TraceScalarId(0), + location: TraceScalarId(0), name: "i", ty: UInt<4>, flow: Source, }, TraceSInt { - id: TraceScalarId(1), + location: TraceScalarId(1), name: "o", ty: SInt<2>, flow: Sink, }, TraceSInt { - id: TraceScalarId(2), + location: TraceScalarId(2), name: "i2", ty: SInt<2>, flow: Source, }, TraceUInt { - id: TraceScalarId(3), + location: TraceScalarId(3), name: "o2", ty: UInt<4>, flow: Sink, @@ -585,25 +586,25 @@ Simulation { name: "child", fields: [ TraceUInt { - id: TraceScalarId(8), + location: TraceScalarId(8), name: "i", ty: UInt<4>, flow: Sink, }, TraceSInt { - id: TraceScalarId(9), + location: TraceScalarId(9), name: "o", ty: SInt<2>, flow: Source, }, TraceSInt { - id: TraceScalarId(10), + location: TraceScalarId(10), name: "i2", ty: SInt<2>, flow: Sink, }, TraceUInt { - id: TraceScalarId(11), + location: TraceScalarId(11), name: "o2", ty: UInt<4>, flow: Source, @@ -627,7 +628,7 @@ Simulation { TraceModuleIO { name: "i", child: TraceUInt { - id: TraceScalarId(4), + location: TraceScalarId(4), name: "i", ty: UInt<4>, flow: Source, @@ -638,7 +639,7 @@ Simulation { TraceModuleIO { name: "o", child: TraceSInt { - id: TraceScalarId(5), + location: TraceScalarId(5), name: "o", ty: SInt<2>, flow: Sink, @@ -649,7 +650,7 @@ Simulation { TraceModuleIO { name: "i2", child: TraceSInt { - id: TraceScalarId(6), + location: TraceScalarId(6), name: "i2", ty: SInt<2>, flow: Source, @@ -660,7 +661,7 @@ Simulation { TraceModuleIO { name: "o2", child: TraceUInt { - id: TraceScalarId(7), + location: TraceScalarId(7), name: "o2", ty: UInt<4>, flow: Sink, @@ -793,6 +794,7 @@ Simulation { last_state: 0xe, }, ], + trace_memories: {}, trace_writers: [ Running( VcdWriter { diff --git a/crates/fayalite/tests/sim/expected/shift_register.txt b/crates/fayalite/tests/sim/expected/shift_register.txt index e89351d..fac75cb 100644 --- a/crates/fayalite/tests/sim/expected/shift_register.txt +++ b/crates/fayalite/tests/sim/expected/shift_register.txt @@ -227,6 +227,7 @@ Simulation { .. }, pc: 30, + memory_write_log: [], memories: StatePart { value: [], }, @@ -513,12 +514,12 @@ Simulation { name: "cd", fields: [ TraceClock { - id: TraceScalarId(0), + location: TraceScalarId(0), name: "clk", flow: Source, }, TraceSyncReset { - id: TraceScalarId(1), + location: TraceScalarId(1), name: "rst", flow: Source, }, @@ -542,7 +543,7 @@ Simulation { TraceModuleIO { name: "d", child: TraceBool { - id: TraceScalarId(2), + location: TraceScalarId(2), name: "d", flow: Source, }, @@ -552,7 +553,7 @@ Simulation { TraceModuleIO { name: "q", child: TraceBool { - id: TraceScalarId(3), + location: TraceScalarId(3), name: "q", flow: Sink, }, @@ -562,7 +563,7 @@ Simulation { TraceReg { name: "reg0", child: TraceBool { - id: TraceScalarId(4), + location: TraceScalarId(4), name: "reg0", flow: Duplex, }, @@ -571,7 +572,7 @@ Simulation { TraceReg { name: "reg1", child: TraceBool { - id: TraceScalarId(5), + location: TraceScalarId(5), name: "reg1", flow: Duplex, }, @@ -580,7 +581,7 @@ Simulation { TraceReg { name: "reg2", child: TraceBool { - id: TraceScalarId(6), + location: TraceScalarId(6), name: "reg2", flow: Duplex, }, @@ -589,7 +590,7 @@ Simulation { TraceReg { name: "reg3", child: TraceBool { - id: TraceScalarId(7), + location: TraceScalarId(7), name: "reg3", flow: Duplex, }, @@ -663,6 +664,7 @@ Simulation { last_state: 0x0, }, ], + trace_memories: {}, trace_writers: [ Running( VcdWriter {