From 001fd31451398eec481375d3ee1395504580a315 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Fri, 4 Apr 2025 19:05:04 -0700 Subject: [PATCH] add UIntInRange[Inclusive][Type] --- crates/fayalite/src/int.rs | 67 ++- crates/fayalite/src/int/uint_in_range.rs | 614 +++++++++++++++++++++++ crates/fayalite/src/phantom_const.rs | 7 +- crates/fayalite/tests/module.rs | 88 +++- 4 files changed, 759 insertions(+), 17 deletions(-) create mode 100644 crates/fayalite/src/int/uint_in_range.rs diff --git a/crates/fayalite/src/int.rs b/crates/fayalite/src/int.rs index 13b8cf1..053bd1d 100644 --- a/crates/fayalite/src/int.rs +++ b/crates/fayalite/src/int.rs @@ -7,6 +7,7 @@ use crate::{ target::{GetTarget, Target}, Expr, NotALiteralExpr, ToExpr, ToLiteralBits, }, + hdl, intern::{Intern, Interned, Memoize}, sim::value::{SimValue, ToSimValueWithType}, source_location::SourceLocation, @@ -30,6 +31,23 @@ use std::{ sync::Arc, }; +mod uint_in_range; + +#[hdl] +pub type UIntInRangeType = uint_in_range::UIntInRangeType; + +#[hdl] +pub type UIntInRange = + UIntInRangeType, ConstUsize>; + +#[hdl] +pub type UIntInRangeInclusiveType = + uint_in_range::UIntInRangeInclusiveType; + +#[hdl] +pub type UIntInRangeInclusive = + UIntInRangeInclusiveType, ConstUsize>; + mod sealed { pub trait BoolOrIntTypeSealed {} pub trait SizeSealed {} @@ -536,19 +554,14 @@ macro_rules! impl_int { pub const $name: $generic_name = $generic_name; impl $name { - pub fn new(width: Width::SizeType) -> Self { + pub const fn new(width: Width::SizeType) -> Self { Self { width } } pub fn width(self) -> usize { Width::as_usize(self.width) } pub fn type_properties(self) -> TypeProperties { - TypeProperties { - is_passive: true, - is_storable: true, - is_castable_from_bits: true, - bit_width: self.width(), - } + self.as_dyn_int().type_properties_dyn() } pub fn bits_from_bigint_wrapping(self, v: &BigInt) -> BitVec { BoolOrIntType::bits_from_bigint_wrapping(self, v) @@ -624,12 +637,20 @@ macro_rules! impl_int { } impl $name { - pub fn new_dyn(width: usize) -> Self { + pub const fn new_dyn(width: usize) -> Self { Self { width } } pub fn bits_to_bigint(bits: &BitSlice) -> BigInt { ::bits_to_bigint(bits) } + pub const fn type_properties_dyn(self) -> TypeProperties { + TypeProperties { + is_passive: true, + is_storable: true, + is_castable_from_bits: true, + bit_width: self.width, + } + } } impl $name { @@ -686,12 +707,10 @@ macro_rules! impl_int { impl StaticType for $name { const TYPE: Self = Self { width: Width::SIZE }; const MASK_TYPE: Self::MaskType = Bool; - const TYPE_PROPERTIES: TypeProperties = TypeProperties { - is_passive: true, - is_storable: true, - is_castable_from_bits: true, - bit_width: Width::VALUE, - }; + const TYPE_PROPERTIES: TypeProperties = $name { + width: Width::VALUE, + } + .type_properties_dyn(); const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES; } @@ -905,6 +924,10 @@ impl UInt { let v: BigUint = v.into(); Self::new(v.bits().try_into().expect("too big")) } + /// gets the smallest `UInt` that fits `v` losslessly + pub const fn for_value_usize(v: usize) -> Self { + Self::new((usize::BITS - v.leading_zeros()) as usize) + } /// gets the smallest `UInt` that fits `r` losslessly, panics if `r` is empty #[track_caller] pub fn range(r: Range>) -> Self { @@ -915,6 +938,12 @@ impl UInt { } /// gets the smallest `UInt` that fits `r` losslessly, panics if `r` is empty #[track_caller] + pub const fn range_usize(r: Range) -> Self { + assert!(r.end != 0, "empty range"); + Self::range_inclusive_usize(r.start..=(r.end - 1)) + } + /// gets the smallest `UInt` that fits `r` losslessly, panics if `r` is empty + #[track_caller] pub fn range_inclusive(r: RangeInclusive>) -> Self { let (start, end) = r.into_inner(); let start: BigUint = start.into(); @@ -924,6 +953,16 @@ impl UInt { // so must not take more bits than `end` Self::for_value(end) } + /// gets the smallest `UInt` that fits `r` losslessly, panics if `r` is empty + #[track_caller] + pub const fn range_inclusive_usize(r: RangeInclusive) -> Self { + let start = *r.start(); + let end = *r.end(); + assert!(start <= end, "empty range"); + // no need to check `start`` since it's no larger than `end` + // so must not take more bits than `end` + Self::for_value_usize(end) + } } impl SInt { diff --git a/crates/fayalite/src/int/uint_in_range.rs b/crates/fayalite/src/int/uint_in_range.rs new file mode 100644 index 0000000..0e2d07e --- /dev/null +++ b/crates/fayalite/src/int/uint_in_range.rs @@ -0,0 +1,614 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::{ + bundle::{Bundle, BundleField, BundleType, BundleTypePropertiesBuilder, NoBuilder}, + expr::{ + ops::{ExprCastTo, ExprPartialEq, ExprPartialOrd}, + CastBitsTo, CastTo, CastToBits, Expr, HdlPartialEq, HdlPartialOrd, + }, + int::{Bool, DynSize, KnownSize, Size, SizeType, UInt, UIntType}, + intern::{Intern, Interned}, + phantom_const::PhantomConst, + sim::value::{SimValue, SimValuePartialEq, ToSimValueWithType}, + source_location::SourceLocation, + ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties}, +}; +use bitvec::{order::Lsb0, slice::BitSlice, view::BitView}; +use serde::{ + de::{value::UsizeDeserializer, Error, Visitor}, + Deserialize, Deserializer, Serialize, Serializer, +}; +use std::{fmt, marker::PhantomData, ops::Index}; + +const UINT_IN_RANGE_TYPE_FIELD_NAMES: [&'static str; 2] = ["value", "range"]; + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] +pub struct UIntInRangeMaskType { + value: Bool, + range: PhantomConstRangeMaskType, +} + +impl Type for UIntInRangeMaskType { + type BaseType = Bundle; + type MaskType = Self; + type SimValue = bool; + impl_match_variant_as_self!(); + + fn mask_type(&self) -> Self::MaskType { + *self + } + + fn canonical(&self) -> CanonicalType { + CanonicalType::Bundle(Bundle::new(self.fields())) + } + + fn from_canonical(canonical_type: CanonicalType) -> Self { + let fields = Bundle::from_canonical(canonical_type).fields(); + let [BundleField { + name: value_name, + flipped: false, + ty: value, + }, BundleField { + name: range_name, + flipped: false, + ty: range, + }] = *fields + else { + panic!("expected UIntInRangeMaskType"); + }; + assert_eq!([&*value_name, &*range_name], UINT_IN_RANGE_TYPE_FIELD_NAMES); + let value = Bool::from_canonical(value); + let range = PhantomConstRangeMaskType::from_canonical(range); + Self { value, range } + } + + fn source_location() -> SourceLocation { + SourceLocation::builtin() + } + + fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { + Bool.sim_value_from_bits(bits) + } + + fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { + Bool.sim_value_clone_from_bits(value, bits); + } + + fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { + Bool.sim_value_to_bits(value, bits); + } +} + +impl BundleType for UIntInRangeMaskType { + type Builder = NoBuilder; + type FilledBuilder = Expr; + + fn fields(&self) -> Interned<[BundleField]> { + let [value_name, range_name] = UINT_IN_RANGE_TYPE_FIELD_NAMES; + let Self { value, range } = self; + [ + BundleField { + name: value_name.intern(), + flipped: false, + ty: value.canonical(), + }, + BundleField { + name: range_name.intern(), + flipped: false, + ty: range.canonical(), + }, + ][..] + .intern() + } +} + +impl StaticType for UIntInRangeMaskType { + const TYPE: Self = Self { + value: Bool, + range: PhantomConstRangeMaskType::TYPE, + }; + const MASK_TYPE: Self::MaskType = Self::TYPE; + const TYPE_PROPERTIES: TypeProperties = BundleTypePropertiesBuilder::new() + .field(false, Bool::TYPE_PROPERTIES) + .field(false, PhantomConstRangeMaskType::TYPE_PROPERTIES) + .finish(); + const MASK_TYPE_PROPERTIES: TypeProperties = Self::TYPE_PROPERTIES; +} + +impl ToSimValueWithType for bool { + fn to_sim_value_with_type(&self, ty: UIntInRangeMaskType) -> SimValue { + SimValue::from_value(ty, *self) + } +} + +impl ExprCastTo for UIntInRangeMaskType { + fn cast_to(src: Expr, to_type: Bool) -> Expr { + src.cast_to_bits().cast_to(to_type) + } +} + +impl ExprCastTo for Bool { + fn cast_to(src: Expr, to_type: UIntInRangeMaskType) -> Expr { + src.cast_to_static::>().cast_bits_to(to_type) + } +} + +impl ExprPartialEq for UIntInRangeMaskType { + fn cmp_eq(lhs: Expr, rhs: Expr) -> Expr { + lhs.cast_to_bits().cmp_eq(rhs.cast_to_bits()) + } + fn cmp_ne(lhs: Expr, rhs: Expr) -> Expr { + lhs.cast_to_bits().cmp_ne(rhs.cast_to_bits()) + } +} + +impl SimValuePartialEq for UIntInRangeMaskType { + fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { + **this == **other + } +} + +type PhantomConstRangeMaskType = > as Type>::MaskType; + +#[derive(Default, Copy, Clone, Debug)] +struct RangeParseError; + +macro_rules! define_uint_in_range_type { + ( + $UIntInRange:ident, + $UIntInRangeType:ident, + $UIntInRangeTypeWithoutGenerics:ident, + $UIntInRangeTypeWithStart:ident, + $SerdeRange:ident, + $range_operator_str:literal, + |$uint_range_usize_start:ident, $uint_range_usize_end:ident| $uint_range_usize:expr, + ) => { + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + struct $SerdeRange { + start: Start::SizeType, + end: End::SizeType, + } + + impl Default for $SerdeRange { + fn default() -> Self { + Self { + start: Start::SIZE, + end: End::SIZE, + } + } + } + + impl std::str::FromStr for $SerdeRange { + type Err = RangeParseError; + + fn from_str(s: &str) -> Result { + let Some((start, end)) = s.split_once($range_operator_str) else { + return Err(RangeParseError); + }; + if start.is_empty() + || start.bytes().any(|b| !b.is_ascii_digit()) + || end.is_empty() + || end.bytes().any(|b| !b.is_ascii_digit()) + { + return Err(RangeParseError); + } + let start = start.parse().map_err(|_| RangeParseError)?; + let end = end.parse().map_err(|_| RangeParseError)?; + let retval = Self { start, end }; + if retval.is_empty() { + Err(RangeParseError) + } else { + Ok(retval) + } + } + } + + impl fmt::Display for $SerdeRange { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { start, end } = *self; + write!( + f, + "{}{}{}", + Start::as_usize(start), + $range_operator_str, + End::as_usize(end), + ) + } + } + + impl Serialize for $SerdeRange { + fn serialize(&self, serializer: S) -> Result { + serializer.collect_str(self) + } + } + + impl<'de, Start: Size, End: Size> Deserialize<'de> for $SerdeRange { + fn deserialize>(deserializer: D) -> Result { + struct SerdeRangeVisitor(PhantomData<(Start, End)>); + impl<'de, Start: Size, End: Size> Visitor<'de> for SerdeRangeVisitor { + type Value = $SerdeRange; + + fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("a string with format \"")?; + if let Some(start) = Start::KNOWN_VALUE { + write!(f, "{start}")?; + } else { + f.write_str("")?; + }; + f.write_str($range_operator_str)?; + if let Some(end) = End::KNOWN_VALUE { + write!(f, "{end}")?; + } else { + f.write_str("")?; + }; + f.write_str("\" that is a non-empty range") + } + + fn visit_str(self, v: &str) -> Result { + let $SerdeRange:: { start, end } = + v.parse().map_err(|_| { + Error::invalid_value(serde::de::Unexpected::Str(v), &self) + })?; + let start = + Start::SizeType::deserialize(UsizeDeserializer::::new(start))?; + let end = End::SizeType::deserialize(UsizeDeserializer::::new(end))?; + Ok($SerdeRange { start, end }) + } + + fn visit_bytes(self, v: &[u8]) -> Result { + match std::str::from_utf8(v) { + Ok(v) => self.visit_str(v), + Err(_) => { + Err(Error::invalid_value(serde::de::Unexpected::Bytes(v), &self)) + } + } + } + } + deserializer.deserialize_str(SerdeRangeVisitor(PhantomData)) + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash)] + pub struct $UIntInRangeType { + value: UInt, + range: PhantomConst<$SerdeRange>, + } + + impl $UIntInRangeType { + fn from_phantom_const_range(range: PhantomConst<$SerdeRange>) -> Self { + let $SerdeRange { start, end } = *range.get(); + let $uint_range_usize_start = Start::as_usize(start); + let $uint_range_usize_end = End::as_usize(end); + Self { + value: $uint_range_usize, + range, + } + } + pub fn new(start: Start::SizeType, end: End::SizeType) -> Self { + Self::from_phantom_const_range(PhantomConst::new( + $SerdeRange { start, end }.intern_sized(), + )) + } + } + + impl fmt::Debug for $UIntInRangeType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { value, range } = self; + let $SerdeRange { start, end } = *range.get(); + f.debug_struct(&format!( + "{}<{}, {}>", + stringify!($UIntInRange), + Start::as_usize(start), + End::as_usize(end), + )) + .field("value", value) + .finish_non_exhaustive() + } + } + + impl Type for $UIntInRangeType { + type BaseType = Bundle; + type MaskType = UIntInRangeMaskType; + type SimValue = usize; + impl_match_variant_as_self!(); + + fn mask_type(&self) -> Self::MaskType { + UIntInRangeMaskType::TYPE + } + + fn canonical(&self) -> CanonicalType { + CanonicalType::Bundle(Bundle::new(self.fields())) + } + + fn from_canonical(canonical_type: CanonicalType) -> Self { + let fields = Bundle::from_canonical(canonical_type).fields(); + let [BundleField { + name: value_name, + flipped: false, + ty: value, + }, BundleField { + name: range_name, + flipped: false, + ty: range, + }] = *fields + else { + panic!("expected {}", stringify!($UIntInRange)); + }; + assert_eq!([&*value_name, &*range_name], UINT_IN_RANGE_TYPE_FIELD_NAMES); + let value = UInt::from_canonical(value); + let range = PhantomConst::<$SerdeRange>::from_canonical(range); + let retval = Self::from_phantom_const_range(range); + assert_eq!(retval, Self { value, range }); + retval + } + + fn source_location() -> SourceLocation { + SourceLocation::builtin() + } + + fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { + let mut retval = 0usize; + retval.view_bits_mut::()[..bits.len()].clone_from_bitslice(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_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { + bits.clone_from_bitslice(&value.view_bits::()[..bits.len()]); + } + } + + impl BundleType for $UIntInRangeType { + type Builder = NoBuilder; + type FilledBuilder = Expr; + + fn fields(&self) -> Interned<[BundleField]> { + let [value_name, range_name] = UINT_IN_RANGE_TYPE_FIELD_NAMES; + let Self { value, range } = self; + [ + BundleField { + name: value_name.intern(), + flipped: false, + ty: value.canonical(), + }, + BundleField { + name: range_name.intern(), + flipped: false, + ty: range.canonical(), + }, + ][..] + .intern() + } + } + + impl Default for $UIntInRangeType { + fn default() -> Self { + Self::TYPE + } + } + + impl StaticType for $UIntInRangeType { + const TYPE: Self = { + let $uint_range_usize_start = Start::VALUE; + let $uint_range_usize_end = End::VALUE; + Self { + value: $uint_range_usize, + range: PhantomConst::<$SerdeRange>::TYPE, + } + }; + const MASK_TYPE: Self::MaskType = UIntInRangeMaskType::TYPE; + const TYPE_PROPERTIES: TypeProperties = BundleTypePropertiesBuilder::new() + .field(false, Self::TYPE.value.type_properties_dyn()) + .field( + false, + PhantomConst::<$SerdeRange>::TYPE_PROPERTIES, + ) + .finish(); + const MASK_TYPE_PROPERTIES: TypeProperties = UIntInRangeMaskType::TYPE_PROPERTIES; + } + + impl ToSimValueWithType<$UIntInRangeType> for usize { + fn to_sim_value_with_type( + &self, + ty: $UIntInRangeType, + ) -> SimValue<$UIntInRangeType> { + SimValue::from_value(ty, *self) + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] + pub struct $UIntInRangeTypeWithoutGenerics; + + #[allow(non_upper_case_globals)] + pub const $UIntInRangeType: $UIntInRangeTypeWithoutGenerics = + $UIntInRangeTypeWithoutGenerics; + + impl Index for $UIntInRangeTypeWithoutGenerics { + type Output = $UIntInRangeTypeWithStart; + + fn index(&self, start: StartSize) -> &Self::Output { + Interned::into_inner($UIntInRangeTypeWithStart(start).intern_sized()) + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub struct $UIntInRangeTypeWithStart(Start::SizeType); + + impl, End: Size> + Index for $UIntInRangeTypeWithStart + { + type Output = $UIntInRangeType; + + fn index(&self, end: EndSize) -> &Self::Output { + Interned::into_inner($UIntInRangeType::new(self.0, end).intern_sized()) + } + } + + impl ExprCastTo for $UIntInRangeType { + fn cast_to(src: Expr, to_type: UInt) -> Expr { + src.cast_to_bits().cast_to(to_type) + } + } + + impl ExprCastTo<$UIntInRangeType> for UInt { + fn cast_to( + src: Expr, + to_type: $UIntInRangeType, + ) -> Expr<$UIntInRangeType> { + src.cast_bits_to(to_type) + } + } + + impl + ExprPartialEq<$UIntInRangeType> + for $UIntInRangeType + { + fn cmp_eq( + lhs: Expr, + rhs: Expr<$UIntInRangeType>, + ) -> Expr { + lhs.cast_to_bits().cmp_eq(rhs.cast_to_bits()) + } + fn cmp_ne( + lhs: Expr, + rhs: Expr<$UIntInRangeType>, + ) -> Expr { + lhs.cast_to_bits().cmp_ne(rhs.cast_to_bits()) + } + } + + impl + ExprPartialOrd<$UIntInRangeType> + for $UIntInRangeType + { + fn cmp_lt( + lhs: Expr, + rhs: Expr<$UIntInRangeType>, + ) -> Expr { + lhs.cast_to_bits().cmp_lt(rhs.cast_to_bits()) + } + fn cmp_le( + lhs: Expr, + rhs: Expr<$UIntInRangeType>, + ) -> Expr { + lhs.cast_to_bits().cmp_le(rhs.cast_to_bits()) + } + fn cmp_gt( + lhs: Expr, + rhs: Expr<$UIntInRangeType>, + ) -> Expr { + lhs.cast_to_bits().cmp_gt(rhs.cast_to_bits()) + } + fn cmp_ge( + lhs: Expr, + rhs: Expr<$UIntInRangeType>, + ) -> Expr { + lhs.cast_to_bits().cmp_ge(rhs.cast_to_bits()) + } + } + + impl + SimValuePartialEq<$UIntInRangeType> + for $UIntInRangeType + { + fn sim_value_eq( + this: &SimValue, + other: &SimValue<$UIntInRangeType>, + ) -> bool { + **this == **other + } + } + + impl ExprPartialEq> + for $UIntInRangeType + { + fn cmp_eq(lhs: Expr, rhs: Expr>) -> Expr { + lhs.cast_to_bits().cmp_eq(rhs) + } + fn cmp_ne(lhs: Expr, rhs: Expr>) -> Expr { + lhs.cast_to_bits().cmp_ne(rhs) + } + } + + impl ExprPartialEq<$UIntInRangeType> + for UIntType + { + fn cmp_eq(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { + lhs.cmp_eq(rhs.cast_to_bits()) + } + fn cmp_ne(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { + lhs.cmp_ne(rhs.cast_to_bits()) + } + } + + impl ExprPartialOrd> + for $UIntInRangeType + { + fn cmp_lt(lhs: Expr, rhs: Expr>) -> Expr { + lhs.cast_to_bits().cmp_lt(rhs) + } + fn cmp_le(lhs: Expr, rhs: Expr>) -> Expr { + lhs.cast_to_bits().cmp_le(rhs) + } + fn cmp_gt(lhs: Expr, rhs: Expr>) -> Expr { + lhs.cast_to_bits().cmp_gt(rhs) + } + fn cmp_ge(lhs: Expr, rhs: Expr>) -> Expr { + lhs.cast_to_bits().cmp_ge(rhs) + } + } + + impl ExprPartialOrd<$UIntInRangeType> + for UIntType + { + fn cmp_lt(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { + lhs.cmp_lt(rhs.cast_to_bits()) + } + fn cmp_le(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { + lhs.cmp_le(rhs.cast_to_bits()) + } + fn cmp_gt(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { + lhs.cmp_gt(rhs.cast_to_bits()) + } + fn cmp_ge(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { + lhs.cmp_ge(rhs.cast_to_bits()) + } + } + }; +} + +define_uint_in_range_type! { + UIntInRange, + UIntInRangeType, + UIntInRangeTypeWithoutGenerics, + UIntInRangeTypeWithStart, + SerdeRange, + "..", + |start, end| UInt::range_usize(start..end), +} + +define_uint_in_range_type! { + UIntInRangeInclusive, + UIntInRangeInclusiveType, + UIntInRangeInclusiveTypeWithoutGenerics, + UIntInRangeInclusiveTypeWithStart, + SerdeRangeInclusive, + "..=", + |start, end| UInt::range_inclusive_usize(start..=end), +} + +impl SerdeRange { + fn is_empty(self) -> bool { + self.start >= self.end + } +} + +impl SerdeRangeInclusive { + fn is_empty(self) -> bool { + self.start > self.end + } +} diff --git a/crates/fayalite/src/phantom_const.rs b/crates/fayalite/src/phantom_const.rs index d2e94d9..c481692 100644 --- a/crates/fayalite/src/phantom_const.rs +++ b/crates/fayalite/src/phantom_const.rs @@ -7,7 +7,7 @@ use crate::{ Expr, ToExpr, }, int::Bool, - intern::{Intern, Interned, InternedCompare, LazyInterned, Memoize}, + intern::{Intern, Interned, InternedCompare, LazyInterned, LazyInternedTrait, Memoize}, sim::value::{SimValue, SimValuePartialEq, ToSimValue, ToSimValueWithType}, source_location::SourceLocation, ty::{ @@ -228,6 +228,11 @@ impl PhantomConst { value: LazyInterned::Interned(value), } } + pub const fn new_lazy(v: &'static dyn LazyInternedTrait) -> Self { + Self { + value: LazyInterned::new_lazy(v), + } + } pub fn get(self) -> Interned { self.value.interned() } diff --git a/crates/fayalite/tests/module.rs b/crates/fayalite/tests/module.rs index 49b226a..c2dc24e 100644 --- a/crates/fayalite/tests/module.rs +++ b/crates/fayalite/tests/module.rs @@ -1,8 +1,13 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information use fayalite::{ - assert_export_firrtl, firrtl::ExportOptions, intern::Intern, - module::transform::simplify_enums::SimplifyEnumsKind, prelude::*, reset::ResetType, + assert_export_firrtl, + firrtl::ExportOptions, + int::{UIntInRange, UIntInRangeInclusive}, + intern::Intern, + module::transform::simplify_enums::SimplifyEnumsKind, + prelude::*, + reset::ResetType, ty::StaticType, }; use serde_json::json; @@ -4547,3 +4552,82 @@ circuit check_struct_cmp_eq: ", }; } + +#[hdl_module(outline_generated)] +pub fn check_uint_in_range() { + #[hdl] + let i_0_to_1: UIntInRange<0, 1> = m.input(); + #[hdl] + let i_0_to_2: UIntInRange<0, 2> = m.input(); + #[hdl] + let i_0_to_3: UIntInRange<0, 3> = m.input(); + #[hdl] + let i_0_to_4: UIntInRange<0, 4> = m.input(); + #[hdl] + let i_0_to_7: UIntInRange<0, 7> = m.input(); + #[hdl] + let i_0_to_8: UIntInRange<0, 8> = m.input(); + #[hdl] + let i_0_to_9: UIntInRange<0, 9> = m.input(); + #[hdl] + let i_0_through_0: UIntInRangeInclusive<0, 0> = m.input(); + #[hdl] + let i_0_through_1: UIntInRangeInclusive<0, 1> = m.input(); + #[hdl] + let i_0_through_2: UIntInRangeInclusive<0, 2> = m.input(); + #[hdl] + let i_0_through_3: UIntInRangeInclusive<0, 3> = m.input(); + #[hdl] + let i_0_through_4: UIntInRangeInclusive<0, 4> = m.input(); + #[hdl] + let i_0_through_7: UIntInRangeInclusive<0, 7> = m.input(); + #[hdl] + let i_0_through_8: UIntInRangeInclusive<0, 8> = m.input(); + #[hdl] + let i_0_through_9: UIntInRangeInclusive<0, 9> = m.input(); +} + +#[test] +fn test_uint_in_range() { + let _n = SourceLocation::normalize_files_for_tests(); + let m = check_uint_in_range(); + dbg!(m); + #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 + assert_export_firrtl! { + m => + "/test/check_uint_in_range.fir": r"FIRRTL version 3.2.0 +circuit check_uint_in_range: + type Ty0 = {value: UInt<0>, range: {}} + type Ty1 = {value: UInt<1>, range: {}} + type Ty2 = {value: UInt<2>, range: {}} + type Ty3 = {value: UInt<2>, range: {}} + type Ty4 = {value: UInt<3>, range: {}} + type Ty5 = {value: UInt<3>, range: {}} + type Ty6 = {value: UInt<4>, range: {}} + type Ty7 = {value: UInt<0>, range: {}} + type Ty8 = {value: UInt<1>, range: {}} + type Ty9 = {value: UInt<2>, range: {}} + type Ty10 = {value: UInt<2>, range: {}} + type Ty11 = {value: UInt<3>, range: {}} + type Ty12 = {value: UInt<3>, range: {}} + type Ty13 = {value: UInt<4>, range: {}} + type Ty14 = {value: UInt<4>, range: {}} + module check_uint_in_range: @[module-XXXXXXXXXX.rs 1:1] + input i_0_to_1: Ty0 @[module-XXXXXXXXXX.rs 2:1] + input i_0_to_2: Ty1 @[module-XXXXXXXXXX.rs 3:1] + input i_0_to_3: Ty2 @[module-XXXXXXXXXX.rs 4:1] + input i_0_to_4: Ty3 @[module-XXXXXXXXXX.rs 5:1] + input i_0_to_7: Ty4 @[module-XXXXXXXXXX.rs 6:1] + input i_0_to_8: Ty5 @[module-XXXXXXXXXX.rs 7:1] + input i_0_to_9: Ty6 @[module-XXXXXXXXXX.rs 8:1] + input i_0_through_0: Ty7 @[module-XXXXXXXXXX.rs 9:1] + input i_0_through_1: Ty8 @[module-XXXXXXXXXX.rs 10:1] + input i_0_through_2: Ty9 @[module-XXXXXXXXXX.rs 11:1] + input i_0_through_3: Ty10 @[module-XXXXXXXXXX.rs 12:1] + input i_0_through_4: Ty11 @[module-XXXXXXXXXX.rs 13:1] + input i_0_through_7: Ty12 @[module-XXXXXXXXXX.rs 14:1] + input i_0_through_8: Ty13 @[module-XXXXXXXXXX.rs 15:1] + input i_0_through_9: Ty14 @[module-XXXXXXXXXX.rs 16:1] +", + }; +}