Compare commits
	
		
			13 commits
		
	
	
		
			11ddbc43c7
			...
			e2653a3245
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| e2653a3245 | |||
| f8b0ab45b0 | |||
| 01dafcea0f | |||
| 18ddab26ba | |||
| 2842c2839f | |||
| 41139d09dc | |||
| 2edd9defd1 | |||
| 1cb394674a | |||
| 435514654c | |||
| 86c711c36e | |||
| 1a769b8162 | |||
| dfca5e7c8f | |||
| 747ad535a2 | 
					 15 changed files with 8969 additions and 18 deletions
				
			
		
							
								
								
									
										29
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										29
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							| 
						 | 
					@ -315,10 +315,12 @@ dependencies = [
 | 
				
			||||||
 "num-bigint",
 | 
					 "num-bigint",
 | 
				
			||||||
 "num-traits",
 | 
					 "num-traits",
 | 
				
			||||||
 "os_pipe",
 | 
					 "os_pipe",
 | 
				
			||||||
 | 
					 "petgraph",
 | 
				
			||||||
 "serde",
 | 
					 "serde",
 | 
				
			||||||
 "serde_json",
 | 
					 "serde_json",
 | 
				
			||||||
 "tempfile",
 | 
					 "tempfile",
 | 
				
			||||||
 "trybuild",
 | 
					 "trybuild",
 | 
				
			||||||
 | 
					 "vec_map",
 | 
				
			||||||
 "which",
 | 
					 "which",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -357,6 +359,12 @@ dependencies = [
 | 
				
			||||||
 "thiserror",
 | 
					 "thiserror",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "fixedbitset"
 | 
				
			||||||
 | 
					version = "0.4.2"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "funty"
 | 
					name = "funty"
 | 
				
			||||||
version = "2.0.0"
 | 
					version = "2.0.0"
 | 
				
			||||||
| 
						 | 
					@ -472,11 +480,10 @@ checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "num-bigint"
 | 
					name = "num-bigint"
 | 
				
			||||||
version = "0.4.4"
 | 
					version = "0.4.6"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
 | 
					checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "autocfg",
 | 
					 | 
				
			||||||
 "num-integer",
 | 
					 "num-integer",
 | 
				
			||||||
 "num-traits",
 | 
					 "num-traits",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
| 
						 | 
					@ -515,6 +522,16 @@ dependencies = [
 | 
				
			||||||
 "windows-sys 0.59.0",
 | 
					 "windows-sys 0.59.0",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "petgraph"
 | 
				
			||||||
 | 
					version = "0.6.5"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "fixedbitset",
 | 
				
			||||||
 | 
					 "indexmap",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "prettyplease"
 | 
					name = "prettyplease"
 | 
				
			||||||
version = "0.2.20"
 | 
					version = "0.2.20"
 | 
				
			||||||
| 
						 | 
					@ -720,6 +737,12 @@ version = "0.2.2"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
 | 
					checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "vec_map"
 | 
				
			||||||
 | 
					version = "0.8.2"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "version_check"
 | 
					name = "version_check"
 | 
				
			||||||
version = "0.9.4"
 | 
					version = "0.9.4"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,9 +26,10 @@ eyre = "0.6.12"
 | 
				
			||||||
hashbrown = "0.14.3"
 | 
					hashbrown = "0.14.3"
 | 
				
			||||||
indexmap = { version = "2.2.6", features = ["serde"] }
 | 
					indexmap = { version = "2.2.6", features = ["serde"] }
 | 
				
			||||||
jobslot = "0.2.19"
 | 
					jobslot = "0.2.19"
 | 
				
			||||||
num-bigint = "0.4.4"
 | 
					num-bigint = "0.4.6"
 | 
				
			||||||
num-traits = "0.2.16"
 | 
					num-traits = "0.2.16"
 | 
				
			||||||
os_pipe = "1.2.1"
 | 
					os_pipe = "1.2.1"
 | 
				
			||||||
 | 
					petgraph = "0.6.5"
 | 
				
			||||||
prettyplease = "0.2.20"
 | 
					prettyplease = "0.2.20"
 | 
				
			||||||
proc-macro2 = "1.0.83"
 | 
					proc-macro2 = "1.0.83"
 | 
				
			||||||
quote = "1.0.36"
 | 
					quote = "1.0.36"
 | 
				
			||||||
| 
						 | 
					@ -39,4 +40,5 @@ syn = { version = "2.0.66", features = ["full", "fold", "visit", "extra-traits"]
 | 
				
			||||||
tempfile = "3.10.1"
 | 
					tempfile = "3.10.1"
 | 
				
			||||||
thiserror = "1.0.61"
 | 
					thiserror = "1.0.61"
 | 
				
			||||||
trybuild = "1.0"
 | 
					trybuild = "1.0"
 | 
				
			||||||
 | 
					vec_map = "0.8.2"
 | 
				
			||||||
which = "6.0.1"
 | 
					which = "6.0.1"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,9 +25,11 @@ jobslot.workspace = true
 | 
				
			||||||
num-bigint.workspace = true
 | 
					num-bigint.workspace = true
 | 
				
			||||||
num-traits.workspace = true
 | 
					num-traits.workspace = true
 | 
				
			||||||
os_pipe.workspace = true
 | 
					os_pipe.workspace = true
 | 
				
			||||||
 | 
					petgraph.workspace = true
 | 
				
			||||||
serde_json.workspace = true
 | 
					serde_json.workspace = true
 | 
				
			||||||
serde.workspace = true
 | 
					serde.workspace = true
 | 
				
			||||||
tempfile.workspace = true
 | 
					tempfile.workspace = true
 | 
				
			||||||
 | 
					vec_map.workspace = true
 | 
				
			||||||
which.workspace = true
 | 
					which.workspace = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[dev-dependencies]
 | 
					[dev-dependencies]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@ use crate::{
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use std::fmt;
 | 
					use std::fmt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 | 
					#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 | 
				
			||||||
pub struct TargetPathBundleField {
 | 
					pub struct TargetPathBundleField {
 | 
				
			||||||
    pub name: Interned<str>,
 | 
					    pub name: Interned<str>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -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 struct TargetPathArrayElement {
 | 
				
			||||||
    pub index: usize,
 | 
					    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 {}
 | 
					pub struct TargetPathDynArrayElement {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl fmt::Display for 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 {
 | 
					pub enum TargetPathElement {
 | 
				
			||||||
    BundleField(TargetPathBundleField),
 | 
					    BundleField(TargetPathBundleField),
 | 
				
			||||||
    ArrayElement(TargetPathArrayElement),
 | 
					    ArrayElement(TargetPathArrayElement),
 | 
				
			||||||
| 
						 | 
					@ -197,7 +197,7 @@ macro_rules! impl_target_base {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl_target_base! {
 | 
					impl_target_base! {
 | 
				
			||||||
    #[derive(Clone, PartialEq, Eq, Hash)]
 | 
					    #[derive(Copy, Clone, PartialEq, Eq, Hash)]
 | 
				
			||||||
    pub enum TargetBase {
 | 
					    pub enum TargetBase {
 | 
				
			||||||
        #[is = is_module_io]
 | 
					        #[is = is_module_io]
 | 
				
			||||||
        #[to = module_io]
 | 
					        #[to = module_io]
 | 
				
			||||||
| 
						 | 
					@ -313,7 +313,7 @@ impl TargetChild {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Clone, PartialEq, Eq, Hash)]
 | 
					#[derive(Copy, Clone, PartialEq, Eq, Hash)]
 | 
				
			||||||
pub enum Target {
 | 
					pub enum Target {
 | 
				
			||||||
    Base(Interned<TargetBase>),
 | 
					    Base(Interned<TargetBase>),
 | 
				
			||||||
    Child(TargetChild),
 | 
					    Child(TargetChild),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -227,12 +227,29 @@ macro_rules! impl_int {
 | 
				
			||||||
        impl<Width: Size> BoolOrIntType for $name<Width> {
 | 
					        impl<Width: Size> BoolOrIntType for $name<Width> {
 | 
				
			||||||
            type Width = Width;
 | 
					            type Width = Width;
 | 
				
			||||||
            type Signed = ConstBool<$SIGNED>;
 | 
					            type Signed = ConstBool<$SIGNED>;
 | 
				
			||||||
 | 
					            type Value = $value<Width>;
 | 
				
			||||||
            fn width(self) -> usize {
 | 
					            fn width(self) -> usize {
 | 
				
			||||||
                $name::width(self)
 | 
					                $name::width(self)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            fn new(width: Width::SizeType) -> Self {
 | 
					            fn new(width: Width::SizeType) -> Self {
 | 
				
			||||||
                $name { width }
 | 
					                $name { width }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            fn value_from_bigint_wrapping(self, v: BigInt) -> Self::Value {
 | 
				
			||||||
 | 
					                $value::<Width>::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<BitVec>;
 | 
				
			||||||
 | 
					                    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<Self> {
 | 
					            fn bits_to_expr(bits: Cow<'_, BitSlice>) -> Expr<Self> {
 | 
				
			||||||
                #[derive(Copy, Clone, Eq, PartialEq, Hash)]
 | 
					                #[derive(Copy, Clone, Eq, PartialEq, Hash)]
 | 
				
			||||||
                struct MemoizeBitsToExpr;
 | 
					                struct MemoizeBitsToExpr;
 | 
				
			||||||
| 
						 | 
					@ -334,6 +351,24 @@ macro_rules! impl_int {
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        impl<Width: Size> PartialOrd for $value<Width> {
 | 
				
			||||||
 | 
					            fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
 | 
				
			||||||
 | 
					                Some(self.cmp(other))
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        impl<Width: Size> Ord for $value<Width> {
 | 
				
			||||||
 | 
					            fn cmp(&self, other: &Self) -> std::cmp::Ordering {
 | 
				
			||||||
 | 
					                self.to_bigint().cmp(&other.to_bigint())
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        impl<Width: Size> From<$value<Width>> for BigInt {
 | 
				
			||||||
 | 
					            fn from(v: $value<Width>) -> BigInt {
 | 
				
			||||||
 | 
					                v.to_bigint()
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        impl<Width: Size> $value<Width> {
 | 
					        impl<Width: Size> $value<Width> {
 | 
				
			||||||
            pub fn width(&self) -> usize {
 | 
					            pub fn width(&self) -> usize {
 | 
				
			||||||
                if let Some(retval) = Width::KNOWN_VALUE {
 | 
					                if let Some(retval) = Width::KNOWN_VALUE {
 | 
				
			||||||
| 
						 | 
					@ -482,6 +517,19 @@ macro_rules! impl_prim_int {
 | 
				
			||||||
        $(#[$meta:meta])*
 | 
					        $(#[$meta:meta])*
 | 
				
			||||||
        $prim_int:ident, $ty:ty
 | 
					        $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<NonZero<$prim_int>> for <$ty as BoolOrIntType>::Value {
 | 
				
			||||||
 | 
					            fn from(v: NonZero<$prim_int>) -> Self {
 | 
				
			||||||
 | 
					                v.get().into()
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        $(#[$meta])*
 | 
					        $(#[$meta])*
 | 
				
			||||||
        impl ToExpr for $prim_int {
 | 
					        impl ToExpr for $prim_int {
 | 
				
			||||||
            type Type = $ty;
 | 
					            type Type = $ty;
 | 
				
			||||||
| 
						 | 
					@ -498,10 +546,7 @@ macro_rules! impl_prim_int {
 | 
				
			||||||
            type Type = $ty;
 | 
					            type Type = $ty;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            fn to_expr(&self) -> Expr<Self::Type> {
 | 
					            fn to_expr(&self) -> Expr<Self::Type> {
 | 
				
			||||||
                <$ty>::le_bytes_to_expr_wrapping(
 | 
					                self.get().to_expr()
 | 
				
			||||||
                    &self.get().to_le_bytes(),
 | 
					 | 
				
			||||||
                    <$ty as BoolOrIntType>::Width::VALUE,
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
| 
						 | 
					@ -531,6 +576,15 @@ impl_prim_int!(
 | 
				
			||||||
pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed {
 | 
					pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed {
 | 
				
			||||||
    type Width: Size;
 | 
					    type Width: Size;
 | 
				
			||||||
    type Signed: GenericConstBool;
 | 
					    type Signed: GenericConstBool;
 | 
				
			||||||
 | 
					    type Value: Clone
 | 
				
			||||||
 | 
					        + Ord
 | 
				
			||||||
 | 
					        + std::hash::Hash
 | 
				
			||||||
 | 
					        + fmt::Debug
 | 
				
			||||||
 | 
					        + Send
 | 
				
			||||||
 | 
					        + Sync
 | 
				
			||||||
 | 
					        + 'static
 | 
				
			||||||
 | 
					        + ToExpr<Type = Self>
 | 
				
			||||||
 | 
					        + Into<BigInt>;
 | 
				
			||||||
    fn width(self) -> usize;
 | 
					    fn width(self) -> usize;
 | 
				
			||||||
    fn new(width: <Self::Width as Size>::SizeType) -> Self;
 | 
					    fn new(width: <Self::Width as Size>::SizeType) -> Self;
 | 
				
			||||||
    fn new_static() -> Self
 | 
					    fn new_static() -> Self
 | 
				
			||||||
| 
						 | 
					@ -545,6 +599,10 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed {
 | 
				
			||||||
    fn as_same_width_uint(self) -> UIntType<Self::Width> {
 | 
					    fn as_same_width_uint(self) -> UIntType<Self::Width> {
 | 
				
			||||||
        UIntType::new(Self::Width::from_usize(self.width()))
 | 
					        UIntType::new(Self::Width::from_usize(self.width()))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    fn value_from_int_wrapping(self, v: impl Into<BigInt>) -> 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 {
 | 
					    fn bits_from_bigint_wrapping(self, v: BigInt) -> BitVec {
 | 
				
			||||||
        let width = self.width();
 | 
					        let width = self.width();
 | 
				
			||||||
        let mut bytes = v.to_signed_bytes_le();
 | 
					        let mut bytes = v.to_signed_bytes_le();
 | 
				
			||||||
| 
						 | 
					@ -567,8 +625,9 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed {
 | 
				
			||||||
        BitSlice::<u8, Lsb0>::from_slice_mut(&mut bytes)[..bits.len()].clone_from_bitslice(bits);
 | 
					        BitSlice::<u8, Lsb0>::from_slice_mut(&mut bytes)[..bits.len()].clone_from_bitslice(bits);
 | 
				
			||||||
        BigInt::from_signed_bytes_le(&bytes)
 | 
					        BigInt::from_signed_bytes_le(&bytes)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    fn bits_to_value(bits: Cow<'_, BitSlice>) -> Self::Value;
 | 
				
			||||||
    fn bits_to_expr(bits: Cow<'_, BitSlice>) -> Expr<Self>;
 | 
					    fn bits_to_expr(bits: Cow<'_, BitSlice>) -> Expr<Self>;
 | 
				
			||||||
    fn le_bytes_to_expr_wrapping(bytes: &[u8], bit_width: usize) -> Expr<Self> {
 | 
					    fn le_bytes_to_bits_wrapping(bytes: &[u8], bit_width: usize) -> BitVec {
 | 
				
			||||||
        let bitslice = BitSlice::<u8, Lsb0>::from_slice(bytes);
 | 
					        let bitslice = BitSlice::<u8, Lsb0>::from_slice(bytes);
 | 
				
			||||||
        let bitslice = &bitslice[..bit_width.min(bitslice.len())];
 | 
					        let bitslice = &bitslice[..bit_width.min(bitslice.len())];
 | 
				
			||||||
        let mut bits = BitVec::new();
 | 
					        let mut bits = BitVec::new();
 | 
				
			||||||
| 
						 | 
					@ -577,7 +636,17 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed {
 | 
				
			||||||
            bit_width,
 | 
					            bit_width,
 | 
				
			||||||
            Self::Signed::VALUE && bits.last().as_deref().copied().unwrap_or(false),
 | 
					            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> {
 | 
				
			||||||
 | 
					        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 {
 | 
					impl BoolOrIntType for Bool {
 | 
				
			||||||
    type Width = ConstUsize<1>;
 | 
					    type Width = ConstUsize<1>;
 | 
				
			||||||
    type Signed = ConstBool<false>;
 | 
					    type Signed = ConstBool<false>;
 | 
				
			||||||
 | 
					    type Value = bool;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn width(self) -> usize {
 | 
					    fn width(self) -> usize {
 | 
				
			||||||
        1
 | 
					        1
 | 
				
			||||||
| 
						 | 
					@ -639,10 +709,19 @@ impl BoolOrIntType for Bool {
 | 
				
			||||||
        Bool
 | 
					        Bool
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn value_from_bigint_wrapping(self, v: BigInt) -> Self::Value {
 | 
				
			||||||
 | 
					        v.bit(0)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn bits_to_expr(bits: Cow<'_, BitSlice>) -> Expr<Self> {
 | 
					    fn bits_to_expr(bits: Cow<'_, BitSlice>) -> Expr<Self> {
 | 
				
			||||||
        assert_eq!(bits.len(), 1);
 | 
					        assert_eq!(bits.len(), 1);
 | 
				
			||||||
        bits[0].to_expr()
 | 
					        bits[0].to_expr()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn bits_to_value(bits: Cow<'_, BitSlice>) -> Self::Value {
 | 
				
			||||||
 | 
					        assert_eq!(bits.len(), 1);
 | 
				
			||||||
 | 
					        bits[0]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Bool {
 | 
					impl Bool {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -46,6 +46,7 @@ pub mod module;
 | 
				
			||||||
pub mod prelude;
 | 
					pub mod prelude;
 | 
				
			||||||
pub mod reg;
 | 
					pub mod reg;
 | 
				
			||||||
pub mod reset;
 | 
					pub mod reset;
 | 
				
			||||||
 | 
					pub mod sim;
 | 
				
			||||||
pub mod source_location;
 | 
					pub mod source_location;
 | 
				
			||||||
pub mod testing;
 | 
					pub mod testing;
 | 
				
			||||||
pub mod ty;
 | 
					pub mod ty;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -714,6 +714,18 @@ impl<T: BundleType> Instance<T> {
 | 
				
			||||||
            source_location,
 | 
					            source_location,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    pub fn from_canonical(v: Instance<Bundle>) -> 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<str> {
 | 
					    pub fn containing_module_name(self) -> Interned<str> {
 | 
				
			||||||
        self.containing_module_name_id().0
 | 
					        self.containing_module_name_id().0
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										4175
									
								
								crates/fayalite/src/sim.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										4175
									
								
								crates/fayalite/src/sim.rs
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										2577
									
								
								crates/fayalite/src/sim/interpreter.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2577
									
								
								crates/fayalite/src/sim/interpreter.rs
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										397
									
								
								crates/fayalite/src/sim/time.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										397
									
								
								crates/fayalite/src/sim/time.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,397 @@
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
				
			||||||
 | 
					// See Notices.txt for copyright information
 | 
				
			||||||
 | 
					use std::{
 | 
				
			||||||
 | 
					    fmt,
 | 
				
			||||||
 | 
					    ops::{Add, AddAssign, Sub, SubAssign},
 | 
				
			||||||
 | 
					    time::Duration,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
 | 
				
			||||||
 | 
					pub struct SimInstant {
 | 
				
			||||||
 | 
					    time_since_start: SimDuration,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl SimInstant {
 | 
				
			||||||
 | 
					    pub const fn checked_add(self, duration: SimDuration) -> Option<Self> {
 | 
				
			||||||
 | 
					        let Some(time_since_start) = self.time_since_start.checked_add(duration) else {
 | 
				
			||||||
 | 
					            return None;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        Some(SimInstant { time_since_start })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    pub const fn checked_duration_since(self, earlier: Self) -> Option<SimDuration> {
 | 
				
			||||||
 | 
					        self.time_since_start.checked_sub(earlier.time_since_start)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    pub const fn checked_sub(self, duration: SimDuration) -> Option<Self> {
 | 
				
			||||||
 | 
					        let Some(time_since_start) = self.time_since_start.checked_sub(duration) else {
 | 
				
			||||||
 | 
					            return None;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        Some(SimInstant { time_since_start })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    #[track_caller]
 | 
				
			||||||
 | 
					    pub const fn duration_since(self, earlier: Self) -> SimDuration {
 | 
				
			||||||
 | 
					        let Some(retval) = self.checked_duration_since(earlier) else {
 | 
				
			||||||
 | 
					            panic!(
 | 
				
			||||||
 | 
					                "tried to compute the duration since a later time -- durations can't be negative"
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        retval
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    pub const fn saturating_duration_since(self, earlier: Self) -> SimDuration {
 | 
				
			||||||
 | 
					        let Some(retval) = self.checked_duration_since(earlier) else {
 | 
				
			||||||
 | 
					            return SimDuration::ZERO;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        retval
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Add<SimDuration> for SimInstant {
 | 
				
			||||||
 | 
					    type Output = SimInstant;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[track_caller]
 | 
				
			||||||
 | 
					    fn add(mut self, rhs: SimDuration) -> Self::Output {
 | 
				
			||||||
 | 
					        self += rhs;
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl AddAssign<SimDuration> for SimInstant {
 | 
				
			||||||
 | 
					    #[track_caller]
 | 
				
			||||||
 | 
					    fn add_assign(&mut self, rhs: SimDuration) {
 | 
				
			||||||
 | 
					        self.time_since_start += rhs;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Add<SimInstant> for SimDuration {
 | 
				
			||||||
 | 
					    type Output = SimInstant;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[track_caller]
 | 
				
			||||||
 | 
					    fn add(self, rhs: SimInstant) -> Self::Output {
 | 
				
			||||||
 | 
					        rhs.add(self)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Sub for SimInstant {
 | 
				
			||||||
 | 
					    type Output = SimDuration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[track_caller]
 | 
				
			||||||
 | 
					    fn sub(self, rhs: SimInstant) -> Self::Output {
 | 
				
			||||||
 | 
					        self.duration_since(rhs)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Sub<SimDuration> for SimInstant {
 | 
				
			||||||
 | 
					    type Output = SimInstant;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[track_caller]
 | 
				
			||||||
 | 
					    fn sub(self, rhs: SimDuration) -> Self::Output {
 | 
				
			||||||
 | 
					        let Some(retval) = self.checked_sub(rhs) else {
 | 
				
			||||||
 | 
					            panic!("SimInstant underflow");
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        retval
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl SubAssign<SimDuration> for SimInstant {
 | 
				
			||||||
 | 
					    #[track_caller]
 | 
				
			||||||
 | 
					    fn sub_assign(&mut self, rhs: SimDuration) {
 | 
				
			||||||
 | 
					        *self = *self - rhs;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
 | 
				
			||||||
 | 
					pub struct SimDuration {
 | 
				
			||||||
 | 
					    attos: u128,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl AddAssign for SimDuration {
 | 
				
			||||||
 | 
					    #[track_caller]
 | 
				
			||||||
 | 
					    fn add_assign(&mut self, rhs: SimDuration) {
 | 
				
			||||||
 | 
					        *self = *self + rhs;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Add for SimDuration {
 | 
				
			||||||
 | 
					    type Output = SimDuration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[track_caller]
 | 
				
			||||||
 | 
					    fn add(self, rhs: SimDuration) -> Self::Output {
 | 
				
			||||||
 | 
					        SimDuration {
 | 
				
			||||||
 | 
					            attos: self
 | 
				
			||||||
 | 
					                .attos
 | 
				
			||||||
 | 
					                .checked_add(rhs.attos)
 | 
				
			||||||
 | 
					                .expect("overflow adding durations"),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Sub for SimDuration {
 | 
				
			||||||
 | 
					    type Output = Self;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[track_caller]
 | 
				
			||||||
 | 
					    fn sub(self, rhs: Self) -> Self::Output {
 | 
				
			||||||
 | 
					        SimDuration {
 | 
				
			||||||
 | 
					            attos: self
 | 
				
			||||||
 | 
					                .attos
 | 
				
			||||||
 | 
					                .checked_add(rhs.attos)
 | 
				
			||||||
 | 
					                .expect("underflow subtracting durations -- durations can't be negative"),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl SubAssign for SimDuration {
 | 
				
			||||||
 | 
					    #[track_caller]
 | 
				
			||||||
 | 
					    fn sub_assign(&mut self, rhs: Self) {
 | 
				
			||||||
 | 
					        *self = *self - rhs;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
 | 
				
			||||||
 | 
					pub struct SimDurationParts {
 | 
				
			||||||
 | 
					    pub attos: u16,
 | 
				
			||||||
 | 
					    pub femtos: u16,
 | 
				
			||||||
 | 
					    pub picos: u16,
 | 
				
			||||||
 | 
					    pub nanos: u16,
 | 
				
			||||||
 | 
					    pub micros: u16,
 | 
				
			||||||
 | 
					    pub millis: u16,
 | 
				
			||||||
 | 
					    pub secs: u128,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					macro_rules! impl_duration_units {
 | 
				
			||||||
 | 
					    (
 | 
				
			||||||
 | 
					        $(
 | 
				
			||||||
 | 
					            #[unit_const = $UNIT:ident, from_units = $from_units:ident, as_units = $as_units:ident, units = $units:ident, suffix = $suffix:literal]
 | 
				
			||||||
 | 
					            const $log10_units_per_sec:ident: u32 = $log10_units_per_sec_value:expr;
 | 
				
			||||||
 | 
					        )*
 | 
				
			||||||
 | 
					    ) => {
 | 
				
			||||||
 | 
					        impl SimDuration {
 | 
				
			||||||
 | 
					            $(
 | 
				
			||||||
 | 
					                const $log10_units_per_sec: u32 = $log10_units_per_sec_value;
 | 
				
			||||||
 | 
					                pub const fn $from_units($units: u128) -> Self {
 | 
				
			||||||
 | 
					                    Self::from_units_helper::<{ Self::$log10_units_per_sec }>($units)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                pub const fn $as_units(self) -> u128 {
 | 
				
			||||||
 | 
					                    self.attos / const { 10u128.pow(Self::LOG10_ATTOS_PER_SEC - Self::$log10_units_per_sec) }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            )*
 | 
				
			||||||
 | 
					            pub const fn to_parts(mut self) -> SimDurationParts {
 | 
				
			||||||
 | 
					                $(
 | 
				
			||||||
 | 
					                    let $units = self.attos / const { 10u128.pow(Self::LOG10_ATTOS_PER_SEC - Self::$log10_units_per_sec) };
 | 
				
			||||||
 | 
					                    self.attos %= const { 10u128.pow(Self::LOG10_ATTOS_PER_SEC - Self::$log10_units_per_sec) };
 | 
				
			||||||
 | 
					                )*
 | 
				
			||||||
 | 
					                SimDurationParts {
 | 
				
			||||||
 | 
					                    $($units: $units as _,)*
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            pub const fn from_parts_checked(parts: SimDurationParts) -> Option<Self> {
 | 
				
			||||||
 | 
					                let attos = 0u128;
 | 
				
			||||||
 | 
					                $(
 | 
				
			||||||
 | 
					                    let Some(product) = const { 10u128.pow(Self::LOG10_ATTOS_PER_SEC - Self::$log10_units_per_sec) }.checked_mul(parts.$units as u128) else {
 | 
				
			||||||
 | 
					                        return None;
 | 
				
			||||||
 | 
					                    };
 | 
				
			||||||
 | 
					                    let Some(attos) = attos.checked_add(product) else {
 | 
				
			||||||
 | 
					                        return None;
 | 
				
			||||||
 | 
					                    };
 | 
				
			||||||
 | 
					                )*
 | 
				
			||||||
 | 
					                Some(Self {
 | 
				
			||||||
 | 
					                    attos,
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        impl fmt::Debug for SimDuration {
 | 
				
			||||||
 | 
					            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
				
			||||||
 | 
					                let ilog10_attos = match self.attos.checked_ilog10() {
 | 
				
			||||||
 | 
					                    Some(v) => v,
 | 
				
			||||||
 | 
					                    None => Self::LOG10_ATTOS_PER_SEC,
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					                let (suffix, int, fraction, fraction_digits) =
 | 
				
			||||||
 | 
					                    match Self::LOG10_ATTOS_PER_SEC.saturating_sub(ilog10_attos) {
 | 
				
			||||||
 | 
					                    $(
 | 
				
			||||||
 | 
					                        ..=Self::$log10_units_per_sec => {
 | 
				
			||||||
 | 
					                            let divisor = const { 10u128.pow(Self::LOG10_ATTOS_PER_SEC - Self::$log10_units_per_sec) };
 | 
				
			||||||
 | 
					                            (
 | 
				
			||||||
 | 
					                                $suffix,
 | 
				
			||||||
 | 
					                                self.attos / divisor,
 | 
				
			||||||
 | 
					                                self.attos % divisor,
 | 
				
			||||||
 | 
					                                (Self::LOG10_ATTOS_PER_SEC - Self::$log10_units_per_sec) as usize,
 | 
				
			||||||
 | 
					                            )
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                    )*
 | 
				
			||||||
 | 
					                    _ => unreachable!(),
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					                write!(f, "{int}")?;
 | 
				
			||||||
 | 
					                if fraction != 0 {
 | 
				
			||||||
 | 
					                    write!(f, ".{fraction:0fraction_digits$}")?;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                write!(f, " {suffix}")
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #[cfg(test)]
 | 
				
			||||||
 | 
					        #[test]
 | 
				
			||||||
 | 
					        fn test_duration_debug() {
 | 
				
			||||||
 | 
					            $(
 | 
				
			||||||
 | 
					                assert_eq!(
 | 
				
			||||||
 | 
					                    format!("{:?}", SimDuration::$from_units(123)),
 | 
				
			||||||
 | 
					                    concat!("123 ", $suffix)
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					                assert_eq!(
 | 
				
			||||||
 | 
					                    format!("{:?}", SimDuration::$from_units(1)),
 | 
				
			||||||
 | 
					                    concat!("1 ", $suffix),
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					                let mut v = SimDuration::$from_units(1);
 | 
				
			||||||
 | 
					                if v.attos < 1 << 53 {
 | 
				
			||||||
 | 
					                    v.attos += 1;
 | 
				
			||||||
 | 
					                    assert_eq!(
 | 
				
			||||||
 | 
					                        format!("{v:?}"),
 | 
				
			||||||
 | 
					                        format!("{} {}", v.attos as f64 / 10.0f64.powf((SimDuration::LOG10_ATTOS_PER_SEC - SimDuration::$log10_units_per_sec) as f64), $suffix),
 | 
				
			||||||
 | 
					                        "1 {} + 1 as == {} as", $suffix, v.attos,
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            )*
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl_duration_units! {
 | 
				
			||||||
 | 
					    #[unit_const = SECOND, from_units = from_secs, as_units = as_secs, units = secs, suffix = "s"]
 | 
				
			||||||
 | 
					    const LOG10_SECS_PER_SEC: u32 = 0;
 | 
				
			||||||
 | 
					    #[unit_const = MILLISECOND, from_units = from_millis, as_units = as_millis, units = millis, suffix = "ms"]
 | 
				
			||||||
 | 
					    const LOG10_MILLIS_PER_SEC: u32 = 3;
 | 
				
			||||||
 | 
					    #[unit_const = MICROSECOND, from_units = from_micros, as_units = as_micros, units = micros, suffix = "μs"]
 | 
				
			||||||
 | 
					    const LOG10_MICROS_PER_SEC: u32 = 6;
 | 
				
			||||||
 | 
					    #[unit_const = NANOSECOND, from_units = from_nanos, as_units = as_nanos, units = nanos, suffix = "ns"]
 | 
				
			||||||
 | 
					    const LOG10_NANOS_PER_SEC: u32 = 9;
 | 
				
			||||||
 | 
					    #[unit_const = PICOSECOND, from_units = from_picos, as_units = as_picos, units = picos, suffix = "ps"]
 | 
				
			||||||
 | 
					    const LOG10_PICOS_PER_SEC: u32 = 12;
 | 
				
			||||||
 | 
					    #[unit_const = FEMTOSECOND, from_units = from_femtos, as_units = as_femtos, units = femtos, suffix = "fs"]
 | 
				
			||||||
 | 
					    const LOG10_FEMTOS_PER_SEC: u32 = 15;
 | 
				
			||||||
 | 
					    #[unit_const = ATTOSECOND, from_units = from_attos, as_units = as_attos, units = attos, suffix = "as"]
 | 
				
			||||||
 | 
					    const LOG10_ATTOS_PER_SEC: u32 = 18;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl SimDuration {
 | 
				
			||||||
 | 
					    const fn from_units_helper<const UNITS_PER_SEC: u32>(units: u128) -> Self {
 | 
				
			||||||
 | 
					        let Some(attos) =
 | 
				
			||||||
 | 
					            units.checked_mul(const { 10u128.pow(Self::LOG10_ATTOS_PER_SEC - UNITS_PER_SEC) })
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            panic!("duration too big");
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        Self { attos }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    pub const ZERO: SimDuration = SimDuration::from_secs(0);
 | 
				
			||||||
 | 
					    pub const fn from_parts(parts: SimDurationParts) -> Self {
 | 
				
			||||||
 | 
					        match Self::from_parts_checked(parts) {
 | 
				
			||||||
 | 
					            Some(v) => v,
 | 
				
			||||||
 | 
					            None => panic!("duration too big"),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    pub const fn abs_diff(self, other: Self) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            attos: self.attos.abs_diff(other.attos),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    pub const fn checked_add(self, rhs: Self) -> Option<Self> {
 | 
				
			||||||
 | 
					        let Some(attos) = self.attos.checked_add(rhs.attos) else {
 | 
				
			||||||
 | 
					            return None;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        Some(Self { attos })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
 | 
				
			||||||
 | 
					        let Some(attos) = self.attos.checked_sub(rhs.attos) else {
 | 
				
			||||||
 | 
					            return None;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        Some(Self { attos })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    pub const fn is_zero(self) -> bool {
 | 
				
			||||||
 | 
					        self.attos == 0
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    pub const fn saturating_add(self, rhs: Self) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            attos: self.attos.saturating_add(rhs.attos),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    pub const fn saturating_sub(self, rhs: Self) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            attos: self.attos.saturating_sub(rhs.attos),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    pub const fn checked_ilog10(self) -> Option<i32> {
 | 
				
			||||||
 | 
					        let Some(ilog10_attos) = self.attos.checked_ilog10() else {
 | 
				
			||||||
 | 
					            return None;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        Some(ilog10_attos as i32 - Self::LOG10_ATTOS_PER_SEC as i32)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    #[track_caller]
 | 
				
			||||||
 | 
					    pub const fn ilog10(self) -> i32 {
 | 
				
			||||||
 | 
					        let Some(retval) = self.checked_ilog10() else {
 | 
				
			||||||
 | 
					            panic!("tried to take the ilog10 of 0");
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        retval
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    pub const fn checked_pow10(log10: i32, underflow_is_zero: bool) -> Option<Self> {
 | 
				
			||||||
 | 
					        let Some(log10) = Self::LOG10_ATTOS_PER_SEC.checked_add_signed(log10) else {
 | 
				
			||||||
 | 
					            return if log10 < 0 && underflow_is_zero {
 | 
				
			||||||
 | 
					                Some(Self::ZERO)
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                None
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        let Some(attos) = 10u128.checked_pow(log10) else {
 | 
				
			||||||
 | 
					            return None;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        Some(Self { attos })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    #[track_caller]
 | 
				
			||||||
 | 
					    pub const fn pow10(log10: i32) -> Self {
 | 
				
			||||||
 | 
					        let Some(retval) = Self::checked_pow10(log10, true) else {
 | 
				
			||||||
 | 
					            panic!("pow10 overflowed");
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        retval
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    pub const fn is_power_of_ten(self) -> bool {
 | 
				
			||||||
 | 
					        const TEN: u128 = 10;
 | 
				
			||||||
 | 
					        const NUMBER_OF_POWERS_OF_TEN: usize = {
 | 
				
			||||||
 | 
					            let mut n = 0;
 | 
				
			||||||
 | 
					            while let Some(_) = TEN.checked_pow(n as u32) {
 | 
				
			||||||
 | 
					                n += 1;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            n
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        const POWERS_OF_TEN: [u128; NUMBER_OF_POWERS_OF_TEN] = {
 | 
				
			||||||
 | 
					            let mut retval = [0; NUMBER_OF_POWERS_OF_TEN];
 | 
				
			||||||
 | 
					            let mut i = 0;
 | 
				
			||||||
 | 
					            while i < NUMBER_OF_POWERS_OF_TEN {
 | 
				
			||||||
 | 
					                retval[i] = TEN.pow(i as u32);
 | 
				
			||||||
 | 
					                i += 1;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            retval
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        let mut i = 0;
 | 
				
			||||||
 | 
					        while i < NUMBER_OF_POWERS_OF_TEN {
 | 
				
			||||||
 | 
					            if self.attos == POWERS_OF_TEN[i] {
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            i += 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        false
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<Duration> for SimDuration {
 | 
				
			||||||
 | 
					    fn from(duration: Duration) -> Self {
 | 
				
			||||||
 | 
					        Self::from_nanos(duration.as_nanos())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										542
									
								
								crates/fayalite/src/sim/vcd.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										542
									
								
								crates/fayalite/src/sim/vcd.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,542 @@
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
				
			||||||
 | 
					// See Notices.txt for copyright information
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::{
 | 
				
			||||||
 | 
					    expr::Flow,
 | 
				
			||||||
 | 
					    int::UInt,
 | 
				
			||||||
 | 
					    sim::{
 | 
				
			||||||
 | 
					        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,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					use bitvec::slice::BitSlice;
 | 
				
			||||||
 | 
					use std::{fmt, io, mem};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct VcdWriterDecls<W: io::Write + 'static> {
 | 
				
			||||||
 | 
					    writer: W,
 | 
				
			||||||
 | 
					    timescale: SimDuration,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<W: io::Write + 'static> VcdWriterDecls<W> {
 | 
				
			||||||
 | 
					    pub fn new(writer: W) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            writer,
 | 
				
			||||||
 | 
					            timescale: SimDuration::from_picos(1),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    pub fn timescale(&self) -> SimDuration {
 | 
				
			||||||
 | 
					        self.timescale
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    #[track_caller]
 | 
				
			||||||
 | 
					    pub fn with_timescale(mut self, timescale: SimDuration) -> Self {
 | 
				
			||||||
 | 
					        // check timescale validity
 | 
				
			||||||
 | 
					        vcd_timescale(timescale);
 | 
				
			||||||
 | 
					        self.timescale = timescale;
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[track_caller]
 | 
				
			||||||
 | 
					const fn vcd_timescale(timescale: SimDuration) -> &'static str {
 | 
				
			||||||
 | 
					    if !timescale.is_power_of_ten() {
 | 
				
			||||||
 | 
					        panic!("VCD timescale must be a power of 10");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    macro_rules! timescales {
 | 
				
			||||||
 | 
					        ($($const_name:ident = ($dur:expr, $text:literal),)*) => {
 | 
				
			||||||
 | 
					            $(const $const_name: SimDuration = $dur;)*
 | 
				
			||||||
 | 
					            match timescale {
 | 
				
			||||||
 | 
					                $($const_name => $text,)*
 | 
				
			||||||
 | 
					                _ => panic!("VCD timescale is too big"),
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    timescales! {
 | 
				
			||||||
 | 
					        TIMESCALE_1_AS = (SimDuration::from_attos(1), "1 as"),
 | 
				
			||||||
 | 
					        TIMESCALE_10_AS = (SimDuration::from_attos(10), "10 as"),
 | 
				
			||||||
 | 
					        TIMESCALE_100_AS = (SimDuration::from_attos(100), "100 as"),
 | 
				
			||||||
 | 
					        TIMESCALE_1_FS = (SimDuration::from_femtos(1), "1 fs"),
 | 
				
			||||||
 | 
					        TIMESCALE_10_FS = (SimDuration::from_femtos(10), "10 fs"),
 | 
				
			||||||
 | 
					        TIMESCALE_100_FS = (SimDuration::from_femtos(100), "100 fs"),
 | 
				
			||||||
 | 
					        TIMESCALE_1_PS = (SimDuration::from_picos(1), "1 ps"),
 | 
				
			||||||
 | 
					        TIMESCALE_10_PS = (SimDuration::from_picos(10), "10 ps"),
 | 
				
			||||||
 | 
					        TIMESCALE_100_PS = (SimDuration::from_picos(100), "100 ps"),
 | 
				
			||||||
 | 
					        TIMESCALE_1_NS = (SimDuration::from_nanos(1), "1 ns"),
 | 
				
			||||||
 | 
					        TIMESCALE_10_NS = (SimDuration::from_nanos(10), "10 ns"),
 | 
				
			||||||
 | 
					        TIMESCALE_100_NS = (SimDuration::from_nanos(100), "100 ns"),
 | 
				
			||||||
 | 
					        TIMESCALE_1_US = (SimDuration::from_micros(1), "1 us"),
 | 
				
			||||||
 | 
					        TIMESCALE_10_US = (SimDuration::from_micros(10), "10 us"),
 | 
				
			||||||
 | 
					        TIMESCALE_100_US = (SimDuration::from_micros(100), "100 us"),
 | 
				
			||||||
 | 
					        TIMESCALE_1_MS = (SimDuration::from_millis(1), "1 ms"),
 | 
				
			||||||
 | 
					        TIMESCALE_10_MS = (SimDuration::from_millis(10), "10 ms"),
 | 
				
			||||||
 | 
					        TIMESCALE_100_MS = (SimDuration::from_millis(100), "100 ms"),
 | 
				
			||||||
 | 
					        TIMESCALE_1_S = (SimDuration::from_secs(1), "1 s"),
 | 
				
			||||||
 | 
					        TIMESCALE_10_S = (SimDuration::from_secs(10), "10 s"),
 | 
				
			||||||
 | 
					        TIMESCALE_100_S = (SimDuration::from_secs(100), "100 s"),
 | 
				
			||||||
 | 
					        TIMESCALE_1000_S = (SimDuration::from_secs(1000), "1000 s"),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<W: io::Write> fmt::Debug for VcdWriterDecls<W> {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
				
			||||||
 | 
					        let Self {
 | 
				
			||||||
 | 
					            writer: _,
 | 
				
			||||||
 | 
					            timescale,
 | 
				
			||||||
 | 
					        } = self;
 | 
				
			||||||
 | 
					        f.debug_struct("VcdWriterDecls")
 | 
				
			||||||
 | 
					            .field("timescale", timescale)
 | 
				
			||||||
 | 
					            .finish_non_exhaustive()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn write_vcd_scope<W: io::Write, R>(
 | 
				
			||||||
 | 
					    writer: &mut W,
 | 
				
			||||||
 | 
					    scope_type: &str,
 | 
				
			||||||
 | 
					    scope_name: &str,
 | 
				
			||||||
 | 
					    f: impl FnOnce(&mut W) -> io::Result<R>,
 | 
				
			||||||
 | 
					) -> io::Result<R> {
 | 
				
			||||||
 | 
					    writeln!(writer, "$scope {scope_type} {scope_name} $end")?;
 | 
				
			||||||
 | 
					    let retval = f(writer)?;
 | 
				
			||||||
 | 
					    writeln!(writer, "$upscope $end")?;
 | 
				
			||||||
 | 
					    Ok(retval)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					macro_rules! trait_arg {
 | 
				
			||||||
 | 
					    (
 | 
				
			||||||
 | 
					        trait $Arg:ident {
 | 
				
			||||||
 | 
					            $(
 | 
				
			||||||
 | 
					                fn $fn:ident(self) -> $ty:ty;
 | 
				
			||||||
 | 
					            )*
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    ) => {
 | 
				
			||||||
 | 
					        trait $Arg: Sized {
 | 
				
			||||||
 | 
					            $(fn $fn(self) -> $ty {
 | 
				
			||||||
 | 
					                unreachable!()
 | 
				
			||||||
 | 
					            })*
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $(
 | 
				
			||||||
 | 
					            impl $Arg for $ty {
 | 
				
			||||||
 | 
					                fn $fn(self) -> $ty {
 | 
				
			||||||
 | 
					                    self
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        )*
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					trait_arg! {
 | 
				
			||||||
 | 
					    trait Arg {
 | 
				
			||||||
 | 
					        fn module(self) -> ArgModule;
 | 
				
			||||||
 | 
					        fn module_body(self) -> ArgModuleBody;
 | 
				
			||||||
 | 
					        fn in_type(self) -> ArgInType;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct ArgModule {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct ArgModuleBody {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Clone, Copy)]
 | 
				
			||||||
 | 
					struct ArgInType {
 | 
				
			||||||
 | 
					    source_var_type: &'static str,
 | 
				
			||||||
 | 
					    sink_var_type: &'static str,
 | 
				
			||||||
 | 
					    duplex_var_type: &'static str,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					trait WriteTrace: Copy {
 | 
				
			||||||
 | 
					    fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()>;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl WriteTrace for TraceDecl {
 | 
				
			||||||
 | 
					    fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            Self::Scope(v) => v.write_trace(writer, arg),
 | 
				
			||||||
 | 
					            Self::Scalar(v) => v.write_trace(writer, arg),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl WriteTrace for TraceScalar {
 | 
				
			||||||
 | 
					    fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            Self::UInt(v) => v.write_trace(writer, arg),
 | 
				
			||||||
 | 
					            Self::SInt(v) => v.write_trace(writer, arg),
 | 
				
			||||||
 | 
					            Self::Bool(v) => v.write_trace(writer, arg),
 | 
				
			||||||
 | 
					            Self::FieldlessEnum(v) => v.write_trace(writer, arg),
 | 
				
			||||||
 | 
					            Self::EnumDiscriminant(v) => v.write_trace(writer, arg),
 | 
				
			||||||
 | 
					            Self::Clock(v) => v.write_trace(writer, arg),
 | 
				
			||||||
 | 
					            Self::SyncReset(v) => v.write_trace(writer, arg),
 | 
				
			||||||
 | 
					            Self::AsyncReset(v) => v.write_trace(writer, arg),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn write_scalar_id<W: io::Write>(writer: &mut W, id: TraceScalarId) -> 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;
 | 
				
			||||||
 | 
					        writer.write_all(&[digit])?;
 | 
				
			||||||
 | 
					        if id == 0 {
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    Ok(())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn write_vcd_var<W: io::Write>(
 | 
				
			||||||
 | 
					    writer: &mut W,
 | 
				
			||||||
 | 
					    var_type: &str,
 | 
				
			||||||
 | 
					    size: usize,
 | 
				
			||||||
 | 
					    id: TraceScalarId,
 | 
				
			||||||
 | 
					    name: &str,
 | 
				
			||||||
 | 
					) -> io::Result<()> {
 | 
				
			||||||
 | 
					    write!(writer, "$var {var_type} {size} ")?;
 | 
				
			||||||
 | 
					    write_scalar_id(writer, id)?;
 | 
				
			||||||
 | 
					    writeln!(writer, " {name} $end")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl WriteTrace for TraceUInt {
 | 
				
			||||||
 | 
					    fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
 | 
				
			||||||
 | 
					        let ArgInType {
 | 
				
			||||||
 | 
					            source_var_type,
 | 
				
			||||||
 | 
					            sink_var_type,
 | 
				
			||||||
 | 
					            duplex_var_type,
 | 
				
			||||||
 | 
					        } = arg.in_type();
 | 
				
			||||||
 | 
					        let Self { id, name, ty, flow } = self;
 | 
				
			||||||
 | 
					        let 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)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl WriteTrace for TraceSInt {
 | 
				
			||||||
 | 
					    fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
 | 
				
			||||||
 | 
					        let Self { id, name, ty, flow } = self;
 | 
				
			||||||
 | 
					        TraceUInt {
 | 
				
			||||||
 | 
					            id,
 | 
				
			||||||
 | 
					            name,
 | 
				
			||||||
 | 
					            ty: UInt::new_dyn(ty.width()),
 | 
				
			||||||
 | 
					            flow,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        .write_trace(writer, arg)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl WriteTrace for TraceBool {
 | 
				
			||||||
 | 
					    fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
 | 
				
			||||||
 | 
					        let Self { id, name, flow } = self;
 | 
				
			||||||
 | 
					        TraceUInt {
 | 
				
			||||||
 | 
					            id,
 | 
				
			||||||
 | 
					            name,
 | 
				
			||||||
 | 
					            flow,
 | 
				
			||||||
 | 
					            ty: UInt::new_dyn(1),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        .write_trace(writer, arg)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl WriteTrace for TraceFieldlessEnum {
 | 
				
			||||||
 | 
					    fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
 | 
				
			||||||
 | 
					        todo!()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl WriteTrace for TraceEnumDiscriminant {
 | 
				
			||||||
 | 
					    fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
 | 
				
			||||||
 | 
					        todo!()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl WriteTrace for TraceClock {
 | 
				
			||||||
 | 
					    fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
 | 
				
			||||||
 | 
					        let Self { id, name, flow } = self;
 | 
				
			||||||
 | 
					        TraceBool { id, name, flow }.write_trace(writer, arg)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl WriteTrace for TraceSyncReset {
 | 
				
			||||||
 | 
					    fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
 | 
				
			||||||
 | 
					        let Self { id, name, flow } = self;
 | 
				
			||||||
 | 
					        TraceBool { id, name, flow }.write_trace(writer, arg)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl WriteTrace for TraceAsyncReset {
 | 
				
			||||||
 | 
					    fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
 | 
				
			||||||
 | 
					        let Self { id, name, flow } = self;
 | 
				
			||||||
 | 
					        TraceBool { id, name, flow }.write_trace(writer, arg)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl WriteTrace for TraceScope {
 | 
				
			||||||
 | 
					    fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            Self::Module(v) => v.write_trace(writer, arg),
 | 
				
			||||||
 | 
					            Self::Instance(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),
 | 
				
			||||||
 | 
					            Self::ModuleIO(v) => v.write_trace(writer, arg),
 | 
				
			||||||
 | 
					            Self::Bundle(v) => v.write_trace(writer, arg),
 | 
				
			||||||
 | 
					            Self::Array(v) => v.write_trace(writer, arg),
 | 
				
			||||||
 | 
					            Self::EnumWithFields(v) => v.write_trace(writer, arg),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl WriteTrace for TraceModule {
 | 
				
			||||||
 | 
					    fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
 | 
				
			||||||
 | 
					        let ArgModule {} = arg.module();
 | 
				
			||||||
 | 
					        let Self { name, children } = self;
 | 
				
			||||||
 | 
					        write_vcd_scope(writer, "module", &name, |writer| {
 | 
				
			||||||
 | 
					            for child in children {
 | 
				
			||||||
 | 
					                child.write_trace(writer, ArgModuleBody {})?;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Ok(())
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl WriteTrace for TraceInstance {
 | 
				
			||||||
 | 
					    fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
 | 
				
			||||||
 | 
					        let ArgModuleBody {} = arg.module_body();
 | 
				
			||||||
 | 
					        let Self {
 | 
				
			||||||
 | 
					            name: _,
 | 
				
			||||||
 | 
					            instance_io,
 | 
				
			||||||
 | 
					            module,
 | 
				
			||||||
 | 
					            ty: _,
 | 
				
			||||||
 | 
					        } = self;
 | 
				
			||||||
 | 
					        instance_io.write_trace(
 | 
				
			||||||
 | 
					            writer,
 | 
				
			||||||
 | 
					            ArgInType {
 | 
				
			||||||
 | 
					                source_var_type: "wire",
 | 
				
			||||||
 | 
					                sink_var_type: "wire",
 | 
				
			||||||
 | 
					                duplex_var_type: "wire",
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        )?;
 | 
				
			||||||
 | 
					        module.write_trace(writer, ArgModule {})
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl WriteTrace for TraceMemPort {
 | 
				
			||||||
 | 
					    fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
 | 
				
			||||||
 | 
					        todo!()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl WriteTrace for TraceWire {
 | 
				
			||||||
 | 
					    fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
 | 
				
			||||||
 | 
					        let ArgModuleBody {} = arg.module_body();
 | 
				
			||||||
 | 
					        let Self {
 | 
				
			||||||
 | 
					            name: _,
 | 
				
			||||||
 | 
					            child,
 | 
				
			||||||
 | 
					            ty: _,
 | 
				
			||||||
 | 
					        } = self;
 | 
				
			||||||
 | 
					        child.write_trace(
 | 
				
			||||||
 | 
					            writer,
 | 
				
			||||||
 | 
					            ArgInType {
 | 
				
			||||||
 | 
					                source_var_type: "wire",
 | 
				
			||||||
 | 
					                sink_var_type: "wire",
 | 
				
			||||||
 | 
					                duplex_var_type: "wire",
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl WriteTrace for TraceReg {
 | 
				
			||||||
 | 
					    fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
 | 
				
			||||||
 | 
					        let ArgModuleBody {} = arg.module_body();
 | 
				
			||||||
 | 
					        let Self {
 | 
				
			||||||
 | 
					            name: _,
 | 
				
			||||||
 | 
					            child,
 | 
				
			||||||
 | 
					            ty: _,
 | 
				
			||||||
 | 
					        } = self;
 | 
				
			||||||
 | 
					        child.write_trace(
 | 
				
			||||||
 | 
					            writer,
 | 
				
			||||||
 | 
					            ArgInType {
 | 
				
			||||||
 | 
					                source_var_type: "reg",
 | 
				
			||||||
 | 
					                sink_var_type: "reg",
 | 
				
			||||||
 | 
					                duplex_var_type: "reg",
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl WriteTrace for TraceModuleIO {
 | 
				
			||||||
 | 
					    fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
 | 
				
			||||||
 | 
					        let ArgModuleBody {} = arg.module_body();
 | 
				
			||||||
 | 
					        let Self {
 | 
				
			||||||
 | 
					            name: _,
 | 
				
			||||||
 | 
					            child,
 | 
				
			||||||
 | 
					            ty: _,
 | 
				
			||||||
 | 
					            flow: _,
 | 
				
			||||||
 | 
					        } = self;
 | 
				
			||||||
 | 
					        child.write_trace(
 | 
				
			||||||
 | 
					            writer,
 | 
				
			||||||
 | 
					            ArgInType {
 | 
				
			||||||
 | 
					                source_var_type: "wire",
 | 
				
			||||||
 | 
					                sink_var_type: "wire",
 | 
				
			||||||
 | 
					                duplex_var_type: "wire",
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl WriteTrace for TraceBundle {
 | 
				
			||||||
 | 
					    fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
 | 
				
			||||||
 | 
					        let arg = arg.in_type();
 | 
				
			||||||
 | 
					        let Self {
 | 
				
			||||||
 | 
					            name,
 | 
				
			||||||
 | 
					            fields,
 | 
				
			||||||
 | 
					            ty: _,
 | 
				
			||||||
 | 
					            flow: _,
 | 
				
			||||||
 | 
					        } = self;
 | 
				
			||||||
 | 
					        write_vcd_scope(writer, "struct", &name, |writer| {
 | 
				
			||||||
 | 
					            for field in fields {
 | 
				
			||||||
 | 
					                field.write_trace(writer, arg)?;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Ok(())
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl WriteTrace for TraceArray {
 | 
				
			||||||
 | 
					    fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
 | 
				
			||||||
 | 
					        let arg = arg.in_type();
 | 
				
			||||||
 | 
					        let Self {
 | 
				
			||||||
 | 
					            name,
 | 
				
			||||||
 | 
					            elements,
 | 
				
			||||||
 | 
					            ty: _,
 | 
				
			||||||
 | 
					            flow: _,
 | 
				
			||||||
 | 
					        } = self;
 | 
				
			||||||
 | 
					        write_vcd_scope(writer, "struct", &name, |writer| {
 | 
				
			||||||
 | 
					            for element in elements {
 | 
				
			||||||
 | 
					                element.write_trace(writer, arg)?;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Ok(())
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl WriteTrace for TraceEnumWithFields {
 | 
				
			||||||
 | 
					    fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
 | 
				
			||||||
 | 
					        todo!()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<W: io::Write> TraceWriterDecls for VcdWriterDecls<W> {
 | 
				
			||||||
 | 
					    type Error = io::Error;
 | 
				
			||||||
 | 
					    type TraceWriter = VcdWriter<W>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn write_decls(self, module: TraceModule) -> Result<Self::TraceWriter, Self::Error> {
 | 
				
			||||||
 | 
					        let Self {
 | 
				
			||||||
 | 
					            mut writer,
 | 
				
			||||||
 | 
					            timescale,
 | 
				
			||||||
 | 
					        } = self;
 | 
				
			||||||
 | 
					        writeln!(writer, "$timescale {} $end", vcd_timescale(timescale))?;
 | 
				
			||||||
 | 
					        module.write_trace(&mut writer, ArgModule {})?;
 | 
				
			||||||
 | 
					        writeln!(writer, "$enddefinitions $end")?;
 | 
				
			||||||
 | 
					        writeln!(writer, "$dumpvars")?;
 | 
				
			||||||
 | 
					        Ok(VcdWriter {
 | 
				
			||||||
 | 
					            writer,
 | 
				
			||||||
 | 
					            finished_init: false,
 | 
				
			||||||
 | 
					            timescale,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct VcdWriter<W: io::Write + 'static> {
 | 
				
			||||||
 | 
					    writer: W,
 | 
				
			||||||
 | 
					    finished_init: bool,
 | 
				
			||||||
 | 
					    timescale: SimDuration,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<W: io::Write + 'static> VcdWriter<W> {
 | 
				
			||||||
 | 
					    pub fn timescale(&self) -> SimDuration {
 | 
				
			||||||
 | 
					        self.timescale
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<W: io::Write> TraceWriter for VcdWriter<W> {
 | 
				
			||||||
 | 
					    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 => write!(self.writer, "{} ", if value[0] { "1" } else { "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")?;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                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")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn set_signal_sint(&mut self, id: TraceScalarId, value: &BitSlice) -> Result<(), Self::Error> {
 | 
				
			||||||
 | 
					        self.set_signal_uint(id, value)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn finish_init(&mut self) -> Result<(), Self::Error> {
 | 
				
			||||||
 | 
					        if mem::replace(&mut self.finished_init, true) {
 | 
				
			||||||
 | 
					            return Ok(());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        writeln!(self.writer, "$end")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn change_time_to(&mut self, instant: SimInstant) -> Result<(), Self::Error> {
 | 
				
			||||||
 | 
					        assert!(self.finished_init);
 | 
				
			||||||
 | 
					        let mut instant_attos = (instant - SimInstant::START).as_attos();
 | 
				
			||||||
 | 
					        instant_attos += self.timescale.as_attos() / 2;
 | 
				
			||||||
 | 
					        let timestamp = instant_attos / self.timescale.as_attos();
 | 
				
			||||||
 | 
					        writeln!(self.writer, "#{timestamp}")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn flush(&mut self) -> Result<(), Self::Error> {
 | 
				
			||||||
 | 
					        self.writer.flush()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn close(mut self) -> Result<(), Self::Error> {
 | 
				
			||||||
 | 
					        self.writer.flush()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<W: io::Write> fmt::Debug for VcdWriter<W> {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
				
			||||||
 | 
					        let Self {
 | 
				
			||||||
 | 
					            writer: _,
 | 
				
			||||||
 | 
					            finished_init,
 | 
				
			||||||
 | 
					            timescale,
 | 
				
			||||||
 | 
					        } = self;
 | 
				
			||||||
 | 
					        f.debug_struct("VcdWriter")
 | 
				
			||||||
 | 
					            .field("finished_init", finished_init)
 | 
				
			||||||
 | 
					            .field("timescale", timescale)
 | 
				
			||||||
 | 
					            .finish_non_exhaustive()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -24,7 +24,8 @@ pub use scoped_ref::ScopedRef;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[doc(inline)]
 | 
					#[doc(inline)]
 | 
				
			||||||
pub use misc::{
 | 
					pub use misc::{
 | 
				
			||||||
    interned_bit, iter_eq_by, BitSliceWriteWithBase, DebugAsDisplay, DebugAsRawString, MakeMutSlice,
 | 
					    get_many_mut, interned_bit, iter_eq_by, BitSliceWriteWithBase, DebugAsDisplay,
 | 
				
			||||||
 | 
					    DebugAsRawString, MakeMutSlice, RcWriter,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub mod job_server;
 | 
					pub mod job_server;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,7 @@
 | 
				
			||||||
use crate::intern::{Intern, Interned};
 | 
					use crate::intern::{Intern, Interned};
 | 
				
			||||||
use bitvec::{bits, order::Lsb0, slice::BitSlice, view::BitView};
 | 
					use bitvec::{bits, order::Lsb0, slice::BitSlice, view::BitView};
 | 
				
			||||||
use std::{
 | 
					use std::{
 | 
				
			||||||
 | 
					    cell::Cell,
 | 
				
			||||||
    fmt::{self, Debug, Write},
 | 
					    fmt::{self, Debug, Write},
 | 
				
			||||||
    rc::Rc,
 | 
					    rc::Rc,
 | 
				
			||||||
    sync::{Arc, OnceLock},
 | 
					    sync::{Arc, OnceLock},
 | 
				
			||||||
| 
						 | 
					@ -94,9 +95,15 @@ pub fn interned_bit(v: bool) -> Interned<BitSlice> {
 | 
				
			||||||
    RETVAL.get_or_init(|| [bits![0; 1].intern(), bits![1; 1].intern()])[v as usize]
 | 
					    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);
 | 
					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<'_> {
 | 
					impl BitSliceWriteWithBase<'_> {
 | 
				
			||||||
    fn fmt_with_base<const BITS_PER_DIGIT: usize, const UPPER_CASE: bool>(
 | 
					    fn fmt_with_base<const BITS_PER_DIGIT: usize, const UPPER_CASE: bool>(
 | 
				
			||||||
        self,
 | 
					        self,
 | 
				
			||||||
| 
						 | 
					@ -155,3 +162,66 @@ impl fmt::UpperHex for BitSliceWriteWithBase<'_> {
 | 
				
			||||||
        self.fmt_with_base::<4, true>(f)
 | 
					        self.fmt_with_base::<4, true>(f)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[inline]
 | 
				
			||||||
 | 
					#[track_caller]
 | 
				
			||||||
 | 
					pub fn get_many_mut<T, const N: usize>(slice: &mut [T], indexes: [usize; N]) -> [&mut T; N] {
 | 
				
			||||||
 | 
					    for i in 0..N {
 | 
				
			||||||
 | 
					        for j in 0..i {
 | 
				
			||||||
 | 
					            assert!(indexes[i] != indexes[j], "duplicate index");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        assert!(indexes[i] < slice.len(), "index out of bounds");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // Safety: checked that no indexes are duplicates and no indexes are out of bounds
 | 
				
			||||||
 | 
					    unsafe {
 | 
				
			||||||
 | 
					        let base = slice.as_mut_ptr(); // convert to a raw pointer before loop to avoid aliasing with &mut [T]
 | 
				
			||||||
 | 
					        std::array::from_fn(|i| &mut *base.add(indexes[i]))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Clone, Default)]
 | 
				
			||||||
 | 
					pub struct RcWriter(Rc<Cell<Vec<u8>>>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Debug for RcWriter {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
				
			||||||
 | 
					        self.borrow_impl(|buf| {
 | 
				
			||||||
 | 
					            f.debug_tuple("RcWriter")
 | 
				
			||||||
 | 
					                .field(&DebugAsDisplay(format_args!("b\"{}\"", buf.escape_ascii())))
 | 
				
			||||||
 | 
					                .finish()
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl RcWriter {
 | 
				
			||||||
 | 
					    fn borrow_impl<R>(&self, f: impl FnOnce(&mut Vec<u8>) -> R) -> R {
 | 
				
			||||||
 | 
					        let buf = Cell::take(&self.0);
 | 
				
			||||||
 | 
					        struct PutBackOnDrop<'a> {
 | 
				
			||||||
 | 
					            buf: Vec<u8>,
 | 
				
			||||||
 | 
					            this: &'a RcWriter,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        impl Drop for PutBackOnDrop<'_> {
 | 
				
			||||||
 | 
					            fn drop(&mut self) {
 | 
				
			||||||
 | 
					                self.this.0.set(std::mem::take(&mut self.buf));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let mut buf = PutBackOnDrop { buf, this: self };
 | 
				
			||||||
 | 
					        f(&mut buf.buf)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    pub fn borrow<R>(&mut self, f: impl FnOnce(&mut Vec<u8>) -> R) -> R {
 | 
				
			||||||
 | 
					        self.borrow_impl(f)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    pub fn take(&mut self) -> Vec<u8> {
 | 
				
			||||||
 | 
					        Cell::take(&self.0)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl std::io::Write for RcWriter {
 | 
				
			||||||
 | 
					    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
 | 
				
			||||||
 | 
					        self.borrow(|v| v.extend_from_slice(buf));
 | 
				
			||||||
 | 
					        Ok(buf.len())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn flush(&mut self) -> std::io::Result<()> {
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,6 +37,18 @@ impl<T: Type> Wire<T> {
 | 
				
			||||||
            ty: ty.canonical(),
 | 
					            ty: ty.canonical(),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    pub fn from_canonical(v: Wire<CanonicalType>) -> Self {
 | 
				
			||||||
 | 
					        let Wire {
 | 
				
			||||||
 | 
					            name,
 | 
				
			||||||
 | 
					            source_location,
 | 
				
			||||||
 | 
					            ty,
 | 
				
			||||||
 | 
					        } = v;
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            name,
 | 
				
			||||||
 | 
					            source_location,
 | 
				
			||||||
 | 
					            ty: T::from_canonical(ty),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    pub fn ty(&self) -> T {
 | 
					    pub fn ty(&self) -> T {
 | 
				
			||||||
        self.ty
 | 
					        self.ty
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										1058
									
								
								crates/fayalite/tests/sim.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1058
									
								
								crates/fayalite/tests/sim.rs
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue