Compare commits
14 commits
e2653a3245
...
11ddbc43c7
Author | SHA1 | Date | |
---|---|---|---|
11ddbc43c7 | |||
c4b5d00419 | |||
09aa9fbc78 | |||
288a6b71b9 | |||
0095570f19 | |||
f54e55a143 | |||
a6e40839ac | |||
3106a6fff6 | |||
f338f37d3e | |||
277d3e0d4d | |||
b288d6f8f2 | |||
479d59b287 | |||
6f904148c4 | |||
3ea0d98924 |
16 changed files with 8982 additions and 33 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]
|
||||||
|
|
|
@ -653,21 +653,19 @@ impl FormalArgs {
|
||||||
self.sby.display()
|
self.sby.display()
|
||||||
)))
|
)))
|
||||||
};
|
};
|
||||||
if do_cache {
|
fs::write(
|
||||||
fs::write(
|
cache_file,
|
||||||
cache_file,
|
serde_json::to_string_pretty(&FormalCache {
|
||||||
serde_json::to_string_pretty(&FormalCache {
|
version: FormalCacheVersion::CURRENT,
|
||||||
version: FormalCacheVersion::CURRENT,
|
contents_hash: contents_hash.unwrap(),
|
||||||
contents_hash: contents_hash.unwrap(),
|
stdout_stderr: captured_output,
|
||||||
stdout_stderr: captured_output,
|
result: match &result {
|
||||||
result: match &result {
|
Ok(FormalOutput { verilog: _ }) => Ok(FormalCacheOutput {}),
|
||||||
Ok(FormalOutput { verilog: _ }) => Ok(FormalCacheOutput {}),
|
Err(error) => Err(error.to_string()),
|
||||||
Err(error) => Err(error.to_string()),
|
},
|
||||||
},
|
})
|
||||||
})
|
.expect("serialization shouldn't ever fail"),
|
||||||
.expect("serialization shouldn't ever fail"),
|
)?;
|
||||||
)?;
|
|
||||||
}
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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