forked from libre-chip/fayalite
2816 lines
87 KiB
Rust
2816 lines
87 KiB
Rust
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
// See Notices.txt for copyright information
|
|
#![allow(clippy::type_complexity)]
|
|
use crate::{
|
|
annotations::{Annotation, IntoAnnotations, TargetedAnnotation},
|
|
array::ArrayType,
|
|
bundle::{Bundle, BundleField, BundleType},
|
|
clock::{Clock, ClockDomain},
|
|
enum_::{Enum, EnumMatchVariantsIter, EnumType},
|
|
expr::{
|
|
ops::VariantAccess,
|
|
target::{
|
|
GetTarget, Target, TargetBase, TargetPathArrayElement, TargetPathBundleField,
|
|
TargetPathElement,
|
|
},
|
|
Expr, Flow, ToExpr,
|
|
},
|
|
int::{Bool, DynSize, Size},
|
|
intern::{Intern, Interned},
|
|
memory::{Mem, MemBuilder, MemBuilderTarget, PortName},
|
|
reg::Reg,
|
|
source_location::SourceLocation,
|
|
ty::{CanonicalType, Type},
|
|
util::ScopedRef,
|
|
wire::{IncompleteWire, Wire},
|
|
};
|
|
use hashbrown::{hash_map::Entry, HashMap, HashSet};
|
|
use num_bigint::BigInt;
|
|
use std::{
|
|
cell::RefCell,
|
|
collections::VecDeque,
|
|
convert::Infallible,
|
|
fmt,
|
|
hash::{Hash, Hasher},
|
|
iter::FusedIterator,
|
|
marker::PhantomData,
|
|
mem,
|
|
ops::Deref,
|
|
rc::Rc,
|
|
sync::Mutex,
|
|
};
|
|
|
|
pub mod transform;
|
|
|
|
mod module_building_status_sealed {
|
|
pub trait Sealed {}
|
|
}
|
|
|
|
pub trait ModuleBuildingStatus:
|
|
'static
|
|
+ Send
|
|
+ Sync
|
|
+ Copy
|
|
+ Ord
|
|
+ Hash
|
|
+ fmt::Debug
|
|
+ Default
|
|
+ module_building_status_sealed::Sealed
|
|
{
|
|
type Block: 'static + Send + Sync + Copy + Eq + Hash + fmt::Debug;
|
|
type ModuleBody: fmt::Debug;
|
|
type StmtAnnotations: 'static + Send + Sync + Copy + Eq + Hash + fmt::Debug;
|
|
type ModuleIOAnnotations;
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Default)]
|
|
pub struct ModuleBuilt;
|
|
|
|
impl module_building_status_sealed::Sealed for ModuleBuilt {}
|
|
|
|
impl ModuleBuildingStatus for ModuleBuilt {
|
|
type Block = Block;
|
|
type ModuleBody = Block;
|
|
type StmtAnnotations = Interned<[TargetedAnnotation]>;
|
|
type ModuleIOAnnotations = Interned<[TargetedAnnotation]>;
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Default)]
|
|
pub(crate) struct ModuleBuilding;
|
|
|
|
impl module_building_status_sealed::Sealed for ModuleBuilding {}
|
|
|
|
impl ModuleBuildingStatus for ModuleBuilding {
|
|
type Block = BlockId;
|
|
type ModuleBody = BuilderModuleBody;
|
|
type StmtAnnotations = ();
|
|
type ModuleIOAnnotations = Vec<TargetedAnnotation>;
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct BuilderModuleBody {
|
|
blocks: Vec<BuilderBlock>,
|
|
annotations_map: HashMap<StmtDeclaration<ModuleBuilding>, Vec<TargetedAnnotation>>,
|
|
memory_map: HashMap<ScopedNameId, Rc<RefCell<MemBuilderTarget>>>,
|
|
}
|
|
|
|
/// implicit name argument that's automatically provided by `#[hdl]` let
|
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
|
pub struct ImplicitName<'a>(pub &'a str);
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
pub struct BlockId(usize);
|
|
|
|
impl BlockId {
|
|
pub const ENTRY_BLOCK: BlockId = BlockId(0);
|
|
pub fn as_usize(self) -> usize {
|
|
self.0
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for BlockId {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "block#{}", self.0)
|
|
}
|
|
}
|
|
|
|
pub trait BlockRef: 'static + Send + Sync + Copy + Eq + Hash + fmt::Debug {}
|
|
|
|
impl BlockRef for BlockId {}
|
|
|
|
pub(crate) enum IncompleteDeclaration {
|
|
Incomplete {
|
|
name: ScopedNameId,
|
|
source_location: SourceLocation,
|
|
},
|
|
Complete(StmtDeclaration<ModuleBuilding>),
|
|
Taken,
|
|
}
|
|
|
|
impl fmt::Debug for IncompleteDeclaration {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::Incomplete {
|
|
name,
|
|
source_location: _,
|
|
} => f
|
|
.debug_struct("Incomplete")
|
|
.field("name", name)
|
|
.finish_non_exhaustive(),
|
|
Self::Complete(v) => v.fmt(f),
|
|
Self::Taken => f.write_str("Taken"),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct BuilderBlock {
|
|
memories: Vec<Rc<RefCell<MemBuilderTarget>>>,
|
|
incomplete_declarations: Vec<Rc<RefCell<IncompleteDeclaration>>>,
|
|
stmts: Vec<Stmt<ModuleBuilding>>,
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Default)]
|
|
pub struct Block {
|
|
pub memories: Interned<[Mem]>,
|
|
pub stmts: Interned<[Stmt]>,
|
|
}
|
|
|
|
impl BlockRef for Block {}
|
|
|
|
impl fmt::Debug for Block {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
f.debug_list()
|
|
.entries(self.memories)
|
|
.entries(self.stmts)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
impl Block {
|
|
pub fn child_blocks(
|
|
&self,
|
|
) -> impl DoubleEndedIterator<Item = Block> + FusedIterator + Clone + '_ {
|
|
self.stmts
|
|
.iter()
|
|
.flat_map(|stmt| stmt.sub_stmt_blocks())
|
|
.copied()
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
|
pub struct StmtConnect {
|
|
pub lhs: Expr<CanonicalType>,
|
|
pub rhs: Expr<CanonicalType>,
|
|
pub source_location: SourceLocation,
|
|
}
|
|
|
|
impl StmtConnect {
|
|
#[track_caller]
|
|
fn assert_validity_with_original_types(&self, lhs_orig_ty: impl Type, rhs_orig_ty: impl Type) {
|
|
let Self {
|
|
lhs,
|
|
rhs,
|
|
source_location,
|
|
} = *self;
|
|
assert!(
|
|
Expr::ty(lhs).can_connect(Expr::ty(rhs)),
|
|
"can't connect types that are not equivalent:\nlhs type:\n{lhs_orig_ty:?}\nrhs type:\n{rhs_orig_ty:?}\nat: {source_location}",
|
|
);
|
|
assert!(
|
|
matches!(Expr::flow(lhs), Flow::Sink | Flow::Duplex),
|
|
"can't connect to source, connect lhs must have sink or duplex flow\nat: {source_location}"
|
|
);
|
|
assert!(
|
|
lhs.target().is_some(),
|
|
"can't connect to non-target\nat: {source_location}"
|
|
);
|
|
match Expr::flow(rhs) {
|
|
Flow::Source | Flow::Duplex => {}
|
|
Flow::Sink => assert!(
|
|
Expr::ty(rhs).is_passive(),
|
|
"can't connect from sink with non-passive type\nat: {source_location}"
|
|
),
|
|
}
|
|
}
|
|
#[track_caller]
|
|
fn assert_validity(&self) {
|
|
self.assert_validity_with_original_types(Expr::ty(self.lhs), Expr::ty(self.rhs));
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for StmtConnect {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let Self {
|
|
lhs,
|
|
rhs,
|
|
source_location: _,
|
|
} = self;
|
|
f.debug_struct("StmtConnect")
|
|
.field("lhs", lhs)
|
|
.field("rhs", rhs)
|
|
.finish_non_exhaustive()
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
|
pub enum StmtFormalKind {
|
|
Assert,
|
|
Assume,
|
|
Cover,
|
|
}
|
|
|
|
impl StmtFormalKind {
|
|
pub fn as_str(self) -> &'static str {
|
|
match self {
|
|
Self::Assert => "assert",
|
|
Self::Assume => "assume",
|
|
Self::Cover => "cover",
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
|
pub struct StmtFormal {
|
|
pub kind: StmtFormalKind,
|
|
pub clk: Expr<Clock>,
|
|
pub pred: Expr<Bool>,
|
|
pub en: Expr<Bool>,
|
|
pub text: Interned<str>,
|
|
pub source_location: SourceLocation,
|
|
}
|
|
|
|
impl fmt::Debug for StmtFormal {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let Self {
|
|
kind,
|
|
clk,
|
|
pred,
|
|
en,
|
|
text,
|
|
source_location: _,
|
|
} = self;
|
|
f.debug_struct(kind.as_str())
|
|
.field("clk", clk)
|
|
.field("pred", pred)
|
|
.field("en", en)
|
|
.field("text", text)
|
|
.finish_non_exhaustive()
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
|
pub struct StmtIf<S: ModuleBuildingStatus = ModuleBuilt> {
|
|
pub cond: Expr<Bool>,
|
|
pub source_location: SourceLocation,
|
|
pub blocks: [S::Block; 2],
|
|
}
|
|
|
|
impl<S: ModuleBuildingStatus> StmtIf<S> {
|
|
pub fn then_block(&self) -> S::Block {
|
|
self.blocks[0]
|
|
}
|
|
pub fn else_block(&self) -> S::Block {
|
|
self.blocks[1]
|
|
}
|
|
}
|
|
|
|
impl<S: ModuleBuildingStatus> fmt::Debug for StmtIf<S> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let Self {
|
|
cond,
|
|
source_location: _,
|
|
blocks: _,
|
|
} = self;
|
|
f.debug_struct("StmtIf")
|
|
.field("cond", cond)
|
|
.field("then_block", &self.then_block())
|
|
.field("else_block", &self.else_block())
|
|
.finish_non_exhaustive()
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
|
pub struct StmtMatch<S: ModuleBuildingStatus = ModuleBuilt> {
|
|
pub expr: Expr<Enum>,
|
|
pub source_location: SourceLocation,
|
|
pub blocks: Interned<[S::Block]>,
|
|
}
|
|
|
|
impl StmtMatch {
|
|
#[track_caller]
|
|
fn assert_validity(&self) {
|
|
assert_eq!(Expr::ty(self.expr).variants().len(), self.blocks.len());
|
|
}
|
|
}
|
|
|
|
impl<S: ModuleBuildingStatus> fmt::Debug for StmtMatch<S> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let Self {
|
|
expr,
|
|
source_location: _,
|
|
blocks,
|
|
} = self;
|
|
f.debug_struct("StmtMatch")
|
|
.field("expr", expr)
|
|
.field("blocks", blocks)
|
|
.finish_non_exhaustive()
|
|
}
|
|
}
|
|
|
|
macro_rules! wrapper_enum {
|
|
(
|
|
#[impl(
|
|
($($T_impl:tt)*) $impl_self:ident: $impl_type:ty = $impl_self_expr:expr
|
|
$(, ($($T_impls:tt)*) $impls_self:ident: $impls_type:ty = $impls_self_expr:expr)*
|
|
)]
|
|
#[to($($T_to:tt $to_types:ty),*)]
|
|
$(#[$enum_meta:meta])*
|
|
$vis:vis enum $enum_name:ident<$T_enum:ident: $T_bound:ident = $T_enum_default:ident> {
|
|
$(
|
|
#[is = $is_fn:ident, as_ref = $as_ref_fn:ident]
|
|
$(#[$variant_meta:meta])*
|
|
$Variant:ident($VariantTy:ty),
|
|
)*
|
|
}
|
|
) => {
|
|
wrapper_enum! {
|
|
#[impl($(($($T_impls)*) $impls_self: $impls_type = $impls_self_expr),*)]
|
|
#[to($($T_to $to_types),*)]
|
|
$(#[$enum_meta])*
|
|
$vis enum $enum_name<$T_enum: $T_bound = $T_enum_default> {
|
|
$(
|
|
#[is = $is_fn, as_ref = $as_ref_fn]
|
|
$(#[$variant_meta])*
|
|
$Variant($VariantTy),
|
|
)*
|
|
}
|
|
}
|
|
|
|
impl $($T_impl)* $impl_type {
|
|
$(
|
|
$vis fn $is_fn(&self) -> bool {
|
|
self.$as_ref_fn().is_some()
|
|
}
|
|
$vis fn $as_ref_fn(&$impl_self) -> Option<&$VariantTy> {
|
|
match $impl_self_expr {
|
|
$enum_name::$Variant(value) => Some(value),
|
|
_ => None,
|
|
}
|
|
}
|
|
)*
|
|
}
|
|
};
|
|
(
|
|
#[impl()]
|
|
#[to($T_to:tt $to_type:ty $(, $T_tos:tt $to_types:ty)*)]
|
|
$(#[$enum_meta:meta])*
|
|
$vis:vis enum $enum_name:ident<$T_enum:ident: $T_bound:ident = $T_enum_default:ident> {
|
|
$(
|
|
#[is = $is_fn:ident, as_ref = $as_ref_fn:ident]
|
|
$(#[$variant_meta:meta])*
|
|
$Variant:ident($VariantTy:ty),
|
|
)*
|
|
}
|
|
) => {
|
|
wrapper_enum! {
|
|
#[impl()]
|
|
#[to($($T_tos $to_types),*)]
|
|
$(#[$enum_meta])*
|
|
$vis enum $enum_name<$T_enum: $T_bound = $T_enum_default> {
|
|
$(
|
|
#[is = $is_fn, as_ref = $as_ref_fn]
|
|
$(#[$variant_meta])*
|
|
$Variant($VariantTy),
|
|
)*
|
|
}
|
|
}
|
|
|
|
$(
|
|
wrapper_enum! {
|
|
impl $T_to From<$VariantTy> for $to_type {
|
|
fn from(value: $VariantTy) -> Self {
|
|
$enum_name::$Variant(value).into()
|
|
}
|
|
}
|
|
}
|
|
)*
|
|
};
|
|
(
|
|
#[impl()]
|
|
#[to()]
|
|
$(#[$enum_meta:meta])*
|
|
$vis:vis enum $enum_name:ident<$T_enum:ident: $T_bound:ident = $T_enum_default:ident> {
|
|
$(
|
|
#[is = $is_fn:ident, as_ref = $as_ref_fn:ident]
|
|
$(#[$variant_meta:meta])*
|
|
$Variant:ident($VariantTy:ty),
|
|
)*
|
|
}
|
|
) => {
|
|
$(#[$enum_meta])*
|
|
$vis enum $enum_name<$T_enum: $T_bound = $T_enum_default> {
|
|
$(
|
|
$(#[$variant_meta])*
|
|
$Variant($VariantTy),
|
|
)*
|
|
}
|
|
|
|
impl<$T_enum: fmt::Debug + $T_bound> fmt::Debug for $enum_name<$T_enum> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
$(Self::$Variant(v) => v.fmt(f),)*
|
|
}
|
|
}
|
|
}
|
|
};
|
|
(
|
|
impl ($($T_to:tt)*) From<$VariantTy:ty> for $to_type:ty {
|
|
$from:item
|
|
}
|
|
) => {
|
|
impl $($T_to)* From<$VariantTy> for $to_type {
|
|
$from
|
|
}
|
|
};
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
|
pub struct StmtWire<S: ModuleBuildingStatus = ModuleBuilt> {
|
|
pub annotations: S::StmtAnnotations,
|
|
pub wire: Wire<CanonicalType>,
|
|
}
|
|
|
|
#[derive(Hash, Clone, PartialEq, Eq, Debug)]
|
|
pub struct StmtReg<S: ModuleBuildingStatus = ModuleBuilt> {
|
|
pub annotations: S::StmtAnnotations,
|
|
pub reg: Reg<CanonicalType>,
|
|
}
|
|
|
|
impl Copy for StmtReg {}
|
|
|
|
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
|
pub struct StmtInstance<S: ModuleBuildingStatus = ModuleBuilt> {
|
|
pub annotations: S::StmtAnnotations,
|
|
pub instance: Instance<Bundle>,
|
|
}
|
|
|
|
wrapper_enum! {
|
|
#[impl(
|
|
(<S: ModuleBuildingStatus>) self: StmtDeclaration<S> = self,
|
|
(<S: ModuleBuildingStatus>) self: Stmt<S> = self.declaration()?
|
|
)]
|
|
#[to((<S: ModuleBuildingStatus>) StmtDeclaration<S>, (<S: ModuleBuildingStatus>) Stmt<S>)]
|
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
|
pub enum StmtDeclaration<S: ModuleBuildingStatus = ModuleBuilt> {
|
|
#[is = is_wire, as_ref = wire]
|
|
Wire(StmtWire<S>),
|
|
#[is = is_reg, as_ref = reg]
|
|
Reg(StmtReg<S>),
|
|
#[is = is_instance, as_ref = instance]
|
|
Instance(StmtInstance<S>),
|
|
}
|
|
}
|
|
|
|
impl<S: ModuleBuildingStatus> StmtDeclaration<S> {
|
|
pub fn annotations(&self) -> S::StmtAnnotations {
|
|
match self {
|
|
StmtDeclaration::Wire(v) => v.annotations,
|
|
StmtDeclaration::Reg(v) => v.annotations,
|
|
StmtDeclaration::Instance(v) => v.annotations,
|
|
}
|
|
}
|
|
pub fn source_location(&self) -> SourceLocation {
|
|
match self {
|
|
StmtDeclaration::Wire(v) => v.wire.source_location(),
|
|
StmtDeclaration::Reg(v) => v.reg.source_location(),
|
|
StmtDeclaration::Instance(v) => v.instance.source_location(),
|
|
}
|
|
}
|
|
pub fn scoped_name(&self) -> ScopedNameId {
|
|
match self {
|
|
StmtDeclaration::Wire(v) => v.wire.scoped_name(),
|
|
StmtDeclaration::Reg(v) => v.reg.scoped_name(),
|
|
StmtDeclaration::Instance(v) => v.instance.scoped_name(),
|
|
}
|
|
}
|
|
pub fn sub_stmt_blocks(&self) -> &[S::Block] {
|
|
match self {
|
|
StmtDeclaration::Wire(_) | StmtDeclaration::Reg(_) | StmtDeclaration::Instance(_) => {
|
|
&[]
|
|
}
|
|
}
|
|
}
|
|
pub fn canonical_ty(&self) -> CanonicalType {
|
|
match self {
|
|
StmtDeclaration::Wire(v) => v.wire.ty(),
|
|
StmtDeclaration::Reg(v) => v.reg.ty(),
|
|
StmtDeclaration::Instance(v) => CanonicalType::Bundle(v.instance.ty()),
|
|
}
|
|
}
|
|
}
|
|
|
|
wrapper_enum! {
|
|
#[impl((<S: ModuleBuildingStatus>) self: Stmt<S> = self)]
|
|
#[to((<S: ModuleBuildingStatus>) Stmt<S>)]
|
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
|
pub enum Stmt<S: ModuleBuildingStatus = ModuleBuilt> {
|
|
#[is = is_connect, as_ref = connect]
|
|
Connect(StmtConnect),
|
|
#[is = is_formal, as_ref = formal]
|
|
Formal(StmtFormal),
|
|
#[is = is_if, as_ref = if_]
|
|
If(StmtIf<S>),
|
|
#[is = is_match, as_ref = match_]
|
|
Match(StmtMatch<S>),
|
|
#[is = is_declaration, as_ref = declaration]
|
|
Declaration(StmtDeclaration<S>),
|
|
}
|
|
}
|
|
|
|
impl<S: ModuleBuildingStatus> Stmt<S> {
|
|
pub fn sub_stmt_blocks(&self) -> &[S::Block] {
|
|
match self {
|
|
Stmt::Connect(_) | Stmt::Formal(_) => &[],
|
|
Stmt::If(v) => &v.blocks,
|
|
Stmt::Match(v) => &v.blocks,
|
|
Stmt::Declaration(v) => v.sub_stmt_blocks(),
|
|
}
|
|
}
|
|
}
|
|
|
|
struct BlockStackEntry {
|
|
// entries are marked removed instead of
|
|
removed: bool,
|
|
block_id: BlockId,
|
|
}
|
|
|
|
struct BlockStack(RefCell<Vec<BlockStackEntry>>);
|
|
|
|
impl BlockStack {
|
|
fn new(top_block: BlockId) -> Self {
|
|
Self(RefCell::new(vec![BlockStackEntry {
|
|
removed: false,
|
|
block_id: top_block,
|
|
}]))
|
|
}
|
|
fn push_scope(self: Rc<Self>, block_id: BlockId) -> Scope {
|
|
let mut block_stack = self.0.borrow_mut();
|
|
Self::clean_stack_and_get_top(&mut block_stack);
|
|
let block_stack_index = block_stack.len();
|
|
block_stack.push(BlockStackEntry {
|
|
removed: false,
|
|
block_id,
|
|
});
|
|
drop(block_stack);
|
|
Scope {
|
|
block_id,
|
|
block_stack_index,
|
|
block_stack: self,
|
|
}
|
|
}
|
|
fn clean_stack_and_get_top(block_stack: &mut Vec<BlockStackEntry>) -> &BlockStackEntry {
|
|
for i in (0..block_stack.len()).rev() {
|
|
if block_stack[i].removed {
|
|
block_stack.pop();
|
|
} else {
|
|
return &block_stack[i];
|
|
}
|
|
}
|
|
unreachable!("top block should never be popped from block stack");
|
|
}
|
|
fn top(&self) -> BlockId {
|
|
let mut block_stack = self.0.borrow_mut();
|
|
Self::clean_stack_and_get_top(&mut block_stack).block_id
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
|
pub struct NameId(pub Interned<str>, pub usize);
|
|
|
|
impl fmt::Debug for NameId {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
fmt::Display::fmt(self, f)
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for NameId {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
if self.0.is_empty() {
|
|
write!(f, "{}", self.1)
|
|
} else if self.1 == 0 {
|
|
f.write_str(&self.0)
|
|
} else {
|
|
write!(f, "{}_{}", self.0, self.1)
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
|
pub struct ScopedNameId(pub NameId, pub NameId);
|
|
|
|
impl fmt::Debug for ScopedNameId {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
self.0.fmt(f)?;
|
|
f.write_str("::")?;
|
|
self.1.fmt(f)
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
|
pub struct TargetName(pub ScopedNameId, pub Option<PortName>);
|
|
|
|
impl fmt::Debug for TargetName {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
self.0.fmt(f)?;
|
|
if let Some(port_name) = self.1 {
|
|
f.write_str("::")?;
|
|
port_name.fmt(f)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[derive(Default)]
|
|
pub struct NameIdGen(HashMap<Interned<str>, usize>);
|
|
|
|
impl NameIdGen {
|
|
pub fn gen(&mut self, name: Interned<str>) -> NameId {
|
|
let next_id = self.0.entry(name).or_default();
|
|
let id = *next_id;
|
|
*next_id += 1;
|
|
NameId(name, id)
|
|
}
|
|
pub fn mark_as_used(&mut self, name_id: NameId) {
|
|
let next_id = self.0.entry(name_id.0).or_default();
|
|
*next_id = (*next_id).max(name_id.1 + 1);
|
|
}
|
|
pub fn for_module(module: Module<Bundle>) -> Self {
|
|
let mut retval = Self::default();
|
|
let Module {
|
|
name: _,
|
|
source_location: _,
|
|
body,
|
|
io_ty: _,
|
|
module_io,
|
|
module_annotations: _,
|
|
} = module;
|
|
for module_io in &module_io {
|
|
retval.mark_as_used(module_io.module_io.name_id());
|
|
}
|
|
match body {
|
|
ModuleBody::Extern(ExternModuleBody {
|
|
verilog_name: _,
|
|
parameters: _,
|
|
}) => {}
|
|
ModuleBody::Normal(NormalModuleBody { body }) => {
|
|
let mut blocks = vec![body];
|
|
while let Some(block) = blocks.pop() {
|
|
let Block { memories, stmts } = block;
|
|
for memory in memories {
|
|
retval.mark_as_used(memory.scoped_name().1);
|
|
}
|
|
for stmt in &stmts {
|
|
blocks.extend_from_slice(stmt.sub_stmt_blocks());
|
|
match stmt {
|
|
Stmt::Connect(StmtConnect {
|
|
lhs: _,
|
|
rhs: _,
|
|
source_location: _,
|
|
})
|
|
| Stmt::Formal(StmtFormal {
|
|
kind: _,
|
|
clk: _,
|
|
pred: _,
|
|
en: _,
|
|
text: _,
|
|
source_location: _,
|
|
})
|
|
| Stmt::If(StmtIf {
|
|
cond: _,
|
|
source_location: _,
|
|
blocks: _,
|
|
})
|
|
| Stmt::Match(StmtMatch {
|
|
expr: _,
|
|
source_location: _,
|
|
blocks: _,
|
|
}) => {}
|
|
Stmt::Declaration(StmtDeclaration::Instance(StmtInstance {
|
|
annotations: _,
|
|
instance,
|
|
})) => {
|
|
retval.mark_as_used(instance.name_id());
|
|
}
|
|
Stmt::Declaration(StmtDeclaration::Reg(StmtReg {
|
|
annotations: _,
|
|
reg,
|
|
})) => {
|
|
retval.mark_as_used(reg.name_id());
|
|
}
|
|
Stmt::Declaration(StmtDeclaration::Wire(StmtWire {
|
|
annotations: _,
|
|
wire,
|
|
})) => {
|
|
retval.mark_as_used(wire.name_id());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
retval
|
|
}
|
|
pub fn gen_module_name(name: Interned<str>) -> NameId {
|
|
static MODULE_NAME_GEN: Mutex<Option<NameIdGen>> = Mutex::new(None);
|
|
MODULE_NAME_GEN
|
|
.lock()
|
|
.unwrap()
|
|
.get_or_insert_with(NameIdGen::default)
|
|
.gen(name)
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
|
pub struct Instance<T: BundleType> {
|
|
scoped_name: ScopedNameId,
|
|
instantiated: Interned<Module<T>>,
|
|
source_location: SourceLocation,
|
|
}
|
|
|
|
impl<T: BundleType> fmt::Debug for Instance<T> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
f.debug_struct("Instance")
|
|
.field("name", &self.scoped_name)
|
|
.field("instantiated", &self.instantiated.debug_short())
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
impl<T: BundleType> Instance<T> {
|
|
pub fn canonical(self) -> Instance<Bundle> {
|
|
let Self {
|
|
scoped_name,
|
|
instantiated,
|
|
source_location,
|
|
} = self;
|
|
Instance {
|
|
scoped_name,
|
|
instantiated: instantiated.canonical().intern_sized(),
|
|
source_location,
|
|
}
|
|
}
|
|
pub fn containing_module_name(self) -> Interned<str> {
|
|
self.containing_module_name_id().0
|
|
}
|
|
pub fn containing_module_name_id(self) -> NameId {
|
|
self.scoped_name.0
|
|
}
|
|
pub fn name(self) -> Interned<str> {
|
|
self.name_id().0
|
|
}
|
|
pub fn name_id(self) -> NameId {
|
|
self.scoped_name.1
|
|
}
|
|
pub fn scoped_name(self) -> ScopedNameId {
|
|
self.scoped_name
|
|
}
|
|
pub fn instantiated(self) -> Interned<Module<T>> {
|
|
self.instantiated
|
|
}
|
|
pub fn source_location(self) -> SourceLocation {
|
|
self.source_location
|
|
}
|
|
pub fn new_unchecked(
|
|
scoped_name: ScopedNameId,
|
|
instantiated: Interned<Module<T>>,
|
|
source_location: SourceLocation,
|
|
) -> Self {
|
|
Self {
|
|
scoped_name,
|
|
instantiated,
|
|
source_location,
|
|
}
|
|
}
|
|
pub fn flow(&self) -> Flow {
|
|
Flow::Source
|
|
}
|
|
pub fn must_connect_to(&self) -> bool {
|
|
true
|
|
}
|
|
pub fn ty(&self) -> T {
|
|
self.instantiated.io_ty()
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
|
pub struct AnnotatedModuleIO<S: ModuleBuildingStatus = ModuleBuilt> {
|
|
pub annotations: S::ModuleIOAnnotations,
|
|
pub module_io: ModuleIO<CanonicalType>,
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
|
pub enum ModuleKind {
|
|
Extern,
|
|
Normal,
|
|
}
|
|
|
|
struct ModuleBuilderImpl {
|
|
body: ModuleBodyBuilding,
|
|
io: Vec<AnnotatedModuleIO<ModuleBuilding>>,
|
|
io_indexes: HashMap<ModuleIO<CanonicalType>, usize>,
|
|
name_id_gen: NameIdGen,
|
|
module_annotations: Vec<Annotation>,
|
|
}
|
|
|
|
pub struct ModuleBuilder {
|
|
name: NameId,
|
|
block_stack: Rc<BlockStack>,
|
|
impl_: RefCell<ModuleBuilderImpl>,
|
|
}
|
|
|
|
#[must_use]
|
|
pub struct Scope {
|
|
block_id: BlockId,
|
|
block_stack_index: usize,
|
|
block_stack: Rc<BlockStack>,
|
|
}
|
|
|
|
impl Drop for Scope {
|
|
fn drop(&mut self) {
|
|
let mut block_stack = self.block_stack.0.borrow_mut();
|
|
let entry = &mut block_stack[self.block_stack_index];
|
|
assert_eq!(
|
|
entry.block_id, self.block_id,
|
|
"block stack not managed properly",
|
|
);
|
|
assert!(!entry.removed, "block stack not managed properly",);
|
|
entry.removed = true; // only mark removed and don't pop it here so scopes can be dropped out of order
|
|
}
|
|
}
|
|
|
|
pub struct IfScope {
|
|
then: Scope,
|
|
else_block: BlockId,
|
|
}
|
|
|
|
impl IfScope {
|
|
pub fn else_(self) -> Scope {
|
|
let block_stack = self.then.block_stack.clone();
|
|
drop(self.then);
|
|
block_stack.push_scope(self.else_block)
|
|
}
|
|
}
|
|
|
|
pub(crate) struct EnumMatchVariantAndInactiveScopeImpl<T: EnumType> {
|
|
block_stack: Rc<BlockStack>,
|
|
match_arm_block_id: BlockId,
|
|
variant_access: VariantAccess<CanonicalType>,
|
|
_phantom: PhantomData<T>,
|
|
}
|
|
|
|
impl<T: EnumType> EnumMatchVariantAndInactiveScopeImpl<T> {
|
|
pub(crate) fn activate(self) -> (VariantAccess<CanonicalType>, Scope) {
|
|
let scope = self.block_stack.push_scope(self.match_arm_block_id);
|
|
(self.variant_access, scope)
|
|
}
|
|
pub(crate) fn variant_access(&self) -> VariantAccess<CanonicalType> {
|
|
self.variant_access
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub(crate) struct EnumMatchVariantsIterImpl<T> {
|
|
block_stack: Rc<BlockStack>,
|
|
blocks: Interned<[BlockId]>,
|
|
base: Expr<Enum>,
|
|
_phantom: PhantomData<T>,
|
|
}
|
|
|
|
impl<T: EnumType> EnumMatchVariantsIterImpl<T> {
|
|
pub(crate) fn for_variant_index(
|
|
&self,
|
|
variant_index: usize,
|
|
) -> EnumMatchVariantAndInactiveScopeImpl<T> {
|
|
EnumMatchVariantAndInactiveScopeImpl {
|
|
block_stack: self.block_stack.clone(),
|
|
match_arm_block_id: self.blocks[variant_index],
|
|
variant_access: VariantAccess::new_by_index(self.base, variant_index),
|
|
_phantom: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
|
pub enum ExternModuleParameterValue {
|
|
Integer(BigInt),
|
|
String(Interned<str>),
|
|
RawVerilog(Interned<str>),
|
|
}
|
|
|
|
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
|
pub struct ExternModuleParameter {
|
|
pub name: Interned<str>,
|
|
pub value: ExternModuleParameterValue,
|
|
}
|
|
|
|
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
|
|
pub struct NormalModuleBody<S: ModuleBuildingStatus = ModuleBuilt> {
|
|
pub body: S::ModuleBody,
|
|
}
|
|
|
|
impl From<NormalModuleBody<ModuleBuilding>> for NormalModuleBody {
|
|
fn from(value: NormalModuleBody<ModuleBuilding>) -> Self {
|
|
let NormalModuleBody {
|
|
body:
|
|
BuilderModuleBody {
|
|
mut blocks,
|
|
mut annotations_map,
|
|
memory_map: _,
|
|
},
|
|
} = value;
|
|
fn finalize_block(
|
|
blocks: &mut [BuilderBlock],
|
|
annotations_map: &mut HashMap<StmtDeclaration<ModuleBuilding>, Vec<TargetedAnnotation>>,
|
|
block_id: BlockId,
|
|
) -> Block {
|
|
let BuilderBlock {
|
|
memories,
|
|
incomplete_declarations,
|
|
stmts,
|
|
} = &mut blocks[block_id.as_usize()];
|
|
let memories = Interned::from_iter(
|
|
memories
|
|
.drain(..)
|
|
.filter_map(|memory| memory.borrow().make_memory()),
|
|
);
|
|
let stmts = Vec::from_iter(
|
|
incomplete_declarations
|
|
.drain(..)
|
|
.map(|decl| {
|
|
match std::mem::replace(
|
|
&mut *decl.borrow_mut(),
|
|
IncompleteDeclaration::Taken,
|
|
) {
|
|
IncompleteDeclaration::Incomplete {
|
|
name,
|
|
source_location,
|
|
} => panic!("incomplete declaration: {name:?}\nat: {source_location}"),
|
|
IncompleteDeclaration::Complete(v) => Stmt::Declaration(v),
|
|
IncompleteDeclaration::Taken => unreachable!(),
|
|
}
|
|
})
|
|
.chain(stmts.drain(..)),
|
|
);
|
|
let stmts = Interned::from_iter(stmts.into_iter().map(|stmt| {
|
|
match stmt {
|
|
Stmt::Connect(stmt) => stmt.into(),
|
|
Stmt::Formal(stmt) => stmt.into(),
|
|
Stmt::If(StmtIf {
|
|
cond,
|
|
source_location,
|
|
blocks: if_blocks,
|
|
}) => StmtIf {
|
|
cond,
|
|
source_location,
|
|
blocks: if_blocks
|
|
.map(|block_id| finalize_block(blocks, annotations_map, block_id)),
|
|
}
|
|
.into(),
|
|
Stmt::Match(StmtMatch {
|
|
expr,
|
|
source_location,
|
|
blocks: match_blocks,
|
|
}) => StmtMatch {
|
|
expr,
|
|
source_location,
|
|
blocks: Interned::from_iter(
|
|
match_blocks
|
|
.into_iter()
|
|
.map(|block_id| finalize_block(blocks, annotations_map, block_id)),
|
|
),
|
|
}
|
|
.into(),
|
|
Stmt::Declaration(decl) => {
|
|
let annotations = annotations_map
|
|
.remove(&decl)
|
|
.map(|v| Intern::intern_owned(v))
|
|
.unwrap_or_default();
|
|
match decl {
|
|
StmtDeclaration::Wire(StmtWire {
|
|
annotations: (),
|
|
wire,
|
|
}) => StmtWire { annotations, wire }.into(),
|
|
StmtDeclaration::Reg(StmtReg {
|
|
annotations: (),
|
|
reg,
|
|
}) => StmtReg { annotations, reg }.into(),
|
|
StmtDeclaration::Instance(StmtInstance {
|
|
annotations: (),
|
|
instance,
|
|
}) => StmtInstance {
|
|
annotations,
|
|
instance,
|
|
}
|
|
.into(),
|
|
}
|
|
}
|
|
}
|
|
}));
|
|
Block { memories, stmts }
|
|
}
|
|
Self {
|
|
body: finalize_block(&mut blocks, &mut annotations_map, BlockId::ENTRY_BLOCK),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl NormalModuleBody<ModuleBuilding> {
|
|
fn block(&mut self, block_id: BlockId) -> &mut BuilderBlock {
|
|
&mut self.body.blocks[block_id.as_usize()]
|
|
}
|
|
fn new_block(&mut self) -> BlockId {
|
|
let index = self.body.blocks.len();
|
|
self.body.blocks.push(BuilderBlock {
|
|
memories: vec![],
|
|
incomplete_declarations: vec![],
|
|
stmts: vec![],
|
|
});
|
|
BlockId(index)
|
|
}
|
|
}
|
|
|
|
impl From<NormalModuleBody> for ModuleBody {
|
|
fn from(value: NormalModuleBody) -> Self {
|
|
Self::Normal(value)
|
|
}
|
|
}
|
|
|
|
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
|
|
pub struct ExternModuleBody<
|
|
P: Deref<Target = [ExternModuleParameter]> = Interned<[ExternModuleParameter]>,
|
|
> {
|
|
pub verilog_name: Interned<str>,
|
|
pub parameters: P,
|
|
}
|
|
|
|
impl From<ExternModuleBody<Vec<ExternModuleParameter>>> for ExternModuleBody {
|
|
fn from(value: ExternModuleBody<Vec<ExternModuleParameter>>) -> Self {
|
|
let ExternModuleBody {
|
|
verilog_name,
|
|
parameters,
|
|
} = value;
|
|
let parameters = Intern::intern_owned(parameters);
|
|
Self {
|
|
verilog_name,
|
|
parameters,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<ExternModuleBody> for ModuleBody {
|
|
fn from(value: ExternModuleBody) -> Self {
|
|
Self::Extern(value)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum ModuleBody<
|
|
S: ModuleBuildingStatus = ModuleBuilt,
|
|
P: Deref<Target = [ExternModuleParameter]> = Interned<[ExternModuleParameter]>,
|
|
> {
|
|
Normal(NormalModuleBody<S>),
|
|
Extern(ExternModuleBody<P>),
|
|
}
|
|
|
|
pub(crate) type ModuleBodyBuilding = ModuleBody<ModuleBuilding, Vec<ExternModuleParameter>>;
|
|
|
|
impl ModuleBodyBuilding {
|
|
pub(crate) fn builder_normal_body_opt(
|
|
&mut self,
|
|
) -> Option<&mut NormalModuleBody<ModuleBuilding>> {
|
|
if let Self::Normal(v) = self {
|
|
Some(v)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
#[track_caller]
|
|
pub(crate) fn builder_normal_body(&mut self) -> &mut NormalModuleBody<ModuleBuilding> {
|
|
if let Self::Normal(v) = self {
|
|
v
|
|
} else {
|
|
panic!("only allowed in a normal module -- must not be in an extern module")
|
|
}
|
|
}
|
|
#[track_caller]
|
|
pub(crate) fn builder_extern_body(
|
|
&mut self,
|
|
) -> &mut ExternModuleBody<Vec<ExternModuleParameter>> {
|
|
if let Self::Extern(v) = self {
|
|
v
|
|
} else {
|
|
panic!("only allowed in an extern module -- must not be in a normal module")
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<ModuleBodyBuilding> for ModuleBody {
|
|
fn from(value: ModuleBodyBuilding) -> Self {
|
|
match value {
|
|
ModuleBody::Normal(v) => Self::Normal(v.into()),
|
|
ModuleBody::Extern(v) => Self::Extern(v.into()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Hash for ModuleBody {
|
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
mem::discriminant(self).hash(state);
|
|
match self {
|
|
ModuleBody::Normal(v) => v.hash(state),
|
|
ModuleBody::Extern(v) => v.hash(state),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Eq for ModuleBody {}
|
|
|
|
impl PartialEq for ModuleBody {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
macro_rules! impl_eq {
|
|
($($Variant:ident),*) => {
|
|
match self {
|
|
$(Self::$Variant(l) => matches!(other, Self::$Variant(r) if l == r),)*
|
|
}
|
|
};
|
|
}
|
|
impl_eq!(Normal, Extern)
|
|
}
|
|
}
|
|
|
|
impl Clone for ModuleBody {
|
|
fn clone(&self) -> Self {
|
|
*self
|
|
}
|
|
}
|
|
|
|
impl Copy for ModuleBody {}
|
|
|
|
impl ModuleBody {
|
|
pub fn kind(&self) -> ModuleKind {
|
|
match self {
|
|
Self::Normal(_) => ModuleKind::Normal,
|
|
Self::Extern(_) => ModuleKind::Extern,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// The runtime representation of a Fayalite module. The preferred way to create a [`Module`] is by
|
|
/// calling a function annotated with the [`#[hdl_module]`][`crate::hdl_module`] attribute.
|
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
|
pub struct Module<T: BundleType> {
|
|
name: NameId,
|
|
source_location: SourceLocation,
|
|
body: ModuleBody,
|
|
io_ty: T,
|
|
module_io: Interned<[AnnotatedModuleIO]>,
|
|
module_annotations: Interned<[Annotation]>,
|
|
}
|
|
|
|
#[derive(Default)]
|
|
struct DebugFmtModulesState {
|
|
seen: HashSet<NameId>,
|
|
unwritten: VecDeque<(NameId, Box<dyn fmt::Debug>)>,
|
|
}
|
|
|
|
#[derive(Default)]
|
|
struct DebugFmtState {
|
|
modules_state: RefCell<Option<DebugFmtModulesState>>,
|
|
}
|
|
|
|
impl DebugFmtState {
|
|
fn with<F: FnOnce(&Self) -> R, R>(f: F) -> R {
|
|
thread_local! {
|
|
static STATE: RefCell<Option<DebugFmtState>> = RefCell::default();
|
|
}
|
|
let mut clear_on_drop = None;
|
|
struct ClearOnDrop;
|
|
impl Drop for ClearOnDrop {
|
|
fn drop(&mut self) {
|
|
STATE.with_borrow_mut(|v| *v = None);
|
|
}
|
|
}
|
|
STATE.with(|state| {
|
|
let mut read = state.borrow();
|
|
if read.is_none() {
|
|
drop(read);
|
|
clear_on_drop = Some(ClearOnDrop);
|
|
state.replace(Some(DebugFmtState::default()));
|
|
read = state.borrow();
|
|
}
|
|
f(read.as_ref().unwrap())
|
|
})
|
|
}
|
|
fn push_module_if_in_module<T: BundleType>(&self, module: Module<T>) -> bool {
|
|
DebugFmtState::with(|state| {
|
|
let mut modules_state_ref = state.modules_state.borrow_mut();
|
|
if let Some(modules_state) = &mut *modules_state_ref {
|
|
if modules_state.seen.insert(module.name) {
|
|
modules_state
|
|
.unwritten
|
|
.push_back((module.name, Box::new(DebugModuleBody(module))));
|
|
return true;
|
|
}
|
|
}
|
|
false
|
|
})
|
|
}
|
|
}
|
|
|
|
struct DebugModuleBody<T: BundleType>(Module<T>);
|
|
|
|
impl<T: BundleType> fmt::Debug for DebugModuleBody<T> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let Module {
|
|
name: _,
|
|
body,
|
|
io_ty,
|
|
module_io,
|
|
source_location: _,
|
|
module_annotations,
|
|
} = &self.0;
|
|
let mut debug_struct = f.debug_struct("Module");
|
|
let kind = body.kind();
|
|
debug_struct
|
|
.field("io_ty", io_ty)
|
|
.field("module_io", module_io)
|
|
.field("module_annotations", module_annotations)
|
|
.field("kind", &kind);
|
|
match body {
|
|
ModuleBody::Normal(NormalModuleBody { body }) => {
|
|
debug_struct.field("body", body);
|
|
}
|
|
ModuleBody::Extern(ExternModuleBody {
|
|
verilog_name,
|
|
parameters,
|
|
}) => {
|
|
debug_struct
|
|
.field("verilog_name", verilog_name)
|
|
.field("parameters", parameters);
|
|
}
|
|
}
|
|
debug_struct.finish_non_exhaustive()
|
|
}
|
|
}
|
|
|
|
pub struct ModuleDebugShort<T: BundleType>(Module<T>);
|
|
|
|
impl<T: BundleType> fmt::Debug for ModuleDebugShort<T> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
DebugFmtState::with(|state| state.push_module_if_in_module(self.0));
|
|
f.debug_struct("Module")
|
|
.field("name", &self.0.name)
|
|
.finish_non_exhaustive()
|
|
}
|
|
}
|
|
|
|
impl<T: BundleType> Module<T> {
|
|
pub fn debug_short(self) -> ModuleDebugShort<T> {
|
|
ModuleDebugShort(self)
|
|
}
|
|
}
|
|
|
|
impl<T: BundleType> fmt::Debug for Module<T> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
DebugFmtState::with(|state| {
|
|
struct Outermost<'a>(&'a DebugFmtState);
|
|
impl Drop for Outermost<'_> {
|
|
fn drop(&mut self) {
|
|
self.0.modules_state.replace(None);
|
|
}
|
|
}
|
|
let mut outermost: Option<Outermost<'_>> = None;
|
|
state.modules_state.borrow_mut().get_or_insert_with(|| {
|
|
outermost = Some(Outermost(state));
|
|
DebugFmtModulesState::default()
|
|
});
|
|
state.push_module_if_in_module(*self);
|
|
if outermost.is_some() {
|
|
let mut debug = f.debug_map();
|
|
loop {
|
|
let mut modules_state_ref = state.modules_state.borrow_mut();
|
|
let modules_state = modules_state_ref.as_mut().unwrap();
|
|
let Some((name, m)) = modules_state.unwritten.pop_front() else {
|
|
break;
|
|
};
|
|
drop(modules_state_ref);
|
|
debug.entry(&name, &m);
|
|
}
|
|
debug.finish()
|
|
} else {
|
|
self.debug_short().fmt(f)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
|
pub struct SubBlockId {
|
|
pub block: BlockId,
|
|
pub stmt_index: usize,
|
|
pub sub_stmt_index: usize,
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
|
|
enum TargetWritten {
|
|
#[default]
|
|
Unwritten,
|
|
MaybeWritten,
|
|
Written,
|
|
}
|
|
|
|
impl TargetWritten {
|
|
fn conditional_merge_written(self, other: Self) -> Self {
|
|
match (self, other) {
|
|
(Self::Written, Self::Written) => Self::Written,
|
|
(Self::MaybeWritten | Self::Written, _) | (_, Self::MaybeWritten | Self::Written) => {
|
|
Self::MaybeWritten
|
|
}
|
|
(Self::Unwritten, Self::Unwritten) => Self::Unwritten,
|
|
}
|
|
}
|
|
fn most_written(self, other: Self) -> Self {
|
|
if self as u8 > other as u8 {
|
|
self
|
|
} else {
|
|
other
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
enum TargetStateInner {
|
|
Single {
|
|
declared_in_block: usize,
|
|
written_in_blocks: RefCell<Vec<TargetWritten>>,
|
|
},
|
|
Decomposed {
|
|
subtargets: HashMap<Interned<TargetPathElement>, TargetState>,
|
|
},
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct TargetState {
|
|
target: Interned<Target>,
|
|
inner: TargetStateInner,
|
|
}
|
|
|
|
impl TargetState {
|
|
#[track_caller]
|
|
fn assert_written(&self) {
|
|
match &self.inner {
|
|
TargetStateInner::Single {
|
|
declared_in_block,
|
|
written_in_blocks,
|
|
} => {
|
|
if self.target.flow() != Flow::Source {
|
|
match written_in_blocks
|
|
.borrow()
|
|
.get(*declared_in_block)
|
|
.copied()
|
|
.unwrap_or_default()
|
|
{
|
|
TargetWritten::Unwritten => panic!(
|
|
"at {}: {} is not connected to",
|
|
self.target.base().source_location(),
|
|
self.target,
|
|
),
|
|
TargetWritten::MaybeWritten => panic!(
|
|
"at {}: {} is not always connected to",
|
|
self.target.base().source_location(),
|
|
self.target,
|
|
),
|
|
TargetWritten::Written => {}
|
|
}
|
|
}
|
|
}
|
|
TargetStateInner::Decomposed { subtargets } => {
|
|
for subtarget in subtargets.values() {
|
|
subtarget.assert_written();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
fn merge_conditional_sub_blocks_into_block(
|
|
&self,
|
|
target_block: usize,
|
|
sub_blocks: impl Clone + IntoIterator<Item = usize>,
|
|
) {
|
|
match &self.inner {
|
|
TargetStateInner::Single {
|
|
declared_in_block: _,
|
|
written_in_blocks,
|
|
} => {
|
|
let Some(written) = sub_blocks
|
|
.into_iter()
|
|
.map(|block| {
|
|
written_in_blocks
|
|
.borrow()
|
|
.get(block)
|
|
.copied()
|
|
.unwrap_or_default()
|
|
})
|
|
.reduce(TargetWritten::conditional_merge_written)
|
|
else {
|
|
unreachable!("merge_conditional_sub_blocks_into_block must be called with at least one sub-block");
|
|
};
|
|
let mut written_in_blocks = written_in_blocks.borrow_mut();
|
|
if target_block >= written_in_blocks.len() {
|
|
written_in_blocks.resize(target_block + 1, Default::default());
|
|
}
|
|
let target_written = &mut written_in_blocks[target_block];
|
|
*target_written = target_written.most_written(written);
|
|
}
|
|
TargetStateInner::Decomposed { subtargets } => {
|
|
for i in subtargets.values() {
|
|
i.merge_conditional_sub_blocks_into_block(target_block, sub_blocks.clone());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
fn new(target: Interned<Target>, declared_in_block: usize) -> Self {
|
|
Self {
|
|
target,
|
|
inner: match target.canonical_ty() {
|
|
CanonicalType::Bundle(ty) => TargetStateInner::Decomposed {
|
|
subtargets: ty
|
|
.fields()
|
|
.iter()
|
|
.map(|&field| {
|
|
let path_element = TargetPathElement::intern_sized(
|
|
TargetPathBundleField { name: field.name }.into(),
|
|
);
|
|
(
|
|
path_element,
|
|
Self::new(
|
|
Target::intern_sized(target.join(path_element)),
|
|
declared_in_block,
|
|
),
|
|
)
|
|
})
|
|
.collect(),
|
|
},
|
|
CanonicalType::Array(ty) => TargetStateInner::Decomposed {
|
|
subtargets: (0..ty.len())
|
|
.map(|index| {
|
|
let path_element =
|
|
Intern::intern_sized(TargetPathArrayElement { index }.into());
|
|
(
|
|
path_element,
|
|
Self::new(
|
|
Target::intern_sized(target.join(path_element)),
|
|
declared_in_block,
|
|
),
|
|
)
|
|
})
|
|
.collect(),
|
|
},
|
|
CanonicalType::Enum(_)
|
|
| CanonicalType::UInt(_)
|
|
| CanonicalType::SInt(_)
|
|
| CanonicalType::Bool(_)
|
|
| CanonicalType::Clock(_)
|
|
| CanonicalType::AsyncReset(_)
|
|
| CanonicalType::SyncReset(_)
|
|
| CanonicalType::Reset(_) => TargetStateInner::Single {
|
|
declared_in_block,
|
|
written_in_blocks: RefCell::default(),
|
|
},
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
struct AssertValidityState {
|
|
module: Module<Bundle>,
|
|
blocks: Vec<Block>,
|
|
target_states: HashMap<Interned<TargetBase>, TargetState>,
|
|
}
|
|
|
|
impl AssertValidityState {
|
|
fn make_block_index(&mut self, block: Block) -> usize {
|
|
let retval = self.blocks.len();
|
|
self.blocks.push(block);
|
|
retval
|
|
}
|
|
fn get_target_states<'a>(
|
|
&'a self,
|
|
target: &Target,
|
|
process_target_state: &dyn Fn(&'a TargetState, bool),
|
|
) -> Result<(), ()> {
|
|
match target {
|
|
Target::Base(target_base) => {
|
|
let target_state = self.get_base_state(*target_base)?;
|
|
process_target_state(target_state, false);
|
|
Ok(())
|
|
}
|
|
Target::Child(target_child) => self.get_target_states(
|
|
&target_child.parent(),
|
|
&|target_state, exact_target_unknown| {
|
|
let TargetStateInner::Decomposed { subtargets } = &target_state.inner else {
|
|
unreachable!(
|
|
"TargetState::new makes TargetState tree match the Target type"
|
|
);
|
|
};
|
|
match *target_child.path_element() {
|
|
TargetPathElement::BundleField(_) => process_target_state(
|
|
subtargets
|
|
.get(&target_child.path_element())
|
|
.expect("bundle fields filled in by TargetState::new"),
|
|
exact_target_unknown,
|
|
),
|
|
TargetPathElement::ArrayElement(_) => process_target_state(
|
|
subtargets
|
|
.get(&target_child.path_element())
|
|
.expect("array elements filled in by TargetState::new"),
|
|
exact_target_unknown,
|
|
),
|
|
TargetPathElement::DynArrayElement(_) => {
|
|
for target_state in subtargets.values() {
|
|
process_target_state(target_state, true);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
),
|
|
}
|
|
}
|
|
fn get_base_state(&self, target_base: Interned<TargetBase>) -> Result<&TargetState, ()> {
|
|
self.target_states.get(&target_base).ok_or(())
|
|
}
|
|
#[track_caller]
|
|
fn insert_new_base(&mut self, target_base: Interned<TargetBase>, declared_in_block: usize) {
|
|
match self.target_states.entry(target_base) {
|
|
Entry::Occupied(_) => panic!(
|
|
"at {}: duplicate declaration: {target_base}",
|
|
target_base.source_location(),
|
|
),
|
|
Entry::Vacant(entry) => {
|
|
entry.insert(TargetState::new(
|
|
Target::intern_sized(target_base.into()),
|
|
declared_in_block,
|
|
));
|
|
}
|
|
}
|
|
}
|
|
fn set_connect_target_written(
|
|
target_state: &TargetState,
|
|
is_lhs: bool,
|
|
block: usize,
|
|
exact_target_unknown: bool,
|
|
) {
|
|
match &target_state.inner {
|
|
TargetStateInner::Single {
|
|
declared_in_block: _,
|
|
written_in_blocks,
|
|
} => {
|
|
if is_lhs {
|
|
let mut written_in_blocks = written_in_blocks.borrow_mut();
|
|
if block >= written_in_blocks.len() {
|
|
written_in_blocks.resize(block + 1, Default::default());
|
|
}
|
|
let written = &mut written_in_blocks[block];
|
|
if exact_target_unknown {
|
|
*written = written.most_written(TargetWritten::MaybeWritten);
|
|
} else {
|
|
*written = TargetWritten::Written;
|
|
}
|
|
}
|
|
}
|
|
TargetStateInner::Decomposed { subtargets } => {
|
|
for (&path_element, sub_target_state) in subtargets {
|
|
match &*path_element {
|
|
&TargetPathElement::BundleField(_) => {
|
|
let field = sub_target_state
|
|
.target
|
|
.child()
|
|
.expect("known to be a child")
|
|
.bundle_field()
|
|
.expect("known to be a bundle field");
|
|
let sub_is_lhs = if field.flipped { !is_lhs } else { is_lhs };
|
|
Self::set_connect_target_written(
|
|
sub_target_state,
|
|
sub_is_lhs,
|
|
block,
|
|
exact_target_unknown,
|
|
);
|
|
}
|
|
TargetPathElement::ArrayElement { .. } => {
|
|
Self::set_connect_target_written(
|
|
sub_target_state,
|
|
is_lhs,
|
|
block,
|
|
exact_target_unknown,
|
|
);
|
|
}
|
|
TargetPathElement::DynArrayElement { .. } => {
|
|
Self::set_connect_target_written(sub_target_state, is_lhs, block, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#[track_caller]
|
|
fn set_connect_side_written(
|
|
&mut self,
|
|
expr: Expr<CanonicalType>,
|
|
source_location: SourceLocation,
|
|
is_lhs: bool,
|
|
block: usize,
|
|
) {
|
|
let Some(target) = expr.target() else {
|
|
debug_assert!(!is_lhs, "the ModuleBuilder asserts lhs.target().is_some()");
|
|
return;
|
|
};
|
|
let result = self.get_target_states(&target, &|target_state, exact_target_unknown| {
|
|
Self::set_connect_target_written(target_state, is_lhs, block, exact_target_unknown);
|
|
});
|
|
if result.is_err() {
|
|
if is_lhs {
|
|
panic!("at {source_location}: tried to connect to not-yet-defined item: {target}");
|
|
} else {
|
|
panic!(
|
|
"at {source_location}: tried to connect from not-yet-defined item: {target}"
|
|
);
|
|
}
|
|
}
|
|
}
|
|
fn process_conditional_sub_blocks(
|
|
&mut self,
|
|
parent_block: usize,
|
|
sub_blocks: impl Clone + IntoIterator<Item = usize>,
|
|
) {
|
|
for i in sub_blocks.clone() {
|
|
self.assert_subtree_validity(i);
|
|
}
|
|
for i in self.target_states.values() {
|
|
i.merge_conditional_sub_blocks_into_block(parent_block, sub_blocks.clone());
|
|
}
|
|
}
|
|
#[track_caller]
|
|
fn assert_subtree_validity(&mut self, block: usize) {
|
|
let module = self.module;
|
|
if block == 0 {
|
|
for module_io in &*module.module_io {
|
|
self.insert_new_base(
|
|
TargetBase::intern_sized(module_io.module_io.clone().into()),
|
|
block,
|
|
);
|
|
}
|
|
}
|
|
let Block { memories, stmts } = self.blocks[block];
|
|
for m in memories {
|
|
for port in m.ports() {
|
|
self.insert_new_base(TargetBase::intern_sized(port.into()), block);
|
|
}
|
|
}
|
|
for stmt in stmts {
|
|
match stmt {
|
|
Stmt::Connect(connect) => {
|
|
connect.assert_validity();
|
|
let StmtConnect {
|
|
lhs,
|
|
rhs,
|
|
source_location,
|
|
} = connect;
|
|
self.set_connect_side_written(lhs, source_location, true, block);
|
|
self.set_connect_side_written(rhs, source_location, false, block);
|
|
}
|
|
Stmt::Formal(_) => {}
|
|
Stmt::If(if_stmt) => {
|
|
let sub_blocks = if_stmt.blocks.map(|block| self.make_block_index(block));
|
|
self.process_conditional_sub_blocks(block, sub_blocks)
|
|
}
|
|
Stmt::Match(match_stmt) => {
|
|
match_stmt.assert_validity();
|
|
let sub_blocks = Vec::from_iter(
|
|
match_stmt
|
|
.blocks
|
|
.into_iter()
|
|
.map(|block| self.make_block_index(block)),
|
|
);
|
|
self.process_conditional_sub_blocks(block, sub_blocks.iter().copied())
|
|
}
|
|
Stmt::Declaration(StmtDeclaration::Wire(StmtWire {
|
|
annotations: _,
|
|
wire,
|
|
})) => self.insert_new_base(TargetBase::intern_sized(wire.into()), block),
|
|
Stmt::Declaration(StmtDeclaration::Reg(StmtReg {
|
|
annotations: _,
|
|
reg,
|
|
})) => self.insert_new_base(TargetBase::intern_sized(reg.into()), block),
|
|
Stmt::Declaration(StmtDeclaration::Instance(StmtInstance {
|
|
annotations: _,
|
|
instance,
|
|
})) => self.insert_new_base(TargetBase::intern_sized(instance.into()), block),
|
|
}
|
|
}
|
|
}
|
|
#[track_caller]
|
|
fn assert_validity(&mut self) {
|
|
match self.module.body {
|
|
ModuleBody::Extern(ExternModuleBody {
|
|
verilog_name: _,
|
|
parameters: _,
|
|
}) => {}
|
|
ModuleBody::Normal(NormalModuleBody { body }) => {
|
|
let body = self.make_block_index(body);
|
|
assert_eq!(body, 0);
|
|
self.assert_subtree_validity(body);
|
|
for (base, state) in &self.target_states {
|
|
if base.must_connect_to() {
|
|
state.assert_written();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T: BundleType> Module<T> {
|
|
/// you generally should use the [`#[hdl_module]`][`crate::hdl_module`] proc-macro and [`ModuleBuilder`] instead
|
|
#[track_caller]
|
|
pub fn new_unchecked(
|
|
name_id: NameId,
|
|
source_location: SourceLocation,
|
|
body: ModuleBody,
|
|
module_io: impl IntoIterator<Item = AnnotatedModuleIO>,
|
|
module_annotations: impl IntoAnnotations,
|
|
) -> Module<T> {
|
|
let module_io: Interned<[_]> = module_io.into_iter().collect();
|
|
let module_annotations = module_annotations.into_annotations().into_iter().collect();
|
|
let retval = Module {
|
|
name: name_id,
|
|
source_location,
|
|
body,
|
|
io_ty: T::from_canonical(CanonicalType::Bundle(Bundle::new(Intern::intern_owned(
|
|
Vec::from_iter(module_io.iter().map(|io| io.module_io.bundle_field)),
|
|
)))),
|
|
module_io,
|
|
module_annotations,
|
|
};
|
|
retval.assert_validity();
|
|
retval
|
|
}
|
|
pub fn canonical(self) -> Module<Bundle> {
|
|
Module {
|
|
name: self.name,
|
|
body: self.body,
|
|
io_ty: Bundle::new(self.io_ty.fields()),
|
|
module_io: self.module_io,
|
|
source_location: self.source_location,
|
|
module_annotations: self.module_annotations,
|
|
}
|
|
}
|
|
pub fn from_canonical(canonical_module: Module<Bundle>) -> Self {
|
|
Module {
|
|
name: canonical_module.name,
|
|
body: canonical_module.body,
|
|
io_ty: T::from_canonical(CanonicalType::Bundle(canonical_module.io_ty)),
|
|
module_io: canonical_module.module_io,
|
|
source_location: canonical_module.source_location,
|
|
module_annotations: canonical_module.module_annotations,
|
|
}
|
|
}
|
|
pub fn top_block(self) -> Option<Block> {
|
|
match self.body {
|
|
ModuleBody::Normal(NormalModuleBody { body }) => Some(body),
|
|
ModuleBody::Extern(_) => None,
|
|
}
|
|
}
|
|
pub fn kind(self) -> ModuleKind {
|
|
self.body.kind()
|
|
}
|
|
pub fn body(self) -> ModuleBody {
|
|
self.body
|
|
}
|
|
pub fn io_ty(self) -> T {
|
|
self.io_ty
|
|
}
|
|
pub fn module_io(self) -> Interned<[AnnotatedModuleIO]> {
|
|
self.module_io
|
|
}
|
|
pub fn name(self) -> Interned<str> {
|
|
self.name_id().0
|
|
}
|
|
pub fn name_id(self) -> NameId {
|
|
self.name
|
|
}
|
|
pub fn source_location(self) -> SourceLocation {
|
|
self.source_location
|
|
}
|
|
pub fn module_annotations(self) -> Interned<[Annotation]> {
|
|
self.module_annotations
|
|
}
|
|
#[track_caller]
|
|
pub fn assert_validity(self) {
|
|
AssertValidityState {
|
|
module: self.canonical(),
|
|
blocks: vec![],
|
|
target_states: HashMap::with_capacity(64),
|
|
}
|
|
.assert_validity();
|
|
}
|
|
}
|
|
|
|
#[must_use = "does nothing unless `build` is called"]
|
|
pub struct RegBuilder<CD, I, T: Type> {
|
|
name: Interned<str>,
|
|
source_location: SourceLocation,
|
|
clock_domain: CD,
|
|
init: I,
|
|
ty: T,
|
|
}
|
|
|
|
impl<CD> RegBuilder<CD, (), ()> {
|
|
pub fn no_reset<T: Type>(self, ty: T) -> RegBuilder<CD, Option<Expr<T>>, T> {
|
|
let Self {
|
|
name,
|
|
source_location,
|
|
clock_domain,
|
|
init: _,
|
|
ty: _,
|
|
} = self;
|
|
RegBuilder {
|
|
name,
|
|
source_location,
|
|
clock_domain,
|
|
init: None,
|
|
ty,
|
|
}
|
|
}
|
|
pub fn opt_reset<T: Type>(
|
|
self,
|
|
ty: T,
|
|
init: Option<impl ToExpr<Type = T>>,
|
|
) -> RegBuilder<CD, Option<Expr<T>>, T> {
|
|
let Self {
|
|
name,
|
|
source_location,
|
|
clock_domain,
|
|
init: _,
|
|
ty: _,
|
|
} = self;
|
|
RegBuilder {
|
|
name,
|
|
source_location,
|
|
clock_domain,
|
|
init: init.map(|e| e.to_expr()),
|
|
ty,
|
|
}
|
|
}
|
|
pub fn reset<T: Type>(self, init: impl ToExpr<Type = T>) -> RegBuilder<CD, Option<Expr<T>>, T> {
|
|
let init = init.to_expr();
|
|
let Self {
|
|
name,
|
|
source_location,
|
|
clock_domain,
|
|
init: _,
|
|
ty: _,
|
|
} = self;
|
|
let ty = Expr::ty(init);
|
|
RegBuilder {
|
|
name,
|
|
source_location,
|
|
clock_domain,
|
|
init: Some(init),
|
|
ty,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<I, T: Type> RegBuilder<(), I, T> {
|
|
pub fn clock_domain(
|
|
self,
|
|
clock_domain: impl ToExpr<Type = ClockDomain>,
|
|
) -> RegBuilder<Expr<ClockDomain>, I, T> {
|
|
let Self {
|
|
name,
|
|
source_location,
|
|
clock_domain: _,
|
|
init,
|
|
ty,
|
|
} = self;
|
|
RegBuilder {
|
|
name,
|
|
source_location,
|
|
clock_domain: clock_domain.to_expr(),
|
|
init,
|
|
ty,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T: Type> RegBuilder<Expr<ClockDomain>, Option<Expr<T>>, T> {
|
|
#[track_caller]
|
|
pub fn build(self) -> Expr<T> {
|
|
let Self {
|
|
name,
|
|
source_location,
|
|
clock_domain,
|
|
init,
|
|
ty,
|
|
} = self;
|
|
ModuleBuilder::with(|module_builder| {
|
|
let mut impl_ = module_builder.impl_.borrow_mut();
|
|
let scoped_name = ScopedNameId(module_builder.name, impl_.name_id_gen.gen(name));
|
|
drop(impl_);
|
|
let reg = Reg::new_unchecked(scoped_name, source_location, ty, clock_domain, init);
|
|
let retval = reg.to_expr();
|
|
// convert before borrow_mut since ModuleBuilder could be reentered by T::canonical()
|
|
let canonical_reg = reg.canonical();
|
|
module_builder
|
|
.impl_
|
|
.borrow_mut()
|
|
.body
|
|
.builder_normal_body()
|
|
.block(module_builder.block_stack.top())
|
|
.stmts
|
|
.push(
|
|
StmtReg {
|
|
annotations: (),
|
|
reg: canonical_reg,
|
|
}
|
|
.into(),
|
|
);
|
|
retval
|
|
})
|
|
}
|
|
}
|
|
|
|
thread_local! {
|
|
static MODULE_BUILDER: ScopedRef<ModuleBuilder> = const { ScopedRef::new() };
|
|
}
|
|
|
|
impl ModuleBuilder {
|
|
#[track_caller]
|
|
pub(crate) fn with<F: FnOnce(&Self) -> R, R>(f: F) -> R {
|
|
MODULE_BUILDER.with(|v| {
|
|
v.with_opt(
|
|
|v| f(v.expect("must be inside a #[hdl_module] fn that's currently running")),
|
|
)
|
|
})
|
|
}
|
|
pub(crate) fn set<F: FnOnce() -> R, R>(&self, f: F) -> R {
|
|
MODULE_BUILDER.with(|v| v.set(self, f))
|
|
}
|
|
#[track_caller]
|
|
pub fn io_with_loc<IO: Type>(
|
|
&self,
|
|
name: &str,
|
|
source_location: SourceLocation,
|
|
is_input: bool,
|
|
ty: IO,
|
|
) -> Expr<IO> {
|
|
let module_io =
|
|
ModuleIO::<IO>::new_unchecked(self.name, name.intern(), source_location, is_input, ty);
|
|
let retval = module_io.to_expr();
|
|
let module_io = module_io.canonical();
|
|
let mut impl_ = self.impl_.borrow_mut();
|
|
let impl_ = &mut *impl_;
|
|
impl_.io_indexes.insert(module_io.clone(), impl_.io.len());
|
|
impl_.io.push(AnnotatedModuleIO {
|
|
annotations: vec![],
|
|
module_io,
|
|
});
|
|
retval
|
|
}
|
|
#[track_caller]
|
|
pub fn io<IO: Type>(
|
|
&self,
|
|
implicit_name: ImplicitName<'_>,
|
|
is_input: bool,
|
|
ty: IO,
|
|
) -> Expr<IO> {
|
|
self.io_with_loc(implicit_name.0, SourceLocation::caller(), is_input, ty)
|
|
}
|
|
#[track_caller]
|
|
pub fn input_with_loc<IO: Type>(
|
|
&self,
|
|
name: &str,
|
|
source_location: SourceLocation,
|
|
ty: IO,
|
|
) -> Expr<IO> {
|
|
self.io_with_loc(name, source_location, true, ty)
|
|
}
|
|
#[track_caller]
|
|
pub fn output_with_loc<IO: Type>(
|
|
&self,
|
|
name: &str,
|
|
source_location: SourceLocation,
|
|
ty: IO,
|
|
) -> Expr<IO> {
|
|
self.io_with_loc(name, source_location, false, ty)
|
|
}
|
|
#[track_caller]
|
|
pub fn input<IO: Type>(&self, implicit_name: ImplicitName<'_>, ty: IO) -> Expr<IO> {
|
|
self.input_with_loc(implicit_name.0, SourceLocation::caller(), ty)
|
|
}
|
|
#[track_caller]
|
|
pub fn output<IO: Type>(&self, implicit_name: ImplicitName<'_>, ty: IO) -> Expr<IO> {
|
|
self.output_with_loc(implicit_name.0, SourceLocation::caller(), ty)
|
|
}
|
|
#[track_caller]
|
|
pub fn run<T: BundleType>(
|
|
name: &str,
|
|
module_kind: ModuleKind,
|
|
f: impl FnOnce(&Self),
|
|
) -> Interned<Module<T>> {
|
|
Self::run_with_loc(name, SourceLocation::caller(), module_kind, f)
|
|
}
|
|
#[track_caller]
|
|
pub fn try_run<T: BundleType, E>(
|
|
name: &str,
|
|
module_kind: ModuleKind,
|
|
f: impl FnOnce(&Self) -> Result<(), E>,
|
|
) -> Result<Interned<Module<T>>, E> {
|
|
Self::try_run_with_loc(name, SourceLocation::caller(), module_kind, f)
|
|
}
|
|
#[track_caller]
|
|
pub fn run_with_loc<T: BundleType>(
|
|
name: &str,
|
|
source_location: SourceLocation,
|
|
module_kind: ModuleKind,
|
|
f: impl FnOnce(&Self),
|
|
) -> Interned<Module<T>> {
|
|
Self::try_run_with_loc(
|
|
name,
|
|
source_location,
|
|
module_kind,
|
|
|m| -> Result<(), Infallible> {
|
|
f(m);
|
|
Ok(())
|
|
},
|
|
)
|
|
.unwrap()
|
|
}
|
|
#[track_caller]
|
|
pub fn try_run_with_loc<T: BundleType, E>(
|
|
name: &str,
|
|
source_location: SourceLocation,
|
|
module_kind: ModuleKind,
|
|
f: impl FnOnce(&Self) -> Result<(), E>,
|
|
) -> Result<Interned<Module<T>>, E> {
|
|
let name = NameIdGen::gen_module_name(name.intern());
|
|
let body = match module_kind {
|
|
ModuleKind::Extern => ModuleBody::Extern(ExternModuleBody {
|
|
verilog_name: name.0,
|
|
parameters: vec![],
|
|
}),
|
|
ModuleKind::Normal => ModuleBody::Normal(NormalModuleBody {
|
|
body: BuilderModuleBody {
|
|
blocks: vec![BuilderBlock {
|
|
memories: vec![],
|
|
incomplete_declarations: vec![],
|
|
stmts: vec![],
|
|
}],
|
|
annotations_map: HashMap::new(),
|
|
memory_map: HashMap::new(),
|
|
},
|
|
}),
|
|
};
|
|
let builder = Self {
|
|
name,
|
|
block_stack: Rc::new(BlockStack::new(BlockId(0))),
|
|
impl_: RefCell::new(ModuleBuilderImpl {
|
|
body,
|
|
io: vec![],
|
|
io_indexes: HashMap::new(),
|
|
name_id_gen: NameIdGen::default(),
|
|
module_annotations: vec![],
|
|
}),
|
|
};
|
|
builder.set(|| f(&builder))?;
|
|
let impl_ = builder.impl_.into_inner();
|
|
Ok(Module::new_unchecked(
|
|
builder.name,
|
|
source_location,
|
|
impl_.body.into(),
|
|
impl_.io.into_iter().map(
|
|
|AnnotatedModuleIO {
|
|
annotations,
|
|
module_io,
|
|
}| AnnotatedModuleIO {
|
|
annotations: Intern::intern_owned(annotations),
|
|
module_io,
|
|
},
|
|
),
|
|
impl_.module_annotations,
|
|
)
|
|
.intern_sized())
|
|
}
|
|
#[track_caller]
|
|
pub fn annotate_module(&self, annotations: impl IntoAnnotations) {
|
|
// run before borrow_mut because into_annotations can call back into ModuleBuilder
|
|
let annotations = Vec::from_iter(annotations.into_annotations());
|
|
self.impl_
|
|
.borrow_mut()
|
|
.module_annotations
|
|
.extend(annotations);
|
|
}
|
|
#[track_caller]
|
|
pub fn verilog_name(&self, name: impl AsRef<str>) {
|
|
let name = name.as_ref();
|
|
assert!(
|
|
name.chars()
|
|
.all(|ch| ch.is_ascii_alphanumeric() || ch == '_'),
|
|
"invalid verilog name -- FIRRTL doesn't yet support verilog names with special characters.\n\
|
|
see https://github.com/chipsalliance/firrtl-spec/issues/134"
|
|
);
|
|
self.impl_
|
|
.borrow_mut()
|
|
.body
|
|
.builder_extern_body()
|
|
.verilog_name = name.intern();
|
|
}
|
|
pub fn parameter(&self, name: impl AsRef<str>, value: ExternModuleParameterValue) {
|
|
let name = name.as_ref();
|
|
self.impl_
|
|
.borrow_mut()
|
|
.body
|
|
.builder_extern_body()
|
|
.parameters
|
|
.push(ExternModuleParameter {
|
|
name: name.intern(),
|
|
value,
|
|
});
|
|
}
|
|
pub fn parameter_int(&self, name: impl AsRef<str>, value: impl Into<BigInt>) {
|
|
let name = name.as_ref();
|
|
let value = value.into();
|
|
self.impl_
|
|
.borrow_mut()
|
|
.body
|
|
.builder_extern_body()
|
|
.parameters
|
|
.push(ExternModuleParameter {
|
|
name: name.intern(),
|
|
value: ExternModuleParameterValue::Integer(value),
|
|
});
|
|
}
|
|
pub fn parameter_str(&self, name: impl AsRef<str>, value: impl AsRef<str>) {
|
|
let name = name.as_ref();
|
|
let value = value.as_ref();
|
|
self.impl_
|
|
.borrow_mut()
|
|
.body
|
|
.builder_extern_body()
|
|
.parameters
|
|
.push(ExternModuleParameter {
|
|
name: name.intern(),
|
|
value: ExternModuleParameterValue::String(value.intern()),
|
|
});
|
|
}
|
|
pub fn parameter_raw_verilog(&self, name: impl AsRef<str>, raw_verilog: impl AsRef<str>) {
|
|
let name = name.as_ref();
|
|
let raw_verilog = raw_verilog.as_ref();
|
|
self.impl_
|
|
.borrow_mut()
|
|
.body
|
|
.builder_extern_body()
|
|
.parameters
|
|
.push(ExternModuleParameter {
|
|
name: name.intern(),
|
|
value: ExternModuleParameterValue::RawVerilog(raw_verilog.intern()),
|
|
});
|
|
}
|
|
}
|
|
|
|
#[track_caller]
|
|
pub fn annotate<T: Type>(target: Expr<T>, annotations: impl IntoAnnotations) {
|
|
macro_rules! unwrap {
|
|
($v:expr) => {
|
|
match $v {
|
|
Some(v) => v,
|
|
None => panic!("not a valid annotation target"),
|
|
}
|
|
};
|
|
}
|
|
let target = unwrap!(target.target());
|
|
TargetedAnnotation::assert_valid_target(target);
|
|
// run before borrow_mut because into_annotations can call back into ModuleBuilder
|
|
let annotations = Vec::from_iter(
|
|
annotations
|
|
.into_annotations()
|
|
.into_iter()
|
|
.map(|annotation| TargetedAnnotation::new(target, annotation)),
|
|
);
|
|
let decl = match TargetBase::clone(&target.base()) {
|
|
TargetBase::ModuleIO(v) => {
|
|
ModuleBuilder::with(|m| {
|
|
let mut impl_ = m.impl_.borrow_mut();
|
|
let io_index = *unwrap!(impl_.io_indexes.get(&v));
|
|
impl_.io[io_index].annotations.extend(annotations);
|
|
});
|
|
return;
|
|
}
|
|
TargetBase::MemPort(v) => {
|
|
ModuleBuilder::with(|m| {
|
|
RefCell::borrow_mut(unwrap!(unwrap!(m
|
|
.impl_
|
|
.borrow_mut()
|
|
.body
|
|
.builder_normal_body_opt())
|
|
.body
|
|
.memory_map
|
|
.get_mut(&v.mem_name())))
|
|
.port_annotations
|
|
.extend(annotations)
|
|
});
|
|
return;
|
|
}
|
|
TargetBase::Reg(reg) => StmtReg {
|
|
annotations: (),
|
|
reg,
|
|
}
|
|
.into(),
|
|
TargetBase::Wire(wire) => StmtWire {
|
|
annotations: (),
|
|
wire,
|
|
}
|
|
.into(),
|
|
TargetBase::Instance(instance) => StmtInstance {
|
|
annotations: (),
|
|
instance,
|
|
}
|
|
.into(),
|
|
};
|
|
ModuleBuilder::with(|m| {
|
|
unwrap!(m.impl_.borrow_mut().body.builder_normal_body_opt())
|
|
.body
|
|
.annotations_map
|
|
.entry(decl)
|
|
.or_default();
|
|
});
|
|
}
|
|
|
|
#[track_caller]
|
|
pub fn wire_with_loc<T: Type>(name: &str, source_location: SourceLocation, ty: T) -> Expr<T> {
|
|
ModuleBuilder::with(|m| {
|
|
let mut impl_ = m.impl_.borrow_mut();
|
|
let scoped_name = ScopedNameId(m.name, impl_.name_id_gen.gen(name.intern()));
|
|
drop(impl_);
|
|
let wire = Wire::<T>::new_unchecked(scoped_name, source_location, ty);
|
|
let retval = wire.to_expr();
|
|
let canonical_wire = wire.canonical();
|
|
let mut impl_ = m.impl_.borrow_mut();
|
|
impl_
|
|
.body
|
|
.builder_normal_body()
|
|
.block(m.block_stack.top())
|
|
.stmts
|
|
.push(
|
|
StmtWire {
|
|
annotations: (),
|
|
wire: canonical_wire,
|
|
}
|
|
.into(),
|
|
);
|
|
retval
|
|
})
|
|
}
|
|
|
|
#[track_caller]
|
|
pub fn wire<T: Type>(implicit_name: ImplicitName<'_>, ty: T) -> Expr<T> {
|
|
wire_with_loc(implicit_name.0, SourceLocation::caller(), ty)
|
|
}
|
|
|
|
#[track_caller]
|
|
fn incomplete_declaration(
|
|
name: &str,
|
|
source_location: SourceLocation,
|
|
) -> Rc<RefCell<IncompleteDeclaration>> {
|
|
ModuleBuilder::with(|m| {
|
|
let mut impl_ = m.impl_.borrow_mut();
|
|
let scoped_name = ScopedNameId(m.name, impl_.name_id_gen.gen(name.intern()));
|
|
drop(impl_);
|
|
let retval = Rc::new(RefCell::new(IncompleteDeclaration::Incomplete {
|
|
name: scoped_name,
|
|
source_location,
|
|
}));
|
|
let mut impl_ = m.impl_.borrow_mut();
|
|
impl_
|
|
.body
|
|
.builder_normal_body()
|
|
.block(m.block_stack.top())
|
|
.incomplete_declarations
|
|
.push(retval.clone());
|
|
retval
|
|
})
|
|
}
|
|
|
|
#[track_caller]
|
|
pub fn incomplete_wire_with_loc(name: &str, source_location: SourceLocation) -> IncompleteWire {
|
|
IncompleteWire {
|
|
declaration: incomplete_declaration(name, source_location),
|
|
}
|
|
}
|
|
|
|
#[track_caller]
|
|
pub fn incomplete_wire(implicit_name: ImplicitName<'_>) -> IncompleteWire {
|
|
incomplete_wire_with_loc(implicit_name.0, SourceLocation::caller())
|
|
}
|
|
|
|
#[track_caller]
|
|
pub fn reg_builder_with_loc(name: &str, source_location: SourceLocation) -> RegBuilder<(), (), ()> {
|
|
ModuleBuilder::with(|m| {
|
|
m.impl_.borrow_mut().body.builder_normal_body();
|
|
});
|
|
RegBuilder {
|
|
name: name.intern(),
|
|
source_location,
|
|
clock_domain: (),
|
|
init: (),
|
|
ty: (),
|
|
}
|
|
}
|
|
|
|
#[track_caller]
|
|
pub fn reg_builder(implicit_name: ImplicitName<'_>) -> RegBuilder<(), (), ()> {
|
|
reg_builder_with_loc(implicit_name.0, SourceLocation::caller())
|
|
}
|
|
|
|
/// intentionally takes Expr instead of impl ToExpr to help prevent
|
|
/// `#[hdl] if a == b {}` instead of using `cmp_eq`
|
|
#[track_caller]
|
|
pub fn if_(cond: Expr<Bool>) -> IfScope {
|
|
if_with_loc(cond, SourceLocation::caller())
|
|
}
|
|
|
|
#[track_caller]
|
|
pub fn if_with_loc(cond: Expr<Bool>, source_location: SourceLocation) -> IfScope {
|
|
ModuleBuilder::with(|m| {
|
|
let outer_block = m.block_stack.top();
|
|
let mut impl_ = m.impl_.borrow_mut();
|
|
let body = impl_.body.builder_normal_body();
|
|
let then_block = body.new_block();
|
|
let else_block = body.new_block();
|
|
body.block(outer_block).stmts.push(
|
|
StmtIf {
|
|
cond,
|
|
source_location,
|
|
blocks: [then_block, else_block],
|
|
}
|
|
.into(),
|
|
);
|
|
IfScope {
|
|
then: m.block_stack.clone().push_scope(then_block),
|
|
else_block,
|
|
}
|
|
})
|
|
}
|
|
|
|
#[track_caller]
|
|
pub fn enum_match_variants_helper<T: EnumType>(
|
|
base: Expr<T>,
|
|
source_location: SourceLocation,
|
|
) -> EnumMatchVariantsIter<T> {
|
|
let base = Expr::as_enum(base);
|
|
ModuleBuilder::with(|m| {
|
|
let mut impl_ = m.impl_.borrow_mut();
|
|
let body = impl_.body.builder_normal_body();
|
|
let enum_ty = Expr::ty(base);
|
|
let outer_block: BlockId = m.block_stack.top();
|
|
let blocks = Interned::from_iter(enum_ty.variants().iter().map(|_| body.new_block()));
|
|
body.block(outer_block).stmts.push(
|
|
StmtMatch {
|
|
expr: base,
|
|
source_location,
|
|
blocks,
|
|
}
|
|
.into(),
|
|
);
|
|
let block_stack = m.block_stack.clone();
|
|
EnumMatchVariantsIter {
|
|
inner: EnumMatchVariantsIterImpl {
|
|
block_stack,
|
|
blocks,
|
|
base,
|
|
_phantom: PhantomData,
|
|
},
|
|
variant_index: 0..blocks.len(),
|
|
}
|
|
})
|
|
}
|
|
|
|
#[track_caller]
|
|
pub fn match_<T: Type>(expr: impl ToExpr<Type = T>) -> T::MatchVariantsIter {
|
|
match_with_loc(expr, SourceLocation::caller())
|
|
}
|
|
|
|
#[track_caller]
|
|
pub fn match_with_loc<T: Type>(
|
|
expr: impl ToExpr<Type = T>,
|
|
source_location: SourceLocation,
|
|
) -> T::MatchVariantsIter {
|
|
T::match_variants(expr.to_expr(), source_location)
|
|
}
|
|
|
|
#[track_caller]
|
|
pub fn formal_with_enable_and_loc(
|
|
kind: StmtFormalKind,
|
|
clk: Expr<Clock>,
|
|
pred: Expr<Bool>,
|
|
en: Expr<Bool>,
|
|
text: &str,
|
|
source_location: SourceLocation,
|
|
) {
|
|
ModuleBuilder::with(|m| {
|
|
m.impl_
|
|
.borrow_mut()
|
|
.body
|
|
.builder_normal_body()
|
|
.block(m.block_stack.top())
|
|
.stmts
|
|
.push(
|
|
StmtFormal {
|
|
kind,
|
|
clk,
|
|
pred,
|
|
en,
|
|
text: text.intern(),
|
|
source_location,
|
|
}
|
|
.into(),
|
|
);
|
|
});
|
|
}
|
|
|
|
#[track_caller]
|
|
pub fn formal_with_enable(
|
|
kind: StmtFormalKind,
|
|
clk: Expr<Clock>,
|
|
pred: Expr<Bool>,
|
|
en: Expr<Bool>,
|
|
text: &str,
|
|
) {
|
|
formal_with_enable_and_loc(kind, clk, pred, en, text, SourceLocation::caller());
|
|
}
|
|
|
|
#[track_caller]
|
|
pub fn formal_with_loc(
|
|
kind: StmtFormalKind,
|
|
clk: Expr<Clock>,
|
|
pred: Expr<Bool>,
|
|
text: &str,
|
|
source_location: SourceLocation,
|
|
) {
|
|
formal_with_enable_and_loc(kind, clk, pred, true.to_expr(), text, source_location);
|
|
}
|
|
|
|
#[track_caller]
|
|
pub fn formal(kind: StmtFormalKind, clk: Expr<Clock>, pred: Expr<Bool>, text: &str) {
|
|
formal_with_loc(kind, clk, pred, text, SourceLocation::caller());
|
|
}
|
|
|
|
macro_rules! make_formal {
|
|
($kind:ident, $formal_with_enable_and_loc:ident, $formal_with_enable:ident, $formal_with_loc:ident, $formal:ident) => {
|
|
#[track_caller]
|
|
pub fn $formal_with_enable_and_loc(
|
|
clk: Expr<Clock>,
|
|
pred: Expr<Bool>,
|
|
en: Expr<Bool>,
|
|
text: &str,
|
|
source_location: SourceLocation,
|
|
) {
|
|
formal_with_enable_and_loc(StmtFormalKind::$kind, clk, pred, en, text, source_location);
|
|
}
|
|
#[track_caller]
|
|
pub fn $formal_with_enable(clk: Expr<Clock>, pred: Expr<Bool>, en: Expr<Bool>, text: &str) {
|
|
formal_with_enable(StmtFormalKind::$kind, clk, pred, en, text);
|
|
}
|
|
#[track_caller]
|
|
pub fn $formal_with_loc(
|
|
clk: Expr<Clock>,
|
|
pred: Expr<Bool>,
|
|
text: &str,
|
|
source_location: SourceLocation,
|
|
) {
|
|
formal_with_loc(StmtFormalKind::$kind, clk, pred, text, source_location);
|
|
}
|
|
#[track_caller]
|
|
pub fn $formal(clk: Expr<Clock>, pred: Expr<Bool>, text: &str) {
|
|
formal(StmtFormalKind::$kind, clk, pred, text);
|
|
}
|
|
};
|
|
}
|
|
|
|
make_formal!(
|
|
Assert,
|
|
hdl_assert_with_enable_and_loc,
|
|
hdl_assert_with_enable,
|
|
hdl_assert_with_loc,
|
|
hdl_assert
|
|
);
|
|
|
|
make_formal!(
|
|
Assume,
|
|
hdl_assume_with_enable_and_loc,
|
|
hdl_assume_with_enable,
|
|
hdl_assume_with_loc,
|
|
hdl_assume
|
|
);
|
|
|
|
make_formal!(
|
|
Cover,
|
|
hdl_cover_with_enable_and_loc,
|
|
hdl_cover_with_enable,
|
|
hdl_cover_with_loc,
|
|
hdl_cover
|
|
);
|
|
|
|
#[track_caller]
|
|
pub fn connect_any_with_loc<Lhs: ToExpr, Rhs: ToExpr>(
|
|
lhs: Lhs,
|
|
rhs: Rhs,
|
|
source_location: SourceLocation,
|
|
) {
|
|
let lhs_orig = lhs.to_expr();
|
|
let rhs_orig = rhs.to_expr();
|
|
let lhs = Expr::canonical(lhs_orig);
|
|
let rhs = Expr::canonical(rhs_orig);
|
|
let connect = StmtConnect {
|
|
lhs,
|
|
rhs,
|
|
source_location,
|
|
};
|
|
connect.assert_validity_with_original_types(Expr::ty(lhs_orig), Expr::ty(rhs_orig));
|
|
ModuleBuilder::with(|m| {
|
|
m.impl_
|
|
.borrow_mut()
|
|
.body
|
|
.builder_normal_body()
|
|
.block(m.block_stack.top())
|
|
.stmts
|
|
.push(connect.into());
|
|
});
|
|
}
|
|
|
|
#[track_caller]
|
|
pub fn connect_any<Lhs: ToExpr, Rhs: ToExpr>(lhs: Lhs, rhs: Rhs) {
|
|
connect_any_with_loc(lhs, rhs, SourceLocation::caller())
|
|
}
|
|
|
|
#[track_caller]
|
|
pub fn connect<Lhs: ToExpr, Rhs: ToExpr<Type = Lhs::Type>>(lhs: Lhs, rhs: Rhs) {
|
|
connect_with_loc(lhs.to_expr(), rhs.to_expr(), SourceLocation::caller())
|
|
}
|
|
|
|
#[track_caller]
|
|
pub fn connect_with_loc<Lhs: ToExpr, Rhs: ToExpr<Type = Lhs::Type>>(
|
|
lhs: Lhs,
|
|
rhs: Rhs,
|
|
source_location: SourceLocation,
|
|
) {
|
|
connect_any_with_loc(lhs.to_expr(), rhs.to_expr(), source_location)
|
|
}
|
|
|
|
#[track_caller]
|
|
pub fn instance_with_loc<T: BundleType>(
|
|
name: &str,
|
|
instantiated: Interned<Module<T>>,
|
|
source_location: SourceLocation,
|
|
) -> Expr<T> {
|
|
ModuleBuilder::with(|m| {
|
|
let mut impl_ = m.impl_.borrow_mut();
|
|
let scoped_name = ScopedNameId(m.name, impl_.name_id_gen.gen(name.intern()));
|
|
drop(impl_);
|
|
let instance = Instance::<T> {
|
|
scoped_name,
|
|
instantiated,
|
|
source_location,
|
|
};
|
|
let canonical_instance = instance.canonical();
|
|
m.impl_
|
|
.borrow_mut()
|
|
.body
|
|
.builder_normal_body()
|
|
.block(m.block_stack.top())
|
|
.stmts
|
|
.push(
|
|
StmtInstance {
|
|
annotations: (),
|
|
instance: canonical_instance,
|
|
}
|
|
.into(),
|
|
);
|
|
instance.to_expr()
|
|
})
|
|
}
|
|
|
|
#[track_caller]
|
|
pub fn instance<T: BundleType>(
|
|
implicit_name: ImplicitName<'_>,
|
|
instantiated: Interned<Module<T>>,
|
|
) -> Expr<T> {
|
|
instance_with_loc(implicit_name.0, instantiated, SourceLocation::caller())
|
|
}
|
|
|
|
#[track_caller]
|
|
fn memory_impl<Element: Type, Len: Size>(
|
|
name: &str,
|
|
mem_element_type: Element,
|
|
source_location: SourceLocation,
|
|
) -> MemBuilder<Element, Len> {
|
|
ModuleBuilder::with(|m| {
|
|
let mut impl_ = m.impl_.borrow_mut();
|
|
let scoped_name = ScopedNameId(m.name, impl_.name_id_gen.gen(name.intern()));
|
|
drop(impl_);
|
|
let (retval, target_mem) = MemBuilder::new(scoped_name, source_location, mem_element_type);
|
|
let mut impl_ = m.impl_.borrow_mut();
|
|
let body = impl_.body.builder_normal_body();
|
|
body.block(m.block_stack.top())
|
|
.memories
|
|
.push(target_mem.clone());
|
|
body.body.memory_map.insert(scoped_name, target_mem);
|
|
retval
|
|
})
|
|
}
|
|
|
|
#[track_caller]
|
|
pub fn memory_array_with_loc<Element: Type, Len: Size>(
|
|
name: &str,
|
|
mem_array_type: ArrayType<Element, Len>,
|
|
source_location: SourceLocation,
|
|
) -> MemBuilder<Element, Len> {
|
|
let mut retval = memory_impl(name, mem_array_type.element().clone(), source_location);
|
|
retval.depth(mem_array_type.len());
|
|
retval
|
|
}
|
|
|
|
#[track_caller]
|
|
pub fn memory_array<Element: Type, Len: Size>(
|
|
implicit_name: ImplicitName<'_>,
|
|
mem_array_type: ArrayType<Element, Len>,
|
|
) -> MemBuilder<Element, Len> {
|
|
memory_array_with_loc(implicit_name.0, mem_array_type, SourceLocation::caller())
|
|
}
|
|
|
|
#[track_caller]
|
|
pub fn memory_with_init_and_loc<Element: Type, Len: Size>(
|
|
name: &str,
|
|
initial_value: impl ToExpr<Type = ArrayType<Element, Len>>,
|
|
source_location: SourceLocation,
|
|
) -> MemBuilder<Element, Len> {
|
|
let initial_value = initial_value.to_expr();
|
|
let mut retval = memory_array_with_loc(name, Expr::ty(initial_value), source_location);
|
|
retval.initial_value(initial_value);
|
|
retval
|
|
}
|
|
|
|
#[track_caller]
|
|
pub fn memory_with_init<Element: Type, Len: Size>(
|
|
implicit_name: ImplicitName<'_>,
|
|
initial_value: impl ToExpr<Type = ArrayType<Element, Len>>,
|
|
) -> MemBuilder<Element, Len> {
|
|
memory_with_init_and_loc(
|
|
implicit_name.0,
|
|
initial_value.to_expr(),
|
|
SourceLocation::caller(),
|
|
)
|
|
}
|
|
|
|
#[track_caller]
|
|
pub fn memory_with_loc<Element: Type>(
|
|
name: &str,
|
|
element_type: Element,
|
|
source_location: SourceLocation,
|
|
) -> MemBuilder<Element, DynSize> {
|
|
memory_impl(name, element_type, source_location)
|
|
}
|
|
|
|
#[track_caller]
|
|
pub fn memory<Element: Type>(
|
|
implicit_name: ImplicitName<'_>,
|
|
element_type: Element,
|
|
) -> MemBuilder<Element, DynSize> {
|
|
memory_with_loc(implicit_name.0, element_type, SourceLocation::caller())
|
|
}
|
|
|
|
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
|
pub struct ModuleIO<T: Type> {
|
|
containing_module_name: NameId,
|
|
bundle_field: BundleField,
|
|
ty: T,
|
|
source_location: SourceLocation,
|
|
}
|
|
|
|
impl<T: Type> fmt::Debug for ModuleIO<T> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
f.debug_struct("ModuleIO")
|
|
.field("name", &self.scoped_name())
|
|
.field("is_input", &self.is_input())
|
|
.field("ty", &self.ty)
|
|
.finish_non_exhaustive()
|
|
}
|
|
}
|
|
|
|
impl<T: Type> ModuleIO<T> {
|
|
pub fn canonical(&self) -> ModuleIO<CanonicalType> {
|
|
let Self {
|
|
containing_module_name,
|
|
bundle_field,
|
|
ty: _,
|
|
source_location,
|
|
} = *self;
|
|
ModuleIO {
|
|
containing_module_name,
|
|
bundle_field,
|
|
ty: bundle_field.ty,
|
|
source_location,
|
|
}
|
|
}
|
|
pub fn bundle_field(&self) -> BundleField {
|
|
self.bundle_field
|
|
}
|
|
pub fn is_input(&self) -> bool {
|
|
self.bundle_field.flipped
|
|
}
|
|
pub fn is_output(&self) -> bool {
|
|
!self.bundle_field.flipped
|
|
}
|
|
pub fn containing_module_name(&self) -> Interned<str> {
|
|
self.containing_module_name.0
|
|
}
|
|
pub fn containing_module_name_id(&self) -> NameId {
|
|
self.containing_module_name
|
|
}
|
|
pub fn name(&self) -> Interned<str> {
|
|
self.bundle_field.name
|
|
}
|
|
pub fn name_id(&self) -> NameId {
|
|
NameId(self.bundle_field.name, 0)
|
|
}
|
|
pub fn scoped_name(&self) -> ScopedNameId {
|
|
ScopedNameId(self.containing_module_name, self.name_id())
|
|
}
|
|
pub fn source_location(&self) -> SourceLocation {
|
|
self.source_location
|
|
}
|
|
pub fn new_unchecked(
|
|
containing_module_name: NameId,
|
|
name: Interned<str>,
|
|
source_location: SourceLocation,
|
|
is_input: bool,
|
|
ty: T,
|
|
) -> Self {
|
|
Self {
|
|
containing_module_name,
|
|
bundle_field: BundleField {
|
|
name,
|
|
flipped: is_input,
|
|
ty: ty.canonical(),
|
|
},
|
|
ty,
|
|
source_location,
|
|
}
|
|
}
|
|
pub fn must_connect_to(&self) -> bool {
|
|
true
|
|
}
|
|
pub fn flow(&self) -> Flow {
|
|
if self.is_input() {
|
|
Flow::Source
|
|
} else if self.is_output() {
|
|
Flow::Sink
|
|
} else {
|
|
unreachable!()
|
|
}
|
|
}
|
|
pub fn ty(&self) -> T {
|
|
self.ty
|
|
}
|
|
}
|