forked from libre-chip/fayalite
Compare commits
10 commits
43797db36e
...
6e0016b370
Author | SHA1 | Date | |
---|---|---|---|
6e0016b370 | |||
920d8d875f | |||
d453755bb2 | |||
450e1004b6 | |||
c0c5b550bc | |||
2fa0ea6192 | |||
bd75fdfefd | |||
50c86e18dc | |||
60734cc9d1 | |||
3458c21f44 |
28 changed files with 1982 additions and 57 deletions
|
@ -12,10 +12,10 @@ jobs:
|
|||
outputs:
|
||||
cache-primary-key: ${{ steps.restore-deps.outputs.cache-primary-key }}
|
||||
steps:
|
||||
- uses: https://code.forgejo.org/actions/checkout@v3
|
||||
- uses: https://git.libre-chip.org/mirrors/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: https://code.forgejo.org/actions/cache/restore@v3
|
||||
- uses: https://git.libre-chip.org/mirrors/cache/restore@v3
|
||||
id: restore-deps
|
||||
with:
|
||||
path: deps
|
||||
|
@ -58,19 +58,19 @@ jobs:
|
|||
- name: Get SymbiYosys
|
||||
if: steps.restore-deps.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
git clone --depth=1 --branch=yosys-0.45 https://github.com/YosysHQ/sby.git deps/sby
|
||||
git clone --depth=1 --branch=yosys-0.45 https://git.libre-chip.org/mirrors/sby deps/sby
|
||||
- name: Build Z3
|
||||
if: steps.restore-deps.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
git clone --depth=1 --recursive --branch=z3-4.13.3 https://github.com/Z3Prover/z3.git deps/z3
|
||||
git clone --depth=1 --recursive --branch=z3-4.13.3 https://git.libre-chip.org/mirrors/z3 deps/z3
|
||||
(cd deps/z3; PYTHON=python3 ./configure --prefix=/usr/local)
|
||||
make -C deps/z3/build -j"$(nproc)"
|
||||
- name: Build Yosys
|
||||
if: steps.restore-deps.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
git clone --depth=1 --recursive --branch=0.45 https://github.com/YosysHQ/yosys.git deps/yosys
|
||||
git clone --depth=1 --recursive --branch=0.45 https://git.libre-chip.org/mirrors/yosys deps/yosys
|
||||
make -C deps/yosys -j"$(nproc)"
|
||||
- uses: https://code.forgejo.org/actions/cache/save@v3
|
||||
- uses: https://git.libre-chip.org/mirrors/cache/save@v3
|
||||
if: steps.restore-deps.outputs.cache-hit != 'true'
|
||||
with:
|
||||
path: deps
|
||||
|
|
|
@ -9,7 +9,7 @@ jobs:
|
|||
runs-on: debian-12
|
||||
needs: deps
|
||||
steps:
|
||||
- uses: https://code.forgejo.org/actions/checkout@v3
|
||||
- uses: https://git.libre-chip.org/mirrors/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- run: |
|
||||
|
@ -41,7 +41,7 @@ jobs:
|
|||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.82.0
|
||||
source "$HOME/.cargo/env"
|
||||
echo "$PATH" >> "$GITHUB_PATH"
|
||||
- uses: https://code.forgejo.org/actions/cache/restore@v3
|
||||
- uses: https://git.libre-chip.org/mirrors/cache/restore@v3
|
||||
with:
|
||||
path: deps
|
||||
key: ${{ needs.deps.outputs.cache-primary-key }}
|
||||
|
@ -52,7 +52,7 @@ jobs:
|
|||
make -C deps/yosys install
|
||||
export PATH="$(realpath deps/firtool/bin):$PATH"
|
||||
echo "$PATH" >> "$GITHUB_PATH"
|
||||
- uses: https://github.com/Swatinem/rust-cache@v2
|
||||
- uses: https://git.libre-chip.org/mirrors/rust-cache@v2
|
||||
with:
|
||||
save-if: ${{ github.ref == 'refs/heads/master' }}
|
||||
- run: cargo test
|
||||
|
|
|
@ -83,6 +83,7 @@ impl ParsedBundle {
|
|||
custom_bounds,
|
||||
no_static: _,
|
||||
no_runtime_generics: _,
|
||||
cmp_eq: _,
|
||||
} = options.body;
|
||||
let mut fields = match fields {
|
||||
syn::Fields::Named(fields) => fields,
|
||||
|
@ -437,6 +438,7 @@ impl ToTokens for ParsedBundle {
|
|||
custom_bounds: _,
|
||||
no_static,
|
||||
no_runtime_generics,
|
||||
cmp_eq,
|
||||
} = &options.body;
|
||||
let target = get_target(target, ident);
|
||||
let mut item_attrs = attrs.clone();
|
||||
|
@ -765,6 +767,69 @@ impl ToTokens for ParsedBundle {
|
|||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
if let Some((cmp_eq,)) = cmp_eq {
|
||||
let mut where_clause =
|
||||
Generics::from(generics)
|
||||
.where_clause
|
||||
.unwrap_or_else(|| syn::WhereClause {
|
||||
where_token: Token,
|
||||
predicates: Punctuated::new(),
|
||||
});
|
||||
let mut fields_cmp_eq = vec![];
|
||||
let mut fields_cmp_ne = vec![];
|
||||
for field in fields.named() {
|
||||
let field_ident = field.ident();
|
||||
let field_ty = field.ty();
|
||||
where_clause
|
||||
.predicates
|
||||
.push(parse_quote_spanned! {cmp_eq.span=>
|
||||
#field_ty: ::fayalite::expr::ops::ExprPartialEq<#field_ty>
|
||||
});
|
||||
fields_cmp_eq.push(quote_spanned! {span=>
|
||||
::fayalite::expr::ops::ExprPartialEq::cmp_eq(__lhs.#field_ident, __rhs.#field_ident)
|
||||
});
|
||||
fields_cmp_ne.push(quote_spanned! {span=>
|
||||
::fayalite::expr::ops::ExprPartialEq::cmp_ne(__lhs.#field_ident, __rhs.#field_ident)
|
||||
});
|
||||
}
|
||||
let cmp_eq_body;
|
||||
let cmp_ne_body;
|
||||
if fields_len == 0 {
|
||||
cmp_eq_body = quote_spanned! {span=>
|
||||
::fayalite::expr::ToExpr::to_expr(&true)
|
||||
};
|
||||
cmp_ne_body = quote_spanned! {span=>
|
||||
::fayalite::expr::ToExpr::to_expr(&false)
|
||||
};
|
||||
} else {
|
||||
cmp_eq_body = quote_spanned! {span=>
|
||||
#(#fields_cmp_eq)&*
|
||||
};
|
||||
cmp_ne_body = quote_spanned! {span=>
|
||||
#(#fields_cmp_ne)|*
|
||||
};
|
||||
};
|
||||
quote_spanned! {span=>
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::expr::ops::ExprPartialEq<Self> for #target #type_generics
|
||||
#where_clause
|
||||
{
|
||||
fn cmp_eq(
|
||||
__lhs: ::fayalite::expr::Expr<Self>,
|
||||
__rhs: ::fayalite::expr::Expr<Self>,
|
||||
) -> ::fayalite::expr::Expr<::fayalite::int::Bool> {
|
||||
#cmp_eq_body
|
||||
}
|
||||
fn cmp_ne(
|
||||
__lhs: ::fayalite::expr::Expr<Self>,
|
||||
__rhs: ::fayalite::expr::Expr<Self>,
|
||||
) -> ::fayalite::expr::Expr<::fayalite::int::Bool> {
|
||||
#cmp_ne_body
|
||||
}
|
||||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
}
|
||||
if let (None, MaybeParsed::Parsed(generics)) = (no_static, &self.generics) {
|
||||
let static_generics = generics.clone().for_static_type();
|
||||
let (static_impl_generics, static_type_generics, static_where_clause) =
|
||||
|
|
|
@ -155,7 +155,11 @@ impl ParsedEnum {
|
|||
custom_bounds,
|
||||
no_static: _,
|
||||
no_runtime_generics: _,
|
||||
cmp_eq,
|
||||
} = options.body;
|
||||
if let Some((cmp_eq,)) = cmp_eq {
|
||||
errors.error(cmp_eq, "#[hdl(cmp_eq)] is not yet implemented for enums");
|
||||
}
|
||||
attrs.retain(|attr| {
|
||||
if attr.path().is_ident("repr") {
|
||||
errors.error(attr, "#[repr] is not supported on #[hdl] enums");
|
||||
|
@ -211,6 +215,7 @@ impl ToTokens for ParsedEnum {
|
|||
custom_bounds: _,
|
||||
no_static,
|
||||
no_runtime_generics,
|
||||
cmp_eq: _, // TODO: implement cmp_eq for enums
|
||||
} = &options.body;
|
||||
let target = get_target(target, ident);
|
||||
let mut struct_attrs = attrs.clone();
|
||||
|
|
|
@ -49,10 +49,14 @@ impl ParsedTypeAlias {
|
|||
custom_bounds,
|
||||
no_static,
|
||||
no_runtime_generics: _,
|
||||
cmp_eq,
|
||||
} = options.body;
|
||||
if let Some((no_static,)) = no_static {
|
||||
errors.error(no_static, "no_static is not valid on type aliases");
|
||||
}
|
||||
if let Some((cmp_eq,)) = cmp_eq {
|
||||
errors.error(cmp_eq, "cmp_eq is not valid on type aliases");
|
||||
}
|
||||
let generics = if custom_bounds.is_some() {
|
||||
MaybeParsed::Unrecognized(generics)
|
||||
} else if let Some(generics) = errors.ok(ParsedGenerics::parse(&mut generics)) {
|
||||
|
@ -95,6 +99,7 @@ impl ToTokens for ParsedTypeAlias {
|
|||
custom_bounds: _,
|
||||
no_static: _,
|
||||
no_runtime_generics,
|
||||
cmp_eq: _,
|
||||
} = &options.body;
|
||||
let target = get_target(target, ident);
|
||||
let mut type_attrs = attrs.clone();
|
||||
|
|
|
@ -26,6 +26,7 @@ crate::options! {
|
|||
CustomBounds(custom_bounds),
|
||||
NoStatic(no_static),
|
||||
NoRuntimeGenerics(no_runtime_generics),
|
||||
CmpEq(cmp_eq),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -72,6 +72,7 @@ mod kw {
|
|||
custom_keyword!(cfg);
|
||||
custom_keyword!(cfg_attr);
|
||||
custom_keyword!(clock_domain);
|
||||
custom_keyword!(cmp_eq);
|
||||
custom_keyword!(connect_inexact);
|
||||
custom_keyword!(custom_bounds);
|
||||
custom_keyword!(flip);
|
||||
|
|
|
@ -2,8 +2,11 @@
|
|||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::{
|
||||
expr::{ops::ArrayIndex, Expr, ToExpr},
|
||||
int::{DynSize, KnownSize, Size, SizeType, DYN_SIZE},
|
||||
expr::{
|
||||
ops::{ArrayLiteral, ExprFromIterator, ExprIntoIterator, ExprPartialEq},
|
||||
CastToBits, Expr, HdlPartialEq, ReduceBits, ToExpr,
|
||||
},
|
||||
int::{Bool, DynSize, KnownSize, Size, SizeType, DYN_SIZE},
|
||||
intern::{Intern, Interned, LazyInterned},
|
||||
module::transform::visit::{Fold, Folder, Visit, Visitor},
|
||||
source_location::SourceLocation,
|
||||
|
@ -12,7 +15,7 @@ use crate::{
|
|||
},
|
||||
util::ConstUsize,
|
||||
};
|
||||
use std::ops::Index;
|
||||
use std::{iter::FusedIterator, ops::Index};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ArrayType<T: Type = CanonicalType, Len: Size = DynSize> {
|
||||
|
@ -148,10 +151,8 @@ impl<T: Type, Len: Size> Type for ArrayType<T, Len> {
|
|||
this: Expr<Self>,
|
||||
source_location: SourceLocation,
|
||||
) -> Self::MatchVariantsIter {
|
||||
let base = Expr::as_dyn_array(this);
|
||||
let base_ty = Expr::ty(base);
|
||||
let _ = source_location;
|
||||
let retval = Vec::from_iter((0..base_ty.len()).map(|i| ArrayIndex::new(base, i).to_expr()));
|
||||
let retval = Vec::from_iter(this);
|
||||
std::iter::once(MatchVariantWithoutScope(
|
||||
Len::ArrayMatch::<T>::try_from(retval)
|
||||
.ok()
|
||||
|
@ -184,9 +185,7 @@ impl<T: Type, Len: Size> Type for ArrayType<T, Len> {
|
|||
|
||||
impl<T: Type, Len: Size> TypeWithDeref for ArrayType<T, Len> {
|
||||
fn expr_deref(this: &Expr<Self>) -> &Self::MatchVariant {
|
||||
let base = Expr::as_dyn_array(*this);
|
||||
let base_ty = Expr::ty(base);
|
||||
let retval = Vec::from_iter((0..base_ty.len()).map(|i| ArrayIndex::new(base, i).to_expr()));
|
||||
let retval = Vec::from_iter(*this);
|
||||
Interned::into_inner(Intern::intern_sized(
|
||||
Len::ArrayMatch::<T>::try_from(retval)
|
||||
.ok()
|
||||
|
@ -218,3 +217,131 @@ impl<T: Type, L: SizeType> Index<L> for ArrayWithoutLen<T> {
|
|||
Interned::into_inner(Intern::intern_sized(ArrayType::new(self.element, len)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<Lhs: Type, Rhs: Type, Len: Size> ExprPartialEq<ArrayType<Rhs, Len>> for ArrayType<Lhs, Len>
|
||||
where
|
||||
Lhs: ExprPartialEq<Rhs>,
|
||||
{
|
||||
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<ArrayType<Rhs, Len>>) -> Expr<Bool> {
|
||||
let lhs_ty = Expr::ty(lhs);
|
||||
let rhs_ty = Expr::ty(rhs);
|
||||
assert_eq!(lhs_ty.len(), rhs_ty.len());
|
||||
lhs.into_iter()
|
||||
.zip(rhs)
|
||||
.map(|(l, r)| l.cmp_eq(r))
|
||||
.collect::<Expr<Array<Bool>>>()
|
||||
.cast_to_bits()
|
||||
.all_one_bits()
|
||||
}
|
||||
|
||||
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<ArrayType<Rhs, Len>>) -> Expr<Bool> {
|
||||
let lhs_ty = Expr::ty(lhs);
|
||||
let rhs_ty = Expr::ty(rhs);
|
||||
assert_eq!(lhs_ty.len(), rhs_ty.len());
|
||||
lhs.into_iter()
|
||||
.zip(rhs)
|
||||
.map(|(l, r)| l.cmp_ne(r))
|
||||
.collect::<Expr<Array<Bool>>>()
|
||||
.cast_to_bits()
|
||||
.any_one_bits()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type, Len: Size> ExprIntoIterator for ArrayType<T, Len> {
|
||||
type Item = T;
|
||||
type ExprIntoIter = ExprArrayIter<T, Len>;
|
||||
|
||||
fn expr_into_iter(e: Expr<Self>) -> Self::ExprIntoIter {
|
||||
ExprArrayIter {
|
||||
base: e,
|
||||
indexes: 0..Expr::ty(e).len(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ExprArrayIter<T: Type, Len: Size> {
|
||||
base: Expr<ArrayType<T, Len>>,
|
||||
indexes: std::ops::Range<usize>,
|
||||
}
|
||||
|
||||
impl<T: Type, Len: Size> ExprArrayIter<T, Len> {
|
||||
pub fn base(&self) -> Expr<ArrayType<T, Len>> {
|
||||
self.base
|
||||
}
|
||||
pub fn indexes(&self) -> std::ops::Range<usize> {
|
||||
self.indexes.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type, Len: Size> Iterator for ExprArrayIter<T, Len> {
|
||||
type Item = Expr<T>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.indexes.next().map(|i| self.base[i])
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.indexes.size_hint()
|
||||
}
|
||||
|
||||
fn count(self) -> usize {
|
||||
self.indexes.count()
|
||||
}
|
||||
|
||||
fn last(mut self) -> Option<Self::Item> {
|
||||
self.next_back()
|
||||
}
|
||||
|
||||
fn nth(&mut self, n: usize) -> Option<Self::Item> {
|
||||
self.indexes.nth(n).map(|i| self.base[i])
|
||||
}
|
||||
|
||||
fn fold<B, F>(self, init: B, mut f: F) -> B
|
||||
where
|
||||
F: FnMut(B, Self::Item) -> B,
|
||||
{
|
||||
self.indexes.fold(init, |b, i| f(b, self.base[i]))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type, Len: Size> DoubleEndedIterator for ExprArrayIter<T, Len> {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
self.indexes.next_back().map(|i| self.base[i])
|
||||
}
|
||||
|
||||
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
|
||||
self.indexes.nth_back(n).map(|i| self.base[i])
|
||||
}
|
||||
|
||||
fn rfold<B, F>(self, init: B, mut f: F) -> B
|
||||
where
|
||||
F: FnMut(B, Self::Item) -> B,
|
||||
{
|
||||
self.indexes.rfold(init, |b, i| f(b, self.base[i]))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type, Len: Size> ExactSizeIterator for ExprArrayIter<T, Len> {
|
||||
fn len(&self) -> usize {
|
||||
self.indexes.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type, Len: Size> FusedIterator for ExprArrayIter<T, Len> {}
|
||||
|
||||
impl<A: StaticType> ExprFromIterator<Expr<A>> for Array<A> {
|
||||
fn expr_from_iter<T: IntoIterator<Item = Expr<A>>>(iter: T) -> Expr<Self> {
|
||||
ArrayLiteral::new(
|
||||
A::TYPE,
|
||||
iter.into_iter().map(|v| Expr::canonical(v)).collect(),
|
||||
)
|
||||
.to_expr()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A: StaticType> ExprFromIterator<&'a Expr<A>> for Array<A> {
|
||||
fn expr_from_iter<T: IntoIterator<Item = &'a Expr<A>>>(iter: T) -> Expr<Self> {
|
||||
iter.into_iter().copied().collect()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,11 @@
|
|||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::{
|
||||
expr::{ops::BundleLiteral, Expr, ToExpr},
|
||||
expr::{
|
||||
ops::{ArrayLiteral, BundleLiteral, ExprPartialEq},
|
||||
CastToBits, Expr, ReduceBits, ToExpr,
|
||||
},
|
||||
int::{Bool, DynSize},
|
||||
intern::{Intern, Interned},
|
||||
sim::{SimValue, ToSimValue},
|
||||
source_location::SourceLocation,
|
||||
|
@ -325,7 +329,19 @@ macro_rules! impl_tuple_builder_fields {
|
|||
}
|
||||
|
||||
macro_rules! impl_tuples {
|
||||
([$({#[num = $num:literal, field = $field:ident, ty = $ty_var:ident: $Ty:ident] $var:ident: $T:ident})*] []) => {
|
||||
(
|
||||
[$({
|
||||
#[
|
||||
num = $num:tt,
|
||||
field = $field:ident,
|
||||
ty = $ty_var:ident: $Ty:ident,
|
||||
lhs = $lhs_var:ident: $Lhs:ident,
|
||||
rhs = $rhs_var:ident: $Rhs:ident
|
||||
]
|
||||
$var:ident: $T:ident
|
||||
})*]
|
||||
[]
|
||||
) => {
|
||||
impl_tuple_builder_fields! {
|
||||
{}
|
||||
[$({
|
||||
|
@ -498,6 +514,29 @@ macro_rules! impl_tuples {
|
|||
Self::into_sim_value(*self, ty)
|
||||
}
|
||||
}
|
||||
impl<$($Lhs: Type + ExprPartialEq<$Rhs>, $Rhs: Type,)*> ExprPartialEq<($($Rhs,)*)> for ($($Lhs,)*) {
|
||||
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> {
|
||||
let ($($lhs_var,)*) = *lhs;
|
||||
let ($($rhs_var,)*) = *rhs;
|
||||
ArrayLiteral::<Bool, DynSize>::new(
|
||||
Bool,
|
||||
FromIterator::from_iter([$(Expr::canonical(ExprPartialEq::cmp_eq($lhs_var, $rhs_var)),)*]),
|
||||
)
|
||||
.cast_to_bits()
|
||||
.all_one_bits()
|
||||
}
|
||||
|
||||
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> {
|
||||
let ($($lhs_var,)*) = *lhs;
|
||||
let ($($rhs_var,)*) = *rhs;
|
||||
ArrayLiteral::<Bool, DynSize>::new(
|
||||
Bool,
|
||||
FromIterator::from_iter([$(Expr::canonical(ExprPartialEq::cmp_ne($lhs_var, $rhs_var)),)*]),
|
||||
)
|
||||
.cast_to_bits()
|
||||
.any_one_bits()
|
||||
}
|
||||
}
|
||||
};
|
||||
([$($lhs:tt)*] [$rhs_first:tt $($rhs:tt)*]) => {
|
||||
impl_tuples!([$($lhs)*] []);
|
||||
|
@ -507,18 +546,18 @@ macro_rules! impl_tuples {
|
|||
|
||||
impl_tuples! {
|
||||
[] [
|
||||
{#[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}
|
||||
{#[num = 0, field = field_0, ty = ty0: Ty0, lhs = lhs0: Lhs0, rhs = rhs0: Rhs0] v0: T0}
|
||||
{#[num = 1, field = field_1, ty = ty1: Ty1, lhs = lhs1: Lhs1, rhs = rhs1: Rhs1] v1: T1}
|
||||
{#[num = 2, field = field_2, ty = ty2: Ty2, lhs = lhs2: Lhs2, rhs = rhs2: Rhs2] v2: T2}
|
||||
{#[num = 3, field = field_3, ty = ty3: Ty3, lhs = lhs3: Lhs3, rhs = rhs3: Rhs3] v3: T3}
|
||||
{#[num = 4, field = field_4, ty = ty4: Ty4, lhs = lhs4: Lhs4, rhs = rhs4: Rhs4] v4: T4}
|
||||
{#[num = 5, field = field_5, ty = ty5: Ty5, lhs = lhs5: Lhs5, rhs = rhs5: Rhs5] v5: T5}
|
||||
{#[num = 6, field = field_6, ty = ty6: Ty6, lhs = lhs6: Lhs6, rhs = rhs6: Rhs6] v6: T6}
|
||||
{#[num = 7, field = field_7, ty = ty7: Ty7, lhs = lhs7: Lhs7, rhs = rhs7: Rhs7] v7: T7}
|
||||
{#[num = 8, field = field_8, ty = ty8: Ty8, lhs = lhs8: Lhs8, rhs = rhs8: Rhs8] v8: T8}
|
||||
{#[num = 9, field = field_9, ty = ty9: Ty9, lhs = lhs9: Lhs9, rhs = rhs9: Rhs9] v9: T9}
|
||||
{#[num = 10, field = field_10, ty = ty10: Ty10, lhs = lhs10: Lhs10, rhs = rhs10: Rhs10] v10: T10}
|
||||
{#[num = 11, field = field_11, ty = ty11: Ty11, lhs = lhs11: Lhs11, rhs = rhs11: Rhs11] v11: T11}
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,10 @@
|
|||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::{
|
||||
expr::{ops::VariantAccess, Expr, ToExpr},
|
||||
expr::{
|
||||
ops::{ExprPartialEq, VariantAccess},
|
||||
Expr, ToExpr,
|
||||
},
|
||||
hdl,
|
||||
int::Bool,
|
||||
intern::{Intern, Interned},
|
||||
|
@ -360,6 +363,60 @@ pub enum HdlOption<T: Type> {
|
|||
HdlSome(T),
|
||||
}
|
||||
|
||||
impl<Lhs: Type + ExprPartialEq<Rhs>, Rhs: Type> ExprPartialEq<HdlOption<Rhs>> for HdlOption<Lhs> {
|
||||
#[hdl]
|
||||
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<HdlOption<Rhs>>) -> Expr<Bool> {
|
||||
#[hdl]
|
||||
let cmp_eq = wire();
|
||||
#[hdl]
|
||||
match lhs {
|
||||
HdlSome(lhs) =>
|
||||
{
|
||||
#[hdl]
|
||||
match rhs {
|
||||
HdlSome(rhs) => connect(cmp_eq, ExprPartialEq::cmp_eq(lhs, rhs)),
|
||||
HdlNone => connect(cmp_eq, false),
|
||||
}
|
||||
}
|
||||
HdlNone =>
|
||||
{
|
||||
#[hdl]
|
||||
match rhs {
|
||||
HdlSome(_) => connect(cmp_eq, false),
|
||||
HdlNone => connect(cmp_eq, true),
|
||||
}
|
||||
}
|
||||
}
|
||||
cmp_eq
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<HdlOption<Rhs>>) -> Expr<Bool> {
|
||||
#[hdl]
|
||||
let cmp_ne = wire();
|
||||
#[hdl]
|
||||
match lhs {
|
||||
HdlSome(lhs) =>
|
||||
{
|
||||
#[hdl]
|
||||
match rhs {
|
||||
HdlSome(rhs) => connect(cmp_ne, ExprPartialEq::cmp_ne(lhs, rhs)),
|
||||
HdlNone => connect(cmp_ne, true),
|
||||
}
|
||||
}
|
||||
HdlNone =>
|
||||
{
|
||||
#[hdl]
|
||||
match rhs {
|
||||
HdlSome(_) => connect(cmp_ne, true),
|
||||
HdlNone => connect(cmp_ne, false),
|
||||
}
|
||||
}
|
||||
}
|
||||
cmp_ne
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn HdlNone<T: StaticType>() -> Expr<HdlOption<T>> {
|
||||
HdlOption[T::TYPE].HdlNone()
|
||||
|
|
|
@ -16,6 +16,7 @@ use crate::{
|
|||
transform::visit::{Fold, Folder, Visit, Visitor},
|
||||
Instance, ModuleIO,
|
||||
},
|
||||
phantom_const::PhantomConst,
|
||||
reg::Reg,
|
||||
reset::{AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset},
|
||||
ty::{CanonicalType, StaticType, Type, TypeWithDeref},
|
||||
|
@ -109,6 +110,7 @@ expr_enum! {
|
|||
UIntLiteral(Interned<UIntValue>),
|
||||
SIntLiteral(Interned<SIntValue>),
|
||||
BoolLiteral(bool),
|
||||
PhantomConst(PhantomConst),
|
||||
BundleLiteral(ops::BundleLiteral),
|
||||
ArrayLiteral(ops::ArrayLiteral<CanonicalType, DynSize>),
|
||||
EnumLiteral(ops::EnumLiteral),
|
||||
|
@ -755,3 +757,27 @@ pub fn repeat<T: Type, L: SizeType>(
|
|||
)
|
||||
.to_expr()
|
||||
}
|
||||
|
||||
impl<T: ?Sized + crate::phantom_const::PhantomConstValue> ToExpr for PhantomConst<T> {
|
||||
type Type = Self;
|
||||
|
||||
fn to_expr(&self) -> Expr<Self::Type> {
|
||||
Expr {
|
||||
__enum: ExprEnum::PhantomConst(self.canonical_phantom_const()).intern_sized(),
|
||||
__ty: *self,
|
||||
__flow: Flow::Source,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + crate::phantom_const::PhantomConstValue> GetTarget for PhantomConst<T> {
|
||||
fn target(&self) -> Option<Interned<Target>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + crate::phantom_const::PhantomConstValue> ToLiteralBits for PhantomConst<T> {
|
||||
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
|
||||
Ok(Interned::default())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,14 +11,15 @@ use crate::{
|
|||
GetTarget, Target, TargetPathArrayElement, TargetPathBundleField,
|
||||
TargetPathDynArrayElement, TargetPathElement,
|
||||
},
|
||||
CastTo, Expr, ExprEnum, Flow, HdlPartialEq, HdlPartialOrd, NotALiteralExpr, ReduceBits,
|
||||
ToExpr, ToLiteralBits,
|
||||
CastBitsTo as _, CastTo, CastToBits as _, Expr, ExprEnum, Flow, HdlPartialEq,
|
||||
HdlPartialOrd, NotALiteralExpr, ReduceBits, ToExpr, ToLiteralBits,
|
||||
},
|
||||
int::{
|
||||
Bool, BoolOrIntType, DynSize, IntType, KnownSize, SInt, SIntType, SIntValue, Size, UInt,
|
||||
UIntType, UIntValue,
|
||||
},
|
||||
intern::{Intern, Interned},
|
||||
phantom_const::{PhantomConst, PhantomConstValue},
|
||||
reset::{
|
||||
AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset, ToAsyncReset, ToReset,
|
||||
ToSyncReset,
|
||||
|
@ -1892,6 +1893,26 @@ impl ExprCastTo<Clock> for Clock {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + PhantomConstValue> ExprCastTo<()> for PhantomConst<T> {
|
||||
fn cast_to(src: Expr<Self>, to_type: ()) -> Expr<()> {
|
||||
src.cast_to_bits().cast_bits_to(to_type)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + PhantomConstValue> ExprCastTo<PhantomConst<T>> for () {
|
||||
fn cast_to(src: Expr<Self>, to_type: PhantomConst<T>) -> Expr<PhantomConst<T>> {
|
||||
src.cast_to_bits().cast_bits_to(to_type)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + PhantomConstValue, U: ?Sized + PhantomConstValue> ExprCastTo<PhantomConst<T>>
|
||||
for PhantomConst<U>
|
||||
{
|
||||
fn cast_to(src: Expr<Self>, to_type: PhantomConst<T>) -> Expr<PhantomConst<T>> {
|
||||
src.cast_to_bits().cast_bits_to(to_type)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct FieldAccess<FieldType: Type = CanonicalType> {
|
||||
base: Expr<Bundle>,
|
||||
|
@ -2708,3 +2729,47 @@ impl<T: Type> ToExpr for Uninit<T> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ExprIntoIterator: Type {
|
||||
type Item: Type;
|
||||
type ExprIntoIter: Iterator<Item = Expr<Self::Item>>;
|
||||
|
||||
fn expr_into_iter(e: Expr<Self>) -> Self::ExprIntoIter;
|
||||
}
|
||||
|
||||
impl<T: ExprIntoIterator> IntoIterator for Expr<T> {
|
||||
type Item = Expr<T::Item>;
|
||||
type IntoIter = T::ExprIntoIter;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
T::expr_into_iter(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ExprIntoIterator> IntoIterator for &'_ Expr<T> {
|
||||
type Item = Expr<T::Item>;
|
||||
type IntoIter = T::ExprIntoIter;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
T::expr_into_iter(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ExprIntoIterator> IntoIterator for &'_ mut Expr<T> {
|
||||
type Item = Expr<T::Item>;
|
||||
type IntoIter = T::ExprIntoIter;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
T::expr_into_iter(*self)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ExprFromIterator<A>: Type {
|
||||
fn expr_from_iter<T: IntoIterator<Item = A>>(iter: T) -> Expr<Self>;
|
||||
}
|
||||
|
||||
impl<This: ExprFromIterator<A>, A> FromIterator<A> for Expr<This> {
|
||||
fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self {
|
||||
This::expr_from_iter(iter)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ use crate::{
|
|||
target::{
|
||||
Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathElement,
|
||||
},
|
||||
Expr, ExprEnum,
|
||||
CastBitsTo, Expr, ExprEnum,
|
||||
},
|
||||
formal::FormalKind,
|
||||
int::{Bool, DynSize, IntType, SIntValue, UInt, UIntValue},
|
||||
|
@ -447,6 +447,7 @@ impl TypeState {
|
|||
CanonicalType::AsyncReset(AsyncReset {}) => "AsyncReset".into(),
|
||||
CanonicalType::SyncReset(SyncReset {}) => "UInt<1>".into(),
|
||||
CanonicalType::Reset(Reset {}) => "Reset".into(),
|
||||
CanonicalType::PhantomConst(_) => "{}".into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1152,6 +1153,7 @@ impl<'a> Exporter<'a> {
|
|||
| CanonicalType::Clock(_)
|
||||
| CanonicalType::AsyncReset(_)
|
||||
| CanonicalType::Reset(_) => format!("asUInt({value_str})"),
|
||||
CanonicalType::PhantomConst(_) => "UInt<0>(0)".into(),
|
||||
}
|
||||
}
|
||||
fn expr_cast_bits_to_bundle(
|
||||
|
@ -1357,6 +1359,12 @@ impl<'a> Exporter<'a> {
|
|||
CanonicalType::AsyncReset(_) => format!("asAsyncReset({value_str})"),
|
||||
CanonicalType::SyncReset(_) => value_str,
|
||||
CanonicalType::Reset(_) => unreachable!("Reset is not bit castable to"),
|
||||
CanonicalType::PhantomConst(_) => {
|
||||
let retval = self.module.ns.make_new("_cast_bits_to_phantom_const_expr");
|
||||
definitions.add_definition_line(format_args!("{extra_indent}wire {retval}: {{}}"));
|
||||
definitions.add_definition_line(format_args!("{extra_indent}invalidate {retval}"));
|
||||
return retval.to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
fn expr_unary<T: Type>(
|
||||
|
@ -1395,6 +1403,11 @@ impl<'a> Exporter<'a> {
|
|||
ExprEnum::UIntLiteral(literal) => self.uint_literal(&literal),
|
||||
ExprEnum::SIntLiteral(literal) => self.sint_literal(&literal),
|
||||
ExprEnum::BoolLiteral(literal) => self.bool_literal(literal),
|
||||
ExprEnum::PhantomConst(ty) => self.expr(
|
||||
UInt[0].zero().cast_bits_to(ty.canonical()),
|
||||
definitions,
|
||||
const_ty,
|
||||
),
|
||||
ExprEnum::ArrayLiteral(array_literal) => {
|
||||
self.array_literal_expr(array_literal, definitions, const_ty)
|
||||
}
|
||||
|
@ -2245,6 +2258,7 @@ impl<'a> Exporter<'a> {
|
|||
ModuleBody::Extern(ExternModuleBody {
|
||||
verilog_name,
|
||||
parameters,
|
||||
simulation: _,
|
||||
}) => {
|
||||
let verilog_name = Ident(verilog_name);
|
||||
writeln!(body, "{indent}defname = {verilog_name}").unwrap();
|
||||
|
|
|
@ -96,6 +96,7 @@ pub mod int;
|
|||
pub mod intern;
|
||||
pub mod memory;
|
||||
pub mod module;
|
||||
pub mod phantom_const;
|
||||
pub mod prelude;
|
||||
pub mod reg;
|
||||
pub mod reset;
|
||||
|
|
|
@ -1082,6 +1082,7 @@ pub fn splat_mask<T: Type>(ty: T, value: Expr<Bool>) -> Expr<AsMask<T>> {
|
|||
)
|
||||
.to_expr(),
|
||||
)),
|
||||
CanonicalType::PhantomConst(_) => Expr::from_canonical(Expr::canonical(().to_expr())),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ use crate::{
|
|||
memory::{Mem, MemBuilder, MemBuilderTarget, PortName},
|
||||
reg::Reg,
|
||||
reset::{AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset},
|
||||
sim::{ExternModuleSimGenerator, ExternModuleSimulation},
|
||||
source_location::SourceLocation,
|
||||
ty::{CanonicalType, Type},
|
||||
util::ScopedRef,
|
||||
|
@ -1081,6 +1082,7 @@ pub struct ExternModuleBody<
|
|||
> {
|
||||
pub verilog_name: Interned<str>,
|
||||
pub parameters: P,
|
||||
pub simulation: Option<ExternModuleSimulation<Bundle>>,
|
||||
}
|
||||
|
||||
impl From<ExternModuleBody<Vec<ExternModuleParameter>>> for ExternModuleBody {
|
||||
|
@ -1088,11 +1090,13 @@ impl From<ExternModuleBody<Vec<ExternModuleParameter>>> for ExternModuleBody {
|
|||
let ExternModuleBody {
|
||||
verilog_name,
|
||||
parameters,
|
||||
simulation,
|
||||
} = value;
|
||||
let parameters = Intern::intern_owned(parameters);
|
||||
Self {
|
||||
verilog_name,
|
||||
parameters,
|
||||
simulation,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1283,10 +1287,12 @@ impl<T: BundleType> fmt::Debug for DebugModuleBody<T> {
|
|||
ModuleBody::Extern(ExternModuleBody {
|
||||
verilog_name,
|
||||
parameters,
|
||||
simulation,
|
||||
}) => {
|
||||
debug_struct
|
||||
.field("verilog_name", verilog_name)
|
||||
.field("parameters", parameters);
|
||||
.field("parameters", parameters)
|
||||
.field("simulation", simulation);
|
||||
}
|
||||
}
|
||||
debug_struct.finish_non_exhaustive()
|
||||
|
@ -1490,6 +1496,9 @@ impl TargetState {
|
|||
})
|
||||
.collect(),
|
||||
},
|
||||
CanonicalType::PhantomConst(_) => TargetStateInner::Decomposed {
|
||||
subtargets: HashMap::new(),
|
||||
},
|
||||
CanonicalType::Array(ty) => TargetStateInner::Decomposed {
|
||||
subtargets: (0..ty.len())
|
||||
.map(|index| {
|
||||
|
@ -1758,7 +1767,12 @@ impl AssertValidityState {
|
|||
ModuleBody::Extern(ExternModuleBody {
|
||||
verilog_name: _,
|
||||
parameters: _,
|
||||
}) => {}
|
||||
simulation,
|
||||
}) => {
|
||||
if let Some(simulation) = simulation {
|
||||
simulation.check_io_ty(self.module.io_ty);
|
||||
}
|
||||
}
|
||||
ModuleBody::Normal(NormalModuleBody { body }) => {
|
||||
let body = self.make_block_index(body);
|
||||
assert_eq!(body, 0);
|
||||
|
@ -2105,6 +2119,7 @@ impl ModuleBuilder {
|
|||
ModuleKind::Extern => ModuleBody::Extern(ExternModuleBody {
|
||||
verilog_name: name.0,
|
||||
parameters: vec![],
|
||||
simulation: None,
|
||||
}),
|
||||
ModuleKind::Normal => ModuleBody::Normal(NormalModuleBody {
|
||||
body: BuilderModuleBody {
|
||||
|
@ -2171,6 +2186,7 @@ impl ModuleBuilder {
|
|||
.builder_extern_body()
|
||||
.verilog_name = name.intern();
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn parameter(&self, name: impl AsRef<str>, value: ExternModuleParameterValue) {
|
||||
let name = name.as_ref();
|
||||
self.impl_
|
||||
|
@ -2183,6 +2199,7 @@ impl ModuleBuilder {
|
|||
value,
|
||||
});
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn parameter_int(&self, name: impl AsRef<str>, value: impl Into<BigInt>) {
|
||||
let name = name.as_ref();
|
||||
let value = value.into();
|
||||
|
@ -2196,6 +2213,7 @@ impl ModuleBuilder {
|
|||
value: ExternModuleParameterValue::Integer(value),
|
||||
});
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn parameter_str(&self, name: impl AsRef<str>, value: impl AsRef<str>) {
|
||||
let name = name.as_ref();
|
||||
let value = value.as_ref();
|
||||
|
@ -2209,6 +2227,7 @@ impl ModuleBuilder {
|
|||
value: ExternModuleParameterValue::String(value.intern()),
|
||||
});
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn parameter_raw_verilog(&self, name: impl AsRef<str>, raw_verilog: impl AsRef<str>) {
|
||||
let name = name.as_ref();
|
||||
let raw_verilog = raw_verilog.as_ref();
|
||||
|
@ -2222,6 +2241,15 @@ impl ModuleBuilder {
|
|||
value: ExternModuleParameterValue::RawVerilog(raw_verilog.intern()),
|
||||
});
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn extern_module_simulation<G: ExternModuleSimGenerator>(&self, generator: G) {
|
||||
let mut impl_ = self.impl_.borrow_mut();
|
||||
let simulation = &mut impl_.body.builder_extern_body().simulation;
|
||||
if simulation.is_some() {
|
||||
panic!("already added an extern module simulation");
|
||||
}
|
||||
*simulation = Some(ExternModuleSimulation::new(generator));
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
|
|
|
@ -155,6 +155,7 @@ impl ResetsLayout {
|
|||
CanonicalType::SyncReset(_) => ResetsLayout::SyncReset,
|
||||
CanonicalType::Reset(_) => ResetsLayout::Reset,
|
||||
CanonicalType::Clock(_) => ResetsLayout::NoResets,
|
||||
CanonicalType::PhantomConst(_) => ResetsLayout::NoResets,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -407,7 +408,8 @@ impl Resets {
|
|||
| CanonicalType::Bool(_)
|
||||
| CanonicalType::AsyncReset(_)
|
||||
| CanonicalType::SyncReset(_)
|
||||
| CanonicalType::Clock(_) => Ok(self.ty),
|
||||
| CanonicalType::Clock(_)
|
||||
| CanonicalType::PhantomConst(_) => Ok(self.ty),
|
||||
CanonicalType::Array(ty) => Ok(CanonicalType::Array(Array::new_dyn(
|
||||
self.array_elements().substituted_type(
|
||||
reset_graph,
|
||||
|
@ -998,7 +1000,8 @@ fn cast_bit_op<P: Pass, T: Type, A: Type>(
|
|||
CanonicalType::Array(_)
|
||||
| CanonicalType::Enum(_)
|
||||
| CanonicalType::Bundle(_)
|
||||
| CanonicalType::Reset(_) => unreachable!(),
|
||||
| CanonicalType::Reset(_)
|
||||
| CanonicalType::PhantomConst(_) => unreachable!(),
|
||||
$(CanonicalType::$Variant(ty) => Expr::expr_enum($arg.cast_to(ty)),)*
|
||||
}
|
||||
};
|
||||
|
@ -1010,6 +1013,7 @@ fn cast_bit_op<P: Pass, T: Type, A: Type>(
|
|||
| CanonicalType::Enum(_)
|
||||
| CanonicalType::Bundle(_)
|
||||
| CanonicalType::Reset(_) => unreachable!(),
|
||||
CanonicalType::PhantomConst(_) => Expr::expr_enum(arg),
|
||||
$(CanonicalType::$Variant(_) => {
|
||||
let arg = Expr::<$Variant>::from_canonical(arg);
|
||||
match_expr_ty!(arg, UInt, SInt, Bool, AsyncReset, SyncReset, Clock)
|
||||
|
@ -1040,6 +1044,7 @@ impl<P: Pass> RunPass<P> for ExprEnum {
|
|||
ExprEnum::UIntLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
||||
ExprEnum::SIntLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
||||
ExprEnum::BoolLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
||||
ExprEnum::PhantomConst(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
||||
ExprEnum::BundleLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
||||
ExprEnum::ArrayLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
||||
ExprEnum::EnumLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
||||
|
@ -1670,7 +1675,8 @@ impl RunPassDispatch for AnyReg {
|
|||
| CanonicalType::Enum(_)
|
||||
| CanonicalType::Bundle(_)
|
||||
| CanonicalType::Reset(_)
|
||||
| CanonicalType::Clock(_) => unreachable!(),
|
||||
| CanonicalType::Clock(_)
|
||||
| CanonicalType::PhantomConst(_) => unreachable!(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1769,6 +1775,7 @@ impl_run_pass_copy!([] SVAttributeAnnotation);
|
|||
impl_run_pass_copy!([] UInt);
|
||||
impl_run_pass_copy!([] usize);
|
||||
impl_run_pass_copy!([] FormalKind);
|
||||
impl_run_pass_copy!([] PhantomConst);
|
||||
|
||||
macro_rules! impl_run_pass_for_struct {
|
||||
(
|
||||
|
|
|
@ -69,7 +69,8 @@ fn contains_any_enum_types(ty: CanonicalType) -> bool {
|
|||
| CanonicalType::AsyncReset(_)
|
||||
| CanonicalType::SyncReset(_)
|
||||
| CanonicalType::Reset(_)
|
||||
| CanonicalType::Clock(_) => false,
|
||||
| CanonicalType::Clock(_)
|
||||
| CanonicalType::PhantomConst(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -512,7 +513,8 @@ impl State {
|
|||
| CanonicalType::AsyncReset(_)
|
||||
| CanonicalType::SyncReset(_)
|
||||
| CanonicalType::Reset(_)
|
||||
| CanonicalType::Clock(_) => unreachable!(),
|
||||
| CanonicalType::Clock(_)
|
||||
| CanonicalType::PhantomConst(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -577,7 +579,8 @@ fn connect_port(
|
|||
| (CanonicalType::Clock(_), _)
|
||||
| (CanonicalType::AsyncReset(_), _)
|
||||
| (CanonicalType::SyncReset(_), _)
|
||||
| (CanonicalType::Reset(_), _) => unreachable!(
|
||||
| (CanonicalType::Reset(_), _)
|
||||
| (CanonicalType::PhantomConst(_), _) => unreachable!(
|
||||
"trying to connect memory ports:\n{:?}\n{:?}",
|
||||
Expr::ty(lhs),
|
||||
Expr::ty(rhs),
|
||||
|
@ -665,6 +668,7 @@ impl Folder for State {
|
|||
ExprEnum::UIntLiteral(_)
|
||||
| ExprEnum::SIntLiteral(_)
|
||||
| ExprEnum::BoolLiteral(_)
|
||||
| ExprEnum::PhantomConst(_)
|
||||
| ExprEnum::BundleLiteral(_)
|
||||
| ExprEnum::ArrayLiteral(_)
|
||||
| ExprEnum::Uninit(_)
|
||||
|
@ -923,7 +927,8 @@ impl Folder for State {
|
|||
| CanonicalType::Clock(_)
|
||||
| CanonicalType::AsyncReset(_)
|
||||
| CanonicalType::SyncReset(_)
|
||||
| CanonicalType::Reset(_) => canonical_type.default_fold(self),
|
||||
| CanonicalType::Reset(_)
|
||||
| CanonicalType::PhantomConst(_) => canonical_type.default_fold(self),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -62,6 +62,7 @@ enum MemSplit {
|
|||
Bundle {
|
||||
fields: Rc<[MemSplit]>,
|
||||
},
|
||||
PhantomConst,
|
||||
Single {
|
||||
output_mem: Option<Mem>,
|
||||
element_type: SingleType,
|
||||
|
@ -76,6 +77,7 @@ impl MemSplit {
|
|||
fn mark_changed_element_type(self) -> Self {
|
||||
match self {
|
||||
MemSplit::Bundle { fields: _ } => self,
|
||||
MemSplit::PhantomConst => self,
|
||||
MemSplit::Single {
|
||||
output_mem,
|
||||
element_type,
|
||||
|
@ -97,6 +99,7 @@ impl MemSplit {
|
|||
.map(|field| Self::new(field.ty).mark_changed_element_type())
|
||||
.collect(),
|
||||
},
|
||||
CanonicalType::PhantomConst(_) => MemSplit::PhantomConst,
|
||||
CanonicalType::Array(ty) => {
|
||||
let element = MemSplit::new(ty.element());
|
||||
if let Self::Single {
|
||||
|
@ -339,6 +342,7 @@ impl SplitMemState<'_, '_> {
|
|||
self.split_state_stack.pop();
|
||||
}
|
||||
}
|
||||
MemSplit::PhantomConst => {}
|
||||
MemSplit::Single {
|
||||
output_mem,
|
||||
element_type: single_type,
|
||||
|
@ -538,7 +542,12 @@ impl ModuleState {
|
|||
};
|
||||
loop {
|
||||
match input_element_type {
|
||||
CanonicalType::Bundle(_) => unreachable!("bundle types are always split"),
|
||||
CanonicalType::Bundle(_) => {
|
||||
unreachable!("bundle types are always split")
|
||||
}
|
||||
CanonicalType::PhantomConst(_) => {
|
||||
unreachable!("PhantomConst are always removed")
|
||||
}
|
||||
CanonicalType::Enum(_)
|
||||
if input_array_types
|
||||
.first()
|
||||
|
@ -743,7 +752,8 @@ impl ModuleState {
|
|||
..
|
||||
}
|
||||
| MemSplit::Bundle { .. }
|
||||
| MemSplit::Array { .. } => {
|
||||
| MemSplit::Array { .. }
|
||||
| MemSplit::PhantomConst => {
|
||||
let mut replacement_ports = Vec::with_capacity(input_mem.ports().len());
|
||||
let mut wire_port_rdata = Vec::with_capacity(input_mem.ports().len());
|
||||
let mut wire_port_wdata = Vec::with_capacity(input_mem.ports().len());
|
||||
|
|
|
@ -28,8 +28,10 @@ use crate::{
|
|||
NormalModuleBody, ScopedNameId, Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtIf,
|
||||
StmtInstance, StmtMatch, StmtReg, StmtWire,
|
||||
},
|
||||
phantom_const::PhantomConst,
|
||||
reg::Reg,
|
||||
reset::{AsyncReset, Reset, ResetType, SyncReset},
|
||||
sim::ExternModuleSimulation,
|
||||
source_location::SourceLocation,
|
||||
ty::{CanonicalType, Type},
|
||||
wire::Wire,
|
||||
|
|
313
crates/fayalite/src/phantom_const.rs
Normal file
313
crates/fayalite/src/phantom_const.rs
Normal file
|
@ -0,0 +1,313 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
expr::{
|
||||
ops::{ExprPartialEq, ExprPartialOrd},
|
||||
Expr, ToExpr,
|
||||
},
|
||||
int::Bool,
|
||||
intern::{Intern, Interned, InternedCompare, LazyInterned, Memoize},
|
||||
source_location::SourceLocation,
|
||||
ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties},
|
||||
};
|
||||
use std::{
|
||||
any::Any,
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
marker::PhantomData,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PhantomConstCanonicalValue {
|
||||
parsed: serde_json::Value,
|
||||
serialized: Interned<str>,
|
||||
}
|
||||
|
||||
impl PhantomConstCanonicalValue {
|
||||
pub fn from_json_value(parsed: serde_json::Value) -> Self {
|
||||
let serialized = Intern::intern_owned(
|
||||
serde_json::to_string(&parsed)
|
||||
.expect("conversion from json value to text shouldn't fail"),
|
||||
);
|
||||
Self { parsed, serialized }
|
||||
}
|
||||
pub fn as_json_value(&self) -> &serde_json::Value {
|
||||
&self.parsed
|
||||
}
|
||||
pub fn as_str(&self) -> Interned<str> {
|
||||
self.serialized
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for PhantomConstCanonicalValue {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(&self.serialized)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PhantomConstCanonicalValue {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(&self.serialized)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for PhantomConstCanonicalValue {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.serialized == other.serialized
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for PhantomConstCanonicalValue {}
|
||||
|
||||
impl Hash for PhantomConstCanonicalValue {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.serialized.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for PhantomConstCanonicalValue {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
self.parsed.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for PhantomConstCanonicalValue {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
Ok(Self::from_json_value(serde_json::Value::deserialize(
|
||||
deserializer,
|
||||
)?))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PhantomConstValue: Intern + InternedCompare + Serialize + fmt::Debug {
|
||||
fn deserialize_value<'de, D>(deserializer: D) -> Result<Interned<Self>, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>;
|
||||
}
|
||||
|
||||
impl<T> PhantomConstValue for T
|
||||
where
|
||||
T: ?Sized + Intern + InternedCompare + Serialize + fmt::Debug,
|
||||
Interned<T>: DeserializeOwned,
|
||||
{
|
||||
fn deserialize_value<'de, D>(deserializer: D) -> Result<Interned<Self>, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
<Interned<T> as Deserialize<'de>>::deserialize(deserializer)
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper type that allows any Rust value to be smuggled as a HDL [`Type`].
|
||||
/// This only works for values that can be [serialized][Serialize] to and [deserialized][Deserialize] from [JSON][serde_json].
|
||||
pub struct PhantomConst<T: ?Sized + PhantomConstValue = PhantomConstCanonicalValue> {
|
||||
value: LazyInterned<T>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized + PhantomConstValue> fmt::Debug for PhantomConst<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_tuple("PhantomConst").field(&self.get()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + PhantomConstValue> Clone for PhantomConst<T> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + PhantomConstValue> Copy for PhantomConst<T> {}
|
||||
|
||||
impl<T: ?Sized + PhantomConstValue> PartialEq for PhantomConst<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.get() == other.get()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + PhantomConstValue> Eq for PhantomConst<T> {}
|
||||
|
||||
impl<T: ?Sized + PhantomConstValue> Hash for PhantomConst<T> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.get().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
struct PhantomConstCanonicalMemoize<T: ?Sized, const IS_FROM_CANONICAL: bool>(PhantomData<T>);
|
||||
|
||||
impl<T: ?Sized, const IS_FROM_CANONICAL: bool> Copy
|
||||
for PhantomConstCanonicalMemoize<T, IS_FROM_CANONICAL>
|
||||
{
|
||||
}
|
||||
|
||||
impl<T: ?Sized, const IS_FROM_CANONICAL: bool> Clone
|
||||
for PhantomConstCanonicalMemoize<T, IS_FROM_CANONICAL>
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized, const IS_FROM_CANONICAL: bool> Eq
|
||||
for PhantomConstCanonicalMemoize<T, IS_FROM_CANONICAL>
|
||||
{
|
||||
}
|
||||
|
||||
impl<T: ?Sized, const IS_FROM_CANONICAL: bool> PartialEq
|
||||
for PhantomConstCanonicalMemoize<T, IS_FROM_CANONICAL>
|
||||
{
|
||||
fn eq(&self, _other: &Self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized, const IS_FROM_CANONICAL: bool> Hash
|
||||
for PhantomConstCanonicalMemoize<T, IS_FROM_CANONICAL>
|
||||
{
|
||||
fn hash<H: Hasher>(&self, _state: &mut H) {}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + PhantomConstValue> Memoize for PhantomConstCanonicalMemoize<T, false> {
|
||||
type Input = Interned<T>;
|
||||
type InputOwned = Interned<T>;
|
||||
type Output = Interned<PhantomConstCanonicalValue>;
|
||||
|
||||
fn inner(self, input: &Self::Input) -> Self::Output {
|
||||
Intern::intern_sized(PhantomConstCanonicalValue::from_json_value(
|
||||
serde_json::to_value(input)
|
||||
.expect("serialization failed when constructing a canonical PhantomConst"),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + PhantomConstValue> Memoize for PhantomConstCanonicalMemoize<T, true> {
|
||||
type Input = Interned<PhantomConstCanonicalValue>;
|
||||
type InputOwned = Interned<PhantomConstCanonicalValue>;
|
||||
type Output = Interned<T>;
|
||||
|
||||
fn inner(self, input: &Self::Input) -> Self::Output {
|
||||
PhantomConstValue::deserialize_value(input.as_json_value())
|
||||
.expect("deserialization failed ")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + PhantomConstValue> PhantomConst<T>
|
||||
where
|
||||
Interned<T>: Default,
|
||||
{
|
||||
pub const fn default() -> Self {
|
||||
PhantomConst {
|
||||
value: LazyInterned::new_lazy(&Interned::<T>::default),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + PhantomConstValue> PhantomConst<T> {
|
||||
pub fn new(value: Interned<T>) -> Self {
|
||||
Self {
|
||||
value: LazyInterned::Interned(value),
|
||||
}
|
||||
}
|
||||
pub fn get(self) -> Interned<T> {
|
||||
self.value.interned()
|
||||
}
|
||||
pub fn type_properties(self) -> TypeProperties {
|
||||
<()>::TYPE_PROPERTIES
|
||||
}
|
||||
pub fn can_connect(self, other: Self) -> bool {
|
||||
self == other
|
||||
}
|
||||
pub fn canonical_phantom_const(self) -> PhantomConst {
|
||||
if let Some(&retval) = <dyn Any>::downcast_ref::<PhantomConst>(&self) {
|
||||
return retval;
|
||||
}
|
||||
<PhantomConst>::new(
|
||||
PhantomConstCanonicalMemoize::<T, false>(PhantomData).get_owned(self.get()),
|
||||
)
|
||||
}
|
||||
pub fn from_canonical_phantom_const(canonical_type: PhantomConst) -> Self {
|
||||
if let Some(&retval) = <dyn Any>::downcast_ref::<Self>(&canonical_type) {
|
||||
return retval;
|
||||
}
|
||||
Self::new(
|
||||
PhantomConstCanonicalMemoize::<T, true>(PhantomData).get_owned(canonical_type.get()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + PhantomConstValue> Type for PhantomConst<T> {
|
||||
type BaseType = PhantomConst;
|
||||
type MaskType = ();
|
||||
impl_match_variant_as_self!();
|
||||
|
||||
fn mask_type(&self) -> Self::MaskType {
|
||||
()
|
||||
}
|
||||
|
||||
fn canonical(&self) -> CanonicalType {
|
||||
CanonicalType::PhantomConst(self.canonical_phantom_const())
|
||||
}
|
||||
|
||||
fn from_canonical(canonical_type: CanonicalType) -> Self {
|
||||
let CanonicalType::PhantomConst(phantom_const) = canonical_type else {
|
||||
panic!("expected PhantomConst");
|
||||
};
|
||||
Self::from_canonical_phantom_const(phantom_const)
|
||||
}
|
||||
|
||||
fn source_location() -> SourceLocation {
|
||||
SourceLocation::builtin()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + PhantomConstValue> StaticType for PhantomConst<T>
|
||||
where
|
||||
Interned<T>: Default,
|
||||
{
|
||||
const TYPE: Self = Self::default();
|
||||
const MASK_TYPE: Self::MaskType = ();
|
||||
const TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES;
|
||||
const MASK_TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES;
|
||||
}
|
||||
|
||||
impl<T: ?Sized + PhantomConstValue> ExprPartialEq<Self> for PhantomConst<T> {
|
||||
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
||||
assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
|
||||
true.to_expr()
|
||||
}
|
||||
|
||||
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
||||
assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
|
||||
false.to_expr()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + PhantomConstValue> ExprPartialOrd<Self> for PhantomConst<T> {
|
||||
fn cmp_lt(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
||||
assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
|
||||
false.to_expr()
|
||||
}
|
||||
|
||||
fn cmp_le(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
||||
assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
|
||||
true.to_expr()
|
||||
}
|
||||
|
||||
fn cmp_gt(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
||||
assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
|
||||
false.to_expr()
|
||||
}
|
||||
|
||||
fn cmp_ge(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
||||
assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
|
||||
true.to_expr()
|
||||
}
|
||||
}
|
|
@ -26,6 +26,7 @@ pub use crate::{
|
|||
annotate, connect, connect_any, incomplete_wire, instance, memory, memory_array,
|
||||
memory_with_init, reg_builder, wire, Instance, Module, ModuleBuilder,
|
||||
},
|
||||
phantom_const::PhantomConst,
|
||||
reg::Reg,
|
||||
reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset},
|
||||
source_location::SourceLocation,
|
||||
|
|
|
@ -15,7 +15,9 @@ use crate::{
|
|||
ExprEnum, Flow, ToLiteralBits,
|
||||
},
|
||||
int::{BoolOrIntType, IntType, SIntValue, UIntValue},
|
||||
intern::{Intern, Interned, Memoize},
|
||||
intern::{
|
||||
Intern, Interned, InternedCompare, Memoize, PtrEqWithTypeId, SupportsPtrEqWithTypeId,
|
||||
},
|
||||
memory::PortKind,
|
||||
module::{
|
||||
transform::deduce_resets::deduce_resets, AnnotatedModuleIO, Block, Id, InstantiatedModule,
|
||||
|
@ -51,7 +53,15 @@ use petgraph::{
|
|||
},
|
||||
};
|
||||
use std::{
|
||||
borrow::Cow, collections::BTreeSet, fmt, marker::PhantomData, mem, ops::IndexMut, sync::Arc,
|
||||
any::Any,
|
||||
borrow::Cow,
|
||||
collections::BTreeSet,
|
||||
fmt,
|
||||
future::{Future, IntoFuture},
|
||||
marker::PhantomData,
|
||||
mem,
|
||||
ops::IndexMut,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
mod interpreter;
|
||||
|
@ -167,6 +177,14 @@ impl<T: Type> CompiledTypeLayout<T> {
|
|||
body: CompiledTypeLayoutBody::Array { element },
|
||||
}
|
||||
}
|
||||
CanonicalType::PhantomConst(_) => {
|
||||
let unit_layout = CompiledTypeLayout::get(());
|
||||
CompiledTypeLayout {
|
||||
ty: *input,
|
||||
layout: unit_layout.layout,
|
||||
body: unit_layout.body,
|
||||
}
|
||||
}
|
||||
CanonicalType::Bundle(bundle) => {
|
||||
let mut layout = TypeLayout::empty();
|
||||
let fields = bundle
|
||||
|
@ -1792,7 +1810,7 @@ impl Compiler {
|
|||
}
|
||||
.into()
|
||||
}
|
||||
CanonicalType::Bundle(_) => unreachable!(),
|
||||
CanonicalType::Bundle(_) | CanonicalType::PhantomConst(_) => unreachable!(),
|
||||
CanonicalType::AsyncReset(_) => TraceAsyncReset {
|
||||
location: self.make_trace_scalar_helper(
|
||||
instantiated_module,
|
||||
|
@ -2009,6 +2027,13 @@ impl Compiler {
|
|||
| CanonicalType::Clock(_) => {
|
||||
self.make_trace_scalar(instantiated_module, target, name, source_location)
|
||||
}
|
||||
CanonicalType::PhantomConst(_) => TraceBundle {
|
||||
name,
|
||||
fields: Interned::default(),
|
||||
ty: Bundle::new(Interned::default()),
|
||||
flow: target.flow(),
|
||||
}
|
||||
.into(),
|
||||
}
|
||||
}
|
||||
fn make_trace_decl(
|
||||
|
@ -2469,6 +2494,9 @@ impl Compiler {
|
|||
Expr::field(Expr::<Bundle>::from_canonical(expr.arg()), &field.name)
|
||||
}),
|
||||
),
|
||||
CanonicalType::PhantomConst(_) => {
|
||||
self.compile_cast_aggregate_to_bits(instantiated_module, [])
|
||||
}
|
||||
}
|
||||
}
|
||||
fn compile_cast_bits_to(
|
||||
|
@ -2518,6 +2546,10 @@ impl Compiler {
|
|||
CanonicalType::SyncReset(ty) => Expr::canonical(expr.arg().cast_to(ty)),
|
||||
CanonicalType::Reset(_) => unreachable!(),
|
||||
CanonicalType::Clock(ty) => Expr::canonical(expr.arg().cast_to(ty)),
|
||||
CanonicalType::PhantomConst(ty) => {
|
||||
let _ = self.compile_expr(instantiated_module, Expr::canonical(expr.arg()));
|
||||
Expr::canonical(ty.to_expr())
|
||||
}
|
||||
};
|
||||
let retval = self.compile_expr(instantiated_module, Expr::canonical(retval));
|
||||
self.compiled_expr_to_value(retval, instantiated_module.leaf_module().source_location())
|
||||
|
@ -2567,6 +2599,7 @@ impl Compiler {
|
|||
CanonicalType::SyncReset(_) => false,
|
||||
CanonicalType::Reset(_) => false,
|
||||
CanonicalType::Clock(_) => false,
|
||||
CanonicalType::PhantomConst(_) => unreachable!(),
|
||||
};
|
||||
let dest_signed = match Expr::ty(expr) {
|
||||
CanonicalType::UInt(_) => false,
|
||||
|
@ -2579,6 +2612,7 @@ impl Compiler {
|
|||
CanonicalType::SyncReset(_) => false,
|
||||
CanonicalType::Reset(_) => false,
|
||||
CanonicalType::Clock(_) => false,
|
||||
CanonicalType::PhantomConst(_) => unreachable!(),
|
||||
};
|
||||
self.simple_nary_big_expr(instantiated_module, Expr::ty(expr), [arg], |dest, [src]| {
|
||||
match (src_signed, dest_signed) {
|
||||
|
@ -2634,6 +2668,9 @@ impl Compiler {
|
|||
}]
|
||||
})
|
||||
.into(),
|
||||
ExprEnum::PhantomConst(_) => self
|
||||
.compile_aggregate_literal(instantiated_module, Expr::ty(expr), Interned::default())
|
||||
.into(),
|
||||
ExprEnum::BundleLiteral(literal) => self
|
||||
.compile_aggregate_literal(
|
||||
instantiated_module,
|
||||
|
@ -3537,6 +3574,7 @@ impl Compiler {
|
|||
CanonicalType::SyncReset(_) => unreachable!(),
|
||||
CanonicalType::Reset(_) => unreachable!(),
|
||||
CanonicalType::Clock(_) => unreachable!(),
|
||||
CanonicalType::PhantomConst(_) => unreachable!("PhantomConst mismatch"),
|
||||
}
|
||||
}
|
||||
let Some(target) = lhs.target() else {
|
||||
|
@ -3901,6 +3939,7 @@ impl Compiler {
|
|||
CanonicalType::SyncReset(_) => false,
|
||||
CanonicalType::Reset(_) => false,
|
||||
CanonicalType::Clock(_) => false,
|
||||
CanonicalType::PhantomConst(_) => unreachable!(),
|
||||
};
|
||||
let width = data_layout.ty.bit_width();
|
||||
if let Some(MemoryPortReadInsns {
|
||||
|
@ -5909,6 +5948,7 @@ impl SimValue<CanonicalType> {
|
|||
CanonicalType::Clock(ty) => {
|
||||
Expr::canonical(Bool::bits_to_expr(Cow::Borrowed(bits)).cast_to(ty))
|
||||
}
|
||||
CanonicalType::PhantomConst(ty) => Expr::canonical(ty.to_expr()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6312,7 +6352,8 @@ impl ToSimValue<CanonicalType> for bool {
|
|||
| CanonicalType::SInt(_)
|
||||
| CanonicalType::Array(_)
|
||||
| CanonicalType::Enum(_)
|
||||
| CanonicalType::Bundle(_) => {
|
||||
| CanonicalType::Bundle(_)
|
||||
| CanonicalType::PhantomConst(_) => {
|
||||
panic!("can't create SimValue from bool: expected value of type: {ty:?}");
|
||||
}
|
||||
CanonicalType::Bool(_)
|
||||
|
@ -6977,6 +7018,7 @@ impl SimulationImpl {
|
|||
CanonicalType::SyncReset(_) => false,
|
||||
CanonicalType::Reset(_) => false,
|
||||
CanonicalType::Clock(_) => false,
|
||||
CanonicalType::PhantomConst(_) => unreachable!(),
|
||||
};
|
||||
match compiled_value.range.len() {
|
||||
TypeLen::A_SMALL_SLOT => read_write_small_scalar(
|
||||
|
@ -7354,3 +7396,108 @@ impl<T: BundleType> Simulation<T> {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ExternModuleSimulationState<T: BundleType> {
|
||||
io_ty: T,
|
||||
}
|
||||
|
||||
impl<T: BundleType> ExternModuleSimulationState<T> {
|
||||
pub fn canonical(self) -> ExternModuleSimulationState<Bundle> {
|
||||
ExternModuleSimulationState {
|
||||
io_ty: Bundle::from_canonical(self.io_ty.canonical()),
|
||||
}
|
||||
}
|
||||
pub fn from_canonical(sim: ExternModuleSimulationState<Bundle>) -> Self {
|
||||
Self {
|
||||
io_ty: T::from_canonical(sim.io_ty.canonical()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ExternModuleSimGenerator:
|
||||
Clone + Eq + std::hash::Hash + Any + Send + Sync + fmt::Debug
|
||||
{
|
||||
type IOType: BundleType;
|
||||
|
||||
fn run<'a>(
|
||||
&'a self,
|
||||
sim: ExternModuleSimulationState<Self::IOType>,
|
||||
) -> impl IntoFuture<Output = ()> + 'a;
|
||||
}
|
||||
|
||||
trait DynExternModuleSimGenerator: Any + Send + Sync + SupportsPtrEqWithTypeId + fmt::Debug {
|
||||
fn dyn_run<'a>(
|
||||
&'a self,
|
||||
sim: ExternModuleSimulationState<Bundle>,
|
||||
) -> Box<dyn Future<Output = ()> + 'a>;
|
||||
#[track_caller]
|
||||
fn check_io_ty(&self, io_ty: Bundle);
|
||||
}
|
||||
|
||||
impl<T: ExternModuleSimGenerator> DynExternModuleSimGenerator for T {
|
||||
fn dyn_run<'a>(
|
||||
&'a self,
|
||||
sim: ExternModuleSimulationState<Bundle>,
|
||||
) -> Box<dyn Future<Output = ()> + 'a> {
|
||||
Box::new(
|
||||
self.run(ExternModuleSimulationState::from_canonical(sim))
|
||||
.into_future(),
|
||||
)
|
||||
}
|
||||
#[track_caller]
|
||||
fn check_io_ty(&self, io_ty: Bundle) {
|
||||
T::IOType::from_canonical(io_ty.canonical());
|
||||
}
|
||||
}
|
||||
|
||||
impl InternedCompare for dyn DynExternModuleSimGenerator {
|
||||
type InternedCompareKey = PtrEqWithTypeId;
|
||||
|
||||
fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey {
|
||||
this.get_ptr_eq_with_type_id()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct ExternModuleSimulation<T: BundleType> {
|
||||
generator: Interned<dyn DynExternModuleSimGenerator>,
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: BundleType> ExternModuleSimulation<T> {
|
||||
pub fn new<G: ExternModuleSimGenerator>(generator: G) -> Self {
|
||||
Self {
|
||||
generator: Interned::cast_unchecked(
|
||||
generator.intern(),
|
||||
|v| -> &dyn DynExternModuleSimGenerator { v },
|
||||
),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
pub fn canonical(self) -> ExternModuleSimulation<Bundle> {
|
||||
ExternModuleSimulation {
|
||||
generator: self.generator,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
pub fn from_canonical(v: ExternModuleSimulation<Bundle>) -> Self {
|
||||
Self {
|
||||
generator: v.generator,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExternModuleSimulation<Bundle> {
|
||||
fn run<'a>(
|
||||
&'a self,
|
||||
sim: ExternModuleSimulationState<Bundle>,
|
||||
) -> Box<dyn Future<Output = ()> + 'a> {
|
||||
self.generator.dyn_run(sim)
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn check_io_ty(self, io_ty: Bundle) {
|
||||
self.generator.check_io_ty(io_ty);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,10 @@ use crate::{
|
|||
expr::Expr,
|
||||
int::{Bool, SInt, UInt},
|
||||
intern::{Intern, Interned},
|
||||
phantom_const::PhantomConst,
|
||||
reset::{AsyncReset, Reset, SyncReset},
|
||||
source_location::SourceLocation,
|
||||
util::ConstUsize,
|
||||
};
|
||||
use std::{fmt, hash::Hash, iter::FusedIterator, ops::Index};
|
||||
|
||||
|
@ -35,6 +37,7 @@ pub enum CanonicalType {
|
|||
SyncReset(SyncReset),
|
||||
Reset(Reset),
|
||||
Clock(Clock),
|
||||
PhantomConst(PhantomConst),
|
||||
}
|
||||
|
||||
impl fmt::Debug for CanonicalType {
|
||||
|
@ -50,6 +53,7 @@ impl fmt::Debug for CanonicalType {
|
|||
Self::SyncReset(v) => v.fmt(f),
|
||||
Self::Reset(v) => v.fmt(f),
|
||||
Self::Clock(v) => v.fmt(f),
|
||||
Self::PhantomConst(v) => v.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -67,6 +71,7 @@ impl CanonicalType {
|
|||
CanonicalType::SyncReset(v) => v.type_properties(),
|
||||
CanonicalType::Reset(v) => v.type_properties(),
|
||||
CanonicalType::Clock(v) => v.type_properties(),
|
||||
CanonicalType::PhantomConst(v) => v.type_properties(),
|
||||
}
|
||||
}
|
||||
pub fn is_passive(self) -> bool {
|
||||
|
@ -143,6 +148,12 @@ impl CanonicalType {
|
|||
};
|
||||
lhs.can_connect(rhs)
|
||||
}
|
||||
CanonicalType::PhantomConst(lhs) => {
|
||||
let CanonicalType::PhantomConst(rhs) = rhs else {
|
||||
return false;
|
||||
};
|
||||
lhs.can_connect(rhs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -166,7 +177,7 @@ impl<T: 'static + Send + Sync> MatchVariantAndInactiveScope for MatchVariantWith
|
|||
}
|
||||
|
||||
pub trait FillInDefaultedGenerics {
|
||||
type Type: Type;
|
||||
type Type;
|
||||
fn fill_in_defaulted_generics(self) -> Self::Type;
|
||||
}
|
||||
|
||||
|
@ -178,6 +189,22 @@ impl<T: Type> FillInDefaultedGenerics for T {
|
|||
}
|
||||
}
|
||||
|
||||
impl FillInDefaultedGenerics for usize {
|
||||
type Type = usize;
|
||||
|
||||
fn fill_in_defaulted_generics(self) -> Self::Type {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<const V: usize> FillInDefaultedGenerics for ConstUsize<V> {
|
||||
type Type = ConstUsize<V>;
|
||||
|
||||
fn fill_in_defaulted_generics(self) -> Self::Type {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
pub trait TypeOrDefaultSealed {}
|
||||
pub trait BaseTypeSealed {}
|
||||
|
@ -205,6 +232,7 @@ impl_base_type!(AsyncReset);
|
|||
impl_base_type!(SyncReset);
|
||||
impl_base_type!(Reset);
|
||||
impl_base_type!(Clock);
|
||||
impl_base_type!(PhantomConst);
|
||||
|
||||
impl sealed::BaseTypeSealed for CanonicalType {}
|
||||
|
||||
|
@ -299,6 +327,7 @@ impl Type for CanonicalType {
|
|||
CanonicalType::SyncReset(v) => v.mask_type().canonical(),
|
||||
CanonicalType::Reset(v) => v.mask_type().canonical(),
|
||||
CanonicalType::Clock(v) => v.mask_type().canonical(),
|
||||
CanonicalType::PhantomConst(v) => v.mask_type().canonical(),
|
||||
}
|
||||
}
|
||||
fn canonical(&self) -> CanonicalType {
|
||||
|
|
|
@ -29,4 +29,5 @@ pub use misc::{
|
|||
};
|
||||
|
||||
pub mod job_server;
|
||||
pub mod prefix_sum;
|
||||
pub mod ready_valid;
|
||||
|
|
839
crates/fayalite/src/util/prefix_sum.rs
Normal file
839
crates/fayalite/src/util/prefix_sum.rs
Normal file
|
@ -0,0 +1,839 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
// code derived from:
|
||||
// https://web.archive.org/web/20250303054010/https://git.libre-soc.org/?p=nmutil.git;a=blob;f=src/nmutil/prefix_sum.py;hb=effeb28e5848392adddcdad1f6e7a098f2a44c9c
|
||||
|
||||
use crate::intern::{Intern, Interned, Memoize};
|
||||
use std::{borrow::Cow, num::NonZeroUsize};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
pub struct PrefixSumOp {
|
||||
pub lhs_index: usize,
|
||||
pub rhs_and_dest_index: NonZeroUsize,
|
||||
pub row: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
#[non_exhaustive]
|
||||
pub struct DiagramConfig {
|
||||
pub space: Cow<'static, str>,
|
||||
pub vertical_bar: Cow<'static, str>,
|
||||
pub plus: Cow<'static, str>,
|
||||
pub slant: Cow<'static, str>,
|
||||
pub connect: Cow<'static, str>,
|
||||
pub no_connect: Cow<'static, str>,
|
||||
pub padding: usize,
|
||||
}
|
||||
|
||||
impl DiagramConfig {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
space: Cow::Borrowed(" "),
|
||||
vertical_bar: Cow::Borrowed("|"),
|
||||
plus: Cow::Borrowed("\u{2295}"), // ⊕
|
||||
slant: Cow::Borrowed(r"\"),
|
||||
connect: Cow::Borrowed("\u{25CF}"), // ●
|
||||
no_connect: Cow::Borrowed("X"),
|
||||
padding: 1,
|
||||
}
|
||||
}
|
||||
pub fn draw(self, ops: impl IntoIterator<Item = PrefixSumOp>, item_count: usize) -> String {
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct DiagramCell {
|
||||
slant: bool,
|
||||
plus: bool,
|
||||
tee: bool,
|
||||
}
|
||||
let mut ops_by_row: Vec<Vec<PrefixSumOp>> = Vec::new();
|
||||
let mut last_row = 0;
|
||||
ops.into_iter().for_each(|op| {
|
||||
assert!(
|
||||
op.lhs_index < op.rhs_and_dest_index.get(),
|
||||
"invalid PrefixSumOp! lhs_index must be less \
|
||||
than rhs_and_dest_index: {op:?}",
|
||||
);
|
||||
assert!(
|
||||
op.row >= last_row,
|
||||
"invalid PrefixSumOp! row must \
|
||||
not decrease (row last was: {last_row}): {op:?}",
|
||||
);
|
||||
let ops = if op.row > last_row || ops_by_row.is_empty() {
|
||||
ops_by_row.push(vec![]);
|
||||
ops_by_row.last_mut().expect("just pushed")
|
||||
} else {
|
||||
ops_by_row
|
||||
.last_mut()
|
||||
.expect("just checked if ops_by_row is empty")
|
||||
};
|
||||
if let Some(last) = ops.last() {
|
||||
assert!(
|
||||
op.rhs_and_dest_index < last.rhs_and_dest_index,
|
||||
"invalid PrefixSumOp! rhs_and_dest_index must strictly \
|
||||
decrease in a row:\nthis op: {op:?}\nlast op: {last:?}",
|
||||
);
|
||||
}
|
||||
ops.push(op);
|
||||
last_row = op.row;
|
||||
});
|
||||
let blank_row = || {
|
||||
vec![
|
||||
DiagramCell {
|
||||
slant: false,
|
||||
plus: false,
|
||||
tee: false
|
||||
};
|
||||
item_count
|
||||
]
|
||||
};
|
||||
let mut cells = vec![blank_row()];
|
||||
for ops in ops_by_row {
|
||||
let max_distance = ops
|
||||
.iter()
|
||||
.map(
|
||||
|&PrefixSumOp {
|
||||
lhs_index,
|
||||
rhs_and_dest_index,
|
||||
..
|
||||
}| { rhs_and_dest_index.get() - lhs_index },
|
||||
)
|
||||
.max()
|
||||
.expect("ops is known to be non-empty");
|
||||
cells.extend((0..max_distance).map(|_| blank_row()));
|
||||
for op in ops {
|
||||
let mut y = cells.len() - 1;
|
||||
assert!(
|
||||
op.rhs_and_dest_index.get() < item_count,
|
||||
"invalid PrefixSumOp! rhs_and_dest_index must be \
|
||||
less than item_count ({item_count}): {op:?}",
|
||||
);
|
||||
let mut x = op.rhs_and_dest_index.get();
|
||||
cells[y][x].plus = true;
|
||||
x -= 1;
|
||||
y -= 1;
|
||||
while op.lhs_index < x {
|
||||
cells[y][x].slant = true;
|
||||
x -= 1;
|
||||
y -= 1;
|
||||
}
|
||||
cells[y][x].tee = true;
|
||||
}
|
||||
}
|
||||
let mut retval = String::new();
|
||||
let mut row_text = vec![String::new(); 2 * self.padding + 1];
|
||||
for cells_row in cells {
|
||||
for cell in cells_row {
|
||||
// top padding
|
||||
for y in 0..self.padding {
|
||||
// top left padding
|
||||
for x in 0..self.padding {
|
||||
row_text[y] += if x == y && (cell.plus || cell.slant) {
|
||||
&self.slant
|
||||
} else {
|
||||
&self.space
|
||||
};
|
||||
}
|
||||
// top vertical bar
|
||||
row_text[y] += &self.vertical_bar;
|
||||
// top right padding
|
||||
for _ in 0..self.padding {
|
||||
row_text[y] += &self.space;
|
||||
}
|
||||
}
|
||||
// center left padding
|
||||
for _ in 0..self.padding {
|
||||
row_text[self.padding] += &self.space;
|
||||
}
|
||||
// center
|
||||
row_text[self.padding] += if cell.plus {
|
||||
&self.plus
|
||||
} else if cell.tee {
|
||||
&self.connect
|
||||
} else if cell.slant {
|
||||
&self.no_connect
|
||||
} else {
|
||||
&self.vertical_bar
|
||||
};
|
||||
// center right padding
|
||||
for _ in 0..self.padding {
|
||||
row_text[self.padding] += &self.space;
|
||||
}
|
||||
let bottom_padding_start = self.padding + 1;
|
||||
let bottom_padding_last = self.padding * 2;
|
||||
// bottom padding
|
||||
for y in bottom_padding_start..=bottom_padding_last {
|
||||
// bottom left padding
|
||||
for _ in 0..self.padding {
|
||||
row_text[y] += &self.space;
|
||||
}
|
||||
// bottom vertical bar
|
||||
row_text[y] += &self.vertical_bar;
|
||||
// bottom right padding
|
||||
for x in bottom_padding_start..=bottom_padding_last {
|
||||
row_text[y] += if x == y && (cell.tee || cell.slant) {
|
||||
&self.slant
|
||||
} else {
|
||||
&self.space
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
for line in &mut row_text {
|
||||
retval += line.trim_end();
|
||||
retval += "\n";
|
||||
line.clear();
|
||||
}
|
||||
}
|
||||
retval
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DiagramConfig {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl PrefixSumOp {
|
||||
pub fn diagram(ops: impl IntoIterator<Item = Self>, item_count: usize) -> String {
|
||||
Self::diagram_with_config(ops, item_count, DiagramConfig::new())
|
||||
}
|
||||
pub fn diagram_with_config(
|
||||
ops: impl IntoIterator<Item = Self>,
|
||||
item_count: usize,
|
||||
config: DiagramConfig,
|
||||
) -> String {
|
||||
config.draw(ops, item_count)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum PrefixSumAlgorithm {
|
||||
/// Uses the algorithm from:
|
||||
/// https://en.wikipedia.org/wiki/Prefix_sum#Algorithm_1:_Shorter_span,_more_parallel
|
||||
LowLatency,
|
||||
/// Uses the algorithm from:
|
||||
/// https://en.wikipedia.org/wiki/Prefix_sum#Algorithm_2:_Work-efficient
|
||||
WorkEfficient,
|
||||
}
|
||||
|
||||
impl PrefixSumAlgorithm {
|
||||
fn ops_impl(self, item_count: usize) -> Vec<PrefixSumOp> {
|
||||
let mut retval = Vec::new();
|
||||
let mut distance = 1;
|
||||
let mut row = 0;
|
||||
while distance < item_count {
|
||||
let double_distance = distance
|
||||
.checked_mul(2)
|
||||
.expect("prefix-sum item_count is too big");
|
||||
let (start, step) = match self {
|
||||
Self::LowLatency => (distance, 1),
|
||||
Self::WorkEfficient => (double_distance - 1, double_distance),
|
||||
};
|
||||
for rhs_and_dest_index in (start..item_count).step_by(step).rev() {
|
||||
let Some(rhs_and_dest_index) = NonZeroUsize::new(rhs_and_dest_index) else {
|
||||
unreachable!();
|
||||
};
|
||||
let lhs_index = rhs_and_dest_index.get() - distance;
|
||||
retval.push(PrefixSumOp {
|
||||
lhs_index,
|
||||
rhs_and_dest_index,
|
||||
row,
|
||||
});
|
||||
}
|
||||
distance = double_distance;
|
||||
row += 1;
|
||||
}
|
||||
match self {
|
||||
Self::LowLatency => {}
|
||||
Self::WorkEfficient => {
|
||||
distance /= 2;
|
||||
while distance >= 1 {
|
||||
let start = distance
|
||||
.checked_mul(3)
|
||||
.expect("prefix-sum item_count is too big")
|
||||
- 1;
|
||||
for rhs_and_dest_index in (start..item_count).step_by(distance * 2).rev() {
|
||||
let Some(rhs_and_dest_index) = NonZeroUsize::new(rhs_and_dest_index) else {
|
||||
unreachable!();
|
||||
};
|
||||
let lhs_index = rhs_and_dest_index.get() - distance;
|
||||
retval.push(PrefixSumOp {
|
||||
lhs_index,
|
||||
rhs_and_dest_index,
|
||||
row,
|
||||
});
|
||||
}
|
||||
row += 1;
|
||||
distance /= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
retval
|
||||
}
|
||||
pub fn ops(self, item_count: usize) -> Interned<[PrefixSumOp]> {
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
struct MyMemoize(PrefixSumAlgorithm);
|
||||
impl Memoize for MyMemoize {
|
||||
type Input = usize;
|
||||
type InputOwned = usize;
|
||||
type Output = Interned<[PrefixSumOp]>;
|
||||
|
||||
fn inner(self, item_count: &Self::Input) -> Self::Output {
|
||||
Intern::intern_owned(self.0.ops_impl(*item_count))
|
||||
}
|
||||
}
|
||||
MyMemoize(self).get_owned(item_count)
|
||||
}
|
||||
pub fn run<T>(self, items: impl IntoIterator<Item = T>, f: impl FnMut(&T, &T) -> T) -> Vec<T> {
|
||||
let mut items = Vec::from_iter(items);
|
||||
self.run_on_slice(&mut items, f);
|
||||
items
|
||||
}
|
||||
pub fn run_on_slice<T>(self, items: &mut [T], mut f: impl FnMut(&T, &T) -> T) -> &mut [T] {
|
||||
self.ops(items.len()).into_iter().for_each(
|
||||
|PrefixSumOp {
|
||||
lhs_index,
|
||||
rhs_and_dest_index,
|
||||
row: _,
|
||||
}| {
|
||||
items[rhs_and_dest_index.get()] =
|
||||
f(&items[lhs_index], &items[rhs_and_dest_index.get()]);
|
||||
},
|
||||
);
|
||||
items
|
||||
}
|
||||
pub fn filtered_ops(
|
||||
self,
|
||||
item_live_out_flags: impl IntoIterator<Item = bool>,
|
||||
) -> Vec<PrefixSumOp> {
|
||||
let mut item_live_out_flags = Vec::from_iter(item_live_out_flags);
|
||||
let prefix_sum_ops = self.ops(item_live_out_flags.len());
|
||||
let mut ops_live_flags = vec![false; prefix_sum_ops.len()];
|
||||
for (
|
||||
op_index,
|
||||
&PrefixSumOp {
|
||||
lhs_index,
|
||||
rhs_and_dest_index,
|
||||
row: _,
|
||||
},
|
||||
) in prefix_sum_ops.iter().enumerate().rev()
|
||||
{
|
||||
let live = item_live_out_flags[rhs_and_dest_index.get()];
|
||||
item_live_out_flags[lhs_index] |= live;
|
||||
ops_live_flags[op_index] = live;
|
||||
}
|
||||
prefix_sum_ops
|
||||
.into_iter()
|
||||
.zip(ops_live_flags)
|
||||
.filter_map(|(op, live)| live.then_some(op))
|
||||
.collect()
|
||||
}
|
||||
pub fn reduce_ops(self, item_count: usize) -> Interned<[PrefixSumOp]> {
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
struct MyMemoize(PrefixSumAlgorithm);
|
||||
impl Memoize for MyMemoize {
|
||||
type Input = usize;
|
||||
type InputOwned = usize;
|
||||
type Output = Interned<[PrefixSumOp]>;
|
||||
|
||||
fn inner(self, item_count: &Self::Input) -> Self::Output {
|
||||
let mut item_live_out_flags = vec![false; *item_count];
|
||||
let Some(last_item_live_out_flag) = item_live_out_flags.last_mut() else {
|
||||
return Interned::default();
|
||||
};
|
||||
*last_item_live_out_flag = true;
|
||||
Intern::intern_owned(self.0.filtered_ops(item_live_out_flags))
|
||||
}
|
||||
}
|
||||
MyMemoize(self).get_owned(item_count)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reduce_ops(item_count: usize) -> Interned<[PrefixSumOp]> {
|
||||
PrefixSumAlgorithm::LowLatency.reduce_ops(item_count)
|
||||
}
|
||||
|
||||
pub fn reduce<T>(items: impl IntoIterator<Item = T>, mut f: impl FnMut(T, T) -> T) -> Option<T> {
|
||||
let mut items: Vec<_> = items.into_iter().map(Some).collect();
|
||||
for op in reduce_ops(items.len()) {
|
||||
let (Some(lhs), Some(rhs)) = (
|
||||
items[op.lhs_index].take(),
|
||||
items[op.rhs_and_dest_index.get()].take(),
|
||||
) else {
|
||||
unreachable!();
|
||||
};
|
||||
items[op.rhs_and_dest_index.get()] = Some(f(lhs, rhs));
|
||||
}
|
||||
items.last_mut().and_then(Option::take)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn input_strings() -> [String; 9] {
|
||||
std::array::from_fn(|i| String::from_utf8(vec![b'a' + i as u8]).unwrap())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prefix_sum_strings() {
|
||||
let input = input_strings();
|
||||
let expected: Vec<String> = input
|
||||
.iter()
|
||||
.scan(String::new(), |l, r| {
|
||||
*l += r;
|
||||
Some(l.clone())
|
||||
})
|
||||
.collect();
|
||||
println!("expected: {expected:?}");
|
||||
assert_eq!(
|
||||
*PrefixSumAlgorithm::WorkEfficient
|
||||
.run_on_slice(&mut input.clone(), |l, r| l.to_string() + r),
|
||||
*expected
|
||||
);
|
||||
assert_eq!(
|
||||
*PrefixSumAlgorithm::LowLatency
|
||||
.run_on_slice(&mut input.clone(), |l, r| l.to_string() + r),
|
||||
*expected
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reduce_string() {
|
||||
let input = input_strings();
|
||||
let expected = input.clone().into_iter().reduce(|l, r| l + &r);
|
||||
assert_eq!(reduce(input, |l, r| l + &r), expected);
|
||||
}
|
||||
|
||||
fn op(lhs_index: usize, rhs_and_dest_index: usize, row: u32) -> PrefixSumOp {
|
||||
PrefixSumOp {
|
||||
lhs_index,
|
||||
rhs_and_dest_index: NonZeroUsize::new(rhs_and_dest_index).expect("should be non-zero"),
|
||||
row,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reduce_ops_9() {
|
||||
let expected = vec![
|
||||
op(7, 8, 0),
|
||||
op(5, 6, 0),
|
||||
op(3, 4, 0),
|
||||
op(1, 2, 0),
|
||||
op(6, 8, 1),
|
||||
op(2, 4, 1),
|
||||
op(4, 8, 2),
|
||||
op(0, 8, 3),
|
||||
];
|
||||
println!("expected: {expected:#?}");
|
||||
let ops = reduce_ops(9);
|
||||
println!("ops: {ops:#?}");
|
||||
assert_eq!(*ops, *expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reduce_ops_8() {
|
||||
let expected = vec![
|
||||
op(6, 7, 0),
|
||||
op(4, 5, 0),
|
||||
op(2, 3, 0),
|
||||
op(0, 1, 0),
|
||||
op(5, 7, 1),
|
||||
op(1, 3, 1),
|
||||
op(3, 7, 2),
|
||||
];
|
||||
println!("expected: {expected:#?}");
|
||||
let ops = reduce_ops(8);
|
||||
println!("ops: {ops:#?}");
|
||||
assert_eq!(*ops, *expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_count_ones() {
|
||||
for width in 0..=10u32 {
|
||||
for v in 0..1u32 << width {
|
||||
let expected = v.count_ones();
|
||||
assert_eq!(
|
||||
reduce((0..width).map(|i| (v >> i) & 1), |l, r| l + r).unwrap_or(0),
|
||||
expected,
|
||||
"v={v:#X}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn test_diagram(ops: impl IntoIterator<Item = PrefixSumOp>, item_count: usize, expected: &str) {
|
||||
let text = PrefixSumOp::diagram_with_config(
|
||||
ops,
|
||||
item_count,
|
||||
DiagramConfig {
|
||||
plus: Cow::Borrowed("@"),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
println!("text:\n{text}\n");
|
||||
assert_eq!(text, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_work_efficient_diagram_16() {
|
||||
let item_count = 16;
|
||||
test_diagram(
|
||||
PrefixSumAlgorithm::WorkEfficient.ops(item_count),
|
||||
item_count,
|
||||
&r"
|
||||
| | | | | | | | | | | | | | | |
|
||||
● | ● | ● | ● | ● | ● | ● | ● |
|
||||
|\ | |\ | |\ | |\ | |\ | |\ | |\ | |\ |
|
||||
| \| | \| | \| | \| | \| | \| | \| | \|
|
||||
| @ | @ | @ | @ | @ | @ | @ | @
|
||||
| |\ | | | |\ | | | |\ | | | |\ | |
|
||||
| | \| | | | \| | | | \| | | | \| |
|
||||
| | X | | | X | | | X | | | X |
|
||||
| | |\ | | | |\ | | | |\ | | | |\ |
|
||||
| | | \| | | | \| | | | \| | | | \|
|
||||
| | | @ | | | @ | | | @ | | | @
|
||||
| | | |\ | | | | | | | |\ | | | |
|
||||
| | | | \| | | | | | | | \| | | |
|
||||
| | | | X | | | | | | | X | | |
|
||||
| | | | |\ | | | | | | | |\ | | |
|
||||
| | | | | \| | | | | | | | \| | |
|
||||
| | | | | X | | | | | | | X | |
|
||||
| | | | | |\ | | | | | | | |\ | |
|
||||
| | | | | | \| | | | | | | | \| |
|
||||
| | | | | | X | | | | | | | X |
|
||||
| | | | | | |\ | | | | | | | |\ |
|
||||
| | | | | | | \| | | | | | | | \|
|
||||
| | | | | | | @ | | | | | | | @
|
||||
| | | | | | | |\ | | | | | | | |
|
||||
| | | | | | | | \| | | | | | | |
|
||||
| | | | | | | | X | | | | | | |
|
||||
| | | | | | | | |\ | | | | | | |
|
||||
| | | | | | | | | \| | | | | | |
|
||||
| | | | | | | | | X | | | | | |
|
||||
| | | | | | | | | |\ | | | | | |
|
||||
| | | | | | | | | | \| | | | | |
|
||||
| | | | | | | | | | X | | | | |
|
||||
| | | | | | | | | | |\ | | | | |
|
||||
| | | | | | | | | | | \| | | | |
|
||||
| | | | | | | | | | | X | | | |
|
||||
| | | | | | | | | | | |\ | | | |
|
||||
| | | | | | | | | | | | \| | | |
|
||||
| | | | | | | | | | | | X | | |
|
||||
| | | | | | | | | | | | |\ | | |
|
||||
| | | | | | | | | | | | | \| | |
|
||||
| | | | | | | | | | | | | X | |
|
||||
| | | | | | | | | | | | | |\ | |
|
||||
| | | | | | | | | | | | | | \| |
|
||||
| | | | | | | | | | | | | | X |
|
||||
| | | | | | | | | | | | | | |\ |
|
||||
| | | | | | | | | | | | | | | \|
|
||||
| | | | | | | ● | | | | | | | @
|
||||
| | | | | | | |\ | | | | | | | |
|
||||
| | | | | | | | \| | | | | | | |
|
||||
| | | | | | | | X | | | | | | |
|
||||
| | | | | | | | |\ | | | | | | |
|
||||
| | | | | | | | | \| | | | | | |
|
||||
| | | | | | | | | X | | | | | |
|
||||
| | | | | | | | | |\ | | | | | |
|
||||
| | | | | | | | | | \| | | | | |
|
||||
| | | | | | | | | | X | | | | |
|
||||
| | | | | | | | | | |\ | | | | |
|
||||
| | | | | | | | | | | \| | | | |
|
||||
| | | ● | | | ● | | | @ | | | |
|
||||
| | | |\ | | | |\ | | | |\ | | | |
|
||||
| | | | \| | | | \| | | | \| | | |
|
||||
| | | | X | | | X | | | X | | |
|
||||
| | | | |\ | | | |\ | | | |\ | | |
|
||||
| | | | | \| | | | \| | | | \| | |
|
||||
| ● | ● | @ | ● | @ | ● | @ | |
|
||||
| |\ | |\ | |\ | |\ | |\ | |\ | |\ | |
|
||||
| | \| | \| | \| | \| | \| | \| | \| |
|
||||
| | @ | @ | @ | @ | @ | @ | @ |
|
||||
| | | | | | | | | | | | | | | |
|
||||
"[1..], // trim newline at start
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_low_latency_diagram_16() {
|
||||
let item_count = 16;
|
||||
test_diagram(
|
||||
PrefixSumAlgorithm::LowLatency.ops(item_count),
|
||||
item_count,
|
||||
&r"
|
||||
| | | | | | | | | | | | | | | |
|
||||
● ● ● ● ● ● ● ● ● ● ● ● ● ● ● |
|
||||
|\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |
|
||||
| \| \| \| \| \| \| \| \| \| \| \| \| \| \| \|
|
||||
● @ @ @ @ @ @ @ @ @ @ @ @ @ @ @
|
||||
|\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ | |
|
||||
| \| \| \| \| \| \| \| \| \| \| \| \| \| \| |
|
||||
| X X X X X X X X X X X X X X |
|
||||
| |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |
|
||||
| | \| \| \| \| \| \| \| \| \| \| \| \| \| \|
|
||||
● ● @ @ @ @ @ @ @ @ @ @ @ @ @ @
|
||||
|\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ | | | |
|
||||
| \| \| \| \| \| \| \| \| \| \| \| \| | | |
|
||||
| X X X X X X X X X X X X | | |
|
||||
| |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ | | |
|
||||
| | \| \| \| \| \| \| \| \| \| \| \| \| | |
|
||||
| | X X X X X X X X X X X X | |
|
||||
| | |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ | |
|
||||
| | | \| \| \| \| \| \| \| \| \| \| \| \| |
|
||||
| | | X X X X X X X X X X X X |
|
||||
| | | |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |
|
||||
| | | | \| \| \| \| \| \| \| \| \| \| \| \|
|
||||
● ● ● ● @ @ @ @ @ @ @ @ @ @ @ @
|
||||
|\ |\ |\ |\ |\ |\ |\ |\ | | | | | | | |
|
||||
| \| \| \| \| \| \| \| \| | | | | | | |
|
||||
| X X X X X X X X | | | | | | |
|
||||
| |\ |\ |\ |\ |\ |\ |\ |\ | | | | | | |
|
||||
| | \| \| \| \| \| \| \| \| | | | | | |
|
||||
| | X X X X X X X X | | | | | |
|
||||
| | |\ |\ |\ |\ |\ |\ |\ |\ | | | | | |
|
||||
| | | \| \| \| \| \| \| \| \| | | | | |
|
||||
| | | X X X X X X X X | | | | |
|
||||
| | | |\ |\ |\ |\ |\ |\ |\ |\ | | | | |
|
||||
| | | | \| \| \| \| \| \| \| \| | | | |
|
||||
| | | | X X X X X X X X | | | |
|
||||
| | | | |\ |\ |\ |\ |\ |\ |\ |\ | | | |
|
||||
| | | | | \| \| \| \| \| \| \| \| | | |
|
||||
| | | | | X X X X X X X X | | |
|
||||
| | | | | |\ |\ |\ |\ |\ |\ |\ |\ | | |
|
||||
| | | | | | \| \| \| \| \| \| \| \| | |
|
||||
| | | | | | X X X X X X X X | |
|
||||
| | | | | | |\ |\ |\ |\ |\ |\ |\ |\ | |
|
||||
| | | | | | | \| \| \| \| \| \| \| \| |
|
||||
| | | | | | | X X X X X X X X |
|
||||
| | | | | | | |\ |\ |\ |\ |\ |\ |\ |\ |
|
||||
| | | | | | | | \| \| \| \| \| \| \| \|
|
||||
| | | | | | | | @ @ @ @ @ @ @ @
|
||||
| | | | | | | | | | | | | | | |
|
||||
"[1..], // trim newline at start
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_work_efficient_diagram_9() {
|
||||
let item_count = 9;
|
||||
test_diagram(
|
||||
PrefixSumAlgorithm::WorkEfficient.ops(item_count),
|
||||
item_count,
|
||||
&r"
|
||||
| | | | | | | | |
|
||||
● | ● | ● | ● | |
|
||||
|\ | |\ | |\ | |\ | |
|
||||
| \| | \| | \| | \| |
|
||||
| @ | @ | @ | @ |
|
||||
| |\ | | | |\ | | |
|
||||
| | \| | | | \| | |
|
||||
| | X | | | X | |
|
||||
| | |\ | | | |\ | |
|
||||
| | | \| | | | \| |
|
||||
| | | @ | | | @ |
|
||||
| | | |\ | | | | |
|
||||
| | | | \| | | | |
|
||||
| | | | X | | | |
|
||||
| | | | |\ | | | |
|
||||
| | | | | \| | | |
|
||||
| | | | | X | | |
|
||||
| | | | | |\ | | |
|
||||
| | | | | | \| | |
|
||||
| | | | | | X | |
|
||||
| | | | | | |\ | |
|
||||
| | | | | | | \| |
|
||||
| | | ● | | | @ |
|
||||
| | | |\ | | | | |
|
||||
| | | | \| | | | |
|
||||
| | | | X | | | |
|
||||
| | | | |\ | | | |
|
||||
| | | | | \| | | |
|
||||
| ● | ● | @ | ● |
|
||||
| |\ | |\ | |\ | |\ |
|
||||
| | \| | \| | \| | \|
|
||||
| | @ | @ | @ | @
|
||||
| | | | | | | | |
|
||||
"[1..], // trim newline at start
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_low_latency_diagram_9() {
|
||||
let item_count = 9;
|
||||
test_diagram(
|
||||
PrefixSumAlgorithm::LowLatency.ops(item_count),
|
||||
item_count,
|
||||
&r"
|
||||
| | | | | | | | |
|
||||
● ● ● ● ● ● ● ● |
|
||||
|\ |\ |\ |\ |\ |\ |\ |\ |
|
||||
| \| \| \| \| \| \| \| \|
|
||||
● @ @ @ @ @ @ @ @
|
||||
|\ |\ |\ |\ |\ |\ |\ | |
|
||||
| \| \| \| \| \| \| \| |
|
||||
| X X X X X X X |
|
||||
| |\ |\ |\ |\ |\ |\ |\ |
|
||||
| | \| \| \| \| \| \| \|
|
||||
● ● @ @ @ @ @ @ @
|
||||
|\ |\ |\ |\ |\ | | | |
|
||||
| \| \| \| \| \| | | |
|
||||
| X X X X X | | |
|
||||
| |\ |\ |\ |\ |\ | | |
|
||||
| | \| \| \| \| \| | |
|
||||
| | X X X X X | |
|
||||
| | |\ |\ |\ |\ |\ | |
|
||||
| | | \| \| \| \| \| |
|
||||
| | | X X X X X |
|
||||
| | | |\ |\ |\ |\ |\ |
|
||||
| | | | \| \| \| \| \|
|
||||
● | | | @ @ @ @ @
|
||||
|\ | | | | | | | |
|
||||
| \| | | | | | | |
|
||||
| X | | | | | | |
|
||||
| |\ | | | | | | |
|
||||
| | \| | | | | | |
|
||||
| | X | | | | | |
|
||||
| | |\ | | | | | |
|
||||
| | | \| | | | | |
|
||||
| | | X | | | | |
|
||||
| | | |\ | | | | |
|
||||
| | | | \| | | | |
|
||||
| | | | X | | | |
|
||||
| | | | |\ | | | |
|
||||
| | | | | \| | | |
|
||||
| | | | | X | | |
|
||||
| | | | | |\ | | |
|
||||
| | | | | | \| | |
|
||||
| | | | | | X | |
|
||||
| | | | | | |\ | |
|
||||
| | | | | | | \| |
|
||||
| | | | | | | X |
|
||||
| | | | | | | |\ |
|
||||
| | | | | | | | \|
|
||||
| | | | | | | | @
|
||||
| | | | | | | | |
|
||||
"[1..], // trim newline at start
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reduce_diagram_16() {
|
||||
let item_count = 16;
|
||||
test_diagram(
|
||||
reduce_ops(item_count),
|
||||
item_count,
|
||||
&r"
|
||||
| | | | | | | | | | | | | | | |
|
||||
● | ● | ● | ● | ● | ● | ● | ● |
|
||||
|\ | |\ | |\ | |\ | |\ | |\ | |\ | |\ |
|
||||
| \| | \| | \| | \| | \| | \| | \| | \|
|
||||
| @ | @ | @ | @ | @ | @ | @ | @
|
||||
| |\ | | | |\ | | | |\ | | | |\ | |
|
||||
| | \| | | | \| | | | \| | | | \| |
|
||||
| | X | | | X | | | X | | | X |
|
||||
| | |\ | | | |\ | | | |\ | | | |\ |
|
||||
| | | \| | | | \| | | | \| | | | \|
|
||||
| | | @ | | | @ | | | @ | | | @
|
||||
| | | |\ | | | | | | | |\ | | | |
|
||||
| | | | \| | | | | | | | \| | | |
|
||||
| | | | X | | | | | | | X | | |
|
||||
| | | | |\ | | | | | | | |\ | | |
|
||||
| | | | | \| | | | | | | | \| | |
|
||||
| | | | | X | | | | | | | X | |
|
||||
| | | | | |\ | | | | | | | |\ | |
|
||||
| | | | | | \| | | | | | | | \| |
|
||||
| | | | | | X | | | | | | | X |
|
||||
| | | | | | |\ | | | | | | | |\ |
|
||||
| | | | | | | \| | | | | | | | \|
|
||||
| | | | | | | @ | | | | | | | @
|
||||
| | | | | | | |\ | | | | | | | |
|
||||
| | | | | | | | \| | | | | | | |
|
||||
| | | | | | | | X | | | | | | |
|
||||
| | | | | | | | |\ | | | | | | |
|
||||
| | | | | | | | | \| | | | | | |
|
||||
| | | | | | | | | X | | | | | |
|
||||
| | | | | | | | | |\ | | | | | |
|
||||
| | | | | | | | | | \| | | | | |
|
||||
| | | | | | | | | | X | | | | |
|
||||
| | | | | | | | | | |\ | | | | |
|
||||
| | | | | | | | | | | \| | | | |
|
||||
| | | | | | | | | | | X | | | |
|
||||
| | | | | | | | | | | |\ | | | |
|
||||
| | | | | | | | | | | | \| | | |
|
||||
| | | | | | | | | | | | X | | |
|
||||
| | | | | | | | | | | | |\ | | |
|
||||
| | | | | | | | | | | | | \| | |
|
||||
| | | | | | | | | | | | | X | |
|
||||
| | | | | | | | | | | | | |\ | |
|
||||
| | | | | | | | | | | | | | \| |
|
||||
| | | | | | | | | | | | | | X |
|
||||
| | | | | | | | | | | | | | |\ |
|
||||
| | | | | | | | | | | | | | | \|
|
||||
| | | | | | | | | | | | | | | @
|
||||
| | | | | | | | | | | | | | | |
|
||||
"[1..], // trim newline at start
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reduce_diagram_9() {
|
||||
let item_count = 9;
|
||||
test_diagram(
|
||||
reduce_ops(item_count),
|
||||
item_count,
|
||||
&r"
|
||||
| | | | | | | | |
|
||||
| ● | ● | ● | ● |
|
||||
| |\ | |\ | |\ | |\ |
|
||||
| | \| | \| | \| | \|
|
||||
| | @ | @ | @ | @
|
||||
| | |\ | | | |\ | |
|
||||
| | | \| | | | \| |
|
||||
| | | X | | | X |
|
||||
| | | |\ | | | |\ |
|
||||
| | | | \| | | | \|
|
||||
| | | | @ | | | @
|
||||
| | | | |\ | | | |
|
||||
| | | | | \| | | |
|
||||
| | | | | X | | |
|
||||
| | | | | |\ | | |
|
||||
| | | | | | \| | |
|
||||
| | | | | | X | |
|
||||
| | | | | | |\ | |
|
||||
| | | | | | | \| |
|
||||
| | | | | | | X |
|
||||
| | | | | | | |\ |
|
||||
| | | | | | | | \|
|
||||
● | | | | | | | @
|
||||
|\ | | | | | | | |
|
||||
| \| | | | | | | |
|
||||
| X | | | | | | |
|
||||
| |\ | | | | | | |
|
||||
| | \| | | | | | |
|
||||
| | X | | | | | |
|
||||
| | |\ | | | | | |
|
||||
| | | \| | | | | |
|
||||
| | | X | | | | |
|
||||
| | | |\ | | | | |
|
||||
| | | | \| | | | |
|
||||
| | | | X | | | |
|
||||
| | | | |\ | | | |
|
||||
| | | | | \| | | |
|
||||
| | | | | X | | |
|
||||
| | | | | |\ | | |
|
||||
| | | | | | \| | |
|
||||
| | | | | | X | |
|
||||
| | | | | | |\ | |
|
||||
| | | | | | | \| |
|
||||
| | | | | | | X |
|
||||
| | | | | | | |\ |
|
||||
| | | | | | | | \|
|
||||
| | | | | | | | @
|
||||
| | | | | | | | |
|
||||
"[1..], // trim newline at start
|
||||
);
|
||||
}
|
||||
}
|
|
@ -380,18 +380,18 @@ circuit check_written_inside_both_if_else:
|
|||
};
|
||||
}
|
||||
|
||||
#[hdl(outline_generated)]
|
||||
#[hdl(outline_generated, cmp_eq)]
|
||||
pub struct TestStruct<T> {
|
||||
pub a: T,
|
||||
pub b: UInt<8>,
|
||||
}
|
||||
|
||||
#[hdl(outline_generated)]
|
||||
#[hdl(outline_generated, cmp_eq)]
|
||||
pub struct TestStruct2 {
|
||||
pub v: UInt<8>,
|
||||
}
|
||||
|
||||
#[hdl(outline_generated)]
|
||||
#[hdl(outline_generated, cmp_eq)]
|
||||
pub struct TestStruct3 {}
|
||||
|
||||
#[hdl_module(outline_generated)]
|
||||
|
@ -4425,3 +4425,125 @@ circuit check_let_patterns:
|
|||
",
|
||||
};
|
||||
}
|
||||
|
||||
#[hdl_module(outline_generated)]
|
||||
pub fn check_struct_cmp_eq() {
|
||||
#[hdl]
|
||||
let tuple_lhs: (UInt<1>, SInt<1>, Bool) = m.input();
|
||||
#[hdl]
|
||||
let tuple_rhs: (UInt<1>, SInt<1>, Bool) = m.input();
|
||||
#[hdl]
|
||||
let tuple_cmp_eq: Bool = m.output();
|
||||
connect(tuple_cmp_eq, tuple_lhs.cmp_eq(tuple_rhs));
|
||||
#[hdl]
|
||||
let tuple_cmp_ne: Bool = m.output();
|
||||
connect(tuple_cmp_ne, tuple_lhs.cmp_ne(tuple_rhs));
|
||||
|
||||
#[hdl]
|
||||
let test_struct_lhs: TestStruct<SInt<8>> = m.input();
|
||||
#[hdl]
|
||||
let test_struct_rhs: TestStruct<SInt<8>> = m.input();
|
||||
#[hdl]
|
||||
let test_struct_cmp_eq: Bool = m.output();
|
||||
connect(test_struct_cmp_eq, test_struct_lhs.cmp_eq(test_struct_rhs));
|
||||
#[hdl]
|
||||
let test_struct_cmp_ne: Bool = m.output();
|
||||
connect(test_struct_cmp_ne, test_struct_lhs.cmp_ne(test_struct_rhs));
|
||||
|
||||
#[hdl]
|
||||
let test_struct_2_lhs: TestStruct2 = m.input();
|
||||
#[hdl]
|
||||
let test_struct_2_rhs: TestStruct2 = m.input();
|
||||
#[hdl]
|
||||
let test_struct_2_cmp_eq: Bool = m.output();
|
||||
connect(
|
||||
test_struct_2_cmp_eq,
|
||||
test_struct_2_lhs.cmp_eq(test_struct_2_rhs),
|
||||
);
|
||||
#[hdl]
|
||||
let test_struct_2_cmp_ne: Bool = m.output();
|
||||
connect(
|
||||
test_struct_2_cmp_ne,
|
||||
test_struct_2_lhs.cmp_ne(test_struct_2_rhs),
|
||||
);
|
||||
|
||||
#[hdl]
|
||||
let test_struct_3_lhs: TestStruct3 = m.input();
|
||||
#[hdl]
|
||||
let test_struct_3_rhs: TestStruct3 = m.input();
|
||||
#[hdl]
|
||||
let test_struct_3_cmp_eq: Bool = m.output();
|
||||
connect(
|
||||
test_struct_3_cmp_eq,
|
||||
test_struct_3_lhs.cmp_eq(test_struct_3_rhs),
|
||||
);
|
||||
#[hdl]
|
||||
let test_struct_3_cmp_ne: Bool = m.output();
|
||||
connect(
|
||||
test_struct_3_cmp_ne,
|
||||
test_struct_3_lhs.cmp_ne(test_struct_3_rhs),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_struct_cmp_eq() {
|
||||
let _n = SourceLocation::normalize_files_for_tests();
|
||||
let m = check_struct_cmp_eq();
|
||||
dbg!(m);
|
||||
#[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
|
||||
assert_export_firrtl! {
|
||||
m =>
|
||||
"/test/check_struct_cmp_eq.fir": r"FIRRTL version 3.2.0
|
||||
circuit check_struct_cmp_eq:
|
||||
type Ty0 = {`0`: UInt<1>, `1`: SInt<1>, `2`: UInt<1>}
|
||||
type Ty1 = {a: SInt<8>, b: UInt<8>}
|
||||
type Ty2 = {v: UInt<8>}
|
||||
type Ty3 = {}
|
||||
module check_struct_cmp_eq: @[module-XXXXXXXXXX.rs 1:1]
|
||||
input tuple_lhs: Ty0 @[module-XXXXXXXXXX.rs 2:1]
|
||||
input tuple_rhs: Ty0 @[module-XXXXXXXXXX.rs 3:1]
|
||||
output tuple_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 4:1]
|
||||
output tuple_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 6:1]
|
||||
input test_struct_lhs: Ty1 @[module-XXXXXXXXXX.rs 8:1]
|
||||
input test_struct_rhs: Ty1 @[module-XXXXXXXXXX.rs 9:1]
|
||||
output test_struct_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 10:1]
|
||||
output test_struct_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 12:1]
|
||||
input test_struct_2_lhs: Ty2 @[module-XXXXXXXXXX.rs 14:1]
|
||||
input test_struct_2_rhs: Ty2 @[module-XXXXXXXXXX.rs 15:1]
|
||||
output test_struct_2_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 16:1]
|
||||
output test_struct_2_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 18:1]
|
||||
input test_struct_3_lhs: Ty3 @[module-XXXXXXXXXX.rs 20:1]
|
||||
input test_struct_3_rhs: Ty3 @[module-XXXXXXXXXX.rs 21:1]
|
||||
output test_struct_3_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 22:1]
|
||||
output test_struct_3_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 24:1]
|
||||
wire _array_literal_expr: UInt<1>[3]
|
||||
connect _array_literal_expr[0], eq(tuple_lhs.`0`, tuple_rhs.`0`)
|
||||
connect _array_literal_expr[1], eq(tuple_lhs.`1`, tuple_rhs.`1`)
|
||||
connect _array_literal_expr[2], eq(tuple_lhs.`2`, tuple_rhs.`2`)
|
||||
wire _cast_array_to_bits_expr: UInt<1>[3]
|
||||
connect _cast_array_to_bits_expr[0], _array_literal_expr[0]
|
||||
connect _cast_array_to_bits_expr[1], _array_literal_expr[1]
|
||||
connect _cast_array_to_bits_expr[2], _array_literal_expr[2]
|
||||
wire _cast_to_bits_expr: UInt<3>
|
||||
connect _cast_to_bits_expr, cat(_cast_array_to_bits_expr[2], cat(_cast_array_to_bits_expr[1], _cast_array_to_bits_expr[0]))
|
||||
connect tuple_cmp_eq, andr(_cast_to_bits_expr) @[module-XXXXXXXXXX.rs 5:1]
|
||||
wire _array_literal_expr_1: UInt<1>[3]
|
||||
connect _array_literal_expr_1[0], neq(tuple_lhs.`0`, tuple_rhs.`0`)
|
||||
connect _array_literal_expr_1[1], neq(tuple_lhs.`1`, tuple_rhs.`1`)
|
||||
connect _array_literal_expr_1[2], neq(tuple_lhs.`2`, tuple_rhs.`2`)
|
||||
wire _cast_array_to_bits_expr_1: UInt<1>[3]
|
||||
connect _cast_array_to_bits_expr_1[0], _array_literal_expr_1[0]
|
||||
connect _cast_array_to_bits_expr_1[1], _array_literal_expr_1[1]
|
||||
connect _cast_array_to_bits_expr_1[2], _array_literal_expr_1[2]
|
||||
wire _cast_to_bits_expr_1: UInt<3>
|
||||
connect _cast_to_bits_expr_1, cat(_cast_array_to_bits_expr_1[2], cat(_cast_array_to_bits_expr_1[1], _cast_array_to_bits_expr_1[0]))
|
||||
connect tuple_cmp_ne, orr(_cast_to_bits_expr_1) @[module-XXXXXXXXXX.rs 7:1]
|
||||
connect test_struct_cmp_eq, and(eq(test_struct_lhs.a, test_struct_rhs.a), eq(test_struct_lhs.b, test_struct_rhs.b)) @[module-XXXXXXXXXX.rs 11:1]
|
||||
connect test_struct_cmp_ne, or(neq(test_struct_lhs.a, test_struct_rhs.a), neq(test_struct_lhs.b, test_struct_rhs.b)) @[module-XXXXXXXXXX.rs 13:1]
|
||||
connect test_struct_2_cmp_eq, eq(test_struct_2_lhs.v, test_struct_2_rhs.v) @[module-XXXXXXXXXX.rs 17:1]
|
||||
connect test_struct_2_cmp_ne, neq(test_struct_2_lhs.v, test_struct_2_rhs.v) @[module-XXXXXXXXXX.rs 19:1]
|
||||
connect test_struct_3_cmp_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 23:1]
|
||||
connect test_struct_3_cmp_ne, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 25:1]
|
||||
",
|
||||
};
|
||||
}
|
||||
|
|
|
@ -49,7 +49,8 @@
|
|||
"AsyncReset": "Visible",
|
||||
"SyncReset": "Visible",
|
||||
"Reset": "Visible",
|
||||
"Clock": "Visible"
|
||||
"Clock": "Visible",
|
||||
"PhantomConst": "Visible"
|
||||
}
|
||||
},
|
||||
"Bundle": {
|
||||
|
@ -159,7 +160,8 @@
|
|||
"data": {
|
||||
"$kind": "Struct",
|
||||
"verilog_name": "Visible",
|
||||
"parameters": "Visible"
|
||||
"parameters": "Visible",
|
||||
"simulation": "Visible"
|
||||
}
|
||||
},
|
||||
"ExternModuleParameter": {
|
||||
|
@ -1262,6 +1264,18 @@
|
|||
"ArrayElement": "Visible",
|
||||
"DynArrayElement": "Visible"
|
||||
}
|
||||
},
|
||||
"PhantomConst": {
|
||||
"data": {
|
||||
"$kind": "Opaque"
|
||||
},
|
||||
"generics": "<T: ?Sized + crate::phantom_const::PhantomConstValue>"
|
||||
},
|
||||
"ExternModuleSimulation": {
|
||||
"data": {
|
||||
"$kind": "Opaque"
|
||||
},
|
||||
"generics": "<T: BundleType>"
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue