add TraceAsString<T> -- sim traces it as a string rather than all its internal fields
All checks were successful
/ test (pull_request) Successful in 4m16s
/ test (push) Successful in 4m54s

This commit is contained in:
Jacob Lifshay 2026-05-11 22:44:54 -07:00
parent 26224abe1c
commit ea183eac87
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ
27 changed files with 5002 additions and 168 deletions

View file

@ -17,7 +17,7 @@ use crate::{
reg::Reg,
reset::{AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset},
sim::value::{SimValue, ToSimValue, ToSimValueWithType},
ty::{CanonicalType, OpaqueSimValue, StaticType, Type, TypeWithDeref},
ty::{CanonicalType, OpaqueSimValue, StaticType, TraceAsString, Type, TypeWithDeref},
util::{ConstBool, ConstUsize},
wire::Wire,
};
@ -218,6 +218,8 @@ expr_enum! {
SliceSInt(ops::SliceSInt),
CastToBits(ops::CastToBits),
CastBitsTo(ops::CastBitsTo),
AsTraceAsString(ops::AsTraceAsString),
TraceAsStringAsInner(ops::TraceAsStringAsInner),
ModuleIO(ModuleIO<CanonicalType>),
Instance(Instance<Bundle>),
Wire(Wire<CanonicalType>),
@ -389,6 +391,35 @@ impl<T: Type> Expr<T> {
__flow: this.__flow,
}
}
#[track_caller]
pub fn as_trace_as_string(this: Self, ty: TraceAsString<T>) -> Expr<TraceAsString<T>> {
assert_eq!(this.ty(), ty.inner_ty());
ops::AsTraceAsString::new(Expr::canonical(this), ty).to_expr()
}
}
impl Expr<CanonicalType> {
pub fn unwrap_transparent_types(mut this: Self) -> Expr<CanonicalType> {
loop {
match this.ty() {
CanonicalType::UInt(_)
| CanonicalType::SInt(_)
| CanonicalType::Bool(_)
| CanonicalType::Array(_)
| CanonicalType::Enum(_)
| CanonicalType::Bundle(_)
| CanonicalType::AsyncReset(_)
| CanonicalType::SyncReset(_)
| CanonicalType::Reset(_)
| CanonicalType::Clock(_)
| CanonicalType::PhantomConst(_)
| CanonicalType::DynSimOnly(_) => return this,
CanonicalType::TraceAsString(_) => {
this = *Expr::<TraceAsString>::from_canonical(this);
}
}
}
}
}
impl<T: Type> ToLiteralBits for Expr<T> {

View file

@ -11,8 +11,9 @@ use crate::{
HdlPartialEqImpl, HdlPartialOrd, HdlPartialOrdImpl, NotALiteralExpr, ReduceBitsImpl,
ToExpr, ToLiteralBits, ToSimValueInner, ToValueless, ValueType, Valueless,
target::{
GetTarget, Target, TargetPathArrayElement, TargetPathBundleField,
TargetPathDynArrayElement, TargetPathElement,
GetTarget, Target, TargetPathArrayElement, TargetPathAsTraceAsString,
TargetPathBundleField, TargetPathDynArrayElement, TargetPathElement,
TargetPathTraceAsStringInner,
},
value_category::ValueCategoryExpr,
},
@ -27,7 +28,7 @@ use crate::{
ToSyncReset,
},
sim::value::{SimValue, ToSimValue},
ty::{CanonicalType, StaticType, Type},
ty::{CanonicalType, StaticType, TraceAsString, Type},
util::ConstUsize,
};
use bitvec::{order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView};
@ -4694,3 +4695,185 @@ impl<This: ExprFromIterator<A>, A> FromIterator<A> for Expr<This> {
This::expr_from_iter(iter)
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct AsTraceAsString<T: Type = CanonicalType> {
inner: Expr<CanonicalType>,
ty: TraceAsString<T>,
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
target: Option<Interned<Target>>,
}
impl<T: Type> fmt::Debug for AsTraceAsString<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
inner,
ty: _,
literal_bits: _,
target: _,
} = self;
f.debug_struct("AsTraceAsString")
.field("inner", inner)
.finish_non_exhaustive()
}
}
impl<T: Type> AsTraceAsString<T> {
pub fn new(inner: Expr<CanonicalType>, ty: TraceAsString<T>) -> Self {
assert_eq!(inner.ty(), ty.inner_ty().canonical());
let literal_bits = inner.to_literal_bits();
let target = inner.target().map(|base| {
Intern::intern_sized(
base.join(TargetPathElement::intern_sized(
TargetPathAsTraceAsString {
ty: ty.canonical_trace_as_string(),
}
.into(),
)),
)
});
Self {
inner,
ty,
literal_bits,
target,
}
}
pub fn inner(self) -> Expr<CanonicalType> {
self.inner
}
}
impl<T: Type> GetTarget for AsTraceAsString<T> {
fn target(&self) -> Option<Interned<Target>> {
self.target
}
}
impl<T: Type> ToLiteralBits for AsTraceAsString<T> {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
self.literal_bits
}
}
impl<T: Type> ValueType for AsTraceAsString<T> {
type Type = TraceAsString<T>;
type ValueCategory = ValueCategoryExpr;
fn ty(&self) -> Self::Type {
self.ty
}
}
impl<T: Type> ToExpr for AsTraceAsString<T> {
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::AsTraceAsString(AsTraceAsString {
inner: self.inner,
ty: self.ty.canonical_trace_as_string(),
literal_bits: self.literal_bits,
target: self.target,
})
.intern(),
__ty: self.ty,
__flow: Expr::flow(self.inner),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct TraceAsStringAsInner<T: Type = CanonicalType> {
arg: Expr<TraceAsString<CanonicalType>>,
ty: T,
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
target: Option<Interned<Target>>,
}
impl<T: Type> fmt::Debug for TraceAsStringAsInner<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
arg,
ty: _,
literal_bits: _,
target: _,
} = self;
f.debug_struct("TraceAsStringAsInner")
.field("arg", arg)
.finish_non_exhaustive()
}
}
impl<T: Type> TraceAsStringAsInner<T> {
pub fn from_arg_and_ty(arg: Expr<TraceAsString<CanonicalType>>, ty: T) -> Self {
assert_eq!(arg.ty().inner_ty(), ty.canonical());
let literal_bits = arg.to_literal_bits();
let target = arg.target().map(|base| {
Intern::intern_sized(base.join(TargetPathElement::intern_sized(
TargetPathTraceAsStringInner {}.into(),
)))
});
Self {
arg,
ty,
literal_bits,
target,
}
}
pub fn new(arg: Expr<TraceAsString<T>>) -> Self {
Self::from_arg_and_ty(
Expr {
__enum: arg.__enum,
__ty: arg.__ty.canonical_trace_as_string(),
__flow: arg.__flow,
},
arg.ty().inner_ty(),
)
}
pub fn arg(self) -> Expr<TraceAsString<CanonicalType>> {
self.arg
}
pub fn arg_typed(self) -> Expr<TraceAsString<T>> {
Expr {
__enum: self.arg.__enum,
__ty: TraceAsString::from_canonical_trace_as_string(self.arg.__ty),
__flow: self.arg.__flow,
}
}
}
impl<T: Type> GetTarget for TraceAsStringAsInner<T> {
fn target(&self) -> Option<Interned<Target>> {
self.target
}
}
impl<T: Type> ToLiteralBits for TraceAsStringAsInner<T> {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
self.literal_bits
}
}
impl<T: Type> ValueType for TraceAsStringAsInner<T> {
type Type = T;
type ValueCategory = ValueCategoryExpr;
fn ty(&self) -> Self::Type {
self.ty
}
}
impl<T: Type> ToExpr for TraceAsStringAsInner<T> {
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::TraceAsStringAsInner(TraceAsStringAsInner {
arg: self.arg,
ty: self.ty.canonical(),
literal_bits: self.literal_bits,
target: self.target,
})
.intern(),
__ty: self.ty,
__flow: Expr::flow(self.arg),
}
}
}

View file

@ -10,7 +10,7 @@ use crate::{
reg::Reg,
reset::{AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset},
source_location::SourceLocation,
ty::{CanonicalType, Type},
ty::{CanonicalType, TraceAsString, Type},
wire::Wire,
};
use std::fmt;
@ -46,11 +46,33 @@ impl fmt::Display for TargetPathDynArrayElement {
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct TargetPathTraceAsStringInner {}
impl fmt::Display for TargetPathTraceAsStringInner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, ".<inner>")
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct TargetPathAsTraceAsString {
pub ty: TraceAsString<CanonicalType>,
}
impl fmt::Display for TargetPathAsTraceAsString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, ".as_trace_as_string(...)")
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum TargetPathElement {
BundleField(TargetPathBundleField),
ArrayElement(TargetPathArrayElement),
DynArrayElement(TargetPathDynArrayElement),
TraceAsStringInner(TargetPathTraceAsStringInner),
AsTraceAsString(TargetPathAsTraceAsString),
}
impl From<TargetPathBundleField> for TargetPathElement {
@ -71,12 +93,26 @@ impl From<TargetPathDynArrayElement> for TargetPathElement {
}
}
impl From<TargetPathTraceAsStringInner> for TargetPathElement {
fn from(value: TargetPathTraceAsStringInner) -> Self {
Self::TraceAsStringInner(value)
}
}
impl From<TargetPathAsTraceAsString> for TargetPathElement {
fn from(value: TargetPathAsTraceAsString) -> Self {
Self::AsTraceAsString(value)
}
}
impl fmt::Display for TargetPathElement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::BundleField(v) => v.fmt(f),
Self::ArrayElement(v) => v.fmt(f),
Self::DynArrayElement(v) => v.fmt(f),
Self::TraceAsStringInner(v) => v.fmt(f),
Self::AsTraceAsString(v) => v.fmt(f),
}
}
}
@ -100,6 +136,15 @@ impl TargetPathElement {
let parent_ty = Array::<CanonicalType>::from_canonical(parent.canonical_ty());
parent_ty.element()
}
Self::TraceAsStringInner(_) => {
let parent_ty =
TraceAsString::<CanonicalType>::from_canonical(parent.canonical_ty());
parent_ty.inner_ty()
}
&Self::AsTraceAsString(TargetPathAsTraceAsString { ty }) => {
assert_eq!(parent.canonical_ty(), ty.inner_ty());
ty.canonical()
}
}
}
pub fn flow(&self, parent: Interned<Target>) -> Flow {
@ -111,13 +156,18 @@ impl TargetPathElement {
.expect("field name is known to be a valid field of parent type");
parent.flow().flip_if(field.flipped)
}
Self::ArrayElement(_) => parent.flow(),
Self::DynArrayElement(_) => parent.flow(),
Self::ArrayElement(_)
| Self::DynArrayElement(_)
| Self::TraceAsStringInner(_)
| Self::AsTraceAsString(_) => parent.flow(),
}
}
pub fn is_static(&self) -> bool {
match self {
Self::BundleField(_) | Self::ArrayElement(_) => true,
Self::BundleField(_)
| Self::ArrayElement(_)
| Self::TraceAsStringInner(_)
| Self::AsTraceAsString(_) => true,
Self::DynArrayElement(_) => false,
}
}

View file

@ -16,6 +16,7 @@ use crate::{
ops::{self, VariantAccess},
target::{
Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathElement,
TargetPathTraceAsStringInner,
},
},
formal::FormalKind,
@ -471,7 +472,7 @@ impl TypeState {
Ok(self.enum_def(ty)?.1.variants.borrow_mut().get(name))
}
fn ty<T: Type>(&self, ty: T) -> Result<String, FirrtlError> {
Ok(match ty.canonical() {
Ok(match ty.canonical().unwrap_transparent_types() {
CanonicalType::Bundle(ty) => self.bundle_ty(ty)?.to_string(),
CanonicalType::Enum(ty) => self.enum_ty(ty)?.to_string(),
CanonicalType::Array(ty) => {
@ -490,6 +491,7 @@ impl TypeState {
CanonicalType::DynSimOnly(_) => {
return Err(FirrtlError::SimOnlyValuesAreNotPermitted);
}
CanonicalType::TraceAsString(_) => unreachable!("handled by unwrap_transparent_types"),
})
}
}
@ -1191,7 +1193,7 @@ impl<'a> Exporter<'a> {
definitions: &RcDefinitions,
extra_indent: Indent<'_>,
) -> Result<String> {
match ty {
match ty.unwrap_transparent_types() {
CanonicalType::Bundle(ty) => {
self.expr_cast_bundle_to_bits(value_str, ty, definitions, extra_indent)
}
@ -1210,6 +1212,7 @@ impl<'a> Exporter<'a> {
| CanonicalType::Reset(_) => Ok(format!("asUInt({value_str})")),
CanonicalType::PhantomConst(_) => Ok("UInt<0>(0)".into()),
CanonicalType::DynSimOnly(_) => Err(FirrtlError::SimOnlyValuesAreNotPermitted.into()),
CanonicalType::TraceAsString(_) => unreachable!("handled by unwrap_transparent_types"),
}
}
fn expr_cast_bits_to_bundle(
@ -1407,7 +1410,7 @@ impl<'a> Exporter<'a> {
definitions: &RcDefinitions,
extra_indent: Indent<'_>,
) -> Result<String> {
match ty {
match ty.unwrap_transparent_types() {
CanonicalType::Bundle(ty) => {
self.expr_cast_bits_to_bundle(value_str, ty, definitions, extra_indent)
}
@ -1431,6 +1434,7 @@ impl<'a> Exporter<'a> {
return Ok(retval.to_string());
}
CanonicalType::DynSimOnly(_) => Err(FirrtlError::SimOnlyValuesAreNotPermitted.into()),
CanonicalType::TraceAsString(_) => unreachable!("handled by unwrap_transparent_types"),
}
}
fn expr_unary<T: Type>(
@ -1798,6 +1802,10 @@ impl<'a> Exporter<'a> {
write!(out, "[{index}]").unwrap();
Ok(out)
}
ExprEnum::AsTraceAsString(expr) => self.expr(expr.inner(), definitions, const_ty),
ExprEnum::TraceAsStringAsInner(expr) => {
self.expr(Expr::canonical(expr.arg()), definitions, const_ty)
}
ExprEnum::ModuleIO(expr) => Ok(self.module.ns.get(expr.name_id()).to_string()),
ExprEnum::Instance(expr) => {
assert!(!const_ty, "not a constant");
@ -1957,6 +1965,10 @@ impl<'a> Exporter<'a> {
.segments
.push(AnnotationTargetRefSegment::Index { index }),
TargetPathElement::DynArrayElement(_) => unreachable!(),
TargetPathElement::AsTraceAsString(_)
| TargetPathElement::TraceAsStringInner(_) => {
// ignored
}
}
Ok(retval)
}
@ -3211,6 +3223,8 @@ impl ScalarizeTreeNode {
TargetPathElement::DynArrayElement(_) => {
unreachable!("annotations are only on static targets");
}
TargetPathElement::AsTraceAsString(_)
| TargetPathElement::TraceAsStringInner(_) => parent,
}
}
}
@ -3337,6 +3351,13 @@ impl ScalarizeTreeBuilder {
CanonicalType::DynSimOnly(_) => {
return Err(ScalarizedModuleABIError::SimOnlyValuesAreNotPermitted);
}
CanonicalType::TraceAsString(_) => self.build(
target
.join(TargetPathElement::intern_sized(
TargetPathTraceAsStringInner {}.into(),
))
.intern_sized(),
)?,
})
}
}

View file

@ -1277,6 +1277,9 @@ macro_rules! impl_int {
pub fn bitvec_mut(&mut self) -> &mut BitVec {
Arc::make_mut(&mut self.bits)
}
pub fn arc_bitvec_mut(&mut self) -> &mut Arc<BitVec> {
&mut self.bits
}
}
};
}

View file

@ -1093,6 +1093,7 @@ pub fn splat_mask<T: Type>(ty: T, value: Expr<Bool>) -> Expr<AsMask<T>> {
.to_expr(),
)),
CanonicalType::PhantomConst(_) => Expr::from_canonical(Expr::canonical(().to_expr())),
CanonicalType::TraceAsString(ty) => Expr::from_canonical(splat_mask(ty.inner_ty(), value)),
}
}

View file

@ -12,7 +12,7 @@ use crate::{
ops::VariantAccess,
target::{
GetTarget, Target, TargetBase, TargetPathArrayElement, TargetPathBundleField,
TargetPathElement,
TargetPathElement, TargetPathTraceAsStringInner,
},
value_category::ValueCategoryExpr,
},
@ -1111,7 +1111,10 @@ fn validate_clock_for_past<S: ModuleBuildingStatus>(
let mut target = clock_for_past;
while let Target::Child(child) = target {
match *child.path_element() {
TargetPathElement::BundleField(_) | TargetPathElement::ArrayElement(_) => {}
TargetPathElement::BundleField(_)
| TargetPathElement::ArrayElement(_)
| TargetPathElement::AsTraceAsString(_)
| TargetPathElement::TraceAsStringInner(_) => {}
TargetPathElement::DynArrayElement(_) => {
panic!(
"clock_for_past: clock must be a static target (you can't use `Expr<UInt>` array indexes):\n{clock_for_past:?}"
@ -1586,6 +1589,14 @@ impl TargetState {
declared_in_block,
written_in_blocks: RefCell::default(),
},
CanonicalType::TraceAsString(_) => {
return Self::new(
target
.join(Intern::intern_sized(TargetPathTraceAsStringInner {}.into()))
.intern_sized(),
declared_in_block,
);
}
},
}
}
@ -1605,44 +1616,58 @@ impl AssertValidityState {
}
fn get_target_states<'a>(
&'a self,
target: &Target,
mut target: &Target,
process_target_state: &dyn Fn(&'a TargetState, bool),
) -> Result<(), ()> {
match target {
Target::Base(target_base) => {
let target_state = self.get_base_state(*target_base)?;
process_target_state(target_state, false);
Ok(())
}
Target::Child(target_child) => self.get_target_states(
&target_child.parent(),
&|target_state, exact_target_unknown| {
let TargetStateInner::Decomposed { subtargets } = &target_state.inner else {
unreachable!(
"TargetState::new makes TargetState tree match the Target type"
);
};
match *target_child.path_element() {
TargetPathElement::BundleField(_) => process_target_state(
subtargets
.get(&target_child.path_element())
.expect("bundle fields filled in by TargetState::new"),
exact_target_unknown,
),
TargetPathElement::ArrayElement(_) => process_target_state(
subtargets
.get(&target_child.path_element())
.expect("array elements filled in by TargetState::new"),
exact_target_unknown,
),
TargetPathElement::DynArrayElement(_) => {
for target_state in subtargets.values() {
process_target_state(target_state, true);
loop {
break match target {
Target::Base(target_base) => {
let target_state = self.get_base_state(*target_base)?;
process_target_state(target_state, false);
Ok(())
}
Target::Child(target_child) => match *target_child.path_element() {
TargetPathElement::BundleField(_)
| TargetPathElement::ArrayElement(_)
| TargetPathElement::DynArrayElement(_) => self.get_target_states(
&target_child.parent(),
&|target_state, exact_target_unknown| {
let TargetStateInner::Decomposed { subtargets } = &target_state.inner
else {
unreachable!(
"TargetState::new makes TargetState tree match the Target type"
);
};
match *target_child.path_element() {
TargetPathElement::BundleField(_) => process_target_state(
subtargets
.get(&target_child.path_element())
.expect("bundle fields filled in by TargetState::new"),
exact_target_unknown,
),
TargetPathElement::ArrayElement(_) => process_target_state(
subtargets
.get(&target_child.path_element())
.expect("array elements filled in by TargetState::new"),
exact_target_unknown,
),
TargetPathElement::DynArrayElement(_) => {
for target_state in subtargets.values() {
process_target_state(target_state, true);
}
}
TargetPathElement::TraceAsStringInner(_)
| TargetPathElement::AsTraceAsString(_) => unreachable!(),
}
}
},
),
TargetPathElement::TraceAsStringInner(_)
| TargetPathElement::AsTraceAsString(_) => {
target = Interned::into_inner(target_child.parent());
continue;
}
},
),
};
}
}
fn get_base_state(&self, target_base: Interned<TargetBase>) -> Result<&TargetState, ()> {
@ -1716,6 +1741,8 @@ impl AssertValidityState {
TargetPathElement::DynArrayElement { .. } => {
Self::set_connect_target_written(sub_target_state, is_lhs, block, true);
}
TargetPathElement::TraceAsStringInner(_)
| TargetPathElement::AsTraceAsString(_) => unreachable!("never added"),
}
}
}

View file

@ -9,8 +9,9 @@ use crate::{
ExprEnum, ValueType,
ops::{self, ArrayLiteral},
target::{
Target, TargetBase, TargetChild, TargetPathArrayElement, TargetPathBundleField,
TargetPathDynArrayElement, TargetPathElement,
Target, TargetBase, TargetChild, TargetPathArrayElement, TargetPathAsTraceAsString,
TargetPathBundleField, TargetPathDynArrayElement, TargetPathElement,
TargetPathTraceAsStringInner,
},
},
formal::FormalKind,
@ -26,6 +27,7 @@ use crate::{
prelude::*,
reset::{ResetType, ResetTypeDispatch},
sim::ExternModuleSimulation,
ty::TraceAsString,
util::{HashMap, HashSet},
};
use hashbrown::hash_map::Entry;
@ -103,6 +105,10 @@ enum ResetsLayout {
element: Interned<ResetsLayout>,
reset_count: usize,
},
Transparent {
inner: Interned<ResetsLayout>,
reset_count: usize,
},
}
impl ResetsLayout {
@ -112,7 +118,8 @@ impl ResetsLayout {
ResetsLayout::Reset | ResetsLayout::SyncReset | ResetsLayout::AsyncReset => 1,
ResetsLayout::Bundle { reset_count, .. }
| ResetsLayout::Enum { reset_count, .. }
| ResetsLayout::Array { reset_count, .. } => reset_count,
| ResetsLayout::Array { reset_count, .. }
| ResetsLayout::Transparent { reset_count, .. } => reset_count,
}
}
fn new(ty: CanonicalType) -> Self {
@ -166,6 +173,13 @@ impl ResetsLayout {
CanonicalType::Clock(_) => ResetsLayout::NoResets,
CanonicalType::PhantomConst(_) => ResetsLayout::NoResets,
CanonicalType::DynSimOnly(_) => ResetsLayout::NoResets,
CanonicalType::TraceAsString(ty) => {
let inner = ResetsLayout::new(ty.inner_ty()).intern_sized();
ResetsLayout::Transparent {
inner,
reset_count: inner.reset_count(),
}
}
}
}
}
@ -315,6 +329,12 @@ impl ResetGraph {
} => {
self.append_new_nodes_for_layout(*element, node_indexes, source_location);
}
ResetsLayout::Transparent {
inner,
reset_count: _,
} => {
self.append_new_nodes_for_layout(*inner, node_indexes, source_location);
}
}
}
}
@ -357,6 +377,21 @@ impl Resets {
node_indexes: self.node_indexes,
}
}
fn trace_as_string_inner(self) -> Self {
let trace_as_string = TraceAsString::from_canonical(self.ty);
let ResetsLayout::Transparent {
inner,
reset_count: _,
} = self.layout
else {
unreachable!();
};
Self {
ty: trace_as_string.inner_ty(),
layout: *inner,
node_indexes: self.node_indexes,
}
}
fn bundle_fields(self) -> impl Iterator<Item = Self> {
let bundle = Bundle::from_canonical(self.ty);
let ResetsLayout::Bundle {
@ -480,6 +515,17 @@ impl Resets {
CanonicalType::SyncReset(SyncReset)
},
),
CanonicalType::TraceAsString(ty) => Ok(CanonicalType::TraceAsString(
ty.with_new_inner_ty(
self.array_elements()
.substituted_type(
reset_graph,
fallback_to_sync_reset,
fallback_error_source_location,
)?
.intern_sized(),
),
)),
}
}
}
@ -1013,7 +1059,8 @@ fn cast_bit_op<P: Pass, T: Type, A: Type>(
| CanonicalType::Bundle(_)
| CanonicalType::Reset(_)
| CanonicalType::PhantomConst(_)
| CanonicalType::DynSimOnly(_) => unreachable!(),
| CanonicalType::DynSimOnly(_)
| CanonicalType::TraceAsString(_) => unreachable!(),
$(CanonicalType::$Variant(ty) => Expr::expr_enum($arg.cast_to(ty)),)*
}
};
@ -1024,7 +1071,8 @@ fn cast_bit_op<P: Pass, T: Type, A: Type>(
CanonicalType::Array(_)
| CanonicalType::Enum(_)
| CanonicalType::Bundle(_)
| CanonicalType::Reset(_) => unreachable!(),
| CanonicalType::Reset(_)
| CanonicalType::TraceAsString(_) => unreachable!(),
CanonicalType::PhantomConst(_) |
CanonicalType::DynSimOnly(_) => Expr::expr_enum(arg),
$(CanonicalType::$Variant(_) => {
@ -1156,6 +1204,10 @@ impl<P: Pass> RunPass<P> for ExprEnum {
ExprEnum::SliceSInt(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CastToBits(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CastBitsTo(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::TraceAsStringAsInner(expr) => {
Ok(expr.run_pass(pass_args)?.map(ExprEnum::from))
}
ExprEnum::AsTraceAsString(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::ModuleIO(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::Instance(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::Wire(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
@ -1536,6 +1588,67 @@ impl RunPassExpr for ops::CastBitsTo {
}
}
impl RunPassExpr for ops::TraceAsStringAsInner {
type Args<'a> = [Expr<CanonicalType>; 1];
fn args<'a>(&'a self) -> Self::Args<'a> {
[Expr::canonical(self.arg())]
}
fn source_location(&self) -> Option<SourceLocation> {
None
}
fn union_parts(
&self,
resets: Resets,
args_resets: Vec<Resets>,
mut pass_args: PassArgs<'_, BuildResetGraph>,
) -> Result<(), DeduceResetsError> {
pass_args.union(resets, args_resets[0].trace_as_string_inner(), None)
}
fn new(
&self,
_ty: CanonicalType,
new_args: Vec<Expr<CanonicalType>>,
) -> Result<Self, DeduceResetsError> {
Ok(Self::new(Expr::from_canonical(new_args[0])))
}
}
impl RunPassExpr for ops::AsTraceAsString {
type Args<'a> = [Expr<CanonicalType>; 1];
fn args<'a>(&'a self) -> Self::Args<'a> {
[Expr::canonical(self.inner())]
}
fn source_location(&self) -> Option<SourceLocation> {
None
}
fn union_parts(
&self,
resets: Resets,
args_resets: Vec<Resets>,
mut pass_args: PassArgs<'_, BuildResetGraph>,
) -> Result<(), DeduceResetsError> {
pass_args.union(resets.trace_as_string_inner(), args_resets[0], None)
}
fn new(
&self,
_ty: CanonicalType,
new_args: Vec<Expr<CanonicalType>>,
) -> Result<Self, DeduceResetsError> {
Ok(Self::new(
new_args[0],
self.ty().with_new_inner_ty(new_args[0].ty().intern_sized()),
))
}
}
impl RunPassExpr for ModuleIO<CanonicalType> {
type Args<'a> = [Expr<CanonicalType>; 0];
@ -1691,7 +1804,8 @@ impl RunPassDispatch for AnyReg {
| CanonicalType::Reset(_)
| CanonicalType::Clock(_)
| CanonicalType::PhantomConst(_)
| CanonicalType::DynSimOnly(_) => unreachable!(),
| CanonicalType::DynSimOnly(_)
| CanonicalType::TraceAsString(_) => unreachable!(),
}
})
}
@ -2173,30 +2287,6 @@ impl<P: Pass> RunPass<P> for StmtDeclaration {
}
}
impl_run_pass_for_struct! {
impl[] RunPass for TargetPathBundleField {
name: _,
}
}
impl_run_pass_for_struct! {
impl[] RunPass for TargetPathArrayElement {
index: _,
}
}
impl_run_pass_for_struct! {
impl[] RunPass for TargetPathDynArrayElement {}
}
impl_run_pass_for_enum! {
impl[] RunPass for TargetPathElement {
BundleField(v),
ArrayElement(v),
DynArrayElement(v),
}
}
impl_run_pass_for_enum! {
impl[] RunPass for Target {
Base(v),
@ -2204,11 +2294,28 @@ impl_run_pass_for_enum! {
}
}
impl_run_pass_for_struct! {
#[constructor = TargetChild::new(parent, path_element)]
impl[] RunPass for TargetChild {
parent(): _,
path_element(): _,
impl<P: Pass> RunPass<P> for TargetChild {
fn run_pass(
&self,
mut pass_args: PassArgs<'_, P>,
) -> Result<PassOutput<Self, P>, DeduceResetsError> {
Ok(self.parent().run_pass(pass_args.as_mut())?.map(|parent| {
let path_element = match *self.path_element() {
TargetPathElement::BundleField(TargetPathBundleField { name: _ })
| TargetPathElement::ArrayElement(TargetPathArrayElement { index: _ })
| TargetPathElement::DynArrayElement(TargetPathDynArrayElement {})
| TargetPathElement::TraceAsStringInner(TargetPathTraceAsStringInner {}) => {
self.path_element()
}
TargetPathElement::AsTraceAsString(TargetPathAsTraceAsString { ty }) => {
TargetPathElement::from(TargetPathAsTraceAsString {
ty: ty.with_new_inner_ty(parent.canonical_ty().intern_sized()),
})
.intern_sized()
}
};
TargetChild::new(parent, path_element)
}))
}
}

View file

@ -17,7 +17,7 @@ use crate::{
transform::visit::{Fold, Folder},
},
source_location::SourceLocation,
ty::{CanonicalType, Type},
ty::{CanonicalType, TraceAsString, Type},
util::HashMap,
wire::Wire,
};
@ -64,6 +64,7 @@ fn contains_any_enum_types(ty: CanonicalType) -> bool {
.fields()
.iter()
.any(|field| contains_any_enum_types(field.ty)),
CanonicalType::TraceAsString(ty) => contains_any_enum_types(ty.inner_ty()),
CanonicalType::UInt(_)
| CanonicalType::SInt(_)
| CanonicalType::Bool(_)
@ -313,6 +314,24 @@ impl State {
}
Ok(())
}
fn handle_stmt_connect_trace_as_string(
&mut self,
unfolded_lhs_ty: TraceAsString,
unfolded_rhs_ty: TraceAsString,
folded_lhs: Expr<TraceAsString>,
folded_rhs: Expr<TraceAsString>,
source_location: SourceLocation,
output_stmts: &mut Vec<Stmt>,
) -> Result<(), SimplifyEnumsError> {
self.handle_stmt_connect(
unfolded_lhs_ty.inner_ty(),
unfolded_rhs_ty.inner_ty(),
ops::TraceAsStringAsInner::new(folded_lhs).to_expr(),
ops::TraceAsStringAsInner::new(folded_rhs).to_expr(),
source_location,
output_stmts,
)
}
fn handle_stmt_connect_bundle(
&mut self,
unfolded_lhs_ty: Bundle,
@ -509,6 +528,15 @@ impl State {
source_location,
output_stmts,
),
CanonicalType::TraceAsString(unfolded_lhs_ty) => self
.handle_stmt_connect_trace_as_string(
unfolded_lhs_ty,
TraceAsString::from_canonical(unfolded_rhs_ty),
Expr::from_canonical(folded_lhs),
Expr::from_canonical(folded_rhs),
source_location,
output_stmts,
),
CanonicalType::UInt(_)
| CanonicalType::SInt(_)
| CanonicalType::Bool(_)
@ -528,6 +556,8 @@ fn connect_port(
rhs: Expr<CanonicalType>,
source_location: SourceLocation,
) {
let lhs = Expr::unwrap_transparent_types(lhs);
let rhs = Expr::unwrap_transparent_types(rhs);
if lhs.ty() == rhs.ty() {
stmts.push(
StmtConnect {
@ -573,6 +603,9 @@ fn connect_port(
connect_port(stmts, lhs[index], rhs[index], source_location);
}
}
(CanonicalType::TraceAsString(_), CanonicalType::TraceAsString(_)) => {
unreachable!("handled by unwrap_transparent_types")
}
(CanonicalType::Bundle(_), _)
| (CanonicalType::Enum(_), _)
| (CanonicalType::Array(_), _)
@ -584,7 +617,8 @@ fn connect_port(
| (CanonicalType::SyncReset(_), _)
| (CanonicalType::Reset(_), _)
| (CanonicalType::PhantomConst(_), _)
| (CanonicalType::DynSimOnly(_), _) => unreachable!(
| (CanonicalType::DynSimOnly(_), _)
| (CanonicalType::TraceAsString(_), _) => unreachable!(
"trying to connect memory ports:\n{:?}\n{:?}",
lhs.ty(),
rhs.ty(),
@ -772,6 +806,8 @@ impl Folder for State {
| ExprEnum::SliceSInt(_)
| ExprEnum::CastToBits(_)
| ExprEnum::CastBitsTo(_)
| ExprEnum::TraceAsStringAsInner(_)
| ExprEnum::AsTraceAsString(_)
| ExprEnum::ModuleIO(_)
| ExprEnum::Instance(_)
| ExprEnum::Wire(_)
@ -936,7 +972,8 @@ impl Folder for State {
| CanonicalType::SyncReset(_)
| CanonicalType::Reset(_)
| CanonicalType::PhantomConst(_)
| CanonicalType::DynSimOnly(_) => canonical_type.default_fold(self),
| CanonicalType::DynSimOnly(_)
| CanonicalType::TraceAsString(_) => canonical_type.default_fold(self),
}
}

View file

@ -90,7 +90,7 @@ impl MemSplit {
}
}
fn new(element_type: CanonicalType) -> Self {
match element_type {
match element_type.unwrap_transparent_types() {
CanonicalType::Bundle(bundle_ty) => MemSplit::Bundle {
fields: bundle_ty
.fields()
@ -195,6 +195,7 @@ impl MemSplit {
| CanonicalType::SyncReset(_)
| CanonicalType::Reset(_) => unreachable!("memory element type is a storable type"),
CanonicalType::DynSimOnly(_) => todo!("memory containing sim-only values"),
CanonicalType::TraceAsString(_) => unreachable!("handled by unwrap_transparent_types"),
}
}
}
@ -306,7 +307,9 @@ impl SplitMemState<'_, '_> {
let outer_mem_name_path_len = self.mem_name_path.len();
match self.split {
MemSplit::Bundle { fields } => {
let CanonicalType::Bundle(bundle_type) = self.element_type else {
let CanonicalType::Bundle(bundle_type) =
self.element_type.unwrap_transparent_types()
else {
unreachable!();
};
for ((field, field_offset), split) in bundle_type
@ -321,7 +324,10 @@ impl SplitMemState<'_, '_> {
let field_ty_bit_width = field.ty.bit_width();
self.split_state_stack.push_map(
|e: Expr<CanonicalType>| {
Expr::field(Expr::<Bundle>::from_canonical(e), &field.name)
Expr::field(
Expr::<Bundle>::from_canonical(Expr::unwrap_transparent_types(e)),
&field.name,
)
},
|initial_value_element| {
let Some(field_offset) = field_offset.only_bit_width() else {
@ -377,8 +383,8 @@ impl SplitMemState<'_, '_> {
};
self.output_stmts.push(
StmtConnect {
lhs: Expr::field(port_expr, name),
rhs: Expr::field(wire_expr, name),
lhs: Expr::unwrap_transparent_types(Expr::field(port_expr, name)),
rhs: Expr::unwrap_transparent_types(Expr::field(wire_expr, name)),
source_location: port.source_location(),
}
.into(),
@ -389,7 +395,8 @@ impl SplitMemState<'_, '_> {
self.output_mems.push(new_mem);
}
MemSplit::Array { elements } => {
let CanonicalType::Array(array_type) = self.element_type else {
let CanonicalType::Array(array_type) = self.element_type.unwrap_transparent_types()
else {
unreachable!();
};
let element_type = array_type.element();
@ -398,7 +405,7 @@ impl SplitMemState<'_, '_> {
self.mem_name_path.truncate(outer_mem_name_path_len);
write!(self.mem_name_path, "_{index}").unwrap();
self.split_state_stack.push_map(
|e| Expr::<Array>::from_canonical(e)[index],
|e| Expr::<Array>::from_canonical(Expr::unwrap_transparent_types(e))[index],
|initial_value_element| {
&initial_value_element[index * element_bit_width..][..element_bit_width]
},
@ -464,7 +471,7 @@ impl ModuleState {
assert_eq!(memory_element_array_range_len % input_array_type.len(), 0);
let chunk_size = memory_element_array_range_len / input_array_type.len();
for index in 0..input_array_type.len() {
let map = |e| Expr::<Array>::from_canonical(e)[index];
let map = |e| Expr::<Array>::from_canonical(Expr::unwrap_transparent_types(e))[index];
let wire_rdata = wire_rdata.map(map);
let wire_wdata = wire_wdata.map(map);
let wire_wmask = wire_wmask.map(map);
@ -505,8 +512,8 @@ impl ModuleState {
port_read: Expr<CanonicalType>| {
output_stmts.push(
StmtConnect {
lhs: wire_read,
rhs: port_read,
lhs: Expr::unwrap_transparent_types(wire_read),
rhs: Expr::unwrap_transparent_types(port_read),
source_location,
}
.into(),
@ -517,8 +524,8 @@ impl ModuleState {
port_write: Expr<CanonicalType>| {
output_stmts.push(
StmtConnect {
lhs: port_write,
rhs: wire_write,
lhs: Expr::unwrap_transparent_types(port_write),
rhs: Expr::unwrap_transparent_types(wire_write),
source_location,
}
.into(),
@ -530,7 +537,8 @@ impl ModuleState {
connect_read(
output_stmts,
wire_read,
Expr::<UInt>::from_canonical(port_read).cast_bits_to(wire_read.ty()),
Expr::<UInt>::from_canonical(Expr::unwrap_transparent_types(port_read))
.cast_bits_to(wire_read.ty()),
);
};
let connect_write_enum =
@ -544,7 +552,7 @@ impl ModuleState {
);
};
loop {
match input_element_type {
match input_element_type.unwrap_transparent_types() {
CanonicalType::Bundle(_) => {
unreachable!("bundle types are always split")
}
@ -625,6 +633,9 @@ impl ModuleState {
| CanonicalType::SyncReset(_)
| CanonicalType::Reset(_) => unreachable!("memory element type is a storable type"),
CanonicalType::DynSimOnly(_) => todo!("memory containing sim-only values"),
CanonicalType::TraceAsString(_) => {
unreachable!("handled by unwrap_transparent_types")
}
}
break;
}

View file

@ -13,8 +13,9 @@ use crate::{
expr::{
Expr, ExprEnum, ValueType, ops,
target::{
Target, TargetBase, TargetChild, TargetPathArrayElement, TargetPathBundleField,
TargetPathDynArrayElement, TargetPathElement,
Target, TargetBase, TargetChild, TargetPathArrayElement, TargetPathAsTraceAsString,
TargetPathBundleField, TargetPathDynArrayElement, TargetPathElement,
TargetPathTraceAsStringInner,
},
},
formal::FormalKind,
@ -32,7 +33,7 @@ use crate::{
reset::{AsyncReset, Reset, ResetType, SyncReset},
sim::{ExternModuleSimulation, value::DynSimOnly},
source_location::SourceLocation,
ty::{CanonicalType, Type},
ty::{CanonicalType, TraceAsString, Type},
vendor::xilinx::{
XdcCreateClockAnnotation, XdcIOStandardAnnotation, XdcLocationAnnotation, XilinxAnnotation,
},

View file

@ -9,6 +9,7 @@ use crate::{
Flow,
target::{
GetTarget, Target, TargetPathArrayElement, TargetPathBundleField, TargetPathElement,
TargetPathTraceAsStringInner,
},
},
int::BoolOrIntType,
@ -38,7 +39,7 @@ use crate::{
},
ty::{
OpaqueSimValue, OpaqueSimValueSize, OpaqueSimValueSizeRange, OpaqueSimValueSlice,
OpaqueSimValueWriter,
OpaqueSimValueWriter, TraceAsString,
},
util::{BitSliceWriteWithBase, DebugAsDisplay, HashMap, HashSet, copy_le_bytes_to_bitslice},
};
@ -432,6 +433,15 @@ impl_trace_decl! {
ty: DynSimOnly,
flow: Flow,
}),
TraceAsString(TraceTraceAsString {
fn location(self) -> _ {
self.location
}
location: TraceLocation,
name: Interned<str>,
ty: TraceAsString,
flow: Flow,
}),
}),
}
@ -543,6 +553,7 @@ pub trait TraceWriter: fmt::Debug + 'static {
id: TraceScalarId,
value: &DynSimOnlyValue,
) -> Result<(), Self::Error>;
fn set_signal_string(&mut self, id: TraceScalarId, value: &str) -> Result<(), Self::Error>;
}
pub struct DynTraceWriterDecls(Box<dyn TraceWriterDeclsDynTrait>);
@ -607,6 +618,7 @@ trait TraceWriterDynTrait: fmt::Debug + 'static {
id: TraceScalarId,
value: &DynSimOnlyValue,
) -> std::io::Result<()>;
fn set_signal_string_dyn(&mut self, id: TraceScalarId, value: &str) -> std::io::Result<()>;
}
impl<T: TraceWriter> TraceWriterDynTrait for T {
@ -680,6 +692,9 @@ impl<T: TraceWriter> TraceWriterDynTrait for T {
) -> std::io::Result<()> {
Ok(TraceWriter::set_signal_sim_only_value(self, id, value).map_err(err_into_io)?)
}
fn set_signal_string_dyn(&mut self, id: TraceScalarId, value: &str) -> std::io::Result<()> {
Ok(TraceWriter::set_signal_string(self, id, value).map_err(err_into_io)?)
}
}
pub struct DynTraceWriter(Box<dyn TraceWriterDynTrait>);
@ -758,6 +773,9 @@ impl TraceWriter for DynTraceWriter {
) -> Result<(), Self::Error> {
self.0.set_signal_sim_only_value_dyn(id, value)
}
fn set_signal_string(&mut self, id: TraceScalarId, value: &str) -> Result<(), Self::Error> {
self.0.set_signal_string_dyn(id, value)
}
}
#[derive(Debug)]
@ -934,6 +952,10 @@ pub(crate) enum SimTraceKind {
PhantomConst {
ty: PhantomConst,
},
TraceAsString {
layout: compiler::CompiledTypeLayout<TraceAsString>,
range: TypeIndexRange,
},
}
#[derive(PartialEq, Eq)]
@ -941,6 +963,7 @@ pub(crate) enum SimTraceState {
Bits(BitVec),
SimOnly(DynSimOnlyValue),
PhantomConst,
OpaqueSimValue(OpaqueSimValue),
}
impl Clone for SimTraceState {
@ -949,6 +972,7 @@ impl Clone for SimTraceState {
Self::Bits(v) => Self::Bits(v.clone()),
Self::SimOnly(v) => Self::SimOnly(v.clone()),
Self::PhantomConst => Self::PhantomConst,
Self::OpaqueSimValue(v) => Self::OpaqueSimValue(v.clone()),
}
}
fn clone_from(&mut self, source: &Self) {
@ -956,6 +980,9 @@ impl Clone for SimTraceState {
(SimTraceState::Bits(dest), SimTraceState::Bits(source)) => {
dest.clone_from_bitslice(source);
}
(SimTraceState::OpaqueSimValue(dest), SimTraceState::OpaqueSimValue(source)) => {
dest.clone_from(source);
}
_ => *self = source.clone(),
}
}
@ -990,6 +1017,20 @@ impl SimTraceState {
unreachable!()
}
}
fn unwrap_opaque_sim_value_ref(&self) -> &OpaqueSimValue {
if let SimTraceState::OpaqueSimValue(v) = self {
v
} else {
unreachable!()
}
}
fn unwrap_opaque_sim_value_mut(&mut self) -> &mut OpaqueSimValue {
if let SimTraceState::OpaqueSimValue(v) = self {
v
} else {
unreachable!()
}
}
}
impl fmt::Debug for SimTraceState {
@ -998,6 +1039,7 @@ impl fmt::Debug for SimTraceState {
SimTraceState::Bits(v) => BitSliceWriteWithBase(v).fmt(f),
SimTraceState::SimOnly(v) => v.fmt(f),
SimTraceState::PhantomConst => f.debug_tuple("PhantomConst").finish(),
SimTraceState::OpaqueSimValue(v) => v.fmt(f),
}
}
}
@ -1026,6 +1068,13 @@ impl SimTraceKind {
}
SimTraceKind::PhantomConst { .. } => SimTraceState::PhantomConst,
SimTraceKind::SimOnly { index: _, ty } => SimTraceState::SimOnly(ty.default_value()),
SimTraceKind::TraceAsString { layout, range: _ } => {
let type_properties = layout.ty.type_properties();
SimTraceState::OpaqueSimValue(OpaqueSimValue::from_bits_and_sim_only_values(
UIntValue::new_dyn(Arc::new(BitVec::repeat(false, type_properties.bit_width))),
Vec::with_capacity(type_properties.sim_only_values_len),
))
}
}
}
}
@ -1180,6 +1229,31 @@ impl SimulationModuleState {
true
}
}
CompiledTypeLayoutBody::Transparent { .. } => {
let value = value.map_ty(|ty| match ty {
CanonicalType::UInt(_)
| CanonicalType::SInt(_)
| CanonicalType::Bool(_)
| CanonicalType::Array(_)
| CanonicalType::Enum(_)
| CanonicalType::Bundle(_)
| CanonicalType::AsyncReset(_)
| CanonicalType::SyncReset(_)
| CanonicalType::Reset(_)
| CanonicalType::Clock(_)
| CanonicalType::PhantomConst(_)
| CanonicalType::DynSimOnly(_) => unreachable!(),
CanonicalType::TraceAsString(ty) => ty,
});
let sub_target = target
.join(TargetPathElement::from(TargetPathTraceAsStringInner {}).intern_sized());
if self.parse_io(sub_target, value.inner()) {
self.uninitialized_ios.insert(target, vec![sub_target]);
true
} else {
false
}
}
}
}
fn mark_target_as_initialized(&mut self, mut target: Target) {
@ -1230,7 +1304,10 @@ impl SimulationModuleState {
Target::Base(_) => break,
Target::Child(child) => {
match *child.path_element() {
TargetPathElement::BundleField(_) | TargetPathElement::ArrayElement(_) => {}
TargetPathElement::BundleField(_)
| TargetPathElement::ArrayElement(_)
| TargetPathElement::TraceAsStringInner(_)
| TargetPathElement::AsTraceAsString(_) => {}
TargetPathElement::DynArrayElement(_) => panic!(
"simulator read/write expression must not have dynamic array indexes"
),
@ -1273,7 +1350,8 @@ impl SimulationModuleState {
| CanonicalType::Reset(_)
| CanonicalType::Clock(_)
| CanonicalType::PhantomConst(_)
| CanonicalType::DynSimOnly(_) => unreachable!(),
| CanonicalType::DynSimOnly(_)
| CanonicalType::TraceAsString(_) => unreachable!(),
CanonicalType::AsyncReset(_) => true,
CanonicalType::SyncReset(_) => false,
}
@ -1438,6 +1516,26 @@ impl SimulationExternModuleClockForPast {
);
}
}
CompiledTypeLayoutBody::Transparent { .. } => {
let map_ty_fn = |ty| match ty {
CanonicalType::UInt(_)
| CanonicalType::SInt(_)
| CanonicalType::Bool(_)
| CanonicalType::Array(_)
| CanonicalType::Enum(_)
| CanonicalType::Bundle(_)
| CanonicalType::AsyncReset(_)
| CanonicalType::SyncReset(_)
| CanonicalType::Reset(_)
| CanonicalType::Clock(_)
| CanonicalType::PhantomConst(_)
| CanonicalType::DynSimOnly(_) => unreachable!(),
CanonicalType::TraceAsString(ty) => ty,
};
let current = current.map_ty(map_ty_fn);
let past = past.map_ty(map_ty_fn);
self.add_current_to_past_mapping(current.inner(), past.inner());
}
}
}
}
@ -1906,6 +2004,7 @@ struct SimulationImpl {
),
>,
waiting_sensitivity_sets_by_address: HashMap<*const SensitivitySet, Rc<SensitivitySet>>,
trace_as_string_buf: String,
}
impl fmt::Debug for SimulationImpl {
@ -1995,6 +2094,7 @@ impl SimulationImpl {
next_sensitivity_set_debug_id: _,
waiting_sensitivity_sets_by_compiled_value,
waiting_sensitivity_sets_by_address,
trace_as_string_buf: _,
} = self;
f.debug_struct("Simulation")
.field("state", state)
@ -2101,6 +2201,7 @@ impl SimulationImpl {
next_sensitivity_set_debug_id: 0,
waiting_sensitivity_sets_by_compiled_value: HashMap::default(),
waiting_sensitivity_sets_by_address: HashMap::default(),
trace_as_string_buf: String::with_capacity(256),
}
}
fn write_traces<const ONLY_IF_CHANGED: bool>(
@ -2181,6 +2282,15 @@ impl SimulationImpl {
SimTraceKind::SimOnly { .. } => {
trace_writer.set_signal_sim_only_value(id, state.unwrap_sim_only_ref())?
}
SimTraceKind::TraceAsString { layout, .. } => {
self.trace_as_string_buf.clear();
layout.ty.trace_fmt_append_to_string(
&mut self.trace_as_string_buf,
state.unwrap_opaque_sim_value_ref().as_slice(),
);
trace_writer.set_signal_string(id, &self.trace_as_string_buf)?;
self.trace_as_string_buf.clear();
}
}
}
Ok(trace_writer)
@ -2233,6 +2343,9 @@ impl SimulationImpl {
.sim_only_slots
.state_index_fetch_maybe_modified_flag(index),
SimTraceKind::PhantomConst { ty: _ } => IS_INITIAL_STEP,
SimTraceKind::TraceAsString { layout: _, range } => self
.state
.type_index_range_fetch_maybe_modified_flags(range),
};
if !new_maybe_changed && !IS_INITIAL_STEP {
if *maybe_changed {
@ -2286,6 +2399,20 @@ impl SimulationImpl {
.unwrap_sim_only_mut()
.clone_from(&self.state.sim_only_slots[index]);
}
SimTraceKind::TraceAsString { layout, range } => {
let CompiledTypeLayoutBody::Transparent { inner } = layout.body else {
unreachable!()
};
Self::read_opaque_no_settle(
&mut self.state,
CompiledValue {
layout: *inner,
range,
write: None,
},
state.unwrap_opaque_sim_value_mut(),
);
}
}
if IS_INITIAL_STEP {
last_state.clone_from(state);
@ -2818,6 +2945,7 @@ impl SimulationImpl {
+ Copy,
read_write_sim_only_scalar: impl Fn(usize, &mut Opaque, &mut DynSimOnlyValue) + Copy,
) {
let compiled_value = compiled_value.unwrap_transparent_types();
match compiled_value.layout.body {
CompiledTypeLayoutBody::Scalar => {
let signed = match compiled_value.layout.ty {
@ -2833,6 +2961,7 @@ impl SimulationImpl {
CanonicalType::Clock(_) => false,
CanonicalType::PhantomConst(_) => unreachable!(),
CanonicalType::DynSimOnly(_) => false,
CanonicalType::TraceAsString(_) => unreachable!(),
};
let indexes = OpaqueSimValueSizeRange::from(
start_index..start_index + compiled_value.layout.ty.size(),
@ -2909,14 +3038,17 @@ impl SimulationImpl {
);
}
}
CompiledTypeLayoutBody::Transparent { .. } => {
unreachable!("handled by unwrap_transparent_types")
}
}
}
#[track_caller]
fn read_no_settle_helper(
fn read_opaque_no_settle(
state: &mut interpreter::State,
io: Expr<CanonicalType>,
compiled_value: CompiledValue<CanonicalType>,
) -> SimValue<CanonicalType> {
opaque: &mut OpaqueSimValue,
) {
#[track_caller]
fn read_write_sim_only_scalar(
index: usize,
@ -2937,8 +3069,7 @@ impl SimulationImpl {
},
);
}
let size = io.ty().size();
let mut opaque = OpaqueSimValue::with_capacity(size);
let size = compiled_value.layout.ty.size();
opaque.rewrite_with(size, |mut writer| {
SimulationImpl::read_write_sim_value_helper(
state,
@ -2970,6 +3101,16 @@ impl SimulationImpl {
);
writer.fill_cloned_from_slice(OpaqueSimValueSlice::empty())
});
}
#[track_caller]
fn read_no_settle_helper(
state: &mut interpreter::State,
io: Expr<CanonicalType>,
compiled_value: CompiledValue<CanonicalType>,
) -> SimValue<CanonicalType> {
let size = io.ty().size();
let mut opaque = OpaqueSimValue::with_capacity(size);
Self::read_opaque_no_settle(state, compiled_value, &mut opaque);
SimValue::from_opaque(io.ty(), opaque)
}
/// doesn't modify `opaque`

View file

@ -9,8 +9,8 @@ use crate::{
expr::{
ExprEnum, Flow, ValueType, ops,
target::{
GetTarget, Target, TargetBase, TargetPathArrayElement, TargetPathBundleField,
TargetPathElement,
GetTarget, Target, TargetBase, TargetPathArrayElement, TargetPathAsTraceAsString,
TargetPathBundleField, TargetPathElement, TargetPathTraceAsStringInner,
},
},
int::BoolOrIntType,
@ -29,7 +29,8 @@ use crate::{
TraceBool, TraceBundle, TraceClock, TraceDecl, TraceEnumDiscriminant, TraceEnumWithFields,
TraceFieldlessEnum, TraceInstance, TraceLocation, TraceMem, TraceMemPort, TraceMemoryId,
TraceMemoryLocation, TraceModule, TraceModuleIO, TracePhantomConst, TraceReg, TraceSInt,
TraceScalarId, TraceScope, TraceSimOnly, TraceSyncReset, TraceUInt, TraceWire,
TraceScalarId, TraceScope, TraceSimOnly, TraceSyncReset, TraceTraceAsString, TraceUInt,
TraceWire,
interpreter::{
self, Insn, InsnField, InsnFieldKind, InsnFieldType, InsnOrLabel, Insns, InsnsBuilding,
InsnsBuildingDone, InsnsBuildingKind, Label, PrefixLinesWrapper, SmallUInt,
@ -42,7 +43,7 @@ use crate::{
},
},
},
ty::{OpaqueSimValueSize, StaticType},
ty::{OpaqueSimValueSize, StaticType, TraceAsString},
util::{HashMap, chain},
};
use bitvec::vec::BitVec;
@ -110,6 +111,9 @@ pub(crate) enum CompiledTypeLayoutBody {
Bundle {
fields: Interned<[CompiledBundleField]>,
},
Transparent {
inner: Interned<CompiledTypeLayout<CanonicalType>>,
},
}
impl CompiledTypeLayoutBody {
@ -128,6 +132,9 @@ impl CompiledTypeLayoutBody {
.map(|field| field.with_prefixed_debug_names(prefix))
.collect(),
},
CompiledTypeLayoutBody::Transparent { inner } => CompiledTypeLayoutBody::Transparent {
inner: inner.with_prefixed_debug_names(prefix).intern_sized(),
},
}
}
fn with_anonymized_debug_info(self) -> Self {
@ -145,6 +152,9 @@ impl CompiledTypeLayoutBody {
.map(|field| field.with_anonymized_debug_info())
.collect(),
},
CompiledTypeLayoutBody::Transparent { inner } => CompiledTypeLayoutBody::Transparent {
inner: inner.with_anonymized_debug_info().intern_sized(),
},
}
}
}
@ -179,7 +189,7 @@ impl<T: Type> CompiledTypeLayout<T> {
impl Memoize for MyMemoize {
type Input = CanonicalType;
type InputOwned = CanonicalType;
type Output = CompiledTypeLayout<CanonicalType>;
type Output = (TypeLayout<InsnsBuildingDone>, CompiledTypeLayoutBody);
fn inner(self, input: &Self::Input) -> Self::Output {
match input {
@ -197,11 +207,7 @@ impl<T: Type> CompiledTypeLayout<T> {
ty: *input,
};
layout.big_slots = StatePartLayout::scalar(debug_data, ());
CompiledTypeLayout {
ty: *input,
layout: layout.into(),
body: CompiledTypeLayoutBody::Scalar,
}
(layout.into(), CompiledTypeLayoutBody::Scalar)
}
CanonicalType::Array(array) => {
let mut layout = TypeLayout::empty();
@ -215,19 +221,16 @@ impl<T: Type> CompiledTypeLayout<T> {
if array.is_empty() {
elements_non_empty.push(element.with_prefixed_debug_names("[<none>]"));
}
CompiledTypeLayout {
ty: *input,
layout: layout.into(),
body: CompiledTypeLayoutBody::Array {
(
layout.into(),
CompiledTypeLayoutBody::Array {
elements_non_empty: elements_non_empty.intern_deref(),
},
}
)
}
CanonicalType::PhantomConst(_) => {
(TypeLayout::empty(), CompiledTypeLayoutBody::PhantomConst)
}
CanonicalType::PhantomConst(_) => CompiledTypeLayout {
ty: *input,
layout: TypeLayout::empty(),
body: CompiledTypeLayoutBody::PhantomConst,
},
CanonicalType::Bundle(bundle) => {
let mut layout = TypeLayout::empty();
let fields = bundle
@ -246,11 +249,7 @@ impl<T: Type> CompiledTypeLayout<T> {
},
)
.collect();
CompiledTypeLayout {
ty: *input,
layout: layout.into(),
body: CompiledTypeLayoutBody::Bundle { fields },
}
(layout.into(), CompiledTypeLayoutBody::Bundle { fields })
}
CanonicalType::DynSimOnly(ty) => {
let mut layout = TypeLayout::empty();
@ -259,24 +258,30 @@ impl<T: Type> CompiledTypeLayout<T> {
ty: *input,
};
layout.sim_only_slots = StatePartLayout::scalar(debug_data, *ty);
CompiledTypeLayout {
ty: *input,
layout: layout.into(),
body: CompiledTypeLayoutBody::Scalar,
}
(layout.into(), CompiledTypeLayoutBody::Scalar)
}
CanonicalType::TraceAsString(ty) => {
let inner = CompiledTypeLayout::get(ty.inner_ty()).intern_sized();
(inner.layout, CompiledTypeLayoutBody::Transparent { inner })
}
}
}
}
let CompiledTypeLayout {
ty: _,
layout,
body,
} = MyMemoize.get_owned(ty.canonical());
let (layout, body) = MyMemoize.get_owned(ty.canonical());
Self { ty, layout, body }
}
}
impl CompiledTypeLayout<CanonicalType> {
#[must_use]
fn unwrap_transparent_types(mut self) -> Self {
while let CompiledTypeLayoutBody::Transparent { inner } = self.body {
self = *inner;
}
self
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub(crate) struct CompiledValue<T: Type> {
pub(crate) layout: CompiledTypeLayout<T>,
@ -324,6 +329,29 @@ impl<T: Type> CompiledValue<T> {
}
}
impl CompiledValue<CanonicalType> {
#[must_use]
pub(crate) fn unwrap_transparent_types(self) -> Self {
let Self {
layout,
range,
write,
} = self;
Self {
layout: layout.unwrap_transparent_types(),
range,
write: write.map(|(layout, range)| (layout.unwrap_transparent_types(), range)),
}
}
#[must_use]
pub(crate) fn wrap_in_trace_as_string(self, ty: TraceAsString) -> CompiledValue<TraceAsString> {
self.map(|layout, range| {
assert_eq!(layout.ty, ty.inner_ty());
(CompiledTypeLayout::get(ty), range)
})
}
}
pub(crate) struct DebugCompiledValueStateAsMap<'a> {
pub(crate) compiled_value: CompiledValue<CanonicalType>,
pub(crate) state_layout: &'a interpreter::parts::StateLayout<InsnsBuildingDone>,
@ -402,6 +430,17 @@ impl CompiledValue<Array> {
}
}
impl CompiledValue<TraceAsString> {
pub(crate) fn inner(self) -> CompiledValue<CanonicalType> {
self.map(|layout, range| {
let CompiledTypeLayoutBody::Transparent { inner } = layout.body else {
unreachable!();
};
(*inner, range)
})
}
}
macro_rules! make_type_array_indexes {
(
type_plural_fields = [$($type_plural_field:ident,)*];
@ -618,6 +657,16 @@ impl<T: Type> CompiledExpr<T> {
}
}
impl CompiledExpr<CanonicalType> {
#[must_use]
pub(crate) fn wrap_in_trace_as_string(self, ty: TraceAsString) -> CompiledExpr<TraceAsString> {
CompiledExpr {
static_part: self.static_part.wrap_in_trace_as_string(ty),
indexes: self.indexes,
}
}
}
impl CompiledExpr<Bundle> {
fn field_by_index(self, field_index: usize) -> CompiledExpr<CanonicalType> {
CompiledExpr {
@ -666,6 +715,15 @@ impl CompiledExpr<Array> {
}
}
impl CompiledExpr<TraceAsString> {
pub(crate) fn inner(self) -> CompiledExpr<CanonicalType> {
CompiledExpr {
static_part: self.static_part.inner(),
indexes: self.indexes,
}
}
}
macro_rules! make_assignment_graph {
(
type_plural_fields = [$($type_plural_field:ident,)*];
@ -1977,6 +2035,39 @@ macro_rules! impl_compiler {
flow,
}
.into(),
CanonicalType::TraceAsString(ty) => {
let location = match target {
MakeTraceDeclTarget::Expr(target) => {
let compiled_value = self.compile_expr(instantiated_module, target);
let CompiledValue { layout, range, write: _ } =
self.compiled_expr_to_value(compiled_value, source_location).map_ty(Type::from_canonical);
TraceLocation::Scalar(self.new_sim_trace(SimTraceKind::TraceAsString {
layout,
range,
}))
}
MakeTraceDeclTarget::Memory {
id,
depth,
stride,
start,
ty: _,
} => TraceLocation::Memory(TraceMemoryLocation {
id,
depth,
stride,
start,
len: ty.type_properties().bit_width,
}),
};
TraceTraceAsString {
location,
name,
ty,
flow,
}
.into()
}
}
}
fn compiled_expr_to_value(
@ -2421,7 +2512,8 @@ impl Compiler {
| CanonicalType::Reset(_)
| CanonicalType::Clock(_)
| CanonicalType::DynSimOnly(_)
| CanonicalType::PhantomConst(_) => {
| CanonicalType::PhantomConst(_)
| CanonicalType::TraceAsString(_) => {
self.make_trace_scalar(instantiated_module, target, name, source_location)
}
}
@ -2591,6 +2683,12 @@ impl Compiler {
parent.map_ty(Array::from_canonical).element(index)
}
TargetPathElement::DynArrayElement(_) => unreachable!(),
TargetPathElement::TraceAsStringInner(TargetPathTraceAsStringInner {}) => {
parent.map_ty(TraceAsString::from_canonical).inner()
}
TargetPathElement::AsTraceAsString(TargetPathAsTraceAsString { ty }) => parent
.wrap_in_trace_as_string(ty)
.map_ty(|ty| ty.canonical()),
}
}
};
@ -2823,6 +2921,12 @@ impl Compiler {
CanonicalType::PhantomConst(_) | CanonicalType::DynSimOnly(_) => {
self.compile_cast_aggregate_to_bits(instantiated_module, [])
}
CanonicalType::TraceAsString(_) => self.compile_cast_to_bits(
instantiated_module,
ops::CastToBits::new(
ops::TraceAsStringAsInner::new(Expr::from_canonical(expr.arg())).to_expr(),
),
),
}
}
fn compile_cast_bits_to_or_uninit(
@ -2912,6 +3016,16 @@ impl Compiler {
vec![]
});
}
CanonicalType::TraceAsString(ty) => Expr::canonical(
ops::AsTraceAsString::new(
match arg {
Some(arg) => arg.cast_bits_to(ty.inner_ty()),
None => ty.inner_ty().uninit(),
},
ty,
)
.to_expr(),
),
};
let retval = self.compile_expr(instantiated_module, Expr::canonical(retval));
self.compiled_expr_to_value(retval, instantiated_module.leaf_module().source_location())
@ -2963,6 +3077,7 @@ impl Compiler {
CanonicalType::Clock(_) => false,
CanonicalType::PhantomConst(_) => unreachable!(),
CanonicalType::DynSimOnly(_) => unreachable!(),
CanonicalType::TraceAsString(_) => unreachable!(),
};
let dest_signed = match expr.ty() {
CanonicalType::UInt(_) => false,
@ -2977,6 +3092,7 @@ impl Compiler {
CanonicalType::Clock(_) => false,
CanonicalType::PhantomConst(_) => unreachable!(),
CanonicalType::DynSimOnly(_) => unreachable!(),
CanonicalType::TraceAsString(_) => unreachable!(),
};
self.simple_nary_big_expr(instantiated_module, expr.ty(), [arg], |dest, [src]| match (
src_signed,
@ -3722,6 +3838,14 @@ impl Compiler {
ExprEnum::CastBitsTo(expr) => self
.compile_cast_bits_to_or_uninit(instantiated_module, Some(expr.arg()), expr.ty())
.into(),
ExprEnum::AsTraceAsString(expr) => self
.compile_expr(instantiated_module, expr.inner())
.wrap_in_trace_as_string(expr.ty())
.map_ty(|ty| ty.canonical()),
ExprEnum::TraceAsStringAsInner(expr) => self
.compile_expr(instantiated_module, Expr::canonical(expr.arg()))
.map_ty(TraceAsString::from_canonical)
.inner(),
ExprEnum::ModuleIO(expr) => self
.compile_value(TargetInInstantiatedModule {
instantiated_module,
@ -3869,6 +3993,21 @@ impl Compiler {
CanonicalType::DynSimOnly(_) => {
unreachable!("DynSimOnly mismatch");
}
CanonicalType::TraceAsString(_) => {
let lhs = Expr::<TraceAsString>::from_canonical(lhs);
let rhs = Expr::<TraceAsString>::from_canonical(rhs);
let lhs_expr = ops::TraceAsStringAsInner::new(lhs).to_expr();
let rhs_expr = ops::TraceAsStringAsInner::new(rhs).to_expr();
return self.compile_connect(
lhs_instantiated_module,
lhs_conditions,
lhs_expr,
rhs_instantiated_module,
rhs_conditions,
rhs_expr,
source_location,
);
}
}
}
let Some(target) = lhs.target() else {
@ -4244,6 +4383,8 @@ impl Compiler {
mut read: Option<MemoryPortReadInsns<'_>>,
mut write: Option<MemoryPortWriteInsns<'_>>,
) {
let data_layout = data_layout.unwrap_transparent_types();
let mask_layout = mask_layout.unwrap_transparent_types();
match data_layout.body {
CompiledTypeLayoutBody::Scalar => {
let CompiledTypeLayoutBody::Scalar = mask_layout.body else {
@ -4262,6 +4403,7 @@ impl Compiler {
CanonicalType::Clock(_) => false,
CanonicalType::PhantomConst(_) => unreachable!(),
CanonicalType::DynSimOnly(_) => false,
CanonicalType::TraceAsString(_) => unreachable!(),
};
let width = data_layout.ty.bit_width();
if let Some(MemoryPortReadInsns {
@ -4484,6 +4626,9 @@ impl Compiler {
start = start + field.ty.ty.bit_width();
}
}
CompiledTypeLayoutBody::Transparent { .. } => {
unreachable!("handled by unwrap_transparent_types")
}
}
}
fn compile_memory_port_rw(

View file

@ -6,9 +6,9 @@ use crate::{
int::{BoolOrIntType, SInt, UInt},
intern::{Intern, Interned, Memoize},
sim::interpreter::parts::{
StateLayout, StatePartIndex, StatePartKind, StatePartKindBigSlots, StatePartKindMemories,
StatePartKindSimOnlySlots, StatePartKindSmallSlots, StatePartLen, TypeIndexRange,
TypeLayout, get_state_part_kinds,
StateLayout, StatePartIndex, StatePartIndexRange, StatePartKind, StatePartKindBigSlots,
StatePartKindMemories, StatePartKindSimOnlySlots, StatePartKindSmallSlots, StatePartLen,
TypeIndexRange, TypeLayout, get_state_part_kinds,
},
source_location::SourceLocation,
util::{HashMap, HashSet},
@ -920,6 +920,12 @@ impl<K: StatePartKind> StatePart<K> {
) -> bool {
K::state_index_fetch_maybe_modified_flag(&self.value, part_index)
}
pub(crate) fn state_index_range_fetch_maybe_modified_flags(
&self,
part_index_range: StatePartIndexRange<K>,
) -> bool {
K::state_index_range_fetch_maybe_modified_flags(&self.value, part_index_range)
}
pub(crate) fn clear_all_maybe_modified_flags(&mut self) {
K::clear_all_maybe_modified_flags(&mut self.value)
}
@ -1018,6 +1024,11 @@ macro_rules! make_state {
$($type_plural_field: self.$type_plural_field.borrow(),)*
}
}
pub(crate) fn type_index_range_fetch_maybe_modified_flags(&self, range: TypeIndexRange) -> bool {
$(self.$type_plural_field.state_index_range_fetch_maybe_modified_flags(
range.$type_plural_field,
))||*
}
pub(crate) fn clear_all_maybe_modified_flags(&mut self) {
$(self.$state_plural_field.clear_all_maybe_modified_flags();)*
$(self.$type_plural_field.clear_all_maybe_modified_flags();)*

View file

@ -260,6 +260,10 @@ pub(crate) trait StatePartKind:
state: &Self::State,
part_index: StatePartIndex<Self>,
) -> bool;
fn state_index_range_fetch_maybe_modified_flags(
state: &Self::State,
part_index_range: StatePartIndexRange<Self>,
) -> bool;
fn clear_all_maybe_modified_flags(state: &mut Self::State);
fn borrowed_state_index<'a, 'b>(
state: &'a Self::BorrowedState<'b>,
@ -342,6 +346,12 @@ impl StatePartKind for StatePartKindMemories {
) -> bool {
true
}
fn state_index_range_fetch_maybe_modified_flags(
_state: &Self::State,
part_index_range: StatePartIndexRange<Self>,
) -> bool {
part_index_range.len.value > 0
}
fn clear_all_maybe_modified_flags(_state: &mut Self::State) {}
fn borrowed_state_index<'a, 'b>(
state: &'a Self::BorrowedState<'b>,
@ -446,6 +456,14 @@ impl StatePartKind for StatePartKindSmallSlots {
) -> bool {
state.modified[part_index.as_usize()]
}
fn state_index_range_fetch_maybe_modified_flags(
state: &Self::State,
part_index_range: StatePartIndexRange<Self>,
) -> bool {
state.modified[part_index_range.start.as_usize()..]
[..part_index_range.len.as_index().as_usize()]
.contains(&true)
}
fn clear_all_maybe_modified_flags(state: &mut Self::State) {
state.modified.fill(false);
}
@ -530,6 +548,14 @@ impl StatePartKind for StatePartKindBigSlots {
) -> bool {
state.modified[part_index.as_usize()]
}
fn state_index_range_fetch_maybe_modified_flags(
state: &Self::State,
part_index_range: StatePartIndexRange<Self>,
) -> bool {
state.modified[part_index_range.start.as_usize()..]
[..part_index_range.len.as_index().as_usize()]
.contains(&true)
}
fn clear_all_maybe_modified_flags(state: &mut Self::State) {
state.modified.fill(false);
}
@ -614,6 +640,14 @@ impl StatePartKind for StatePartKindSimOnlySlots {
) -> bool {
state.modified[part_index.as_usize()]
}
fn state_index_range_fetch_maybe_modified_flags(
state: &Self::State,
part_index_range: StatePartIndexRange<Self>,
) -> bool {
state.modified[part_index_range.start.as_usize()..]
[..part_index_range.len.as_index().as_usize()]
.contains(&true)
}
fn clear_all_maybe_modified_flags(state: &mut Self::State) {
state.modified.fill(false);
}

View file

@ -1098,7 +1098,8 @@ impl ToSimValueWithType<CanonicalType> for bool {
| CanonicalType::Enum(_)
| CanonicalType::Bundle(_)
| CanonicalType::PhantomConst(_)
| CanonicalType::DynSimOnly(_) => {
| CanonicalType::DynSimOnly(_)
| CanonicalType::TraceAsString(_) => {
panic!("can't create SimValue from bool: expected value of type: {ty:?}");
}
CanonicalType::Bool(_)

View file

@ -12,11 +12,12 @@ use crate::{
TraceEnumDiscriminant, TraceEnumWithFields, TraceFieldlessEnum, TraceInstance,
TraceLocation, TraceMem, TraceMemPort, TraceMemoryId, TraceMemoryLocation, TraceModule,
TraceModuleIO, TracePhantomConst, TraceReg, TraceSInt, TraceScalar, TraceScalarId,
TraceScope, TraceSimOnly, TraceSyncReset, TraceUInt, TraceWire, TraceWriter,
TraceWriterDecls,
TraceScope, TraceSimOnly, TraceSyncReset, TraceTraceAsString, TraceUInt, TraceWire,
TraceWriter, TraceWriterDecls,
time::{SimDuration, SimInstant},
value::DynSimOnlyValue,
},
ty::{OpaqueSimValueSlice, TraceAsString},
util::HashMap,
};
use bitvec::{order::Lsb0, slice::BitSlice};
@ -331,6 +332,7 @@ impl WriteTrace for TraceScalar {
Self::AsyncReset(v) => v.write_trace(writer, arg),
Self::PhantomConst(v) => v.write_trace(writer, arg),
Self::SimOnly(v) => v.write_trace(writer, arg),
Self::TraceAsString(v) => v.write_trace(writer, arg),
}
}
}
@ -726,6 +728,34 @@ impl WriteTrace for TraceSimOnly {
}
}
impl WriteTrace for TraceTraceAsString {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> {
let ArgInType {
source_var_type: _,
sink_var_type: _,
duplex_var_type: _,
properties,
scope,
} = arg.in_type();
let Self {
location,
name,
ty,
flow: _,
} = self;
write_vcd_var(
properties,
scope,
MemoryElementPartBody::TraceAsString { ty },
writer,
"string",
1,
location,
name,
)
}
}
impl WriteTrace for TraceScope {
fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, arg: A) -> io::Result<()> {
match self {
@ -1093,6 +1123,7 @@ impl<W: io::Write> TraceWriterDecls for VcdWriterDecls<W> {
finished_init: false,
timescale,
properties,
trace_as_string_buf: String::with_capacity(256),
})
}
}
@ -1100,6 +1131,7 @@ impl<W: io::Write> TraceWriterDecls for VcdWriterDecls<W> {
enum MemoryElementPartBody {
Scalar,
EnumDiscriminant { ty: Enum },
TraceAsString { ty: TraceAsString },
}
struct MemoryElementPart {
@ -1217,6 +1249,7 @@ pub struct VcdWriter<W: io::Write + 'static> {
finished_init: bool,
timescale: SimDuration,
properties: VcdWriterProperties,
trace_as_string_buf: String,
}
impl<W: io::Write + 'static> VcdWriter<W> {
@ -1326,6 +1359,21 @@ impl<W: io::Write> TraceWriter for VcdWriter<W> {
.built_scalar_id_to_vcd_id(first_id + element_index),
)?
}
MemoryElementPartBody::TraceAsString { ty } => {
self.trace_as_string_buf.clear();
ty.trace_fmt_append_to_string(
&mut self.trace_as_string_buf,
OpaqueSimValueSlice::from_bitslice(&element_data[start..start + len]),
);
write_string_value_change(
&mut self.writer,
&self.trace_as_string_buf,
self.properties
.scalar_id_to_vcd_id_map
.built_scalar_id_to_vcd_id(first_id + element_index),
)?;
self.trace_as_string_buf.clear();
}
}
}
Ok(())
@ -1419,6 +1467,16 @@ impl<W: io::Write> TraceWriter for VcdWriter<W> {
.built_scalar_id_to_vcd_id(id.as_usize()),
)
}
fn set_signal_string(&mut self, id: TraceScalarId, value: &str) -> Result<(), Self::Error> {
write_string_value_change(
&mut self.writer,
value,
self.properties
.scalar_id_to_vcd_id_map
.built_scalar_id_to_vcd_id(id.as_usize()),
)
}
}
impl<W: io::Write> fmt::Debug for VcdWriter<W> {
@ -1428,6 +1486,7 @@ impl<W: io::Write> fmt::Debug for VcdWriter<W> {
finished_init,
timescale,
properties: _,
trace_as_string_buf: _,
} = self;
f.debug_struct("VcdWriter")
.field("finished_init", finished_init)

View file

@ -3,22 +3,27 @@
use crate::{
array::Array,
bundle::Bundle,
bundle::{Bundle, BundleField, BundleType},
clock::Clock,
enum_::Enum,
expr::Expr,
enum_::{Enum, EnumType, EnumVariant},
expr::{Expr, ToExpr, ValueType, ops},
int::{Bool, SInt, UInt, UIntValue},
intern::{Intern, Interned},
intern::{Intern, Interned, LazyInterned, Memoize, SupportsPtrEqWithTypeId},
module::transform::visit::{Fold, Folder, Visit, Visitor},
phantom_const::PhantomConst,
reset::{AsyncReset, Reset, SyncReset},
sim::value::{DynSimOnly, DynSimOnlyValue, SimValue, ToSimValueWithType},
sim::value::{DynSimOnly, DynSimOnlyValue, SimValue, ToSimValue, ToSimValueWithType},
source_location::SourceLocation,
util::{ConstUsize, slice_range, try_slice_range},
util::{
ConstUsize, iter_eq_by,
serde_by_id::{SerdeByIdProperties, SerdeByIdTable, SerdeByIdTrait},
slice_range, try_slice_range,
},
};
use bitvec::{slice::BitSlice, vec::BitVec};
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::DeserializeOwned};
use std::{
fmt,
fmt::{self, Write},
hash::Hash,
iter::{FusedIterator, Sum},
marker::PhantomData,
@ -69,6 +74,7 @@ pub enum CanonicalType {
Clock(Clock),
PhantomConst(PhantomConst),
DynSimOnly(DynSimOnly),
TraceAsString(TraceAsString),
}
impl fmt::Debug for CanonicalType {
@ -86,6 +92,7 @@ impl fmt::Debug for CanonicalType {
Self::Clock(v) => v.fmt(f),
Self::PhantomConst(v) => v.fmt(f),
Self::DynSimOnly(v) => v.fmt(f),
Self::TraceAsString(v) => v.fmt(f),
}
}
}
@ -123,6 +130,7 @@ impl CanonicalType {
CanonicalType::Clock(v) => v.type_properties(),
CanonicalType::PhantomConst(v) => v.type_properties(),
CanonicalType::DynSimOnly(v) => v.type_properties(),
CanonicalType::TraceAsString(v) => v.type_properties(),
}
}
pub fn is_passive(self) -> bool {
@ -217,11 +225,134 @@ impl CanonicalType {
};
lhs.can_connect(rhs)
}
CanonicalType::TraceAsString(lhs) => {
let CanonicalType::TraceAsString(rhs) = rhs else {
return false;
};
lhs.can_connect(rhs)
}
}
}
pub(crate) fn as_serde_unexpected_str(self) -> &'static str {
serde_impls::SerdeCanonicalType::from(self).as_serde_unexpected_str()
}
/// Unwrap transparent types until reaching a non-transparent type. Currently [`TraceAsString`] is the only transparent type.
///
/// [`TraceAsString`]: struct@TraceAsString
pub fn unwrap_transparent_types(mut self) -> Self {
loop {
self = match self {
Self::UInt(_)
| Self::SInt(_)
| Self::Bool(_)
| Self::Array(_)
| Self::Enum(_)
| Self::Bundle(_)
| Self::AsyncReset(_)
| Self::SyncReset(_)
| Self::Reset(_)
| Self::Clock(_)
| Self::PhantomConst(_)
| Self::DynSimOnly(_) => return self,
Self::TraceAsString(ty) => ty.inner_ty(),
};
}
}
pub fn is_layout_equivalent(self, other: Self) -> bool {
fn is_bit(ty: CanonicalType) -> bool {
match ty {
CanonicalType::UInt(ty) => ty.width() == 1,
CanonicalType::SInt(_) => false, // SInt<1> doesn't count since it would be -1/0 instead of 1/0
CanonicalType::Bool(_)
| CanonicalType::AsyncReset(_)
| CanonicalType::SyncReset(_)
| CanonicalType::Reset(_)
| CanonicalType::Clock(_) => true,
CanonicalType::Array(_)
| CanonicalType::Enum(_)
| CanonicalType::Bundle(_)
| CanonicalType::PhantomConst(_)
| CanonicalType::DynSimOnly(_) => false,
CanonicalType::TraceAsString(_) => {
unreachable!("handled by unwrap_transparent_types")
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
struct MyMemoize;
impl Memoize for MyMemoize {
type Input = (CanonicalType, CanonicalType);
type InputOwned = (CanonicalType, CanonicalType);
type Output = bool;
fn inner(self, input: &Self::Input) -> Self::Output {
let (this, other) = *input;
let this = this.unwrap_transparent_types();
let other = other.unwrap_transparent_types();
let this_is_bit = is_bit(this);
let other_is_bit = is_bit(other);
if this_is_bit || other_is_bit {
return this_is_bit && other_is_bit;
}
let this_is_empty = this.size().is_empty();
let other_is_empty = other.size().is_empty();
if this_is_empty || other_is_empty {
return this_is_empty && other_is_empty;
}
match this {
CanonicalType::UInt(_)
| CanonicalType::SInt(_)
| CanonicalType::DynSimOnly(_) => this == other,
CanonicalType::Array(this) => {
let CanonicalType::Array(other) = other else {
return false;
};
this.len() == other.len()
&& this.element().is_layout_equivalent(other.element())
}
CanonicalType::Enum(this) => {
let CanonicalType::Enum(other) = other else {
return false;
};
iter_eq_by(
this.variants(),
other.variants(),
|EnumVariant { name, ty }, other_variant| {
name == other_variant.name
&& ty.unwrap_or_else(|| ().canonical()).is_layout_equivalent(
other_variant.ty.unwrap_or_else(|| ().canonical()),
)
},
)
}
CanonicalType::Bundle(this) => {
let CanonicalType::Bundle(other) = other else {
return false;
};
iter_eq_by(
this.fields().iter().filter(|f| !f.ty.size().is_empty()),
other.fields().iter().filter(|f| !f.ty.size().is_empty()),
|&BundleField { name, flipped, ty }, other_field| {
name == other_field.name
&& flipped == other_field.flipped
&& ty.is_layout_equivalent(other_field.ty)
},
)
}
CanonicalType::Bool(_)
| CanonicalType::AsyncReset(_)
| CanonicalType::SyncReset(_)
| CanonicalType::Reset(_)
| CanonicalType::Clock(_) => unreachable!("handled by is_bit"),
CanonicalType::PhantomConst(_) => unreachable!("handled by is_empty"),
CanonicalType::TraceAsString(_) => {
unreachable!("handled by unwrap_transparent_types")
}
}
}
}
MyMemoize.get_owned((self, other))
}
}
pub trait MatchVariantAndInactiveScope: Sized {
@ -328,6 +459,7 @@ impl_base_type!(Reset);
impl_base_type!(Clock);
impl_base_type!(PhantomConst);
impl_base_type!(DynSimOnly);
impl_base_type!(TraceAsString);
impl_base_type_serde!(Bool, "a Bool");
impl_base_type_serde!(Enum, "an Enum");
@ -336,6 +468,7 @@ impl_base_type_serde!(AsyncReset, "an AsyncReset");
impl_base_type_serde!(SyncReset, "a SyncReset");
impl_base_type_serde!(Reset, "a Reset");
impl_base_type_serde!(Clock, "a Clock");
impl_base_type_serde!(TraceAsString, "a TraceAsString");
impl sealed::BaseTypeSealed for CanonicalType {}
@ -473,6 +606,7 @@ impl Type for CanonicalType {
CanonicalType::Clock(v) => v.mask_type().canonical(),
CanonicalType::PhantomConst(v) => v.mask_type().canonical(),
CanonicalType::DynSimOnly(v) => v.mask_type().canonical(),
CanonicalType::TraceAsString(v) => v.mask_type().canonical(),
}
}
fn canonical(&self) -> CanonicalType {
@ -757,13 +891,34 @@ impl Sum for OpaqueSimValueSize {
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
#[derive(PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
pub struct OpaqueSimValue {
bits: UIntValue,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
sim_only_values: Vec<DynSimOnlyValue>,
}
impl Clone for OpaqueSimValue {
fn clone(&self) -> Self {
Self {
bits: self.bits.clone(),
sim_only_values: self.sim_only_values.clone(),
}
}
fn clone_from(&mut self, source: &Self) {
let Self {
bits,
sim_only_values,
} = self;
if let Some(bits) = Arc::get_mut(bits.arc_bitvec_mut()) {
bits.clone_from(source.bits.bits());
} else {
*bits = source.bits.clone();
}
sim_only_values.clone_from(&source.sim_only_values);
}
}
impl OpaqueSimValue {
pub fn empty() -> Self {
Self {
@ -842,6 +997,9 @@ impl OpaqueSimValue {
pub fn sim_only_values(&self) -> &[DynSimOnlyValue] {
&self.sim_only_values
}
pub(crate) fn sim_only_values_vec(&self) -> &Vec<DynSimOnlyValue> {
&self.sim_only_values
}
pub fn sim_only_values_mut(&mut self) -> &mut Vec<DynSimOnlyValue> {
&mut self.sim_only_values
}
@ -1143,3 +1301,413 @@ impl<T: Type> Index<T> for AsMaskWithoutGenerics {
Interned::into_inner(Intern::intern_sized(ty.mask_type()))
}
}
trait TraceAsStringTrait: fmt::Debug + 'static + Send + Sync + SupportsPtrEqWithTypeId {
fn trace_fmt(&self, opaque: OpaqueSimValueSlice<'_>, f: &mut fmt::Formatter<'_>)
-> fmt::Result;
fn serde_by_id_properties(&self) -> SerdeByIdProperties<Interned<dyn TraceAsStringTrait>> {
SerdeByIdProperties::of::<Self>()
}
fn can_substitute_type(&self, new_type: CanonicalType) -> bool;
}
impl<T: Type> TraceAsStringTrait for T {
fn trace_fmt(
&self,
opaque: OpaqueSimValueSlice<'_>,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
fmt::Debug::fmt(&Type::sim_value_from_opaque(self, opaque), f)
}
fn can_substitute_type(&self, new_type: CanonicalType) -> bool {
self.canonical().is_layout_equivalent(new_type)
}
}
impl crate::intern::InternedCompare for dyn TraceAsStringTrait {
type InternedCompareKey = crate::intern::PtrEqWithTypeId;
fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey {
this.get_ptr_eq_with_type_id()
}
}
impl SerdeByIdTrait for Interned<dyn TraceAsStringTrait> {
fn serde_by_id_properties(&self) -> SerdeByIdProperties<Self> {
TraceAsStringTrait::serde_by_id_properties(&**self)
}
fn static_table() -> &'static SerdeByIdTable<Self> {
static TABLE: SerdeByIdTable<Interned<dyn TraceAsStringTrait>> = SerdeByIdTable::new();
&TABLE
}
const NAME: &'static str = "dyn TraceAsStringTrait";
}
/// When running the fayalite simulator, outputs a single string signal containing a formatted version of the inner value (uses [`fmt::Debug`] by default).
/// This is a transparent type, meaning [`CanonicalType::unwrap_transparent_types`] will unwrap this type.
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct TraceAsString<T: Type = CanonicalType> {
inner_ty: LazyInterned<T>,
trace_as_string: LazyInterned<dyn TraceAsStringTrait>,
}
#[expect(non_upper_case_globals)]
pub const TraceAsString: TraceAsStringWithoutGenerics = TraceAsStringWithoutGenerics;
impl<T: Type> fmt::Debug for TraceAsString<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
inner_ty,
trace_as_string: _,
} = self;
f.debug_struct("TraceAsString")
.field("inner_ty", &inner_ty.interned())
.finish_non_exhaustive()
}
}
impl<T: Type> TraceAsString<T> {
pub fn new(inner_ty: T) -> Self {
Self::new_interned(inner_ty.intern_sized())
}
pub fn new_interned(inner_ty: Interned<T>) -> Self {
Self {
inner_ty: LazyInterned::Interned(inner_ty),
trace_as_string: LazyInterned::Interned(Interned::cast_unchecked(
inner_ty,
|v| -> &dyn TraceAsStringTrait { v },
)),
}
}
pub fn interned_inner_ty(self) -> Interned<T> {
self.inner_ty.interned()
}
pub fn inner_ty(self) -> T {
*self.interned_inner_ty()
}
/// create a new `TraceAsString` but try to keep the old formatting method
pub fn with_new_inner_ty<U: Type>(self, inner_ty: Interned<U>) -> TraceAsString<U> {
if self
.trace_as_string
.can_substitute_type(inner_ty.canonical())
{
TraceAsString {
inner_ty: LazyInterned::Interned(inner_ty),
trace_as_string: self.trace_as_string,
}
} else {
TraceAsString::new_interned(inner_ty)
}
}
pub fn canonical_trace_as_string(self) -> TraceAsString<CanonicalType> {
let Self {
inner_ty,
trace_as_string,
} = self;
TraceAsString {
inner_ty: LazyInterned::Interned(inner_ty.interned().canonical().intern_sized()),
trace_as_string,
}
}
pub fn from_canonical_trace_as_string(canonical: TraceAsString<CanonicalType>) -> Self {
let TraceAsString {
inner_ty,
trace_as_string,
} = canonical;
Self {
inner_ty: LazyInterned::Interned(
T::from_canonical(*inner_ty.interned()).intern_sized(),
),
trace_as_string,
}
}
pub fn type_properties(self) -> TypeProperties {
self.interned_inner_ty().canonical().type_properties()
}
pub fn can_connect<T2: Type>(self, rhs: TraceAsString<T2>) -> bool {
self.interned_inner_ty()
.canonical()
.can_connect(rhs.interned_inner_ty().canonical())
}
pub fn trace_fmt(
self,
opaque: OpaqueSimValueSlice<'_>,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
self.trace_as_string.interned().trace_fmt(opaque, f)
}
pub fn trace_fmt_append_to_string(self, output: &mut String, opaque: OpaqueSimValueSlice<'_>) {
fn impl_fn(
trace_as_string: Interned<dyn TraceAsStringTrait>,
output: &mut String,
opaque: OpaqueSimValueSlice<'_>,
) {
let initial_len = output.len();
if let Err(fmt::Error {}) = write!(
output,
"{}",
fmt::from_fn(|f| trace_as_string.trace_fmt(opaque, f))
) {
output.truncate(initial_len);
output.push_str("<!!!failed to format!!!>");
}
}
impl_fn(self.trace_as_string.interned(), output, opaque)
}
}
impl<T: Type> SimValueDebug for TraceAsString<T> {
fn sim_value_debug(
value: &<Self as Type>::SimValue,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
T::sim_value_debug(value, f)
}
}
impl<T: Type> Type for TraceAsString<T> {
type BaseType = TraceAsString<CanonicalType>;
type MaskType = T::MaskType;
type SimValue = TraceAsStringSimValue<T::SimValue>;
type MatchVariant = Expr<T>;
type MatchActiveScope = ();
type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Self::MatchVariant>;
type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>;
fn match_variants(
this: Expr<Self>,
source_location: SourceLocation,
) -> Self::MatchVariantsIter {
let _ = source_location;
std::iter::once(MatchVariantWithoutScope(
ops::TraceAsStringAsInner::new(this).to_expr(),
))
}
fn mask_type(&self) -> Self::MaskType {
self.inner_ty.mask_type()
}
fn canonical(&self) -> CanonicalType {
CanonicalType::TraceAsString(self.canonical_trace_as_string())
}
fn from_canonical(canonical_type: CanonicalType) -> Self {
let CanonicalType::TraceAsString(canonical) = canonical_type else {
panic!("expected TraceAsString");
};
Self::from_canonical_trace_as_string(canonical)
}
fn source_location() -> SourceLocation {
SourceLocation::builtin()
}
fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue {
TraceAsStringSimValue {
inner: self.inner_ty.sim_value_from_opaque(opaque),
}
}
fn sim_value_clone_from_opaque(
&self,
value: &mut Self::SimValue,
opaque: OpaqueSimValueSlice<'_>,
) {
self.inner_ty
.sim_value_clone_from_opaque(&mut value.inner, opaque);
}
fn sim_value_to_opaque<'w>(
&self,
value: &Self::SimValue,
writer: OpaqueSimValueWriter<'w>,
) -> OpaqueSimValueWritten<'w> {
self.inner_ty.sim_value_to_opaque(&value.inner, writer)
}
}
impl<T: Type> TypeWithDeref for TraceAsString<T> {
fn expr_deref(this: &Expr<Self>) -> &Self::MatchVariant {
Interned::into_inner(
ops::TraceAsStringAsInner::new(*this)
.to_expr()
.intern_sized(),
)
}
}
struct TraceAsStringStaticTypeHelper<T: StaticType>(PhantomData<T>);
impl<T: StaticType> Default for TraceAsStringStaticTypeHelper<T> {
fn default() -> Self {
Self(PhantomData)
}
}
impl<T: StaticType> From<TraceAsStringStaticTypeHelper<T>> for Interned<dyn TraceAsStringTrait> {
fn from(_value: TraceAsStringStaticTypeHelper<T>) -> Self {
Interned::cast_unchecked(T::TYPE.intern_sized(), |v| -> &dyn TraceAsStringTrait { v })
}
}
impl<T: StaticType> Default for TraceAsString<T> {
fn default() -> Self {
Self::TYPE
}
}
struct MakeType<T: StaticType>(Interned<T>);
impl<T: StaticType> From<MakeType<T>> for Interned<T> {
fn from(value: MakeType<T>) -> Self {
value.0
}
}
impl<T: StaticType> Default for MakeType<T> {
fn default() -> Self {
Self(T::TYPE.intern_sized())
}
}
impl<T: StaticType> StaticType for TraceAsString<T> {
const TYPE: Self = Self {
inner_ty: LazyInterned::new_const::<MakeType<T>>(),
trace_as_string: LazyInterned::new_const::<TraceAsStringStaticTypeHelper<T>>(),
};
const MASK_TYPE: Self::MaskType = T::MASK_TYPE;
const TYPE_PROPERTIES: TypeProperties = T::TYPE_PROPERTIES;
const MASK_TYPE_PROPERTIES: TypeProperties = T::MASK_TYPE_PROPERTIES;
}
#[doc(hidden)]
pub struct TraceAsStringWithoutGenerics;
impl<T: Type> Index<T> for TraceAsStringWithoutGenerics {
type Output = TraceAsString<T>;
fn index(&self, inner_ty: T) -> &Self::Output {
Interned::into_inner(TraceAsString::new(inner_ty).intern_sized())
}
}
#[derive(Copy, Clone, Eq, Ord, Hash, Default, Serialize, Deserialize)]
pub struct TraceAsStringSimValue<T> {
inner: T,
}
impl<T> TraceAsStringSimValue<T> {
pub const fn new(inner: T) -> Self {
Self { inner }
}
pub fn into_inner(this: Self) -> T {
let Self { inner } = this;
inner
}
}
impl<T: ValueType> ValueType for TraceAsStringSimValue<T> {
type Type = TraceAsString<T::Type>;
type ValueCategory = T::ValueCategory;
fn ty(&self) -> Self::Type {
TraceAsString::new(self.inner.ty())
}
}
impl<T: ToExpr> ToExpr for TraceAsStringSimValue<T> {
fn to_expr(&self) -> Expr<Self::Type> {
let inner = self.inner.to_expr();
ops::AsTraceAsString::new(Expr::canonical(inner), TraceAsString::new(inner.ty())).to_expr()
}
}
impl<T: ToSimValueWithType<Ty>, Ty: Type> ToSimValueWithType<TraceAsString<Ty>>
for TraceAsStringSimValue<T>
{
fn to_sim_value_with_type(&self, ty: TraceAsString<Ty>) -> SimValue<TraceAsString<Ty>> {
let inner = self.inner.to_sim_value_with_type(ty.inner_ty());
let inner = SimValue::into_value(inner);
SimValue::from_value(ty, TraceAsStringSimValue { inner })
}
fn into_sim_value_with_type(self, ty: TraceAsString<Ty>) -> SimValue<TraceAsString<Ty>> {
let inner = self.inner.into_sim_value_with_type(ty.inner_ty());
let inner = SimValue::into_value(inner);
SimValue::from_value(ty, TraceAsStringSimValue { inner })
}
}
impl<T: ToSimValue> ToSimValue for TraceAsStringSimValue<T> {
fn to_sim_value(&self) -> SimValue<Self::Type> {
self.to_sim_value_with_type(self.ty())
}
fn into_sim_value(self) -> SimValue<Self::Type> {
let ty = self.ty();
self.into_sim_value_with_type(ty)
}
}
impl<T> std::ops::Deref for TraceAsStringSimValue<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T> std::ops::DerefMut for TraceAsStringSimValue<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl<T: fmt::Debug> fmt::Debug for TraceAsStringSimValue<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { inner } = self;
fmt::Debug::fmt(inner, f)
}
}
impl<T: fmt::Display> fmt::Display for TraceAsStringSimValue<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { inner } = self;
fmt::Display::fmt(inner, f)
}
}
impl<T: PartialOrd<U>, U> PartialOrd<TraceAsStringSimValue<U>> for TraceAsStringSimValue<T> {
fn partial_cmp(&self, other: &TraceAsStringSimValue<U>) -> Option<std::cmp::Ordering> {
let Self { inner } = self;
inner.partial_cmp(&other.inner)
}
}
impl<T: PartialEq<U>, U> PartialEq<TraceAsStringSimValue<U>> for TraceAsStringSimValue<T> {
fn eq(&self, other: &TraceAsStringSimValue<U>) -> bool {
let Self { inner } = self;
*inner == other.inner
}
}
impl<T: Type + Fold<State>, State: ?Sized + Folder> Fold<State> for TraceAsString<T> {
fn fold(self, state: &mut State) -> Result<Self, State::Error> {
state.fold_trace_as_string(self)
}
fn default_fold(self, state: &mut State) -> Result<Self, State::Error> {
Ok(self.with_new_inner_ty(self.interned_inner_ty().fold(state)?))
}
}
impl<T: Type + Visit<State>, State: ?Sized + Visitor> Visit<State> for TraceAsString<T> {
fn visit(&self, state: &mut State) -> Result<(), <State as Visitor>::Error> {
state.visit_trace_as_string(self)
}
fn default_visit(&self, state: &mut State) -> Result<(), <State as Visitor>::Error> {
self.interned_inner_ty().visit(state)
}
}

View file

@ -12,7 +12,8 @@ use crate::{
prelude::PhantomConst,
reset::{AsyncReset, Reset, SyncReset},
sim::value::DynSimOnly,
ty::{BaseType, CanonicalType},
ty::{BaseType, CanonicalType, TraceAsString, TraceAsStringTrait},
util::serde_by_id::SerdeById,
};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
@ -65,6 +66,10 @@ pub(crate) enum SerdeCanonicalType<
Clock,
PhantomConst(ThePhantomConst),
DynSimOnly(DynSimOnly),
TraceAsString {
inner_ty: Interned<CanonicalType>,
trace_as_string: SerdeById<Interned<dyn TraceAsStringTrait>>,
},
}
impl<ArrayElement, PhantomConstInner> SerdeCanonicalType<ArrayElement, PhantomConstInner> {
@ -82,6 +87,7 @@ impl<ArrayElement, PhantomConstInner> SerdeCanonicalType<ArrayElement, PhantomCo
Self::Clock => "a Clock",
Self::PhantomConst(_) => "a PhantomConst",
Self::DynSimOnly(_) => "a SimOnlyValue",
Self::TraceAsString { .. } => "a TraceAsString",
}
}
}
@ -109,6 +115,15 @@ impl<T: BaseType> From<T> for SerdeCanonicalType {
CanonicalType::Clock(Clock {}) => Self::Clock,
CanonicalType::PhantomConst(ty) => Self::PhantomConst(SerdePhantomConst(ty.get())),
CanonicalType::DynSimOnly(ty) => Self::DynSimOnly(ty),
CanonicalType::TraceAsString(TraceAsString {
inner_ty,
trace_as_string,
}) => Self::TraceAsString {
inner_ty: inner_ty.interned(),
trace_as_string: SerdeById {
inner: trace_as_string.interned(),
},
},
}
}
}
@ -130,6 +145,13 @@ impl From<SerdeCanonicalType> for CanonicalType {
Self::PhantomConst(PhantomConst::new_interned(value.0))
}
SerdeCanonicalType::DynSimOnly(value) => Self::DynSimOnly(value),
SerdeCanonicalType::TraceAsString {
inner_ty,
trace_as_string,
} => Self::TraceAsString(TraceAsString {
inner_ty: crate::intern::LazyInterned::Interned(inner_ty),
trace_as_string: crate::intern::LazyInterned::Interned(trace_as_string.inner),
}),
}
}
}

View file

@ -46,3 +46,4 @@ pub(crate) use misc::{InternedStrCompareAsStr, chain, copy_le_bytes_to_bitslice}
pub mod job_server;
pub mod prefix_sum;
pub mod ready_valid;
pub(crate) mod serde_by_id;

View file

@ -0,0 +1,234 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::util::HashMap;
use hashbrown::hash_map::Entry;
use serde::{Deserialize, Serialize, de::Error};
use std::{
any::TypeId,
borrow::Cow,
fmt::Write,
hash::{BuildHasher, Hash, Hasher},
marker::PhantomData,
sync::Mutex,
};
pub(crate) struct SerdeByIdProperties<T: SerdeByIdTrait> {
type_id: TypeId,
type_name: &'static str,
_phantom: PhantomData<fn(T) -> T>,
}
impl<T: SerdeByIdTrait> Clone for SerdeByIdProperties<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: SerdeByIdTrait> Copy for SerdeByIdProperties<T> {}
impl<T: SerdeByIdTrait> SerdeByIdProperties<T> {
pub fn of<U: ?Sized + 'static>() -> Self {
Self {
type_id: TypeId::of::<U>(),
type_name: std::any::type_name::<U>(),
_phantom: PhantomData,
}
}
}
pub(crate) trait SerdeByIdTrait: Hash + Eq + Clone + 'static + Send {
fn serde_by_id_properties(&self) -> SerdeByIdProperties<Self>;
fn static_table() -> &'static SerdeByIdTable<Self>;
const NAME: &'static str;
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
#[serde(transparent)]
struct SerdeRandomId([u32; 4]);
#[derive(Serialize, Deserialize)]
pub(crate) struct SerdeId<'a, T: SerdeByIdTrait> {
random_id: SerdeRandomId,
#[serde(borrow)]
type_name: Cow<'a, str>,
#[serde(skip)]
_phantom: PhantomData<fn(T) -> T>,
}
impl<'a, T: SerdeByIdTrait> Clone for SerdeId<'a, T> {
fn clone(&self) -> Self {
Self {
random_id: self.random_id,
type_name: self.type_name.clone(),
_phantom: PhantomData,
}
}
}
impl<'a, T: SerdeByIdTrait> Eq for SerdeId<'a, T> {}
impl<'a, 'b, T: SerdeByIdTrait> PartialEq<SerdeId<'b, T>> for SerdeId<'a, T> {
fn eq(&self, other: &SerdeId<'b, T>) -> bool {
let Self {
random_id,
type_name,
_phantom: _,
} = self;
*random_id == other.random_id && *type_name == other.type_name
}
}
impl<'a, T: SerdeByIdTrait> Hash for SerdeId<'a, T> {
fn hash<H: Hasher>(&self, state: &mut H) {
let Self {
random_id,
type_name: _,
_phantom: _,
} = self;
random_id.hash(state);
}
}
struct SerdeByIdTableRest<T: SerdeByIdTrait> {
from_serde: HashMap<SerdeId<'static, T>, T>,
serde_id_random_state: std::hash::RandomState,
buffer: String,
}
impl<T: SerdeByIdTrait> Default for SerdeByIdTableRest<T> {
fn default() -> Self {
Self {
from_serde: Default::default(),
serde_id_random_state: Default::default(),
buffer: Default::default(),
}
}
}
impl<T: SerdeByIdTrait> SerdeByIdTableRest<T> {
fn add_new(&mut self, value: T) -> SerdeId<'static, T> {
let properties = value.serde_by_id_properties();
let mut try_number = 0u64;
let mut hasher = self.serde_id_random_state.build_hasher();
// extract more bits of randomness from TypeId -- its Hash impl only hashes 64-bits
write!(self.buffer, "{:?}", properties.type_id).expect("shouldn't ever fail");
self.buffer.hash(&mut hasher);
loop {
let mut hasher = hasher.clone();
try_number.hash(&mut hasher);
try_number += 1;
let key = SerdeId {
random_id: SerdeRandomId(std::array::from_fn(|i| {
let mut hasher = hasher.clone();
i.hash(&mut hasher);
hasher.finish() as u32
})),
type_name: Cow::Borrowed(properties.type_name),
_phantom: PhantomData,
};
match self.from_serde.entry(key) {
Entry::Occupied(_) => continue,
Entry::Vacant(e) => {
let key = e.key().clone();
e.insert(value);
return key;
}
}
}
}
}
pub(crate) struct SerdeByIdTableMut<T: SerdeByIdTrait> {
to_serde: HashMap<T, SerdeId<'static, T>>,
rest: SerdeByIdTableRest<T>,
}
impl<T: SerdeByIdTrait> Default for SerdeByIdTableMut<T> {
fn default() -> Self {
Self {
to_serde: Default::default(),
rest: Default::default(),
}
}
}
impl<T: SerdeByIdTrait> SerdeByIdTableMut<T> {
pub(crate) fn to_serde(&mut self, value: &T) -> SerdeId<'static, T> {
if let Some(retval) = self.to_serde.get(value) {
return retval.clone();
}
self.to_serde_insert(value)
}
#[cold]
fn to_serde_insert(&mut self, value: &T) -> SerdeId<'static, T> {
let value = value.clone();
let retval = self.rest.add_new(value.clone());
self.to_serde.insert(value, retval.clone());
retval
}
pub(crate) fn from_serde(&self, id: &SerdeId<'_, T>) -> Option<T> {
self.rest.from_serde.get(id).cloned()
}
}
pub(crate) struct SerdeByIdTable<T: SerdeByIdTrait>(Mutex<Option<SerdeByIdTableMut<T>>>);
impl<T: SerdeByIdTrait> SerdeByIdTable<T> {
pub(crate) const fn new() -> Self {
Self(Mutex::new(None))
}
pub(crate) fn to_serde(&self, value: &T) -> SerdeId<'static, T> {
self.0
.lock()
.expect("shouldn't be poison")
.get_or_insert_with(
#[cold]
|| Default::default(),
)
.to_serde(value)
}
pub(crate) fn from_serde(&self, id: &SerdeId<'_, T>) -> Option<T> {
self.0
.lock()
.expect("shouldn't be poison")
.get_or_insert_with(
#[cold]
|| Default::default(),
)
.from_serde(id)
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default, Ord, PartialOrd)]
pub(crate) struct SerdeById<T: SerdeByIdTrait> {
pub(crate) inner: T,
}
impl<'de, T: SerdeByIdTrait> Deserialize<'de> for SerdeById<T> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let id = SerdeId::deserialize(deserializer)?;
let inner = T::static_table().from_serde(&id).ok_or_else(|| {
D::Error::custom(format_args!(
"doesn't match any {} that was serialized this time this program was run: type_name={:?}",
T::NAME,
id.type_name,
))
})?;
Ok(Self { inner })
}
}
impl<T: SerdeByIdTrait> Serialize for SerdeById<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
T::static_table()
.to_serde(&self.inner)
.serialize(serializer)
}
}

View file

@ -546,7 +546,7 @@ impl<W: fmt::Write> Visitor for XdcFileWriter<W> {
base.source_location(),
)? {},
}
match base.canonical_ty() {
match base.canonical_ty().unwrap_transparent_types() {
CanonicalType::UInt(_)
| CanonicalType::SInt(_)
| CanonicalType::Bool(_)
@ -563,6 +563,9 @@ impl<W: fmt::Write> Visitor for XdcFileWriter<W> {
v,
base.source_location(),
)? {},
CanonicalType::TraceAsString(_) => {
unreachable!("handled by unwrap_transparent_types")
}
}
self.required_dont_touch_targets.insert(target);
match v {

View file

@ -1,12 +1,19 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use bitvec::{order::Lsb0, view::BitView};
use fayalite::{
memory::{ReadStruct, ReadWriteStruct, WriteStruct},
module::{instance_with_loc, memory_with_init_and_loc, reg_builder_with_loc},
assert_export_firrtl,
firrtl::ExportOptions,
memory::{ReadStruct, ReadWriteStruct, WriteStruct, splat_mask},
module::{
instance_with_loc, memory_with_init_and_loc, reg_builder_with_loc,
transform::simplify_enums::SimplifyEnumsKind,
},
prelude::*,
reset::ResetType,
sim::vcd::VcdWriterDecls,
ty::{SimValueDebug, StaticType, TraceAsString, TraceAsStringSimValue},
util::{RcWriter, ready_valid::queue},
};
use std::{collections::BTreeMap, num::NonZeroUsize, rc::Rc};
@ -2985,3 +2992,621 @@ fn test_queue_4_true_true() {
include_str!("sim/expected/queue_4_true_true.txt"),
);
}
#[hdl(outline_generated, custom_debug(sim), cmp_eq)]
pub enum HasCustomDebug {
Text(UInt<512>),
FmtError,
}
impl HasCustomDebug {
#[hdl]
pub fn new_sim(text: Result<&str, std::fmt::Error>) -> SimValue<Self> {
match text {
Ok(text) => {
let mut retval = HasCustomDebug.Text.zero();
let src = text.as_bytes().view_bits::<Lsb0>();
let dest = retval.bits_mut();
let len = src.len().min(dest.len());
dest[..len].clone_from_bitslice(&src[..len]);
#[hdl(sim)]
HasCustomDebug.Text(retval)
}
Err(std::fmt::Error) =>
{
#[hdl(sim)]
HasCustomDebug.FmtError()
}
}
}
}
impl SimValueDebug for HasCustomDebug {
#[hdl]
fn sim_value_debug(
value: &<Self as Type>::SimValue,
f: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
if f.alternate() {
return #[hdl(sim)]
match value {
Self::FmtError => f.write_str("FmtError"),
Self::Text(text) => f.debug_tuple("Text").field(text).finish(),
};
}
#[hdl(sim)]
match value {
Self::FmtError => Err(std::fmt::Error),
Self::Text(text) => {
assert_eq!(text.ty().width() % u8::BITS as usize, 0);
let mut bytes = vec![0u8; text.ty().width() / u8::BITS as usize];
bytes
.view_bits_mut::<Lsb0>()
.clone_from_bitslice(text.bits());
if let Some(len) = bytes.iter().position(|b| *b == 0) {
bytes.truncate(len);
}
f.write_str(&String::from_utf8_lossy(&bytes))
}
}
}
}
#[hdl_module(outline_generated)]
pub fn sim_trace_as_string() {
#[hdl]
let clk: Clock = m.input();
#[hdl]
let read: ReadStruct<TraceAsString<Array<HasCustomDebug, 2>>, ConstUsize<8>> = m.input();
#[hdl]
let write: WriteStruct<Array<TraceAsString<HasCustomDebug>, 2>, ConstUsize<8>> = m.input();
#[hdl]
let mut mem = memory_with_init(
[[Expr::as_trace_as_string(HasCustomDebug::new_sim(Ok("")).to_expr(), StaticType::TYPE); 2];
4],
);
let read_port = mem.new_read_port();
connect(read_port.clk, clk);
connect_any(read_port.addr, read.addr);
connect(read_port.en, read.en);
for (l, r) in read.data.iter().zip(read_port.data.iter()) {
connect(l, &**r);
}
let write_port = mem.new_write_port();
connect(write_port.clk, clk);
connect_any(write_port.addr, write.addr);
connect(write_port.data, write.data);
connect(write_port.en, write.en);
connect(write_port.mask, write.mask);
}
#[hdl]
#[test]
fn test_sim_trace_as_string() {
let _n = SourceLocation::normalize_files_for_tests();
let m = sim_trace_as_string();
let mut sim = Simulation::new(m);
// sim.set_breakpoints_unstable(Default::default(), true);
let mut writer = RcWriter::default();
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
sim.write(sim.io().clk, false);
sim.write(sim.io().read.clk, false);
sim.write(sim.io().write.clk, false);
#[derive(Debug)]
struct TestCase {
read: Option<(u8, [Result<&'static str, std::fmt::Error>; 2])>,
write_addr: Option<u8>,
write_data: [Result<&'static str, std::fmt::Error>; 2],
write_mask: [bool; 2],
}
const TEST_CASES: &[TestCase] = &[
TestCase {
read: None,
write_addr: None,
write_data: [Ok(""); 2],
write_mask: [false; 2],
},
TestCase {
read: None,
write_addr: Some(0),
write_data: [Ok("mem[0][0]"), Ok("mem[0][1]")],
write_mask: [true; 2],
},
TestCase {
read: None,
write_addr: Some(1),
write_data: [Ok("mem[1][0]"), Ok("mem[1][1]")],
write_mask: [true; 2],
},
TestCase {
read: None,
write_addr: Some(2),
write_data: [Ok("mem[2][0]"), Ok("mem[2][1]")],
write_mask: [true; 2],
},
TestCase {
read: None,
write_addr: Some(3),
write_data: [Ok("mem[3][0]"), Ok("mem[3][1]")],
write_mask: [true; 2],
},
TestCase {
read: Some((1, [Ok("mem[1][0]"), Ok("mem[1][1]")])),
write_addr: None,
write_data: [Err(std::fmt::Error), Err(std::fmt::Error)],
write_mask: [true; 2],
},
TestCase {
read: Some((1, [Err(std::fmt::Error), Err(std::fmt::Error)])),
write_addr: Some(1),
write_data: [Err(std::fmt::Error), Err(std::fmt::Error)],
write_mask: [true; 2],
},
];
for test_case in TEST_CASES {
let TestCase {
read,
write_addr,
write_data,
write_mask,
} = test_case;
sim.write(sim.io().read.addr, read.map(|v| v.0).unwrap_or(0));
sim.write(sim.io().read.en, read.is_some());
sim.write(sim.io().write.addr, write_addr.unwrap_or(0));
sim.write(sim.io().write.en, write_addr.is_some());
sim.write(
sim.io().write.data,
write_data.map(|v| TraceAsStringSimValue::new(HasCustomDebug::new_sim(v))),
);
sim.write(
sim.io().write.mask,
write_mask.map(|v| splat_mask(TraceAsString[HasCustomDebug], v.to_expr())),
);
sim.write(sim.io().clk, false);
sim.advance_time(SimDuration::from_nanos(500));
sim.write(sim.io().clk, true);
sim.advance_time(SimDuration::from_nanos(500));
if let Some((_, expected_read_data)) = read {
let read_data = sim.read(sim.io().read.data);
let expected_read_data = expected_read_data
.map(HasCustomDebug::new_sim)
.into_sim_value();
assert!(
**read_data == *expected_read_data,
"{read_data:#?}\n!= {expected_read_data:#?}",
);
}
}
sim.flush_traces().unwrap();
let vcd = String::from_utf8(writer.take()).unwrap();
println!("####### VCD:\n{vcd}\n#######");
if vcd != include_str!("sim/expected/sim_trace_as_string.vcd") {
panic!();
}
let sim_debug = format!("{sim:#?}");
println!("#######\n{sim_debug}\n#######");
if sim_debug != include_str!("sim/expected/sim_trace_as_string.txt") {
panic!();
}
}
#[test]
fn test_firrtl_trace_as_string() {
let _n = SourceLocation::normalize_files_for_tests();
let m = sim_trace_as_string();
#[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
assert_export_firrtl! {
m =>
options: ExportOptions {
simplify_memories: false,
simplify_enums: None,
..ExportOptions::default()
},
"/test/sim_trace_as_string.fir": r#"FIRRTL version 3.2.0
circuit sim_trace_as_string: %[[
{
"class": "firrtl.annotations.MemoryFileInlineAnnotation",
"filename": "/test/sim_trace_as_string/mem.mem",
"hexOrBinary": "b",
"target": "~sim_trace_as_string|sim_trace_as_string>mem"
}
]]
type Ty0 = {|Text: UInt<512>, FmtError|}
type Ty1 = {addr: UInt<8>, en: UInt<1>, clk: Clock, flip data: Ty0[2]}
type Ty2 = {addr: UInt<8>, en: UInt<1>, clk: Clock, data: Ty0[2], mask: UInt<1>[2]}
type Ty3 = {addr: UInt<2>, en: UInt<1>, clk: Clock, flip data: Ty0[2]}
type Ty4 = {addr: UInt<2>, en: UInt<1>, clk: Clock, data: Ty0[2], mask: UInt<1>[2]}
module sim_trace_as_string: @[module-XXXXXXXXXX.rs 1:1]
input clk: Clock @[module-XXXXXXXXXX.rs 2:1]
input `read`: Ty1 @[module-XXXXXXXXXX.rs 3:1]
input `write`: Ty2 @[module-XXXXXXXXXX.rs 4:1]
mem `mem`: @[module-XXXXXXXXXX.rs 5:1]
data-type => Ty0[2]
depth => 4
read-latency => 0
write-latency => 1
read-under-write => old
reader => r0
writer => w1
connect `mem`.r0.clk, clk @[module-XXXXXXXXXX.rs 7:1]
; connect different types:
; lhs: UInt<2>
; rhs: UInt<8>
connect `mem`.r0.addr, `read`.addr @[module-XXXXXXXXXX.rs 8:1]
connect `mem`.r0.en, `read`.en @[module-XXXXXXXXXX.rs 9:1]
connect `read`.data[0], `mem`.r0.data[0] @[module-XXXXXXXXXX.rs 10:1]
connect `read`.data[1], `mem`.r0.data[1] @[module-XXXXXXXXXX.rs 10:1]
connect `mem`.w1.clk, clk @[module-XXXXXXXXXX.rs 12:1]
; connect different types:
; lhs: UInt<2>
; rhs: UInt<8>
connect `mem`.w1.addr, `write`.addr @[module-XXXXXXXXXX.rs 13:1]
connect `mem`.w1.data, `write`.data @[module-XXXXXXXXXX.rs 14:1]
connect `mem`.w1.en, `write`.en @[module-XXXXXXXXXX.rs 15:1]
connect `mem`.w1.mask, `write`.mask @[module-XXXXXXXXXX.rs 16:1]
"#,
"/test/sim_trace_as_string/mem.mem": r"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
",
};
#[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
assert_export_firrtl! {
m =>
options: ExportOptions {
simplify_memories: true,
simplify_enums: None,
..ExportOptions::default()
},
"/test/sim_trace_as_string.fir": r#"FIRRTL version 3.2.0
circuit sim_trace_as_string: %[[
{
"class": "firrtl.annotations.MemoryFileInlineAnnotation",
"filename": "/test/sim_trace_as_string/mem.mem",
"hexOrBinary": "b",
"target": "~sim_trace_as_string|sim_trace_as_string>mem"
}
]]
type Ty0 = {|Text: UInt<512>, FmtError|}
type Ty1 = {addr: UInt<8>, en: UInt<1>, clk: Clock, flip data: Ty0[2]}
type Ty2 = {addr: UInt<8>, en: UInt<1>, clk: Clock, data: Ty0[2], mask: UInt<1>[2]}
type Ty3 = {addr: UInt<2>, en: UInt<1>, clk: Clock, flip data: Ty0[2]}
type Ty4 = {addr: UInt<2>, en: UInt<1>, clk: Clock, data: Ty0[2], mask: UInt<1>[2]}
type Ty5 = {addr: UInt<2>, en: UInt<1>, clk: Clock, flip data: UInt<513>[2]}
type Ty6 = {addr: UInt<2>, en: UInt<1>, clk: Clock, data: UInt<513>[2], mask: UInt<1>[2]}
module sim_trace_as_string: @[module-XXXXXXXXXX.rs 1:1]
input clk: Clock @[module-XXXXXXXXXX.rs 2:1]
input `read`: Ty1 @[module-XXXXXXXXXX.rs 3:1]
input `write`: Ty2 @[module-XXXXXXXXXX.rs 4:1]
mem `mem`: @[module-XXXXXXXXXX.rs 5:1]
data-type => UInt<513>[2]
depth => 4
read-latency => 0
write-latency => 1
read-under-write => old
reader => r0
writer => w1
wire mem_r0: Ty3 @[module-XXXXXXXXXX.rs 6:1]
wire mem_w1: Ty4 @[module-XXXXXXXXXX.rs 11:1]
wire _cast_bits_to_enum_expr: Ty0
wire _cast_bits_to_enum_expr_body: UInt<512>
connect _cast_bits_to_enum_expr_body, head(`mem`.r0.data[0], 512)
when eq(UInt<1>(0), tail(`mem`.r0.data[0], 512)):
connect _cast_bits_to_enum_expr, {|Text: UInt<512>, FmtError|}(Text, _cast_bits_to_enum_expr_body)
else:
connect _cast_bits_to_enum_expr, {|Text: UInt<512>, FmtError|}(FmtError)
connect mem_r0.data[0], _cast_bits_to_enum_expr @[module-XXXXXXXXXX.rs 6:1]
wire _cast_bits_to_enum_expr_1: Ty0
wire _cast_bits_to_enum_expr_body_1: UInt<512>
connect _cast_bits_to_enum_expr_body_1, head(`mem`.r0.data[1], 512)
when eq(UInt<1>(0), tail(`mem`.r0.data[1], 512)):
connect _cast_bits_to_enum_expr_1, {|Text: UInt<512>, FmtError|}(Text, _cast_bits_to_enum_expr_body_1)
else:
connect _cast_bits_to_enum_expr_1, {|Text: UInt<512>, FmtError|}(FmtError)
connect mem_r0.data[1], _cast_bits_to_enum_expr_1 @[module-XXXXXXXXXX.rs 6:1]
wire _cast_enum_to_bits_expr: UInt<513>
match mem_w1.data[0]:
Text(_cast_enum_to_bits_expr_Text):
connect _cast_enum_to_bits_expr, pad(cat(_cast_enum_to_bits_expr_Text, UInt<1>(0)), 513)
FmtError:
connect _cast_enum_to_bits_expr, UInt<513>(1)
connect `mem`.w1.data[0], _cast_enum_to_bits_expr @[module-XXXXXXXXXX.rs 11:1]
connect `mem`.w1.mask[0], mem_w1.mask[0] @[module-XXXXXXXXXX.rs 11:1]
wire _cast_enum_to_bits_expr_1: UInt<513>
match mem_w1.data[1]:
Text(_cast_enum_to_bits_expr_Text_1):
connect _cast_enum_to_bits_expr_1, pad(cat(_cast_enum_to_bits_expr_Text_1, UInt<1>(0)), 513)
FmtError:
connect _cast_enum_to_bits_expr_1, UInt<513>(1)
connect `mem`.w1.data[1], _cast_enum_to_bits_expr_1 @[module-XXXXXXXXXX.rs 11:1]
connect `mem`.w1.mask[1], mem_w1.mask[1] @[module-XXXXXXXXXX.rs 11:1]
connect `mem`.r0.addr, mem_r0.addr @[module-XXXXXXXXXX.rs 6:1]
connect `mem`.r0.clk, mem_r0.clk @[module-XXXXXXXXXX.rs 6:1]
connect `mem`.r0.en, mem_r0.en @[module-XXXXXXXXXX.rs 6:1]
connect `mem`.w1.addr, mem_w1.addr @[module-XXXXXXXXXX.rs 11:1]
connect `mem`.w1.clk, mem_w1.clk @[module-XXXXXXXXXX.rs 11:1]
connect `mem`.w1.en, mem_w1.en @[module-XXXXXXXXXX.rs 11:1]
connect mem_r0.clk, clk @[module-XXXXXXXXXX.rs 7:1]
; connect different types:
; lhs: UInt<2>
; rhs: UInt<8>
connect mem_r0.addr, `read`.addr @[module-XXXXXXXXXX.rs 8:1]
connect mem_r0.en, `read`.en @[module-XXXXXXXXXX.rs 9:1]
connect `read`.data[0], mem_r0.data[0] @[module-XXXXXXXXXX.rs 10:1]
connect `read`.data[1], mem_r0.data[1] @[module-XXXXXXXXXX.rs 10:1]
connect mem_w1.clk, clk @[module-XXXXXXXXXX.rs 12:1]
; connect different types:
; lhs: UInt<2>
; rhs: UInt<8>
connect mem_w1.addr, `write`.addr @[module-XXXXXXXXXX.rs 13:1]
connect mem_w1.data, `write`.data @[module-XXXXXXXXXX.rs 14:1]
connect mem_w1.en, `write`.en @[module-XXXXXXXXXX.rs 15:1]
connect mem_w1.mask, `write`.mask @[module-XXXXXXXXXX.rs 16:1]
"#,
"/test/sim_trace_as_string/mem.mem": r"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
",
};
#[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
assert_export_firrtl! {
m =>
options: ExportOptions {
simplify_memories: false,
simplify_enums: Some(SimplifyEnumsKind::SimplifyToEnumsWithNoBody),
..ExportOptions::default()
},
"/test/sim_trace_as_string.fir": r#"FIRRTL version 3.2.0
circuit sim_trace_as_string: %[[
{
"class": "firrtl.annotations.MemoryFileInlineAnnotation",
"filename": "/test/sim_trace_as_string/mem.mem",
"hexOrBinary": "b",
"target": "~sim_trace_as_string|sim_trace_as_string>mem"
}
]]
type Ty0 = {|Text, FmtError|}
type Ty1 = {tag: Ty0, body: UInt<512>}
type Ty2 = {addr: UInt<8>, en: UInt<1>, clk: Clock, flip data: Ty1[2]}
type Ty3 = {addr: UInt<8>, en: UInt<1>, clk: Clock, data: Ty1[2], mask: UInt<1>[2]}
type Ty4 = {addr: UInt<2>, en: UInt<1>, clk: Clock, data: Ty1[2], mask: UInt<1>[2]}
type Ty5 = {tag: UInt<1>, body: UInt<1>}
type Ty6 = {addr: UInt<2>, en: UInt<1>, clk: Clock, data: Ty1[2], mask: Ty5[2]}
type Ty7 = {addr: UInt<2>, en: UInt<1>, clk: Clock, flip data: Ty1[2]}
module sim_trace_as_string: @[module-XXXXXXXXXX.rs 1:1]
input clk: Clock @[module-XXXXXXXXXX.rs 2:1]
input `read`: Ty2 @[module-XXXXXXXXXX.rs 3:1]
input `write`: Ty3 @[module-XXXXXXXXXX.rs 4:1]
mem `mem`: @[module-XXXXXXXXXX.rs 5:1]
data-type => Ty1[2]
depth => 4
read-latency => 0
write-latency => 1
read-under-write => old
reader => r0
writer => w1
wire mem_w1: Ty4 @[module-XXXXXXXXXX.rs 11:1]
connect `mem`.w1.addr, mem_w1.addr @[module-XXXXXXXXXX.rs 11:1]
connect `mem`.w1.en, mem_w1.en @[module-XXXXXXXXXX.rs 11:1]
connect `mem`.w1.clk, mem_w1.clk @[module-XXXXXXXXXX.rs 11:1]
connect `mem`.w1.data, mem_w1.data @[module-XXXXXXXXXX.rs 11:1]
connect `mem`.w1.mask[0].tag, mem_w1.mask[0] @[module-XXXXXXXXXX.rs 11:1]
connect `mem`.w1.mask[0].body, mem_w1.mask[0] @[module-XXXXXXXXXX.rs 11:1]
connect `mem`.w1.mask[1].tag, mem_w1.mask[1] @[module-XXXXXXXXXX.rs 11:1]
connect `mem`.w1.mask[1].body, mem_w1.mask[1] @[module-XXXXXXXXXX.rs 11:1]
connect `mem`.r0.clk, clk @[module-XXXXXXXXXX.rs 7:1]
; connect different types:
; lhs: UInt<2>
; rhs: UInt<8>
connect `mem`.r0.addr, `read`.addr @[module-XXXXXXXXXX.rs 8:1]
connect `mem`.r0.en, `read`.en @[module-XXXXXXXXXX.rs 9:1]
connect `read`.data[0], `mem`.r0.data[0] @[module-XXXXXXXXXX.rs 10:1]
connect `read`.data[1], `mem`.r0.data[1] @[module-XXXXXXXXXX.rs 10:1]
connect mem_w1.clk, clk @[module-XXXXXXXXXX.rs 12:1]
; connect different types:
; lhs: UInt<2>
; rhs: UInt<8>
connect mem_w1.addr, `write`.addr @[module-XXXXXXXXXX.rs 13:1]
connect mem_w1.data, `write`.data @[module-XXXXXXXXXX.rs 14:1]
connect mem_w1.en, `write`.en @[module-XXXXXXXXXX.rs 15:1]
connect mem_w1.mask, `write`.mask @[module-XXXXXXXXXX.rs 16:1]
"#,
"/test/sim_trace_as_string/mem.mem": r"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
",
};
#[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
assert_export_firrtl! {
m =>
options: ExportOptions {
simplify_memories: true,
simplify_enums: Some(SimplifyEnumsKind::SimplifyToEnumsWithNoBody),
..ExportOptions::default()
},
"/test/sim_trace_as_string.fir": r#"FIRRTL version 3.2.0
circuit sim_trace_as_string: %[[
{
"class": "firrtl.annotations.MemoryFileInlineAnnotation",
"filename": "/test/sim_trace_as_string/mem_0_tag.mem",
"hexOrBinary": "b",
"target": "~sim_trace_as_string|sim_trace_as_string>mem_0_tag"
},
{
"class": "firrtl.annotations.MemoryFileInlineAnnotation",
"filename": "/test/sim_trace_as_string/mem_0_body.mem",
"hexOrBinary": "h",
"target": "~sim_trace_as_string|sim_trace_as_string>mem_0_body"
},
{
"class": "firrtl.annotations.MemoryFileInlineAnnotation",
"filename": "/test/sim_trace_as_string/mem_1_tag.mem",
"hexOrBinary": "b",
"target": "~sim_trace_as_string|sim_trace_as_string>mem_1_tag"
},
{
"class": "firrtl.annotations.MemoryFileInlineAnnotation",
"filename": "/test/sim_trace_as_string/mem_1_body.mem",
"hexOrBinary": "h",
"target": "~sim_trace_as_string|sim_trace_as_string>mem_1_body"
}
]]
type Ty0 = {|Text, FmtError|}
type Ty1 = {tag: Ty0, body: UInt<512>}
type Ty2 = {addr: UInt<8>, en: UInt<1>, clk: Clock, flip data: Ty1[2]}
type Ty3 = {addr: UInt<8>, en: UInt<1>, clk: Clock, data: Ty1[2], mask: UInt<1>[2]}
type Ty4 = {addr: UInt<2>, en: UInt<1>, clk: Clock, flip data: Ty1[2]}
type Ty5 = {tag: UInt<1>, body: UInt<1>}
type Ty6 = {addr: UInt<2>, en: UInt<1>, clk: Clock, data: Ty1[2], mask: Ty5[2]}
type Ty7 = {addr: UInt<2>, en: UInt<1>, clk: Clock, flip data: UInt<1>}
type Ty8 = {addr: UInt<2>, en: UInt<1>, clk: Clock, data: UInt<1>, mask: UInt<1>}
type Ty9 = {addr: UInt<2>, en: UInt<1>, clk: Clock, flip data: UInt<512>}
type Ty10 = {addr: UInt<2>, en: UInt<1>, clk: Clock, data: UInt<512>, mask: UInt<1>}
type Ty11 = {addr: UInt<2>, en: UInt<1>, clk: Clock, data: Ty1[2], mask: UInt<1>[2]}
module sim_trace_as_string: @[module-XXXXXXXXXX.rs 1:1]
input clk: Clock @[module-XXXXXXXXXX.rs 2:1]
input `read`: Ty2 @[module-XXXXXXXXXX.rs 3:1]
input `write`: Ty3 @[module-XXXXXXXXXX.rs 4:1]
mem mem_0_tag: @[module-XXXXXXXXXX.rs 5:1]
data-type => UInt<1>
depth => 4
read-latency => 0
write-latency => 1
read-under-write => old
reader => r0
writer => w1
mem mem_0_body: @[module-XXXXXXXXXX.rs 5:1]
data-type => UInt<512>
depth => 4
read-latency => 0
write-latency => 1
read-under-write => old
reader => r0
writer => w1
mem mem_1_tag: @[module-XXXXXXXXXX.rs 5:1]
data-type => UInt<1>
depth => 4
read-latency => 0
write-latency => 1
read-under-write => old
reader => r0
writer => w1
mem mem_1_body: @[module-XXXXXXXXXX.rs 5:1]
data-type => UInt<512>
depth => 4
read-latency => 0
write-latency => 1
read-under-write => old
reader => r0
writer => w1
wire mem_r0: Ty4 @[module-XXXXXXXXXX.rs 6:1]
wire mem_w1: Ty6 @[module-XXXXXXXXXX.rs 11:1]
wire _cast_bits_to_enum_expr: Ty0
when eq(UInt<1>(0), tail(mem_0_tag.r0.data, 0)):
connect _cast_bits_to_enum_expr, {|Text, FmtError|}(Text)
else:
connect _cast_bits_to_enum_expr, {|Text, FmtError|}(FmtError)
connect mem_r0.data[0].tag, _cast_bits_to_enum_expr @[module-XXXXXXXXXX.rs 6:1]
wire _cast_enum_to_bits_expr: UInt<1>
match mem_w1.data[0].tag:
Text:
connect _cast_enum_to_bits_expr, UInt<1>(0)
FmtError:
connect _cast_enum_to_bits_expr, UInt<1>(1)
connect mem_0_tag.w1.data, _cast_enum_to_bits_expr @[module-XXXXXXXXXX.rs 11:1]
connect mem_0_tag.w1.mask, mem_w1.mask[0].tag @[module-XXXXXXXXXX.rs 11:1]
connect mem_0_tag.r0.addr, mem_r0.addr @[module-XXXXXXXXXX.rs 6:1]
connect mem_0_tag.r0.clk, mem_r0.clk @[module-XXXXXXXXXX.rs 6:1]
connect mem_0_tag.r0.en, mem_r0.en @[module-XXXXXXXXXX.rs 6:1]
connect mem_0_tag.w1.addr, mem_w1.addr @[module-XXXXXXXXXX.rs 11:1]
connect mem_0_tag.w1.clk, mem_w1.clk @[module-XXXXXXXXXX.rs 11:1]
connect mem_0_tag.w1.en, mem_w1.en @[module-XXXXXXXXXX.rs 11:1]
connect mem_r0.data[0].body, mem_0_body.r0.data @[module-XXXXXXXXXX.rs 6:1]
connect mem_0_body.w1.data, mem_w1.data[0].body @[module-XXXXXXXXXX.rs 11:1]
connect mem_0_body.w1.mask, mem_w1.mask[0].body @[module-XXXXXXXXXX.rs 11:1]
connect mem_0_body.r0.addr, mem_r0.addr @[module-XXXXXXXXXX.rs 6:1]
connect mem_0_body.r0.clk, mem_r0.clk @[module-XXXXXXXXXX.rs 6:1]
connect mem_0_body.r0.en, mem_r0.en @[module-XXXXXXXXXX.rs 6:1]
connect mem_0_body.w1.addr, mem_w1.addr @[module-XXXXXXXXXX.rs 11:1]
connect mem_0_body.w1.clk, mem_w1.clk @[module-XXXXXXXXXX.rs 11:1]
connect mem_0_body.w1.en, mem_w1.en @[module-XXXXXXXXXX.rs 11:1]
wire _cast_bits_to_enum_expr_1: Ty0
when eq(UInt<1>(0), tail(mem_1_tag.r0.data, 0)):
connect _cast_bits_to_enum_expr_1, {|Text, FmtError|}(Text)
else:
connect _cast_bits_to_enum_expr_1, {|Text, FmtError|}(FmtError)
connect mem_r0.data[1].tag, _cast_bits_to_enum_expr_1 @[module-XXXXXXXXXX.rs 6:1]
wire _cast_enum_to_bits_expr_1: UInt<1>
match mem_w1.data[1].tag:
Text:
connect _cast_enum_to_bits_expr_1, UInt<1>(0)
FmtError:
connect _cast_enum_to_bits_expr_1, UInt<1>(1)
connect mem_1_tag.w1.data, _cast_enum_to_bits_expr_1 @[module-XXXXXXXXXX.rs 11:1]
connect mem_1_tag.w1.mask, mem_w1.mask[1].tag @[module-XXXXXXXXXX.rs 11:1]
connect mem_1_tag.r0.addr, mem_r0.addr @[module-XXXXXXXXXX.rs 6:1]
connect mem_1_tag.r0.clk, mem_r0.clk @[module-XXXXXXXXXX.rs 6:1]
connect mem_1_tag.r0.en, mem_r0.en @[module-XXXXXXXXXX.rs 6:1]
connect mem_1_tag.w1.addr, mem_w1.addr @[module-XXXXXXXXXX.rs 11:1]
connect mem_1_tag.w1.clk, mem_w1.clk @[module-XXXXXXXXXX.rs 11:1]
connect mem_1_tag.w1.en, mem_w1.en @[module-XXXXXXXXXX.rs 11:1]
connect mem_r0.data[1].body, mem_1_body.r0.data @[module-XXXXXXXXXX.rs 6:1]
connect mem_1_body.w1.data, mem_w1.data[1].body @[module-XXXXXXXXXX.rs 11:1]
connect mem_1_body.w1.mask, mem_w1.mask[1].body @[module-XXXXXXXXXX.rs 11:1]
connect mem_1_body.r0.addr, mem_r0.addr @[module-XXXXXXXXXX.rs 6:1]
connect mem_1_body.r0.clk, mem_r0.clk @[module-XXXXXXXXXX.rs 6:1]
connect mem_1_body.r0.en, mem_r0.en @[module-XXXXXXXXXX.rs 6:1]
connect mem_1_body.w1.addr, mem_w1.addr @[module-XXXXXXXXXX.rs 11:1]
connect mem_1_body.w1.clk, mem_w1.clk @[module-XXXXXXXXXX.rs 11:1]
connect mem_1_body.w1.en, mem_w1.en @[module-XXXXXXXXXX.rs 11:1]
wire mem_w1_1: Ty11 @[module-XXXXXXXXXX.rs 11:1]
connect mem_w1.addr, mem_w1_1.addr @[module-XXXXXXXXXX.rs 11:1]
connect mem_w1.en, mem_w1_1.en @[module-XXXXXXXXXX.rs 11:1]
connect mem_w1.clk, mem_w1_1.clk @[module-XXXXXXXXXX.rs 11:1]
connect mem_w1.data, mem_w1_1.data @[module-XXXXXXXXXX.rs 11:1]
connect mem_w1.mask[0].tag, mem_w1_1.mask[0] @[module-XXXXXXXXXX.rs 11:1]
connect mem_w1.mask[0].body, mem_w1_1.mask[0] @[module-XXXXXXXXXX.rs 11:1]
connect mem_w1.mask[1].tag, mem_w1_1.mask[1] @[module-XXXXXXXXXX.rs 11:1]
connect mem_w1.mask[1].body, mem_w1_1.mask[1] @[module-XXXXXXXXXX.rs 11:1]
connect mem_r0.clk, clk @[module-XXXXXXXXXX.rs 7:1]
; connect different types:
; lhs: UInt<2>
; rhs: UInt<8>
connect mem_r0.addr, `read`.addr @[module-XXXXXXXXXX.rs 8:1]
connect mem_r0.en, `read`.en @[module-XXXXXXXXXX.rs 9:1]
connect `read`.data[0], mem_r0.data[0] @[module-XXXXXXXXXX.rs 10:1]
connect `read`.data[1], mem_r0.data[1] @[module-XXXXXXXXXX.rs 10:1]
connect mem_w1_1.clk, clk @[module-XXXXXXXXXX.rs 12:1]
; connect different types:
; lhs: UInt<2>
; rhs: UInt<8>
connect mem_w1_1.addr, `write`.addr @[module-XXXXXXXXXX.rs 13:1]
connect mem_w1_1.data, `write`.data @[module-XXXXXXXXXX.rs 14:1]
connect mem_w1_1.en, `write`.en @[module-XXXXXXXXXX.rs 15:1]
connect mem_w1_1.mask, `write`.mask @[module-XXXXXXXXXX.rs 16:1]
"#,
"/test/sim_trace_as_string/mem_0_body.mem": r"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
",
"/test/sim_trace_as_string/mem_0_tag.mem": r"0
0
0
0
",
"/test/sim_trace_as_string/mem_1_body.mem": r"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
",
"/test/sim_trace_as_string/mem_1_tag.mem": r"0
0
0
0
",
};
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,221 @@
$timescale 1 ps $end
$scope module sim_trace_as_string $end
$var wire 1 J(7*b clk $end
$scope struct read $end
$var wire 8 @t0}\ addr $end
$var wire 1 78"T5 en $end
$var wire 1 G7v@m clk $end
$var string 1 F&^FN data $end
$upscope $end
$scope struct write $end
$var wire 8 "fUdW addr $end
$var wire 1 r1OK) en $end
$var wire 1 ,ADvU clk $end
$scope struct data $end
$var string 1 pD.mP \[0] $end
$var string 1 !V!em \[1] $end
$upscope $end
$scope struct mask $end
$var wire 1 l8dgD \[0] $end
$var wire 1 1/sDs \[1] $end
$upscope $end
$upscope $end
$scope struct mem $end
$scope struct contents $end
$scope struct \[0] $end
$scope struct mem $end
$var string 1 sz>#| \[0] $end
$var string 1 G._83 \[1] $end
$upscope $end
$upscope $end
$scope struct \[1] $end
$scope struct mem $end
$var string 1 2r3#W \[0] $end
$var string 1 AbGF% \[1] $end
$upscope $end
$upscope $end
$scope struct \[2] $end
$scope struct mem $end
$var string 1 .^<$p \[0] $end
$var string 1 ?s@Dc \[1] $end
$upscope $end
$upscope $end
$scope struct \[3] $end
$scope struct mem $end
$var string 1 {*||o \[0] $end
$var string 1 Bg,vB \[1] $end
$upscope $end
$upscope $end
$upscope $end
$scope struct r0 $end
$var wire 2 .0()- addr $end
$var wire 1 GEbRA en $end
$var wire 1 ;`9BK clk $end
$scope struct data $end
$var string 1 _Xe"P \[0] $end
$var string 1 jXrsx \[1] $end
$upscope $end
$upscope $end
$scope struct w1 $end
$var wire 2 '8u?z addr $end
$var wire 1 ~o=`& en $end
$var wire 1 *q>M1 clk $end
$scope struct data $end
$var string 1 N\zBe \[0] $end
$var string 1 c3h8{ \[1] $end
$upscope $end
$scope struct mask $end
$var wire 1 .SYGD \[0] $end
$var wire 1 />wYd \[1] $end
$upscope $end
$upscope $end
$upscope $end
$upscope $end
$enddefinitions $end
$dumpvars
s sz>#|
s G._83
s 2r3#W
s AbGF%
s .^<$p
s ?s@Dc
s {*||o
s Bg,vB
0J(7*b
b0 @t0}\
078"T5
0G7v@m
s[,\x20] F&^FN
b0 "fUdW
0r1OK)
0,ADvU
s pD.mP
s !V!em
0l8dgD
01/sDs
b0 .0()-
0GEbRA
0;`9BK
s _Xe"P
s jXrsx
b0 '8u?z
0~o=`&
0*q>M1
s N\zBe
s c3h8{
0.SYGD
0/>wYd
$end
#500000
1J(7*b
1;`9BK
1*q>M1
#1000000
0J(7*b
1r1OK)
smem[0][0] pD.mP
smem[0][1] !V!em
1l8dgD
11/sDs
0;`9BK
1~o=`&
0*q>M1
smem[0][0] N\zBe
smem[0][1] c3h8{
1.SYGD
1/>wYd
#1500000
smem[0][0] sz>#|
smem[0][1] G._83
1J(7*b
1;`9BK
1*q>M1
#2000000
0J(7*b
b1 "fUdW
smem[1][0] pD.mP
smem[1][1] !V!em
0;`9BK
b1 '8u?z
0*q>M1
smem[1][0] N\zBe
smem[1][1] c3h8{
#2500000
smem[1][0] 2r3#W
smem[1][1] AbGF%
1J(7*b
1;`9BK
1*q>M1
#3000000
0J(7*b
b10 "fUdW
smem[2][0] pD.mP
smem[2][1] !V!em
0;`9BK
b10 '8u?z
0*q>M1
smem[2][0] N\zBe
smem[2][1] c3h8{
#3500000
smem[2][0] .^<$p
smem[2][1] ?s@Dc
1J(7*b
1;`9BK
1*q>M1
#4000000
0J(7*b
b11 "fUdW
smem[3][0] pD.mP
smem[3][1] !V!em
0;`9BK
b11 '8u?z
0*q>M1
smem[3][0] N\zBe
smem[3][1] c3h8{
#4500000
smem[3][0] {*||o
smem[3][1] Bg,vB
1J(7*b
1;`9BK
1*q>M1
#5000000
0J(7*b
b1 @t0}\
178"T5
s[mem[1][0],\x20mem[1][1]] F&^FN
b0 "fUdW
0r1OK)
s<!!!failed\x20to\x20format!!!> pD.mP
s<!!!failed\x20to\x20format!!!> !V!em
b1 .0()-
1GEbRA
0;`9BK
smem[1][0] _Xe"P
smem[1][1] jXrsx
b0 '8u?z
0~o=`&
0*q>M1
s<!!!failed\x20to\x20format!!!> N\zBe
s<!!!failed\x20to\x20format!!!> c3h8{
#5500000
1J(7*b
1;`9BK
1*q>M1
#6000000
0J(7*b
b1 "fUdW
1r1OK)
0;`9BK
b1 '8u?z
1~o=`&
0*q>M1
#6500000
s<!!!failed\x20to\x20format!!!> 2r3#W
s<!!!failed\x20to\x20format!!!> AbGF%
1J(7*b
s<!!!failed\x20to\x20format!!!> F&^FN
1;`9BK
s<!!!failed\x20to\x20format!!!> _Xe"P
s<!!!failed\x20to\x20format!!!> jXrsx
1*q>M1
#7000000

View file

@ -75,7 +75,7 @@ note: required because it appears within the type `Vec<DynSimOnlyValue>`
note: required because it appears within the type `OpaqueSimValue`
--> src/ty.rs
|
761 | pub struct OpaqueSimValue {
895 | pub struct OpaqueSimValue {
| ^^^^^^^^^^^^^^
note: required because it appears within the type `value::SimValueInner<()>`
--> src/sim/value.rs
@ -214,7 +214,7 @@ note: required because it appears within the type `Vec<DynSimOnlyValue>`
note: required because it appears within the type `OpaqueSimValue`
--> src/ty.rs
|
761 | pub struct OpaqueSimValue {
895 | pub struct OpaqueSimValue {
| ^^^^^^^^^^^^^^
note: required because it appears within the type `value::SimValueInner<()>`
--> src/sim/value.rs
@ -326,7 +326,7 @@ note: required because it appears within the type `Vec<DynSimOnlyValue>`
note: required because it appears within the type `OpaqueSimValue`
--> src/ty.rs
|
761 | pub struct OpaqueSimValue {
895 | pub struct OpaqueSimValue {
| ^^^^^^^^^^^^^^
note: required because it appears within the type `value::SimValueInner<()>`
--> src/sim/value.rs

View file

@ -51,7 +51,8 @@
"Reset": "Visible",
"Clock": "Visible",
"PhantomConst": "Visible",
"DynSimOnly": "Visible"
"DynSimOnly": "Visible",
"TraceAsString": "Visible"
}
},
"Bundle": {
@ -1021,6 +1022,27 @@
"fold_where": "T: Fold<State>",
"visit_where": "T: Visit<State>"
},
"ops::AsTraceAsString": {
"data": {
"$kind": "Struct",
"$constructor": "ops::AsTraceAsString::new",
"inner()": "Visible",
"ty()": "Visible"
},
"generics": "<T: Type>",
"fold_where": "T: Fold<State>",
"visit_where": "T: Visit<State>"
},
"ops::TraceAsStringAsInner": {
"data": {
"$kind": "Struct",
"$constructor": "ops::TraceAsStringAsInner::new",
"arg_typed()": "Visible"
},
"generics": "<T: Type>",
"fold_where": "T: Fold<State>",
"visit_where": "T: Visit<State>"
},
"BlockId": {
"data": {
"$kind": "Opaque"
@ -1283,12 +1305,25 @@
"$kind": "Struct"
}
},
"TargetPathTraceAsStringInner": {
"data": {
"$kind": "Struct"
}
},
"TargetPathAsTraceAsString": {
"data": {
"$kind": "Struct",
"ty": "Visible"
}
},
"TargetPathElement": {
"data": {
"$kind": "Enum",
"BundleField": "Visible",
"ArrayElement": "Visible",
"DynArrayElement": "Visible"
"DynArrayElement": "Visible",
"TraceAsStringInner": "Visible",
"AsTraceAsString": "Visible"
}
},
"PhantomConst": {
@ -1306,6 +1341,14 @@
"data": {
"$kind": "ManualImpl"
}
},
"TraceAsString": {
"data": {
"$kind": "ManualImpl"
},
"generics": "<T: Type>",
"fold_where": "T: Fold<State>",
"visit_where": "T: Visit<State>"
}
}
}