diff --git a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs index d881ecd..538c2da 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs @@ -674,23 +674,24 @@ impl ToTokens for ParsedBundle { } }, )); - let sim_value_from_bits_fields = Vec::from_iter(fields.named().into_iter().map(|field| { - let ident: &Ident = field.ident().as_ref().unwrap(); - quote_spanned! {span=> - #ident: v.field_from_bits(), - } - })); - let sim_value_clone_from_bits_fields = + let sim_value_from_opaque_fields = Vec::from_iter(fields.named().into_iter().map(|field| { let ident: &Ident = field.ident().as_ref().unwrap(); quote_spanned! {span=> - v.field_clone_from_bits(&mut value.#ident); + #ident: v.field_from_opaque(), } })); - let sim_value_to_bits_fields = Vec::from_iter(fields.named().into_iter().map(|field| { + let sim_value_clone_from_opaque_fields = + Vec::from_iter(fields.named().into_iter().map(|field| { + let ident: &Ident = field.ident().as_ref().unwrap(); + quote_spanned! {span=> + v.field_clone_from_opaque(&mut value.#ident); + } + })); + let sim_value_to_opaque_fields = Vec::from_iter(fields.named().into_iter().map(|field| { let ident: &Ident = field.ident().as_ref().unwrap(); quote_spanned! {span=> - v.field_to_bits(&value.#ident); + v.field(&value.#ident); } })); let to_sim_value_fields = Vec::from_iter(fields.named().into_iter().map(|field| { @@ -745,33 +746,34 @@ impl ToTokens for ParsedBundle { fn source_location() -> ::fayalite::source_location::SourceLocation { ::fayalite::source_location::SourceLocation::caller() } - fn sim_value_from_bits( + fn sim_value_from_opaque( &self, - bits: &::fayalite::bitvec::slice::BitSlice, + opaque: ::fayalite::ty::OpaqueSimValueSlice<'_>, ) -> ::SimValue { #![allow(unused_mut, unused_variables)] - let mut v = ::fayalite::bundle::BundleSimValueFromBits::new(*self, bits); + let mut v = ::fayalite::bundle::BundleSimValueFromOpaque::new(*self, opaque); #mask_type_sim_value_ident { - #(#sim_value_from_bits_fields)* + #(#sim_value_from_opaque_fields)* } } - fn sim_value_clone_from_bits( + fn sim_value_clone_from_opaque( &self, value: &mut ::SimValue, - bits: &::fayalite::bitvec::slice::BitSlice, + opaque: ::fayalite::ty::OpaqueSimValueSlice<'_>, ) { #![allow(unused_mut, unused_variables)] - let mut v = ::fayalite::bundle::BundleSimValueFromBits::new(*self, bits); - #(#sim_value_clone_from_bits_fields)* + let mut v = ::fayalite::bundle::BundleSimValueFromOpaque::new(*self, opaque); + #(#sim_value_clone_from_opaque_fields)* } - fn sim_value_to_bits( + fn sim_value_to_opaque<'__w>( &self, value: &::SimValue, - bits: &mut ::fayalite::bitvec::slice::BitSlice, - ) { + writer: ::fayalite::ty::OpaqueSimValueWriter<'__w>, + ) -> ::fayalite::ty::OpaqueSimValueWritten<'__w> { #![allow(unused_mut, unused_variables)] - let mut v = ::fayalite::bundle::BundleSimValueToBits::new(*self, bits); - #(#sim_value_to_bits_fields)* + let mut v = ::fayalite::bundle::BundleSimValueToOpaque::new(*self, writer); + #(#sim_value_to_opaque_fields)* + v.finish() } } #[automatically_derived] @@ -894,33 +896,34 @@ impl ToTokens for ParsedBundle { fn source_location() -> ::fayalite::source_location::SourceLocation { ::fayalite::source_location::SourceLocation::caller() } - fn sim_value_from_bits( + fn sim_value_from_opaque( &self, - bits: &::fayalite::bitvec::slice::BitSlice, + opaque: ::fayalite::ty::OpaqueSimValueSlice<'_>, ) -> ::SimValue { #![allow(unused_mut, unused_variables)] - let mut v = ::fayalite::bundle::BundleSimValueFromBits::new(*self, bits); + let mut v = ::fayalite::bundle::BundleSimValueFromOpaque::new(*self, opaque); #sim_value_ident { - #(#sim_value_from_bits_fields)* + #(#sim_value_from_opaque_fields)* } } - fn sim_value_clone_from_bits( + fn sim_value_clone_from_opaque( &self, value: &mut ::SimValue, - bits: &::fayalite::bitvec::slice::BitSlice, + opaque: ::fayalite::ty::OpaqueSimValueSlice<'_>, ) { #![allow(unused_mut, unused_variables)] - let mut v = ::fayalite::bundle::BundleSimValueFromBits::new(*self, bits); - #(#sim_value_clone_from_bits_fields)* + let mut v = ::fayalite::bundle::BundleSimValueFromOpaque::new(*self, opaque); + #(#sim_value_clone_from_opaque_fields)* } - fn sim_value_to_bits( + fn sim_value_to_opaque<'__w>( &self, value: &::SimValue, - bits: &mut ::fayalite::bitvec::slice::BitSlice, - ) { + writer: ::fayalite::ty::OpaqueSimValueWriter<'__w>, + ) -> ::fayalite::ty::OpaqueSimValueWritten<'__w> { #![allow(unused_mut, unused_variables)] - let mut v = ::fayalite::bundle::BundleSimValueToBits::new(*self, bits); - #(#sim_value_to_bits_fields)* + let mut v = ::fayalite::bundle::BundleSimValueToOpaque::new(*self, writer); + #(#sim_value_to_opaque_fields)* + v.finish() } } #[automatically_derived] diff --git a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs index a891f5c..e5cbe27 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs @@ -701,18 +701,18 @@ impl ToTokens for ParsedEnum { } }, )); - let sim_value_from_bits_unknown_match_arm = if let Some(sim_value_unknown_variant_name) = + let sim_value_from_opaque_unknown_match_arm = if let Some(sim_value_unknown_variant_name) = &sim_value_unknown_variant_name { quote_spanned! {span=> - _ => #sim_value_ident::#sim_value_unknown_variant_name(v.unknown_variant_from_bits()), + _ => #sim_value_ident::#sim_value_unknown_variant_name(v.unknown_variant_from_opaque()), } } else { quote_spanned! {span=> _ => ::fayalite::__std::unreachable!(), } }; - let sim_value_from_bits_match_arms = Vec::from_iter( + let sim_value_from_opaque_match_arms = Vec::from_iter( variants .iter() .enumerate() @@ -729,29 +729,29 @@ impl ToTokens for ParsedEnum { if let Some(_) = field { quote_spanned! {span=> #index => { - let (field, padding) = v.variant_with_field_from_bits(); + let (field, padding) = v.variant_with_field_from_opaque(); #sim_value_ident::#ident(field, padding) } } } else { quote_spanned! {span=> #index => #sim_value_ident::#ident( - v.variant_no_field_from_bits(), + v.variant_no_field_from_opaque(), ), } } }, ) - .chain([sim_value_from_bits_unknown_match_arm]), + .chain([sim_value_from_opaque_unknown_match_arm]), ); - let sim_value_clone_from_bits_unknown_match_arm = + let sim_value_clone_from_opaque_unknown_match_arm = if let Some(sim_value_unknown_variant_name) = &sim_value_unknown_variant_name { quote_spanned! {span=> _ => if let #sim_value_ident::#sim_value_unknown_variant_name(value) = value { - v.unknown_variant_clone_from_bits(value); + v.unknown_variant_clone_from_opaque(value); } else { *value = #sim_value_ident::#sim_value_unknown_variant_name( - v.unknown_variant_from_bits(), + v.unknown_variant_from_opaque(), ); }, } @@ -760,7 +760,7 @@ impl ToTokens for ParsedEnum { _ => ::fayalite::__std::unreachable!(), } }; - let sim_value_clone_from_bits_match_arms = Vec::from_iter( + let sim_value_clone_from_opaque_match_arms = Vec::from_iter( variants .iter() .enumerate() @@ -777,28 +777,28 @@ impl ToTokens for ParsedEnum { if let Some(_) = field { quote_spanned! {span=> #index => if let #sim_value_ident::#ident(field, padding) = value { - v.variant_with_field_clone_from_bits(field, padding); + v.variant_with_field_clone_from_opaque(field, padding); } else { - let (field, padding) = v.variant_with_field_from_bits(); + let (field, padding) = v.variant_with_field_from_opaque(); *value = #sim_value_ident::#ident(field, padding); }, } } else { quote_spanned! {span=> #index => if let #sim_value_ident::#ident(padding) = value { - v.variant_no_field_clone_from_bits(padding); + v.variant_no_field_clone_from_opaque(padding); } else { *value = #sim_value_ident::#ident( - v.variant_no_field_from_bits(), + v.variant_no_field_from_opaque(), ); }, } } }, ) - .chain([sim_value_clone_from_bits_unknown_match_arm]), + .chain([sim_value_clone_from_opaque_unknown_match_arm]), ); - let sim_value_to_bits_match_arms = Vec::from_iter( + let sim_value_to_opaque_match_arms = Vec::from_iter( variants .iter() .enumerate() @@ -815,13 +815,13 @@ impl ToTokens for ParsedEnum { if let Some(_) = field { quote_spanned! {span=> #sim_value_ident::#ident(field, padding) => { - v.variant_with_field_to_bits(#index, field, padding); + v.variant_with_field_to_opaque(#index, field, padding) } } } else { quote_spanned! {span=> #sim_value_ident::#ident(padding) => { - v.variant_no_field_to_bits(#index, padding); + v.variant_no_field_to_opaque(#index, padding) } } } @@ -831,7 +831,7 @@ impl ToTokens for ParsedEnum { |sim_value_unknown_variant_name| { quote_spanned! {span=> #sim_value_ident::#sim_value_unknown_variant_name(value) => { - v.unknown_variant_to_bits(value); + v.unknown_variant_to_opaque(value) } } }, @@ -878,33 +878,33 @@ impl ToTokens for ParsedEnum { fn source_location() -> ::fayalite::source_location::SourceLocation { ::fayalite::source_location::SourceLocation::caller() } - fn sim_value_from_bits( + fn sim_value_from_opaque( &self, - bits: &::fayalite::bitvec::slice::BitSlice, + opaque: ::fayalite::ty::OpaqueSimValueSlice<'_>, ) -> ::SimValue { - let v = ::fayalite::enum_::EnumSimValueFromBits::new(*self, bits); + let v = ::fayalite::enum_::EnumSimValueFromOpaque::new(*self, opaque); match v.discriminant() { - #(#sim_value_from_bits_match_arms)* + #(#sim_value_from_opaque_match_arms)* } } - fn sim_value_clone_from_bits( + fn sim_value_clone_from_opaque( &self, value: &mut ::SimValue, - bits: &::fayalite::bitvec::slice::BitSlice, + opaque: ::fayalite::ty::OpaqueSimValueSlice<'_>, ) { - let v = ::fayalite::enum_::EnumSimValueFromBits::new(*self, bits); + let v = ::fayalite::enum_::EnumSimValueFromOpaque::new(*self, opaque); match v.discriminant() { - #(#sim_value_clone_from_bits_match_arms)* + #(#sim_value_clone_from_opaque_match_arms)* } } - fn sim_value_to_bits( + fn sim_value_to_opaque<'__w>( &self, value: &::SimValue, - bits: &mut ::fayalite::bitvec::slice::BitSlice, - ) { - let v = ::fayalite::enum_::EnumSimValueToBits::new(*self, bits); + writer: ::fayalite::ty::OpaqueSimValueWriter<'__w>, + ) -> ::fayalite::ty::OpaqueSimValueWritten<'__w> { + let v = ::fayalite::enum_::EnumSimValueToOpaque::new(*self, writer); match value { - #(#sim_value_to_bits_match_arms)* + #(#sim_value_to_opaque_match_arms)* } } } diff --git a/crates/fayalite/src/array.rs b/crates/fayalite/src/array.rs index c953aea..569f2e2 100644 --- a/crates/fayalite/src/array.rs +++ b/crates/fayalite/src/array.rs @@ -12,12 +12,12 @@ use crate::{ sim::value::{SimValue, SimValuePartialEq}, source_location::SourceLocation, ty::{ - CanonicalType, MatchVariantWithoutScope, StaticType, Type, TypeProperties, TypeWithDeref, + CanonicalType, MatchVariantWithoutScope, OpaqueSimValueSlice, OpaqueSimValueWriter, + OpaqueSimValueWritten, StaticType, Type, TypeProperties, TypeWithDeref, serde_impls::SerdeCanonicalType, }, util::ConstUsize, }; -use bitvec::slice::BitSlice; use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error}; use std::{iter::FusedIterator, ops::Index}; @@ -48,15 +48,20 @@ impl ArrayType { is_storable, is_castable_from_bits, bit_width, + sim_only_values_len, } = element; let Some(bit_width) = bit_width.checked_mul(len) else { panic!("array too big"); }; + let Some(sim_only_values_len) = sim_only_values_len.checked_mul(len) else { + panic!("array too big"); + }; TypeProperties { is_passive, is_storable, is_castable_from_bits, bit_width, + sim_only_values_len, } } pub fn new(element: T, len: Len::SizeType) -> Self { @@ -194,42 +199,51 @@ impl Type for ArrayType { SourceLocation::builtin() } - fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { - assert_eq!(bits.len(), self.type_properties.bit_width); - let element = self.element(); - let element_bit_width = element.canonical().bit_width(); - TryFrom::try_from(Vec::from_iter((0..self.len()).map(|i| { - SimValue::from_bitslice(element, &bits[i * element_bit_width..][..element_bit_width]) - }))) - .ok() - .expect("used correct length") + fn sim_value_from_opaque(&self, mut opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { + let element_ty = self.element(); + let element_size = element_ty.canonical().size(); + let mut value = Vec::with_capacity(self.len()); + for _ in 0..self.len() { + let (element_opaque, rest) = opaque.split_at(element_size); + value.push(SimValue::from_opaque(element_ty, element_opaque.to_owned())); + opaque = rest; + } + value.try_into().ok().expect("used correct length") } - fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { - assert_eq!(bits.len(), self.type_properties.bit_width); + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + mut opaque: OpaqueSimValueSlice<'_>, + ) { let element_ty = self.element(); - let element_bit_width = element_ty.canonical().bit_width(); - let value: &mut [SimValue] = value.as_mut(); + let element_size = element_ty.canonical().size(); + let value = AsMut::<[SimValue]>::as_mut(value); assert_eq!(self.len(), value.len()); - for (i, element_value) in value.iter_mut().enumerate() { + for element_value in value { assert_eq!(SimValue::ty(element_value), element_ty); - SimValue::bits_mut(element_value) - .bits_mut() - .copy_from_bitslice(&bits[i * element_bit_width..][..element_bit_width]); + let (element_opaque, rest) = opaque.split_at(element_size); + SimValue::opaque_mut(element_value).clone_from_slice(element_opaque); + opaque = rest; } } - fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { - assert_eq!(bits.len(), self.type_properties.bit_width); + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + mut writer: OpaqueSimValueWriter<'w>, + ) -> OpaqueSimValueWritten<'w> { let element_ty = self.element(); - let element_bit_width = element_ty.canonical().bit_width(); - let value: &[SimValue] = value.as_ref(); + let element_size = element_ty.canonical().size(); + let value = AsRef::<[SimValue]>::as_ref(value); assert_eq!(self.len(), value.len()); - for (i, element_value) in value.iter().enumerate() { + for element_value in value { assert_eq!(SimValue::ty(element_value), element_ty); - bits[i * element_bit_width..][..element_bit_width] - .copy_from_bitslice(SimValue::bits(element_value).bits()); + writer.fill_prefix_with(element_size, |writer| { + writer.fill_cloned_from_slice(SimValue::opaque(element_value).as_slice()) + }); } + writer.fill_cloned_from_slice(OpaqueSimValueSlice::empty()) } } diff --git a/crates/fayalite/src/bundle.rs b/crates/fayalite/src/bundle.rs index 30a70d5..55843ea 100644 --- a/crates/fayalite/src/bundle.rs +++ b/crates/fayalite/src/bundle.rs @@ -11,12 +11,12 @@ use crate::{ sim::value::{SimValue, SimValuePartialEq, ToSimValue, ToSimValueWithType}, source_location::SourceLocation, ty::{ - CanonicalType, MatchVariantWithoutScope, OpaqueSimValue, StaticType, Type, TypeProperties, - TypeWithDeref, impl_match_variant_as_self, + CanonicalType, MatchVariantWithoutScope, OpaqueSimValue, OpaqueSimValueSize, + OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, StaticType, Type, + TypeProperties, TypeWithDeref, impl_match_variant_as_self, }, util::HashMap, }; -use bitvec::{slice::BitSlice, vec::BitVec}; use serde::{Deserialize, Serialize}; use std::{fmt, marker::PhantomData}; @@ -69,7 +69,7 @@ impl fmt::Display for FmtDebugInStruct { struct BundleImpl { fields: Interned<[BundleField]>, name_indexes: HashMap, usize>, - field_offsets: Interned<[usize]>, + field_offsets: Interned<[OpaqueSimValueSize]>, type_properties: TypeProperties, } @@ -89,12 +89,9 @@ impl std::fmt::Debug for BundleImpl { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str("Bundle ")?; f.debug_set() - .entries( - self.fields - .iter() - .enumerate() - .map(|(index, field)| field.fmt_debug_in_struct(self.field_offsets[index])), - ) + .entries(self.fields.iter().enumerate().map(|(index, field)| { + field.fmt_debug_in_struct(self.field_offsets[index].bit_width) + })) .finish() } } @@ -119,6 +116,7 @@ impl BundleTypePropertiesBuilder { is_storable: true, is_castable_from_bits: true, bit_width: 0, + sim_only_values_len: 0, }) } pub const fn clone(&self) -> Self { @@ -126,8 +124,12 @@ impl BundleTypePropertiesBuilder { } #[must_use] pub const fn field(self, flipped: bool, field_props: TypeProperties) -> Self { - let Some(bit_width) = self.0.bit_width.checked_add(field_props.bit_width) else { - panic!("bundle is too big: bit-width overflowed"); + let Some(OpaqueSimValueSize { + bit_width, + sim_only_values_len, + }) = self.0.size().checked_add(field_props.size()) + else { + panic!("bundle is too big: size overflowed"); }; if flipped { Self(TypeProperties { @@ -135,6 +137,7 @@ impl BundleTypePropertiesBuilder { is_storable: false, is_castable_from_bits: false, bit_width, + sim_only_values_len, }) } else { Self(TypeProperties { @@ -143,6 +146,7 @@ impl BundleTypePropertiesBuilder { is_castable_from_bits: self.0.is_castable_from_bits & field_props.is_castable_from_bits, bit_width, + sim_only_values_len, }) } } @@ -167,7 +171,7 @@ impl Bundle { if let Some(old_index) = name_indexes.insert(name, index) { panic!("duplicate field name {name:?}: at both index {old_index} and {index}"); } - field_offsets.push(type_props_builder.0.bit_width); + field_offsets.push(type_props_builder.0.size()); type_props_builder = type_props_builder.field(flipped, ty.type_properties()); } Self(Intern::intern_sized(BundleImpl { @@ -183,7 +187,7 @@ impl Bundle { pub fn field_by_name(&self, name: Interned) -> Option { Some(self.0.fields[*self.0.name_indexes.get(&name)?]) } - pub fn field_offsets(self) -> Interned<[usize]> { + pub fn field_offsets(self) -> Interned<[OpaqueSimValueSize]> { self.0.field_offsets } pub fn type_properties(self) -> TypeProperties { @@ -241,19 +245,27 @@ impl Type for Bundle { fn source_location() -> SourceLocation { SourceLocation::builtin() } - fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { - assert_eq!(bits.len(), self.type_properties().bit_width); - OpaqueSimValue::from_bitslice(bits) + fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { + assert_eq!(self.type_properties().size(), opaque.size()); + opaque.to_owned() } - fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { - assert_eq!(bits.len(), self.type_properties().bit_width); - assert_eq!(value.bit_width(), self.type_properties().bit_width); - value.bits_mut().bits_mut().copy_from_bitslice(bits); + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ) { + assert_eq!(self.type_properties().size(), opaque.size()); + assert_eq!(value.size(), opaque.size()); + value.clone_from_slice(opaque); } - fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { - assert_eq!(bits.len(), self.type_properties().bit_width); - assert_eq!(value.bit_width(), self.type_properties().bit_width); - bits.copy_from_bitslice(value.bits().bits()); + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> OpaqueSimValueWritten<'w> { + assert_eq!(self.type_properties().size(), writer.size()); + assert_eq!(value.size(), writer.size()); + writer.fill_cloned_from_slice(value.as_slice()) } } @@ -263,29 +275,29 @@ pub trait BundleType: Type { fn fields(&self) -> Interned<[BundleField]>; } -pub struct BundleSimValueFromBits<'a> { +pub struct BundleSimValueFromOpaque<'a> { fields: std::slice::Iter<'static, BundleField>, - bits: &'a BitSlice, + opaque: OpaqueSimValueSlice<'a>, } -impl<'a> BundleSimValueFromBits<'a> { +impl<'a> BundleSimValueFromOpaque<'a> { #[track_caller] - pub fn new(bundle_ty: T, bits: &'a BitSlice) -> Self { + pub fn new(bundle_ty: T, opaque: OpaqueSimValueSlice<'a>) -> Self { let fields = bundle_ty.fields(); assert_eq!( - bits.len(), + opaque.size(), fields .iter() - .map(|BundleField { ty, .. }| ty.bit_width()) - .sum::() + .map(|BundleField { ty, .. }| ty.size()) + .sum::() ); Self { fields: Interned::into_inner(fields).iter(), - bits, + opaque, } } #[track_caller] - fn field_ty_and_bits(&mut self) -> (T, &'a BitSlice) { + fn field_ty_and_opaque(&mut self) -> (T, OpaqueSimValueSlice<'a>) { let Some(&BundleField { name: _, flipped: _, @@ -294,59 +306,68 @@ impl<'a> BundleSimValueFromBits<'a> { else { panic!("tried to read too many fields from BundleSimValueFromBits"); }; - let (field_bits, rest) = self.bits.split_at(ty.bit_width()); - self.bits = rest; - (T::from_canonical(ty), field_bits) + let (field_opaque, rest) = self.opaque.split_at(ty.size()); + self.opaque = rest; + (T::from_canonical(ty), field_opaque) } #[track_caller] - pub fn field_from_bits(&mut self) -> SimValue { - let (field_ty, field_bits) = self.field_ty_and_bits::(); - SimValue::from_bitslice(field_ty, field_bits) + pub fn field_from_opaque(&mut self) -> SimValue { + let (field_ty, field_opaque) = self.field_ty_and_opaque::(); + SimValue::from_opaque(field_ty, field_opaque.to_owned()) } #[track_caller] - pub fn field_clone_from_bits(&mut self, field_value: &mut SimValue) { - let (field_ty, field_bits) = self.field_ty_and_bits::(); + pub fn field_clone_from_opaque(&mut self, field_value: &mut SimValue) { + let (field_ty, field_opaque) = self.field_ty_and_opaque::(); assert_eq!(field_ty, SimValue::ty(field_value)); - SimValue::bits_mut(field_value) - .bits_mut() - .copy_from_bitslice(field_bits); + SimValue::opaque_mut(field_value).clone_from_slice(field_opaque); } } -pub struct BundleSimValueToBits<'a> { +pub struct BundleSimValueToOpaque<'a> { fields: std::slice::Iter<'static, BundleField>, - bits: &'a mut BitSlice, + writer: OpaqueSimValueWriter<'a>, } -impl<'a> BundleSimValueToBits<'a> { +impl<'a> BundleSimValueToOpaque<'a> { #[track_caller] - pub fn new(bundle_ty: T, bits: &'a mut BitSlice) -> Self { + pub fn new(bundle_ty: T, writer: OpaqueSimValueWriter<'a>) -> Self { let fields = bundle_ty.fields(); assert_eq!( - bits.len(), + writer.size(), fields .iter() - .map(|BundleField { ty, .. }| ty.bit_width()) - .sum::() + .map(|BundleField { ty, .. }| ty.size()) + .sum::() ); Self { fields: Interned::into_inner(fields).iter(), - bits, + writer, } } #[track_caller] - pub fn field_to_bits(&mut self, field_value: &SimValue) { + pub fn field(&mut self, field_value: &SimValue) { let Some(&BundleField { name: _, flipped: _, ty, }) = self.fields.next() else { - panic!("tried to read too many fields from BundleSimValueFromBits"); + panic!("tried to write too many fields with BundleSimValueToOpaque"); }; assert_eq!(T::from_canonical(ty), SimValue::ty(field_value)); - self.bits[..ty.bit_width()].copy_from_bitslice(SimValue::bits(field_value).bits()); - self.bits = &mut std::mem::take(&mut self.bits)[ty.bit_width()..]; + self.writer.fill_prefix_with(ty.size(), |writer| { + writer.fill_cloned_from_slice(SimValue::opaque(field_value).as_slice()) + }); + } + #[track_caller] + pub fn finish(mut self) -> OpaqueSimValueWritten<'a> { + assert_eq!( + self.fields.next(), + None, + "wrote too few fields with BundleSimValueToOpaque" + ); + self.writer + .fill_cloned_from_slice(OpaqueSimValueSlice::empty()) } } @@ -495,23 +516,32 @@ macro_rules! impl_tuples { fn source_location() -> SourceLocation { SourceLocation::builtin() } - fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { + fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { #![allow(unused_mut, unused_variables)] - let mut v = BundleSimValueFromBits::new(*self, bits); - $(let $var = v.field_from_bits();)* + let mut v = BundleSimValueFromOpaque::new(*self, opaque); + $(let $var = v.field_from_opaque();)* ($($var,)*) } - fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ) { #![allow(unused_mut, unused_variables)] - let mut v = BundleSimValueFromBits::new(*self, bits); + let mut v = BundleSimValueFromOpaque::new(*self, opaque); let ($($var,)*) = value; - $(v.field_clone_from_bits($var);)* + $(v.field_clone_from_opaque($var);)* } - fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> OpaqueSimValueWritten<'w> { #![allow(unused_mut, unused_variables)] - let mut v = BundleSimValueToBits::new(*self, bits); + let mut v = BundleSimValueToOpaque::new(*self, writer); let ($($var,)*) = value; - $(v.field_to_bits($var);)* + $(v.field($var);)* + v.finish() } } impl<$($T: Type,)*> BundleType for ($($T,)*) { @@ -592,12 +622,12 @@ macro_rules! impl_tuples { let [$($ty_var,)*] = *ty.fields() else { panic!("bundle has wrong number of fields"); }; - let mut bits = BitVec::new(); + let mut opaque = OpaqueSimValue::empty(); $(let $var = $var.into_sim_value_with_type($ty_var.ty); assert_eq!(SimValue::ty(&$var), $ty_var.ty); - bits.extend_from_bitslice(SimValue::bits(&$var).bits()); + opaque.extend_from_slice(SimValue::opaque(&$var).as_slice()); )* - bits.into_sim_value_with_type(ty) + SimValue::from_opaque(ty, opaque) } } impl<$($T: ToSimValueWithType<$Ty>, $Ty: Type,)*> ToSimValueWithType<($($Ty,)*)> for ($($T,)*) { @@ -723,15 +753,23 @@ impl Type for PhantomData { fn source_location() -> SourceLocation { SourceLocation::builtin() } - fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { - assert!(bits.is_empty()); + fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { + assert!(opaque.is_empty()); *self } - fn sim_value_clone_from_bits(&self, _value: &mut Self::SimValue, bits: &BitSlice) { - assert!(bits.is_empty()); + fn sim_value_clone_from_opaque( + &self, + _value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ) { + assert!(opaque.is_empty()); } - fn sim_value_to_bits(&self, _value: &Self::SimValue, bits: &mut BitSlice) { - assert!(bits.is_empty()); + fn sim_value_to_opaque<'w>( + &self, + _value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> OpaqueSimValueWritten<'w> { + writer.fill_cloned_from_slice(OpaqueSimValueSlice::empty()) } } @@ -800,18 +838,15 @@ impl ToSimValueWithType for PhantomData { #[track_caller] fn to_sim_value_with_type(&self, ty: Bundle) -> SimValue { assert!(ty.fields().is_empty()); - ToSimValueWithType::into_sim_value_with_type(BitVec::new(), ty) + SimValue::from_opaque(ty, OpaqueSimValue::empty()) } } impl ToSimValueWithType for PhantomData { #[track_caller] - fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue { - let ty = Bundle::from_canonical(ty); + fn to_sim_value_with_type(&self, canonical_ty: CanonicalType) -> SimValue { + let ty = Bundle::from_canonical(canonical_ty); assert!(ty.fields().is_empty()); - SimValue::into_canonical(ToSimValueWithType::into_sim_value_with_type( - BitVec::new(), - ty, - )) + SimValue::from_opaque(canonical_ty, OpaqueSimValue::empty()) } } diff --git a/crates/fayalite/src/clock.rs b/crates/fayalite/src/clock.rs index 66b0e20..909edbd 100644 --- a/crates/fayalite/src/clock.rs +++ b/crates/fayalite/src/clock.rs @@ -6,9 +6,12 @@ use crate::{ int::Bool, reset::{Reset, ResetType}, source_location::SourceLocation, - ty::{CanonicalType, StaticType, Type, TypeProperties, impl_match_variant_as_self}, + ty::{ + CanonicalType, OpaqueSimValueSize, OpaqueSimValueSlice, OpaqueSimValueWriter, + OpaqueSimValueWritten, StaticType, Type, TypeProperties, impl_match_variant_as_self, + }, }; -use bitvec::slice::BitSlice; +use bitvec::{bits, order::Lsb0}; #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)] pub struct Clock; @@ -39,19 +42,29 @@ impl Type for Clock { retval } - fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { - assert_eq!(bits.len(), 1); - bits[0] + fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { + assert_eq!(opaque.size(), OpaqueSimValueSize::from_bit_width(1)); + opaque.bits()[0] } - fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { - assert_eq!(bits.len(), 1); - *value = bits[0]; + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ) { + assert_eq!(opaque.size(), OpaqueSimValueSize::from_bit_width(1)); + *value = opaque.bits()[0]; } - fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { - assert_eq!(bits.len(), 1); - bits.set(0, *value); + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> OpaqueSimValueWritten<'w> { + assert_eq!(writer.size(), OpaqueSimValueSize::from_bit_width(1)); + writer.fill_cloned_from_slice(OpaqueSimValueSlice::from_bitslice( + [bits![0], bits![1]][*value as usize], + )) } } @@ -72,6 +85,7 @@ impl StaticType for Clock { is_storable: false, is_castable_from_bits: true, bit_width: 1, + sim_only_values_len: 0, }; const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES; } diff --git a/crates/fayalite/src/enum_.rs b/crates/fayalite/src/enum_.rs index 283e4ff..5fbac9f 100644 --- a/crates/fayalite/src/enum_.rs +++ b/crates/fayalite/src/enum_.rs @@ -16,7 +16,8 @@ use crate::{ sim::value::{SimValue, SimValuePartialEq}, source_location::SourceLocation, ty::{ - CanonicalType, MatchVariantAndInactiveScope, OpaqueSimValue, StaticType, Type, + CanonicalType, MatchVariantAndInactiveScope, OpaqueSimValue, OpaqueSimValueSize, + OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, StaticType, Type, TypeProperties, }, util::HashMap, @@ -120,6 +121,7 @@ impl EnumTypePropertiesBuilder { is_storable: true, is_castable_from_bits: true, bit_width: 0, + sim_only_values_len: 0, }, variant_count: 0, } @@ -138,9 +140,14 @@ impl EnumTypePropertiesBuilder { is_storable, is_castable_from_bits, bit_width, + sim_only_values_len, }) = field_props { assert!(is_passive, "variant type must be a passive type"); + assert!( + sim_only_values_len == 0, + "can't have `SimOnlyValue`s in an Enum" + ); type_properties = TypeProperties { is_passive: true, is_storable: type_properties.is_storable & is_storable, @@ -151,6 +158,7 @@ impl EnumTypePropertiesBuilder { } else { type_properties.bit_width }, + sim_only_values_len: 0, }; } Self { @@ -381,19 +389,27 @@ impl Type for Enum { fn source_location() -> SourceLocation { SourceLocation::builtin() } - fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { - assert_eq!(bits.len(), self.type_properties().bit_width); - OpaqueSimValue::from_bitslice(bits) + fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { + assert_eq!(self.type_properties().size(), opaque.size()); + opaque.to_owned() } - fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { - assert_eq!(bits.len(), self.type_properties().bit_width); - assert_eq!(value.bit_width(), self.type_properties().bit_width); - value.bits_mut().bits_mut().copy_from_bitslice(bits); + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ) { + assert_eq!(self.type_properties().size(), opaque.size()); + assert_eq!(value.size(), opaque.size()); + value.clone_from_slice(opaque); } - fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { - assert_eq!(bits.len(), self.type_properties().bit_width); - assert_eq!(value.bit_width(), self.type_properties().bit_width); - bits.copy_from_bitslice(value.bits().bits()); + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> OpaqueSimValueWritten<'w> { + assert_eq!(self.type_properties().size(), writer.size()); + assert_eq!(value.size(), writer.size()); + writer.fill_cloned_from_slice(value.as_slice()) } } @@ -458,23 +474,30 @@ impl UnknownVariantSimValue { } } -pub struct EnumSimValueFromBits<'a> { +pub struct EnumSimValueFromOpaque<'a> { variants: Interned<[EnumVariant]>, discriminant: usize, body_bits: &'a BitSlice, } -impl<'a> EnumSimValueFromBits<'a> { +impl<'a> EnumSimValueFromOpaque<'a> { #[track_caller] - pub fn new(ty: T, bits: &'a BitSlice) -> Self { + pub fn new(ty: T, opaque: OpaqueSimValueSlice<'a>) -> Self { let variants = ty.variants(); - let bit_width = EnumTypePropertiesBuilder::new() + let size @ OpaqueSimValueSize { + bit_width: _, + sim_only_values_len: 0, + } = EnumTypePropertiesBuilder::new() .variants(variants) .finish() - .bit_width; - assert_eq!(bit_width, bits.len()); - let (discriminant_bits, body_bits) = - bits.split_at(discriminant_bit_width_impl(variants.len())); + .size() + else { + unreachable!(); + }; + assert_eq!(size, opaque.size()); + let (discriminant_bits, body_bits) = opaque + .bits() + .split_at(discriminant_bit_width_impl(variants.len())); let mut discriminant = 0usize; discriminant.view_bits_mut::()[..discriminant_bits.len()] .copy_from_bitslice(discriminant_bits); @@ -517,7 +540,7 @@ impl<'a> EnumSimValueFromBits<'a> { (*ty, variant_bits, padding_bits) } #[track_caller] - pub fn unknown_variant_from_bits(self) -> UnknownVariantSimValue { + pub fn unknown_variant_from_opaque(self) -> UnknownVariantSimValue { let None = self.variants.get(self.discriminant) else { self.usage_error(false); }; @@ -527,7 +550,7 @@ impl<'a> EnumSimValueFromBits<'a> { ) } #[track_caller] - pub fn unknown_variant_clone_from_bits(self, value: &mut UnknownVariantSimValue) { + pub fn unknown_variant_clone_from_opaque(self, value: &mut UnknownVariantSimValue) { let None = self.variants.get(self.discriminant) else { self.usage_error(true); }; @@ -539,14 +562,14 @@ impl<'a> EnumSimValueFromBits<'a> { .copy_from_bitslice(self.body_bits); } #[track_caller] - pub fn variant_no_field_from_bits(self) -> EnumPaddingSimValue { + pub fn variant_no_field_from_opaque(self) -> EnumPaddingSimValue { let (None, _variant_bits, padding_bits) = self.known_variant(false) else { self.usage_error(false); }; EnumPaddingSimValue::from_bitslice(padding_bits) } #[track_caller] - pub fn variant_with_field_from_bits(self) -> (SimValue, EnumPaddingSimValue) { + pub fn variant_with_field_from_opaque(self) -> (SimValue, EnumPaddingSimValue) { let (Some(variant_ty), variant_bits, padding_bits) = self.known_variant(false) else { self.usage_error(false); }; @@ -566,14 +589,14 @@ impl<'a> EnumSimValueFromBits<'a> { } } #[track_caller] - pub fn variant_no_field_clone_from_bits(self, padding: &mut EnumPaddingSimValue) { + pub fn variant_no_field_clone_from_opaque(self, padding: &mut EnumPaddingSimValue) { let (None, _variant_bits, padding_bits) = self.known_variant(true) else { self.usage_error(true); }; Self::clone_padding_from_bits(padding, padding_bits); } #[track_caller] - pub fn variant_with_field_clone_from_bits( + pub fn variant_with_field_clone_from_opaque( self, value: &mut SimValue, padding: &mut EnumPaddingSimValue, @@ -589,35 +612,46 @@ impl<'a> EnumSimValueFromBits<'a> { } } -pub struct EnumSimValueToBits<'a> { +pub struct EnumSimValueToOpaque<'a> { variants: Interned<[EnumVariant]>, bit_width: usize, discriminant_bit_width: usize, - bits: &'a mut BitSlice, + writer: OpaqueSimValueWriter<'a>, } -impl<'a> EnumSimValueToBits<'a> { +impl<'a> EnumSimValueToOpaque<'a> { #[track_caller] - pub fn new(ty: T, bits: &'a mut BitSlice) -> Self { + pub fn new(ty: T, writer: OpaqueSimValueWriter<'a>) -> Self { let variants = ty.variants(); - let bit_width = EnumTypePropertiesBuilder::new() + let size @ OpaqueSimValueSize { + bit_width, + sim_only_values_len: 0, + } = EnumTypePropertiesBuilder::new() .variants(variants) .finish() - .bit_width; - assert_eq!(bit_width, bits.len()); + .size() + else { + unreachable!(); + }; + assert_eq!(size, writer.size()); Self { variants, bit_width, discriminant_bit_width: discriminant_bit_width_impl(variants.len()), - bits, + writer, } } #[track_caller] - fn discriminant_to_bits(&mut self, mut discriminant: usize) { + fn write_discriminant(&mut self, mut discriminant: usize) { let orig_discriminant = discriminant; let discriminant_bits = &mut discriminant.view_bits_mut::()[..self.discriminant_bit_width]; - self.bits[..self.discriminant_bit_width].copy_from_bitslice(discriminant_bits); + self.writer.fill_prefix_with( + OpaqueSimValueSize::from_bit_width(self.discriminant_bit_width), + |writer| { + writer.fill_cloned_from_slice(OpaqueSimValueSlice::from_bitslice(discriminant_bits)) + }, + ); discriminant_bits.fill(false); assert!( discriminant == 0, @@ -625,8 +659,11 @@ impl<'a> EnumSimValueToBits<'a> { ); } #[track_caller] - pub fn unknown_variant_to_bits(mut self, value: &UnknownVariantSimValue) { - self.discriminant_to_bits(value.discriminant); + pub fn unknown_variant_to_opaque( + mut self, + value: &UnknownVariantSimValue, + ) -> OpaqueSimValueWritten<'a> { + self.write_discriminant(value.discriminant); let None = self.variants.get(value.discriminant) else { panic!("can't use UnknownVariantSimValue to set known discriminant"); }; @@ -634,45 +671,57 @@ impl<'a> EnumSimValueToBits<'a> { self.bit_width - self.discriminant_bit_width, value.body_bits.width() ); - self.bits[self.discriminant_bit_width..].copy_from_bitslice(value.body_bits.bits()); + self.writer + .fill_cloned_from_slice(OpaqueSimValueSlice::from_bitslice(value.body_bits.bits())) } #[track_caller] fn known_variant( mut self, discriminant: usize, + value: Option<&OpaqueSimValue>, padding: &EnumPaddingSimValue, - ) -> (Option, &'a mut BitSlice) { - self.discriminant_to_bits(discriminant); + ) -> OpaqueSimValueWritten<'a> { + self.write_discriminant(discriminant); let variant_ty = self.variants[discriminant].ty; - let variant_bit_width = variant_ty.map_or(0, CanonicalType::bit_width); - let padding_bits = &mut self.bits[self.discriminant_bit_width..][variant_bit_width..]; - if let Some(padding) = padding.bits() { - assert_eq!(padding.width(), padding_bits.len()); - padding_bits.copy_from_bitslice(padding.bits()); - } else { - padding_bits.fill(false); + let variant_size = variant_ty.map_or(OpaqueSimValueSize::empty(), CanonicalType::size); + if let Some(value) = value { + if variant_ty.is_none() { + panic!("expected variant to have no field"); + } + self.writer.fill_prefix_with(variant_size, |writer| { + writer.fill_cloned_from_slice(value.as_slice()) + }); + } else if variant_ty.is_some() { + panic!("expected variant to have a field"); + } + if let Some(padding) = padding.bits() { + assert_eq!(padding.ty().type_properties().size(), self.writer.size()); + self.writer + .fill_cloned_from_slice(OpaqueSimValueSlice::from_bitslice(padding.bits())) + } else { + self.writer.fill_with_zeros() } - let variant_bits = &mut self.bits[self.discriminant_bit_width..][..variant_bit_width]; - (variant_ty, variant_bits) } #[track_caller] - pub fn variant_no_field_to_bits(self, discriminant: usize, padding: &EnumPaddingSimValue) { - let (None, _variant_bits) = self.known_variant(discriminant, padding) else { - panic!("expected variant to have no field"); - }; + pub fn variant_no_field_to_opaque( + self, + discriminant: usize, + padding: &EnumPaddingSimValue, + ) -> OpaqueSimValueWritten<'a> { + self.known_variant(discriminant, None, padding) } #[track_caller] - pub fn variant_with_field_to_bits( + pub fn variant_with_field_to_opaque( self, discriminant: usize, value: &SimValue, padding: &EnumPaddingSimValue, - ) { - let (Some(variant_ty), variant_bits) = self.known_variant(discriminant, padding) else { - panic!("expected variant to have a field"); + ) -> OpaqueSimValueWritten<'a> { + let Some(variant_ty) = self.variants[discriminant].ty else { + panic!("expected variant to have no field"); }; assert_eq!(SimValue::ty(value), T::from_canonical(variant_ty)); - variant_bits.copy_from_bitslice(SimValue::bits(value).bits()); + self.known_variant(discriminant, Some(SimValue::opaque(value)), padding) } } diff --git a/crates/fayalite/src/expr/ops.rs b/crates/fayalite/src/expr/ops.rs index 4f482ab..b10e3ae 100644 --- a/crates/fayalite/src/expr/ops.rs +++ b/crates/fayalite/src/expr/ops.rs @@ -1937,7 +1937,8 @@ impl FieldAccess { let field = Expr::ty(base).fields()[field_index]; let field_type = FieldType::from_canonical(field.ty); let literal_bits = base.to_literal_bits().map(|bits| { - bits[Expr::ty(base).field_offsets()[field_index]..][..field.ty.bit_width()].intern() + bits[Expr::ty(base).field_offsets()[field_index].bit_width..][..field.ty.bit_width()] + .intern() }); let target = base.target().map(|base| { Intern::intern_sized(base.join(TargetPathElement::intern_sized( diff --git a/crates/fayalite/src/firrtl.rs b/crates/fayalite/src/firrtl.rs index b766cf6..abfe518 100644 --- a/crates/fayalite/src/firrtl.rs +++ b/crates/fayalite/src/firrtl.rs @@ -33,7 +33,7 @@ use crate::{ }, reset::{AsyncReset, Reset, ResetType, SyncReset}, source_location::SourceLocation, - ty::{CanonicalType, Type}, + ty::{CanonicalType, OpaqueSimValueSize, Type}, util::{ BitSliceWriteWithBase, DebugAsRawString, GenericConstBool, HashMap, HashSet, const_str_array_is_strictly_ascending, @@ -57,6 +57,43 @@ use std::{ rc::Rc, }; +#[derive(Clone, Debug)] +#[non_exhaustive] +enum FirrtlError { + SimOnlyValuesAreNotPermitted, +} + +impl fmt::Display for FirrtlError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + FirrtlError::SimOnlyValuesAreNotPermitted => { + f.write_str("`SimOnlyValue`s are not permitted") + } + } + } +} + +impl std::error::Error for FirrtlError {} + +enum FirrtlOrWrappedError { + FirrtlError(FirrtlError), + WrappedError(WrappedError), +} + +impl From for FirrtlOrWrappedError { + fn from(value: FirrtlError) -> Self { + Self::FirrtlError(value) + } +} + +impl From for FirrtlOrWrappedError { + fn from(value: WrappedError) -> Self { + Self::WrappedError(value) + } +} + +type Result = std::result::Result; + struct EscapedString<'a> { value: &'a str, raw: bool, @@ -320,20 +357,20 @@ impl DefinitionsMap { map: Default::default(), } } - fn get_or_make<'a>( + fn get_or_make<'a, E>( &'a self, key: K, - make: impl FnOnce(&K, &'a RcDefinitions) -> (Ident, V), - ) -> (Ident, V) + make: impl FnOnce(&K, &'a RcDefinitions) -> Result<(Ident, V), E>, + ) -> Result<(Ident, V), E> where K: Hash + Eq, V: Clone, { if let Some(retval) = self.map.borrow().get(&key) { - return retval.clone(); + return Ok(retval.clone()); } - let value = make(&key, &self.definitions); - self.map.borrow_mut().entry(key).or_insert(value).clone() + let value = make(&key, &self.definitions)?; + Ok(self.map.borrow_mut().entry(key).or_insert(value).clone()) } } @@ -367,10 +404,10 @@ impl TypeState { self.next_type_name.set(id + 1); Ident(Intern::intern_owned(format!("Ty{id}"))) } - fn get_bundle_field(&mut self, ty: Bundle, name: Interned) -> Ident { - self.bundle_ns(ty).borrow_mut().get(name) + fn get_bundle_field(&mut self, ty: Bundle, name: Interned) -> Result { + Ok(self.bundle_ns(ty)?.borrow_mut().get(name)) } - fn bundle_def(&self, ty: Bundle) -> (Ident, Rc>) { + fn bundle_def(&self, ty: Bundle) -> Result<(Ident, Rc>)> { self.bundle_defs.get_or_make(ty, |&ty, definitions| { let mut ns = Namespace::default(); let mut body = String::new(); @@ -383,21 +420,21 @@ impl TypeState { body.push_str("flip "); } write!(body, "{}: ", ns.get(name)).unwrap(); - body.push_str(&self.ty(ty)); + body.push_str(&self.ty(ty)?); } body.push('}'); let name = self.make_type_name(); definitions.add_definition_line(format_args!("type {name} = {body}")); - (name, Rc::new(RefCell::new(ns))) + Ok((name, Rc::new(RefCell::new(ns)))) }) } - fn bundle_ty(&self, ty: Bundle) -> Ident { - self.bundle_def(ty).0 + fn bundle_ty(&self, ty: Bundle) -> Result { + Ok(self.bundle_def(ty)?.0) } - fn bundle_ns(&self, ty: Bundle) -> Rc> { - self.bundle_def(ty).1 + fn bundle_ns(&self, ty: Bundle) -> Result>> { + Ok(self.bundle_def(ty)?.1) } - fn enum_def(&self, ty: Enum) -> (Ident, Rc) { + fn enum_def(&self, ty: Enum) -> Result<(Ident, Rc)> { self.enum_defs.get_or_make(ty, |&ty, definitions| { let mut variants = Namespace::default(); let mut body = String::new(); @@ -409,33 +446,33 @@ impl TypeState { write!(body, "{}", variants.get(name)).unwrap(); if let Some(ty) = ty { body.push_str(": "); - body.push_str(&self.ty(ty)); + body.push_str(&self.ty(ty)?); } } body.push_str("|}"); let name = self.make_type_name(); definitions.add_definition_line(format_args!("type {name} = {body}")); - ( + Ok(( name, Rc::new(EnumDef { variants: RefCell::new(variants), body, }), - ) + )) }) } - fn enum_ty(&self, ty: Enum) -> Ident { - self.enum_def(ty).0 + fn enum_ty(&self, ty: Enum) -> Result { + Ok(self.enum_def(ty)?.0) } - fn get_enum_variant(&mut self, ty: Enum, name: Interned) -> Ident { - self.enum_def(ty).1.variants.borrow_mut().get(name) + fn get_enum_variant(&mut self, ty: Enum, name: Interned) -> Result { + Ok(self.enum_def(ty)?.1.variants.borrow_mut().get(name)) } - fn ty(&self, ty: T) -> String { - match ty.canonical() { - CanonicalType::Bundle(ty) => self.bundle_ty(ty).to_string(), - CanonicalType::Enum(ty) => self.enum_ty(ty).to_string(), + fn ty(&self, ty: T) -> Result { + Ok(match ty.canonical() { + CanonicalType::Bundle(ty) => self.bundle_ty(ty)?.to_string(), + CanonicalType::Enum(ty) => self.enum_ty(ty)?.to_string(), CanonicalType::Array(ty) => { - let mut retval = self.ty(ty.element()); + let mut retval = self.ty(ty.element())?; write!(retval, "[{}]", ty.len()).unwrap(); retval } @@ -447,7 +484,10 @@ impl TypeState { CanonicalType::SyncReset(SyncReset {}) => "UInt<1>".into(), CanonicalType::Reset(Reset {}) => "Reset".into(), CanonicalType::PhantomConst(_) => "{}".into(), - } + CanonicalType::DynSimOnlyValueType(_) => { + return Err(FirrtlError::SimOnlyValuesAreNotPermitted.into()); + } + }) } } @@ -483,6 +523,7 @@ trait WrappedFileBackendTrait { contents: String, ) -> Result<(), WrappedError>; fn simplify_enums_error(&mut self, error: SimplifyEnumsError) -> WrappedError; + fn firrtl_error(&mut self, error: FirrtlError) -> WrappedError; } struct WrappedFileBackend { @@ -545,6 +586,11 @@ impl WrappedFileBackendTrait for WrappedFileBackend { self.error = Err(error.into()); WrappedError } + + fn firrtl_error(&mut self, error: FirrtlError) -> WrappedError { + self.error = Err(self.file_backend.custom_error(Box::new(error))); + WrappedError + } } #[derive(Clone)] @@ -747,7 +793,10 @@ impl<'a> Exporter<'a> { } fn run(&mut self, top_module: Interned>) -> Result<(), WrappedError> { let mut contents = self.version(); - let circuit = self.circuit(top_module)?; + let circuit = self.circuit(top_module).map_err(|e| match e { + FirrtlOrWrappedError::FirrtlError(e) => self.file_backend.firrtl_error(e), + FirrtlOrWrappedError::WrappedError(e) => e, + })?; contents.push_str(&circuit); self.file_backend .write_top_fir_file(self.circuit_name.to_string(), contents) @@ -755,7 +804,7 @@ impl<'a> Exporter<'a> { fn version(&mut self) -> String { "FIRRTL version 3.2.0\n".to_string() } - fn circuit(&mut self, top_module: Interned>) -> Result { + fn circuit(&mut self, top_module: Interned>) -> Result { let indent = self.indent; self.add_module(top_module); let circuit_indent = indent.push(); @@ -785,9 +834,9 @@ impl<'a> Exporter<'a> { enum_ty: Enum, variant_name: Interned, variant_expr: Option, - ) -> String { - let (_, enum_def) = self.type_state.enum_def(enum_ty); - let variant_ident = self.type_state.get_enum_variant(enum_ty, variant_name); + ) -> Result { + let (_, enum_def) = self.type_state.enum_def(enum_ty)?; + let variant_ident = self.type_state.get_enum_variant(enum_ty, variant_name)?; let mut retval = enum_def.body.clone(); write!(retval, "({variant_ident}").unwrap(); if let Some(variant_expr) = variant_expr { @@ -795,7 +844,7 @@ impl<'a> Exporter<'a> { retval.push_str(&variant_expr); } retval.push(')'); - retval + Ok(retval) } fn uint_literal(&mut self, value: &UIntValue) -> String { format!( @@ -824,32 +873,32 @@ impl<'a> Exporter<'a> { to_ty: ToTy, definitions: &RcDefinitions, const_ty: bool, - ) -> String { + ) -> Result { let from_ty = Expr::ty(value); - let mut value = self.expr(Expr::canonical(value), definitions, const_ty); + let mut value = self.expr(Expr::canonical(value), definitions, const_ty)?; if from_ty.width().checked_add(1) == Some(to_ty.width()) && !FromTy::Signed::VALUE && ToTy::Signed::VALUE { - format!("cvt({value})") + Ok(format!("cvt({value})")) } else if from_ty.width() <= to_ty.width() { // must pad before changing type to preserve value modulo 2^to_ty.width if from_ty.width() < to_ty.width() { value = format!("pad({value}, {})", to_ty.width()); } if FromTy::Signed::VALUE == ToTy::Signed::VALUE { - value + Ok(value) } else if ToTy::Signed::VALUE { - format!("asSInt({value})") + Ok(format!("asSInt({value})")) } else { - format!("asUInt({value})") + Ok(format!("asUInt({value})")) } } else { value = format!("tail({value}, {})", from_ty.width() - to_ty.width()); if ToTy::Signed::VALUE { - format!("asSInt({value})") + Ok(format!("asSInt({value})")) } else { - value + Ok(value) } } } @@ -859,12 +908,12 @@ impl<'a> Exporter<'a> { value: Expr, definitions: &RcDefinitions, const_ty: bool, - ) -> String { - let value = self.expr(Expr::canonical(value), definitions, const_ty); + ) -> Result { + let value = self.expr(Expr::canonical(value), definitions, const_ty)?; if let Some(firrtl_cast_fn) = firrtl_cast_fn { - format!("{firrtl_cast_fn}({value})") + Ok(format!("{firrtl_cast_fn}({value})")) } else { - value + Ok(value) } } fn slice( @@ -873,17 +922,17 @@ impl<'a> Exporter<'a> { range: Range, definitions: &RcDefinitions, const_ty: bool, - ) -> String { + ) -> Result { let base_width = Expr::ty(base).width(); - let base = self.expr(Expr::canonical(base), definitions, const_ty); + let base = self.expr(Expr::canonical(base), definitions, const_ty)?; if range.is_empty() { - format!("tail({base}, {base_width})") + Ok(format!("tail({base}, {base_width})")) } else { - format!( + Ok(format!( "bits({base}, {hi}, {lo})", hi = range.end - 1, lo = range.start, - ) + )) } } fn array_literal_expr( @@ -891,29 +940,29 @@ impl<'a> Exporter<'a> { expr: ops::ArrayLiteral, definitions: &RcDefinitions, const_ty: bool, - ) -> String { + ) -> Result { let ident = self.module.ns.make_new("_array_literal_expr"); - let ty_str = self.type_state.ty(expr.ty()); + let ty_str = self.type_state.ty(expr.ty())?; let const_ = if const_ty { "const " } else { "" }; definitions.add_definition_line(format_args!("wire {ident}: {const_}{ty_str}")); for (index, element) in expr.element_values().into_iter().enumerate() { - let element = self.expr(Expr::canonical(element), definitions, const_ty); + let element = self.expr(Expr::canonical(element), definitions, const_ty)?; definitions.add_definition_line(format_args!("connect {ident}[{index}], {element}")); } if expr.element_values().is_empty() { definitions.add_definition_line(format_args!("invalidate {ident}")); } - ident.to_string() + Ok(ident.to_string()) } fn bundle_literal_expr( &mut self, expr: ops::BundleLiteral, definitions: &RcDefinitions, const_ty: bool, - ) -> String { + ) -> Result { let ident = self.module.ns.make_new("_bundle_literal_expr"); let ty = expr.ty(); - let (ty_ident, bundle_ns) = self.type_state.bundle_def(ty); + let (ty_ident, bundle_ns) = self.type_state.bundle_def(ty)?; let const_ = if const_ty { "const " } else { "" }; definitions.add_definition_line(format_args!("wire {ident}: {const_}{ty_ident}")); for ( @@ -930,37 +979,38 @@ impl<'a> Exporter<'a> { "can't have bundle literal with flipped field -- this should have been caught in BundleLiteral::new_unchecked" ); let name = bundle_ns.borrow_mut().get(name); - let field_value = self.expr(Expr::canonical(field_value), definitions, const_ty); + let field_value = self.expr(Expr::canonical(field_value), definitions, const_ty)?; definitions.add_definition_line(format_args!("connect {ident}.{name}, {field_value}")); } if ty.fields().is_empty() { definitions.add_definition_line(format_args!("invalidate {ident}")); } - ident.to_string() + Ok(ident.to_string()) } fn uninit_expr( &mut self, expr: ops::Uninit, definitions: &RcDefinitions, const_ty: bool, - ) -> String { + ) -> Result { let ident = self.module.ns.make_new("_uninit_expr"); let ty = expr.ty(); - let ty_ident = self.type_state.ty(ty); + let ty_ident = self.type_state.ty(ty)?; let const_ = if const_ty { "const " } else { "" }; definitions.add_definition_line(format_args!("wire {ident}: {const_}{ty_ident}")); definitions.add_definition_line(format_args!("invalidate {ident}")); - ident.to_string() + Ok(ident.to_string()) } fn enum_literal_expr( &mut self, expr: ops::EnumLiteral, definitions: &RcDefinitions, const_ty: bool, - ) -> String { + ) -> Result { let variant_expr = expr .variant_value() - .map(|variant_value| self.expr(variant_value, definitions, const_ty)); + .map(|variant_value| self.expr(variant_value, definitions, const_ty)) + .transpose()?; self.enum_expr_impl(expr.ty(), expr.variant_name(), variant_expr) } fn expr_cast_bundle_to_bits( @@ -969,12 +1019,12 @@ impl<'a> Exporter<'a> { ty: Bundle, definitions: &RcDefinitions, extra_indent: Indent<'_>, - ) -> String { + ) -> Result { if ty.fields().is_empty() { - return "UInt<0>(0)".into(); + return Ok("UInt<0>(0)".into()); } if let [field] = *ty.fields() { - let field_ident = self.type_state.get_bundle_field(ty, field.name); + let field_ident = self.type_state.get_bundle_field(ty, field.name)?; return self.expr_cast_to_bits( format!("{value_str}.{field_ident}"), field.ty, @@ -993,23 +1043,23 @@ impl<'a> Exporter<'a> { ty: UInt[field_ty.bit_width()].canonical(), }, ))); - let (flattened_ty_ident, _) = self.type_state.bundle_def(flattened_bundle_ty); + let (flattened_ty_ident, _) = self.type_state.bundle_def(flattened_bundle_ty)?; let ident = self.module.ns.make_new("_cast_bundle_to_bits_expr"); definitions.add_definition_line(format_args!( "{extra_indent}wire {ident}: {flattened_ty_ident}" )); let mut cat_expr = None; for field in ty.fields() { - let field_ident = self.type_state.get_bundle_field(ty, field.name); + let field_ident = self.type_state.get_bundle_field(ty, field.name)?; let flattened_field_ident = self .type_state - .get_bundle_field(flattened_bundle_ty, field.name); + .get_bundle_field(flattened_bundle_ty, field.name)?; let field_bits = self.expr_cast_to_bits( format!("{value_str}.{field_ident}"), field.ty, definitions, extra_indent, - ); + )?; definitions.add_definition_line(format_args!( "{extra_indent}connect {ident}.{flattened_field_ident}, {field_bits}" )); @@ -1026,7 +1076,7 @@ impl<'a> Exporter<'a> { )); let cat_expr = cat_expr.expect("bundle already checked to have fields"); definitions.add_definition_line(format_args!("{extra_indent}connect {retval}, {cat_expr}")); - retval.to_string() + Ok(retval.to_string()) } fn expr_cast_enum_to_bits( &mut self, @@ -1034,9 +1084,9 @@ impl<'a> Exporter<'a> { ty: Enum, definitions: &RcDefinitions, extra_indent: Indent<'_>, - ) -> String { + ) -> Result { if ty.variants().is_empty() { - return "UInt<0>(0)".into(); + return Ok("UInt<0>(0)".into()); } let retval = self.module.ns.make_new("_cast_enum_to_bits_expr"); definitions.add_definition_line(format_args!( @@ -1053,7 +1103,7 @@ impl<'a> Exporter<'a> { .make_new(&format!("_cast_enum_to_bits_expr_{}", variant.name)); definitions.add_definition_line(format_args!( "{extra_indent}{}({variant_value}):", - self.type_state.get_enum_variant(ty, variant.name), + self.type_state.get_enum_variant(ty, variant.name)?, )); let _match_arm_indent = extra_indent.push(); let variant_bits = self.expr_cast_to_bits( @@ -1061,7 +1111,7 @@ impl<'a> Exporter<'a> { variant_ty, definitions, extra_indent, - ); + )?; definitions.add_definition_line(format_args!( "{extra_indent}connect {retval}, pad(cat({variant_bits}, UInt<{}>({variant_index})), {})", ty.discriminant_bit_width(), @@ -1070,7 +1120,7 @@ impl<'a> Exporter<'a> { } else { definitions.add_definition_line(format_args!( "{extra_indent}{}:", - self.type_state.get_enum_variant(ty, variant.name), + self.type_state.get_enum_variant(ty, variant.name)?, )); let _match_arm_indent = extra_indent.push(); definitions.add_definition_line(format_args!( @@ -1079,7 +1129,7 @@ impl<'a> Exporter<'a> { )); } } - retval.to_string() + Ok(retval.to_string()) } fn expr_cast_array_to_bits( &mut self, @@ -1087,9 +1137,9 @@ impl<'a> Exporter<'a> { ty: Array, definitions: &RcDefinitions, extra_indent: Indent<'_>, - ) -> String { + ) -> Result { if ty.is_empty() { - return "UInt<0>(0)".into(); + return Ok("UInt<0>(0)".into()); } if ty.len() == 1 { return self.expr_cast_to_bits( @@ -1112,7 +1162,7 @@ impl<'a> Exporter<'a> { ty.element(), definitions, extra_indent, - ); + )?; definitions.add_definition_line(format_args!( "{extra_indent}connect {ident}[{index}], {element_bits}" )); @@ -1129,7 +1179,7 @@ impl<'a> Exporter<'a> { )); let cat_expr = cat_expr.expect("array already checked to have elements"); definitions.add_definition_line(format_args!("{extra_indent}connect {retval}, {cat_expr}")); - retval.to_string() + Ok(retval.to_string()) } fn expr_cast_to_bits( &mut self, @@ -1137,7 +1187,7 @@ impl<'a> Exporter<'a> { ty: CanonicalType, definitions: &RcDefinitions, extra_indent: Indent<'_>, - ) -> String { + ) -> Result { match ty { CanonicalType::Bundle(ty) => { self.expr_cast_bundle_to_bits(value_str, ty, definitions, extra_indent) @@ -1149,13 +1199,16 @@ impl<'a> Exporter<'a> { self.expr_cast_array_to_bits(value_str, ty, definitions, extra_indent) } CanonicalType::UInt(_) | CanonicalType::SyncReset(_) | CanonicalType::Bool(_) => { - value_str + Ok(value_str) } CanonicalType::SInt(_) | CanonicalType::Clock(_) | CanonicalType::AsyncReset(_) - | CanonicalType::Reset(_) => format!("asUInt({value_str})"), - CanonicalType::PhantomConst(_) => "UInt<0>(0)".into(), + | CanonicalType::Reset(_) => Ok(format!("asUInt({value_str})")), + CanonicalType::PhantomConst(_) => Ok("UInt<0>(0)".into()), + CanonicalType::DynSimOnlyValueType(_) => { + Err(FirrtlError::SimOnlyValuesAreNotPermitted.into()) + } } } fn expr_cast_bits_to_bundle( @@ -1164,13 +1217,13 @@ impl<'a> Exporter<'a> { ty: Bundle, definitions: &RcDefinitions, extra_indent: Indent<'_>, - ) -> String { - let (ty_ident, _) = self.type_state.bundle_def(ty); + ) -> Result { + let (ty_ident, _) = self.type_state.bundle_def(ty)?; let retval = self.module.ns.make_new("_cast_bits_to_bundle_expr"); definitions.add_definition_line(format_args!("{extra_indent}wire {retval}: {ty_ident}")); if ty.fields().is_empty() { definitions.add_definition_line(format_args!("{extra_indent}invalidate {retval}")); - return retval.to_string(); + return Ok(retval.to_string()); } let flattened_bundle_ty = Bundle::new(Interned::from_iter(ty.fields().iter().map( |&BundleField { @@ -1183,7 +1236,7 @@ impl<'a> Exporter<'a> { ty: UInt[field_ty.bit_width()].canonical(), }, ))); - let (flattened_ty_ident, _) = self.type_state.bundle_def(flattened_bundle_ty); + let (flattened_ty_ident, _) = self.type_state.bundle_def(flattened_bundle_ty)?; let flattened_ident = self .module .ns @@ -1191,11 +1244,18 @@ impl<'a> Exporter<'a> { definitions.add_definition_line(format_args!( "{extra_indent}wire {flattened_ident}: {flattened_ty_ident}" )); - for (field, field_offset) in ty.fields().into_iter().zip(ty.field_offsets()) { + for ( + field, + OpaqueSimValueSize { + bit_width: field_offset, + sim_only_values_len: _, + }, + ) in ty.fields().into_iter().zip(ty.field_offsets()) + { let flattened_field_ident = self .type_state - .get_bundle_field(flattened_bundle_ty, field.name); - let field_ident = self.type_state.get_bundle_field(ty, field.name); + .get_bundle_field(flattened_bundle_ty, field.name)?; + let field_ident = self.type_state.get_bundle_field(ty, field.name)?; if let Some(field_bit_width_minus_one) = field.ty.bit_width().checked_sub(1usize) { definitions.add_definition_line(format_args!( "{extra_indent}connect {flattened_ident}.{flattened_field_ident}, bits({value_str}, {}, {field_offset})", @@ -1211,12 +1271,12 @@ impl<'a> Exporter<'a> { field.ty, definitions, extra_indent, - ); + )?; definitions.add_definition_line(format_args!( "{extra_indent}connect {retval}.{field_ident}, {field_value}" )); } - retval.to_string() + Ok(retval.to_string()) } fn expr_cast_bits_to_enum( &mut self, @@ -1224,19 +1284,19 @@ impl<'a> Exporter<'a> { ty: Enum, definitions: &RcDefinitions, extra_indent: Indent<'_>, - ) -> String { - let (ty_ident, enum_def) = self.type_state.enum_def(ty); + ) -> Result { + let (ty_ident, enum_def) = self.type_state.enum_def(ty)?; let retval = self.module.ns.make_new("_cast_bits_to_enum_expr"); definitions.add_definition_line(format_args!("{extra_indent}wire {retval}: {ty_ident}")); if ty.variants().is_empty() { definitions.add_definition_line(format_args!("{extra_indent}invalidate {retval}")); - return retval.to_string(); + return Ok(retval.to_string()); } if let [variant] = *ty.variants() { - let enum_variant = self.type_state.get_enum_variant(ty, variant.name); + let enum_variant = self.type_state.get_enum_variant(ty, variant.name)?; if let Some(variant_ty) = variant.ty { let variant_value = - self.expr_cast_bits_to(value_str, variant_ty, definitions, extra_indent); + self.expr_cast_bits_to(value_str, variant_ty, definitions, extra_indent)?; definitions.add_definition_line(format_args!( "{extra_indent}connect {retval}, {}({enum_variant}, {variant_value})", enum_def.body @@ -1247,7 +1307,7 @@ impl<'a> Exporter<'a> { enum_def.body )); } - return retval.to_string(); + return Ok(retval.to_string()); } let discriminant_bit_width = ty.discriminant_bit_width(); let body_bit_width = ty.type_properties().bit_width - discriminant_bit_width; @@ -1276,14 +1336,14 @@ impl<'a> Exporter<'a> { .add_definition_line(format_args!("{extra_indent}else when {when_cond}:")); } let when_pushed_indent = extra_indent.push(); - let enum_variant = self.type_state.get_enum_variant(ty, variant.name); + let enum_variant = self.type_state.get_enum_variant(ty, variant.name)?; if let Some(variant_ty) = variant.ty { let variant_value = self.expr_cast_bits_to( body_value.clone(), variant_ty, definitions, extra_indent, - ); + )?; definitions.add_definition_line(format_args!( "{extra_indent}connect {retval}, {}({enum_variant}, {variant_value})", enum_def.body @@ -1296,7 +1356,7 @@ impl<'a> Exporter<'a> { } drop(when_pushed_indent); } - retval.to_string() + Ok(retval.to_string()) } fn expr_cast_bits_to_array( &mut self, @@ -1304,14 +1364,14 @@ impl<'a> Exporter<'a> { ty: Array, definitions: &RcDefinitions, extra_indent: Indent<'_>, - ) -> String { + ) -> Result { let retval = self.module.ns.make_new("_cast_bits_to_array_expr"); - let array_ty = self.type_state.ty(ty); + let array_ty = self.type_state.ty(ty)?; definitions.add_definition_line(format_args!("{extra_indent}wire {retval}: {array_ty}")); let element_bit_width = ty.element().bit_width(); if ty.is_empty() || element_bit_width == 0 { definitions.add_definition_line(format_args!("{extra_indent}invalidate {retval}")); - return retval.to_string(); + return Ok(retval.to_string()); } let flattened_ident = self .module @@ -1332,12 +1392,12 @@ impl<'a> Exporter<'a> { ty.element(), definitions, extra_indent, - ); + )?; definitions.add_definition_line(format_args!( "{extra_indent}connect {retval}[{index}], {element_value}" )); } - retval.to_string() + Ok(retval.to_string()) } fn expr_cast_bits_to( &mut self, @@ -1345,7 +1405,7 @@ impl<'a> Exporter<'a> { ty: CanonicalType, definitions: &RcDefinitions, extra_indent: Indent<'_>, - ) -> String { + ) -> Result { match ty { CanonicalType::Bundle(ty) => { self.expr_cast_bits_to_bundle(value_str, ty, definitions, extra_indent) @@ -1356,18 +1416,21 @@ impl<'a> Exporter<'a> { CanonicalType::Array(ty) => { self.expr_cast_bits_to_array(value_str, ty, definitions, extra_indent) } - CanonicalType::UInt(_) => value_str, - CanonicalType::SInt(_) => format!("asSInt({value_str})"), - CanonicalType::Bool(_) => value_str, - CanonicalType::Clock(_) => format!("asClock({value_str})"), - CanonicalType::AsyncReset(_) => format!("asAsyncReset({value_str})"), - CanonicalType::SyncReset(_) => value_str, + CanonicalType::UInt(_) => Ok(value_str), + CanonicalType::SInt(_) => Ok(format!("asSInt({value_str})")), + CanonicalType::Bool(_) => Ok(value_str), + CanonicalType::Clock(_) => Ok(format!("asClock({value_str})")), + CanonicalType::AsyncReset(_) => Ok(format!("asAsyncReset({value_str})")), + CanonicalType::SyncReset(_) => Ok(value_str), CanonicalType::Reset(_) => unreachable!("Reset is not bit castable to"), CanonicalType::PhantomConst(_) => { let retval = self.module.ns.make_new("_cast_bits_to_phantom_const_expr"); definitions.add_definition_line(format_args!("{extra_indent}wire {retval}: {{}}")); definitions.add_definition_line(format_args!("{extra_indent}invalidate {retval}")); - return retval.to_string(); + return Ok(retval.to_string()); + } + CanonicalType::DynSimOnlyValueType(_) => { + Err(FirrtlError::SimOnlyValuesAreNotPermitted.into()) } } } @@ -1377,11 +1440,11 @@ impl<'a> Exporter<'a> { arg: Expr, definitions: &RcDefinitions, const_ty: bool, - ) -> String { - format!( + ) -> Result { + Ok(format!( "{func}({arg})", - arg = self.expr(Expr::canonical(arg), definitions, const_ty) - ) + arg = self.expr(Expr::canonical(arg), definitions, const_ty)?, + )) } fn expr_binary( &mut self, @@ -1390,23 +1453,23 @@ impl<'a> Exporter<'a> { rhs: Expr, definitions: &RcDefinitions, const_ty: bool, - ) -> String { - format!( + ) -> Result { + Ok(format!( "{func}({lhs}, {rhs})", - lhs = self.expr(Expr::canonical(lhs), definitions, const_ty), - rhs = self.expr(Expr::canonical(rhs), definitions, const_ty) - ) + lhs = self.expr(Expr::canonical(lhs), definitions, const_ty)?, + rhs = self.expr(Expr::canonical(rhs), definitions, const_ty)?, + )) } fn expr( &mut self, expr: Expr, definitions: &RcDefinitions, const_ty: bool, - ) -> String { + ) -> Result { match *Expr::expr_enum(expr) { - ExprEnum::UIntLiteral(literal) => self.uint_literal(&literal), - ExprEnum::SIntLiteral(literal) => self.sint_literal(&literal), - ExprEnum::BoolLiteral(literal) => self.bool_literal(literal), + ExprEnum::UIntLiteral(literal) => Ok(self.uint_literal(&literal)), + ExprEnum::SIntLiteral(literal) => Ok(self.sint_literal(&literal)), + ExprEnum::BoolLiteral(literal) => Ok(self.bool_literal(literal)), ExprEnum::PhantomConst(ty) => self.expr( UInt[0].zero().cast_bits_to(ty.canonical()), definitions, @@ -1495,34 +1558,26 @@ impl<'a> Exporter<'a> { ExprEnum::DynShrS(expr) => { self.expr_binary("dshr", expr.lhs(), expr.rhs(), definitions, const_ty) } - ExprEnum::FixedShlU(expr) => { - format!( - "shl({lhs}, {rhs})", - lhs = self.expr(Expr::canonical(expr.lhs()), definitions, const_ty), - rhs = expr.rhs(), - ) - } - ExprEnum::FixedShlS(expr) => { - format!( - "shl({lhs}, {rhs})", - lhs = self.expr(Expr::canonical(expr.lhs()), definitions, const_ty), - rhs = expr.rhs(), - ) - } - ExprEnum::FixedShrU(expr) => { - format!( - "shr({lhs}, {rhs})", - lhs = self.expr(Expr::canonical(expr.lhs()), definitions, const_ty), - rhs = expr.rhs(), - ) - } - ExprEnum::FixedShrS(expr) => { - format!( - "shr({lhs}, {rhs})", - lhs = self.expr(Expr::canonical(expr.lhs()), definitions, const_ty), - rhs = expr.rhs(), - ) - } + ExprEnum::FixedShlU(expr) => Ok(format!( + "shl({lhs}, {rhs})", + lhs = self.expr(Expr::canonical(expr.lhs()), definitions, const_ty)?, + rhs = expr.rhs(), + )), + ExprEnum::FixedShlS(expr) => Ok(format!( + "shl({lhs}, {rhs})", + lhs = self.expr(Expr::canonical(expr.lhs()), definitions, const_ty)?, + rhs = expr.rhs(), + )), + ExprEnum::FixedShrU(expr) => Ok(format!( + "shr({lhs}, {rhs})", + lhs = self.expr(Expr::canonical(expr.lhs()), definitions, const_ty)?, + rhs = expr.rhs(), + )), + ExprEnum::FixedShrS(expr) => Ok(format!( + "shr({lhs}, {rhs})", + lhs = self.expr(Expr::canonical(expr.lhs()), definitions, const_ty)?, + rhs = expr.rhs(), + )), ExprEnum::CmpLtU(expr) => { self.expr_binary("lt", expr.lhs(), expr.rhs(), definitions, const_ty) } @@ -1596,7 +1651,7 @@ impl<'a> Exporter<'a> { self.slice(expr.base(), expr.range(), definitions, const_ty) } ExprEnum::CastToBits(expr) => { - let value_str = self.expr(expr.arg(), definitions, const_ty); + let value_str = self.expr(expr.arg(), definitions, const_ty)?; self.expr_cast_to_bits( value_str, Expr::ty(expr.arg()), @@ -1608,7 +1663,7 @@ impl<'a> Exporter<'a> { ) } ExprEnum::CastBitsTo(expr) => { - let value_str = self.expr(Expr::canonical(expr.arg()), definitions, const_ty); + let value_str = self.expr(Expr::canonical(expr.arg()), definitions, const_ty)?; self.expr_cast_bits_to( value_str, expr.ty(), @@ -1719,56 +1774,57 @@ impl<'a> Exporter<'a> { self.expr_unary("xorr", expr.arg(), definitions, const_ty) } ExprEnum::FieldAccess(expr) => { - let mut out = self.expr(Expr::canonical(expr.base()), definitions, const_ty); + let mut out = self.expr(Expr::canonical(expr.base()), definitions, const_ty)?; let name = self .type_state - .get_bundle_field(Expr::ty(expr.base()), expr.field_name()); + .get_bundle_field(Expr::ty(expr.base()), expr.field_name())?; write!(out, ".{name}").unwrap(); - out + Ok(out) } - ExprEnum::VariantAccess(variant_access) => self + ExprEnum::VariantAccess(variant_access) => Ok(self .module .match_arm_values .get(&variant_access) .expect("VariantAccess must be in its corresponding match arm") - .to_string(), + .to_string()), ExprEnum::ArrayIndex(expr) => { - let mut out = self.expr(Expr::canonical(expr.base()), definitions, const_ty); + let mut out = self.expr(Expr::canonical(expr.base()), definitions, const_ty)?; write!(out, "[{}]", expr.element_index()).unwrap(); - out + Ok(out) } ExprEnum::DynArrayIndex(expr) => { - let mut out = self.expr(Expr::canonical(expr.base()), definitions, const_ty); - let index = self.expr(Expr::canonical(expr.element_index()), definitions, const_ty); + let mut out = self.expr(Expr::canonical(expr.base()), definitions, const_ty)?; + let index = + self.expr(Expr::canonical(expr.element_index()), definitions, const_ty)?; write!(out, "[{index}]").unwrap(); - out + Ok(out) } - ExprEnum::ModuleIO(expr) => self.module.ns.get(expr.name_id()).to_string(), + ExprEnum::ModuleIO(expr) => Ok(self.module.ns.get(expr.name_id()).to_string()), ExprEnum::Instance(expr) => { assert!(!const_ty, "not a constant"); - self.module.ns.get(expr.scoped_name().1).to_string() + Ok(self.module.ns.get(expr.scoped_name().1).to_string()) } ExprEnum::Wire(expr) => { assert!(!const_ty, "not a constant"); - self.module.ns.get(expr.scoped_name().1).to_string() + Ok(self.module.ns.get(expr.scoped_name().1).to_string()) } ExprEnum::Reg(expr) => { assert!(!const_ty, "not a constant"); - self.module.ns.get(expr.scoped_name().1).to_string() + Ok(self.module.ns.get(expr.scoped_name().1).to_string()) } ExprEnum::RegSync(expr) => { assert!(!const_ty, "not a constant"); - self.module.ns.get(expr.scoped_name().1).to_string() + Ok(self.module.ns.get(expr.scoped_name().1).to_string()) } ExprEnum::RegAsync(expr) => { assert!(!const_ty, "not a constant"); - self.module.ns.get(expr.scoped_name().1).to_string() + Ok(self.module.ns.get(expr.scoped_name().1).to_string()) } ExprEnum::MemPort(expr) => { assert!(!const_ty, "not a constant"); let mem_name = self.module.ns.get(expr.mem_name().1); let port_name = Ident::from(expr.port_name()); - format!("{mem_name}.{port_name}") + Ok(format!("{mem_name}.{port_name}")) } } } @@ -1778,7 +1834,7 @@ impl<'a> Exporter<'a> { memory_name: Ident, array_type: Array, initial_value: Interned, - ) -> Result<(), WrappedError> { + ) -> Result<()> { assert_eq!( initial_value.len(), array_type.type_properties().bit_width, @@ -1860,7 +1916,7 @@ impl<'a> Exporter<'a> { }, }) } - fn annotation_target_ref(&mut self, target: Interned) -> AnnotationTargetRef { + fn annotation_target_ref(&mut self, target: Interned) -> Result { match *target { Target::Base(base) => { let mut segments = vec![]; @@ -1878,17 +1934,17 @@ impl<'a> Exporter<'a> { TargetBase::Wire(v) => self.module.ns.get(v.name_id()), TargetBase::Instance(v) => self.module.ns.get(v.name_id()), }; - AnnotationTargetRef { base, segments } + Ok(AnnotationTargetRef { base, segments }) } Target::Child(child) => { - let mut retval = self.annotation_target_ref(child.parent()); + let mut retval = self.annotation_target_ref(child.parent())?; match *child.path_element() { TargetPathElement::BundleField(TargetPathBundleField { name }) => { retval.segments.push(AnnotationTargetRefSegment::Field { name: self.type_state.get_bundle_field( Bundle::from_canonical(child.parent().canonical_ty()), name, - ), + )?, }) } TargetPathElement::ArrayElement(TargetPathArrayElement { index, .. }) => retval @@ -1896,7 +1952,7 @@ impl<'a> Exporter<'a> { .push(AnnotationTargetRefSegment::Index { index }), TargetPathElement::DynArrayElement(_) => unreachable!(), } - retval + Ok(retval) } } } @@ -1905,9 +1961,9 @@ impl<'a> Exporter<'a> { base_module: Ident, submodules: Vec, annotations: &[crate::annotations::TargetedAnnotation], - ) { + ) -> Result<()> { for annotation in annotations { - let target_ref = Some(self.annotation_target_ref(annotation.target())); + let target_ref = Some(self.annotation_target_ref(annotation.target())?); self.annotation( AnnotationTargetPath { base_module, @@ -1917,8 +1973,9 @@ impl<'a> Exporter<'a> { annotation.annotation(), ); } + Ok(()) } - fn write_mem(&mut self, module_name: Ident, memory: Mem) -> Result { + fn write_mem(&mut self, module_name: Ident, memory: Mem) -> Result { let indent = self.indent; let name_id = memory.scoped_name().1; let source_location = memory.source_location(); @@ -1942,11 +1999,11 @@ impl<'a> Exporter<'a> { annotation, ); } - self.targeted_annotations(module_name, vec![], &memory.port_annotations()); + self.targeted_annotations(module_name, vec![], &memory.port_annotations())?; if let Some(initial_value) = initial_value { self.write_mem_init(module_name, name, array_type, initial_value)?; } - let data_type = self.type_state.ty(array_type.element()); + let data_type = self.type_state.ty(array_type.element())?; let mut body = String::new(); writeln!( body, @@ -1989,16 +2046,16 @@ impl<'a> Exporter<'a> { module_name: Ident, definitions: &RcDefinitions, body: &mut String, - ) { + ) -> Result<()> { let StmtReg { annotations, reg } = stmt_reg; let indent = self.indent; self.targeted_annotations(module_name, vec![], &annotations); let name = self.module.ns.get(reg.name_id()); - let ty = self.type_state.ty(reg.ty()); - let clk = self.expr(Expr::canonical(reg.clock_domain().clk), definitions, false); + let ty = self.type_state.ty(reg.ty())?; + let clk = self.expr(Expr::canonical(reg.clock_domain().clk), definitions, false)?; if let Some(init) = reg.init() { - let rst = self.expr(Expr::canonical(reg.clock_domain().rst), definitions, false); - let init = self.expr(init, definitions, false); + let rst = self.expr(Expr::canonical(reg.clock_domain().rst), definitions, false)?; + let init = self.expr(init, definitions, false)?; writeln!( body, "{indent}regreset {name}: {ty}, {clk}, {rst}, {init}{}", @@ -2013,6 +2070,7 @@ impl<'a> Exporter<'a> { ) .unwrap(); } + Ok(()) } fn block( &mut self, @@ -2020,7 +2078,7 @@ impl<'a> Exporter<'a> { block: Block, _block_indent: &PushIndent<'_>, definitions: Option, - ) -> Result { + ) -> Result { let indent = self.indent; let definitions = definitions.unwrap_or_default(); let mut body = String::new(); @@ -2046,8 +2104,8 @@ impl<'a> Exporter<'a> { ) .unwrap(); } - let lhs = self.expr(lhs, &definitions, false); - let rhs = self.expr(rhs, &definitions, false); + let lhs = self.expr(lhs, &definitions, false)?; + let rhs = self.expr(rhs, &definitions, false)?; writeln!( body, "{indent}connect {lhs}, {rhs}{}", @@ -2063,9 +2121,9 @@ impl<'a> Exporter<'a> { text, source_location, }) => { - let clk = self.expr(Expr::canonical(clk), &definitions, false); - let pred = self.expr(Expr::canonical(pred), &definitions, false); - let en = self.expr(Expr::canonical(en), &definitions, false); + let clk = self.expr(Expr::canonical(clk), &definitions, false)?; + let pred = self.expr(Expr::canonical(pred), &definitions, false)?; + let en = self.expr(Expr::canonical(en), &definitions, false)?; let kind = match kind { FormalKind::Assert => "assert", FormalKind::Assume => "assume", @@ -2090,7 +2148,7 @@ impl<'a> Exporter<'a> { let mut when = "when"; let mut pushed_indent; loop { - let cond_str = self.expr(Expr::canonical(cond), &definitions, false); + let cond_str = self.expr(Expr::canonical(cond), &definitions, false)?; writeln!( body, "{indent}{when} {cond_str}:{}", @@ -2132,7 +2190,7 @@ impl<'a> Exporter<'a> { writeln!( body, "{indent}match {}:{}", - self.expr(Expr::canonical(expr), &definitions, false), + self.expr(Expr::canonical(expr), &definitions, false)?, FileInfo::new(source_location), ) .unwrap(); @@ -2144,7 +2202,7 @@ impl<'a> Exporter<'a> { write!( body, "{indent}{}", - self.type_state.get_enum_variant(enum_ty, variant.name), + self.type_state.get_enum_variant(enum_ty, variant.name)?, ) .unwrap(); let variant_access = if variant.ty.is_some() { @@ -2176,7 +2234,7 @@ impl<'a> Exporter<'a> { Stmt::Declaration(StmtDeclaration::Wire(StmtWire { annotations, wire })) => { self.targeted_annotations(module_name, vec![], &annotations); let name = self.module.ns.get(wire.name_id()); - let ty = self.type_state.ty(wire.ty()); + let ty = self.type_state.ty(wire.ty())?; writeln!( body, "{indent}wire {name}: {ty}{}", @@ -2185,13 +2243,13 @@ impl<'a> Exporter<'a> { .unwrap(); } Stmt::Declaration(StmtDeclaration::Reg(stmt_reg)) => { - self.stmt_reg(stmt_reg, module_name, &definitions, &mut body); + self.stmt_reg(stmt_reg, module_name, &definitions, &mut body)?; } Stmt::Declaration(StmtDeclaration::RegSync(stmt_reg)) => { - self.stmt_reg(stmt_reg, module_name, &definitions, &mut body); + self.stmt_reg(stmt_reg, module_name, &definitions, &mut body)?; } Stmt::Declaration(StmtDeclaration::RegAsync(stmt_reg)) => { - self.stmt_reg(stmt_reg, module_name, &definitions, &mut body); + self.stmt_reg(stmt_reg, module_name, &definitions, &mut body)?; } Stmt::Declaration(StmtDeclaration::Instance(StmtInstance { annotations, @@ -2216,7 +2274,7 @@ impl<'a> Exporter<'a> { } Ok(out) } - fn module(&mut self, module: Interned>) -> Result { + fn module(&mut self, module: Interned>) -> Result { self.module = ModuleState::default(); let indent = self.indent; let module_name = self.global_ns.get(module.name_id()); @@ -2239,7 +2297,7 @@ impl<'a> Exporter<'a> { { self.targeted_annotations(module_name, vec![], annotations); let name = self.module.ns.get(module_io.name_id()); - let ty = self.type_state.ty(module_io.ty()); + let ty = self.type_state.ty(module_io.ty())?; if module_io.is_input() { writeln!( body, @@ -2314,6 +2372,7 @@ pub trait FileBackendTrait { type Error: From; type Path: AsRef + fmt::Debug + ?Sized; type PathBuf: AsRef + fmt::Debug; + fn custom_error(&self, error: Box) -> Self::Error; fn path_to_string(&mut self, path: &Self::Path) -> Result; fn write_mem_init_file( &mut self, @@ -2333,6 +2392,10 @@ impl FileBackendTrait for Box { type Path = T::Path; type PathBuf = T::PathBuf; + fn custom_error(&self, error: Box) -> Self::Error { + (**self).custom_error(error) + } + fn path_to_string(&mut self, path: &Self::Path) -> Result { (**self).path_to_string(path) } @@ -2360,6 +2423,10 @@ impl FileBackendTrait for &'_ mut T { type Path = T::Path; type PathBuf = T::PathBuf; + fn custom_error(&self, error: Box) -> Self::Error { + (**self).custom_error(error) + } + fn path_to_string(&mut self, path: &Self::Path) -> Result { (**self).path_to_string(path) } @@ -2405,6 +2472,10 @@ impl FileBackendTrait for FileBackend { type Path = Path; type PathBuf = PathBuf; + fn custom_error(&self, error: Box) -> Self::Error { + io::Error::new(io::ErrorKind::Other, error) + } + fn path_to_string(&mut self, path: &Self::Path) -> Result { path.to_str() .map(String::from) @@ -2571,6 +2642,10 @@ impl FileBackendTrait for TestBackend { type Path = str; type PathBuf = String; + fn custom_error(&self, error: Box) -> Self::Error { + TestBackendError(error.to_string()) + } + fn path_to_string(&mut self, path: &Self::Path) -> Result { self.step_error_after(&path)?; Ok(path.to_owned()) diff --git a/crates/fayalite/src/int.rs b/crates/fayalite/src/int.rs index c491cdc..7fa77ce 100644 --- a/crates/fayalite/src/int.rs +++ b/crates/fayalite/src/int.rs @@ -11,10 +11,13 @@ use crate::{ intern::{Intern, Interned, Memoize}, sim::value::{SimValue, ToSimValueWithType}, source_location::SourceLocation, - ty::{CanonicalType, StaticType, Type, TypeProperties, impl_match_variant_as_self}, - util::{ConstBool, ConstUsize, GenericConstBool, GenericConstUsize, interned_bit}, + ty::{ + CanonicalType, OpaqueSimValueSize, OpaqueSimValueSlice, OpaqueSimValueWriter, + OpaqueSimValueWritten, StaticType, Type, TypeProperties, impl_match_variant_as_self, + }, + util::{ConstBool, ConstUsize, GenericConstBool, GenericConstUsize, interned_bit, slice_range}, }; -use bitvec::{order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView}; +use bitvec::{bits, order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView}; use num_bigint::{BigInt, BigUint, Sign}; use num_traits::{One, Signed, Zero}; use serde::{ @@ -26,7 +29,7 @@ use std::{ fmt, marker::PhantomData, num::NonZero, - ops::{Bound, Index, Not, Range, RangeBounds, RangeInclusive}, + ops::{Index, Not, Range, RangeBounds, RangeInclusive}, str::FromStr, sync::Arc, }; @@ -645,6 +648,7 @@ macro_rules! impl_int { is_storable: true, is_castable_from_bits: true, bit_width: self.width, + sim_only_values_len: 0, } } } @@ -678,19 +682,36 @@ macro_rules! impl_int { fn source_location() -> SourceLocation { SourceLocation::builtin() } - fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { - assert_eq!(bits.len(), self.width()); - $value::new(Arc::new(bits.to_bitvec())) + fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { + assert_eq!( + opaque.size(), + OpaqueSimValueSize::from_bit_width(self.width()) + ); + $value::new(Arc::new(opaque.bits().to_bitvec())) } - fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { - assert_eq!(bits.len(), self.width()); + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ) { + assert_eq!( + opaque.size(), + OpaqueSimValueSize::from_bit_width(self.width()) + ); assert_eq!(value.width(), self.width()); - value.bits_mut().copy_from_bitslice(bits); + value.bits_mut().copy_from_bitslice(opaque.bits()); } - fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { - assert_eq!(bits.len(), self.width()); + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> OpaqueSimValueWritten<'w> { + assert_eq!( + writer.size(), + OpaqueSimValueSize::from_bit_width(self.width()) + ); assert_eq!(value.width(), self.width()); - bits.copy_from_bitslice(value.bits()); + writer.fill_cloned_from_slice(OpaqueSimValueSlice::from_bitslice(value.bits())) } } @@ -898,6 +919,9 @@ macro_rules! impl_int { _phantom: PhantomData, } } + pub fn bitvec_mut(&mut self) -> &mut BitVec { + Arc::make_mut(&mut self.bits) + } } }; } @@ -1160,19 +1184,7 @@ pub trait IntType: Self::Dyn::new(width) } fn slice_index_to_range>(self, index: I) -> Range { - let width = self.width(); - let start = match index.start_bound() { - Bound::Included(start) => *start, - Bound::Excluded(start) => *start + 1, - Bound::Unbounded => 0, - }; - let end = match index.end_bound() { - Bound::Included(end) => *end + 1, - Bound::Excluded(end) => *end, - Bound::Unbounded => width, - }; - assert!(start <= end && end <= width, "slice range out-of-range"); - start..end + slice_range(index, self.width()) } fn slice_and_shift>(self, index: I) -> (UInt, usize) { let range = self.slice_index_to_range(index); @@ -1252,17 +1264,27 @@ impl Type for Bool { fn source_location() -> SourceLocation { SourceLocation::builtin() } - fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { - assert_eq!(bits.len(), 1); - bits[0] + fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { + assert_eq!(opaque.size(), OpaqueSimValueSize::from_bit_width(1)); + opaque.bits()[0] } - fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { - assert_eq!(bits.len(), 1); - *value = bits[0]; + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ) { + assert_eq!(opaque.size(), OpaqueSimValueSize::from_bit_width(1)); + *value = opaque.bits()[0]; } - fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { - assert_eq!(bits.len(), 1); - bits.set(0, *value); + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> OpaqueSimValueWritten<'w> { + assert_eq!(writer.size(), OpaqueSimValueSize::from_bit_width(1)); + writer.fill_cloned_from_slice(OpaqueSimValueSlice::from_bitslice( + [bits![0], bits![1]][*value as usize], + )) } } @@ -1274,6 +1296,7 @@ impl StaticType for Bool { is_storable: true, is_castable_from_bits: true, bit_width: 1, + sim_only_values_len: 0, }; const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES; } diff --git a/crates/fayalite/src/int/uint_in_range.rs b/crates/fayalite/src/int/uint_in_range.rs index ae80a93..5ddd38c 100644 --- a/crates/fayalite/src/int/uint_in_range.rs +++ b/crates/fayalite/src/int/uint_in_range.rs @@ -12,9 +12,12 @@ use crate::{ phantom_const::PhantomConst, sim::value::{SimValue, SimValuePartialEq, ToSimValueWithType}, source_location::SourceLocation, - ty::{CanonicalType, StaticType, Type, TypeProperties, impl_match_variant_as_self}, + ty::{ + CanonicalType, OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, + StaticType, Type, TypeProperties, impl_match_variant_as_self, + }, }; -use bitvec::{order::Lsb0, slice::BitSlice, view::BitView}; +use bitvec::{order::Lsb0, view::BitView}; use serde::{ Deserialize, Deserializer, Serialize, Serializer, de::{Error, Visitor, value::UsizeDeserializer}, @@ -70,16 +73,24 @@ impl Type for UIntInRangeMaskType { SourceLocation::builtin() } - fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { - Bool.sim_value_from_bits(bits) + fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { + Bool.sim_value_from_opaque(opaque) } - fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { - Bool.sim_value_clone_from_bits(value, bits); + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ) { + Bool.sim_value_clone_from_opaque(value, opaque); } - fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { - Bool.sim_value_to_bits(value, bits); + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> OpaqueSimValueWritten<'w> { + Bool.sim_value_to_opaque(value, writer) } } @@ -353,18 +364,30 @@ macro_rules! define_uint_in_range_type { SourceLocation::builtin() } - fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { + fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { + assert_eq!(opaque.size(), self.value.type_properties().size()); let mut retval = 0usize; - retval.view_bits_mut::()[..bits.len()].clone_from_bitslice(bits); + retval.view_bits_mut::()[..opaque.bit_width()] + .clone_from_bitslice(opaque.bits()); retval } - fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { - *value = self.sim_value_from_bits(bits); + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ) { + *value = self.sim_value_from_opaque(opaque); } - fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { - bits.clone_from_bitslice(&value.view_bits::()[..bits.len()]); + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> OpaqueSimValueWritten<'w> { + writer.fill_cloned_from_slice(OpaqueSimValueSlice::from_bitslice( + &value.view_bits::()[..self.value.width()], + )) } } diff --git a/crates/fayalite/src/memory.rs b/crates/fayalite/src/memory.rs index a146ac6..15789c8 100644 --- a/crates/fayalite/src/memory.rs +++ b/crates/fayalite/src/memory.rs @@ -1066,7 +1066,8 @@ pub fn splat_mask(ty: T, value: Expr) -> Expr> { | CanonicalType::SyncReset(_) | CanonicalType::Reset(_) | CanonicalType::Clock(_) - | CanonicalType::Enum(_) => Expr::from_canonical(Expr::canonical(value)), + | CanonicalType::Enum(_) + | CanonicalType::DynSimOnlyValueType(_) => Expr::from_canonical(Expr::canonical(value)), CanonicalType::Array(array) => Expr::from_canonical(Expr::canonical(repeat( splat_mask(array.element(), value), array.len(), diff --git a/crates/fayalite/src/module.rs b/crates/fayalite/src/module.rs index aaa9340..de54fb2 100644 --- a/crates/fayalite/src/module.rs +++ b/crates/fayalite/src/module.rs @@ -1524,7 +1524,8 @@ impl TargetState { | CanonicalType::Clock(_) | CanonicalType::AsyncReset(_) | CanonicalType::SyncReset(_) - | CanonicalType::Reset(_) => TargetStateInner::Single { + | CanonicalType::Reset(_) + | CanonicalType::DynSimOnlyValueType(_) => TargetStateInner::Single { declared_in_block, written_in_blocks: RefCell::default(), }, diff --git a/crates/fayalite/src/phantom_const.rs b/crates/fayalite/src/phantom_const.rs index 44b36ca..b852056 100644 --- a/crates/fayalite/src/phantom_const.rs +++ b/crates/fayalite/src/phantom_const.rs @@ -11,11 +11,11 @@ use crate::{ sim::value::{SimValue, SimValuePartialEq, ToSimValue, ToSimValueWithType}, source_location::SourceLocation, ty::{ - CanonicalType, StaticType, Type, TypeProperties, impl_match_variant_as_self, + CanonicalType, OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, + StaticType, Type, TypeProperties, impl_match_variant_as_self, serde_impls::{SerdeCanonicalType, SerdePhantomConst}, }, }; -use bitvec::slice::BitSlice; use serde::{ Deserialize, Deserializer, Serialize, Serializer, de::{DeserializeOwned, Error}, @@ -284,19 +284,27 @@ impl Type for PhantomConst { SourceLocation::builtin() } - fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { - assert!(bits.is_empty()); + fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { + assert!(opaque.is_empty()); *self } - fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { - assert!(bits.is_empty()); + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ) { + assert!(opaque.is_empty()); assert_eq!(*value, *self); } - fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { - assert!(bits.is_empty()); + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> OpaqueSimValueWritten<'w> { assert_eq!(*value, *self); + writer.fill_cloned_from_slice(OpaqueSimValueSlice::empty()) } } diff --git a/crates/fayalite/src/reset.rs b/crates/fayalite/src/reset.rs index f3392a2..5dff278 100644 --- a/crates/fayalite/src/reset.rs +++ b/crates/fayalite/src/reset.rs @@ -5,9 +5,12 @@ use crate::{ expr::{Expr, ToExpr, ops}, int::{Bool, SInt, UInt}, source_location::SourceLocation, - ty::{CanonicalType, StaticType, Type, TypeProperties, impl_match_variant_as_self}, + ty::{ + CanonicalType, OpaqueSimValueSize, OpaqueSimValueSlice, OpaqueSimValueWriter, + OpaqueSimValueWritten, StaticType, Type, TypeProperties, impl_match_variant_as_self, + }, }; -use bitvec::slice::BitSlice; +use bitvec::{bits, order::Lsb0}; mod sealed { pub trait ResetTypeSealed {} @@ -69,19 +72,29 @@ macro_rules! reset_type { retval } - fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { - assert_eq!(bits.len(), 1); - bits[0] + fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { + assert_eq!(opaque.size(), OpaqueSimValueSize::from_bit_width(1)); + opaque.bits()[0] } - fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { - assert_eq!(bits.len(), 1); - *value = bits[0]; + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ) { + assert_eq!(opaque.size(), OpaqueSimValueSize::from_bit_width(1)); + *value = opaque.bits()[0]; } - fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { - assert_eq!(bits.len(), 1); - bits.set(0, *value); + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> OpaqueSimValueWritten<'w> { + assert_eq!(writer.size(), OpaqueSimValueSize::from_bit_width(1)); + writer.fill_cloned_from_slice(OpaqueSimValueSlice::from_bitslice( + [bits![0], bits![1]][*value as usize], + )) } } @@ -102,6 +115,7 @@ macro_rules! reset_type { is_storable: false, is_castable_from_bits: $is_castable_from_bits, bit_width: 1, + sim_only_values_len: 0, }; const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES; } diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index d91427f..0be99bc 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -22,11 +22,11 @@ use crate::{ }, interpreter::{ BreakAction, BreakpointsSet, RunResult, SmallUInt, State, StatePartIndex, - StatePartKindBigSlots, StatePartKindMemories, StatePartKindSmallSlots, TypeIndexRange, - TypeLen, + StatePartKindBigSlots, StatePartKindMemories, StatePartKindSimOnlyValues, + StatePartKindSmallSlots, TypeIndexRange, TypeLen, TypeLenSingle, }, time::{SimDuration, SimInstant}, - value::SimValue, + value::{DynSimOnlyValue, DynSimOnlyValueType, SimValue}, }, util::{BitSliceWriteWithBase, DebugAsDisplay, HashMap, HashSet}, }; @@ -503,6 +503,11 @@ pub trait TraceWriter: fmt::Debug + 'static { variant_index: usize, ty: Enum, ) -> Result<(), Self::Error>; + fn set_signal_sim_only_value( + &mut self, + id: TraceScalarId, + value: &DynSimOnlyValue, + ) -> Result<(), Self::Error>; } pub struct DynTraceWriterDecls(Box); @@ -557,6 +562,11 @@ trait TraceWriterDynTrait: fmt::Debug + 'static { variant_index: usize, ty: Enum, ) -> std::io::Result<()>; + fn set_signal_sim_only_value( + &mut self, + id: TraceScalarId, + value: &DynSimOnlyValue, + ) -> std::io::Result<()>; } impl TraceWriterDynTrait for T { @@ -616,6 +626,13 @@ impl TraceWriterDynTrait for T { .map_err(err_into_io)?, ) } + fn set_signal_sim_only_value( + &mut self, + id: TraceScalarId, + value: &DynSimOnlyValue, + ) -> std::io::Result<()> { + Ok(TraceWriter::set_signal_sim_only_value(self, id, value).map_err(err_into_io)?) + } } pub struct DynTraceWriter(Box); @@ -680,6 +697,13 @@ impl TraceWriter for DynTraceWriter { self.0 .set_signal_enum_discriminant_dyn(id, variant_index, ty) } + fn set_signal_sim_only_value( + &mut self, + id: TraceScalarId, + value: &DynSimOnlyValue, + ) -> Result<(), Self::Error> { + self.0.set_signal_sim_only_value(id, value) + } } #[derive(Debug)] @@ -844,11 +868,20 @@ pub(crate) enum SimTraceKind { index: StatePartIndex, ty: Enum, }, + SimOnlyValue { + index: StatePartIndex, + ty: DynSimOnlyValueType, + }, +} + +pub(crate) enum SimTraceState { + BitVec(BitVec), + SimOnlyValue(DynSimOnlyValueType), } impl SimTraceKind { - fn make_state(self) -> BitVec { - match self { + fn make_state(self) -> SimTraceState { + SimTraceState::BitVec(match self { SimTraceKind::BigUInt { index: _, ty } | SimTraceKind::SmallUInt { index: _, ty } => { BitVec::repeat(false, ty.width) } @@ -866,7 +899,8 @@ impl SimTraceKind { SimTraceKind::EnumDiscriminant { index: _, ty } => { BitVec::repeat(false, ty.discriminant_bit_width()) } - } + SimTraceKind::SimOnlyValue { index: _, ty } => return SimTraceState::SimOnlyValue(ty), + }) } } @@ -1325,14 +1359,15 @@ impl MaybeNeedsSettleFn<&'_ mut interpreter::State> for ReadBitFn { type Output = bool; fn call(self, state: &mut interpreter::State) -> Self::Output { - match self.compiled_value.range.len() { - TypeLen::A_SMALL_SLOT => { + match self.compiled_value.range.len().single() { + Some(TypeLenSingle::SmallSlot) => { state.small_slots[self.compiled_value.range.small_slots.start] != 0 } - TypeLen::A_BIG_SLOT => !state.big_slots[self.compiled_value.range.big_slots.start] + Some(TypeLenSingle::BigSlot) => !state.big_slots + [self.compiled_value.range.big_slots.start] .clone() .is_zero(), - _ => unreachable!(), + Some(TypeLenSingle::SimOnlyValue) | None => unreachable!(), } } } @@ -1347,13 +1382,13 @@ impl MaybeNeedsSettleFn<&'_ mut interpreter::State> for ReadBo fn call(self, state: &mut interpreter::State) -> Self::Output { let Self { compiled_value, io } = self; - match compiled_value.range.len() { - TypeLen::A_SMALL_SLOT => Expr::ty(io) + match compiled_value.range.len().single() { + Some(TypeLenSingle::SmallSlot) => Expr::ty(io) .value_from_int_wrapping(state.small_slots[compiled_value.range.small_slots.start]), - TypeLen::A_BIG_SLOT => Expr::ty(io).value_from_int_wrapping( + Some(TypeLenSingle::BigSlot) => Expr::ty(io).value_from_int_wrapping( state.big_slots[compiled_value.range.big_slots.start].clone(), ), - _ => unreachable!(), + Some(TypeLenSingle::SimOnlyValue) | None => unreachable!(), } } } diff --git a/crates/fayalite/src/sim/compiler.rs b/crates/fayalite/src/sim/compiler.rs index dd06267..c4a9c51 100644 --- a/crates/fayalite/src/sim/compiler.rs +++ b/crates/fayalite/src/sim/compiler.rs @@ -34,9 +34,10 @@ use crate::{ Insn, InsnField, InsnFieldKind, InsnFieldType, InsnOrLabel, Insns, InsnsBuilding, InsnsBuildingDone, InsnsBuildingKind, Label, MemoryData, SlotDebugData, SmallUInt, StatePartArrayIndex, StatePartArrayIndexed, StatePartIndex, StatePartIndexRange, - StatePartKind, StatePartKindBigSlots, StatePartKindMemories, StatePartKindSmallSlots, - StatePartLayout, StatePartLen, StatePartsValue, TypeArrayIndex, TypeArrayIndexes, - TypeIndex, TypeIndexRange, TypeLayout, TypeLen, TypeParts, + StatePartKind, StatePartKindBigSlots, StatePartKindMemories, + StatePartKindSimOnlyValues, StatePartKindSmallSlots, StatePartLayout, StatePartLen, + StatePartsValue, TypeArrayIndex, TypeArrayIndexes, TypeIndex, TypeIndexRange, + TypeLayout, TypeLen, TypeLenSingle, TypeParts, }, }, ty::StaticType, @@ -198,6 +199,19 @@ impl CompiledTypeLayout { body: CompiledTypeLayoutBody::Bundle { fields }, } } + CanonicalType::DynSimOnlyValueType(_) => { + let mut layout = TypeLayout::empty(); + let debug_data = SlotDebugData { + name: Interned::default(), + ty: *input, + }; + layout.sim_only_values = StatePartLayout::scalar(debug_data, ()); + CompiledTypeLayout { + ty: *input, + layout: layout.into(), + body: CompiledTypeLayoutBody::Scalar, + } + } } } } @@ -1711,16 +1725,26 @@ impl Compiler { source_location: SourceLocation, small_kind: impl FnOnce(StatePartIndex) -> SimTraceKind, big_kind: impl FnOnce(StatePartIndex) -> SimTraceKind, + sim_only_value_kind: impl FnOnce(StatePartIndex) -> SimTraceKind, ) -> TraceLocation { match target { MakeTraceDeclTarget::Expr(target) => { let compiled_value = self.compile_expr(instantiated_module, target); let compiled_value = self.compiled_expr_to_value(compiled_value, source_location); - TraceLocation::Scalar(self.new_sim_trace(match compiled_value.range.len() { - TypeLen::A_SMALL_SLOT => small_kind(compiled_value.range.small_slots.start), - TypeLen::A_BIG_SLOT => big_kind(compiled_value.range.big_slots.start), - _ => unreachable!(), - })) + TraceLocation::Scalar(self.new_sim_trace( + match compiled_value.range.len().single() { + Some(TypeLenSingle::SmallSlot) => { + small_kind(compiled_value.range.small_slots.start) + } + Some(TypeLenSingle::BigSlot) => { + big_kind(compiled_value.range.big_slots.start) + } + Some(TypeLenSingle::SimOnlyValue) => { + sim_only_value_kind(compiled_value.range.big_slots.start) + } + None => unreachable!(), + }, + )) } MakeTraceDeclTarget::Memory { id, diff --git a/crates/fayalite/src/sim/interpreter.rs b/crates/fayalite/src/sim/interpreter.rs index 35a25d0..c49a9db 100644 --- a/crates/fayalite/src/sim/interpreter.rs +++ b/crates/fayalite/src/sim/interpreter.rs @@ -5,6 +5,7 @@ use crate::{ array::Array, int::{BoolOrIntType, SInt, UInt}, intern::{Intern, Interned, Memoize}, + sim::value::DynSimOnlyValue, source_location::SourceLocation, ty::CanonicalType, util::{HashMap, HashSet}, @@ -115,8 +116,10 @@ insn_field_enum! { Memory(Transform::Type>), SmallSlot(Transform::Type>), BigSlot(Transform::Type>), + SimOnlyValue(Transform::Type>), SmallSlotArrayIndexed(Transform::Type>), BigSlotArrayIndexed(Transform::Type>), + SimOnlyValueArrayIndexed(Transform::Type>), SmallUInt(Transform::Type), SmallSInt(Transform::Type), InternedBigInt(Transform::Type>), @@ -251,8 +254,10 @@ impl Insn { InsnFieldType::Memory(_) | InsnFieldType::SmallSlot(_) | InsnFieldType::BigSlot(_) + | InsnFieldType::SimOnlyValue(_) | InsnFieldType::SmallSlotArrayIndexed(_) | InsnFieldType::BigSlotArrayIndexed(_) + | InsnFieldType::SimOnlyValueArrayIndexed(_) | InsnFieldType::SmallUInt(_) | InsnFieldType::SmallSInt(_) | InsnFieldType::InternedBigInt(_) @@ -279,12 +284,18 @@ impl Insn { InsnFieldType::BigSlot(v) => { debug_fmt_state_part!(v)?; } + InsnFieldType::SimOnlyValue(v) => { + debug_fmt_state_part!(v)?; + } InsnFieldType::SmallSlotArrayIndexed(v) => { debug_fmt_state_part!(v)?; } InsnFieldType::BigSlotArrayIndexed(v) => { debug_fmt_state_part!(v)?; } + InsnFieldType::SimOnlyValueArrayIndexed(v) => { + debug_fmt_state_part!(v)?; + } InsnFieldType::SmallUInt(v) => write!(f, "{v:#x}")?, InsnFieldType::SmallSInt(v) => write!(f, "{v:#x}")?, InsnFieldType::InternedBigInt(v) => write!(f, "{v:#x}")?, @@ -1394,6 +1405,11 @@ impl> fmt::Debug for MemoryData { } } +#[derive( + Copy, Clone, Eq, PartialEq, Hash, Debug, Default, serde::Serialize, serde::Deserialize, +)] +struct SimOnlyValueNotYetWritten; + make_state_part_kinds! { /*#[state, field = small_stack] impl StatePartKind for StatePartKindSmallStack { @@ -1521,6 +1537,44 @@ make_state_part_kinds! { write!(f, "{:#x}", state.big_slots[index]) } } + #[type, field = sim_only_values] + impl StatePartKind for StatePartKindSimOnlyValues { + const NAME: &'static str = "SimOnlyValues"; + type DebugData = SlotDebugData; + type LayoutData = (); + type State = Box<[Option]>; + type BorrowedState<'a> = &'a mut [Option]; + fn new_state(layout_data: &[Self::LayoutData]) -> Self::State { + layout_data.iter().map(|_| None).collect() + } + fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> { + state + } + fn part_debug_data( + state_layout: &StateLayout, + part_index: StatePartIndex, + ) -> Option<&Self::DebugData> { + state_layout.ty.sim_only_values.debug_data.get(part_index.as_usize()) + } + fn debug_fmt_state_value( + state: &State, + index: StatePartIndex, + f: &mut impl fmt::Write, + ) -> fmt::Result { + if let Some(value) = &state.sim_only_values[index] { + write!(f, "{:?}", value) + } else { + f.write_str("") + } + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub(crate) enum TypeLenSingle { + SmallSlot, + BigSlot, + SimOnlyValue, } impl TypeLen { @@ -1532,32 +1586,121 @@ impl TypeLen { value: 0, _phantom: _, }, + sim_only_values: + StatePartLen { + value: 0, + _phantom: _, + }, } = self else { return None; }; Some(small_slots) } - pub(crate) const A_SMALL_SLOT: Self = TypeLen { - small_slots: StatePartLen { - value: 1, - _phantom: PhantomData, - }, - big_slots: StatePartLen { - value: 0, - _phantom: PhantomData, - }, - }; - pub(crate) const A_BIG_SLOT: Self = TypeLen { - small_slots: StatePartLen { - value: 0, - _phantom: PhantomData, - }, - big_slots: StatePartLen { - value: 1, - _phantom: PhantomData, - }, - }; + pub(crate) const fn single(self) -> Option { + match self { + Self { + small_slots: + StatePartLen { + value: 1, + _phantom: PhantomData, + }, + big_slots: + StatePartLen { + value: 0, + _phantom: PhantomData, + }, + sim_only_values: + StatePartLen { + value: 0, + _phantom: PhantomData, + }, + } => Some(TypeLenSingle::SmallSlot), + Self { + small_slots: + StatePartLen { + value: 0, + _phantom: PhantomData, + }, + big_slots: + StatePartLen { + value: 1, + _phantom: PhantomData, + }, + sim_only_values: + StatePartLen { + value: 0, + _phantom: PhantomData, + }, + } => Some(TypeLenSingle::BigSlot), + Self { + small_slots: + StatePartLen { + value: 0, + _phantom: PhantomData, + }, + big_slots: + StatePartLen { + value: 0, + _phantom: PhantomData, + }, + sim_only_values: + StatePartLen { + value: 1, + _phantom: PhantomData, + }, + } => Some(TypeLenSingle::SimOnlyValue), + _ => None, + } + } + pub(crate) const fn a_small_slot() -> Self { + Self { + small_slots: StatePartLen { + value: 1, + _phantom: PhantomData, + }, + big_slots: StatePartLen { + value: 0, + _phantom: PhantomData, + }, + sim_only_values: StatePartLen { + value: 0, + _phantom: PhantomData, + }, + } + } + pub(crate) const fn a_big_slot() -> Self { + Self { + small_slots: StatePartLen { + value: 0, + _phantom: PhantomData, + }, + big_slots: StatePartLen { + value: 1, + _phantom: PhantomData, + }, + sim_only_values: StatePartLen { + value: 0, + _phantom: PhantomData, + }, + } + } + pub(crate) const fn a_sim_only_value() -> Self { + Self { + small_slots: StatePartLen { + value: 0, + _phantom: PhantomData, + }, + big_slots: StatePartLen { + value: 0, + _phantom: PhantomData, + }, + sim_only_values: StatePartLen { + value: 1, + _phantom: PhantomData, + }, + } + } } #[derive(Debug, Clone)] @@ -1669,12 +1812,14 @@ impl TypeLayout { Self { small_slots: self.small_slots.with_prefixed_debug_names(prefix), big_slots: self.big_slots.with_prefixed_debug_names(prefix), + sim_only_values: self.sim_only_values.with_prefixed_debug_names(prefix), } } pub(crate) fn with_anonymized_debug_info(&self) -> Self { Self { small_slots: self.small_slots.with_anonymized_debug_info(), big_slots: self.big_slots.with_anonymized_debug_info(), + sim_only_values: self.sim_only_values.with_anonymized_debug_info(), } } } @@ -2358,8 +2503,10 @@ impl From> for Insns { InsnFieldType::Memory(_) | InsnFieldType::SmallSlot(_) | InsnFieldType::BigSlot(_) + | InsnFieldType::SimOnlyValue(_) | InsnFieldType::SmallSlotArrayIndexed(_) | InsnFieldType::BigSlotArrayIndexed(_) + | InsnFieldType::SimOnlyValueArrayIndexed(_) | InsnFieldType::SmallUInt(_) | InsnFieldType::SmallSInt(_) | InsnFieldType::InternedBigInt(_) @@ -2393,6 +2540,7 @@ impl State { memories: _, small_slots: _, big_slots: _, + sim_only_values: _, } = self; *pc = entry_pc; } @@ -2437,6 +2585,7 @@ impl TypeIndexRange { let Self { small_slots, big_slots, + sim_only_values, } = self; small_slots .iter() @@ -2448,6 +2597,12 @@ impl TypeIndexRange { .zip(dest.big_slots.iter()) .map(|(src, dest)| Insn::Copy { dest, src }), ) + .chain( + sim_only_values + .iter() + .zip(dest.sim_only_values.iter()) + .map(|(src, dest)| Insn::CopySimOnlyValue { dest, src }), + ) } #[must_use] pub(crate) fn insns_for_copy_from(self, src: TypeIndexRange) -> impl Iterator { @@ -2582,6 +2737,18 @@ impl_insns! { state.small_slots[dest] = state.small_slots[src]; next!(); } + CopySimOnlyValue { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + src: StatePartIndex, + } => { + if dest != src { + let [dest, src] = state.sim_only_values.get_disjoint_mut([dest, src]); + dest.clone_from(src); + } + next!(); + } ReadIndexed { #[kind = Output] dest: StatePartIndex, @@ -2611,6 +2778,22 @@ impl_insns! { } next!(); } + ReadSimOnlyValueIndexed { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + src: StatePartArrayIndexed, + } => { + if let Some(src) = state.eval_array_indexed(src) { + if dest != src { + let [dest, src] = state.sim_only_values.get_disjoint_mut([dest, src]); + dest.clone_from(src); + } + } else { + state.sim_only_values[dest] = None; + } + next!(); + } WriteIndexed { #[kind = Output] dest: StatePartArrayIndexed, @@ -2636,6 +2819,20 @@ impl_insns! { } next!(); } + WriteSimOnlyValueIndexed { + #[kind = Output] + dest: StatePartArrayIndexed, + #[kind = Input] + src: StatePartIndex, + } => { + if let Some(dest) = state.eval_array_indexed(dest) { + if dest != src { + let [dest, src] = state.sim_only_values.get_disjoint_mut([dest, src]); + dest.clone_from(src); + } + } + next!(); + } CastBigToArrayIndex { #[kind = Output] dest: StatePartIndex, diff --git a/crates/fayalite/src/sim/value.rs b/crates/fayalite/src/sim/value.rs index 70cb943..7214bb0 100644 --- a/crates/fayalite/src/sim/value.rs +++ b/crates/fayalite/src/sim/value.rs @@ -9,60 +9,82 @@ use crate::{ expr::{CastBitsTo, Expr, ToExpr}, int::{Bool, IntType, KnownSize, SInt, SIntType, SIntValue, Size, UInt, UIntType, UIntValue}, reset::{AsyncReset, Reset, SyncReset}, - ty::{CanonicalType, StaticType, Type}, + source_location::SourceLocation, + ty::{ + CanonicalType, OpaqueSimValue, OpaqueSimValueSize, OpaqueSimValueSlice, + OpaqueSimValueWriter, StaticType, Type, TypeProperties, impl_match_variant_as_self, + }, util::{ - ConstUsize, + ConstUsize, HashMap, alternating_cell::{AlternatingCell, AlternatingCellMethods}, }, }; use bitvec::{slice::BitSlice, vec::BitVec}; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use hashbrown::hash_map::Entry; +use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as _, ser::Error as _}; use std::{ - fmt, + borrow::Cow, + fmt::{self, Write}, + hash::{BuildHasher, Hash, Hasher, RandomState}, ops::{Deref, DerefMut}, - sync::Arc, + sync::{Arc, Mutex}, +}; + +pub(crate) mod sim_only_value_unsafe; + +pub use sim_only_value_unsafe::{ + DynSimOnlyValue, DynSimOnlyValueType, SimOnlyValue, SimOnlyValueTrait, SimOnlyValueType, }; #[derive(Copy, Clone, Eq, PartialEq)] enum ValidFlags { BothValid = 0, OnlyValueValid = 1, - OnlyBitsValid = 2, + OnlyOpaqueValid = 2, } #[derive(Clone)] struct SimValueInner { value: T::SimValue, - bits: UIntValue, + opaque: OpaqueSimValue, valid_flags: ValidFlags, ty: T, + sim_only_values_len: usize, } impl SimValueInner { - fn fill_bits(&mut self) { + fn size(&self) -> OpaqueSimValueSize { + OpaqueSimValueSize { + bit_width: self.opaque.bit_width(), + sim_only_values_len: self.sim_only_values_len, + } + } + fn fill_opaque(&mut self) { match self.valid_flags { - ValidFlags::BothValid | ValidFlags::OnlyBitsValid => {} + ValidFlags::BothValid | ValidFlags::OnlyOpaqueValid => {} ValidFlags::OnlyValueValid => { - self.ty.sim_value_to_bits(&self.value, self.bits.bits_mut()); + OpaqueSimValueWriter::rewrite_with(self.size(), &mut self.opaque, |writer| { + self.ty.sim_value_to_opaque(&self.value, writer) + }); self.valid_flags = ValidFlags::BothValid; } } } - fn into_bits(mut self) -> UIntValue { - self.fill_bits(); - self.bits + fn into_opaque(mut self) -> OpaqueSimValue { + self.fill_opaque(); + self.opaque } - fn bits_mut(&mut self) -> &mut UIntValue { - self.fill_bits(); - self.valid_flags = ValidFlags::OnlyBitsValid; - &mut self.bits + fn opaque_mut(&mut self) -> &mut OpaqueSimValue { + self.fill_opaque(); + self.valid_flags = ValidFlags::OnlyOpaqueValid; + &mut self.opaque } fn fill_value(&mut self) { match self.valid_flags { ValidFlags::BothValid | ValidFlags::OnlyValueValid => {} - ValidFlags::OnlyBitsValid => { + ValidFlags::OnlyOpaqueValid => { self.ty - .sim_value_clone_from_bits(&mut self.value, self.bits.bits()); + .sim_value_clone_from_opaque(&mut self.value, self.opaque.as_slice()); self.valid_flags = ValidFlags::BothValid; } } @@ -83,11 +105,13 @@ impl AlternatingCellMethods for SimValueInner { match self.valid_flags { ValidFlags::BothValid => return, ValidFlags::OnlyValueValid => { - self.ty.sim_value_to_bits(&self.value, self.bits.bits_mut()) + OpaqueSimValueWriter::rewrite_with(self.size(), &mut self.opaque, |writer| { + self.ty.sim_value_to_opaque(&self.value, writer) + }) } - ValidFlags::OnlyBitsValid => self + ValidFlags::OnlyOpaqueValid => self .ty - .sim_value_clone_from_bits(&mut self.value, self.bits.bits()), + .sim_value_clone_from_opaque(&mut self.value, self.opaque.as_slice()), } self.valid_flags = ValidFlags::BothValid; } @@ -143,13 +167,15 @@ impl Clone for SimValue { impl SimValue { #[track_caller] - pub fn from_bits(ty: T, bits: UIntValue) -> Self { - assert_eq!(ty.canonical().bit_width(), bits.width()); + pub fn from_opaque(ty: T, opaque: OpaqueSimValue) -> Self { + let size = ty.canonical().size(); + assert_eq!(size, opaque.size()); let inner = SimValueInner { - value: ty.sim_value_from_bits(bits.bits()), - bits, + value: ty.sim_value_from_opaque(opaque.as_slice()), + opaque, valid_flags: ValidFlags::BothValid, ty, + sim_only_values_len: size.sim_only_values_len, }; Self { inner: AlternatingCell::new_shared(inner), @@ -157,14 +183,30 @@ impl SimValue { } #[track_caller] pub fn from_bitslice(ty: T, bits: &BitSlice) -> Self { - Self::from_bits(ty, UIntValue::new(Arc::new(bits.to_bitvec()))) + Self::from_bitslice_and_sim_only_values(ty, bits, Vec::new()) + } + #[track_caller] + pub fn from_bitslice_and_sim_only_values( + ty: T, + bits: &BitSlice, + sim_only_values: Vec, + ) -> Self { + Self::from_opaque( + ty, + OpaqueSimValue::from_bitslice_and_sim_only_values(bits, sim_only_values), + ) } pub fn from_value(ty: T, value: T::SimValue) -> Self { + let type_properties = ty.canonical().type_properties(); let inner = SimValueInner { - bits: UIntValue::new_dyn(Arc::new(BitVec::repeat(false, ty.canonical().bit_width()))), + opaque: 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), + ), value, valid_flags: ValidFlags::OnlyValueValid, ty, + sim_only_values_len: type_properties.sim_only_values_len, }; Self { inner: AlternatingCell::new_unique(inner), @@ -173,18 +215,24 @@ impl SimValue { pub fn ty(this: &Self) -> T { this.inner.share().ty } - pub fn into_bits(this: Self) -> UIntValue { - this.inner.into_inner().into_bits() + pub fn into_opaque(this: Self) -> OpaqueSimValue { + this.inner.into_inner().into_opaque() } - pub fn into_ty_and_bits(this: Self) -> (T, UIntValue) { + pub fn into_ty_and_opaque(this: Self) -> (T, OpaqueSimValue) { let inner = this.inner.into_inner(); - (inner.ty, inner.into_bits()) + (inner.ty, inner.into_opaque()) + } + pub fn opaque(this: &Self) -> &OpaqueSimValue { + &this.inner.share().opaque + } + pub fn opaque_mut(this: &mut Self) -> &mut OpaqueSimValue { + &mut this.inner.unique().opaque } pub fn bits(this: &Self) -> &UIntValue { - &this.inner.share().bits + Self::opaque(this).bits() } pub fn bits_mut(this: &mut Self) -> &mut UIntValue { - this.inner.unique().bits_mut() + Self::opaque_mut(this).bits_mut() } pub fn into_value(this: Self) -> T::SimValue { this.inner.into_inner().into_value() @@ -197,59 +245,59 @@ impl SimValue { } #[track_caller] pub fn from_canonical(v: SimValue) -> Self { - let (ty, bits) = SimValue::into_ty_and_bits(v); - Self::from_bits(T::from_canonical(ty), bits) + let (ty, opaque) = SimValue::into_ty_and_opaque(v); + Self::from_opaque(T::from_canonical(ty), opaque) } pub fn into_canonical(this: Self) -> SimValue { - let (ty, bits) = Self::into_ty_and_bits(this); - SimValue::from_bits(ty.canonical(), bits) + let (ty, opaque) = Self::into_ty_and_opaque(this); + SimValue::from_opaque(ty.canonical(), opaque) } pub fn canonical(this: &Self) -> SimValue { - SimValue::from_bits(Self::ty(this).canonical(), Self::bits(this).clone()) + SimValue::from_opaque(Self::ty(this).canonical(), Self::opaque(this).clone()) } #[track_caller] pub fn from_dyn_int(v: SimValue) -> Self where T: IntType, { - let (ty, bits) = SimValue::into_ty_and_bits(v); - SimValue::from_bits(T::from_dyn_int(ty), bits) + let (ty, opaque) = SimValue::into_ty_and_opaque(v); + SimValue::from_opaque(T::from_dyn_int(ty), opaque) } pub fn into_dyn_int(this: Self) -> SimValue where T: IntType, { - let (ty, bits) = Self::into_ty_and_bits(this); - SimValue::from_bits(ty.as_dyn_int(), bits) + let (ty, opaque) = Self::into_ty_and_opaque(this); + SimValue::from_opaque(ty.as_dyn_int(), opaque) } pub fn to_dyn_int(this: &Self) -> SimValue where T: IntType, { - SimValue::from_bits(Self::ty(this).as_dyn_int(), Self::bits(&this).clone()) + SimValue::from_opaque(Self::ty(this).as_dyn_int(), Self::opaque(&this).clone()) } #[track_caller] pub fn from_bundle(v: SimValue) -> Self where T: BundleType, { - let (ty, bits) = SimValue::into_ty_and_bits(v); - SimValue::from_bits(T::from_canonical(CanonicalType::Bundle(ty)), bits) + let (ty, opaque) = SimValue::into_ty_and_opaque(v); + SimValue::from_opaque(T::from_canonical(CanonicalType::Bundle(ty)), opaque) } pub fn into_bundle(this: Self) -> SimValue where T: BundleType, { - let (ty, bits) = Self::into_ty_and_bits(this); - SimValue::from_bits(Bundle::from_canonical(ty.canonical()), bits) + let (ty, opaque) = Self::into_ty_and_opaque(this); + SimValue::from_opaque(Bundle::from_canonical(ty.canonical()), opaque) } pub fn to_bundle(this: &Self) -> SimValue where T: BundleType, { - SimValue::from_bits( + SimValue::from_opaque( Bundle::from_canonical(Self::ty(this).canonical()), - Self::bits(&this).clone(), + Self::opaque(&this).clone(), ) } #[track_caller] @@ -257,23 +305,23 @@ impl SimValue { where T: EnumType, { - let (ty, bits) = SimValue::into_ty_and_bits(v); - SimValue::from_bits(T::from_canonical(CanonicalType::Enum(ty)), bits) + let (ty, opaque) = SimValue::into_ty_and_opaque(v); + SimValue::from_opaque(T::from_canonical(CanonicalType::Enum(ty)), opaque) } pub fn into_enum(this: Self) -> SimValue where T: EnumType, { - let (ty, bits) = Self::into_ty_and_bits(this); - SimValue::from_bits(Enum::from_canonical(ty.canonical()), bits) + let (ty, opaque) = Self::into_ty_and_opaque(this); + SimValue::from_opaque(Enum::from_canonical(ty.canonical()), opaque) } pub fn to_enum(this: &Self) -> SimValue where T: EnumType, { - SimValue::from_bits( + SimValue::from_opaque( Enum::from_canonical(Self::ty(this).canonical()), - Self::bits(&this).clone(), + Self::opaque(&this).clone(), ) } } @@ -308,7 +356,11 @@ impl ToExpr for SimValue { #[track_caller] fn to_expr(&self) -> Expr { let inner = self.inner.share(); - inner.bits.cast_bits_to(inner.ty) + assert_eq!( + inner.sim_only_values_len, 0, + "can't convert sim-only values to Expr" + ); + inner.opaque.bits().cast_bits_to(inner.ty) } } @@ -443,12 +495,15 @@ impl ToSimValueWithType for BitVec { #[track_caller] fn arc_into_sim_value_with_type(self: Arc, ty: T) -> SimValue { - SimValue::from_bits(ty, UIntValue::new_dyn(self)) + SimValue::from_opaque(ty, OpaqueSimValue::from_bits(UIntValue::new_dyn(self))) } #[track_caller] fn arc_to_sim_value_with_type(self: &Arc, ty: T) -> SimValue { - SimValue::from_bits(ty, UIntValue::new_dyn(self.clone())) + SimValue::from_opaque( + ty, + OpaqueSimValue::from_bits(UIntValue::new_dyn(self.clone())), + ) } } @@ -792,16 +847,18 @@ impl ToSimValueWithType for bool { | CanonicalType::Array(_) | CanonicalType::Enum(_) | CanonicalType::Bundle(_) - | CanonicalType::PhantomConst(_) => { + | CanonicalType::PhantomConst(_) + | CanonicalType::DynSimOnlyValueType(_) => { panic!("can't create SimValue from bool: expected value of type: {ty:?}"); } CanonicalType::Bool(_) | CanonicalType::AsyncReset(_) | CanonicalType::SyncReset(_) | CanonicalType::Reset(_) - | CanonicalType::Clock(_) => { - SimValue::from_bits(ty, UIntValue::new(Arc::new(BitVec::repeat(*self, 1)))) - } + | CanonicalType::Clock(_) => SimValue::from_opaque( + ty, + OpaqueSimValue::from_bits(UIntValue::new(Arc::new(BitVec::repeat(*self, 1)))), + ), } } } @@ -911,3 +968,330 @@ macro_rules! impl_to_sim_value_for_int_value { impl_to_sim_value_for_int_value!(UIntValue, UInt, UIntType); impl_to_sim_value_for_int_value!(SIntValue, SInt, SIntType); + +#[derive(Default)] +struct DynSimOnlyValueTypeSerdeTableRest { + from_serde: HashMap, + serde_id_random_state: RandomState, + buffer: String, +} + +impl DynSimOnlyValueTypeSerdeTableRest { + #[cold] + fn add_new(&mut self, ty: DynSimOnlyValueType) -> DynSimOnlyValueTypeSerdeId { + 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, "{:?}", ty.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 retval = DynSimOnlyValueTypeSerdeId(std::array::from_fn(|i| { + let mut hasher = hasher.clone(); + i.hash(&mut hasher); + hasher.finish() as u32 + })); + match self.from_serde.entry(retval) { + Entry::Occupied(_) => continue, + Entry::Vacant(e) => { + e.insert(ty); + return retval; + } + } + } + } +} + +#[derive(Default)] +struct DynSimOnlyValueTypeSerdeTable { + to_serde: HashMap, + rest: DynSimOnlyValueTypeSerdeTableRest, +} + +static DYN_SIM_ONLY_VALUE_TYPE_SERDE_TABLE: Mutex> = + Mutex::new(None); + +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)] +#[serde(transparent)] +struct DynSimOnlyValueTypeSerdeId([u32; 4]); + +impl From for DynSimOnlyValueTypeSerdeId { + fn from(ty: DynSimOnlyValueType) -> Self { + let mut locked = DYN_SIM_ONLY_VALUE_TYPE_SERDE_TABLE + .lock() + .expect("shouldn't be poison"); + let DynSimOnlyValueTypeSerdeTable { to_serde, rest } = locked.get_or_insert_default(); + match to_serde.entry(ty) { + Entry::Occupied(occupied_entry) => *occupied_entry.get(), + Entry::Vacant(vacant_entry) => *vacant_entry.insert(rest.add_new(ty)), + } + } +} + +impl DynSimOnlyValueTypeSerdeId { + fn ty(self) -> Option { + let locked = DYN_SIM_ONLY_VALUE_TYPE_SERDE_TABLE + .lock() + .expect("shouldn't be poison"); + Some(*locked.as_ref()?.rest.from_serde.get(&self)?) + } +} + +#[derive(Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)] +struct DynSimOnlyValueTypeSerde<'a> { + random_id: DynSimOnlyValueTypeSerdeId, + #[serde(borrow)] + type_name: Cow<'a, str>, +} + +impl Serialize for DynSimOnlyValueType { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + DynSimOnlyValueTypeSerde { + random_id: (*self).into(), + type_name: Cow::Borrowed(self.type_name()), + } + .serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for DynSimOnlyValueType { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let deserialized = DynSimOnlyValueTypeSerde::deserialize(deserializer)?; + let retval = deserialized + .random_id + .ty() + .filter(|ty| ty.type_name() == deserialized.type_name); + retval.ok_or_else(|| D::Error::custom("doesn't match any DynSimOnlyValueType that was serialized this time this program was run")) + } +} + +impl DynSimOnlyValueType { + pub const fn type_properties(self) -> TypeProperties { + TypeProperties { + is_passive: true, + is_storable: true, + is_castable_from_bits: false, + bit_width: 0, + sim_only_values_len: 1, + } + } + pub fn can_connect(self, other: Self) -> bool { + self == other + } +} + +impl Type for DynSimOnlyValueType { + type BaseType = DynSimOnlyValueType; + type MaskType = Bool; + type SimValue = DynSimOnlyValue; + + impl_match_variant_as_self!(); + + fn mask_type(&self) -> Self::MaskType { + Bool + } + + fn canonical(&self) -> CanonicalType { + CanonicalType::DynSimOnlyValueType(*self) + } + + fn from_canonical(canonical_type: CanonicalType) -> Self { + let CanonicalType::DynSimOnlyValueType(v) = canonical_type else { + panic!("expected DynSimOnlyValueType"); + }; + v + } + + fn source_location() -> SourceLocation { + SourceLocation::builtin() + } + + fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { + assert_eq!(opaque.size(), self.type_properties().size()); + opaque.sim_only_values()[0].clone() + } + + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ) { + assert_eq!(opaque.size(), self.type_properties().size()); + value.clone_from(&opaque.sim_only_values()[0]); + } + + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> crate::ty::OpaqueSimValueWritten<'w> { + writer.fill_cloned_from_slice(OpaqueSimValueSlice::from_parts( + BitSlice::empty(), + std::array::from_ref(value), + )) + } +} + +impl Type for SimOnlyValueType { + type BaseType = DynSimOnlyValueType; + type MaskType = Bool; + type SimValue = SimOnlyValue; + + impl_match_variant_as_self!(); + + fn mask_type(&self) -> Self::MaskType { + Bool + } + + fn canonical(&self) -> CanonicalType { + DynSimOnlyValueType::from(*self).canonical() + } + + fn from_canonical(canonical_type: CanonicalType) -> Self { + DynSimOnlyValueType::from_canonical(canonical_type) + .downcast() + .expect("got wrong SimOnlyValueType") + } + + fn source_location() -> SourceLocation { + SourceLocation::builtin() + } + + fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { + assert_eq!(Self::TYPE_PROPERTIES.size(), opaque.size()); + SimOnlyValue::new( + opaque.sim_only_values()[0] + .downcast_ref::() + .expect("type mismatch") + .clone(), + ) + } + + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ) { + assert_eq!(Self::TYPE_PROPERTIES.size(), opaque.size()); + (**value).clone_from( + &opaque.sim_only_values()[0] + .downcast_ref::() + .expect("type mismatch"), + ) + } + + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> crate::ty::OpaqueSimValueWritten<'w> { + SimOnlyValue::with_dyn_ref(value, |value| { + writer.fill_cloned_from_slice(OpaqueSimValueSlice::from_parts( + BitSlice::empty(), + std::array::from_ref(value), + )) + }) + } +} + +impl StaticType for SimOnlyValueType { + const TYPE: Self = Self::new(); + + const MASK_TYPE: Self::MaskType = Bool; + + const TYPE_PROPERTIES: TypeProperties = DynSimOnlyValueType::of::().type_properties(); + + const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES; +} + +impl fmt::Debug for SimOnlyValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Self::with_dyn_ref(self, |this| fmt::Debug::fmt(this, f)) + } +} + +#[derive(Serialize, Deserialize)] +#[serde(rename = "SimOnlyValue")] +struct SerdeSimOnlyValue<'a> { + ty: DynSimOnlyValueType, + #[serde(borrow)] + value: Cow<'a, str>, +} + +impl Serialize for DynSimOnlyValue { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + SerdeSimOnlyValue { + ty: self.ty(), + value: Cow::Owned(self.serialize_to_json_string().map_err(S::Error::custom)?), + } + .serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for DynSimOnlyValue { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let SerdeSimOnlyValue { ty, value } = Deserialize::deserialize(deserializer)?; + ty.deserialize_from_json_string(&value) + .map_err(D::Error::custom) + } +} + +impl ToSimValueWithType for DynSimOnlyValue { + #[track_caller] + fn to_sim_value_with_type(&self, ty: DynSimOnlyValueType) -> SimValue { + assert_eq!(self.ty(), ty, "mismatched type"); + SimValue::from_value(ty, self.clone()) + } + #[track_caller] + fn into_sim_value_with_type(self, ty: DynSimOnlyValueType) -> SimValue { + assert_eq!(self.ty(), ty, "mismatched type"); + SimValue::from_value(ty, self) + } +} + +impl ToSimValueWithType> for SimOnlyValue { + fn to_sim_value_with_type(&self, ty: SimOnlyValueType) -> SimValue> { + SimValue::from_value(ty, self.clone()) + } + fn into_sim_value_with_type(self, ty: SimOnlyValueType) -> SimValue> { + SimValue::from_value(ty, self) + } +} + +impl ToSimValue for DynSimOnlyValue { + type Type = DynSimOnlyValueType; + + fn to_sim_value(&self) -> SimValue { + SimValue::from_value(self.ty(), self.clone()) + } + + fn into_sim_value(self) -> SimValue { + SimValue::from_value(self.ty(), self) + } +} + +impl ToSimValue for SimOnlyValue { + type Type = SimOnlyValueType; + + fn to_sim_value(&self) -> SimValue { + SimValue::from_value(Default::default(), self.clone()) + } + + fn into_sim_value(self) -> SimValue { + SimValue::from_value(Default::default(), self) + } +} diff --git a/crates/fayalite/src/sim/value/sim_only_value_unsafe.rs b/crates/fayalite/src/sim/value/sim_only_value_unsafe.rs new file mode 100644 index 0000000..59134c1 --- /dev/null +++ b/crates/fayalite/src/sim/value/sim_only_value_unsafe.rs @@ -0,0 +1,357 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +//! `unsafe` parts of [`DynSimOnlyValue`] + +use serde::{Serialize, de::DeserializeOwned}; +use std::{ + alloc::{Layout, alloc, dealloc, handle_alloc_error}, + any::TypeId, + fmt, + hash::{Hash, Hasher}, + marker::PhantomData, + mem::ManuallyDrop, + ptr::{self, NonNull}, +}; + +struct SimOnlyValueVTable { + layout: Layout, + // TODO: replace with TypeId once TypeId::of is const-stable + type_id: fn() -> TypeId, + type_name: fn() -> &'static str, + drop_in_place: unsafe fn(this: NonNull<()>), + eq: unsafe fn(this: NonNull<()>, other: NonNull<()>) -> bool, + hash: unsafe fn(this: NonNull<()>, hasher: &mut dyn Hasher), + debug_fmt: unsafe fn(this: NonNull<()>, f: &mut fmt::Formatter<'_>) -> fmt::Result, + serialize_to_json_string: unsafe fn(this: NonNull<()>) -> serde_json::Result, + deserialize_into_uninit_from_json_string: + unsafe fn(this: NonNull<()>, json_str: &str) -> serde_json::Result<()>, + clone_into_uninit: unsafe fn(target: NonNull<()>, src: NonNull<()>), + clone_from: unsafe fn(this: NonNull<()>, src: NonNull<()>), +} + +pub trait SimOnlyValueTrait: + 'static + Eq + Hash + fmt::Debug + Serialize + DeserializeOwned + Clone +{ +} + +impl SimOnlyValueTrait + for T +{ +} + +unsafe trait GetSimOnlyValueVTable: SimOnlyValueTrait { + const VTABLE: &'static SimOnlyValueVTable; +} + +unsafe impl GetSimOnlyValueVTable for T { + const VTABLE: &'static SimOnlyValueVTable = &SimOnlyValueVTable { + layout: Layout::new::(), + type_id: TypeId::of::, + type_name: std::any::type_name::, + drop_in_place: |this| unsafe { + this.cast::().drop_in_place(); + }, + eq: |this, other| unsafe { this.cast::().as_ref() == other.cast::().as_ref() }, + hash: |this, mut hasher| unsafe { this.cast::().as_ref().hash(&mut hasher) }, + debug_fmt: |this, f| unsafe { fmt::Debug::fmt(this.cast::().as_ref(), f) }, + serialize_to_json_string: |this| unsafe { + serde_json::to_string(this.cast::().as_ref()) + }, + deserialize_into_uninit_from_json_string: |this, json_str| unsafe { + serde_json::from_str(json_str).map(|v| this.cast::().write(v)) + }, + clone_into_uninit: |target, src| unsafe { + target + .cast::() + .write(Clone::clone(src.cast::().as_ref())); + }, + clone_from: |this, src| unsafe { + Clone::clone_from(this.cast::().as_mut(), src.cast::().as_ref()); + }, + }; +} + +#[derive(Copy, Clone)] +pub struct DynSimOnlyValueType { + vtable: &'static SimOnlyValueVTable, +} + +struct DynSimOnlyValueUninit { + ty: DynSimOnlyValueType, + value: NonNull<()>, +} + +impl DynSimOnlyValueUninit { + fn new(ty: DynSimOnlyValueType) -> Self { + let layout = ty.vtable.layout; + let value = if layout.size() == 0 { + ptr::without_provenance_mut(layout.align()) + } else { + unsafe { alloc(layout).cast() } + }; + let Some(value) = NonNull::new(value) else { + handle_alloc_error(layout) + }; + Self { ty, value } + } + unsafe fn assume_init(self) -> DynSimOnlyValue { + let this = ManuallyDrop::new(self); + DynSimOnlyValue { + ty: this.ty, + value: this.value, + } + } +} + +impl Drop for DynSimOnlyValueUninit { + fn drop(&mut self) { + let layout = self.ty.vtable.layout; + if layout.size() != 0 { + unsafe { + dealloc(self.value.as_ptr().cast(), layout); + } + } + } +} + +impl DynSimOnlyValueType { + pub const fn of() -> Self { + Self { + vtable: ::VTABLE, + } + } + pub fn type_id(self) -> TypeId { + (self.vtable.type_id)() + } + pub fn type_name(self) -> &'static str { + (self.vtable.type_name)() + } + pub fn is(self) -> bool { + self.type_id() == TypeId::of::() + } + pub fn downcast(self) -> Option> { + self.is::().then_some(SimOnlyValueType::default()) + } + pub fn deserialize_from_json_string( + self, + json_str: &str, + ) -> serde_json::Result { + let retval = DynSimOnlyValueUninit::new(self); + unsafe { + (self.vtable.deserialize_into_uninit_from_json_string)(retval.value, json_str)?; + Ok(retval.assume_init()) + } + } +} + +impl PartialEq for DynSimOnlyValueType { + fn eq(&self, other: &Self) -> bool { + if ptr::eq(self.vtable, other.vtable) { + true + } else if self.vtable.layout != other.vtable.layout { + false + } else { + (self.vtable.type_id)() == (other.vtable.type_id)() + } + } +} + +impl Eq for DynSimOnlyValueType {} + +impl Hash for DynSimOnlyValueType { + fn hash(&self, state: &mut H) { + (self.vtable.type_id)().hash(state); + } +} + +impl fmt::Debug for DynSimOnlyValueType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "SimOnlyValueType<{}>", (self.vtable.type_name)()) + } +} + +impl From> for DynSimOnlyValueType { + fn from(value: SimOnlyValueType) -> Self { + let SimOnlyValueType(PhantomData) = value; + Self { + vtable: ::VTABLE, + } + } +} + +#[derive(Clone, Eq, PartialEq, Hash, Debug)] +pub struct SimOnlyValueType(PhantomData T>); + +impl SimOnlyValueType { + pub const fn new() -> Self { + Self(PhantomData) + } +} + +impl Copy for SimOnlyValueType {} + +impl Default for SimOnlyValueType { + fn default() -> Self { + Self::new() + } +} + +#[derive(Clone, Eq, PartialEq, Hash, Default, PartialOrd, Ord)] +pub struct SimOnlyValue(Box); + +impl SimOnlyValue { + pub fn with_dyn_ref R, R>(&self, f: F) -> R { + let dyn_ref = ManuallyDrop::new(DynSimOnlyValue { + ty: SimOnlyValueType::::default().into(), + value: NonNull::::from_ref(&self.0).cast(), + }); + f(&dyn_ref) + } + pub fn from_box(v: Box) -> Self { + Self(v) + } + pub fn new(v: T) -> Self { + Self(Box::new(v)) + } + pub fn into_inner(this: Self) -> T { + *this.0 + } + pub fn into_inner_box(this: Self) -> Box { + this.0 + } + pub fn into_dyn(this: Self) -> DynSimOnlyValue { + DynSimOnlyValue::from(this) + } +} + +impl std::ops::Deref for SimOnlyValue { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl std::ops::DerefMut for SimOnlyValue { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +pub struct DynSimOnlyValue { + ty: DynSimOnlyValueType, + value: NonNull<()>, +} + +struct DebugDynSimOnlyValueInner<'a>(&'a DynSimOnlyValue); + +impl<'a> fmt::Debug for DebugDynSimOnlyValueInner<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + unsafe { (self.0.ty.vtable.debug_fmt)(self.0.value, f) } + } +} + +impl fmt::Debug for DynSimOnlyValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "SimOnlyValue<{}>", self.ty.type_name())?; + f.debug_tuple("") + .field(&DebugDynSimOnlyValueInner(self)) + .finish() + } +} + +impl PartialEq for DynSimOnlyValue { + fn eq(&self, other: &Self) -> bool { + self.ty == other.ty && unsafe { (self.ty.vtable.eq)(self.value, other.value) } + } +} + +impl Eq for DynSimOnlyValue {} + +impl Hash for DynSimOnlyValue { + fn hash(&self, state: &mut H) { + self.ty.hash(state); + unsafe { (self.ty.vtable.hash)(self.value, state) }; + } +} + +impl Clone for DynSimOnlyValue { + fn clone(&self) -> Self { + let retval = DynSimOnlyValueUninit::new(self.ty); + unsafe { + (self.ty.vtable.clone_into_uninit)(retval.value, self.value); + retval.assume_init() + } + } + fn clone_from(&mut self, source: &Self) { + if self.ty == source.ty { + unsafe { (self.ty.vtable.clone_from)(self.value, source.value) }; + } else { + *self = source.clone(); + } + } +} + +impl Drop for DynSimOnlyValue { + fn drop(&mut self) { + unsafe { + ptr::read(self).drop_in_place_and_keep_alloc(); + } + } +} + +impl From> for DynSimOnlyValue { + fn from(value: SimOnlyValue) -> Self { + unsafe { + Self { + ty: SimOnlyValueType::::default().into(), + value: NonNull::new_unchecked(Box::into_raw(value.0)).cast::<()>(), + } + } + } +} + +impl DynSimOnlyValue { + pub fn ty(&self) -> DynSimOnlyValueType { + self.ty + } + pub fn type_id(&self) -> TypeId { + self.ty.type_id() + } + pub fn is(&self) -> bool { + self.ty.is::() + } + pub fn downcast(self) -> Result, DynSimOnlyValue> { + let Some(_) = self.ty.downcast::() else { + return Err(self); + }; + Ok(SimOnlyValue(unsafe { + Box::from_raw(ManuallyDrop::new(self).value.as_ptr().cast::()) + })) + } + pub fn downcast_ref(&self) -> Option<&T> { + self.ty + .downcast::() + .map(|_| unsafe { &*self.value.as_ptr().cast::() }) + } + pub fn downcast_mut(&mut self) -> Option<&mut T> { + self.ty + .downcast::() + .map(|_| unsafe { &mut *self.value.as_ptr().cast::() }) + } + pub fn serialize_to_json_string(&self) -> serde_json::Result { + unsafe { (self.ty.vtable.serialize_to_json_string)(self.value) } + } + fn forget_and_keep_alloc(self) -> DynSimOnlyValueUninit { + let this = ManuallyDrop::new(self); + DynSimOnlyValueUninit { + ty: this.ty, + value: this.value, + } + } + fn drop_in_place_and_keep_alloc(self) -> DynSimOnlyValueUninit { + let retval = self.forget_and_keep_alloc(); + unsafe { (retval.ty.vtable.drop_in_place)(retval.value) }; + retval + } +} diff --git a/crates/fayalite/src/sim/vcd.rs b/crates/fayalite/src/sim/vcd.rs index 4a2b564..8918db0 100644 --- a/crates/fayalite/src/sim/vcd.rs +++ b/crates/fayalite/src/sim/vcd.rs @@ -13,6 +13,7 @@ use crate::{ TraceModuleIO, TraceReg, TraceSInt, TraceScalar, TraceScalarId, TraceScope, TraceSyncReset, TraceUInt, TraceWire, TraceWriter, TraceWriterDecls, time::{SimDuration, SimInstant}, + value::DynSimOnlyValue, }, util::HashMap, }; @@ -1061,6 +1062,14 @@ impl TraceWriter for VcdWriter { ) -> Result<(), Self::Error> { write_enum_discriminant_value_change(&mut self.writer, variant_index, ty, id.as_usize()) } + + fn set_signal_sim_only_value( + &mut self, + id: TraceScalarId, + value: &DynSimOnlyValue, + ) -> Result<(), Self::Error> { + write_string_value_change(&mut self.writer, format_args!("{value:?}"), id) + } } impl fmt::Debug for VcdWriter { diff --git a/crates/fayalite/src/ty.rs b/crates/fayalite/src/ty.rs index 787869d..90b39e8 100644 --- a/crates/fayalite/src/ty.rs +++ b/crates/fayalite/src/ty.rs @@ -11,13 +11,21 @@ use crate::{ intern::{Intern, Interned}, phantom_const::PhantomConst, reset::{AsyncReset, Reset, SyncReset}, - sim::value::{SimValue, ToSimValueWithType}, + sim::value::{DynSimOnlyValue, DynSimOnlyValueType, SimValue, ToSimValueWithType}, source_location::SourceLocation, - util::ConstUsize, + util::{ConstUsize, slice_range, try_slice_range}, }; -use bitvec::slice::BitSlice; +use bitvec::{slice::BitSlice, vec::BitVec}; use serde::{Deserialize, Deserializer, Serialize, Serializer, de::DeserializeOwned}; -use std::{fmt, hash::Hash, iter::FusedIterator, ops::Index, sync::Arc}; +use std::{ + fmt, + hash::Hash, + iter::{FusedIterator, Sum}, + marker::PhantomData, + mem, + ops::{Add, AddAssign, Bound, Index, Mul, MulAssign, Range, Sub, SubAssign}, + sync::Arc, +}; pub(crate) mod serde_impls; @@ -28,6 +36,23 @@ pub struct TypeProperties { pub is_storable: bool, pub is_castable_from_bits: bool, pub bit_width: usize, + pub sim_only_values_len: usize, +} + +impl TypeProperties { + pub const fn size(self) -> OpaqueSimValueSize { + let Self { + is_passive: _, + is_storable: _, + is_castable_from_bits: _, + bit_width, + sim_only_values_len, + } = self; + OpaqueSimValueSize { + bit_width, + sim_only_values_len, + } + } } #[derive(Copy, Clone, Hash, PartialEq, Eq)] @@ -43,6 +68,7 @@ pub enum CanonicalType { Reset(Reset), Clock(Clock), PhantomConst(PhantomConst), + DynSimOnlyValueType(DynSimOnlyValueType), } impl fmt::Debug for CanonicalType { @@ -59,6 +85,7 @@ impl fmt::Debug for CanonicalType { Self::Reset(v) => v.fmt(f), Self::Clock(v) => v.fmt(f), Self::PhantomConst(v) => v.fmt(f), + Self::DynSimOnlyValueType(v) => v.fmt(f), } } } @@ -95,6 +122,7 @@ impl CanonicalType { CanonicalType::Reset(v) => v.type_properties(), CanonicalType::Clock(v) => v.type_properties(), CanonicalType::PhantomConst(v) => v.type_properties(), + CanonicalType::DynSimOnlyValueType(v) => v.type_properties(), } } pub fn is_passive(self) -> bool { @@ -109,6 +137,12 @@ impl CanonicalType { pub fn bit_width(self) -> usize { self.type_properties().bit_width } + pub fn sim_only_values_len(self) -> usize { + self.type_properties().sim_only_values_len + } + pub fn size(self) -> OpaqueSimValueSize { + self.type_properties().size() + } pub fn can_connect(self, rhs: Self) -> bool { match self { CanonicalType::UInt(lhs) => { @@ -177,6 +211,12 @@ impl CanonicalType { }; lhs.can_connect(rhs) } + CanonicalType::DynSimOnlyValueType(lhs) => { + let CanonicalType::DynSimOnlyValueType(rhs) = rhs else { + return false; + }; + lhs.can_connect(rhs) + } } } pub(crate) fn as_serde_unexpected_str(self) -> &'static str { @@ -287,6 +327,7 @@ impl_base_type!(SyncReset); impl_base_type!(Reset); impl_base_type!(Clock); impl_base_type!(PhantomConst); +impl_base_type!(DynSimOnlyValueType); impl_base_type_serde!(Bool, "a Bool"); impl_base_type_serde!(Enum, "an Enum"); @@ -348,9 +389,17 @@ pub trait Type: fn canonical(&self) -> CanonicalType; fn from_canonical(canonical_type: CanonicalType) -> Self; fn source_location() -> SourceLocation; - fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue; - fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice); - fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice); + fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue; + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ); + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> OpaqueSimValueWritten<'w>; } pub trait BaseType: @@ -405,6 +454,7 @@ impl Type for CanonicalType { CanonicalType::Reset(v) => v.mask_type().canonical(), CanonicalType::Clock(v) => v.mask_type().canonical(), CanonicalType::PhantomConst(v) => v.mask_type().canonical(), + CanonicalType::DynSimOnlyValueType(v) => v.mask_type().canonical(), } } fn canonical(&self) -> CanonicalType { @@ -416,28 +466,288 @@ impl Type for CanonicalType { fn source_location() -> SourceLocation { SourceLocation::builtin() } - fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { - assert_eq!(bits.len(), self.bit_width()); - OpaqueSimValue::from_bitslice(bits) + fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { + assert_eq!(self.type_properties().size(), opaque.size()); + opaque.to_owned() } - fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { - assert_eq!(bits.len(), self.bit_width()); - assert_eq!(value.bit_width(), self.bit_width()); - value.bits_mut().bits_mut().copy_from_bitslice(bits); + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ) { + assert_eq!(self.type_properties().size(), opaque.size()); + assert_eq!(value.size(), opaque.size()); + value.clone_from_slice(opaque); } - fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { - assert_eq!(bits.len(), self.bit_width()); - assert_eq!(value.bit_width(), self.bit_width()); - bits.copy_from_bitslice(value.bits().bits()); + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> OpaqueSimValueWritten<'w> { + assert_eq!(self.type_properties().size(), writer.size()); + assert_eq!(value.size(), writer.size()); + writer.fill_cloned_from_slice(value.as_slice()) + } +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize, Default)] +#[non_exhaustive] +pub struct OpaqueSimValueSizeRange { + pub bit_width: Range, + pub sim_only_values_len: Range, +} + +impl OpaqueSimValueSizeRange { + pub fn start(&self) -> OpaqueSimValueSize { + OpaqueSimValueSize { + bit_width: self.bit_width.start, + sim_only_values_len: self.sim_only_values_len.start, + } + } + pub fn end(&self) -> OpaqueSimValueSize { + OpaqueSimValueSize { + bit_width: self.bit_width.end, + sim_only_values_len: self.sim_only_values_len.end, + } + } + pub fn is_empty(&self) -> bool { + let Self { + bit_width, + sim_only_values_len, + } = self; + bit_width.is_empty() && sim_only_values_len.is_empty() + } +} + +impl From> for OpaqueSimValueSizeRange { + fn from(value: Range) -> Self { + Self { + bit_width: value.start.bit_width..value.end.bit_width, + sim_only_values_len: value.start.sim_only_values_len..value.end.sim_only_values_len, + } + } +} + +impl From for Range { + fn from(value: OpaqueSimValueSizeRange) -> Self { + value.start()..value.end() + } +} + +pub trait OpaqueSimValueSizeRangeBounds { + fn start_bound(&self) -> Bound; + fn end_bound(&self) -> Bound; +} + +impl OpaqueSimValueSizeRangeBounds for OpaqueSimValueSizeRange { + fn start_bound(&self) -> Bound { + Bound::Included(self.start()) + } + + fn end_bound(&self) -> Bound { + Bound::Excluded(self.end()) + } +} + +impl> OpaqueSimValueSizeRangeBounds for T { + fn start_bound(&self) -> Bound { + std::ops::RangeBounds::start_bound(self).cloned() + } + fn end_bound(&self) -> Bound { + std::ops::RangeBounds::end_bound(self).cloned() + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize, Default)] +#[non_exhaustive] +pub struct OpaqueSimValueSize { + pub bit_width: usize, + pub sim_only_values_len: usize, +} + +impl OpaqueSimValueSize { + pub const fn from_bit_width(bit_width: usize) -> Self { + Self::from_bit_width_and_sim_only_values_len(bit_width, 0) + } + pub const fn from_bit_width_and_sim_only_values_len( + bit_width: usize, + sim_only_values_len: usize, + ) -> Self { + Self { + bit_width, + sim_only_values_len, + } + } + pub const fn empty() -> Self { + Self { + bit_width: 0, + sim_only_values_len: 0, + } + } + pub const fn is_empty(self) -> bool { + let Self { + bit_width, + sim_only_values_len, + } = self; + bit_width == 0 && sim_only_values_len == 0 + } + pub const fn checked_mul(self, factor: usize) -> Option { + let Some(bit_width) = self.bit_width.checked_mul(factor) else { + return None; + }; + let Some(sim_only_values_len) = self.sim_only_values_len.checked_mul(factor) else { + return None; + }; + Some(Self { + bit_width, + sim_only_values_len, + }) + } + pub const fn checked_add(self, rhs: Self) -> Option { + let Some(bit_width) = self.bit_width.checked_add(rhs.bit_width) else { + return None; + }; + let Some(sim_only_values_len) = self + .sim_only_values_len + .checked_add(rhs.sim_only_values_len) + else { + return None; + }; + Some(Self { + bit_width, + sim_only_values_len, + }) + } + pub const fn checked_sub(self, rhs: Self) -> Option { + let Some(bit_width) = self.bit_width.checked_sub(rhs.bit_width) else { + return None; + }; + let Some(sim_only_values_len) = self + .sim_only_values_len + .checked_sub(rhs.sim_only_values_len) + else { + return None; + }; + Some(Self { + bit_width, + sim_only_values_len, + }) + } + pub fn try_slice_range( + self, + range: R, + ) -> Option { + let start = range.start_bound(); + let end = range.end_bound(); + let bit_width = try_slice_range( + (start.map(|v| v.bit_width), end.map(|v| v.bit_width)), + self.bit_width, + )?; + let sim_only_values_len = try_slice_range( + ( + start.map(|v| v.sim_only_values_len), + end.map(|v| v.sim_only_values_len), + ), + self.sim_only_values_len, + )?; + Some(OpaqueSimValueSizeRange { + bit_width, + sim_only_values_len, + }) + } + pub fn slice_range( + self, + range: R, + ) -> OpaqueSimValueSizeRange { + self.try_slice_range(range).expect("range out of bounds") + } +} + +impl Mul for OpaqueSimValueSize { + type Output = OpaqueSimValueSize; + + fn mul(self, rhs: usize) -> Self::Output { + self.checked_mul(rhs).expect("multiplication overflowed") + } +} + +impl Mul for usize { + type Output = OpaqueSimValueSize; + + fn mul(self, rhs: OpaqueSimValueSize) -> Self::Output { + rhs.checked_mul(self).expect("multiplication overflowed") + } +} + +impl Add for OpaqueSimValueSize { + type Output = OpaqueSimValueSize; + + fn add(self, rhs: OpaqueSimValueSize) -> Self::Output { + rhs.checked_add(self).expect("addition overflowed") + } +} + +impl Sub for OpaqueSimValueSize { + type Output = OpaqueSimValueSize; + + fn sub(self, rhs: OpaqueSimValueSize) -> Self::Output { + rhs.checked_sub(self).expect("subtraction underflowed") + } +} + +impl MulAssign for OpaqueSimValueSize { + fn mul_assign(&mut self, rhs: usize) { + *self = *self * rhs; + } +} + +impl AddAssign for OpaqueSimValueSize { + fn add_assign(&mut self, rhs: OpaqueSimValueSize) { + *self = *self + rhs; + } +} + +impl SubAssign for OpaqueSimValueSize { + fn sub_assign(&mut self, rhs: OpaqueSimValueSize) { + *self = *self - rhs; + } +} + +impl Sum for OpaqueSimValueSize { + fn sum>(iter: I) -> Self { + iter.fold(OpaqueSimValueSize::empty(), Add::add) } } #[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)] pub struct OpaqueSimValue { bits: UIntValue, + #[serde(skip_serializing_if = "Vec::is_empty", default)] + sim_only_values: Vec, } impl OpaqueSimValue { + pub fn empty() -> Self { + Self { + bits: UIntValue::new(Default::default()), + sim_only_values: Vec::new(), + } + } + pub fn with_capacity(capacity: OpaqueSimValueSize) -> Self { + Self { + bits: UIntValue::new(Arc::new(BitVec::with_capacity(capacity.bit_width))), + sim_only_values: Vec::with_capacity(capacity.sim_only_values_len), + } + } + pub fn size(&self) -> OpaqueSimValueSize { + OpaqueSimValueSize { + bit_width: self.bits.width(), + sim_only_values_len: self.sim_only_values.len(), + } + } + pub fn is_empty(&self) -> bool { + self.size().is_empty() + } pub fn bit_width(&self) -> usize { self.bits.width() } @@ -451,11 +761,109 @@ impl OpaqueSimValue { self.bits } pub fn from_bits(bits: UIntValue) -> Self { - Self { bits } + Self { + bits, + sim_only_values: Vec::new(), + } } pub fn from_bitslice(v: &BitSlice) -> Self { + Self::from_bitslice_and_sim_only_values(v, Vec::new()) + } + pub fn from_bitslice_and_sim_only_values( + bits: &BitSlice, + sim_only_values: Vec, + ) -> Self { Self { - bits: UIntValue::new(Arc::new(v.to_bitvec())), + bits: UIntValue::new(Arc::new(bits.to_bitvec())), + sim_only_values, + } + } + pub fn from_bits_and_sim_only_values( + bits: UIntValue, + sim_only_values: Vec, + ) -> Self { + Self { + bits, + sim_only_values, + } + } + pub fn into_parts(self) -> (UIntValue, Vec) { + let Self { + bits, + sim_only_values, + } = self; + (bits, sim_only_values) + } + pub fn parts_mut(&mut self) -> (&mut UIntValue, &mut Vec) { + let Self { + bits, + sim_only_values, + } = self; + (bits, sim_only_values) + } + pub fn sim_only_values(&self) -> &[DynSimOnlyValue] { + &self.sim_only_values + } + pub fn sim_only_values_mut(&mut self) -> &mut Vec { + &mut self.sim_only_values + } + pub fn as_slice(&self) -> OpaqueSimValueSlice<'_> { + OpaqueSimValueSlice { + bits: self.bits.bits(), + sim_only_values: &self.sim_only_values, + } + } + pub fn slice(&self, range: R) -> OpaqueSimValueSlice<'_> { + self.as_slice().slice(range) + } + pub fn rewrite_with(&mut self, target_size: OpaqueSimValueSize, f: F) + where + F: for<'b> FnOnce(OpaqueSimValueWriter<'b>) -> OpaqueSimValueWritten<'b>, // 'b is used as a brand + { + OpaqueSimValueWriter::rewrite_with(target_size, self, f); + } + pub fn clone_from_slice(&mut self, slice: OpaqueSimValueSlice<'_>) { + let OpaqueSimValueSlice { + bits, + sim_only_values, + } = slice; + self.bits.bits_mut().copy_from_bitslice(bits); + self.sim_only_values.clone_from_slice(sim_only_values); + } + pub fn extend_from_slice(&mut self, slice: OpaqueSimValueSlice<'_>) { + let OpaqueSimValueSlice { + bits, + sim_only_values, + } = slice; + self.bits.bitvec_mut().extend_from_bitslice(bits); + self.sim_only_values.extend_from_slice(sim_only_values); + } +} + +impl<'a> Extend> for OpaqueSimValue { + fn extend>>(&mut self, iter: T) { + let Self { + bits, + sim_only_values, + } = self; + let bits = bits.bitvec_mut(); + for slice in iter { + bits.extend_from_bitslice(slice.bits); + sim_only_values.extend_from_slice(slice.sim_only_values); + } + } +} + +impl Extend for OpaqueSimValue { + fn extend>(&mut self, iter: T) { + let Self { + bits, + sim_only_values, + } = self; + let bits = bits.bitvec_mut(); + for value in iter { + bits.extend_from_bitslice(value.bits().bits()); + sim_only_values.extend_from_slice(value.sim_only_values()); } } } @@ -469,6 +877,207 @@ impl> ToSimValueWithType for OpaqueSimValu } } +#[derive(Copy, Clone, Debug)] +pub struct OpaqueSimValueSlice<'a> { + bits: &'a BitSlice, + sim_only_values: &'a [DynSimOnlyValue], +} + +impl<'a> Default for OpaqueSimValueSlice<'a> { + fn default() -> Self { + Self::empty() + } +} + +impl<'a> OpaqueSimValueSlice<'a> { + pub fn from_parts(bits: &'a BitSlice, sim_only_values: &'a [DynSimOnlyValue]) -> Self { + Self { + bits, + sim_only_values, + } + } + pub fn from_bitslice(bits: &'a BitSlice) -> Self { + Self::from_parts(bits, &[]) + } + pub fn empty() -> Self { + Self { + bits: BitSlice::empty(), + sim_only_values: &[], + } + } + pub fn size(self) -> OpaqueSimValueSize { + OpaqueSimValueSize { + bit_width: self.bit_width(), + sim_only_values_len: self.sim_only_values_len(), + } + } + pub fn is_empty(self) -> bool { + self.size().is_empty() + } + pub fn bit_width(self) -> usize { + self.bits.len() + } + pub fn bits(self) -> &'a BitSlice { + self.bits + } + pub fn sim_only_values(self) -> &'a [DynSimOnlyValue] { + self.sim_only_values + } + pub fn sim_only_values_len(self) -> usize { + self.sim_only_values.len() + } + pub fn to_owned(self) -> OpaqueSimValue { + OpaqueSimValue::from_bitslice_and_sim_only_values(self.bits, self.sim_only_values.to_vec()) + } + pub fn slice(self, range: R) -> OpaqueSimValueSlice<'a> { + let start = range.start_bound(); + let end = range.end_bound(); + let bits_range = slice_range( + (start.map(|v| v.bit_width), end.map(|v| v.bit_width)), + self.bit_width(), + ); + let sim_only_values_range = slice_range( + (start.map(|v| v.bit_width), end.map(|v| v.bit_width)), + self.sim_only_values_len(), + ); + Self { + bits: &self.bits[bits_range], + sim_only_values: &self.sim_only_values[sim_only_values_range], + } + } + pub fn split_at(self, index: OpaqueSimValueSize) -> (Self, Self) { + let bits = self.bits.split_at(index.bit_width); + let sim_only_values = self.sim_only_values.split_at(index.sim_only_values_len); + ( + Self { + bits: bits.0, + sim_only_values: sim_only_values.0, + }, + Self { + bits: bits.1, + sim_only_values: sim_only_values.1, + }, + ) + } +} + +#[derive(Debug)] +pub struct OpaqueSimValueWriter<'a> { + bits: &'a mut BitSlice, + sim_only_values: &'a mut Vec, + sim_only_values_range: std::ops::Range, +} + +#[derive(Debug)] +pub struct OpaqueSimValueWritten<'a> { + _phantom: PhantomData<&'a ()>, +} + +impl<'a> OpaqueSimValueWriter<'a> { + pub fn rewrite_with(target_size: OpaqueSimValueSize, value: &mut OpaqueSimValue, f: F) + where + F: for<'b> FnOnce(OpaqueSimValueWriter<'b>) -> OpaqueSimValueWritten<'b>, // 'b is used as a brand + { + let OpaqueSimValueWritten { + _phantom: PhantomData, + } = f(OpaqueSimValueWriter::rewrite_helper(target_size, value)); + } + pub(crate) fn rewrite_helper( + target_size: OpaqueSimValueSize, + value: &'a mut OpaqueSimValue, + ) -> Self { + let (bits, sim_only_values) = value.parts_mut(); + let OpaqueSimValueSize { + bit_width, + sim_only_values_len, + } = target_size; + let bits = bits.bitvec_mut(); + bits.resize(bit_width, false); + sim_only_values.truncate(sim_only_values_len); + sim_only_values.reserve_exact(sim_only_values_len - sim_only_values.len()); + Self { + bits, + sim_only_values, + sim_only_values_range: 0..sim_only_values_len, + } + } + pub fn size(&self) -> OpaqueSimValueSize { + OpaqueSimValueSize { + bit_width: self.bit_width(), + sim_only_values_len: self.sim_only_values_len(), + } + } + pub fn bit_width(&self) -> usize { + self.bits.len() + } + pub fn sim_only_values_len(&self) -> usize { + self.sim_only_values_range.len() + } + pub fn is_empty(&self) -> bool { + self.size().is_empty() + } + pub fn fill_cloned_from_slice( + self, + slice: OpaqueSimValueSlice<'_>, + ) -> OpaqueSimValueWritten<'a> { + assert_eq!(self.size(), slice.size()); + let Self { + bits, + sim_only_values, + sim_only_values_range, + } = self; + bits.copy_from_bitslice(slice.bits); + let (clone_from_src, clone_src) = slice.sim_only_values.split_at( + (sim_only_values.len() - sim_only_values_range.start).min(slice.sim_only_values.len()), + ); + sim_only_values[sim_only_values_range][..clone_from_src.len()] + .clone_from_slice(clone_from_src); + sim_only_values.extend_from_slice(clone_src); + OpaqueSimValueWritten { + _phantom: PhantomData, + } + } + pub fn fill_with_zeros(self) -> OpaqueSimValueWritten<'a> { + assert_eq!( + self.size(), + OpaqueSimValueSize::from_bit_width(self.bit_width()), + "can't fill things other than bits with zeros", + ); + let Self { + bits, + sim_only_values, + sim_only_values_range, + } = self; + bits.fill(false); + assert_eq!(sim_only_values.len(), sim_only_values_range.end); + OpaqueSimValueWritten { + _phantom: PhantomData, + } + } + pub fn fill_prefix_with(&mut self, prefix_size: OpaqueSimValueSize, f: F) + where + F: for<'b> FnOnce(OpaqueSimValueWriter<'b>) -> OpaqueSimValueWritten<'b>, // 'b is used as a brand + { + let OpaqueSimValueSize { + bit_width, + sim_only_values_len, + } = prefix_size; + assert!(bit_width <= self.bit_width()); + assert!(sim_only_values_len <= self.sim_only_values_len()); + let next_start = self.sim_only_values_range.start + sim_only_values_len; + let OpaqueSimValueWritten { + _phantom: PhantomData, + } = f(OpaqueSimValueWriter { + bits: &mut self.bits[..bit_width], + sim_only_values: self.sim_only_values, + sim_only_values_range: self.sim_only_values_range.start..next_start, + }); + assert!(self.sim_only_values.len() >= next_start); + self.bits = &mut mem::take(&mut self.bits)[bit_width..]; + self.sim_only_values_range.start = next_start; + } +} + pub trait StaticType: Type + Default { const TYPE: Self; const MASK_TYPE: Self::MaskType; diff --git a/crates/fayalite/src/ty/serde_impls.rs b/crates/fayalite/src/ty/serde_impls.rs index 2ea4362..1af2287 100644 --- a/crates/fayalite/src/ty/serde_impls.rs +++ b/crates/fayalite/src/ty/serde_impls.rs @@ -11,6 +11,7 @@ use crate::{ phantom_const::{PhantomConstCanonicalValue, PhantomConstValue}, prelude::PhantomConst, reset::{AsyncReset, Reset, SyncReset}, + sim::value::DynSimOnlyValueType, ty::{BaseType, CanonicalType}, }; use serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -63,6 +64,7 @@ pub(crate) enum SerdeCanonicalType< Reset, Clock, PhantomConst(ThePhantomConst), + DynSimOnlyValueType(DynSimOnlyValueType), } impl SerdeCanonicalType { @@ -79,6 +81,7 @@ impl SerdeCanonicalType "a Reset", Self::Clock => "a Clock", Self::PhantomConst(_) => "a PhantomConst", + Self::DynSimOnlyValueType(_) => "a SimOnlyValue", } } } @@ -105,6 +108,7 @@ impl From for SerdeCanonicalType { CanonicalType::Reset(Reset {}) => Self::Reset, CanonicalType::Clock(Clock {}) => Self::Clock, CanonicalType::PhantomConst(ty) => Self::PhantomConst(SerdePhantomConst(ty.get())), + CanonicalType::DynSimOnlyValueType(ty) => Self::DynSimOnlyValueType(ty), } } } @@ -125,6 +129,7 @@ impl From for CanonicalType { SerdeCanonicalType::PhantomConst(value) => { Self::PhantomConst(PhantomConst::new(value.0)) } + SerdeCanonicalType::DynSimOnlyValueType(value) => Self::DynSimOnlyValueType(value), } } } diff --git a/crates/fayalite/src/util.rs b/crates/fayalite/src/util.rs index 4670a1f..f3d2c7c 100644 --- a/crates/fayalite/src/util.rs +++ b/crates/fayalite/src/util.rs @@ -36,7 +36,7 @@ pub use scoped_ref::ScopedRef; #[doc(inline)] pub use misc::{ BitSliceWriteWithBase, DebugAsDisplay, DebugAsRawString, MakeMutSlice, RcWriter, interned_bit, - iter_eq_by, + iter_eq_by, slice_range, try_slice_range, }; pub mod job_server; diff --git a/crates/fayalite/src/util/misc.rs b/crates/fayalite/src/util/misc.rs index 99b7343..d70605b 100644 --- a/crates/fayalite/src/util/misc.rs +++ b/crates/fayalite/src/util/misc.rs @@ -5,6 +5,7 @@ use bitvec::{bits, order::Lsb0, slice::BitSlice, view::BitView}; use std::{ cell::Cell, fmt::{self, Debug, Write}, + ops::{Bound, Range, RangeBounds}, rc::Rc, sync::{Arc, OnceLock}, }; @@ -209,3 +210,21 @@ impl std::io::Write for RcWriter { Ok(()) } } + +pub fn try_slice_range>(range: R, size: usize) -> Option> { + let start = match range.start_bound() { + Bound::Included(start) => *start, + Bound::Excluded(start) => start.checked_add(1)?, + Bound::Unbounded => 0, + }; + let end = match range.end_bound() { + Bound::Included(end) => end.checked_add(1)?, + Bound::Excluded(end) => *end, + Bound::Unbounded => size, + }; + (start <= end && end <= size).then_some(start..end) +} + +pub fn slice_range>(range: R, size: usize) -> Range { + try_slice_range(range, size).expect("range out of bounds") +}