fayalite/crates/fayalite/src/module.rs
Jacob Lifshay 1e2831da47
add validation of connects and matches when validating module
this is useful for catching errors in transformation passes
2024-09-30 21:20:35 -07:00

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
}
}