forked from libre-chip/fayalite
Compare commits
10 commits
a115585d5a
...
57aae7b7fb
Author | SHA1 | Date | |
---|---|---|---|
57aae7b7fb | |||
6929352be7 | |||
62058dc141 | |||
c4b6a0fee6 | |||
9092e45447 | |||
a40eaaa2da | |||
5028401a5a | |||
e0f978fbb6 | |||
ec3a61513b | |||
fdc73b5f3b |
34 changed files with 7926 additions and 1372 deletions
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
@ -125,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,
|
||||
|
@ -427,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;
|
||||
|
@ -523,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>
|
||||
|
@ -565,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;
|
||||
|
@ -615,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]
|
||||
|
@ -623,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<
|
||||
|
@ -660,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
|
||||
|
@ -691,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<
|
||||
|
@ -735,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
|
||||
|
@ -765,26 +951,81 @@ 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 where_clause =
|
||||
let mut expr_where_clause =
|
||||
Generics::from(generics)
|
||||
.where_clause
|
||||
.unwrap_or_else(|| syn::WhereClause {
|
||||
where_token: Token,
|
||||
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();
|
||||
where_clause
|
||||
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)
|
||||
});
|
||||
|
@ -792,9 +1033,13 @@ impl ToTokens for ParsedBundle {
|
|||
::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)
|
||||
};
|
||||
|
@ -802,6 +1047,9 @@ impl ToTokens for ParsedBundle {
|
|||
::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)&*
|
||||
};
|
||||
|
@ -812,7 +1060,7 @@ impl ToTokens for ParsedBundle {
|
|||
quote_spanned! {span=>
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::expr::ops::ExprPartialEq<Self> for #target #type_generics
|
||||
#where_clause
|
||||
#expr_where_clause
|
||||
{
|
||||
fn cmp_eq(
|
||||
__lhs: ::fayalite::expr::Expr<Self>,
|
||||
|
@ -827,6 +1075,17 @@ impl ToTokens for ParsedBundle {
|
|||
#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);
|
||||
}
|
||||
|
@ -865,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
|
||||
|
@ -887,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
|
||||
{
|
||||
|
|
|
@ -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 {
|
||||
|
@ -190,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,
|
||||
})
|
||||
}
|
||||
|
@ -207,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 {
|
||||
|
@ -409,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,
|
||||
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),
|
||||
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))),
|
||||
),
|
||||
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;
|
||||
for (index, ParsedVariant { ident, field, .. }) in variants.iter().enumerate() {
|
||||
if let Some(ParsedVariantField { ty, .. }) = field {
|
||||
|
@ -435,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=>
|
||||
|
@ -453,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);
|
||||
|
@ -534,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]
|
||||
|
@ -542,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>;
|
||||
|
@ -574,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) {
|
||||
|
@ -597,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) {
|
||||
|
@ -634,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
|
||||
|
@ -652,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);
|
||||
}
|
||||
|
|
|
@ -93,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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -9,12 +9,16 @@ use crate::{
|
|||
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 bitvec::slice::BitSlice;
|
||||
use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::{iter::FusedIterator, ops::Index};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
|
@ -94,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()),
|
||||
|
@ -142,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>>;
|
||||
|
@ -178,9 +189,92 @@ 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> {
|
||||
|
@ -247,6 +341,18 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
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>;
|
||||
|
|
|
@ -8,18 +8,19 @@ use crate::{
|
|||
},
|
||||
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,
|
||||
},
|
||||
};
|
||||
use bitvec::vec::BitVec;
|
||||
use bitvec::{slice::BitSlice, vec::BitVec};
|
||||
use hashbrown::HashMap;
|
||||
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,
|
||||
|
@ -216,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(
|
||||
|
@ -239,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> {
|
||||
|
@ -247,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;
|
||||
|
||||
|
@ -353,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>;
|
||||
|
@ -391,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>,)*)>;
|
||||
|
@ -441,77 +563,72 @@ 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,)*) {
|
||||
|
@ -537,6 +654,15 @@ macro_rules! impl_tuples {
|
|||
.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
|
||||
}
|
||||
}
|
||||
};
|
||||
([$($lhs:tt)*] [$rhs_first:tt $($rhs:tt)*]) => {
|
||||
impl_tuples!([$($lhs)*] []);
|
||||
|
@ -564,6 +690,7 @@ impl_tuples! {
|
|||
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>;
|
||||
|
@ -596,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>);
|
||||
|
@ -643,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,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -7,19 +7,25 @@ use crate::{
|
|||
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,
|
||||
},
|
||||
};
|
||||
use bitvec::{order::Lsb0, slice::BitSlice, view::BitView};
|
||||
use hashbrown::HashMap;
|
||||
use std::{convert::Infallible, fmt, iter::FusedIterator};
|
||||
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>,
|
||||
|
@ -152,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,
|
||||
|
@ -248,6 +260,7 @@ pub trait EnumType:
|
|||
MatchVariantsIter = EnumMatchVariantsIter<Self>,
|
||||
>
|
||||
{
|
||||
type SimBuilder: From<Self>;
|
||||
fn variants(&self) -> Interned<[EnumVariant]>;
|
||||
fn match_activate_scope(
|
||||
v: Self::MatchVariantAndInactiveScope,
|
||||
|
@ -310,7 +323,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) {
|
||||
|
@ -325,6 +349,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>;
|
||||
|
@ -355,6 +380,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]
|
||||
|
@ -417,6 +745,25 @@ impl<Lhs: Type + ExprPartialEq<Rhs>, Rhs: Type> ExprPartialEq<HdlOption<Rhs>> fo
|
|||
}
|
||||
}
|
||||
|
||||
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()
|
||||
|
|
|
@ -700,6 +700,7 @@ impl<T: ToExpr + ?Sized> CastToBits for T {
|
|||
}
|
||||
|
||||
pub trait CastBitsTo {
|
||||
#[track_caller]
|
||||
fn cast_bits_to<T: Type>(&self, ty: T) -> Expr<T>;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,24 +2,31 @@
|
|||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::{
|
||||
array::ArrayType,
|
||||
expr::{
|
||||
target::{GetTarget, Target},
|
||||
Expr, NotALiteralExpr, ToExpr, ToLiteralBits,
|
||||
},
|
||||
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,
|
||||
};
|
||||
|
||||
|
@ -49,6 +56,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 +77,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 +90,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 +98,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 +137,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 +172,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 +195,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 +214,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)]
|
||||
|
@ -263,6 +611,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> {
|
||||
|
@ -287,6 +641,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 +652,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,6 +661,26 @@ 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> {
|
||||
|
@ -320,6 +695,46 @@ macro_rules! impl_int {
|
|||
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;
|
||||
|
||||
|
@ -331,13 +746,13 @@ macro_rules! impl_int {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Clone, Eq, Hash)]
|
||||
pub struct $value<Width: Size = DynSize> {
|
||||
bits: Arc<BitVec>,
|
||||
_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,9 +766,41 @@ 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> 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<LhsWidth: Size, RhsWidth: Size> PartialEq<$value<RhsWidth>> for $value<LhsWidth> {
|
||||
fn eq(&self, other: &$value<RhsWidth>) -> bool {
|
||||
self.to_bigint() == other.to_bigint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<LhsWidth: Size, RhsWidth: Size> PartialOrd<$value<RhsWidth>> for $value<LhsWidth> {
|
||||
fn partial_cmp(&self, other: &$value<RhsWidth>) -> Option<std::cmp::Ordering> {
|
||||
Some(self.to_bigint().cmp(&other.to_bigint()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -401,6 +848,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> {
|
||||
|
@ -583,11 +1033,13 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed {
|
|||
+ Ord
|
||||
+ 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
|
||||
|
@ -660,9 +1112,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())
|
||||
|
@ -702,7 +1157,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 {}
|
||||
|
@ -734,6 +1189,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 {
|
||||
|
@ -748,6 +1207,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
|
||||
|
@ -765,6 +1225,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 {
|
||||
|
@ -811,4 +1283,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" });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 __;
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
expr::{
|
||||
ops::{ExprPartialEq, ExprPartialOrd},
|
||||
|
@ -10,14 +8,25 @@ use crate::{
|
|||
},
|
||||
int::Bool,
|
||||
intern::{Intern, Interned, InternedCompare, LazyInterned, Memoize},
|
||||
sim::value::{SimValue, SimValuePartialEq, ToSimValue, ToSimValueWithType},
|
||||
source_location::SourceLocation,
|
||||
ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties},
|
||||
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)]
|
||||
|
@ -113,6 +122,20 @@ pub struct PhantomConst<T: ?Sized + PhantomConstValue = PhantomConstCanonicalVal
|
|||
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()
|
||||
|
@ -199,17 +222,6 @@ impl<T: ?Sized + PhantomConstValue> Memoize for PhantomConstCanonicalMemoize<T,
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + PhantomConstValue> PhantomConst<T>
|
||||
where
|
||||
Interned<T>: Default,
|
||||
{
|
||||
pub const fn default() -> Self {
|
||||
PhantomConst {
|
||||
value: LazyInterned::new_lazy(&Interned::<T>::default),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + PhantomConstValue> PhantomConst<T> {
|
||||
pub fn new(value: Interned<T>) -> Self {
|
||||
Self {
|
||||
|
@ -246,6 +258,7 @@ impl<T: ?Sized + PhantomConstValue> PhantomConst<T> {
|
|||
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 {
|
||||
|
@ -266,18 +279,70 @@ impl<T: ?Sized + PhantomConstValue> Type for PhantomConst<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());
|
||||
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 = Self::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));
|
||||
|
@ -311,3 +376,30 @@ impl<T: ?Sized + PhantomConstValue> ExprPartialOrd<Self> for PhantomConst<T> {
|
|||
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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ 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,
|
||||
|
@ -29,9 +29,15 @@ pub use crate::{
|
|||
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};
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -14,7 +14,7 @@ use crate::{
|
|||
},
|
||||
ExprEnum, Flow, ToLiteralBits,
|
||||
},
|
||||
int::{BoolOrIntType, IntType, SIntValue, UIntValue},
|
||||
int::{BoolOrIntType, UIntValue},
|
||||
intern::{
|
||||
Intern, Interned, InternedCompare, Memoize, PtrEqWithTypeId, SupportsPtrEqWithTypeId,
|
||||
},
|
||||
|
@ -38,6 +38,7 @@ use crate::{
|
|||
TypeIndexRange, TypeLayout, TypeLen, TypeParts,
|
||||
},
|
||||
time::{SimDuration, SimInstant},
|
||||
value::SimValue,
|
||||
},
|
||||
ty::StaticType,
|
||||
util::{BitSliceWriteWithBase, DebugAsDisplay},
|
||||
|
@ -72,6 +73,7 @@ use std::{
|
|||
|
||||
mod interpreter;
|
||||
pub mod time;
|
||||
pub mod value;
|
||||
pub mod vcd;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
||||
|
@ -5904,635 +5906,6 @@ impl SimTraceKind {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub struct SimValue<T: Type> {
|
||||
ty: T,
|
||||
bits: BitVec,
|
||||
}
|
||||
|
||||
impl<T: Type> fmt::Debug for SimValue<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("SimValue")
|
||||
.field("ty", &self.ty)
|
||||
.field("bits", &BitSliceWriteWithBase(&self.bits))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl SimValue<CanonicalType> {
|
||||
#[track_caller]
|
||||
fn to_expr_impl(ty: CanonicalType, bits: &BitSlice) -> Expr<CanonicalType> {
|
||||
match ty {
|
||||
CanonicalType::UInt(_) => Expr::canonical(<UInt>::bits_to_expr(Cow::Borrowed(bits))),
|
||||
CanonicalType::SInt(_) => Expr::canonical(<SInt>::bits_to_expr(Cow::Borrowed(bits))),
|
||||
CanonicalType::Bool(_) => Expr::canonical(Bool::bits_to_expr(Cow::Borrowed(bits))),
|
||||
CanonicalType::Array(ty) => {
|
||||
let element_bit_width = ty.element().bit_width();
|
||||
Expr::<Array>::canonical(
|
||||
crate::expr::ops::ArrayLiteral::new(
|
||||
ty.element(),
|
||||
(0..ty.len())
|
||||
.map(|array_index| {
|
||||
let start = element_bit_width * array_index;
|
||||
let end = start + element_bit_width;
|
||||
Self::to_expr_impl(ty.element(), &bits[start..end])
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
.to_expr(),
|
||||
)
|
||||
}
|
||||
CanonicalType::Enum(ty) => {
|
||||
let discriminant_bit_width = ty.discriminant_bit_width();
|
||||
let mut variant_index = [0; mem::size_of::<usize>()];
|
||||
variant_index.view_bits_mut::<Lsb0>()[0..discriminant_bit_width]
|
||||
.clone_from_bitslice(&bits[..discriminant_bit_width]);
|
||||
let variant_index = usize::from_le_bytes(variant_index);
|
||||
if let Some(variant) = ty.variants().get(variant_index) {
|
||||
let data_bit_width = variant.ty.map_or(0, CanonicalType::bit_width);
|
||||
Expr::canonical(
|
||||
crate::expr::ops::EnumLiteral::new_by_index(
|
||||
ty,
|
||||
variant_index,
|
||||
variant.ty.map(|ty| {
|
||||
Self::to_expr_impl(
|
||||
ty,
|
||||
&bits[discriminant_bit_width
|
||||
..discriminant_bit_width + data_bit_width],
|
||||
)
|
||||
}),
|
||||
)
|
||||
.to_expr(),
|
||||
)
|
||||
} else {
|
||||
Expr::canonical(<UInt>::bits_to_expr(Cow::Borrowed(bits)).cast_bits_to(ty))
|
||||
}
|
||||
}
|
||||
CanonicalType::Bundle(ty) => Expr::canonical(
|
||||
crate::expr::ops::BundleLiteral::new(
|
||||
ty,
|
||||
ty.fields()
|
||||
.iter()
|
||||
.zip(ty.field_offsets().iter())
|
||||
.map(|(field, &field_offset)| {
|
||||
Self::to_expr_impl(
|
||||
field.ty,
|
||||
&bits[field_offset..field_offset + field.ty.bit_width()],
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
.to_expr(),
|
||||
),
|
||||
CanonicalType::AsyncReset(ty) => {
|
||||
Expr::canonical(Bool::bits_to_expr(Cow::Borrowed(bits)).cast_to(ty))
|
||||
}
|
||||
CanonicalType::SyncReset(ty) => {
|
||||
Expr::canonical(Bool::bits_to_expr(Cow::Borrowed(bits)).cast_to(ty))
|
||||
}
|
||||
CanonicalType::Reset(_) => panic!(
|
||||
"can't convert SimValue<Reset> to Expr<Reset> -- \
|
||||
can't deduce whether reset value should be sync or async"
|
||||
),
|
||||
CanonicalType::Clock(ty) => {
|
||||
Expr::canonical(Bool::bits_to_expr(Cow::Borrowed(bits)).cast_to(ty))
|
||||
}
|
||||
CanonicalType::PhantomConst(ty) => Expr::canonical(ty.to_expr()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type> ToExpr for SimValue<T> {
|
||||
type Type = T;
|
||||
|
||||
#[track_caller]
|
||||
fn to_expr(&self) -> Expr<Self::Type> {
|
||||
Expr::from_canonical(SimValue::to_expr_impl(self.ty.canonical(), &self.bits))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type> ToSimValue<T> for SimValue<T> {
|
||||
#[track_caller]
|
||||
fn to_sim_value(&self, ty: T) -> SimValue<T> {
|
||||
assert_eq!(self.ty, ty);
|
||||
self.clone()
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn into_sim_value(self, ty: T) -> SimValue<T> {
|
||||
assert_eq!(self.ty, ty);
|
||||
self
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn box_into_sim_value(self: Box<Self>, ty: T) -> SimValue<T> {
|
||||
assert_eq!(self.ty, ty);
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type> ToSimValue<T> for BitVec {
|
||||
#[track_caller]
|
||||
fn to_sim_value(&self, ty: T) -> SimValue<T> {
|
||||
self.clone().into_sim_value(ty)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn into_sim_value(self, ty: T) -> SimValue<T> {
|
||||
assert_eq!(ty.canonical().bit_width(), self.len());
|
||||
SimValue { ty, bits: self }
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn box_into_sim_value(self: Box<Self>, ty: T) -> SimValue<T> {
|
||||
Self::into_sim_value(*self, ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type> ToSimValue<T> for bitvec::boxed::BitBox {
|
||||
#[track_caller]
|
||||
fn to_sim_value(&self, ty: T) -> SimValue<T> {
|
||||
self.clone().into_sim_value(ty)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn into_sim_value(self, ty: T) -> SimValue<T> {
|
||||
assert_eq!(ty.canonical().bit_width(), self.len());
|
||||
SimValue {
|
||||
ty,
|
||||
bits: self.into_bitvec(),
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn box_into_sim_value(self: Box<Self>, ty: T) -> SimValue<T> {
|
||||
Self::into_sim_value(*self, ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type> ToSimValue<T> for BitSlice {
|
||||
#[track_caller]
|
||||
fn to_sim_value(&self, ty: T) -> SimValue<T> {
|
||||
assert_eq!(ty.canonical().bit_width(), self.len());
|
||||
SimValue {
|
||||
ty,
|
||||
bits: self.to_bitvec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type> SimValue<T> {
|
||||
pub fn ty(&self) -> T {
|
||||
self.ty
|
||||
}
|
||||
pub fn bits(&self) -> &BitSlice {
|
||||
&self.bits
|
||||
}
|
||||
pub fn into_bits(self) -> BitVec {
|
||||
self.bits
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn from_canonical(v: SimValue<CanonicalType>) -> Self {
|
||||
Self {
|
||||
ty: T::from_canonical(v.ty),
|
||||
bits: v.bits,
|
||||
}
|
||||
}
|
||||
pub fn into_canonical(self) -> SimValue<CanonicalType> {
|
||||
SimValue {
|
||||
ty: self.ty.canonical(),
|
||||
bits: self.bits,
|
||||
}
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn from_dyn_int(v: SimValue<T::Dyn>) -> Self
|
||||
where
|
||||
T: IntType,
|
||||
{
|
||||
Self {
|
||||
ty: T::from_dyn_int(v.ty),
|
||||
bits: v.bits,
|
||||
}
|
||||
}
|
||||
pub fn into_dyn_int(self) -> SimValue<T::Dyn>
|
||||
where
|
||||
T: IntType,
|
||||
{
|
||||
SimValue {
|
||||
ty: self.ty.as_dyn_int(),
|
||||
bits: self.bits,
|
||||
}
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn from_bundle(v: SimValue<Bundle>) -> Self
|
||||
where
|
||||
T: BundleType,
|
||||
{
|
||||
Self {
|
||||
ty: T::from_canonical(CanonicalType::Bundle(v.ty)),
|
||||
bits: v.bits,
|
||||
}
|
||||
}
|
||||
pub fn into_bundle(self) -> SimValue<Bundle>
|
||||
where
|
||||
T: BundleType,
|
||||
{
|
||||
SimValue {
|
||||
ty: Bundle::from_canonical(self.ty.canonical()),
|
||||
bits: self.bits,
|
||||
}
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn from_enum(v: SimValue<Enum>) -> Self
|
||||
where
|
||||
T: EnumType,
|
||||
{
|
||||
Self {
|
||||
ty: T::from_canonical(CanonicalType::Enum(v.ty)),
|
||||
bits: v.bits,
|
||||
}
|
||||
}
|
||||
pub fn into_enum(self) -> SimValue<Enum>
|
||||
where
|
||||
T: EnumType,
|
||||
{
|
||||
SimValue {
|
||||
ty: Enum::from_canonical(self.ty.canonical()),
|
||||
bits: self.bits,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ToSimValue<T: Type> {
|
||||
#[track_caller]
|
||||
fn to_sim_value(&self, ty: T) -> SimValue<T>;
|
||||
#[track_caller]
|
||||
fn into_sim_value(self, ty: T) -> SimValue<T>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
self.to_sim_value(ty)
|
||||
}
|
||||
#[track_caller]
|
||||
fn box_into_sim_value(self: Box<Self>, ty: T) -> SimValue<T> {
|
||||
self.to_sim_value(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl<This: ?Sized + ToSimValue<T>, T: Type> ToSimValue<T> for &'_ This {
|
||||
#[track_caller]
|
||||
fn to_sim_value(&self, ty: T) -> SimValue<T> {
|
||||
This::to_sim_value(self, ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl<This: ?Sized + ToSimValue<T>, T: Type> ToSimValue<T> for &'_ mut This {
|
||||
#[track_caller]
|
||||
fn to_sim_value(&self, ty: T) -> SimValue<T> {
|
||||
This::to_sim_value(self, ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl<This: ?Sized + ToSimValue<T>, T: Type> ToSimValue<T> for Box<This> {
|
||||
#[track_caller]
|
||||
fn to_sim_value(&self, ty: T) -> SimValue<T> {
|
||||
This::to_sim_value(self, ty)
|
||||
}
|
||||
#[track_caller]
|
||||
fn into_sim_value(self, ty: T) -> SimValue<T> {
|
||||
This::box_into_sim_value(self, ty)
|
||||
}
|
||||
#[track_caller]
|
||||
fn box_into_sim_value(self: Box<Self>, ty: T) -> SimValue<T> {
|
||||
This::box_into_sim_value(*self, ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl<This: ?Sized + ToSimValue<T> + Send + Sync + 'static, T: Type> ToSimValue<T>
|
||||
for Interned<This>
|
||||
{
|
||||
#[track_caller]
|
||||
fn to_sim_value(&self, ty: T) -> SimValue<T> {
|
||||
This::to_sim_value(self, ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type, Len: Size> SimValue<ArrayType<T, Len>> {
|
||||
#[track_caller]
|
||||
pub fn from_array_elements<
|
||||
I: IntoIterator<Item: ToSimValue<T>, IntoIter: ExactSizeIterator>,
|
||||
>(
|
||||
elements: I,
|
||||
ty: ArrayType<T, Len>,
|
||||
) -> Self {
|
||||
let mut iter = elements.into_iter();
|
||||
assert_eq!(iter.len(), ty.len());
|
||||
let Some(first) = iter.next() else {
|
||||
return SimValue {
|
||||
ty,
|
||||
bits: BitVec::new(),
|
||||
};
|
||||
};
|
||||
let SimValue {
|
||||
ty: element_ty,
|
||||
mut bits,
|
||||
} = first.into_sim_value(ty.element());
|
||||
assert_eq!(element_ty, ty.element());
|
||||
bits.reserve(ty.type_properties().bit_width - bits.len());
|
||||
for element in iter {
|
||||
let SimValue {
|
||||
ty: element_ty,
|
||||
bits: element_bits,
|
||||
} = element.into_sim_value(ty.element());
|
||||
assert_eq!(element_ty, ty.element());
|
||||
bits.extend_from_bitslice(&element_bits);
|
||||
}
|
||||
SimValue { ty, bits }
|
||||
}
|
||||
}
|
||||
|
||||
impl<Element: ToSimValue<T>, T: Type> ToSimValue<Array<T>> for [Element] {
|
||||
#[track_caller]
|
||||
fn to_sim_value(&self, ty: Array<T>) -> SimValue<Array<T>> {
|
||||
SimValue::from_array_elements(self, ty)
|
||||
}
|
||||
#[track_caller]
|
||||
fn box_into_sim_value(self: Box<Self>, ty: Array<T>) -> SimValue<Array<T>> {
|
||||
SimValue::from_array_elements(self, ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Element: ToSimValue<CanonicalType>> ToSimValue<CanonicalType> for [Element] {
|
||||
#[track_caller]
|
||||
fn to_sim_value(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
|
||||
SimValue::from_array_elements(self, <Array>::from_canonical(ty)).into_canonical()
|
||||
}
|
||||
#[track_caller]
|
||||
fn box_into_sim_value(self: Box<Self>, ty: CanonicalType) -> SimValue<CanonicalType> {
|
||||
SimValue::from_array_elements(self, <Array>::from_canonical(ty)).into_canonical()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Element: ToSimValue<T>, T: Type, const N: usize> ToSimValue<Array<T, N>> for [Element; N]
|
||||
where
|
||||
ConstUsize<N>: KnownSize,
|
||||
{
|
||||
#[track_caller]
|
||||
fn to_sim_value(&self, ty: Array<T, N>) -> SimValue<Array<T, N>> {
|
||||
SimValue::from_array_elements(self, ty)
|
||||
}
|
||||
#[track_caller]
|
||||
fn into_sim_value(self, ty: Array<T, N>) -> SimValue<Array<T, N>> {
|
||||
SimValue::from_array_elements(self, ty)
|
||||
}
|
||||
#[track_caller]
|
||||
fn box_into_sim_value(self: Box<Self>, ty: Array<T, N>) -> SimValue<Array<T, N>> {
|
||||
SimValue::from_array_elements(<Vec<Element> as From<Box<[Element]>>>::from(self), ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Element: ToSimValue<T>, T: Type, const N: usize> ToSimValue<Array<T>> for [Element; N]
|
||||
where
|
||||
ConstUsize<N>: KnownSize,
|
||||
{
|
||||
#[track_caller]
|
||||
fn to_sim_value(&self, ty: Array<T>) -> SimValue<Array<T>> {
|
||||
SimValue::from_array_elements(self, ty)
|
||||
}
|
||||
#[track_caller]
|
||||
fn into_sim_value(self, ty: Array<T>) -> SimValue<Array<T>> {
|
||||
SimValue::from_array_elements(self, ty)
|
||||
}
|
||||
#[track_caller]
|
||||
fn box_into_sim_value(self: Box<Self>, ty: Array<T>) -> SimValue<Array<T>> {
|
||||
SimValue::from_array_elements(<Vec<Element> as From<Box<[Element]>>>::from(self), ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Element: ToSimValue<CanonicalType>, const N: usize> ToSimValue<CanonicalType>
|
||||
for [Element; N]
|
||||
{
|
||||
#[track_caller]
|
||||
fn to_sim_value(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
|
||||
SimValue::from_array_elements(self, <Array>::from_canonical(ty)).into_canonical()
|
||||
}
|
||||
#[track_caller]
|
||||
fn into_sim_value(self, ty: CanonicalType) -> SimValue<CanonicalType> {
|
||||
SimValue::from_array_elements(self, <Array>::from_canonical(ty)).into_canonical()
|
||||
}
|
||||
#[track_caller]
|
||||
fn box_into_sim_value(self: Box<Self>, ty: CanonicalType) -> SimValue<CanonicalType> {
|
||||
SimValue::from_array_elements(
|
||||
<Vec<Element> as From<Box<[Element]>>>::from(self),
|
||||
<Array>::from_canonical(ty),
|
||||
)
|
||||
.into_canonical()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Element: ToSimValue<T>, T: Type> ToSimValue<Array<T>> for Vec<Element> {
|
||||
#[track_caller]
|
||||
fn to_sim_value(&self, ty: Array<T>) -> SimValue<Array<T>> {
|
||||
SimValue::from_array_elements(self, ty)
|
||||
}
|
||||
#[track_caller]
|
||||
fn into_sim_value(self, ty: Array<T>) -> SimValue<Array<T>> {
|
||||
SimValue::from_array_elements(self, ty)
|
||||
}
|
||||
#[track_caller]
|
||||
fn box_into_sim_value(self: Box<Self>, ty: Array<T>) -> SimValue<Array<T>> {
|
||||
SimValue::from_array_elements(*self, ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Element: ToSimValue<CanonicalType>> ToSimValue<CanonicalType> for Vec<Element> {
|
||||
#[track_caller]
|
||||
fn to_sim_value(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
|
||||
SimValue::from_array_elements(self, <Array>::from_canonical(ty)).into_canonical()
|
||||
}
|
||||
#[track_caller]
|
||||
fn into_sim_value(self, ty: CanonicalType) -> SimValue<CanonicalType> {
|
||||
SimValue::from_array_elements(self, <Array>::from_canonical(ty)).into_canonical()
|
||||
}
|
||||
#[track_caller]
|
||||
fn box_into_sim_value(self: Box<Self>, ty: CanonicalType) -> SimValue<CanonicalType> {
|
||||
SimValue::from_array_elements(*self, <Array>::from_canonical(ty)).into_canonical()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type> ToSimValue<T> for Expr<T> {
|
||||
#[track_caller]
|
||||
fn to_sim_value(&self, ty: T) -> SimValue<T> {
|
||||
assert_eq!(Expr::ty(*self), ty);
|
||||
SimValue {
|
||||
ty,
|
||||
bits: self
|
||||
.to_literal_bits()
|
||||
.expect("must be a literal expression")
|
||||
.to_bitvec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_to_sim_value_for_bool_like {
|
||||
($ty:ident) => {
|
||||
impl ToSimValue<$ty> for bool {
|
||||
fn to_sim_value(&self, ty: $ty) -> SimValue<$ty> {
|
||||
SimValue {
|
||||
ty,
|
||||
bits: BitVec::repeat(*self, 1),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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 ToSimValue<CanonicalType> for bool {
|
||||
#[track_caller]
|
||||
fn to_sim_value(&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 {
|
||||
ty,
|
||||
bits: BitVec::repeat(*self, 1),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_to_sim_value_for_primitive_int {
|
||||
($prim:ident) => {
|
||||
impl ToSimValue<<$prim as ToExpr>::Type> for $prim {
|
||||
#[track_caller]
|
||||
fn to_sim_value(
|
||||
&self,
|
||||
ty: <$prim as ToExpr>::Type,
|
||||
) -> SimValue<<$prim as ToExpr>::Type> {
|
||||
SimValue {
|
||||
ty,
|
||||
bits: <<$prim as ToExpr>::Type as BoolOrIntType>::le_bytes_to_bits_wrapping(
|
||||
&self.to_le_bytes(),
|
||||
ty.width(),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToSimValue<<<$prim as ToExpr>::Type as IntType>::Dyn> for $prim {
|
||||
#[track_caller]
|
||||
fn to_sim_value(
|
||||
&self,
|
||||
ty: <<$prim as ToExpr>::Type as IntType>::Dyn,
|
||||
) -> SimValue<<<$prim as ToExpr>::Type as IntType>::Dyn> {
|
||||
SimValue {
|
||||
ty,
|
||||
bits: <<$prim as ToExpr>::Type as BoolOrIntType>::le_bytes_to_bits_wrapping(
|
||||
&self.to_le_bytes(),
|
||||
ty.width(),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToSimValue<CanonicalType> for $prim {
|
||||
#[track_caller]
|
||||
fn to_sim_value(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
|
||||
let ty: <<$prim as ToExpr>::Type as IntType>::Dyn = Type::from_canonical(ty);
|
||||
self.to_sim_value(ty).into_canonical()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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: KnownSize> ToSimValue<$IntType<Width>> for $IntValue<Width> {
|
||||
fn to_sim_value(&self, ty: $IntType<Width>) -> SimValue<$IntType<Width>> {
|
||||
self.bits().to_bitvec().into_sim_value(ty)
|
||||
}
|
||||
|
||||
fn into_sim_value(self, ty: $IntType<Width>) -> SimValue<$IntType<Width>> {
|
||||
Arc::try_unwrap(self.into_bits())
|
||||
.unwrap_or_else(|v: Arc<BitVec>| v.to_bitvec())
|
||||
.into_sim_value(ty)
|
||||
}
|
||||
|
||||
fn box_into_sim_value(
|
||||
self: Box<Self>,
|
||||
ty: $IntType<Width>,
|
||||
) -> SimValue<$IntType<Width>> {
|
||||
Self::into_sim_value(*self, ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Width: Size> ToSimValue<$Int> for $IntValue<Width> {
|
||||
fn to_sim_value(&self, ty: $Int) -> SimValue<$Int> {
|
||||
self.bits().to_bitvec().into_sim_value(ty)
|
||||
}
|
||||
|
||||
fn into_sim_value(self, ty: $Int) -> SimValue<$Int> {
|
||||
Arc::try_unwrap(self.into_bits())
|
||||
.unwrap_or_else(|v: Arc<BitVec>| v.to_bitvec())
|
||||
.into_sim_value(ty)
|
||||
}
|
||||
|
||||
fn box_into_sim_value(self: Box<Self>, ty: $Int) -> SimValue<$Int> {
|
||||
Self::into_sim_value(*self, ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Width: Size> ToSimValue<CanonicalType> for $IntValue<Width> {
|
||||
#[track_caller]
|
||||
fn to_sim_value(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
|
||||
ToSimValue::<$Int>::to_sim_value(self, $Int::from_canonical(ty)).into_canonical()
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn into_sim_value(self, ty: CanonicalType) -> SimValue<CanonicalType> {
|
||||
ToSimValue::<$Int>::into_sim_value(self, $Int::from_canonical(ty)).into_canonical()
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn box_into_sim_value(self: Box<Self>, ty: CanonicalType) -> SimValue<CanonicalType> {
|
||||
Self::into_sim_value(*self, ty)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_to_sim_value_for_int_value!(UIntValue, UInt, UIntType);
|
||||
impl_to_sim_value_for_int_value!(SIntValue, SInt, SIntType);
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
enum MaybeNeedsSettle<S, N = S> {
|
||||
NeedsSettle(S),
|
||||
|
@ -6698,6 +6071,11 @@ impl SimulationModuleState {
|
|||
mut target: Target,
|
||||
which_module: WhichModule,
|
||||
) -> CompiledValue<CanonicalType> {
|
||||
assert!(
|
||||
target.canonical_ty().is_passive(),
|
||||
"simulator read/write expression must have a passive type \
|
||||
(recursively contains no fields with `#[hdl(flip)]`)"
|
||||
);
|
||||
if let Some(&retval) = self.io_targets.get(&target) {
|
||||
return retval;
|
||||
}
|
||||
|
@ -7019,19 +6397,19 @@ impl<I: BoolOrIntType> MaybeNeedsSettleFn<&'_ mut interpreter::State> for ReadBo
|
|||
struct ReadFn {
|
||||
compiled_value: CompiledValue<CanonicalType>,
|
||||
io: Expr<CanonicalType>,
|
||||
bits: BitVec,
|
||||
}
|
||||
|
||||
impl MaybeNeedsSettleFn<&'_ mut interpreter::State> for ReadFn {
|
||||
type Output = SimValue<CanonicalType>;
|
||||
|
||||
fn call(self, state: &mut interpreter::State) -> Self::Output {
|
||||
let Self {
|
||||
compiled_value,
|
||||
let Self { compiled_value, io } = self;
|
||||
SimulationImpl::read_no_settle_helper(
|
||||
state,
|
||||
io,
|
||||
bits,
|
||||
} = self;
|
||||
SimulationImpl::read_no_settle_helper(state, io, compiled_value, bits)
|
||||
compiled_value,
|
||||
UIntValue::new(Arc::new(BitVec::repeat(false, Expr::ty(io).bit_width()))),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7385,7 +6763,7 @@ impl SimulationImpl {
|
|||
(WaitTarget::Instant(instant), None) => Some(instant),
|
||||
(WaitTarget::Instant(instant), Some(retval)) => Some(instant.min(retval)),
|
||||
(WaitTarget::Change { key, value }, retval) => {
|
||||
if Self::value_changed(&mut self.state, key, &value.bits) {
|
||||
if Self::value_changed(&mut self.state, key, SimValue::bits(value).bits()) {
|
||||
Some(self.instant)
|
||||
} else {
|
||||
retval
|
||||
|
@ -7683,7 +7061,7 @@ impl SimulationImpl {
|
|||
}
|
||||
}
|
||||
#[track_caller]
|
||||
fn read_write_sim_value_helper<Bits>(
|
||||
fn read_write_sim_value_helper<Bits: ?Sized>(
|
||||
state: &mut interpreter::State,
|
||||
compiled_value: CompiledValue<CanonicalType>,
|
||||
start_bit_index: usize,
|
||||
|
@ -7778,15 +7156,13 @@ impl SimulationImpl {
|
|||
state: &mut interpreter::State,
|
||||
io: Expr<CanonicalType>,
|
||||
compiled_value: CompiledValue<CanonicalType>,
|
||||
mut bits: BitVec,
|
||||
mut bits: UIntValue,
|
||||
) -> SimValue<CanonicalType> {
|
||||
bits.clear();
|
||||
bits.resize(compiled_value.layout.ty.bit_width(), false);
|
||||
SimulationImpl::read_write_sim_value_helper(
|
||||
state,
|
||||
compiled_value,
|
||||
0,
|
||||
&mut bits,
|
||||
bits.bits_mut(),
|
||||
|_signed, bit_range, bits, value| {
|
||||
<UInt>::copy_bits_from_bigint_wrapping(value, &mut bits[bit_range]);
|
||||
},
|
||||
|
@ -7797,10 +7173,7 @@ impl SimulationImpl {
|
|||
bits[bit_range].clone_from_bitslice(bitslice);
|
||||
},
|
||||
);
|
||||
SimValue {
|
||||
ty: Expr::ty(io),
|
||||
bits,
|
||||
}
|
||||
SimValue::from_bits(Expr::ty(io), bits)
|
||||
}
|
||||
/// doesn't modify `bits`
|
||||
fn value_changed(
|
||||
|
@ -7842,11 +7215,7 @@ impl SimulationImpl {
|
|||
) {
|
||||
let compiled_value = self.get_module(which_module).read_helper(io, which_module);
|
||||
let value = compiled_value
|
||||
.map(|compiled_value| ReadFn {
|
||||
compiled_value,
|
||||
io,
|
||||
bits: BitVec::new(),
|
||||
})
|
||||
.map(|compiled_value| ReadFn { compiled_value, io })
|
||||
.apply_no_settle(&mut self.state);
|
||||
let (MaybeNeedsSettle::NeedsSettle(compiled_value)
|
||||
| MaybeNeedsSettle::NoSettleNeeded(compiled_value)) = compiled_value;
|
||||
|
@ -7863,12 +7232,12 @@ impl SimulationImpl {
|
|||
.get_module_mut(which_module)
|
||||
.write_helper(io, which_module);
|
||||
self.state_ready_to_run = true;
|
||||
assert_eq!(Expr::ty(io), value.ty());
|
||||
assert_eq!(Expr::ty(io), SimValue::ty(value));
|
||||
Self::read_write_sim_value_helper(
|
||||
&mut self.state,
|
||||
compiled_value,
|
||||
0,
|
||||
&mut value.bits(),
|
||||
&mut value.bits().bits(),
|
||||
|signed, bit_range, bits, value| {
|
||||
if signed {
|
||||
*value = SInt::bits_to_bigint(&bits[bit_range]);
|
||||
|
@ -8162,10 +7531,10 @@ macro_rules! impl_simulation_methods {
|
|||
SimValue::from_canonical($self.settle_if_needed(retval)$(.$await)?)
|
||||
}
|
||||
$(#[$track_caller])?
|
||||
pub $($async)? fn write<IO: Type, V: ToSimValue<IO>>(&mut $self, io: Expr<IO>, value: V) {
|
||||
pub $($async)? fn write<IO: Type, V: value::ToSimValueWithType<IO>>(&mut $self, io: Expr<IO>, value: V) {
|
||||
$self.sim_impl.borrow_mut().write(
|
||||
Expr::canonical(io),
|
||||
&value.into_sim_value(Expr::ty(io)).into_canonical(),
|
||||
&SimValue::into_canonical(value.into_sim_value_with_type(Expr::ty(io))),
|
||||
$which_module,
|
||||
);
|
||||
}
|
||||
|
|
913
crates/fayalite/src/sim/value.rs
Normal file
913
crates/fayalite/src/sim/value.rs
Normal 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<LhsWidth: Size, RhsWidth: Size> SimValuePartialEq<UIntType<RhsWidth>> for UIntType<LhsWidth> {
|
||||
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<UIntType<RhsWidth>>) -> bool {
|
||||
**this == **other
|
||||
}
|
||||
}
|
||||
|
||||
impl<LhsWidth: Size, RhsWidth: Size> SimValuePartialEq<SIntType<RhsWidth>> for SIntType<LhsWidth> {
|
||||
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<SIntType<RhsWidth>>) -> 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);
|
|
@ -7,14 +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]
|
||||
|
@ -58,6 +63,24 @@ impl fmt::Debug for CanonicalType {
|
|||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -156,6 +179,9 @@ impl CanonicalType {
|
|||
}
|
||||
}
|
||||
}
|
||||
pub(crate) fn as_serde_unexpected_str(self) -> &'static str {
|
||||
serde_impls::SerdeCanonicalType::from(self).as_serde_unexpected_str()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait MatchVariantAndInactiveScope: Sized {
|
||||
|
@ -222,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);
|
||||
|
@ -234,6 +288,14 @@ 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 {}
|
||||
|
||||
impl BaseType for CanonicalType {}
|
||||
|
@ -268,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<
|
||||
|
@ -285,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 {
|
||||
() => {
|
||||
|
@ -314,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 {
|
||||
|
@ -339,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;
|
||||
|
|
130
crates/fayalite/src/ty/serde_impls.rs
Normal file
130
crates/fayalite/src/ty/serde_impls.rs
Normal 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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
// 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;
|
||||
|
|
122
crates/fayalite/src/util/alternating_cell.rs
Normal file
122
crates/fayalite/src/util/alternating_cell.rs
Normal 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()
|
||||
}
|
||||
}
|
|
@ -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>;
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -210,10 +210,10 @@ impl PrefixSumOp {
|
|||
#[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
|
||||
/// <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
|
||||
/// <https://en.wikipedia.org/wiki/Prefix_sum#Algorithm_2:_Work-efficient>
|
||||
WorkEfficient,
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
@ -1532,3 +1546,83 @@ fn test_extern_module2() {
|
|||
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
|
@ -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
|
||||
|
|
|
@ -222,7 +222,9 @@ Simulation {
|
|||
},
|
||||
value: SimValue {
|
||||
ty: Clock,
|
||||
bits: 0x1,
|
||||
value: OpaqueSimValue {
|
||||
bits: 0x1_u1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
1498
crates/fayalite/tests/sim/expected/ripple_counter.txt
Normal file
1498
crates/fayalite/tests/sim/expected/ripple_counter.txt
Normal file
File diff suppressed because it is too large
Load diff
1753
crates/fayalite/tests/sim/expected/ripple_counter.vcd
Normal file
1753
crates/fayalite/tests/sim/expected/ripple_counter.vcd
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue