From 0095570f1911b5afe4d248cb3cbea2e96408d2dc Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 14 Nov 2024 20:28:43 -0800 Subject: [PATCH] simple combinatorial simulation works! --- crates/fayalite/src/int.rs | 91 +++- crates/fayalite/src/module.rs | 12 + crates/fayalite/src/sim.rs | 353 +++++++++++++-- crates/fayalite/src/sim/interpreter.rs | 17 + crates/fayalite/src/wire.rs | 12 + crates/fayalite/tests/sim.rs | 597 ++++++++++++++++--------- 6 files changed, 831 insertions(+), 251 deletions(-) diff --git a/crates/fayalite/src/int.rs b/crates/fayalite/src/int.rs index 03b2c88..68f1cac 100644 --- a/crates/fayalite/src/int.rs +++ b/crates/fayalite/src/int.rs @@ -227,12 +227,29 @@ macro_rules! impl_int { impl BoolOrIntType for $name { type Width = Width; type Signed = ConstBool<$SIGNED>; + type Value = $value; fn width(self) -> usize { $name::width(self) } fn new(width: Width::SizeType) -> Self { $name { width } } + fn value_from_bigint_wrapping(self, v: BigInt) -> Self::Value { + $value::::from_bigint_wrapping(self, v) + } + fn bits_to_value(bits: Cow<'_, BitSlice>) -> Self::Value { + #[derive(Copy, Clone, Eq, PartialEq, Hash)] + struct MemoizeBitsToValue; + impl Memoize for MemoizeBitsToValue { + type Input = BitSlice; + type InputOwned = BitVec; + type Output = Arc; + fn inner(self, input: &Self::Input) -> Self::Output { + Arc::new(input.to_bitvec()) + } + } + $value::new(MemoizeBitsToValue.get_cow(bits)) + } fn bits_to_expr(bits: Cow<'_, BitSlice>) -> Expr { #[derive(Copy, Clone, Eq, PartialEq, Hash)] struct MemoizeBitsToExpr; @@ -334,6 +351,24 @@ macro_rules! impl_int { } } + impl PartialOrd for $value { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } + } + + impl Ord for $value { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.to_bigint().cmp(&other.to_bigint()) + } + } + + impl From<$value> for BigInt { + fn from(v: $value) -> BigInt { + v.to_bigint() + } + } + impl $value { pub fn width(&self) -> usize { if let Some(retval) = Width::KNOWN_VALUE { @@ -482,6 +517,19 @@ macro_rules! impl_prim_int { $(#[$meta:meta])* $prim_int:ident, $ty:ty ) => { + impl From<$prim_int> for <$ty as BoolOrIntType>::Value { + fn from(v: $prim_int) -> Self { + <$ty>::le_bytes_to_value_wrapping( + &v.to_le_bytes(), + <$ty as BoolOrIntType>::Width::VALUE, + ) + } + } + impl From> for <$ty as BoolOrIntType>::Value { + fn from(v: NonZero<$prim_int>) -> Self { + v.get().into() + } + } $(#[$meta])* impl ToExpr for $prim_int { type Type = $ty; @@ -498,10 +546,7 @@ macro_rules! impl_prim_int { type Type = $ty; fn to_expr(&self) -> Expr { - <$ty>::le_bytes_to_expr_wrapping( - &self.get().to_le_bytes(), - <$ty as BoolOrIntType>::Width::VALUE, - ) + self.get().to_expr() } } }; @@ -531,6 +576,15 @@ impl_prim_int!( pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed { type Width: Size; type Signed: GenericConstBool; + type Value: Clone + + Ord + + std::hash::Hash + + fmt::Debug + + Send + + Sync + + 'static + + ToExpr + + Into; fn width(self) -> usize; fn new(width: ::SizeType) -> Self; fn new_static() -> Self @@ -545,6 +599,10 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed { fn as_same_width_uint(self) -> UIntType { UIntType::new(Self::Width::from_usize(self.width())) } + fn value_from_int_wrapping(self, v: impl Into) -> Self::Value { + self.value_from_bigint_wrapping(v.into()) + } + fn value_from_bigint_wrapping(self, v: BigInt) -> Self::Value; fn bits_from_bigint_wrapping(self, v: BigInt) -> BitVec { let width = self.width(); let mut bytes = v.to_signed_bytes_le(); @@ -567,8 +625,9 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed { BitSlice::::from_slice_mut(&mut bytes)[..bits.len()].clone_from_bitslice(bits); BigInt::from_signed_bytes_le(&bytes) } + fn bits_to_value(bits: Cow<'_, BitSlice>) -> Self::Value; fn bits_to_expr(bits: Cow<'_, BitSlice>) -> Expr; - fn le_bytes_to_expr_wrapping(bytes: &[u8], bit_width: usize) -> Expr { + fn le_bytes_to_bits_wrapping(bytes: &[u8], bit_width: usize) -> BitVec { let bitslice = BitSlice::::from_slice(bytes); let bitslice = &bitslice[..bit_width.min(bitslice.len())]; let mut bits = BitVec::new(); @@ -577,7 +636,17 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed { bit_width, Self::Signed::VALUE && bits.last().as_deref().copied().unwrap_or(false), ); - Self::bits_to_expr(Cow::Owned(bits)) + bits + } + fn le_bytes_to_expr_wrapping(bytes: &[u8], bit_width: usize) -> Expr { + Self::bits_to_expr(Cow::Owned(Self::le_bytes_to_bits_wrapping( + bytes, bit_width, + ))) + } + fn le_bytes_to_value_wrapping(bytes: &[u8], bit_width: usize) -> Self::Value { + Self::bits_to_value(Cow::Owned(Self::le_bytes_to_bits_wrapping( + bytes, bit_width, + ))) } } @@ -629,6 +698,7 @@ impl sealed::BoolOrIntTypeSealed for Bool {} impl BoolOrIntType for Bool { type Width = ConstUsize<1>; type Signed = ConstBool; + type Value = bool; fn width(self) -> usize { 1 @@ -639,10 +709,19 @@ impl BoolOrIntType for Bool { Bool } + fn value_from_bigint_wrapping(self, v: BigInt) -> Self::Value { + v.bit(0) + } + fn bits_to_expr(bits: Cow<'_, BitSlice>) -> Expr { assert_eq!(bits.len(), 1); bits[0].to_expr() } + + fn bits_to_value(bits: Cow<'_, BitSlice>) -> Self::Value { + assert_eq!(bits.len(), 1); + bits[0] + } } impl Bool { diff --git a/crates/fayalite/src/module.rs b/crates/fayalite/src/module.rs index 7387832..16010cf 100644 --- a/crates/fayalite/src/module.rs +++ b/crates/fayalite/src/module.rs @@ -714,6 +714,18 @@ impl Instance { source_location, } } + pub fn from_canonical(v: Instance) -> Self { + let Instance { + scoped_name, + instantiated, + source_location, + } = v; + Self { + scoped_name, + instantiated: Module::from_canonical(*instantiated).intern_sized(), + source_location, + } + } pub fn containing_module_name(self) -> Interned { self.containing_module_name_id().0 } diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index 7793dce..90991b4 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -11,29 +11,33 @@ use crate::{ GetTarget, Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathElement, }, - ExprEnum, Flow, + ExprEnum, Flow, ToLiteralBits, }, + int::BoolOrIntType, intern::{Intern, Interned, Memoize}, module::{ - AnnotatedModuleIO, Block, ModuleBody, NormalModuleBody, Stmt, StmtConnect, StmtDeclaration, - StmtFormal, StmtIf, StmtInstance, StmtMatch, StmtReg, StmtWire, + AnnotatedModuleIO, Block, Id, ModuleBody, NameId, NormalModuleBody, ScopedNameId, Stmt, + StmtConnect, StmtDeclaration, StmtFormal, StmtIf, StmtInstance, StmtMatch, StmtReg, + StmtWire, }, prelude::*, sim::interpreter::{ Insn, InsnField, InsnFieldKind, InsnFieldType, Insns, InsnsBuilding, InsnsBuildingDone, - SlotDebugData, SmallUInt, StatePartArrayIndex, StatePartArrayIndexed, StatePartIndex, - StatePartIndexRange, StatePartKind, StatePartKindBigSlots, StatePartKindSmallSlots, - StatePartLayout, StatePartLen, StatePartsValue, TypeArrayIndex, TypeArrayIndexes, - TypeIndex, TypeIndexRange, TypeLayout, TypeLen, TypeParts, + SlotDebugData, SmallUInt, State, StatePartArrayIndex, StatePartArrayIndexed, + StatePartIndex, StatePartIndexRange, StatePartKind, StatePartKindBigSlots, + StatePartKindSmallSlots, StatePartLayout, StatePartLen, StatePartsValue, TypeArrayIndex, + TypeArrayIndexes, TypeIndex, TypeIndexRange, TypeLayout, TypeLen, TypeParts, }, ty::StaticType, + util::DebugAsDisplay, }; -use hashbrown::HashMap; +use hashbrown::{HashMap, HashSet}; use num_bigint::BigInt; +use num_traits::ToPrimitive; use petgraph::visit::{ GraphBase, IntoNeighbors, IntoNeighborsDirected, IntoNodeIdentifiers, VisitMap, Visitable, }; -use std::{collections::BTreeSet, fmt, marker::PhantomData, mem, ops::IndexMut}; +use std::{borrow::Cow, collections::BTreeSet, fmt, marker::PhantomData, mem, ops::IndexMut}; mod interpreter; @@ -2744,7 +2748,14 @@ impl Compiler { Compiled { insns: Insns::from(self.insns).intern_sized(), base_module, - base_module_io_ty: self.base_module.io_ty(), + io: Instance::new_unchecked( + ScopedNameId( + NameId("".intern(), Id::new()), + self.base_module.name_id(), + ), + self.base_module, + self.base_module.source_location(), + ), } } } @@ -2758,7 +2769,7 @@ struct CompiledModule { pub struct Compiled { insns: Interned>, base_module: CompiledModule, - base_module_io_ty: T, + io: Instance, } impl Compiled { @@ -2769,48 +2780,338 @@ impl Compiled { let Self { insns, base_module, - base_module_io_ty, + io, } = self; Compiled { insns, base_module, - base_module_io_ty: Bundle::from_canonical(base_module_io_ty.canonical()), + io: Instance::from_canonical(io.canonical()), } } pub fn from_canonical(canonical: Compiled) -> Self { let Compiled { insns, base_module, - base_module_io_ty, + io, } = canonical; Self { insns, base_module, - base_module_io_ty: T::from_canonical(base_module_io_ty.canonical()), + io: Instance::from_canonical(io.canonical()), } } } -#[derive(Debug)] -pub struct Simulation { +struct SimulationImpl { state: interpreter::State, - base_module: CompiledModule, - base_module_io_ty: T, + io: Expr, + uninitialized_inputs: HashSet, + io_targets: HashMap>, + made_initial_step: bool, +} + +impl SimulationImpl { + fn parse_io(&mut self, target: Target, value: CompiledValue) { + self.io_targets.insert(target, value); + match value.layout.body { + CompiledTypeLayoutBody::Scalar => match target.flow() { + Flow::Source => {} + Flow::Sink => { + self.uninitialized_inputs.insert(target); + } + Flow::Duplex => unreachable!(), + }, + CompiledTypeLayoutBody::Array { .. } => { + let value = value.map_ty(Array::from_canonical); + for index in 0..value.layout.ty.len() { + self.parse_io( + target.join( + TargetPathElement::from(TargetPathArrayElement { index }) + .intern_sized(), + ), + value.element(index), + ); + } + } + CompiledTypeLayoutBody::Bundle { .. } => { + let value = value.map_ty(Bundle::from_canonical); + for BundleField { name, .. } in value.layout.ty.fields() { + self.parse_io( + target.join( + TargetPathElement::from(TargetPathBundleField { name }).intern_sized(), + ), + value.field_by_name(name), + ); + } + } + } + } + fn new(compiled: Compiled) -> Self { + let mut retval = Self { + state: State::new(compiled.insns), + io: compiled.io.to_expr(), + uninitialized_inputs: HashSet::new(), + io_targets: HashMap::new(), + made_initial_step: false, + }; + let io_target = Target::from(compiled.io); + for (BundleField { name, .. }, value) in compiled + .io + .ty() + .fields() + .into_iter() + .zip(compiled.base_module.module_io) + { + retval.parse_io( + io_target + .join(TargetPathElement::from(TargetPathBundleField { name }).intern_sized()), + value, + ); + } + retval + } + #[track_caller] + fn settle_step(&mut self) { + assert!( + self.uninitialized_inputs.is_empty(), + "didn't initialize all inputs", + ); + self.state.setup_call(0); + self.state.run(); + self.made_initial_step = true; + } + #[track_caller] + fn get_io(&self, target: Target) -> CompiledValue { + if let Some(&retval) = self.io_targets.get(&target) { + return retval; + } + if Some(&target) == self.io.target().as_deref() + || Some(target.base()) != self.io.target().map(|v| v.base()) + { + panic!("simulator read/write expression must be an array element/field of `Simulator::io()`"); + }; + panic!("simulator read/write expression must not have dynamic array indexes"); + } + #[track_caller] + fn read_bool_or_int(&self, io: Expr) -> I::Value { + let Some(target) = io.target() else { + panic!("can't read from expression that's not a field/element of `Simulation::io()`"); + }; + let compiled_value = self.get_io(*target); + if !self.made_initial_step { + match target.flow() { + Flow::Source => { + panic!("can't read from an output before the simulation has made any steps"); + } + Flow::Sink => { + if self.uninitialized_inputs.contains(&*target) { + panic!("can't read from an uninitialized input"); + } + } + Flow::Duplex => unreachable!(), + } + } + 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( + 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( + self.state.big_slots[compiled_value.range.big_slots.start].clone(), + ), + _ => unreachable!(), + } + } + #[track_caller] + fn write_bool_or_int(&mut self, io: Expr, value: I::Value) { + let Some(target) = io.target() else { + panic!("can't write to an expression that's not a field/element of `Simulation::io()`"); + }; + let compiled_value = self.get_io(*target); + match target.flow() { + Flow::Source => { + panic!("can't write to an output"); + } + Flow::Sink => {} + Flow::Duplex => unreachable!(), + } + let value: BigInt = value.into(); + if !self.made_initial_step { + self.uninitialized_inputs.remove(&*target); + } + match compiled_value.range.len() { + TypeLen { + small_slots: + StatePartLen { + value: 1, + _phantom: _, + }, + big_slots: + StatePartLen { + value: 0, + _phantom: _, + }, + } => { + 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, + _ => unreachable!(), + } + } +} + +pub struct Simulation { + sim_impl: SimulationImpl, + io: Expr, +} + +struct SortedSetDebug<'a, T>(&'a HashSet); + +impl fmt::Debug for SortedSetDebug<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut entries = Vec::from_iter(self.0.iter().map(|v| { + if f.alternate() { + format!("{v:#?}") + } else { + format!("{v:?}") + } + })); + entries.sort(); + f.debug_set() + .entries(entries.iter().map(DebugAsDisplay)) + .finish() + } +} + +struct SortedMapDebug<'a, K, V>(&'a HashMap); + +impl fmt::Debug for SortedMapDebug<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut entries = Vec::from_iter(self.0.iter().map(|(k, v)| { + if f.alternate() { + (format!("{k:#?}"), format!("{v:#?}")) + } else { + (format!("{k:?}"), format!("{v:?}")) + } + })); + entries.sort(); + f.debug_map() + .entries( + entries + .iter() + .map(|(k, v)| (DebugAsDisplay(k), DebugAsDisplay(v))), + ) + .finish() + } +} + +impl fmt::Debug for Simulation { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + sim_impl: + SimulationImpl { + state, + io: _, + uninitialized_inputs, + io_targets, + made_initial_step, + }, + io, + } = self; + f.debug_struct("Simulation") + .field("state", state) + .field("io", io) + .field( + "uninitialized_inputs", + &SortedSetDebug(uninitialized_inputs), + ) + .field("io_targets", &SortedMapDebug(io_targets)) + .field("made_initial_step", made_initial_step) + .finish() + } } impl Simulation { pub fn new(module: Interned>) -> Self { Self::from_compiled(Compiled::new(module)) } - pub fn from_compiled(compiled: Compiled) -> Self { - Self { - state: interpreter::State::new(compiled.insns), - base_module: compiled.base_module, - base_module_io_ty: compiled.base_module_io_ty, + pub fn canonical(self) -> Simulation { + let Self { sim_impl, io } = self; + Simulation { + sim_impl, + io: Expr::as_bundle(io), } } + pub fn from_canonical(canonical: Simulation) -> Self { + let Simulation { sim_impl, io } = canonical; + Self { + sim_impl, + io: Expr::from_bundle(io), + } + } + pub fn io(&self) -> Expr { + self.io.to_expr() + } + pub fn from_compiled(compiled: Compiled) -> Self { + let sim_impl = SimulationImpl::new(compiled.canonical()); + Self { + io: Expr::from_bundle(sim_impl.io), + sim_impl, + } + } + #[track_caller] pub fn settle_step(&mut self) { - self.state.setup_call(0); - self.state.run(); + self.sim_impl.settle_step(); + } + #[track_caller] + pub fn read_bool_or_int(&self, io: Expr) -> I::Value { + self.sim_impl.read_bool_or_int(io) + } + #[track_caller] + pub fn write_bool_or_int( + &mut self, + io: Expr, + value: impl ToExpr, + ) { + let value = value.to_expr(); + assert_eq!(Expr::ty(io), Expr::ty(value), "type mismatch"); + let value = value + .to_literal_bits() + .expect("the value that is being written to an input must be a literal"); + self.sim_impl + .write_bool_or_int(io, I::bits_to_value(Cow::Borrowed(&value))); } } diff --git a/crates/fayalite/src/sim/interpreter.rs b/crates/fayalite/src/sim/interpreter.rs index e713300..930c933 100644 --- a/crates/fayalite/src/sim/interpreter.rs +++ b/crates/fayalite/src/sim/interpreter.rs @@ -1718,6 +1718,23 @@ impl< } } +impl>>, T> Index> + for StatePart +{ + type Output = T; + fn index(&self, index: StatePartIndex) -> &Self::Output { + &self.value[index.value as usize] + } +} + +impl>>, T> + IndexMut> for StatePart +{ + fn index_mut(&mut self, index: StatePartIndex) -> &mut Self::Output { + &mut self.value[index.value as usize] + } +} + impl<'a, K: StatePartKind: Deref>>, T> Index> for BorrowedStatePart<'a, K> { diff --git a/crates/fayalite/src/wire.rs b/crates/fayalite/src/wire.rs index 85ab342..51f8cf7 100644 --- a/crates/fayalite/src/wire.rs +++ b/crates/fayalite/src/wire.rs @@ -37,6 +37,18 @@ impl Wire { ty: ty.canonical(), } } + pub fn from_canonical(v: Wire) -> Self { + let Wire { + name, + source_location, + ty, + } = v; + Self { + name, + source_location, + ty: T::from_canonical(ty), + } + } pub fn ty(&self) -> T { self.ty } diff --git a/crates/fayalite/tests/sim.rs b/crates/fayalite/tests/sim.rs index 89302af..1696d6a 100644 --- a/crates/fayalite/tests/sim.rs +++ b/crates/fayalite/tests/sim.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information -use fayalite::{prelude::*, sim::Simulation}; +use fayalite::{int::UIntValue, prelude::*, sim::Simulation}; #[hdl_module(outline_generated)] pub fn connect_const() { @@ -70,44 +70,55 @@ fn test_connect_const() { ], }, }, - base_module: CompiledModule { - module_io: [ - CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartAllocationLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartAllocationLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(connect_const: connect_const).connect_const::o", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 1 }, - }, - write: None, + io: Instance { + name: ::connect_const, + instantiated: Module { + name: connect_const, + .. + }, + }, + uninitialized_inputs: {}, + io_targets: { + Instance { + name: ::connect_const, + instantiated: Module { + name: connect_const, + .. }, - ], - }, - base_module_io_ty: connect_const { - o: UInt<8>, + }.o: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<8>, + layout: TypeLayout { + small_slots: StatePartAllocationLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartAllocationLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(connect_const: connect_const).connect_const::o", + ty: UInt<8>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 0, len: 1 }, + }, + write: None, + }, }, + made_initial_step: true, }"# { panic!(); } + assert_eq!(sim.read_bool_or_int(sim.io().o), UIntValue::from(5u8)); } #[hdl_module(outline_generated)] @@ -137,10 +148,13 @@ pub fn mod1() { connect(o, child); } +#[hdl] #[test] fn test_mod1() { let _n = SourceLocation::normalize_files_for_tests(); let mut sim = Simulation::new(mod1()); + sim.write_bool_or_int(sim.io().o.i, 0xA_hdl_u4); + sim.write_bool_or_int(sim.io().o.i2, -2_hdl_i2); sim.settle_step(); let sim_debug = format!("{sim:#?}"); println!("#######\n{sim_debug}\n#######"); @@ -326,199 +340,344 @@ fn test_mod1() { }, big_slots: StatePart { value: [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + 10, + -2, + -2, + 15, + 10, + -2, + -2, + 15, + 10, + -2, + -2, + 15, + -2, + 14, 5, - 0, + 1, 15, ], }, }, - base_module: CompiledModule { - module_io: [ - CompiledValue { - layout: CompiledTypeLayout { - 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>, - }, - layout: TypeLayout { - small_slots: StatePartAllocationLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartAllocationLayout { - len: 4, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(mod1: mod1).mod1::o.i", - ty: UInt<4>, - }, - SlotDebugData { - name: "InstantiatedModule(mod1: mod1).mod1::o.o", - ty: SInt<2>, - }, - SlotDebugData { - name: "InstantiatedModule(mod1: mod1).mod1::o.i2", - ty: SInt<2>, - }, - SlotDebugData { - name: "InstantiatedModule(mod1: mod1).mod1::o.o2", - ty: UInt<4>, - }, - ], - .. - }, - }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: UInt<4>, - layout: TypeLayout { - small_slots: StatePartAllocationLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartAllocationLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<4>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: SInt<2>, - layout: TypeLayout { - small_slots: StatePartAllocationLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartAllocationLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SInt<2>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(2), - }, - ty: CompiledTypeLayout { - ty: SInt<2>, - layout: TypeLayout { - small_slots: StatePartAllocationLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartAllocationLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SInt<2>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(3), - }, - ty: CompiledTypeLayout { - ty: UInt<4>, - layout: TypeLayout { - small_slots: StatePartAllocationLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartAllocationLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<4>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - ], - }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 4 }, - }, - write: None, - }, - ], - }, - base_module_io_ty: mod1 { - o: mod1_child { - i: UInt<4>, - o: SInt<2>, - i2: SInt<2>, - o2: UInt<4>, + io: Instance { + name: ::mod1, + instantiated: Module { + name: mod1, + .. }, }, + uninitialized_inputs: {}, + io_targets: { + Instance { + name: ::mod1, + instantiated: Module { + name: mod1, + .. + }, + }.o: CompiledValue { + layout: CompiledTypeLayout { + 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>, + }, + layout: TypeLayout { + small_slots: StatePartAllocationLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartAllocationLayout { + len: 4, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(mod1: mod1).mod1::o.i", + ty: UInt<4>, + }, + SlotDebugData { + name: "InstantiatedModule(mod1: mod1).mod1::o.o", + ty: SInt<2>, + }, + SlotDebugData { + name: "InstantiatedModule(mod1: mod1).mod1::o.i2", + ty: SInt<2>, + }, + SlotDebugData { + name: "InstantiatedModule(mod1: mod1).mod1::o.o2", + ty: UInt<4>, + }, + ], + .. + }, + }, + body: Bundle { + fields: [ + CompiledBundleField { + offset: TypeIndex { + small_slots: StatePartIndex(0), + big_slots: StatePartIndex(0), + }, + ty: CompiledTypeLayout { + ty: UInt<4>, + layout: TypeLayout { + small_slots: StatePartAllocationLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartAllocationLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<4>, + }, + ], + .. + }, + }, + body: Scalar, + }, + }, + CompiledBundleField { + offset: TypeIndex { + small_slots: StatePartIndex(0), + big_slots: StatePartIndex(1), + }, + ty: CompiledTypeLayout { + ty: SInt<2>, + layout: TypeLayout { + small_slots: StatePartAllocationLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartAllocationLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: SInt<2>, + }, + ], + .. + }, + }, + body: Scalar, + }, + }, + CompiledBundleField { + offset: TypeIndex { + small_slots: StatePartIndex(0), + big_slots: StatePartIndex(2), + }, + ty: CompiledTypeLayout { + ty: SInt<2>, + layout: TypeLayout { + small_slots: StatePartAllocationLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartAllocationLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: SInt<2>, + }, + ], + .. + }, + }, + body: Scalar, + }, + }, + CompiledBundleField { + offset: TypeIndex { + small_slots: StatePartIndex(0), + big_slots: StatePartIndex(3), + }, + ty: CompiledTypeLayout { + ty: UInt<4>, + layout: TypeLayout { + small_slots: StatePartAllocationLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartAllocationLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<4>, + }, + ], + .. + }, + }, + body: Scalar, + }, + }, + ], + }, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 0, len: 4 }, + }, + write: None, + }, + Instance { + name: ::mod1, + instantiated: Module { + name: mod1, + .. + }, + }.o.i: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<4>, + layout: TypeLayout { + small_slots: StatePartAllocationLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartAllocationLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<4>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 0, len: 1 }, + }, + write: None, + }, + Instance { + name: ::mod1, + instantiated: Module { + name: mod1, + .. + }, + }.o.i2: CompiledValue { + layout: CompiledTypeLayout { + ty: SInt<2>, + layout: TypeLayout { + small_slots: StatePartAllocationLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartAllocationLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: SInt<2>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 2, len: 1 }, + }, + write: None, + }, + Instance { + name: ::mod1, + instantiated: Module { + name: mod1, + .. + }, + }.o.o: CompiledValue { + layout: CompiledTypeLayout { + ty: SInt<2>, + layout: TypeLayout { + small_slots: StatePartAllocationLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartAllocationLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: SInt<2>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 1, len: 1 }, + }, + write: None, + }, + Instance { + name: ::mod1, + instantiated: Module { + name: mod1, + .. + }, + }.o.o2: CompiledValue { + layout: CompiledTypeLayout { + ty: UInt<4>, + layout: TypeLayout { + small_slots: StatePartAllocationLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartAllocationLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<4>, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 3, len: 1 }, + }, + write: None, + }, + }, + made_initial_step: true, }"# { panic!(); } + let expected = -2_hdl_i2; + assert_eq!(sim.read_bool_or_int(sim.io().o.o).to_expr(), expected); + let expected = 0xF_hdl_u4; + assert_eq!(sim.read_bool_or_int(sim.io().o.o2).to_expr(), expected); }