Compare commits

...

11 commits

17 changed files with 13276 additions and 513 deletions

8
Cargo.lock generated
View file

@ -543,9 +543,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.83"
version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43"
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
dependencies = [
"unicode-ident",
]
@ -647,9 +647,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "2.0.66"
version = "2.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058"
dependencies = [
"proc-macro2",
"quote",

View file

@ -37,7 +37,7 @@ quote = "1.0.36"
serde = { version = "1.0.202", features = ["derive"] }
serde_json = { version = "1.0.117", features = ["preserve_order"] }
sha2 = "0.10.8"
syn = { version = "2.0.66", features = ["full", "fold", "visit", "extra-traits"] }
syn = { version = "2.0.93", features = ["full", "fold", "visit", "extra-traits"] }
tempfile = "3.10.1"
thiserror = "1.0.61"
trybuild = "1.0"

View file

@ -3,14 +3,20 @@
#![cfg_attr(test, recursion_limit = "512")]
use proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens};
use std::io::{ErrorKind, Write};
use std::{
collections::{hash_map::Entry, HashMap},
io::{ErrorKind, Write},
};
use syn::{
bracketed, parenthesized,
bracketed,
ext::IdentExt,
parenthesized,
parse::{Parse, ParseStream, Parser},
parse_quote,
punctuated::Pair,
punctuated::{Pair, Punctuated},
spanned::Spanned,
AttrStyle, Attribute, Error, Item, ItemFn, Token,
token::{Bracket, Paren},
AttrStyle, Attribute, Error, Ident, Item, ItemFn, LitBool, LitStr, Meta, Token,
};
mod fold;
@ -19,6 +25,7 @@ mod hdl_enum;
mod hdl_type_alias;
mod hdl_type_common;
mod module;
mod process_cfg;
pub(crate) trait CustomToken:
Copy
@ -59,6 +66,11 @@ mod kw {
};
}
custom_keyword!(__evaluated_cfgs);
custom_keyword!(all);
custom_keyword!(any);
custom_keyword!(cfg);
custom_keyword!(cfg_attr);
custom_keyword!(clock_domain);
custom_keyword!(connect_inexact);
custom_keyword!(custom_bounds);
@ -75,6 +87,7 @@ mod kw {
custom_keyword!(no_reset);
custom_keyword!(no_runtime_generics);
custom_keyword!(no_static);
custom_keyword!(not);
custom_keyword!(outline_generated);
custom_keyword!(output);
custom_keyword!(reg_builder);
@ -901,15 +914,346 @@ fn hdl_module_impl(item: ItemFn) -> syn::Result<TokenStream> {
Ok(contents)
}
pub fn hdl_module(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
let kw = kw::hdl_module::default();
hdl_module_impl(syn::parse2(quote! { #[#kw(#attr)] #item })?)
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub(crate) enum CfgExpr {
Option {
ident: Ident,
value: Option<(Token![=], LitStr)>,
},
All {
all: kw::all,
paren: Paren,
exprs: Punctuated<CfgExpr, Token![,]>,
},
Any {
any: kw::any,
paren: Paren,
exprs: Punctuated<CfgExpr, Token![,]>,
},
Not {
not: kw::not,
paren: Paren,
expr: Box<CfgExpr>,
trailing_comma: Option<Token![,]>,
},
}
pub fn hdl_attr(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
let kw = kw::hdl::default();
let item = quote! { #[#kw(#attr)] #item };
let item = syn::parse2::<Item>(item)?;
impl Parse for CfgExpr {
fn parse(input: ParseStream) -> syn::Result<Self> {
match input.cursor().ident() {
Some((_, cursor)) if cursor.eof() => {
return Ok(CfgExpr::Option {
ident: input.call(Ident::parse_any)?,
value: None,
});
}
_ => {}
}
if input.peek(Ident::peek_any) && input.peek2(Token![=]) {
return Ok(CfgExpr::Option {
ident: input.call(Ident::parse_any)?,
value: Some((input.parse()?, input.parse()?)),
});
}
let contents;
if input.peek(kw::all) {
Ok(CfgExpr::All {
all: input.parse()?,
paren: parenthesized!(contents in input),
exprs: contents.call(Punctuated::parse_terminated)?,
})
} else if input.peek(kw::any) {
Ok(CfgExpr::Any {
any: input.parse()?,
paren: parenthesized!(contents in input),
exprs: contents.call(Punctuated::parse_terminated)?,
})
} else if input.peek(kw::not) {
Ok(CfgExpr::Not {
not: input.parse()?,
paren: parenthesized!(contents in input),
expr: contents.parse()?,
trailing_comma: contents.parse()?,
})
} else {
Err(input.error("expected cfg-pattern"))
}
}
}
impl ToTokens for CfgExpr {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
CfgExpr::Option { ident, value } => {
ident.to_tokens(tokens);
if let Some((eq, value)) = value {
eq.to_tokens(tokens);
value.to_tokens(tokens);
}
}
CfgExpr::All { all, paren, exprs } => {
all.to_tokens(tokens);
paren.surround(tokens, |tokens| exprs.to_tokens(tokens));
}
CfgExpr::Any { any, paren, exprs } => {
any.to_tokens(tokens);
paren.surround(tokens, |tokens| exprs.to_tokens(tokens));
}
CfgExpr::Not {
not,
paren,
expr,
trailing_comma,
} => {
not.to_tokens(tokens);
paren.surround(tokens, |tokens| {
expr.to_tokens(tokens);
trailing_comma.to_tokens(tokens);
});
}
}
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub(crate) struct Cfg {
cfg: kw::cfg,
paren: Paren,
expr: CfgExpr,
trailing_comma: Option<Token![,]>,
}
impl Cfg {
fn parse_meta(meta: &Meta) -> syn::Result<Self> {
syn::parse2(meta.to_token_stream())
}
}
impl ToTokens for Cfg {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self {
cfg,
paren,
expr,
trailing_comma,
} = self;
cfg.to_tokens(tokens);
paren.surround(tokens, |tokens| {
expr.to_tokens(tokens);
trailing_comma.to_tokens(tokens);
});
}
}
impl Parse for Cfg {
fn parse(input: ParseStream) -> syn::Result<Self> {
let contents;
Ok(Self {
cfg: input.parse()?,
paren: parenthesized!(contents in input),
expr: contents.parse()?,
trailing_comma: contents.parse()?,
})
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub(crate) struct CfgAttr {
cfg_attr: kw::cfg_attr,
paren: Paren,
expr: CfgExpr,
comma: Token![,],
attrs: Punctuated<Meta, Token![,]>,
}
impl CfgAttr {
pub(crate) fn to_cfg(&self) -> Cfg {
Cfg {
cfg: kw::cfg(self.cfg_attr.span),
paren: self.paren,
expr: self.expr.clone(),
trailing_comma: None,
}
}
fn parse_meta(meta: &Meta) -> syn::Result<Self> {
syn::parse2(meta.to_token_stream())
}
}
impl Parse for CfgAttr {
fn parse(input: ParseStream) -> syn::Result<Self> {
let contents;
Ok(Self {
cfg_attr: input.parse()?,
paren: parenthesized!(contents in input),
expr: contents.parse()?,
comma: contents.parse()?,
attrs: contents.call(Punctuated::parse_terminated)?,
})
}
}
pub(crate) struct CfgAndValue {
cfg: Cfg,
eq_token: Token![=],
value: LitBool,
}
impl Parse for CfgAndValue {
fn parse(input: ParseStream) -> syn::Result<Self> {
Ok(Self {
cfg: input.parse()?,
eq_token: input.parse()?,
value: input.parse()?,
})
}
}
pub(crate) struct Cfgs<T> {
pub(crate) bracket: Bracket,
pub(crate) cfgs_map: HashMap<Cfg, T>,
pub(crate) cfgs_list: Vec<Cfg>,
}
impl<T> Default for Cfgs<T> {
fn default() -> Self {
Self {
bracket: Default::default(),
cfgs_map: Default::default(),
cfgs_list: Default::default(),
}
}
}
impl<T> Cfgs<T> {
fn insert_cfg(&mut self, cfg: Cfg, value: T) {
match self.cfgs_map.entry(cfg) {
Entry::Occupied(_) => {}
Entry::Vacant(entry) => {
self.cfgs_list.push(entry.key().clone());
entry.insert(value);
}
}
}
}
impl Parse for Cfgs<bool> {
fn parse(input: ParseStream) -> syn::Result<Self> {
let contents;
let bracket = bracketed!(contents in input);
let mut cfgs_map = HashMap::new();
let mut cfgs_list = Vec::new();
for CfgAndValue {
cfg,
eq_token,
value,
} in contents.call(Punctuated::<CfgAndValue, Token![,]>::parse_terminated)?
{
let _ = eq_token;
match cfgs_map.entry(cfg) {
Entry::Occupied(_) => {}
Entry::Vacant(entry) => {
cfgs_list.push(entry.key().clone());
entry.insert(value.value);
}
}
}
Ok(Self {
bracket,
cfgs_map,
cfgs_list,
})
}
}
impl Parse for Cfgs<()> {
fn parse(input: ParseStream) -> syn::Result<Self> {
let contents;
let bracket = bracketed!(contents in input);
let mut cfgs_map = HashMap::new();
let mut cfgs_list = Vec::new();
for cfg in contents.call(Punctuated::<Cfg, Token![,]>::parse_terminated)? {
match cfgs_map.entry(cfg) {
Entry::Occupied(_) => {}
Entry::Vacant(entry) => {
cfgs_list.push(entry.key().clone());
entry.insert(());
}
}
}
Ok(Self {
bracket,
cfgs_map,
cfgs_list,
})
}
}
impl ToTokens for Cfgs<()> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self {
bracket,
cfgs_map: _,
cfgs_list,
} = self;
bracket.surround(tokens, |tokens| {
for cfg in cfgs_list {
cfg.to_tokens(tokens);
<Token![,]>::default().to_tokens(tokens);
}
});
}
}
fn hdl_main(
kw: impl CustomToken,
attr: TokenStream,
item: TokenStream,
) -> syn::Result<TokenStream> {
fn parse_evaluated_cfgs_attr<R>(
input: ParseStream,
parse_inner: impl FnOnce(ParseStream) -> syn::Result<R>,
) -> syn::Result<R> {
let _: Token![#] = input.parse()?;
let bracket_content;
bracketed!(bracket_content in input);
let _: kw::__evaluated_cfgs = bracket_content.parse()?;
let paren_content;
parenthesized!(paren_content in bracket_content);
parse_inner(&paren_content)
}
let (evaluated_cfgs, item): (_, TokenStream) = Parser::parse2(
|input: ParseStream| {
let peek = input.fork();
if parse_evaluated_cfgs_attr(&peek, |_| Ok(())).is_ok() {
let evaluated_cfgs = parse_evaluated_cfgs_attr(input, Cfgs::<bool>::parse)?;
Ok((Some(evaluated_cfgs), input.parse()?))
} else {
Ok((None, input.parse()?))
}
},
item,
)?;
let cfgs = if let Some(cfgs) = evaluated_cfgs {
cfgs
} else {
let cfgs = process_cfg::collect_cfgs(syn::parse2(item.clone())?)?;
if cfgs.cfgs_list.is_empty() {
Cfgs::default()
} else {
return Ok(quote! {
::fayalite::__cfg_expansion_helper! {
[]
#cfgs
{#[::fayalite::#kw(#attr)]} { #item }
}
});
}
};
let item = syn::parse2(quote! { #[#kw(#attr)] #item })?;
let Some(item) = process_cfg::process_cfgs(item, cfgs)? else {
return Ok(TokenStream::new());
};
match item {
Item::Enum(item) => hdl_enum::hdl_enum(item),
Item::Struct(item) => hdl_bundle::hdl_bundle(item),
@ -921,3 +1265,11 @@ pub fn hdl_attr(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream
)),
}
}
pub fn hdl_module(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
hdl_main(kw::hdl_module::default(), attr, item)
}
pub fn hdl_attr(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
hdl_main(kw::hdl::default(), attr, item)
}

File diff suppressed because it is too large Load diff

View file

@ -5,6 +5,9 @@ use std::{env, fs, path::Path};
fn main() {
println!("cargo::rustc-check-cfg=cfg(todo)");
println!("cargo::rustc-check-cfg=cfg(cfg_false_for_tests)");
println!("cargo::rustc-check-cfg=cfg(cfg_true_for_tests)");
println!("cargo::rustc-cfg=cfg_true_for_tests");
let path = "visit_types.json";
println!("cargo::rerun-if-changed={path}");
println!("cargo::rerun-if-changed=build.rs");

View file

@ -4,12 +4,14 @@
use crate::{
expr::{ops::BundleLiteral, Expr, ToExpr},
intern::{Intern, Interned},
sim::{SimValue, ToSimValue},
source_location::SourceLocation,
ty::{
impl_match_variant_as_self, CanonicalType, MatchVariantWithoutScope, StaticType, Type,
TypeProperties, TypeWithDeref,
},
};
use bitvec::vec::BitVec;
use hashbrown::HashMap;
use std::{fmt, marker::PhantomData};
@ -323,7 +325,7 @@ macro_rules! impl_tuple_builder_fields {
}
macro_rules! impl_tuples {
([$({#[num = $num:literal, field = $field:ident] $var:ident: $T:ident})*] []) => {
([$({#[num = $num:literal, field = $field:ident, ty = $ty_var:ident: $Ty:ident] $var:ident: $T:ident})*] []) => {
impl_tuple_builder_fields! {
{}
[$({
@ -423,6 +425,79 @@ macro_rules! impl_tuples {
BundleLiteral::new(ty, field_values[..].intern()).to_expr()
}
}
impl<$($T: ToSimValue<CanonicalType>,)*> ToSimValue<CanonicalType> for ($($T,)*) {
#[track_caller]
fn to_sim_value(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
ToSimValue::<Bundle>::to_sim_value(self, Bundle::from_canonical(ty)).into_canonical()
}
#[track_caller]
fn into_sim_value(self, ty: CanonicalType) -> SimValue<CanonicalType>
{
ToSimValue::<Bundle>::into_sim_value(self, Bundle::from_canonical(ty)).into_canonical()
}
#[track_caller]
fn box_into_sim_value(self: Box<Self>, ty: CanonicalType) -> SimValue<CanonicalType> {
ToSimValue::<Bundle>::box_into_sim_value(self, Bundle::from_canonical(ty)).into_canonical()
}
}
impl<$($T: ToSimValue<CanonicalType>,)*> ToSimValue<Bundle> for ($($T,)*) {
#[track_caller]
fn to_sim_value(&self, ty: Bundle) -> SimValue<Bundle> {
let ($($var,)*) = self;
let [$($ty_var,)*] = *ty.fields() else {
panic!("bundle has wrong number of fields");
};
$(let $var = $var.to_sim_value($ty_var.ty);)*
ToSimValue::into_sim_value(($($var,)*), ty)
}
#[track_caller]
fn into_sim_value(self, ty: Bundle) -> SimValue<Bundle> {
#![allow(unused_mut)]
#![allow(clippy::unused_unit)]
let ($($var,)*) = self;
let [$($ty_var,)*] = *ty.fields() else {
panic!("bundle has wrong number of fields");
};
let mut bits: Option<BitVec> = None;
$(let $var = $var.into_sim_value($ty_var.ty);
assert_eq!($var.ty(), $ty_var.ty);
if !$var.bits().is_empty() {
if let Some(bits) = &mut bits {
bits.extend_from_bitslice($var.bits());
} else {
let mut $var = $var.into_bits();
$var.reserve(ty.type_properties().bit_width - $var.len());
bits = Some($var);
}
}
)*
bits.unwrap_or_else(BitVec::new).into_sim_value(ty)
}
#[track_caller]
fn box_into_sim_value(self: Box<Self>, ty: Bundle) -> SimValue<Bundle> {
Self::into_sim_value(*self, ty)
}
}
impl<$($T: ToSimValue<$Ty>, $Ty: Type,)*> ToSimValue<($($Ty,)*)> for ($($T,)*) {
#[track_caller]
fn to_sim_value(&self, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> {
let ($($var,)*) = self;
let ($($ty_var,)*) = ty;
$(let $var = $var.to_sim_value($ty_var).into_canonical();)*
SimValue::from_canonical(ToSimValue::into_sim_value(($($var,)*), ty.canonical()))
}
#[track_caller]
fn into_sim_value(self, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> {
let ($($var,)*) = self;
let ($($ty_var,)*) = ty;
$(let $var = $var.into_sim_value($ty_var).into_canonical();)*
SimValue::from_canonical(ToSimValue::into_sim_value(($($var,)*), ty.canonical()))
}
#[track_caller]
fn box_into_sim_value(self: Box<Self>, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> {
Self::into_sim_value(*self, ty)
}
}
};
([$($lhs:tt)*] [$rhs_first:tt $($rhs:tt)*]) => {
impl_tuples!([$($lhs)*] []);
@ -432,18 +507,18 @@ macro_rules! impl_tuples {
impl_tuples! {
[] [
{#[num = 0, field = field_0] v0: T0}
{#[num = 1, field = field_1] v1: T1}
{#[num = 2, field = field_2] v2: T2}
{#[num = 3, field = field_3] v3: T3}
{#[num = 4, field = field_4] v4: T4}
{#[num = 5, field = field_5] v5: T5}
{#[num = 6, field = field_6] v6: T6}
{#[num = 7, field = field_7] v7: T7}
{#[num = 8, field = field_8] v8: T8}
{#[num = 9, field = field_9] v9: T9}
{#[num = 10, field = field_10] v10: T10}
{#[num = 11, field = field_11] v11: T11}
{#[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}
]
}
@ -528,3 +603,27 @@ impl<T: ?Sized + Send + Sync + 'static> ToExpr for PhantomData<T> {
BundleLiteral::new(PhantomData, Interned::default()).to_expr()
}
}
impl<T: ?Sized + Send + Sync + 'static> ToSimValue<Self> for PhantomData<T> {
#[track_caller]
fn to_sim_value(&self, ty: Self) -> SimValue<Self> {
ToSimValue::into_sim_value(BitVec::new(), ty)
}
}
impl<T: ?Sized> ToSimValue<Bundle> for PhantomData<T> {
#[track_caller]
fn to_sim_value(&self, ty: Bundle) -> SimValue<Bundle> {
assert!(ty.fields().is_empty());
ToSimValue::into_sim_value(BitVec::new(), ty)
}
}
impl<T: ?Sized> ToSimValue<CanonicalType> for PhantomData<T> {
#[track_caller]
fn to_sim_value(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
let ty = Bundle::from_canonical(ty);
assert!(ty.fields().is_empty());
ToSimValue::into_sim_value(BitVec::new(), ty).into_canonical()
}
}

View file

@ -11,6 +11,59 @@ extern crate self as fayalite;
#[doc(hidden)]
pub use std as __std;
#[doc(hidden)]
#[macro_export]
macro_rules! __cfg_expansion_helper {
(
[
$($evaluated_cfgs:ident($($evaluated_exprs:tt)*) = $evaluated_results:ident,)*
]
[
$cfg:ident($($expr:tt)*),
$($unevaluated_cfgs:ident($($unevaluated_exprs:tt)*),)*
]
// pass as tt so we get right span for attribute
$after_evaluation_attr:tt $after_evaluation_body:tt
) => {
#[$cfg($($expr)*)]
$crate::__cfg_expansion_helper! {
[
$($evaluated_cfgs($($evaluated_exprs)*) = $evaluated_results,)*
$cfg($($expr)*) = true,
]
[
$($unevaluated_cfgs($($unevaluated_exprs)*),)*
]
$after_evaluation_attr $after_evaluation_body
}
#[$cfg(not($($expr)*))]
$crate::__cfg_expansion_helper! {
[
$($evaluated_cfgs($($evaluated_exprs)*) = $evaluated_results,)*
$cfg($($expr)*) = false,
]
[
$($unevaluated_cfgs($($unevaluated_exprs)*),)*
]
$after_evaluation_attr $after_evaluation_body
}
};
(
[
$($evaluated_cfgs:ident($($evaluated_exprs:tt)*) = $evaluated_results:ident,)*
]
[]
// don't use #[...] so we get right span for `#` and `[]` of attribute
{$($after_evaluation_attr:tt)*} {$($after_evaluation_body:tt)*}
) => {
$($after_evaluation_attr)*
#[__evaluated_cfgs([
$($evaluated_cfgs($($evaluated_exprs)*) = $evaluated_results,)*
])]
$($after_evaluation_body)*
};
}
#[doc(inline)]
/// The `#[hdl_module]` attribute is applied to a Rust function so that that function creates
/// a [`Module`][`::fayalite::module::Module`] when called.

File diff suppressed because it is too large Load diff

View file

@ -7,12 +7,13 @@ use fayalite::{
clock::{Clock, ClockDomain},
expr::{CastTo, HdlPartialEq},
firrtl::ExportOptions,
formal::{any_seq, formal_reset, hdl_assert, hdl_assume},
hdl_module,
int::{Bool, UInt},
module::{connect, connect_any, reg_builder, wire},
formal::{any_const, any_seq, formal_reset, hdl_assert, hdl_assume},
hdl, hdl_module,
int::{Bool, DynSize, Size, UInt, UIntType},
module::{connect, connect_any, instance, memory, reg_builder, wire},
reset::ToReset,
testing::assert_formal,
ty::StaticType,
};
/// Test hidden state
@ -131,3 +132,164 @@ mod hidden_state {
);
}
}
/// Formal verification of designs containing memories
///
/// There is a trick for memories, described in the [Zipcpu blog].
/// First, select a fixed but arbitrary memory address, monitoring all reads
/// and writes made to it. Then, assert that anything read from that location
/// matches the last stored value.
///
/// A difficulty for induction is that the memory represents [hidden_state]. A
/// solution is to include an additional read port to the memory and assert
/// that the memory location effectively contains the last stored value.
/// This additional debug port is present only to assist the proof and is
/// unused (optimized out) in actual use.
///
/// [Zipcpu blog]: <https://zipcpu.com/zipcpu/2018/07/13/memories.html>
mod memory {
use super::*;
/// Test a simple 8-bit SRAM model
#[test]
fn test_sram() {
#[hdl]
struct WritePort<AddrWidth: Size> {
addr: UIntType<AddrWidth>,
data: UInt<8>,
en: Bool,
}
#[hdl]
struct ReadPort<AddrWidth: Size> {
addr: UIntType<AddrWidth>,
#[hdl(flip)]
data: UInt<8>,
}
/// This debug port is only meant to assist the proof.
/// For normal use in a design, a wrapper could be provided,
/// omitting this port.
/// The implementation is forbidden to use any information
/// provided on this port in its internal workings.
#[hdl]
struct DebugPort<AddrWidth: Size> {
selected: UIntType<AddrWidth>,
stored: UInt<8>,
wrote: Bool,
}
/// simple 1R1W SRAM model (one asynchronous read port and one
/// independent write port) with `n`-bit address width
#[hdl_module]
fn example_sram(n: usize) {
#[hdl]
let wr: WritePort<DynSize> = m.input(WritePort[n]);
#[hdl]
let rd: ReadPort<DynSize> = m.input(ReadPort[n]);
#[hdl]
let cd: ClockDomain = m.input();
// declare and connect the backing memory
#[hdl]
let mut mem = memory();
mem.depth(1 << n);
let read_port = mem.new_read_port();
let write_port = mem.new_write_port();
connect(write_port.clk, cd.clk);
connect(write_port.addr, wr.addr);
connect(write_port.en, wr.en);
connect(write_port.data, wr.data);
connect(write_port.mask, true);
connect(read_port.clk, cd.clk);
connect(read_port.addr, rd.addr);
connect(read_port.en, true);
connect(rd.data, read_port.data);
// To assist with induction, ensure that the chosen memory location
// really contains, always, the last value written to it.
#[hdl]
let dbg: DebugPort<DynSize> = m.input(DebugPort[n]);
let debug_port = mem.new_read_port();
connect(debug_port.en, true);
connect(debug_port.clk, cd.clk);
connect(debug_port.addr, dbg.selected);
#[hdl]
if dbg.wrote {
hdl_assert(cd.clk, debug_port.data.cmp_eq(dbg.stored), "");
// Try commenting out the assert above, induction will fail.
// Opening the trace, it can be seen that the memory contents
// and the stored value don't match, which is an unreachable
// state. By asserting the above, it will become invalid
// as well, so induction will skip this kind of situation.
}
}
/// formal verification of the SRAM module, parametrized by the
/// address bit-width
#[hdl_module]
fn test_module(n: usize) {
#[hdl]
let clk: Clock = m.input();
let cd = #[hdl]
ClockDomain {
clk,
rst: formal_reset().to_reset(),
};
// instantiate the SRAM model, connecting its inputs to
// a random sequence
#[hdl]
let rd: ReadPort<DynSize> = wire(ReadPort[n]);
connect(rd.addr, any_seq(UInt[n]));
#[hdl]
let wr: WritePort<DynSize> = wire(WritePort[n]);
connect(wr.addr, any_seq(UInt[n]));
connect(wr.data, any_seq(UInt::<8>::TYPE));
connect(wr.en, any_seq(Bool));
#[hdl]
let dut = instance(example_sram(n));
connect(dut.cd, cd);
connect(dut.rd, rd);
connect(dut.wr, wr);
// select a fixed but arbitrary test address
#[hdl]
let selected = wire(UInt[n]);
connect(selected, any_const(UInt[n]));
// store the last value written to that address
#[hdl]
let stored: UInt<8> = reg_builder().clock_domain(cd).reset(0u8);
// since memories are not initialized, track whether we wrote to the
// memory at least once
#[hdl]
let wrote: Bool = reg_builder().clock_domain(cd).reset(false);
// on a write, capture the last written value
#[hdl]
if wr.en & wr.addr.cmp_eq(selected) {
connect(stored, wr.data);
connect(wrote, true);
}
// on a read, assert that the read value is the same which was stored
#[hdl]
if rd.addr.cmp_eq(selected) & wrote {
hdl_assert(clk, rd.data.cmp_eq(stored), "");
}
// to assist induction, pass our state to the underlying instance
let dbg = #[hdl]
DebugPort {
selected,
stored,
wrote,
};
connect(dut.dbg, dbg);
}
assert_formal(
"sram",
test_module(8),
FormalMode::Prove,
2,
None,
ExportOptions::default(),
);
}
}

View file

@ -4287,3 +4287,61 @@ circuit check_deduce_resets:
",
};
}
// intentionally not outline_generated to ensure we get correct macro hygiene
#[hdl_module]
pub fn check_cfgs<#[cfg(cfg_false_for_tests)] A: Type, #[cfg(cfg_true_for_tests)] B: Type>(
#[cfg(cfg_false_for_tests)] a: A,
#[cfg(cfg_true_for_tests)] b: B,
) {
#[hdl]
struct S<#[cfg(cfg_false_for_tests)] A, #[cfg(cfg_true_for_tests)] B> {
#[cfg(cfg_false_for_tests)]
a: A,
#[cfg(cfg_true_for_tests)]
b: B,
}
#[hdl]
#[cfg(cfg_false_for_tests)]
let i_a: A = m.input(a);
#[hdl]
#[cfg(cfg_true_for_tests)]
let i_b: B = m.input(b);
#[hdl]
let w: S<UInt<8>> = wire();
#[cfg(cfg_false_for_tests)]
{
#[hdl]
let o_a: A = m.output(a);
connect(o_a, w.a.cast_bits_to(a));
connect_any(w.a, i_a.cast_to_bits());
}
#[cfg(cfg_true_for_tests)]
{
#[hdl]
let o_b: B = m.output(b);
connect(o_b, w.b.cast_bits_to(b));
connect_any(w.b, i_b.cast_to_bits());
}
}
#[test]
fn test_cfgs() {
let _n = SourceLocation::normalize_files_for_tests();
let m = check_cfgs(UInt[8]);
dbg!(m);
#[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
assert_export_firrtl! {
m =>
"/test/check_cfgs.fir": r"FIRRTL version 3.2.0
circuit check_cfgs:
type Ty0 = {b: UInt<8>}
module check_cfgs: @[the_test_file.rs 9962:1]
input i_b: UInt<8> @[the_test_file.rs 9979:20]
output o_b: UInt<8> @[the_test_file.rs 9992:24]
wire w: Ty0 @[the_test_file.rs 9981:25]
connect o_b, w.b @[the_test_file.rs 9993:9]
connect w.b, i_b @[the_test_file.rs 9994:9]
",
};
}

View file

@ -5,7 +5,8 @@ use fayalite::{
int::UIntValue,
prelude::*,
reset::ResetType,
sim::{time::SimDuration, vcd::VcdWriterDecls, Simulation},
sim::{time::SimDuration, vcd::VcdWriterDecls, Simulation, ToSimValue},
ty::StaticType,
util::RcWriter,
};
use std::num::NonZeroUsize;
@ -254,8 +255,14 @@ fn test_shift_register() {
let mut sim = Simulation::new(shift_register());
let mut writer = RcWriter::default();
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
sim.write_clock(sim.io().cd.clk, false);
sim.write_reset(sim.io().cd.rst, true);
sim.write(
sim.io().cd,
#[hdl]
ClockDomain {
clk: false.to_clock(),
rst: true.to_sync_reset(),
},
);
sim.write_bool(sim.io().d, false);
sim.advance_time(SimDuration::from_micros(1));
sim.write_clock(sim.io().cd.clk, true);
@ -271,7 +278,7 @@ fn test_shift_register() {
assert_eq!(
*expected,
sim.read_bool(sim.io().q),
"cycle: {cycle}\nvcd:\n{}",
"vcd:\n{}\ncycle: {cycle}",
String::from_utf8(writer.take()).unwrap(),
);
}
@ -309,6 +316,8 @@ pub fn enums() {
let which_out: UInt<2> = m.output();
#[hdl]
let data_out: UInt<4> = m.output();
#[hdl]
let b_out: HdlOption<(UInt<1>, Bool)> = m.output();
#[hdl]
struct MyStruct<T> {
@ -348,6 +357,8 @@ pub fn enums() {
}
}
connect(b_out, HdlNone());
#[hdl]
match the_reg {
MyEnum::A => {
@ -357,6 +368,7 @@ pub fn enums() {
MyEnum::B(v) => {
connect(which_out, 1_hdl_u2);
connect_any(data_out, v.0 | (v.1.cast_to_static::<UInt<1>>() << 1));
connect(b_out, HdlSome(v));
}
MyEnum::C(v) => {
connect(which_out, 2_hdl_u2);
@ -382,13 +394,33 @@ fn test_enums() {
sim.advance_time(SimDuration::from_nanos(100));
sim.write_reset(sim.io().cd.rst, false);
sim.advance_time(SimDuration::from_nanos(900));
#[derive(Debug, PartialEq, Eq)]
type BOutTy = HdlOption<(UInt<1>, Bool)>;
#[derive(Debug)]
struct IO {
en: bool,
which_in: u8,
data_in: u8,
which_out: u8,
data_out: u8,
b_out: Expr<BOutTy>,
}
impl PartialEq for IO {
fn eq(&self, other: &Self) -> bool {
let Self {
en,
which_in,
data_in,
which_out,
data_out,
b_out,
} = *self;
en == other.en
&& which_in == other.which_in
&& data_in == other.data_in
&& which_out == other.which_out
&& data_out == other.data_out
&& b_out.to_sim_value(BOutTy::TYPE) == other.b_out.to_sim_value(BOutTy::TYPE)
}
}
let io_cycles = [
IO {
@ -397,6 +429,7 @@ fn test_enums() {
data_in: 0,
which_out: 0,
data_out: 0,
b_out: HdlNone(),
},
IO {
en: true,
@ -404,6 +437,7 @@ fn test_enums() {
data_in: 0,
which_out: 0,
data_out: 0,
b_out: HdlNone(),
},
IO {
en: false,
@ -411,6 +445,7 @@ fn test_enums() {
data_in: 0,
which_out: 1,
data_out: 0,
b_out: HdlSome((0_hdl_u1, false)),
},
IO {
en: true,
@ -418,6 +453,7 @@ fn test_enums() {
data_in: 0xF,
which_out: 1,
data_out: 0,
b_out: HdlSome((0_hdl_u1, false)),
},
IO {
en: true,
@ -425,6 +461,7 @@ fn test_enums() {
data_in: 0xF,
which_out: 1,
data_out: 0x3,
b_out: HdlSome((1_hdl_u1, true)),
},
IO {
en: true,
@ -432,6 +469,7 @@ fn test_enums() {
data_in: 0xF,
which_out: 1,
data_out: 0x3,
b_out: HdlSome((1_hdl_u1, true)),
},
IO {
en: true,
@ -439,6 +477,7 @@ fn test_enums() {
data_in: 0xF,
which_out: 2,
data_out: 0xF,
b_out: HdlNone(),
},
];
for (
@ -449,6 +488,7 @@ fn test_enums() {
data_in,
which_out: _,
data_out: _,
b_out: _,
},
) in io_cycles.into_iter().enumerate()
{
@ -469,11 +509,12 @@ fn test_enums() {
.to_bigint()
.try_into()
.expect("known to be in range"),
b_out: sim.read(sim.io().b_out).to_expr(),
};
assert_eq!(
expected,
io,
"cycle: {cycle}\nvcd:\n{}",
"vcd:\n{}\ncycle: {cycle}",
String::from_utf8(writer.take()).unwrap(),
);
sim.write_clock(sim.io().cd.clk, false);
@ -671,7 +712,7 @@ fn test_memories() {
assert_eq!(
expected,
io,
"cycle: {cycle}\nvcd:\n{}",
"vcd:\n{}\ncycle: {cycle}",
String::from_utf8(writer.take()).unwrap(),
);
sim.advance_time(SimDuration::from_micros(1));
@ -694,4 +735,514 @@ fn test_memories() {
}
}
// TODO: add more tests for memories
#[hdl_module(outline_generated)]
pub fn memories2() {
#[hdl]
let rw: fayalite::memory::ReadWriteStruct<UInt<2>, ConstUsize<3>> = m.input();
#[hdl]
let mut mem = memory_with_init([HdlSome(true); 5]);
mem.read_latency(1);
mem.write_latency(NonZeroUsize::new(1).unwrap());
mem.read_under_write(ReadUnderWrite::New);
let rw_port = mem.new_rw_port();
connect_any(rw_port.addr, rw.addr);
connect(rw_port.en, rw.en);
connect(rw_port.clk, rw.clk);
connect_any(rw.rdata, rw_port.rdata.cast_to_bits());
connect(rw_port.wmode, rw.wmode);
connect(rw_port.wdata, HdlNone());
#[hdl]
if rw.wdata[0] {
connect(rw_port.wdata, HdlSome(rw.wdata[1]));
}
connect(rw_port.wmask, rw.wmask);
}
#[hdl]
#[test]
fn test_memories2() {
let _n = SourceLocation::normalize_files_for_tests();
let mut sim = Simulation::new(memories2());
let mut writer = RcWriter::default();
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
sim.write_clock(sim.io().rw.clk, false);
#[derive(Debug, PartialEq, Eq)]
struct IO {
addr: u8,
en: bool,
rdata: u8,
wmode: bool,
wdata: u8,
wmask: bool,
}
let io_cycles = [
IO {
addr: 0,
en: false,
rdata: 0,
wmode: false,
wdata: 0,
wmask: false,
},
IO {
addr: 0,
en: true,
rdata: 0x3,
wmode: false,
wdata: 0,
wmask: false,
},
IO {
addr: 0,
en: false,
rdata: 0,
wmode: false,
wdata: 0,
wmask: false,
},
IO {
addr: 0,
en: true,
rdata: 0,
wmode: true,
wdata: 0,
wmask: true,
},
IO {
addr: 0,
en: true,
rdata: 0,
wmode: false,
wdata: 0,
wmask: false,
},
IO {
addr: 0,
en: true,
rdata: 0,
wmode: true,
wdata: 3,
wmask: false,
},
IO {
addr: 1,
en: true,
rdata: 0,
wmode: true,
wdata: 1,
wmask: true,
},
IO {
addr: 2,
en: true,
rdata: 0,
wmode: true,
wdata: 2,
wmask: true,
},
IO {
addr: 3,
en: true,
rdata: 0,
wmode: true,
wdata: 3,
wmask: true,
},
IO {
addr: 4,
en: true,
rdata: 0,
wmode: true,
wdata: 2,
wmask: true,
},
IO {
addr: 5,
en: true,
rdata: 0,
wmode: true,
wdata: 1,
wmask: true,
},
IO {
addr: 6,
en: true,
rdata: 0,
wmode: true,
wdata: 1,
wmask: true,
},
IO {
addr: 7,
en: true,
rdata: 0,
wmode: true,
wdata: 1,
wmask: true,
},
IO {
addr: 7,
en: true,
rdata: 0,
wmode: false,
wdata: 0,
wmask: false,
},
IO {
addr: 6,
en: true,
rdata: 0,
wmode: false,
wdata: 0,
wmask: false,
},
IO {
addr: 5,
en: true,
rdata: 0,
wmode: false,
wdata: 0,
wmask: false,
},
IO {
addr: 4,
en: true,
rdata: 0,
wmode: false,
wdata: 0,
wmask: false,
},
IO {
addr: 3,
en: true,
rdata: 3,
wmode: false,
wdata: 0,
wmask: false,
},
IO {
addr: 2,
en: true,
rdata: 0,
wmode: false,
wdata: 0,
wmask: false,
},
IO {
addr: 0,
en: true,
rdata: 0,
wmode: false,
wdata: 0,
wmask: false,
},
IO {
addr: 1,
en: true,
rdata: 1,
wmode: false,
wdata: 0,
wmask: false,
},
IO {
addr: 0,
en: false,
rdata: 0,
wmode: false,
wdata: 0,
wmask: false,
},
];
for (
cycle,
expected @ IO {
addr,
en,
rdata: _,
wmode,
wdata,
wmask,
},
) in io_cycles.into_iter().enumerate()
{
sim.write_bool_or_int(sim.io().rw.addr, addr.cast_to_static());
sim.write_bool(sim.io().rw.en, en);
sim.write_bool(sim.io().rw.wmode, wmode);
sim.write_bool_or_int(sim.io().rw.wdata, wdata.cast_to_static());
sim.write_bool(sim.io().rw.wmask, wmask);
sim.advance_time(SimDuration::from_nanos(250));
sim.write_clock(sim.io().rw.clk, true);
sim.advance_time(SimDuration::from_nanos(250));
let io = IO {
addr,
en,
rdata: sim
.read_bool_or_int(sim.io().rw.rdata)
.to_bigint()
.try_into()
.expect("known to be in range"),
wmode,
wdata,
wmask,
};
assert_eq!(
expected,
io,
"vcd:\n{}\ncycle: {cycle}",
String::from_utf8(writer.take()).unwrap(),
);
sim.advance_time(SimDuration::from_nanos(250));
sim.write_clock(sim.io().rw.clk, false);
sim.advance_time(SimDuration::from_nanos(250));
}
sim.flush_traces().unwrap();
let vcd = String::from_utf8(writer.take()).unwrap();
println!("####### VCD:\n{vcd}\n#######");
if vcd != include_str!("sim/expected/memories2.vcd") {
panic!();
}
let sim_debug = format!("{sim:#?}");
println!("#######\n{sim_debug}\n#######");
if sim_debug != include_str!("sim/expected/memories2.txt") {
panic!();
}
}
#[hdl_module(outline_generated)]
pub fn memories3() {
#[hdl]
let r: fayalite::memory::ReadStruct<Array<UInt<8>, 8>, ConstUsize<3>> = m.input();
#[hdl]
let w: fayalite::memory::WriteStruct<Array<UInt<8>, 8>, ConstUsize<3>> = m.input();
#[hdl]
let mut mem: MemBuilder<Array<UInt<8>, 8>> = memory();
mem.depth(8);
mem.read_latency(2);
mem.write_latency(NonZeroUsize::new(2).unwrap());
mem.read_under_write(ReadUnderWrite::Old);
connect_any(mem.new_read_port(), r);
connect_any(mem.new_write_port(), w);
}
#[hdl]
#[test]
fn test_memories3() {
let _n = SourceLocation::normalize_files_for_tests();
let mut sim = Simulation::new(memories3());
let mut writer = RcWriter::default();
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
sim.write_clock(sim.io().r.clk, false);
sim.write_clock(sim.io().w.clk, false);
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
struct IO {
r_addr: u8,
r_en: bool,
r_data: [u8; 8],
w_addr: u8,
w_en: bool,
w_data: [u8; 8],
w_mask: [bool; 8],
}
let io_cycles = [
IO {
r_addr: 0,
r_en: false,
r_data: [0; 8],
w_addr: 0,
w_en: true,
w_data: [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0],
w_mask: [false, true, false, true, true, false, false, true],
},
IO {
r_addr: 0,
r_en: true,
r_data: [0; 8],
w_addr: 1,
w_en: false,
w_data: [0; 8],
w_mask: [false; 8],
},
IO {
r_addr: 0,
r_en: true,
r_data: [0, 0x34, 0, 0x78, 0x9A, 0, 0, 0xF0],
w_addr: 1,
w_en: false,
w_data: [0; 8],
w_mask: [false; 8],
},
IO {
r_addr: 0,
r_en: true,
r_data: [0, 0x34, 0, 0x78, 0x9A, 0, 0, 0xF0],
w_addr: 0,
w_en: true,
w_data: [0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10],
w_mask: [true; 8],
},
IO {
r_addr: 0,
r_en: true,
r_data: [0, 0x34, 0, 0x78, 0x9A, 0, 0, 0xF0],
w_addr: 0,
w_en: true,
w_data: [0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10],
w_mask: [true; 8],
},
IO {
r_addr: 0,
r_en: true,
r_data: [0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10],
w_addr: 0,
w_en: true,
w_data: [0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10],
w_mask: [true; 8],
},
IO {
r_addr: 0,
r_en: false,
r_data: [0; 8],
w_addr: 1,
w_en: true,
w_data: [0x13, 0x57, 0x9B, 0xDF, 0x02, 0x46, 0x8A, 0xCE],
w_mask: [true; 8],
},
IO {
r_addr: 0,
r_en: false,
r_data: [0; 8],
w_addr: 2,
w_en: true,
w_data: *b"testing!",
w_mask: [true; 8],
},
IO {
r_addr: 0,
r_en: false,
r_data: [0; 8],
w_addr: 3,
w_en: true,
w_data: *b"more tst",
w_mask: [true; 8],
},
IO {
r_addr: 0,
r_en: true,
r_data: [0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10],
w_addr: 0,
w_en: false,
w_data: [0; 8],
w_mask: [false; 8],
},
IO {
r_addr: 1,
r_en: true,
r_data: [0x13, 0x57, 0x9B, 0xDF, 0x02, 0x46, 0x8A, 0xCE],
w_addr: 0,
w_en: false,
w_data: [0; 8],
w_mask: [false; 8],
},
IO {
r_addr: 2,
r_en: true,
r_data: *b"testing!",
w_addr: 0,
w_en: false,
w_data: [0; 8],
w_mask: [false; 8],
},
IO {
r_addr: 3,
r_en: true,
r_data: *b"more tst",
w_addr: 0,
w_en: false,
w_data: [0; 8],
w_mask: [false; 8],
},
];
for cycle in 0..io_cycles.len() + 2 {
{
let IO {
r_addr,
r_en,
r_data: _,
w_addr,
w_en,
w_data,
w_mask,
} = io_cycles.get(cycle).copied().unwrap_or(IO {
r_addr: 0,
r_en: false,
r_data: [0; 8],
w_addr: 0,
w_en: false,
w_data: [0; 8],
w_mask: [false; 8],
});
sim.write_bool_or_int(sim.io().r.addr, r_addr.cast_to_static());
sim.write_bool(sim.io().r.en, r_en);
sim.write_bool_or_int(sim.io().w.addr, w_addr.cast_to_static());
sim.write_bool(sim.io().w.en, w_en);
for (i, v) in w_data.into_iter().enumerate() {
sim.write_bool_or_int(sim.io().w.data[i], v);
}
for (i, v) in w_mask.into_iter().enumerate() {
sim.write_bool_or_int(sim.io().w.mask[i], v);
}
}
sim.advance_time(SimDuration::from_nanos(250));
sim.write_clock(sim.io().r.clk, true);
sim.write_clock(sim.io().w.clk, true);
sim.advance_time(SimDuration::from_nanos(250));
if let Some(
expected @ IO {
r_addr,
r_en,
r_data: _,
w_addr,
w_en,
w_data,
w_mask,
},
) = cycle.checked_sub(1).and_then(|i| io_cycles.get(i).copied())
{
let io = IO {
r_addr,
r_en,
r_data: std::array::from_fn(|i| {
sim.read_bool_or_int(sim.io().r.data[i])
.to_bigint()
.try_into()
.expect("known to be in range")
}),
w_addr,
w_en,
w_data,
w_mask,
};
assert_eq!(
expected,
io,
"vcd:\n{}\ncycle: {cycle}",
String::from_utf8(writer.take()).unwrap(),
);
}
sim.advance_time(SimDuration::from_nanos(250));
sim.write_clock(sim.io().r.clk, false);
sim.write_clock(sim.io().w.clk, false);
sim.advance_time(SimDuration::from_nanos(250));
}
sim.flush_traces().unwrap();
let vcd = String::from_utf8(writer.take()).unwrap();
println!("####### VCD:\n{vcd}\n#######");
if vcd != include_str!("sim/expected/memories3.vcd") {
panic!();
}
let sim_debug = format!("{sim:#?}");
println!("#######\n{sim_debug}\n#######");
if sim_debug != include_str!("sim/expected/memories3.txt") {
panic!();
}
}

File diff suppressed because it is too large Load diff

View file

@ -9,18 +9,25 @@ $var wire 2 $ which_in $end
$var wire 4 % data_in $end
$var wire 2 & which_out $end
$var wire 4 ' data_out $end
$scope struct the_reg $end
$scope struct b_out $end
$var string 1 ( \$tag $end
$scope struct HdlSome $end
$var wire 1 ) \0 $end
$var wire 1 * \1 $end
$upscope $end
$upscope $end
$scope struct the_reg $end
$var string 1 + \$tag $end
$scope struct B $end
$var reg 1 ) \0 $end
$var reg 1 * \1 $end
$var reg 1 , \0 $end
$var reg 1 - \1 $end
$upscope $end
$scope struct C $end
$scope struct a $end
$var reg 1 + \[0] $end
$var reg 1 , \[1] $end
$var reg 1 . \[0] $end
$var reg 1 / \[1] $end
$upscope $end
$var reg 2 - b $end
$var reg 2 0 b $end
$upscope $end
$upscope $end
$upscope $end
@ -33,12 +40,15 @@ b0 $
b0 %
b0 &
b0 '
sA\x20(0) (
sHdlNone\x20(0) (
0)
0*
0+
sA\x20(0) +
0,
b0 -
0-
0.
0/
b0 0
$end
#1000000
1!
@ -55,7 +65,8 @@ b1 $
#5000000
1!
b1 &
sB\x20(1) (
sHdlSome\x20(1) (
sB\x20(1) +
#6000000
0#
b0 $
@ -72,8 +83,10 @@ b1111 %
b11 '
1)
1*
1+
1,
1-
1.
1/
#10000000
0!
#11000000
@ -85,8 +98,11 @@ b10 $
1!
b10 &
b1111 '
sC\x20(2) (
b11 -
sHdlNone\x20(0) (
0)
0*
sC\x20(2) +
b11 0
#14000000
0!
#15000000

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,363 @@
$timescale 1 ps $end
$scope module memories2 $end
$scope struct rw $end
$var wire 3 ! addr $end
$var wire 1 " en $end
$var wire 1 # clk $end
$var wire 2 $ rdata $end
$var wire 1 % wmode $end
$var wire 2 & wdata $end
$var wire 1 ' wmask $end
$upscope $end
$scope struct mem $end
$scope struct contents $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 mem $end
$var string 1 2 \$tag $end
$var reg 1 7 HdlSome $end
$upscope $end
$upscope $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 mem $end
$var string 1 4 \$tag $end
$var reg 1 9 HdlSome $end
$upscope $end
$upscope $end
$scope struct [4] $end
$scope struct mem $end
$var string 1 5 \$tag $end
$var reg 1 : HdlSome $end
$upscope $end
$upscope $end
$upscope $end
$scope struct rw0 $end
$var wire 3 ( addr $end
$var wire 1 ) en $end
$var wire 1 * clk $end
$scope struct rdata $end
$var string 1 + \$tag $end
$var wire 1 , HdlSome $end
$upscope $end
$var wire 1 - wmode $end
$scope struct wdata $end
$var string 1 . \$tag $end
$var wire 1 / HdlSome $end
$upscope $end
$var wire 1 0 wmask $end
$upscope $end
$upscope $end
$upscope $end
$enddefinitions $end
$dumpvars
sHdlSome\x20(1) 1
16
sHdlSome\x20(1) 2
17
sHdlSome\x20(1) 3
18
sHdlSome\x20(1) 4
19
sHdlSome\x20(1) 5
1:
b0 !
0"
0#
b0 $
0%
b0 &
0'
b0 (
0)
0*
sHdlNone\x20(0) +
0,
0-
sHdlNone\x20(0) .
0/
00
$end
#250000
1#
1*
#500000
#750000
0#
0*
#1000000
1"
1)
#1250000
1#
1*
b11 $
sHdlSome\x20(1) +
1,
#1500000
#1750000
0#
0*
#2000000
0"
0)
#2250000
1#
1*
b0 $
sHdlNone\x20(0) +
0,
#2500000
#2750000
0#
0*
#3000000
1"
1%
1'
1)
1-
10
#3250000
sHdlNone\x20(0) 1
06
1#
1*
#3500000
#3750000
0#
0*
#4000000
0%
0'
0-
00
#4250000
1#
1*
#4500000
#4750000
0#
0*
#5000000
1%
b11 &
1-
sHdlSome\x20(1) .
1/
#5250000
1#
1*
#5500000
#5750000
0#
0*
#6000000
b1 !
b1 &
1'
b1 (
0/
10
#6250000
sHdlSome\x20(1) 2
07
1#
1*
#6500000
#6750000
0#
0*
#7000000
b10 !
b10 &
b10 (
sHdlNone\x20(0) .
#7250000
sHdlNone\x20(0) 3
08
1#
1*
#7500000
#7750000
0#
0*
#8000000
b11 !
b11 &
b11 (
sHdlSome\x20(1) .
1/
#8250000
sHdlSome\x20(1) 4
19
1#
1*
#8500000
#8750000
0#
0*
#9000000
b100 !
b10 &
b100 (
sHdlNone\x20(0) .
0/
#9250000
sHdlNone\x20(0) 5
0:
1#
1*
#9500000
#9750000
0#
0*
#10000000
b101 !
b1 &
b101 (
sHdlSome\x20(1) .
#10250000
1#
1*
#10500000
#10750000
0#
0*
#11000000
b110 !
b110 (
#11250000
1#
1*
#11500000
#11750000
0#
0*
#12000000
b111 !
b111 (
#12250000
1#
1*
#12500000
#12750000
0#
0*
#13000000
0%
b0 &
0'
0-
sHdlNone\x20(0) .
00
#13250000
1#
1*
#13500000
#13750000
0#
0*
#14000000
b110 !
b110 (
#14250000
1#
1*
#14500000
#14750000
0#
0*
#15000000
b101 !
b101 (
#15250000
1#
1*
#15500000
#15750000
0#
0*
#16000000
b100 !
b100 (
#16250000
1#
1*
#16500000
#16750000
0#
0*
#17000000
b11 !
b11 (
#17250000
1#
1*
b11 $
sHdlSome\x20(1) +
1,
#17500000
#17750000
0#
0*
#18000000
b10 !
b10 (
#18250000
1#
1*
b0 $
sHdlNone\x20(0) +
0,
#18500000
#18750000
0#
0*
#19000000
b0 !
b0 (
#19250000
1#
1*
#19500000
#19750000
0#
0*
#20000000
b1 !
b1 (
#20250000
1#
1*
b1 $
sHdlSome\x20(1) +
#20500000
#20750000
0#
0*
#21000000
b0 !
0"
b0 (
0)
#21250000
1#
1*
b0 $
sHdlNone\x20(0) +
#21500000
#21750000
0#
0*
#22000000

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,836 @@
$timescale 1 ps $end
$scope module memories3 $end
$scope struct r $end
$var wire 3 ! addr $end
$var wire 1 " en $end
$var wire 1 # clk $end
$scope struct data $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
$upscope $end
$upscope $end
$scope struct w $end
$var wire 3 , addr $end
$var wire 1 - en $end
$var wire 1 . clk $end
$scope struct data $end
$var wire 8 / \[0] $end
$var wire 8 0 \[1] $end
$var wire 8 1 \[2] $end
$var wire 8 2 \[3] $end
$var wire 8 3 \[4] $end
$var wire 8 4 \[5] $end
$var wire 8 5 \[6] $end
$var wire 8 6 \[7] $end
$upscope $end
$scope struct mask $end
$var wire 1 7 \[0] $end
$var wire 1 8 \[1] $end
$var wire 1 9 \[2] $end
$var wire 1 : \[3] $end
$var wire 1 ; \[4] $end
$var wire 1 < \[5] $end
$var wire 1 = \[6] $end
$var wire 1 > \[7] $end
$upscope $end
$upscope $end
$scope struct mem $end
$scope struct contents $end
$scope struct [0] $end
$scope struct mem $end
$var reg 8 ] \[0] $end
$var reg 8 e \[1] $end
$var reg 8 m \[2] $end
$var reg 8 u \[3] $end
$var reg 8 } \[4] $end
$var reg 8 '" \[5] $end
$var reg 8 /" \[6] $end
$var reg 8 7" \[7] $end
$upscope $end
$upscope $end
$scope struct [1] $end
$scope struct mem $end
$var reg 8 ^ \[0] $end
$var reg 8 f \[1] $end
$var reg 8 n \[2] $end
$var reg 8 v \[3] $end
$var reg 8 ~ \[4] $end
$var reg 8 (" \[5] $end
$var reg 8 0" \[6] $end
$var reg 8 8" \[7] $end
$upscope $end
$upscope $end
$scope struct [2] $end
$scope struct mem $end
$var reg 8 _ \[0] $end
$var reg 8 g \[1] $end
$var reg 8 o \[2] $end
$var reg 8 w \[3] $end
$var reg 8 !" \[4] $end
$var reg 8 )" \[5] $end
$var reg 8 1" \[6] $end
$var reg 8 9" \[7] $end
$upscope $end
$upscope $end
$scope struct [3] $end
$scope struct mem $end
$var reg 8 ` \[0] $end
$var reg 8 h \[1] $end
$var reg 8 p \[2] $end
$var reg 8 x \[3] $end
$var reg 8 "" \[4] $end
$var reg 8 *" \[5] $end
$var reg 8 2" \[6] $end
$var reg 8 :" \[7] $end
$upscope $end
$upscope $end
$scope struct [4] $end
$scope struct mem $end
$var reg 8 a \[0] $end
$var reg 8 i \[1] $end
$var reg 8 q \[2] $end
$var reg 8 y \[3] $end
$var reg 8 #" \[4] $end
$var reg 8 +" \[5] $end
$var reg 8 3" \[6] $end
$var reg 8 ;" \[7] $end
$upscope $end
$upscope $end
$scope struct [5] $end
$scope struct mem $end
$var reg 8 b \[0] $end
$var reg 8 j \[1] $end
$var reg 8 r \[2] $end
$var reg 8 z \[3] $end
$var reg 8 $" \[4] $end
$var reg 8 ," \[5] $end
$var reg 8 4" \[6] $end
$var reg 8 <" \[7] $end
$upscope $end
$upscope $end
$scope struct [6] $end
$scope struct mem $end
$var reg 8 c \[0] $end
$var reg 8 k \[1] $end
$var reg 8 s \[2] $end
$var reg 8 { \[3] $end
$var reg 8 %" \[4] $end
$var reg 8 -" \[5] $end
$var reg 8 5" \[6] $end
$var reg 8 =" \[7] $end
$upscope $end
$upscope $end
$scope struct [7] $end
$scope struct mem $end
$var reg 8 d \[0] $end
$var reg 8 l \[1] $end
$var reg 8 t \[2] $end
$var reg 8 | \[3] $end
$var reg 8 &" \[4] $end
$var reg 8 ." \[5] $end
$var reg 8 6" \[6] $end
$var reg 8 >" \[7] $end
$upscope $end
$upscope $end
$upscope $end
$scope struct r0 $end
$var wire 3 ? addr $end
$var wire 1 @ en $end
$var wire 1 A clk $end
$scope struct data $end
$var wire 8 B \[0] $end
$var wire 8 C \[1] $end
$var wire 8 D \[2] $end
$var wire 8 E \[3] $end
$var wire 8 F \[4] $end
$var wire 8 G \[5] $end
$var wire 8 H \[6] $end
$var wire 8 I \[7] $end
$upscope $end
$upscope $end
$scope struct w1 $end
$var wire 3 J addr $end
$var wire 1 K en $end
$var wire 1 L clk $end
$scope struct data $end
$var wire 8 M \[0] $end
$var wire 8 N \[1] $end
$var wire 8 O \[2] $end
$var wire 8 P \[3] $end
$var wire 8 Q \[4] $end
$var wire 8 R \[5] $end
$var wire 8 S \[6] $end
$var wire 8 T \[7] $end
$upscope $end
$scope struct mask $end
$var wire 1 U \[0] $end
$var wire 1 V \[1] $end
$var wire 1 W \[2] $end
$var wire 1 X \[3] $end
$var wire 1 Y \[4] $end
$var wire 1 Z \[5] $end
$var wire 1 [ \[6] $end
$var wire 1 \ \[7] $end
$upscope $end
$upscope $end
$upscope $end
$upscope $end
$enddefinitions $end
$dumpvars
b0 ]
b0 e
b0 m
b0 u
b0 }
b0 '"
b0 /"
b0 7"
b0 ^
b0 f
b0 n
b0 v
b0 ~
b0 ("
b0 0"
b0 8"
b0 _
b0 g
b0 o
b0 w
b0 !"
b0 )"
b0 1"
b0 9"
b0 `
b0 h
b0 p
b0 x
b0 ""
b0 *"
b0 2"
b0 :"
b0 a
b0 i
b0 q
b0 y
b0 #"
b0 +"
b0 3"
b0 ;"
b0 b
b0 j
b0 r
b0 z
b0 $"
b0 ,"
b0 4"
b0 <"
b0 c
b0 k
b0 s
b0 {
b0 %"
b0 -"
b0 5"
b0 ="
b0 d
b0 l
b0 t
b0 |
b0 &"
b0 ."
b0 6"
b0 >"
b0 !
0"
0#
b0 $
b0 %
b0 &
b0 '
b0 (
b0 )
b0 *
b0 +
b0 ,
1-
0.
b10010 /
b110100 0
b1010110 1
b1111000 2
b10011010 3
b10111100 4
b11011110 5
b11110000 6
07
18
09
1:
1;
0<
0=
1>
b0 ?
0@
0A
b0 B
b0 C
b0 D
b0 E
b0 F
b0 G
b0 H
b0 I
b0 J
1K
0L
b10010 M
b110100 N
b1010110 O
b1111000 P
b10011010 Q
b10111100 R
b11011110 S
b11110000 T
0U
1V
0W
1X
1Y
0Z
0[
1\
$end
#250000
1#
1.
1A
1L
#500000
#750000
0#
0.
0A
0L
#1000000
1"
b1 ,
0-
b0 /
b0 0
b0 1
b0 2
b0 3
b0 4
b0 5
b0 6
08
0:
0;
0>
1@
b1 J
0K
b0 M
b0 N
b0 O
b0 P
b0 Q
b0 R
b0 S
b0 T
0V
0X
0Y
0\
#1250000
b0 ]
b110100 e
b0 m
b1111000 u
b10011010 }
b0 '"
b0 /"
b11110000 7"
1#
1.
1A
1L
#1500000
#1750000
0#
0.
0A
0L
#2000000
#2250000
1#
1.
1A
1L
#2500000
#2750000
0#
0.
0A
0L
#3000000
b0 ,
1-
b11111110 /
b11011100 0
b10111010 1
b10011000 2
b1110110 3
b1010100 4
b110010 5
b10000 6
17
18
19
1:
1;
1<
1=
1>
b0 J
1K
b11111110 M
b11011100 N
b10111010 O
b10011000 P
b1110110 Q
b1010100 R
b110010 S
b10000 T
1U
1V
1W
1X
1Y
1Z
1[
1\
#3250000
1#
1.
1A
b110100 C
b1111000 E
b10011010 F
b11110000 I
1L
b110100 %
b1111000 '
b10011010 (
b11110000 +
#3500000
#3750000
0#
0.
0A
0L
#4000000
#4250000
b11111110 ]
b11011100 e
b10111010 m
b10011000 u
b1110110 }
b1010100 '"
b110010 /"
b10000 7"
1#
1.
1A
1L
#4500000
#4750000
0#
0.
0A
0L
#5000000
#5250000
b11111110 ]
b11011100 e
b10111010 m
b10011000 u
b1110110 }
b1010100 '"
b110010 /"
b10000 7"
1#
1.
1A
1L
#5500000
#5750000
0#
0.
0A
0L
#6000000
0"
b1 ,
b10011 /
b1010111 0
b10011011 1
b11011111 2
b10 3
b1000110 4
b10001010 5
b11001110 6
0@
b1 J
b10011 M
b1010111 N
b10011011 O
b11011111 P
b10 Q
b1000110 R
b10001010 S
b11001110 T
#6250000
b11111110 ]
b11011100 e
b10111010 m
b10011000 u
b1110110 }
b1010100 '"
b110010 /"
b10000 7"
1#
1.
1A
b11111110 B
b11011100 C
b10111010 D
b10011000 E
b1110110 F
b1010100 G
b110010 H
b10000 I
1L
b11111110 $
b11011100 %
b10111010 &
b10011000 '
b1110110 (
b1010100 )
b110010 *
b10000 +
#6500000
#6750000
0#
0.
0A
0L
#7000000
b10 ,
b1110100 /
b1100101 0
b1110011 1
b1110100 2
b1101001 3
b1101110 4
b1100111 5
b100001 6
b10 J
b1110100 M
b1100101 N
b1110011 O
b1110100 P
b1101001 Q
b1101110 R
b1100111 S
b100001 T
#7250000
b10011 ^
b1010111 f
b10011011 n
b11011111 v
b10 ~
b1000110 ("
b10001010 0"
b11001110 8"
1#
1.
1A
b0 B
b0 C
b0 D
b0 E
b0 F
b0 G
b0 H
b0 I
1L
b0 $
b0 %
b0 &
b0 '
b0 (
b0 )
b0 *
b0 +
#7500000
#7750000
0#
0.
0A
0L
#8000000
b11 ,
b1101101 /
b1101111 0
b1110010 1
b1100101 2
b100000 3
b1110100 4
b1110011 5
b1110100 6
b11 J
b1101101 M
b1101111 N
b1110010 O
b1100101 P
b100000 Q
b1110100 R
b1110011 S
b1110100 T
#8250000
b1110100 _
b1100101 g
b1110011 o
b1110100 w
b1101001 !"
b1101110 )"
b1100111 1"
b100001 9"
1#
1.
1A
1L
#8500000
#8750000
0#
0.
0A
0L
#9000000
1"
b0 ,
0-
b0 /
b0 0
b0 1
b0 2
b0 3
b0 4
b0 5
b0 6
07
08
09
0:
0;
0<
0=
0>
1@
b0 J
0K
b0 M
b0 N
b0 O
b0 P
b0 Q
b0 R
b0 S
b0 T
0U
0V
0W
0X
0Y
0Z
0[
0\
#9250000
b1101101 `
b1101111 h
b1110010 p
b1100101 x
b100000 ""
b1110100 *"
b1110011 2"
b1110100 :"
1#
1.
1A
1L
#9500000
#9750000
0#
0.
0A
0L
#10000000
b1 !
b1 ?
#10250000
1#
1.
1A
b11111110 B
b11011100 C
b10111010 D
b10011000 E
b1110110 F
b1010100 G
b110010 H
b10000 I
1L
b11111110 $
b11011100 %
b10111010 &
b10011000 '
b1110110 (
b1010100 )
b110010 *
b10000 +
#10500000
#10750000
0#
0.
0A
0L
#11000000
b10 !
b10 ?
#11250000
1#
1.
1A
b10011 B
b1010111 C
b10011011 D
b11011111 E
b10 F
b1000110 G
b10001010 H
b11001110 I
1L
b10011 $
b1010111 %
b10011011 &
b11011111 '
b10 (
b1000110 )
b10001010 *
b11001110 +
#11500000
#11750000
0#
0.
0A
0L
#12000000
b11 !
b11 ?
#12250000
1#
1.
1A
b1110100 B
b1100101 C
b1110011 D
b1110100 E
b1101001 F
b1101110 G
b1100111 H
b100001 I
1L
b1110100 $
b1100101 %
b1110011 &
b1110100 '
b1101001 (
b1101110 )
b1100111 *
b100001 +
#12500000
#12750000
0#
0.
0A
0L
#13000000
b0 !
0"
b0 ?
0@
#13250000
1#
1.
1A
b1101101 B
b1101111 C
b1110010 D
b1100101 E
b100000 F
b1110100 G
b1110011 H
b1110100 I
1L
b1101101 $
b1101111 %
b1110010 &
b1100101 '
b100000 (
b1110100 )
b1110011 *
b1110100 +
#13500000
#13750000
0#
0.
0A
0L
#14000000
#14250000
1#
1.
1A
b0 B
b0 C
b0 D
b0 E
b0 F
b0 G
b0 H
b0 I
1L
b0 $
b0 %
b0 &
b0 '
b0 (
b0 )
b0 *
b0 +
#14500000
#14750000
0#
0.
0A
0L
#15000000