support Rust's default binding modes when destructuring with #[hdl(sim)] let/match
All checks were successful
/ test (pull_request) Successful in 4m47s
/ test (push) Successful in 5m25s

This commit is contained in:
Jacob Lifshay 2025-11-13 23:42:37 -08:00
parent 053c1b2b10
commit 2817cd3d58
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ
6 changed files with 371 additions and 70 deletions

View file

@ -88,6 +88,9 @@ impl Visitor<'_> {
field.expr = parse_quote_spanned! {field.member.span()=>
::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr))
};
field
.colon_token
.get_or_insert(Token![:](field.member.span()));
}
return parse_quote_spanned! {name_span=>
{

View file

@ -10,7 +10,7 @@ use crate::{
};
use proc_macro2::{Span, TokenStream};
use quote::{ToTokens, TokenStreamExt, format_ident, quote_spanned};
use std::collections::BTreeSet;
use std::collections::BTreeMap;
use syn::{
Arm, Attribute, Expr, ExprMatch, FieldPat, Ident, Local, Member, Pat, PatIdent, PatOr,
PatParen, PatPath, PatRest, PatStruct, PatTuple, PatTupleStruct, PatWild, Path, PathSegment,
@ -24,65 +24,65 @@ use syn::{
macro_rules! visit_trait {
(
$($vis:vis fn $fn:ident($state:ident: _, $value:ident: &$Value:ty) $block:block)*
$($vis:vis fn $fn:ident($state:ident: _, $value:ident: &mut $Value:ty) $block:block)*
) => {
trait VisitMatchPat<'a> {
$(fn $fn(&mut self, $value: &'a $Value) {
$(fn $fn(&mut self, $value: &'a mut $Value) {
$fn(self, $value);
})*
}
$($vis fn $fn<'a>($state: &mut (impl ?Sized + VisitMatchPat<'a>), $value: &'a $Value) $block)*
$($vis fn $fn<'a>($state: &mut (impl ?Sized + VisitMatchPat<'a>), $value: &'a mut $Value) $block)*
};
}
visit_trait! {
fn visit_match_pat_binding(_state: _, v: &MatchPatBinding) {
let MatchPatBinding { ident: _ } = v;
fn visit_match_pat_binding(_state: _, v: &mut MatchPatBinding) {
let MatchPatBinding { mutability: _, ident: _ } = v;
}
fn visit_match_pat_wild(_state: _, v: &MatchPatWild) {
fn visit_match_pat_wild(_state: _, v: &mut MatchPatWild) {
let MatchPatWild { underscore_token: _ } = v;
}
fn visit_match_pat_rest(_state: _, v: &MatchPatRest) {
fn visit_match_pat_rest(_state: _, v: &mut MatchPatRest) {
let MatchPatRest { dot2_token: _ } = v;
}
fn visit_match_pat_paren(state: _, v: &MatchPatParen<MatchPat>) {
fn visit_match_pat_paren(state: _, v: &mut MatchPatParen<MatchPat>) {
let MatchPatParen { paren_token: _, pat } = v;
state.visit_match_pat(pat);
}
fn visit_match_pat_paren_simple(state: _, v: &MatchPatParen<MatchPatSimple>) {
fn visit_match_pat_paren_simple(state: _, v: &mut MatchPatParen<MatchPatSimple>) {
let MatchPatParen { paren_token: _, pat } = v;
state.visit_match_pat_simple(pat);
}
fn visit_match_pat_or(state: _, v: &MatchPatOr<MatchPat>) {
fn visit_match_pat_or(state: _, v: &mut MatchPatOr<MatchPat>) {
let MatchPatOr { leading_vert: _, cases } = v;
for v in cases {
state.visit_match_pat(v);
}
}
fn visit_match_pat_or_simple(state: _, v: &MatchPatOr<MatchPatSimple>) {
fn visit_match_pat_or_simple(state: _, v: &mut MatchPatOr<MatchPatSimple>) {
let MatchPatOr { leading_vert: _, cases } = v;
for v in cases {
state.visit_match_pat_simple(v);
}
}
fn visit_match_pat_struct_field(state: _, v: &MatchPatStructField) {
fn visit_match_pat_struct_field(state: _, v: &mut MatchPatStructField) {
let MatchPatStructField { field_name: _, colon_token: _, pat } = v;
state.visit_match_pat_simple(pat);
}
fn visit_match_pat_struct(state: _, v: &MatchPatStruct) {
fn visit_match_pat_struct(state: _, v: &mut MatchPatStruct) {
let MatchPatStruct { match_span: _, path: _, brace_token: _, fields, rest: _ } = v;
for v in fields {
state.visit_match_pat_struct_field(v);
}
}
fn visit_match_pat_tuple(state: _, v: &MatchPatTuple) {
fn visit_match_pat_tuple(state: _, v: &mut MatchPatTuple) {
let MatchPatTuple { paren_token: _, fields } = v;
for v in fields {
state.visit_match_pat_simple(v);
}
}
fn visit_match_pat_enum_variant(state: _, v: &MatchPatEnumVariant) {
fn visit_match_pat_enum_variant(state: _, v: &mut MatchPatEnumVariant) {
let MatchPatEnumVariant {
match_span:_,
sim:_,
@ -95,7 +95,7 @@ visit_trait! {
state.visit_match_pat_simple(v);
}
}
fn visit_match_pat_simple(state: _, v: &MatchPatSimple) {
fn visit_match_pat_simple(state: _, v: &mut MatchPatSimple) {
match v {
MatchPatSimple::Paren(v) => state.visit_match_pat_paren_simple(v),
MatchPatSimple::Or(v) => state.visit_match_pat_or_simple(v),
@ -104,7 +104,7 @@ visit_trait! {
MatchPatSimple::Rest(v) => state.visit_match_pat_rest(v),
}
}
fn visit_match_pat(state: _, v: &MatchPat) {
fn visit_match_pat(state: _, v: &mut MatchPat) {
match v {
MatchPat::Simple(v) => state.visit_match_pat_simple(v),
MatchPat::Or(v) => state.visit_match_pat_or(v),
@ -118,13 +118,15 @@ visit_trait! {
with_debug_clone_and_fold! {
struct MatchPatBinding<> {
mutability: Option<Token![mut]>,
ident: Ident,
}
}
impl ToTokens for MatchPatBinding {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self { ident } = self;
let Self { mutability, ident } = self;
mutability.to_tokens(tokens);
ident.to_tokens(tokens);
}
}
@ -211,12 +213,20 @@ impl ToTokens for MatchPatStructField {
colon_token,
pat,
} = self;
field_name.to_tokens(tokens);
if let (None, MatchPatSimple::Binding(MatchPatBinding { ident })) = (colon_token, pat) {
if let (
None,
MatchPatSimple::Binding(MatchPatBinding {
mutability: _,
ident,
}),
) = (colon_token, pat)
{
if field_name == ident {
pat.to_tokens(tokens);
return;
}
}
field_name.to_tokens(tokens);
colon_token
.unwrap_or_else(|| Token![:](field_name.span()))
.to_tokens(tokens);
@ -450,7 +460,7 @@ trait ParseMatchPat: Sized {
Pat::Ident(PatIdent {
attrs: _,
by_ref,
mutability,
mut mutability,
ident,
subpat,
}) => {
@ -459,10 +469,13 @@ trait ParseMatchPat: Sized {
.errors
.error(by_ref, "ref not allowed in #[hdl] patterns");
}
if let Some(mutability) = mutability {
state
.errors
.error(mutability, "mut not allowed in #[hdl] patterns");
if let Some(mut_token) = mutability {
if state.sim.is_none() {
state
.errors
.error(mut_token, "mut not allowed in #[hdl] patterns");
mutability = None; // avoid duplicate errors
}
}
if let Some((at_token, _)) = subpat {
state
@ -474,18 +487,26 @@ trait ParseMatchPat: Sized {
variant_path,
enum_path,
variant_name,
}) => Self::enum_variant(
state,
MatchPatEnumVariant {
match_span: state.match_span,
sim: state.sim,
variant_path,
enum_path,
variant_name,
field: None,
},
),
}) => {
if let Some(mut_token) = mutability {
state
.errors
.error(mut_token, "mut not allowed on unit variants");
}
Self::enum_variant(
state,
MatchPatEnumVariant {
match_span: state.match_span,
sim: state.sim,
variant_path,
enum_path,
variant_name,
field: None,
},
)
}
Err(ident) => Ok(Self::simple(MatchPatSimple::Binding(MatchPatBinding {
mutability,
ident,
}))),
}
@ -980,15 +1001,16 @@ struct HdlMatchParseState<'a> {
struct HdlLetPatVisitState<'a> {
errors: &'a mut Errors,
bindings: BTreeSet<&'a Ident>,
bindings: BTreeMap<Ident, MatchPatBinding>,
}
impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> {
fn visit_match_pat_binding(&mut self, v: &'a MatchPatBinding) {
self.bindings.insert(&v.ident);
fn visit_match_pat_binding(&mut self, v: &'a mut MatchPatBinding) {
self.bindings.insert(v.ident.clone(), v.clone());
v.mutability = None;
}
fn visit_match_pat_or(&mut self, v: &'a MatchPatOr<MatchPat>) {
fn visit_match_pat_or(&mut self, v: &'a mut MatchPatOr<MatchPat>) {
if let Some(first_inner_vert) = v.first_inner_vert() {
self.errors.error(
first_inner_vert,
@ -998,7 +1020,7 @@ impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> {
visit_match_pat_or(self, v);
}
fn visit_match_pat_or_simple(&mut self, v: &'a MatchPatOr<MatchPatSimple>) {
fn visit_match_pat_or_simple(&mut self, v: &'a mut MatchPatOr<MatchPatSimple>) {
if let Some(first_inner_vert) = v.first_inner_vert() {
self.errors.error(
first_inner_vert,
@ -1008,7 +1030,7 @@ impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> {
visit_match_pat_or_simple(self, v);
}
fn visit_match_pat_enum_variant(&mut self, v: &'a MatchPatEnumVariant) {
fn visit_match_pat_enum_variant(&mut self, v: &'a mut MatchPatEnumVariant) {
self.errors.error(v, "refutable pattern in let statement");
}
}
@ -1048,7 +1070,7 @@ impl Visitor<'_> {
.error(else_, "#[hdl] let ... else { ... } is not implemented");
return empty_let();
}
let Ok(pat) = MatchPat::parse(
let Ok(mut pat) = MatchPat::parse(
&mut HdlMatchParseState {
sim,
match_span: span,
@ -1060,20 +1082,27 @@ impl Visitor<'_> {
};
let mut state = HdlLetPatVisitState {
errors: &mut self.errors,
bindings: BTreeSet::new(),
bindings: BTreeMap::new(),
};
state.visit_match_pat(&pat);
state.visit_match_pat(&mut pat);
let HdlLetPatVisitState {
errors: _,
bindings,
} = state;
let bindings_idents = bindings.keys();
let bindings = bindings.values();
let retval = if sim.is_some() {
parse_quote_spanned! {span=>
let (#(#bindings,)*) = {
type __MatchTy<T> = <T as ::fayalite::ty::Type>::SimValue;
let __match_value = ::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr));
#let_token #pat #eq_token ::fayalite::sim::value::SimValue::into_value(__match_value) #semi_token
(#(#bindings,)*)
let __match_value = #expr;
let __match_value = {
use ::fayalite::sim::value::match_sim_value::*;
// use method syntax to deduce the correct trait to call
::fayalite::sim::value::match_sim_value::MatchSimValueHelper::new(__match_value).__fayalite_match_sim_value()
};
#let_token #pat #eq_token __match_value #semi_token
(#(#bindings_idents,)*)
};
}
} else {
@ -1105,7 +1134,7 @@ impl Visitor<'_> {
__match_variant,
);
#let_token #pat #eq_token __match_variant #semi_token
(#(#bindings,)* __scope,)
(#(#bindings_idents,)* __scope,)
};
}
};
@ -1142,8 +1171,13 @@ impl Visitor<'_> {
quote_spanned! {span=>
{
type __MatchTy<T> = <T as ::fayalite::ty::Type>::SimValue;
let __match_expr = ::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr));
#match_token ::fayalite::sim::value::SimValue::into_value(__match_expr) {
let __match_value = #expr;
let __match_value = {
use ::fayalite::sim::value::match_sim_value::*;
// use method syntax to deduce the correct trait to call
::fayalite::sim::value::match_sim_value::MatchSimValueHelper::new(__match_value).__fayalite_match_sim_value()
};
#match_token __match_value {
#(#arms)*
}
}

View file

@ -31,3 +31,81 @@
//! }
//! }
//! ```
//!
//! You can also use `#[hdl(sim)] let` to destructure [`SimValue`]s (or anything that implements [`ToSimValue`]).
//!
//! [`SimValue`]: crate::sim::value::SimValue
//! [`ToSimValue`]: crate::sim::value::ToSimValue
//!
//! ```
//! # use fayalite::prelude::*;
//! #[hdl]
//! struct MyStruct<T> {
//! a: UInt<8>,
//! b: Bool,
//! c: T,
//! }
//!
//! #[hdl]
//! fn destructure<T: Type>(v: SimValue<MyStruct<T>>) {
//! #[hdl(sim)]
//! let MyStruct::<T> {
//! a,
//! mut b,
//! c,
//! } = v;
//!
//! // that gives these types:
//! let _: SimValue<UInt<8>> = a;
//! let _: SimValue<Bool> = b;
//! let _: SimValue<T> = c;
//! *b = false; // can modify b since mut was used
//! }
//!
//! #[hdl]
//! fn destructure_ref<'a, T: Type>(v: &'a SimValue<MyStruct<T>>) {
//! #[hdl(sim)]
//! let MyStruct::<T> {
//! a,
//! b,
//! c,
//! } = v;
//!
//! // that gives these types:
//! let _: &'a SimValue<UInt<8>> = a;
//! let _: &'a SimValue<Bool> = b;
//! let _: &'a SimValue<T> = c;
//! }
//!
//! #[hdl]
//! fn destructure_mut<'a, T: Type>(v: &'a mut SimValue<MyStruct<T>>) {
//! #[hdl(sim)]
//! let MyStruct::<T> {
//! a,
//! b,
//! c,
//! } = v;
//!
//! **b = true; // you can modify v by modifying b which borrows from it
//!
//! // that gives these types:
//! let _: &'a mut SimValue<UInt<8>> = a;
//! let _: &'a mut SimValue<Bool> = b;
//! let _: &'a mut SimValue<T> = c;
//! }
//!
//! #[hdl]
//! fn destructure_to_sim_value<'a, T: Type>(v: impl ToSimValue<Type = MyStruct<T>>) {
//! #[hdl(sim)]
//! let MyStruct::<T> {
//! a,
//! b,
//! c,
//! } = v;
//!
//! // that gives these types:
//! let _: SimValue<UInt<8>> = a;
//! let _: SimValue<Bool> = b;
//! let _: SimValue<T> = c;
//! }
//! ```

View file

@ -9,3 +9,78 @@
//!
//! `#[hdl] match` statements can only match one level of struct/tuple/enum pattern for now,
//! e.g. you can match with the pattern `HdlSome(v)`, but not `HdlSome(HdlSome(_))`.
//!
//! You can also use `#[hdl(sim)] match` to match [`SimValue`]s (or anything that implements [`ToSimValue`]).
//!
//! `#[hdl(sim)] match` statements' bodies may have any type, unlike `#[hdl] match`.
//!
//! [`SimValue`]: crate::sim::value::SimValue
//! [`ToSimValue`]: crate::sim::value::ToSimValue
//!
//! ```
//! # use fayalite::prelude::*;
//! #[hdl]
//! enum MyEnum<T> {
//! A,
//! B(Bool),
//! C(T),
//! }
//!
//! #[hdl]
//! fn match_move<T: Type>(v: SimValue<MyEnum<T>>) -> String {
//! #[hdl(sim)]
//! match v {
//! MyEnum::<T>::A => String::from("got A"),
//! MyEnum::<T>::B(mut b) => {
//! let _: SimValue<Bool> = b; // b has this type
//! let text = format!("got B({b})");
//! *b = true; // can modify b since mut was used
//! text
//! }
//! _ => String::from("something else"),
//! }
//! }
//!
//! #[hdl]
//! fn match_ref<'a, T: Type>(v: &'a SimValue<MyEnum<T>>) -> u32 {
//! #[hdl(sim)]
//! match v {
//! MyEnum::<T>::A => 1,
//! MyEnum::<T>::B(b) => {
//! let _: &'a SimValue<Bool> = b; // b has this type
//! println!("got B({b})");
//! 5
//! }
//! _ => 42,
//! }
//! }
//!
//! #[hdl]
//! fn match_mut<'a, T: Type>(v: &'a mut SimValue<MyEnum<T>>) -> Option<&'a mut SimValue<T>> {
//! #[hdl(sim)]
//! match v {
//! MyEnum::<T>::A => None,
//! MyEnum::<T>::B(b) => {
//! println!("got B({b})");
//! **b = true; // you can modify v by modifying b which borrows from it
//! let _: &'a mut SimValue<Bool> = b; // b has this type
//! None
//! }
//! MyEnum::<T>::C(v) => Some(v), // you can return matched values
//! _ => None, // HDL enums can have invalid discriminants, so we need this extra match arm
//! }
//! }
//!
//! #[hdl]
//! fn match_to_sim_value<'a, T: Type>(v: impl ToSimValue<Type = MyEnum<T>>) {
//! #[hdl(sim)]
//! match v {
//! MyEnum::<T>::A => println!("got A"),
//! MyEnum::<T>::B(b) => {
//! let _: SimValue<Bool> = b; // b has this type
//! println!("got B({b})");
//! }
//! _ => println!("something else"),
//! }
//! }
//! ```

View file

@ -492,6 +492,117 @@ impl SimValuePartialEq for AsyncReset {
}
}
#[doc(hidden)]
pub mod match_sim_value {
use crate::{
sim::value::{SimValue, ToSimValue},
ty::Type,
};
#[doc(hidden)]
pub struct MatchSimValueHelper<T>(Option<T>);
impl<T> MatchSimValueHelper<T> {
pub fn new(v: T) -> Self {
Self(Some(v))
}
}
#[doc(hidden)]
pub trait MatchSimValue {
type MatchValue;
/// use `self` so it comes first in the method resolution order
fn __fayalite_match_sim_value(self) -> Self::MatchValue
where
Self: Sized;
}
impl<T: Type> MatchSimValue for MatchSimValueHelper<SimValue<T>> {
type MatchValue = T::SimValue;
fn __fayalite_match_sim_value(self) -> Self::MatchValue {
SimValue::into_value(self.0.expect("should be Some"))
}
}
impl<'a, T: Type> MatchSimValue for MatchSimValueHelper<&'a SimValue<T>> {
type MatchValue = &'a T::SimValue;
fn __fayalite_match_sim_value(self) -> Self::MatchValue {
SimValue::value(self.0.expect("should be Some"))
}
}
impl<'a, T: Type> MatchSimValue for MatchSimValueHelper<&'a mut SimValue<T>> {
type MatchValue = &'a mut T::SimValue;
fn __fayalite_match_sim_value(self) -> Self::MatchValue {
SimValue::value_mut(self.0.expect("should be Some"))
}
}
impl<'a, T> MatchSimValue for MatchSimValueHelper<&'_ &'a T>
where
MatchSimValueHelper<&'a T>: MatchSimValue,
{
type MatchValue = <MatchSimValueHelper<&'a T> as MatchSimValue>::MatchValue;
fn __fayalite_match_sim_value(self) -> Self::MatchValue {
MatchSimValue::__fayalite_match_sim_value(MatchSimValueHelper(self.0.map(|v| *v)))
}
}
impl<'a, T> MatchSimValue for MatchSimValueHelper<&'_ mut &'a T>
where
MatchSimValueHelper<&'a T>: MatchSimValue,
{
type MatchValue = <MatchSimValueHelper<&'a T> as MatchSimValue>::MatchValue;
fn __fayalite_match_sim_value(self) -> Self::MatchValue {
MatchSimValue::__fayalite_match_sim_value(MatchSimValueHelper(self.0.map(|v| *v)))
}
}
impl<'a, T> MatchSimValue for MatchSimValueHelper<&'a &'_ mut T>
where
MatchSimValueHelper<&'a T>: MatchSimValue,
{
type MatchValue = <MatchSimValueHelper<&'a T> as MatchSimValue>::MatchValue;
fn __fayalite_match_sim_value(self) -> Self::MatchValue {
MatchSimValue::__fayalite_match_sim_value(MatchSimValueHelper(self.0.map(|v| &**v)))
}
}
impl<'a, T> MatchSimValue for MatchSimValueHelper<&'a mut &'_ mut T>
where
MatchSimValueHelper<&'a mut T>: MatchSimValue,
{
type MatchValue = <MatchSimValueHelper<&'a mut T> as MatchSimValue>::MatchValue;
fn __fayalite_match_sim_value(self) -> Self::MatchValue {
MatchSimValue::__fayalite_match_sim_value(MatchSimValueHelper(self.0.map(|v| &mut **v)))
}
}
#[doc(hidden)]
pub trait MatchSimValueFallback {
type MatchValue;
/// use `&mut self` so it comes later in the method resolution order than MatchSimValue
fn __fayalite_match_sim_value(&mut self) -> Self::MatchValue;
}
impl<T: ToSimValue> MatchSimValueFallback for MatchSimValueHelper<T> {
type MatchValue = <T::Type as Type>::SimValue;
fn __fayalite_match_sim_value(&mut self) -> Self::MatchValue {
SimValue::into_value(self.0.take().expect("should be Some").into_sim_value())
}
}
}
pub trait ToSimValue: ToSimValueWithType<<Self as ToSimValue>::Type> {
type Type: Type;
@ -624,7 +735,7 @@ impl<T: Type> ToSimValueWithType<T> for BitSlice {
}
}
impl<This: ?Sized + ToSimValue> ToSimValue for &'_ This {
impl<'a, This: ?Sized + ToSimValue> ToSimValue for &'a This {
type Type = This::Type;
fn to_sim_value(&self) -> SimValue<Self::Type> {

View file

@ -506,10 +506,10 @@ fn test_enums() {
data_out: _,
b_out: _,
b2_out: _,
} = expected;
sim.write(sim.io().en, &en);
sim.write(sim.io().which_in, &which_in);
sim.write(sim.io().data_in, &data_in);
} = &expected;
sim.write(sim.io().en, en);
sim.write(sim.io().which_in, which_in);
sim.write(sim.io().data_in, data_in);
let io = #[hdl(sim)]
IO::<_> {
en,
@ -528,7 +528,7 @@ fn test_enums() {
);
// make sure matching on SimValue<SomeEnum> works
#[hdl(sim)]
match io.b_out {
match &io.b_out {
HdlNone => println!("io.b_out is HdlNone"),
HdlSome(v) => println!("io.b_out is HdlSome(({:?}, {:?}))", *v.0, *v.1),
}
@ -706,13 +706,13 @@ fn test_memories() {
w_en,
w_data,
w_mask,
} = expected;
sim.write(sim.io().r.addr, &r_addr);
sim.write(sim.io().r.en, &r_en);
sim.write(sim.io().w.addr, &w_addr);
sim.write(sim.io().w.en, &w_en);
sim.write(sim.io().w.data, &w_data);
sim.write(sim.io().w.mask, &w_mask);
} = &expected;
sim.write(sim.io().r.addr, r_addr);
sim.write(sim.io().r.en, r_en);
sim.write(sim.io().w.addr, w_addr);
sim.write(sim.io().w.en, w_en);
sim.write(sim.io().w.data, w_data);
sim.write(sim.io().w.mask, w_mask);
let io = #[hdl(sim)]
IO {
r_addr,
@ -1505,7 +1505,7 @@ fn test_many_memories() {
w_en,
w_data,
w_mask,
} = expected;
} = &expected;
for (((r, w), w_data), w_mask) in sim
.io()
.r
@ -1514,10 +1514,10 @@ fn test_many_memories() {
.zip(w_data.iter())
.zip(w_mask.iter())
{
sim.write(r.addr, &r_addr);
sim.write(r.en, &r_en);
sim.write(w.addr, &w_addr);
sim.write(w.en, &w_en);
sim.write(r.addr, r_addr);
sim.write(r.en, r_en);
sim.write(w.addr, w_addr);
sim.write(w.en, w_en);
sim.write(w.data, w_data);
sim.write(w.mask, w_mask);
}