Compare commits

...
Sign in to create a new pull request.

38 commits

Author SHA1 Message Date
668e714dc9
actually test always_zero hasher 2025-04-09 21:11:09 -07:00
88323a8c16
run some tests with always_zero hasher 2025-04-09 21:03:57 -07:00
91e1b619e8
switch to petgraph 0.8.1 now that my PR was merged and released to crates.io 2025-04-09 20:48:40 -07:00
e2d2d4110b
upgrade hashbrown to 0.15.2 2025-04-09 20:33:21 -07:00
b1f9706e4e
add custom hasher for testing 2025-04-09 20:27:22 -07:00
4eda4366c8
check types in debug mode in impl Debug for Expr, helping to catch bugs 2025-04-09 20:23:19 -07:00
122c08d3cf
add fake which for miri 2025-04-09 20:21:43 -07:00
b08a747e20
switch to using type aliases for HashMap/HashSet to allow easily switching hashers 2025-04-09 20:17:46 -07:00
e0c9939147
add test that SimValue can't be interned, since its PartialEq may ignore types 2025-04-09 19:55:09 -07:00
07725ab489
switch interning to use HashTable rather than HashMap 2025-04-09 19:30:02 -07:00
36f1b9bbb6
add derive(Debug) to all types that are interned 2025-04-09 19:24:08 -07:00
9a1b047d2f
change TypeIdMap to not use any unsafe code 2025-04-09 16:25:56 -07:00
5967e812a2
fix [SU]IntValue's PartialEq for interning
different widths must make values compare not equal otherwise interning
will e.g. substitute a 0x0_u8 for a 0x0_u2
2025-04-08 21:57:56 -07:00
001fd31451
add UIntInRange[Inclusive][Type] 2025-04-07 18:27:54 -07:00
57aae7b7fb
implement [de]serializing BaseTypes, SimValues, and support PhantomConst<T> in #[hdl] struct S<T> 2025-04-04 01:04:26 -07:00
6929352be7
re-export bitvec and add types useful for simulation to the prelude 2025-04-03 16:01:39 -07:00
62058dc141
fix cargo doc warnings -- convert urls to auto links 2025-04-01 22:22:54 -07:00
c4b6a0fee6
add support for #[hdl(sim)] enum_ty.Variant(value) and #[hdl(sim)] EnumTy::Variant(value) and non-sim variants too 2025-04-01 22:16:47 -07:00
9092e45447
fix #[hdl(sim)] match on enums 2025-03-30 01:25:07 -07:00
a40eaaa2da
expand SimValue support 2025-03-30 00:55:38 -07:00
5028401a5a
change SimValue to contain and deref to a value and not just contain bits 2025-03-27 23:44:36 -07:00
e0f978fbb6
silence unused m variable warning in #[hdl_module] with an empty body. 2025-03-27 23:17:28 -07:00
ec3a61513b
simulator read/write types must be passive 2025-03-27 23:03:44 -07:00
fdc73b5f3b
add ripple counter test to test simulating alternating circuits and extern modules 2025-03-25 18:56:26 -07:00
a115585d5a
simulator: allow external module generators to wait for value changes and/or clock edges 2025-03-25 18:26:48 -07:00
ab9ff4f2db
simplify setting an extern module simulation 2025-03-21 17:08:29 -07:00
d1bd176b28
implement simulation of extern modules 2025-03-21 01:47:14 -07:00
920d8d875f
add some missing #[track_caller] 2025-03-19 17:10:51 -07:00
d453755bb2
add ExprPartialEq/ExprPartialOrd impls for PhantomConst 2025-03-10 19:40:03 -07:00
450e1004b6
fix using fayalite as a dependency 2025-03-09 23:14:14 -07:00
c0c5b550bc
add PhantomConst 2025-03-09 21:03:47 -07:00
2fa0ea6192
make FillInDefaultedGenerics work with Sizes and not just Types 2025-03-09 20:59:21 -07:00
bd75fdfefd
add efficient prefix-sums and reductions 2025-03-02 23:04:17 -08:00
50c86e18dc
add Expr<ArrayType<T, Len>>: IntoIterator and Expr<Array<T>>: FromIterator<T> 2025-03-02 18:02:34 -08:00
60734cc9d1
switch CI to use mirrors 2025-03-02 17:43:29 -08:00
3458c21f44
add #[hdl(cmp_eq)] to implement HdlPartialEq automatically 2025-02-16 20:48:16 -08:00
43797db36e
sort custom keywords 2025-02-16 20:46:54 -08:00
cdd84953d0
support unknown trait bounds in type parameters 2025-02-13 18:35:30 -08:00
78 changed files with 14589 additions and 8502 deletions

View file

@ -12,10 +12,10 @@ jobs:
outputs:
cache-primary-key: ${{ steps.restore-deps.outputs.cache-primary-key }}
steps:
- uses: https://code.forgejo.org/actions/checkout@v3
- uses: https://git.libre-chip.org/mirrors/checkout@v3
with:
fetch-depth: 0
- uses: https://code.forgejo.org/actions/cache/restore@v3
- uses: https://git.libre-chip.org/mirrors/cache/restore@v3
id: restore-deps
with:
path: deps
@ -58,19 +58,19 @@ jobs:
- name: Get SymbiYosys
if: steps.restore-deps.outputs.cache-hit != 'true'
run: |
git clone --depth=1 --branch=yosys-0.45 https://github.com/YosysHQ/sby.git deps/sby
git clone --depth=1 --branch=yosys-0.45 https://git.libre-chip.org/mirrors/sby deps/sby
- name: Build Z3
if: steps.restore-deps.outputs.cache-hit != 'true'
run: |
git clone --depth=1 --recursive --branch=z3-4.13.3 https://github.com/Z3Prover/z3.git deps/z3
git clone --depth=1 --recursive --branch=z3-4.13.3 https://git.libre-chip.org/mirrors/z3 deps/z3
(cd deps/z3; PYTHON=python3 ./configure --prefix=/usr/local)
make -C deps/z3/build -j"$(nproc)"
- name: Build Yosys
if: steps.restore-deps.outputs.cache-hit != 'true'
run: |
git clone --depth=1 --recursive --branch=0.45 https://github.com/YosysHQ/yosys.git deps/yosys
git clone --depth=1 --recursive --branch=0.45 https://git.libre-chip.org/mirrors/yosys deps/yosys
make -C deps/yosys -j"$(nproc)"
- uses: https://code.forgejo.org/actions/cache/save@v3
- uses: https://git.libre-chip.org/mirrors/cache/save@v3
if: steps.restore-deps.outputs.cache-hit != 'true'
with:
path: deps

View file

@ -9,7 +9,7 @@ jobs:
runs-on: debian-12
needs: deps
steps:
- uses: https://code.forgejo.org/actions/checkout@v3
- uses: https://git.libre-chip.org/mirrors/checkout@v3
with:
fetch-depth: 0
- run: |
@ -41,7 +41,7 @@ jobs:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.82.0
source "$HOME/.cargo/env"
echo "$PATH" >> "$GITHUB_PATH"
- uses: https://code.forgejo.org/actions/cache/restore@v3
- uses: https://git.libre-chip.org/mirrors/cache/restore@v3
with:
path: deps
key: ${{ needs.deps.outputs.cache-primary-key }}
@ -52,10 +52,11 @@ jobs:
make -C deps/yosys install
export PATH="$(realpath deps/firtool/bin):$PATH"
echo "$PATH" >> "$GITHUB_PATH"
- uses: https://github.com/Swatinem/rust-cache@v2
- uses: https://git.libre-chip.org/mirrors/rust-cache@v2
with:
save-if: ${{ github.ref == 'refs/heads/master' }}
- run: cargo test
- run: cargo build --tests --features=unstable-doc
- run: cargo test --doc --features=unstable-doc
- run: cargo doc --features=unstable-doc
- run: FAYALITE_TEST_HASHER=always_zero cargo test --test=module --features=unstable-doc,unstable-test-hasher

56
Cargo.lock generated
View file

@ -2,18 +2,6 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "ahash"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "allocator-api2"
version = "0.2.16"
@ -365,6 +353,12 @@ version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "funty"
version = "2.0.0"
@ -400,12 +394,13 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "hashbrown"
version = "0.14.3"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
dependencies = [
"ahash",
"allocator-api2",
"equivalent",
"foldhash",
]
[[package]]
@ -431,9 +426,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
[[package]]
name = "indexmap"
version = "2.5.0"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
dependencies = [
"equivalent",
"hashbrown",
@ -524,11 +519,14 @@ dependencies = [
[[package]]
name = "petgraph"
version = "0.6.5"
source = "git+https://github.com/programmerjake/petgraph.git?rev=258ea8071209a924b73fe96f9f87a3b7b45cbc9f#258ea8071209a924b73fe96f9f87a3b7b45cbc9f"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a98c6720655620a521dcc722d0ad66cd8afd5d86e34a89ef691c50b7b24de06"
dependencies = [
"fixedbitset",
"hashbrown",
"indexmap",
"serde",
]
[[package]]
@ -893,23 +891,3 @@ checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
dependencies = [
"tap",
]
[[package]]
name = "zerocopy"
version = "0.7.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

View file

@ -23,14 +23,13 @@ blake3 = { version = "1.5.4", features = ["serde"] }
clap = { version = "4.5.9", features = ["derive", "env", "string"] }
ctor = "0.2.8"
eyre = "0.6.12"
hashbrown = "0.14.3"
hashbrown = "0.15.2"
indexmap = { version = "2.5.0", features = ["serde"] }
jobslot = "0.2.19"
num-bigint = "0.4.6"
num-traits = "0.2.16"
os_pipe = "1.2.1"
# TODO: switch back to crates.io once petgraph accepts PR #684 and releases a new version
petgraph = { git = "https://github.com/programmerjake/petgraph.git", rev = "258ea8071209a924b73fe96f9f87a3b7b45cbc9f" }
petgraph = "0.8.1"
prettyplease = "0.2.20"
proc-macro2 = "1.0.83"
quote = "1.0.36"

View file

@ -220,6 +220,7 @@ forward_fold!(syn::ExprArray => fold_expr_array);
forward_fold!(syn::ExprCall => fold_expr_call);
forward_fold!(syn::ExprIf => fold_expr_if);
forward_fold!(syn::ExprMatch => fold_expr_match);
forward_fold!(syn::ExprMethodCall => fold_expr_method_call);
forward_fold!(syn::ExprPath => fold_expr_path);
forward_fold!(syn::ExprRepeat => fold_expr_repeat);
forward_fold!(syn::ExprStruct => fold_expr_struct);

View file

@ -30,7 +30,9 @@ pub(crate) struct ParsedBundle {
pub(crate) field_flips: Vec<Option<HdlAttr<kw::flip, kw::hdl>>>,
pub(crate) mask_type_ident: Ident,
pub(crate) mask_type_match_variant_ident: Ident,
pub(crate) mask_type_sim_value_ident: Ident,
pub(crate) match_variant_ident: Ident,
pub(crate) sim_value_ident: Ident,
pub(crate) builder_ident: Ident,
pub(crate) mask_type_builder_ident: Ident,
}
@ -83,6 +85,7 @@ impl ParsedBundle {
custom_bounds,
no_static: _,
no_runtime_generics: _,
cmp_eq: _,
} = options.body;
let mut fields = match fields {
syn::Fields::Named(fields) => fields,
@ -124,7 +127,9 @@ impl ParsedBundle {
field_flips,
mask_type_ident: format_ident!("__{}__MaskType", ident),
mask_type_match_variant_ident: format_ident!("__{}__MaskType__MatchVariant", ident),
mask_type_sim_value_ident: format_ident!("__{}__MaskType__SimValue", ident),
match_variant_ident: format_ident!("__{}__MatchVariant", ident),
sim_value_ident: format_ident!("__{}__SimValue", ident),
mask_type_builder_ident: format_ident!("__{}__MaskType__Builder", ident),
builder_ident: format_ident!("__{}__Builder", ident),
ident,
@ -426,7 +431,9 @@ impl ToTokens for ParsedBundle {
field_flips,
mask_type_ident,
mask_type_match_variant_ident,
mask_type_sim_value_ident,
match_variant_ident,
sim_value_ident,
builder_ident,
mask_type_builder_ident,
} = self;
@ -437,6 +444,7 @@ impl ToTokens for ParsedBundle {
custom_bounds: _,
no_static,
no_runtime_generics,
cmp_eq,
} = &options.body;
let target = get_target(target, ident);
let mut item_attrs = attrs.clone();
@ -521,7 +529,7 @@ impl ToTokens for ParsedBundle {
semi_token: None,
}
.to_tokens(tokens);
let mut mask_type_match_variant_fields = mask_type_fields;
let mut mask_type_match_variant_fields = mask_type_fields.clone();
for Field { ty, .. } in &mut mask_type_match_variant_fields.named {
*ty = parse_quote_spanned! {span=>
::fayalite::expr::Expr<#ty>
@ -563,6 +571,58 @@ impl ToTokens for ParsedBundle {
semi_token: None,
}
.to_tokens(tokens);
let mut mask_type_sim_value_fields = mask_type_fields;
for Field { ty, .. } in &mut mask_type_sim_value_fields.named {
*ty = parse_quote_spanned! {span=>
::fayalite::sim::value::SimValue<#ty>
};
}
ItemStruct {
attrs: vec![
parse_quote_spanned! {span=>
#[::fayalite::__std::prelude::v1::derive(
::fayalite::__std::fmt::Debug,
::fayalite::__std::clone::Clone,
)]
},
parse_quote_spanned! {span=>
#[allow(non_camel_case_types, dead_code)]
},
],
vis: vis.clone(),
struct_token: *struct_token,
ident: mask_type_sim_value_ident.clone(),
generics: generics.into(),
fields: Fields::Named(mask_type_sim_value_fields),
semi_token: None,
}
.to_tokens(tokens);
let mut sim_value_fields = FieldsNamed::from(fields.clone());
for Field { ty, .. } in &mut sim_value_fields.named {
*ty = parse_quote_spanned! {span=>
::fayalite::sim::value::SimValue<#ty>
};
}
ItemStruct {
attrs: vec![
parse_quote_spanned! {span=>
#[::fayalite::__std::prelude::v1::derive(
::fayalite::__std::fmt::Debug,
::fayalite::__std::clone::Clone,
)]
},
parse_quote_spanned! {span=>
#[allow(non_camel_case_types, dead_code)]
},
],
vis: vis.clone(),
struct_token: *struct_token,
ident: sim_value_ident.clone(),
generics: generics.into(),
fields: Fields::Named(sim_value_fields),
semi_token: None,
}
.to_tokens(tokens);
let this_token = Ident::new("__this", span);
let fields_token = Ident::new("__fields", span);
let self_token = Token![self](span);
@ -613,6 +673,31 @@ impl ToTokens for ParsedBundle {
}
},
));
let sim_value_from_bits_fields = Vec::from_iter(fields.named().into_iter().map(|field| {
let ident: &Ident = field.ident().as_ref().unwrap();
quote_spanned! {span=>
#ident: v.field_from_bits(),
}
}));
let sim_value_clone_from_bits_fields =
Vec::from_iter(fields.named().into_iter().map(|field| {
let ident: &Ident = field.ident().as_ref().unwrap();
quote_spanned! {span=>
v.field_clone_from_bits(&mut value.#ident);
}
}));
let sim_value_to_bits_fields = Vec::from_iter(fields.named().into_iter().map(|field| {
let ident: &Ident = field.ident().as_ref().unwrap();
quote_spanned! {span=>
v.field_to_bits(&value.#ident);
}
}));
let to_sim_value_fields = Vec::from_iter(fields.named().into_iter().map(|field| {
let ident: &Ident = field.ident().as_ref().unwrap();
quote_spanned! {span=>
#ident: ::fayalite::sim::value::SimValue::ty(&self.#ident),
}
}));
let fields_len = fields.named().into_iter().len();
quote_spanned! {span=>
#[automatically_derived]
@ -621,6 +706,7 @@ impl ToTokens for ParsedBundle {
{
type BaseType = ::fayalite::bundle::Bundle;
type MaskType = #mask_type_ident #type_generics;
type SimValue = #mask_type_sim_value_ident #type_generics;
type MatchVariant = #mask_type_match_variant_ident #type_generics;
type MatchActiveScope = ();
type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope<
@ -658,6 +744,34 @@ impl ToTokens for ParsedBundle {
fn source_location() -> ::fayalite::source_location::SourceLocation {
::fayalite::source_location::SourceLocation::caller()
}
fn sim_value_from_bits(
&self,
bits: &::fayalite::bitvec::slice::BitSlice,
) -> <Self as ::fayalite::ty::Type>::SimValue {
#![allow(unused_mut, unused_variables)]
let mut v = ::fayalite::bundle::BundleSimValueFromBits::new(*self, bits);
#mask_type_sim_value_ident {
#(#sim_value_from_bits_fields)*
}
}
fn sim_value_clone_from_bits(
&self,
value: &mut <Self as ::fayalite::ty::Type>::SimValue,
bits: &::fayalite::bitvec::slice::BitSlice,
) {
#![allow(unused_mut, unused_variables)]
let mut v = ::fayalite::bundle::BundleSimValueFromBits::new(*self, bits);
#(#sim_value_clone_from_bits_fields)*
}
fn sim_value_to_bits(
&self,
value: &<Self as ::fayalite::ty::Type>::SimValue,
bits: &mut ::fayalite::bitvec::slice::BitSlice,
) {
#![allow(unused_mut, unused_variables)]
let mut v = ::fayalite::bundle::BundleSimValueToBits::new(*self, bits);
#(#sim_value_to_bits_fields)*
}
}
#[automatically_derived]
impl #impl_generics ::fayalite::bundle::BundleType for #mask_type_ident #type_generics
@ -689,11 +803,57 @@ impl ToTokens for ParsedBundle {
}
}
#[automatically_derived]
impl #impl_generics ::fayalite::sim::value::ToSimValue for #mask_type_sim_value_ident #type_generics
#where_clause
{
type Type = #mask_type_ident #type_generics;
fn to_sim_value(
&self,
) -> ::fayalite::sim::value::SimValue<
<Self as ::fayalite::sim::value::ToSimValue>::Type,
> {
let ty = #mask_type_ident {
#(#to_sim_value_fields)*
};
::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self))
}
fn into_sim_value(
self,
) -> ::fayalite::sim::value::SimValue<
<Self as ::fayalite::sim::value::ToSimValue>::Type,
> {
let ty = #mask_type_ident {
#(#to_sim_value_fields)*
};
::fayalite::sim::value::SimValue::from_value(ty, self)
}
}
#[automatically_derived]
impl #impl_generics ::fayalite::sim::value::ToSimValueWithType<#mask_type_ident #type_generics>
for #mask_type_sim_value_ident #type_generics
#where_clause
{
fn to_sim_value_with_type(
&self,
ty: #mask_type_ident #type_generics,
) -> ::fayalite::sim::value::SimValue<#mask_type_ident #type_generics> {
::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self))
}
fn into_sim_value_with_type(
self,
ty: #mask_type_ident #type_generics,
) -> ::fayalite::sim::value::SimValue<#mask_type_ident #type_generics> {
::fayalite::sim::value::SimValue::from_value(ty, self)
}
}
#[automatically_derived]
impl #impl_generics ::fayalite::ty::Type for #target #type_generics
#where_clause
{
type BaseType = ::fayalite::bundle::Bundle;
type MaskType = #mask_type_ident #type_generics;
type SimValue = #sim_value_ident #type_generics;
type MatchVariant = #match_variant_ident #type_generics;
type MatchActiveScope = ();
type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope<
@ -733,6 +893,34 @@ impl ToTokens for ParsedBundle {
fn source_location() -> ::fayalite::source_location::SourceLocation {
::fayalite::source_location::SourceLocation::caller()
}
fn sim_value_from_bits(
&self,
bits: &::fayalite::bitvec::slice::BitSlice,
) -> <Self as ::fayalite::ty::Type>::SimValue {
#![allow(unused_mut, unused_variables)]
let mut v = ::fayalite::bundle::BundleSimValueFromBits::new(*self, bits);
#sim_value_ident {
#(#sim_value_from_bits_fields)*
}
}
fn sim_value_clone_from_bits(
&self,
value: &mut <Self as ::fayalite::ty::Type>::SimValue,
bits: &::fayalite::bitvec::slice::BitSlice,
) {
#![allow(unused_mut, unused_variables)]
let mut v = ::fayalite::bundle::BundleSimValueFromBits::new(*self, bits);
#(#sim_value_clone_from_bits_fields)*
}
fn sim_value_to_bits(
&self,
value: &<Self as ::fayalite::ty::Type>::SimValue,
bits: &mut ::fayalite::bitvec::slice::BitSlice,
) {
#![allow(unused_mut, unused_variables)]
let mut v = ::fayalite::bundle::BundleSimValueToBits::new(*self, bits);
#(#sim_value_to_bits_fields)*
}
}
#[automatically_derived]
impl #impl_generics ::fayalite::bundle::BundleType for #target #type_generics
@ -763,8 +951,144 @@ impl ToTokens for ParsedBundle {
::fayalite::intern::Interned::into_inner(::fayalite::intern::Intern::intern_sized(__retval))
}
}
#[automatically_derived]
impl #impl_generics ::fayalite::sim::value::ToSimValue for #sim_value_ident #type_generics
#where_clause
{
type Type = #target #type_generics;
fn to_sim_value(
&self,
) -> ::fayalite::sim::value::SimValue<
<Self as ::fayalite::sim::value::ToSimValue>::Type,
> {
let ty = #target {
#(#to_sim_value_fields)*
};
::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self))
}
fn into_sim_value(
self,
) -> ::fayalite::sim::value::SimValue<
<Self as ::fayalite::sim::value::ToSimValue>::Type,
> {
let ty = #target {
#(#to_sim_value_fields)*
};
::fayalite::sim::value::SimValue::from_value(ty, self)
}
}
#[automatically_derived]
impl #impl_generics ::fayalite::sim::value::ToSimValueWithType<#target #type_generics>
for #sim_value_ident #type_generics
#where_clause
{
fn to_sim_value_with_type(
&self,
ty: #target #type_generics,
) -> ::fayalite::sim::value::SimValue<#target #type_generics> {
::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self))
}
fn into_sim_value_with_type(
self,
ty: #target #type_generics,
) -> ::fayalite::sim::value::SimValue<#target #type_generics> {
::fayalite::sim::value::SimValue::from_value(ty, self)
}
}
}
.to_tokens(tokens);
if let Some((cmp_eq,)) = cmp_eq {
let mut expr_where_clause =
Generics::from(generics)
.where_clause
.unwrap_or_else(|| syn::WhereClause {
where_token: Token![where](span),
predicates: Punctuated::new(),
});
let mut sim_value_where_clause = expr_where_clause.clone();
let mut fields_sim_value_eq = vec![];
let mut fields_cmp_eq = vec![];
let mut fields_cmp_ne = vec![];
for field in fields.named() {
let field_ident = field.ident();
let field_ty = field.ty();
expr_where_clause
.predicates
.push(parse_quote_spanned! {cmp_eq.span=>
#field_ty: ::fayalite::expr::ops::ExprPartialEq<#field_ty>
});
sim_value_where_clause
.predicates
.push(parse_quote_spanned! {cmp_eq.span=>
#field_ty: ::fayalite::sim::value::SimValuePartialEq<#field_ty>
});
fields_sim_value_eq.push(quote_spanned! {span=>
::fayalite::sim::value::SimValuePartialEq::sim_value_eq(&__lhs.#field_ident, &__rhs.#field_ident)
});
fields_cmp_eq.push(quote_spanned! {span=>
::fayalite::expr::ops::ExprPartialEq::cmp_eq(__lhs.#field_ident, __rhs.#field_ident)
});
fields_cmp_ne.push(quote_spanned! {span=>
::fayalite::expr::ops::ExprPartialEq::cmp_ne(__lhs.#field_ident, __rhs.#field_ident)
});
}
let sim_value_eq_body;
let cmp_eq_body;
let cmp_ne_body;
if fields_len == 0 {
sim_value_eq_body = quote_spanned! {span=>
true
};
cmp_eq_body = quote_spanned! {span=>
::fayalite::expr::ToExpr::to_expr(&true)
};
cmp_ne_body = quote_spanned! {span=>
::fayalite::expr::ToExpr::to_expr(&false)
};
} else {
sim_value_eq_body = quote_spanned! {span=>
#(#fields_sim_value_eq)&&*
};
cmp_eq_body = quote_spanned! {span=>
#(#fields_cmp_eq)&*
};
cmp_ne_body = quote_spanned! {span=>
#(#fields_cmp_ne)|*
};
};
quote_spanned! {span=>
#[automatically_derived]
impl #impl_generics ::fayalite::expr::ops::ExprPartialEq<Self> for #target #type_generics
#expr_where_clause
{
fn cmp_eq(
__lhs: ::fayalite::expr::Expr<Self>,
__rhs: ::fayalite::expr::Expr<Self>,
) -> ::fayalite::expr::Expr<::fayalite::int::Bool> {
#cmp_eq_body
}
fn cmp_ne(
__lhs: ::fayalite::expr::Expr<Self>,
__rhs: ::fayalite::expr::Expr<Self>,
) -> ::fayalite::expr::Expr<::fayalite::int::Bool> {
#cmp_ne_body
}
}
#[automatically_derived]
impl #impl_generics ::fayalite::sim::value::SimValuePartialEq<Self> for #target #type_generics
#sim_value_where_clause
{
fn sim_value_eq(
__lhs: &::fayalite::sim::value::SimValue<Self>,
__rhs: &::fayalite::sim::value::SimValue<Self>,
) -> bool {
#sim_value_eq_body
}
}
}
.to_tokens(tokens);
}
if let (None, MaybeParsed::Parsed(generics)) = (no_static, &self.generics) {
let static_generics = generics.clone().for_static_type();
let (static_impl_generics, static_type_generics, static_where_clause) =
@ -800,6 +1124,14 @@ impl ToTokens for ParsedBundle {
}
}));
quote_spanned! {span=>
#[automatically_derived]
impl #static_impl_generics ::fayalite::__std::default::Default for #mask_type_ident #static_type_generics
#static_where_clause
{
fn default() -> Self {
<Self as ::fayalite::ty::StaticType>::TYPE
}
}
#[automatically_derived]
impl #static_impl_generics ::fayalite::ty::StaticType for #mask_type_ident #static_type_generics
#static_where_clause
@ -822,6 +1154,15 @@ impl ToTokens for ParsedBundle {
};
}
#[automatically_derived]
impl #static_impl_generics ::fayalite::__std::default::Default
for #target #static_type_generics
#static_where_clause
{
fn default() -> Self {
<Self as ::fayalite::ty::StaticType>::TYPE
}
}
#[automatically_derived]
impl #static_impl_generics ::fayalite::ty::StaticType for #target #static_type_generics
#static_where_clause
{

View file

@ -129,6 +129,9 @@ pub(crate) struct ParsedEnum {
pub(crate) brace_token: Brace,
pub(crate) variants: Punctuated<ParsedVariant, Token![,]>,
pub(crate) match_variant_ident: Ident,
pub(crate) sim_value_ident: Ident,
pub(crate) sim_builder_ident: Ident,
pub(crate) sim_builder_ty_field_ident: Ident,
}
impl ParsedEnum {
@ -155,7 +158,11 @@ impl ParsedEnum {
custom_bounds,
no_static: _,
no_runtime_generics: _,
cmp_eq,
} = options.body;
if let Some((cmp_eq,)) = cmp_eq {
errors.error(cmp_eq, "#[hdl(cmp_eq)] is not yet implemented for enums");
}
attrs.retain(|attr| {
if attr.path().is_ident("repr") {
errors.error(attr, "#[repr] is not supported on #[hdl] enums");
@ -186,6 +193,9 @@ impl ParsedEnum {
brace_token,
variants,
match_variant_ident: format_ident!("__{}__MatchVariant", ident),
sim_value_ident: format_ident!("__{}__SimValue", ident),
sim_builder_ident: format_ident!("__{}__SimBuilder", ident),
sim_builder_ty_field_ident: format_ident!("__ty", span = ident.span()),
ident,
})
}
@ -203,6 +213,9 @@ impl ToTokens for ParsedEnum {
brace_token,
variants,
match_variant_ident,
sim_value_ident,
sim_builder_ident,
sim_builder_ty_field_ident,
} = self;
let span = ident.span();
let ItemOptions {
@ -211,6 +224,7 @@ impl ToTokens for ParsedEnum {
custom_bounds: _,
no_static,
no_runtime_generics,
cmp_eq: _, // TODO: implement cmp_eq for enums
} = &options.body;
let target = get_target(target, ident);
let mut struct_attrs = attrs.clone();
@ -404,6 +418,133 @@ impl ToTokens for ParsedEnum {
)),
}
.to_tokens(tokens);
let mut struct_attrs = attrs.clone();
struct_attrs.push(parse_quote_spanned! {span=>
#[allow(dead_code, non_camel_case_types)]
});
ItemStruct {
attrs: struct_attrs,
vis: vis.clone(),
struct_token: Token![struct](enum_token.span),
ident: sim_builder_ident.clone(),
generics: generics.into(),
fields: FieldsNamed {
brace_token: *brace_token,
named: Punctuated::from_iter([Field {
attrs: vec![],
vis: Visibility::Inherited,
mutability: FieldMutability::None,
ident: Some(sim_builder_ty_field_ident.clone()),
colon_token: Some(Token![:](span)),
ty: parse_quote_spanned! {span=>
#target #type_generics
},
}]),
}
.into(),
semi_token: None,
}
.to_tokens(tokens);
let mut enum_attrs = attrs.clone();
enum_attrs.push(parse_quote_spanned! {span=>
#[::fayalite::__std::prelude::v1::derive(
::fayalite::__std::fmt::Debug,
::fayalite::__std::clone::Clone,
)]
});
enum_attrs.push(parse_quote_spanned! {span=>
#[allow(dead_code, non_camel_case_types)]
});
let sim_value_has_unknown_variant = !variants.len().is_power_of_two();
let sim_value_unknown_variant_name = sim_value_has_unknown_variant.then(|| {
let mut name = String::new();
let unknown = "Unknown";
loop {
let orig_len = name.len();
name.push_str(unknown);
if variants.iter().all(|v| v.ident != name) {
break Ident::new(&name, span);
}
name.truncate(orig_len);
name.push('_');
}
});
let sim_value_unknown_variant =
sim_value_unknown_variant_name
.as_ref()
.map(|unknown_variant_name| {
Pair::End(parse_quote_spanned! {span=>
#unknown_variant_name(::fayalite::enum_::UnknownVariantSimValue)
})
});
ItemEnum {
attrs: enum_attrs,
vis: vis.clone(),
enum_token: *enum_token,
ident: sim_value_ident.clone(),
generics: generics.into(),
brace_token: *brace_token,
variants: Punctuated::from_iter(
variants
.pairs()
.map_pair_value_ref(
|ParsedVariant {
attrs,
options: _,
ident,
field,
}| Variant {
attrs: attrs.clone(),
ident: ident.clone(),
fields: match field {
Some(ParsedVariantField {
paren_token,
attrs,
options: _,
ty,
comma_token,
}) => Fields::Unnamed(FieldsUnnamed {
paren_token: *paren_token,
unnamed: Punctuated::from_iter([
Pair::new(
Field {
attrs: attrs.clone(),
vis: Visibility::Inherited,
mutability: FieldMutability::None,
ident: None,
colon_token: None,
ty: parse_quote_spanned! {span=>
::fayalite::sim::value::SimValue<#ty>
},
},
Some(comma_token.unwrap_or(Token![,](ident.span()))),
),
Pair::new(
Field {
attrs: vec![],
vis: Visibility::Inherited,
mutability: FieldMutability::None,
ident: None,
colon_token: None,
ty: parse_quote_spanned! {span=>
::fayalite::enum_::EnumPaddingSimValue
},
},
None,
),
]),
}),
None => Fields::Unnamed(parse_quote_spanned! {span=>
(::fayalite::enum_::EnumPaddingSimValue)
}),
},
discriminant: None,
},
)
.chain(sim_value_unknown_variant),
),
}
.to_tokens(tokens);
let self_token = Token![self](span);
for (index, ParsedVariant { ident, field, .. }) in variants.iter().enumerate() {
if let Some(ParsedVariantField { ty, .. }) = field {
@ -430,6 +571,25 @@ impl ToTokens for ParsedEnum {
)
}
}
#[automatically_derived]
impl #impl_generics #sim_builder_ident #type_generics
#where_clause
{
#[allow(non_snake_case, dead_code)]
#vis fn #ident<__V: ::fayalite::sim::value::ToSimValueWithType<#ty>>(
#self_token,
v: __V,
) -> ::fayalite::sim::value::SimValue<#target #type_generics> {
let v = ::fayalite::sim::value::ToSimValueWithType::into_sim_value_with_type(
v,
#self_token.#sim_builder_ty_field_ident.#ident,
);
::fayalite::sim::value::SimValue::from_value(
#self_token.#sim_builder_ty_field_ident,
#sim_value_ident::#ident(v, ::fayalite::enum_::EnumPaddingSimValue::new()),
)
}
}
}
} else {
quote_spanned! {span=>
@ -448,6 +608,18 @@ impl ToTokens for ParsedEnum {
)
}
}
#[automatically_derived]
impl #impl_generics #sim_builder_ident #type_generics
#where_clause
{
#[allow(non_snake_case, dead_code)]
#vis fn #ident(#self_token) -> ::fayalite::sim::value::SimValue<#target #type_generics> {
::fayalite::sim::value::SimValue::from_value(
#self_token.#sim_builder_ty_field_ident,
#sim_value_ident::#ident(::fayalite::enum_::EnumPaddingSimValue::new()),
)
}
}
}
}
.to_tokens(tokens);
@ -529,6 +701,142 @@ impl ToTokens for ParsedEnum {
}
},
));
let sim_value_from_bits_unknown_match_arm = if let Some(sim_value_unknown_variant_name) =
&sim_value_unknown_variant_name
{
quote_spanned! {span=>
_ => #sim_value_ident::#sim_value_unknown_variant_name(v.unknown_variant_from_bits()),
}
} else {
quote_spanned! {span=>
_ => ::fayalite::__std::unreachable!(),
}
};
let sim_value_from_bits_match_arms = Vec::from_iter(
variants
.iter()
.enumerate()
.map(
|(
index,
ParsedVariant {
attrs: _,
options: _,
ident,
field,
},
)| {
if let Some(_) = field {
quote_spanned! {span=>
#index => {
let (field, padding) = v.variant_with_field_from_bits();
#sim_value_ident::#ident(field, padding)
}
}
} else {
quote_spanned! {span=>
#index => #sim_value_ident::#ident(
v.variant_no_field_from_bits(),
),
}
}
},
)
.chain([sim_value_from_bits_unknown_match_arm]),
);
let sim_value_clone_from_bits_unknown_match_arm =
if let Some(sim_value_unknown_variant_name) = &sim_value_unknown_variant_name {
quote_spanned! {span=>
_ => if let #sim_value_ident::#sim_value_unknown_variant_name(value) = value {
v.unknown_variant_clone_from_bits(value);
} else {
*value = #sim_value_ident::#sim_value_unknown_variant_name(
v.unknown_variant_from_bits(),
);
},
}
} else {
quote_spanned! {span=>
_ => ::fayalite::__std::unreachable!(),
}
};
let sim_value_clone_from_bits_match_arms = Vec::from_iter(
variants
.iter()
.enumerate()
.map(
|(
index,
ParsedVariant {
attrs: _,
options: _,
ident,
field,
},
)| {
if let Some(_) = field {
quote_spanned! {span=>
#index => if let #sim_value_ident::#ident(field, padding) = value {
v.variant_with_field_clone_from_bits(field, padding);
} else {
let (field, padding) = v.variant_with_field_from_bits();
*value = #sim_value_ident::#ident(field, padding);
},
}
} else {
quote_spanned! {span=>
#index => if let #sim_value_ident::#ident(padding) = value {
v.variant_no_field_clone_from_bits(padding);
} else {
*value = #sim_value_ident::#ident(
v.variant_no_field_from_bits(),
);
},
}
}
},
)
.chain([sim_value_clone_from_bits_unknown_match_arm]),
);
let sim_value_to_bits_match_arms = Vec::from_iter(
variants
.iter()
.enumerate()
.map(
|(
index,
ParsedVariant {
attrs: _,
options: _,
ident,
field,
},
)| {
if let Some(_) = field {
quote_spanned! {span=>
#sim_value_ident::#ident(field, padding) => {
v.variant_with_field_to_bits(#index, field, padding);
}
}
} else {
quote_spanned! {span=>
#sim_value_ident::#ident(padding) => {
v.variant_no_field_to_bits(#index, padding);
}
}
}
},
)
.chain(sim_value_unknown_variant_name.as_ref().map(
|sim_value_unknown_variant_name| {
quote_spanned! {span=>
#sim_value_ident::#sim_value_unknown_variant_name(value) => {
v.unknown_variant_to_bits(value);
}
}
},
)),
);
let variants_len = variants.len();
quote_spanned! {span=>
#[automatically_derived]
@ -537,6 +845,7 @@ impl ToTokens for ParsedEnum {
{
type BaseType = ::fayalite::enum_::Enum;
type MaskType = ::fayalite::int::Bool;
type SimValue = #sim_value_ident #type_generics;
type MatchVariant = #match_variant_ident #type_generics;
type MatchActiveScope = ::fayalite::module::Scope;
type MatchVariantAndInactiveScope = ::fayalite::enum_::EnumMatchVariantAndInactiveScope<Self>;
@ -569,11 +878,41 @@ impl ToTokens for ParsedEnum {
fn source_location() -> ::fayalite::source_location::SourceLocation {
::fayalite::source_location::SourceLocation::caller()
}
fn sim_value_from_bits(
&self,
bits: &::fayalite::bitvec::slice::BitSlice,
) -> <Self as ::fayalite::ty::Type>::SimValue {
let v = ::fayalite::enum_::EnumSimValueFromBits::new(*self, bits);
match v.discriminant() {
#(#sim_value_from_bits_match_arms)*
}
}
fn sim_value_clone_from_bits(
&self,
value: &mut <Self as ::fayalite::ty::Type>::SimValue,
bits: &::fayalite::bitvec::slice::BitSlice,
) {
let v = ::fayalite::enum_::EnumSimValueFromBits::new(*self, bits);
match v.discriminant() {
#(#sim_value_clone_from_bits_match_arms)*
}
}
fn sim_value_to_bits(
&self,
value: &<Self as ::fayalite::ty::Type>::SimValue,
bits: &mut ::fayalite::bitvec::slice::BitSlice,
) {
let v = ::fayalite::enum_::EnumSimValueToBits::new(*self, bits);
match value {
#(#sim_value_to_bits_match_arms)*
}
}
}
#[automatically_derived]
impl #impl_generics ::fayalite::enum_::EnumType for #target #type_generics
#where_clause
{
type SimBuilder = #sim_builder_ident #type_generics;
fn match_activate_scope(
v: <Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope,
) -> (<Self as ::fayalite::ty::Type>::MatchVariant, <Self as ::fayalite::ty::Type>::MatchActiveScope) {
@ -592,6 +931,33 @@ impl ToTokens for ParsedEnum {
][..])
}
}
#[automatically_derived]
impl #impl_generics ::fayalite::sim::value::ToSimValueWithType<#target #type_generics>
for #sim_value_ident #type_generics
#where_clause
{
fn to_sim_value_with_type(
&self,
ty: #target #type_generics,
) -> ::fayalite::sim::value::SimValue<#target #type_generics> {
::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self))
}
fn into_sim_value_with_type(
self,
ty: #target #type_generics,
) -> ::fayalite::sim::value::SimValue<#target #type_generics> {
::fayalite::sim::value::SimValue::from_value(ty, self)
}
}
#[automatically_derived]
impl #impl_generics ::fayalite::__std::convert::From<#target #type_generics>
for #sim_builder_ident #type_generics
#where_clause
{
fn from(#sim_builder_ty_field_ident: #target #type_generics) -> Self {
Self { #sim_builder_ty_field_ident }
}
}
}
.to_tokens(tokens);
if let (None, MaybeParsed::Parsed(generics)) = (no_static, &self.generics) {
@ -629,6 +995,15 @@ impl ToTokens for ParsedEnum {
}
}));
quote_spanned! {span=>
#[automatically_derived]
impl #static_impl_generics ::fayalite::__std::default::Default
for #target #static_type_generics
#static_where_clause
{
fn default() -> Self {
<Self as ::fayalite::ty::StaticType>::TYPE
}
}
#[automatically_derived]
impl #static_impl_generics ::fayalite::ty::StaticType
for #target #static_type_generics
@ -647,6 +1022,34 @@ impl ToTokens for ParsedEnum {
const MASK_TYPE_PROPERTIES: ::fayalite::ty::TypeProperties =
<::fayalite::int::Bool as ::fayalite::ty::StaticType>::TYPE_PROPERTIES;
}
#[automatically_derived]
impl #static_impl_generics ::fayalite::sim::value::ToSimValue
for #sim_value_ident #static_type_generics
#static_where_clause
{
type Type = #target #static_type_generics;
fn to_sim_value(
&self,
) -> ::fayalite::sim::value::SimValue<
<Self as ::fayalite::sim::value::ToSimValue>::Type,
> {
::fayalite::sim::value::SimValue::from_value(
::fayalite::ty::StaticType::TYPE,
::fayalite::__std::clone::Clone::clone(self),
)
}
fn into_sim_value(
self,
) -> ::fayalite::sim::value::SimValue<
<Self as ::fayalite::sim::value::ToSimValue>::Type,
> {
::fayalite::sim::value::SimValue::from_value(
::fayalite::ty::StaticType::TYPE,
self,
)
}
}
}
.to_tokens(tokens);
}

View file

@ -49,10 +49,14 @@ impl ParsedTypeAlias {
custom_bounds,
no_static,
no_runtime_generics: _,
cmp_eq,
} = options.body;
if let Some((no_static,)) = no_static {
errors.error(no_static, "no_static is not valid on type aliases");
}
if let Some((cmp_eq,)) = cmp_eq {
errors.error(cmp_eq, "cmp_eq is not valid on type aliases");
}
let generics = if custom_bounds.is_some() {
MaybeParsed::Unrecognized(generics)
} else if let Some(generics) = errors.ok(ParsedGenerics::parse(&mut generics)) {
@ -95,6 +99,7 @@ impl ToTokens for ParsedTypeAlias {
custom_bounds: _,
no_static: _,
no_runtime_generics,
cmp_eq: _,
} = &options.body;
let target = get_target(target, ident);
let mut type_attrs = attrs.clone();

View file

@ -26,6 +26,7 @@ crate::options! {
CustomBounds(custom_bounds),
NoStatic(no_static),
NoRuntimeGenerics(no_runtime_generics),
CmpEq(cmp_eq),
}
}
@ -2069,11 +2070,16 @@ macro_rules! impl_bounds {
$(
$Variant:ident,
)*
$(
#[unknown]
$Unknown:ident,
)?
}
) => {
#[derive(Clone, Debug)]
$vis enum $enum_type {
$($Variant(known_items::$Variant),)*
$($Unknown(syn::TypeParamBound),)?
}
$(impl From<known_items::$Variant> for $enum_type {
@ -2086,28 +2092,54 @@ macro_rules! impl_bounds {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
$(Self::$Variant(v) => v.to_tokens(tokens),)*
$(Self::$Unknown(v) => v.to_tokens(tokens),)?
}
}
}
impl $enum_type {
$vis fn parse_path(path: Path) -> Result<Self, Path> {
#![allow(unreachable_code)]
$(let path = match known_items::$Variant::parse_path(path) {
Ok(v) => return Ok(Self::$Variant(v)),
Err(path) => path,
};)*
$(return Ok(Self::$Unknown(syn::TraitBound {
paren_token: None,
modifier: syn::TraitBoundModifier::None,
lifetimes: None,
path,
}.into()));)?
Err(path)
}
$vis fn parse_type_param_bound(mut type_param_bound: syn::TypeParamBound) -> Result<Self, syn::TypeParamBound> {
#![allow(unreachable_code)]
if let syn::TypeParamBound::Trait(mut trait_bound) = type_param_bound {
if let syn::TraitBound {
paren_token: _,
modifier: syn::TraitBoundModifier::None,
lifetimes: None,
path: _,
} = trait_bound {
match Self::parse_path(trait_bound.path) {
Ok(retval) => return Ok(retval),
Err(path) => trait_bound.path = path,
}
}
type_param_bound = trait_bound.into();
}
$(return Ok(Self::$Unknown(type_param_bound));)?
Err(type_param_bound)
}
}
impl Parse for $enum_type {
fn parse(input: ParseStream) -> syn::Result<Self> {
Self::parse_path(Path::parse_mod_style(input)?).map_err(|path| {
syn::Error::new_spanned(
path,
Self::parse_type_param_bound(input.parse()?)
.map_err(|type_param_bound| syn::Error::new_spanned(
type_param_bound,
format_args!("expected one of: {}", [$(stringify!($Variant)),*].join(", ")),
)
})
))
}
}
@ -2115,6 +2147,7 @@ macro_rules! impl_bounds {
#[allow(non_snake_case)]
$vis struct $struct_type {
$($vis $Variant: Option<known_items::$Variant>,)*
$($vis $Unknown: Vec<syn::TypeParamBound>,)?
}
impl ToTokens for $struct_type {
@ -2126,42 +2159,63 @@ macro_rules! impl_bounds {
separator = Some(<Token![+]>::default());
v.to_tokens(tokens);
})*
$(for v in &self.$Unknown {
separator.to_tokens(tokens);
separator = Some(<Token![+]>::default());
v.to_tokens(tokens);
})*
}
}
const _: () = {
#[derive(Clone, Debug)]
$vis struct Iter($vis $struct_type);
#[allow(non_snake_case)]
$vis struct Iter {
$($Variant: Option<known_items::$Variant>,)*
$($Unknown: std::vec::IntoIter<syn::TypeParamBound>,)?
}
impl IntoIterator for $struct_type {
type Item = $enum_type;
type IntoIter = Iter;
fn into_iter(self) -> Self::IntoIter {
Iter(self)
Iter {
$($Variant: self.$Variant,)*
$($Unknown: self.$Unknown.into_iter(),)?
}
}
}
impl Iterator for Iter {
type Item = $enum_type;
fn next(&mut self) -> Option<Self::Item> {
$(
if let Some(value) = self.0.$Variant.take() {
if let Some(value) = self.$Variant.take() {
return Some($enum_type::$Variant(value));
}
)*
$(
if let Some(value) = self.$Unknown.next() {
return Some($enum_type::$Unknown(value));
}
)?
None
}
#[allow(unused_mut, unused_variables)]
fn fold<B, F: FnMut(B, Self::Item) -> B>(mut self, mut init: B, mut f: F) -> B {
$(
if let Some(value) = self.0.$Variant.take() {
if let Some(value) = self.$Variant.take() {
init = f(init, $enum_type::$Variant(value));
}
)*
$(
if let Some(value) = self.$Unknown.next() {
init = f(init, $enum_type::$Unknown(value));
}
)?
init
}
}
@ -2173,6 +2227,9 @@ macro_rules! impl_bounds {
$($enum_type::$Variant(v) => {
self.$Variant = Some(v);
})*
$($enum_type::$Unknown(v) => {
self.$Unknown.push(v);
})?
});
}
}
@ -2191,6 +2248,7 @@ macro_rules! impl_bounds {
$(if let Some(v) = v.$Variant {
self.$Variant = Some(v);
})*
$(self.$Unknown.extend(v.$Unknown);)*
});
}
}
@ -2244,6 +2302,8 @@ impl_bounds! {
Size,
StaticType,
Type,
#[unknown]
Unknown,
}
}
@ -2257,6 +2317,8 @@ impl_bounds! {
ResetType,
StaticType,
Type,
#[unknown]
Unknown,
}
}
@ -2270,6 +2332,7 @@ impl From<ParsedTypeBound> for ParsedBound {
ParsedTypeBound::ResetType(v) => ParsedBound::ResetType(v),
ParsedTypeBound::StaticType(v) => ParsedBound::StaticType(v),
ParsedTypeBound::Type(v) => ParsedBound::Type(v),
ParsedTypeBound::Unknown(v) => ParsedBound::Unknown(v),
}
}
}
@ -2284,6 +2347,7 @@ impl From<ParsedTypeBounds> for ParsedBounds {
ResetType,
StaticType,
Type,
Unknown,
} = value;
Self {
BoolOrIntType,
@ -2295,6 +2359,7 @@ impl From<ParsedTypeBounds> for ParsedBounds {
Size: None,
StaticType,
Type,
Unknown,
}
}
}
@ -2330,6 +2395,7 @@ impl ParsedTypeBound {
ParsedTypeBound::Type(known_items::Type(span)),
]),
Self::Type(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::from(v)]),
Self::Unknown(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::Unknown(v)]),
}
}
}
@ -2364,6 +2430,7 @@ impl From<ParsedSizeTypeBounds> for ParsedBounds {
Size,
StaticType: None,
Type: None,
Unknown: vec![],
}
}
}
@ -2391,6 +2458,7 @@ impl ParsedBounds {
fn categorize(self, errors: &mut Errors, span: Span) -> ParsedBoundsCategory {
let mut type_bounds = None;
let mut size_type_bounds = None;
let mut unknown_bounds = vec![];
self.into_iter().for_each(|bound| match bound.categorize() {
ParsedBoundCategory::Type(bound) => {
type_bounds
@ -2402,15 +2470,37 @@ impl ParsedBounds {
.get_or_insert_with(ParsedSizeTypeBounds::default)
.extend([bound]);
}
ParsedBoundCategory::Unknown(bound) => unknown_bounds.push(bound),
});
match (type_bounds, size_type_bounds) {
(None, None) => ParsedBoundsCategory::Type(ParsedTypeBounds {
match (type_bounds, size_type_bounds, unknown_bounds.is_empty()) {
(None, None, true) => ParsedBoundsCategory::Type(ParsedTypeBounds {
Type: Some(known_items::Type(span)),
..Default::default()
}),
(None, Some(bounds)) => ParsedBoundsCategory::SizeType(bounds),
(Some(bounds), None) => ParsedBoundsCategory::Type(bounds),
(Some(type_bounds), Some(size_type_bounds)) => {
(None, None, false) => {
errors.error(
unknown_bounds.remove(0),
"unknown bounds: must use at least one known bound (such as `Type`) with any unknown bounds",
);
ParsedBoundsCategory::Type(ParsedTypeBounds {
Unknown: unknown_bounds,
..Default::default()
})
}
(None, Some(bounds), true) => ParsedBoundsCategory::SizeType(bounds),
(None, Some(bounds), false) => {
// TODO: implement
errors.error(
unknown_bounds.remove(0),
"unknown bounds with `Size` bounds are not implemented",
);
ParsedBoundsCategory::SizeType(bounds)
}
(Some(bounds), None, _) => ParsedBoundsCategory::Type(ParsedTypeBounds {
Unknown: unknown_bounds,
..bounds
}),
(Some(type_bounds), Some(size_type_bounds), _) => {
errors.error(
size_type_bounds
.Size
@ -2427,6 +2517,7 @@ impl ParsedBounds {
pub(crate) enum ParsedBoundCategory {
Type(ParsedTypeBound),
SizeType(ParsedSizeTypeBound),
Unknown(syn::TypeParamBound),
}
impl ParsedBound {
@ -2441,12 +2532,14 @@ impl ParsedBound {
Self::Size(v) => ParsedBoundCategory::SizeType(ParsedSizeTypeBound::Size(v)),
Self::StaticType(v) => ParsedBoundCategory::Type(ParsedTypeBound::StaticType(v)),
Self::Type(v) => ParsedBoundCategory::Type(ParsedTypeBound::Type(v)),
Self::Unknown(v) => ParsedBoundCategory::Unknown(v),
}
}
fn implied_bounds(self) -> ParsedBounds {
match self.categorize() {
ParsedBoundCategory::Type(v) => v.implied_bounds().into(),
ParsedBoundCategory::SizeType(v) => v.implied_bounds().into(),
ParsedBoundCategory::Unknown(v) => ParsedBounds::from_iter([ParsedBound::Unknown(v)]),
}
}
}
@ -3325,7 +3418,7 @@ impl ParsedGenerics {
| ParsedTypeBound::EnumType(_)
| ParsedTypeBound::IntType(_)
| ParsedTypeBound::ResetType(_) => {
errors.error(bound, "bound on mask type not implemented");
errors.error(bound, "bounds on mask types are not implemented");
}
ParsedTypeBound::StaticType(bound) => {
if bounds.StaticType.is_none() {
@ -3337,6 +3430,12 @@ impl ParsedGenerics {
}
}
ParsedTypeBound::Type(_) => {}
ParsedTypeBound::Unknown(_) => {
errors.error(
bound,
"unknown bounds on mask types are not implemented",
);
}
}
}
bounds.add_implied_bounds();

View file

@ -72,13 +72,14 @@ mod kw {
custom_keyword!(cfg);
custom_keyword!(cfg_attr);
custom_keyword!(clock_domain);
custom_keyword!(cmp_eq);
custom_keyword!(connect_inexact);
custom_keyword!(custom_bounds);
custom_keyword!(flip);
custom_keyword!(hdl);
custom_keyword!(hdl_module);
custom_keyword!(input);
custom_keyword!(incomplete_wire);
custom_keyword!(input);
custom_keyword!(instance);
custom_keyword!(m);
custom_keyword!(memory);
@ -92,6 +93,7 @@ mod kw {
custom_keyword!(output);
custom_keyword!(reg_builder);
custom_keyword!(reset);
custom_keyword!(sim);
custom_keyword!(skip);
custom_keyword!(target);
custom_keyword!(wire);

View file

@ -377,7 +377,7 @@ impl ModuleFn {
module_kind,
vis,
sig,
block,
mut block,
struct_generics,
the_struct,
} = match self.0 {
@ -439,6 +439,12 @@ impl ModuleFn {
body_sig
.inputs
.insert(0, parse_quote! { m: &::fayalite::module::ModuleBuilder });
block.stmts.insert(
0,
parse_quote! {
let _ = m;
},
);
let body_fn = ItemFn {
attrs: vec![],
vis: Visibility::Inherited,

View file

@ -16,7 +16,7 @@ use std::{borrow::Borrow, convert::Infallible};
use syn::{
fold::{fold_expr, fold_expr_lit, fold_expr_unary, fold_local, fold_stmt, Fold},
parenthesized,
parse::{Nothing, Parse, ParseStream},
parse::{Parse, ParseStream},
parse_quote, parse_quote_spanned,
spanned::Spanned,
token::Paren,
@ -27,6 +27,13 @@ use syn::{
mod expand_aggregate_literals;
mod expand_match;
options! {
#[options = ExprOptions]
pub(crate) enum ExprOption {
Sim(sim),
}
}
options! {
pub(crate) enum LetFnKind {
Input(input),
@ -952,7 +959,7 @@ with_debug_clone_and_fold! {
#[allow(dead_code)]
pub(crate) struct HdlLet<Kind = HdlLetKind> {
pub(crate) attrs: Vec<Attribute>,
pub(crate) hdl_attr: HdlAttr<Nothing, kw::hdl>,
pub(crate) hdl_attr: HdlAttr<syn::parse::Nothing, kw::hdl>,
pub(crate) let_token: Token![let],
pub(crate) mut_token: Option<Token![mut]>,
pub(crate) name: Ident,
@ -1173,7 +1180,7 @@ impl Visitor<'_> {
Some(_) => {}
}
}
fn process_hdl_if(&mut self, hdl_attr: HdlAttr<Nothing, kw::hdl>, expr_if: ExprIf) -> Expr {
fn process_hdl_if(&mut self, hdl_attr: HdlAttr<ExprOptions, kw::hdl>, expr_if: ExprIf) -> Expr {
let ExprIf {
attrs,
if_token,
@ -1181,10 +1188,10 @@ impl Visitor<'_> {
then_branch,
else_branch,
} = expr_if;
self.require_normal_module_or_fn(if_token);
let else_expr = else_branch.unzip().1.map(|else_expr| match *else_expr {
Expr::If(expr_if) => self.process_hdl_if(hdl_attr.clone(), expr_if),
expr => expr,
let (else_token, else_expr) = else_branch.unzip();
let else_expr = else_expr.map(|else_expr| match *else_expr {
Expr::If(expr_if) => Box::new(self.process_hdl_if(hdl_attr.clone(), expr_if)),
_ => else_expr,
});
if let Expr::Let(ExprLet {
attrs: let_attrs,
@ -1206,7 +1213,19 @@ impl Visitor<'_> {
},
);
}
if let Some(else_expr) = else_expr {
let ExprOptions { sim } = hdl_attr.body;
if sim.is_some() {
ExprIf {
attrs,
if_token,
cond: parse_quote_spanned! {if_token.span=>
*::fayalite::sim::value::SimValue::<::fayalite::int::Bool>::value(&::fayalite::sim::value::ToSimValue::into_sim_value(#cond))
},
then_branch,
else_branch: else_token.zip(else_expr),
}
.into()
} else if let Some(else_expr) = else_expr {
parse_quote_spanned! {if_token.span=>
#(#attrs)*
{
@ -1668,6 +1687,8 @@ impl Fold for Visitor<'_> {
Repeat => process_hdl_repeat,
Struct => process_hdl_struct,
Tuple => process_hdl_tuple,
MethodCall => process_hdl_method_call,
Call => process_hdl_call,
}
}
}
@ -1675,7 +1696,7 @@ impl Fold for Visitor<'_> {
fn fold_local(&mut self, mut let_stmt: Local) -> Local {
match self
.errors
.ok(HdlAttr::<Nothing, kw::hdl>::parse_and_leave_attr(
.ok(HdlAttr::<ExprOptions, kw::hdl>::parse_and_leave_attr(
&let_stmt.attrs,
)) {
None => return empty_let(),
@ -1694,10 +1715,11 @@ impl Fold for Visitor<'_> {
subpat: None,
}) = pat
else {
let hdl_attr = HdlAttr::<Nothing, kw::hdl>::parse_and_take_attr(&mut let_stmt.attrs)
.ok()
.flatten()
.expect("already checked above");
let hdl_attr =
HdlAttr::<ExprOptions, kw::hdl>::parse_and_take_attr(&mut let_stmt.attrs)
.ok()
.flatten()
.expect("already checked above");
let let_stmt = fold_local(self, let_stmt);
return self.process_hdl_let_pat(hdl_attr, let_stmt);
};

View file

@ -1,45 +1,103 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{kw, module::transform_body::Visitor, HdlAttr};
use crate::{
kw,
module::transform_body::{
expand_match::{parse_enum_path, EnumPath},
ExprOptions, Visitor,
},
HdlAttr,
};
use quote::{format_ident, quote_spanned};
use std::mem;
use syn::{
parse::Nothing, parse_quote, parse_quote_spanned, spanned::Spanned, Expr, ExprArray, ExprPath,
ExprRepeat, ExprStruct, ExprTuple, FieldValue, TypePath,
parse_quote_spanned, punctuated::Punctuated, spanned::Spanned, token::Paren, Expr, ExprArray,
ExprCall, ExprGroup, ExprMethodCall, ExprParen, ExprPath, ExprRepeat, ExprStruct, ExprTuple,
FieldValue, Token, TypePath,
};
impl Visitor<'_> {
pub(crate) fn process_hdl_array(
&mut self,
hdl_attr: HdlAttr<Nothing, kw::hdl>,
hdl_attr: HdlAttr<ExprOptions, kw::hdl>,
mut expr_array: ExprArray,
) -> Expr {
self.require_normal_module_or_fn(hdl_attr);
for elem in &mut expr_array.elems {
*elem = parse_quote_spanned! {elem.span()=>
::fayalite::expr::ToExpr::to_expr(&(#elem))
};
let ExprOptions { sim } = hdl_attr.body;
let span = hdl_attr.kw.span;
if sim.is_some() {
for elem in &mut expr_array.elems {
*elem = parse_quote_spanned! {elem.span()=>
::fayalite::sim::value::ToSimValue::to_sim_value(&(#elem))
};
}
parse_quote_spanned! {span=>
::fayalite::sim::value::ToSimValue::into_sim_value(#expr_array)
}
} else {
for elem in &mut expr_array.elems {
*elem = parse_quote_spanned! {elem.span()=>
::fayalite::expr::ToExpr::to_expr(&(#elem))
};
}
parse_quote_spanned! {span=>
::fayalite::expr::ToExpr::to_expr(&#expr_array)
}
}
parse_quote! {::fayalite::expr::ToExpr::to_expr(&#expr_array)}
}
pub(crate) fn process_hdl_repeat(
&mut self,
hdl_attr: HdlAttr<Nothing, kw::hdl>,
hdl_attr: HdlAttr<ExprOptions, kw::hdl>,
mut expr_repeat: ExprRepeat,
) -> Expr {
self.require_normal_module_or_fn(hdl_attr);
let repeated_value = &expr_repeat.expr;
*expr_repeat.expr = parse_quote_spanned! {repeated_value.span()=>
::fayalite::expr::ToExpr::to_expr(&(#repeated_value))
};
parse_quote! {::fayalite::expr::ToExpr::to_expr(&#expr_repeat)}
let ExprOptions { sim } = hdl_attr.body;
let span = hdl_attr.kw.span;
if sim.is_some() {
*expr_repeat.expr = parse_quote_spanned! {repeated_value.span()=>
::fayalite::sim::value::ToSimValue::to_sim_value(&(#repeated_value))
};
parse_quote_spanned! {span=>
::fayalite::sim::value::ToSimValue::into_sim_value(#expr_repeat)
}
} else {
*expr_repeat.expr = parse_quote_spanned! {repeated_value.span()=>
::fayalite::expr::ToExpr::to_expr(&(#repeated_value))
};
parse_quote_spanned! {span=>
::fayalite::expr::ToExpr::to_expr(&#expr_repeat)
}
}
}
pub(crate) fn process_hdl_struct(
&mut self,
hdl_attr: HdlAttr<Nothing, kw::hdl>,
expr_struct: ExprStruct,
hdl_attr: HdlAttr<ExprOptions, kw::hdl>,
mut expr_struct: ExprStruct,
) -> Expr {
self.require_normal_module_or_fn(&hdl_attr);
let name_span = expr_struct.path.segments.last().unwrap().ident.span();
let ExprOptions { sim } = hdl_attr.body;
if sim.is_some() {
let ty_path = TypePath {
qself: expr_struct.qself.take(),
path: expr_struct.path,
};
expr_struct.path = parse_quote_spanned! {name_span=>
__SimValue::<#ty_path>
};
for field in &mut expr_struct.fields {
let expr = &field.expr;
field.expr = parse_quote_spanned! {field.member.span()=>
::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr))
};
}
return parse_quote_spanned! {name_span=>
{
type __SimValue<T> = <T as ::fayalite::ty::Type>::SimValue;
let value: ::fayalite::sim::value::SimValue<#ty_path> = ::fayalite::sim::value::ToSimValue::into_sim_value(#expr_struct);
value
}
};
}
let builder_ident = format_ident!("__builder", span = name_span);
let empty_builder = if expr_struct.qself.is_some()
|| expr_struct
@ -91,12 +149,126 @@ impl Visitor<'_> {
}
pub(crate) fn process_hdl_tuple(
&mut self,
hdl_attr: HdlAttr<Nothing, kw::hdl>,
expr_tuple: ExprTuple,
hdl_attr: HdlAttr<ExprOptions, kw::hdl>,
mut expr_tuple: ExprTuple,
) -> Expr {
self.require_normal_module_or_fn(hdl_attr);
parse_quote_spanned! {expr_tuple.span()=>
::fayalite::expr::ToExpr::to_expr(&#expr_tuple)
let ExprOptions { sim } = hdl_attr.body;
if sim.is_some() {
for element in &mut expr_tuple.elems {
*element = parse_quote_spanned! {element.span()=>
&(#element)
};
}
parse_quote_spanned! {expr_tuple.span()=>
::fayalite::sim::value::ToSimValue::into_sim_value(#expr_tuple)
}
} else {
parse_quote_spanned! {expr_tuple.span()=>
::fayalite::expr::ToExpr::to_expr(&#expr_tuple)
}
}
}
pub(crate) fn process_hdl_call(
&mut self,
hdl_attr: HdlAttr<ExprOptions, kw::hdl>,
mut expr_call: ExprCall,
) -> Expr {
let span = hdl_attr.kw.span;
let mut func = &mut *expr_call.func;
let EnumPath {
variant_path: _,
enum_path,
variant_name,
} = loop {
match func {
Expr::Group(ExprGroup { expr, .. }) | Expr::Paren(ExprParen { expr, .. }) => {
func = &mut **expr;
}
Expr::Path(_) => {
let Expr::Path(ExprPath { attrs, qself, path }) =
mem::replace(func, Expr::PLACEHOLDER)
else {
unreachable!();
};
match parse_enum_path(TypePath { qself, path }) {
Ok(path) => break path,
Err(path) => {
self.errors.error(&path, "unsupported enum variant path");
let TypePath { qself, path } = path;
*func = ExprPath { attrs, qself, path }.into();
return expr_call.into();
}
}
}
_ => {
self.errors.error(
&expr_call.func,
"#[hdl] function call -- function must be a possibly-parenthesized path",
);
return expr_call.into();
}
}
};
self.process_hdl_method_call(
hdl_attr,
ExprMethodCall {
attrs: expr_call.attrs,
receiver: parse_quote_spanned! {span=>
<#enum_path as ::fayalite::ty::StaticType>::TYPE
},
dot_token: Token![.](span),
method: variant_name,
turbofish: None,
paren_token: expr_call.paren_token,
args: expr_call.args,
},
)
}
pub(crate) fn process_hdl_method_call(
&mut self,
hdl_attr: HdlAttr<ExprOptions, kw::hdl>,
mut expr_method_call: ExprMethodCall,
) -> Expr {
let ExprOptions { sim } = hdl_attr.body;
let span = hdl_attr.kw.span;
// remove any number of groups and up to one paren
let mut receiver = &mut *expr_method_call.receiver;
let mut has_group = false;
let receiver = loop {
match receiver {
Expr::Group(ExprGroup { expr, .. }) => {
has_group = true;
receiver = expr;
}
Expr::Paren(ExprParen { expr, .. }) => break &mut **expr,
receiver @ Expr::Path(_) => break receiver,
_ => {
if !has_group {
self.errors.error(
&expr_method_call.receiver,
"#[hdl] on a method call needs parenthesized receiver",
);
}
break &mut *expr_method_call.receiver;
}
}
};
let func = if sim.is_some() {
parse_quote_spanned! {span=>
::fayalite::enum_::enum_type_to_sim_builder
}
} else {
parse_quote_spanned! {span=>
::fayalite::enum_::assert_is_enum_type
}
};
*expr_method_call.receiver = ExprCall {
attrs: vec![],
func,
paren_token: Paren(span),
args: Punctuated::from_iter([mem::replace(receiver, Expr::PLACEHOLDER)]),
}
.into();
expr_method_call.into()
}
}

View file

@ -3,7 +3,9 @@
use crate::{
fold::{impl_fold, DoFold},
kw,
module::transform_body::{empty_let, with_debug_clone_and_fold, wrap_ty_with_expr, Visitor},
module::transform_body::{
empty_let, with_debug_clone_and_fold, wrap_ty_with_expr, ExprOptions, Visitor,
},
Errors, HdlAttr, PairsIterExt,
};
use proc_macro2::{Span, TokenStream};
@ -11,7 +13,6 @@ use quote::{format_ident, quote_spanned, ToTokens, TokenStreamExt};
use std::collections::BTreeSet;
use syn::{
fold::{fold_arm, fold_expr_match, fold_local, fold_pat, Fold},
parse::Nothing,
parse_quote_spanned,
punctuated::Punctuated,
spanned::Spanned,
@ -82,7 +83,14 @@ visit_trait! {
}
}
fn visit_match_pat_enum_variant(state: _, v: &MatchPatEnumVariant) {
let MatchPatEnumVariant {match_span:_, variant_path: _, enum_path: _, variant_name: _, field } = v;
let MatchPatEnumVariant {
match_span:_,
sim:_,
variant_path: _,
enum_path: _,
variant_name: _,
field,
} = v;
if let Some((_, v)) = field {
state.visit_match_pat_simple(v);
}
@ -292,6 +300,7 @@ impl ToTokens for MatchPatTuple {
with_debug_clone_and_fold! {
struct MatchPatEnumVariant<> {
match_span: Span,
sim: Option<(kw::sim,)>,
variant_path: Path,
enum_path: Path,
variant_name: Ident,
@ -303,6 +312,7 @@ impl ToTokens for MatchPatEnumVariant {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self {
match_span,
sim,
variant_path: _,
enum_path,
variant_name,
@ -312,7 +322,28 @@ impl ToTokens for MatchPatEnumVariant {
__MatchTy::<#enum_path>::#variant_name
}
.to_tokens(tokens);
if let Some((paren_token, field)) = field {
if sim.is_some() {
if let Some((paren_token, field)) = field {
paren_token.surround(tokens, |tokens| {
field.to_tokens(tokens);
match field {
MatchPatSimple::Paren(_)
| MatchPatSimple::Or(_)
| MatchPatSimple::Binding(_)
| MatchPatSimple::Wild(_) => quote_spanned! {*match_span=>
, _
}
.to_tokens(tokens),
MatchPatSimple::Rest(_) => {}
}
});
} else {
quote_spanned! {*match_span=>
(_)
}
.to_tokens(tokens);
}
} else if let Some((paren_token, field)) = field {
paren_token.surround(tokens, |tokens| field.to_tokens(tokens));
}
}
@ -349,13 +380,13 @@ impl ToTokens for MatchPatSimple {
}
}
struct EnumPath {
variant_path: Path,
enum_path: Path,
variant_name: Ident,
pub(crate) struct EnumPath {
pub(crate) variant_path: Path,
pub(crate) enum_path: Path,
pub(crate) variant_name: Ident,
}
fn parse_enum_path(variant_path: TypePath) -> Result<EnumPath, TypePath> {
pub(crate) fn parse_enum_path(variant_path: TypePath) -> Result<EnumPath, TypePath> {
let TypePath {
qself: None,
path: variant_path,
@ -447,6 +478,7 @@ trait ParseMatchPat: Sized {
state,
MatchPatEnumVariant {
match_span: state.match_span,
sim: state.sim,
variant_path,
enum_path,
variant_name,
@ -493,6 +525,7 @@ trait ParseMatchPat: Sized {
state,
MatchPatEnumVariant {
match_span: state.match_span,
sim: state.sim,
variant_path,
enum_path,
variant_name,
@ -577,6 +610,7 @@ trait ParseMatchPat: Sized {
state,
MatchPatEnumVariant {
match_span: state.match_span,
sim: state.sim,
variant_path,
enum_path,
variant_name,
@ -939,6 +973,7 @@ impl Fold for RewriteAsCheckMatch {
}
struct HdlMatchParseState<'a> {
sim: Option<(kw::sim,)>,
match_span: Span,
errors: &'a mut Errors,
}
@ -981,10 +1016,11 @@ impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> {
impl Visitor<'_> {
pub(crate) fn process_hdl_let_pat(
&mut self,
_hdl_attr: HdlAttr<Nothing, kw::hdl>,
hdl_attr: HdlAttr<ExprOptions, kw::hdl>,
mut let_stmt: Local,
) -> Local {
let span = let_stmt.let_token.span();
let ExprOptions { sim } = hdl_attr.body;
if let Pat::Type(pat) = &mut let_stmt.pat {
*pat.ty = wrap_ty_with_expr((*pat.ty).clone());
}
@ -996,7 +1032,6 @@ impl Visitor<'_> {
init,
semi_token,
} = let_stmt;
self.require_normal_module_or_fn(let_token);
let Some(syn::LocalInit {
eq_token,
expr,
@ -1015,6 +1050,7 @@ impl Visitor<'_> {
}
let Ok(pat) = MatchPat::parse(
&mut HdlMatchParseState {
sim,
match_span: span,
errors: &mut self.errors,
},
@ -1031,29 +1067,47 @@ impl Visitor<'_> {
errors: _,
bindings,
} = state;
let retval = parse_quote_spanned! {span=>
let (#(#bindings,)* __scope,) = {
type __MatchTy<T> = <T as ::fayalite::ty::Type>::MatchVariant;
let __match_expr = ::fayalite::expr::ToExpr::to_expr(&(#expr));
::fayalite::expr::check_match_expr(__match_expr, |__match_value, __infallible| {
#[allow(unused_variables)]
#check_let_stmt
match __infallible {}
});
let mut __match_iter = ::fayalite::module::match_(__match_expr);
let ::fayalite::__std::option::Option::Some(__match_variant) = ::fayalite::__std::iter::Iterator::next(&mut __match_iter) else {
::fayalite::__std::unreachable!("#[hdl] let with uninhabited type");
let retval = if sim.is_some() {
parse_quote_spanned! {span=>
let (#(#bindings,)*) = {
type __MatchTy<T> = <T as ::fayalite::ty::Type>::SimValue;
let __match_value = ::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr));
#let_token #pat #eq_token ::fayalite::sim::value::SimValue::into_value(__match_value) #semi_token
(#(#bindings,)*)
};
let ::fayalite::__std::option::Option::None = ::fayalite::__std::iter::Iterator::next(&mut __match_iter) else {
::fayalite::__std::unreachable!("#[hdl] let with refutable pattern");
};
let (__match_variant, __scope) =
::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope(
__match_variant,
}
} else {
parse_quote_spanned! {span=>
let (#(#bindings,)* __scope,) = {
type __MatchTy<T> = <T as ::fayalite::ty::Type>::MatchVariant;
let __match_expr = ::fayalite::expr::ToExpr::to_expr(&(#expr));
::fayalite::expr::check_match_expr(
__match_expr,
|__match_value, __infallible| {
#[allow(unused_variables)]
#check_let_stmt
match __infallible {}
},
);
#let_token #pat #eq_token __match_variant #semi_token
(#(#bindings,)* __scope,)
};
let mut __match_iter = ::fayalite::module::match_(__match_expr);
let ::fayalite::__std::option::Option::Some(__match_variant) =
::fayalite::__std::iter::Iterator::next(&mut __match_iter)
else {
::fayalite::__std::unreachable!("#[hdl] let with uninhabited type");
};
let ::fayalite::__std::option::Option::None =
::fayalite::__std::iter::Iterator::next(&mut __match_iter)
else {
::fayalite::__std::unreachable!("#[hdl] let with refutable pattern");
};
let (__match_variant, __scope) =
::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope(
__match_variant,
);
#let_token #pat #eq_token __match_variant #semi_token
(#(#bindings,)* __scope,)
};
}
};
match retval {
syn::Stmt::Local(retval) => retval,
@ -1062,7 +1116,7 @@ impl Visitor<'_> {
}
pub(crate) fn process_hdl_match(
&mut self,
_hdl_attr: HdlAttr<Nothing, kw::hdl>,
hdl_attr: HdlAttr<ExprOptions, kw::hdl>,
expr_match: ExprMatch,
) -> Expr {
let span = expr_match.match_token.span();
@ -1074,8 +1128,9 @@ impl Visitor<'_> {
brace_token: _,
arms,
} = expr_match;
self.require_normal_module_or_fn(match_token);
let ExprOptions { sim } = hdl_attr.body;
let mut state = HdlMatchParseState {
sim,
match_span: span,
errors: &mut self.errors,
};
@ -1083,24 +1138,36 @@ impl Visitor<'_> {
arms.into_iter()
.filter_map(|arm| MatchArm::parse(&mut state, arm).ok()),
);
let expr = quote_spanned! {span=>
{
type __MatchTy<T> = <T as ::fayalite::ty::Type>::MatchVariant;
let __match_expr = ::fayalite::expr::ToExpr::to_expr(&(#expr));
::fayalite::expr::check_match_expr(__match_expr, |__match_value, __infallible| {
#[allow(unused_variables)]
#check_match
});
for __match_variant in ::fayalite::module::match_(__match_expr) {
let (__match_variant, __scope) =
::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope(
__match_variant,
);
#match_token __match_variant {
let expr = if sim.is_some() {
quote_spanned! {span=>
{
type __MatchTy<T> = <T as ::fayalite::ty::Type>::SimValue;
let __match_expr = ::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr));
#match_token ::fayalite::sim::value::SimValue::into_value(__match_expr) {
#(#arms)*
}
}
}
} else {
quote_spanned! {span=>
{
type __MatchTy<T> = <T as ::fayalite::ty::Type>::MatchVariant;
let __match_expr = ::fayalite::expr::ToExpr::to_expr(&(#expr));
::fayalite::expr::check_match_expr(__match_expr, |__match_value, __infallible| {
#[allow(unused_variables)]
#check_match
});
for __match_variant in ::fayalite::module::match_(__match_expr) {
let (__match_variant, __scope) =
::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope(
__match_variant,
);
#match_token __match_variant {
#(#arms)*
}
}
}
}
};
syn::parse2(expr).unwrap()
}

View file

@ -40,6 +40,7 @@ fayalite-visit-gen.workspace = true
[features]
unstable-doc = []
unstable-test-hasher = []
[package.metadata.docs.rs]
features = ["unstable-doc"]

View file

@ -12,7 +12,7 @@ use std::{
ops::Deref,
};
#[derive(Clone)]
#[derive(Clone, Debug)]
struct CustomFirrtlAnnotationFieldsImpl {
value: serde_json::Map<String, serde_json::Value>,
serialized: Interned<str>,

View file

@ -2,17 +2,24 @@
// See Notices.txt for copyright information
use crate::{
expr::{ops::ArrayIndex, Expr, ToExpr},
int::{DynSize, KnownSize, Size, SizeType, DYN_SIZE},
expr::{
ops::{ArrayLiteral, ExprFromIterator, ExprIntoIterator, ExprPartialEq},
CastToBits, Expr, HdlPartialEq, ReduceBits, ToExpr,
},
int::{Bool, DynSize, KnownSize, Size, SizeType, DYN_SIZE},
intern::{Intern, Interned, LazyInterned},
module::transform::visit::{Fold, Folder, Visit, Visitor},
sim::value::{SimValue, SimValuePartialEq},
source_location::SourceLocation,
ty::{
CanonicalType, MatchVariantWithoutScope, StaticType, Type, TypeProperties, TypeWithDeref,
serde_impls::SerdeCanonicalType, CanonicalType, MatchVariantWithoutScope, StaticType, Type,
TypeProperties, TypeWithDeref,
},
util::ConstUsize,
};
use std::ops::Index;
use bitvec::slice::BitSlice;
use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
use std::{iter::FusedIterator, ops::Index};
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct ArrayType<T: Type = CanonicalType, Len: Size = DynSize> {
@ -91,6 +98,12 @@ impl<T: Type, Len: KnownSize + Size<SizeType = Len>> ArrayType<T, Len> {
}
}
impl<T: StaticType, Len: KnownSize> Default for ArrayType<T, Len> {
fn default() -> Self {
Self::TYPE
}
}
impl<T: StaticType, Len: KnownSize> StaticType for ArrayType<T, Len> {
const TYPE: Self = Self {
element: LazyInterned::new_lazy(&|| T::TYPE.intern_sized()),
@ -139,6 +152,7 @@ impl<T: Type + Visit<State>, Len: Size, State: Visitor + ?Sized> Visit<State>
impl<T: Type, Len: Size> Type for ArrayType<T, Len> {
type BaseType = Array;
type MaskType = ArrayType<T::MaskType, Len>;
type SimValue = Len::ArraySimValue<T>;
type MatchVariant = Len::ArrayMatch<T>;
type MatchActiveScope = ();
type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Len::ArrayMatch<T>>;
@ -148,10 +162,8 @@ impl<T: Type, Len: Size> Type for ArrayType<T, Len> {
this: Expr<Self>,
source_location: SourceLocation,
) -> Self::MatchVariantsIter {
let base = Expr::as_dyn_array(this);
let base_ty = Expr::ty(base);
let _ = source_location;
let retval = Vec::from_iter((0..base_ty.len()).map(|i| ArrayIndex::new(base, i).to_expr()));
let retval = Vec::from_iter(this);
std::iter::once(MatchVariantWithoutScope(
Len::ArrayMatch::<T>::try_from(retval)
.ok()
@ -177,16 +189,97 @@ impl<T: Type, Len: Size> Type for ArrayType<T, Len> {
Len::from_usize(array.len()),
)
}
fn source_location() -> SourceLocation {
SourceLocation::builtin()
}
fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue {
assert_eq!(bits.len(), self.type_properties.bit_width);
let element = self.element();
let element_bit_width = element.canonical().bit_width();
TryFrom::try_from(Vec::from_iter((0..self.len()).map(|i| {
SimValue::from_bitslice(element, &bits[i * element_bit_width..][..element_bit_width])
})))
.ok()
.expect("used correct length")
}
fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) {
assert_eq!(bits.len(), self.type_properties.bit_width);
let element_ty = self.element();
let element_bit_width = element_ty.canonical().bit_width();
let value: &mut [SimValue<T>] = value.as_mut();
assert_eq!(self.len(), value.len());
for (i, element_value) in value.iter_mut().enumerate() {
assert_eq!(SimValue::ty(element_value), element_ty);
SimValue::bits_mut(element_value)
.bits_mut()
.copy_from_bitslice(&bits[i * element_bit_width..][..element_bit_width]);
}
}
fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) {
assert_eq!(bits.len(), self.type_properties.bit_width);
let element_ty = self.element();
let element_bit_width = element_ty.canonical().bit_width();
let value: &[SimValue<T>] = value.as_ref();
assert_eq!(self.len(), value.len());
for (i, element_value) in value.iter().enumerate() {
assert_eq!(SimValue::ty(element_value), element_ty);
bits[i * element_bit_width..][..element_bit_width]
.copy_from_bitslice(SimValue::bits(element_value).bits());
}
}
}
impl<T: Type + Serialize, Len: Size> Serialize for ArrayType<T, Len> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
SerdeCanonicalType::<T>::Array {
element: self.element(),
len: self.len(),
}
.serialize(serializer)
}
}
impl<'de, T: Type + Deserialize<'de>, Len: Size> Deserialize<'de> for ArrayType<T, Len> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let name = |len| -> String {
if let Some(len) = len {
format!("an Array<_, {len}>")
} else {
"an Array<_>".to_string()
}
};
match SerdeCanonicalType::<T>::deserialize(deserializer)? {
SerdeCanonicalType::Array { element, len } => {
if let Some(len) = Len::try_from_usize(len) {
Ok(Self::new(element, len))
} else {
Err(Error::invalid_value(
serde::de::Unexpected::Other(&name(Some(len))),
&&*name(Len::KNOWN_VALUE),
))
}
}
ty => Err(Error::invalid_value(
serde::de::Unexpected::Other(ty.as_serde_unexpected_str()),
&&*name(Len::KNOWN_VALUE),
)),
}
}
}
impl<T: Type, Len: Size> TypeWithDeref for ArrayType<T, Len> {
fn expr_deref(this: &Expr<Self>) -> &Self::MatchVariant {
let base = Expr::as_dyn_array(*this);
let base_ty = Expr::ty(base);
let retval = Vec::from_iter((0..base_ty.len()).map(|i| ArrayIndex::new(base, i).to_expr()));
let retval = Vec::from_iter(*this);
Interned::into_inner(Intern::intern_sized(
Len::ArrayMatch::<T>::try_from(retval)
.ok()
@ -218,3 +311,143 @@ impl<T: Type, L: SizeType> Index<L> for ArrayWithoutLen<T> {
Interned::into_inner(Intern::intern_sized(ArrayType::new(self.element, len)))
}
}
impl<Lhs: Type, Rhs: Type, Len: Size> ExprPartialEq<ArrayType<Rhs, Len>> for ArrayType<Lhs, Len>
where
Lhs: ExprPartialEq<Rhs>,
{
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<ArrayType<Rhs, Len>>) -> Expr<Bool> {
let lhs_ty = Expr::ty(lhs);
let rhs_ty = Expr::ty(rhs);
assert_eq!(lhs_ty.len(), rhs_ty.len());
lhs.into_iter()
.zip(rhs)
.map(|(l, r)| l.cmp_eq(r))
.collect::<Expr<Array<Bool>>>()
.cast_to_bits()
.all_one_bits()
}
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<ArrayType<Rhs, Len>>) -> Expr<Bool> {
let lhs_ty = Expr::ty(lhs);
let rhs_ty = Expr::ty(rhs);
assert_eq!(lhs_ty.len(), rhs_ty.len());
lhs.into_iter()
.zip(rhs)
.map(|(l, r)| l.cmp_ne(r))
.collect::<Expr<Array<Bool>>>()
.cast_to_bits()
.any_one_bits()
}
}
impl<Lhs: Type, Rhs: Type, Len: Size> SimValuePartialEq<ArrayType<Rhs, Len>> for ArrayType<Lhs, Len>
where
Lhs: SimValuePartialEq<Rhs>,
{
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<ArrayType<Rhs, Len>>) -> bool {
AsRef::<[_]>::as_ref(&**this)
.iter()
.zip(AsRef::<[_]>::as_ref(&**other))
.all(|(l, r)| SimValuePartialEq::sim_value_eq(l, r))
}
}
impl<T: Type, Len: Size> ExprIntoIterator for ArrayType<T, Len> {
type Item = T;
type ExprIntoIter = ExprArrayIter<T, Len>;
fn expr_into_iter(e: Expr<Self>) -> Self::ExprIntoIter {
ExprArrayIter {
base: e,
indexes: 0..Expr::ty(e).len(),
}
}
}
#[derive(Clone, Debug)]
pub struct ExprArrayIter<T: Type, Len: Size> {
base: Expr<ArrayType<T, Len>>,
indexes: std::ops::Range<usize>,
}
impl<T: Type, Len: Size> ExprArrayIter<T, Len> {
pub fn base(&self) -> Expr<ArrayType<T, Len>> {
self.base
}
pub fn indexes(&self) -> std::ops::Range<usize> {
self.indexes.clone()
}
}
impl<T: Type, Len: Size> Iterator for ExprArrayIter<T, Len> {
type Item = Expr<T>;
fn next(&mut self) -> Option<Self::Item> {
self.indexes.next().map(|i| self.base[i])
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.indexes.size_hint()
}
fn count(self) -> usize {
self.indexes.count()
}
fn last(mut self) -> Option<Self::Item> {
self.next_back()
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
self.indexes.nth(n).map(|i| self.base[i])
}
fn fold<B, F>(self, init: B, mut f: F) -> B
where
F: FnMut(B, Self::Item) -> B,
{
self.indexes.fold(init, |b, i| f(b, self.base[i]))
}
}
impl<T: Type, Len: Size> DoubleEndedIterator for ExprArrayIter<T, Len> {
fn next_back(&mut self) -> Option<Self::Item> {
self.indexes.next_back().map(|i| self.base[i])
}
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
self.indexes.nth_back(n).map(|i| self.base[i])
}
fn rfold<B, F>(self, init: B, mut f: F) -> B
where
F: FnMut(B, Self::Item) -> B,
{
self.indexes.rfold(init, |b, i| f(b, self.base[i]))
}
}
impl<T: Type, Len: Size> ExactSizeIterator for ExprArrayIter<T, Len> {
fn len(&self) -> usize {
self.indexes.len()
}
}
impl<T: Type, Len: Size> FusedIterator for ExprArrayIter<T, Len> {}
impl<A: StaticType> ExprFromIterator<Expr<A>> for Array<A> {
fn expr_from_iter<T: IntoIterator<Item = Expr<A>>>(iter: T) -> Expr<Self> {
ArrayLiteral::new(
A::TYPE,
iter.into_iter().map(|v| Expr::canonical(v)).collect(),
)
.to_expr()
}
}
impl<'a, A: StaticType> ExprFromIterator<&'a Expr<A>> for Array<A> {
fn expr_from_iter<T: IntoIterator<Item = &'a Expr<A>>>(iter: T) -> Expr<Self> {
iter.into_iter().copied().collect()
}
}

View file

@ -2,20 +2,25 @@
// See Notices.txt for copyright information
use crate::{
expr::{ops::BundleLiteral, Expr, ToExpr},
expr::{
ops::{ArrayLiteral, BundleLiteral, ExprPartialEq},
CastToBits, Expr, ReduceBits, ToExpr,
},
int::{Bool, DynSize},
intern::{Intern, Interned},
sim::{SimValue, ToSimValue},
sim::value::{SimValue, SimValuePartialEq, ToSimValue, ToSimValueWithType},
source_location::SourceLocation,
ty::{
impl_match_variant_as_self, CanonicalType, MatchVariantWithoutScope, StaticType, Type,
TypeProperties, TypeWithDeref,
impl_match_variant_as_self, CanonicalType, MatchVariantWithoutScope, OpaqueSimValue,
StaticType, Type, TypeProperties, TypeWithDeref,
},
util::HashMap,
};
use bitvec::vec::BitVec;
use hashbrown::HashMap;
use bitvec::{slice::BitSlice, vec::BitVec};
use serde::{Deserialize, Serialize};
use std::{fmt, marker::PhantomData};
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub struct BundleField {
pub name: Interned<str>,
pub flipped: bool,
@ -155,7 +160,7 @@ impl Default for BundleTypePropertiesBuilder {
impl Bundle {
#[track_caller]
pub fn new(fields: Interned<[BundleField]>) -> Self {
let mut name_indexes = HashMap::with_capacity(fields.len());
let mut name_indexes = HashMap::with_capacity_and_hasher(fields.len(), Default::default());
let mut field_offsets = Vec::with_capacity(fields.len());
let mut type_props_builder = BundleTypePropertiesBuilder::new();
for (index, &BundleField { name, flipped, ty }) in fields.iter().enumerate() {
@ -212,6 +217,7 @@ impl Bundle {
impl Type for Bundle {
type BaseType = Bundle;
type MaskType = Bundle;
type SimValue = OpaqueSimValue;
impl_match_variant_as_self!();
fn mask_type(&self) -> Self::MaskType {
Self::new(Interned::from_iter(self.0.fields.into_iter().map(
@ -235,6 +241,20 @@ impl Type for Bundle {
fn source_location() -> SourceLocation {
SourceLocation::builtin()
}
fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue {
assert_eq!(bits.len(), self.type_properties().bit_width);
OpaqueSimValue::from_bitslice(bits)
}
fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) {
assert_eq!(bits.len(), self.type_properties().bit_width);
assert_eq!(value.bit_width(), self.type_properties().bit_width);
value.bits_mut().bits_mut().copy_from_bitslice(bits);
}
fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) {
assert_eq!(bits.len(), self.type_properties().bit_width);
assert_eq!(value.bit_width(), self.type_properties().bit_width);
bits.copy_from_bitslice(value.bits().bits());
}
}
pub trait BundleType: Type<BaseType = Bundle> {
@ -243,6 +263,93 @@ pub trait BundleType: Type<BaseType = Bundle> {
fn fields(&self) -> Interned<[BundleField]>;
}
pub struct BundleSimValueFromBits<'a> {
fields: std::slice::Iter<'static, BundleField>,
bits: &'a BitSlice,
}
impl<'a> BundleSimValueFromBits<'a> {
#[track_caller]
pub fn new<T: BundleType>(bundle_ty: T, bits: &'a BitSlice) -> Self {
let fields = bundle_ty.fields();
assert_eq!(
bits.len(),
fields
.iter()
.map(|BundleField { ty, .. }| ty.bit_width())
.sum::<usize>()
);
Self {
fields: Interned::into_inner(fields).iter(),
bits,
}
}
#[track_caller]
fn field_ty_and_bits<T: Type>(&mut self) -> (T, &'a BitSlice) {
let Some(&BundleField {
name: _,
flipped: _,
ty,
}) = self.fields.next()
else {
panic!("tried to read too many fields from BundleSimValueFromBits");
};
let (field_bits, rest) = self.bits.split_at(ty.bit_width());
self.bits = rest;
(T::from_canonical(ty), field_bits)
}
#[track_caller]
pub fn field_from_bits<T: Type>(&mut self) -> SimValue<T> {
let (field_ty, field_bits) = self.field_ty_and_bits::<T>();
SimValue::from_bitslice(field_ty, field_bits)
}
#[track_caller]
pub fn field_clone_from_bits<T: Type>(&mut self, field_value: &mut SimValue<T>) {
let (field_ty, field_bits) = self.field_ty_and_bits::<T>();
assert_eq!(field_ty, SimValue::ty(field_value));
SimValue::bits_mut(field_value)
.bits_mut()
.copy_from_bitslice(field_bits);
}
}
pub struct BundleSimValueToBits<'a> {
fields: std::slice::Iter<'static, BundleField>,
bits: &'a mut BitSlice,
}
impl<'a> BundleSimValueToBits<'a> {
#[track_caller]
pub fn new<T: BundleType>(bundle_ty: T, bits: &'a mut BitSlice) -> Self {
let fields = bundle_ty.fields();
assert_eq!(
bits.len(),
fields
.iter()
.map(|BundleField { ty, .. }| ty.bit_width())
.sum::<usize>()
);
Self {
fields: Interned::into_inner(fields).iter(),
bits,
}
}
#[track_caller]
pub fn field_to_bits<T: Type>(&mut self, field_value: &SimValue<T>) {
let Some(&BundleField {
name: _,
flipped: _,
ty,
}) = self.fields.next()
else {
panic!("tried to read too many fields from BundleSimValueFromBits");
};
assert_eq!(T::from_canonical(ty), SimValue::ty(field_value));
self.bits[..ty.bit_width()].copy_from_bitslice(SimValue::bits(field_value).bits());
self.bits = &mut std::mem::take(&mut self.bits)[ty.bit_width()..];
}
}
#[derive(Default)]
pub struct NoBuilder;
@ -325,7 +432,19 @@ macro_rules! impl_tuple_builder_fields {
}
macro_rules! impl_tuples {
([$({#[num = $num:literal, field = $field:ident, ty = $ty_var:ident: $Ty:ident] $var:ident: $T:ident})*] []) => {
(
[$({
#[
num = $num:tt,
field = $field:ident,
ty = $ty_var:ident: $Ty:ident,
lhs = $lhs_var:ident: $Lhs:ident,
rhs = $rhs_var:ident: $Rhs:ident
]
$var:ident: $T:ident
})*]
[]
) => {
impl_tuple_builder_fields! {
{}
[$({
@ -337,6 +456,7 @@ macro_rules! impl_tuples {
impl<$($T: Type,)*> Type for ($($T,)*) {
type BaseType = Bundle;
type MaskType = ($($T::MaskType,)*);
type SimValue = ($(SimValue<$T>,)*);
type MatchVariant = ($(Expr<$T>,)*);
type MatchActiveScope = ();
type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Self::MatchVariant>;
@ -375,6 +495,24 @@ macro_rules! impl_tuples {
fn source_location() -> SourceLocation {
SourceLocation::builtin()
}
fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue {
#![allow(unused_mut, unused_variables)]
let mut v = BundleSimValueFromBits::new(*self, bits);
$(let $var = v.field_from_bits();)*
($($var,)*)
}
fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) {
#![allow(unused_mut, unused_variables)]
let mut v = BundleSimValueFromBits::new(*self, bits);
let ($($var,)*) = value;
$(v.field_clone_from_bits($var);)*
}
fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) {
#![allow(unused_mut, unused_variables)]
let mut v = BundleSimValueToBits::new(*self, bits);
let ($($var,)*) = value;
$(v.field_to_bits($var);)*
}
}
impl<$($T: Type,)*> BundleType for ($($T,)*) {
type Builder = TupleBuilder<($(Unfilled<$T>,)*)>;
@ -425,77 +563,104 @@ macro_rules! impl_tuples {
BundleLiteral::new(ty, field_values[..].intern()).to_expr()
}
}
impl<$($T: ToSimValue<CanonicalType>,)*> ToSimValue<CanonicalType> for ($($T,)*) {
impl<$($T: ToSimValueWithType<CanonicalType>,)*> ToSimValueWithType<CanonicalType> for ($($T,)*) {
#[track_caller]
fn to_sim_value(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
ToSimValue::<Bundle>::to_sim_value(self, Bundle::from_canonical(ty)).into_canonical()
fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
SimValue::into_canonical(ToSimValueWithType::<Bundle>::to_sim_value_with_type(self, Bundle::from_canonical(ty)))
}
#[track_caller]
fn into_sim_value(self, ty: CanonicalType) -> SimValue<CanonicalType>
fn into_sim_value_with_type(self, ty: CanonicalType) -> SimValue<CanonicalType>
{
ToSimValue::<Bundle>::into_sim_value(self, Bundle::from_canonical(ty)).into_canonical()
}
#[track_caller]
fn box_into_sim_value(self: Box<Self>, ty: CanonicalType) -> SimValue<CanonicalType> {
ToSimValue::<Bundle>::box_into_sim_value(self, Bundle::from_canonical(ty)).into_canonical()
SimValue::into_canonical(ToSimValueWithType::<Bundle>::into_sim_value_with_type(self, Bundle::from_canonical(ty)))
}
}
impl<$($T: ToSimValue<CanonicalType>,)*> ToSimValue<Bundle> for ($($T,)*) {
impl<$($T: ToSimValueWithType<CanonicalType>,)*> ToSimValueWithType<Bundle> for ($($T,)*) {
#[track_caller]
fn to_sim_value(&self, ty: Bundle) -> SimValue<Bundle> {
fn to_sim_value_with_type(&self, ty: Bundle) -> SimValue<Bundle> {
let ($($var,)*) = self;
let [$($ty_var,)*] = *ty.fields() else {
panic!("bundle has wrong number of fields");
};
$(let $var = $var.to_sim_value($ty_var.ty);)*
ToSimValue::into_sim_value(($($var,)*), ty)
$(let $var = $var.to_sim_value_with_type($ty_var.ty);)*
ToSimValueWithType::into_sim_value_with_type(($($var,)*), ty)
}
#[track_caller]
fn into_sim_value(self, ty: Bundle) -> SimValue<Bundle> {
fn into_sim_value_with_type(self, ty: Bundle) -> SimValue<Bundle> {
#![allow(unused_mut)]
#![allow(clippy::unused_unit)]
let ($($var,)*) = self;
let [$($ty_var,)*] = *ty.fields() else {
panic!("bundle has wrong number of fields");
};
let mut bits: Option<BitVec> = None;
$(let $var = $var.into_sim_value($ty_var.ty);
assert_eq!($var.ty(), $ty_var.ty);
if !$var.bits().is_empty() {
if let Some(bits) = &mut bits {
bits.extend_from_bitslice($var.bits());
} else {
let mut $var = $var.into_bits();
$var.reserve(ty.type_properties().bit_width - $var.len());
bits = Some($var);
}
}
let mut bits = BitVec::new();
$(let $var = $var.into_sim_value_with_type($ty_var.ty);
assert_eq!(SimValue::ty(&$var), $ty_var.ty);
bits.extend_from_bitslice(SimValue::bits(&$var).bits());
)*
bits.unwrap_or_else(BitVec::new).into_sim_value(ty)
}
#[track_caller]
fn box_into_sim_value(self: Box<Self>, ty: Bundle) -> SimValue<Bundle> {
Self::into_sim_value(*self, ty)
bits.into_sim_value_with_type(ty)
}
}
impl<$($T: ToSimValue<$Ty>, $Ty: Type,)*> ToSimValue<($($Ty,)*)> for ($($T,)*) {
impl<$($T: ToSimValueWithType<$Ty>, $Ty: Type,)*> ToSimValueWithType<($($Ty,)*)> for ($($T,)*) {
#[track_caller]
fn to_sim_value(&self, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> {
fn to_sim_value_with_type(&self, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> {
let ($($var,)*) = self;
let ($($ty_var,)*) = ty;
$(let $var = $var.to_sim_value($ty_var).into_canonical();)*
SimValue::from_canonical(ToSimValue::into_sim_value(($($var,)*), ty.canonical()))
$(let $var = $var.to_sim_value_with_type($ty_var);)*
SimValue::from_value(ty, ($($var,)*))
}
#[track_caller]
fn into_sim_value(self, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> {
fn into_sim_value_with_type(self, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> {
let ($($var,)*) = self;
let ($($ty_var,)*) = ty;
$(let $var = $var.into_sim_value($ty_var).into_canonical();)*
SimValue::from_canonical(ToSimValue::into_sim_value(($($var,)*), ty.canonical()))
$(let $var = $var.into_sim_value_with_type($ty_var);)*
SimValue::from_value(ty, ($($var,)*))
}
}
impl<$($T: ToSimValue,)*> ToSimValue for ($($T,)*) {
type Type = ($($T::Type,)*);
#[track_caller]
fn to_sim_value(&self) -> SimValue<Self::Type> {
let ($($var,)*) = self;
$(let $var = $var.to_sim_value();)*
SimValue::from_value(($(SimValue::ty(&$var),)*), ($($var,)*))
}
#[track_caller]
fn box_into_sim_value(self: Box<Self>, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> {
Self::into_sim_value(*self, ty)
fn into_sim_value(self) -> SimValue<Self::Type> {
let ($($var,)*) = self;
$(let $var = $var.to_sim_value();)*
SimValue::from_value(($(SimValue::ty(&$var),)*), ($($var,)*))
}
}
impl<$($Lhs: Type + ExprPartialEq<$Rhs>, $Rhs: Type,)*> ExprPartialEq<($($Rhs,)*)> for ($($Lhs,)*) {
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> {
let ($($lhs_var,)*) = *lhs;
let ($($rhs_var,)*) = *rhs;
ArrayLiteral::<Bool, DynSize>::new(
Bool,
FromIterator::from_iter([$(Expr::canonical(ExprPartialEq::cmp_eq($lhs_var, $rhs_var)),)*]),
)
.cast_to_bits()
.all_one_bits()
}
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> {
let ($($lhs_var,)*) = *lhs;
let ($($rhs_var,)*) = *rhs;
ArrayLiteral::<Bool, DynSize>::new(
Bool,
FromIterator::from_iter([$(Expr::canonical(ExprPartialEq::cmp_ne($lhs_var, $rhs_var)),)*]),
)
.cast_to_bits()
.any_one_bits()
}
}
impl<$($Lhs: SimValuePartialEq<$Rhs>, $Rhs: Type,)*> SimValuePartialEq<($($Rhs,)*)> for ($($Lhs,)*) {
fn sim_value_eq(lhs: &SimValue<Self>, rhs: &SimValue<($($Rhs,)*)>) -> bool {
let ($($lhs_var,)*) = &**lhs;
let ($($rhs_var,)*) = &**rhs;
let retval = true;
$(let retval = retval && $lhs_var == $rhs_var;)*
retval
}
}
};
@ -507,24 +672,25 @@ macro_rules! impl_tuples {
impl_tuples! {
[] [
{#[num = 0, field = field_0, ty = ty0: Ty0] v0: T0}
{#[num = 1, field = field_1, ty = ty1: Ty1] v1: T1}
{#[num = 2, field = field_2, ty = ty2: Ty2] v2: T2}
{#[num = 3, field = field_3, ty = ty3: Ty3] v3: T3}
{#[num = 4, field = field_4, ty = ty4: Ty4] v4: T4}
{#[num = 5, field = field_5, ty = ty5: Ty5] v5: T5}
{#[num = 6, field = field_6, ty = ty6: Ty6] v6: T6}
{#[num = 7, field = field_7, ty = ty7: Ty7] v7: T7}
{#[num = 8, field = field_8, ty = ty8: Ty8] v8: T8}
{#[num = 9, field = field_9, ty = ty9: Ty9] v9: T9}
{#[num = 10, field = field_10, ty = ty10: Ty10] v10: T10}
{#[num = 11, field = field_11, ty = ty11: Ty11] v11: T11}
{#[num = 0, field = field_0, ty = ty0: Ty0, lhs = lhs0: Lhs0, rhs = rhs0: Rhs0] v0: T0}
{#[num = 1, field = field_1, ty = ty1: Ty1, lhs = lhs1: Lhs1, rhs = rhs1: Rhs1] v1: T1}
{#[num = 2, field = field_2, ty = ty2: Ty2, lhs = lhs2: Lhs2, rhs = rhs2: Rhs2] v2: T2}
{#[num = 3, field = field_3, ty = ty3: Ty3, lhs = lhs3: Lhs3, rhs = rhs3: Rhs3] v3: T3}
{#[num = 4, field = field_4, ty = ty4: Ty4, lhs = lhs4: Lhs4, rhs = rhs4: Rhs4] v4: T4}
{#[num = 5, field = field_5, ty = ty5: Ty5, lhs = lhs5: Lhs5, rhs = rhs5: Rhs5] v5: T5}
{#[num = 6, field = field_6, ty = ty6: Ty6, lhs = lhs6: Lhs6, rhs = rhs6: Rhs6] v6: T6}
{#[num = 7, field = field_7, ty = ty7: Ty7, lhs = lhs7: Lhs7, rhs = rhs7: Rhs7] v7: T7}
{#[num = 8, field = field_8, ty = ty8: Ty8, lhs = lhs8: Lhs8, rhs = rhs8: Rhs8] v8: T8}
{#[num = 9, field = field_9, ty = ty9: Ty9, lhs = lhs9: Lhs9, rhs = rhs9: Rhs9] v9: T9}
{#[num = 10, field = field_10, ty = ty10: Ty10, lhs = lhs10: Lhs10, rhs = rhs10: Rhs10] v10: T10}
{#[num = 11, field = field_11, ty = ty11: Ty11, lhs = lhs11: Lhs11, rhs = rhs11: Rhs11] v11: T11}
]
}
impl<T: ?Sized + Send + Sync + 'static> Type for PhantomData<T> {
type BaseType = Bundle;
type MaskType = ();
type SimValue = PhantomData<T>;
type MatchVariant = PhantomData<T>;
type MatchActiveScope = ();
type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Self::MatchVariant>;
@ -557,6 +723,16 @@ impl<T: ?Sized + Send + Sync + 'static> Type for PhantomData<T> {
fn source_location() -> SourceLocation {
SourceLocation::builtin()
}
fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue {
assert!(bits.is_empty());
*self
}
fn sim_value_clone_from_bits(&self, _value: &mut Self::SimValue, bits: &BitSlice) {
assert!(bits.is_empty());
}
fn sim_value_to_bits(&self, _value: &Self::SimValue, bits: &mut BitSlice) {
assert!(bits.is_empty());
}
}
pub struct PhantomDataBuilder<T: ?Sized + Send + Sync + 'static>(PhantomData<T>);
@ -604,26 +780,38 @@ impl<T: ?Sized + Send + Sync + 'static> ToExpr for PhantomData<T> {
}
}
impl<T: ?Sized + Send + Sync + 'static> ToSimValue<Self> for PhantomData<T> {
impl<T: ?Sized + Send + Sync + 'static> ToSimValue for PhantomData<T> {
type Type = PhantomData<T>;
#[track_caller]
fn to_sim_value(&self, ty: Self) -> SimValue<Self> {
ToSimValue::into_sim_value(BitVec::new(), ty)
fn to_sim_value(&self) -> SimValue<Self> {
SimValue::from_value(*self, *self)
}
}
impl<T: ?Sized> ToSimValue<Bundle> for PhantomData<T> {
impl<T: ?Sized + Send + Sync + 'static> ToSimValueWithType<Self> for PhantomData<T> {
#[track_caller]
fn to_sim_value(&self, ty: Bundle) -> SimValue<Bundle> {
fn to_sim_value_with_type(&self, ty: Self) -> SimValue<Self> {
SimValue::from_value(ty, *self)
}
}
impl<T: ?Sized> ToSimValueWithType<Bundle> for PhantomData<T> {
#[track_caller]
fn to_sim_value_with_type(&self, ty: Bundle) -> SimValue<Bundle> {
assert!(ty.fields().is_empty());
ToSimValue::into_sim_value(BitVec::new(), ty)
ToSimValueWithType::into_sim_value_with_type(BitVec::new(), ty)
}
}
impl<T: ?Sized> ToSimValue<CanonicalType> for PhantomData<T> {
impl<T: ?Sized> ToSimValueWithType<CanonicalType> for PhantomData<T> {
#[track_caller]
fn to_sim_value(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
let ty = Bundle::from_canonical(ty);
assert!(ty.fields().is_empty());
ToSimValue::into_sim_value(BitVec::new(), ty).into_canonical()
SimValue::into_canonical(ToSimValueWithType::into_sim_value_with_type(
BitVec::new(),
ty,
))
}
}

View file

@ -258,7 +258,7 @@ pub struct VerilogArgs {
default_value = "firtool",
env = "FIRTOOL",
value_hint = ValueHint::CommandName,
value_parser = OsStringValueParser::new().try_map(which::which)
value_parser = OsStringValueParser::new().try_map(which)
)]
pub firtool: PathBuf,
#[arg(long)]
@ -428,6 +428,13 @@ impl clap::Args for FormalAdjustArgs {
}
}
fn which(v: std::ffi::OsString) -> which::Result<PathBuf> {
#[cfg(not(miri))]
return which::which(v);
#[cfg(miri)]
return Ok(Path::new("/").join(v));
}
#[derive(Parser, Clone)]
#[non_exhaustive]
pub struct FormalArgs {
@ -438,7 +445,7 @@ pub struct FormalArgs {
default_value = "sby",
env = "SBY",
value_hint = ValueHint::CommandName,
value_parser = OsStringValueParser::new().try_map(which::which)
value_parser = OsStringValueParser::new().try_map(which)
)]
pub sby: PathBuf,
#[arg(long)]

View file

@ -8,6 +8,7 @@ use crate::{
source_location::SourceLocation,
ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties},
};
use bitvec::slice::BitSlice;
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
pub struct Clock;
@ -15,6 +16,7 @@ pub struct Clock;
impl Type for Clock {
type BaseType = Clock;
type MaskType = Bool;
type SimValue = bool;
impl_match_variant_as_self!();
@ -36,6 +38,21 @@ impl Type for Clock {
};
retval
}
fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue {
assert_eq!(bits.len(), 1);
bits[0]
}
fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) {
assert_eq!(bits.len(), 1);
*value = bits[0];
}
fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) {
assert_eq!(bits.len(), 1);
bits.set(0, *value);
}
}
impl Clock {

View file

@ -2,21 +2,30 @@
// See Notices.txt for copyright information
use crate::{
expr::{ops::VariantAccess, Expr, ToExpr},
expr::{
ops::{ExprPartialEq, VariantAccess},
Expr, ToExpr,
},
hdl,
int::Bool,
int::{Bool, UIntValue},
intern::{Intern, Interned},
module::{
connect, enum_match_variants_helper, incomplete_wire, wire,
EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, Scope,
},
sim::value::{SimValue, SimValuePartialEq},
source_location::SourceLocation,
ty::{CanonicalType, MatchVariantAndInactiveScope, StaticType, Type, TypeProperties},
ty::{
CanonicalType, MatchVariantAndInactiveScope, OpaqueSimValue, StaticType, Type,
TypeProperties,
},
util::HashMap,
};
use hashbrown::HashMap;
use std::{convert::Infallible, fmt, iter::FusedIterator};
use bitvec::{order::Lsb0, slice::BitSlice, view::BitView};
use serde::{Deserialize, Serialize};
use std::{convert::Infallible, fmt, iter::FusedIterator, sync::Arc};
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
pub struct EnumVariant {
pub name: Interned<str>,
pub ty: Option<CanonicalType>,
@ -149,6 +158,12 @@ impl EnumTypePropertiesBuilder {
variant_count: variant_count + 1,
}
}
#[must_use]
pub fn variants(self, variants: impl IntoIterator<Item = EnumVariant>) -> Self {
variants.into_iter().fold(self, |this, variant| {
this.variant(variant.ty.map(CanonicalType::type_properties))
})
}
pub const fn finish(self) -> TypeProperties {
assert!(
self.variant_count != 0,
@ -178,7 +193,8 @@ impl Default for EnumTypePropertiesBuilder {
impl Enum {
#[track_caller]
pub fn new(variants: Interned<[EnumVariant]>) -> Self {
let mut name_indexes = HashMap::with_capacity(variants.len());
let mut name_indexes =
HashMap::with_capacity_and_hasher(variants.len(), Default::default());
let mut type_props_builder = EnumTypePropertiesBuilder::new();
for (index, EnumVariant { name, ty }) in variants.iter().enumerate() {
if let Some(old_index) = name_indexes.insert(*name, index) {
@ -245,6 +261,7 @@ pub trait EnumType:
MatchVariantsIter = EnumMatchVariantsIter<Self>,
>
{
type SimBuilder: From<Self>;
fn variants(&self) -> Interned<[EnumVariant]>;
fn match_activate_scope(
v: Self::MatchVariantAndInactiveScope,
@ -307,7 +324,18 @@ impl<T: EnumType> DoubleEndedIterator for EnumMatchVariantsIter<T> {
}
}
pub struct NoBuilder {
_ty: Enum,
}
impl From<Enum> for NoBuilder {
fn from(_ty: Enum) -> Self {
Self { _ty }
}
}
impl EnumType for Enum {
type SimBuilder = NoBuilder;
fn match_activate_scope(
v: Self::MatchVariantAndInactiveScope,
) -> (Self::MatchVariant, Self::MatchActiveScope) {
@ -322,6 +350,7 @@ impl EnumType for Enum {
impl Type for Enum {
type BaseType = Enum;
type MaskType = Bool;
type SimValue = OpaqueSimValue;
type MatchVariant = Option<Expr<CanonicalType>>;
type MatchActiveScope = Scope;
type MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>;
@ -352,6 +381,309 @@ impl Type for Enum {
fn source_location() -> SourceLocation {
SourceLocation::builtin()
}
fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue {
assert_eq!(bits.len(), self.type_properties().bit_width);
OpaqueSimValue::from_bitslice(bits)
}
fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) {
assert_eq!(bits.len(), self.type_properties().bit_width);
assert_eq!(value.bit_width(), self.type_properties().bit_width);
value.bits_mut().bits_mut().copy_from_bitslice(bits);
}
fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) {
assert_eq!(bits.len(), self.type_properties().bit_width);
assert_eq!(value.bit_width(), self.type_properties().bit_width);
bits.copy_from_bitslice(value.bits().bits());
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug, Default)]
pub struct EnumPaddingSimValue {
bits: Option<UIntValue>,
}
impl EnumPaddingSimValue {
pub const fn new() -> Self {
Self { bits: None }
}
pub fn bit_width(&self) -> Option<usize> {
self.bits.as_ref().map(UIntValue::width)
}
pub fn bits(&self) -> &Option<UIntValue> {
&self.bits
}
pub fn bits_mut(&mut self) -> &mut Option<UIntValue> {
&mut self.bits
}
pub fn into_bits(self) -> Option<UIntValue> {
self.bits
}
pub fn from_bits(bits: Option<UIntValue>) -> Self {
Self { bits }
}
pub fn from_bitslice(v: &BitSlice) -> Self {
Self {
bits: Some(UIntValue::new(Arc::new(v.to_bitvec()))),
}
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct UnknownVariantSimValue {
discriminant: usize,
body_bits: UIntValue,
}
impl UnknownVariantSimValue {
pub fn discriminant(&self) -> usize {
self.discriminant
}
pub fn body_bits(&self) -> &UIntValue {
&self.body_bits
}
pub fn body_bits_mut(&mut self) -> &mut UIntValue {
&mut self.body_bits
}
pub fn into_body_bits(self) -> UIntValue {
self.body_bits
}
pub fn into_parts(self) -> (usize, UIntValue) {
(self.discriminant, self.body_bits)
}
pub fn new(discriminant: usize, body_bits: UIntValue) -> Self {
Self {
discriminant,
body_bits,
}
}
}
pub struct EnumSimValueFromBits<'a> {
variants: Interned<[EnumVariant]>,
discriminant: usize,
body_bits: &'a BitSlice,
}
impl<'a> EnumSimValueFromBits<'a> {
#[track_caller]
pub fn new<T: EnumType>(ty: T, bits: &'a BitSlice) -> Self {
let variants = ty.variants();
let bit_width = EnumTypePropertiesBuilder::new()
.variants(variants)
.finish()
.bit_width;
assert_eq!(bit_width, bits.len());
let (discriminant_bits, body_bits) =
bits.split_at(discriminant_bit_width_impl(variants.len()));
let mut discriminant = 0usize;
discriminant.view_bits_mut::<Lsb0>()[..discriminant_bits.len()]
.copy_from_bitslice(discriminant_bits);
Self {
variants,
discriminant,
body_bits,
}
}
pub fn discriminant(&self) -> usize {
self.discriminant
}
#[track_caller]
#[cold]
fn usage_error(&self, clone: bool) -> ! {
let clone = if clone { "clone_" } else { "" };
match self.variants.get(self.discriminant) {
None => {
panic!("should have called EnumSimValueFromBits::unknown_variant_{clone}from_bits");
}
Some(EnumVariant { ty: None, .. }) => {
panic!(
"should have called EnumSimValueFromBits::variant_no_field_{clone}from_bits"
);
}
Some(EnumVariant { ty: Some(_), .. }) => {
panic!(
"should have called EnumSimValueFromBits::variant_with_field_{clone}from_bits"
);
}
}
}
#[track_caller]
fn known_variant(&self, clone: bool) -> (Option<CanonicalType>, &'a BitSlice, &'a BitSlice) {
let Some(EnumVariant { ty, .. }) = self.variants.get(self.discriminant) else {
self.usage_error(clone);
};
let variant_bit_width = ty.map_or(0, CanonicalType::bit_width);
let (variant_bits, padding_bits) = self.body_bits.split_at(variant_bit_width);
(*ty, variant_bits, padding_bits)
}
#[track_caller]
pub fn unknown_variant_from_bits(self) -> UnknownVariantSimValue {
let None = self.variants.get(self.discriminant) else {
self.usage_error(false);
};
UnknownVariantSimValue::new(
self.discriminant,
UIntValue::new(Arc::new(self.body_bits.to_bitvec())),
)
}
#[track_caller]
pub fn unknown_variant_clone_from_bits(self, value: &mut UnknownVariantSimValue) {
let None = self.variants.get(self.discriminant) else {
self.usage_error(true);
};
value.discriminant = self.discriminant;
assert_eq!(value.body_bits.width(), self.body_bits.len());
value
.body_bits
.bits_mut()
.copy_from_bitslice(self.body_bits);
}
#[track_caller]
pub fn variant_no_field_from_bits(self) -> EnumPaddingSimValue {
let (None, _variant_bits, padding_bits) = self.known_variant(false) else {
self.usage_error(false);
};
EnumPaddingSimValue::from_bitslice(padding_bits)
}
#[track_caller]
pub fn variant_with_field_from_bits<T: Type>(self) -> (SimValue<T>, EnumPaddingSimValue) {
let (Some(variant_ty), variant_bits, padding_bits) = self.known_variant(false) else {
self.usage_error(false);
};
(
SimValue::from_bitslice(T::from_canonical(variant_ty), variant_bits),
EnumPaddingSimValue::from_bitslice(padding_bits),
)
}
#[track_caller]
fn clone_padding_from_bits(padding: &mut EnumPaddingSimValue, padding_bits: &BitSlice) {
match padding.bits_mut() {
None => *padding = EnumPaddingSimValue::from_bitslice(padding_bits),
Some(padding) => {
assert_eq!(padding.width(), padding_bits.len());
padding.bits_mut().copy_from_bitslice(padding_bits);
}
}
}
#[track_caller]
pub fn variant_no_field_clone_from_bits(self, padding: &mut EnumPaddingSimValue) {
let (None, _variant_bits, padding_bits) = self.known_variant(true) else {
self.usage_error(true);
};
Self::clone_padding_from_bits(padding, padding_bits);
}
#[track_caller]
pub fn variant_with_field_clone_from_bits<T: Type>(
self,
value: &mut SimValue<T>,
padding: &mut EnumPaddingSimValue,
) {
let (Some(variant_ty), variant_bits, padding_bits) = self.known_variant(true) else {
self.usage_error(true);
};
assert_eq!(SimValue::ty(value), T::from_canonical(variant_ty));
SimValue::bits_mut(value)
.bits_mut()
.copy_from_bitslice(variant_bits);
Self::clone_padding_from_bits(padding, padding_bits);
}
}
pub struct EnumSimValueToBits<'a> {
variants: Interned<[EnumVariant]>,
bit_width: usize,
discriminant_bit_width: usize,
bits: &'a mut BitSlice,
}
impl<'a> EnumSimValueToBits<'a> {
#[track_caller]
pub fn new<T: EnumType>(ty: T, bits: &'a mut BitSlice) -> Self {
let variants = ty.variants();
let bit_width = EnumTypePropertiesBuilder::new()
.variants(variants)
.finish()
.bit_width;
assert_eq!(bit_width, bits.len());
Self {
variants,
bit_width,
discriminant_bit_width: discriminant_bit_width_impl(variants.len()),
bits,
}
}
#[track_caller]
fn discriminant_to_bits(&mut self, mut discriminant: usize) {
let orig_discriminant = discriminant;
let discriminant_bits =
&mut discriminant.view_bits_mut::<Lsb0>()[..self.discriminant_bit_width];
self.bits[..self.discriminant_bit_width].copy_from_bitslice(discriminant_bits);
discriminant_bits.fill(false);
assert!(
discriminant == 0,
"{orig_discriminant:#x} is too big to fit in enum discriminant bits",
);
}
#[track_caller]
pub fn unknown_variant_to_bits(mut self, value: &UnknownVariantSimValue) {
self.discriminant_to_bits(value.discriminant);
let None = self.variants.get(value.discriminant) else {
panic!("can't use UnknownVariantSimValue to set known discriminant");
};
assert_eq!(
self.bit_width - self.discriminant_bit_width,
value.body_bits.width()
);
self.bits[self.discriminant_bit_width..].copy_from_bitslice(value.body_bits.bits());
}
#[track_caller]
fn known_variant(
mut self,
discriminant: usize,
padding: &EnumPaddingSimValue,
) -> (Option<CanonicalType>, &'a mut BitSlice) {
self.discriminant_to_bits(discriminant);
let variant_ty = self.variants[discriminant].ty;
let variant_bit_width = variant_ty.map_or(0, CanonicalType::bit_width);
let padding_bits = &mut self.bits[self.discriminant_bit_width..][variant_bit_width..];
if let Some(padding) = padding.bits() {
assert_eq!(padding.width(), padding_bits.len());
padding_bits.copy_from_bitslice(padding.bits());
} else {
padding_bits.fill(false);
}
let variant_bits = &mut self.bits[self.discriminant_bit_width..][..variant_bit_width];
(variant_ty, variant_bits)
}
#[track_caller]
pub fn variant_no_field_to_bits(self, discriminant: usize, padding: &EnumPaddingSimValue) {
let (None, _variant_bits) = self.known_variant(discriminant, padding) else {
panic!("expected variant to have no field");
};
}
#[track_caller]
pub fn variant_with_field_to_bits<T: Type>(
self,
discriminant: usize,
value: &SimValue<T>,
padding: &EnumPaddingSimValue,
) {
let (Some(variant_ty), variant_bits) = self.known_variant(discriminant, padding) else {
panic!("expected variant to have a field");
};
assert_eq!(SimValue::ty(value), T::from_canonical(variant_ty));
variant_bits.copy_from_bitslice(SimValue::bits(value).bits());
}
}
#[doc(hidden)]
pub fn assert_is_enum_type<T: EnumType>(v: T) -> T {
v
}
#[doc(hidden)]
pub fn enum_type_to_sim_builder<T: EnumType>(v: T) -> T::SimBuilder {
v.into()
}
#[hdl]
@ -360,6 +692,79 @@ pub enum HdlOption<T: Type> {
HdlSome(T),
}
impl<Lhs: Type + ExprPartialEq<Rhs>, Rhs: Type> ExprPartialEq<HdlOption<Rhs>> for HdlOption<Lhs> {
#[hdl]
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<HdlOption<Rhs>>) -> Expr<Bool> {
#[hdl]
let cmp_eq = wire();
#[hdl]
match lhs {
HdlSome(lhs) =>
{
#[hdl]
match rhs {
HdlSome(rhs) => connect(cmp_eq, ExprPartialEq::cmp_eq(lhs, rhs)),
HdlNone => connect(cmp_eq, false),
}
}
HdlNone =>
{
#[hdl]
match rhs {
HdlSome(_) => connect(cmp_eq, false),
HdlNone => connect(cmp_eq, true),
}
}
}
cmp_eq
}
#[hdl]
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<HdlOption<Rhs>>) -> Expr<Bool> {
#[hdl]
let cmp_ne = wire();
#[hdl]
match lhs {
HdlSome(lhs) =>
{
#[hdl]
match rhs {
HdlSome(rhs) => connect(cmp_ne, ExprPartialEq::cmp_ne(lhs, rhs)),
HdlNone => connect(cmp_ne, true),
}
}
HdlNone =>
{
#[hdl]
match rhs {
HdlSome(_) => connect(cmp_ne, true),
HdlNone => connect(cmp_ne, false),
}
}
}
cmp_ne
}
}
impl<Lhs: SimValuePartialEq<Rhs>, Rhs: Type> SimValuePartialEq<HdlOption<Rhs>> for HdlOption<Lhs> {
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<HdlOption<Rhs>>) -> bool {
type SimValueMatch<T> = <T as Type>::SimValue;
match (&**this, &**other) {
(SimValueMatch::<Self>::HdlNone(_), SimValueMatch::<HdlOption<Rhs>>::HdlNone(_)) => {
true
}
(SimValueMatch::<Self>::HdlSome(..), SimValueMatch::<HdlOption<Rhs>>::HdlNone(_))
| (SimValueMatch::<Self>::HdlNone(_), SimValueMatch::<HdlOption<Rhs>>::HdlSome(..)) => {
false
}
(
SimValueMatch::<Self>::HdlSome(l, _),
SimValueMatch::<HdlOption<Rhs>>::HdlSome(r, _),
) => l == r,
}
}
}
#[allow(non_snake_case)]
pub fn HdlNone<T: StaticType>() -> Expr<HdlOption<T>> {
HdlOption[T::TYPE].HdlNone()

View file

@ -16,6 +16,7 @@ use crate::{
transform::visit::{Fold, Folder, Visit, Visitor},
Instance, ModuleIO,
},
phantom_const::PhantomConst,
reg::Reg,
reset::{AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset},
ty::{CanonicalType, StaticType, Type, TypeWithDeref},
@ -109,6 +110,7 @@ expr_enum! {
UIntLiteral(Interned<UIntValue>),
SIntLiteral(Interned<SIntValue>),
BoolLiteral(bool),
PhantomConst(PhantomConst),
BundleLiteral(ops::BundleLiteral),
ArrayLiteral(ops::ArrayLiteral<CanonicalType, DynSize>),
EnumLiteral(ops::EnumLiteral),
@ -272,6 +274,17 @@ pub struct Expr<T: Type> {
impl<T: Type + fmt::Debug> fmt::Debug for Expr<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(debug_assertions)]
{
let Self {
__enum,
__ty,
__flow,
} = self;
let expr_ty = __ty.canonical();
let enum_ty = __enum.to_expr().__ty;
assert_eq!(expr_ty, enum_ty, "expr ty mismatch:\nExpr {{\n__enum: {__enum:?},\n__ty: {__ty:?},\n__flow: {__flow:?}\n}}");
}
self.__enum.fmt(f)
}
}
@ -698,6 +711,7 @@ impl<T: ToExpr + ?Sized> CastToBits for T {
}
pub trait CastBitsTo {
#[track_caller]
fn cast_bits_to<T: Type>(&self, ty: T) -> Expr<T>;
}
@ -755,3 +769,27 @@ pub fn repeat<T: Type, L: SizeType>(
)
.to_expr()
}
impl<T: ?Sized + crate::phantom_const::PhantomConstValue> ToExpr for PhantomConst<T> {
type Type = Self;
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::PhantomConst(self.canonical_phantom_const()).intern_sized(),
__ty: *self,
__flow: Flow::Source,
}
}
}
impl<T: ?Sized + crate::phantom_const::PhantomConstValue> GetTarget for PhantomConst<T> {
fn target(&self) -> Option<Interned<Target>> {
None
}
}
impl<T: ?Sized + crate::phantom_const::PhantomConstValue> ToLiteralBits for PhantomConst<T> {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
Ok(Interned::default())
}
}

View file

@ -11,14 +11,15 @@ use crate::{
GetTarget, Target, TargetPathArrayElement, TargetPathBundleField,
TargetPathDynArrayElement, TargetPathElement,
},
CastTo, Expr, ExprEnum, Flow, HdlPartialEq, HdlPartialOrd, NotALiteralExpr, ReduceBits,
ToExpr, ToLiteralBits,
CastBitsTo as _, CastTo, CastToBits as _, Expr, ExprEnum, Flow, HdlPartialEq,
HdlPartialOrd, NotALiteralExpr, ReduceBits, ToExpr, ToLiteralBits,
},
int::{
Bool, BoolOrIntType, DynSize, IntType, KnownSize, SInt, SIntType, SIntValue, Size, UInt,
UIntType, UIntValue,
},
intern::{Intern, Interned},
phantom_const::{PhantomConst, PhantomConstValue},
reset::{
AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset, ToAsyncReset, ToReset,
ToSyncReset,
@ -1892,6 +1893,26 @@ impl ExprCastTo<Clock> for Clock {
}
}
impl<T: ?Sized + PhantomConstValue> ExprCastTo<()> for PhantomConst<T> {
fn cast_to(src: Expr<Self>, to_type: ()) -> Expr<()> {
src.cast_to_bits().cast_bits_to(to_type)
}
}
impl<T: ?Sized + PhantomConstValue> ExprCastTo<PhantomConst<T>> for () {
fn cast_to(src: Expr<Self>, to_type: PhantomConst<T>) -> Expr<PhantomConst<T>> {
src.cast_to_bits().cast_bits_to(to_type)
}
}
impl<T: ?Sized + PhantomConstValue, U: ?Sized + PhantomConstValue> ExprCastTo<PhantomConst<T>>
for PhantomConst<U>
{
fn cast_to(src: Expr<Self>, to_type: PhantomConst<T>) -> Expr<PhantomConst<T>> {
src.cast_to_bits().cast_bits_to(to_type)
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct FieldAccess<FieldType: Type = CanonicalType> {
base: Expr<Bundle>,
@ -2708,3 +2729,47 @@ impl<T: Type> ToExpr for Uninit<T> {
}
}
}
pub trait ExprIntoIterator: Type {
type Item: Type;
type ExprIntoIter: Iterator<Item = Expr<Self::Item>>;
fn expr_into_iter(e: Expr<Self>) -> Self::ExprIntoIter;
}
impl<T: ExprIntoIterator> IntoIterator for Expr<T> {
type Item = Expr<T::Item>;
type IntoIter = T::ExprIntoIter;
fn into_iter(self) -> Self::IntoIter {
T::expr_into_iter(self)
}
}
impl<T: ExprIntoIterator> IntoIterator for &'_ Expr<T> {
type Item = Expr<T::Item>;
type IntoIter = T::ExprIntoIter;
fn into_iter(self) -> Self::IntoIter {
T::expr_into_iter(*self)
}
}
impl<T: ExprIntoIterator> IntoIterator for &'_ mut Expr<T> {
type Item = Expr<T::Item>;
type IntoIter = T::ExprIntoIter;
fn into_iter(self) -> Self::IntoIter {
T::expr_into_iter(*self)
}
}
pub trait ExprFromIterator<A>: Type {
fn expr_from_iter<T: IntoIterator<Item = A>>(iter: T) -> Expr<Self>;
}
impl<This: ExprFromIterator<A>, A> FromIterator<A> for Expr<This> {
fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self {
This::expr_from_iter(iter)
}
}

View file

@ -15,7 +15,7 @@ use crate::{
target::{
Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathElement,
},
Expr, ExprEnum,
CastBitsTo, Expr, ExprEnum,
},
formal::FormalKind,
int::{Bool, DynSize, IntType, SIntValue, UInt, UIntValue},
@ -36,12 +36,11 @@ use crate::{
ty::{CanonicalType, Type},
util::{
const_str_array_is_strictly_ascending, BitSliceWriteWithBase, DebugAsRawString,
GenericConstBool,
GenericConstBool, HashMap, HashSet,
},
};
use bitvec::slice::BitSlice;
use clap::value_parser;
use hashbrown::{HashMap, HashSet};
use num_traits::Signed;
use serde::Serialize;
use std::{
@ -447,6 +446,7 @@ impl TypeState {
CanonicalType::AsyncReset(AsyncReset {}) => "AsyncReset".into(),
CanonicalType::SyncReset(SyncReset {}) => "UInt<1>".into(),
CanonicalType::Reset(Reset {}) => "Reset".into(),
CanonicalType::PhantomConst(_) => "{}".into(),
}
}
}
@ -1152,6 +1152,7 @@ impl<'a> Exporter<'a> {
| CanonicalType::Clock(_)
| CanonicalType::AsyncReset(_)
| CanonicalType::Reset(_) => format!("asUInt({value_str})"),
CanonicalType::PhantomConst(_) => "UInt<0>(0)".into(),
}
}
fn expr_cast_bits_to_bundle(
@ -1357,6 +1358,12 @@ impl<'a> Exporter<'a> {
CanonicalType::AsyncReset(_) => format!("asAsyncReset({value_str})"),
CanonicalType::SyncReset(_) => value_str,
CanonicalType::Reset(_) => unreachable!("Reset is not bit castable to"),
CanonicalType::PhantomConst(_) => {
let retval = self.module.ns.make_new("_cast_bits_to_phantom_const_expr");
definitions.add_definition_line(format_args!("{extra_indent}wire {retval}: {{}}"));
definitions.add_definition_line(format_args!("{extra_indent}invalidate {retval}"));
return retval.to_string();
}
}
}
fn expr_unary<T: Type>(
@ -1395,6 +1402,11 @@ impl<'a> Exporter<'a> {
ExprEnum::UIntLiteral(literal) => self.uint_literal(&literal),
ExprEnum::SIntLiteral(literal) => self.sint_literal(&literal),
ExprEnum::BoolLiteral(literal) => self.bool_literal(literal),
ExprEnum::PhantomConst(ty) => self.expr(
UInt[0].zero().cast_bits_to(ty.canonical()),
definitions,
const_ty,
),
ExprEnum::ArrayLiteral(array_literal) => {
self.array_literal_expr(array_literal, definitions, const_ty)
}
@ -2245,6 +2257,7 @@ impl<'a> Exporter<'a> {
ModuleBody::Extern(ExternModuleBody {
verilog_name,
parameters,
simulation: _,
}) => {
let verilog_name = Ident(verilog_name);
writeln!(body, "{indent}defname = {verilog_name}").unwrap();
@ -2608,7 +2621,7 @@ fn export_impl(
indent_depth: &indent_depth,
indent: " ",
},
seen_modules: HashSet::new(),
seen_modules: HashSet::default(),
unwritten_modules: VecDeque::new(),
global_ns,
module: ModuleState::default(),

View file

@ -2,27 +2,52 @@
// See Notices.txt for copyright information
use crate::{
array::ArrayType,
expr::{
target::{GetTarget, Target},
Expr, NotALiteralExpr, ToExpr, ToLiteralBits,
},
hdl,
intern::{Intern, Interned, Memoize},
sim::value::{SimValue, ToSimValueWithType},
source_location::SourceLocation,
ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties},
util::{interned_bit, ConstBool, ConstUsize, GenericConstBool, GenericConstUsize},
};
use bitvec::{order::Lsb0, slice::BitSlice, vec::BitVec};
use bitvec::{order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView};
use num_bigint::{BigInt, BigUint, Sign};
use num_traits::{Signed, Zero};
use num_traits::{One, Signed, Zero};
use serde::{
de::{DeserializeOwned, Error, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};
use std::{
borrow::{BorrowMut, Cow},
fmt,
marker::PhantomData,
num::NonZero,
ops::{Bound, Index, Not, Range, RangeBounds, RangeInclusive},
str::FromStr,
sync::Arc,
};
mod uint_in_range;
#[hdl]
pub type UIntInRangeType<Start: Size, End: Size> = uint_in_range::UIntInRangeType<Start, End>;
#[hdl]
pub type UIntInRange<const START: usize, const END: usize> =
UIntInRangeType<ConstUsize<START>, ConstUsize<END>>;
#[hdl]
pub type UIntInRangeInclusiveType<Start: Size, End: Size> =
uint_in_range::UIntInRangeInclusiveType<Start, End>;
#[hdl]
pub type UIntInRangeInclusive<const START: usize, const END: usize> =
UIntInRangeInclusiveType<ConstUsize<START>, ConstUsize<END>>;
mod sealed {
pub trait BoolOrIntTypeSealed {}
pub trait SizeSealed {}
@ -49,6 +74,16 @@ pub trait KnownSize:
+ IntoIterator<Item = Expr<Element>>
+ TryFrom<Vec<Expr<Element>>>
+ Into<Vec<Expr<Element>>>;
type ArraySimValue<Element: Type>: AsRef<[SimValue<Element>]>
+ AsMut<[SimValue<Element>]>
+ BorrowMut<[SimValue<Element>]>
+ 'static
+ Clone
+ std::fmt::Debug
+ IntoIterator<Item = SimValue<Element>>
+ TryFrom<Vec<SimValue<Element>>>
+ Into<Vec<SimValue<Element>>>
+ ToSimValueWithType<ArrayType<Element, Self>>;
}
macro_rules! known_widths {
@ -60,6 +95,7 @@ macro_rules! known_widths {
}> {
const SIZE: Self = Self;
type ArrayMatch<Element: Type> = [Expr<Element>; Self::VALUE];
type ArraySimValue<Element: Type> = [SimValue<Element>; Self::VALUE];
}
};
([2 $($rest:tt)*] $($bits:literal)+) => {
@ -72,6 +108,7 @@ macro_rules! known_widths {
impl KnownSize for ConstUsize<{2 $(* $rest)*}> {
const SIZE: Self = Self;
type ArrayMatch<Element: Type> = [Expr<Element>; Self::VALUE];
type ArraySimValue<Element: Type> = [SimValue<Element>; Self::VALUE];
}
};
}
@ -79,13 +116,31 @@ macro_rules! known_widths {
known_widths!([2 2 2 2 2 2 2 2 2]);
pub trait SizeType:
sealed::SizeTypeSealed + Copy + Ord + std::hash::Hash + std::fmt::Debug + Send + Sync + 'static
sealed::SizeTypeSealed
+ Copy
+ Ord
+ std::hash::Hash
+ std::fmt::Debug
+ Send
+ Sync
+ 'static
+ Serialize
+ DeserializeOwned
{
type Size: Size<SizeType = Self>;
}
pub trait Size:
sealed::SizeSealed + Copy + Ord + std::hash::Hash + std::fmt::Debug + Send + Sync + 'static
sealed::SizeSealed
+ Copy
+ Ord
+ std::hash::Hash
+ std::fmt::Debug
+ Send
+ Sync
+ 'static
+ Serialize
+ DeserializeOwned
{
type ArrayMatch<Element: Type>: AsRef<[Expr<Element>]>
+ AsMut<[Expr<Element>]>
@ -100,6 +155,16 @@ pub trait Size:
+ IntoIterator<Item = Expr<Element>>
+ TryFrom<Vec<Expr<Element>>>
+ Into<Vec<Expr<Element>>>;
type ArraySimValue<Element: Type>: AsRef<[SimValue<Element>]>
+ AsMut<[SimValue<Element>]>
+ BorrowMut<[SimValue<Element>]>
+ 'static
+ Clone
+ std::fmt::Debug
+ IntoIterator<Item = SimValue<Element>>
+ TryFrom<Vec<SimValue<Element>>>
+ Into<Vec<SimValue<Element>>>
+ ToSimValueWithType<ArrayType<Element, Self>>;
const KNOWN_VALUE: Option<usize>;
type SizeType: SizeType<Size = Self>
+ Copy
@ -125,6 +190,7 @@ impl SizeType for usize {
impl Size for DynSize {
type ArrayMatch<Element: Type> = Box<[Expr<Element>]>;
type ArraySimValue<Element: Type> = Box<[SimValue<Element>]>;
const KNOWN_VALUE: Option<usize> = None;
type SizeType = usize;
@ -147,6 +213,7 @@ impl<T: KnownSize> SizeType for T {
impl<T: KnownSize> Size for T {
type ArrayMatch<Element: Type> = <T as KnownSize>::ArrayMatch<Element>;
type ArraySimValue<Element: Type> = <T as KnownSize>::ArraySimValue<Element>;
const KNOWN_VALUE: Option<usize> = Some(T::VALUE);
@ -165,6 +232,305 @@ impl<T: KnownSize> Size for T {
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum ParseIntValueError {
Empty,
InvalidDigit,
MissingDigits,
InvalidRadix,
MissingType,
InvalidType,
TypeMismatch {
parsed_signed: bool,
parsed_width: usize,
expected_signed: bool,
expected_width: usize,
},
PosOverflow,
NegOverflow,
WidthOverflow,
MissingWidth,
}
impl std::error::Error for ParseIntValueError {}
impl fmt::Display for ParseIntValueError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::Empty => "can't parse integer from empty string",
Self::InvalidDigit => "invalid digit",
Self::MissingDigits => "missing digits",
Self::InvalidRadix => "invalid radix",
Self::MissingType => "missing type",
Self::InvalidType => "invalid type",
Self::TypeMismatch {
parsed_signed,
parsed_width,
expected_signed,
expected_width,
} => {
return write!(
f,
"type mismatch: parsed type {parsed_signed_str}{parsed_width}, \
expected type {expected_signed_str}{expected_width}",
parsed_signed_str = if *parsed_signed { "i" } else { "u" },
expected_signed_str = if *expected_signed { "i" } else { "u" },
);
}
Self::PosOverflow => "value too large to fit in type",
Self::NegOverflow => "value too small to fit in type",
Self::WidthOverflow => "width is too large",
Self::MissingWidth => "missing width",
})
}
}
fn parse_int_value(
s: &str,
type_is_signed: bool,
type_width: Option<usize>,
parse_type: bool,
) -> Result<Arc<BitVec>, ParseIntValueError> {
if !parse_type && type_width.is_none() {
return Err(ParseIntValueError::MissingWidth);
}
let mut s = s.trim();
if s.is_empty() {
return Err(ParseIntValueError::Empty);
}
let negative = match s.bytes().next() {
Some(ch @ (b'+' | b'-')) => {
s = s[1..].trim_start();
ch == b'-'
}
_ => false,
};
let radix = match s.bytes().next() {
Some(b'0') => match s.bytes().nth(1) {
Some(b'x' | b'X') => {
s = &s[2..];
16
}
Some(b'b' | b'B') => {
s = &s[2..];
2
}
Some(b'o' | b'O') => {
s = &s[2..];
8
}
_ => 10,
},
Some(b'1'..=b'9') => 10,
_ => return Err(ParseIntValueError::InvalidDigit),
};
let mut any_digits = false;
let digits_end = s
.as_bytes()
.iter()
.position(|&ch| {
if ch == b'_' {
false
} else if (ch as char).to_digit(radix).is_some() {
any_digits = true;
false
} else {
true
}
})
.unwrap_or(s.len());
let digits = &s[..digits_end];
s = &s[digits_end..];
if !any_digits {
return Err(ParseIntValueError::MissingDigits);
}
let width = if parse_type {
const HDL_PREFIX: &[u8] = b"hdl_";
let mut missing_type = ParseIntValueError::MissingType;
if s.as_bytes()
.get(..HDL_PREFIX.len())
.is_some_and(|bytes| bytes.eq_ignore_ascii_case(HDL_PREFIX))
{
s = &s[HDL_PREFIX.len()..];
missing_type = ParseIntValueError::InvalidType;
}
let signed = match s.bytes().next() {
Some(b'u' | b'U') => false,
Some(b'i' | b'I') => true,
Some(_) => return Err(ParseIntValueError::InvalidType),
None => return Err(missing_type),
};
s = &s[1..];
let mut width = 0usize;
let mut any_digits = false;
for ch in s.bytes() {
let digit = (ch as char)
.to_digit(10)
.ok_or(ParseIntValueError::InvalidDigit)?;
any_digits = true;
width = width
.checked_mul(10)
.and_then(|v| v.checked_add(digit as usize))
.ok_or(ParseIntValueError::WidthOverflow)?;
}
if !any_digits {
return Err(ParseIntValueError::MissingDigits);
}
if width > <BitSlice>::MAX_BITS {
return Err(ParseIntValueError::WidthOverflow);
}
let expected_width = type_width.unwrap_or(width);
if type_is_signed != signed || expected_width != width {
let expected_width = type_width.unwrap_or(width);
return Err(ParseIntValueError::TypeMismatch {
parsed_signed: signed,
parsed_width: width,
expected_signed: type_is_signed,
expected_width,
});
}
width
} else {
if !s.is_empty() {
return Err(ParseIntValueError::InvalidDigit);
}
type_width.expect("checked earlier")
};
if !type_is_signed && negative {
return Err(ParseIntValueError::InvalidDigit);
}
if radix == 10 {
let mut value: BigInt = digits
.replace("_", "")
.parse()
.expect("checked that the digits are valid already");
if negative {
value = -value;
}
let uint_value: UIntValue = UInt::new(width).from_bigint_wrapping(&value);
if value.is_zero() {
Ok(uint_value.into_bits())
} else {
for i in 0..width {
value.set_bit(i as u64, type_is_signed && negative);
}
if value.is_zero() {
Ok(uint_value.into_bits())
} else if type_is_signed && negative {
if value.sign() == Sign::Minus && value.magnitude().is_one() {
Ok(uint_value.into_bits())
} else {
Err(ParseIntValueError::NegOverflow)
}
} else {
Err(ParseIntValueError::PosOverflow)
}
}
} else {
let mut value = BitVec::repeat(false, width);
let bits_per_digit = match radix {
2 => 1,
8 => 3,
16 => 4,
_ => unreachable!(),
};
let mut digits = digits
.bytes()
.rev()
.filter_map(|ch| (ch as char).to_digit(radix));
let overflow_error = if negative {
ParseIntValueError::NegOverflow
} else {
ParseIntValueError::PosOverflow
};
for chunk in value.chunks_mut(bits_per_digit) {
if let Some(mut digit) = digits.next() {
let digit_bits = &mut digit.view_bits_mut::<Lsb0>()[..chunk.len()];
chunk.clone_from_bitslice(digit_bits);
digit_bits.fill(false);
if digit != 0 {
return Err(overflow_error);
}
} else {
break;
}
}
for digit in digits {
if digit != 0 {
return Err(overflow_error);
}
}
let negative_zero = if negative {
// negating a value happens in three regions:
// * the least-significant zeros, which are left as zeros
// * the least-significant one bit, which is left as a one bit
// * all the most-significant bits, which are inverted
// e.g.:
const {
let inp = 0b1010_1_000_u8;
let out = 0b0101_1_000_u8;
assert!(inp.wrapping_neg() == out);
};
if let Some(first_one) = value.first_one() {
let most_significant_bits = &mut value[first_one + 1..];
// modifies in-place despite using `Not::not`
let _ = Not::not(most_significant_bits);
false
} else {
true
}
} else {
false
};
if !negative_zero && type_is_signed && negative != value[value.len() - 1] {
Err(overflow_error)
} else {
Ok(Arc::new(value))
}
}
}
fn deserialize_int_value<'de, D: Deserializer<'de>>(
deserializer: D,
type_is_signed: bool,
type_width: Option<usize>,
) -> Result<Arc<BitVec>, D::Error> {
struct IntValueVisitor {
type_is_signed: bool,
type_width: Option<usize>,
}
impl<'de> Visitor<'de> for IntValueVisitor {
type Value = Arc<BitVec>;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(if self.type_is_signed {
"SIntValue"
} else {
"UIntValue"
})?;
if let Some(type_width) = self.type_width {
write!(f, "<{type_width}>")?;
}
Ok(())
}
fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
parse_int_value(v, self.type_is_signed, self.type_width, true).map_err(E::custom)
}
fn visit_bytes<E: Error>(self, v: &[u8]) -> Result<Self::Value, E> {
match std::str::from_utf8(v) {
Ok(v) => self.visit_str(v),
Err(_) => Err(Error::invalid_value(serde::de::Unexpected::Bytes(v), &self)),
}
}
}
deserializer.deserialize_str(IntValueVisitor {
type_is_signed,
type_width,
})
}
macro_rules! impl_int {
($pretty_name:ident, $name:ident, $generic_name:ident, $value:ident, $SIGNED:literal) => {
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
@ -188,19 +554,14 @@ macro_rules! impl_int {
pub const $name: $generic_name = $generic_name;
impl<Width: Size> $name<Width> {
pub fn new(width: Width::SizeType) -> Self {
pub const fn new(width: Width::SizeType) -> Self {
Self { width }
}
pub fn width(self) -> usize {
Width::as_usize(self.width)
}
pub fn type_properties(self) -> TypeProperties {
TypeProperties {
is_passive: true,
is_storable: true,
is_castable_from_bits: true,
bit_width: self.width(),
}
self.as_dyn_int().type_properties_dyn()
}
pub fn bits_from_bigint_wrapping(self, v: &BigInt) -> BitVec {
BoolOrIntType::bits_from_bigint_wrapping(self, v)
@ -263,6 +624,12 @@ macro_rules! impl_int {
}
Expr::from_dyn_int(MemoizeBitsToExpr.get_cow(bits))
}
fn from_str_without_ty(
self,
s: &str,
) -> Result<Self::Value, <Self::Value as FromStr>::Err> {
parse_int_value(s, $SIGNED, Some(self.width()), false).map(Self::Value::new)
}
}
impl<Width: Size> IntType for $name<Width> {
@ -270,12 +637,20 @@ macro_rules! impl_int {
}
impl $name {
pub fn new_dyn(width: usize) -> Self {
pub const fn new_dyn(width: usize) -> Self {
Self { width }
}
pub fn bits_to_bigint(bits: &BitSlice) -> BigInt {
<Self as BoolOrIntType>::bits_to_bigint(bits)
}
pub const fn type_properties_dyn(self) -> TypeProperties {
TypeProperties {
is_passive: true,
is_storable: true,
is_castable_from_bits: true,
bit_width: self.width,
}
}
}
impl<Width: KnownSize> $name<Width> {
@ -287,6 +662,7 @@ macro_rules! impl_int {
impl<Width: Size> Type for $name<Width> {
type BaseType = $pretty_name;
type MaskType = Bool;
type SimValue = $value<Width>;
impl_match_variant_as_self!();
fn mask_type(&self) -> Self::MaskType {
Bool
@ -297,7 +673,7 @@ macro_rules! impl_int {
#[track_caller]
fn from_canonical(canonical_type: CanonicalType) -> Self {
let CanonicalType::$pretty_name(retval) = canonical_type else {
panic!("expected {}", stringify!($name));
panic!("expected {}", stringify!($pretty_name));
};
$name {
width: Width::from_usize(retval.width),
@ -306,20 +682,78 @@ macro_rules! impl_int {
fn source_location() -> SourceLocation {
SourceLocation::builtin()
}
fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue {
assert_eq!(bits.len(), self.width());
$value::new(Arc::new(bits.to_bitvec()))
}
fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) {
assert_eq!(bits.len(), self.width());
assert_eq!(value.width(), self.width());
value.bits_mut().copy_from_bitslice(bits);
}
fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) {
assert_eq!(bits.len(), self.width());
assert_eq!(value.width(), self.width());
bits.copy_from_bitslice(value.bits());
}
}
impl<Width: KnownSize> Default for $name<Width> {
fn default() -> Self {
Self::TYPE
}
}
impl<Width: KnownSize> StaticType for $name<Width> {
const TYPE: Self = Self { width: Width::SIZE };
const MASK_TYPE: Self::MaskType = Bool;
const TYPE_PROPERTIES: TypeProperties = TypeProperties {
is_passive: true,
is_storable: true,
is_castable_from_bits: true,
bit_width: Width::VALUE,
};
const TYPE_PROPERTIES: TypeProperties = $name {
width: Width::VALUE,
}
.type_properties_dyn();
const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES;
}
impl<Width: Size> Serialize for $name<Width> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.canonical().serialize(serializer)
}
}
impl<'de, Width: Size> Deserialize<'de> for $name<Width> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let name = |width| -> String {
if let Some(width) = width {
format!("a {}<{width}>", stringify!($pretty_name))
} else {
format!("a {}", stringify!($pretty_name))
}
};
match CanonicalType::deserialize(deserializer)? {
CanonicalType::$pretty_name(retval) => {
if let Some(width) = Width::try_from_usize(retval.width()) {
Ok($name { width })
} else {
Err(Error::invalid_value(
serde::de::Unexpected::Other(&name(Some(retval.width()))),
&&*name(Width::KNOWN_VALUE),
))
}
}
ty => Err(Error::invalid_value(
serde::de::Unexpected::Other(ty.as_serde_unexpected_str()),
&&*name(Width::KNOWN_VALUE),
)),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
pub struct $generic_name;
@ -337,7 +771,7 @@ macro_rules! impl_int {
_phantom: PhantomData<Width>,
}
impl<Width: Size> fmt::Debug for $value<Width> {
impl<Width: Size> fmt::Display for $value<Width> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let value = self.to_bigint();
let (sign, magnitude) = value.into_parts();
@ -351,15 +785,38 @@ 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> fmt::Debug for $value<Width> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
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> std::str::FromStr for $value<Width> {
type Err = ParseIntValueError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
parse_int_value(s, $SIGNED, Width::KNOWN_VALUE, true).map(Self::new)
}
}
impl<Width: Size> Serialize for $value<Width> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.to_string().serialize(serializer)
}
}
impl<'de, Width: Size> Deserialize<'de> for $value<Width> {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
deserialize_int_value(deserializer, $SIGNED, Width::KNOWN_VALUE).map(Self::new)
}
}
impl<Width: Size> PartialOrd for $value<Width> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
if self.width() != other.width() {
return None;
}
Some(self.to_bigint().cmp(&other.to_bigint()))
}
}
@ -401,6 +858,9 @@ macro_rules! impl_int {
pub fn bits(&self) -> &Arc<BitVec> {
&self.bits
}
pub fn bits_mut(&mut self) -> &mut BitSlice {
Arc::<BitVec>::make_mut(&mut self.bits)
}
}
impl<Width: Size> ToLiteralBits for $value<Width> {
@ -455,6 +915,10 @@ impl UInt {
let v: BigUint = v.into();
Self::new(v.bits().try_into().expect("too big"))
}
/// gets the smallest `UInt` that fits `v` losslessly
pub const fn for_value_usize(v: usize) -> Self {
Self::new((usize::BITS - v.leading_zeros()) as usize)
}
/// gets the smallest `UInt` that fits `r` losslessly, panics if `r` is empty
#[track_caller]
pub fn range(r: Range<impl Into<BigUint>>) -> Self {
@ -465,6 +929,12 @@ impl UInt {
}
/// gets the smallest `UInt` that fits `r` losslessly, panics if `r` is empty
#[track_caller]
pub const fn range_usize(r: Range<usize>) -> Self {
assert!(r.end != 0, "empty range");
Self::range_inclusive_usize(r.start..=(r.end - 1))
}
/// gets the smallest `UInt` that fits `r` losslessly, panics if `r` is empty
#[track_caller]
pub fn range_inclusive(r: RangeInclusive<impl Into<BigUint>>) -> Self {
let (start, end) = r.into_inner();
let start: BigUint = start.into();
@ -474,6 +944,16 @@ impl UInt {
// so must not take more bits than `end`
Self::for_value(end)
}
/// gets the smallest `UInt` that fits `r` losslessly, panics if `r` is empty
#[track_caller]
pub const fn range_inclusive_usize(r: RangeInclusive<usize>) -> Self {
let start = *r.start();
let end = *r.end();
assert!(start <= end, "empty range");
// no need to check `start`` since it's no larger than `end`
// so must not take more bits than `end`
Self::for_value_usize(end)
}
}
impl SInt {
@ -580,14 +1060,17 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed {
type Width: Size;
type Signed: GenericConstBool;
type Value: Clone
+ Ord
+ PartialOrd
+ Eq
+ std::hash::Hash
+ fmt::Debug
+ fmt::Display
+ Send
+ Sync
+ 'static
+ ToExpr<Type = Self>
+ Into<BigInt>;
+ Into<BigInt>
+ std::str::FromStr;
fn width(self) -> usize;
fn new(width: <Self::Width as Size>::SizeType) -> Self;
fn new_static() -> Self
@ -621,6 +1104,12 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed {
let bitslice = &BitSlice::<u8, Lsb0>::from_slice(&bytes)[..width];
bits.clone_from_bitslice(bitslice);
}
fn bits_equal_bigint_wrapping(v: &BigInt, bits: &BitSlice) -> bool {
bits.iter()
.by_vals()
.enumerate()
.all(|(bit_index, bit): (usize, bool)| v.bit(bit_index as u64) == bit)
}
fn bits_to_bigint(bits: &BitSlice) -> BigInt {
let sign_byte = if Self::Signed::VALUE && bits.last().as_deref().copied().unwrap_or(false) {
0xFF
@ -654,9 +1143,12 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed {
bytes, bit_width,
)))
}
fn from_str_without_ty(self, s: &str) -> Result<Self::Value, <Self::Value as FromStr>::Err>;
}
pub trait IntType: BoolOrIntType<BaseType = <Self as IntType>::Dyn> {
pub trait IntType:
BoolOrIntType<BaseType = <Self as IntType>::Dyn, Value: FromStr<Err = ParseIntValueError>>
{
type Dyn: IntType<Dyn = Self::Dyn, Signed = Self::Signed, Width = DynSize>;
fn as_dyn_int(self) -> Self::Dyn {
Self::new_dyn(self.width())
@ -696,7 +1188,7 @@ pub trait IntType: BoolOrIntType<BaseType = <Self as IntType>::Dyn> {
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
pub struct Bool;
impl sealed::BoolOrIntTypeSealed for Bool {}
@ -728,6 +1220,10 @@ impl BoolOrIntType for Bool {
assert_eq!(bits.len(), 1);
bits[0]
}
fn from_str_without_ty(self, s: &str) -> Result<Self::Value, <Self::Value as FromStr>::Err> {
FromStr::from_str(s)
}
}
impl Bool {
@ -742,6 +1238,7 @@ impl Bool {
impl Type for Bool {
type BaseType = Bool;
type MaskType = Bool;
type SimValue = bool;
impl_match_variant_as_self!();
fn mask_type(&self) -> Self::MaskType {
Bool
@ -759,6 +1256,18 @@ impl Type for Bool {
fn source_location() -> SourceLocation {
SourceLocation::builtin()
}
fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue {
assert_eq!(bits.len(), 1);
bits[0]
}
fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) {
assert_eq!(bits.len(), 1);
*value = bits[0];
}
fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) {
assert_eq!(bits.len(), 1);
bits.set(0, *value);
}
}
impl StaticType for Bool {
@ -783,6 +1292,13 @@ impl ToLiteralBits for bool {
mod tests {
use super::*;
#[test]
fn test_different_value_widths_compare_ne() {
// interning relies on [SU]IntValue with different `width` comparing not equal
assert_ne!(UInt[3].from_int_wrapping(0), UInt[4].from_int_wrapping(0));
assert_ne!(SInt[3].from_int_wrapping(0), SInt[4].from_int_wrapping(0));
}
#[test]
fn test_uint_for_value() {
assert_eq!(UInt::for_value(0u8).width, 0);
@ -805,4 +1321,104 @@ mod tests {
assert_eq!(SInt::for_value(3).width, 3);
assert_eq!(SInt::for_value(4).width, 4);
}
#[test]
fn test_serde_round_trip() {
use serde_json::json;
#[track_caller]
fn check<T: Serialize + DeserializeOwned + PartialEq + fmt::Debug>(
value: T,
expected: serde_json::Value,
) {
assert_eq!(serde_json::to_value(&value).unwrap(), expected);
assert_eq!(value, T::deserialize(expected).unwrap());
}
check(UInt[0], json! { { "UInt": { "width": 0 } } });
check(UInt::<0>::TYPE, json! { { "UInt": { "width": 0 } } });
check(UInt::<35>::TYPE, json! { { "UInt": { "width": 35 } } });
check(SInt[0], json! { { "SInt": { "width": 0 } } });
check(SInt::<0>::TYPE, json! { { "SInt": { "width": 0 } } });
check(SInt::<35>::TYPE, json! { { "SInt": { "width": 35 } } });
check(Bool, json! { "Bool" });
check(UIntValue::from(0u8), json! { "0x0_u8" });
check(SIntValue::from(-128i8), json! { "-0x80_i8" });
check(UInt[3].from_int_wrapping(5), json! { "0x5_u3" });
check(UInt[12].from_int_wrapping(0x1123), json! { "0x123_u12" });
check(SInt[12].from_int_wrapping(0xFEE), json! { "-0x12_i12" });
check(SInt[12].from_int_wrapping(0x7EE), json! { "0x7EE_i12" });
}
#[test]
fn test_deserialize() {
use serde_json::json;
#[track_caller]
fn check<T: DeserializeOwned + fmt::Debug + PartialEq>(
expected: Result<T, &str>,
input: serde_json::Value,
) {
let mut error = String::new();
let value = T::deserialize(input).map_err(|e| -> &str {
error = e.to_string();
&error
});
assert_eq!(value, expected);
}
check::<UInt<0>>(
Err("invalid value: a UInt<2>, expected a UInt<0>"),
json! { { "UInt": { "width": 2 } } },
);
check::<UInt<0>>(
Err("invalid value: a Bool, expected a UInt<0>"),
json! { "Bool" },
);
check::<SInt<0>>(
Err("invalid value: a Bool, expected a SInt<0>"),
json! { "Bool" },
);
check::<UInt>(
Err("invalid value: a Bool, expected a UInt"),
json! { "Bool" },
);
check::<SInt>(
Err("invalid value: a Bool, expected a SInt"),
json! { "Bool" },
);
check::<UIntValue>(Err("value too large to fit in type"), json! { "2_u1" });
check::<UIntValue>(Err("value too large to fit in type"), json! { "10_u1" });
check::<UIntValue>(Err("value too large to fit in type"), json! { "0x2_u1" });
check::<UIntValue>(Err("value too large to fit in type"), json! { "0b10_u1" });
check::<UIntValue>(Err("value too large to fit in type"), json! { "0o2_u1" });
check::<SIntValue>(Err("value too large to fit in type"), json! { "0o377_i8" });
check::<SIntValue>(Err("value too large to fit in type"), json! { "0o200_i8" });
check(Ok(SInt[8].from_int_wrapping(i8::MAX)), json! { "0o177_i8" });
check::<SIntValue>(Err("value too small to fit in type"), json! { "-0o201_i8" });
check::<SIntValue>(Err("value too small to fit in type"), json! { "-0o377_i8" });
check::<SIntValue>(Err("value too small to fit in type"), json! { "-0o400_i8" });
check::<SIntValue>(
Err("value too small to fit in type"),
json! { "-0o4000_i8" },
);
check(Ok(UIntValue::from(0u8)), json! { "0_u8" });
check(Ok(UIntValue::from(0u8)), json! { "0b0_u8" });
check(Ok(UIntValue::from(0u8)), json! { "00_u8" });
check(Ok(UIntValue::from(0u8)), json! { "0x0_u8" });
check(Ok(UIntValue::from(0u8)), json! { "0o0_u8" });
check(Ok(SIntValue::from(-128i8)), json! { "-0x000_80_i8" });
check(Ok(SIntValue::from(-128i8)), json! { "-0o002_00_hdl_i8" });
check(Ok(SIntValue::from(-128i8)), json! { "-0b1__000_0000_i8" });
check(Ok(UInt[3].from_int_wrapping(5)), json! { " + 0x5_u3 " });
check(
Ok(UInt[12].from_int_wrapping(0x1123)),
json! { "0x1_2_3_hdl_u12" },
);
check(Ok(SInt[12].from_int_wrapping(0xFEE)), json! { "-0x12_i12" });
check(
Ok(SInt[12].from_int_wrapping(0x7EE)),
json! { " + \t0x7__E_e_i012\n" },
);
check(Ok(SInt[0].from_int_wrapping(0)), json! { "-0i0" });
check(Ok(SInt[1].from_int_wrapping(0)), json! { "-0i1" });
check(Ok(SInt[0].from_int_wrapping(0)), json! { "-0x0i0" });
check(Ok(SInt[1].from_int_wrapping(0)), json! { "-0x0i1" });
}
}

View file

@ -0,0 +1,614 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
bundle::{Bundle, BundleField, BundleType, BundleTypePropertiesBuilder, NoBuilder},
expr::{
ops::{ExprCastTo, ExprPartialEq, ExprPartialOrd},
CastBitsTo, CastTo, CastToBits, Expr, HdlPartialEq, HdlPartialOrd,
},
int::{Bool, DynSize, KnownSize, Size, SizeType, UInt, UIntType},
intern::{Intern, Interned},
phantom_const::PhantomConst,
sim::value::{SimValue, SimValuePartialEq, ToSimValueWithType},
source_location::SourceLocation,
ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties},
};
use bitvec::{order::Lsb0, slice::BitSlice, view::BitView};
use serde::{
de::{value::UsizeDeserializer, Error, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};
use std::{fmt, marker::PhantomData, ops::Index};
const UINT_IN_RANGE_TYPE_FIELD_NAMES: [&'static str; 2] = ["value", "range"];
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
pub struct UIntInRangeMaskType {
value: Bool,
range: PhantomConstRangeMaskType,
}
impl Type for UIntInRangeMaskType {
type BaseType = Bundle;
type MaskType = Self;
type SimValue = bool;
impl_match_variant_as_self!();
fn mask_type(&self) -> Self::MaskType {
*self
}
fn canonical(&self) -> CanonicalType {
CanonicalType::Bundle(Bundle::new(self.fields()))
}
fn from_canonical(canonical_type: CanonicalType) -> Self {
let fields = Bundle::from_canonical(canonical_type).fields();
let [BundleField {
name: value_name,
flipped: false,
ty: value,
}, BundleField {
name: range_name,
flipped: false,
ty: range,
}] = *fields
else {
panic!("expected UIntInRangeMaskType");
};
assert_eq!([&*value_name, &*range_name], UINT_IN_RANGE_TYPE_FIELD_NAMES);
let value = Bool::from_canonical(value);
let range = PhantomConstRangeMaskType::from_canonical(range);
Self { value, range }
}
fn source_location() -> SourceLocation {
SourceLocation::builtin()
}
fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue {
Bool.sim_value_from_bits(bits)
}
fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) {
Bool.sim_value_clone_from_bits(value, bits);
}
fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) {
Bool.sim_value_to_bits(value, bits);
}
}
impl BundleType for UIntInRangeMaskType {
type Builder = NoBuilder;
type FilledBuilder = Expr<UIntInRangeMaskType>;
fn fields(&self) -> Interned<[BundleField]> {
let [value_name, range_name] = UINT_IN_RANGE_TYPE_FIELD_NAMES;
let Self { value, range } = self;
[
BundleField {
name: value_name.intern(),
flipped: false,
ty: value.canonical(),
},
BundleField {
name: range_name.intern(),
flipped: false,
ty: range.canonical(),
},
][..]
.intern()
}
}
impl StaticType for UIntInRangeMaskType {
const TYPE: Self = Self {
value: Bool,
range: PhantomConstRangeMaskType::TYPE,
};
const MASK_TYPE: Self::MaskType = Self::TYPE;
const TYPE_PROPERTIES: TypeProperties = BundleTypePropertiesBuilder::new()
.field(false, Bool::TYPE_PROPERTIES)
.field(false, PhantomConstRangeMaskType::TYPE_PROPERTIES)
.finish();
const MASK_TYPE_PROPERTIES: TypeProperties = Self::TYPE_PROPERTIES;
}
impl ToSimValueWithType<UIntInRangeMaskType> for bool {
fn to_sim_value_with_type(&self, ty: UIntInRangeMaskType) -> SimValue<UIntInRangeMaskType> {
SimValue::from_value(ty, *self)
}
}
impl ExprCastTo<Bool> for UIntInRangeMaskType {
fn cast_to(src: Expr<Self>, to_type: Bool) -> Expr<Bool> {
src.cast_to_bits().cast_to(to_type)
}
}
impl ExprCastTo<UIntInRangeMaskType> for Bool {
fn cast_to(src: Expr<Self>, to_type: UIntInRangeMaskType) -> Expr<UIntInRangeMaskType> {
src.cast_to_static::<UInt<1>>().cast_bits_to(to_type)
}
}
impl ExprPartialEq<Self> for UIntInRangeMaskType {
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
lhs.cast_to_bits().cmp_eq(rhs.cast_to_bits())
}
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
lhs.cast_to_bits().cmp_ne(rhs.cast_to_bits())
}
}
impl SimValuePartialEq<Self> for UIntInRangeMaskType {
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Self>) -> bool {
**this == **other
}
}
type PhantomConstRangeMaskType = <PhantomConst<SerdeRange<DynSize, DynSize>> as Type>::MaskType;
#[derive(Default, Copy, Clone, Debug)]
struct RangeParseError;
macro_rules! define_uint_in_range_type {
(
$UIntInRange:ident,
$UIntInRangeType:ident,
$UIntInRangeTypeWithoutGenerics:ident,
$UIntInRangeTypeWithStart:ident,
$SerdeRange:ident,
$range_operator_str:literal,
|$uint_range_usize_start:ident, $uint_range_usize_end:ident| $uint_range_usize:expr,
) => {
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
struct $SerdeRange<Start: Size, End: Size> {
start: Start::SizeType,
end: End::SizeType,
}
impl<Start: KnownSize, End: KnownSize> Default for $SerdeRange<Start, End> {
fn default() -> Self {
Self {
start: Start::SIZE,
end: End::SIZE,
}
}
}
impl std::str::FromStr for $SerdeRange<DynSize, DynSize> {
type Err = RangeParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let Some((start, end)) = s.split_once($range_operator_str) else {
return Err(RangeParseError);
};
if start.is_empty()
|| start.bytes().any(|b| !b.is_ascii_digit())
|| end.is_empty()
|| end.bytes().any(|b| !b.is_ascii_digit())
{
return Err(RangeParseError);
}
let start = start.parse().map_err(|_| RangeParseError)?;
let end = end.parse().map_err(|_| RangeParseError)?;
let retval = Self { start, end };
if retval.is_empty() {
Err(RangeParseError)
} else {
Ok(retval)
}
}
}
impl<Start: Size, End: Size> fmt::Display for $SerdeRange<Start, End> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { start, end } = *self;
write!(
f,
"{}{}{}",
Start::as_usize(start),
$range_operator_str,
End::as_usize(end),
)
}
}
impl<Start: Size, End: Size> Serialize for $SerdeRange<Start, End> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.collect_str(self)
}
}
impl<'de, Start: Size, End: Size> Deserialize<'de> for $SerdeRange<Start, End> {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct SerdeRangeVisitor<Start: Size, End: Size>(PhantomData<(Start, End)>);
impl<'de, Start: Size, End: Size> Visitor<'de> for SerdeRangeVisitor<Start, End> {
type Value = $SerdeRange<Start, End>;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("a string with format \"")?;
if let Some(start) = Start::KNOWN_VALUE {
write!(f, "{start}")?;
} else {
f.write_str("<int>")?;
};
f.write_str($range_operator_str)?;
if let Some(end) = End::KNOWN_VALUE {
write!(f, "{end}")?;
} else {
f.write_str("<int>")?;
};
f.write_str("\" that is a non-empty range")
}
fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
let $SerdeRange::<DynSize, DynSize> { start, end } =
v.parse().map_err(|_| {
Error::invalid_value(serde::de::Unexpected::Str(v), &self)
})?;
let start =
Start::SizeType::deserialize(UsizeDeserializer::<E>::new(start))?;
let end = End::SizeType::deserialize(UsizeDeserializer::<E>::new(end))?;
Ok($SerdeRange { start, end })
}
fn visit_bytes<E: Error>(self, v: &[u8]) -> Result<Self::Value, E> {
match std::str::from_utf8(v) {
Ok(v) => self.visit_str(v),
Err(_) => {
Err(Error::invalid_value(serde::de::Unexpected::Bytes(v), &self))
}
}
}
}
deserializer.deserialize_str(SerdeRangeVisitor(PhantomData))
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct $UIntInRangeType<Start: Size, End: Size> {
value: UInt,
range: PhantomConst<$SerdeRange<Start, End>>,
}
impl<Start: Size, End: Size> $UIntInRangeType<Start, End> {
fn from_phantom_const_range(range: PhantomConst<$SerdeRange<Start, End>>) -> Self {
let $SerdeRange { start, end } = *range.get();
let $uint_range_usize_start = Start::as_usize(start);
let $uint_range_usize_end = End::as_usize(end);
Self {
value: $uint_range_usize,
range,
}
}
pub fn new(start: Start::SizeType, end: End::SizeType) -> Self {
Self::from_phantom_const_range(PhantomConst::new(
$SerdeRange { start, end }.intern_sized(),
))
}
}
impl<Start: Size, End: Size> fmt::Debug for $UIntInRangeType<Start, End> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { value, range } = self;
let $SerdeRange { start, end } = *range.get();
f.debug_struct(&format!(
"{}<{}, {}>",
stringify!($UIntInRange),
Start::as_usize(start),
End::as_usize(end),
))
.field("value", value)
.finish_non_exhaustive()
}
}
impl<Start: Size, End: Size> Type for $UIntInRangeType<Start, End> {
type BaseType = Bundle;
type MaskType = UIntInRangeMaskType;
type SimValue = usize;
impl_match_variant_as_self!();
fn mask_type(&self) -> Self::MaskType {
UIntInRangeMaskType::TYPE
}
fn canonical(&self) -> CanonicalType {
CanonicalType::Bundle(Bundle::new(self.fields()))
}
fn from_canonical(canonical_type: CanonicalType) -> Self {
let fields = Bundle::from_canonical(canonical_type).fields();
let [BundleField {
name: value_name,
flipped: false,
ty: value,
}, BundleField {
name: range_name,
flipped: false,
ty: range,
}] = *fields
else {
panic!("expected {}", stringify!($UIntInRange));
};
assert_eq!([&*value_name, &*range_name], UINT_IN_RANGE_TYPE_FIELD_NAMES);
let value = UInt::from_canonical(value);
let range = PhantomConst::<$SerdeRange<Start, End>>::from_canonical(range);
let retval = Self::from_phantom_const_range(range);
assert_eq!(retval, Self { value, range });
retval
}
fn source_location() -> SourceLocation {
SourceLocation::builtin()
}
fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue {
let mut retval = 0usize;
retval.view_bits_mut::<Lsb0>()[..bits.len()].clone_from_bitslice(bits);
retval
}
fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) {
*value = self.sim_value_from_bits(bits);
}
fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) {
bits.clone_from_bitslice(&value.view_bits::<Lsb0>()[..bits.len()]);
}
}
impl<Start: Size, End: Size> BundleType for $UIntInRangeType<Start, End> {
type Builder = NoBuilder;
type FilledBuilder = Expr<Self>;
fn fields(&self) -> Interned<[BundleField]> {
let [value_name, range_name] = UINT_IN_RANGE_TYPE_FIELD_NAMES;
let Self { value, range } = self;
[
BundleField {
name: value_name.intern(),
flipped: false,
ty: value.canonical(),
},
BundleField {
name: range_name.intern(),
flipped: false,
ty: range.canonical(),
},
][..]
.intern()
}
}
impl<Start: KnownSize, End: KnownSize> Default for $UIntInRangeType<Start, End> {
fn default() -> Self {
Self::TYPE
}
}
impl<Start: KnownSize, End: KnownSize> StaticType for $UIntInRangeType<Start, End> {
const TYPE: Self = {
let $uint_range_usize_start = Start::VALUE;
let $uint_range_usize_end = End::VALUE;
Self {
value: $uint_range_usize,
range: PhantomConst::<$SerdeRange<Start, End>>::TYPE,
}
};
const MASK_TYPE: Self::MaskType = UIntInRangeMaskType::TYPE;
const TYPE_PROPERTIES: TypeProperties = BundleTypePropertiesBuilder::new()
.field(false, Self::TYPE.value.type_properties_dyn())
.field(
false,
PhantomConst::<$SerdeRange<Start, End>>::TYPE_PROPERTIES,
)
.finish();
const MASK_TYPE_PROPERTIES: TypeProperties = UIntInRangeMaskType::TYPE_PROPERTIES;
}
impl<Start: Size, End: Size> ToSimValueWithType<$UIntInRangeType<Start, End>> for usize {
fn to_sim_value_with_type(
&self,
ty: $UIntInRangeType<Start, End>,
) -> SimValue<$UIntInRangeType<Start, End>> {
SimValue::from_value(ty, *self)
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
pub struct $UIntInRangeTypeWithoutGenerics;
#[allow(non_upper_case_globals)]
pub const $UIntInRangeType: $UIntInRangeTypeWithoutGenerics =
$UIntInRangeTypeWithoutGenerics;
impl<StartSize: SizeType> Index<StartSize> for $UIntInRangeTypeWithoutGenerics {
type Output = $UIntInRangeTypeWithStart<StartSize::Size>;
fn index(&self, start: StartSize) -> &Self::Output {
Interned::into_inner($UIntInRangeTypeWithStart(start).intern_sized())
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct $UIntInRangeTypeWithStart<Start: Size>(Start::SizeType);
impl<Start: Size, EndSize: SizeType<Size = End>, End: Size<SizeType = EndSize>>
Index<EndSize> for $UIntInRangeTypeWithStart<Start>
{
type Output = $UIntInRangeType<Start, End>;
fn index(&self, end: EndSize) -> &Self::Output {
Interned::into_inner($UIntInRangeType::new(self.0, end).intern_sized())
}
}
impl<Start: Size, End: Size> ExprCastTo<UInt> for $UIntInRangeType<Start, End> {
fn cast_to(src: Expr<Self>, to_type: UInt) -> Expr<UInt> {
src.cast_to_bits().cast_to(to_type)
}
}
impl<Start: Size, End: Size> ExprCastTo<$UIntInRangeType<Start, End>> for UInt {
fn cast_to(
src: Expr<Self>,
to_type: $UIntInRangeType<Start, End>,
) -> Expr<$UIntInRangeType<Start, End>> {
src.cast_bits_to(to_type)
}
}
impl<LhsStart: Size, LhsEnd: Size, RhsStart: Size, RhsEnd: Size>
ExprPartialEq<$UIntInRangeType<RhsStart, RhsEnd>>
for $UIntInRangeType<LhsStart, LhsEnd>
{
fn cmp_eq(
lhs: Expr<Self>,
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
) -> Expr<Bool> {
lhs.cast_to_bits().cmp_eq(rhs.cast_to_bits())
}
fn cmp_ne(
lhs: Expr<Self>,
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
) -> Expr<Bool> {
lhs.cast_to_bits().cmp_ne(rhs.cast_to_bits())
}
}
impl<LhsStart: Size, LhsEnd: Size, RhsStart: Size, RhsEnd: Size>
ExprPartialOrd<$UIntInRangeType<RhsStart, RhsEnd>>
for $UIntInRangeType<LhsStart, LhsEnd>
{
fn cmp_lt(
lhs: Expr<Self>,
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
) -> Expr<Bool> {
lhs.cast_to_bits().cmp_lt(rhs.cast_to_bits())
}
fn cmp_le(
lhs: Expr<Self>,
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
) -> Expr<Bool> {
lhs.cast_to_bits().cmp_le(rhs.cast_to_bits())
}
fn cmp_gt(
lhs: Expr<Self>,
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
) -> Expr<Bool> {
lhs.cast_to_bits().cmp_gt(rhs.cast_to_bits())
}
fn cmp_ge(
lhs: Expr<Self>,
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
) -> Expr<Bool> {
lhs.cast_to_bits().cmp_ge(rhs.cast_to_bits())
}
}
impl<LhsStart: Size, LhsEnd: Size, RhsStart: Size, RhsEnd: Size>
SimValuePartialEq<$UIntInRangeType<RhsStart, RhsEnd>>
for $UIntInRangeType<LhsStart, LhsEnd>
{
fn sim_value_eq(
this: &SimValue<Self>,
other: &SimValue<$UIntInRangeType<RhsStart, RhsEnd>>,
) -> bool {
**this == **other
}
}
impl<Start: Size, End: Size, Width: Size> ExprPartialEq<UIntType<Width>>
for $UIntInRangeType<Start, End>
{
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
lhs.cast_to_bits().cmp_eq(rhs)
}
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
lhs.cast_to_bits().cmp_ne(rhs)
}
}
impl<Start: Size, End: Size, Width: Size> ExprPartialEq<$UIntInRangeType<Start, End>>
for UIntType<Width>
{
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
lhs.cmp_eq(rhs.cast_to_bits())
}
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
lhs.cmp_ne(rhs.cast_to_bits())
}
}
impl<Start: Size, End: Size, Width: Size> ExprPartialOrd<UIntType<Width>>
for $UIntInRangeType<Start, End>
{
fn cmp_lt(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
lhs.cast_to_bits().cmp_lt(rhs)
}
fn cmp_le(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
lhs.cast_to_bits().cmp_le(rhs)
}
fn cmp_gt(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
lhs.cast_to_bits().cmp_gt(rhs)
}
fn cmp_ge(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
lhs.cast_to_bits().cmp_ge(rhs)
}
}
impl<Start: Size, End: Size, Width: Size> ExprPartialOrd<$UIntInRangeType<Start, End>>
for UIntType<Width>
{
fn cmp_lt(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
lhs.cmp_lt(rhs.cast_to_bits())
}
fn cmp_le(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
lhs.cmp_le(rhs.cast_to_bits())
}
fn cmp_gt(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
lhs.cmp_gt(rhs.cast_to_bits())
}
fn cmp_ge(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
lhs.cmp_ge(rhs.cast_to_bits())
}
}
};
}
define_uint_in_range_type! {
UIntInRange,
UIntInRangeType,
UIntInRangeTypeWithoutGenerics,
UIntInRangeTypeWithStart,
SerdeRange,
"..",
|start, end| UInt::range_usize(start..end),
}
define_uint_in_range_type! {
UIntInRangeInclusive,
UIntInRangeInclusiveType,
UIntInRangeInclusiveTypeWithoutGenerics,
UIntInRangeInclusiveTypeWithStart,
SerdeRangeInclusive,
"..=",
|start, end| UInt::range_inclusive_usize(start..=end),
}
impl SerdeRange<DynSize, DynSize> {
fn is_empty(self) -> bool {
self.start >= self.end
}
}
impl SerdeRangeInclusive<DynSize, DynSize> {
fn is_empty(self) -> bool {
self.start > self.end
}
}

View file

@ -1,9 +1,9 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
#![allow(clippy::type_complexity)]
use crate::intern::type_map::TypeIdMap;
use crate::{intern::type_map::TypeIdMap, util::DefaultBuildHasher};
use bitvec::{ptr::BitPtr, slice::BitSlice, vec::BitVec};
use hashbrown::{hash_map::RawEntryMut, HashMap, HashTable};
use hashbrown::HashTable;
use serde::{Deserialize, Serialize};
use std::{
any::{Any, TypeId},
@ -17,7 +17,7 @@ use std::{
sync::{Mutex, RwLock},
};
pub mod type_map;
mod type_map;
pub trait LazyInternedTrait<T: ?Sized + Send + Sync + 'static>: Send + Sync + Any {
fn get(&self) -> Interned<T>;
@ -316,8 +316,13 @@ pub trait Intern: Any + Send + Sync {
}
}
struct InternerState<T: ?Sized + 'static + Send + Sync> {
table: HashTable<&'static T>,
hasher: DefaultBuildHasher,
}
pub struct Interner<T: ?Sized + 'static + Send + Sync> {
map: Mutex<HashMap<&'static T, ()>>,
state: Mutex<InternerState<T>>,
}
impl<T: ?Sized + 'static + Send + Sync> Interner<T> {
@ -330,7 +335,10 @@ impl<T: ?Sized + 'static + Send + Sync> Interner<T> {
impl<T: ?Sized + 'static + Send + Sync> Default for Interner<T> {
fn default() -> Self {
Self {
map: Default::default(),
state: Mutex::new(InternerState {
table: HashTable::new(),
hasher: Default::default(),
}),
}
}
}
@ -341,17 +349,16 @@ impl<T: ?Sized + 'static + Send + Sync + Hash + Eq + ToOwned> Interner<T> {
alloc: F,
value: Cow<'_, T>,
) -> Interned<T> {
let mut map = self.map.lock().unwrap();
let hasher = map.hasher().clone();
let hash = hasher.hash_one(&*value);
let inner = match map.raw_entry_mut().from_hash(hash, |k| **k == *value) {
RawEntryMut::Occupied(entry) => *entry.key(),
RawEntryMut::Vacant(entry) => {
*entry
.insert_with_hasher(hash, alloc(value), (), |k| hasher.hash_one(&**k))
.0
}
};
let mut state = self.state.lock().unwrap();
let InternerState { table, hasher } = &mut *state;
let inner = *table
.entry(
hasher.hash_one(&*value),
|k| **k == *value,
|k| hasher.hash_one(&**k),
)
.or_insert_with(|| alloc(value))
.get();
Interned { inner }
}
}
@ -742,7 +749,7 @@ pub trait MemoizeGeneric: 'static + Send + Sync + Hash + Eq + Copy {
fn get_cow(self, input: Self::InputCow<'_>) -> Self::Output {
static TYPE_ID_MAP: TypeIdMap = TypeIdMap::new();
let map: &RwLock<(
hashbrown::hash_map::DefaultHashBuilder,
DefaultBuildHasher,
HashTable<(Self, Self::InputOwned, Self::Output)>,
)> = TYPE_ID_MAP.get_or_insert_default();
fn hash_eq_key<'a, 'b, T: MemoizeGeneric>(

View file

@ -1,10 +1,8 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use hashbrown::HashMap;
use std::{
any::{Any, TypeId},
hash::{BuildHasher, Hasher},
ptr::NonNull,
sync::RwLock,
};
@ -75,59 +73,36 @@ impl BuildHasher for TypeIdBuildHasher {
}
}
struct Value(NonNull<dyn Any + Send + Sync>);
impl Value {
unsafe fn get_transmute_lifetime<'b>(&self) -> &'b (dyn Any + Send + Sync) {
unsafe { &*self.0.as_ptr() }
}
fn new(v: Box<dyn Any + Send + Sync>) -> Self {
unsafe { Self(NonNull::new_unchecked(Box::into_raw(v))) }
}
}
unsafe impl Send for Value {}
unsafe impl Sync for Value {}
impl Drop for Value {
fn drop(&mut self) {
unsafe { std::ptr::drop_in_place(self.0.as_ptr()) }
}
}
pub struct TypeIdMap(RwLock<HashMap<TypeId, Value, TypeIdBuildHasher>>);
pub(crate) struct TypeIdMap(
RwLock<hashbrown::HashMap<TypeId, &'static (dyn Any + Send + Sync), TypeIdBuildHasher>>,
);
impl TypeIdMap {
pub const fn new() -> Self {
Self(RwLock::new(HashMap::with_hasher(TypeIdBuildHasher)))
pub(crate) const fn new() -> Self {
Self(RwLock::new(hashbrown::HashMap::with_hasher(
TypeIdBuildHasher,
)))
}
#[cold]
unsafe fn insert_slow(
fn insert_slow(
&self,
type_id: TypeId,
make: fn() -> Box<dyn Any + Sync + Send>,
) -> &(dyn Any + Sync + Send) {
let value = Value::new(make());
) -> &'static (dyn Any + Sync + Send) {
let value = Box::leak(make());
let mut write_guard = self.0.write().unwrap();
unsafe {
write_guard
.entry(type_id)
.or_insert(value)
.get_transmute_lifetime()
}
*write_guard.entry(type_id).or_insert(value)
}
pub fn get_or_insert_default<T: Sized + Any + Send + Sync + Default>(&self) -> &T {
pub(crate) fn get_or_insert_default<T: Sized + Any + Send + Sync + Default>(&self) -> &T {
let type_id = TypeId::of::<T>();
let read_guard = self.0.read().unwrap();
let retval = read_guard
.get(&type_id)
.map(|v| unsafe { Value::get_transmute_lifetime(v) });
let retval = read_guard.get(&type_id).map(|v| *v);
drop(read_guard);
let retval = match retval {
Some(retval) => retval,
None => unsafe { self.insert_slow(type_id, move || Box::new(T::default())) },
None => self.insert_slow(type_id, move || Box::new(T::default())),
};
unsafe { &*(retval as *const dyn Any as *const T) }
retval.downcast_ref().expect("known to have correct TypeId")
}
}

View file

@ -76,6 +76,8 @@ pub use fayalite_proc_macros::hdl_module;
#[doc(inline)]
pub use fayalite_proc_macros::hdl;
pub use bitvec;
/// struct used as a placeholder when applying defaults
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct __;
@ -96,6 +98,7 @@ pub mod int;
pub mod intern;
pub mod memory;
pub mod module;
pub mod phantom_const;
pub mod prelude;
pub mod reg;
pub mod reset;

View file

@ -470,7 +470,7 @@ pub enum ReadUnderWrite {
Undefined,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
struct MemImpl<Element: Type, Len: Size, P> {
scoped_name: ScopedNameId,
source_location: SourceLocation,
@ -1082,6 +1082,7 @@ pub fn splat_mask<T: Type>(ty: T, value: Expr<Bool>) -> Expr<AsMask<T>> {
)
.to_expr(),
)),
CanonicalType::PhantomConst(_) => Expr::from_canonical(Expr::canonical(().to_expr())),
}
}

View file

@ -21,18 +21,20 @@ use crate::{
memory::{Mem, MemBuilder, MemBuilderTarget, PortName},
reg::Reg,
reset::{AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset},
sim::{ExternModuleSimGenerator, ExternModuleSimulation},
source_location::SourceLocation,
ty::{CanonicalType, Type},
util::ScopedRef,
util::{HashMap, HashSet, ScopedRef},
wire::{IncompleteWire, Wire},
};
use hashbrown::{hash_map::Entry, HashMap, HashSet};
use hashbrown::hash_map::Entry;
use num_bigint::BigInt;
use std::{
cell::RefCell,
collections::VecDeque,
convert::Infallible,
fmt,
future::IntoFuture,
hash::{Hash, Hasher},
iter::FusedIterator,
marker::PhantomData,
@ -1081,6 +1083,7 @@ pub struct ExternModuleBody<
> {
pub verilog_name: Interned<str>,
pub parameters: P,
pub simulation: Option<ExternModuleSimulation>,
}
impl From<ExternModuleBody<Vec<ExternModuleParameter>>> for ExternModuleBody {
@ -1088,11 +1091,13 @@ impl From<ExternModuleBody<Vec<ExternModuleParameter>>> for ExternModuleBody {
let ExternModuleBody {
verilog_name,
parameters,
simulation,
} = value;
let parameters = Intern::intern_owned(parameters);
Self {
verilog_name,
parameters,
simulation,
}
}
}
@ -1283,10 +1288,12 @@ impl<T: BundleType> fmt::Debug for DebugModuleBody<T> {
ModuleBody::Extern(ExternModuleBody {
verilog_name,
parameters,
simulation,
}) => {
debug_struct
.field("verilog_name", verilog_name)
.field("parameters", parameters);
.field("parameters", parameters)
.field("simulation", simulation);
}
}
debug_struct.finish_non_exhaustive()
@ -1490,6 +1497,9 @@ impl TargetState {
})
.collect(),
},
CanonicalType::PhantomConst(_) => TargetStateInner::Decomposed {
subtargets: HashMap::default(),
},
CanonicalType::Array(ty) => TargetStateInner::Decomposed {
subtargets: (0..ty.len())
.map(|index| {
@ -1758,6 +1768,7 @@ impl AssertValidityState {
ModuleBody::Extern(ExternModuleBody {
verilog_name: _,
parameters: _,
simulation: _,
}) => {}
ModuleBody::Normal(NormalModuleBody { body }) => {
let body = self.make_block_index(body);
@ -1853,7 +1864,7 @@ impl<T: BundleType> Module<T> {
AssertValidityState {
module: self.canonical(),
blocks: vec![],
target_states: HashMap::with_capacity(64),
target_states: HashMap::with_capacity_and_hasher(64, Default::default()),
}
.assert_validity();
}
@ -2105,6 +2116,7 @@ impl ModuleBuilder {
ModuleKind::Extern => ModuleBody::Extern(ExternModuleBody {
verilog_name: name.0,
parameters: vec![],
simulation: None,
}),
ModuleKind::Normal => ModuleBody::Normal(NormalModuleBody {
body: BuilderModuleBody {
@ -2113,8 +2125,8 @@ impl ModuleBuilder {
incomplete_declarations: vec![],
stmts: vec![],
}],
annotations_map: HashMap::new(),
memory_map: HashMap::new(),
annotations_map: HashMap::default(),
memory_map: HashMap::default(),
},
}),
};
@ -2124,7 +2136,7 @@ impl ModuleBuilder {
impl_: RefCell::new(ModuleBuilderImpl {
body,
io: vec![],
io_indexes: HashMap::new(),
io_indexes: HashMap::default(),
module_annotations: vec![],
}),
};
@ -2171,6 +2183,7 @@ impl ModuleBuilder {
.builder_extern_body()
.verilog_name = name.intern();
}
#[track_caller]
pub fn parameter(&self, name: impl AsRef<str>, value: ExternModuleParameterValue) {
let name = name.as_ref();
self.impl_
@ -2183,6 +2196,7 @@ impl ModuleBuilder {
value,
});
}
#[track_caller]
pub fn parameter_int(&self, name: impl AsRef<str>, value: impl Into<BigInt>) {
let name = name.as_ref();
let value = value.into();
@ -2196,6 +2210,7 @@ impl ModuleBuilder {
value: ExternModuleParameterValue::Integer(value),
});
}
#[track_caller]
pub fn parameter_str(&self, name: impl AsRef<str>, value: impl AsRef<str>) {
let name = name.as_ref();
let value = value.as_ref();
@ -2209,6 +2224,7 @@ impl ModuleBuilder {
value: ExternModuleParameterValue::String(value.intern()),
});
}
#[track_caller]
pub fn parameter_raw_verilog(&self, name: impl AsRef<str>, raw_verilog: impl AsRef<str>) {
let name = name.as_ref();
let raw_verilog = raw_verilog.as_ref();
@ -2222,6 +2238,26 @@ impl ModuleBuilder {
value: ExternModuleParameterValue::RawVerilog(raw_verilog.intern()),
});
}
#[track_caller]
pub fn extern_module_simulation<G: ExternModuleSimGenerator>(&self, generator: G) {
let mut impl_ = self.impl_.borrow_mut();
let simulation = &mut impl_.body.builder_extern_body().simulation;
if simulation.is_some() {
panic!("already added an extern module simulation");
}
*simulation = Some(ExternModuleSimulation::new(generator));
}
#[track_caller]
pub fn extern_module_simulation_fn<
Args: fmt::Debug + Clone + Hash + Eq + Send + Sync + 'static,
Fut: IntoFuture<Output = ()> + 'static,
>(
&self,
args: Args,
f: fn(Args, crate::sim::ExternModuleSimulationState) -> Fut,
) {
self.extern_module_simulation(crate::sim::SimGeneratorFn { args, f });
}
}
#[track_caller]

View file

@ -24,8 +24,9 @@ use crate::{
},
prelude::*,
reset::{ResetType, ResetTypeDispatch},
util::{HashMap, HashSet},
};
use hashbrown::{hash_map::Entry, HashMap, HashSet};
use hashbrown::hash_map::Entry;
use num_bigint::BigInt;
use petgraph::unionfind::UnionFind;
use std::{fmt, marker::PhantomData};
@ -155,6 +156,7 @@ impl ResetsLayout {
CanonicalType::SyncReset(_) => ResetsLayout::SyncReset,
CanonicalType::Reset(_) => ResetsLayout::Reset,
CanonicalType::Clock(_) => ResetsLayout::NoResets,
CanonicalType::PhantomConst(_) => ResetsLayout::NoResets,
}
}
}
@ -407,7 +409,8 @@ impl Resets {
| CanonicalType::Bool(_)
| CanonicalType::AsyncReset(_)
| CanonicalType::SyncReset(_)
| CanonicalType::Clock(_) => Ok(self.ty),
| CanonicalType::Clock(_)
| CanonicalType::PhantomConst(_) => Ok(self.ty),
CanonicalType::Array(ty) => Ok(CanonicalType::Array(Array::new_dyn(
self.array_elements().substituted_type(
reset_graph,
@ -998,7 +1001,8 @@ fn cast_bit_op<P: Pass, T: Type, A: Type>(
CanonicalType::Array(_)
| CanonicalType::Enum(_)
| CanonicalType::Bundle(_)
| CanonicalType::Reset(_) => unreachable!(),
| CanonicalType::Reset(_)
| CanonicalType::PhantomConst(_) => unreachable!(),
$(CanonicalType::$Variant(ty) => Expr::expr_enum($arg.cast_to(ty)),)*
}
};
@ -1010,6 +1014,7 @@ fn cast_bit_op<P: Pass, T: Type, A: Type>(
| CanonicalType::Enum(_)
| CanonicalType::Bundle(_)
| CanonicalType::Reset(_) => unreachable!(),
CanonicalType::PhantomConst(_) => Expr::expr_enum(arg),
$(CanonicalType::$Variant(_) => {
let arg = Expr::<$Variant>::from_canonical(arg);
match_expr_ty!(arg, UInt, SInt, Bool, AsyncReset, SyncReset, Clock)
@ -1040,6 +1045,7 @@ impl<P: Pass> RunPass<P> for ExprEnum {
ExprEnum::UIntLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::SIntLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::BoolLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::PhantomConst(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::BundleLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::ArrayLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::EnumLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
@ -1670,7 +1676,8 @@ impl RunPassDispatch for AnyReg {
| CanonicalType::Enum(_)
| CanonicalType::Bundle(_)
| CanonicalType::Reset(_)
| CanonicalType::Clock(_) => unreachable!(),
| CanonicalType::Clock(_)
| CanonicalType::PhantomConst(_) => unreachable!(),
}
})
}
@ -1769,6 +1776,7 @@ impl_run_pass_copy!([] SVAttributeAnnotation);
impl_run_pass_copy!([] UInt);
impl_run_pass_copy!([] usize);
impl_run_pass_copy!([] FormalKind);
impl_run_pass_copy!([] PhantomConst);
macro_rules! impl_run_pass_for_struct {
(
@ -2244,9 +2252,9 @@ pub fn deduce_resets(
fallback_to_sync_reset: bool,
) -> Result<Interned<Module<Bundle>>, DeduceResetsError> {
let mut state = State {
modules_added_to_graph: HashSet::new(),
substituted_modules: HashMap::new(),
expr_resets: HashMap::new(),
modules_added_to_graph: HashSet::default(),
substituted_modules: HashMap::default(),
expr_resets: HashMap::default(),
reset_graph: ResetGraph::default(),
fallback_to_sync_reset,
};

View file

@ -18,10 +18,10 @@ use crate::{
},
source_location::SourceLocation,
ty::{CanonicalType, Type},
util::HashMap,
wire::Wire,
};
use core::fmt;
use hashbrown::HashMap;
#[derive(Debug)]
pub enum SimplifyEnumsError {
@ -69,7 +69,8 @@ fn contains_any_enum_types(ty: CanonicalType) -> bool {
| CanonicalType::AsyncReset(_)
| CanonicalType::SyncReset(_)
| CanonicalType::Reset(_)
| CanonicalType::Clock(_) => false,
| CanonicalType::Clock(_)
| CanonicalType::PhantomConst(_) => false,
}
}
}
@ -512,7 +513,8 @@ impl State {
| CanonicalType::AsyncReset(_)
| CanonicalType::SyncReset(_)
| CanonicalType::Reset(_)
| CanonicalType::Clock(_) => unreachable!(),
| CanonicalType::Clock(_)
| CanonicalType::PhantomConst(_) => unreachable!(),
}
}
}
@ -577,7 +579,8 @@ fn connect_port(
| (CanonicalType::Clock(_), _)
| (CanonicalType::AsyncReset(_), _)
| (CanonicalType::SyncReset(_), _)
| (CanonicalType::Reset(_), _) => unreachable!(
| (CanonicalType::Reset(_), _)
| (CanonicalType::PhantomConst(_), _) => unreachable!(
"trying to connect memory ports:\n{:?}\n{:?}",
Expr::ty(lhs),
Expr::ty(rhs),
@ -665,6 +668,7 @@ impl Folder for State {
ExprEnum::UIntLiteral(_)
| ExprEnum::SIntLiteral(_)
| ExprEnum::BoolLiteral(_)
| ExprEnum::PhantomConst(_)
| ExprEnum::BundleLiteral(_)
| ExprEnum::ArrayLiteral(_)
| ExprEnum::Uninit(_)
@ -923,7 +927,8 @@ impl Folder for State {
| CanonicalType::Clock(_)
| CanonicalType::AsyncReset(_)
| CanonicalType::SyncReset(_)
| CanonicalType::Reset(_) => canonical_type.default_fold(self),
| CanonicalType::Reset(_)
| CanonicalType::PhantomConst(_) => canonical_type.default_fold(self),
}
}
@ -960,8 +965,8 @@ pub fn simplify_enums(
kind: SimplifyEnumsKind,
) -> Result<Interned<Module<Bundle>>, SimplifyEnumsError> {
module.fold(&mut State {
enum_types: HashMap::new(),
replacement_mem_ports: HashMap::new(),
enum_types: HashMap::default(),
replacement_mem_ports: HashMap::default(),
kind,
module_state_stack: vec![],
})

View file

@ -14,11 +14,10 @@ use crate::{
},
source_location::SourceLocation,
ty::{CanonicalType, Type},
util::MakeMutSlice,
util::{HashMap, MakeMutSlice},
wire::Wire,
};
use bitvec::{slice::BitSlice, vec::BitVec};
use hashbrown::HashMap;
use std::{
convert::Infallible,
fmt::Write,
@ -62,6 +61,7 @@ enum MemSplit {
Bundle {
fields: Rc<[MemSplit]>,
},
PhantomConst,
Single {
output_mem: Option<Mem>,
element_type: SingleType,
@ -76,6 +76,7 @@ impl MemSplit {
fn mark_changed_element_type(self) -> Self {
match self {
MemSplit::Bundle { fields: _ } => self,
MemSplit::PhantomConst => self,
MemSplit::Single {
output_mem,
element_type,
@ -97,6 +98,7 @@ impl MemSplit {
.map(|field| Self::new(field.ty).mark_changed_element_type())
.collect(),
},
CanonicalType::PhantomConst(_) => MemSplit::PhantomConst,
CanonicalType::Array(ty) => {
let element = MemSplit::new(ty.element());
if let Self::Single {
@ -339,6 +341,7 @@ impl SplitMemState<'_, '_> {
self.split_state_stack.pop();
}
}
MemSplit::PhantomConst => {}
MemSplit::Single {
output_mem,
element_type: single_type,
@ -538,7 +541,12 @@ impl ModuleState {
};
loop {
match input_element_type {
CanonicalType::Bundle(_) => unreachable!("bundle types are always split"),
CanonicalType::Bundle(_) => {
unreachable!("bundle types are always split")
}
CanonicalType::PhantomConst(_) => {
unreachable!("PhantomConst are always removed")
}
CanonicalType::Enum(_)
if input_array_types
.first()
@ -743,7 +751,8 @@ impl ModuleState {
..
}
| MemSplit::Bundle { .. }
| MemSplit::Array { .. } => {
| MemSplit::Array { .. }
| MemSplit::PhantomConst => {
let mut replacement_ports = Vec::with_capacity(input_mem.ports().len());
let mut wire_port_rdata = Vec::with_capacity(input_mem.ports().len());
let mut wire_port_wdata = Vec::with_capacity(input_mem.ports().len());
@ -887,7 +896,7 @@ impl Folder for State {
module,
ModuleState {
output_module: None,
memories: HashMap::new(),
memories: HashMap::default(),
},
);
let mut this = PushedState::push_module(self, module);

View file

@ -28,8 +28,10 @@ use crate::{
NormalModuleBody, ScopedNameId, Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtIf,
StmtInstance, StmtMatch, StmtReg, StmtWire,
},
phantom_const::PhantomConst,
reg::Reg,
reset::{AsyncReset, Reset, ResetType, SyncReset},
sim::ExternModuleSimulation,
source_location::SourceLocation,
ty::{CanonicalType, Type},
wire::Wire,

View file

@ -0,0 +1,410 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
expr::{
ops::{ExprPartialEq, ExprPartialOrd},
Expr, ToExpr,
},
int::Bool,
intern::{Intern, Interned, InternedCompare, LazyInterned, LazyInternedTrait, Memoize},
sim::value::{SimValue, SimValuePartialEq, ToSimValue, ToSimValueWithType},
source_location::SourceLocation,
ty::{
impl_match_variant_as_self,
serde_impls::{SerdeCanonicalType, SerdePhantomConst},
CanonicalType, StaticType, Type, TypeProperties,
},
};
use bitvec::slice::BitSlice;
use serde::{
de::{DeserializeOwned, Error},
Deserialize, Deserializer, Serialize, Serializer,
};
use std::{
any::Any,
fmt,
hash::{Hash, Hasher},
marker::PhantomData,
ops::Index,
};
#[derive(Clone)]
pub struct PhantomConstCanonicalValue {
parsed: serde_json::Value,
serialized: Interned<str>,
}
impl PhantomConstCanonicalValue {
pub fn from_json_value(parsed: serde_json::Value) -> Self {
let serialized = Intern::intern_owned(
serde_json::to_string(&parsed)
.expect("conversion from json value to text shouldn't fail"),
);
Self { parsed, serialized }
}
pub fn as_json_value(&self) -> &serde_json::Value {
&self.parsed
}
pub fn as_str(&self) -> Interned<str> {
self.serialized
}
}
impl fmt::Debug for PhantomConstCanonicalValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.serialized)
}
}
impl fmt::Display for PhantomConstCanonicalValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.serialized)
}
}
impl PartialEq for PhantomConstCanonicalValue {
fn eq(&self, other: &Self) -> bool {
self.serialized == other.serialized
}
}
impl Eq for PhantomConstCanonicalValue {}
impl Hash for PhantomConstCanonicalValue {
fn hash<H: Hasher>(&self, state: &mut H) {
self.serialized.hash(state);
}
}
impl Serialize for PhantomConstCanonicalValue {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.parsed.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for PhantomConstCanonicalValue {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
Ok(Self::from_json_value(serde_json::Value::deserialize(
deserializer,
)?))
}
}
pub trait PhantomConstValue: Intern + InternedCompare + Serialize + fmt::Debug {
fn deserialize_value<'de, D>(deserializer: D) -> Result<Interned<Self>, D::Error>
where
D: serde::Deserializer<'de>;
}
impl<T> PhantomConstValue for T
where
T: ?Sized + Intern + InternedCompare + Serialize + fmt::Debug,
Interned<T>: DeserializeOwned,
{
fn deserialize_value<'de, D>(deserializer: D) -> Result<Interned<Self>, D::Error>
where
D: serde::Deserializer<'de>,
{
<Interned<T> as Deserialize<'de>>::deserialize(deserializer)
}
}
/// Wrapper type that allows any Rust value to be smuggled as a HDL [`Type`].
/// This only works for values that can be [serialized][Serialize] to and [deserialized][Deserialize] from [JSON][serde_json].
pub struct PhantomConst<T: ?Sized + PhantomConstValue = PhantomConstCanonicalValue> {
value: LazyInterned<T>,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
pub struct PhantomConstWithoutGenerics;
#[allow(non_upper_case_globals)]
pub const PhantomConst: PhantomConstWithoutGenerics = PhantomConstWithoutGenerics;
impl<T: Type + PhantomConstValue> Index<T> for PhantomConstWithoutGenerics {
type Output = PhantomConst<T>;
fn index(&self, value: T) -> &Self::Output {
Interned::into_inner(PhantomConst::new(value.intern()).intern_sized())
}
}
impl<T: ?Sized + PhantomConstValue> fmt::Debug for PhantomConst<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("PhantomConst").field(&self.get()).finish()
}
}
impl<T: ?Sized + PhantomConstValue> Clone for PhantomConst<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized + PhantomConstValue> Copy for PhantomConst<T> {}
impl<T: ?Sized + PhantomConstValue> PartialEq for PhantomConst<T> {
fn eq(&self, other: &Self) -> bool {
self.get() == other.get()
}
}
impl<T: ?Sized + PhantomConstValue> Eq for PhantomConst<T> {}
impl<T: ?Sized + PhantomConstValue> Hash for PhantomConst<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.get().hash(state);
}
}
struct PhantomConstCanonicalMemoize<T: ?Sized, const IS_FROM_CANONICAL: bool>(PhantomData<T>);
impl<T: ?Sized, const IS_FROM_CANONICAL: bool> Copy
for PhantomConstCanonicalMemoize<T, IS_FROM_CANONICAL>
{
}
impl<T: ?Sized, const IS_FROM_CANONICAL: bool> Clone
for PhantomConstCanonicalMemoize<T, IS_FROM_CANONICAL>
{
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized, const IS_FROM_CANONICAL: bool> Eq
for PhantomConstCanonicalMemoize<T, IS_FROM_CANONICAL>
{
}
impl<T: ?Sized, const IS_FROM_CANONICAL: bool> PartialEq
for PhantomConstCanonicalMemoize<T, IS_FROM_CANONICAL>
{
fn eq(&self, _other: &Self) -> bool {
true
}
}
impl<T: ?Sized, const IS_FROM_CANONICAL: bool> Hash
for PhantomConstCanonicalMemoize<T, IS_FROM_CANONICAL>
{
fn hash<H: Hasher>(&self, _state: &mut H) {}
}
impl<T: ?Sized + PhantomConstValue> Memoize for PhantomConstCanonicalMemoize<T, false> {
type Input = Interned<T>;
type InputOwned = Interned<T>;
type Output = Interned<PhantomConstCanonicalValue>;
fn inner(self, input: &Self::Input) -> Self::Output {
Intern::intern_sized(PhantomConstCanonicalValue::from_json_value(
serde_json::to_value(input)
.expect("serialization failed when constructing a canonical PhantomConst"),
))
}
}
impl<T: ?Sized + PhantomConstValue> Memoize for PhantomConstCanonicalMemoize<T, true> {
type Input = Interned<PhantomConstCanonicalValue>;
type InputOwned = Interned<PhantomConstCanonicalValue>;
type Output = Interned<T>;
fn inner(self, input: &Self::Input) -> Self::Output {
PhantomConstValue::deserialize_value(input.as_json_value())
.expect("deserialization failed ")
}
}
impl<T: ?Sized + PhantomConstValue> PhantomConst<T> {
pub fn new(value: Interned<T>) -> Self {
Self {
value: LazyInterned::Interned(value),
}
}
pub const fn new_lazy(v: &'static dyn LazyInternedTrait<T>) -> Self {
Self {
value: LazyInterned::new_lazy(v),
}
}
pub fn get(self) -> Interned<T> {
self.value.interned()
}
pub fn type_properties(self) -> TypeProperties {
<()>::TYPE_PROPERTIES
}
pub fn can_connect(self, other: Self) -> bool {
self == other
}
pub fn canonical_phantom_const(self) -> PhantomConst {
if let Some(&retval) = <dyn Any>::downcast_ref::<PhantomConst>(&self) {
return retval;
}
<PhantomConst>::new(
PhantomConstCanonicalMemoize::<T, false>(PhantomData).get_owned(self.get()),
)
}
pub fn from_canonical_phantom_const(canonical_type: PhantomConst) -> Self {
if let Some(&retval) = <dyn Any>::downcast_ref::<Self>(&canonical_type) {
return retval;
}
Self::new(
PhantomConstCanonicalMemoize::<T, true>(PhantomData).get_owned(canonical_type.get()),
)
}
}
impl<T: ?Sized + PhantomConstValue> Type for PhantomConst<T> {
type BaseType = PhantomConst;
type MaskType = ();
type SimValue = PhantomConst<T>;
impl_match_variant_as_self!();
fn mask_type(&self) -> Self::MaskType {
()
}
fn canonical(&self) -> CanonicalType {
CanonicalType::PhantomConst(self.canonical_phantom_const())
}
fn from_canonical(canonical_type: CanonicalType) -> Self {
let CanonicalType::PhantomConst(phantom_const) = canonical_type else {
panic!("expected PhantomConst");
};
Self::from_canonical_phantom_const(phantom_const)
}
fn source_location() -> SourceLocation {
SourceLocation::builtin()
}
fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue {
assert!(bits.is_empty());
*self
}
fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) {
assert!(bits.is_empty());
assert_eq!(*value, *self);
}
fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) {
assert!(bits.is_empty());
assert_eq!(*value, *self);
}
}
impl<T: ?Sized + PhantomConstValue> Default for PhantomConst<T>
where
Interned<T>: Default,
{
fn default() -> Self {
Self::TYPE
}
}
impl<T: ?Sized + PhantomConstValue> StaticType for PhantomConst<T>
where
Interned<T>: Default,
{
const TYPE: Self = PhantomConst {
value: LazyInterned::new_lazy(&Interned::<T>::default),
};
const MASK_TYPE: Self::MaskType = ();
const TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES;
const MASK_TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES;
}
type SerdeType<T> = SerdeCanonicalType<CanonicalType, SerdePhantomConst<Interned<T>>>;
impl<T: ?Sized + PhantomConstValue> Serialize for PhantomConst<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
SerdeType::<T>::PhantomConst(SerdePhantomConst(self.get())).serialize(serializer)
}
}
impl<'de, T: ?Sized + PhantomConstValue> Deserialize<'de> for PhantomConst<T> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
match SerdeType::<T>::deserialize(deserializer)? {
SerdeCanonicalType::PhantomConst(SerdePhantomConst(value)) => Ok(Self::new(value)),
ty => Err(Error::invalid_value(
serde::de::Unexpected::Other(ty.as_serde_unexpected_str()),
&"a PhantomConst",
)),
}
}
}
impl<T: ?Sized + PhantomConstValue> ExprPartialEq<Self> for PhantomConst<T> {
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
true.to_expr()
}
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
false.to_expr()
}
}
impl<T: ?Sized + PhantomConstValue> ExprPartialOrd<Self> for PhantomConst<T> {
fn cmp_lt(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
false.to_expr()
}
fn cmp_le(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
true.to_expr()
}
fn cmp_gt(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
false.to_expr()
}
fn cmp_ge(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
true.to_expr()
}
}
impl<T: ?Sized + PhantomConstValue> SimValuePartialEq<Self> for PhantomConst<T> {
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Self>) -> bool {
assert_eq!(SimValue::ty(this), SimValue::ty(other));
true
}
}
impl<T: ?Sized + PhantomConstValue> ToSimValue for PhantomConst<T> {
type Type = PhantomConst<T>;
fn to_sim_value(&self) -> SimValue<Self::Type> {
SimValue::from_value(*self, *self)
}
}
impl<T: ?Sized + PhantomConstValue> ToSimValueWithType<PhantomConst<T>> for PhantomConst<T> {
fn to_sim_value_with_type(&self, ty: PhantomConst<T>) -> SimValue<PhantomConst<T>> {
SimValue::from_value(ty, *self)
}
}
impl<T: ?Sized + PhantomConstValue> ToSimValueWithType<CanonicalType> for PhantomConst<T> {
fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
SimValue::into_canonical(SimValue::from_value(Self::from_canonical(ty), *self))
}
}

View file

@ -20,17 +20,24 @@ pub use crate::{
hdl_cover_with_enable, MakeFormalExpr,
},
hdl, hdl_module,
int::{Bool, DynSize, KnownSize, SInt, SIntType, Size, UInt, UIntType},
int::{Bool, DynSize, KnownSize, SInt, SIntType, SIntValue, Size, UInt, UIntType, UIntValue},
memory::{Mem, MemBuilder, ReadUnderWrite},
module::{
annotate, connect, connect_any, incomplete_wire, instance, memory, memory_array,
memory_with_init, reg_builder, wire, Instance, Module, ModuleBuilder,
},
phantom_const::PhantomConst,
reg::Reg,
reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset},
sim::{
time::{SimDuration, SimInstant},
value::{SimValue, ToSimValue, ToSimValueWithType},
ExternModuleSimulationState, Simulation,
},
source_location::SourceLocation,
ty::{AsMask, CanonicalType, Type},
util::{ConstUsize, GenericConstUsize},
wire::Wire,
__,
};
pub use bitvec::{slice::BitSlice, vec::BitVec};

View file

@ -7,6 +7,7 @@ use crate::{
source_location::SourceLocation,
ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties},
};
use bitvec::slice::BitSlice;
mod sealed {
pub trait ResetTypeSealed {}
@ -45,6 +46,7 @@ macro_rules! reset_type {
impl Type for $name {
type BaseType = $name;
type MaskType = Bool;
type SimValue = bool;
impl_match_variant_as_self!();
@ -66,6 +68,21 @@ macro_rules! reset_type {
};
retval
}
fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue {
assert_eq!(bits.len(), 1);
bits[0]
}
fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) {
assert_eq!(bits.len(), 1);
*value = bits[0];
}
fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) {
assert_eq!(bits.len(), 1);
bits.set(0, *value);
}
}
impl $name {

File diff suppressed because it is too large Load diff

View file

@ -7,10 +7,9 @@ use crate::{
intern::{Intern, Interned, Memoize},
source_location::SourceLocation,
ty::CanonicalType,
util::get_many_mut,
util::{get_many_mut, HashMap, HashSet},
};
use bitvec::{boxed::BitBox, slice::BitSlice};
use hashbrown::{HashMap, HashSet};
use num_bigint::BigInt;
use num_traits::{One, Signed, ToPrimitive, Zero};
use std::{

View file

@ -0,0 +1,913 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
array::{Array, ArrayType},
bundle::{Bundle, BundleType},
clock::Clock,
enum_::{Enum, EnumType},
expr::{CastBitsTo, Expr, ToExpr},
int::{Bool, IntType, KnownSize, SInt, SIntType, SIntValue, Size, UInt, UIntType, UIntValue},
reset::{AsyncReset, Reset, SyncReset},
ty::{CanonicalType, StaticType, Type},
util::{
alternating_cell::{AlternatingCell, AlternatingCellMethods},
ConstUsize,
},
};
use bitvec::{slice::BitSlice, vec::BitVec};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::{
fmt,
ops::{Deref, DerefMut},
sync::Arc,
};
#[derive(Copy, Clone, Eq, PartialEq)]
enum ValidFlags {
BothValid = 0,
OnlyValueValid = 1,
OnlyBitsValid = 2,
}
#[derive(Clone)]
struct SimValueInner<T: Type> {
value: T::SimValue,
bits: UIntValue,
valid_flags: ValidFlags,
ty: T,
}
impl<T: Type> SimValueInner<T> {
fn fill_bits(&mut self) {
match self.valid_flags {
ValidFlags::BothValid | ValidFlags::OnlyBitsValid => {}
ValidFlags::OnlyValueValid => {
self.ty.sim_value_to_bits(&self.value, self.bits.bits_mut());
self.valid_flags = ValidFlags::BothValid;
}
}
}
fn into_bits(mut self) -> UIntValue {
self.fill_bits();
self.bits
}
fn bits_mut(&mut self) -> &mut UIntValue {
self.fill_bits();
self.valid_flags = ValidFlags::OnlyBitsValid;
&mut self.bits
}
fn fill_value(&mut self) {
match self.valid_flags {
ValidFlags::BothValid | ValidFlags::OnlyValueValid => {}
ValidFlags::OnlyBitsValid => {
self.ty
.sim_value_clone_from_bits(&mut self.value, self.bits.bits());
self.valid_flags = ValidFlags::BothValid;
}
}
}
fn into_value(mut self) -> T::SimValue {
self.fill_value();
self.value
}
fn value_mut(&mut self) -> &mut T::SimValue {
self.fill_value();
self.valid_flags = ValidFlags::OnlyValueValid;
&mut self.value
}
}
impl<T: Type> AlternatingCellMethods for SimValueInner<T> {
fn unique_to_shared(&mut self) {
match self.valid_flags {
ValidFlags::BothValid => return,
ValidFlags::OnlyValueValid => {
self.ty.sim_value_to_bits(&self.value, self.bits.bits_mut())
}
ValidFlags::OnlyBitsValid => self
.ty
.sim_value_clone_from_bits(&mut self.value, self.bits.bits()),
}
self.valid_flags = ValidFlags::BothValid;
}
fn shared_to_unique(&mut self) {}
}
#[derive(Serialize, Deserialize)]
#[serde(rename = "SimValue")]
#[serde(bound(
serialize = "T: Type<SimValue: Serialize> + Serialize",
deserialize = "T: Type<SimValue: Deserialize<'de>> + Deserialize<'de>"
))]
struct SerdeSimValue<'a, T: Type> {
ty: T,
value: std::borrow::Cow<'a, T::SimValue>,
}
impl<T: Type<SimValue: Serialize> + Serialize> Serialize for SimValue<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
SerdeSimValue {
ty: SimValue::ty(self),
value: std::borrow::Cow::Borrowed(&*self),
}
.serialize(serializer)
}
}
impl<'de, T: Type<SimValue: Deserialize<'de>> + Deserialize<'de>> Deserialize<'de> for SimValue<T> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let SerdeSimValue { ty, value } = SerdeSimValue::<T>::deserialize(deserializer)?;
Ok(SimValue::from_value(ty, value.into_owned()))
}
}
pub struct SimValue<T: Type> {
inner: AlternatingCell<SimValueInner<T>>,
}
impl<T: Type + Clone> Clone for SimValue<T> {
fn clone(&self) -> Self {
Self {
inner: AlternatingCell::new_unique(self.inner.share().clone()),
}
}
}
impl<T: Type> SimValue<T> {
#[track_caller]
pub fn from_bits(ty: T, bits: UIntValue) -> Self {
assert_eq!(ty.canonical().bit_width(), bits.width());
let inner = SimValueInner {
value: ty.sim_value_from_bits(bits.bits()),
bits,
valid_flags: ValidFlags::BothValid,
ty,
};
Self {
inner: AlternatingCell::new_shared(inner),
}
}
#[track_caller]
pub fn from_bitslice(ty: T, bits: &BitSlice) -> Self {
Self::from_bits(ty, UIntValue::new(Arc::new(bits.to_bitvec())))
}
pub fn from_value(ty: T, value: T::SimValue) -> Self {
let inner = SimValueInner {
bits: UIntValue::new_dyn(Arc::new(BitVec::repeat(false, ty.canonical().bit_width()))),
value,
valid_flags: ValidFlags::OnlyValueValid,
ty,
};
Self {
inner: AlternatingCell::new_unique(inner),
}
}
pub fn ty(this: &Self) -> T {
this.inner.share().ty
}
pub fn into_bits(this: Self) -> UIntValue {
this.inner.into_inner().into_bits()
}
pub fn into_ty_and_bits(this: Self) -> (T, UIntValue) {
let inner = this.inner.into_inner();
(inner.ty, inner.into_bits())
}
pub fn bits(this: &Self) -> &UIntValue {
&this.inner.share().bits
}
pub fn bits_mut(this: &mut Self) -> &mut UIntValue {
this.inner.unique().bits_mut()
}
pub fn into_value(this: Self) -> T::SimValue {
this.inner.into_inner().into_value()
}
pub fn value(this: &Self) -> &T::SimValue {
&this.inner.share().value
}
pub fn value_mut(this: &mut Self) -> &mut T::SimValue {
this.inner.unique().value_mut()
}
#[track_caller]
pub fn from_canonical(v: SimValue<CanonicalType>) -> Self {
let (ty, bits) = SimValue::into_ty_and_bits(v);
Self::from_bits(T::from_canonical(ty), bits)
}
pub fn into_canonical(this: Self) -> SimValue<CanonicalType> {
let (ty, bits) = Self::into_ty_and_bits(this);
SimValue::from_bits(ty.canonical(), bits)
}
pub fn canonical(this: &Self) -> SimValue<CanonicalType> {
SimValue::from_bits(Self::ty(this).canonical(), Self::bits(this).clone())
}
#[track_caller]
pub fn from_dyn_int(v: SimValue<T::Dyn>) -> Self
where
T: IntType,
{
let (ty, bits) = SimValue::into_ty_and_bits(v);
SimValue::from_bits(T::from_dyn_int(ty), bits)
}
pub fn into_dyn_int(this: Self) -> SimValue<T::Dyn>
where
T: IntType,
{
let (ty, bits) = Self::into_ty_and_bits(this);
SimValue::from_bits(ty.as_dyn_int(), bits)
}
pub fn to_dyn_int(this: &Self) -> SimValue<T::Dyn>
where
T: IntType,
{
SimValue::from_bits(Self::ty(this).as_dyn_int(), Self::bits(&this).clone())
}
#[track_caller]
pub fn from_bundle(v: SimValue<Bundle>) -> Self
where
T: BundleType,
{
let (ty, bits) = SimValue::into_ty_and_bits(v);
SimValue::from_bits(T::from_canonical(CanonicalType::Bundle(ty)), bits)
}
pub fn into_bundle(this: Self) -> SimValue<Bundle>
where
T: BundleType,
{
let (ty, bits) = Self::into_ty_and_bits(this);
SimValue::from_bits(Bundle::from_canonical(ty.canonical()), bits)
}
pub fn to_bundle(this: &Self) -> SimValue<Bundle>
where
T: BundleType,
{
SimValue::from_bits(
Bundle::from_canonical(Self::ty(this).canonical()),
Self::bits(&this).clone(),
)
}
#[track_caller]
pub fn from_enum(v: SimValue<Enum>) -> Self
where
T: EnumType,
{
let (ty, bits) = SimValue::into_ty_and_bits(v);
SimValue::from_bits(T::from_canonical(CanonicalType::Enum(ty)), bits)
}
pub fn into_enum(this: Self) -> SimValue<Enum>
where
T: EnumType,
{
let (ty, bits) = Self::into_ty_and_bits(this);
SimValue::from_bits(Enum::from_canonical(ty.canonical()), bits)
}
pub fn to_enum(this: &Self) -> SimValue<Enum>
where
T: EnumType,
{
SimValue::from_bits(
Enum::from_canonical(Self::ty(this).canonical()),
Self::bits(&this).clone(),
)
}
}
impl<T: Type> Deref for SimValue<T> {
type Target = T::SimValue;
fn deref(&self) -> &Self::Target {
Self::value(self)
}
}
impl<T: Type> DerefMut for SimValue<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
Self::value_mut(self)
}
}
impl<T: Type> fmt::Debug for SimValue<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let inner = self.inner.share();
f.debug_struct("SimValue")
.field("ty", &inner.ty)
.field("value", &inner.value)
.finish()
}
}
impl<T: Type> ToExpr for SimValue<T> {
type Type = T;
#[track_caller]
fn to_expr(&self) -> Expr<Self::Type> {
let inner = self.inner.share();
inner.bits.cast_bits_to(inner.ty)
}
}
pub trait SimValuePartialEq<T: Type = Self>: Type {
#[track_caller]
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<T>) -> bool;
}
impl<T: SimValuePartialEq<U>, U: Type> PartialEq<SimValue<U>> for SimValue<T> {
#[track_caller]
fn eq(&self, other: &SimValue<U>) -> bool {
T::sim_value_eq(self, other)
}
}
impl<Width: Size> SimValuePartialEq<Self> for UIntType<Width> {
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Self>) -> bool {
**this == **other
}
}
impl<Width: Size> SimValuePartialEq<Self> for SIntType<Width> {
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Self>) -> bool {
**this == **other
}
}
impl SimValuePartialEq<Bool> for Bool {
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Bool>) -> bool {
**this == **other
}
}
pub trait ToSimValue: ToSimValueWithType<<Self as ToSimValue>::Type> {
type Type: Type;
#[track_caller]
fn to_sim_value(&self) -> SimValue<Self::Type>;
#[track_caller]
fn into_sim_value(self) -> SimValue<Self::Type>
where
Self: Sized,
{
self.to_sim_value()
}
#[track_caller]
fn arc_into_sim_value(self: Arc<Self>) -> SimValue<Self::Type> {
self.to_sim_value()
}
#[track_caller]
fn arc_to_sim_value(self: &Arc<Self>) -> SimValue<Self::Type> {
self.to_sim_value()
}
}
pub trait ToSimValueWithType<T: Type> {
#[track_caller]
fn to_sim_value_with_type(&self, ty: T) -> SimValue<T>;
#[track_caller]
fn into_sim_value_with_type(self, ty: T) -> SimValue<T>
where
Self: Sized,
{
self.to_sim_value_with_type(ty)
}
#[track_caller]
fn arc_into_sim_value_with_type(self: Arc<Self>, ty: T) -> SimValue<T> {
self.to_sim_value_with_type(ty)
}
#[track_caller]
fn arc_to_sim_value_with_type(self: &Arc<Self>, ty: T) -> SimValue<T> {
self.to_sim_value_with_type(ty)
}
}
macro_rules! forward_to_sim_value_with_type {
([$($generics:tt)*] $ty:ty) => {
impl<$($generics)*> ToSimValueWithType<<Self as ToSimValue>::Type> for $ty {
fn to_sim_value_with_type(&self, ty: <Self as ToSimValue>::Type) -> SimValue<<Self as ToSimValue>::Type> {
let retval = Self::to_sim_value(self);
assert_eq!(SimValue::ty(&retval), ty);
retval
}
#[track_caller]
fn into_sim_value_with_type(self, ty: <Self as ToSimValue>::Type) -> SimValue<<Self as ToSimValue>::Type>
where
Self: Sized,
{
let retval = Self::into_sim_value(self);
assert_eq!(SimValue::ty(&retval), ty);
retval
}
#[track_caller]
fn arc_into_sim_value_with_type(self: Arc<Self>, ty: <Self as ToSimValue>::Type) -> SimValue<<Self as ToSimValue>::Type> {
let retval = Self::arc_into_sim_value(self);
assert_eq!(SimValue::ty(&retval), ty);
retval
}
#[track_caller]
fn arc_to_sim_value_with_type(self: &Arc<Self>, ty: <Self as ToSimValue>::Type) -> SimValue<<Self as ToSimValue>::Type> {
let retval = Self::arc_to_sim_value(self);
assert_eq!(SimValue::ty(&retval), ty);
retval
}
}
};
}
impl<T: Type> ToSimValue for SimValue<T> {
type Type = T;
fn to_sim_value(&self) -> SimValue<Self::Type> {
self.clone()
}
fn into_sim_value(self) -> SimValue<Self::Type> {
self
}
}
forward_to_sim_value_with_type!([T: Type] SimValue<T>);
impl<T: Type> ToSimValueWithType<T> for BitVec {
#[track_caller]
fn to_sim_value_with_type(&self, ty: T) -> SimValue<T> {
self.clone().into_sim_value_with_type(ty)
}
#[track_caller]
fn into_sim_value_with_type(self, ty: T) -> SimValue<T> {
Arc::new(self).arc_into_sim_value_with_type(ty)
}
#[track_caller]
fn arc_into_sim_value_with_type(self: Arc<Self>, ty: T) -> SimValue<T> {
SimValue::from_bits(ty, UIntValue::new_dyn(self))
}
#[track_caller]
fn arc_to_sim_value_with_type(self: &Arc<Self>, ty: T) -> SimValue<T> {
SimValue::from_bits(ty, UIntValue::new_dyn(self.clone()))
}
}
impl<T: Type> ToSimValueWithType<T> for bitvec::boxed::BitBox {
#[track_caller]
fn to_sim_value_with_type(&self, ty: T) -> SimValue<T> {
self.clone().into_sim_value_with_type(ty)
}
#[track_caller]
fn into_sim_value_with_type(self, ty: T) -> SimValue<T> {
self.into_bitvec().into_sim_value_with_type(ty)
}
}
impl<T: Type> ToSimValueWithType<T> for BitSlice {
#[track_caller]
fn to_sim_value_with_type(&self, ty: T) -> SimValue<T> {
self.to_bitvec().into_sim_value_with_type(ty)
}
}
impl<This: ?Sized + ToSimValue> ToSimValue for &'_ This {
type Type = This::Type;
fn to_sim_value(&self) -> SimValue<Self::Type> {
This::to_sim_value(self)
}
}
impl<This: ?Sized + ToSimValueWithType<T>, T: Type> ToSimValueWithType<T> for &'_ This {
fn to_sim_value_with_type(&self, ty: T) -> SimValue<T> {
This::to_sim_value_with_type(self, ty)
}
}
impl<This: ?Sized + ToSimValue> ToSimValue for &'_ mut This {
type Type = This::Type;
fn to_sim_value(&self) -> SimValue<Self::Type> {
This::to_sim_value(self)
}
}
impl<This: ?Sized + ToSimValueWithType<T>, T: Type> ToSimValueWithType<T> for &'_ mut This {
fn to_sim_value_with_type(&self, ty: T) -> SimValue<T> {
This::to_sim_value_with_type(self, ty)
}
}
impl<This: ?Sized + ToSimValue> ToSimValue for Arc<This> {
type Type = This::Type;
fn to_sim_value(&self) -> SimValue<Self::Type> {
This::arc_to_sim_value(self)
}
fn into_sim_value(self) -> SimValue<Self::Type> {
This::arc_into_sim_value(self)
}
}
impl<This: ?Sized + ToSimValueWithType<T>, T: Type> ToSimValueWithType<T> for Arc<This> {
fn to_sim_value_with_type(&self, ty: T) -> SimValue<T> {
This::arc_to_sim_value_with_type(self, ty)
}
fn into_sim_value_with_type(self, ty: T) -> SimValue<T> {
This::arc_into_sim_value_with_type(self, ty)
}
}
impl<This: ?Sized + ToSimValue + Send + Sync + 'static> ToSimValue
for crate::intern::Interned<This>
{
type Type = This::Type;
fn to_sim_value(&self) -> SimValue<Self::Type> {
This::to_sim_value(self)
}
}
impl<This: ?Sized + ToSimValueWithType<T> + Send + Sync + 'static, T: Type> ToSimValueWithType<T>
for crate::intern::Interned<This>
{
fn to_sim_value_with_type(&self, ty: T) -> SimValue<T> {
This::to_sim_value_with_type(self, ty)
}
}
impl<This: ToSimValue> ToSimValue for Box<This> {
type Type = This::Type;
fn to_sim_value(&self) -> SimValue<Self::Type> {
This::to_sim_value(self)
}
fn into_sim_value(self) -> SimValue<Self::Type> {
This::into_sim_value(*self)
}
}
impl<This: ToSimValueWithType<T>, T: Type> ToSimValueWithType<T> for Box<This> {
fn to_sim_value_with_type(&self, ty: T) -> SimValue<T> {
This::to_sim_value_with_type(self, ty)
}
fn into_sim_value_with_type(self, ty: T) -> SimValue<T> {
This::into_sim_value_with_type(*self, ty)
}
}
impl<T: Type, Len: Size> SimValue<ArrayType<T, Len>> {
#[track_caller]
pub fn from_array_elements<I: IntoIterator<Item: ToSimValueWithType<T>>>(
ty: ArrayType<T, Len>,
elements: I,
) -> Self {
let element_ty = ty.element();
let elements = Vec::from_iter(
elements
.into_iter()
.map(|element| element.into_sim_value_with_type(element_ty)),
);
assert_eq!(elements.len(), ty.len());
SimValue::from_value(ty, elements.try_into().ok().expect("already checked len"))
}
}
impl<Element: ToSimValueWithType<T>, T: Type> ToSimValueWithType<Array<T>> for [Element] {
#[track_caller]
fn to_sim_value_with_type(&self, ty: Array<T>) -> SimValue<Array<T>> {
SimValue::from_array_elements(ty, self)
}
}
impl<Element: ToSimValue<Type: StaticType>> ToSimValue for [Element] {
type Type = Array<Element::Type>;
#[track_caller]
fn to_sim_value(&self) -> SimValue<Self::Type> {
SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self)
}
}
impl<Element: ToSimValueWithType<CanonicalType>> ToSimValueWithType<CanonicalType> for [Element] {
#[track_caller]
fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
SimValue::into_canonical(SimValue::from_array_elements(
<Array>::from_canonical(ty),
self,
))
}
}
impl<Element: ToSimValueWithType<T>, T: Type, const N: usize> ToSimValueWithType<Array<T, N>>
for [Element; N]
where
ConstUsize<N>: KnownSize,
{
#[track_caller]
fn to_sim_value_with_type(&self, ty: Array<T, N>) -> SimValue<Array<T, N>> {
SimValue::from_array_elements(ty, self)
}
#[track_caller]
fn into_sim_value_with_type(self, ty: Array<T, N>) -> SimValue<Array<T, N>> {
SimValue::from_array_elements(ty, self)
}
}
impl<Element: ToSimValue<Type: StaticType>, const N: usize> ToSimValue for [Element; N]
where
ConstUsize<N>: KnownSize,
{
type Type = Array<Element::Type, N>;
fn to_sim_value(&self) -> SimValue<Self::Type> {
SimValue::from_array_elements(StaticType::TYPE, self)
}
fn into_sim_value(self) -> SimValue<Self::Type> {
SimValue::from_array_elements(StaticType::TYPE, self)
}
}
impl<Element: ToSimValueWithType<T>, T: Type, const N: usize> ToSimValueWithType<Array<T>>
for [Element; N]
{
#[track_caller]
fn to_sim_value_with_type(&self, ty: Array<T>) -> SimValue<Array<T>> {
SimValue::from_array_elements(ty, self)
}
#[track_caller]
fn into_sim_value_with_type(self, ty: Array<T>) -> SimValue<Array<T>> {
SimValue::from_array_elements(ty, self)
}
}
impl<Element: ToSimValueWithType<CanonicalType>, const N: usize> ToSimValueWithType<CanonicalType>
for [Element; N]
{
#[track_caller]
fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
SimValue::into_canonical(SimValue::from_array_elements(
<Array>::from_canonical(ty),
self,
))
}
#[track_caller]
fn into_sim_value_with_type(self, ty: CanonicalType) -> SimValue<CanonicalType> {
SimValue::into_canonical(SimValue::from_array_elements(
<Array>::from_canonical(ty),
self,
))
}
}
impl<Element: ToSimValueWithType<T>, T: Type> ToSimValueWithType<Array<T>> for Vec<Element> {
#[track_caller]
fn to_sim_value_with_type(&self, ty: Array<T>) -> SimValue<Array<T>> {
SimValue::from_array_elements(ty, self)
}
#[track_caller]
fn into_sim_value_with_type(self, ty: Array<T>) -> SimValue<Array<T>> {
SimValue::from_array_elements(ty, self)
}
}
impl<Element: ToSimValue<Type: StaticType>> ToSimValue for Vec<Element> {
type Type = Array<Element::Type>;
fn to_sim_value(&self) -> SimValue<Self::Type> {
SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self)
}
fn into_sim_value(self) -> SimValue<Self::Type> {
SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self)
}
}
impl<Element: ToSimValueWithType<CanonicalType>> ToSimValueWithType<CanonicalType>
for Vec<Element>
{
#[track_caller]
fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
SimValue::into_canonical(SimValue::from_array_elements(
<Array>::from_canonical(ty),
self,
))
}
#[track_caller]
fn into_sim_value_with_type(self, ty: CanonicalType) -> SimValue<CanonicalType> {
SimValue::into_canonical(SimValue::from_array_elements(
<Array>::from_canonical(ty),
self,
))
}
}
impl<Element: ToSimValueWithType<T>, T: Type> ToSimValueWithType<Array<T>> for Box<[Element]> {
#[track_caller]
fn to_sim_value_with_type(&self, ty: Array<T>) -> SimValue<Array<T>> {
SimValue::from_array_elements(ty, self)
}
#[track_caller]
fn into_sim_value_with_type(self, ty: Array<T>) -> SimValue<Array<T>> {
SimValue::from_array_elements(ty, self)
}
}
impl<Element: ToSimValue<Type: StaticType>> ToSimValue for Box<[Element]> {
type Type = Array<Element::Type>;
fn to_sim_value(&self) -> SimValue<Self::Type> {
SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self)
}
fn into_sim_value(self) -> SimValue<Self::Type> {
SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self)
}
}
impl<Element: ToSimValueWithType<CanonicalType>> ToSimValueWithType<CanonicalType>
for Box<[Element]>
{
#[track_caller]
fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
SimValue::into_canonical(SimValue::from_array_elements(
<Array>::from_canonical(ty),
self,
))
}
#[track_caller]
fn into_sim_value_with_type(self, ty: CanonicalType) -> SimValue<CanonicalType> {
SimValue::into_canonical(SimValue::from_array_elements(
<Array>::from_canonical(ty),
self,
))
}
}
impl<T: Type> ToSimValue for Expr<T> {
type Type = T;
#[track_caller]
fn to_sim_value(&self) -> SimValue<Self::Type> {
SimValue::from_bitslice(
Expr::ty(*self),
&crate::expr::ToLiteralBits::to_literal_bits(self)
.expect("must be a literal expression"),
)
}
}
forward_to_sim_value_with_type!([T: Type] Expr<T>);
macro_rules! impl_to_sim_value_for_bool_like {
($ty:ident) => {
impl ToSimValueWithType<$ty> for bool {
fn to_sim_value_with_type(&self, ty: $ty) -> SimValue<$ty> {
SimValue::from_value(ty, *self)
}
}
};
}
impl ToSimValue for bool {
type Type = Bool;
fn to_sim_value(&self) -> SimValue<Self::Type> {
SimValue::from_value(Bool, *self)
}
}
impl_to_sim_value_for_bool_like!(Bool);
impl_to_sim_value_for_bool_like!(AsyncReset);
impl_to_sim_value_for_bool_like!(SyncReset);
impl_to_sim_value_for_bool_like!(Reset);
impl_to_sim_value_for_bool_like!(Clock);
impl ToSimValueWithType<CanonicalType> for bool {
#[track_caller]
fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
match ty {
CanonicalType::UInt(_)
| CanonicalType::SInt(_)
| CanonicalType::Array(_)
| CanonicalType::Enum(_)
| CanonicalType::Bundle(_)
| CanonicalType::PhantomConst(_) => {
panic!("can't create SimValue from bool: expected value of type: {ty:?}");
}
CanonicalType::Bool(_)
| CanonicalType::AsyncReset(_)
| CanonicalType::SyncReset(_)
| CanonicalType::Reset(_)
| CanonicalType::Clock(_) => {
SimValue::from_bits(ty, UIntValue::new(Arc::new(BitVec::repeat(*self, 1))))
}
}
}
}
macro_rules! impl_to_sim_value_for_primitive_int {
($prim:ident) => {
impl ToSimValue for $prim {
type Type = <$prim as ToExpr>::Type;
#[track_caller]
fn to_sim_value(
&self,
) -> SimValue<Self::Type> {
SimValue::from_value(StaticType::TYPE, (*self).into())
}
}
forward_to_sim_value_with_type!([] $prim);
impl ToSimValueWithType<<<$prim as ToExpr>::Type as IntType>::Dyn> for $prim {
#[track_caller]
fn to_sim_value_with_type(
&self,
ty: <<$prim as ToExpr>::Type as IntType>::Dyn,
) -> SimValue<<<$prim as ToExpr>::Type as IntType>::Dyn> {
SimValue::from_value(
ty,
<<$prim as ToExpr>::Type as Type>::SimValue::from(*self).as_dyn_int(),
)
}
}
impl ToSimValueWithType<CanonicalType> for $prim {
#[track_caller]
fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
let ty: <<$prim as ToExpr>::Type as IntType>::Dyn = Type::from_canonical(ty);
SimValue::into_canonical(self.to_sim_value_with_type(ty))
}
}
};
}
impl_to_sim_value_for_primitive_int!(u8);
impl_to_sim_value_for_primitive_int!(u16);
impl_to_sim_value_for_primitive_int!(u32);
impl_to_sim_value_for_primitive_int!(u64);
impl_to_sim_value_for_primitive_int!(u128);
impl_to_sim_value_for_primitive_int!(usize);
impl_to_sim_value_for_primitive_int!(i8);
impl_to_sim_value_for_primitive_int!(i16);
impl_to_sim_value_for_primitive_int!(i32);
impl_to_sim_value_for_primitive_int!(i64);
impl_to_sim_value_for_primitive_int!(i128);
impl_to_sim_value_for_primitive_int!(isize);
macro_rules! impl_to_sim_value_for_int_value {
($IntValue:ident, $Int:ident, $IntType:ident) => {
impl<Width: Size> ToSimValue for $IntValue<Width> {
type Type = $IntType<Width>;
fn to_sim_value(&self) -> SimValue<Self::Type> {
SimValue::from_value(self.ty(), self.clone())
}
fn into_sim_value(self) -> SimValue<Self::Type> {
SimValue::from_value(self.ty(), self)
}
}
impl<Width: Size> ToSimValueWithType<$IntType<Width>> for $IntValue<Width> {
fn to_sim_value_with_type(&self, ty: $IntType<Width>) -> SimValue<$IntType<Width>> {
SimValue::from_value(ty, self.clone())
}
fn into_sim_value_with_type(self, ty: $IntType<Width>) -> SimValue<$IntType<Width>> {
SimValue::from_value(ty, self)
}
}
impl<Width: KnownSize> ToSimValueWithType<$Int> for $IntValue<Width> {
fn to_sim_value_with_type(&self, ty: $Int) -> SimValue<$Int> {
self.bits().to_sim_value_with_type(ty)
}
fn into_sim_value_with_type(self, ty: $Int) -> SimValue<$Int> {
self.into_bits().into_sim_value_with_type(ty)
}
}
impl<Width: Size> ToSimValueWithType<CanonicalType> for $IntValue<Width> {
#[track_caller]
fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
SimValue::into_canonical(
self.to_sim_value_with_type($IntType::<Width>::from_canonical(ty)),
)
}
#[track_caller]
fn into_sim_value_with_type(self, ty: CanonicalType) -> SimValue<CanonicalType> {
SimValue::into_canonical(
self.into_sim_value_with_type($IntType::<Width>::from_canonical(ty)),
)
}
}
};
}
impl_to_sim_value_for_int_value!(UIntValue, UInt, UIntType);
impl_to_sim_value_for_int_value!(SIntValue, SInt, SIntType);

View file

@ -14,9 +14,10 @@ use crate::{
TraceModuleIO, TraceReg, TraceSInt, TraceScalar, TraceScalarId, TraceScope, TraceSyncReset,
TraceUInt, TraceWire, TraceWriter, TraceWriterDecls,
},
util::HashMap,
};
use bitvec::{order::Lsb0, slice::BitSlice};
use hashbrown::{hash_map::Entry, HashMap};
use hashbrown::hash_map::Entry;
use std::{
fmt::{self, Write as _},
io, mem,

View file

@ -2,9 +2,8 @@
// See Notices.txt for copyright information
use crate::{
intern::{Intern, Interned},
util::DebugAsDisplay,
util::{DebugAsDisplay, HashMap},
};
use hashbrown::HashMap;
use std::{cell::RefCell, fmt, num::NonZeroUsize, panic, path::Path};
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -97,7 +96,7 @@ impl NormalizeFilesForTestsState {
fn new() -> Self {
Self {
test_position: panic::Location::caller(),
file_pattern_matches: HashMap::new(),
file_pattern_matches: HashMap::default(),
}
}
}
@ -143,7 +142,7 @@ impl From<&'_ panic::Location<'_>> for SourceLocation {
map.entry_ref(file)
.or_insert_with(|| NormalizedFileForTestState {
file_name_id: NonZeroUsize::new(len + 1).unwrap(),
positions_map: HashMap::new(),
positions_map: HashMap::default(),
});
file_str = m.generate_file_name(file_state.file_name_id);
file = &file_str;

View file

@ -3,9 +3,9 @@
use crate::{
cli::{FormalArgs, FormalMode, FormalOutput, RunPhase},
firrtl::ExportOptions,
util::HashMap,
};
use clap::Parser;
use hashbrown::HashMap;
use serde::Deserialize;
use std::{
fmt::Write,
@ -87,7 +87,7 @@ fn get_assert_formal_target_path(test_name: &dyn std::fmt::Display) -> PathBuf {
let index = *DIRS
.lock()
.unwrap()
.get_or_insert_with(HashMap::new)
.get_or_insert_with(HashMap::default)
.entry_ref(&dir)
.and_modify(|v| *v += 1)
.or_insert(0);

View file

@ -7,12 +7,19 @@ use crate::{
clock::Clock,
enum_::Enum,
expr::Expr,
int::{Bool, SInt, UInt},
int::{Bool, SInt, UInt, UIntValue},
intern::{Intern, Interned},
phantom_const::PhantomConst,
reset::{AsyncReset, Reset, SyncReset},
sim::value::{SimValue, ToSimValueWithType},
source_location::SourceLocation,
util::ConstUsize,
};
use std::{fmt, hash::Hash, iter::FusedIterator, ops::Index};
use bitvec::slice::BitSlice;
use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize, Serializer};
use std::{fmt, hash::Hash, iter::FusedIterator, ops::Index, sync::Arc};
pub(crate) mod serde_impls;
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
#[non_exhaustive]
@ -35,6 +42,7 @@ pub enum CanonicalType {
SyncReset(SyncReset),
Reset(Reset),
Clock(Clock),
PhantomConst(PhantomConst),
}
impl fmt::Debug for CanonicalType {
@ -50,10 +58,29 @@ impl fmt::Debug for CanonicalType {
Self::SyncReset(v) => v.fmt(f),
Self::Reset(v) => v.fmt(f),
Self::Clock(v) => v.fmt(f),
Self::PhantomConst(v) => v.fmt(f),
}
}
}
impl Serialize for CanonicalType {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serde_impls::SerdeCanonicalType::from(*self).serialize(serializer)
}
}
impl<'de> Deserialize<'de> for CanonicalType {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
Ok(serde_impls::SerdeCanonicalType::deserialize(deserializer)?.into())
}
}
impl CanonicalType {
pub fn type_properties(self) -> TypeProperties {
match self {
@ -67,6 +94,7 @@ impl CanonicalType {
CanonicalType::SyncReset(v) => v.type_properties(),
CanonicalType::Reset(v) => v.type_properties(),
CanonicalType::Clock(v) => v.type_properties(),
CanonicalType::PhantomConst(v) => v.type_properties(),
}
}
pub fn is_passive(self) -> bool {
@ -143,8 +171,17 @@ impl CanonicalType {
};
lhs.can_connect(rhs)
}
CanonicalType::PhantomConst(lhs) => {
let CanonicalType::PhantomConst(rhs) = rhs else {
return false;
};
lhs.can_connect(rhs)
}
}
}
pub(crate) fn as_serde_unexpected_str(self) -> &'static str {
serde_impls::SerdeCanonicalType::from(self).as_serde_unexpected_str()
}
}
pub trait MatchVariantAndInactiveScope: Sized {
@ -166,7 +203,7 @@ impl<T: 'static + Send + Sync> MatchVariantAndInactiveScope for MatchVariantWith
}
pub trait FillInDefaultedGenerics {
type Type: Type;
type Type;
fn fill_in_defaulted_generics(self) -> Self::Type;
}
@ -178,6 +215,22 @@ impl<T: Type> FillInDefaultedGenerics for T {
}
}
impl FillInDefaultedGenerics for usize {
type Type = usize;
fn fill_in_defaulted_generics(self) -> Self::Type {
self
}
}
impl<const V: usize> FillInDefaultedGenerics for ConstUsize<V> {
type Type = ConstUsize<V>;
fn fill_in_defaulted_generics(self) -> Self::Type {
self
}
}
mod sealed {
pub trait TypeOrDefaultSealed {}
pub trait BaseTypeSealed {}
@ -195,6 +248,34 @@ macro_rules! impl_base_type {
};
}
macro_rules! impl_base_type_serde {
($name:ident, $expected:literal) => {
impl Serialize for $name {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.canonical().serialize(serializer)
}
}
impl<'de> Deserialize<'de> for $name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
match CanonicalType::deserialize(deserializer)? {
CanonicalType::$name(retval) => Ok(retval),
ty => Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Other(ty.as_serde_unexpected_str()),
&$expected,
)),
}
}
}
};
}
impl_base_type!(UInt);
impl_base_type!(SInt);
impl_base_type!(Bool);
@ -205,6 +286,15 @@ impl_base_type!(AsyncReset);
impl_base_type!(SyncReset);
impl_base_type!(Reset);
impl_base_type!(Clock);
impl_base_type!(PhantomConst);
impl_base_type_serde!(Bool, "a Bool");
impl_base_type_serde!(Enum, "an Enum");
impl_base_type_serde!(Bundle, "a Bundle");
impl_base_type_serde!(AsyncReset, "an AsyncReset");
impl_base_type_serde!(SyncReset, "a SyncReset");
impl_base_type_serde!(Reset, "a Reset");
impl_base_type_serde!(Clock, "a Clock");
impl sealed::BaseTypeSealed for CanonicalType {}
@ -240,6 +330,7 @@ pub trait Type:
{
type BaseType: BaseType;
type MaskType: Type<MaskType = Self::MaskType>;
type SimValue: fmt::Debug + Clone + 'static + ToSimValueWithType<Self>;
type MatchVariant: 'static + Send + Sync;
type MatchActiveScope;
type MatchVariantAndInactiveScope: MatchVariantAndInactiveScope<
@ -257,9 +348,22 @@ pub trait Type:
fn canonical(&self) -> CanonicalType;
fn from_canonical(canonical_type: CanonicalType) -> Self;
fn source_location() -> SourceLocation;
fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue;
fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice);
fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice);
}
pub trait BaseType: Type<BaseType = Self> + sealed::BaseTypeSealed + Into<CanonicalType> {}
pub trait BaseType:
Type<
BaseType = Self,
MaskType: Serialize + DeserializeOwned,
SimValue: Serialize + DeserializeOwned,
> + sealed::BaseTypeSealed
+ Into<CanonicalType>
+ Serialize
+ DeserializeOwned
{
}
macro_rules! impl_match_variant_as_self {
() => {
@ -286,6 +390,7 @@ pub trait TypeWithDeref: Type {
impl Type for CanonicalType {
type BaseType = CanonicalType;
type MaskType = CanonicalType;
type SimValue = OpaqueSimValue;
impl_match_variant_as_self!();
fn mask_type(&self) -> Self::MaskType {
match self {
@ -299,6 +404,7 @@ impl Type for CanonicalType {
CanonicalType::SyncReset(v) => v.mask_type().canonical(),
CanonicalType::Reset(v) => v.mask_type().canonical(),
CanonicalType::Clock(v) => v.mask_type().canonical(),
CanonicalType::PhantomConst(v) => v.mask_type().canonical(),
}
}
fn canonical(&self) -> CanonicalType {
@ -310,9 +416,60 @@ impl Type for CanonicalType {
fn source_location() -> SourceLocation {
SourceLocation::builtin()
}
fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue {
assert_eq!(bits.len(), self.bit_width());
OpaqueSimValue::from_bitslice(bits)
}
fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) {
assert_eq!(bits.len(), self.bit_width());
assert_eq!(value.bit_width(), self.bit_width());
value.bits_mut().bits_mut().copy_from_bitslice(bits);
}
fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) {
assert_eq!(bits.len(), self.bit_width());
assert_eq!(value.bit_width(), self.bit_width());
bits.copy_from_bitslice(value.bits().bits());
}
}
pub trait StaticType: Type {
#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
pub struct OpaqueSimValue {
bits: UIntValue,
}
impl OpaqueSimValue {
pub fn bit_width(&self) -> usize {
self.bits.width()
}
pub fn bits(&self) -> &UIntValue {
&self.bits
}
pub fn bits_mut(&mut self) -> &mut UIntValue {
&mut self.bits
}
pub fn into_bits(self) -> UIntValue {
self.bits
}
pub fn from_bits(bits: UIntValue) -> Self {
Self { bits }
}
pub fn from_bitslice(v: &BitSlice) -> Self {
Self {
bits: UIntValue::new(Arc::new(v.to_bitvec())),
}
}
}
impl<T: Type<SimValue = OpaqueSimValue>> ToSimValueWithType<T> for OpaqueSimValue {
fn to_sim_value_with_type(&self, ty: T) -> SimValue<T> {
SimValue::from_value(ty, self.clone())
}
fn into_sim_value_with_type(self, ty: T) -> SimValue<T> {
SimValue::from_value(ty, self)
}
}
pub trait StaticType: Type + Default {
const TYPE: Self;
const MASK_TYPE: Self::MaskType;
const TYPE_PROPERTIES: TypeProperties;

View file

@ -0,0 +1,130 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
array::Array,
bundle::{Bundle, BundleType},
clock::Clock,
enum_::{Enum, EnumType},
int::{Bool, SInt, UInt},
intern::Interned,
phantom_const::{PhantomConstCanonicalValue, PhantomConstValue},
prelude::PhantomConst,
reset::{AsyncReset, Reset, SyncReset},
ty::{BaseType, CanonicalType},
};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub(crate) struct SerdePhantomConst<T>(pub T);
impl<T: ?Sized + PhantomConstValue> Serialize for SerdePhantomConst<Interned<T>> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.0.serialize(serializer)
}
}
impl<'de, T: ?Sized + PhantomConstValue> Deserialize<'de> for SerdePhantomConst<Interned<T>> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
T::deserialize_value(deserializer).map(Self)
}
}
#[derive(Serialize, Deserialize)]
#[serde(rename = "CanonicalType")]
pub(crate) enum SerdeCanonicalType<
ArrayElement = CanonicalType,
ThePhantomConst = SerdePhantomConst<Interned<PhantomConstCanonicalValue>>,
> {
UInt {
width: usize,
},
SInt {
width: usize,
},
Bool,
Array {
element: ArrayElement,
len: usize,
},
Enum {
variants: Interned<[crate::enum_::EnumVariant]>,
},
Bundle {
fields: Interned<[crate::bundle::BundleField]>,
},
AsyncReset,
SyncReset,
Reset,
Clock,
PhantomConst(ThePhantomConst),
}
impl<ArrayElement, PhantomConstInner> SerdeCanonicalType<ArrayElement, PhantomConstInner> {
pub(crate) fn as_serde_unexpected_str(&self) -> &'static str {
match self {
Self::UInt { .. } => "a UInt",
Self::SInt { .. } => "a SInt",
Self::Bool => "a Bool",
Self::Array { .. } => "an Array",
Self::Enum { .. } => "an Enum",
Self::Bundle { .. } => "a Bundle",
Self::AsyncReset => "an AsyncReset",
Self::SyncReset => "a SyncReset",
Self::Reset => "a Reset",
Self::Clock => "a Clock",
Self::PhantomConst(_) => "a PhantomConst",
}
}
}
impl<T: BaseType> From<T> for SerdeCanonicalType {
fn from(ty: T) -> Self {
let ty: CanonicalType = ty.into();
match ty {
CanonicalType::UInt(ty) => Self::UInt { width: ty.width() },
CanonicalType::SInt(ty) => Self::SInt { width: ty.width() },
CanonicalType::Bool(Bool {}) => Self::Bool,
CanonicalType::Array(ty) => Self::Array {
element: ty.element(),
len: ty.len(),
},
CanonicalType::Enum(ty) => Self::Enum {
variants: ty.variants(),
},
CanonicalType::Bundle(ty) => Self::Bundle {
fields: ty.fields(),
},
CanonicalType::AsyncReset(AsyncReset {}) => Self::AsyncReset,
CanonicalType::SyncReset(SyncReset {}) => Self::SyncReset,
CanonicalType::Reset(Reset {}) => Self::Reset,
CanonicalType::Clock(Clock {}) => Self::Clock,
CanonicalType::PhantomConst(ty) => Self::PhantomConst(SerdePhantomConst(ty.get())),
}
}
}
impl From<SerdeCanonicalType> for CanonicalType {
fn from(ty: SerdeCanonicalType) -> Self {
match ty {
SerdeCanonicalType::UInt { width } => Self::UInt(UInt::new(width)),
SerdeCanonicalType::SInt { width } => Self::SInt(SInt::new(width)),
SerdeCanonicalType::Bool => Self::Bool(Bool),
SerdeCanonicalType::Array { element, len } => Self::Array(Array::new(element, len)),
SerdeCanonicalType::Enum { variants } => Self::Enum(Enum::new(variants)),
SerdeCanonicalType::Bundle { fields } => Self::Bundle(Bundle::new(fields)),
SerdeCanonicalType::AsyncReset => Self::AsyncReset(AsyncReset),
SerdeCanonicalType::SyncReset => Self::SyncReset(SyncReset),
SerdeCanonicalType::Reset => Self::Reset(Reset),
SerdeCanonicalType::Clock => Self::Clock(Clock),
SerdeCanonicalType::PhantomConst(value) => {
Self::PhantomConst(PhantomConst::new(value.0))
}
}
}
}

View file

@ -1,12 +1,23 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
pub(crate) mod alternating_cell;
mod const_bool;
mod const_cmp;
mod const_usize;
mod misc;
mod scoped_ref;
pub(crate) mod streaming_read_utf8;
mod test_hasher;
// allow easily switching the hasher crate-wide for testing
#[cfg(feature = "unstable-test-hasher")]
pub type DefaultBuildHasher = test_hasher::DefaultBuildHasher;
#[cfg(not(feature = "unstable-test-hasher"))]
pub(crate) type DefaultBuildHasher = hashbrown::DefaultHashBuilder;
pub(crate) type HashMap<K, V> = hashbrown::HashMap<K, V, DefaultBuildHasher>;
pub(crate) type HashSet<T> = hashbrown::HashSet<T, DefaultBuildHasher>;
#[doc(inline)]
pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool};
@ -29,4 +40,5 @@ pub use misc::{
};
pub mod job_server;
pub mod prefix_sum;
pub mod ready_valid;

View file

@ -0,0 +1,122 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::util::DebugAsDisplay;
use std::{
cell::{Cell, UnsafeCell},
fmt,
};
pub(crate) trait AlternatingCellMethods {
fn unique_to_shared(&mut self);
fn shared_to_unique(&mut self);
}
#[derive(Copy, Clone, Debug)]
enum State {
Unique,
Shared,
Locked,
}
pub(crate) struct AlternatingCell<T: ?Sized> {
state: Cell<State>,
value: UnsafeCell<T>,
}
impl<T: ?Sized + fmt::Debug + AlternatingCellMethods> fmt::Debug for AlternatingCell<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("AlternatingCell")
.field(
self.try_share()
.as_ref()
.map(|v| -> &dyn fmt::Debug { v })
.unwrap_or(&DebugAsDisplay("<...>")),
)
.finish()
}
}
impl<T: ?Sized> AlternatingCell<T> {
pub(crate) const fn new_shared(value: T) -> Self
where
T: Sized,
{
Self {
state: Cell::new(State::Shared),
value: UnsafeCell::new(value),
}
}
pub(crate) const fn new_unique(value: T) -> Self
where
T: Sized,
{
Self {
state: Cell::new(State::Unique),
value: UnsafeCell::new(value),
}
}
pub(crate) fn is_unique(&self) -> bool {
matches!(self.state.get(), State::Unique)
}
pub(crate) fn is_shared(&self) -> bool {
matches!(self.state.get(), State::Shared)
}
pub(crate) fn into_inner(self) -> T
where
T: Sized,
{
self.value.into_inner()
}
pub(crate) fn try_share(&self) -> Option<&T>
where
T: AlternatingCellMethods,
{
match self.state.get() {
State::Shared => {}
State::Unique => {
struct Locked<'a>(&'a Cell<State>);
impl Drop for Locked<'_> {
fn drop(&mut self) {
self.0.set(State::Shared);
}
}
self.state.set(State::Locked);
let lock = Locked(&self.state);
// Safety: state is Locked, so nothing else will
// access value while calling unique_to_shared.
unsafe { &mut *self.value.get() }.unique_to_shared();
drop(lock);
}
State::Locked => return None,
}
// Safety: state is Shared so nothing will create any mutable
// references until the returned reference's lifetime expires.
Some(unsafe { &*self.value.get() })
}
#[track_caller]
pub(crate) fn share(&self) -> &T
where
T: AlternatingCellMethods,
{
let Some(retval) = self.try_share() else {
panic!("`share` called recursively");
};
retval
}
pub(crate) fn unique(&mut self) -> &mut T
where
T: AlternatingCellMethods,
{
match self.state.get() {
State::Shared => {
self.state.set(State::Unique);
self.value.get_mut().shared_to_unique();
}
State::Unique => {}
State::Locked => unreachable!(),
}
self.value.get_mut()
}
}

View file

@ -1,5 +1,9 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use serde::{
de::{DeserializeOwned, Error, Unexpected},
Deserialize, Deserializer, Serialize, Serializer,
};
use std::{fmt::Debug, hash::Hash, mem::ManuallyDrop, ptr};
mod sealed {
@ -9,7 +13,17 @@ mod sealed {
/// # Safety
/// the only implementation is `ConstBool<Self::VALUE>`
pub unsafe trait GenericConstBool:
sealed::Sealed + Copy + Ord + Hash + Default + Debug + 'static + Send + Sync
sealed::Sealed
+ Copy
+ Ord
+ Hash
+ Default
+ Debug
+ 'static
+ Send
+ Sync
+ Serialize
+ DeserializeOwned
{
const VALUE: bool;
}
@ -30,6 +44,32 @@ unsafe impl<const VALUE: bool> GenericConstBool for ConstBool<VALUE> {
const VALUE: bool = VALUE;
}
impl<const VALUE: bool> Serialize for ConstBool<VALUE> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
VALUE.serialize(serializer)
}
}
impl<'de, const VALUE: bool> Deserialize<'de> for ConstBool<VALUE> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let value = bool::deserialize(deserializer)?;
if value == VALUE {
Ok(ConstBool)
} else {
Err(D::Error::invalid_value(
Unexpected::Bool(value),
&if VALUE { "true" } else { "false" },
))
}
}
}
pub trait ConstBoolDispatchTag {
type Type<Select: GenericConstBool>;
}

View file

@ -1,5 +1,9 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use serde::{
de::{DeserializeOwned, Error, Unexpected},
Deserialize, Deserializer, Serialize, Serializer,
};
use std::{fmt::Debug, hash::Hash};
mod sealed {
@ -8,7 +12,17 @@ mod sealed {
/// the only implementation is `ConstUsize<Self::VALUE>`
pub trait GenericConstUsize:
sealed::Sealed + Copy + Ord + Hash + Default + Debug + 'static + Send + Sync
sealed::Sealed
+ Copy
+ Ord
+ Hash
+ Default
+ Debug
+ 'static
+ Send
+ Sync
+ Serialize
+ DeserializeOwned
{
const VALUE: usize;
}
@ -27,3 +41,29 @@ impl<const VALUE: usize> sealed::Sealed for ConstUsize<VALUE> {}
impl<const VALUE: usize> GenericConstUsize for ConstUsize<VALUE> {
const VALUE: usize = VALUE;
}
impl<const VALUE: usize> Serialize for ConstUsize<VALUE> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
VALUE.serialize(serializer)
}
}
impl<'de, const VALUE: usize> Deserialize<'de> for ConstUsize<VALUE> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let value = usize::deserialize(deserializer)?;
if value == VALUE {
Ok(ConstUsize)
} else {
Err(D::Error::invalid_value(
Unexpected::Unsigned(value as u64),
&&*VALUE.to_string(),
))
}
}
}

View file

@ -0,0 +1,839 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
// code derived from:
// https://web.archive.org/web/20250303054010/https://git.libre-soc.org/?p=nmutil.git;a=blob;f=src/nmutil/prefix_sum.py;hb=effeb28e5848392adddcdad1f6e7a098f2a44c9c
use crate::intern::{Intern, Interned, Memoize};
use std::{borrow::Cow, num::NonZeroUsize};
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct PrefixSumOp {
pub lhs_index: usize,
pub rhs_and_dest_index: NonZeroUsize,
pub row: u32,
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub struct DiagramConfig {
pub space: Cow<'static, str>,
pub vertical_bar: Cow<'static, str>,
pub plus: Cow<'static, str>,
pub slant: Cow<'static, str>,
pub connect: Cow<'static, str>,
pub no_connect: Cow<'static, str>,
pub padding: usize,
}
impl DiagramConfig {
pub const fn new() -> Self {
Self {
space: Cow::Borrowed(" "),
vertical_bar: Cow::Borrowed("|"),
plus: Cow::Borrowed("\u{2295}"), // ⊕
slant: Cow::Borrowed(r"\"),
connect: Cow::Borrowed("\u{25CF}"), // ●
no_connect: Cow::Borrowed("X"),
padding: 1,
}
}
pub fn draw(self, ops: impl IntoIterator<Item = PrefixSumOp>, item_count: usize) -> String {
#[derive(Copy, Clone, Debug)]
struct DiagramCell {
slant: bool,
plus: bool,
tee: bool,
}
let mut ops_by_row: Vec<Vec<PrefixSumOp>> = Vec::new();
let mut last_row = 0;
ops.into_iter().for_each(|op| {
assert!(
op.lhs_index < op.rhs_and_dest_index.get(),
"invalid PrefixSumOp! lhs_index must be less \
than rhs_and_dest_index: {op:?}",
);
assert!(
op.row >= last_row,
"invalid PrefixSumOp! row must \
not decrease (row last was: {last_row}): {op:?}",
);
let ops = if op.row > last_row || ops_by_row.is_empty() {
ops_by_row.push(vec![]);
ops_by_row.last_mut().expect("just pushed")
} else {
ops_by_row
.last_mut()
.expect("just checked if ops_by_row is empty")
};
if let Some(last) = ops.last() {
assert!(
op.rhs_and_dest_index < last.rhs_and_dest_index,
"invalid PrefixSumOp! rhs_and_dest_index must strictly \
decrease in a row:\nthis op: {op:?}\nlast op: {last:?}",
);
}
ops.push(op);
last_row = op.row;
});
let blank_row = || {
vec![
DiagramCell {
slant: false,
plus: false,
tee: false
};
item_count
]
};
let mut cells = vec![blank_row()];
for ops in ops_by_row {
let max_distance = ops
.iter()
.map(
|&PrefixSumOp {
lhs_index,
rhs_and_dest_index,
..
}| { rhs_and_dest_index.get() - lhs_index },
)
.max()
.expect("ops is known to be non-empty");
cells.extend((0..max_distance).map(|_| blank_row()));
for op in ops {
let mut y = cells.len() - 1;
assert!(
op.rhs_and_dest_index.get() < item_count,
"invalid PrefixSumOp! rhs_and_dest_index must be \
less than item_count ({item_count}): {op:?}",
);
let mut x = op.rhs_and_dest_index.get();
cells[y][x].plus = true;
x -= 1;
y -= 1;
while op.lhs_index < x {
cells[y][x].slant = true;
x -= 1;
y -= 1;
}
cells[y][x].tee = true;
}
}
let mut retval = String::new();
let mut row_text = vec![String::new(); 2 * self.padding + 1];
for cells_row in cells {
for cell in cells_row {
// top padding
for y in 0..self.padding {
// top left padding
for x in 0..self.padding {
row_text[y] += if x == y && (cell.plus || cell.slant) {
&self.slant
} else {
&self.space
};
}
// top vertical bar
row_text[y] += &self.vertical_bar;
// top right padding
for _ in 0..self.padding {
row_text[y] += &self.space;
}
}
// center left padding
for _ in 0..self.padding {
row_text[self.padding] += &self.space;
}
// center
row_text[self.padding] += if cell.plus {
&self.plus
} else if cell.tee {
&self.connect
} else if cell.slant {
&self.no_connect
} else {
&self.vertical_bar
};
// center right padding
for _ in 0..self.padding {
row_text[self.padding] += &self.space;
}
let bottom_padding_start = self.padding + 1;
let bottom_padding_last = self.padding * 2;
// bottom padding
for y in bottom_padding_start..=bottom_padding_last {
// bottom left padding
for _ in 0..self.padding {
row_text[y] += &self.space;
}
// bottom vertical bar
row_text[y] += &self.vertical_bar;
// bottom right padding
for x in bottom_padding_start..=bottom_padding_last {
row_text[y] += if x == y && (cell.tee || cell.slant) {
&self.slant
} else {
&self.space
};
}
}
}
for line in &mut row_text {
retval += line.trim_end();
retval += "\n";
line.clear();
}
}
retval
}
}
impl Default for DiagramConfig {
fn default() -> Self {
Self::new()
}
}
impl PrefixSumOp {
pub fn diagram(ops: impl IntoIterator<Item = Self>, item_count: usize) -> String {
Self::diagram_with_config(ops, item_count, DiagramConfig::new())
}
pub fn diagram_with_config(
ops: impl IntoIterator<Item = Self>,
item_count: usize,
config: DiagramConfig,
) -> String {
config.draw(ops, item_count)
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum PrefixSumAlgorithm {
/// Uses the algorithm from:
/// <https://en.wikipedia.org/wiki/Prefix_sum#Algorithm_1:_Shorter_span,_more_parallel>
LowLatency,
/// Uses the algorithm from:
/// <https://en.wikipedia.org/wiki/Prefix_sum#Algorithm_2:_Work-efficient>
WorkEfficient,
}
impl PrefixSumAlgorithm {
fn ops_impl(self, item_count: usize) -> Vec<PrefixSumOp> {
let mut retval = Vec::new();
let mut distance = 1;
let mut row = 0;
while distance < item_count {
let double_distance = distance
.checked_mul(2)
.expect("prefix-sum item_count is too big");
let (start, step) = match self {
Self::LowLatency => (distance, 1),
Self::WorkEfficient => (double_distance - 1, double_distance),
};
for rhs_and_dest_index in (start..item_count).step_by(step).rev() {
let Some(rhs_and_dest_index) = NonZeroUsize::new(rhs_and_dest_index) else {
unreachable!();
};
let lhs_index = rhs_and_dest_index.get() - distance;
retval.push(PrefixSumOp {
lhs_index,
rhs_and_dest_index,
row,
});
}
distance = double_distance;
row += 1;
}
match self {
Self::LowLatency => {}
Self::WorkEfficient => {
distance /= 2;
while distance >= 1 {
let start = distance
.checked_mul(3)
.expect("prefix-sum item_count is too big")
- 1;
for rhs_and_dest_index in (start..item_count).step_by(distance * 2).rev() {
let Some(rhs_and_dest_index) = NonZeroUsize::new(rhs_and_dest_index) else {
unreachable!();
};
let lhs_index = rhs_and_dest_index.get() - distance;
retval.push(PrefixSumOp {
lhs_index,
rhs_and_dest_index,
row,
});
}
row += 1;
distance /= 2;
}
}
}
retval
}
pub fn ops(self, item_count: usize) -> Interned<[PrefixSumOp]> {
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
struct MyMemoize(PrefixSumAlgorithm);
impl Memoize for MyMemoize {
type Input = usize;
type InputOwned = usize;
type Output = Interned<[PrefixSumOp]>;
fn inner(self, item_count: &Self::Input) -> Self::Output {
Intern::intern_owned(self.0.ops_impl(*item_count))
}
}
MyMemoize(self).get_owned(item_count)
}
pub fn run<T>(self, items: impl IntoIterator<Item = T>, f: impl FnMut(&T, &T) -> T) -> Vec<T> {
let mut items = Vec::from_iter(items);
self.run_on_slice(&mut items, f);
items
}
pub fn run_on_slice<T>(self, items: &mut [T], mut f: impl FnMut(&T, &T) -> T) -> &mut [T] {
self.ops(items.len()).into_iter().for_each(
|PrefixSumOp {
lhs_index,
rhs_and_dest_index,
row: _,
}| {
items[rhs_and_dest_index.get()] =
f(&items[lhs_index], &items[rhs_and_dest_index.get()]);
},
);
items
}
pub fn filtered_ops(
self,
item_live_out_flags: impl IntoIterator<Item = bool>,
) -> Vec<PrefixSumOp> {
let mut item_live_out_flags = Vec::from_iter(item_live_out_flags);
let prefix_sum_ops = self.ops(item_live_out_flags.len());
let mut ops_live_flags = vec![false; prefix_sum_ops.len()];
for (
op_index,
&PrefixSumOp {
lhs_index,
rhs_and_dest_index,
row: _,
},
) in prefix_sum_ops.iter().enumerate().rev()
{
let live = item_live_out_flags[rhs_and_dest_index.get()];
item_live_out_flags[lhs_index] |= live;
ops_live_flags[op_index] = live;
}
prefix_sum_ops
.into_iter()
.zip(ops_live_flags)
.filter_map(|(op, live)| live.then_some(op))
.collect()
}
pub fn reduce_ops(self, item_count: usize) -> Interned<[PrefixSumOp]> {
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
struct MyMemoize(PrefixSumAlgorithm);
impl Memoize for MyMemoize {
type Input = usize;
type InputOwned = usize;
type Output = Interned<[PrefixSumOp]>;
fn inner(self, item_count: &Self::Input) -> Self::Output {
let mut item_live_out_flags = vec![false; *item_count];
let Some(last_item_live_out_flag) = item_live_out_flags.last_mut() else {
return Interned::default();
};
*last_item_live_out_flag = true;
Intern::intern_owned(self.0.filtered_ops(item_live_out_flags))
}
}
MyMemoize(self).get_owned(item_count)
}
}
pub fn reduce_ops(item_count: usize) -> Interned<[PrefixSumOp]> {
PrefixSumAlgorithm::LowLatency.reduce_ops(item_count)
}
pub fn reduce<T>(items: impl IntoIterator<Item = T>, mut f: impl FnMut(T, T) -> T) -> Option<T> {
let mut items: Vec<_> = items.into_iter().map(Some).collect();
for op in reduce_ops(items.len()) {
let (Some(lhs), Some(rhs)) = (
items[op.lhs_index].take(),
items[op.rhs_and_dest_index.get()].take(),
) else {
unreachable!();
};
items[op.rhs_and_dest_index.get()] = Some(f(lhs, rhs));
}
items.last_mut().and_then(Option::take)
}
#[cfg(test)]
mod tests {
use super::*;
fn input_strings() -> [String; 9] {
std::array::from_fn(|i| String::from_utf8(vec![b'a' + i as u8]).unwrap())
}
#[test]
fn test_prefix_sum_strings() {
let input = input_strings();
let expected: Vec<String> = input
.iter()
.scan(String::new(), |l, r| {
*l += r;
Some(l.clone())
})
.collect();
println!("expected: {expected:?}");
assert_eq!(
*PrefixSumAlgorithm::WorkEfficient
.run_on_slice(&mut input.clone(), |l, r| l.to_string() + r),
*expected
);
assert_eq!(
*PrefixSumAlgorithm::LowLatency
.run_on_slice(&mut input.clone(), |l, r| l.to_string() + r),
*expected
);
}
#[test]
fn test_reduce_string() {
let input = input_strings();
let expected = input.clone().into_iter().reduce(|l, r| l + &r);
assert_eq!(reduce(input, |l, r| l + &r), expected);
}
fn op(lhs_index: usize, rhs_and_dest_index: usize, row: u32) -> PrefixSumOp {
PrefixSumOp {
lhs_index,
rhs_and_dest_index: NonZeroUsize::new(rhs_and_dest_index).expect("should be non-zero"),
row,
}
}
#[test]
fn test_reduce_ops_9() {
let expected = vec![
op(7, 8, 0),
op(5, 6, 0),
op(3, 4, 0),
op(1, 2, 0),
op(6, 8, 1),
op(2, 4, 1),
op(4, 8, 2),
op(0, 8, 3),
];
println!("expected: {expected:#?}");
let ops = reduce_ops(9);
println!("ops: {ops:#?}");
assert_eq!(*ops, *expected);
}
#[test]
fn test_reduce_ops_8() {
let expected = vec![
op(6, 7, 0),
op(4, 5, 0),
op(2, 3, 0),
op(0, 1, 0),
op(5, 7, 1),
op(1, 3, 1),
op(3, 7, 2),
];
println!("expected: {expected:#?}");
let ops = reduce_ops(8);
println!("ops: {ops:#?}");
assert_eq!(*ops, *expected);
}
#[test]
fn test_count_ones() {
for width in 0..=10u32 {
for v in 0..1u32 << width {
let expected = v.count_ones();
assert_eq!(
reduce((0..width).map(|i| (v >> i) & 1), |l, r| l + r).unwrap_or(0),
expected,
"v={v:#X}"
);
}
}
}
#[track_caller]
fn test_diagram(ops: impl IntoIterator<Item = PrefixSumOp>, item_count: usize, expected: &str) {
let text = PrefixSumOp::diagram_with_config(
ops,
item_count,
DiagramConfig {
plus: Cow::Borrowed("@"),
..Default::default()
},
);
println!("text:\n{text}\n");
assert_eq!(text, expected);
}
#[test]
fn test_work_efficient_diagram_16() {
let item_count = 16;
test_diagram(
PrefixSumAlgorithm::WorkEfficient.ops(item_count),
item_count,
&r"
| | | | | | | | | | | | | | | |
| | | | | | | |
|\ | |\ | |\ | |\ | |\ | |\ | |\ | |\ |
| \| | \| | \| | \| | \| | \| | \| | \|
| @ | @ | @ | @ | @ | @ | @ | @
| |\ | | | |\ | | | |\ | | | |\ | |
| | \| | | | \| | | | \| | | | \| |
| | X | | | X | | | X | | | X |
| | |\ | | | |\ | | | |\ | | | |\ |
| | | \| | | | \| | | | \| | | | \|
| | | @ | | | @ | | | @ | | | @
| | | |\ | | | | | | | |\ | | | |
| | | | \| | | | | | | | \| | | |
| | | | X | | | | | | | X | | |
| | | | |\ | | | | | | | |\ | | |
| | | | | \| | | | | | | | \| | |
| | | | | X | | | | | | | X | |
| | | | | |\ | | | | | | | |\ | |
| | | | | | \| | | | | | | | \| |
| | | | | | X | | | | | | | X |
| | | | | | |\ | | | | | | | |\ |
| | | | | | | \| | | | | | | | \|
| | | | | | | @ | | | | | | | @
| | | | | | | |\ | | | | | | | |
| | | | | | | | \| | | | | | | |
| | | | | | | | X | | | | | | |
| | | | | | | | |\ | | | | | | |
| | | | | | | | | \| | | | | | |
| | | | | | | | | X | | | | | |
| | | | | | | | | |\ | | | | | |
| | | | | | | | | | \| | | | | |
| | | | | | | | | | X | | | | |
| | | | | | | | | | |\ | | | | |
| | | | | | | | | | | \| | | | |
| | | | | | | | | | | X | | | |
| | | | | | | | | | | |\ | | | |
| | | | | | | | | | | | \| | | |
| | | | | | | | | | | | X | | |
| | | | | | | | | | | | |\ | | |
| | | | | | | | | | | | | \| | |
| | | | | | | | | | | | | X | |
| | | | | | | | | | | | | |\ | |
| | | | | | | | | | | | | | \| |
| | | | | | | | | | | | | | X |
| | | | | | | | | | | | | | |\ |
| | | | | | | | | | | | | | | \|
| | | | | | | | | | | | | | @
| | | | | | | |\ | | | | | | | |
| | | | | | | | \| | | | | | | |
| | | | | | | | X | | | | | | |
| | | | | | | | |\ | | | | | | |
| | | | | | | | | \| | | | | | |
| | | | | | | | | X | | | | | |
| | | | | | | | | |\ | | | | | |
| | | | | | | | | | \| | | | | |
| | | | | | | | | | X | | | | |
| | | | | | | | | | |\ | | | | |
| | | | | | | | | | | \| | | | |
| | | | | | | | | @ | | | |
| | | |\ | | | |\ | | | |\ | | | |
| | | | \| | | | \| | | | \| | | |
| | | | X | | | X | | | X | | |
| | | | |\ | | | |\ | | | |\ | | |
| | | | | \| | | | \| | | | \| | |
| | | @ | | @ | | @ | |
| |\ | |\ | |\ | |\ | |\ | |\ | |\ | |
| | \| | \| | \| | \| | \| | \| | \| |
| | @ | @ | @ | @ | @ | @ | @ |
| | | | | | | | | | | | | | | |
"[1..], // trim newline at start
);
}
#[test]
fn test_low_latency_diagram_16() {
let item_count = 16;
test_diagram(
PrefixSumAlgorithm::LowLatency.ops(item_count),
item_count,
&r"
| | | | | | | | | | | | | | | |
|
|\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |
| \| \| \| \| \| \| \| \| \| \| \| \| \| \| \|
@ @ @ @ @ @ @ @ @ @ @ @ @ @ @
|\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ | |
| \| \| \| \| \| \| \| \| \| \| \| \| \| \| |
| X X X X X X X X X X X X X X |
| |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |
| | \| \| \| \| \| \| \| \| \| \| \| \| \| \|
@ @ @ @ @ @ @ @ @ @ @ @ @ @
|\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ | | | |
| \| \| \| \| \| \| \| \| \| \| \| \| | | |
| X X X X X X X X X X X X | | |
| |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ | | |
| | \| \| \| \| \| \| \| \| \| \| \| \| | |
| | X X X X X X X X X X X X | |
| | |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ | |
| | | \| \| \| \| \| \| \| \| \| \| \| \| |
| | | X X X X X X X X X X X X |
| | | |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |
| | | | \| \| \| \| \| \| \| \| \| \| \| \|
@ @ @ @ @ @ @ @ @ @ @ @
|\ |\ |\ |\ |\ |\ |\ |\ | | | | | | | |
| \| \| \| \| \| \| \| \| | | | | | | |
| X X X X X X X X | | | | | | |
| |\ |\ |\ |\ |\ |\ |\ |\ | | | | | | |
| | \| \| \| \| \| \| \| \| | | | | | |
| | X X X X X X X X | | | | | |
| | |\ |\ |\ |\ |\ |\ |\ |\ | | | | | |
| | | \| \| \| \| \| \| \| \| | | | | |
| | | X X X X X X X X | | | | |
| | | |\ |\ |\ |\ |\ |\ |\ |\ | | | | |
| | | | \| \| \| \| \| \| \| \| | | | |
| | | | X X X X X X X X | | | |
| | | | |\ |\ |\ |\ |\ |\ |\ |\ | | | |
| | | | | \| \| \| \| \| \| \| \| | | |
| | | | | X X X X X X X X | | |
| | | | | |\ |\ |\ |\ |\ |\ |\ |\ | | |
| | | | | | \| \| \| \| \| \| \| \| | |
| | | | | | X X X X X X X X | |
| | | | | | |\ |\ |\ |\ |\ |\ |\ |\ | |
| | | | | | | \| \| \| \| \| \| \| \| |
| | | | | | | X X X X X X X X |
| | | | | | | |\ |\ |\ |\ |\ |\ |\ |\ |
| | | | | | | | \| \| \| \| \| \| \| \|
| | | | | | | | @ @ @ @ @ @ @ @
| | | | | | | | | | | | | | | |
"[1..], // trim newline at start
);
}
#[test]
fn test_work_efficient_diagram_9() {
let item_count = 9;
test_diagram(
PrefixSumAlgorithm::WorkEfficient.ops(item_count),
item_count,
&r"
| | | | | | | | |
| | | | |
|\ | |\ | |\ | |\ | |
| \| | \| | \| | \| |
| @ | @ | @ | @ |
| |\ | | | |\ | | |
| | \| | | | \| | |
| | X | | | X | |
| | |\ | | | |\ | |
| | | \| | | | \| |
| | | @ | | | @ |
| | | |\ | | | | |
| | | | \| | | | |
| | | | X | | | |
| | | | |\ | | | |
| | | | | \| | | |
| | | | | X | | |
| | | | | |\ | | |
| | | | | | \| | |
| | | | | | X | |
| | | | | | |\ | |
| | | | | | | \| |
| | | | | | @ |
| | | |\ | | | | |
| | | | \| | | | |
| | | | X | | | |
| | | | |\ | | | |
| | | | | \| | | |
| | | @ | |
| |\ | |\ | |\ | |\ |
| | \| | \| | \| | \|
| | @ | @ | @ | @
| | | | | | | | |
"[1..], // trim newline at start
);
}
#[test]
fn test_low_latency_diagram_9() {
let item_count = 9;
test_diagram(
PrefixSumAlgorithm::LowLatency.ops(item_count),
item_count,
&r"
| | | | | | | | |
|
|\ |\ |\ |\ |\ |\ |\ |\ |
| \| \| \| \| \| \| \| \|
@ @ @ @ @ @ @ @
|\ |\ |\ |\ |\ |\ |\ | |
| \| \| \| \| \| \| \| |
| X X X X X X X |
| |\ |\ |\ |\ |\ |\ |\ |
| | \| \| \| \| \| \| \|
@ @ @ @ @ @ @
|\ |\ |\ |\ |\ | | | |
| \| \| \| \| \| | | |
| X X X X X | | |
| |\ |\ |\ |\ |\ | | |
| | \| \| \| \| \| | |
| | X X X X X | |
| | |\ |\ |\ |\ |\ | |
| | | \| \| \| \| \| |
| | | X X X X X |
| | | |\ |\ |\ |\ |\ |
| | | | \| \| \| \| \|
| | | @ @ @ @ @
|\ | | | | | | | |
| \| | | | | | | |
| X | | | | | | |
| |\ | | | | | | |
| | \| | | | | | |
| | X | | | | | |
| | |\ | | | | | |
| | | \| | | | | |
| | | X | | | | |
| | | |\ | | | | |
| | | | \| | | | |
| | | | X | | | |
| | | | |\ | | | |
| | | | | \| | | |
| | | | | X | | |
| | | | | |\ | | |
| | | | | | \| | |
| | | | | | X | |
| | | | | | |\ | |
| | | | | | | \| |
| | | | | | | X |
| | | | | | | |\ |
| | | | | | | | \|
| | | | | | | | @
| | | | | | | | |
"[1..], // trim newline at start
);
}
#[test]
fn test_reduce_diagram_16() {
let item_count = 16;
test_diagram(
reduce_ops(item_count),
item_count,
&r"
| | | | | | | | | | | | | | | |
| | | | | | | |
|\ | |\ | |\ | |\ | |\ | |\ | |\ | |\ |
| \| | \| | \| | \| | \| | \| | \| | \|
| @ | @ | @ | @ | @ | @ | @ | @
| |\ | | | |\ | | | |\ | | | |\ | |
| | \| | | | \| | | | \| | | | \| |
| | X | | | X | | | X | | | X |
| | |\ | | | |\ | | | |\ | | | |\ |
| | | \| | | | \| | | | \| | | | \|
| | | @ | | | @ | | | @ | | | @
| | | |\ | | | | | | | |\ | | | |
| | | | \| | | | | | | | \| | | |
| | | | X | | | | | | | X | | |
| | | | |\ | | | | | | | |\ | | |
| | | | | \| | | | | | | | \| | |
| | | | | X | | | | | | | X | |
| | | | | |\ | | | | | | | |\ | |
| | | | | | \| | | | | | | | \| |
| | | | | | X | | | | | | | X |
| | | | | | |\ | | | | | | | |\ |
| | | | | | | \| | | | | | | | \|
| | | | | | | @ | | | | | | | @
| | | | | | | |\ | | | | | | | |
| | | | | | | | \| | | | | | | |
| | | | | | | | X | | | | | | |
| | | | | | | | |\ | | | | | | |
| | | | | | | | | \| | | | | | |
| | | | | | | | | X | | | | | |
| | | | | | | | | |\ | | | | | |
| | | | | | | | | | \| | | | | |
| | | | | | | | | | X | | | | |
| | | | | | | | | | |\ | | | | |
| | | | | | | | | | | \| | | | |
| | | | | | | | | | | X | | | |
| | | | | | | | | | | |\ | | | |
| | | | | | | | | | | | \| | | |
| | | | | | | | | | | | X | | |
| | | | | | | | | | | | |\ | | |
| | | | | | | | | | | | | \| | |
| | | | | | | | | | | | | X | |
| | | | | | | | | | | | | |\ | |
| | | | | | | | | | | | | | \| |
| | | | | | | | | | | | | | X |
| | | | | | | | | | | | | | |\ |
| | | | | | | | | | | | | | | \|
| | | | | | | | | | | | | | | @
| | | | | | | | | | | | | | | |
"[1..], // trim newline at start
);
}
#[test]
fn test_reduce_diagram_9() {
let item_count = 9;
test_diagram(
reduce_ops(item_count),
item_count,
&r"
| | | | | | | | |
| | | | |
| |\ | |\ | |\ | |\ |
| | \| | \| | \| | \|
| | @ | @ | @ | @
| | |\ | | | |\ | |
| | | \| | | | \| |
| | | X | | | X |
| | | |\ | | | |\ |
| | | | \| | | | \|
| | | | @ | | | @
| | | | |\ | | | |
| | | | | \| | | |
| | | | | X | | |
| | | | | |\ | | |
| | | | | | \| | |
| | | | | | X | |
| | | | | | |\ | |
| | | | | | | \| |
| | | | | | | X |
| | | | | | | |\ |
| | | | | | | | \|
| | | | | | | @
|\ | | | | | | | |
| \| | | | | | | |
| X | | | | | | |
| |\ | | | | | | |
| | \| | | | | | |
| | X | | | | | |
| | |\ | | | | | |
| | | \| | | | | |
| | | X | | | | |
| | | |\ | | | | |
| | | | \| | | | |
| | | | X | | | |
| | | | |\ | | | |
| | | | | \| | | |
| | | | | X | | |
| | | | | |\ | | |
| | | | | | \| | |
| | | | | | X | |
| | | | | | |\ | |
| | | | | | | \| |
| | | | | | | X |
| | | | | | | |\ |
| | | | | | | | \|
| | | | | | | | @
| | | | | | | | |
"[1..], // trim newline at start
);
}
}

View file

@ -0,0 +1,240 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
#![cfg(feature = "unstable-test-hasher")]
use std::{
fmt::Write as _,
hash::{BuildHasher, Hash, Hasher},
io::Write as _,
marker::PhantomData,
sync::LazyLock,
};
type BoxDynHasher = Box<dyn Hasher + Send + Sync>;
type BoxDynBuildHasher = Box<dyn DynBuildHasherTrait + Send + Sync>;
type BoxDynMakeBuildHasher = Box<dyn Fn() -> BoxDynBuildHasher + Send + Sync>;
trait TryGetDynBuildHasher: Copy {
type Type;
fn try_get_make_build_hasher(self) -> Option<BoxDynMakeBuildHasher>;
}
impl<T> TryGetDynBuildHasher for PhantomData<T> {
type Type = T;
fn try_get_make_build_hasher(self) -> Option<BoxDynMakeBuildHasher> {
None
}
}
impl<T: Default + BuildHasher<Hasher: Send + Sync + 'static> + Send + Sync + 'static + Clone>
TryGetDynBuildHasher for &'_ PhantomData<T>
{
type Type = T;
fn try_get_make_build_hasher(self) -> Option<BoxDynMakeBuildHasher> {
Some(Box::new(|| Box::<DynBuildHasher<T>>::default()))
}
}
#[derive(Default, Clone)]
struct DynBuildHasher<T>(T);
trait DynBuildHasherTrait: BuildHasher<Hasher = BoxDynHasher> {
fn clone_dyn_build_hasher(&self) -> BoxDynBuildHasher;
}
impl<BH: BuildHasher<Hasher: Send + Sync + 'static>> BuildHasher for DynBuildHasher<BH> {
type Hasher = BoxDynHasher;
fn build_hasher(&self) -> Self::Hasher {
Box::new(self.0.build_hasher())
}
fn hash_one<T: Hash>(&self, x: T) -> u64 {
self.0.hash_one(x)
}
}
impl<BH> DynBuildHasherTrait for DynBuildHasher<BH>
where
Self: Clone + BuildHasher<Hasher = BoxDynHasher> + Send + Sync + 'static,
{
fn clone_dyn_build_hasher(&self) -> BoxDynBuildHasher {
Box::new(self.clone())
}
}
pub struct DefaultBuildHasher(BoxDynBuildHasher);
impl Clone for DefaultBuildHasher {
fn clone(&self) -> Self {
DefaultBuildHasher(self.0.clone_dyn_build_hasher())
}
}
const ENV_VAR_NAME: &'static str = "FAYALITE_TEST_HASHER";
struct EnvVarValue {
key: &'static str,
try_get_make_build_hasher: fn() -> Option<BoxDynMakeBuildHasher>,
description: &'static str,
}
macro_rules! env_var_value {
(
key: $key:literal,
build_hasher: $build_hasher:ty,
description: $description:literal,
) => {
EnvVarValue {
key: $key,
try_get_make_build_hasher: || {
// use rust method resolution to detect if $build_hasher is usable
// (e.g. hashbrown's hasher won't be usable without the right feature enabled)
(&PhantomData::<DynBuildHasher<$build_hasher>>).try_get_make_build_hasher()
},
description: $description,
}
};
}
#[derive(Default)]
struct AlwaysZeroHasher;
impl Hasher for AlwaysZeroHasher {
fn write(&mut self, _bytes: &[u8]) {}
fn finish(&self) -> u64 {
0
}
}
const ENV_VAR_VALUES: &'static [EnvVarValue] = &[
env_var_value! {
key: "std",
build_hasher: std::hash::RandomState,
description: "use std::hash::RandomState",
},
env_var_value! {
key: "hashbrown",
build_hasher: hashbrown::DefaultHashBuilder,
description: "use hashbrown's DefaultHashBuilder",
},
env_var_value! {
key: "always_zero",
build_hasher: std::hash::BuildHasherDefault<AlwaysZeroHasher>,
description: "use a hasher that always returns 0 for all hashes,\n \
this is useful for checking that PartialEq impls are correct",
},
];
fn report_bad_env_var(msg: impl std::fmt::Display) -> ! {
let mut msg = format!("{ENV_VAR_NAME}: {msg}\n");
for &EnvVarValue {
key,
try_get_make_build_hasher,
description,
} in ENV_VAR_VALUES
{
let availability = match try_get_make_build_hasher() {
Some(_) => "available",
None => "unavailable",
};
writeln!(msg, "{key}: ({availability})\n {description}").expect("can't fail");
}
std::io::stderr()
.write_all(msg.as_bytes())
.expect("should be able to write to stderr");
std::process::abort();
}
impl Default for DefaultBuildHasher {
fn default() -> Self {
static DEFAULT_FN: LazyLock<BoxDynMakeBuildHasher> = LazyLock::new(|| {
let var = std::env::var_os(ENV_VAR_NAME);
let var = var.as_deref().unwrap_or("std".as_ref());
for &EnvVarValue {
key,
try_get_make_build_hasher,
description: _,
} in ENV_VAR_VALUES
{
if var.as_encoded_bytes().eq_ignore_ascii_case(key.as_bytes()) {
return try_get_make_build_hasher().unwrap_or_else(|| {
report_bad_env_var(format_args!(
"unavailable hasher: {key} (is the appropriate feature enabled?)"
));
});
}
}
report_bad_env_var(format_args!("unrecognized hasher: {var:?}"));
});
Self(DEFAULT_FN())
}
}
pub struct DefaultHasher(BoxDynHasher);
impl BuildHasher for DefaultBuildHasher {
type Hasher = DefaultHasher;
fn build_hasher(&self) -> Self::Hasher {
DefaultHasher(self.0.build_hasher())
}
}
impl Hasher for DefaultHasher {
fn finish(&self) -> u64 {
self.0.finish()
}
fn write(&mut self, bytes: &[u8]) {
self.0.write(bytes)
}
fn write_u8(&mut self, i: u8) {
self.0.write_u8(i)
}
fn write_u16(&mut self, i: u16) {
self.0.write_u16(i)
}
fn write_u32(&mut self, i: u32) {
self.0.write_u32(i)
}
fn write_u64(&mut self, i: u64) {
self.0.write_u64(i)
}
fn write_u128(&mut self, i: u128) {
self.0.write_u128(i)
}
fn write_usize(&mut self, i: usize) {
self.0.write_usize(i)
}
fn write_i8(&mut self, i: i8) {
self.0.write_i8(i)
}
fn write_i16(&mut self, i: i16) {
self.0.write_i16(i)
}
fn write_i32(&mut self, i: i32) {
self.0.write_i32(i)
}
fn write_i64(&mut self, i: i64) {
self.0.write_i64(i)
}
fn write_i128(&mut self, i: i128) {
self.0.write_i128(i)
}
fn write_isize(&mut self, i: isize) {
self.0.write_isize(i)
}
}

View file

@ -4,11 +4,17 @@ use fayalite::{
bundle::BundleType,
enum_::EnumType,
int::{BoolOrIntType, IntType},
phantom_const::PhantomConst,
prelude::*,
ty::StaticType,
};
use std::marker::PhantomData;
#[hdl(outline_generated)]
pub struct MyConstSize<V: Size> {
pub size: PhantomConst<UIntType<V>>,
}
#[hdl(outline_generated)]
pub struct S<T, Len: Size, T2> {
pub a: T,

View file

@ -1,8 +1,13 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use fayalite::{
assert_export_firrtl, firrtl::ExportOptions, intern::Intern,
module::transform::simplify_enums::SimplifyEnumsKind, prelude::*, reset::ResetType,
assert_export_firrtl,
firrtl::ExportOptions,
int::{UIntInRange, UIntInRangeInclusive},
intern::Intern,
module::transform::simplify_enums::SimplifyEnumsKind,
prelude::*,
reset::ResetType,
ty::StaticType,
};
use serde_json::json;
@ -191,10 +196,14 @@ circuit check_array_repeat:
};
}
pub trait UnknownTrait {}
impl<T: ?Sized> UnknownTrait for T {}
#[hdl_module(outline_generated)]
pub fn check_skipped_generics<T, #[hdl(skip)] U, const N: usize, #[hdl(skip)] const M: usize>(v: U)
where
T: StaticType,
T: StaticType + UnknownTrait,
ConstUsize<N>: KnownSize,
U: std::fmt::Display,
{
@ -376,18 +385,18 @@ circuit check_written_inside_both_if_else:
};
}
#[hdl(outline_generated)]
#[hdl(outline_generated, cmp_eq)]
pub struct TestStruct<T> {
pub a: T,
pub b: UInt<8>,
}
#[hdl(outline_generated)]
#[hdl(outline_generated, cmp_eq)]
pub struct TestStruct2 {
pub v: UInt<8>,
}
#[hdl(outline_generated)]
#[hdl(outline_generated, cmp_eq)]
pub struct TestStruct3 {}
#[hdl_module(outline_generated)]
@ -4421,3 +4430,204 @@ circuit check_let_patterns:
",
};
}
#[hdl_module(outline_generated)]
pub fn check_struct_cmp_eq() {
#[hdl]
let tuple_lhs: (UInt<1>, SInt<1>, Bool) = m.input();
#[hdl]
let tuple_rhs: (UInt<1>, SInt<1>, Bool) = m.input();
#[hdl]
let tuple_cmp_eq: Bool = m.output();
connect(tuple_cmp_eq, tuple_lhs.cmp_eq(tuple_rhs));
#[hdl]
let tuple_cmp_ne: Bool = m.output();
connect(tuple_cmp_ne, tuple_lhs.cmp_ne(tuple_rhs));
#[hdl]
let test_struct_lhs: TestStruct<SInt<8>> = m.input();
#[hdl]
let test_struct_rhs: TestStruct<SInt<8>> = m.input();
#[hdl]
let test_struct_cmp_eq: Bool = m.output();
connect(test_struct_cmp_eq, test_struct_lhs.cmp_eq(test_struct_rhs));
#[hdl]
let test_struct_cmp_ne: Bool = m.output();
connect(test_struct_cmp_ne, test_struct_lhs.cmp_ne(test_struct_rhs));
#[hdl]
let test_struct_2_lhs: TestStruct2 = m.input();
#[hdl]
let test_struct_2_rhs: TestStruct2 = m.input();
#[hdl]
let test_struct_2_cmp_eq: Bool = m.output();
connect(
test_struct_2_cmp_eq,
test_struct_2_lhs.cmp_eq(test_struct_2_rhs),
);
#[hdl]
let test_struct_2_cmp_ne: Bool = m.output();
connect(
test_struct_2_cmp_ne,
test_struct_2_lhs.cmp_ne(test_struct_2_rhs),
);
#[hdl]
let test_struct_3_lhs: TestStruct3 = m.input();
#[hdl]
let test_struct_3_rhs: TestStruct3 = m.input();
#[hdl]
let test_struct_3_cmp_eq: Bool = m.output();
connect(
test_struct_3_cmp_eq,
test_struct_3_lhs.cmp_eq(test_struct_3_rhs),
);
#[hdl]
let test_struct_3_cmp_ne: Bool = m.output();
connect(
test_struct_3_cmp_ne,
test_struct_3_lhs.cmp_ne(test_struct_3_rhs),
);
}
#[test]
fn test_struct_cmp_eq() {
let _n = SourceLocation::normalize_files_for_tests();
let m = check_struct_cmp_eq();
dbg!(m);
#[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
assert_export_firrtl! {
m =>
"/test/check_struct_cmp_eq.fir": r"FIRRTL version 3.2.0
circuit check_struct_cmp_eq:
type Ty0 = {`0`: UInt<1>, `1`: SInt<1>, `2`: UInt<1>}
type Ty1 = {a: SInt<8>, b: UInt<8>}
type Ty2 = {v: UInt<8>}
type Ty3 = {}
module check_struct_cmp_eq: @[module-XXXXXXXXXX.rs 1:1]
input tuple_lhs: Ty0 @[module-XXXXXXXXXX.rs 2:1]
input tuple_rhs: Ty0 @[module-XXXXXXXXXX.rs 3:1]
output tuple_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 4:1]
output tuple_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 6:1]
input test_struct_lhs: Ty1 @[module-XXXXXXXXXX.rs 8:1]
input test_struct_rhs: Ty1 @[module-XXXXXXXXXX.rs 9:1]
output test_struct_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 10:1]
output test_struct_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 12:1]
input test_struct_2_lhs: Ty2 @[module-XXXXXXXXXX.rs 14:1]
input test_struct_2_rhs: Ty2 @[module-XXXXXXXXXX.rs 15:1]
output test_struct_2_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 16:1]
output test_struct_2_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 18:1]
input test_struct_3_lhs: Ty3 @[module-XXXXXXXXXX.rs 20:1]
input test_struct_3_rhs: Ty3 @[module-XXXXXXXXXX.rs 21:1]
output test_struct_3_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 22:1]
output test_struct_3_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 24:1]
wire _array_literal_expr: UInt<1>[3]
connect _array_literal_expr[0], eq(tuple_lhs.`0`, tuple_rhs.`0`)
connect _array_literal_expr[1], eq(tuple_lhs.`1`, tuple_rhs.`1`)
connect _array_literal_expr[2], eq(tuple_lhs.`2`, tuple_rhs.`2`)
wire _cast_array_to_bits_expr: UInt<1>[3]
connect _cast_array_to_bits_expr[0], _array_literal_expr[0]
connect _cast_array_to_bits_expr[1], _array_literal_expr[1]
connect _cast_array_to_bits_expr[2], _array_literal_expr[2]
wire _cast_to_bits_expr: UInt<3>
connect _cast_to_bits_expr, cat(_cast_array_to_bits_expr[2], cat(_cast_array_to_bits_expr[1], _cast_array_to_bits_expr[0]))
connect tuple_cmp_eq, andr(_cast_to_bits_expr) @[module-XXXXXXXXXX.rs 5:1]
wire _array_literal_expr_1: UInt<1>[3]
connect _array_literal_expr_1[0], neq(tuple_lhs.`0`, tuple_rhs.`0`)
connect _array_literal_expr_1[1], neq(tuple_lhs.`1`, tuple_rhs.`1`)
connect _array_literal_expr_1[2], neq(tuple_lhs.`2`, tuple_rhs.`2`)
wire _cast_array_to_bits_expr_1: UInt<1>[3]
connect _cast_array_to_bits_expr_1[0], _array_literal_expr_1[0]
connect _cast_array_to_bits_expr_1[1], _array_literal_expr_1[1]
connect _cast_array_to_bits_expr_1[2], _array_literal_expr_1[2]
wire _cast_to_bits_expr_1: UInt<3>
connect _cast_to_bits_expr_1, cat(_cast_array_to_bits_expr_1[2], cat(_cast_array_to_bits_expr_1[1], _cast_array_to_bits_expr_1[0]))
connect tuple_cmp_ne, orr(_cast_to_bits_expr_1) @[module-XXXXXXXXXX.rs 7:1]
connect test_struct_cmp_eq, and(eq(test_struct_lhs.a, test_struct_rhs.a), eq(test_struct_lhs.b, test_struct_rhs.b)) @[module-XXXXXXXXXX.rs 11:1]
connect test_struct_cmp_ne, or(neq(test_struct_lhs.a, test_struct_rhs.a), neq(test_struct_lhs.b, test_struct_rhs.b)) @[module-XXXXXXXXXX.rs 13:1]
connect test_struct_2_cmp_eq, eq(test_struct_2_lhs.v, test_struct_2_rhs.v) @[module-XXXXXXXXXX.rs 17:1]
connect test_struct_2_cmp_ne, neq(test_struct_2_lhs.v, test_struct_2_rhs.v) @[module-XXXXXXXXXX.rs 19:1]
connect test_struct_3_cmp_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 23:1]
connect test_struct_3_cmp_ne, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 25:1]
",
};
}
#[hdl_module(outline_generated)]
pub fn check_uint_in_range() {
#[hdl]
let i_0_to_1: UIntInRange<0, 1> = m.input();
#[hdl]
let i_0_to_2: UIntInRange<0, 2> = m.input();
#[hdl]
let i_0_to_3: UIntInRange<0, 3> = m.input();
#[hdl]
let i_0_to_4: UIntInRange<0, 4> = m.input();
#[hdl]
let i_0_to_7: UIntInRange<0, 7> = m.input();
#[hdl]
let i_0_to_8: UIntInRange<0, 8> = m.input();
#[hdl]
let i_0_to_9: UIntInRange<0, 9> = m.input();
#[hdl]
let i_0_through_0: UIntInRangeInclusive<0, 0> = m.input();
#[hdl]
let i_0_through_1: UIntInRangeInclusive<0, 1> = m.input();
#[hdl]
let i_0_through_2: UIntInRangeInclusive<0, 2> = m.input();
#[hdl]
let i_0_through_3: UIntInRangeInclusive<0, 3> = m.input();
#[hdl]
let i_0_through_4: UIntInRangeInclusive<0, 4> = m.input();
#[hdl]
let i_0_through_7: UIntInRangeInclusive<0, 7> = m.input();
#[hdl]
let i_0_through_8: UIntInRangeInclusive<0, 8> = m.input();
#[hdl]
let i_0_through_9: UIntInRangeInclusive<0, 9> = m.input();
}
#[test]
fn test_uint_in_range() {
let _n = SourceLocation::normalize_files_for_tests();
let m = check_uint_in_range();
dbg!(m);
#[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
assert_export_firrtl! {
m =>
"/test/check_uint_in_range.fir": r"FIRRTL version 3.2.0
circuit check_uint_in_range:
type Ty0 = {value: UInt<0>, range: {}}
type Ty1 = {value: UInt<1>, range: {}}
type Ty2 = {value: UInt<2>, range: {}}
type Ty3 = {value: UInt<2>, range: {}}
type Ty4 = {value: UInt<3>, range: {}}
type Ty5 = {value: UInt<3>, range: {}}
type Ty6 = {value: UInt<4>, range: {}}
type Ty7 = {value: UInt<0>, range: {}}
type Ty8 = {value: UInt<1>, range: {}}
type Ty9 = {value: UInt<2>, range: {}}
type Ty10 = {value: UInt<2>, range: {}}
type Ty11 = {value: UInt<3>, range: {}}
type Ty12 = {value: UInt<3>, range: {}}
type Ty13 = {value: UInt<4>, range: {}}
type Ty14 = {value: UInt<4>, range: {}}
module check_uint_in_range: @[module-XXXXXXXXXX.rs 1:1]
input i_0_to_1: Ty0 @[module-XXXXXXXXXX.rs 2:1]
input i_0_to_2: Ty1 @[module-XXXXXXXXXX.rs 3:1]
input i_0_to_3: Ty2 @[module-XXXXXXXXXX.rs 4:1]
input i_0_to_4: Ty3 @[module-XXXXXXXXXX.rs 5:1]
input i_0_to_7: Ty4 @[module-XXXXXXXXXX.rs 6:1]
input i_0_to_8: Ty5 @[module-XXXXXXXXXX.rs 7:1]
input i_0_to_9: Ty6 @[module-XXXXXXXXXX.rs 8:1]
input i_0_through_0: Ty7 @[module-XXXXXXXXXX.rs 9:1]
input i_0_through_1: Ty8 @[module-XXXXXXXXXX.rs 10:1]
input i_0_through_2: Ty9 @[module-XXXXXXXXXX.rs 11:1]
input i_0_through_3: Ty10 @[module-XXXXXXXXXX.rs 12:1]
input i_0_through_4: Ty11 @[module-XXXXXXXXXX.rs 13:1]
input i_0_through_7: Ty12 @[module-XXXXXXXXXX.rs 14:1]
input i_0_through_8: Ty13 @[module-XXXXXXXXXX.rs 15:1]
input i_0_through_9: Ty14 @[module-XXXXXXXXXX.rs 16:1]
",
};
}

View file

@ -2,11 +2,11 @@
// See Notices.txt for copyright information
use fayalite::{
int::UIntValue,
memory::{ReadStruct, ReadWriteStruct, WriteStruct},
module::{instance_with_loc, reg_builder_with_loc},
prelude::*,
reset::ResetType,
sim::{time::SimDuration, vcd::VcdWriterDecls, Simulation, ToSimValue},
ty::StaticType,
sim::vcd::VcdWriterDecls,
util::RcWriter,
};
use std::num::NonZeroUsize;
@ -316,8 +316,13 @@ pub fn enums() {
let which_out: UInt<2> = m.output();
#[hdl]
let data_out: UInt<4> = m.output();
let b_out_ty = HdlOption[(UInt[1], Bool)];
#[hdl]
let b_out: HdlOption<(UInt<1>, Bool)> = m.output();
let b_out: HdlOption<(UInt, Bool)> = m.output(HdlOption[(UInt[1], Bool)]);
#[hdl]
let b2_out: HdlOption<(UInt<1>, Bool)> = m.output();
connect_any(b2_out, b_out);
#[hdl]
struct MyStruct<T> {
@ -357,7 +362,7 @@ pub fn enums() {
}
}
connect(b_out, HdlNone());
connect(b_out, b_out_ty.HdlNone());
#[hdl]
match the_reg {
@ -368,7 +373,7 @@ pub fn enums() {
MyEnum::B(v) => {
connect(which_out, 1_hdl_u2);
connect_any(data_out, v.0 | (v.1.cast_to_static::<UInt<1>>() << 1));
connect(b_out, HdlSome(v));
connect_any(b_out, HdlSome(v));
}
MyEnum::C(v) => {
connect(which_out, 2_hdl_u2);
@ -384,132 +389,136 @@ fn test_enums() {
let mut sim = Simulation::new(enums());
let mut writer = RcWriter::default();
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
sim.write_clock(sim.io().cd.clk, false);
sim.write_reset(sim.io().cd.rst, true);
sim.write_bool(sim.io().en, false);
sim.write_bool_or_int(sim.io().which_in, 0_hdl_u2);
sim.write_bool_or_int(sim.io().data_in, 0_hdl_u4);
sim.write(sim.io().cd.clk, false);
sim.write(sim.io().cd.rst, true);
sim.write(sim.io().en, false);
sim.write(sim.io().which_in, 0_hdl_u2);
sim.write(sim.io().data_in, 0_hdl_u4);
sim.advance_time(SimDuration::from_micros(1));
sim.write_clock(sim.io().cd.clk, true);
sim.write(sim.io().cd.clk, true);
sim.advance_time(SimDuration::from_nanos(100));
sim.write_reset(sim.io().cd.rst, false);
sim.write(sim.io().cd.rst, false);
sim.advance_time(SimDuration::from_nanos(900));
type BOutTy = HdlOption<(UInt<1>, Bool)>;
#[derive(Debug)]
struct IO {
en: bool,
which_in: u8,
data_in: u8,
which_out: u8,
data_out: u8,
b_out: Expr<BOutTy>,
}
impl PartialEq for IO {
fn eq(&self, other: &Self) -> bool {
let Self {
en,
which_in,
data_in,
which_out,
data_out,
b_out,
} = *self;
en == other.en
&& which_in == other.which_in
&& data_in == other.data_in
&& which_out == other.which_out
&& data_out == other.data_out
&& b_out.to_sim_value(BOutTy::TYPE) == other.b_out.to_sim_value(BOutTy::TYPE)
}
#[hdl(cmp_eq)]
struct IO<W: Size> {
en: Bool,
which_in: UInt<2>,
data_in: UInt<4>,
which_out: UInt<2>,
data_out: UInt<4>,
b_out: HdlOption<(UIntType<W>, Bool)>,
b2_out: HdlOption<(UInt<1>, Bool)>,
}
let io_ty = IO[1];
let io_cycles = [
IO {
#[hdl(sim)]
IO::<_> {
en: false,
which_in: 0,
data_in: 0,
which_out: 0,
data_out: 0,
b_out: HdlNone(),
which_in: 0_hdl_u2,
data_in: 0_hdl_u4,
which_out: 0_hdl_u2,
data_out: 0_hdl_u4,
b_out: #[hdl(sim)]
(io_ty.b_out).HdlNone(),
b2_out: #[hdl(sim)]
HdlNone(),
},
IO {
#[hdl(sim)]
IO::<_> {
en: true,
which_in: 1,
data_in: 0,
which_out: 0,
data_out: 0,
b_out: HdlNone(),
which_in: 1_hdl_u2,
data_in: 0_hdl_u4,
which_out: 0_hdl_u2,
data_out: 0_hdl_u4,
b_out: #[hdl(sim)]
(io_ty.b_out).HdlNone(),
b2_out: #[hdl(sim)]
HdlNone(),
},
IO {
#[hdl(sim)]
IO::<_> {
en: false,
which_in: 0,
data_in: 0,
which_out: 1,
data_out: 0,
b_out: HdlSome((0_hdl_u1, false)),
which_in: 0_hdl_u2,
data_in: 0_hdl_u4,
which_out: 1_hdl_u2,
data_out: 0_hdl_u4,
b_out: #[hdl(sim)]
(io_ty.b_out).HdlSome((0u8.cast_to(UInt[1]), false)),
b2_out: #[hdl(sim)]
HdlSome((0_hdl_u1, false)),
},
IO {
#[hdl(sim)]
IO::<_> {
en: true,
which_in: 1,
data_in: 0xF,
which_out: 1,
data_out: 0,
b_out: HdlSome((0_hdl_u1, false)),
which_in: 1_hdl_u2,
data_in: 0xF_hdl_u4,
which_out: 1_hdl_u2,
data_out: 0_hdl_u4,
b_out: #[hdl(sim)]
(io_ty.b_out).HdlSome((0u8.cast_to(UInt[1]), false)),
b2_out: #[hdl(sim)]
HdlSome((0_hdl_u1, false)),
},
IO {
#[hdl(sim)]
IO::<_> {
en: true,
which_in: 1,
data_in: 0xF,
which_out: 1,
data_out: 0x3,
b_out: HdlSome((1_hdl_u1, true)),
which_in: 1_hdl_u2,
data_in: 0xF_hdl_u4,
which_out: 1_hdl_u2,
data_out: 0x3_hdl_u4,
b_out: #[hdl(sim)]
(io_ty.b_out).HdlSome((1u8.cast_to(UInt[1]), true)),
b2_out: #[hdl(sim)]
HdlSome((1_hdl_u1, true)),
},
IO {
#[hdl(sim)]
IO::<_> {
en: true,
which_in: 2,
data_in: 0xF,
which_out: 1,
data_out: 0x3,
b_out: HdlSome((1_hdl_u1, true)),
which_in: 2_hdl_u2,
data_in: 0xF_hdl_u4,
which_out: 1_hdl_u2,
data_out: 0x3_hdl_u4,
b_out: #[hdl(sim)]
(io_ty.b_out).HdlSome((1u8.cast_to(UInt[1]), true)),
b2_out: #[hdl(sim)]
HdlSome((1_hdl_u1, true)),
},
IO {
#[hdl(sim)]
IO::<_> {
en: true,
which_in: 2,
data_in: 0xF,
which_out: 2,
data_out: 0xF,
b_out: HdlNone(),
which_in: 2_hdl_u2,
data_in: 0xF_hdl_u4,
which_out: 2_hdl_u2,
data_out: 0xF_hdl_u4,
b_out: #[hdl(sim)]
(io_ty.b_out).HdlNone(),
b2_out: #[hdl(sim)]
HdlNone(),
},
];
for (
cycle,
expected @ IO {
for (cycle, expected) in io_cycles.into_iter().enumerate() {
#[hdl(sim)]
let IO::<_> {
en,
which_in,
data_in,
which_out: _,
data_out: _,
b_out: _,
},
) in io_cycles.into_iter().enumerate()
{
sim.write_bool(sim.io().en, en);
sim.write_bool_or_int(sim.io().which_in, which_in.cast_to_static());
sim.write_bool_or_int(sim.io().data_in, data_in.cast_to_static());
let io = IO {
b2_out: _,
} = expected;
sim.write(sim.io().en, &en);
sim.write(sim.io().which_in, &which_in);
sim.write(sim.io().data_in, &data_in);
let io = #[hdl(sim)]
IO::<_> {
en,
which_in,
data_in,
which_out: sim
.read_bool_or_int(sim.io().which_out)
.to_bigint()
.try_into()
.expect("known to be in range"),
data_out: sim
.read_bool_or_int(sim.io().data_out)
.to_bigint()
.try_into()
.expect("known to be in range"),
b_out: sim.read(sim.io().b_out).to_expr(),
which_out: sim.read(sim.io().which_out),
data_out: sim.read(sim.io().data_out),
b_out: sim.read(sim.io().b_out),
b2_out: sim.read(sim.io().b2_out),
};
assert_eq!(
expected,
@ -517,6 +526,12 @@ fn test_enums() {
"vcd:\n{}\ncycle: {cycle}",
String::from_utf8(writer.take()).unwrap(),
);
// make sure matching on SimValue<SomeEnum> works
#[hdl(sim)]
match io.b_out {
HdlNone => println!("io.b_out is HdlNone"),
HdlSome(v) => println!("io.b_out is HdlSome(({:?}, {:?}))", *v.0, *v.1),
}
sim.write_clock(sim.io().cd.clk, false);
sim.advance_time(SimDuration::from_micros(1));
sim.write_clock(sim.io().cd.clk, true);
@ -538,9 +553,9 @@ fn test_enums() {
#[hdl_module(outline_generated)]
pub fn memories() {
#[hdl]
let r: fayalite::memory::ReadStruct<(UInt<8>, SInt<8>), ConstUsize<4>> = m.input();
let r: ReadStruct<(UInt<8>, SInt<8>), ConstUsize<4>> = m.input();
#[hdl]
let w: fayalite::memory::WriteStruct<(UInt<8>, SInt<8>), ConstUsize<4>> = m.input();
let w: WriteStruct<(UInt<8>, SInt<8>), ConstUsize<4>> = m.input();
#[hdl]
let mut mem = memory_with_init([(0x01u8, 0x23i8); 16]);
mem.read_latency(0);
@ -559,120 +574,131 @@ fn test_memories() {
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
sim.write_clock(sim.io().r.clk, false);
sim.write_clock(sim.io().w.clk, false);
#[derive(Debug, PartialEq, Eq)]
#[hdl(cmp_eq)]
struct IO {
r_addr: u8,
r_en: bool,
r_data: (u8, i8),
w_addr: u8,
w_en: bool,
w_data: (u8, i8),
w_mask: (bool, bool),
r_addr: UInt<4>,
r_en: Bool,
r_data: (UInt<8>, SInt<8>),
w_addr: UInt<4>,
w_en: Bool,
w_data: (UInt<8>, SInt<8>),
w_mask: (Bool, Bool),
}
let io_cycles = [
#[hdl(sim)]
IO {
r_addr: 0,
r_addr: 0_hdl_u4,
r_en: false,
r_data: (0, 0),
w_addr: 0,
r_data: (0u8, 0i8),
w_addr: 0_hdl_u4,
w_en: false,
w_data: (0, 0),
w_data: (0u8, 0i8),
w_mask: (false, false),
},
#[hdl(sim)]
IO {
r_addr: 0,
r_addr: 0_hdl_u4,
r_en: true,
r_data: (0x1, 0x23),
w_addr: 0,
r_data: (0x1u8, 0x23i8),
w_addr: 0_hdl_u4,
w_en: true,
w_data: (0x10, 0x20),
w_data: (0x10u8, 0x20i8),
w_mask: (true, true),
},
#[hdl(sim)]
IO {
r_addr: 0,
r_addr: 0_hdl_u4,
r_en: true,
r_data: (0x10, 0x20),
w_addr: 0,
r_data: (0x10u8, 0x20i8),
w_addr: 0_hdl_u4,
w_en: true,
w_data: (0x30, 0x40),
w_data: (0x30u8, 0x40i8),
w_mask: (false, true),
},
#[hdl(sim)]
IO {
r_addr: 0,
r_addr: 0_hdl_u4,
r_en: true,
r_data: (0x10, 0x40),
w_addr: 0,
r_data: (0x10u8, 0x40i8),
w_addr: 0_hdl_u4,
w_en: true,
w_data: (0x50, 0x60),
w_data: (0x50u8, 0x60i8),
w_mask: (true, false),
},
#[hdl(sim)]
IO {
r_addr: 0,
r_addr: 0_hdl_u4,
r_en: true,
r_data: (0x50, 0x40),
w_addr: 0,
r_data: (0x50u8, 0x40i8),
w_addr: 0_hdl_u4,
w_en: true,
w_data: (0x70, -0x80),
w_data: (0x70u8, -0x80i8),
w_mask: (false, false),
},
#[hdl(sim)]
IO {
r_addr: 0,
r_addr: 0_hdl_u4,
r_en: true,
r_data: (0x50, 0x40),
w_addr: 0,
r_data: (0x50u8, 0x40i8),
w_addr: 0_hdl_u4,
w_en: false,
w_data: (0x90, 0xA0u8 as i8),
w_data: (0x90u8, 0xA0u8 as i8),
w_mask: (false, false),
},
#[hdl(sim)]
IO {
r_addr: 0,
r_addr: 0_hdl_u4,
r_en: true,
r_data: (0x50, 0x40),
w_addr: 1,
r_data: (0x50u8, 0x40i8),
w_addr: 1_hdl_u4,
w_en: true,
w_data: (0x90, 0xA0u8 as i8),
w_data: (0x90u8, 0xA0u8 as i8),
w_mask: (true, true),
},
#[hdl(sim)]
IO {
r_addr: 0,
r_addr: 0_hdl_u4,
r_en: true,
r_data: (0x50, 0x40),
w_addr: 2,
r_data: (0x50u8, 0x40i8),
w_addr: 2_hdl_u4,
w_en: true,
w_data: (0xB0, 0xC0u8 as i8),
w_data: (0xB0u8, 0xC0u8 as i8),
w_mask: (true, true),
},
#[hdl(sim)]
IO {
r_addr: 0,
r_addr: 0_hdl_u4,
r_en: true,
r_data: (0x50, 0x40),
w_addr: 2,
r_data: (0x50u8, 0x40i8),
w_addr: 2_hdl_u4,
w_en: false,
w_data: (0xD0, 0xE0u8 as i8),
w_data: (0xD0u8, 0xE0u8 as i8),
w_mask: (true, true),
},
#[hdl(sim)]
IO {
r_addr: 1,
r_addr: 1_hdl_u4,
r_en: true,
r_data: (0x90, 0xA0u8 as i8),
w_addr: 2,
r_data: (0x90u8, 0xA0u8 as i8),
w_addr: 2_hdl_u4,
w_en: false,
w_data: (0xD0, 0xE0u8 as i8),
w_data: (0xD0u8, 0xE0u8 as i8),
w_mask: (true, true),
},
#[hdl(sim)]
IO {
r_addr: 2,
r_addr: 2_hdl_u4,
r_en: true,
r_data: (0xB0, 0xC0u8 as i8),
w_addr: 2,
r_data: (0xB0u8, 0xC0u8 as i8),
w_addr: 2_hdl_u4,
w_en: false,
w_data: (0xD0, 0xE0u8 as i8),
w_data: (0xD0u8, 0xE0u8 as i8),
w_mask: (true, true),
},
];
for (
cycle,
expected @ IO {
for (cycle, expected) in io_cycles.into_iter().enumerate() {
#[hdl(sim)]
let IO {
r_addr,
r_en,
r_data: _,
@ -680,30 +706,18 @@ fn test_memories() {
w_en,
w_data,
w_mask,
},
) in io_cycles.into_iter().enumerate()
{
sim.write_bool_or_int(sim.io().r.addr, r_addr.cast_to_static());
sim.write_bool(sim.io().r.en, r_en);
sim.write_bool_or_int(sim.io().w.addr, w_addr.cast_to_static());
sim.write_bool(sim.io().w.en, w_en);
sim.write_bool_or_int(sim.io().w.data.0, w_data.0);
sim.write_bool_or_int(sim.io().w.data.1, w_data.1);
sim.write_bool(sim.io().w.mask.0, w_mask.0);
sim.write_bool(sim.io().w.mask.1, w_mask.1);
let io = IO {
} = expected;
sim.write(sim.io().r.addr, &r_addr);
sim.write(sim.io().r.en, &r_en);
sim.write(sim.io().w.addr, &w_addr);
sim.write(sim.io().w.en, &w_en);
sim.write(sim.io().w.data, &w_data);
sim.write(sim.io().w.mask, &w_mask);
let io = #[hdl(sim)]
IO {
r_addr,
r_en,
r_data: (
sim.read_bool_or_int(sim.io().r.data.0)
.to_bigint()
.try_into()
.expect("known to be in range"),
sim.read_bool_or_int(sim.io().r.data.1)
.to_bigint()
.try_into()
.expect("known to be in range"),
),
r_data: sim.read(sim.io().r.data),
w_addr,
w_en,
w_data,
@ -716,11 +730,11 @@ fn test_memories() {
String::from_utf8(writer.take()).unwrap(),
);
sim.advance_time(SimDuration::from_micros(1));
sim.write_clock(sim.io().r.clk, true);
sim.write_clock(sim.io().w.clk, true);
sim.write(sim.io().r.clk, true);
sim.write(sim.io().w.clk, true);
sim.advance_time(SimDuration::from_micros(1));
sim.write_clock(sim.io().r.clk, false);
sim.write_clock(sim.io().w.clk, false);
sim.write(sim.io().r.clk, false);
sim.write(sim.io().w.clk, false);
}
sim.flush_traces().unwrap();
let vcd = String::from_utf8(writer.take()).unwrap();
@ -738,7 +752,7 @@ fn test_memories() {
#[hdl_module(outline_generated)]
pub fn memories2() {
#[hdl]
let rw: fayalite::memory::ReadWriteStruct<UInt<2>, ConstUsize<3>> = m.input();
let rw: ReadWriteStruct<UInt<2>, ConstUsize<3>> = m.input();
#[hdl]
let mut mem = memory_with_init([HdlSome(true); 5]);
mem.read_latency(1);
@ -1011,9 +1025,9 @@ fn test_memories2() {
#[hdl_module(outline_generated)]
pub fn memories3() {
#[hdl]
let r: fayalite::memory::ReadStruct<Array<UInt<8>, 8>, ConstUsize<3>> = m.input();
let r: ReadStruct<Array<UInt<8>, 8>, ConstUsize<3>> = m.input();
#[hdl]
let w: fayalite::memory::WriteStruct<Array<UInt<8>, 8>, ConstUsize<3>> = m.input();
let w: WriteStruct<Array<UInt<8>, 8>, ConstUsize<3>> = m.input();
#[hdl]
let mut mem: MemBuilder<Array<UInt<8>, 8>> = memory();
mem.depth(8);
@ -1443,3 +1457,172 @@ fn test_conditional_assignment_last() {
panic!();
}
}
#[hdl_module(outline_generated, extern)]
pub fn extern_module() {
#[hdl]
let i: Bool = m.input();
#[hdl]
let o: Bool = m.output();
m.extern_module_simulation_fn((i, o), |(i, o), mut sim| async move {
sim.write(o, true).await;
sim.advance_time(SimDuration::from_nanos(500)).await;
let mut invert = false;
loop {
sim.advance_time(SimDuration::from_micros(1)).await;
let v = sim.read_bool(i).await;
sim.write(o, v ^ invert).await;
invert = !invert;
}
});
}
#[test]
fn test_extern_module() {
let _n = SourceLocation::normalize_files_for_tests();
let mut sim = Simulation::new(extern_module());
let mut writer = RcWriter::default();
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
sim.write(sim.io().i, false);
sim.advance_time(SimDuration::from_micros(10));
sim.write(sim.io().i, true);
sim.advance_time(SimDuration::from_micros(10));
sim.flush_traces().unwrap();
let vcd = String::from_utf8(writer.take()).unwrap();
println!("####### VCD:\n{vcd}\n#######");
if vcd != include_str!("sim/expected/extern_module.vcd") {
panic!();
}
let sim_debug = format!("{sim:#?}");
println!("#######\n{sim_debug}\n#######");
if sim_debug != include_str!("sim/expected/extern_module.txt") {
panic!();
}
}
#[hdl_module(outline_generated, extern)]
pub fn extern_module2() {
#[hdl]
let en: Bool = m.input();
#[hdl]
let clk: Clock = m.input();
#[hdl]
let o: UInt<8> = m.output();
m.extern_module_simulation_fn((en, clk, o), |(en, clk, o), mut sim| async move {
for b in "Hello, World!\n".bytes().cycle() {
sim.write(o, b).await;
loop {
sim.wait_for_clock_edge(clk).await;
if sim.read_bool(en).await {
break;
}
}
}
});
}
#[test]
fn test_extern_module2() {
let _n = SourceLocation::normalize_files_for_tests();
let mut sim = Simulation::new(extern_module2());
let mut writer = RcWriter::default();
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
for i in 0..30 {
sim.write(sim.io().en, i % 10 < 5);
sim.write(sim.io().clk, false);
sim.advance_time(SimDuration::from_micros(1));
sim.write(sim.io().clk, true);
sim.advance_time(SimDuration::from_micros(1));
}
sim.flush_traces().unwrap();
let vcd = String::from_utf8(writer.take()).unwrap();
println!("####### VCD:\n{vcd}\n#######");
if vcd != include_str!("sim/expected/extern_module2.vcd") {
panic!();
}
let sim_debug = format!("{sim:#?}");
println!("#######\n{sim_debug}\n#######");
if sim_debug != include_str!("sim/expected/extern_module2.txt") {
panic!();
}
}
// use an extern module to simulate a register to test that the
// simulator can handle chains of alternating circuits and extern modules.
#[hdl_module(outline_generated, extern)]
pub fn sw_reg() {
#[hdl]
let clk: Clock = m.input();
#[hdl]
let o: Bool = m.output();
m.extern_module_simulation_fn((clk, o), |(clk, o), mut sim| async move {
let mut state = false;
loop {
sim.write(o, state).await;
sim.wait_for_clock_edge(clk).await;
state = !state;
}
});
}
#[hdl_module(outline_generated)]
pub fn ripple_counter() {
#[hdl]
let clk: Clock = m.input();
#[hdl]
let o: UInt<6> = m.output();
#[hdl]
let bits: Array<Bool, 6> = wire();
connect_any(o, bits.cast_to_bits());
let mut clk_in = clk;
for (i, bit) in bits.into_iter().enumerate() {
if i % 2 == 0 {
let bit_reg = reg_builder_with_loc(&format!("bit_reg_{i}"), SourceLocation::caller())
.clock_domain(
#[hdl]
ClockDomain {
clk: clk_in,
rst: false.to_sync_reset(),
},
)
.no_reset(Bool)
.build();
connect(bit, bit_reg);
connect(bit_reg, !bit_reg);
} else {
let bit_reg =
instance_with_loc(&format!("bit_reg_{i}"), sw_reg(), SourceLocation::caller());
connect(bit_reg.clk, clk_in);
connect(bit, bit_reg.o);
}
clk_in = bit.to_clock();
}
}
#[test]
fn test_ripple_counter() {
let _n = SourceLocation::normalize_files_for_tests();
let mut sim = Simulation::new(ripple_counter());
let mut writer = RcWriter::default();
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
for _ in 0..0x80 {
sim.write(sim.io().clk, false);
sim.advance_time(SimDuration::from_micros(1));
sim.write(sim.io().clk, true);
sim.advance_time(SimDuration::from_micros(1));
}
sim.flush_traces().unwrap();
let vcd = String::from_utf8(writer.take()).unwrap();
println!("####### VCD:\n{vcd}\n#######");
if vcd != include_str!("sim/expected/ripple_counter.vcd") {
panic!();
}
let sim_debug = format!("{sim:#?}");
println!("#######\n{sim_debug}\n#######");
if sim_debug != include_str!("sim/expected/ripple_counter.txt") {
panic!();
}
}

File diff suppressed because it is too large Load diff

View file

@ -92,45 +92,30 @@ Simulation {
..
},
},
uninitialized_inputs: {},
io_targets: {
Instance {
name: <simulator>::conditional_assignment_last,
instantiated: Module {
name: conditional_assignment_last,
..
},
}.i: CompiledValue {
layout: CompiledTypeLayout {
ty: Bool,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::i",
ty: Bool,
},
],
..
},
main_module: SimulationModuleState {
base_targets: [
Instance {
name: <simulator>::conditional_assignment_last,
instantiated: Module {
name: conditional_assignment_last,
..
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 1 },
},
write: None,
}.i,
],
uninitialized_ios: {},
io_targets: {
Instance {
name: <simulator>::conditional_assignment_last,
instantiated: Module {
name: conditional_assignment_last,
..
},
}.i,
},
did_initial_settle: true,
},
made_initial_step: true,
needs_settle: false,
extern_modules: [],
state_ready_to_run: false,
trace_decls: TraceModule {
name: "conditional_assignment_last",
children: [

View file

@ -68,45 +68,30 @@ Simulation {
..
},
},
uninitialized_inputs: {},
io_targets: {
Instance {
name: <simulator>::connect_const,
instantiated: Module {
name: connect_const,
..
},
}.o: CompiledValue {
layout: CompiledTypeLayout {
ty: UInt<8>,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(connect_const: connect_const).connect_const::o",
ty: UInt<8>,
},
],
..
},
main_module: SimulationModuleState {
base_targets: [
Instance {
name: <simulator>::connect_const,
instantiated: Module {
name: connect_const,
..
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 1 },
},
write: None,
}.o,
],
uninitialized_ios: {},
io_targets: {
Instance {
name: <simulator>::connect_const,
instantiated: Module {
name: connect_const,
..
},
}.o,
},
did_initial_settle: true,
},
made_initial_step: true,
needs_settle: false,
extern_modules: [],
state_ready_to_run: false,
trace_decls: TraceModule {
name: "connect_const",
children: [

View file

@ -97,79 +97,44 @@ Simulation {
..
},
},
uninitialized_inputs: {},
io_targets: {
Instance {
name: <simulator>::connect_const_reset,
instantiated: Module {
name: connect_const_reset,
..
},
}.bit_out: CompiledValue {
layout: CompiledTypeLayout {
ty: Bool,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(connect_const_reset: connect_const_reset).connect_const_reset::bit_out",
ty: Bool,
},
],
..
},
main_module: SimulationModuleState {
base_targets: [
Instance {
name: <simulator>::connect_const_reset,
instantiated: Module {
name: connect_const_reset,
..
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 1, len: 1 },
},
write: None,
},
Instance {
name: <simulator>::connect_const_reset,
instantiated: Module {
name: connect_const_reset,
..
},
}.reset_out: CompiledValue {
layout: CompiledTypeLayout {
ty: AsyncReset,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(connect_const_reset: connect_const_reset).connect_const_reset::reset_out",
ty: AsyncReset,
},
],
..
},
}.reset_out,
Instance {
name: <simulator>::connect_const_reset,
instantiated: Module {
name: connect_const_reset,
..
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 1 },
},
write: None,
}.bit_out,
],
uninitialized_ios: {},
io_targets: {
Instance {
name: <simulator>::connect_const_reset,
instantiated: Module {
name: connect_const_reset,
..
},
}.bit_out,
Instance {
name: <simulator>::connect_const_reset,
instantiated: Module {
name: connect_const_reset,
..
},
}.reset_out,
},
did_initial_settle: true,
},
made_initial_step: true,
needs_settle: false,
extern_modules: [],
state_ready_to_run: false,
trace_decls: TraceModule {
name: "connect_const_reset",
children: [

View file

@ -203,213 +203,58 @@ Simulation {
..
},
},
uninitialized_inputs: {},
io_targets: {
Instance {
name: <simulator>::counter,
instantiated: Module {
name: counter,
..
},
}.cd: CompiledValue {
layout: CompiledTypeLayout {
ty: Bundle {
/* offset = 0 */
clk: Clock,
/* offset = 1 */
rst: AsyncReset,
main_module: SimulationModuleState {
base_targets: [
Instance {
name: <simulator>::counter,
instantiated: Module {
name: counter,
..
},
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 2,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(counter: counter).counter::cd.clk",
ty: Clock,
},
SlotDebugData {
name: "InstantiatedModule(counter: counter).counter::cd.rst",
ty: AsyncReset,
},
],
..
},
}.cd,
Instance {
name: <simulator>::counter,
instantiated: Module {
name: counter,
..
},
body: Bundle {
fields: [
CompiledBundleField {
offset: TypeIndex {
small_slots: StatePartIndex<SmallSlots>(0),
big_slots: StatePartIndex<BigSlots>(0),
},
ty: CompiledTypeLayout {
ty: Clock,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: Clock,
},
],
..
},
},
body: Scalar,
},
},
CompiledBundleField {
offset: TypeIndex {
small_slots: StatePartIndex<SmallSlots>(0),
big_slots: StatePartIndex<BigSlots>(1),
},
ty: CompiledTypeLayout {
ty: AsyncReset,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: AsyncReset,
},
],
..
},
},
body: Scalar,
},
},
],
}.count,
],
uninitialized_ios: {},
io_targets: {
Instance {
name: <simulator>::counter,
instantiated: Module {
name: counter,
..
},
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 2 },
},
write: None,
},
Instance {
name: <simulator>::counter,
instantiated: Module {
name: counter,
..
},
}.cd.clk: CompiledValue {
layout: CompiledTypeLayout {
ty: Clock,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: Clock,
},
],
..
},
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 1 },
},
write: None,
},
Instance {
name: <simulator>::counter,
instantiated: Module {
name: counter,
..
},
}.cd.rst: CompiledValue {
layout: CompiledTypeLayout {
ty: AsyncReset,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: AsyncReset,
},
],
..
},
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 1, len: 1 },
},
write: None,
},
Instance {
name: <simulator>::counter,
instantiated: Module {
name: counter,
..
},
}.count: CompiledValue {
layout: CompiledTypeLayout {
ty: UInt<4>,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(counter: counter).counter::count",
ty: UInt<4>,
},
],
..
},
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 2, len: 1 },
},
write: None,
}.cd,
Instance {
name: <simulator>::counter,
instantiated: Module {
name: counter,
..
},
}.cd.clk,
Instance {
name: <simulator>::counter,
instantiated: Module {
name: counter,
..
},
}.cd.rst,
Instance {
name: <simulator>::counter,
instantiated: Module {
name: counter,
..
},
}.count,
},
did_initial_settle: true,
},
made_initial_step: true,
needs_settle: false,
extern_modules: [],
state_ready_to_run: false,
trace_decls: TraceModule {
name: "counter",
children: [

View file

@ -184,213 +184,58 @@ Simulation {
..
},
},
uninitialized_inputs: {},
io_targets: {
Instance {
name: <simulator>::counter,
instantiated: Module {
name: counter,
..
},
}.cd: CompiledValue {
layout: CompiledTypeLayout {
ty: Bundle {
/* offset = 0 */
clk: Clock,
/* offset = 1 */
rst: SyncReset,
main_module: SimulationModuleState {
base_targets: [
Instance {
name: <simulator>::counter,
instantiated: Module {
name: counter,
..
},
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 2,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(counter: counter).counter::cd.clk",
ty: Clock,
},
SlotDebugData {
name: "InstantiatedModule(counter: counter).counter::cd.rst",
ty: SyncReset,
},
],
..
},
}.cd,
Instance {
name: <simulator>::counter,
instantiated: Module {
name: counter,
..
},
body: Bundle {
fields: [
CompiledBundleField {
offset: TypeIndex {
small_slots: StatePartIndex<SmallSlots>(0),
big_slots: StatePartIndex<BigSlots>(0),
},
ty: CompiledTypeLayout {
ty: Clock,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: Clock,
},
],
..
},
},
body: Scalar,
},
},
CompiledBundleField {
offset: TypeIndex {
small_slots: StatePartIndex<SmallSlots>(0),
big_slots: StatePartIndex<BigSlots>(1),
},
ty: CompiledTypeLayout {
ty: SyncReset,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: SyncReset,
},
],
..
},
},
body: Scalar,
},
},
],
}.count,
],
uninitialized_ios: {},
io_targets: {
Instance {
name: <simulator>::counter,
instantiated: Module {
name: counter,
..
},
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 2 },
},
write: None,
},
Instance {
name: <simulator>::counter,
instantiated: Module {
name: counter,
..
},
}.cd.clk: CompiledValue {
layout: CompiledTypeLayout {
ty: Clock,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: Clock,
},
],
..
},
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 1 },
},
write: None,
},
Instance {
name: <simulator>::counter,
instantiated: Module {
name: counter,
..
},
}.cd.rst: CompiledValue {
layout: CompiledTypeLayout {
ty: SyncReset,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: SyncReset,
},
],
..
},
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 1, len: 1 },
},
write: None,
},
Instance {
name: <simulator>::counter,
instantiated: Module {
name: counter,
..
},
}.count: CompiledValue {
layout: CompiledTypeLayout {
ty: UInt<4>,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(counter: counter).counter::count",
ty: UInt<4>,
},
],
..
},
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 2, len: 1 },
},
write: None,
}.cd,
Instance {
name: <simulator>::counter,
instantiated: Module {
name: counter,
..
},
}.cd.clk,
Instance {
name: <simulator>::counter,
instantiated: Module {
name: counter,
..
},
}.cd.rst,
Instance {
name: <simulator>::counter,
instantiated: Module {
name: counter,
..
},
}.count,
},
did_initial_settle: true,
},
made_initial_step: true,
needs_settle: false,
extern_modules: [],
state_ready_to_run: false,
trace_decls: TraceModule {
name: "counter",
children: [

View file

@ -88,10 +88,14 @@ Simulation {
..
},
},
uninitialized_inputs: {},
io_targets: {},
made_initial_step: true,
needs_settle: false,
main_module: SimulationModuleState {
base_targets: [],
uninitialized_ios: {},
io_targets: {},
did_initial_settle: true,
},
extern_modules: [],
state_ready_to_run: false,
trace_decls: TraceModule {
name: "duplicate_names",
children: [

File diff suppressed because it is too large Load diff

View file

@ -16,18 +16,25 @@ $var wire 1 ) \0 $end
$var wire 1 * \1 $end
$upscope $end
$upscope $end
$scope struct the_reg $end
$scope struct b2_out $end
$var string 1 + \$tag $end
$scope struct HdlSome $end
$var wire 1 , \0 $end
$var wire 1 - \1 $end
$upscope $end
$upscope $end
$scope struct the_reg $end
$var string 1 . \$tag $end
$scope struct B $end
$var reg 1 , \0 $end
$var reg 1 - \1 $end
$var reg 1 / \0 $end
$var reg 1 0 \1 $end
$upscope $end
$scope struct C $end
$scope struct a $end
$var reg 1 . \[0] $end
$var reg 1 / \[1] $end
$var reg 1 1 \[0] $end
$var reg 1 2 \[1] $end
$upscope $end
$var reg 2 0 b $end
$var reg 2 3 b $end
$upscope $end
$upscope $end
$upscope $end
@ -43,12 +50,15 @@ b0 '
sHdlNone\x20(0) (
0)
0*
sA\x20(0) +
sHdlNone\x20(0) +
0,
0-
0.
sA\x20(0) .
0/
b0 0
00
01
02
b0 3
$end
#1000000
1!
@ -66,7 +76,8 @@ b1 $
1!
b1 &
sHdlSome\x20(1) (
sB\x20(1) +
sHdlSome\x20(1) +
sB\x20(1) .
#6000000
0#
b0 $
@ -85,8 +96,10 @@ b11 '
1*
1,
1-
1.
1/
10
11
12
#10000000
0!
#11000000
@ -101,8 +114,11 @@ b1111 '
sHdlNone\x20(0) (
0)
0*
sC\x20(2) +
b11 0
sHdlNone\x20(0) +
0,
0-
sC\x20(2) .
b11 3
#14000000
0!
#15000000

View file

@ -0,0 +1,220 @@
Simulation {
state: State {
insns: Insns {
state_layout: StateLayout {
ty: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 2,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(extern_module: extern_module).extern_module::i",
ty: Bool,
},
SlotDebugData {
name: "InstantiatedModule(extern_module: extern_module).extern_module::o",
ty: Bool,
},
],
..
},
},
memories: StatePartLayout<Memories> {
len: 0,
debug_data: [],
layout_data: [],
..
},
},
insns: [
// at: module-XXXXXXXXXX.rs:1:1
0: Return,
],
..
},
pc: 0,
memory_write_log: [],
memories: StatePart {
value: [],
},
small_slots: StatePart {
value: [],
},
big_slots: StatePart {
value: [
1,
1,
],
},
},
io: Instance {
name: <simulator>::extern_module,
instantiated: Module {
name: extern_module,
..
},
},
main_module: SimulationModuleState {
base_targets: [
Instance {
name: <simulator>::extern_module,
instantiated: Module {
name: extern_module,
..
},
}.i,
Instance {
name: <simulator>::extern_module,
instantiated: Module {
name: extern_module,
..
},
}.o,
],
uninitialized_ios: {},
io_targets: {
Instance {
name: <simulator>::extern_module,
instantiated: Module {
name: extern_module,
..
},
}.i,
Instance {
name: <simulator>::extern_module,
instantiated: Module {
name: extern_module,
..
},
}.o,
},
did_initial_settle: true,
},
extern_modules: [
SimulationExternModuleState {
module_state: SimulationModuleState {
base_targets: [
ModuleIO {
name: extern_module::i,
is_input: true,
ty: Bool,
..
},
ModuleIO {
name: extern_module::o,
is_input: false,
ty: Bool,
..
},
],
uninitialized_ios: {},
io_targets: {
ModuleIO {
name: extern_module::i,
is_input: true,
ty: Bool,
..
},
ModuleIO {
name: extern_module::o,
is_input: false,
ty: Bool,
..
},
},
did_initial_settle: true,
},
sim: ExternModuleSimulation {
generator: SimGeneratorFn {
args: (
ModuleIO {
name: extern_module::i,
is_input: true,
ty: Bool,
..
},
ModuleIO {
name: extern_module::o,
is_input: false,
ty: Bool,
..
},
),
f: ...,
},
source_location: SourceLocation(
module-XXXXXXXXXX.rs:4:1,
),
},
running_generator: Some(
...,
),
wait_targets: {
Instant(
20.500000000000 μs,
),
},
},
],
state_ready_to_run: false,
trace_decls: TraceModule {
name: "extern_module",
children: [
TraceModuleIO {
name: "i",
child: TraceBool {
location: TraceScalarId(0),
name: "i",
flow: Source,
},
ty: Bool,
flow: Source,
},
TraceModuleIO {
name: "o",
child: TraceBool {
location: TraceScalarId(1),
name: "o",
flow: Sink,
},
ty: Bool,
flow: Sink,
},
],
},
traces: [
SimTrace {
id: TraceScalarId(0),
kind: BigBool {
index: StatePartIndex<BigSlots>(0),
},
state: 0x1,
last_state: 0x1,
},
SimTrace {
id: TraceScalarId(1),
kind: BigBool {
index: StatePartIndex<BigSlots>(1),
},
state: 0x1,
last_state: 0x1,
},
],
trace_memories: {},
trace_writers: [
Running(
VcdWriter {
finished_init: true,
timescale: 1 ps,
..
},
),
],
instant: 20 μs,
clocks_triggered: [],
..
}

View file

@ -0,0 +1,51 @@
$timescale 1 ps $end
$scope module extern_module $end
$var wire 1 ! i $end
$var wire 1 " o $end
$upscope $end
$enddefinitions $end
$dumpvars
0!
1"
$end
#500000
#1500000
0"
#2500000
1"
#3500000
0"
#4500000
1"
#5500000
0"
#6500000
1"
#7500000
0"
#8500000
1"
#9500000
0"
#10000000
1!
#10500000
#11500000
1"
#12500000
0"
#13500000
1"
#14500000
0"
#15500000
1"
#16500000
0"
#17500000
1"
#18500000
0"
#19500000
1"
#20000000

View file

@ -0,0 +1,310 @@
Simulation {
state: State {
insns: Insns {
state_layout: StateLayout {
ty: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 3,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(extern_module2: extern_module2).extern_module2::en",
ty: Bool,
},
SlotDebugData {
name: "InstantiatedModule(extern_module2: extern_module2).extern_module2::clk",
ty: Clock,
},
SlotDebugData {
name: "InstantiatedModule(extern_module2: extern_module2).extern_module2::o",
ty: UInt<8>,
},
],
..
},
},
memories: StatePartLayout<Memories> {
len: 0,
debug_data: [],
layout_data: [],
..
},
},
insns: [
// at: module-XXXXXXXXXX.rs:1:1
0: Return,
],
..
},
pc: 0,
memory_write_log: [],
memories: StatePart {
value: [],
},
small_slots: StatePart {
value: [],
},
big_slots: StatePart {
value: [
0,
1,
101,
],
},
},
io: Instance {
name: <simulator>::extern_module2,
instantiated: Module {
name: extern_module2,
..
},
},
main_module: SimulationModuleState {
base_targets: [
Instance {
name: <simulator>::extern_module2,
instantiated: Module {
name: extern_module2,
..
},
}.en,
Instance {
name: <simulator>::extern_module2,
instantiated: Module {
name: extern_module2,
..
},
}.clk,
Instance {
name: <simulator>::extern_module2,
instantiated: Module {
name: extern_module2,
..
},
}.o,
],
uninitialized_ios: {},
io_targets: {
Instance {
name: <simulator>::extern_module2,
instantiated: Module {
name: extern_module2,
..
},
}.clk,
Instance {
name: <simulator>::extern_module2,
instantiated: Module {
name: extern_module2,
..
},
}.en,
Instance {
name: <simulator>::extern_module2,
instantiated: Module {
name: extern_module2,
..
},
}.o,
},
did_initial_settle: true,
},
extern_modules: [
SimulationExternModuleState {
module_state: SimulationModuleState {
base_targets: [
ModuleIO {
name: extern_module2::en,
is_input: true,
ty: Bool,
..
},
ModuleIO {
name: extern_module2::clk,
is_input: true,
ty: Clock,
..
},
ModuleIO {
name: extern_module2::o,
is_input: false,
ty: UInt<8>,
..
},
],
uninitialized_ios: {},
io_targets: {
ModuleIO {
name: extern_module2::clk,
is_input: true,
ty: Clock,
..
},
ModuleIO {
name: extern_module2::en,
is_input: true,
ty: Bool,
..
},
ModuleIO {
name: extern_module2::o,
is_input: false,
ty: UInt<8>,
..
},
},
did_initial_settle: true,
},
sim: ExternModuleSimulation {
generator: SimGeneratorFn {
args: (
ModuleIO {
name: extern_module2::en,
is_input: true,
ty: Bool,
..
},
ModuleIO {
name: extern_module2::clk,
is_input: true,
ty: Clock,
..
},
ModuleIO {
name: extern_module2::o,
is_input: false,
ty: UInt<8>,
..
},
),
f: ...,
},
source_location: SourceLocation(
module-XXXXXXXXXX.rs:5:1,
),
},
running_generator: Some(
...,
),
wait_targets: {
Change {
key: CompiledValue {
layout: CompiledTypeLayout {
ty: Clock,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(extern_module2: extern_module2).extern_module2::clk",
ty: Clock,
},
],
..
},
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 1, len: 1 },
},
write: None,
},
value: SimValue {
ty: Clock,
value: OpaqueSimValue {
bits: 0x1_u1,
},
},
},
},
},
],
state_ready_to_run: false,
trace_decls: TraceModule {
name: "extern_module2",
children: [
TraceModuleIO {
name: "en",
child: TraceBool {
location: TraceScalarId(0),
name: "en",
flow: Source,
},
ty: Bool,
flow: Source,
},
TraceModuleIO {
name: "clk",
child: TraceClock {
location: TraceScalarId(1),
name: "clk",
flow: Source,
},
ty: Clock,
flow: Source,
},
TraceModuleIO {
name: "o",
child: TraceUInt {
location: TraceScalarId(2),
name: "o",
ty: UInt<8>,
flow: Sink,
},
ty: UInt<8>,
flow: Sink,
},
],
},
traces: [
SimTrace {
id: TraceScalarId(0),
kind: BigBool {
index: StatePartIndex<BigSlots>(0),
},
state: 0x0,
last_state: 0x0,
},
SimTrace {
id: TraceScalarId(1),
kind: BigClock {
index: StatePartIndex<BigSlots>(1),
},
state: 0x1,
last_state: 0x1,
},
SimTrace {
id: TraceScalarId(2),
kind: BigUInt {
index: StatePartIndex<BigSlots>(2),
ty: UInt<8>,
},
state: 0x65,
last_state: 0x65,
},
],
trace_memories: {},
trace_writers: [
Running(
VcdWriter {
finished_init: true,
timescale: 1 ps,
..
},
),
],
instant: 60 μs,
clocks_triggered: [],
..
}

View file

@ -0,0 +1,150 @@
$timescale 1 ps $end
$scope module extern_module2 $end
$var wire 1 ! en $end
$var wire 1 " clk $end
$var wire 8 # o $end
$upscope $end
$enddefinitions $end
$dumpvars
1!
0"
b1001000 #
$end
#1000000
1"
b1100101 #
#2000000
0"
#3000000
1"
b1101100 #
#4000000
0"
#5000000
1"
#6000000
0"
#7000000
1"
b1101111 #
#8000000
0"
#9000000
1"
b101100 #
#10000000
0!
0"
#11000000
1"
#12000000
0"
#13000000
1"
#14000000
0"
#15000000
1"
#16000000
0"
#17000000
1"
#18000000
0"
#19000000
1"
#20000000
1!
0"
#21000000
1"
b100000 #
#22000000
0"
#23000000
1"
b1010111 #
#24000000
0"
#25000000
1"
b1101111 #
#26000000
0"
#27000000
1"
b1110010 #
#28000000
0"
#29000000
1"
b1101100 #
#30000000
0!
0"
#31000000
1"
#32000000
0"
#33000000
1"
#34000000
0"
#35000000
1"
#36000000
0"
#37000000
1"
#38000000
0"
#39000000
1"
#40000000
1!
0"
#41000000
1"
b1100100 #
#42000000
0"
#43000000
1"
b100001 #
#44000000
0"
#45000000
1"
b1010 #
#46000000
0"
#47000000
1"
b1001000 #
#48000000
0"
#49000000
1"
b1100101 #
#50000000
0!
0"
#51000000
1"
#52000000
0"
#53000000
1"
#54000000
0"
#55000000
1"
#56000000
0"
#57000000
1"
#58000000
0"
#59000000
1"
#60000000

File diff suppressed because it is too large Load diff

View file

@ -598,514 +598,79 @@ Simulation {
..
},
},
uninitialized_inputs: {},
io_targets: {
Instance {
name: <simulator>::memories2,
instantiated: Module {
name: memories2,
..
},
}.rw: CompiledValue {
layout: CompiledTypeLayout {
ty: Bundle {
/* offset = 0 */
addr: UInt<3>,
/* offset = 3 */
en: Bool,
/* offset = 4 */
clk: Clock,
#[hdl(flip)] /* offset = 5 */
rdata: UInt<2>,
/* offset = 7 */
wmode: Bool,
/* offset = 8 */
wdata: UInt<2>,
/* offset = 10 */
wmask: Bool,
main_module: SimulationModuleState {
base_targets: [
Instance {
name: <simulator>::memories2,
instantiated: Module {
name: memories2,
..
},
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 7,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(memories2: memories2).memories2::rw.addr",
ty: UInt<3>,
},
SlotDebugData {
name: "InstantiatedModule(memories2: memories2).memories2::rw.en",
ty: Bool,
},
SlotDebugData {
name: "InstantiatedModule(memories2: memories2).memories2::rw.clk",
ty: Clock,
},
SlotDebugData {
name: "InstantiatedModule(memories2: memories2).memories2::rw.rdata",
ty: UInt<2>,
},
SlotDebugData {
name: "InstantiatedModule(memories2: memories2).memories2::rw.wmode",
ty: Bool,
},
SlotDebugData {
name: "InstantiatedModule(memories2: memories2).memories2::rw.wdata",
ty: UInt<2>,
},
SlotDebugData {
name: "InstantiatedModule(memories2: memories2).memories2::rw.wmask",
ty: Bool,
},
],
..
},
}.rw,
],
uninitialized_ios: {},
io_targets: {
Instance {
name: <simulator>::memories2,
instantiated: Module {
name: memories2,
..
},
body: Bundle {
fields: [
CompiledBundleField {
offset: TypeIndex {
small_slots: StatePartIndex<SmallSlots>(0),
big_slots: StatePartIndex<BigSlots>(0),
},
ty: CompiledTypeLayout {
ty: UInt<3>,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: UInt<3>,
},
],
..
},
},
body: Scalar,
},
},
CompiledBundleField {
offset: TypeIndex {
small_slots: StatePartIndex<SmallSlots>(0),
big_slots: StatePartIndex<BigSlots>(1),
},
ty: CompiledTypeLayout {
ty: Bool,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: Bool,
},
],
..
},
},
body: Scalar,
},
},
CompiledBundleField {
offset: TypeIndex {
small_slots: StatePartIndex<SmallSlots>(0),
big_slots: StatePartIndex<BigSlots>(2),
},
ty: CompiledTypeLayout {
ty: Clock,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: Clock,
},
],
..
},
},
body: Scalar,
},
},
CompiledBundleField {
offset: TypeIndex {
small_slots: StatePartIndex<SmallSlots>(0),
big_slots: StatePartIndex<BigSlots>(3),
},
ty: CompiledTypeLayout {
ty: UInt<2>,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: UInt<2>,
},
],
..
},
},
body: Scalar,
},
},
CompiledBundleField {
offset: TypeIndex {
small_slots: StatePartIndex<SmallSlots>(0),
big_slots: StatePartIndex<BigSlots>(4),
},
ty: CompiledTypeLayout {
ty: Bool,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: Bool,
},
],
..
},
},
body: Scalar,
},
},
CompiledBundleField {
offset: TypeIndex {
small_slots: StatePartIndex<SmallSlots>(0),
big_slots: StatePartIndex<BigSlots>(5),
},
ty: CompiledTypeLayout {
ty: UInt<2>,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: UInt<2>,
},
],
..
},
},
body: Scalar,
},
},
CompiledBundleField {
offset: TypeIndex {
small_slots: StatePartIndex<SmallSlots>(0),
big_slots: StatePartIndex<BigSlots>(6),
},
ty: CompiledTypeLayout {
ty: Bool,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: Bool,
},
],
..
},
},
body: Scalar,
},
},
],
}.rw,
Instance {
name: <simulator>::memories2,
instantiated: Module {
name: memories2,
..
},
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 7 },
},
write: None,
},
Instance {
name: <simulator>::memories2,
instantiated: Module {
name: memories2,
..
},
}.rw.addr: CompiledValue {
layout: CompiledTypeLayout {
ty: UInt<3>,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: UInt<3>,
},
],
..
},
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 1 },
},
write: None,
},
Instance {
name: <simulator>::memories2,
instantiated: Module {
name: memories2,
..
},
}.rw.clk: CompiledValue {
layout: CompiledTypeLayout {
ty: Clock,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: Clock,
},
],
..
},
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 2, len: 1 },
},
write: None,
},
Instance {
name: <simulator>::memories2,
instantiated: Module {
name: memories2,
..
},
}.rw.en: CompiledValue {
layout: CompiledTypeLayout {
ty: Bool,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: Bool,
},
],
..
},
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 1, len: 1 },
},
write: None,
},
Instance {
name: <simulator>::memories2,
instantiated: Module {
name: memories2,
..
},
}.rw.rdata: CompiledValue {
layout: CompiledTypeLayout {
ty: UInt<2>,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: UInt<2>,
},
],
..
},
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 3, len: 1 },
},
write: None,
},
Instance {
name: <simulator>::memories2,
instantiated: Module {
name: memories2,
..
},
}.rw.wdata: CompiledValue {
layout: CompiledTypeLayout {
ty: UInt<2>,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: UInt<2>,
},
],
..
},
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 5, len: 1 },
},
write: None,
},
Instance {
name: <simulator>::memories2,
instantiated: Module {
name: memories2,
..
},
}.rw.wmask: CompiledValue {
layout: CompiledTypeLayout {
ty: Bool,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: Bool,
},
],
..
},
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 6, len: 1 },
},
write: None,
},
Instance {
name: <simulator>::memories2,
instantiated: Module {
name: memories2,
..
},
}.rw.wmode: CompiledValue {
layout: CompiledTypeLayout {
ty: Bool,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: Bool,
},
],
..
},
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 4, len: 1 },
},
write: None,
}.rw.addr,
Instance {
name: <simulator>::memories2,
instantiated: Module {
name: memories2,
..
},
}.rw.clk,
Instance {
name: <simulator>::memories2,
instantiated: Module {
name: memories2,
..
},
}.rw.en,
Instance {
name: <simulator>::memories2,
instantiated: Module {
name: memories2,
..
},
}.rw.rdata,
Instance {
name: <simulator>::memories2,
instantiated: Module {
name: memories2,
..
},
}.rw.wdata,
Instance {
name: <simulator>::memories2,
instantiated: Module {
name: memories2,
..
},
}.rw.wmask,
Instance {
name: <simulator>::memories2,
instantiated: Module {
name: memories2,
..
},
}.rw.wmode,
},
did_initial_settle: true,
},
made_initial_step: true,
needs_settle: false,
extern_modules: [],
state_ready_to_run: false,
trace_decls: TraceModule {
name: "memories2",
children: [

File diff suppressed because it is too large Load diff

View file

@ -216,313 +216,58 @@ Simulation {
..
},
},
uninitialized_inputs: {},
io_targets: {
Instance {
name: <simulator>::mod1,
instantiated: Module {
name: mod1,
..
},
}.o: CompiledValue {
layout: CompiledTypeLayout {
ty: Bundle {
#[hdl(flip)] /* offset = 0 */
i: UInt<4>,
/* offset = 4 */
o: SInt<2>,
#[hdl(flip)] /* offset = 6 */
i2: SInt<2>,
/* offset = 8 */
o2: UInt<4>,
main_module: SimulationModuleState {
base_targets: [
Instance {
name: <simulator>::mod1,
instantiated: Module {
name: mod1,
..
},
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 4,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(mod1: mod1).mod1::o.i",
ty: UInt<4>,
},
SlotDebugData {
name: "InstantiatedModule(mod1: mod1).mod1::o.o",
ty: SInt<2>,
},
SlotDebugData {
name: "InstantiatedModule(mod1: mod1).mod1::o.i2",
ty: SInt<2>,
},
SlotDebugData {
name: "InstantiatedModule(mod1: mod1).mod1::o.o2",
ty: UInt<4>,
},
],
..
},
}.o,
],
uninitialized_ios: {},
io_targets: {
Instance {
name: <simulator>::mod1,
instantiated: Module {
name: mod1,
..
},
body: Bundle {
fields: [
CompiledBundleField {
offset: TypeIndex {
small_slots: StatePartIndex<SmallSlots>(0),
big_slots: StatePartIndex<BigSlots>(0),
},
ty: CompiledTypeLayout {
ty: UInt<4>,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: UInt<4>,
},
],
..
},
},
body: Scalar,
},
},
CompiledBundleField {
offset: TypeIndex {
small_slots: StatePartIndex<SmallSlots>(0),
big_slots: StatePartIndex<BigSlots>(1),
},
ty: CompiledTypeLayout {
ty: SInt<2>,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: SInt<2>,
},
],
..
},
},
body: Scalar,
},
},
CompiledBundleField {
offset: TypeIndex {
small_slots: StatePartIndex<SmallSlots>(0),
big_slots: StatePartIndex<BigSlots>(2),
},
ty: CompiledTypeLayout {
ty: SInt<2>,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: SInt<2>,
},
],
..
},
},
body: Scalar,
},
},
CompiledBundleField {
offset: TypeIndex {
small_slots: StatePartIndex<SmallSlots>(0),
big_slots: StatePartIndex<BigSlots>(3),
},
ty: CompiledTypeLayout {
ty: UInt<4>,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: UInt<4>,
},
],
..
},
},
body: Scalar,
},
},
],
}.o,
Instance {
name: <simulator>::mod1,
instantiated: Module {
name: mod1,
..
},
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 4 },
},
write: None,
},
Instance {
name: <simulator>::mod1,
instantiated: Module {
name: mod1,
..
},
}.o.i: CompiledValue {
layout: CompiledTypeLayout {
ty: UInt<4>,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: UInt<4>,
},
],
..
},
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 1 },
},
write: None,
},
Instance {
name: <simulator>::mod1,
instantiated: Module {
name: mod1,
..
},
}.o.i2: CompiledValue {
layout: CompiledTypeLayout {
ty: SInt<2>,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: SInt<2>,
},
],
..
},
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 2, len: 1 },
},
write: None,
},
Instance {
name: <simulator>::mod1,
instantiated: Module {
name: mod1,
..
},
}.o.o: CompiledValue {
layout: CompiledTypeLayout {
ty: SInt<2>,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: SInt<2>,
},
],
..
},
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 1, len: 1 },
},
write: None,
},
Instance {
name: <simulator>::mod1,
instantiated: Module {
name: mod1,
..
},
}.o.o2: CompiledValue {
layout: CompiledTypeLayout {
ty: UInt<4>,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: UInt<4>,
},
],
..
},
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 3, len: 1 },
},
write: None,
}.o.i,
Instance {
name: <simulator>::mod1,
instantiated: Module {
name: mod1,
..
},
}.o.i2,
Instance {
name: <simulator>::mod1,
instantiated: Module {
name: mod1,
..
},
}.o.o,
Instance {
name: <simulator>::mod1,
instantiated: Module {
name: mod1,
..
},
}.o.o2,
},
did_initial_settle: true,
},
made_initial_step: true,
needs_settle: false,
extern_modules: [],
state_ready_to_run: false,
trace_decls: TraceModule {
name: "mod1",
children: [

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -265,247 +265,72 @@ Simulation {
..
},
},
uninitialized_inputs: {},
io_targets: {
Instance {
name: <simulator>::shift_register,
instantiated: Module {
name: shift_register,
..
},
}.cd: CompiledValue {
layout: CompiledTypeLayout {
ty: Bundle {
/* offset = 0 */
clk: Clock,
/* offset = 1 */
rst: SyncReset,
main_module: SimulationModuleState {
base_targets: [
Instance {
name: <simulator>::shift_register,
instantiated: Module {
name: shift_register,
..
},
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 2,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(shift_register: shift_register).shift_register::cd.clk",
ty: Clock,
},
SlotDebugData {
name: "InstantiatedModule(shift_register: shift_register).shift_register::cd.rst",
ty: SyncReset,
},
],
..
},
}.cd,
Instance {
name: <simulator>::shift_register,
instantiated: Module {
name: shift_register,
..
},
body: Bundle {
fields: [
CompiledBundleField {
offset: TypeIndex {
small_slots: StatePartIndex<SmallSlots>(0),
big_slots: StatePartIndex<BigSlots>(0),
},
ty: CompiledTypeLayout {
ty: Clock,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: Clock,
},
],
..
},
},
body: Scalar,
},
},
CompiledBundleField {
offset: TypeIndex {
small_slots: StatePartIndex<SmallSlots>(0),
big_slots: StatePartIndex<BigSlots>(1),
},
ty: CompiledTypeLayout {
ty: SyncReset,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: SyncReset,
},
],
..
},
},
body: Scalar,
},
},
],
}.d,
Instance {
name: <simulator>::shift_register,
instantiated: Module {
name: shift_register,
..
},
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 2 },
},
write: None,
},
Instance {
name: <simulator>::shift_register,
instantiated: Module {
name: shift_register,
..
},
}.cd.clk: CompiledValue {
layout: CompiledTypeLayout {
ty: Clock,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: Clock,
},
],
..
},
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 1 },
},
write: None,
},
Instance {
name: <simulator>::shift_register,
instantiated: Module {
name: shift_register,
..
},
}.cd.rst: CompiledValue {
layout: CompiledTypeLayout {
ty: SyncReset,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "",
ty: SyncReset,
},
],
..
},
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 1, len: 1 },
},
write: None,
},
Instance {
name: <simulator>::shift_register,
instantiated: Module {
name: shift_register,
..
},
}.d: CompiledValue {
layout: CompiledTypeLayout {
ty: Bool,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(shift_register: shift_register).shift_register::d",
ty: Bool,
},
],
..
},
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 2, len: 1 },
},
write: None,
},
Instance {
name: <simulator>::shift_register,
instantiated: Module {
name: shift_register,
..
},
}.q: CompiledValue {
layout: CompiledTypeLayout {
ty: Bool,
layout: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 1,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(shift_register: shift_register).shift_register::q",
ty: Bool,
},
],
..
},
},
body: Scalar,
},
range: TypeIndexRange {
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
big_slots: StatePartIndexRange<BigSlots> { start: 3, len: 1 },
},
write: None,
}.q,
],
uninitialized_ios: {},
io_targets: {
Instance {
name: <simulator>::shift_register,
instantiated: Module {
name: shift_register,
..
},
}.cd,
Instance {
name: <simulator>::shift_register,
instantiated: Module {
name: shift_register,
..
},
}.cd.clk,
Instance {
name: <simulator>::shift_register,
instantiated: Module {
name: shift_register,
..
},
}.cd.rst,
Instance {
name: <simulator>::shift_register,
instantiated: Module {
name: shift_register,
..
},
}.d,
Instance {
name: <simulator>::shift_register,
instantiated: Module {
name: shift_register,
..
},
}.q,
},
did_initial_settle: true,
},
made_initial_step: true,
needs_settle: false,
extern_modules: [],
state_ready_to_run: false,
trace_decls: TraceModule {
name: "shift_register",
children: [

View file

@ -0,0 +1,15 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
//! check that SimValue can't be interned, since equality may ignore types
use fayalite::{
intern::{Intern, Interned},
sim::value::SimValue,
};
fn f(v: SimValue<()>) -> Interned<SimValue<()>> {
Intern::intern_sized(v)
}
fn main() {}

View file

@ -0,0 +1,178 @@
error[E0277]: `Cell<util::alternating_cell::State>` cannot be shared between threads safely
--> tests/ui/simvalue_is_not_internable.rs:11:26
|
11 | fn f(v: SimValue<()>) -> Interned<SimValue<()>> {
| ^^^^^^^^^^^^^^^^^^^^^^ `Cell<util::alternating_cell::State>` cannot be shared between threads safely
|
= help: within `SimValue<()>`, the trait `Sync` is not implemented for `Cell<util::alternating_cell::State>`, which is required by `SimValue<()>: Sync`
= note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock`
note: required because it appears within the type `util::alternating_cell::AlternatingCell<value::SimValueInner<()>>`
--> src/util/alternating_cell.rs
|
| pub(crate) struct AlternatingCell<T: ?Sized> {
| ^^^^^^^^^^^^^^^
note: required because it appears within the type `SimValue<()>`
--> src/sim/value.rs
|
| pub struct SimValue<T: Type> {
| ^^^^^^^^
note: required by a bound in `fayalite::intern::Interned`
--> src/intern.rs
|
| pub struct Interned<T: ?Sized + 'static + Send + Sync> {
| ^^^^ required by this bound in `Interned`
error[E0277]: `UnsafeCell<value::SimValueInner<()>>` cannot be shared between threads safely
--> tests/ui/simvalue_is_not_internable.rs:11:26
|
11 | fn f(v: SimValue<()>) -> Interned<SimValue<()>> {
| ^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<value::SimValueInner<()>>` cannot be shared between threads safely
|
= help: within `SimValue<()>`, the trait `Sync` is not implemented for `UnsafeCell<value::SimValueInner<()>>`, which is required by `SimValue<()>: Sync`
note: required because it appears within the type `util::alternating_cell::AlternatingCell<value::SimValueInner<()>>`
--> src/util/alternating_cell.rs
|
| pub(crate) struct AlternatingCell<T: ?Sized> {
| ^^^^^^^^^^^^^^^
note: required because it appears within the type `SimValue<()>`
--> src/sim/value.rs
|
| pub struct SimValue<T: Type> {
| ^^^^^^^^
note: required by a bound in `fayalite::intern::Interned`
--> src/intern.rs
|
| pub struct Interned<T: ?Sized + 'static + Send + Sync> {
| ^^^^ required by this bound in `Interned`
error[E0277]: the trait bound `SimValue<()>: Intern` is not satisfied
--> tests/ui/simvalue_is_not_internable.rs:12:26
|
12 | Intern::intern_sized(v)
| -------------------- ^ the trait `Hash` is not implemented for `SimValue<()>`, which is required by `SimValue<()>: Intern`
| |
| required by a bound introduced by this call
|
= help: the following other types implement trait `Intern`:
BitSlice
[T]
str
= note: required for `SimValue<()>` to implement `Intern`
error[E0277]: the trait bound `SimValue<()>: Intern` is not satisfied
--> tests/ui/simvalue_is_not_internable.rs:12:26
|
12 | Intern::intern_sized(v)
| -------------------- ^ the trait `std::cmp::Eq` is not implemented for `SimValue<()>`, which is required by `SimValue<()>: Intern`
| |
| required by a bound introduced by this call
|
= help: the following other types implement trait `Intern`:
BitSlice
[T]
str
= note: required for `SimValue<()>` to implement `Intern`
error[E0277]: `Cell<util::alternating_cell::State>` cannot be shared between threads safely
--> tests/ui/simvalue_is_not_internable.rs:12:26
|
12 | Intern::intern_sized(v)
| -------------------- ^ `Cell<util::alternating_cell::State>` cannot be shared between threads safely
| |
| required by a bound introduced by this call
|
= help: within `SimValue<()>`, the trait `Sync` is not implemented for `Cell<util::alternating_cell::State>`, which is required by `SimValue<()>: Sync`
= note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock`
note: required because it appears within the type `util::alternating_cell::AlternatingCell<value::SimValueInner<()>>`
--> src/util/alternating_cell.rs
|
| pub(crate) struct AlternatingCell<T: ?Sized> {
| ^^^^^^^^^^^^^^^
note: required because it appears within the type `SimValue<()>`
--> src/sim/value.rs
|
| pub struct SimValue<T: Type> {
| ^^^^^^^^
note: required by a bound in `intern_sized`
--> src/intern.rs
|
| pub trait Intern: Any + Send + Sync {
| ^^^^ required by this bound in `Intern::intern_sized`
| fn intern(&self) -> Interned<Self>;
| fn intern_sized(self) -> Interned<Self>
| ------------ required by a bound in this associated function
error[E0277]: `UnsafeCell<value::SimValueInner<()>>` cannot be shared between threads safely
--> tests/ui/simvalue_is_not_internable.rs:12:26
|
12 | Intern::intern_sized(v)
| -------------------- ^ `UnsafeCell<value::SimValueInner<()>>` cannot be shared between threads safely
| |
| required by a bound introduced by this call
|
= help: within `SimValue<()>`, the trait `Sync` is not implemented for `UnsafeCell<value::SimValueInner<()>>`, which is required by `SimValue<()>: Sync`
note: required because it appears within the type `util::alternating_cell::AlternatingCell<value::SimValueInner<()>>`
--> src/util/alternating_cell.rs
|
| pub(crate) struct AlternatingCell<T: ?Sized> {
| ^^^^^^^^^^^^^^^
note: required because it appears within the type `SimValue<()>`
--> src/sim/value.rs
|
| pub struct SimValue<T: Type> {
| ^^^^^^^^
note: required by a bound in `intern_sized`
--> src/intern.rs
|
| pub trait Intern: Any + Send + Sync {
| ^^^^ required by this bound in `Intern::intern_sized`
| fn intern(&self) -> Interned<Self>;
| fn intern_sized(self) -> Interned<Self>
| ------------ required by a bound in this associated function
error[E0277]: `Cell<util::alternating_cell::State>` cannot be shared between threads safely
--> tests/ui/simvalue_is_not_internable.rs:12:5
|
12 | Intern::intern_sized(v)
| ^^^^^^^^^^^^^^^^^^^^^^^ `Cell<util::alternating_cell::State>` cannot be shared between threads safely
|
= help: within `SimValue<()>`, the trait `Sync` is not implemented for `Cell<util::alternating_cell::State>`, which is required by `SimValue<()>: Sync`
= note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock`
note: required because it appears within the type `util::alternating_cell::AlternatingCell<value::SimValueInner<()>>`
--> src/util/alternating_cell.rs
|
| pub(crate) struct AlternatingCell<T: ?Sized> {
| ^^^^^^^^^^^^^^^
note: required because it appears within the type `SimValue<()>`
--> src/sim/value.rs
|
| pub struct SimValue<T: Type> {
| ^^^^^^^^
note: required by a bound in `fayalite::intern::Interned`
--> src/intern.rs
|
| pub struct Interned<T: ?Sized + 'static + Send + Sync> {
| ^^^^ required by this bound in `Interned`
error[E0277]: `UnsafeCell<value::SimValueInner<()>>` cannot be shared between threads safely
--> tests/ui/simvalue_is_not_internable.rs:12:5
|
12 | Intern::intern_sized(v)
| ^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<value::SimValueInner<()>>` cannot be shared between threads safely
|
= help: within `SimValue<()>`, the trait `Sync` is not implemented for `UnsafeCell<value::SimValueInner<()>>`, which is required by `SimValue<()>: Sync`
note: required because it appears within the type `util::alternating_cell::AlternatingCell<value::SimValueInner<()>>`
--> src/util/alternating_cell.rs
|
| pub(crate) struct AlternatingCell<T: ?Sized> {
| ^^^^^^^^^^^^^^^
note: required because it appears within the type `SimValue<()>`
--> src/sim/value.rs
|
| pub struct SimValue<T: Type> {
| ^^^^^^^^
note: required by a bound in `fayalite::intern::Interned`
--> src/intern.rs
|
| pub struct Interned<T: ?Sized + 'static + Send + Sync> {
| ^^^^ required by this bound in `Interned`

View file

@ -49,7 +49,8 @@
"AsyncReset": "Visible",
"SyncReset": "Visible",
"Reset": "Visible",
"Clock": "Visible"
"Clock": "Visible",
"PhantomConst": "Visible"
}
},
"Bundle": {
@ -159,7 +160,8 @@
"data": {
"$kind": "Struct",
"verilog_name": "Visible",
"parameters": "Visible"
"parameters": "Visible",
"simulation": "Visible"
}
},
"ExternModuleParameter": {
@ -1262,6 +1264,17 @@
"ArrayElement": "Visible",
"DynArrayElement": "Visible"
}
},
"PhantomConst": {
"data": {
"$kind": "Opaque"
},
"generics": "<T: ?Sized + crate::phantom_const::PhantomConstValue>"
},
"ExternModuleSimulation": {
"data": {
"$kind": "Opaque"
}
}
}
}