fayalite/crates/fayalite/src/int.rs
Jacob Lifshay 9334dff776
Some checks failed
/ deps (pull_request) Successful in 17s
/ test (pull_request) Failing after 2m55s
add simulator support for sim-only values
2025-09-08 22:04:01 -07:00

1443 lines
48 KiB
Rust

// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
array::ArrayType,
expr::{
Expr, NotALiteralExpr, ToExpr, ToLiteralBits,
target::{GetTarget, Target},
},
hdl,
intern::{Intern, Interned, Memoize},
sim::value::{SimValue, ToSimValueWithType},
source_location::SourceLocation,
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::{bits, order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView};
use num_bigint::{BigInt, BigUint, Sign};
use num_traits::{One, Signed, Zero};
use serde::{
Deserialize, Deserializer, Serialize, Serializer,
de::{DeserializeOwned, Error, Visitor},
};
use std::{
borrow::{BorrowMut, Cow},
fmt,
marker::PhantomData,
num::NonZero,
ops::{Index, Not, Range, RangeBounds, RangeInclusive},
str::FromStr,
sync::Arc,
};
mod uint_in_range;
#[hdl]
pub type UIntInRangeType<Start: Size, End: Size> = uint_in_range::UIntInRangeType<Start, End>;
#[hdl]
pub type UIntInRange<const START: usize, const END: usize> =
UIntInRangeType<ConstUsize<START>, ConstUsize<END>>;
#[hdl]
pub type UIntInRangeInclusiveType<Start: Size, End: Size> =
uint_in_range::UIntInRangeInclusiveType<Start, End>;
#[hdl]
pub type UIntInRangeInclusive<const START: usize, const END: usize> =
UIntInRangeInclusiveType<ConstUsize<START>, ConstUsize<END>>;
mod sealed {
pub trait BoolOrIntTypeSealed {}
pub trait SizeSealed {}
pub trait SizeTypeSealed {}
}
pub const DYN_SIZE: usize = !0;
pub type DynSize = ConstUsize<DYN_SIZE>;
pub trait KnownSize:
GenericConstUsize + sealed::SizeTypeSealed + sealed::SizeSealed + Default
{
const SIZE: Self;
type ArrayMatch<Element: Type>: AsRef<[Expr<Element>]>
+ AsMut<[Expr<Element>]>
+ BorrowMut<[Expr<Element>]>
+ 'static
+ Send
+ Sync
+ Eq
+ Clone
+ std::hash::Hash
+ std::fmt::Debug
+ IntoIterator<Item = Expr<Element>>
+ TryFrom<Vec<Expr<Element>>>
+ Into<Vec<Expr<Element>>>;
type ArraySimValue<Element: Type>: AsRef<[SimValue<Element>]>
+ AsMut<[SimValue<Element>]>
+ BorrowMut<[SimValue<Element>]>
+ 'static
+ Clone
+ std::fmt::Debug
+ IntoIterator<Item = SimValue<Element>>
+ TryFrom<Vec<SimValue<Element>>>
+ Into<Vec<SimValue<Element>>>
+ ToSimValueWithType<ArrayType<Element, Self>>;
}
macro_rules! known_widths {
([] $($bits:literal)+) => {
impl KnownSize for ConstUsize<{
let v = 0;
$(let v = v * 2 + $bits;)*
v
}> {
const SIZE: Self = Self;
type ArrayMatch<Element: Type> = [Expr<Element>; Self::VALUE];
type ArraySimValue<Element: Type> = [SimValue<Element>; Self::VALUE];
}
};
([2 $($rest:tt)*] $($bits:literal)+) => {
known_widths!([$($rest)*] $($bits)* 0);
known_widths!([$($rest)*] $($bits)* 1);
};
([2 $($rest:tt)*]) => {
known_widths!([$($rest)*] 0);
known_widths!([$($rest)*] 1);
impl KnownSize for ConstUsize<{2 $(* $rest)*}> {
const SIZE: Self = Self;
type ArrayMatch<Element: Type> = [Expr<Element>; Self::VALUE];
type ArraySimValue<Element: Type> = [SimValue<Element>; Self::VALUE];
}
};
}
known_widths!([2 2 2 2 2 2 2 2 2]);
pub trait SizeType:
sealed::SizeTypeSealed
+ Copy
+ Ord
+ std::hash::Hash
+ std::fmt::Debug
+ Send
+ Sync
+ 'static
+ Serialize
+ DeserializeOwned
{
type Size: Size<SizeType = Self>;
}
pub trait Size:
sealed::SizeSealed
+ Copy
+ Ord
+ std::hash::Hash
+ std::fmt::Debug
+ Send
+ Sync
+ 'static
+ Serialize
+ DeserializeOwned
{
type ArrayMatch<Element: Type>: AsRef<[Expr<Element>]>
+ AsMut<[Expr<Element>]>
+ BorrowMut<[Expr<Element>]>
+ 'static
+ Send
+ Sync
+ Eq
+ Clone
+ std::hash::Hash
+ std::fmt::Debug
+ IntoIterator<Item = Expr<Element>>
+ TryFrom<Vec<Expr<Element>>>
+ Into<Vec<Expr<Element>>>;
type ArraySimValue<Element: Type>: AsRef<[SimValue<Element>]>
+ AsMut<[SimValue<Element>]>
+ BorrowMut<[SimValue<Element>]>
+ 'static
+ Clone
+ std::fmt::Debug
+ IntoIterator<Item = SimValue<Element>>
+ TryFrom<Vec<SimValue<Element>>>
+ Into<Vec<SimValue<Element>>>
+ ToSimValueWithType<ArrayType<Element, Self>>;
const KNOWN_VALUE: Option<usize>;
type SizeType: SizeType<Size = Self>
+ Copy
+ Ord
+ std::hash::Hash
+ std::fmt::Debug
+ Send
+ Sync
+ 'static;
fn as_usize(size_type: Self::SizeType) -> usize;
fn try_from_usize(v: usize) -> Option<Self::SizeType>;
#[track_caller]
fn from_usize(v: usize) -> Self::SizeType {
Self::try_from_usize(v).expect("wrong size")
}
}
impl sealed::SizeTypeSealed for usize {}
impl SizeType for usize {
type Size = DynSize;
}
impl Size for DynSize {
type ArrayMatch<Element: Type> = Box<[Expr<Element>]>;
type ArraySimValue<Element: Type> = Box<[SimValue<Element>]>;
const KNOWN_VALUE: Option<usize> = None;
type SizeType = usize;
fn as_usize(size_type: Self::SizeType) -> usize {
size_type
}
fn try_from_usize(v: usize) -> Option<Self::SizeType> {
Some(v)
}
}
impl<const VALUE: usize> sealed::SizeSealed for ConstUsize<VALUE> {}
impl<const VALUE: usize> sealed::SizeTypeSealed for ConstUsize<VALUE> {}
impl<T: KnownSize> SizeType for T {
type Size = T;
}
impl<T: KnownSize> Size for T {
type ArrayMatch<Element: Type> = <T as KnownSize>::ArrayMatch<Element>;
type ArraySimValue<Element: Type> = <T as KnownSize>::ArraySimValue<Element>;
const KNOWN_VALUE: Option<usize> = Some(T::VALUE);
type SizeType = T;
fn as_usize(_size_type: Self::SizeType) -> usize {
T::VALUE
}
fn try_from_usize(v: usize) -> Option<Self::SizeType> {
if v == T::VALUE { Some(T::SIZE) } else { None }
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum ParseIntValueError {
Empty,
InvalidDigit,
MissingDigits,
InvalidRadix,
MissingType,
InvalidType,
TypeMismatch {
parsed_signed: bool,
parsed_width: usize,
expected_signed: bool,
expected_width: usize,
},
PosOverflow,
NegOverflow,
WidthOverflow,
MissingWidth,
}
impl std::error::Error for ParseIntValueError {}
impl fmt::Display for ParseIntValueError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::Empty => "can't parse integer from empty string",
Self::InvalidDigit => "invalid digit",
Self::MissingDigits => "missing digits",
Self::InvalidRadix => "invalid radix",
Self::MissingType => "missing type",
Self::InvalidType => "invalid type",
Self::TypeMismatch {
parsed_signed,
parsed_width,
expected_signed,
expected_width,
} => {
return write!(
f,
"type mismatch: parsed type {parsed_signed_str}{parsed_width}, \
expected type {expected_signed_str}{expected_width}",
parsed_signed_str = if *parsed_signed { "i" } else { "u" },
expected_signed_str = if *expected_signed { "i" } else { "u" },
);
}
Self::PosOverflow => "value too large to fit in type",
Self::NegOverflow => "value too small to fit in type",
Self::WidthOverflow => "width is too large",
Self::MissingWidth => "missing width",
})
}
}
fn parse_int_value(
s: &str,
type_is_signed: bool,
type_width: Option<usize>,
parse_type: bool,
) -> Result<Arc<BitVec>, ParseIntValueError> {
if !parse_type && type_width.is_none() {
return Err(ParseIntValueError::MissingWidth);
}
let mut s = s.trim();
if s.is_empty() {
return Err(ParseIntValueError::Empty);
}
let negative = match s.bytes().next() {
Some(ch @ (b'+' | b'-')) => {
s = s[1..].trim_start();
ch == b'-'
}
_ => false,
};
let radix = match s.bytes().next() {
Some(b'0') => match s.bytes().nth(1) {
Some(b'x' | b'X') => {
s = &s[2..];
16
}
Some(b'b' | b'B') => {
s = &s[2..];
2
}
Some(b'o' | b'O') => {
s = &s[2..];
8
}
_ => 10,
},
Some(b'1'..=b'9') => 10,
_ => return Err(ParseIntValueError::InvalidDigit),
};
let mut any_digits = false;
let digits_end = s
.as_bytes()
.iter()
.position(|&ch| {
if ch == b'_' {
false
} else if (ch as char).to_digit(radix).is_some() {
any_digits = true;
false
} else {
true
}
})
.unwrap_or(s.len());
let digits = &s[..digits_end];
s = &s[digits_end..];
if !any_digits {
return Err(ParseIntValueError::MissingDigits);
}
let width = if parse_type {
const HDL_PREFIX: &[u8] = b"hdl_";
let mut missing_type = ParseIntValueError::MissingType;
if s.as_bytes()
.get(..HDL_PREFIX.len())
.is_some_and(|bytes| bytes.eq_ignore_ascii_case(HDL_PREFIX))
{
s = &s[HDL_PREFIX.len()..];
missing_type = ParseIntValueError::InvalidType;
}
let signed = match s.bytes().next() {
Some(b'u' | b'U') => false,
Some(b'i' | b'I') => true,
Some(_) => return Err(ParseIntValueError::InvalidType),
None => return Err(missing_type),
};
s = &s[1..];
let mut width = 0usize;
let mut any_digits = false;
for ch in s.bytes() {
let digit = (ch as char)
.to_digit(10)
.ok_or(ParseIntValueError::InvalidDigit)?;
any_digits = true;
width = width
.checked_mul(10)
.and_then(|v| v.checked_add(digit as usize))
.ok_or(ParseIntValueError::WidthOverflow)?;
}
if !any_digits {
return Err(ParseIntValueError::MissingDigits);
}
if width > <BitSlice>::MAX_BITS {
return Err(ParseIntValueError::WidthOverflow);
}
let expected_width = type_width.unwrap_or(width);
if type_is_signed != signed || expected_width != width {
let expected_width = type_width.unwrap_or(width);
return Err(ParseIntValueError::TypeMismatch {
parsed_signed: signed,
parsed_width: width,
expected_signed: type_is_signed,
expected_width,
});
}
width
} else {
if !s.is_empty() {
return Err(ParseIntValueError::InvalidDigit);
}
type_width.expect("checked earlier")
};
if !type_is_signed && negative {
return Err(ParseIntValueError::InvalidDigit);
}
if radix == 10 {
let mut value: BigInt = digits
.replace("_", "")
.parse()
.expect("checked that the digits are valid already");
if negative {
value = -value;
}
let uint_value: UIntValue = UInt::new(width).from_bigint_wrapping(&value);
if value.is_zero() {
Ok(uint_value.into_bits())
} else {
for i in 0..width {
value.set_bit(i as u64, type_is_signed && negative);
}
if value.is_zero() {
Ok(uint_value.into_bits())
} else if type_is_signed && negative {
if value.sign() == Sign::Minus && value.magnitude().is_one() {
Ok(uint_value.into_bits())
} else {
Err(ParseIntValueError::NegOverflow)
}
} else {
Err(ParseIntValueError::PosOverflow)
}
}
} else {
let mut value = BitVec::repeat(false, width);
let bits_per_digit = match radix {
2 => 1,
8 => 3,
16 => 4,
_ => unreachable!(),
};
let mut digits = digits
.bytes()
.rev()
.filter_map(|ch| (ch as char).to_digit(radix));
let overflow_error = if negative {
ParseIntValueError::NegOverflow
} else {
ParseIntValueError::PosOverflow
};
for chunk in value.chunks_mut(bits_per_digit) {
if let Some(mut digit) = digits.next() {
let digit_bits = &mut digit.view_bits_mut::<Lsb0>()[..chunk.len()];
chunk.clone_from_bitslice(digit_bits);
digit_bits.fill(false);
if digit != 0 {
return Err(overflow_error);
}
} else {
break;
}
}
for digit in digits {
if digit != 0 {
return Err(overflow_error);
}
}
let negative_zero = if negative {
// negating a value happens in three regions:
// * the least-significant zeros, which are left as zeros
// * the least-significant one bit, which is left as a one bit
// * all the most-significant bits, which are inverted
// e.g.:
const {
let inp = 0b1010_1_000_u8;
let out = 0b0101_1_000_u8;
assert!(inp.wrapping_neg() == out);
};
if let Some(first_one) = value.first_one() {
let most_significant_bits = &mut value[first_one + 1..];
// modifies in-place despite using `Not::not`
let _ = Not::not(most_significant_bits);
false
} else {
true
}
} else {
false
};
if !negative_zero && type_is_signed && negative != value[value.len() - 1] {
Err(overflow_error)
} else {
Ok(Arc::new(value))
}
}
}
fn deserialize_int_value<'de, D: Deserializer<'de>>(
deserializer: D,
type_is_signed: bool,
type_width: Option<usize>,
) -> Result<Arc<BitVec>, D::Error> {
struct IntValueVisitor {
type_is_signed: bool,
type_width: Option<usize>,
}
impl<'de> Visitor<'de> for IntValueVisitor {
type Value = Arc<BitVec>;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(if self.type_is_signed {
"SIntValue"
} else {
"UIntValue"
})?;
if let Some(type_width) = self.type_width {
write!(f, "<{type_width}>")?;
}
Ok(())
}
fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
parse_int_value(v, self.type_is_signed, self.type_width, true).map_err(E::custom)
}
fn visit_bytes<E: Error>(self, v: &[u8]) -> Result<Self::Value, E> {
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(IntValueVisitor {
type_is_signed,
type_width,
})
}
macro_rules! impl_int {
($pretty_name:ident, $name:ident, $generic_name:ident, $value:ident, $SIGNED:literal) => {
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct $name<Width: Size = DynSize> {
pub width: Width::SizeType,
}
impl<Width: Size> fmt::Debug for $name<Width> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}<{}>", stringify!($pretty_name), self.width())
}
}
pub type $pretty_name<const WIDTH: usize = DYN_SIZE> = $name<ConstUsize<WIDTH>>;
#[allow(non_upper_case_globals)]
pub const $pretty_name: $generic_name = $generic_name;
#[allow(non_upper_case_globals)]
pub const $name: $generic_name = $generic_name;
impl<Width: Size> $name<Width> {
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 {
self.as_dyn_int().type_properties_dyn()
}
pub fn bits_from_bigint_wrapping(self, v: &BigInt) -> BitVec {
BoolOrIntType::bits_from_bigint_wrapping(self, v)
}
pub fn from_bigint_wrapping(self, v: &BigInt) -> $value<Width> {
$value {
bits: Arc::new(self.bits_from_bigint_wrapping(v)),
_phantom: PhantomData,
}
}
pub fn from_int_wrapping(self, v: impl Into<BigInt>) -> $value<Width> {
self.from_bigint_wrapping(&v.into())
}
pub fn zero(self) -> $value<Width> {
self.from_int_wrapping(0u8)
}
pub fn can_connect<RhsWidth: Size>(self, _rhs: $name<RhsWidth>) -> bool {
true
}
}
impl<Width: Size> sealed::BoolOrIntTypeSealed for $name<Width> {}
impl<Width: Size> BoolOrIntType for $name<Width> {
type Width = Width;
type Signed = ConstBool<$SIGNED>;
type Value = $value<Width>;
fn width(self) -> usize {
$name::width(self)
}
fn new(width: Width::SizeType) -> Self {
$name { width }
}
fn value_from_bigint_wrapping(self, v: &BigInt) -> Self::Value {
$value::<Width>::from_bigint_wrapping(self, v)
}
fn bits_to_value(bits: Cow<'_, BitSlice>) -> Self::Value {
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
struct MemoizeBitsToValue;
impl Memoize for MemoizeBitsToValue {
type Input = BitSlice;
type InputOwned = BitVec;
type Output = Arc<BitVec>;
fn inner(self, input: &Self::Input) -> Self::Output {
Arc::new(input.to_bitvec())
}
}
$value::new(MemoizeBitsToValue.get_cow(bits))
}
fn bits_to_expr(bits: Cow<'_, BitSlice>) -> Expr<Self> {
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
struct MemoizeBitsToExpr;
impl Memoize for MemoizeBitsToExpr {
type Input = BitSlice;
type InputOwned = BitVec;
type Output = Expr<$name>;
fn inner(self, input: &Self::Input) -> Self::Output {
$value::new(Arc::new(input.to_bitvec())).to_expr()
}
}
Expr::from_dyn_int(MemoizeBitsToExpr.get_cow(bits))
}
fn from_str_without_ty(
self,
s: &str,
) -> Result<Self::Value, <Self::Value as FromStr>::Err> {
parse_int_value(s, $SIGNED, Some(self.width()), false).map(Self::Value::new)
}
}
impl<Width: Size> IntType for $name<Width> {
type Dyn = $name;
}
impl $name {
pub const fn new_dyn(width: usize) -> Self {
Self { width }
}
pub fn bits_to_bigint(bits: &BitSlice) -> BigInt {
<Self as BoolOrIntType>::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,
sim_only_values_len: 0,
}
}
}
impl<Width: KnownSize> $name<Width> {
pub fn new_static() -> Self {
Self { width: Width::SIZE }
}
}
impl<Width: Size> Type for $name<Width> {
type BaseType = $pretty_name;
type MaskType = Bool;
type SimValue = $value<Width>;
impl_match_variant_as_self!();
fn mask_type(&self) -> Self::MaskType {
Bool
}
fn canonical(&self) -> CanonicalType {
CanonicalType::$pretty_name($name::new_dyn(self.width()))
}
#[track_caller]
fn from_canonical(canonical_type: CanonicalType) -> Self {
let CanonicalType::$pretty_name(retval) = canonical_type else {
panic!("expected {}", stringify!($pretty_name));
};
$name {
width: Width::from_usize(retval.width),
}
}
fn source_location() -> SourceLocation {
SourceLocation::builtin()
}
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_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(opaque.bits());
}
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());
writer.fill_cloned_from_slice(OpaqueSimValueSlice::from_bitslice(value.bits()))
}
}
impl<Width: KnownSize> Default for $name<Width> {
fn default() -> Self {
Self::TYPE
}
}
impl<Width: KnownSize> StaticType for $name<Width> {
const TYPE: Self = Self { width: Width::SIZE };
const MASK_TYPE: Self::MaskType = Bool;
const TYPE_PROPERTIES: TypeProperties = $name {
width: Width::VALUE,
}
.type_properties_dyn();
const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES;
}
impl<Width: Size> Serialize for $name<Width> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.canonical().serialize(serializer)
}
}
impl<'de, Width: Size> Deserialize<'de> for $name<Width> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let name = |width| -> String {
if let Some(width) = width {
format!("a {}<{width}>", stringify!($pretty_name))
} else {
format!("a {}", stringify!($pretty_name))
}
};
match CanonicalType::deserialize(deserializer)? {
CanonicalType::$pretty_name(retval) => {
if let Some(width) = Width::try_from_usize(retval.width()) {
Ok($name { width })
} else {
Err(Error::invalid_value(
serde::de::Unexpected::Other(&name(Some(retval.width()))),
&&*name(Width::KNOWN_VALUE),
))
}
}
ty => Err(Error::invalid_value(
serde::de::Unexpected::Other(ty.as_serde_unexpected_str()),
&&*name(Width::KNOWN_VALUE),
)),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
pub struct $generic_name;
impl<Width: SizeType> Index<Width> for $generic_name {
type Output = $name<Width::Size>;
fn index(&self, width: Width) -> &Self::Output {
Interned::into_inner(Intern::intern_sized($name::new(width)))
}
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct $value<Width: Size = DynSize> {
bits: Arc<BitVec>,
_phantom: PhantomData<Width>,
}
impl<Width: Size> fmt::Display for $value<Width> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let value = self.to_bigint();
let (sign, magnitude) = value.into_parts();
let sign = match sign {
Sign::Minus => "-",
Sign::NoSign | Sign::Plus => "",
};
let signed = if $SIGNED { "i" } else { "u" };
let width = self.width();
write!(f, "{sign}0x{magnitude:X}_{signed}{width}")
}
}
impl<Width: Size> fmt::Debug for $value<Width> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl<Width: Size> std::str::FromStr for $value<Width> {
type Err = ParseIntValueError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
parse_int_value(s, $SIGNED, Width::KNOWN_VALUE, true).map(Self::new)
}
}
impl<Width: Size> Serialize for $value<Width> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.to_string().serialize(serializer)
}
}
impl<'de, Width: Size> Deserialize<'de> for $value<Width> {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
deserialize_int_value(deserializer, $SIGNED, Width::KNOWN_VALUE).map(Self::new)
}
}
impl<Width: Size> PartialOrd for $value<Width> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
if self.width() != other.width() {
return None;
}
Some(self.to_bigint().cmp(&other.to_bigint()))
}
}
impl<Width: Size> From<$value<Width>> for BigInt {
fn from(v: $value<Width>) -> BigInt {
v.to_bigint()
}
}
impl<Width: Size> $value<Width> {
pub fn width(&self) -> usize {
if let Some(retval) = Width::KNOWN_VALUE {
debug_assert!(self.bits.len() == retval);
retval
} else {
self.bits.len()
}
}
pub fn from_bigint_wrapping(ty: $name<Width>, v: &BigInt) -> $value<Width> {
ty.from_bigint_wrapping(v)
}
pub fn to_bigint(&self) -> BigInt {
$name::bits_to_bigint(&self.bits)
}
pub fn into_bits(self) -> Arc<BitVec> {
self.bits
}
pub fn ty(&self) -> $name<Width> {
$name {
width: Width::from_usize(self.width()),
}
}
pub fn as_dyn_int(self) -> $value {
$value {
bits: self.bits,
_phantom: PhantomData,
}
}
pub fn bits(&self) -> &Arc<BitVec> {
&self.bits
}
pub fn bits_mut(&mut self) -> &mut BitSlice {
Arc::<BitVec>::make_mut(&mut self.bits)
}
}
impl<Width: Size> ToLiteralBits for $value<Width> {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
Ok(BitSlice::intern(&self.bits))
}
}
impl<Width: Size> GetTarget for $value<Width> {
fn target(&self) -> Option<Interned<Target>> {
None
}
}
impl<Width: Size> $value<Width> {
pub fn new(bits: Arc<BitVec>) -> Self {
Width::from_usize(bits.len()); // check that len is correct
Self {
bits,
_phantom: PhantomData,
}
}
}
impl<Width: KnownSize> $value<Width> {
pub fn new_static(bits: Arc<BitVec>) -> Self {
assert!(bits.len() == Width::VALUE);
Self {
bits,
_phantom: PhantomData,
}
}
}
impl $value {
pub fn new_dyn(bits: Arc<BitVec>) -> Self {
Self {
bits,
_phantom: PhantomData,
}
}
pub fn bitvec_mut(&mut self) -> &mut BitVec {
Arc::make_mut(&mut self.bits)
}
}
};
}
impl_int!(UInt, UIntType, UIntWithoutGenerics, UIntValue, false);
impl_int!(SInt, SIntType, SIntWithoutGenerics, SIntValue, true);
impl UInt {
/// gets the smallest `UInt` that fits `v` losslessly
pub fn for_value(v: impl Into<BigUint>) -> Self {
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<impl Into<BigUint>>) -> Self {
let start: BigUint = r.start.into();
let end: BigUint = r.end.into();
assert!(!end.is_zero(), "empty range");
Self::range_inclusive(start..=(end - 1u8))
}
/// gets the smallest `UInt` that fits `r` losslessly, panics if `r` is empty
#[track_caller]
pub const fn range_usize(r: Range<usize>) -> 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<impl Into<BigUint>>) -> Self {
let (start, end) = r.into_inner();
let start: BigUint = start.into();
let end: BigUint = end.into();
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(end)
}
/// gets the smallest `UInt` that fits `r` losslessly, panics if `r` is empty
#[track_caller]
pub const fn range_inclusive_usize(r: RangeInclusive<usize>) -> 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 {
/// gets the smallest `SInt` that fits `v` losslessly
pub fn for_value(v: impl Into<BigInt>) -> Self {
let v: BigInt = v.into();
Self::new(
match v.sign() {
Sign::Minus => {
// account for sign bit and for the minimum value of an `SInt`
// being the negative of the maximum value minus one.
v.not().bits().checked_add(1).expect("too big")
}
Sign::NoSign => 0,
Sign::Plus => {
// account for sign bit
v.bits().checked_add(1).expect("too big")
}
}
.try_into()
.expect("too big"),
)
}
/// gets the smallest `SInt` that fits `r` losslessly, panics if `r` is empty
#[track_caller]
pub fn range(r: Range<impl Into<BigInt>>) -> Self {
let start: BigInt = r.start.into();
let end: BigInt = r.end.into();
Self::range_inclusive(start..=(end - 1))
}
/// gets the smallest `SInt` that fits `r` losslessly, panics if `r` is empty
#[track_caller]
pub fn range_inclusive(r: RangeInclusive<impl Into<BigInt>>) -> Self {
let (start, end) = r.into_inner();
let start: BigInt = start.into();
let end: BigInt = end.into();
assert!(start <= end, "empty range");
Self::new(Self::for_value(start).width.max(Self::for_value(end).width))
}
}
macro_rules! impl_prim_int {
(
$(#[$meta:meta])*
$prim_int:ident, $ty:ty
) => {
impl From<$prim_int> for <$ty as BoolOrIntType>::Value {
fn from(v: $prim_int) -> Self {
<$ty>::le_bytes_to_value_wrapping(
&v.to_le_bytes(),
<$ty as BoolOrIntType>::Width::VALUE,
)
}
}
impl From<NonZero<$prim_int>> for <$ty as BoolOrIntType>::Value {
fn from(v: NonZero<$prim_int>) -> Self {
v.get().into()
}
}
$(#[$meta])*
impl ToExpr for $prim_int {
type Type = $ty;
fn to_expr(&self) -> Expr<Self::Type> {
<$ty>::le_bytes_to_expr_wrapping(
&self.to_le_bytes(),
<$ty as BoolOrIntType>::Width::VALUE,
)
}
}
$(#[$meta])*
impl ToExpr for NonZero<$prim_int> {
type Type = $ty;
fn to_expr(&self) -> Expr<Self::Type> {
self.get().to_expr()
}
}
};
}
impl_prim_int!(u8, UInt<8>);
impl_prim_int!(u16, UInt<16>);
impl_prim_int!(u32, UInt<32>);
impl_prim_int!(u64, UInt<64>);
impl_prim_int!(u128, UInt<128>);
impl_prim_int!(i8, SInt<8>);
impl_prim_int!(i16, SInt<16>);
impl_prim_int!(i32, SInt<32>);
impl_prim_int!(i64, SInt<64>);
impl_prim_int!(i128, SInt<128>);
impl_prim_int!(
/// for portability reasons, [`usize`] always translates to [`UInt<64>`][type@UInt]
usize, UInt<64>
);
impl_prim_int!(
/// for portability reasons, [`isize`] always translates to [`SInt<64>`][type@SInt]
isize, SInt<64>
);
pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed {
type Width: Size;
type Signed: GenericConstBool;
type Value: Clone
+ PartialOrd
+ Eq
+ std::hash::Hash
+ fmt::Debug
+ fmt::Display
+ Send
+ Sync
+ 'static
+ ToExpr<Type = Self>
+ Into<BigInt>
+ std::str::FromStr;
fn width(self) -> usize;
fn new(width: <Self::Width as Size>::SizeType) -> Self;
fn new_static() -> Self
where
Self::Width: KnownSize + Size<SizeType = Self::Width>,
{
Self::new(Self::Width::default())
}
fn as_same_width_sint(self) -> SIntType<Self::Width> {
SIntType::new(Self::Width::from_usize(self.width()))
}
fn as_same_width_uint(self) -> UIntType<Self::Width> {
UIntType::new(Self::Width::from_usize(self.width()))
}
fn value_from_int_wrapping(self, v: impl Into<BigInt>) -> Self::Value {
self.value_from_bigint_wrapping(&v.into())
}
fn value_from_bigint_wrapping(self, v: &BigInt) -> Self::Value;
fn bits_from_bigint_wrapping(self, v: &BigInt) -> BitVec {
let mut bits = BitVec::repeat(false, self.width());
Self::copy_bits_from_bigint_wrapping(v, &mut bits);
bits
}
fn copy_bits_from_bigint_wrapping(v: &BigInt, bits: &mut BitSlice) {
let width = bits.len();
let mut bytes = v.to_signed_bytes_le();
bytes.resize(
width.div_ceil(u8::BITS as usize),
if v.is_negative() { 0xFF } else { 0 },
);
let bitslice = &BitSlice::<u8, Lsb0>::from_slice(&bytes)[..width];
bits.clone_from_bitslice(bitslice);
}
fn bits_equal_bigint_wrapping(v: &BigInt, bits: &BitSlice) -> bool {
bits.iter()
.by_vals()
.enumerate()
.all(|(bit_index, bit): (usize, bool)| v.bit(bit_index as u64) == bit)
}
fn bits_to_bigint(bits: &BitSlice) -> BigInt {
let sign_byte = if Self::Signed::VALUE && bits.last().as_deref().copied().unwrap_or(false) {
0xFF
} else {
0
};
let mut bytes = vec![sign_byte; bits.len() / u8::BITS as usize + 1];
BitSlice::<u8, Lsb0>::from_slice_mut(&mut bytes)[..bits.len()].clone_from_bitslice(bits);
BigInt::from_signed_bytes_le(&bytes)
}
fn bits_to_value(bits: Cow<'_, BitSlice>) -> Self::Value;
fn bits_to_expr(bits: Cow<'_, BitSlice>) -> Expr<Self>;
fn le_bytes_to_bits_wrapping(bytes: &[u8], bit_width: usize) -> BitVec {
let bitslice = BitSlice::<u8, Lsb0>::from_slice(bytes);
let bitslice = &bitslice[..bit_width.min(bitslice.len())];
let mut bits = BitVec::new();
bits.extend_from_bitslice(bitslice);
bits.resize(
bit_width,
Self::Signed::VALUE && bits.last().as_deref().copied().unwrap_or(false),
);
bits
}
fn le_bytes_to_expr_wrapping(bytes: &[u8], bit_width: usize) -> Expr<Self> {
Self::bits_to_expr(Cow::Owned(Self::le_bytes_to_bits_wrapping(
bytes, bit_width,
)))
}
fn le_bytes_to_value_wrapping(bytes: &[u8], bit_width: usize) -> Self::Value {
Self::bits_to_value(Cow::Owned(Self::le_bytes_to_bits_wrapping(
bytes, bit_width,
)))
}
fn from_str_without_ty(self, s: &str) -> Result<Self::Value, <Self::Value as FromStr>::Err>;
}
pub trait IntType:
BoolOrIntType<BaseType = <Self as IntType>::Dyn, Value: FromStr<Err = ParseIntValueError>>
{
type Dyn: IntType<Dyn = Self::Dyn, Signed = Self::Signed, Width = DynSize>;
fn as_dyn_int(self) -> Self::Dyn {
Self::new_dyn(self.width())
}
fn try_from_dyn_int(value: Self::Dyn) -> Option<Self> {
Some(Self::new(Self::Width::try_from_usize(value.width())?))
}
#[track_caller]
fn from_dyn_int(value: Self::Dyn) -> Self {
Self::try_from_dyn_int(value).expect("wrong size")
}
fn new_dyn(width: usize) -> Self::Dyn {
Self::Dyn::new(width)
}
fn slice_index_to_range<I: RangeBounds<usize>>(self, index: I) -> Range<usize> {
slice_range(index, self.width())
}
fn slice_and_shift<I: RangeBounds<usize>>(self, index: I) -> (UInt, usize) {
let range = self.slice_index_to_range(index);
let width = range.end - range.start;
(UInt::new_dyn(width), range.start)
}
fn slice<I: RangeBounds<usize>>(self, index: I) -> UInt {
self.slice_and_shift(index).0
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
pub struct Bool;
impl sealed::BoolOrIntTypeSealed for Bool {}
impl BoolOrIntType for Bool {
type Width = ConstUsize<1>;
type Signed = ConstBool<false>;
type Value = bool;
fn width(self) -> usize {
1
}
fn new(width: <Self::Width as Size>::SizeType) -> Self {
let ConstUsize {} = width;
Bool
}
fn value_from_bigint_wrapping(self, v: &BigInt) -> Self::Value {
v.bit(0)
}
fn bits_to_expr(bits: Cow<'_, BitSlice>) -> Expr<Self> {
assert_eq!(bits.len(), 1);
bits[0].to_expr()
}
fn bits_to_value(bits: Cow<'_, BitSlice>) -> Self::Value {
assert_eq!(bits.len(), 1);
bits[0]
}
fn from_str_without_ty(self, s: &str) -> Result<Self::Value, <Self::Value as FromStr>::Err> {
FromStr::from_str(s)
}
}
impl Bool {
pub fn type_properties(self) -> TypeProperties {
Self::TYPE_PROPERTIES
}
pub fn can_connect(self, _rhs: Self) -> bool {
true
}
}
impl Type for Bool {
type BaseType = Bool;
type MaskType = Bool;
type SimValue = bool;
impl_match_variant_as_self!();
fn mask_type(&self) -> Self::MaskType {
Bool
}
fn canonical(&self) -> CanonicalType {
CanonicalType::Bool(Bool)
}
#[track_caller]
fn from_canonical(canonical_type: CanonicalType) -> Self {
let CanonicalType::Bool(retval) = canonical_type else {
panic!("expected Bool");
};
retval
}
fn source_location() -> SourceLocation {
SourceLocation::builtin()
}
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_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_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],
))
}
}
impl StaticType for Bool {
const TYPE: Self = Bool;
const MASK_TYPE: Self::MaskType = Bool;
const TYPE_PROPERTIES: TypeProperties = TypeProperties {
is_passive: true,
is_storable: true,
is_castable_from_bits: true,
bit_width: 1,
sim_only_values_len: 0,
};
const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES;
}
impl ToLiteralBits for bool {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
Ok(interned_bit(*self))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_different_value_widths_compare_ne() {
// interning relies on [SU]IntValue with different `width` comparing not equal
assert_ne!(UInt[3].from_int_wrapping(0), UInt[4].from_int_wrapping(0));
assert_ne!(SInt[3].from_int_wrapping(0), SInt[4].from_int_wrapping(0));
}
#[test]
fn test_uint_for_value() {
assert_eq!(UInt::for_value(0u8).width, 0);
assert_eq!(UInt::for_value(1u8).width, 1);
assert_eq!(UInt::for_value(2u8).width, 2);
assert_eq!(UInt::for_value(3u8).width, 2);
assert_eq!(UInt::for_value(4u8).width, 3);
}
#[test]
fn test_sint_for_value() {
assert_eq!(SInt::for_value(-5).width, 4);
assert_eq!(SInt::for_value(-4).width, 3);
assert_eq!(SInt::for_value(-3).width, 3);
assert_eq!(SInt::for_value(-2).width, 2);
assert_eq!(SInt::for_value(-1).width, 1);
assert_eq!(SInt::for_value(0).width, 0);
assert_eq!(SInt::for_value(1).width, 2);
assert_eq!(SInt::for_value(2).width, 3);
assert_eq!(SInt::for_value(3).width, 3);
assert_eq!(SInt::for_value(4).width, 4);
}
#[test]
fn test_serde_round_trip() {
use serde_json::json;
#[track_caller]
fn check<T: Serialize + DeserializeOwned + PartialEq + fmt::Debug>(
value: T,
expected: serde_json::Value,
) {
assert_eq!(serde_json::to_value(&value).unwrap(), expected);
assert_eq!(value, T::deserialize(expected).unwrap());
}
check(UInt[0], json! { { "UInt": { "width": 0 } } });
check(UInt::<0>::TYPE, json! { { "UInt": { "width": 0 } } });
check(UInt::<35>::TYPE, json! { { "UInt": { "width": 35 } } });
check(SInt[0], json! { { "SInt": { "width": 0 } } });
check(SInt::<0>::TYPE, json! { { "SInt": { "width": 0 } } });
check(SInt::<35>::TYPE, json! { { "SInt": { "width": 35 } } });
check(Bool, json! { "Bool" });
check(UIntValue::from(0u8), json! { "0x0_u8" });
check(SIntValue::from(-128i8), json! { "-0x80_i8" });
check(UInt[3].from_int_wrapping(5), json! { "0x5_u3" });
check(UInt[12].from_int_wrapping(0x1123), json! { "0x123_u12" });
check(SInt[12].from_int_wrapping(0xFEE), json! { "-0x12_i12" });
check(SInt[12].from_int_wrapping(0x7EE), json! { "0x7EE_i12" });
}
#[test]
fn test_deserialize() {
use serde_json::json;
#[track_caller]
fn check<T: DeserializeOwned + fmt::Debug + PartialEq>(
expected: Result<T, &str>,
input: serde_json::Value,
) {
let mut error = String::new();
let value = T::deserialize(input).map_err(|e| -> &str {
error = e.to_string();
&error
});
assert_eq!(value, expected);
}
check::<UInt<0>>(
Err("invalid value: a UInt<2>, expected a UInt<0>"),
json! { { "UInt": { "width": 2 } } },
);
check::<UInt<0>>(
Err("invalid value: a Bool, expected a UInt<0>"),
json! { "Bool" },
);
check::<SInt<0>>(
Err("invalid value: a Bool, expected a SInt<0>"),
json! { "Bool" },
);
check::<UInt>(
Err("invalid value: a Bool, expected a UInt"),
json! { "Bool" },
);
check::<SInt>(
Err("invalid value: a Bool, expected a SInt"),
json! { "Bool" },
);
check::<UIntValue>(Err("value too large to fit in type"), json! { "2_u1" });
check::<UIntValue>(Err("value too large to fit in type"), json! { "10_u1" });
check::<UIntValue>(Err("value too large to fit in type"), json! { "0x2_u1" });
check::<UIntValue>(Err("value too large to fit in type"), json! { "0b10_u1" });
check::<UIntValue>(Err("value too large to fit in type"), json! { "0o2_u1" });
check::<SIntValue>(Err("value too large to fit in type"), json! { "0o377_i8" });
check::<SIntValue>(Err("value too large to fit in type"), json! { "0o200_i8" });
check(Ok(SInt[8].from_int_wrapping(i8::MAX)), json! { "0o177_i8" });
check::<SIntValue>(Err("value too small to fit in type"), json! { "-0o201_i8" });
check::<SIntValue>(Err("value too small to fit in type"), json! { "-0o377_i8" });
check::<SIntValue>(Err("value too small to fit in type"), json! { "-0o400_i8" });
check::<SIntValue>(
Err("value too small to fit in type"),
json! { "-0o4000_i8" },
);
check(Ok(UIntValue::from(0u8)), json! { "0_u8" });
check(Ok(UIntValue::from(0u8)), json! { "0b0_u8" });
check(Ok(UIntValue::from(0u8)), json! { "00_u8" });
check(Ok(UIntValue::from(0u8)), json! { "0x0_u8" });
check(Ok(UIntValue::from(0u8)), json! { "0o0_u8" });
check(Ok(SIntValue::from(-128i8)), json! { "-0x000_80_i8" });
check(Ok(SIntValue::from(-128i8)), json! { "-0o002_00_hdl_i8" });
check(Ok(SIntValue::from(-128i8)), json! { "-0b1__000_0000_i8" });
check(Ok(UInt[3].from_int_wrapping(5)), json! { " + 0x5_u3 " });
check(
Ok(UInt[12].from_int_wrapping(0x1123)),
json! { "0x1_2_3_hdl_u12" },
);
check(Ok(SInt[12].from_int_wrapping(0xFEE)), json! { "-0x12_i12" });
check(
Ok(SInt[12].from_int_wrapping(0x7EE)),
json! { " + \t0x7__E_e_i012\n" },
);
check(Ok(SInt[0].from_int_wrapping(0)), json! { "-0i0" });
check(Ok(SInt[1].from_int_wrapping(0)), json! { "-0i1" });
check(Ok(SInt[0].from_int_wrapping(0)), json! { "-0x0i0" });
check(Ok(SInt[1].from_int_wrapping(0)), json! { "-0x0i1" });
}
}