1
0
Fork 0
fayalite/crates/fayalite/src/module/transform/deduce_resets.rs

2451 lines
83 KiB
Rust

// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
annotations::{Annotation, TargetedAnnotation},
bundle::{BundleField, BundleType},
enum_::{EnumType, EnumVariant},
expr::{
ExprEnum, ValueType,
ops::{self, ArrayLiteral},
target::{
Target, TargetBase, TargetChild, TargetPathArrayElement, TargetPathBundleField,
TargetPathDynArrayElement, TargetPathElement, TargetPathToTraceAsString,
TargetPathTraceAsStringInner,
},
},
formal::FormalKind,
int::{SIntValue, UIntValue},
intern::{Intern, Interned, Memoize},
memory::{DynPortType, MemPort},
module::{
AnnotatedModuleIO, Block, ExprInInstantiatedModule, ExternModuleBody,
ExternModuleParameter, InstantiatedModule, ModuleBody, ModuleIO, NameId, NormalModuleBody,
Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtIf, StmtInstance, StmtMatch, StmtReg,
StmtWire,
},
prelude::*,
reset::{ResetType, ResetTypeDispatch},
sim::ExternModuleSimulation,
ty::TraceAsString,
util::{HashMap, HashSet},
};
use hashbrown::hash_map::Entry;
use num_bigint::BigInt;
use petgraph::unionfind::UnionFind;
use std::{collections::BTreeMap, fmt, marker::PhantomData};
#[derive(Debug)]
pub enum DeduceResetsError {
ResetIsNotDrivenByAsyncOrSync { source_location: SourceLocation },
ResetIsDrivenByBothAsyncAndSync { source_location: SourceLocation },
}
impl fmt::Display for DeduceResetsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DeduceResetsError::ResetIsNotDrivenByAsyncOrSync { source_location } => {
write!(
f,
"deduce_reset failed: Reset signal is not driven by any AsyncReset or SyncReset signals: {source_location}"
)
}
DeduceResetsError::ResetIsDrivenByBothAsyncAndSync { source_location } => {
write!(
f,
"deduce_reset failed: Reset signal is driven by both AsyncReset and SyncReset signals: {source_location}"
)
}
}
}
}
impl std::error::Error for DeduceResetsError {}
impl From<DeduceResetsError> for std::io::Error {
fn from(value: DeduceResetsError) -> Self {
std::io::Error::new(std::io::ErrorKind::Other, value)
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
enum AnyReg {
Reg(Reg<CanonicalType, Reset>),
RegSync(Reg<CanonicalType, SyncReset>),
RegAsync(Reg<CanonicalType, AsyncReset>),
}
macro_rules! match_any_reg {
(
$match_expr:expr, $fn:expr
) => {
match $match_expr {
AnyReg::Reg(reg) => $fn(reg),
AnyReg::RegSync(reg) => $fn(reg),
AnyReg::RegAsync(reg) => $fn(reg),
}
};
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
enum ResetsLayout {
NoResets,
Reset,
SyncReset,
AsyncReset,
Bundle {
fields: Interned<[ResetsLayout]>,
reset_count: usize,
},
Enum {
variants: Interned<[ResetsLayout]>,
reset_count: usize,
},
Array {
element: Interned<ResetsLayout>,
reset_count: usize,
},
Transparent {
inner: Interned<ResetsLayout>,
reset_count: usize,
},
}
impl ResetsLayout {
fn reset_count(self) -> usize {
match self {
ResetsLayout::NoResets => 0,
ResetsLayout::Reset | ResetsLayout::SyncReset | ResetsLayout::AsyncReset => 1,
ResetsLayout::Bundle { reset_count, .. }
| ResetsLayout::Enum { reset_count, .. }
| ResetsLayout::Array { reset_count, .. }
| ResetsLayout::Transparent { reset_count, .. } => reset_count,
}
}
fn new(ty: CanonicalType) -> Self {
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
struct MyMemoize;
impl Memoize for MyMemoize {
type Input = CanonicalType;
type InputOwned = CanonicalType;
type Output = ResetsLayout;
fn inner(self, ty: &Self::Input) -> Self::Output {
match *ty {
CanonicalType::UInt(_) => ResetsLayout::NoResets,
CanonicalType::SInt(_) => ResetsLayout::NoResets,
CanonicalType::Bool(_) => ResetsLayout::NoResets,
CanonicalType::Array(ty) => {
let element = ResetsLayout::new(ty.element()).intern_sized();
ResetsLayout::Array {
element,
reset_count: element.reset_count(),
}
}
CanonicalType::Enum(ty) => {
let mut reset_count = 0;
let variants = Interned::from_iter(ty.variants().iter().map(|variant| {
let resets_layout =
variant.ty.map_or(ResetsLayout::NoResets, ResetsLayout::new);
reset_count += resets_layout.reset_count();
resets_layout
}));
ResetsLayout::Enum {
variants,
reset_count,
}
}
CanonicalType::Bundle(ty) => {
let mut reset_count = 0;
let fields = Interned::from_iter(ty.fields().iter().map(|field| {
let resets_layout = ResetsLayout::new(field.ty);
reset_count += resets_layout.reset_count();
resets_layout
}));
ResetsLayout::Bundle {
fields,
reset_count,
}
}
CanonicalType::AsyncReset(_) => ResetsLayout::AsyncReset,
CanonicalType::SyncReset(_) => ResetsLayout::SyncReset,
CanonicalType::Reset(_) => ResetsLayout::Reset,
CanonicalType::Clock(_) => ResetsLayout::NoResets,
CanonicalType::PhantomConst(_) => ResetsLayout::NoResets,
CanonicalType::DynSimOnly(_) => ResetsLayout::NoResets,
CanonicalType::TraceAsString(ty) => {
let inner = ResetsLayout::new(ty.inner_ty()).intern_sized();
ResetsLayout::Transparent {
inner,
reset_count: inner.reset_count(),
}
}
}
}
}
MyMemoize.get_owned(ty)
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
struct ResetNodeIndex(usize);
#[derive(Copy, Clone, Debug)]
struct ResetNode {
is_async: Option<bool>,
source_location: Option<SourceLocation>,
}
impl ResetNode {
fn union(
self,
other: Self,
fallback_error_source_location: SourceLocation,
) -> Result<Self, DeduceResetsError> {
match (self.is_async, other.is_async) {
(None, None) => Ok(Self {
is_async: None,
source_location: self.source_location.or(other.source_location),
}),
(None, is_async @ Some(_)) => Ok(Self {
is_async,
// prioritize `other`
source_location: other.source_location.or(self.source_location),
}),
(is_async @ Some(_), None) => Ok(Self {
is_async,
// prioritize `self`
source_location: self.source_location.or(other.source_location),
}),
(Some(self_is_async), Some(other_is_async)) => {
if self_is_async == other_is_async {
Ok(Self {
is_async: Some(self_is_async),
source_location: self.source_location.or(other.source_location),
})
} else {
Err(DeduceResetsError::ResetIsDrivenByBothAsyncAndSync {
source_location: self
.source_location
.or(other.source_location)
.unwrap_or(fallback_error_source_location),
})
}
}
}
}
}
#[derive(Debug, Default)]
struct ResetGraph {
union_find: UnionFind<usize>,
nodes: Vec<ResetNode>,
}
impl ResetGraph {
fn new_node(
&mut self,
is_async: Option<bool>,
source_location: Option<SourceLocation>,
) -> ResetNodeIndex {
let index = self.union_find.new_set();
assert_eq!(index, self.nodes.len());
self.nodes.push(ResetNode {
is_async,
source_location,
});
ResetNodeIndex(index)
}
fn union(
&mut self,
a: ResetNodeIndex,
b: ResetNodeIndex,
fallback_error_source_location: SourceLocation,
) -> Result<(), DeduceResetsError> {
let a = self.union_find.find_mut(a.0);
let b = self.union_find.find_mut(b.0);
if a != b {
self.union_find.union(a, b);
let merged = self.union_find.find_mut(a);
self.nodes[merged] =
self.nodes[a].union(self.nodes[b], fallback_error_source_location)?;
}
Ok(())
}
fn is_async(
&mut self,
node: ResetNodeIndex,
fallback_to_sync_reset: bool,
fallback_error_source_location: SourceLocation,
) -> Result<bool, DeduceResetsError> {
let ResetNode {
is_async,
source_location,
} = self.nodes[self.union_find.find_mut(node.0)];
if let Some(is_async) = is_async {
Ok(is_async)
} else if fallback_to_sync_reset {
Ok(false)
} else {
Err(DeduceResetsError::ResetIsNotDrivenByAsyncOrSync {
source_location: source_location.unwrap_or(fallback_error_source_location),
})
}
}
fn append_new_nodes_for_layout(
&mut self,
layout: ResetsLayout,
node_indexes: &mut Vec<ResetNodeIndex>,
source_location: Option<SourceLocation>,
) {
match layout {
ResetsLayout::NoResets => {}
ResetsLayout::Reset => node_indexes.push(self.new_node(None, source_location)),
ResetsLayout::SyncReset => {
node_indexes.push(self.new_node(Some(false), source_location))
}
ResetsLayout::AsyncReset => {
node_indexes.push(self.new_node(Some(true), source_location))
}
ResetsLayout::Bundle {
fields,
reset_count: _,
} => {
for field in fields {
self.append_new_nodes_for_layout(field, node_indexes, source_location);
}
}
ResetsLayout::Enum {
variants,
reset_count: _,
} => {
for variant in variants {
self.append_new_nodes_for_layout(variant, node_indexes, source_location);
}
}
ResetsLayout::Array {
element,
reset_count: _,
} => {
self.append_new_nodes_for_layout(*element, node_indexes, source_location);
}
ResetsLayout::Transparent {
inner,
reset_count: _,
} => {
self.append_new_nodes_for_layout(*inner, node_indexes, source_location);
}
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
struct Resets {
ty: CanonicalType,
layout: ResetsLayout,
node_indexes: Interned<[ResetNodeIndex]>,
}
impl Resets {
fn with_new_nodes(
reset_graph: &mut ResetGraph,
ty: CanonicalType,
source_location: Option<SourceLocation>,
) -> Self {
let layout = ResetsLayout::new(ty);
let mut node_indexes = Vec::with_capacity(layout.reset_count());
reset_graph.append_new_nodes_for_layout(layout, &mut node_indexes, source_location);
let node_indexes = Intern::intern_owned(node_indexes);
Self {
ty,
layout,
node_indexes,
}
}
fn array_elements(self) -> Self {
let array = <Array>::from_canonical(self.ty);
let ResetsLayout::Array {
element,
reset_count: _,
} = self.layout
else {
unreachable!();
};
Self {
ty: array.element(),
layout: *element,
node_indexes: self.node_indexes,
}
}
fn trace_as_string_inner(self) -> Self {
let trace_as_string = TraceAsString::from_canonical(self.ty);
let ResetsLayout::Transparent {
inner,
reset_count: _,
} = self.layout
else {
unreachable!();
};
Self {
ty: trace_as_string.inner_ty(),
layout: *inner,
node_indexes: self.node_indexes,
}
}
fn bundle_fields(self) -> impl Iterator<Item = Self> {
let bundle = Bundle::from_canonical(self.ty);
let ResetsLayout::Bundle {
fields,
reset_count: _,
} = self.layout
else {
unreachable!();
};
bundle.fields().into_iter().zip(fields).scan(
0,
move |start_index, (BundleField { ty, .. }, layout)| {
let end_index = *start_index + layout.reset_count();
let node_indexes = self.node_indexes[*start_index..end_index].intern();
*start_index = end_index;
Some(Self {
ty,
layout,
node_indexes,
})
},
)
}
fn enum_variants(self) -> impl Iterator<Item = Option<Self>> {
let enum_ = Enum::from_canonical(self.ty);
let ResetsLayout::Enum {
variants,
reset_count: _,
} = self.layout
else {
unreachable!();
};
enum_.variants().into_iter().zip(variants).scan(
0,
move |start_index, (EnumVariant { ty, .. }, layout)| {
let end_index = *start_index + layout.reset_count();
let node_indexes = self.node_indexes[*start_index..end_index].intern();
*start_index = end_index;
Some(ty.map(|ty| Self {
ty,
layout,
node_indexes,
}))
},
)
}
fn substituted_type(
self,
reset_graph: &mut ResetGraph,
fallback_to_sync_reset: bool,
fallback_error_source_location: SourceLocation,
) -> Result<CanonicalType, DeduceResetsError> {
if self.layout.reset_count() == 0 {
return Ok(self.ty);
}
match self.ty {
CanonicalType::UInt(_)
| CanonicalType::SInt(_)
| CanonicalType::Bool(_)
| CanonicalType::AsyncReset(_)
| CanonicalType::SyncReset(_)
| CanonicalType::Clock(_)
| CanonicalType::PhantomConst(_)
| CanonicalType::DynSimOnly(_) => Ok(self.ty),
CanonicalType::Array(ty) => Ok(CanonicalType::Array(Array::new_dyn(
self.array_elements().substituted_type(
reset_graph,
fallback_to_sync_reset,
fallback_error_source_location,
)?,
ty.len(),
))),
CanonicalType::Enum(ty) => Ok(CanonicalType::Enum(Enum::new(Result::from_iter(
self.enum_variants().zip(ty.variants()).map(
|(resets, EnumVariant { name, ty: _ })| {
Ok(EnumVariant {
name,
ty: resets
.map(|resets| {
resets.substituted_type(
reset_graph,
fallback_to_sync_reset,
fallback_error_source_location,
)
})
.transpose()?,
})
},
),
)?))),
CanonicalType::Bundle(ty) => Ok(CanonicalType::Bundle(Bundle::new(Result::from_iter(
self.bundle_fields().zip(ty.fields()).map(
|(
resets,
BundleField {
name,
flipped,
ty: _,
},
)| {
Ok(BundleField {
name,
flipped,
ty: resets.substituted_type(
reset_graph,
fallback_to_sync_reset,
fallback_error_source_location,
)?,
})
},
),
)?))),
CanonicalType::Reset(_) => Ok(
if reset_graph.is_async(
self.node_indexes[0],
fallback_to_sync_reset,
fallback_error_source_location,
)? {
CanonicalType::AsyncReset(AsyncReset)
} else {
CanonicalType::SyncReset(SyncReset)
},
),
CanonicalType::TraceAsString(ty) => Ok(CanonicalType::TraceAsString(
ty.with_new_inner_ty(
self.array_elements()
.substituted_type(
reset_graph,
fallback_to_sync_reset,
fallback_error_source_location,
)?
.intern_sized(),
),
)),
}
}
}
#[derive(Debug)]
struct State {
modules_added_to_graph: HashSet<InstantiatedModule>,
substituted_modules: HashMap<InstantiatedModule, Module<Bundle>>,
expr_resets: HashMap<ExprInInstantiatedModule<CanonicalType>, Resets>,
reset_graph: ResetGraph,
fallback_to_sync_reset: bool,
}
impl State {
fn get_resets(
&self,
instantiated_module: InstantiatedModule,
expr: impl ToExpr,
) -> Option<Resets> {
self.expr_resets
.get(&ExprInInstantiatedModule {
instantiated_module,
expr: Expr::canonical(expr.to_expr()),
})
.copied()
}
fn get_or_make_resets(
&mut self,
instantiated_module: InstantiatedModule,
expr: impl ToExpr,
source_location: Option<SourceLocation>,
) -> (Resets, bool) {
let expr = Expr::canonical(expr.to_expr());
match self.expr_resets.entry(ExprInInstantiatedModule {
instantiated_module,
expr,
}) {
Entry::Occupied(entry) => (*entry.get(), false),
Entry::Vacant(entry) => (
*entry.insert(Resets::with_new_nodes(
&mut self.reset_graph,
expr.ty(),
source_location,
)),
true,
),
}
}
}
struct PassOutput<T, P: Pass>(P::Output<T>);
impl<T, P: Pass> PassOutput<T, P> {
fn new(v: T) -> Self {
P::output_new(v)
}
fn from_fn(f: impl FnOnce() -> T) -> Self {
PassOutput::new(()).map(|()| f())
}
fn map<U>(self, f: impl FnOnce(T) -> U) -> PassOutput<U, P> {
P::map(self, f)
}
}
trait PassOutputZip<P: Pass>: Sized {
type Zipped;
fn zip(self) -> PassOutput<Self::Zipped, P>;
fn call<U>(self, f: impl FnOnce(Self::Zipped) -> U) -> PassOutput<U, P> {
self.zip().map(f)
}
}
impl<P: Pass> PassOutputZip<P> for () {
type Zipped = ();
fn zip(self) -> PassOutput<Self::Zipped, P> {
PassOutput::new(())
}
}
impl<T, P: Pass> PassOutputZip<P> for (PassOutput<T, P>,) {
type Zipped = (T,);
fn zip(self) -> PassOutput<Self::Zipped, P> {
self.0.map(|v| (v,))
}
}
macro_rules! impl_zip {
($first_arg:ident: $first_T:ident, $($arg:ident: $T:ident),* $(,)?) => {
impl_zip!(@step [], [($first_arg: $first_T) $(($arg: $T))*], (),);
};
(
@impl($first_arg:tt,),
$tuple_pat:tt,
) => {};
(
@impl(($first_arg:ident: $first_T:ident),
$(($arg:ident: $T:ident),)*),
$tuple_pat:tt,
) => {
impl<$first_T, $($T,)* P: Pass> PassOutputZip<P> for (PassOutput<$first_T, P>, $(PassOutput<$T, P>),*) {
type Zipped = ($first_T, $($T),*);
fn zip(self) -> PassOutput<($first_T, $($T),*), P> {
let (tuples, $($arg),*) = self;
$(let tuples = P::zip(tuples, $arg);)*
tuples.map(|$tuple_pat| ($first_arg, $($arg),*))
}
}
};
(
@step [$($cur:tt)*],
[],
$tuple_pat:tt,
) => {};
(
@step [$($cur:tt)*],
[($next_arg:ident: $next_T:ident) $($rest:tt)*],
(),
) => {
impl_zip!(@impl($($cur,)* ($next_arg: $next_T),), $next_arg,);
impl_zip!(@step [$($cur)* ($next_arg: $next_T)], [$($rest)*], $next_arg,);
};
(
@step [$($cur:tt)*],
[($next_arg:ident: $next_T:ident) $($rest:tt)*],
$tuple_pat:tt,
) => {
impl_zip!(@impl($($cur,)* ($next_arg: $next_T),), ($tuple_pat, $next_arg),);
impl_zip!(@step [$($cur)* ($next_arg: $next_T)], [$($rest)*], ($tuple_pat, $next_arg),);
};
}
impl_zip!(t0: T0, t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7, t8: T8, t9: T9, t10: T10, t11: T11);
impl<T: FromIterator<A>, P: Pass, A> FromIterator<PassOutput<A, P>> for PassOutput<T, P> {
fn from_iter<I: IntoIterator<Item = PassOutput<A, P>>>(iter: I) -> Self {
P::output_from_iter(iter)
}
}
trait PassDispatch: Sized {
type Input<P: Pass>;
type Output<P: Pass>;
fn build_reset_graph(
self,
input: Self::Input<BuildResetGraph>,
) -> Self::Output<BuildResetGraph>;
fn substitute_resets(
self,
input: Self::Input<SubstituteResets>,
) -> Self::Output<SubstituteResets>;
}
trait Pass: Sized {
type Output<T>;
fn output_new<T>(v: T) -> PassOutput<T, Self>;
fn output_from_iter<T: FromIterator<A>, A>(
iter: impl IntoIterator<Item = PassOutput<A, Self>>,
) -> PassOutput<T, Self>;
fn try_array_from_fn<T, const N: usize, E>(
f: impl FnMut(usize) -> Result<PassOutput<T, Self>, E>,
) -> Result<PassOutput<[T; N], Self>, E>;
fn map<T, U>(v: PassOutput<T, Self>, f: impl FnOnce(T) -> U) -> PassOutput<U, Self>;
fn zip<T, U>(t: PassOutput<T, Self>, u: PassOutput<U, Self>) -> PassOutput<(T, U), Self>;
fn dispatch<D: PassDispatch>(dispatch: D, input: D::Input<Self>) -> D::Output<Self>;
}
struct BuildResetGraph;
impl Pass for BuildResetGraph {
type Output<T> = ();
fn output_new<T>(_v: T) -> PassOutput<T, Self> {
PassOutput(())
}
fn output_from_iter<T: FromIterator<A>, A>(
iter: impl IntoIterator<Item = PassOutput<A, Self>>,
) -> PassOutput<T, Self> {
iter.into_iter().for_each(|_| {});
PassOutput(())
}
fn try_array_from_fn<T, const N: usize, E>(
mut f: impl FnMut(usize) -> Result<PassOutput<T, Self>, E>,
) -> Result<PassOutput<[T; N], Self>, E> {
for i in 0..N {
f(i)?;
}
Ok(PassOutput(()))
}
fn map<T, U>(_v: PassOutput<T, Self>, _f: impl FnOnce(T) -> U) -> PassOutput<U, Self> {
PassOutput(())
}
fn zip<T, U>(_t: PassOutput<T, Self>, _u: PassOutput<U, Self>) -> PassOutput<(T, U), Self> {
PassOutput(())
}
fn dispatch<D: PassDispatch>(dispatch: D, input: D::Input<Self>) -> D::Output<Self> {
dispatch.build_reset_graph(input)
}
}
struct SubstituteResets;
impl Pass for SubstituteResets {
type Output<T> = T;
fn output_new<T>(v: T) -> PassOutput<T, Self> {
PassOutput(v)
}
fn output_from_iter<T: FromIterator<A>, A>(
iter: impl IntoIterator<Item = PassOutput<A, Self>>,
) -> PassOutput<T, Self> {
PassOutput(T::from_iter(iter.into_iter().map(|PassOutput(v)| v)))
}
fn try_array_from_fn<T, const N: usize, E>(
mut f: impl FnMut(usize) -> Result<PassOutput<T, Self>, E>,
) -> Result<PassOutput<[T; N], Self>, E> {
let mut retval = [const { None }; N];
for i in 0..N {
retval[i] = Some(f(i)?.0);
}
Ok(PassOutput(
retval.map(|v| v.expect("just wrote Some to all elements")),
))
}
fn map<T, U>(v: PassOutput<T, Self>, f: impl FnOnce(T) -> U) -> PassOutput<U, Self> {
PassOutput(f(v.0))
}
fn zip<T, U>(t: PassOutput<T, Self>, u: PassOutput<U, Self>) -> PassOutput<(T, U), Self> {
PassOutput((t.0, u.0))
}
fn dispatch<D: PassDispatch>(dispatch: D, input: D::Input<Self>) -> D::Output<Self> {
dispatch.substitute_resets(input)
}
}
struct PassArgs<'a, P: Pass> {
state: &'a mut State,
instantiated_module: InstantiatedModule,
fallback_error_source_location: SourceLocation,
_phantom: PhantomData<P>,
}
impl<'a, P: Pass> fmt::Debug for PassArgs<'a, P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
state,
instantiated_module,
fallback_error_source_location,
_phantom: _,
} = self;
f.debug_struct("PassArgs")
.field("state", state)
.field("instantiated_module", instantiated_module)
.field(
"fallback_error_source_location",
fallback_error_source_location,
)
.finish()
}
}
impl<'a, P: Pass> PassArgs<'a, P> {
fn as_mut(&mut self) -> PassArgs<'_, P> {
let PassArgs {
ref mut state,
instantiated_module,
fallback_error_source_location,
_phantom: _,
} = *self;
PassArgs {
state: &mut **state,
instantiated_module,
fallback_error_source_location,
_phantom: PhantomData,
}
}
fn get_resets(&self, expr: impl ToExpr) -> Option<Resets> {
self.state.get_resets(self.instantiated_module, expr)
}
fn get_or_make_resets(
&mut self,
expr: impl ToExpr,
source_location: Option<SourceLocation>,
) -> (Resets, bool) {
self.state
.get_or_make_resets(self.instantiated_module, expr, source_location)
}
fn union(
&mut self,
a: Resets,
b: Resets,
fallback_error_source_location: Option<SourceLocation>,
) -> Result<(), DeduceResetsError> {
assert_eq!(a.layout, b.layout);
assert!(
a.ty.can_connect(b.ty),
"can't connect types! a:\n{a:?}\nb:\n{b:?}"
);
for (a_node_index, b_node_index) in a.node_indexes.into_iter().zip(b.node_indexes) {
self.state.reset_graph.union(
a_node_index,
b_node_index,
fallback_error_source_location.unwrap_or(self.fallback_error_source_location),
)?;
}
Ok(())
}
}
trait RunPass<P: Pass>: Sized {
fn run_pass(
&self,
pass_args: PassArgs<'_, P>,
) -> Result<PassOutput<Self, P>, DeduceResetsError>;
}
trait RunPassDispatch: Sized {
fn build_reset_graph(
&self,
pass_args: PassArgs<'_, BuildResetGraph>,
) -> Result<PassOutput<Self, BuildResetGraph>, DeduceResetsError>;
fn substitute_resets(
&self,
pass_args: PassArgs<'_, SubstituteResets>,
) -> Result<PassOutput<Self, SubstituteResets>, DeduceResetsError>;
fn dispatch<P: Pass>(
&self,
pass_args: PassArgs<'_, P>,
) -> Result<PassOutput<Self, P>, DeduceResetsError> {
struct Dispatch<'a, T>(T, PhantomData<&'a mut ()>);
impl<'a, T: RunPassDispatch> PassDispatch for Dispatch<'a, &'_ T> {
type Input<P: Pass> = PassArgs<'a, P>;
type Output<P: Pass> = Result<PassOutput<T, P>, DeduceResetsError>;
fn build_reset_graph(
self,
input: Self::Input<BuildResetGraph>,
) -> Self::Output<BuildResetGraph> {
self.0.build_reset_graph(input)
}
fn substitute_resets(
self,
input: Self::Input<SubstituteResets>,
) -> Self::Output<SubstituteResets> {
self.0.substitute_resets(input)
}
}
P::dispatch(Dispatch(self, PhantomData), pass_args)
}
}
impl<T: RunPassDispatch, P: Pass> RunPass<P> for T {
fn run_pass(
&self,
pass_args: PassArgs<'_, P>,
) -> Result<PassOutput<Self, P>, DeduceResetsError> {
T::dispatch(self, pass_args)
}
}
trait RunPassExpr: ToExpr + Sized {
type Args<'a>: IntoIterator<Item = Expr<CanonicalType>> + 'a
where
Self: 'a;
fn args<'a>(&'a self) -> Self::Args<'a>;
fn source_location(&self) -> Option<SourceLocation>;
fn union_parts(
&self,
resets: Resets,
args_resets: Vec<Resets>,
pass_args: PassArgs<'_, BuildResetGraph>,
) -> Result<(), DeduceResetsError>;
fn new(
&self,
ty: CanonicalType,
new_args: Vec<Expr<CanonicalType>>,
) -> Result<Self, DeduceResetsError>;
}
impl<T: RunPassExpr> RunPassDispatch for T {
fn build_reset_graph(
&self,
mut pass_args: PassArgs<'_, BuildResetGraph>,
) -> Result<PassOutput<Self, BuildResetGraph>, DeduceResetsError> {
let source_location = self.source_location();
let (resets, _) = pass_args.get_or_make_resets(self, source_location);
let args_resets = Result::from_iter(self.args().into_iter().map(|arg| {
arg.run_pass(pass_args.as_mut())?;
let (resets, _) = pass_args.get_or_make_resets(arg, source_location);
Ok(resets)
}))?;
self.union_parts(resets, args_resets, pass_args)?;
Ok(PassOutput(()))
}
fn substitute_resets(
&self,
mut pass_args: PassArgs<'_, SubstituteResets>,
) -> Result<PassOutput<Self, SubstituteResets>, DeduceResetsError> {
let source_location = self.source_location();
let (resets, _) = pass_args.get_or_make_resets(self, source_location);
let ty = resets.substituted_type(
&mut pass_args.state.reset_graph,
pass_args.state.fallback_to_sync_reset,
pass_args.fallback_error_source_location,
)?;
let new_args = Result::from_iter(
self.args()
.into_iter()
.map(|arg| Ok(arg.run_pass(pass_args.as_mut())?.0)),
)?;
Ok(PassOutput(self.new(ty, new_args)?))
}
}
impl<T: RunPass<P> + Intern + Clone, P: Pass> RunPass<P> for Interned<T> {
fn run_pass(
&self,
pass_args: PassArgs<'_, P>,
) -> Result<PassOutput<Self, P>, DeduceResetsError> {
Ok(T::run_pass(self, pass_args)?.map(Intern::intern_sized))
}
}
impl<T: RunPass<P> + Clone, P: Pass> RunPass<P> for Interned<[T]>
where
[T]: Intern,
{
fn run_pass(
&self,
mut pass_args: PassArgs<'_, P>,
) -> Result<PassOutput<Self, P>, DeduceResetsError> {
Result::from_iter(self.iter().map(|v| v.run_pass(pass_args.as_mut())))
}
}
impl<T: RunPass<P>, P: Pass, const N: usize> RunPass<P> for [T; N] {
fn run_pass(
&self,
mut pass_args: PassArgs<'_, P>,
) -> Result<PassOutput<Self, P>, DeduceResetsError> {
P::try_array_from_fn(|i| self[i].run_pass(pass_args.as_mut()))
}
}
impl<T: RunPass<P>, P: Pass> RunPass<P> for Option<T> {
fn run_pass(
&self,
pass_args: PassArgs<'_, P>,
) -> Result<PassOutput<Self, P>, DeduceResetsError> {
match self {
Some(v) => Ok(v.run_pass(pass_args)?.map(Some)),
None => Ok(PassOutput::new(None)),
}
}
}
fn reg_expr_run_pass<P: Pass, R: ResetType>(
reg: &Reg<CanonicalType, R>,
pass_args: PassArgs<'_, P>,
) -> Result<PassOutput<ExprEnum, P>, DeduceResetsError> {
Ok(AnyReg::from(*reg)
.run_pass(pass_args)?
.map(|reg| match_any_reg!(reg, ExprEnum::from)))
}
fn cast_bit_op<P: Pass, T: Type, A: Type>(
expr: impl ToExpr<Type = T>,
arg: Expr<A>,
pass_args: PassArgs<'_, P>,
) -> Result<PassOutput<ExprEnum, P>, DeduceResetsError> {
struct Dispatch<'a, T: Type, A: Type> {
expr: Expr<T>,
arg: Expr<A>,
_phantom: PhantomData<&'a mut ()>,
}
impl<'a, T: Type, A: Type> PassDispatch for Dispatch<'a, T, A> {
type Input<P: Pass> = PassArgs<'a, P>;
type Output<P: Pass> = Result<PassOutput<ExprEnum, P>, DeduceResetsError>;
fn build_reset_graph(
self,
mut pass_args: Self::Input<BuildResetGraph>,
) -> Self::Output<BuildResetGraph> {
Expr::canonical(self.arg).run_pass(pass_args.as_mut())?;
let (expr_resets, _) = pass_args.get_or_make_resets(self.expr, None);
let (arg_resets, _) = pass_args.get_or_make_resets(self.arg, None);
// don't use PassArgs::union since types don't match and we want to just union resets if they exist
for (expr_node_index, arg_node_index) in expr_resets
.node_indexes
.into_iter()
.zip(arg_resets.node_indexes)
{
pass_args.state.reset_graph.union(
expr_node_index,
arg_node_index,
pass_args.fallback_error_source_location,
)?;
}
Ok(PassOutput(()))
}
fn substitute_resets(
self,
mut pass_args: Self::Input<SubstituteResets>,
) -> Self::Output<SubstituteResets> {
let resets = pass_args
.get_resets(self.expr)
.expect("added resets in build_reset_graph");
let arg = Expr::canonical(self.arg).run_pass(pass_args.as_mut())?;
let ty = resets.substituted_type(
&mut pass_args.state.reset_graph,
pass_args.state.fallback_to_sync_reset,
pass_args.fallback_error_source_location,
)?;
Ok(arg.map(|arg| {
macro_rules! match_expr_ty {
($arg:ident, $($Variant:ident),*) => {
match ty {
CanonicalType::Array(_)
| CanonicalType::Enum(_)
| CanonicalType::Bundle(_)
| CanonicalType::Reset(_)
| CanonicalType::PhantomConst(_)
| CanonicalType::DynSimOnly(_)
| CanonicalType::TraceAsString(_) => unreachable!(),
$(CanonicalType::$Variant(ty) => Expr::expr_enum($arg.cast_to(ty)),)*
}
};
}
macro_rules! match_arg_ty {
($($Variant:ident),*) => {
*match arg.ty() {
CanonicalType::Array(_)
| CanonicalType::Enum(_)
| CanonicalType::Bundle(_)
| CanonicalType::Reset(_)
| CanonicalType::TraceAsString(_) => unreachable!(),
CanonicalType::PhantomConst(_) |
CanonicalType::DynSimOnly(_) => Expr::expr_enum(arg),
$(CanonicalType::$Variant(_) => {
let arg = Expr::<$Variant>::from_canonical(arg);
match_expr_ty!(arg, UInt, SInt, Bool, AsyncReset, SyncReset, Clock)
})*
}
};
}
match_arg_ty!(UInt, SInt, Bool, AsyncReset, SyncReset, Clock)
}))
}
}
P::dispatch(
Dispatch {
expr: expr.to_expr(),
arg,
_phantom: PhantomData,
},
pass_args,
)
}
impl<P: Pass> RunPass<P> for ExprEnum {
fn run_pass(
&self,
pass_args: PassArgs<'_, P>,
) -> Result<PassOutput<Self, P>, DeduceResetsError> {
match self {
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)),
ExprEnum::Uninit(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::NotU(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::NotS(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::NotB(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::Neg(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::BitAndU(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::BitAndS(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::BitAndB(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::BitOrU(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::BitOrS(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::BitOrB(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::BitXorU(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::BitXorS(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::BitXorB(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::AddU(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::AddS(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::SubU(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::SubS(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::MulU(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::MulS(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::DivU(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::DivS(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::RemU(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::RemS(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::DynShlU(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::DynShlS(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::DynShrU(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::DynShrS(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::FixedShlU(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::FixedShlS(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::FixedShrU(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::FixedShrS(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CmpLtB(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CmpLeB(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CmpGtB(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CmpGeB(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CmpEqB(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CmpNeB(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CmpLtU(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CmpLeU(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CmpGtU(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CmpGeU(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CmpEqU(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CmpNeU(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CmpLtS(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CmpLeS(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CmpGtS(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CmpGeS(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CmpEqS(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CmpNeS(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CastUIntToUInt(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CastUIntToSInt(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CastSIntToUInt(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CastSIntToSInt(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CastBoolToUInt(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CastBoolToSInt(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CastUIntToBool(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CastSIntToBool(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CastBoolToSyncReset(expr) => cast_bit_op(expr, expr.arg(), pass_args),
ExprEnum::CastUIntToSyncReset(expr) => cast_bit_op(expr, expr.arg(), pass_args),
ExprEnum::CastSIntToSyncReset(expr) => cast_bit_op(expr, expr.arg(), pass_args),
ExprEnum::CastBoolToAsyncReset(expr) => cast_bit_op(expr, expr.arg(), pass_args),
ExprEnum::CastUIntToAsyncReset(expr) => cast_bit_op(expr, expr.arg(), pass_args),
ExprEnum::CastSIntToAsyncReset(expr) => cast_bit_op(expr, expr.arg(), pass_args),
ExprEnum::CastSyncResetToBool(expr) => cast_bit_op(expr, expr.arg(), pass_args),
ExprEnum::CastSyncResetToUInt(expr) => cast_bit_op(expr, expr.arg(), pass_args),
ExprEnum::CastSyncResetToSInt(expr) => cast_bit_op(expr, expr.arg(), pass_args),
ExprEnum::CastSyncResetToReset(expr) => cast_bit_op(expr, expr.arg(), pass_args),
ExprEnum::CastAsyncResetToBool(expr) => cast_bit_op(expr, expr.arg(), pass_args),
ExprEnum::CastAsyncResetToUInt(expr) => cast_bit_op(expr, expr.arg(), pass_args),
ExprEnum::CastAsyncResetToSInt(expr) => cast_bit_op(expr, expr.arg(), pass_args),
ExprEnum::CastAsyncResetToReset(expr) => cast_bit_op(expr, expr.arg(), pass_args),
ExprEnum::CastResetToBool(expr) => cast_bit_op(expr, expr.arg(), pass_args),
ExprEnum::CastResetToUInt(expr) => cast_bit_op(expr, expr.arg(), pass_args),
ExprEnum::CastResetToSInt(expr) => cast_bit_op(expr, expr.arg(), pass_args),
ExprEnum::CastBoolToClock(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CastUIntToClock(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CastSIntToClock(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CastClockToBool(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CastClockToUInt(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CastClockToSInt(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::FieldAccess(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::VariantAccess(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::ArrayIndex(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::DynArrayIndex(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::ReduceBitAndU(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::ReduceBitAndS(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::ReduceBitOrU(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::ReduceBitOrS(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::ReduceBitXorU(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::ReduceBitXorS(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::SliceUInt(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::SliceSInt(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CastToBits(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::CastBitsTo(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::TraceAsStringAsInner(expr) => {
Ok(expr.run_pass(pass_args)?.map(ExprEnum::from))
}
ExprEnum::ToTraceAsString(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::ModuleIO(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::Instance(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::Wire(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::Reg(expr) => reg_expr_run_pass(expr, pass_args),
ExprEnum::RegSync(expr) => reg_expr_run_pass(expr, pass_args),
ExprEnum::RegAsync(expr) => reg_expr_run_pass(expr, pass_args),
ExprEnum::MemPort(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::FormalInput(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
ExprEnum::SimIoForGlobal(_) => {
unreachable!("Module is known to not contain SimIoForGlobal from validation")
}
}
}
}
impl<P: Pass> RunPass<P> for Expr<CanonicalType> {
fn run_pass(
&self,
pass_args: PassArgs<'_, P>,
) -> Result<PassOutput<Self, P>, DeduceResetsError> {
Ok(Expr::expr_enum(*self)
.run_pass(pass_args)?
.map(|expr_enum| expr_enum.to_expr()))
}
}
impl<P: Pass, Width: Size> RunPass<P> for Expr<UIntType<Width>> {
fn run_pass(
&self,
pass_args: PassArgs<'_, P>,
) -> Result<PassOutput<Self, P>, DeduceResetsError> {
Ok(Expr::canonical(*self)
.run_pass(pass_args)?
.map(Expr::from_canonical))
}
}
impl<P: Pass, Width: Size> RunPass<P> for Expr<SIntType<Width>> {
fn run_pass(
&self,
pass_args: PassArgs<'_, P>,
) -> Result<PassOutput<Self, P>, DeduceResetsError> {
Ok(Expr::canonical(*self)
.run_pass(pass_args)?
.map(Expr::from_canonical))
}
}
impl<P: Pass> RunPass<P> for Expr<Bool> {
fn run_pass(
&self,
pass_args: PassArgs<'_, P>,
) -> Result<PassOutput<Self, P>, DeduceResetsError> {
Ok(Expr::canonical(*self)
.run_pass(pass_args)?
.map(Expr::from_canonical))
}
}
impl<P: Pass> RunPass<P> for Expr<Enum> {
fn run_pass(
&self,
pass_args: PassArgs<'_, P>,
) -> Result<PassOutput<Self, P>, DeduceResetsError> {
Ok(Expr::canonical(*self)
.run_pass(pass_args)?
.map(Expr::from_canonical))
}
}
impl<P: Pass> RunPass<P> for Expr<Clock> {
fn run_pass(
&self,
pass_args: PassArgs<'_, P>,
) -> Result<PassOutput<Self, P>, DeduceResetsError> {
Ok(Expr::canonical(*self)
.run_pass(pass_args)?
.map(Expr::from_canonical))
}
}
impl RunPassExpr for ops::Uninit {
type Args<'a> = [Expr<CanonicalType>; 0];
fn args<'a>(&'a self) -> Self::Args<'a> {
[]
}
fn source_location(&self) -> Option<SourceLocation> {
None
}
fn union_parts(
&self,
_resets: Resets,
_args_resets: Vec<Resets>,
_pass_args: PassArgs<'_, BuildResetGraph>,
) -> Result<(), DeduceResetsError> {
Ok(())
}
fn new(
&self,
ty: CanonicalType,
_new_args: Vec<Expr<CanonicalType>>,
) -> Result<Self, DeduceResetsError> {
Ok(ops::Uninit::new(ty))
}
}
impl RunPassExpr for ops::BundleLiteral {
type Args<'a> = Interned<[Expr<CanonicalType>]>;
fn args<'a>(&'a self) -> Self::Args<'a> {
self.field_values()
}
fn source_location(&self) -> Option<SourceLocation> {
None
}
fn union_parts(
&self,
resets: Resets,
args_resets: Vec<Resets>,
mut pass_args: PassArgs<'_, BuildResetGraph>,
) -> Result<(), DeduceResetsError> {
for (resets_field, field_expr_resets) in resets.bundle_fields().zip(args_resets) {
pass_args.union(resets_field, field_expr_resets, None)?;
}
Ok(())
}
fn new(
&self,
ty: CanonicalType,
new_args: Vec<Expr<CanonicalType>>,
) -> Result<Self, DeduceResetsError> {
Ok(ops::BundleLiteral::new(
Bundle::from_canonical(ty),
Intern::intern_owned(new_args),
))
}
}
impl RunPassExpr for ArrayLiteral<CanonicalType, DynSize> {
type Args<'a> = Interned<[Expr<CanonicalType>]>;
fn args<'a>(&'a self) -> Self::Args<'a> {
self.element_values()
}
fn source_location(&self) -> Option<SourceLocation> {
None
}
fn union_parts(
&self,
resets: Resets,
args_resets: Vec<Resets>,
mut pass_args: PassArgs<'_, BuildResetGraph>,
) -> Result<(), DeduceResetsError> {
let resets_elements = resets.array_elements();
for arg_resets in args_resets {
pass_args.union(resets_elements, arg_resets, None)?;
}
Ok(())
}
fn new(
&self,
ty: CanonicalType,
new_args: Vec<Expr<CanonicalType>>,
) -> Result<Self, DeduceResetsError> {
Ok(Self::new(
<Array>::from_canonical(ty).element(),
Intern::intern_owned(new_args),
))
}
}
impl RunPassExpr for ops::EnumLiteral {
type Args<'a> = Option<Expr<CanonicalType>>;
fn args<'a>(&'a self) -> Self::Args<'a> {
self.variant_value()
}
fn source_location(&self) -> Option<SourceLocation> {
None
}
fn union_parts(
&self,
resets: Resets,
args_resets: Vec<Resets>,
mut pass_args: PassArgs<'_, BuildResetGraph>,
) -> Result<(), DeduceResetsError> {
if let Some(Some(variant_resets)) = resets.enum_variants().nth(self.variant_index()) {
pass_args.union(variant_resets, args_resets[0], None)?;
}
Ok(())
}
fn new(
&self,
ty: CanonicalType,
new_args: Vec<Expr<CanonicalType>>,
) -> Result<Self, DeduceResetsError> {
Ok(Self::new_by_index(
Enum::from_canonical(ty),
self.variant_index(),
new_args.get(0).copied(),
))
}
}
impl RunPassExpr for ops::FieldAccess {
type Args<'a> = [Expr<CanonicalType>; 1];
fn args<'a>(&'a self) -> Self::Args<'a> {
[Expr::canonical(self.base())]
}
fn source_location(&self) -> Option<SourceLocation> {
None
}
fn union_parts(
&self,
resets: Resets,
args_resets: Vec<Resets>,
mut pass_args: PassArgs<'_, BuildResetGraph>,
) -> Result<(), DeduceResetsError> {
let Some(field_resets) = args_resets[0].bundle_fields().nth(self.field_index()) else {
unreachable!();
};
pass_args.union(resets, field_resets, None)
}
fn new(
&self,
_ty: CanonicalType,
new_args: Vec<Expr<CanonicalType>>,
) -> Result<Self, DeduceResetsError> {
Ok(Self::new_by_index(
Expr::from_canonical(new_args[0]),
self.field_index(),
))
}
}
impl RunPassExpr for ops::VariantAccess {
type Args<'a> = [Expr<CanonicalType>; 1];
fn args<'a>(&'a self) -> Self::Args<'a> {
[Expr::canonical(self.base())]
}
fn source_location(&self) -> Option<SourceLocation> {
None
}
fn union_parts(
&self,
resets: Resets,
args_resets: Vec<Resets>,
mut pass_args: PassArgs<'_, BuildResetGraph>,
) -> Result<(), DeduceResetsError> {
if let Some(Some(variant_resets)) = args_resets[0].enum_variants().nth(self.variant_index())
{
pass_args.union(resets, variant_resets, None)?;
}
Ok(())
}
fn new(
&self,
_ty: CanonicalType,
new_args: Vec<Expr<CanonicalType>>,
) -> Result<Self, DeduceResetsError> {
Ok(Self::new_by_index(
Expr::from_canonical(new_args[0]),
self.variant_index(),
))
}
}
impl RunPassExpr for ops::ArrayIndex {
type Args<'a> = [Expr<CanonicalType>; 1];
fn args<'a>(&'a self) -> Self::Args<'a> {
[Expr::canonical(self.base())]
}
fn source_location(&self) -> Option<SourceLocation> {
None
}
fn union_parts(
&self,
resets: Resets,
args_resets: Vec<Resets>,
mut pass_args: PassArgs<'_, BuildResetGraph>,
) -> Result<(), DeduceResetsError> {
pass_args.union(resets, args_resets[0].array_elements(), None)
}
fn new(
&self,
_ty: CanonicalType,
new_args: Vec<Expr<CanonicalType>>,
) -> Result<Self, DeduceResetsError> {
Ok(Self::new(
Expr::from_canonical(new_args[0]),
self.element_index(),
))
}
}
impl RunPassExpr for ops::DynArrayIndex {
type Args<'a> = [Expr<CanonicalType>; 2];
fn args<'a>(&'a self) -> Self::Args<'a> {
[
Expr::canonical(self.base()),
Expr::canonical(self.element_index()),
]
}
fn source_location(&self) -> Option<SourceLocation> {
None
}
fn union_parts(
&self,
resets: Resets,
args_resets: Vec<Resets>,
mut pass_args: PassArgs<'_, BuildResetGraph>,
) -> Result<(), DeduceResetsError> {
pass_args.union(resets, args_resets[0].array_elements(), None)
}
fn new(
&self,
_ty: CanonicalType,
new_args: Vec<Expr<CanonicalType>>,
) -> Result<Self, DeduceResetsError> {
Ok(Self::new(
Expr::from_canonical(new_args[0]),
Expr::from_canonical(new_args[1]),
))
}
}
impl RunPassExpr for ops::CastBitsTo {
type Args<'a> = [Expr<CanonicalType>; 1];
fn args<'a>(&'a self) -> Self::Args<'a> {
[Expr::canonical(self.arg())]
}
fn source_location(&self) -> Option<SourceLocation> {
None
}
fn union_parts(
&self,
_resets: Resets,
_args_resets: Vec<Resets>,
_pass_args: PassArgs<'_, BuildResetGraph>,
) -> Result<(), DeduceResetsError> {
Ok(())
}
fn new(
&self,
ty: CanonicalType,
new_args: Vec<Expr<CanonicalType>>,
) -> Result<Self, DeduceResetsError> {
Ok(Self::new(Expr::from_canonical(new_args[0]), ty))
}
}
impl RunPassExpr for ops::TraceAsStringAsInner {
type Args<'a> = [Expr<CanonicalType>; 1];
fn args<'a>(&'a self) -> Self::Args<'a> {
[Expr::canonical(self.arg())]
}
fn source_location(&self) -> Option<SourceLocation> {
None
}
fn union_parts(
&self,
resets: Resets,
args_resets: Vec<Resets>,
mut pass_args: PassArgs<'_, BuildResetGraph>,
) -> Result<(), DeduceResetsError> {
pass_args.union(resets, args_resets[0].trace_as_string_inner(), None)
}
fn new(
&self,
_ty: CanonicalType,
new_args: Vec<Expr<CanonicalType>>,
) -> Result<Self, DeduceResetsError> {
Ok(Self::new(Expr::from_canonical(new_args[0])))
}
}
impl RunPassExpr for ops::ToTraceAsString {
type Args<'a> = [Expr<CanonicalType>; 1];
fn args<'a>(&'a self) -> Self::Args<'a> {
[Expr::canonical(self.inner())]
}
fn source_location(&self) -> Option<SourceLocation> {
None
}
fn union_parts(
&self,
resets: Resets,
args_resets: Vec<Resets>,
mut pass_args: PassArgs<'_, BuildResetGraph>,
) -> Result<(), DeduceResetsError> {
pass_args.union(resets.trace_as_string_inner(), args_resets[0], None)
}
fn new(
&self,
_ty: CanonicalType,
new_args: Vec<Expr<CanonicalType>>,
) -> Result<Self, DeduceResetsError> {
Ok(Self::new(
new_args[0],
self.ty().with_new_inner_ty(new_args[0].ty().intern_sized()),
))
}
}
impl RunPassExpr for ModuleIO<CanonicalType> {
type Args<'a> = [Expr<CanonicalType>; 0];
fn args<'a>(&'a self) -> Self::Args<'a> {
[]
}
fn source_location(&self) -> Option<SourceLocation> {
Some(self.source_location())
}
fn union_parts(
&self,
_resets: Resets,
_args_resets: Vec<Resets>,
_pass_args: PassArgs<'_, BuildResetGraph>,
) -> Result<(), DeduceResetsError> {
Ok(())
}
fn new(
&self,
ty: CanonicalType,
_new_args: Vec<Expr<CanonicalType>>,
) -> Result<Self, DeduceResetsError> {
Ok(Self::new_unchecked(
self.containing_module_name_id(),
self.name_id(),
self.source_location(),
self.is_input(),
ty,
))
}
}
impl RunPassExpr for Wire<CanonicalType> {
type Args<'a> = [Expr<CanonicalType>; 0];
fn args<'a>(&'a self) -> Self::Args<'a> {
[]
}
fn source_location(&self) -> Option<SourceLocation> {
Some(self.source_location())
}
fn union_parts(
&self,
_resets: Resets,
_args_resets: Vec<Resets>,
_pass_args: PassArgs<'_, BuildResetGraph>,
) -> Result<(), DeduceResetsError> {
Ok(())
}
fn new(
&self,
ty: CanonicalType,
_new_args: Vec<Expr<CanonicalType>>,
) -> Result<Self, DeduceResetsError> {
Ok(Self::new_unchecked(
self.scoped_name(),
self.source_location(),
ty,
))
}
}
impl<T: ResetType> From<Reg<CanonicalType, T>> for AnyReg {
fn from(value: Reg<CanonicalType, T>) -> Self {
struct Dispatch;
impl ResetTypeDispatch for Dispatch {
type Input<T: ResetType> = Reg<CanonicalType, T>;
type Output<T: ResetType> = AnyReg;
fn reset(self, input: Self::Input<Reset>) -> Self::Output<Reset> {
AnyReg::Reg(input)
}
fn sync_reset(self, input: Self::Input<SyncReset>) -> Self::Output<SyncReset> {
AnyReg::RegSync(input)
}
fn async_reset(self, input: Self::Input<AsyncReset>) -> Self::Output<AsyncReset> {
AnyReg::RegAsync(input)
}
}
T::dispatch(value, Dispatch)
}
}
impl RunPassDispatch for AnyReg {
fn build_reset_graph(
&self,
mut pass_args: PassArgs<'_, BuildResetGraph>,
) -> Result<PassOutput<Self, BuildResetGraph>, DeduceResetsError> {
match_any_reg!(self, |reg: &Reg<CanonicalType, _>| {
pass_args
.get_or_make_resets(Expr::canonical(reg.to_expr()), Some(reg.source_location()));
reg.init().run_pass(pass_args.as_mut())?;
Expr::canonical(reg.clock_domain()).run_pass(pass_args)?;
Ok(PassOutput(()))
})
}
fn substitute_resets(
&self,
mut pass_args: PassArgs<'_, SubstituteResets>,
) -> Result<PassOutput<Self, SubstituteResets>, DeduceResetsError> {
match_any_reg!(self, |reg: &Reg<CanonicalType, _>| {
let scoped_name = reg.scoped_name();
let source_location = reg.source_location();
let resets = pass_args
.get_resets(Expr::canonical(reg.to_expr()))
.expect("added resets in build_reset_graph");
let ty = resets.substituted_type(
&mut pass_args.state.reset_graph,
pass_args.state.fallback_to_sync_reset,
source_location,
)?;
let init = reg.init().run_pass(pass_args.as_mut())?.0;
let clock_domain = Expr::<Bundle>::from_canonical(
Expr::canonical(reg.clock_domain()).run_pass(pass_args)?.0,
);
match clock_domain
.ty()
.field_by_name("rst".intern())
.expect("ClockDomain has rst field")
.ty
{
CanonicalType::AsyncReset(_) => {
Ok(PassOutput(AnyReg::RegAsync(Reg::new_unchecked(
scoped_name,
source_location,
ty,
Expr::from_bundle(clock_domain),
init,
))))
}
CanonicalType::SyncReset(_) => Ok(PassOutput(AnyReg::RegSync(Reg::new_unchecked(
scoped_name,
source_location,
ty,
Expr::from_bundle(clock_domain),
init,
)))),
CanonicalType::UInt(_)
| CanonicalType::SInt(_)
| CanonicalType::Bool(_)
| CanonicalType::Array(_)
| CanonicalType::Enum(_)
| CanonicalType::Bundle(_)
| CanonicalType::Reset(_)
| CanonicalType::Clock(_)
| CanonicalType::PhantomConst(_)
| CanonicalType::DynSimOnly(_)
| CanonicalType::TraceAsString(_) => unreachable!(),
}
})
}
}
impl RunPassDispatch for Instance<Bundle> {
fn build_reset_graph(
&self,
mut pass_args: PassArgs<'_, BuildResetGraph>,
) -> Result<PassOutput<Self, BuildResetGraph>, DeduceResetsError> {
self.instantiated().run_pass(PassArgs::<BuildResetGraph> {
state: pass_args.state,
instantiated_module: InstantiatedModule::Child {
parent: pass_args.instantiated_module.intern_sized(),
instance: self.intern(),
},
fallback_error_source_location: self.instantiated().source_location(),
_phantom: PhantomData,
})?;
let (resets, _) = pass_args.get_or_make_resets(self, Some(self.source_location()));
for (resets_field, module_io) in resets.bundle_fields().zip(self.instantiated().module_io())
{
let (module_io_resets, _) = pass_args.get_or_make_resets(
module_io.module_io,
Some(self.instantiated().source_location()),
);
pass_args.union(resets_field, module_io_resets, Some(self.source_location()))?;
}
Ok(PassOutput(()))
}
fn substitute_resets(
&self,
pass_args: PassArgs<'_, SubstituteResets>,
) -> Result<PassOutput<Self, SubstituteResets>, DeduceResetsError> {
let PassOutput(instantiated) =
self.instantiated().run_pass(PassArgs::<SubstituteResets> {
state: pass_args.state,
instantiated_module: InstantiatedModule::Child {
parent: pass_args.instantiated_module.intern_sized(),
instance: self.intern(),
},
fallback_error_source_location: self.instantiated().source_location(),
_phantom: PhantomData,
})?;
Ok(PassOutput(Self::new_unchecked(
self.scoped_name(),
instantiated,
self.source_location(),
)))
}
}
impl<P: Pass> RunPass<P> for ExternModuleSimulation {
fn run_pass(
&self,
mut pass_args: PassArgs<'_, P>,
) -> Result<PassOutput<Self, P>, DeduceResetsError> {
let Self {
generator,
sim_io_to_generator_map,
source_location,
} = *self;
let sim_io_to_generator_map = Result::<PassOutput<BTreeMap<_, _>, P>, _>::from_iter(
sim_io_to_generator_map
.iter()
.map(|(sim_io, generator_io)| {
Ok(sim_io
.run_pass(pass_args.as_mut())?
.map(|v| (v, *generator_io)))
}),
)?;
Ok(sim_io_to_generator_map.map(|sim_io_to_generator_map| Self {
generator,
sim_io_to_generator_map: sim_io_to_generator_map.intern_sized(),
source_location,
}))
}
}
macro_rules! impl_run_pass_copy {
([$($generics:tt)*] $ty:ty) => {
impl<P: Pass, $($generics)*> RunPass<P> for $ty {
fn run_pass(
&self,
_pass_args: PassArgs<'_, P>,
) -> Result<PassOutput<Self, P>, DeduceResetsError> {
Ok(PassOutput::new(*self))
}
}
};
}
macro_rules! impl_run_pass_clone {
([$($generics:tt)*] $ty:ty) => {
impl<P: Pass, $($generics)*> RunPass<P> for $ty {
fn run_pass(
&self,
_pass_args: PassArgs<'_, P>,
) -> Result<PassOutput<Self, P>, DeduceResetsError> {
Ok(PassOutput::from_fn(|| self.clone()))
}
}
};
}
impl_run_pass_clone!([] BigInt);
impl_run_pass_clone!([] ExternModuleParameter);
impl_run_pass_clone!([] SIntValue);
impl_run_pass_clone!([] std::ops::Range<usize>);
impl_run_pass_clone!([] UIntValue);
impl_run_pass_clone!([] crate::vendor::xilinx::XilinxAnnotation);
impl_run_pass_copy!([] BlackBoxInlineAnnotation);
impl_run_pass_copy!([] BlackBoxPathAnnotation);
impl_run_pass_copy!([] bool);
impl_run_pass_copy!([] CustomFirrtlAnnotation);
impl_run_pass_copy!([] DocStringAnnotation);
impl_run_pass_copy!([] DontTouchAnnotation);
impl_run_pass_copy!([] Interned<str>);
impl_run_pass_copy!([] NameId);
impl_run_pass_copy!([] SInt);
impl_run_pass_copy!([] SourceLocation);
impl_run_pass_copy!([] SVAttributeAnnotation);
impl_run_pass_copy!([] UInt);
impl_run_pass_copy!([] usize);
impl_run_pass_copy!([] FormalKind);
impl_run_pass_copy!([] crate::formal::FormalInput);
impl_run_pass_copy!([] PhantomConst);
macro_rules! impl_run_pass_for_struct {
(
$(#[adjust_pass_args = $adjust_pass_args:expr])?
#[constructor = $constructor:expr]
impl[$($generics:tt)*] $RunPass:ident for $ty:ty {
$($field:ident $(($($args:tt)*))?: _,)*
}
) => {
impl<P: Pass, $($generics)*> $RunPass<P> for $ty {
#[allow(unused_mut, unused_variables)]
fn run_pass(
&self,
mut pass_args: PassArgs<'_, P>,
) -> Result<PassOutput<Self, P>, DeduceResetsError> {
$($adjust_pass_args(self, &mut pass_args);)?
Ok(($(self.$field$(($($args)*))?.run_pass(pass_args.as_mut())?,)*).call(|($($field,)*)| $constructor))
}
}
};
(
$(#[adjust_pass_args = $adjust_pass_args:expr])?
impl[$($generics:tt)*] $RunPass:ident for $ty:ty {
$($field:ident: _,)*
}
) => {
impl_run_pass_for_struct! {
$(#[adjust_pass_args = $adjust_pass_args])?
#[constructor = {
type Ty<T> = T;
Ty::<$ty> { $($field,)* }
}]
impl[$($generics)*] $RunPass for $ty {
$($field: _,)*
}
}
};
}
macro_rules! impl_run_pass_for_enum {
(impl[$($generics:tt)*] $RunPass:ident for $ty:ty {
$($variant:ident($arg:ident),)*
}) => {
impl<P: Pass, $($generics)*> $RunPass<P> for $ty {
fn run_pass(
&self,
pass_args: PassArgs<'_, P>,
) -> Result<PassOutput<Self, P>, DeduceResetsError> {
type Ty<T> = T;
match self {
$(Ty::<$ty>::$variant($arg) => Ok($arg.run_pass(pass_args)?.map(<$ty>::$variant)),)*
}
}
}
};
}
macro_rules! impl_run_pass_for_unary_op {
($path:path) => {
impl_run_pass_for_struct! {
#[constructor = <$path>::new(arg)]
impl[] RunPass for $path {
arg(): _,
}
}
};
}
impl_run_pass_for_unary_op!(ops::NotU);
impl_run_pass_for_unary_op!(ops::NotS);
impl_run_pass_for_unary_op!(ops::NotB);
impl_run_pass_for_unary_op!(ops::Neg);
impl_run_pass_for_unary_op!(ops::CastBoolToUInt);
impl_run_pass_for_unary_op!(ops::CastBoolToSInt);
impl_run_pass_for_unary_op!(ops::CastUIntToBool);
impl_run_pass_for_unary_op!(ops::CastSIntToBool);
impl_run_pass_for_unary_op!(ops::CastBoolToClock);
impl_run_pass_for_unary_op!(ops::CastUIntToClock);
impl_run_pass_for_unary_op!(ops::CastSIntToClock);
impl_run_pass_for_unary_op!(ops::CastClockToBool);
impl_run_pass_for_unary_op!(ops::CastClockToUInt);
impl_run_pass_for_unary_op!(ops::CastClockToSInt);
impl_run_pass_for_unary_op!(ops::ReduceBitAndU);
impl_run_pass_for_unary_op!(ops::ReduceBitAndS);
impl_run_pass_for_unary_op!(ops::ReduceBitOrU);
impl_run_pass_for_unary_op!(ops::ReduceBitOrS);
impl_run_pass_for_unary_op!(ops::ReduceBitXorU);
impl_run_pass_for_unary_op!(ops::ReduceBitXorS);
macro_rules! impl_run_pass_for_binary_op {
($path:path) => {
impl_run_pass_for_struct! {
#[constructor = <$path>::new(lhs, rhs)]
impl[] RunPass for $path {
lhs(): _,
rhs(): _,
}
}
};
}
impl_run_pass_for_binary_op!(ops::BitAndU);
impl_run_pass_for_binary_op!(ops::BitAndS);
impl_run_pass_for_binary_op!(ops::BitAndB);
impl_run_pass_for_binary_op!(ops::BitOrU);
impl_run_pass_for_binary_op!(ops::BitOrS);
impl_run_pass_for_binary_op!(ops::BitOrB);
impl_run_pass_for_binary_op!(ops::BitXorU);
impl_run_pass_for_binary_op!(ops::BitXorS);
impl_run_pass_for_binary_op!(ops::BitXorB);
impl_run_pass_for_binary_op!(ops::AddU);
impl_run_pass_for_binary_op!(ops::AddS);
impl_run_pass_for_binary_op!(ops::SubU);
impl_run_pass_for_binary_op!(ops::SubS);
impl_run_pass_for_binary_op!(ops::MulU);
impl_run_pass_for_binary_op!(ops::MulS);
impl_run_pass_for_binary_op!(ops::DivU);
impl_run_pass_for_binary_op!(ops::DivS);
impl_run_pass_for_binary_op!(ops::RemU);
impl_run_pass_for_binary_op!(ops::RemS);
impl_run_pass_for_binary_op!(ops::DynShlU);
impl_run_pass_for_binary_op!(ops::DynShlS);
impl_run_pass_for_binary_op!(ops::DynShrU);
impl_run_pass_for_binary_op!(ops::DynShrS);
impl_run_pass_for_binary_op!(ops::FixedShlU);
impl_run_pass_for_binary_op!(ops::FixedShlS);
impl_run_pass_for_binary_op!(ops::FixedShrU);
impl_run_pass_for_binary_op!(ops::FixedShrS);
impl_run_pass_for_binary_op!(ops::CmpLtB);
impl_run_pass_for_binary_op!(ops::CmpLeB);
impl_run_pass_for_binary_op!(ops::CmpGtB);
impl_run_pass_for_binary_op!(ops::CmpGeB);
impl_run_pass_for_binary_op!(ops::CmpEqB);
impl_run_pass_for_binary_op!(ops::CmpNeB);
impl_run_pass_for_binary_op!(ops::CmpLtU);
impl_run_pass_for_binary_op!(ops::CmpLeU);
impl_run_pass_for_binary_op!(ops::CmpGtU);
impl_run_pass_for_binary_op!(ops::CmpGeU);
impl_run_pass_for_binary_op!(ops::CmpEqU);
impl_run_pass_for_binary_op!(ops::CmpNeU);
impl_run_pass_for_binary_op!(ops::CmpLtS);
impl_run_pass_for_binary_op!(ops::CmpLeS);
impl_run_pass_for_binary_op!(ops::CmpGtS);
impl_run_pass_for_binary_op!(ops::CmpGeS);
impl_run_pass_for_binary_op!(ops::CmpEqS);
impl_run_pass_for_binary_op!(ops::CmpNeS);
macro_rules! impl_run_pass_for_int_cast_op {
($path:path) => {
impl_run_pass_for_struct! {
#[constructor = <$path>::new(arg, ty)]
impl[] RunPass for $path {
arg(): _,
ty(): _,
}
}
};
}
impl_run_pass_for_int_cast_op!(ops::CastUIntToUInt);
impl_run_pass_for_int_cast_op!(ops::CastUIntToSInt);
impl_run_pass_for_int_cast_op!(ops::CastSIntToUInt);
impl_run_pass_for_int_cast_op!(ops::CastSIntToSInt);
impl_run_pass_for_struct! {
#[constructor = ops::SliceUInt::new(base, range)]
impl[] RunPass for ops::SliceUInt {
base(): _,
range(): _,
}
}
impl_run_pass_for_struct! {
#[constructor = ops::SliceSInt::new(base, range)]
impl[] RunPass for ops::SliceSInt {
base(): _,
range(): _,
}
}
impl_run_pass_for_struct! {
#[constructor = ops::CastToBits::new(arg)]
impl[] RunPass for ops::CastToBits {
arg(): _,
}
}
impl_run_pass_for_struct! {
impl[] RunPass for StmtFormal {
kind: _,
clk: _,
pred: _,
en: _,
text: _,
source_location: _,
}
}
impl_run_pass_for_struct! {
impl[] RunPass for StmtIf {
cond: _,
source_location: _,
blocks: _,
}
}
impl_run_pass_for_struct! {
impl[] RunPass for StmtMatch {
expr: _,
source_location: _,
blocks: _,
}
}
impl_run_pass_for_struct! {
impl[] RunPass for StmtWire {
annotations: _,
wire: _,
}
}
impl_run_pass_for_struct! {
impl[] RunPass for StmtInstance {
annotations: _,
instance: _,
}
}
impl_run_pass_for_enum! {
impl[] RunPass for Stmt {
Connect(v),
Formal(v),
If(v),
Match(v),
Declaration(v),
}
}
impl_run_pass_for_struct! {
impl[] RunPass for Block {
memories: _,
stmts: _,
}
}
impl_run_pass_for_struct! {
impl[] RunPass for NormalModuleBody {
body: _,
}
}
impl_run_pass_for_struct! {
impl[] RunPass for ExternModuleBody {
verilog_name: _,
parameters: _,
clocks_for_past: _,
simulation: _,
}
}
impl_run_pass_copy!([] MemPort<DynPortType>); // Mem can't contain any `Reset` types
impl_run_pass_copy!([] Mem); // Mem can't contain any `Reset` types
impl RunPassDispatch for StmtConnect {
fn build_reset_graph(
&self,
mut pass_args: PassArgs<'_, BuildResetGraph>,
) -> Result<PassOutput<Self, BuildResetGraph>, DeduceResetsError> {
let Self {
lhs,
rhs,
source_location,
} = *self;
pass_args.fallback_error_source_location = source_location;
lhs.run_pass(pass_args.as_mut())?;
rhs.run_pass(pass_args.as_mut())?;
let (lhs_resets, _) = pass_args.get_or_make_resets(lhs, Some(source_location));
let (rhs_resets, _) = pass_args.get_or_make_resets(rhs, Some(source_location));
pass_args.union(lhs_resets, rhs_resets, Some(source_location))?;
Ok(PassOutput(()))
}
fn substitute_resets(
&self,
mut pass_args: PassArgs<'_, SubstituteResets>,
) -> Result<PassOutput<Self, SubstituteResets>, DeduceResetsError> {
let StmtConnect {
lhs,
rhs,
source_location,
} = *self;
pass_args.fallback_error_source_location = source_location;
let lhs = lhs.run_pass(pass_args.as_mut())?.0;
let rhs = rhs.run_pass(pass_args)?.0;
Ok(PassOutput(StmtConnect {
lhs,
rhs,
source_location,
}))
}
}
impl<P: Pass> RunPass<P> for TargetBase {
fn run_pass(
&self,
pass_args: PassArgs<'_, P>,
) -> Result<PassOutput<Self, P>, DeduceResetsError> {
let reg: AnyReg = match self {
TargetBase::ModuleIO(v) => return Ok(v.run_pass(pass_args)?.map(TargetBase::ModuleIO)),
TargetBase::MemPort(v) => return Ok(v.run_pass(pass_args)?.map(TargetBase::MemPort)),
&TargetBase::Reg(v) => v.into(),
&TargetBase::RegSync(v) => v.into(),
&TargetBase::RegAsync(v) => v.into(),
TargetBase::Wire(v) => return Ok(v.run_pass(pass_args)?.map(TargetBase::Wire)),
TargetBase::Instance(v) => return Ok(v.run_pass(pass_args)?.map(TargetBase::Instance)),
TargetBase::FormalInput(v) => {
return Ok(v.run_pass(pass_args)?.map(TargetBase::FormalInput));
}
TargetBase::SimIoForGlobal(_) => {
unreachable!("Module is known to not contain SimIoForGlobal from validation")
}
};
Ok(reg.run_pass(pass_args)?.map(|reg| match reg {
AnyReg::Reg(reg) => TargetBase::Reg(reg),
AnyReg::RegSync(reg) => TargetBase::RegSync(reg),
AnyReg::RegAsync(reg) => TargetBase::RegAsync(reg),
}))
}
}
impl<P: Pass> RunPass<P> for StmtDeclaration {
fn run_pass(
&self,
mut pass_args: PassArgs<'_, P>,
) -> Result<PassOutput<Self, P>, DeduceResetsError> {
let (annotations, reg) = match self {
StmtDeclaration::Wire(v) => {
return Ok(v.run_pass(pass_args)?.map(StmtDeclaration::Wire));
}
&StmtDeclaration::Reg(StmtReg { annotations, reg }) => (annotations, AnyReg::from(reg)),
&StmtDeclaration::RegSync(StmtReg { annotations, reg }) => {
(annotations, AnyReg::from(reg))
}
&StmtDeclaration::RegAsync(StmtReg { annotations, reg }) => {
(annotations, AnyReg::from(reg))
}
StmtDeclaration::Instance(v) => {
return Ok(v.run_pass(pass_args)?.map(StmtDeclaration::Instance));
}
};
let annotations = annotations.run_pass(pass_args.as_mut())?;
let reg = reg.run_pass(pass_args)?;
Ok((annotations, reg).call(|(annotations, reg)| match reg {
AnyReg::Reg(reg) => StmtReg { annotations, reg }.into(),
AnyReg::RegSync(reg) => StmtReg { annotations, reg }.into(),
AnyReg::RegAsync(reg) => StmtReg { annotations, reg }.into(),
}))
}
}
impl_run_pass_for_enum! {
impl[] RunPass for Target {
Base(v),
Child(v),
}
}
impl<P: Pass> RunPass<P> for TargetChild {
fn run_pass(
&self,
mut pass_args: PassArgs<'_, P>,
) -> Result<PassOutput<Self, P>, DeduceResetsError> {
Ok(self.parent().run_pass(pass_args.as_mut())?.map(|parent| {
let path_element = match *self.path_element() {
TargetPathElement::BundleField(TargetPathBundleField { name: _ })
| TargetPathElement::ArrayElement(TargetPathArrayElement { index: _ })
| TargetPathElement::DynArrayElement(TargetPathDynArrayElement {})
| TargetPathElement::TraceAsStringInner(TargetPathTraceAsStringInner {}) => {
self.path_element()
}
TargetPathElement::ToTraceAsString(TargetPathToTraceAsString { ty }) => {
TargetPathElement::from(TargetPathToTraceAsString {
ty: ty.with_new_inner_ty(parent.canonical_ty().intern_sized()),
})
.intern_sized()
}
};
TargetChild::new(parent, path_element)
}))
}
}
impl_run_pass_for_enum! {
impl[] RunPass for Annotation {
DontTouch(v),
SVAttribute(v),
BlackBoxInline(v),
BlackBoxPath(v),
DocString(v),
CustomFirrtl(v),
Xilinx(v),
}
}
impl_run_pass_for_enum! {
impl[] RunPass for ModuleBody {
Normal(v),
Extern(v),
}
}
impl_run_pass_for_struct! {
#[constructor = TargetedAnnotation::new(target, annotation)]
impl[] RunPass for TargetedAnnotation {
target(): _,
annotation(): _,
}
}
impl_run_pass_for_struct! {
impl[] RunPass for AnnotatedModuleIO {
annotations: _,
module_io: _,
}
}
impl RunPassDispatch for Module<Bundle> {
fn build_reset_graph(
&self,
mut pass_args: PassArgs<'_, BuildResetGraph>,
) -> Result<PassOutput<Self, BuildResetGraph>, DeduceResetsError> {
pass_args.fallback_error_source_location = self.source_location();
if pass_args
.state
.modules_added_to_graph
.insert(pass_args.instantiated_module)
{
self.name_id().run_pass(pass_args.as_mut())?;
self.source_location().run_pass(pass_args.as_mut())?;
self.module_io().run_pass(pass_args.as_mut())?;
self.body().run_pass(pass_args.as_mut())?;
self.module_annotations().run_pass(pass_args.as_mut())?;
}
Ok(PassOutput(()))
}
fn substitute_resets(
&self,
mut pass_args: PassArgs<'_, SubstituteResets>,
) -> Result<PassOutput<Self, SubstituteResets>, DeduceResetsError> {
pass_args.fallback_error_source_location = self.source_location();
if let Some(&retval) = pass_args
.state
.substituted_modules
.get(&pass_args.instantiated_module)
{
return Ok(PassOutput(retval));
}
let PassOutput(name_id) = self.name_id().run_pass(pass_args.as_mut())?;
let PassOutput(source_location) = self.source_location().run_pass(pass_args.as_mut())?;
let PassOutput(module_io) = self.module_io().run_pass(pass_args.as_mut())?;
let PassOutput(body) = self.body().run_pass(pass_args.as_mut())?;
let PassOutput(module_annotations) =
self.module_annotations().run_pass(pass_args.as_mut())?;
let retval = Module::new_unchecked(
name_id,
source_location,
body,
module_io,
module_annotations,
);
pass_args
.state
.substituted_modules
.insert(pass_args.instantiated_module, retval);
Ok(PassOutput(retval))
}
}
pub fn deduce_resets(
module: Interned<Module<Bundle>>,
fallback_to_sync_reset: bool,
) -> Result<Interned<Module<Bundle>>, DeduceResetsError> {
let mut state = State {
modules_added_to_graph: HashSet::default(),
substituted_modules: HashMap::default(),
expr_resets: HashMap::default(),
reset_graph: ResetGraph::default(),
fallback_to_sync_reset,
};
RunPass::<BuildResetGraph>::run_pass(
&*module,
PassArgs {
state: &mut state,
instantiated_module: InstantiatedModule::Base(module),
fallback_error_source_location: module.source_location(),
_phantom: PhantomData,
},
)?;
Ok(RunPass::<SubstituteResets>::run_pass(
&*module,
PassArgs {
state: &mut state,
instantiated_module: InstantiatedModule::Base(module),
fallback_error_source_location: module.source_location(),
_phantom: PhantomData,
},
)?
.0
.intern_sized())
}