forked from libre-chip/fayalite
Compare commits
8 commits
fifo-proof
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
3458c21f44 | ||
|
43797db36e | ||
|
cdd84953d0 | ||
|
86a1bb46be | ||
|
209d5b5fe1 | ||
|
d4ea826051 | ||
|
404a2ee043 | ||
|
e3a2ccd41c |
|
@ -83,6 +83,7 @@ impl ParsedBundle {
|
|||
custom_bounds,
|
||||
no_static: _,
|
||||
no_runtime_generics: _,
|
||||
cmp_eq: _,
|
||||
} = options.body;
|
||||
let mut fields = match fields {
|
||||
syn::Fields::Named(fields) => fields,
|
||||
|
@ -437,6 +438,7 @@ impl ToTokens for ParsedBundle {
|
|||
custom_bounds: _,
|
||||
no_static,
|
||||
no_runtime_generics,
|
||||
cmp_eq,
|
||||
} = &options.body;
|
||||
let target = get_target(target, ident);
|
||||
let mut item_attrs = attrs.clone();
|
||||
|
@ -765,6 +767,69 @@ impl ToTokens for ParsedBundle {
|
|||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
if let Some((cmp_eq,)) = cmp_eq {
|
||||
let mut where_clause =
|
||||
Generics::from(generics)
|
||||
.where_clause
|
||||
.unwrap_or_else(|| syn::WhereClause {
|
||||
where_token: Token,
|
||||
predicates: Punctuated::new(),
|
||||
});
|
||||
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
|
||||
.predicates
|
||||
.push(parse_quote_spanned! {cmp_eq.span=>
|
||||
#field_ty: ::fayalite::expr::ops::ExprPartialEq<#field_ty>
|
||||
});
|
||||
fields_cmp_eq.push(quote_spanned! {span=>
|
||||
::fayalite::expr::ops::ExprPartialEq::cmp_eq(__lhs.#field_ident, __rhs.#field_ident)
|
||||
});
|
||||
fields_cmp_ne.push(quote_spanned! {span=>
|
||||
::fayalite::expr::ops::ExprPartialEq::cmp_ne(__lhs.#field_ident, __rhs.#field_ident)
|
||||
});
|
||||
}
|
||||
let cmp_eq_body;
|
||||
let cmp_ne_body;
|
||||
if fields_len == 0 {
|
||||
cmp_eq_body = quote_spanned! {span=>
|
||||
::fayalite::expr::ToExpr::to_expr(&true)
|
||||
};
|
||||
cmp_ne_body = quote_spanned! {span=>
|
||||
::fayalite::expr::ToExpr::to_expr(&false)
|
||||
};
|
||||
} else {
|
||||
cmp_eq_body = quote_spanned! {span=>
|
||||
#(#fields_cmp_eq)&*
|
||||
};
|
||||
cmp_ne_body = quote_spanned! {span=>
|
||||
#(#fields_cmp_ne)|*
|
||||
};
|
||||
};
|
||||
quote_spanned! {span=>
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::expr::ops::ExprPartialEq<Self> for #target #type_generics
|
||||
#where_clause
|
||||
{
|
||||
fn cmp_eq(
|
||||
__lhs: ::fayalite::expr::Expr<Self>,
|
||||
__rhs: ::fayalite::expr::Expr<Self>,
|
||||
) -> ::fayalite::expr::Expr<::fayalite::int::Bool> {
|
||||
#cmp_eq_body
|
||||
}
|
||||
fn cmp_ne(
|
||||
__lhs: ::fayalite::expr::Expr<Self>,
|
||||
__rhs: ::fayalite::expr::Expr<Self>,
|
||||
) -> ::fayalite::expr::Expr<::fayalite::int::Bool> {
|
||||
#cmp_ne_body
|
||||
}
|
||||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
}
|
||||
if let (None, MaybeParsed::Parsed(generics)) = (no_static, &self.generics) {
|
||||
let static_generics = generics.clone().for_static_type();
|
||||
let (static_impl_generics, static_type_generics, static_where_clause) =
|
||||
|
|
|
@ -155,7 +155,11 @@ impl ParsedEnum {
|
|||
custom_bounds,
|
||||
no_static: _,
|
||||
no_runtime_generics: _,
|
||||
cmp_eq,
|
||||
} = options.body;
|
||||
if let Some((cmp_eq,)) = cmp_eq {
|
||||
errors.error(cmp_eq, "#[hdl(cmp_eq)] is not yet implemented for enums");
|
||||
}
|
||||
attrs.retain(|attr| {
|
||||
if attr.path().is_ident("repr") {
|
||||
errors.error(attr, "#[repr] is not supported on #[hdl] enums");
|
||||
|
@ -211,6 +215,7 @@ impl ToTokens for ParsedEnum {
|
|||
custom_bounds: _,
|
||||
no_static,
|
||||
no_runtime_generics,
|
||||
cmp_eq: _, // TODO: implement cmp_eq for enums
|
||||
} = &options.body;
|
||||
let target = get_target(target, ident);
|
||||
let mut struct_attrs = attrs.clone();
|
||||
|
|
|
@ -49,10 +49,14 @@ impl ParsedTypeAlias {
|
|||
custom_bounds,
|
||||
no_static,
|
||||
no_runtime_generics: _,
|
||||
cmp_eq,
|
||||
} = options.body;
|
||||
if let Some((no_static,)) = no_static {
|
||||
errors.error(no_static, "no_static is not valid on type aliases");
|
||||
}
|
||||
if let Some((cmp_eq,)) = cmp_eq {
|
||||
errors.error(cmp_eq, "cmp_eq is not valid on type aliases");
|
||||
}
|
||||
let generics = if custom_bounds.is_some() {
|
||||
MaybeParsed::Unrecognized(generics)
|
||||
} else if let Some(generics) = errors.ok(ParsedGenerics::parse(&mut generics)) {
|
||||
|
@ -95,6 +99,7 @@ impl ToTokens for ParsedTypeAlias {
|
|||
custom_bounds: _,
|
||||
no_static: _,
|
||||
no_runtime_generics,
|
||||
cmp_eq: _,
|
||||
} = &options.body;
|
||||
let target = get_target(target, ident);
|
||||
let mut type_attrs = attrs.clone();
|
||||
|
|
|
@ -26,6 +26,7 @@ crate::options! {
|
|||
CustomBounds(custom_bounds),
|
||||
NoStatic(no_static),
|
||||
NoRuntimeGenerics(no_runtime_generics),
|
||||
CmpEq(cmp_eq),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2069,11 +2070,16 @@ macro_rules! impl_bounds {
|
|||
$(
|
||||
$Variant:ident,
|
||||
)*
|
||||
$(
|
||||
#[unknown]
|
||||
$Unknown:ident,
|
||||
)?
|
||||
}
|
||||
) => {
|
||||
#[derive(Clone, Debug)]
|
||||
$vis enum $enum_type {
|
||||
$($Variant(known_items::$Variant),)*
|
||||
$($Unknown(syn::TypeParamBound),)?
|
||||
}
|
||||
|
||||
$(impl From<known_items::$Variant> for $enum_type {
|
||||
|
@ -2086,28 +2092,54 @@ macro_rules! impl_bounds {
|
|||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
match self {
|
||||
$(Self::$Variant(v) => v.to_tokens(tokens),)*
|
||||
$(Self::$Unknown(v) => v.to_tokens(tokens),)?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl $enum_type {
|
||||
$vis fn parse_path(path: Path) -> Result<Self, Path> {
|
||||
#![allow(unreachable_code)]
|
||||
$(let path = match known_items::$Variant::parse_path(path) {
|
||||
Ok(v) => return Ok(Self::$Variant(v)),
|
||||
Err(path) => path,
|
||||
};)*
|
||||
$(return Ok(Self::$Unknown(syn::TraitBound {
|
||||
paren_token: None,
|
||||
modifier: syn::TraitBoundModifier::None,
|
||||
lifetimes: None,
|
||||
path,
|
||||
}.into()));)?
|
||||
Err(path)
|
||||
}
|
||||
$vis fn parse_type_param_bound(mut type_param_bound: syn::TypeParamBound) -> Result<Self, syn::TypeParamBound> {
|
||||
#![allow(unreachable_code)]
|
||||
if let syn::TypeParamBound::Trait(mut trait_bound) = type_param_bound {
|
||||
if let syn::TraitBound {
|
||||
paren_token: _,
|
||||
modifier: syn::TraitBoundModifier::None,
|
||||
lifetimes: None,
|
||||
path: _,
|
||||
} = trait_bound {
|
||||
match Self::parse_path(trait_bound.path) {
|
||||
Ok(retval) => return Ok(retval),
|
||||
Err(path) => trait_bound.path = path,
|
||||
}
|
||||
}
|
||||
type_param_bound = trait_bound.into();
|
||||
}
|
||||
$(return Ok(Self::$Unknown(type_param_bound));)?
|
||||
Err(type_param_bound)
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for $enum_type {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
Self::parse_path(Path::parse_mod_style(input)?).map_err(|path| {
|
||||
syn::Error::new_spanned(
|
||||
path,
|
||||
Self::parse_type_param_bound(input.parse()?)
|
||||
.map_err(|type_param_bound| syn::Error::new_spanned(
|
||||
type_param_bound,
|
||||
format_args!("expected one of: {}", [$(stringify!($Variant)),*].join(", ")),
|
||||
)
|
||||
})
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2115,6 +2147,7 @@ macro_rules! impl_bounds {
|
|||
#[allow(non_snake_case)]
|
||||
$vis struct $struct_type {
|
||||
$($vis $Variant: Option<known_items::$Variant>,)*
|
||||
$($vis $Unknown: Vec<syn::TypeParamBound>,)?
|
||||
}
|
||||
|
||||
impl ToTokens for $struct_type {
|
||||
|
@ -2126,42 +2159,63 @@ macro_rules! impl_bounds {
|
|||
separator = Some(<Token![+]>::default());
|
||||
v.to_tokens(tokens);
|
||||
})*
|
||||
$(for v in &self.$Unknown {
|
||||
separator.to_tokens(tokens);
|
||||
separator = Some(<Token![+]>::default());
|
||||
v.to_tokens(tokens);
|
||||
})*
|
||||
}
|
||||
}
|
||||
|
||||
const _: () = {
|
||||
#[derive(Clone, Debug)]
|
||||
$vis struct Iter($vis $struct_type);
|
||||
#[allow(non_snake_case)]
|
||||
$vis struct Iter {
|
||||
$($Variant: Option<known_items::$Variant>,)*
|
||||
$($Unknown: std::vec::IntoIter<syn::TypeParamBound>,)?
|
||||
}
|
||||
|
||||
impl IntoIterator for $struct_type {
|
||||
type Item = $enum_type;
|
||||
type IntoIter = Iter;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
Iter(self)
|
||||
Iter {
|
||||
$($Variant: self.$Variant,)*
|
||||
$($Unknown: self.$Unknown.into_iter(),)?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for Iter {
|
||||
type Item = $enum_type;
|
||||
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
$(
|
||||
if let Some(value) = self.0.$Variant.take() {
|
||||
if let Some(value) = self.$Variant.take() {
|
||||
return Some($enum_type::$Variant(value));
|
||||
}
|
||||
)*
|
||||
$(
|
||||
if let Some(value) = self.$Unknown.next() {
|
||||
return Some($enum_type::$Unknown(value));
|
||||
}
|
||||
)?
|
||||
None
|
||||
}
|
||||
|
||||
#[allow(unused_mut, unused_variables)]
|
||||
fn fold<B, F: FnMut(B, Self::Item) -> B>(mut self, mut init: B, mut f: F) -> B {
|
||||
$(
|
||||
if let Some(value) = self.0.$Variant.take() {
|
||||
if let Some(value) = self.$Variant.take() {
|
||||
init = f(init, $enum_type::$Variant(value));
|
||||
}
|
||||
)*
|
||||
$(
|
||||
if let Some(value) = self.$Unknown.next() {
|
||||
init = f(init, $enum_type::$Unknown(value));
|
||||
}
|
||||
)?
|
||||
init
|
||||
}
|
||||
}
|
||||
|
@ -2173,6 +2227,9 @@ macro_rules! impl_bounds {
|
|||
$($enum_type::$Variant(v) => {
|
||||
self.$Variant = Some(v);
|
||||
})*
|
||||
$($enum_type::$Unknown(v) => {
|
||||
self.$Unknown.push(v);
|
||||
})?
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -2191,6 +2248,7 @@ macro_rules! impl_bounds {
|
|||
$(if let Some(v) = v.$Variant {
|
||||
self.$Variant = Some(v);
|
||||
})*
|
||||
$(self.$Unknown.extend(v.$Unknown);)*
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -2244,6 +2302,8 @@ impl_bounds! {
|
|||
Size,
|
||||
StaticType,
|
||||
Type,
|
||||
#[unknown]
|
||||
Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2257,6 +2317,8 @@ impl_bounds! {
|
|||
ResetType,
|
||||
StaticType,
|
||||
Type,
|
||||
#[unknown]
|
||||
Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2270,6 +2332,7 @@ impl From<ParsedTypeBound> for ParsedBound {
|
|||
ParsedTypeBound::ResetType(v) => ParsedBound::ResetType(v),
|
||||
ParsedTypeBound::StaticType(v) => ParsedBound::StaticType(v),
|
||||
ParsedTypeBound::Type(v) => ParsedBound::Type(v),
|
||||
ParsedTypeBound::Unknown(v) => ParsedBound::Unknown(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2284,6 +2347,7 @@ impl From<ParsedTypeBounds> for ParsedBounds {
|
|||
ResetType,
|
||||
StaticType,
|
||||
Type,
|
||||
Unknown,
|
||||
} = value;
|
||||
Self {
|
||||
BoolOrIntType,
|
||||
|
@ -2295,6 +2359,7 @@ impl From<ParsedTypeBounds> for ParsedBounds {
|
|||
Size: None,
|
||||
StaticType,
|
||||
Type,
|
||||
Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2330,6 +2395,7 @@ impl ParsedTypeBound {
|
|||
ParsedTypeBound::Type(known_items::Type(span)),
|
||||
]),
|
||||
Self::Type(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::from(v)]),
|
||||
Self::Unknown(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::Unknown(v)]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2364,6 +2430,7 @@ impl From<ParsedSizeTypeBounds> for ParsedBounds {
|
|||
Size,
|
||||
StaticType: None,
|
||||
Type: None,
|
||||
Unknown: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2391,6 +2458,7 @@ impl ParsedBounds {
|
|||
fn categorize(self, errors: &mut Errors, span: Span) -> ParsedBoundsCategory {
|
||||
let mut type_bounds = None;
|
||||
let mut size_type_bounds = None;
|
||||
let mut unknown_bounds = vec![];
|
||||
self.into_iter().for_each(|bound| match bound.categorize() {
|
||||
ParsedBoundCategory::Type(bound) => {
|
||||
type_bounds
|
||||
|
@ -2402,15 +2470,37 @@ impl ParsedBounds {
|
|||
.get_or_insert_with(ParsedSizeTypeBounds::default)
|
||||
.extend([bound]);
|
||||
}
|
||||
ParsedBoundCategory::Unknown(bound) => unknown_bounds.push(bound),
|
||||
});
|
||||
match (type_bounds, size_type_bounds) {
|
||||
(None, None) => ParsedBoundsCategory::Type(ParsedTypeBounds {
|
||||
match (type_bounds, size_type_bounds, unknown_bounds.is_empty()) {
|
||||
(None, None, true) => ParsedBoundsCategory::Type(ParsedTypeBounds {
|
||||
Type: Some(known_items::Type(span)),
|
||||
..Default::default()
|
||||
}),
|
||||
(None, Some(bounds)) => ParsedBoundsCategory::SizeType(bounds),
|
||||
(Some(bounds), None) => ParsedBoundsCategory::Type(bounds),
|
||||
(Some(type_bounds), Some(size_type_bounds)) => {
|
||||
(None, None, false) => {
|
||||
errors.error(
|
||||
unknown_bounds.remove(0),
|
||||
"unknown bounds: must use at least one known bound (such as `Type`) with any unknown bounds",
|
||||
);
|
||||
ParsedBoundsCategory::Type(ParsedTypeBounds {
|
||||
Unknown: unknown_bounds,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
(None, Some(bounds), true) => ParsedBoundsCategory::SizeType(bounds),
|
||||
(None, Some(bounds), false) => {
|
||||
// TODO: implement
|
||||
errors.error(
|
||||
unknown_bounds.remove(0),
|
||||
"unknown bounds with `Size` bounds are not implemented",
|
||||
);
|
||||
ParsedBoundsCategory::SizeType(bounds)
|
||||
}
|
||||
(Some(bounds), None, _) => ParsedBoundsCategory::Type(ParsedTypeBounds {
|
||||
Unknown: unknown_bounds,
|
||||
..bounds
|
||||
}),
|
||||
(Some(type_bounds), Some(size_type_bounds), _) => {
|
||||
errors.error(
|
||||
size_type_bounds
|
||||
.Size
|
||||
|
@ -2427,6 +2517,7 @@ impl ParsedBounds {
|
|||
pub(crate) enum ParsedBoundCategory {
|
||||
Type(ParsedTypeBound),
|
||||
SizeType(ParsedSizeTypeBound),
|
||||
Unknown(syn::TypeParamBound),
|
||||
}
|
||||
|
||||
impl ParsedBound {
|
||||
|
@ -2441,12 +2532,14 @@ impl ParsedBound {
|
|||
Self::Size(v) => ParsedBoundCategory::SizeType(ParsedSizeTypeBound::Size(v)),
|
||||
Self::StaticType(v) => ParsedBoundCategory::Type(ParsedTypeBound::StaticType(v)),
|
||||
Self::Type(v) => ParsedBoundCategory::Type(ParsedTypeBound::Type(v)),
|
||||
Self::Unknown(v) => ParsedBoundCategory::Unknown(v),
|
||||
}
|
||||
}
|
||||
fn implied_bounds(self) -> ParsedBounds {
|
||||
match self.categorize() {
|
||||
ParsedBoundCategory::Type(v) => v.implied_bounds().into(),
|
||||
ParsedBoundCategory::SizeType(v) => v.implied_bounds().into(),
|
||||
ParsedBoundCategory::Unknown(v) => ParsedBounds::from_iter([ParsedBound::Unknown(v)]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3325,7 +3418,7 @@ impl ParsedGenerics {
|
|||
| ParsedTypeBound::EnumType(_)
|
||||
| ParsedTypeBound::IntType(_)
|
||||
| ParsedTypeBound::ResetType(_) => {
|
||||
errors.error(bound, "bound on mask type not implemented");
|
||||
errors.error(bound, "bounds on mask types are not implemented");
|
||||
}
|
||||
ParsedTypeBound::StaticType(bound) => {
|
||||
if bounds.StaticType.is_none() {
|
||||
|
@ -3337,6 +3430,12 @@ impl ParsedGenerics {
|
|||
}
|
||||
}
|
||||
ParsedTypeBound::Type(_) => {}
|
||||
ParsedTypeBound::Unknown(_) => {
|
||||
errors.error(
|
||||
bound,
|
||||
"unknown bounds on mask types are not implemented",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
bounds.add_implied_bounds();
|
||||
|
|
|
@ -72,13 +72,14 @@ mod kw {
|
|||
custom_keyword!(cfg);
|
||||
custom_keyword!(cfg_attr);
|
||||
custom_keyword!(clock_domain);
|
||||
custom_keyword!(cmp_eq);
|
||||
custom_keyword!(connect_inexact);
|
||||
custom_keyword!(custom_bounds);
|
||||
custom_keyword!(flip);
|
||||
custom_keyword!(hdl);
|
||||
custom_keyword!(hdl_module);
|
||||
custom_keyword!(input);
|
||||
custom_keyword!(incomplete_wire);
|
||||
custom_keyword!(input);
|
||||
custom_keyword!(instance);
|
||||
custom_keyword!(m);
|
||||
custom_keyword!(memory);
|
||||
|
|
|
@ -1109,7 +1109,7 @@ fn parse_quote_let_pat<T, R: ToTokens, C: Borrow<Token![:]>>(
|
|||
}
|
||||
}
|
||||
|
||||
fn wrap_ty_with_expr(ty: impl ToTokens) -> Type {
|
||||
pub(crate) fn wrap_ty_with_expr(ty: impl ToTokens) -> Type {
|
||||
parse_quote_spanned! {ty.span()=>
|
||||
::fayalite::expr::Expr<#ty>
|
||||
}
|
||||
|
@ -1586,7 +1586,7 @@ impl Visitor<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
fn empty_let() -> Local {
|
||||
pub(crate) fn empty_let() -> Local {
|
||||
Local {
|
||||
attrs: vec![],
|
||||
let_token: Default::default(),
|
||||
|
@ -1672,7 +1672,7 @@ impl Fold for Visitor<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
fn fold_local(&mut self, let_stmt: Local) -> Local {
|
||||
fn fold_local(&mut self, mut let_stmt: Local) -> Local {
|
||||
match self
|
||||
.errors
|
||||
.ok(HdlAttr::<Nothing, kw::hdl>::parse_and_leave_attr(
|
||||
|
@ -1682,6 +1682,25 @@ impl Fold for Visitor<'_> {
|
|||
Some(None) => return fold_local(self, let_stmt),
|
||||
Some(Some(HdlAttr { .. })) => {}
|
||||
};
|
||||
let mut pat = &let_stmt.pat;
|
||||
if let Pat::Type(pat_type) = pat {
|
||||
pat = &pat_type.pat;
|
||||
}
|
||||
let Pat::Ident(syn::PatIdent {
|
||||
attrs: _,
|
||||
by_ref: None,
|
||||
mutability: _,
|
||||
ident: _,
|
||||
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 let_stmt = fold_local(self, let_stmt);
|
||||
return self.process_hdl_let_pat(hdl_attr, let_stmt);
|
||||
};
|
||||
let hdl_let = syn::parse2::<HdlLet<HdlLetKind<Type>>>(let_stmt.into_token_stream());
|
||||
let Some(hdl_let) = self.errors.ok(hdl_let) else {
|
||||
return empty_let();
|
||||
|
|
|
@ -3,22 +3,111 @@
|
|||
use crate::{
|
||||
fold::{impl_fold, DoFold},
|
||||
kw,
|
||||
module::transform_body::{with_debug_clone_and_fold, Visitor},
|
||||
module::transform_body::{empty_let, with_debug_clone_and_fold, wrap_ty_with_expr, Visitor},
|
||||
Errors, HdlAttr, PairsIterExt,
|
||||
};
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::{format_ident, quote_spanned, ToTokens, TokenStreamExt};
|
||||
use std::collections::BTreeSet;
|
||||
use syn::{
|
||||
fold::{fold_arm, fold_expr_match, fold_pat, Fold},
|
||||
fold::{fold_arm, fold_expr_match, fold_local, fold_pat, Fold},
|
||||
parse::Nothing,
|
||||
parse_quote_spanned,
|
||||
punctuated::Punctuated,
|
||||
spanned::Spanned,
|
||||
token::{Brace, Paren},
|
||||
Arm, Attribute, Expr, ExprMatch, FieldPat, Ident, Member, Pat, PatIdent, PatOr, PatParen,
|
||||
PatPath, PatRest, PatStruct, PatTupleStruct, PatWild, Path, PathSegment, Token, TypePath,
|
||||
Arm, Attribute, Expr, ExprMatch, FieldPat, Ident, Local, Member, Pat, PatIdent, PatOr,
|
||||
PatParen, PatPath, PatRest, PatStruct, PatTuple, PatTupleStruct, PatWild, Path, PathSegment,
|
||||
Token, TypePath,
|
||||
};
|
||||
|
||||
macro_rules! visit_trait {
|
||||
(
|
||||
$($vis:vis fn $fn:ident($state:ident: _, $value:ident: &$Value:ty) $block:block)*
|
||||
) => {
|
||||
trait VisitMatchPat<'a> {
|
||||
$(fn $fn(&mut self, $value: &'a $Value) {
|
||||
$fn(self, $value);
|
||||
})*
|
||||
}
|
||||
|
||||
$($vis fn $fn<'a>($state: &mut (impl ?Sized + VisitMatchPat<'a>), $value: &'a $Value) $block)*
|
||||
};
|
||||
}
|
||||
|
||||
visit_trait! {
|
||||
fn visit_match_pat_binding(_state: _, v: &MatchPatBinding) {
|
||||
let MatchPatBinding { ident: _ } = v;
|
||||
}
|
||||
fn visit_match_pat_wild(_state: _, v: &MatchPatWild) {
|
||||
let MatchPatWild { underscore_token: _ } = v;
|
||||
}
|
||||
fn visit_match_pat_rest(_state: _, v: &MatchPatRest) {
|
||||
let MatchPatRest { dot2_token: _ } = v;
|
||||
}
|
||||
fn visit_match_pat_paren(state: _, v: &MatchPatParen<MatchPat>) {
|
||||
let MatchPatParen { paren_token: _, pat } = v;
|
||||
state.visit_match_pat(pat);
|
||||
}
|
||||
fn visit_match_pat_paren_simple(state: _, v: &MatchPatParen<MatchPatSimple>) {
|
||||
let MatchPatParen { paren_token: _, pat } = v;
|
||||
state.visit_match_pat_simple(pat);
|
||||
}
|
||||
fn visit_match_pat_or(state: _, v: &MatchPatOr<MatchPat>) {
|
||||
let MatchPatOr { leading_vert: _, cases } = v;
|
||||
for v in cases {
|
||||
state.visit_match_pat(v);
|
||||
}
|
||||
}
|
||||
fn visit_match_pat_or_simple(state: _, v: &MatchPatOr<MatchPatSimple>) {
|
||||
let MatchPatOr { leading_vert: _, cases } = v;
|
||||
for v in cases {
|
||||
state.visit_match_pat_simple(v);
|
||||
}
|
||||
}
|
||||
fn visit_match_pat_struct_field(state: _, v: &MatchPatStructField) {
|
||||
let MatchPatStructField { field_name: _, colon_token: _, pat } = v;
|
||||
state.visit_match_pat_simple(pat);
|
||||
}
|
||||
fn visit_match_pat_struct(state: _, v: &MatchPatStruct) {
|
||||
let MatchPatStruct { match_span: _, path: _, brace_token: _, fields, rest: _ } = v;
|
||||
for v in fields {
|
||||
state.visit_match_pat_struct_field(v);
|
||||
}
|
||||
}
|
||||
fn visit_match_pat_tuple(state: _, v: &MatchPatTuple) {
|
||||
let MatchPatTuple { paren_token: _, fields } = v;
|
||||
for v in fields {
|
||||
state.visit_match_pat_simple(v);
|
||||
}
|
||||
}
|
||||
fn visit_match_pat_enum_variant(state: _, v: &MatchPatEnumVariant) {
|
||||
let MatchPatEnumVariant {match_span:_, variant_path: _, enum_path: _, variant_name: _, field } = v;
|
||||
if let Some((_, v)) = field {
|
||||
state.visit_match_pat_simple(v);
|
||||
}
|
||||
}
|
||||
fn visit_match_pat_simple(state: _, v: &MatchPatSimple) {
|
||||
match v {
|
||||
MatchPatSimple::Paren(v) => state.visit_match_pat_paren_simple(v),
|
||||
MatchPatSimple::Or(v) => state.visit_match_pat_or_simple(v),
|
||||
MatchPatSimple::Binding(v) => state.visit_match_pat_binding(v),
|
||||
MatchPatSimple::Wild(v) => state.visit_match_pat_wild(v),
|
||||
MatchPatSimple::Rest(v) => state.visit_match_pat_rest(v),
|
||||
}
|
||||
}
|
||||
fn visit_match_pat(state: _, v: &MatchPat) {
|
||||
match v {
|
||||
MatchPat::Simple(v) => state.visit_match_pat_simple(v),
|
||||
MatchPat::Or(v) => state.visit_match_pat_or(v),
|
||||
MatchPat::Paren(v) => state.visit_match_pat_paren(v),
|
||||
MatchPat::Struct(v) => state.visit_match_pat_struct(v),
|
||||
MatchPat::Tuple(v) => state.visit_match_pat_tuple(v),
|
||||
MatchPat::EnumVariant(v) => state.visit_match_pat_enum_variant(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
with_debug_clone_and_fold! {
|
||||
struct MatchPatBinding<> {
|
||||
ident: Ident,
|
||||
|
@ -53,6 +142,15 @@ with_debug_clone_and_fold! {
|
|||
}
|
||||
}
|
||||
|
||||
impl<P> MatchPatOr<P> {
|
||||
/// returns the first `|` between two patterns
|
||||
fn first_inner_vert(&self) -> Option<Token![|]> {
|
||||
let mut pairs = self.cases.pairs();
|
||||
pairs.next_back();
|
||||
pairs.next().and_then(|v| v.into_tuple().1.copied())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: ToTokens> ToTokens for MatchPatOr<P> {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let Self {
|
||||
|
@ -77,6 +175,19 @@ impl ToTokens for MatchPatWild {
|
|||
}
|
||||
}
|
||||
|
||||
with_debug_clone_and_fold! {
|
||||
struct MatchPatRest<> {
|
||||
dot2_token: Token![..],
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for MatchPatRest {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let Self { dot2_token } = self;
|
||||
dot2_token.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
|
||||
with_debug_clone_and_fold! {
|
||||
struct MatchPatStructField<> {
|
||||
field_name: Ident,
|
||||
|
@ -159,6 +270,25 @@ impl ToTokens for MatchPatStruct {
|
|||
}
|
||||
}
|
||||
|
||||
with_debug_clone_and_fold! {
|
||||
struct MatchPatTuple<> {
|
||||
paren_token: Paren,
|
||||
fields: Punctuated<MatchPatSimple, Token![,]>,
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for MatchPatTuple {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let Self {
|
||||
paren_token,
|
||||
fields,
|
||||
} = self;
|
||||
paren_token.surround(tokens, |tokens| {
|
||||
fields.to_tokens(tokens);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
with_debug_clone_and_fold! {
|
||||
struct MatchPatEnumVariant<> {
|
||||
match_span: Span,
|
||||
|
@ -194,6 +324,7 @@ enum MatchPatSimple {
|
|||
Or(MatchPatOr<MatchPatSimple>),
|
||||
Binding(MatchPatBinding),
|
||||
Wild(MatchPatWild),
|
||||
Rest(MatchPatRest),
|
||||
}
|
||||
|
||||
impl_fold! {
|
||||
|
@ -202,6 +333,7 @@ impl_fold! {
|
|||
Or(MatchPatOr<MatchPatSimple>),
|
||||
Binding(MatchPatBinding),
|
||||
Wild(MatchPatWild),
|
||||
Rest(MatchPatRest),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -212,6 +344,7 @@ impl ToTokens for MatchPatSimple {
|
|||
Self::Paren(v) => v.to_tokens(tokens),
|
||||
Self::Binding(v) => v.to_tokens(tokens),
|
||||
Self::Wild(v) => v.to_tokens(tokens),
|
||||
Self::Rest(v) => v.to_tokens(tokens),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -278,6 +411,7 @@ trait ParseMatchPat: Sized {
|
|||
fn or(v: MatchPatOr<Self>) -> Self;
|
||||
fn paren(v: MatchPatParen<Self>) -> Self;
|
||||
fn struct_(state: &mut HdlMatchParseState<'_>, v: MatchPatStruct) -> Result<Self, ()>;
|
||||
fn tuple(state: &mut HdlMatchParseState<'_>, v: MatchPatTuple) -> Result<Self, ()>;
|
||||
fn enum_variant(state: &mut HdlMatchParseState<'_>, v: MatchPatEnumVariant)
|
||||
-> Result<Self, ()>;
|
||||
fn parse(state: &mut HdlMatchParseState<'_>, pat: Pat) -> Result<Self, ()> {
|
||||
|
@ -462,7 +596,34 @@ trait ParseMatchPat: Sized {
|
|||
}) => Ok(Self::simple(MatchPatSimple::Wild(MatchPatWild {
|
||||
underscore_token,
|
||||
}))),
|
||||
Pat::Tuple(_) | Pat::Slice(_) | Pat::Const(_) | Pat::Lit(_) | Pat::Range(_) => {
|
||||
Pat::Tuple(PatTuple {
|
||||
attrs: _,
|
||||
paren_token,
|
||||
elems,
|
||||
}) => {
|
||||
let fields = elems
|
||||
.into_pairs()
|
||||
.filter_map_pair_value(|field_pat| {
|
||||
if let Pat::Rest(PatRest {
|
||||
attrs: _,
|
||||
dot2_token,
|
||||
}) = field_pat
|
||||
{
|
||||
Some(MatchPatSimple::Rest(MatchPatRest { dot2_token }))
|
||||
} else {
|
||||
MatchPatSimple::parse(state, field_pat).ok()
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
Self::tuple(
|
||||
state,
|
||||
MatchPatTuple {
|
||||
paren_token,
|
||||
fields,
|
||||
},
|
||||
)
|
||||
}
|
||||
Pat::Slice(_) | Pat::Const(_) | Pat::Lit(_) | Pat::Range(_) => {
|
||||
state
|
||||
.errors
|
||||
.error(pat, "not yet implemented in #[hdl] patterns");
|
||||
|
@ -497,6 +658,14 @@ impl ParseMatchPat for MatchPatSimple {
|
|||
Err(())
|
||||
}
|
||||
|
||||
fn tuple(state: &mut HdlMatchParseState<'_>, v: MatchPatTuple) -> Result<Self, ()> {
|
||||
state.errors.push(syn::Error::new(
|
||||
v.paren_token.span.open(),
|
||||
"matching tuples is not yet implemented inside structs/enums in #[hdl] patterns",
|
||||
));
|
||||
Err(())
|
||||
}
|
||||
|
||||
fn enum_variant(
|
||||
state: &mut HdlMatchParseState<'_>,
|
||||
v: MatchPatEnumVariant,
|
||||
|
@ -515,6 +684,7 @@ enum MatchPat {
|
|||
Or(MatchPatOr<MatchPat>),
|
||||
Paren(MatchPatParen<MatchPat>),
|
||||
Struct(MatchPatStruct),
|
||||
Tuple(MatchPatTuple),
|
||||
EnumVariant(MatchPatEnumVariant),
|
||||
}
|
||||
|
||||
|
@ -524,6 +694,7 @@ impl_fold! {
|
|||
Or(MatchPatOr<MatchPat>),
|
||||
Paren(MatchPatParen<MatchPat>),
|
||||
Struct(MatchPatStruct),
|
||||
Tuple(MatchPatTuple),
|
||||
EnumVariant(MatchPatEnumVariant),
|
||||
}
|
||||
}
|
||||
|
@ -545,6 +716,10 @@ impl ParseMatchPat for MatchPat {
|
|||
Ok(Self::Struct(v))
|
||||
}
|
||||
|
||||
fn tuple(_state: &mut HdlMatchParseState<'_>, v: MatchPatTuple) -> Result<Self, ()> {
|
||||
Ok(Self::Tuple(v))
|
||||
}
|
||||
|
||||
fn enum_variant(
|
||||
_state: &mut HdlMatchParseState<'_>,
|
||||
v: MatchPatEnumVariant,
|
||||
|
@ -560,6 +735,7 @@ impl ToTokens for MatchPat {
|
|||
Self::Or(v) => v.to_tokens(tokens),
|
||||
Self::Paren(v) => v.to_tokens(tokens),
|
||||
Self::Struct(v) => v.to_tokens(tokens),
|
||||
Self::Tuple(v) => v.to_tokens(tokens),
|
||||
Self::EnumVariant(v) => v.to_tokens(tokens),
|
||||
}
|
||||
}
|
||||
|
@ -622,10 +798,6 @@ struct RewriteAsCheckMatch {
|
|||
}
|
||||
|
||||
impl Fold for RewriteAsCheckMatch {
|
||||
fn fold_field_pat(&mut self, mut i: FieldPat) -> FieldPat {
|
||||
i.colon_token = Some(Token));
|
||||
i
|
||||
}
|
||||
fn fold_pat(&mut self, pat: Pat) -> Pat {
|
||||
match pat {
|
||||
Pat::Ident(mut pat_ident) => match parse_enum_ident(pat_ident.ident) {
|
||||
|
@ -740,6 +912,30 @@ impl Fold for RewriteAsCheckMatch {
|
|||
// don't recurse into expressions
|
||||
i
|
||||
}
|
||||
fn fold_local(&mut self, mut let_stmt: Local) -> Local {
|
||||
if let Some(syn::LocalInit {
|
||||
eq_token,
|
||||
expr: _,
|
||||
diverge,
|
||||
}) = let_stmt.init.take()
|
||||
{
|
||||
let_stmt.init = Some(syn::LocalInit {
|
||||
eq_token,
|
||||
expr: parse_quote_spanned! {self.span=>
|
||||
__match_value
|
||||
},
|
||||
diverge: diverge.map(|(else_, _expr)| {
|
||||
(
|
||||
else_,
|
||||
parse_quote_spanned! {self.span=>
|
||||
match __infallible {}
|
||||
},
|
||||
)
|
||||
}),
|
||||
});
|
||||
}
|
||||
fold_local(self, let_stmt)
|
||||
}
|
||||
}
|
||||
|
||||
struct HdlMatchParseState<'a> {
|
||||
|
@ -747,7 +943,123 @@ struct HdlMatchParseState<'a> {
|
|||
errors: &'a mut Errors,
|
||||
}
|
||||
|
||||
struct HdlLetPatVisitState<'a> {
|
||||
errors: &'a mut Errors,
|
||||
bindings: BTreeSet<&'a Ident>,
|
||||
}
|
||||
|
||||
impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> {
|
||||
fn visit_match_pat_binding(&mut self, v: &'a MatchPatBinding) {
|
||||
self.bindings.insert(&v.ident);
|
||||
}
|
||||
|
||||
fn visit_match_pat_or(&mut self, v: &'a MatchPatOr<MatchPat>) {
|
||||
if let Some(first_inner_vert) = v.first_inner_vert() {
|
||||
self.errors.error(
|
||||
first_inner_vert,
|
||||
"or-patterns are not supported in let statements",
|
||||
);
|
||||
}
|
||||
visit_match_pat_or(self, v);
|
||||
}
|
||||
|
||||
fn visit_match_pat_or_simple(&mut self, v: &'a MatchPatOr<MatchPatSimple>) {
|
||||
if let Some(first_inner_vert) = v.first_inner_vert() {
|
||||
self.errors.error(
|
||||
first_inner_vert,
|
||||
"or-patterns are not supported in let statements",
|
||||
);
|
||||
}
|
||||
visit_match_pat_or_simple(self, v);
|
||||
}
|
||||
|
||||
fn visit_match_pat_enum_variant(&mut self, v: &'a MatchPatEnumVariant) {
|
||||
self.errors.error(v, "refutable pattern in let statement");
|
||||
}
|
||||
}
|
||||
|
||||
impl Visitor<'_> {
|
||||
pub(crate) fn process_hdl_let_pat(
|
||||
&mut self,
|
||||
_hdl_attr: HdlAttr<Nothing, kw::hdl>,
|
||||
mut let_stmt: Local,
|
||||
) -> Local {
|
||||
let span = let_stmt.let_token.span();
|
||||
if let Pat::Type(pat) = &mut let_stmt.pat {
|
||||
*pat.ty = wrap_ty_with_expr((*pat.ty).clone());
|
||||
}
|
||||
let check_let_stmt = RewriteAsCheckMatch { span }.fold_local(let_stmt.clone());
|
||||
let Local {
|
||||
attrs: _,
|
||||
let_token,
|
||||
pat,
|
||||
init,
|
||||
semi_token,
|
||||
} = let_stmt;
|
||||
self.require_normal_module_or_fn(let_token);
|
||||
let Some(syn::LocalInit {
|
||||
eq_token,
|
||||
expr,
|
||||
diverge,
|
||||
}) = init
|
||||
else {
|
||||
self.errors
|
||||
.error(let_token, "#[hdl] let must be assigned a value");
|
||||
return empty_let();
|
||||
};
|
||||
if let Some((else_, _)) = diverge {
|
||||
// TODO: implement let-else
|
||||
self.errors
|
||||
.error(else_, "#[hdl] let ... else { ... } is not implemented");
|
||||
return empty_let();
|
||||
}
|
||||
let Ok(pat) = MatchPat::parse(
|
||||
&mut HdlMatchParseState {
|
||||
match_span: span,
|
||||
errors: &mut self.errors,
|
||||
},
|
||||
pat,
|
||||
) else {
|
||||
return empty_let();
|
||||
};
|
||||
let mut state = HdlLetPatVisitState {
|
||||
errors: &mut self.errors,
|
||||
bindings: BTreeSet::new(),
|
||||
};
|
||||
state.visit_match_pat(&pat);
|
||||
let HdlLetPatVisitState {
|
||||
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 ::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,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
pub(crate) fn process_hdl_match(
|
||||
&mut self,
|
||||
_hdl_attr: HdlAttr<Nothing, kw::hdl>,
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// See Notices.txt for copyright information
|
||||
//! ## `#[hdl] let` statements
|
||||
|
||||
pub mod destructuring;
|
||||
pub mod inputs_outputs;
|
||||
pub mod instances;
|
||||
pub mod memories;
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
//! ### Destructuring Let
|
||||
//!
|
||||
//! You can use `#[hdl] let` to destructure types, similarly to Rust `let` statements with non-trivial patterns.
|
||||
//!
|
||||
//! `#[hdl] let` statements can only match one level of struct/tuple pattern for now,
|
||||
//! e.g. you can match with the pattern `MyStruct { a, b }`, but not `MyStruct { a, b: Struct2 { v } }`.
|
||||
//!
|
||||
//! ```
|
||||
//! # use fayalite::prelude::*;
|
||||
//! #[hdl]
|
||||
//! struct MyStruct {
|
||||
//! a: UInt<8>,
|
||||
//! b: Bool,
|
||||
//! }
|
||||
//!
|
||||
//! #[hdl_module]
|
||||
//! fn my_module() {
|
||||
//! #[hdl]
|
||||
//! let my_input: MyStruct = m.input();
|
||||
//! #[hdl]
|
||||
//! let my_output: UInt<8> = m.input();
|
||||
//! #[hdl]
|
||||
//! let MyStruct { a, b } = my_input;
|
||||
//! #[hdl]
|
||||
//! if b {
|
||||
//! connect(my_output, a);
|
||||
//! } else {
|
||||
//! connect(my_output, 0_hdl_u8);
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
|
@ -7,5 +7,5 @@
|
|||
//!
|
||||
//! `#[hdl] match` statements' bodies must evaluate to type `()` for now.
|
||||
//!
|
||||
//! `#[hdl] match` statements can only match one level of struct/enum pattern for now,
|
||||
//! `#[hdl] match` statements can only match one level of struct/tuple/enum pattern for now,
|
||||
//! e.g. you can match with the pattern `HdlSome(v)`, but not `HdlSome(HdlSome(_))`.
|
||||
|
|
|
@ -2,8 +2,11 @@
|
|||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::{
|
||||
expr::{ops::ArrayIndex, Expr, ToExpr},
|
||||
int::{DynSize, KnownSize, Size, SizeType, DYN_SIZE},
|
||||
expr::{
|
||||
ops::{ArrayIndex, ArrayLiteral, ExprPartialEq},
|
||||
CastToBits, Expr, HdlPartialEq, ReduceBits, ToExpr,
|
||||
},
|
||||
int::{Bool, DynSize, KnownSize, Size, SizeType, DYN_SIZE},
|
||||
intern::{Intern, Interned, LazyInterned},
|
||||
module::transform::visit::{Fold, Folder, Visit, Visitor},
|
||||
source_location::SourceLocation,
|
||||
|
@ -218,3 +221,36 @@ impl<T: Type, L: SizeType> Index<L> for ArrayWithoutLen<T> {
|
|||
Interned::into_inner(Intern::intern_sized(ArrayType::new(self.element, len)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<Lhs: Type, Rhs: Type, Len: Size> ExprPartialEq<ArrayType<Rhs, Len>> for ArrayType<Lhs, Len>
|
||||
where
|
||||
Lhs: ExprPartialEq<Rhs>,
|
||||
{
|
||||
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<ArrayType<Rhs, Len>>) -> Expr<Bool> {
|
||||
let lhs_ty = Expr::ty(lhs);
|
||||
let rhs_ty = Expr::ty(rhs);
|
||||
assert_eq!(lhs_ty.len(), rhs_ty.len());
|
||||
ArrayLiteral::<Bool, DynSize>::new(
|
||||
Bool,
|
||||
(0..lhs_ty.len())
|
||||
.map(|i| Expr::canonical(lhs[i].cmp_eq(rhs[i])))
|
||||
.collect(),
|
||||
)
|
||||
.cast_to_bits()
|
||||
.all_one_bits()
|
||||
}
|
||||
|
||||
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<ArrayType<Rhs, Len>>) -> Expr<Bool> {
|
||||
let lhs_ty = Expr::ty(lhs);
|
||||
let rhs_ty = Expr::ty(rhs);
|
||||
assert_eq!(lhs_ty.len(), rhs_ty.len());
|
||||
ArrayLiteral::<Bool, DynSize>::new(
|
||||
Bool,
|
||||
(0..lhs_ty.len())
|
||||
.map(|i| Expr::canonical(lhs[i].cmp_ne(rhs[i])))
|
||||
.collect(),
|
||||
)
|
||||
.cast_to_bits()
|
||||
.any_one_bits()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,11 @@
|
|||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::{
|
||||
expr::{ops::BundleLiteral, Expr, ToExpr},
|
||||
expr::{
|
||||
ops::{ArrayLiteral, BundleLiteral, ExprPartialEq},
|
||||
CastToBits, Expr, ReduceBits, ToExpr,
|
||||
},
|
||||
int::{Bool, DynSize},
|
||||
intern::{Intern, Interned},
|
||||
sim::{SimValue, ToSimValue},
|
||||
source_location::SourceLocation,
|
||||
|
@ -325,7 +329,19 @@ macro_rules! impl_tuple_builder_fields {
|
|||
}
|
||||
|
||||
macro_rules! impl_tuples {
|
||||
([$({#[num = $num:literal, field = $field:ident, ty = $ty_var:ident: $Ty:ident] $var:ident: $T:ident})*] []) => {
|
||||
(
|
||||
[$({
|
||||
#[
|
||||
num = $num:tt,
|
||||
field = $field:ident,
|
||||
ty = $ty_var:ident: $Ty:ident,
|
||||
lhs = $lhs_var:ident: $Lhs:ident,
|
||||
rhs = $rhs_var:ident: $Rhs:ident
|
||||
]
|
||||
$var:ident: $T:ident
|
||||
})*]
|
||||
[]
|
||||
) => {
|
||||
impl_tuple_builder_fields! {
|
||||
{}
|
||||
[$({
|
||||
|
@ -498,6 +514,29 @@ macro_rules! impl_tuples {
|
|||
Self::into_sim_value(*self, ty)
|
||||
}
|
||||
}
|
||||
impl<$($Lhs: Type + ExprPartialEq<$Rhs>, $Rhs: Type,)*> ExprPartialEq<($($Rhs,)*)> for ($($Lhs,)*) {
|
||||
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> {
|
||||
let ($($lhs_var,)*) = *lhs;
|
||||
let ($($rhs_var,)*) = *rhs;
|
||||
ArrayLiteral::<Bool, DynSize>::new(
|
||||
Bool,
|
||||
FromIterator::from_iter([$(Expr::canonical(ExprPartialEq::cmp_eq($lhs_var, $rhs_var)),)*]),
|
||||
)
|
||||
.cast_to_bits()
|
||||
.all_one_bits()
|
||||
}
|
||||
|
||||
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> {
|
||||
let ($($lhs_var,)*) = *lhs;
|
||||
let ($($rhs_var,)*) = *rhs;
|
||||
ArrayLiteral::<Bool, DynSize>::new(
|
||||
Bool,
|
||||
FromIterator::from_iter([$(Expr::canonical(ExprPartialEq::cmp_ne($lhs_var, $rhs_var)),)*]),
|
||||
)
|
||||
.cast_to_bits()
|
||||
.any_one_bits()
|
||||
}
|
||||
}
|
||||
};
|
||||
([$($lhs:tt)*] [$rhs_first:tt $($rhs:tt)*]) => {
|
||||
impl_tuples!([$($lhs)*] []);
|
||||
|
@ -507,18 +546,18 @@ macro_rules! impl_tuples {
|
|||
|
||||
impl_tuples! {
|
||||
[] [
|
||||
{#[num = 0, field = field_0, ty = ty0: Ty0] v0: T0}
|
||||
{#[num = 1, field = field_1, ty = ty1: Ty1] v1: T1}
|
||||
{#[num = 2, field = field_2, ty = ty2: Ty2] v2: T2}
|
||||
{#[num = 3, field = field_3, ty = ty3: Ty3] v3: T3}
|
||||
{#[num = 4, field = field_4, ty = ty4: Ty4] v4: T4}
|
||||
{#[num = 5, field = field_5, ty = ty5: Ty5] v5: T5}
|
||||
{#[num = 6, field = field_6, ty = ty6: Ty6] v6: T6}
|
||||
{#[num = 7, field = field_7, ty = ty7: Ty7] v7: T7}
|
||||
{#[num = 8, field = field_8, ty = ty8: Ty8] v8: T8}
|
||||
{#[num = 9, field = field_9, ty = ty9: Ty9] v9: T9}
|
||||
{#[num = 10, field = field_10, ty = ty10: Ty10] v10: T10}
|
||||
{#[num = 11, field = field_11, ty = ty11: Ty11] v11: T11}
|
||||
{#[num = 0, field = field_0, ty = ty0: Ty0, lhs = lhs0: Lhs0, rhs = rhs0: Rhs0] v0: T0}
|
||||
{#[num = 1, field = field_1, ty = ty1: Ty1, lhs = lhs1: Lhs1, rhs = rhs1: Rhs1] v1: T1}
|
||||
{#[num = 2, field = field_2, ty = ty2: Ty2, lhs = lhs2: Lhs2, rhs = rhs2: Rhs2] v2: T2}
|
||||
{#[num = 3, field = field_3, ty = ty3: Ty3, lhs = lhs3: Lhs3, rhs = rhs3: Rhs3] v3: T3}
|
||||
{#[num = 4, field = field_4, ty = ty4: Ty4, lhs = lhs4: Lhs4, rhs = rhs4: Rhs4] v4: T4}
|
||||
{#[num = 5, field = field_5, ty = ty5: Ty5, lhs = lhs5: Lhs5, rhs = rhs5: Rhs5] v5: T5}
|
||||
{#[num = 6, field = field_6, ty = ty6: Ty6, lhs = lhs6: Lhs6, rhs = rhs6: Rhs6] v6: T6}
|
||||
{#[num = 7, field = field_7, ty = ty7: Ty7, lhs = lhs7: Lhs7, rhs = rhs7: Rhs7] v7: T7}
|
||||
{#[num = 8, field = field_8, ty = ty8: Ty8, lhs = lhs8: Lhs8, rhs = rhs8: Rhs8] v8: T8}
|
||||
{#[num = 9, field = field_9, ty = ty9: Ty9, lhs = lhs9: Lhs9, rhs = rhs9: Rhs9] v9: T9}
|
||||
{#[num = 10, field = field_10, ty = ty10: Ty10, lhs = lhs10: Lhs10, rhs = rhs10: Rhs10] v10: T10}
|
||||
{#[num = 11, field = field_11, ty = ty11: Ty11, lhs = lhs11: Lhs11, rhs = rhs11: Rhs11] v11: T11}
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,10 @@
|
|||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::{
|
||||
expr::{ops::VariantAccess, Expr, ToExpr},
|
||||
expr::{
|
||||
ops::{ExprPartialEq, VariantAccess},
|
||||
Expr, ToExpr,
|
||||
},
|
||||
hdl,
|
||||
int::Bool,
|
||||
intern::{Intern, Interned},
|
||||
|
@ -360,6 +363,60 @@ pub enum HdlOption<T: Type> {
|
|||
HdlSome(T),
|
||||
}
|
||||
|
||||
impl<Lhs: Type + ExprPartialEq<Rhs>, Rhs: Type> ExprPartialEq<HdlOption<Rhs>> for HdlOption<Lhs> {
|
||||
#[hdl]
|
||||
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<HdlOption<Rhs>>) -> Expr<Bool> {
|
||||
#[hdl]
|
||||
let cmp_eq = wire();
|
||||
#[hdl]
|
||||
match lhs {
|
||||
HdlSome(lhs) =>
|
||||
{
|
||||
#[hdl]
|
||||
match rhs {
|
||||
HdlSome(rhs) => connect(cmp_eq, ExprPartialEq::cmp_eq(lhs, rhs)),
|
||||
HdlNone => connect(cmp_eq, false),
|
||||
}
|
||||
}
|
||||
HdlNone =>
|
||||
{
|
||||
#[hdl]
|
||||
match rhs {
|
||||
HdlSome(_) => connect(cmp_eq, false),
|
||||
HdlNone => connect(cmp_eq, true),
|
||||
}
|
||||
}
|
||||
}
|
||||
cmp_eq
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<HdlOption<Rhs>>) -> Expr<Bool> {
|
||||
#[hdl]
|
||||
let cmp_ne = wire();
|
||||
#[hdl]
|
||||
match lhs {
|
||||
HdlSome(lhs) =>
|
||||
{
|
||||
#[hdl]
|
||||
match rhs {
|
||||
HdlSome(rhs) => connect(cmp_ne, ExprPartialEq::cmp_ne(lhs, rhs)),
|
||||
HdlNone => connect(cmp_ne, true),
|
||||
}
|
||||
}
|
||||
HdlNone =>
|
||||
{
|
||||
#[hdl]
|
||||
match rhs {
|
||||
HdlSome(_) => connect(cmp_ne, true),
|
||||
HdlNone => connect(cmp_ne, false),
|
||||
}
|
||||
}
|
||||
}
|
||||
cmp_ne
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn HdlNone<T: StaticType>() -> Expr<HdlOption<T>> {
|
||||
HdlOption[T::TYPE].HdlNone()
|
||||
|
|
|
@ -567,12 +567,12 @@ impl_prim_int!(i64, SInt<64>);
|
|||
impl_prim_int!(i128, SInt<128>);
|
||||
|
||||
impl_prim_int!(
|
||||
/// for portability reasons, [`usize`] always translates to [`UInt<64>`]
|
||||
/// for portability reasons, [`usize`] always translates to [`UInt<64>`][type@UInt]
|
||||
usize, UInt<64>
|
||||
);
|
||||
|
||||
impl_prim_int!(
|
||||
/// for portability reasons, [`isize`] always translates to [`SInt<64>`]
|
||||
/// for portability reasons, [`isize`] always translates to [`SInt<64>`][type@SInt]
|
||||
isize, SInt<64>
|
||||
);
|
||||
|
||||
|
|
|
@ -4773,6 +4773,9 @@ impl Compiler {
|
|||
}
|
||||
self.insns.extend(insns.iter().copied(), *source_location);
|
||||
}
|
||||
for CondStackEntry { cond: _, end_label } in cond_stack {
|
||||
self.insns.define_label_at_next_insn(end_label);
|
||||
}
|
||||
}
|
||||
fn process_clocks(&mut self) -> Interned<[StatePartIndex<StatePartKindSmallSlots>]> {
|
||||
mem::take(&mut self.clock_triggers)
|
||||
|
|
|
@ -5,6 +5,7 @@ use crate::{
|
|||
enum_::{Enum, EnumType},
|
||||
expr::Flow,
|
||||
int::UInt,
|
||||
intern::{Intern, Interned},
|
||||
sim::{
|
||||
time::{SimDuration, SimInstant},
|
||||
TraceArray, TraceAsyncReset, TraceBool, TraceBundle, TraceClock, TraceDecl,
|
||||
|
@ -15,12 +16,73 @@ use crate::{
|
|||
},
|
||||
};
|
||||
use bitvec::{order::Lsb0, slice::BitSlice};
|
||||
use hashbrown::{hash_map::Entry, HashMap};
|
||||
use std::{
|
||||
fmt,
|
||||
io::{self, Write},
|
||||
mem,
|
||||
fmt::{self, Write as _},
|
||||
io, mem,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
struct Scope {
|
||||
last_inserted: HashMap<Interned<str>, usize>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct VerilogIdentifier {
|
||||
unescaped_name: Interned<str>,
|
||||
}
|
||||
|
||||
impl VerilogIdentifier {
|
||||
fn needs_escape(self) -> bool {
|
||||
// we only allow ascii, so we can just check bytes
|
||||
let Some((&first, rest)) = self.unescaped_name.as_bytes().split_first() else {
|
||||
unreachable!("Scope::new_identifier guarantees a non-empty name");
|
||||
};
|
||||
if !first.is_ascii_alphabetic() && first != b'_' {
|
||||
true
|
||||
} else {
|
||||
rest.iter()
|
||||
.any(|&ch| !ch.is_ascii_alphanumeric() && ch != b'_' && ch != b'$')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for VerilogIdentifier {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.needs_escape() {
|
||||
f.write_str("\\")?;
|
||||
}
|
||||
write!(f, "{}", Escaped(self.unescaped_name))
|
||||
}
|
||||
}
|
||||
|
||||
impl Scope {
|
||||
fn new_identifier(&mut self, unescaped_name: Interned<str>) -> VerilogIdentifier {
|
||||
let next_disambiguator = match self.last_inserted.entry(unescaped_name) {
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(1);
|
||||
return VerilogIdentifier { unescaped_name };
|
||||
}
|
||||
Entry::Occupied(entry) => entry.get() + 1,
|
||||
};
|
||||
let mut disambiguated_name = String::from(&*unescaped_name);
|
||||
for disambiguator in next_disambiguator.. {
|
||||
disambiguated_name.truncate(unescaped_name.len());
|
||||
write!(disambiguated_name, "_{disambiguator}").expect("can't fail");
|
||||
if let Entry::Vacant(entry) = self.last_inserted.entry((*disambiguated_name).intern()) {
|
||||
let retval = VerilogIdentifier {
|
||||
unescaped_name: *entry.key(),
|
||||
};
|
||||
entry.insert(1);
|
||||
// speed up future searches
|
||||
self.last_inserted.insert(unescaped_name, disambiguator);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
panic!("too many names");
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VcdWriterDecls<W: io::Write + 'static> {
|
||||
writer: W,
|
||||
timescale: SimDuration,
|
||||
|
@ -97,14 +159,20 @@ impl<W: io::Write> fmt::Debug for VcdWriterDecls<W> {
|
|||
}
|
||||
}
|
||||
|
||||
/// pass in scope to ensure it's not available in child scope
|
||||
fn write_vcd_scope<W: io::Write, R>(
|
||||
writer: &mut W,
|
||||
scope_type: &str,
|
||||
scope_name: &str,
|
||||
f: impl FnOnce(&mut W) -> io::Result<R>,
|
||||
scope_name: Interned<str>,
|
||||
scope: &mut Scope,
|
||||
f: impl FnOnce(&mut W, &mut Scope) -> io::Result<R>,
|
||||
) -> io::Result<R> {
|
||||
writeln!(writer, "$scope {scope_type} {scope_name} $end")?;
|
||||
let retval = f(writer)?;
|
||||
writeln!(
|
||||
writer,
|
||||
"$scope {scope_type} {} $end",
|
||||
scope.new_identifier(scope_name),
|
||||
)?;
|
||||
let retval = f(writer, &mut Scope::default())?;
|
||||
writeln!(writer, "$upscope $end")?;
|
||||
Ok(retval)
|
||||
}
|
||||
|
@ -143,24 +211,28 @@ trait_arg! {
|
|||
|
||||
struct ArgModule<'a> {
|
||||
properties: &'a mut VcdWriterProperties,
|
||||
scope: &'a mut Scope,
|
||||
}
|
||||
|
||||
impl<'a> ArgModule<'a> {
|
||||
fn reborrow(&mut self) -> ArgModule<'_> {
|
||||
ArgModule {
|
||||
properties: self.properties,
|
||||
scope: self.scope,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ArgModuleBody<'a> {
|
||||
properties: &'a mut VcdWriterProperties,
|
||||
scope: &'a mut Scope,
|
||||
}
|
||||
|
||||
impl<'a> ArgModuleBody<'a> {
|
||||
fn reborrow(&mut self) -> ArgModuleBody<'_> {
|
||||
ArgModuleBody {
|
||||
properties: self.properties,
|
||||
scope: self.scope,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -170,6 +242,7 @@ struct ArgInType<'a> {
|
|||
sink_var_type: &'static str,
|
||||
duplex_var_type: &'static str,
|
||||
properties: &'a mut VcdWriterProperties,
|
||||
scope: &'a mut Scope,
|
||||
}
|
||||
|
||||
impl<'a> ArgInType<'a> {
|
||||
|
@ -179,6 +252,7 @@ impl<'a> ArgInType<'a> {
|
|||
sink_var_type: self.sink_var_type,
|
||||
duplex_var_type: self.duplex_var_type,
|
||||
properties: self.properties,
|
||||
scope: self.scope,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -226,55 +300,42 @@ fn write_vcd_id<W: io::Write>(writer: &mut W, mut id: usize) -> io::Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn write_escaped<W: io::Write>(writer: &mut W, value: impl fmt::Display) -> io::Result<()> {
|
||||
// escaping rules from function GTKWave uses to decode VCD strings:
|
||||
// https://github.com/gtkwave/gtkwave/blob/491f24d7e8619cfc1fcc65704ee5c967d1083c18/lib/libfst/fstapi.c#L7090
|
||||
struct Wrapper<W>(W);
|
||||
impl<W: io::Write> io::Write for Wrapper<W> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
if buf.is_empty() {
|
||||
return self.0.write(buf);
|
||||
}
|
||||
let mut retval = 0;
|
||||
for &byte in buf {
|
||||
match byte {
|
||||
b'\\' | b'\'' | b'"' | b'?' => self.0.write_all(&[b'\\', byte])?,
|
||||
b'\n' => self.0.write_all(br"\n")?,
|
||||
b'\r' => self.0.write_all(br"\r")?,
|
||||
b'\t' => self.0.write_all(br"\t")?,
|
||||
0x7 => self.0.write_all(br"\a")?,
|
||||
0x8 => self.0.write_all(br"\b")?,
|
||||
0xC => self.0.write_all(br"\f")?,
|
||||
0xB => self.0.write_all(br"\v")?,
|
||||
_ => {
|
||||
if byte.is_ascii_graphic() {
|
||||
self.0.write_all(&[byte])?;
|
||||
} else {
|
||||
write!(self.0, r"\x{byte:02x}")?;
|
||||
struct Escaped<T: fmt::Display>(T);
|
||||
|
||||
impl<T: fmt::Display> fmt::Display for Escaped<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// escaping rules from function GTKWave uses to decode VCD strings:
|
||||
// https://github.com/gtkwave/gtkwave/blob/491f24d7e8619cfc1fcc65704ee5c967d1083c18/lib/libfst/fstapi.c#L7090
|
||||
struct Wrapper<W>(W);
|
||||
impl<W: fmt::Write> fmt::Write for Wrapper<W> {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
for byte in s.bytes() {
|
||||
match byte {
|
||||
b'\\' | b'\'' | b'"' | b'?' => {
|
||||
self.0.write_str("\\")?;
|
||||
self.0.write_char(byte as char)?;
|
||||
}
|
||||
b'\n' => self.0.write_str(r"\n")?,
|
||||
b'\r' => self.0.write_str(r"\r")?,
|
||||
b'\t' => self.0.write_str(r"\t")?,
|
||||
0x7 => self.0.write_str(r"\a")?,
|
||||
0x8 => self.0.write_str(r"\b")?,
|
||||
0xC => self.0.write_str(r"\f")?,
|
||||
0xB => self.0.write_str(r"\v")?,
|
||||
_ => {
|
||||
if byte.is_ascii_graphic() {
|
||||
self.0.write_char(byte as char)?;
|
||||
} else {
|
||||
write!(self.0, r"\x{byte:02x}")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
retval += 1;
|
||||
Ok(())
|
||||
}
|
||||
Ok(retval)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.0.flush()
|
||||
}
|
||||
write!(Wrapper(f), "{}", self.0)
|
||||
}
|
||||
write!(Wrapper(writer), "{value}")
|
||||
}
|
||||
|
||||
fn is_unescaped_verilog_identifier(ident: &str) -> bool {
|
||||
// we only allow ascii, so we can just check bytes
|
||||
let Some((&first, rest)) = ident.as_bytes().split_first() else {
|
||||
return false; // empty string is not an identifier
|
||||
};
|
||||
(first.is_ascii_alphabetic() || first == b'_')
|
||||
&& rest
|
||||
.iter()
|
||||
.all(|&ch| ch.is_ascii_alphanumeric() || ch == b'_' || ch == b'$')
|
||||
}
|
||||
|
||||
fn write_vcd_var<W: io::Write>(
|
||||
|
@ -284,7 +345,7 @@ fn write_vcd_var<W: io::Write>(
|
|||
var_type: &str,
|
||||
size: usize,
|
||||
location: TraceLocation,
|
||||
name: &str,
|
||||
name: VerilogIdentifier,
|
||||
) -> io::Result<()> {
|
||||
let id = match location {
|
||||
TraceLocation::Scalar(id) => id.as_usize(),
|
||||
|
@ -319,12 +380,7 @@ fn write_vcd_var<W: io::Write>(
|
|||
};
|
||||
write!(writer, "$var {var_type} {size} ")?;
|
||||
write_vcd_id(writer, id)?;
|
||||
writer.write_all(b" ")?;
|
||||
if !is_unescaped_verilog_identifier(name) {
|
||||
writer.write_all(b"\\")?;
|
||||
}
|
||||
write_escaped(writer, name)?;
|
||||
writer.write_all(b" $end\n")
|
||||
writeln!(writer, " {name} $end")
|
||||
}
|
||||
|
||||
impl WriteTrace for TraceUInt {
|
||||
|
@ -334,6 +390,7 @@ impl WriteTrace for TraceUInt {
|
|||
sink_var_type,
|
||||
duplex_var_type,
|
||||
properties,
|
||||
scope,
|
||||
} = arg.in_type();
|
||||
let Self {
|
||||
location,
|
||||
|
@ -356,7 +413,7 @@ impl WriteTrace for TraceUInt {
|
|||
var_type,
|
||||
ty.width(),
|
||||
location,
|
||||
&name,
|
||||
scope.new_identifier(name),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -421,6 +478,7 @@ impl WriteTrace for TraceEnumDiscriminant {
|
|||
sink_var_type: _,
|
||||
duplex_var_type: _,
|
||||
properties,
|
||||
scope,
|
||||
} = arg.in_type();
|
||||
let Self {
|
||||
location,
|
||||
|
@ -435,7 +493,7 @@ impl WriteTrace for TraceEnumDiscriminant {
|
|||
"string",
|
||||
1,
|
||||
location,
|
||||
&name,
|
||||
scope.new_identifier(name),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -507,11 +565,11 @@ impl WriteTrace for TraceScope {
|
|||
|
||||
impl WriteTrace for TraceModule {
|
||||
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
|
||||
let ArgModule { properties } = arg.module();
|
||||
let ArgModule { properties, scope } = arg.module();
|
||||
let Self { name, children } = self;
|
||||
write_vcd_scope(writer, "module", &name, |writer| {
|
||||
write_vcd_scope(writer, "module", name, scope, |writer, scope| {
|
||||
for child in children {
|
||||
child.write_trace(writer, ArgModuleBody { properties })?;
|
||||
child.write_trace(writer, ArgModuleBody { properties, scope })?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
|
@ -520,7 +578,7 @@ impl WriteTrace for TraceModule {
|
|||
|
||||
impl WriteTrace for TraceInstance {
|
||||
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
|
||||
let ArgModuleBody { properties } = arg.module_body();
|
||||
let ArgModuleBody { properties, scope } = arg.module_body();
|
||||
let Self {
|
||||
name: _,
|
||||
instance_io,
|
||||
|
@ -534,15 +592,16 @@ impl WriteTrace for TraceInstance {
|
|||
sink_var_type: "wire",
|
||||
duplex_var_type: "wire",
|
||||
properties,
|
||||
scope,
|
||||
},
|
||||
)?;
|
||||
module.write_trace(writer, ArgModule { properties })
|
||||
module.write_trace(writer, ArgModule { properties, scope })
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteTrace for TraceMem {
|
||||
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
|
||||
let ArgModuleBody { properties } = arg.module_body();
|
||||
let ArgModuleBody { properties, scope } = arg.module_body();
|
||||
let Self {
|
||||
id,
|
||||
name,
|
||||
|
@ -551,27 +610,41 @@ impl WriteTrace for TraceMem {
|
|||
ports,
|
||||
array_type,
|
||||
} = self;
|
||||
write_vcd_scope(writer, "struct", &*name, |writer| {
|
||||
write_vcd_scope(writer, "struct", "contents", |writer| {
|
||||
for element_index in 0..array_type.len() {
|
||||
write_vcd_scope(writer, "struct", &format!("[{element_index}]"), |writer| {
|
||||
properties.memory_properties[id.as_usize()].element_index = element_index;
|
||||
properties.memory_properties[id.as_usize()].element_part_index = 0;
|
||||
element_type.write_trace(
|
||||
write_vcd_scope(writer, "struct", name, scope, |writer, scope| {
|
||||
write_vcd_scope(
|
||||
writer,
|
||||
"struct",
|
||||
"contents".intern(),
|
||||
scope,
|
||||
|writer, scope| {
|
||||
for element_index in 0..array_type.len() {
|
||||
write_vcd_scope(
|
||||
writer,
|
||||
ArgInType {
|
||||
source_var_type: "reg",
|
||||
sink_var_type: "reg",
|
||||
duplex_var_type: "reg",
|
||||
properties,
|
||||
"struct",
|
||||
Intern::intern_owned(format!("[{element_index}]")),
|
||||
scope,
|
||||
|writer, scope| {
|
||||
properties.memory_properties[id.as_usize()].element_index =
|
||||
element_index;
|
||||
properties.memory_properties[id.as_usize()].element_part_index = 0;
|
||||
element_type.write_trace(
|
||||
writer,
|
||||
ArgInType {
|
||||
source_var_type: "reg",
|
||||
sink_var_type: "reg",
|
||||
duplex_var_type: "reg",
|
||||
properties,
|
||||
scope,
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
for port in ports {
|
||||
port.write_trace(writer, ArgModuleBody { properties })?;
|
||||
port.write_trace(writer, ArgModuleBody { properties, scope })?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
|
@ -580,7 +653,7 @@ impl WriteTrace for TraceMem {
|
|||
|
||||
impl WriteTrace for TraceMemPort {
|
||||
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
|
||||
let ArgModuleBody { properties } = arg.module_body();
|
||||
let ArgModuleBody { properties, scope } = arg.module_body();
|
||||
let Self {
|
||||
name: _,
|
||||
bundle,
|
||||
|
@ -593,6 +666,7 @@ impl WriteTrace for TraceMemPort {
|
|||
sink_var_type: "wire",
|
||||
duplex_var_type: "wire",
|
||||
properties,
|
||||
scope,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -600,7 +674,7 @@ impl WriteTrace for TraceMemPort {
|
|||
|
||||
impl WriteTrace for TraceWire {
|
||||
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
|
||||
let ArgModuleBody { properties } = arg.module_body();
|
||||
let ArgModuleBody { properties, scope } = arg.module_body();
|
||||
let Self {
|
||||
name: _,
|
||||
child,
|
||||
|
@ -613,6 +687,7 @@ impl WriteTrace for TraceWire {
|
|||
sink_var_type: "wire",
|
||||
duplex_var_type: "wire",
|
||||
properties,
|
||||
scope,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -620,7 +695,7 @@ impl WriteTrace for TraceWire {
|
|||
|
||||
impl WriteTrace for TraceReg {
|
||||
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
|
||||
let ArgModuleBody { properties } = arg.module_body();
|
||||
let ArgModuleBody { properties, scope } = arg.module_body();
|
||||
let Self {
|
||||
name: _,
|
||||
child,
|
||||
|
@ -633,6 +708,7 @@ impl WriteTrace for TraceReg {
|
|||
sink_var_type: "reg",
|
||||
duplex_var_type: "reg",
|
||||
properties,
|
||||
scope,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -640,7 +716,7 @@ impl WriteTrace for TraceReg {
|
|||
|
||||
impl WriteTrace for TraceModuleIO {
|
||||
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
|
||||
let ArgModuleBody { properties } = arg.module_body();
|
||||
let ArgModuleBody { properties, scope } = arg.module_body();
|
||||
let Self {
|
||||
name: _,
|
||||
child,
|
||||
|
@ -654,6 +730,7 @@ impl WriteTrace for TraceModuleIO {
|
|||
sink_var_type: "wire",
|
||||
duplex_var_type: "wire",
|
||||
properties,
|
||||
scope,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -661,16 +738,31 @@ impl WriteTrace for TraceModuleIO {
|
|||
|
||||
impl WriteTrace for TraceBundle {
|
||||
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
|
||||
let mut arg = arg.in_type();
|
||||
let ArgInType {
|
||||
source_var_type,
|
||||
sink_var_type,
|
||||
duplex_var_type,
|
||||
properties,
|
||||
scope,
|
||||
} = arg.in_type();
|
||||
let Self {
|
||||
name,
|
||||
fields,
|
||||
ty: _,
|
||||
flow: _,
|
||||
} = self;
|
||||
write_vcd_scope(writer, "struct", &name, |writer| {
|
||||
write_vcd_scope(writer, "struct", name, scope, |writer, scope| {
|
||||
for field in fields {
|
||||
field.write_trace(writer, arg.reborrow())?;
|
||||
field.write_trace(
|
||||
writer,
|
||||
ArgInType {
|
||||
source_var_type,
|
||||
sink_var_type,
|
||||
duplex_var_type,
|
||||
properties,
|
||||
scope,
|
||||
},
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
|
@ -679,16 +771,31 @@ impl WriteTrace for TraceBundle {
|
|||
|
||||
impl WriteTrace for TraceArray {
|
||||
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
|
||||
let mut arg = arg.in_type();
|
||||
let ArgInType {
|
||||
source_var_type,
|
||||
sink_var_type,
|
||||
duplex_var_type,
|
||||
properties,
|
||||
scope,
|
||||
} = arg.in_type();
|
||||
let Self {
|
||||
name,
|
||||
elements,
|
||||
ty: _,
|
||||
flow: _,
|
||||
} = self;
|
||||
write_vcd_scope(writer, "struct", &name, |writer| {
|
||||
write_vcd_scope(writer, "struct", name, scope, |writer, scope| {
|
||||
for element in elements {
|
||||
element.write_trace(writer, arg.reborrow())?;
|
||||
element.write_trace(
|
||||
writer,
|
||||
ArgInType {
|
||||
source_var_type,
|
||||
sink_var_type,
|
||||
duplex_var_type,
|
||||
properties,
|
||||
scope,
|
||||
},
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
|
@ -697,7 +804,13 @@ impl WriteTrace for TraceArray {
|
|||
|
||||
impl WriteTrace for TraceEnumWithFields {
|
||||
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
|
||||
let mut arg = arg.in_type();
|
||||
let ArgInType {
|
||||
source_var_type,
|
||||
sink_var_type,
|
||||
duplex_var_type,
|
||||
properties,
|
||||
scope,
|
||||
} = arg.in_type();
|
||||
let Self {
|
||||
name,
|
||||
discriminant,
|
||||
|
@ -705,10 +818,28 @@ impl WriteTrace for TraceEnumWithFields {
|
|||
ty: _,
|
||||
flow: _,
|
||||
} = self;
|
||||
write_vcd_scope(writer, "struct", &name, |writer| {
|
||||
discriminant.write_trace(writer, arg.reborrow())?;
|
||||
write_vcd_scope(writer, "struct", name, scope, |writer, scope| {
|
||||
discriminant.write_trace(
|
||||
writer,
|
||||
ArgInType {
|
||||
source_var_type,
|
||||
sink_var_type,
|
||||
duplex_var_type,
|
||||
properties,
|
||||
scope,
|
||||
},
|
||||
)?;
|
||||
for field in non_empty_fields {
|
||||
field.write_trace(writer, arg.reborrow())?;
|
||||
field.write_trace(
|
||||
writer,
|
||||
ArgInType {
|
||||
source_var_type,
|
||||
sink_var_type,
|
||||
duplex_var_type,
|
||||
properties,
|
||||
scope,
|
||||
},
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
|
@ -744,6 +875,7 @@ impl<W: io::Write> TraceWriterDecls for VcdWriterDecls<W> {
|
|||
&mut writer,
|
||||
ArgModule {
|
||||
properties: &mut properties,
|
||||
scope: &mut Scope::default(),
|
||||
},
|
||||
)?;
|
||||
writeln!(writer, "$enddefinitions $end")?;
|
||||
|
@ -798,9 +930,7 @@ fn write_string_value_change(
|
|||
value: impl fmt::Display,
|
||||
id: usize,
|
||||
) -> io::Result<()> {
|
||||
writer.write_all(b"s")?;
|
||||
write_escaped(writer, value)?;
|
||||
writer.write_all(b" ")?;
|
||||
write!(writer, "s{} ", Escaped(value))?;
|
||||
write_vcd_id(writer, id)?;
|
||||
writer.write_all(b"\n")
|
||||
}
|
||||
|
@ -946,3 +1076,49 @@ impl<W: io::Write> fmt::Debug for VcdWriter<W> {
|
|||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_scope() {
|
||||
let mut scope = Scope::default();
|
||||
assert_eq!(&*scope.new_identifier("foo".intern()).unescaped_name, "foo");
|
||||
assert_eq!(
|
||||
&*scope.new_identifier("foo_0".intern()).unescaped_name,
|
||||
"foo_0"
|
||||
);
|
||||
assert_eq!(
|
||||
&*scope.new_identifier("foo_1".intern()).unescaped_name,
|
||||
"foo_1"
|
||||
);
|
||||
assert_eq!(
|
||||
&*scope.new_identifier("foo_3".intern()).unescaped_name,
|
||||
"foo_3"
|
||||
);
|
||||
assert_eq!(
|
||||
&*scope.new_identifier("foo".intern()).unescaped_name,
|
||||
"foo_2"
|
||||
);
|
||||
assert_eq!(
|
||||
&*scope.new_identifier("foo".intern()).unescaped_name,
|
||||
"foo_4"
|
||||
);
|
||||
assert_eq!(
|
||||
&*scope.new_identifier("foo_0".intern()).unescaped_name,
|
||||
"foo_0_2"
|
||||
);
|
||||
assert_eq!(
|
||||
&*scope.new_identifier("foo_1".intern()).unescaped_name,
|
||||
"foo_1_2"
|
||||
);
|
||||
for i in 5..1000u64 {
|
||||
// verify it actually picks the next available identifier with no skips or duplicates
|
||||
assert_eq!(
|
||||
*scope.new_identifier("foo".intern()).unescaped_name,
|
||||
format!("foo_{i}"),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -191,10 +191,14 @@ circuit check_array_repeat:
|
|||
};
|
||||
}
|
||||
|
||||
pub trait UnknownTrait {}
|
||||
|
||||
impl<T: ?Sized> UnknownTrait for T {}
|
||||
|
||||
#[hdl_module(outline_generated)]
|
||||
pub fn check_skipped_generics<T, #[hdl(skip)] U, const N: usize, #[hdl(skip)] const M: usize>(v: U)
|
||||
where
|
||||
T: StaticType,
|
||||
T: StaticType + UnknownTrait,
|
||||
ConstUsize<N>: KnownSize,
|
||||
U: std::fmt::Display,
|
||||
{
|
||||
|
@ -376,18 +380,18 @@ circuit check_written_inside_both_if_else:
|
|||
};
|
||||
}
|
||||
|
||||
#[hdl(outline_generated)]
|
||||
#[hdl(outline_generated, cmp_eq)]
|
||||
pub struct TestStruct<T> {
|
||||
pub a: T,
|
||||
pub b: UInt<8>,
|
||||
}
|
||||
|
||||
#[hdl(outline_generated)]
|
||||
#[hdl(outline_generated, cmp_eq)]
|
||||
pub struct TestStruct2 {
|
||||
pub v: UInt<8>,
|
||||
}
|
||||
|
||||
#[hdl(outline_generated)]
|
||||
#[hdl(outline_generated, cmp_eq)]
|
||||
pub struct TestStruct3 {}
|
||||
|
||||
#[hdl_module(outline_generated)]
|
||||
|
@ -4345,3 +4349,201 @@ circuit check_cfgs:
|
|||
",
|
||||
};
|
||||
}
|
||||
|
||||
#[hdl_module(outline_generated)]
|
||||
pub fn check_let_patterns() {
|
||||
#[hdl]
|
||||
let tuple_in: (UInt<1>, SInt<1>, Bool) = m.input();
|
||||
#[hdl]
|
||||
let (tuple_0, tuple_1, tuple_2) = tuple_in;
|
||||
#[hdl]
|
||||
let tuple_0_out: UInt<1> = m.output();
|
||||
connect(tuple_0_out, tuple_0);
|
||||
#[hdl]
|
||||
let tuple_1_out: SInt<1> = m.output();
|
||||
connect(tuple_1_out, tuple_1);
|
||||
#[hdl]
|
||||
let tuple_2_out: Bool = m.output();
|
||||
connect(tuple_2_out, tuple_2);
|
||||
|
||||
#[hdl]
|
||||
let test_struct_in: TestStruct<SInt<8>> = m.input();
|
||||
#[hdl]
|
||||
let TestStruct::<_> { a, b } = test_struct_in;
|
||||
#[hdl]
|
||||
let test_struct_a_out: SInt<8> = m.output();
|
||||
connect(test_struct_a_out, a);
|
||||
#[hdl]
|
||||
let test_struct_b_out: UInt<8> = m.output();
|
||||
connect(test_struct_b_out, b);
|
||||
|
||||
#[hdl]
|
||||
let test_struct_2_in: TestStruct2 = m.input();
|
||||
#[hdl]
|
||||
let TestStruct2 { v } = test_struct_2_in;
|
||||
#[hdl]
|
||||
let test_struct_2_v_out: UInt<8> = m.output();
|
||||
connect(test_struct_2_v_out, v);
|
||||
|
||||
#[hdl]
|
||||
let test_struct_3_in: TestStruct3 = m.input();
|
||||
#[hdl]
|
||||
let TestStruct3 {} = test_struct_3_in;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_let_patterns() {
|
||||
let _n = SourceLocation::normalize_files_for_tests();
|
||||
let m = check_let_patterns();
|
||||
dbg!(m);
|
||||
#[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
|
||||
assert_export_firrtl! {
|
||||
m =>
|
||||
"/test/check_let_patterns.fir": r"FIRRTL version 3.2.0
|
||||
circuit check_let_patterns:
|
||||
type Ty0 = {`0`: UInt<1>, `1`: SInt<1>, `2`: UInt<1>}
|
||||
type Ty1 = {a: SInt<8>, b: UInt<8>}
|
||||
type Ty2 = {v: UInt<8>}
|
||||
type Ty3 = {}
|
||||
module check_let_patterns: @[module-XXXXXXXXXX.rs 1:1]
|
||||
input tuple_in: Ty0 @[module-XXXXXXXXXX.rs 2:1]
|
||||
output tuple_0_out: UInt<1> @[module-XXXXXXXXXX.rs 4:1]
|
||||
output tuple_1_out: SInt<1> @[module-XXXXXXXXXX.rs 6:1]
|
||||
output tuple_2_out: UInt<1> @[module-XXXXXXXXXX.rs 8:1]
|
||||
input test_struct_in: Ty1 @[module-XXXXXXXXXX.rs 10:1]
|
||||
output test_struct_a_out: SInt<8> @[module-XXXXXXXXXX.rs 12:1]
|
||||
output test_struct_b_out: UInt<8> @[module-XXXXXXXXXX.rs 14:1]
|
||||
input test_struct_2_in: Ty2 @[module-XXXXXXXXXX.rs 16:1]
|
||||
output test_struct_2_v_out: UInt<8> @[module-XXXXXXXXXX.rs 18:1]
|
||||
input test_struct_3_in: Ty3 @[module-XXXXXXXXXX.rs 20:1]
|
||||
connect tuple_0_out, tuple_in.`0` @[module-XXXXXXXXXX.rs 5:1]
|
||||
connect tuple_1_out, tuple_in.`1` @[module-XXXXXXXXXX.rs 7:1]
|
||||
connect tuple_2_out, tuple_in.`2` @[module-XXXXXXXXXX.rs 9:1]
|
||||
connect test_struct_a_out, test_struct_in.a @[module-XXXXXXXXXX.rs 13:1]
|
||||
connect test_struct_b_out, test_struct_in.b @[module-XXXXXXXXXX.rs 15:1]
|
||||
connect test_struct_2_v_out, test_struct_2_in.v @[module-XXXXXXXXXX.rs 19:1]
|
||||
",
|
||||
};
|
||||
}
|
||||
|
||||
#[hdl_module(outline_generated)]
|
||||
pub fn check_struct_cmp_eq() {
|
||||
#[hdl]
|
||||
let tuple_lhs: (UInt<1>, SInt<1>, Bool) = m.input();
|
||||
#[hdl]
|
||||
let tuple_rhs: (UInt<1>, SInt<1>, Bool) = m.input();
|
||||
#[hdl]
|
||||
let tuple_cmp_eq: Bool = m.output();
|
||||
connect(tuple_cmp_eq, tuple_lhs.cmp_eq(tuple_rhs));
|
||||
#[hdl]
|
||||
let tuple_cmp_ne: Bool = m.output();
|
||||
connect(tuple_cmp_ne, tuple_lhs.cmp_ne(tuple_rhs));
|
||||
|
||||
#[hdl]
|
||||
let test_struct_lhs: TestStruct<SInt<8>> = m.input();
|
||||
#[hdl]
|
||||
let test_struct_rhs: TestStruct<SInt<8>> = m.input();
|
||||
#[hdl]
|
||||
let test_struct_cmp_eq: Bool = m.output();
|
||||
connect(test_struct_cmp_eq, test_struct_lhs.cmp_eq(test_struct_rhs));
|
||||
#[hdl]
|
||||
let test_struct_cmp_ne: Bool = m.output();
|
||||
connect(test_struct_cmp_ne, test_struct_lhs.cmp_ne(test_struct_rhs));
|
||||
|
||||
#[hdl]
|
||||
let test_struct_2_lhs: TestStruct2 = m.input();
|
||||
#[hdl]
|
||||
let test_struct_2_rhs: TestStruct2 = m.input();
|
||||
#[hdl]
|
||||
let test_struct_2_cmp_eq: Bool = m.output();
|
||||
connect(
|
||||
test_struct_2_cmp_eq,
|
||||
test_struct_2_lhs.cmp_eq(test_struct_2_rhs),
|
||||
);
|
||||
#[hdl]
|
||||
let test_struct_2_cmp_ne: Bool = m.output();
|
||||
connect(
|
||||
test_struct_2_cmp_ne,
|
||||
test_struct_2_lhs.cmp_ne(test_struct_2_rhs),
|
||||
);
|
||||
|
||||
#[hdl]
|
||||
let test_struct_3_lhs: TestStruct3 = m.input();
|
||||
#[hdl]
|
||||
let test_struct_3_rhs: TestStruct3 = m.input();
|
||||
#[hdl]
|
||||
let test_struct_3_cmp_eq: Bool = m.output();
|
||||
connect(
|
||||
test_struct_3_cmp_eq,
|
||||
test_struct_3_lhs.cmp_eq(test_struct_3_rhs),
|
||||
);
|
||||
#[hdl]
|
||||
let test_struct_3_cmp_ne: Bool = m.output();
|
||||
connect(
|
||||
test_struct_3_cmp_ne,
|
||||
test_struct_3_lhs.cmp_ne(test_struct_3_rhs),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_struct_cmp_eq() {
|
||||
let _n = SourceLocation::normalize_files_for_tests();
|
||||
let m = check_struct_cmp_eq();
|
||||
dbg!(m);
|
||||
#[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
|
||||
assert_export_firrtl! {
|
||||
m =>
|
||||
"/test/check_struct_cmp_eq.fir": r"FIRRTL version 3.2.0
|
||||
circuit check_struct_cmp_eq:
|
||||
type Ty0 = {`0`: UInt<1>, `1`: SInt<1>, `2`: UInt<1>}
|
||||
type Ty1 = {a: SInt<8>, b: UInt<8>}
|
||||
type Ty2 = {v: UInt<8>}
|
||||
type Ty3 = {}
|
||||
module check_struct_cmp_eq: @[module-XXXXXXXXXX.rs 1:1]
|
||||
input tuple_lhs: Ty0 @[module-XXXXXXXXXX.rs 2:1]
|
||||
input tuple_rhs: Ty0 @[module-XXXXXXXXXX.rs 3:1]
|
||||
output tuple_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 4:1]
|
||||
output tuple_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 6:1]
|
||||
input test_struct_lhs: Ty1 @[module-XXXXXXXXXX.rs 8:1]
|
||||
input test_struct_rhs: Ty1 @[module-XXXXXXXXXX.rs 9:1]
|
||||
output test_struct_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 10:1]
|
||||
output test_struct_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 12:1]
|
||||
input test_struct_2_lhs: Ty2 @[module-XXXXXXXXXX.rs 14:1]
|
||||
input test_struct_2_rhs: Ty2 @[module-XXXXXXXXXX.rs 15:1]
|
||||
output test_struct_2_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 16:1]
|
||||
output test_struct_2_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 18:1]
|
||||
input test_struct_3_lhs: Ty3 @[module-XXXXXXXXXX.rs 20:1]
|
||||
input test_struct_3_rhs: Ty3 @[module-XXXXXXXXXX.rs 21:1]
|
||||
output test_struct_3_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 22:1]
|
||||
output test_struct_3_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 24:1]
|
||||
wire _array_literal_expr: UInt<1>[3]
|
||||
connect _array_literal_expr[0], eq(tuple_lhs.`0`, tuple_rhs.`0`)
|
||||
connect _array_literal_expr[1], eq(tuple_lhs.`1`, tuple_rhs.`1`)
|
||||
connect _array_literal_expr[2], eq(tuple_lhs.`2`, tuple_rhs.`2`)
|
||||
wire _cast_array_to_bits_expr: UInt<1>[3]
|
||||
connect _cast_array_to_bits_expr[0], _array_literal_expr[0]
|
||||
connect _cast_array_to_bits_expr[1], _array_literal_expr[1]
|
||||
connect _cast_array_to_bits_expr[2], _array_literal_expr[2]
|
||||
wire _cast_to_bits_expr: UInt<3>
|
||||
connect _cast_to_bits_expr, cat(_cast_array_to_bits_expr[2], cat(_cast_array_to_bits_expr[1], _cast_array_to_bits_expr[0]))
|
||||
connect tuple_cmp_eq, andr(_cast_to_bits_expr) @[module-XXXXXXXXXX.rs 5:1]
|
||||
wire _array_literal_expr_1: UInt<1>[3]
|
||||
connect _array_literal_expr_1[0], neq(tuple_lhs.`0`, tuple_rhs.`0`)
|
||||
connect _array_literal_expr_1[1], neq(tuple_lhs.`1`, tuple_rhs.`1`)
|
||||
connect _array_literal_expr_1[2], neq(tuple_lhs.`2`, tuple_rhs.`2`)
|
||||
wire _cast_array_to_bits_expr_1: UInt<1>[3]
|
||||
connect _cast_array_to_bits_expr_1[0], _array_literal_expr_1[0]
|
||||
connect _cast_array_to_bits_expr_1[1], _array_literal_expr_1[1]
|
||||
connect _cast_array_to_bits_expr_1[2], _array_literal_expr_1[2]
|
||||
wire _cast_to_bits_expr_1: UInt<3>
|
||||
connect _cast_to_bits_expr_1, cat(_cast_array_to_bits_expr_1[2], cat(_cast_array_to_bits_expr_1[1], _cast_array_to_bits_expr_1[0]))
|
||||
connect tuple_cmp_ne, orr(_cast_to_bits_expr_1) @[module-XXXXXXXXXX.rs 7:1]
|
||||
connect test_struct_cmp_eq, and(eq(test_struct_lhs.a, test_struct_rhs.a), eq(test_struct_lhs.b, test_struct_rhs.b)) @[module-XXXXXXXXXX.rs 11:1]
|
||||
connect test_struct_cmp_ne, or(neq(test_struct_lhs.a, test_struct_rhs.a), neq(test_struct_lhs.b, test_struct_rhs.b)) @[module-XXXXXXXXXX.rs 13:1]
|
||||
connect test_struct_2_cmp_eq, eq(test_struct_2_lhs.v, test_struct_2_rhs.v) @[module-XXXXXXXXXX.rs 17:1]
|
||||
connect test_struct_2_cmp_ne, neq(test_struct_2_lhs.v, test_struct_2_rhs.v) @[module-XXXXXXXXXX.rs 19:1]
|
||||
connect test_struct_3_cmp_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 23:1]
|
||||
connect test_struct_3_cmp_ne, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 25:1]
|
||||
",
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1246,3 +1246,200 @@ fn test_memories3() {
|
|||
panic!();
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl_module(outline_generated)]
|
||||
pub fn duplicate_names() {
|
||||
#[hdl]
|
||||
let w = wire();
|
||||
connect(w, 5u8);
|
||||
#[hdl]
|
||||
let w = wire();
|
||||
connect(w, 6u8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_duplicate_names() {
|
||||
let _n = SourceLocation::normalize_files_for_tests();
|
||||
let mut sim = Simulation::new(duplicate_names());
|
||||
let mut writer = RcWriter::default();
|
||||
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
|
||||
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/duplicate_names.vcd") {
|
||||
panic!();
|
||||
}
|
||||
let sim_debug = format!("{sim:#?}");
|
||||
println!("#######\n{sim_debug}\n#######");
|
||||
if sim_debug != include_str!("sim/expected/duplicate_names.txt") {
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl_module(outline_generated)]
|
||||
pub fn array_rw() {
|
||||
#[hdl]
|
||||
let array_in: Array<UInt<8>, 16> = m.input();
|
||||
#[hdl]
|
||||
let array_out: Array<UInt<8>, 16> = m.output();
|
||||
#[hdl]
|
||||
let read_index: UInt<8> = m.input();
|
||||
#[hdl]
|
||||
let read_data: UInt<8> = m.output();
|
||||
#[hdl]
|
||||
let write_index: UInt<8> = m.input();
|
||||
#[hdl]
|
||||
let write_data: UInt<8> = m.input();
|
||||
#[hdl]
|
||||
let write_en: Bool = m.input();
|
||||
#[hdl]
|
||||
let array_wire = wire();
|
||||
connect(array_wire, array_in);
|
||||
connect(array_out, array_wire);
|
||||
#[hdl]
|
||||
if write_en {
|
||||
connect(array_wire[write_index], write_data);
|
||||
}
|
||||
connect(read_data, array_wire[read_index]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_array_rw() {
|
||||
let _n = SourceLocation::normalize_files_for_tests();
|
||||
let mut sim = Simulation::new(array_rw());
|
||||
let mut writer = RcWriter::default();
|
||||
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct State {
|
||||
array_in: [u8; 16],
|
||||
array_out: [u8; 16],
|
||||
read_index: u8,
|
||||
read_data: u8,
|
||||
write_index: u8,
|
||||
write_data: u8,
|
||||
write_en: bool,
|
||||
}
|
||||
let mut states = Vec::new();
|
||||
let array_in = [
|
||||
0xFFu8, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01, //
|
||||
0x00u8, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE,
|
||||
];
|
||||
for i in 0..=16 {
|
||||
states.push(State {
|
||||
array_in,
|
||||
array_out: array_in,
|
||||
read_index: i,
|
||||
read_data: array_in.get(i as usize).copied().unwrap_or(0),
|
||||
write_index: 0,
|
||||
write_data: 0,
|
||||
write_en: false,
|
||||
});
|
||||
}
|
||||
for i in 0..=16u8 {
|
||||
let mut array_out = array_in;
|
||||
let write_data = i.wrapping_mul(i);
|
||||
if let Some(v) = array_out.get_mut(i as usize) {
|
||||
*v = write_data;
|
||||
}
|
||||
states.push(State {
|
||||
array_in,
|
||||
array_out,
|
||||
read_index: 0,
|
||||
read_data: array_out[0],
|
||||
write_index: i,
|
||||
write_data,
|
||||
write_en: true,
|
||||
});
|
||||
}
|
||||
for (cycle, expected) in states.into_iter().enumerate() {
|
||||
let State {
|
||||
array_in,
|
||||
array_out: _,
|
||||
read_index,
|
||||
read_data: _,
|
||||
write_index,
|
||||
write_data,
|
||||
write_en,
|
||||
} = expected;
|
||||
sim.write(sim.io().array_in, array_in);
|
||||
sim.write(sim.io().read_index, read_index);
|
||||
sim.write(sim.io().write_index, write_index);
|
||||
sim.write(sim.io().write_data, write_data);
|
||||
sim.write(sim.io().write_en, write_en);
|
||||
sim.advance_time(SimDuration::from_micros(1));
|
||||
let array_out = std::array::from_fn(|index| {
|
||||
sim.read_bool_or_int(sim.io().array_out[index])
|
||||
.to_bigint()
|
||||
.try_into()
|
||||
.expect("known to be in range")
|
||||
});
|
||||
let read_data = sim
|
||||
.read_bool_or_int(sim.io().read_data)
|
||||
.to_bigint()
|
||||
.try_into()
|
||||
.expect("known to be in range");
|
||||
let state = State {
|
||||
array_in,
|
||||
array_out,
|
||||
read_index,
|
||||
read_data,
|
||||
write_index,
|
||||
write_data,
|
||||
write_en,
|
||||
};
|
||||
assert_eq!(
|
||||
state,
|
||||
expected,
|
||||
"vcd:\n{}\ncycle: {cycle}",
|
||||
String::from_utf8(writer.take()).unwrap(),
|
||||
);
|
||||
}
|
||||
sim.flush_traces().unwrap();
|
||||
let vcd = String::from_utf8(writer.take()).unwrap();
|
||||
println!("####### VCD:\n{vcd}\n#######");
|
||||
if vcd != include_str!("sim/expected/array_rw.vcd") {
|
||||
panic!();
|
||||
}
|
||||
let sim_debug = format!("{sim:#?}");
|
||||
println!("#######\n{sim_debug}\n#######");
|
||||
if sim_debug != include_str!("sim/expected/array_rw.txt") {
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl_module(outline_generated)]
|
||||
pub fn conditional_assignment_last() {
|
||||
#[hdl]
|
||||
let i: Bool = m.input();
|
||||
#[hdl]
|
||||
let w = wire();
|
||||
connect(w, true);
|
||||
#[hdl]
|
||||
if i {
|
||||
connect(w, false);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conditional_assignment_last() {
|
||||
let _n = SourceLocation::normalize_files_for_tests();
|
||||
let mut sim = Simulation::new(conditional_assignment_last());
|
||||
let mut writer = RcWriter::default();
|
||||
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
|
||||
sim.write(sim.io().i, false);
|
||||
sim.advance_time(SimDuration::from_micros(1));
|
||||
sim.write(sim.io().i, 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/conditional_assignment_last.vcd") {
|
||||
panic!();
|
||||
}
|
||||
let sim_debug = format!("{sim:#?}");
|
||||
println!("#######\n{sim_debug}\n#######");
|
||||
if sim_debug != include_str!("sim/expected/conditional_assignment_last.txt") {
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
|
|
2859
crates/fayalite/tests/sim/expected/array_rw.txt
Normal file
2859
crates/fayalite/tests/sim/expected/array_rw.txt
Normal file
File diff suppressed because it is too large
Load diff
283
crates/fayalite/tests/sim/expected/array_rw.vcd
Normal file
283
crates/fayalite/tests/sim/expected/array_rw.vcd
Normal file
|
@ -0,0 +1,283 @@
|
|||
$timescale 1 ps $end
|
||||
$scope module array_rw $end
|
||||
$scope struct array_in $end
|
||||
$var wire 8 ! \[0] $end
|
||||
$var wire 8 " \[1] $end
|
||||
$var wire 8 # \[2] $end
|
||||
$var wire 8 $ \[3] $end
|
||||
$var wire 8 % \[4] $end
|
||||
$var wire 8 & \[5] $end
|
||||
$var wire 8 ' \[6] $end
|
||||
$var wire 8 ( \[7] $end
|
||||
$var wire 8 ) \[8] $end
|
||||
$var wire 8 * \[9] $end
|
||||
$var wire 8 + \[10] $end
|
||||
$var wire 8 , \[11] $end
|
||||
$var wire 8 - \[12] $end
|
||||
$var wire 8 . \[13] $end
|
||||
$var wire 8 / \[14] $end
|
||||
$var wire 8 0 \[15] $end
|
||||
$upscope $end
|
||||
$scope struct array_out $end
|
||||
$var wire 8 1 \[0] $end
|
||||
$var wire 8 2 \[1] $end
|
||||
$var wire 8 3 \[2] $end
|
||||
$var wire 8 4 \[3] $end
|
||||
$var wire 8 5 \[4] $end
|
||||
$var wire 8 6 \[5] $end
|
||||
$var wire 8 7 \[6] $end
|
||||
$var wire 8 8 \[7] $end
|
||||
$var wire 8 9 \[8] $end
|
||||
$var wire 8 : \[9] $end
|
||||
$var wire 8 ; \[10] $end
|
||||
$var wire 8 < \[11] $end
|
||||
$var wire 8 = \[12] $end
|
||||
$var wire 8 > \[13] $end
|
||||
$var wire 8 ? \[14] $end
|
||||
$var wire 8 @ \[15] $end
|
||||
$upscope $end
|
||||
$var wire 8 A read_index $end
|
||||
$var wire 8 B read_data $end
|
||||
$var wire 8 C write_index $end
|
||||
$var wire 8 D write_data $end
|
||||
$var wire 1 E write_en $end
|
||||
$scope struct array_wire $end
|
||||
$var wire 8 F \[0] $end
|
||||
$var wire 8 G \[1] $end
|
||||
$var wire 8 H \[2] $end
|
||||
$var wire 8 I \[3] $end
|
||||
$var wire 8 J \[4] $end
|
||||
$var wire 8 K \[5] $end
|
||||
$var wire 8 L \[6] $end
|
||||
$var wire 8 M \[7] $end
|
||||
$var wire 8 N \[8] $end
|
||||
$var wire 8 O \[9] $end
|
||||
$var wire 8 P \[10] $end
|
||||
$var wire 8 Q \[11] $end
|
||||
$var wire 8 R \[12] $end
|
||||
$var wire 8 S \[13] $end
|
||||
$var wire 8 T \[14] $end
|
||||
$var wire 8 U \[15] $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$enddefinitions $end
|
||||
$dumpvars
|
||||
b11111111 !
|
||||
b1111111 "
|
||||
b111111 #
|
||||
b11111 $
|
||||
b1111 %
|
||||
b111 &
|
||||
b11 '
|
||||
b1 (
|
||||
b0 )
|
||||
b10000000 *
|
||||
b11000000 +
|
||||
b11100000 ,
|
||||
b11110000 -
|
||||
b11111000 .
|
||||
b11111100 /
|
||||
b11111110 0
|
||||
b11111111 1
|
||||
b1111111 2
|
||||
b111111 3
|
||||
b11111 4
|
||||
b1111 5
|
||||
b111 6
|
||||
b11 7
|
||||
b1 8
|
||||
b0 9
|
||||
b10000000 :
|
||||
b11000000 ;
|
||||
b11100000 <
|
||||
b11110000 =
|
||||
b11111000 >
|
||||
b11111100 ?
|
||||
b11111110 @
|
||||
b0 A
|
||||
b11111111 B
|
||||
b0 C
|
||||
b0 D
|
||||
0E
|
||||
b11111111 F
|
||||
b1111111 G
|
||||
b111111 H
|
||||
b11111 I
|
||||
b1111 J
|
||||
b111 K
|
||||
b11 L
|
||||
b1 M
|
||||
b0 N
|
||||
b10000000 O
|
||||
b11000000 P
|
||||
b11100000 Q
|
||||
b11110000 R
|
||||
b11111000 S
|
||||
b11111100 T
|
||||
b11111110 U
|
||||
$end
|
||||
#1000000
|
||||
b1 A
|
||||
b1111111 B
|
||||
#2000000
|
||||
b10 A
|
||||
b111111 B
|
||||
#3000000
|
||||
b11 A
|
||||
b11111 B
|
||||
#4000000
|
||||
b100 A
|
||||
b1111 B
|
||||
#5000000
|
||||
b101 A
|
||||
b111 B
|
||||
#6000000
|
||||
b110 A
|
||||
b11 B
|
||||
#7000000
|
||||
b111 A
|
||||
b1 B
|
||||
#8000000
|
||||
b1000 A
|
||||
b0 B
|
||||
#9000000
|
||||
b1001 A
|
||||
b10000000 B
|
||||
#10000000
|
||||
b1010 A
|
||||
b11000000 B
|
||||
#11000000
|
||||
b1011 A
|
||||
b11100000 B
|
||||
#12000000
|
||||
b1100 A
|
||||
b11110000 B
|
||||
#13000000
|
||||
b1101 A
|
||||
b11111000 B
|
||||
#14000000
|
||||
b1110 A
|
||||
b11111100 B
|
||||
#15000000
|
||||
b1111 A
|
||||
b11111110 B
|
||||
#16000000
|
||||
b10000 A
|
||||
b0 B
|
||||
#17000000
|
||||
b0 1
|
||||
b0 A
|
||||
1E
|
||||
b0 F
|
||||
#18000000
|
||||
b11111111 1
|
||||
b1 2
|
||||
b11111111 B
|
||||
b1 C
|
||||
b1 D
|
||||
b11111111 F
|
||||
b1 G
|
||||
#19000000
|
||||
b1111111 2
|
||||
b100 3
|
||||
b10 C
|
||||
b100 D
|
||||
b1111111 G
|
||||
b100 H
|
||||
#20000000
|
||||
b111111 3
|
||||
b1001 4
|
||||
b11 C
|
||||
b1001 D
|
||||
b111111 H
|
||||
b1001 I
|
||||
#21000000
|
||||
b11111 4
|
||||
b10000 5
|
||||
b100 C
|
||||
b10000 D
|
||||
b11111 I
|
||||
b10000 J
|
||||
#22000000
|
||||
b1111 5
|
||||
b11001 6
|
||||
b101 C
|
||||
b11001 D
|
||||
b1111 J
|
||||
b11001 K
|
||||
#23000000
|
||||
b111 6
|
||||
b100100 7
|
||||
b110 C
|
||||
b100100 D
|
||||
b111 K
|
||||
b100100 L
|
||||
#24000000
|
||||
b11 7
|
||||
b110001 8
|
||||
b111 C
|
||||
b110001 D
|
||||
b11 L
|
||||
b110001 M
|
||||
#25000000
|
||||
b1 8
|
||||
b1000000 9
|
||||
b1000 C
|
||||
b1000000 D
|
||||
b1 M
|
||||
b1000000 N
|
||||
#26000000
|
||||
b0 9
|
||||
b1010001 :
|
||||
b1001 C
|
||||
b1010001 D
|
||||
b0 N
|
||||
b1010001 O
|
||||
#27000000
|
||||
b10000000 :
|
||||
b1100100 ;
|
||||
b1010 C
|
||||
b1100100 D
|
||||
b10000000 O
|
||||
b1100100 P
|
||||
#28000000
|
||||
b11000000 ;
|
||||
b1111001 <
|
||||
b1011 C
|
||||
b1111001 D
|
||||
b11000000 P
|
||||
b1111001 Q
|
||||
#29000000
|
||||
b11100000 <
|
||||
b10010000 =
|
||||
b1100 C
|
||||
b10010000 D
|
||||
b11100000 Q
|
||||
b10010000 R
|
||||
#30000000
|
||||
b11110000 =
|
||||
b10101001 >
|
||||
b1101 C
|
||||
b10101001 D
|
||||
b11110000 R
|
||||
b10101001 S
|
||||
#31000000
|
||||
b11111000 >
|
||||
b11000100 ?
|
||||
b1110 C
|
||||
b11000100 D
|
||||
b11111000 S
|
||||
b11000100 T
|
||||
#32000000
|
||||
b11111100 ?
|
||||
b11100001 @
|
||||
b1111 C
|
||||
b11100001 D
|
||||
b11111100 T
|
||||
b11100001 U
|
||||
#33000000
|
||||
b11111110 @
|
||||
b10000 C
|
||||
b0 D
|
||||
b11111110 U
|
||||
#34000000
|
|
@ -0,0 +1,189 @@
|
|||
Simulation {
|
||||
state: State {
|
||||
insns: Insns {
|
||||
state_layout: StateLayout {
|
||||
ty: TypeLayout {
|
||||
small_slots: StatePartLayout<SmallSlots> {
|
||||
len: 0,
|
||||
debug_data: [],
|
||||
..
|
||||
},
|
||||
big_slots: StatePartLayout<BigSlots> {
|
||||
len: 4,
|
||||
debug_data: [
|
||||
SlotDebugData {
|
||||
name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::i",
|
||||
ty: Bool,
|
||||
},
|
||||
SlotDebugData {
|
||||
name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::w",
|
||||
ty: Bool,
|
||||
},
|
||||
SlotDebugData {
|
||||
name: "",
|
||||
ty: Bool,
|
||||
},
|
||||
SlotDebugData {
|
||||
name: "",
|
||||
ty: Bool,
|
||||
},
|
||||
],
|
||||
..
|
||||
},
|
||||
},
|
||||
memories: StatePartLayout<Memories> {
|
||||
len: 0,
|
||||
debug_data: [],
|
||||
layout_data: [],
|
||||
..
|
||||
},
|
||||
},
|
||||
insns: [
|
||||
// at: module-XXXXXXXXXX.rs:1:1
|
||||
0: Const {
|
||||
dest: StatePartIndex<BigSlots>(3), // (0x0) SlotDebugData { name: "", ty: Bool },
|
||||
value: 0x0,
|
||||
},
|
||||
1: Const {
|
||||
dest: StatePartIndex<BigSlots>(2), // (0x1) SlotDebugData { name: "", ty: Bool },
|
||||
value: 0x1,
|
||||
},
|
||||
// at: module-XXXXXXXXXX.rs:4:1
|
||||
2: Copy {
|
||||
dest: StatePartIndex<BigSlots>(1), // (0x0) SlotDebugData { name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::w", ty: Bool },
|
||||
src: StatePartIndex<BigSlots>(2), // (0x1) SlotDebugData { name: "", ty: Bool },
|
||||
},
|
||||
// at: module-XXXXXXXXXX.rs:5:1
|
||||
3: BranchIfZero {
|
||||
target: 5,
|
||||
value: StatePartIndex<BigSlots>(0), // (0x1) SlotDebugData { name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::i", ty: Bool },
|
||||
},
|
||||
// at: module-XXXXXXXXXX.rs:6:1
|
||||
4: Copy {
|
||||
dest: StatePartIndex<BigSlots>(1), // (0x0) SlotDebugData { name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::w", ty: Bool },
|
||||
src: StatePartIndex<BigSlots>(3), // (0x0) SlotDebugData { name: "", ty: Bool },
|
||||
},
|
||||
// at: module-XXXXXXXXXX.rs:1:1
|
||||
5: Return,
|
||||
],
|
||||
..
|
||||
},
|
||||
pc: 5,
|
||||
memory_write_log: [],
|
||||
memories: StatePart {
|
||||
value: [],
|
||||
},
|
||||
small_slots: StatePart {
|
||||
value: [],
|
||||
},
|
||||
big_slots: StatePart {
|
||||
value: [
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
],
|
||||
},
|
||||
},
|
||||
io: Instance {
|
||||
name: <simulator>::conditional_assignment_last,
|
||||
instantiated: Module {
|
||||
name: conditional_assignment_last,
|
||||
..
|
||||
},
|
||||
},
|
||||
uninitialized_inputs: {},
|
||||
io_targets: {
|
||||
Instance {
|
||||
name: <simulator>::conditional_assignment_last,
|
||||
instantiated: Module {
|
||||
name: conditional_assignment_last,
|
||||
..
|
||||
},
|
||||
}.i: CompiledValue {
|
||||
layout: CompiledTypeLayout {
|
||||
ty: Bool,
|
||||
layout: TypeLayout {
|
||||
small_slots: StatePartLayout<SmallSlots> {
|
||||
len: 0,
|
||||
debug_data: [],
|
||||
..
|
||||
},
|
||||
big_slots: StatePartLayout<BigSlots> {
|
||||
len: 1,
|
||||
debug_data: [
|
||||
SlotDebugData {
|
||||
name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::i",
|
||||
ty: Bool,
|
||||
},
|
||||
],
|
||||
..
|
||||
},
|
||||
},
|
||||
body: Scalar,
|
||||
},
|
||||
range: TypeIndexRange {
|
||||
small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 },
|
||||
big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 1 },
|
||||
},
|
||||
write: None,
|
||||
},
|
||||
},
|
||||
made_initial_step: true,
|
||||
needs_settle: false,
|
||||
trace_decls: TraceModule {
|
||||
name: "conditional_assignment_last",
|
||||
children: [
|
||||
TraceModuleIO {
|
||||
name: "i",
|
||||
child: TraceBool {
|
||||
location: TraceScalarId(0),
|
||||
name: "i",
|
||||
flow: Source,
|
||||
},
|
||||
ty: Bool,
|
||||
flow: Source,
|
||||
},
|
||||
TraceWire {
|
||||
name: "w",
|
||||
child: TraceBool {
|
||||
location: TraceScalarId(1),
|
||||
name: "w",
|
||||
flow: Duplex,
|
||||
},
|
||||
ty: Bool,
|
||||
},
|
||||
],
|
||||
},
|
||||
traces: [
|
||||
SimTrace {
|
||||
id: TraceScalarId(0),
|
||||
kind: BigBool {
|
||||
index: StatePartIndex<BigSlots>(0),
|
||||
},
|
||||
state: 0x1,
|
||||
last_state: 0x0,
|
||||
},
|
||||
SimTrace {
|
||||
id: TraceScalarId(1),
|
||||
kind: BigBool {
|
||||
index: StatePartIndex<BigSlots>(1),
|
||||
},
|
||||
state: 0x0,
|
||||
last_state: 0x1,
|
||||
},
|
||||
],
|
||||
trace_memories: {},
|
||||
trace_writers: [
|
||||
Running(
|
||||
VcdWriter {
|
||||
finished_init: true,
|
||||
timescale: 1 ps,
|
||||
..
|
||||
},
|
||||
),
|
||||
],
|
||||
instant: 2 μs,
|
||||
clocks_triggered: [],
|
||||
..
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
$timescale 1 ps $end
|
||||
$scope module conditional_assignment_last $end
|
||||
$var wire 1 ! i $end
|
||||
$var wire 1 " w $end
|
||||
$upscope $end
|
||||
$enddefinitions $end
|
||||
$dumpvars
|
||||
0!
|
||||
1"
|
||||
$end
|
||||
#1000000
|
||||
1!
|
||||
0"
|
||||
#2000000
|
153
crates/fayalite/tests/sim/expected/duplicate_names.txt
Normal file
153
crates/fayalite/tests/sim/expected/duplicate_names.txt
Normal file
|
@ -0,0 +1,153 @@
|
|||
Simulation {
|
||||
state: State {
|
||||
insns: Insns {
|
||||
state_layout: StateLayout {
|
||||
ty: TypeLayout {
|
||||
small_slots: StatePartLayout<SmallSlots> {
|
||||
len: 0,
|
||||
debug_data: [],
|
||||
..
|
||||
},
|
||||
big_slots: StatePartLayout<BigSlots> {
|
||||
len: 4,
|
||||
debug_data: [
|
||||
SlotDebugData {
|
||||
name: "InstantiatedModule(duplicate_names: duplicate_names).duplicate_names::w",
|
||||
ty: UInt<8>,
|
||||
},
|
||||
SlotDebugData {
|
||||
name: "",
|
||||
ty: UInt<8>,
|
||||
},
|
||||
SlotDebugData {
|
||||
name: "InstantiatedModule(duplicate_names: duplicate_names).duplicate_names::w",
|
||||
ty: UInt<8>,
|
||||
},
|
||||
SlotDebugData {
|
||||
name: "",
|
||||
ty: UInt<8>,
|
||||
},
|
||||
],
|
||||
..
|
||||
},
|
||||
},
|
||||
memories: StatePartLayout<Memories> {
|
||||
len: 0,
|
||||
debug_data: [],
|
||||
layout_data: [],
|
||||
..
|
||||
},
|
||||
},
|
||||
insns: [
|
||||
// at: module-XXXXXXXXXX.rs:1:1
|
||||
0: Const {
|
||||
dest: StatePartIndex<BigSlots>(3), // (0x6) SlotDebugData { name: "", ty: UInt<8> },
|
||||
value: 0x6,
|
||||
},
|
||||
// at: module-XXXXXXXXXX.rs:5:1
|
||||
1: Copy {
|
||||
dest: StatePartIndex<BigSlots>(2), // (0x6) SlotDebugData { name: "InstantiatedModule(duplicate_names: duplicate_names).duplicate_names::w", ty: UInt<8> },
|
||||
src: StatePartIndex<BigSlots>(3), // (0x6) SlotDebugData { name: "", ty: UInt<8> },
|
||||
},
|
||||
// at: module-XXXXXXXXXX.rs:1:1
|
||||
2: Const {
|
||||
dest: StatePartIndex<BigSlots>(1), // (0x5) SlotDebugData { name: "", ty: UInt<8> },
|
||||
value: 0x5,
|
||||
},
|
||||
// at: module-XXXXXXXXXX.rs:3:1
|
||||
3: Copy {
|
||||
dest: StatePartIndex<BigSlots>(0), // (0x5) SlotDebugData { name: "InstantiatedModule(duplicate_names: duplicate_names).duplicate_names::w", ty: UInt<8> },
|
||||
src: StatePartIndex<BigSlots>(1), // (0x5) SlotDebugData { name: "", ty: UInt<8> },
|
||||
},
|
||||
// at: module-XXXXXXXXXX.rs:1:1
|
||||
4: Return,
|
||||
],
|
||||
..
|
||||
},
|
||||
pc: 4,
|
||||
memory_write_log: [],
|
||||
memories: StatePart {
|
||||
value: [],
|
||||
},
|
||||
small_slots: StatePart {
|
||||
value: [],
|
||||
},
|
||||
big_slots: StatePart {
|
||||
value: [
|
||||
5,
|
||||
5,
|
||||
6,
|
||||
6,
|
||||
],
|
||||
},
|
||||
},
|
||||
io: Instance {
|
||||
name: <simulator>::duplicate_names,
|
||||
instantiated: Module {
|
||||
name: duplicate_names,
|
||||
..
|
||||
},
|
||||
},
|
||||
uninitialized_inputs: {},
|
||||
io_targets: {},
|
||||
made_initial_step: true,
|
||||
needs_settle: false,
|
||||
trace_decls: TraceModule {
|
||||
name: "duplicate_names",
|
||||
children: [
|
||||
TraceWire {
|
||||
name: "w",
|
||||
child: TraceUInt {
|
||||
location: TraceScalarId(0),
|
||||
name: "w",
|
||||
ty: UInt<8>,
|
||||
flow: Duplex,
|
||||
},
|
||||
ty: UInt<8>,
|
||||
},
|
||||
TraceWire {
|
||||
name: "w",
|
||||
child: TraceUInt {
|
||||
location: TraceScalarId(1),
|
||||
name: "w",
|
||||
ty: UInt<8>,
|
||||
flow: Duplex,
|
||||
},
|
||||
ty: UInt<8>,
|
||||
},
|
||||
],
|
||||
},
|
||||
traces: [
|
||||
SimTrace {
|
||||
id: TraceScalarId(0),
|
||||
kind: BigUInt {
|
||||
index: StatePartIndex<BigSlots>(0),
|
||||
ty: UInt<8>,
|
||||
},
|
||||
state: 0x05,
|
||||
last_state: 0x05,
|
||||
},
|
||||
SimTrace {
|
||||
id: TraceScalarId(1),
|
||||
kind: BigUInt {
|
||||
index: StatePartIndex<BigSlots>(2),
|
||||
ty: UInt<8>,
|
||||
},
|
||||
state: 0x06,
|
||||
last_state: 0x06,
|
||||
},
|
||||
],
|
||||
trace_memories: {},
|
||||
trace_writers: [
|
||||
Running(
|
||||
VcdWriter {
|
||||
finished_init: true,
|
||||
timescale: 1 ps,
|
||||
..
|
||||
},
|
||||
),
|
||||
],
|
||||
instant: 1 μs,
|
||||
clocks_triggered: [],
|
||||
..
|
||||
}
|
11
crates/fayalite/tests/sim/expected/duplicate_names.vcd
Normal file
11
crates/fayalite/tests/sim/expected/duplicate_names.vcd
Normal file
|
@ -0,0 +1,11 @@
|
|||
$timescale 1 ps $end
|
||||
$scope module duplicate_names $end
|
||||
$var wire 8 ! w $end
|
||||
$var wire 8 " w_2 $end
|
||||
$upscope $end
|
||||
$enddefinitions $end
|
||||
$dumpvars
|
||||
b101 !
|
||||
b110 "
|
||||
$end
|
||||
#1000000
|
|
@ -24,97 +24,97 @@ $upscope $end
|
|||
$upscope $end
|
||||
$scope struct mem $end
|
||||
$scope struct contents $end
|
||||
$scope struct [0] $end
|
||||
$scope struct \[0] $end
|
||||
$scope struct mem $end
|
||||
$var reg 8 9 \0 $end
|
||||
$var reg 8 I \1 $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope struct [1] $end
|
||||
$scope struct \[1] $end
|
||||
$scope struct mem $end
|
||||
$var reg 8 : \0 $end
|
||||
$var reg 8 J \1 $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope struct [2] $end
|
||||
$scope struct \[2] $end
|
||||
$scope struct mem $end
|
||||
$var reg 8 ; \0 $end
|
||||
$var reg 8 K \1 $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope struct [3] $end
|
||||
$scope struct \[3] $end
|
||||
$scope struct mem $end
|
||||
$var reg 8 < \0 $end
|
||||
$var reg 8 L \1 $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope struct [4] $end
|
||||
$scope struct \[4] $end
|
||||
$scope struct mem $end
|
||||
$var reg 8 = \0 $end
|
||||
$var reg 8 M \1 $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope struct [5] $end
|
||||
$scope struct \[5] $end
|
||||
$scope struct mem $end
|
||||
$var reg 8 > \0 $end
|
||||
$var reg 8 N \1 $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope struct [6] $end
|
||||
$scope struct \[6] $end
|
||||
$scope struct mem $end
|
||||
$var reg 8 ? \0 $end
|
||||
$var reg 8 O \1 $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope struct [7] $end
|
||||
$scope struct \[7] $end
|
||||
$scope struct mem $end
|
||||
$var reg 8 @ \0 $end
|
||||
$var reg 8 P \1 $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope struct [8] $end
|
||||
$scope struct \[8] $end
|
||||
$scope struct mem $end
|
||||
$var reg 8 A \0 $end
|
||||
$var reg 8 Q \1 $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope struct [9] $end
|
||||
$scope struct \[9] $end
|
||||
$scope struct mem $end
|
||||
$var reg 8 B \0 $end
|
||||
$var reg 8 R \1 $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope struct [10] $end
|
||||
$scope struct \[10] $end
|
||||
$scope struct mem $end
|
||||
$var reg 8 C \0 $end
|
||||
$var reg 8 S \1 $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope struct [11] $end
|
||||
$scope struct \[11] $end
|
||||
$scope struct mem $end
|
||||
$var reg 8 D \0 $end
|
||||
$var reg 8 T \1 $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope struct [12] $end
|
||||
$scope struct \[12] $end
|
||||
$scope struct mem $end
|
||||
$var reg 8 E \0 $end
|
||||
$var reg 8 U \1 $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope struct [13] $end
|
||||
$scope struct \[13] $end
|
||||
$scope struct mem $end
|
||||
$var reg 8 F \0 $end
|
||||
$var reg 8 V \1 $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope struct [14] $end
|
||||
$scope struct \[14] $end
|
||||
$scope struct mem $end
|
||||
$var reg 8 G \0 $end
|
||||
$var reg 8 W \1 $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope struct [15] $end
|
||||
$scope struct \[15] $end
|
||||
$scope struct mem $end
|
||||
$var reg 8 H \0 $end
|
||||
$var reg 8 X \1 $end
|
||||
|
|
|
@ -11,31 +11,31 @@ $var wire 1 ' wmask $end
|
|||
$upscope $end
|
||||
$scope struct mem $end
|
||||
$scope struct contents $end
|
||||
$scope struct [0] $end
|
||||
$scope struct \[0] $end
|
||||
$scope struct mem $end
|
||||
$var string 1 1 \$tag $end
|
||||
$var reg 1 6 HdlSome $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope struct [1] $end
|
||||
$scope struct \[1] $end
|
||||
$scope struct mem $end
|
||||
$var string 1 2 \$tag $end
|
||||
$var reg 1 7 HdlSome $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope struct [2] $end
|
||||
$scope struct \[2] $end
|
||||
$scope struct mem $end
|
||||
$var string 1 3 \$tag $end
|
||||
$var reg 1 8 HdlSome $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope struct [3] $end
|
||||
$scope struct \[3] $end
|
||||
$scope struct mem $end
|
||||
$var string 1 4 \$tag $end
|
||||
$var reg 1 9 HdlSome $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope struct [4] $end
|
||||
$scope struct \[4] $end
|
||||
$scope struct mem $end
|
||||
$var string 1 5 \$tag $end
|
||||
$var reg 1 : HdlSome $end
|
||||
|
|
|
@ -42,7 +42,7 @@ $upscope $end
|
|||
$upscope $end
|
||||
$scope struct mem $end
|
||||
$scope struct contents $end
|
||||
$scope struct [0] $end
|
||||
$scope struct \[0] $end
|
||||
$scope struct mem $end
|
||||
$var reg 8 ] \[0] $end
|
||||
$var reg 8 e \[1] $end
|
||||
|
@ -54,7 +54,7 @@ $var reg 8 /" \[6] $end
|
|||
$var reg 8 7" \[7] $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope struct [1] $end
|
||||
$scope struct \[1] $end
|
||||
$scope struct mem $end
|
||||
$var reg 8 ^ \[0] $end
|
||||
$var reg 8 f \[1] $end
|
||||
|
@ -66,7 +66,7 @@ $var reg 8 0" \[6] $end
|
|||
$var reg 8 8" \[7] $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope struct [2] $end
|
||||
$scope struct \[2] $end
|
||||
$scope struct mem $end
|
||||
$var reg 8 _ \[0] $end
|
||||
$var reg 8 g \[1] $end
|
||||
|
@ -78,7 +78,7 @@ $var reg 8 1" \[6] $end
|
|||
$var reg 8 9" \[7] $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope struct [3] $end
|
||||
$scope struct \[3] $end
|
||||
$scope struct mem $end
|
||||
$var reg 8 ` \[0] $end
|
||||
$var reg 8 h \[1] $end
|
||||
|
@ -90,7 +90,7 @@ $var reg 8 2" \[6] $end
|
|||
$var reg 8 :" \[7] $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope struct [4] $end
|
||||
$scope struct \[4] $end
|
||||
$scope struct mem $end
|
||||
$var reg 8 a \[0] $end
|
||||
$var reg 8 i \[1] $end
|
||||
|
@ -102,7 +102,7 @@ $var reg 8 3" \[6] $end
|
|||
$var reg 8 ;" \[7] $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope struct [5] $end
|
||||
$scope struct \[5] $end
|
||||
$scope struct mem $end
|
||||
$var reg 8 b \[0] $end
|
||||
$var reg 8 j \[1] $end
|
||||
|
@ -114,7 +114,7 @@ $var reg 8 4" \[6] $end
|
|||
$var reg 8 <" \[7] $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope struct [6] $end
|
||||
$scope struct \[6] $end
|
||||
$scope struct mem $end
|
||||
$var reg 8 c \[0] $end
|
||||
$var reg 8 k \[1] $end
|
||||
|
@ -126,7 +126,7 @@ $var reg 8 5" \[6] $end
|
|||
$var reg 8 =" \[7] $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$scope struct [7] $end
|
||||
$scope struct \[7] $end
|
||||
$scope struct mem $end
|
||||
$var reg 8 d \[0] $end
|
||||
$var reg 8 l \[1] $end
|
||||
|
|
Loading…
Reference in a new issue