1443 lines
48 KiB
Rust
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" });
|
|
}
|
|
}
|