sim: add SimValue and reading/writing more than just a scalar
All checks were successful
/ deps (pull_request) Successful in 14s
/ test (pull_request) Successful in 5m14s
/ deps (push) Successful in 14s
/ test (push) Successful in 5m12s

This commit is contained in:
Jacob Lifshay 2024-12-18 01:39:35 -08:00
parent 304d8da0e8
commit 21c73051ec
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ
5 changed files with 1719 additions and 460 deletions

View file

@ -4,12 +4,14 @@
use crate::{
expr::{ops::BundleLiteral, Expr, ToExpr},
intern::{Intern, Interned},
sim::{SimValue, ToSimValue},
source_location::SourceLocation,
ty::{
impl_match_variant_as_self, CanonicalType, MatchVariantWithoutScope, StaticType, Type,
TypeProperties, TypeWithDeref,
},
};
use bitvec::vec::BitVec;
use hashbrown::HashMap;
use std::{fmt, marker::PhantomData};
@ -323,7 +325,7 @@ macro_rules! impl_tuple_builder_fields {
}
macro_rules! impl_tuples {
([$({#[num = $num:literal, field = $field:ident] $var:ident: $T:ident})*] []) => {
([$({#[num = $num:literal, field = $field:ident, ty = $ty_var:ident: $Ty:ident] $var:ident: $T:ident})*] []) => {
impl_tuple_builder_fields! {
{}
[$({
@ -423,6 +425,79 @@ macro_rules! impl_tuples {
BundleLiteral::new(ty, field_values[..].intern()).to_expr()
}
}
impl<$($T: ToSimValue<CanonicalType>,)*> ToSimValue<CanonicalType> for ($($T,)*) {
#[track_caller]
fn to_sim_value(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
ToSimValue::<Bundle>::to_sim_value(self, Bundle::from_canonical(ty)).into_canonical()
}
#[track_caller]
fn into_sim_value(self, ty: CanonicalType) -> SimValue<CanonicalType>
{
ToSimValue::<Bundle>::into_sim_value(self, Bundle::from_canonical(ty)).into_canonical()
}
#[track_caller]
fn box_into_sim_value(self: Box<Self>, ty: CanonicalType) -> SimValue<CanonicalType> {
ToSimValue::<Bundle>::box_into_sim_value(self, Bundle::from_canonical(ty)).into_canonical()
}
}
impl<$($T: ToSimValue<CanonicalType>,)*> ToSimValue<Bundle> for ($($T,)*) {
#[track_caller]
fn to_sim_value(&self, ty: Bundle) -> SimValue<Bundle> {
let ($($var,)*) = self;
let [$($ty_var,)*] = *ty.fields() else {
panic!("bundle has wrong number of fields");
};
$(let $var = $var.to_sim_value($ty_var.ty);)*
ToSimValue::into_sim_value(($($var,)*), ty)
}
#[track_caller]
fn into_sim_value(self, ty: Bundle) -> SimValue<Bundle> {
#![allow(unused_mut)]
#![allow(clippy::unused_unit)]
let ($($var,)*) = self;
let [$($ty_var,)*] = *ty.fields() else {
panic!("bundle has wrong number of fields");
};
let mut bits: Option<BitVec> = None;
$(let $var = $var.into_sim_value($ty_var.ty);
assert_eq!($var.ty(), $ty_var.ty);
if !$var.bits().is_empty() {
if let Some(bits) = &mut bits {
bits.extend_from_bitslice($var.bits());
} else {
let mut $var = $var.into_bits();
$var.reserve(ty.type_properties().bit_width - $var.len());
bits = Some($var);
}
}
)*
bits.unwrap_or_else(BitVec::new).into_sim_value(ty)
}
#[track_caller]
fn box_into_sim_value(self: Box<Self>, ty: Bundle) -> SimValue<Bundle> {
Self::into_sim_value(*self, ty)
}
}
impl<$($T: ToSimValue<$Ty>, $Ty: Type,)*> ToSimValue<($($Ty,)*)> for ($($T,)*) {
#[track_caller]
fn to_sim_value(&self, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> {
let ($($var,)*) = self;
let ($($ty_var,)*) = ty;
$(let $var = $var.to_sim_value($ty_var).into_canonical();)*
SimValue::from_canonical(ToSimValue::into_sim_value(($($var,)*), ty.canonical()))
}
#[track_caller]
fn into_sim_value(self, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> {
let ($($var,)*) = self;
let ($($ty_var,)*) = ty;
$(let $var = $var.into_sim_value($ty_var).into_canonical();)*
SimValue::from_canonical(ToSimValue::into_sim_value(($($var,)*), ty.canonical()))
}
#[track_caller]
fn box_into_sim_value(self: Box<Self>, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> {
Self::into_sim_value(*self, ty)
}
}
};
([$($lhs:tt)*] [$rhs_first:tt $($rhs:tt)*]) => {
impl_tuples!([$($lhs)*] []);
@ -432,18 +507,18 @@ macro_rules! impl_tuples {
impl_tuples! {
[] [
{#[num = 0, field = field_0] v0: T0}
{#[num = 1, field = field_1] v1: T1}
{#[num = 2, field = field_2] v2: T2}
{#[num = 3, field = field_3] v3: T3}
{#[num = 4, field = field_4] v4: T4}
{#[num = 5, field = field_5] v5: T5}
{#[num = 6, field = field_6] v6: T6}
{#[num = 7, field = field_7] v7: T7}
{#[num = 8, field = field_8] v8: T8}
{#[num = 9, field = field_9] v9: T9}
{#[num = 10, field = field_10] v10: T10}
{#[num = 11, field = field_11] v11: T11}
{#[num = 0, field = field_0, ty = ty0: Ty0] v0: T0}
{#[num = 1, field = field_1, ty = ty1: Ty1] v1: T1}
{#[num = 2, field = field_2, ty = ty2: Ty2] v2: T2}
{#[num = 3, field = field_3, ty = ty3: Ty3] v3: T3}
{#[num = 4, field = field_4, ty = ty4: Ty4] v4: T4}
{#[num = 5, field = field_5, ty = ty5: Ty5] v5: T5}
{#[num = 6, field = field_6, ty = ty6: Ty6] v6: T6}
{#[num = 7, field = field_7, ty = ty7: Ty7] v7: T7}
{#[num = 8, field = field_8, ty = ty8: Ty8] v8: T8}
{#[num = 9, field = field_9, ty = ty9: Ty9] v9: T9}
{#[num = 10, field = field_10, ty = ty10: Ty10] v10: T10}
{#[num = 11, field = field_11, ty = ty11: Ty11] v11: T11}
]
}
@ -528,3 +603,27 @@ impl<T: ?Sized + Send + Sync + 'static> ToExpr for PhantomData<T> {
BundleLiteral::new(PhantomData, Interned::default()).to_expr()
}
}
impl<T: ?Sized + Send + Sync + 'static> ToSimValue<Self> for PhantomData<T> {
#[track_caller]
fn to_sim_value(&self, ty: Self) -> SimValue<Self> {
ToSimValue::into_sim_value(BitVec::new(), ty)
}
}
impl<T: ?Sized> ToSimValue<Bundle> for PhantomData<T> {
#[track_caller]
fn to_sim_value(&self, ty: Bundle) -> SimValue<Bundle> {
assert!(ty.fields().is_empty());
ToSimValue::into_sim_value(BitVec::new(), ty)
}
}
impl<T: ?Sized> ToSimValue<CanonicalType> for PhantomData<T> {
#[track_caller]
fn to_sim_value(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
let ty = Bundle::from_canonical(ty);
assert!(ty.fields().is_empty());
ToSimValue::into_sim_value(BitVec::new(), ty).into_canonical()
}
}

View file

@ -14,7 +14,7 @@ use crate::{
},
ExprEnum, Flow, ToLiteralBits,
},
int::BoolOrIntType,
int::{BoolOrIntType, IntType, SIntValue, UIntValue},
intern::{Intern, Interned, Memoize},
memory::PortKind,
module::{
@ -42,7 +42,7 @@ use crate::{
use bitvec::{bits, order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView};
use hashbrown::{HashMap, HashSet};
use num_bigint::BigInt;
use num_traits::{Signed, ToPrimitive, Zero};
use num_traits::{Signed, Zero};
use petgraph::{
data::FromElements,
visit::{
@ -50,7 +50,9 @@ use petgraph::{
IntoNodeIdentifiers, IntoNodeReferences, NodeRef, VisitMap, Visitable,
},
};
use std::{borrow::Cow, collections::BTreeSet, fmt, marker::PhantomData, mem, ops::IndexMut};
use std::{
borrow::Cow, collections::BTreeSet, fmt, marker::PhantomData, mem, ops::IndexMut, sync::Arc,
};
mod interpreter;
pub mod time;
@ -5806,6 +5808,624 @@ impl SimTraceKind {
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct SimValue<T: Type> {
ty: T,
bits: BitVec,
}
impl SimValue<CanonicalType> {
#[track_caller]
fn to_expr_impl(ty: CanonicalType, bits: &BitSlice) -> Expr<CanonicalType> {
match ty {
CanonicalType::UInt(_) => Expr::canonical(<UInt>::bits_to_expr(Cow::Borrowed(bits))),
CanonicalType::SInt(_) => Expr::canonical(<SInt>::bits_to_expr(Cow::Borrowed(bits))),
CanonicalType::Bool(_) => Expr::canonical(Bool::bits_to_expr(Cow::Borrowed(bits))),
CanonicalType::Array(ty) => {
let element_bit_width = ty.element().bit_width();
Expr::<Array>::canonical(
crate::expr::ops::ArrayLiteral::new(
ty.element(),
(0..ty.len())
.map(|array_index| {
let start = element_bit_width * array_index;
let end = start + element_bit_width;
Self::to_expr_impl(ty.element(), &bits[start..end])
})
.collect(),
)
.to_expr(),
)
}
CanonicalType::Enum(ty) => {
let discriminant_bit_width = ty.discriminant_bit_width();
let mut variant_index = [0; mem::size_of::<usize>()];
variant_index.view_bits_mut::<Lsb0>()[0..discriminant_bit_width]
.clone_from_bitslice(&bits[..discriminant_bit_width]);
let variant_index = usize::from_le_bytes(variant_index);
if let Some(variant) = ty.variants().get(variant_index) {
let data_bit_width = variant.ty.map_or(0, CanonicalType::bit_width);
Expr::canonical(
crate::expr::ops::EnumLiteral::new_by_index(
ty,
variant_index,
variant.ty.map(|ty| {
Self::to_expr_impl(
ty,
&bits[discriminant_bit_width
..discriminant_bit_width + data_bit_width],
)
}),
)
.to_expr(),
)
} else {
Expr::canonical(<UInt>::bits_to_expr(Cow::Borrowed(bits)).cast_bits_to(ty))
}
}
CanonicalType::Bundle(ty) => Expr::canonical(
crate::expr::ops::BundleLiteral::new(
ty,
ty.fields()
.iter()
.zip(ty.field_offsets().iter())
.map(|(field, &field_offset)| {
Self::to_expr_impl(
field.ty,
&bits[field_offset..field_offset + field.ty.bit_width()],
)
})
.collect(),
)
.to_expr(),
),
CanonicalType::AsyncReset(ty) => {
Expr::canonical(Bool::bits_to_expr(Cow::Borrowed(bits)).cast_to(ty))
}
CanonicalType::SyncReset(ty) => {
Expr::canonical(Bool::bits_to_expr(Cow::Borrowed(bits)).cast_to(ty))
}
CanonicalType::Reset(_) => panic!(
"can't convert SimValue<Reset> to Expr<Reset> -- \
can't deduce whether reset value should be sync or async"
),
CanonicalType::Clock(ty) => {
Expr::canonical(Bool::bits_to_expr(Cow::Borrowed(bits)).cast_to(ty))
}
}
}
}
impl<T: Type> ToExpr for SimValue<T> {
type Type = T;
#[track_caller]
fn to_expr(&self) -> Expr<Self::Type> {
Expr::from_canonical(SimValue::to_expr_impl(self.ty.canonical(), &self.bits))
}
}
impl<T: Type> ToSimValue<T> for SimValue<T> {
#[track_caller]
fn to_sim_value(&self, ty: T) -> SimValue<T> {
assert_eq!(self.ty, ty);
self.clone()
}
#[track_caller]
fn into_sim_value(self, ty: T) -> SimValue<T> {
assert_eq!(self.ty, ty);
self
}
#[track_caller]
fn box_into_sim_value(self: Box<Self>, ty: T) -> SimValue<T> {
assert_eq!(self.ty, ty);
*self
}
}
impl<T: Type> ToSimValue<T> for BitVec {
#[track_caller]
fn to_sim_value(&self, ty: T) -> SimValue<T> {
self.clone().into_sim_value(ty)
}
#[track_caller]
fn into_sim_value(self, ty: T) -> SimValue<T> {
assert_eq!(ty.canonical().bit_width(), self.len());
SimValue { ty, bits: self }
}
#[track_caller]
fn box_into_sim_value(self: Box<Self>, ty: T) -> SimValue<T> {
Self::into_sim_value(*self, ty)
}
}
impl<T: Type> ToSimValue<T> for bitvec::boxed::BitBox {
#[track_caller]
fn to_sim_value(&self, ty: T) -> SimValue<T> {
self.clone().into_sim_value(ty)
}
#[track_caller]
fn into_sim_value(self, ty: T) -> SimValue<T> {
assert_eq!(ty.canonical().bit_width(), self.len());
SimValue {
ty,
bits: self.into_bitvec(),
}
}
#[track_caller]
fn box_into_sim_value(self: Box<Self>, ty: T) -> SimValue<T> {
Self::into_sim_value(*self, ty)
}
}
impl<T: Type> ToSimValue<T> for BitSlice {
#[track_caller]
fn to_sim_value(&self, ty: T) -> SimValue<T> {
assert_eq!(ty.canonical().bit_width(), self.len());
SimValue {
ty,
bits: self.to_bitvec(),
}
}
}
impl<T: Type> SimValue<T> {
pub fn ty(&self) -> T {
self.ty
}
pub fn bits(&self) -> &BitSlice {
&self.bits
}
pub fn into_bits(self) -> BitVec {
self.bits
}
#[track_caller]
pub fn from_canonical(v: SimValue<CanonicalType>) -> Self {
Self {
ty: T::from_canonical(v.ty),
bits: v.bits,
}
}
pub fn into_canonical(self) -> SimValue<CanonicalType> {
SimValue {
ty: self.ty.canonical(),
bits: self.bits,
}
}
#[track_caller]
pub fn from_dyn_int(v: SimValue<T::Dyn>) -> Self
where
T: IntType,
{
Self {
ty: T::from_dyn_int(v.ty),
bits: v.bits,
}
}
pub fn into_dyn_int(self) -> SimValue<T::Dyn>
where
T: IntType,
{
SimValue {
ty: self.ty.as_dyn_int(),
bits: self.bits,
}
}
#[track_caller]
pub fn from_bundle(v: SimValue<Bundle>) -> Self
where
T: BundleType,
{
Self {
ty: T::from_canonical(CanonicalType::Bundle(v.ty)),
bits: v.bits,
}
}
pub fn into_bundle(self) -> SimValue<Bundle>
where
T: BundleType,
{
SimValue {
ty: Bundle::from_canonical(self.ty.canonical()),
bits: self.bits,
}
}
#[track_caller]
pub fn from_enum(v: SimValue<Enum>) -> Self
where
T: EnumType,
{
Self {
ty: T::from_canonical(CanonicalType::Enum(v.ty)),
bits: v.bits,
}
}
pub fn into_enum(self) -> SimValue<Enum>
where
T: EnumType,
{
SimValue {
ty: Enum::from_canonical(self.ty.canonical()),
bits: self.bits,
}
}
}
pub trait ToSimValue<T: Type> {
#[track_caller]
fn to_sim_value(&self, ty: T) -> SimValue<T>;
#[track_caller]
fn into_sim_value(self, ty: T) -> SimValue<T>
where
Self: Sized,
{
self.to_sim_value(ty)
}
#[track_caller]
fn box_into_sim_value(self: Box<Self>, ty: T) -> SimValue<T> {
self.to_sim_value(ty)
}
}
impl<This: ?Sized + ToSimValue<T>, T: Type> ToSimValue<T> for &'_ This {
#[track_caller]
fn to_sim_value(&self, ty: T) -> SimValue<T> {
This::to_sim_value(self, ty)
}
}
impl<This: ?Sized + ToSimValue<T>, T: Type> ToSimValue<T> for &'_ mut This {
#[track_caller]
fn to_sim_value(&self, ty: T) -> SimValue<T> {
This::to_sim_value(self, ty)
}
}
impl<This: ?Sized + ToSimValue<T>, T: Type> ToSimValue<T> for Box<This> {
#[track_caller]
fn to_sim_value(&self, ty: T) -> SimValue<T> {
This::to_sim_value(self, ty)
}
#[track_caller]
fn into_sim_value(self, ty: T) -> SimValue<T> {
This::box_into_sim_value(self, ty)
}
#[track_caller]
fn box_into_sim_value(self: Box<Self>, ty: T) -> SimValue<T> {
This::box_into_sim_value(*self, ty)
}
}
impl<This: ?Sized + ToSimValue<T> + Send + Sync + 'static, T: Type> ToSimValue<T>
for Interned<This>
{
#[track_caller]
fn to_sim_value(&self, ty: T) -> SimValue<T> {
This::to_sim_value(self, ty)
}
}
impl<T: Type, Len: Size> SimValue<ArrayType<T, Len>> {
#[track_caller]
pub fn from_array_elements<
I: IntoIterator<Item: ToSimValue<T>, IntoIter: ExactSizeIterator>,
>(
elements: I,
ty: ArrayType<T, Len>,
) -> Self {
let mut iter = elements.into_iter();
assert_eq!(iter.len(), ty.len());
let Some(first) = iter.next() else {
return SimValue {
ty,
bits: BitVec::new(),
};
};
let SimValue {
ty: element_ty,
mut bits,
} = first.into_sim_value(ty.element());
assert_eq!(element_ty, ty.element());
bits.reserve(ty.type_properties().bit_width - bits.len());
for element in iter {
let SimValue {
ty: element_ty,
bits: element_bits,
} = element.into_sim_value(ty.element());
assert_eq!(element_ty, ty.element());
bits.extend_from_bitslice(&element_bits);
}
SimValue { ty, bits }
}
}
impl<Element: ToSimValue<T>, T: Type> ToSimValue<Array<T>> for [Element] {
#[track_caller]
fn to_sim_value(&self, ty: Array<T>) -> SimValue<Array<T>> {
SimValue::from_array_elements(self, ty)
}
#[track_caller]
fn box_into_sim_value(self: Box<Self>, ty: Array<T>) -> SimValue<Array<T>> {
SimValue::from_array_elements(self, ty)
}
}
impl<Element: ToSimValue<CanonicalType>> ToSimValue<CanonicalType> for [Element] {
#[track_caller]
fn to_sim_value(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
SimValue::from_array_elements(self, <Array>::from_canonical(ty)).into_canonical()
}
#[track_caller]
fn box_into_sim_value(self: Box<Self>, ty: CanonicalType) -> SimValue<CanonicalType> {
SimValue::from_array_elements(self, <Array>::from_canonical(ty)).into_canonical()
}
}
impl<Element: ToSimValue<T>, T: Type, const N: usize> ToSimValue<Array<T, N>> for [Element; N]
where
ConstUsize<N>: KnownSize,
{
#[track_caller]
fn to_sim_value(&self, ty: Array<T, N>) -> SimValue<Array<T, N>> {
SimValue::from_array_elements(self, ty)
}
#[track_caller]
fn into_sim_value(self, ty: Array<T, N>) -> SimValue<Array<T, N>> {
SimValue::from_array_elements(self, ty)
}
#[track_caller]
fn box_into_sim_value(self: Box<Self>, ty: Array<T, N>) -> SimValue<Array<T, N>> {
SimValue::from_array_elements(<Vec<Element> as From<Box<[Element]>>>::from(self), ty)
}
}
impl<Element: ToSimValue<T>, T: Type, const N: usize> ToSimValue<Array<T>> for [Element; N]
where
ConstUsize<N>: KnownSize,
{
#[track_caller]
fn to_sim_value(&self, ty: Array<T>) -> SimValue<Array<T>> {
SimValue::from_array_elements(self, ty)
}
#[track_caller]
fn into_sim_value(self, ty: Array<T>) -> SimValue<Array<T>> {
SimValue::from_array_elements(self, ty)
}
#[track_caller]
fn box_into_sim_value(self: Box<Self>, ty: Array<T>) -> SimValue<Array<T>> {
SimValue::from_array_elements(<Vec<Element> as From<Box<[Element]>>>::from(self), ty)
}
}
impl<Element: ToSimValue<CanonicalType>, const N: usize> ToSimValue<CanonicalType>
for [Element; N]
{
#[track_caller]
fn to_sim_value(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
SimValue::from_array_elements(self, <Array>::from_canonical(ty)).into_canonical()
}
#[track_caller]
fn into_sim_value(self, ty: CanonicalType) -> SimValue<CanonicalType> {
SimValue::from_array_elements(self, <Array>::from_canonical(ty)).into_canonical()
}
#[track_caller]
fn box_into_sim_value(self: Box<Self>, ty: CanonicalType) -> SimValue<CanonicalType> {
SimValue::from_array_elements(
<Vec<Element> as From<Box<[Element]>>>::from(self),
<Array>::from_canonical(ty),
)
.into_canonical()
}
}
impl<Element: ToSimValue<T>, T: Type> ToSimValue<Array<T>> for Vec<Element> {
#[track_caller]
fn to_sim_value(&self, ty: Array<T>) -> SimValue<Array<T>> {
SimValue::from_array_elements(self, ty)
}
#[track_caller]
fn into_sim_value(self, ty: Array<T>) -> SimValue<Array<T>> {
SimValue::from_array_elements(self, ty)
}
#[track_caller]
fn box_into_sim_value(self: Box<Self>, ty: Array<T>) -> SimValue<Array<T>> {
SimValue::from_array_elements(*self, ty)
}
}
impl<Element: ToSimValue<CanonicalType>> ToSimValue<CanonicalType> for Vec<Element> {
#[track_caller]
fn to_sim_value(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
SimValue::from_array_elements(self, <Array>::from_canonical(ty)).into_canonical()
}
#[track_caller]
fn into_sim_value(self, ty: CanonicalType) -> SimValue<CanonicalType> {
SimValue::from_array_elements(self, <Array>::from_canonical(ty)).into_canonical()
}
#[track_caller]
fn box_into_sim_value(self: Box<Self>, ty: CanonicalType) -> SimValue<CanonicalType> {
SimValue::from_array_elements(*self, <Array>::from_canonical(ty)).into_canonical()
}
}
impl<T: Type> ToSimValue<T> for Expr<T> {
#[track_caller]
fn to_sim_value(&self, ty: T) -> SimValue<T> {
assert_eq!(Expr::ty(*self), ty);
SimValue {
ty,
bits: self
.to_literal_bits()
.expect("must be a literal expression")
.to_bitvec(),
}
}
}
macro_rules! impl_to_sim_value_for_bool_like {
($ty:ident) => {
impl ToSimValue<$ty> for bool {
fn to_sim_value(&self, ty: $ty) -> SimValue<$ty> {
SimValue {
ty,
bits: BitVec::repeat(*self, 1),
}
}
}
};
}
impl_to_sim_value_for_bool_like!(Bool);
impl_to_sim_value_for_bool_like!(AsyncReset);
impl_to_sim_value_for_bool_like!(SyncReset);
impl_to_sim_value_for_bool_like!(Reset);
impl_to_sim_value_for_bool_like!(Clock);
impl ToSimValue<CanonicalType> for bool {
#[track_caller]
fn to_sim_value(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
match ty {
CanonicalType::UInt(_)
| CanonicalType::SInt(_)
| CanonicalType::Array(_)
| CanonicalType::Enum(_)
| CanonicalType::Bundle(_) => {
panic!("can't create SimValue from bool: expected value of type: {ty:?}");
}
CanonicalType::Bool(_)
| CanonicalType::AsyncReset(_)
| CanonicalType::SyncReset(_)
| CanonicalType::Reset(_)
| CanonicalType::Clock(_) => SimValue {
ty,
bits: BitVec::repeat(*self, 1),
},
}
}
}
macro_rules! impl_to_sim_value_for_primitive_int {
($prim:ident) => {
impl ToSimValue<<$prim as ToExpr>::Type> for $prim {
#[track_caller]
fn to_sim_value(
&self,
ty: <$prim as ToExpr>::Type,
) -> SimValue<<$prim as ToExpr>::Type> {
SimValue {
ty,
bits: <<$prim as ToExpr>::Type as BoolOrIntType>::le_bytes_to_bits_wrapping(
&self.to_le_bytes(),
ty.width(),
),
}
}
}
impl ToSimValue<<<$prim as ToExpr>::Type as IntType>::Dyn> for $prim {
#[track_caller]
fn to_sim_value(
&self,
ty: <<$prim as ToExpr>::Type as IntType>::Dyn,
) -> SimValue<<<$prim as ToExpr>::Type as IntType>::Dyn> {
SimValue {
ty,
bits: <<$prim as ToExpr>::Type as BoolOrIntType>::le_bytes_to_bits_wrapping(
&self.to_le_bytes(),
ty.width(),
),
}
}
}
impl ToSimValue<CanonicalType> for $prim {
#[track_caller]
fn to_sim_value(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
let ty: <<$prim as ToExpr>::Type as IntType>::Dyn = Type::from_canonical(ty);
self.to_sim_value(ty).into_canonical()
}
}
};
}
impl_to_sim_value_for_primitive_int!(u8);
impl_to_sim_value_for_primitive_int!(u16);
impl_to_sim_value_for_primitive_int!(u32);
impl_to_sim_value_for_primitive_int!(u64);
impl_to_sim_value_for_primitive_int!(u128);
impl_to_sim_value_for_primitive_int!(usize);
impl_to_sim_value_for_primitive_int!(i8);
impl_to_sim_value_for_primitive_int!(i16);
impl_to_sim_value_for_primitive_int!(i32);
impl_to_sim_value_for_primitive_int!(i64);
impl_to_sim_value_for_primitive_int!(i128);
impl_to_sim_value_for_primitive_int!(isize);
macro_rules! impl_to_sim_value_for_int_value {
($IntValue:ident, $Int:ident, $IntType:ident) => {
impl<Width: KnownSize> ToSimValue<$IntType<Width>> for $IntValue<Width> {
fn to_sim_value(&self, ty: $IntType<Width>) -> SimValue<$IntType<Width>> {
self.bits().to_bitvec().into_sim_value(ty)
}
fn into_sim_value(self, ty: $IntType<Width>) -> SimValue<$IntType<Width>> {
Arc::try_unwrap(self.into_bits())
.unwrap_or_else(|v: Arc<BitVec>| v.to_bitvec())
.into_sim_value(ty)
}
fn box_into_sim_value(
self: Box<Self>,
ty: $IntType<Width>,
) -> SimValue<$IntType<Width>> {
Self::into_sim_value(*self, ty)
}
}
impl<Width: Size> ToSimValue<$Int> for $IntValue<Width> {
fn to_sim_value(&self, ty: $Int) -> SimValue<$Int> {
self.bits().to_bitvec().into_sim_value(ty)
}
fn into_sim_value(self, ty: $Int) -> SimValue<$Int> {
Arc::try_unwrap(self.into_bits())
.unwrap_or_else(|v: Arc<BitVec>| v.to_bitvec())
.into_sim_value(ty)
}
fn box_into_sim_value(self: Box<Self>, ty: $Int) -> SimValue<$Int> {
Self::into_sim_value(*self, ty)
}
}
impl<Width: Size> ToSimValue<CanonicalType> for $IntValue<Width> {
#[track_caller]
fn to_sim_value(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
ToSimValue::<$Int>::to_sim_value(self, $Int::from_canonical(ty)).into_canonical()
}
#[track_caller]
fn into_sim_value(self, ty: CanonicalType) -> SimValue<CanonicalType> {
ToSimValue::<$Int>::into_sim_value(self, $Int::from_canonical(ty)).into_canonical()
}
#[track_caller]
fn box_into_sim_value(self: Box<Self>, ty: CanonicalType) -> SimValue<CanonicalType> {
Self::into_sim_value(*self, ty)
}
}
};
}
impl_to_sim_value_for_int_value!(UIntValue, UInt, UIntType);
impl_to_sim_value_for_int_value!(SIntValue, SInt, SIntType);
struct SimulationImpl {
state: interpreter::State,
io: Expr<Bundle>,
@ -6263,8 +6883,11 @@ impl SimulationImpl {
let value: BigInt = value.into();
match compiled_value.range.len() {
TypeLen::A_SMALL_SLOT => {
self.state.small_slots[compiled_value.range.small_slots.start] =
value.to_u64().expect("value out of range");
let mut small_value = value.iter_u64_digits().next().unwrap_or(0);
if value.is_negative() {
small_value = small_value.wrapping_neg();
}
self.state.small_slots[compiled_value.range.small_slots.start] = small_value;
}
TypeLen::A_BIG_SLOT => {
self.state.big_slots[compiled_value.range.big_slots.start] = value
@ -6272,6 +6895,130 @@ impl SimulationImpl {
_ => unreachable!(),
}
}
#[track_caller]
fn read_write_sim_value_helper(
&mut self,
compiled_value: CompiledValue<CanonicalType>,
bits: &mut BitSlice,
read_write_big_scalar: impl Fn(bool, &mut BitSlice, &mut BigInt) + Copy,
read_write_small_scalar: impl Fn(bool, &mut BitSlice, &mut SmallUInt) + Copy,
) {
match compiled_value.layout.body {
CompiledTypeLayoutBody::Scalar => {
let signed = match compiled_value.layout.ty {
CanonicalType::UInt(_) => false,
CanonicalType::SInt(_) => true,
CanonicalType::Bool(_) => false,
CanonicalType::Array(_) => unreachable!(),
CanonicalType::Enum(_) => false,
CanonicalType::Bundle(_) => unreachable!(),
CanonicalType::AsyncReset(_) => false,
CanonicalType::SyncReset(_) => false,
CanonicalType::Reset(_) => false,
CanonicalType::Clock(_) => false,
};
match compiled_value.range.len() {
TypeLen::A_SMALL_SLOT => read_write_small_scalar(
signed,
bits,
&mut self.state.small_slots[compiled_value.range.small_slots.start],
),
TypeLen::A_BIG_SLOT => read_write_big_scalar(
signed,
bits,
&mut self.state.big_slots[compiled_value.range.big_slots.start],
),
_ => unreachable!(),
}
}
CompiledTypeLayoutBody::Array { element } => {
let ty = <Array>::from_canonical(compiled_value.layout.ty);
let element_bit_width = ty.element().bit_width();
for element_index in 0..ty.len() {
self.read_write_sim_value_helper(
CompiledValue {
layout: *element,
range: compiled_value
.range
.index_array(element.layout.len(), element_index),
write: None,
},
&mut bits[element_index * element_bit_width..][..element_bit_width],
read_write_big_scalar,
read_write_small_scalar,
);
}
}
CompiledTypeLayoutBody::Bundle { fields } => {
let ty = Bundle::from_canonical(compiled_value.layout.ty);
for (
(field, offset),
CompiledBundleField {
offset: layout_offset,
ty: field_layout,
},
) in ty.fields().iter().zip(ty.field_offsets()).zip(fields)
{
self.read_write_sim_value_helper(
CompiledValue {
layout: field_layout,
range: compiled_value.range.slice(TypeIndexRange::new(
layout_offset,
field_layout.layout.len(),
)),
write: None,
},
&mut bits[offset..][..field.ty.bit_width()],
read_write_big_scalar,
read_write_small_scalar,
);
}
}
}
}
#[track_caller]
fn read(&mut self, io: Expr<CanonicalType>) -> SimValue<CanonicalType> {
let compiled_value = self.read_helper(io);
let mut bits = BitVec::repeat(false, compiled_value.layout.ty.bit_width());
self.read_write_sim_value_helper(
compiled_value,
&mut bits,
|_signed, bits, value| <UInt>::copy_bits_from_bigint_wrapping(value, bits),
|_signed, bits, value| {
let bytes = value.to_le_bytes();
let bitslice = BitSlice::<u8, Lsb0>::from_slice(&bytes);
bits.clone_from_bitslice(&bitslice[..bits.len()]);
},
);
SimValue {
ty: Expr::ty(io),
bits,
}
}
#[track_caller]
fn write(&mut self, io: Expr<CanonicalType>, value: SimValue<CanonicalType>) {
let compiled_value = self.write_helper(io);
assert_eq!(Expr::ty(io), value.ty());
self.read_write_sim_value_helper(
compiled_value,
&mut value.into_bits(),
|signed, bits, value| {
if signed {
*value = SInt::bits_to_bigint(bits);
} else {
*value = UInt::bits_to_bigint(bits);
}
},
|signed, bits, value| {
let mut small_value = [0; mem::size_of::<SmallUInt>()];
if signed && bits.last().as_deref().copied() == Some(true) {
small_value.fill(u8::MAX);
}
small_value.view_bits_mut::<Lsb0>()[0..bits.len()].clone_from_bitslice(bits);
*value = SmallUInt::from_le_bytes(small_value);
},
);
}
fn close_all_trace_writers(&mut self) -> std::io::Result<()> {
let trace_writers = mem::take(&mut self.trace_writers);
let mut retval = Ok(());
@ -6525,6 +7272,17 @@ impl<T: BundleType> Simulation<T> {
pub fn read_reset<R: ResetType>(&mut self, io: Expr<R>) -> bool {
self.sim_impl.read_bit(Expr::canonical(io))
}
#[track_caller]
pub fn read<IO: Type>(&mut self, io: Expr<IO>) -> SimValue<IO> {
SimValue::from_canonical(self.sim_impl.read(Expr::canonical(io)))
}
#[track_caller]
pub fn write<IO: Type, V: ToSimValue<IO>>(&mut self, io: Expr<IO>, value: V) {
self.sim_impl.write(
Expr::canonical(io),
value.into_sim_value(Expr::ty(io)).into_canonical(),
);
}
#[doc(hidden)]
/// This is explicitly unstable and may be changed/removed at any time
pub fn set_breakpoints_unstable(&mut self, pcs: HashSet<usize>, trace: bool) {

View file

@ -5,7 +5,8 @@ use fayalite::{
int::UIntValue,
prelude::*,
reset::ResetType,
sim::{time::SimDuration, vcd::VcdWriterDecls, Simulation},
sim::{time::SimDuration, vcd::VcdWriterDecls, Simulation, ToSimValue},
ty::StaticType,
util::RcWriter,
};
use std::num::NonZeroUsize;
@ -309,6 +310,8 @@ pub fn enums() {
let which_out: UInt<2> = m.output();
#[hdl]
let data_out: UInt<4> = m.output();
#[hdl]
let b_out: HdlOption<(UInt<1>, Bool)> = m.output();
#[hdl]
struct MyStruct<T> {
@ -348,6 +351,8 @@ pub fn enums() {
}
}
connect(b_out, HdlNone());
#[hdl]
match the_reg {
MyEnum::A => {
@ -357,6 +362,7 @@ pub fn enums() {
MyEnum::B(v) => {
connect(which_out, 1_hdl_u2);
connect_any(data_out, v.0 | (v.1.cast_to_static::<UInt<1>>() << 1));
connect(b_out, HdlSome(v));
}
MyEnum::C(v) => {
connect(which_out, 2_hdl_u2);
@ -382,13 +388,33 @@ fn test_enums() {
sim.advance_time(SimDuration::from_nanos(100));
sim.write_reset(sim.io().cd.rst, false);
sim.advance_time(SimDuration::from_nanos(900));
#[derive(Debug, PartialEq, Eq)]
type BOutTy = HdlOption<(UInt<1>, Bool)>;
#[derive(Debug)]
struct IO {
en: bool,
which_in: u8,
data_in: u8,
which_out: u8,
data_out: u8,
b_out: Expr<BOutTy>,
}
impl PartialEq for IO {
fn eq(&self, other: &Self) -> bool {
let Self {
en,
which_in,
data_in,
which_out,
data_out,
b_out,
} = *self;
en == other.en
&& which_in == other.which_in
&& data_in == other.data_in
&& which_out == other.which_out
&& data_out == other.data_out
&& b_out.to_sim_value(BOutTy::TYPE) == other.b_out.to_sim_value(BOutTy::TYPE)
}
}
let io_cycles = [
IO {
@ -397,6 +423,7 @@ fn test_enums() {
data_in: 0,
which_out: 0,
data_out: 0,
b_out: HdlNone(),
},
IO {
en: true,
@ -404,6 +431,7 @@ fn test_enums() {
data_in: 0,
which_out: 0,
data_out: 0,
b_out: HdlNone(),
},
IO {
en: false,
@ -411,6 +439,7 @@ fn test_enums() {
data_in: 0,
which_out: 1,
data_out: 0,
b_out: HdlSome((0_hdl_u1, false)),
},
IO {
en: true,
@ -418,6 +447,7 @@ fn test_enums() {
data_in: 0xF,
which_out: 1,
data_out: 0,
b_out: HdlSome((0_hdl_u1, false)),
},
IO {
en: true,
@ -425,6 +455,7 @@ fn test_enums() {
data_in: 0xF,
which_out: 1,
data_out: 0x3,
b_out: HdlSome((1_hdl_u1, true)),
},
IO {
en: true,
@ -432,6 +463,7 @@ fn test_enums() {
data_in: 0xF,
which_out: 1,
data_out: 0x3,
b_out: HdlSome((1_hdl_u1, true)),
},
IO {
en: true,
@ -439,6 +471,7 @@ fn test_enums() {
data_in: 0xF,
which_out: 2,
data_out: 0xF,
b_out: HdlNone(),
},
];
for (
@ -449,6 +482,7 @@ fn test_enums() {
data_in,
which_out: _,
data_out: _,
b_out: _,
},
) in io_cycles.into_iter().enumerate()
{
@ -469,6 +503,7 @@ fn test_enums() {
.to_bigint()
.try_into()
.expect("known to be in range"),
b_out: sim.read(sim.io().b_out).to_expr(),
};
assert_eq!(
expected,

File diff suppressed because it is too large Load diff

View file

@ -9,18 +9,25 @@ $var wire 2 $ which_in $end
$var wire 4 % data_in $end
$var wire 2 & which_out $end
$var wire 4 ' data_out $end
$scope struct the_reg $end
$scope struct b_out $end
$var string 1 ( \$tag $end
$scope struct HdlSome $end
$var wire 1 ) \0 $end
$var wire 1 * \1 $end
$upscope $end
$upscope $end
$scope struct the_reg $end
$var string 1 + \$tag $end
$scope struct B $end
$var reg 1 ) \0 $end
$var reg 1 * \1 $end
$var reg 1 , \0 $end
$var reg 1 - \1 $end
$upscope $end
$scope struct C $end
$scope struct a $end
$var reg 1 + \[0] $end
$var reg 1 , \[1] $end
$var reg 1 . \[0] $end
$var reg 1 / \[1] $end
$upscope $end
$var reg 2 - b $end
$var reg 2 0 b $end
$upscope $end
$upscope $end
$upscope $end
@ -33,12 +40,15 @@ b0 $
b0 %
b0 &
b0 '
sA\x20(0) (
sHdlNone\x20(0) (
0)
0*
0+
sA\x20(0) +
0,
b0 -
0-
0.
0/
b0 0
$end
#1000000
1!
@ -55,7 +65,8 @@ b1 $
#5000000
1!
b1 &
sB\x20(1) (
sHdlSome\x20(1) (
sB\x20(1) +
#6000000
0#
b0 $
@ -72,8 +83,10 @@ b1111 %
b11 '
1)
1*
1+
1,
1-
1.
1/
#10000000
0!
#11000000
@ -85,8 +98,11 @@ b10 $
1!
b10 &
b1111 '
sC\x20(2) (
b11 -
sHdlNone\x20(0) (
0)
0*
sC\x20(2) +
b11 0
#14000000
0!
#15000000