forked from libre-chip/fayalite
2451 lines
83 KiB
Rust
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())
|
|
}
|