diff --git a/crates/fayalite/src/expr/target.rs b/crates/fayalite/src/expr/target.rs index e95ff84..849be00 100644 --- a/crates/fayalite/src/expr/target.rs +++ b/crates/fayalite/src/expr/target.rs @@ -14,7 +14,7 @@ use crate::{ }; use std::fmt; -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct TargetPathBundleField { pub name: Interned, } @@ -25,7 +25,7 @@ impl fmt::Display for TargetPathBundleField { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct TargetPathArrayElement { pub index: usize, } @@ -36,7 +36,7 @@ impl fmt::Display for TargetPathArrayElement { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct TargetPathDynArrayElement {} impl fmt::Display for TargetPathDynArrayElement { @@ -45,7 +45,7 @@ impl fmt::Display for TargetPathDynArrayElement { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum TargetPathElement { BundleField(TargetPathBundleField), ArrayElement(TargetPathArrayElement), @@ -197,7 +197,7 @@ macro_rules! impl_target_base { } impl_target_base! { - #[derive(Clone, PartialEq, Eq, Hash)] + #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub enum TargetBase { #[is = is_module_io] #[to = module_io] diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index 2eed11c..d33b0bb 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -30,15 +30,15 @@ use crate::{ TypeArrayIndex, TypeArrayIndexes, TypeIndex, TypeIndexRange, TypeLayout, TypeLen, TypeParts, }, - time::SimInstant, + time::{SimDuration, SimInstant}, }, ty::StaticType, - util::DebugAsDisplay, + util::{BitSliceWriteWithBase, DebugAsDisplay}, }; -use bitvec::{bits, order::Lsb0, slice::BitSlice}; +use bitvec::{bits, order::Lsb0, slice::BitSlice, vec::BitVec}; use hashbrown::{HashMap, HashSet}; use num_bigint::BigInt; -use num_traits::ToPrimitive; +use num_traits::{Signed, ToPrimitive, Zero}; use petgraph::visit::{ GraphBase, IntoNeighbors, IntoNeighborsDirected, IntoNodeIdentifiers, VisitMap, Visitable, }; @@ -1232,6 +1232,7 @@ pub struct Compiler { compiled_values_to_dyn_array_indexes: HashMap, StatePartIndex>, assignments: Assignments, + traces: Vec>, } impl Compiler { @@ -1246,6 +1247,228 @@ impl Compiler { decl_conditions: HashMap::new(), compiled_values_to_dyn_array_indexes: HashMap::new(), assignments: Assignments::default(), + traces: Vec::new(), + } + } + fn new_sim_trace(&mut self, kind: SimTraceKind) -> TraceScalarId { + let id = TraceScalarId(self.traces.len()); + self.traces.push(SimTrace { + id, + kind, + state: (), + last_state: (), + }); + id + } + fn make_trace_scalar_helper( + &mut self, + target: TargetInInstantiatedModule, + small_kind: impl FnOnce(StatePartIndex) -> SimTraceKind, + big_kind: impl FnOnce(StatePartIndex) -> SimTraceKind, + ) -> TraceScalarId { + let compiled_value = self.compile_value(target); + 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!(), + }) + } + fn make_trace_scalar( + &mut self, + target: TargetInInstantiatedModule, + name: Interned, + ) -> TraceDecl { + let flow = target.target.flow(); + match target.target.canonical_ty() { + CanonicalType::UInt(ty) => TraceUInt { + id: self.make_trace_scalar_helper( + target, + |index| SimTraceKind::SmallUInt { index, ty }, + |index| SimTraceKind::BigUInt { index, ty }, + ), + name, + ty, + flow, + } + .into(), + CanonicalType::SInt(ty) => TraceSInt { + id: self.make_trace_scalar_helper( + target, + |index| SimTraceKind::SmallSInt { index, ty }, + |index| SimTraceKind::BigSInt { index, ty }, + ), + name, + ty, + flow, + } + .into(), + CanonicalType::Bool(ty) => TraceBool { + id: self.make_trace_scalar_helper( + target, + |index| SimTraceKind::SmallBool { index }, + |index| SimTraceKind::BigBool { index }, + ), + name, + flow, + } + .into(), + CanonicalType::Array(ty) => unreachable!(), + CanonicalType::Enum(ty) => todo!(), + CanonicalType::Bundle(ty) => unreachable!(), + CanonicalType::AsyncReset(ty) => TraceAsyncReset { + id: self.make_trace_scalar_helper( + target, + |index| SimTraceKind::SmallAsyncReset { index }, + |index| SimTraceKind::BigAsyncReset { index }, + ), + name, + flow, + } + .into(), + CanonicalType::SyncReset(ty) => TraceSyncReset { + id: self.make_trace_scalar_helper( + target, + |index| SimTraceKind::SmallSyncReset { index }, + |index| SimTraceKind::BigSyncReset { index }, + ), + name, + flow, + } + .into(), + CanonicalType::Reset(ty) => todo!(), + CanonicalType::Clock(ty) => TraceClock { + id: self.make_trace_scalar_helper( + target, + |index| SimTraceKind::SmallClock { index }, + |index| SimTraceKind::BigClock { index }, + ), + name, + flow, + } + .into(), + } + } + fn make_trace_decl_child( + &mut self, + target: TargetInInstantiatedModule, + name: Interned, + ) -> TraceDecl { + match target.target.canonical_ty() { + CanonicalType::Array(ty) => { + let elements = Interned::from_iter((0..ty.len()).map(|index| { + self.make_trace_decl_child( + TargetInInstantiatedModule { + instantiated_module: target.instantiated_module, + target: target.target.join( + TargetPathElement::from(TargetPathArrayElement { index }) + .intern_sized(), + ), + }, + Intern::intern_owned(format!("[{index}]")), + ) + })); + TraceArray { + name, + elements, + ty, + flow: target.target.flow(), + } + .into() + } + CanonicalType::Enum(ty) => todo!(), + CanonicalType::Bundle(ty) => { + let fields = Interned::from_iter(ty.fields().iter().map(|field| { + self.make_trace_decl_child( + TargetInInstantiatedModule { + instantiated_module: target.instantiated_module, + target: target.target.join( + TargetPathElement::from(TargetPathBundleField { name: field.name }) + .intern_sized(), + ), + }, + field.name, + ) + })); + TraceBundle { + name, + fields, + ty, + flow: target.target.flow(), + } + .into() + } + CanonicalType::UInt(_) + | CanonicalType::SInt(_) + | CanonicalType::Bool(_) + | CanonicalType::AsyncReset(_) + | CanonicalType::SyncReset(_) + | CanonicalType::Reset(_) + | CanonicalType::Clock(_) => self.make_trace_scalar(target, name), + } + } + fn make_trace_decl( + &mut self, + instantiated_module: InstantiatedModule, + target_base: TargetBase, + ) -> TraceDecl { + let target = TargetInInstantiatedModule { + instantiated_module, + target: target_base.into(), + }; + match target_base { + TargetBase::ModuleIO(module_io) => TraceModuleIO { + name: module_io.name(), + child: self + .make_trace_decl_child(target, module_io.name()) + .intern(), + ty: module_io.ty(), + flow: module_io.flow(), + } + .into(), + TargetBase::MemPort(mem_port) => { + let name = Intern::intern_owned(mem_port.port_name().to_string()); + let TraceDecl::Scope(TraceScope::Bundle(bundle)) = + self.make_trace_decl_child(target, name) + else { + unreachable!() + }; + TraceMemPort { + name, + bundle, + ty: mem_port.ty(), + } + .into() + } + TargetBase::Reg(reg) => TraceReg { + name: reg.name(), + child: self.make_trace_decl_child(target, reg.name()).intern(), + ty: reg.ty(), + } + .into(), + TargetBase::Wire(wire) => TraceWire { + name: wire.name(), + child: self.make_trace_decl_child(target, wire.name()).intern(), + ty: wire.ty(), + } + .into(), + TargetBase::Instance(instance) => { + let TraceDecl::Scope(TraceScope::Bundle(instance_io)) = + self.make_trace_decl_child(target, instance.name()) + else { + unreachable!() + }; + let compiled_module = &self.modules[&InstantiatedModule::Child { + parent: target.instantiated_module.intern(), + instance: instance.intern(), + }]; + TraceInstance { + name: instance.name(), + instance_io, + module: compiled_module.trace_decls, + ty: instance.ty(), + } + .into() + } } } fn compile_value( @@ -1434,30 +1657,8 @@ impl Compiler { return retval; } let retval = match compiled_value.range.len() { - TypeLen { - small_slots: - StatePartLen { - value: 1, - _phantom: _, - }, - big_slots: - StatePartLen { - value: 0, - _phantom: _, - }, - } => compiled_value.range.small_slots.start, - TypeLen { - small_slots: - StatePartLen { - value: 0, - _phantom: _, - }, - big_slots: - StatePartLen { - value: 1, - _phantom: _, - }, - } => { + TypeLen::A_SMALL_SLOT => compiled_value.range.small_slots.start, + TypeLen::A_BIG_SLOT => { let debug_data = SlotDebugData { name: Interned::default(), ty: UInt::<{ SmallUInt::BITS as usize }>::TYPE.canonical(), @@ -2441,7 +2642,7 @@ impl Compiler { declaration: StmtDeclaration, parent_module: Interned, conditions: Interned<[Cond]>, - ) { + ) -> TraceDecl { let target_base: TargetBase = match &declaration { StmtDeclaration::Wire(v) => v.wire.into(), StmtDeclaration::Reg(v) => v.reg.into(), @@ -2506,12 +2707,14 @@ impl Compiler { } } } + self.make_trace_decl(*parent_module, target_base) } fn compile_block( &mut self, parent_module: Interned, block: Block, conditions: Interned<[Cond]>, + trace_decls: &mut Vec, ) { let Block { memories, stmts } = block; for memory in memories { @@ -2548,6 +2751,7 @@ impl Compiler { body: CondBody::IfTrue { cond }, source_location, }])), + trace_decls, ); self.compile_block( parent_module, @@ -2556,6 +2760,7 @@ impl Compiler { body: CondBody::IfFalse { cond }, source_location, }])), + trace_decls, ); } Stmt::Match(StmtMatch { @@ -2577,16 +2782,22 @@ impl Compiler { }, source_location, }])), + trace_decls, ); } } Stmt::Declaration(declaration) => { - self.compile_declaration(declaration, parent_module, conditions); + trace_decls.push(self.compile_declaration( + declaration, + parent_module, + conditions, + )); } } } } fn compile_module(&mut self, module: Interned) -> &CompiledModule { + let mut trace_decls = Vec::new(); let module_io = module .leaf_module() .module_io() @@ -2601,13 +2812,14 @@ impl Compiler { target: Target::from(module_io), }; self.decl_conditions.insert(target, Interned::default()); + trace_decls.push(self.make_trace_decl(*module, module_io.into())); self.compile_value(target) }, ) .collect(); match module.leaf_module().body() { ModuleBody::Normal(NormalModuleBody { body }) => { - self.compile_block(module, body, Interned::default()); + self.compile_block(module, body, Interned::default(), &mut trace_decls); } ModuleBody::Extern(_extern_module_body) => { todo!("simulating extern module: {:?}", module); @@ -2616,7 +2828,13 @@ impl Compiler { let hashbrown::hash_map::Entry::Vacant(entry) = self.modules.entry(*module) else { unreachable!("compiled same instantiated module twice"); }; - entry.insert(CompiledModule { module_io }) + entry.insert(CompiledModule { + module_io, + trace_decls: TraceModule { + name: module.leaf_module().name(), + children: Intern::intern_owned(trace_decls), + }, + }) } fn process_assignments(&mut self) { self.assignments @@ -2677,18 +2895,7 @@ impl Compiler { CondBody::IfTrue { cond: cond_value } | CondBody::IfFalse { cond: cond_value } => { let (branch_if_zero, branch_if_non_zero) = match cond_value.range.len() { - TypeLen { - small_slots: - StatePartLen { - value: 1, - _phantom: _, - }, - big_slots: - StatePartLen { - value: 0, - _phantom: _, - }, - } => ( + TypeLen::A_SMALL_SLOT => ( Insn::BranchIfSmallZero { target: end_label_index, value: cond_value.range.small_slots.start, @@ -2698,18 +2905,7 @@ impl Compiler { value: cond_value.range.small_slots.start, }, ), - TypeLen { - small_slots: - StatePartLen { - value: 0, - _phantom: _, - }, - big_slots: - StatePartLen { - value: 1, - _phantom: _, - }, - } => ( + TypeLen::A_BIG_SLOT => ( Insn::BranchIfZero { target: end_label_index, value: cond_value.range.big_slots.start, @@ -2762,11 +2958,7 @@ impl Compiler { self.base_module, self.base_module.source_location(), ), - trace_decls: TraceModule { - name: self.base_module.name(), - children: Interned::default(), // TODO: finish - }, - traces: Interned::default(), // TODO: finish + traces: Intern::intern_owned(self.traces), } } } @@ -2774,6 +2966,7 @@ impl Compiler { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] struct CompiledModule { module_io: Interned<[CompiledValue]>, + trace_decls: TraceModule, } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] @@ -2781,8 +2974,7 @@ pub struct Compiled { insns: Interned>, base_module: CompiledModule, io: Instance, - trace_decls: TraceModule, - traces: Interned<[SimTrace]>, + traces: Interned<[SimTrace<()>]>, } impl Compiled { @@ -2794,14 +2986,12 @@ impl Compiled { insns, base_module, io, - trace_decls, traces, } = self; Compiled { insns, base_module, io: Instance::from_canonical(io.canonical()), - trace_decls, traces, } } @@ -2810,22 +3000,26 @@ impl Compiled { insns, base_module, io, - trace_decls, traces, } = canonical; Self { insns, base_module, io: Instance::from_canonical(io.canonical()), - trace_decls, traces, } } } -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct TraceScalarId(usize); +impl fmt::Debug for TraceScalarId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "TraceScalarId({})", self.0) + } +} + impl TraceScalarId { pub fn as_usize(self) -> usize { self.0 @@ -2865,7 +3059,7 @@ macro_rules! impl_trace_decl { } } - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + #[derive(Copy, Clone, PartialEq, Eq, Hash)] #[non_exhaustive] $(#[$category_meta])* pub enum $category_enum { @@ -2873,6 +3067,14 @@ macro_rules! impl_trace_decl { $variant($struct),)* } + impl fmt::Debug for $category_enum { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + $(Self::$variant(v) => v.fmt(f),)* + } + } + } + impl $category_enum { pub fn kind(self) -> $category_kind { match self { @@ -2924,17 +3126,33 @@ macro_rules! impl_trace_decl { )* )* - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum TraceKind { $($(#[$category_meta])* $category_variant($category_kind),)* } - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + impl fmt::Debug for TraceKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + $(Self::$category_variant(v) => v.fmt(f),)* + } + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub enum TraceDecl { $($(#[$category_meta])* $category_variant($category_enum),)* } + + impl fmt::Debug for TraceDecl { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + $(Self::$category_variant(v) => v.fmt(f),)* + } + } + } }; } @@ -2951,46 +3169,71 @@ impl_trace_decl! { }), Instance(TraceInstance { fn children(self) -> _ { - self.children + [self.instance_io.into(), self.module.into()][..].intern() } name: Interned, - children: Interned<[TraceDecl]>, + instance_io: TraceBundle, + module: TraceModule, ty: Bundle, - flow: Flow, + }), + MemPort(TraceMemPort { + fn children(self) -> _ { + [self.bundle.into()][..].intern() + } + name: Interned, + bundle: TraceBundle, + ty: Bundle, + }), + Wire(TraceWire { + fn children(self) -> _ { + [*self.child][..].intern() + } + name: Interned, + child: Interned, + ty: CanonicalType, + }), + Reg(TraceReg { + fn children(self) -> _ { + [*self.child][..].intern() + } + name: Interned, + child: Interned, + ty: CanonicalType, }), ModuleIO(TraceModuleIO { fn children(self) -> _ { - self.children + [*self.child][..].intern() } name: Interned, - children: Interned<[TraceDecl]>, - ty: Bundle, + child: Interned, + ty: CanonicalType, flow: Flow, }), Bundle(TraceBundle { fn children(self) -> _ { - self.children + self.fields } name: Interned, - children: Interned<[TraceDecl]>, + fields: Interned<[TraceDecl]>, ty: Bundle, flow: Flow, }), Array(TraceArray { fn children(self) -> _ { - self.children + self.elements } name: Interned, - children: Interned<[TraceDecl]>, + elements: Interned<[TraceDecl]>, ty: Array, flow: Flow, }), EnumWithFields(TraceEnumWithFields { fn children(self) -> _ { - self.children + Interned::from_iter([self.discriminant.into()].into_iter().chain(self.non_empty_fields)) } name: Interned, - children: Interned<[TraceDecl]>, + discriminant: TraceEnumDiscriminant, + non_empty_fields: Interned<[TraceDecl]>, ty: Enum, flow: Flow, }), @@ -3022,7 +3265,6 @@ impl_trace_decl! { } id: TraceScalarId, name: Interned, - ty: Bool, flow: Flow, }), FieldlessEnum(TraceFieldlessEnum { @@ -3049,7 +3291,6 @@ impl_trace_decl! { } id: TraceScalarId, name: Interned, - ty: Clock, flow: Flow, }), SyncReset(TraceSyncReset { @@ -3058,7 +3299,6 @@ impl_trace_decl! { } id: TraceScalarId, name: Interned, - ty: SyncReset, flow: Flow, }), AsyncReset(TraceAsyncReset { @@ -3067,7 +3307,6 @@ impl_trace_decl! { } id: TraceScalarId, name: Interned, - ty: AsyncReset, flow: Flow, }), }), @@ -3142,6 +3381,12 @@ pub trait TraceWriter: fmt::Debug + 'static { pub struct DynTraceWriterDecls(Box); +impl DynTraceWriterDecls { + pub fn new(writer: W) -> Self { + Self(Box::new(writer)) + } +} + impl fmt::Debug for DynTraceWriterDecls { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) @@ -3261,9 +3506,111 @@ enum TraceWriterState { Errored(Option), } -#[derive(Clone, PartialEq, Eq, Hash, Debug)] -struct SimTrace { +#[derive(Clone, PartialEq, Eq, Hash)] +struct SimTrace { id: TraceScalarId, + kind: SimTraceKind, + state: S, + last_state: S, +} + +impl fmt::Debug for SimTrace<()> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + id, + kind, + state, + last_state, + } = self; + f.debug_struct("SimTrace") + .field("id", id) + .field("kind", kind) + .field("state", state) + .field("last_state", last_state) + .finish() + } +} + +impl fmt::Debug for SimTrace { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + id, + kind, + state, + last_state, + } = self; + f.debug_struct("SimTrace") + .field("id", id) + .field("kind", kind) + .field("state", &BitSliceWriteWithBase(state)) + .field("last_state", &BitSliceWriteWithBase(last_state)) + .finish() + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +enum SimTraceKind { + BigUInt { + index: StatePartIndex, + ty: UInt, + }, + BigSInt { + index: StatePartIndex, + ty: SInt, + }, + BigBool { + index: StatePartIndex, + }, + BigAsyncReset { + index: StatePartIndex, + }, + BigSyncReset { + index: StatePartIndex, + }, + BigClock { + index: StatePartIndex, + }, + SmallUInt { + index: StatePartIndex, + ty: UInt, + }, + SmallSInt { + index: StatePartIndex, + ty: SInt, + }, + SmallBool { + index: StatePartIndex, + }, + SmallAsyncReset { + index: StatePartIndex, + }, + SmallSyncReset { + index: StatePartIndex, + }, + SmallClock { + index: StatePartIndex, + }, +} + +impl SimTraceKind { + fn make_state(self) -> BitVec { + match self { + SimTraceKind::BigUInt { index: _, ty } | SimTraceKind::SmallUInt { index: _, ty } => { + BitVec::repeat(false, ty.width) + } + SimTraceKind::BigSInt { index: _, ty } | SimTraceKind::SmallSInt { index: _, ty } => { + BitVec::repeat(false, ty.width) + } + SimTraceKind::BigBool { index: _ } + | SimTraceKind::BigAsyncReset { index: _ } + | SimTraceKind::BigSyncReset { index: _ } + | SimTraceKind::BigClock { index: _ } + | SimTraceKind::SmallBool { index: _ } + | SimTraceKind::SmallAsyncReset { index: _ } + | SimTraceKind::SmallSyncReset { index: _ } + | SimTraceKind::SmallClock { index: _ } => BitVec::repeat(false, 1), + } + } } struct SimulationImpl { @@ -3273,8 +3620,9 @@ struct SimulationImpl { io_targets: HashMap>, made_initial_step: bool, trace_decls: TraceModule, - traces: Interned<[SimTrace]>, + traces: Box<[SimTrace]>, trace_writers: Vec>, + instant: SimInstant, } impl SimulationImpl { @@ -3320,9 +3668,22 @@ impl SimulationImpl { uninitialized_inputs: HashSet::new(), io_targets: HashMap::new(), made_initial_step: false, - trace_decls: compiled.trace_decls, - traces: compiled.traces, + trace_decls: compiled.base_module.trace_decls, + traces: Box::from_iter(compiled.traces.iter().map( + |&SimTrace { + id, + kind, + state: _, + last_state: _, + }| SimTrace { + id, + kind, + state: kind.make_state(), + last_state: kind.make_state(), + }, + )), trace_writers: vec![], + instant: SimInstant::START, }; let io_target = Target::from(compiled.io); for (BundleField { name, .. }, value) in compiled @@ -3340,6 +3701,123 @@ impl SimulationImpl { } retval } + fn write_traces( + &mut self, + mut trace_writer: DynTraceWriter, + ) -> std::io::Result { + for &SimTrace { + id, + kind, + ref state, + ref last_state, + } in &self.traces + { + if ONLY_IF_CHANGED && state == last_state { + continue; + } + match kind { + SimTraceKind::BigUInt { .. } | SimTraceKind::SmallUInt { .. } => { + trace_writer.set_signal_uint(id, state)?; + } + SimTraceKind::BigSInt { .. } | SimTraceKind::SmallSInt { .. } => { + trace_writer.set_signal_sint(id, state)?; + } + SimTraceKind::BigBool { .. } | SimTraceKind::SmallBool { .. } => { + trace_writer.set_signal_bool(id, state[0])?; + } + SimTraceKind::BigAsyncReset { .. } | SimTraceKind::SmallAsyncReset { .. } => { + trace_writer.set_signal_async_reset(id, state[0])?; + } + SimTraceKind::BigSyncReset { .. } | SimTraceKind::SmallSyncReset { .. } => { + trace_writer.set_signal_sync_reset(id, state[0])?; + } + SimTraceKind::BigClock { .. } | SimTraceKind::SmallClock { .. } => { + trace_writer.set_signal_clock(id, state[0])?; + } + } + } + Ok(trace_writer) + } + fn init_trace_writer( + &mut self, + trace_writer: DynTraceWriter, + ) -> std::io::Result { + let mut trace_writer = self.write_traces::(trace_writer)?; + trace_writer.finish_init()?; + Ok(trace_writer) + } + fn update_trace_writer( + &mut self, + trace_writer: DynTraceWriter, + ) -> std::io::Result { + self.write_traces::(trace_writer) + } + #[inline(never)] + fn read_traces(&mut self) { + for &mut SimTrace { + id: _, + kind, + ref mut state, + ref mut last_state, + } in &mut self.traces + { + if !IS_INITIAL_STEP { + mem::swap(state, last_state); + } + match kind { + SimTraceKind::BigUInt { index, ty: _ } | SimTraceKind::BigSInt { index, ty: _ } => { + let bigint = &self.state.big_slots[index]; + let mut bytes = bigint.to_signed_bytes_le(); + bytes.resize( + state.len().div_ceil(8), + if bigint.is_negative() { 0xFF } else { 0 }, + ); + let bitslice = BitSlice::::from_slice(&bytes); + let bitslice = &bitslice[..state.len()]; + state.clone_from_bitslice(bitslice); + } + SimTraceKind::BigBool { index } + | SimTraceKind::BigAsyncReset { index } + | SimTraceKind::BigSyncReset { index } + | SimTraceKind::BigClock { index } => { + state.set(0, !self.state.big_slots[index].is_zero()); + } + SimTraceKind::SmallUInt { index, ty: _ } + | SimTraceKind::SmallSInt { index, ty: _ } => { + let bytes = self.state.small_slots[index].to_le_bytes(); + let bitslice = BitSlice::::from_slice(&bytes); + let bitslice = &bitslice[..state.len()]; + state.clone_from_bitslice(bitslice); + } + SimTraceKind::SmallBool { index } + | SimTraceKind::SmallAsyncReset { index } + | SimTraceKind::SmallSyncReset { index } + | SimTraceKind::SmallClock { index } => { + state.set(0, self.state.small_slots[index] != 0); + } + } + if IS_INITIAL_STEP { + last_state.copy_from_bitslice(state); + } + } + } + #[track_caller] + fn advance_time(&mut self, duration: SimDuration) { + if !self.made_initial_step { + self.settle_step(); + } + self.instant += duration; + self.for_each_trace_writer_storing_error(|this, mut trace_writer_state| { + match &mut trace_writer_state { + TraceWriterState::Decls(_) | TraceWriterState::Init(_) => unreachable!(), + TraceWriterState::Running(trace_writer) => { + trace_writer.change_time_to(this.instant)?; + } + TraceWriterState::Errored(_) => {} + } + Ok(trace_writer_state) + }); + } #[track_caller] fn settle_step(&mut self) { assert!( @@ -3348,7 +3826,26 @@ impl SimulationImpl { ); self.state.setup_call(0); self.state.run(); + if self.made_initial_step { + self.read_traces::(); + } else { + self.read_traces::(); + } self.made_initial_step = true; + 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)?)?, + ), + TraceWriterState::Init(trace_writer) => { + TraceWriterState::Running(this.init_trace_writer(trace_writer)?) + } + TraceWriterState::Running(trace_writer) => { + TraceWriterState::Running(this.update_trace_writer(trace_writer)?) + } + TraceWriterState::Errored(e) => TraceWriterState::Errored(e), + }) + }); } #[track_caller] fn get_io(&self, target: Target) -> CompiledValue { @@ -3382,32 +3879,10 @@ impl SimulationImpl { } } match compiled_value.range.len() { - TypeLen { - small_slots: - StatePartLen { - value: 1, - _phantom: _, - }, - big_slots: - StatePartLen { - value: 0, - _phantom: _, - }, - } => Expr::ty(io).value_from_int_wrapping( + TypeLen::A_SMALL_SLOT => Expr::ty(io).value_from_int_wrapping( self.state.small_slots[compiled_value.range.small_slots.start], ), - TypeLen { - small_slots: - StatePartLen { - value: 0, - _phantom: _, - }, - big_slots: - StatePartLen { - value: 1, - _phantom: _, - }, - } => Expr::ty(io).value_from_int_wrapping( + TypeLen::A_BIG_SLOT => Expr::ty(io).value_from_int_wrapping( self.state.big_slots[compiled_value.range.big_slots.start].clone(), ), _ => unreachable!(), @@ -3431,33 +3906,13 @@ impl SimulationImpl { self.uninitialized_inputs.remove(&*target); } match compiled_value.range.len() { - TypeLen { - small_slots: - StatePartLen { - value: 1, - _phantom: _, - }, - big_slots: - StatePartLen { - value: 0, - _phantom: _, - }, - } => { + TypeLen::A_SMALL_SLOT => { self.state.small_slots[compiled_value.range.small_slots.start] = value.to_u64().expect("value out of range"); } - TypeLen { - small_slots: - StatePartLen { - value: 0, - _phantom: _, - }, - big_slots: - StatePartLen { - value: 1, - _phantom: _, - }, - } => self.state.big_slots[compiled_value.range.big_slots.start] = value, + TypeLen::A_BIG_SLOT => { + self.state.big_slots[compiled_value.range.big_slots.start] = value + } _ => unreachable!(), } } @@ -3477,37 +3932,40 @@ impl SimulationImpl { } retval } - fn close(mut self) -> std::io::Result<()> { - self.close_all_trace_writers() + fn for_each_trace_writer_storing_error( + &mut self, + mut f: impl FnMut( + &mut Self, + TraceWriterState, + ) -> std::io::Result>, + ) { + let mut trace_writers = mem::take(&mut self.trace_writers); + for trace_writer in &mut trace_writers { + *trace_writer = match f( + self, + mem::replace(trace_writer, TraceWriterState::Errored(None)), + ) { + Ok(v) => v, + Err(e) => TraceWriterState::Errored(Some(e)), + }; + } + self.trace_writers = trace_writers; } - fn flush_traces(&mut self) -> std::io::Result<()> { - let flush_trace_writer = |trace_writer: TraceWriterState| { - Ok(Some(match trace_writer { - TraceWriterState::Decls(v) => { - let mut v = v.write_decls(self.trace_decls)?; - v.flush()?; - TraceWriterState::Init(v) - } - TraceWriterState::Init(mut v) => { - v.flush()?; - TraceWriterState::Init(v) - } - TraceWriterState::Running(mut v) => { - v.flush()?; - TraceWriterState::Running(v) - } - TraceWriterState::Errored(Some(e)) => return Err(e), - TraceWriterState::Errored(None) => return Ok(None), - })) - }; + fn for_each_trace_writer_getting_error( + &mut self, + mut f: impl FnMut( + &mut Self, + TraceWriterState, + ) -> std::io::Result>, + ) -> std::io::Result<()> { let mut retval = Ok(()); - for trace_writer in &mut self.trace_writers { - *trace_writer = match flush_trace_writer(mem::replace( - trace_writer, - TraceWriterState::Errored(None), - )) { - Ok(Some(v)) => v, - Ok(None) => TraceWriterState::Errored(None), + let mut trace_writers = mem::take(&mut self.trace_writers); + for trace_writer in &mut trace_writers { + *trace_writer = match f( + self, + mem::replace(trace_writer, TraceWriterState::Errored(None)), + ) { + Ok(v) => v, Err(e) => { if retval.is_ok() { retval = Err(e); @@ -3518,8 +3976,33 @@ impl SimulationImpl { } }; } + self.trace_writers = trace_writers; retval } + fn close(mut self) -> std::io::Result<()> { + self.close_all_trace_writers() + } + fn flush_traces(&mut self) -> std::io::Result<()> { + 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)?; + v.flush()?; + Ok(TraceWriterState::Init(v)) + } + TraceWriterState::Init(mut v) => { + v.flush()?; + Ok(TraceWriterState::Init(v)) + } + TraceWriterState::Running(mut v) => { + v.flush()?; + Ok(TraceWriterState::Running(v)) + } + TraceWriterState::Errored(Some(e)) => Err(e), + TraceWriterState::Errored(None) => Ok(TraceWriterState::Errored(None)), + }, + ) + } } impl Drop for SimulationImpl { @@ -3587,6 +4070,7 @@ impl fmt::Debug for Simulation { trace_decls, traces, trace_writers, + instant, }, io, } = self; @@ -3602,6 +4086,7 @@ impl fmt::Debug for Simulation { .field("trace_decls", trace_decls) .field("traces", traces) .field("trace_writers", trace_writers) + .field("instant", instant) .finish() } } @@ -3610,6 +4095,11 @@ impl Simulation { pub fn new(module: Interned>) -> Self { Self::from_compiled(Compiled::new(module)) } + pub fn add_trace_writer(&mut self, writer: W) { + self.sim_impl + .trace_writers + .push(TraceWriterState::Decls(DynTraceWriterDecls::new(writer))); + } pub fn flush_traces(&mut self) -> std::io::Result<()> { self.sim_impl.flush_traces() } @@ -3645,6 +4135,10 @@ impl Simulation { self.sim_impl.settle_step(); } #[track_caller] + pub fn advance_time(&mut self, duration: SimDuration) { + self.sim_impl.advance_time(duration); + } + #[track_caller] pub fn read_bool_or_int(&self, io: Expr) -> I::Value { self.sim_impl.read_bool_or_int(io) } diff --git a/crates/fayalite/src/sim/interpreter.rs b/crates/fayalite/src/sim/interpreter.rs index 930c933..5ef2fbc 100644 --- a/crates/fayalite/src/sim/interpreter.rs +++ b/crates/fayalite/src/sim/interpreter.rs @@ -477,10 +477,10 @@ pub(crate) struct Labels { #[derive(Clone, PartialEq, Eq, Hash)] pub(crate) struct Insns { - insns: BK::Vec, - insn_source_locations: BK::Vec, - labels: BK::Labels, - state_layout: StateLayout, + pub(crate) insns: BK::Vec, + pub(crate) insn_source_locations: BK::Vec, + pub(crate) labels: BK::Labels, + pub(crate) state_layout: StateLayout, } impl Insns { @@ -1228,6 +1228,26 @@ impl TypeLen { }; Some(small_slots) } + pub(crate) const A_SMALL_SLOT: Self = TypeLen { + small_slots: StatePartLen { + value: 1, + _phantom: PhantomData, + }, + big_slots: StatePartLen { + value: 0, + _phantom: PhantomData, + }, + }; + pub(crate) const A_BIG_SLOT: Self = TypeLen { + small_slots: StatePartLen { + value: 0, + _phantom: PhantomData, + }, + big_slots: StatePartLen { + value: 1, + _phantom: PhantomData, + }, + }; } #[derive(Debug, Clone)] diff --git a/crates/fayalite/src/sim/time.rs b/crates/fayalite/src/sim/time.rs index d548ab9..07e2b79 100644 --- a/crates/fayalite/src/sim/time.rs +++ b/crates/fayalite/src/sim/time.rs @@ -1,12 +1,45 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information -use std::{fmt, time::Duration}; +use std::{ + fmt, + ops::{Add, AddAssign}, + time::Duration, +}; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct SimInstant { time_since_start: SimDuration, } +impl Add for SimInstant { + type Output = SimInstant; + + fn add(mut self, rhs: SimDuration) -> Self::Output { + self += rhs; + self + } +} + +impl AddAssign for SimInstant { + fn add_assign(&mut self, rhs: SimDuration) { + self.time_since_start += rhs; + } +} + +impl Add for SimDuration { + type Output = SimInstant; + + fn add(self, rhs: SimInstant) -> Self::Output { + rhs.add(self) + } +} + +impl SimInstant { + pub const START: SimInstant = SimInstant { + time_since_start: SimDuration::ZERO, + }; +} + impl fmt::Debug for SimInstant { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.time_since_start.fmt(f) @@ -18,6 +51,25 @@ pub struct SimDuration { attos: u128, } +impl AddAssign for SimDuration { + fn add_assign(&mut self, rhs: SimDuration) { + *self = *self + rhs; + } +} + +impl Add for SimDuration { + type Output = SimDuration; + + fn add(self, rhs: SimDuration) -> Self::Output { + SimDuration { + attos: self + .attos + .checked_add(rhs.attos) + .expect("overflow adding durations"), + } + } +} + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] pub struct SimDurationParts { pub attos: u16, diff --git a/crates/fayalite/src/util/misc.rs b/crates/fayalite/src/util/misc.rs index 85e376a..e1cc9f4 100644 --- a/crates/fayalite/src/util/misc.rs +++ b/crates/fayalite/src/util/misc.rs @@ -94,9 +94,15 @@ pub fn interned_bit(v: bool) -> Interned { RETVAL.get_or_init(|| [bits![0; 1].intern(), bits![1; 1].intern()])[v as usize] } -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone)] pub struct BitSliceWriteWithBase<'a>(pub &'a BitSlice); +impl<'a> Debug for BitSliceWriteWithBase<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{self:#x}") + } +} + impl BitSliceWriteWithBase<'_> { fn fmt_with_base( self, diff --git a/crates/fayalite/tests/sim.rs b/crates/fayalite/tests/sim.rs index fef71e5..3576b1e 100644 --- a/crates/fayalite/tests/sim.rs +++ b/crates/fayalite/tests/sim.rs @@ -117,10 +117,33 @@ fn test_connect_const() { made_initial_step: true, trace_decls: TraceModule { name: "connect_const", - children: [], + children: [ + TraceModuleIO { + name: "o", + child: TraceUInt { + id: TraceScalarId(0), + name: "o", + ty: UInt<8>, + flow: Sink, + }, + ty: UInt<8>, + flow: Sink, + }, + ], }, - traces: [], + traces: [ + SimTrace { + id: TraceScalarId(0), + kind: BigUInt { + index: StatePartIndex(0), + ty: UInt<8>, + }, + state: 0x05, + last_state: 0x05, + }, + ], trace_writers: [], + instant: 0 s, }"# { panic!(); } @@ -681,10 +704,277 @@ fn test_mod1() { made_initial_step: true, trace_decls: TraceModule { name: "mod1", - children: [], + children: [ + TraceModuleIO { + name: "o", + child: TraceBundle { + name: "o", + fields: [ + TraceUInt { + id: TraceScalarId(0), + name: "i", + ty: UInt<4>, + flow: Source, + }, + TraceSInt { + id: TraceScalarId(1), + name: "o", + ty: SInt<2>, + flow: Sink, + }, + TraceSInt { + id: TraceScalarId(2), + name: "i2", + ty: SInt<2>, + flow: Source, + }, + TraceUInt { + id: TraceScalarId(3), + name: "o2", + ty: UInt<4>, + flow: Sink, + }, + ], + ty: Bundle { + #[hdl(flip)] /* offset = 0 */ + i: UInt<4>, + /* offset = 4 */ + o: SInt<2>, + #[hdl(flip)] /* offset = 6 */ + i2: SInt<2>, + /* offset = 8 */ + o2: UInt<4>, + }, + flow: Sink, + }, + ty: Bundle { + #[hdl(flip)] /* offset = 0 */ + i: UInt<4>, + /* offset = 4 */ + o: SInt<2>, + #[hdl(flip)] /* offset = 6 */ + i2: SInt<2>, + /* offset = 8 */ + o2: UInt<4>, + }, + flow: Sink, + }, + TraceInstance { + name: "child", + instance_io: TraceBundle { + name: "child", + fields: [ + TraceUInt { + id: TraceScalarId(8), + name: "i", + ty: UInt<4>, + flow: Sink, + }, + TraceSInt { + id: TraceScalarId(9), + name: "o", + ty: SInt<2>, + flow: Source, + }, + TraceSInt { + id: TraceScalarId(10), + name: "i2", + ty: SInt<2>, + flow: Sink, + }, + TraceUInt { + id: TraceScalarId(11), + name: "o2", + ty: UInt<4>, + flow: Source, + }, + ], + ty: Bundle { + #[hdl(flip)] /* offset = 0 */ + i: UInt<4>, + /* offset = 4 */ + o: SInt<2>, + #[hdl(flip)] /* offset = 6 */ + i2: SInt<2>, + /* offset = 8 */ + o2: UInt<4>, + }, + flow: Source, + }, + module: TraceModule { + name: "mod1_child", + children: [ + TraceModuleIO { + name: "i", + child: TraceUInt { + id: TraceScalarId(4), + name: "i", + ty: UInt<4>, + flow: Source, + }, + ty: UInt<4>, + flow: Source, + }, + TraceModuleIO { + name: "o", + child: TraceSInt { + id: TraceScalarId(5), + name: "o", + ty: SInt<2>, + flow: Sink, + }, + ty: SInt<2>, + flow: Sink, + }, + TraceModuleIO { + name: "i2", + child: TraceSInt { + id: TraceScalarId(6), + name: "i2", + ty: SInt<2>, + flow: Source, + }, + ty: SInt<2>, + flow: Source, + }, + TraceModuleIO { + name: "o2", + child: TraceUInt { + id: TraceScalarId(7), + name: "o2", + ty: UInt<4>, + flow: Sink, + }, + ty: UInt<4>, + flow: Sink, + }, + ], + }, + ty: Bundle { + #[hdl(flip)] /* offset = 0 */ + i: UInt<4>, + /* offset = 4 */ + o: SInt<2>, + #[hdl(flip)] /* offset = 6 */ + i2: SInt<2>, + /* offset = 8 */ + o2: UInt<4>, + }, + }, + ], }, - traces: [], + traces: [ + SimTrace { + id: TraceScalarId(0), + kind: BigUInt { + index: StatePartIndex(0), + ty: UInt<4>, + }, + state: 0xa, + last_state: 0xa, + }, + SimTrace { + id: TraceScalarId(1), + kind: BigSInt { + index: StatePartIndex(1), + ty: SInt<2>, + }, + state: 0x2, + last_state: 0x2, + }, + SimTrace { + id: TraceScalarId(2), + kind: BigSInt { + index: StatePartIndex(2), + ty: SInt<2>, + }, + state: 0x2, + last_state: 0x2, + }, + SimTrace { + id: TraceScalarId(3), + kind: BigUInt { + index: StatePartIndex(3), + ty: UInt<4>, + }, + state: 0xf, + last_state: 0xf, + }, + SimTrace { + id: TraceScalarId(4), + kind: BigUInt { + index: StatePartIndex(8), + ty: UInt<4>, + }, + state: 0xa, + last_state: 0xa, + }, + SimTrace { + id: TraceScalarId(5), + kind: BigSInt { + index: StatePartIndex(9), + ty: SInt<2>, + }, + state: 0x2, + last_state: 0x2, + }, + SimTrace { + id: TraceScalarId(6), + kind: BigSInt { + index: StatePartIndex(10), + ty: SInt<2>, + }, + state: 0x2, + last_state: 0x2, + }, + SimTrace { + id: TraceScalarId(7), + kind: BigUInt { + index: StatePartIndex(11), + ty: UInt<4>, + }, + state: 0xf, + last_state: 0xf, + }, + SimTrace { + id: TraceScalarId(8), + kind: BigUInt { + index: StatePartIndex(4), + ty: UInt<4>, + }, + state: 0xa, + last_state: 0xa, + }, + SimTrace { + id: TraceScalarId(9), + kind: BigSInt { + index: StatePartIndex(5), + ty: SInt<2>, + }, + state: 0x2, + last_state: 0x2, + }, + SimTrace { + id: TraceScalarId(10), + kind: BigSInt { + index: StatePartIndex(6), + ty: SInt<2>, + }, + state: 0x2, + last_state: 0x2, + }, + SimTrace { + id: TraceScalarId(11), + kind: BigUInt { + index: StatePartIndex(7), + ty: UInt<4>, + }, + state: 0xf, + last_state: 0xf, + }, + ], trace_writers: [], + instant: 0 s, }"# { panic!(); }