// 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, ValueArrayOrSlice}, bundle::{BundleType, BundleValue, DynBundle, DynBundleType, FieldType, FieldsHint}, clock::ClockDomain, enum_::{DynEnum, EnumMatchVariantsIter, EnumType, EnumValue}, expr::{ ops::VariantAccess, Expr, Flow, Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathElement, ToExpr, }, int::{DynUInt, DynUIntType, FixedOrDynIntType, IntValue, UInt}, intern::{Intern, Interned}, memory::{Mem, MemBuilder, MemBuilderTarget, PortName}, reg::Reg, source_location::SourceLocation, ty::{ CanonicalType, Connect, DynCanonicalType, DynCanonicalValue, DynType, FixedType, Type, TypeEnum, Value, }, util::ConstBool, wire::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, 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; 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 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; } #[derive(Debug)] pub struct BuilderModuleBody { blocks: Vec, annotations_map: HashMap, Vec>, memory_map: HashMap>>, } /// 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 {} #[derive(Debug)] pub struct BuilderBlock { memories: Vec>>, stmts: Vec>, } #[derive(Copy, Clone, PartialEq, Eq, Hash, Default)] pub struct Block { pub memories: Interned<[Mem<[DynCanonicalValue]>]>, 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 + FusedIterator + Clone + '_ { self.stmts .iter() .flat_map(|stmt| stmt.sub_stmt_blocks()) .copied() } } #[derive(Clone, PartialEq, Eq, Hash)] pub struct StmtConnect { pub lhs: Expr, pub rhs: Expr, pub source_location: SourceLocation, } 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(Clone, PartialEq, Eq, Hash)] pub struct StmtIf { pub cond: Expr>, pub source_location: SourceLocation, pub blocks: [S::Block; 2], } impl StmtIf { pub fn then_block(&self) -> S::Block { self.blocks[0] } pub fn else_block(&self) -> S::Block { self.blocks[1] } } impl fmt::Debug for StmtIf { 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 { pub expr: Expr, pub source_location: SourceLocation, pub blocks: Interned<[S::Block]>, } impl fmt::Debug for StmtMatch { 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 { pub annotations: S::StmtAnnotations, pub wire: Wire>, } #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub struct StmtReg { pub annotations: S::StmtAnnotations, pub reg: Reg>, } #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub struct StmtInstance { pub annotations: S::StmtAnnotations, pub instance: Instance, } wrapper_enum! { #[impl( () self: StmtDeclaration = self, () self: Stmt = self.declaration()? )] #[to(() StmtDeclaration, () Stmt)] #[derive(Clone, PartialEq, Eq, Hash)] pub enum StmtDeclaration { #[is = is_wire, as_ref = wire] Wire(StmtWire), #[is = is_reg, as_ref = reg] Reg(StmtReg), #[is = is_instance, as_ref = instance] Instance(StmtInstance), } } impl StmtDeclaration { 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) -> Interned { match self { StmtDeclaration::Wire(v) => v.wire.ty(), StmtDeclaration::Reg(v) => v.reg.ty(), StmtDeclaration::Instance(v) => v.instance.ty().canonical_dyn(), } } } wrapper_enum! { #[impl(() self: Stmt = self)] #[to(() Stmt)] #[derive(Clone, PartialEq, Eq, Hash)] pub enum Stmt { #[is = is_connect, as_ref = connect] Connect(StmtConnect), #[is = is_if, as_ref = if_] If(StmtIf), #[is = is_match, as_ref = match_] Match(StmtMatch), #[is = is_declaration, as_ref = declaration] Declaration(StmtDeclaration), } } impl Stmt { pub fn sub_stmt_blocks(&self) -> &[S::Block] { match self { Stmt::Connect(_) => &[], 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>); impl BlockStack { fn new(top_block: BlockId) -> Self { Self(RefCell::new(vec![BlockStackEntry { removed: false, block_id: top_block, }])) } fn push_scope(self: Rc, 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 { 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, 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); 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, usize>); impl NameIdGen { pub fn gen(&mut self, name: Interned) -> 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) -> 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::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) -> NameId { static MODULE_NAME_GEN: Mutex> = Mutex::new(None); MODULE_NAME_GEN .lock() .unwrap() .get_or_insert_with(NameIdGen::default) .gen(name) } } pub struct Instance where T::Type: BundleType, { scoped_name: ScopedNameId, instantiated: Interned>, source_location: SourceLocation, } impl Clone for Instance where T::Type: BundleType, { fn clone(&self) -> Self { *self } } impl Copy for Instance where T::Type: BundleType {} impl Eq for Instance where T::Type: BundleType {} impl fmt::Debug for Instance where T::Type: BundleType, { 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 PartialEq for Instance where T::Type: BundleType, { fn eq(&self, other: &Self) -> bool { let Self { scoped_name, instantiated, source_location, } = *self; scoped_name == other.scoped_name && instantiated == other.instantiated && source_location == other.source_location } } impl Hash for Instance where T::Type: BundleType, { fn hash(&self, state: &mut H) { let Self { scoped_name, instantiated, source_location, } = self; scoped_name.hash(state); instantiated.hash(state); source_location.hash(state); } } impl Instance where T::Type: BundleType, { pub fn canonical(self) -> Instance { 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 { self.containing_module_name_id().0 } pub fn containing_module_name_id(self) -> NameId { self.scoped_name.0 } pub fn name(self) -> Interned { 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> { self.instantiated } pub fn source_location(self) -> SourceLocation { self.source_location } pub fn new_unchecked( scoped_name: ScopedNameId, instantiated: Interned>, 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 } } mod module_kind_trait_sealed { pub trait Sealed {} } pub trait ModuleKindTrait: Into + 'static + Send + Sync + Copy + Ord + Hash + fmt::Debug + Default + module_kind_trait_sealed::Sealed { const MODULE_KIND: ModuleKind; type ModuleBuilderBody: fmt::Debug; type ModuleBody: Into + From + 'static + Send + Sync + Copy + Eq + Hash + fmt::Debug; fn builder_normal_body( body: &mut Self::ModuleBuilderBody, ) -> Option<&mut NormalModuleBody>; fn new_body(module_name: NameId) -> Self::ModuleBuilderBody; } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] pub struct ExternModule; impl From for ModuleKind { fn from(value: ExternModule) -> Self { Self::Extern(value) } } impl module_kind_trait_sealed::Sealed for ExternModule {} impl ModuleKindTrait for ExternModule { const MODULE_KIND: ModuleKind = ModuleKind::Extern(Self); type ModuleBuilderBody = ExternModuleBody>; type ModuleBody = ExternModuleBody; fn builder_normal_body( _body: &mut Self::ModuleBuilderBody, ) -> Option<&mut NormalModuleBody> { None } fn new_body(name: NameId) -> Self::ModuleBuilderBody { ExternModuleBody { verilog_name: name.0, parameters: vec![], } } } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] pub struct NormalModule; impl From for ModuleKind { fn from(value: NormalModule) -> Self { Self::Normal(value) } } impl module_kind_trait_sealed::Sealed for NormalModule {} impl ModuleKindTrait for NormalModule { const MODULE_KIND: ModuleKind = ModuleKind::Normal(Self); type ModuleBuilderBody = NormalModuleBody; type ModuleBody = NormalModuleBody; fn builder_normal_body( body: &mut Self::ModuleBuilderBody, ) -> Option<&mut NormalModuleBody> { Some(body) } fn new_body(_module_name: NameId) -> Self::ModuleBuilderBody { NormalModuleBody { body: BuilderModuleBody { blocks: vec![BuilderBlock { memories: vec![], stmts: vec![], }], annotations_map: HashMap::new(), memory_map: HashMap::new(), }, } } } #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub struct AnnotatedModuleIO { pub annotations: S::ModuleIOAnnotations, pub module_io: ModuleIO>, } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub enum ModuleKind { Extern(ExternModule), Normal(NormalModule), } pub struct ModuleBuilder where T::Type: BundleType, { name: NameId, body: Kind::ModuleBuilderBody, block_stack: Rc, io: Vec>, io_indexes: HashMap>, usize>, io_fields_hint: FieldsHint, name_id_gen: NameIdGen, module_annotations: Vec, _phantom: PhantomData T::Type>, } #[must_use] pub struct Scope { block_id: BlockId, block_stack_index: usize, block_stack: Rc, } 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 where T::Value: EnumValue, { block_stack: Rc, match_arm_block_id: BlockId, variant_access: Interned>>, } impl EnumMatchVariantAndInactiveScopeImpl where T::Value: EnumValue, { pub(crate) fn activate( self, ) -> ( Interned>>, Scope, ) { let scope = self.block_stack.push_scope(self.match_arm_block_id); (self.variant_access, scope) } pub(crate) fn variant_access( &self, ) -> Interned>> { self.variant_access } } #[derive(Clone)] pub(crate) struct EnumMatchVariantsIterImpl { block_stack: Rc, blocks: Interned<[BlockId]>, base: Expr, _phantom: PhantomData, } impl EnumMatchVariantsIterImpl where T::Value: EnumValue, { pub(crate) fn for_variant_index( &self, variant_index: usize, ) -> EnumMatchVariantAndInactiveScopeImpl { EnumMatchVariantAndInactiveScopeImpl { block_stack: self.block_stack.clone(), match_arm_block_id: self.blocks[variant_index], variant_access: Intern::intern_sized(VariantAccess::new_unchecked( self.base, variant_index, )), } } } #[derive(PartialEq, Eq, Hash, Clone, Debug)] pub enum ExternModuleParameterValue { Integer(BigInt), String(Interned), RawVerilog(Interned), } #[derive(PartialEq, Eq, Hash, Clone, Debug)] pub struct ExternModuleParameter { pub name: Interned, pub value: ExternModuleParameterValue, } #[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] pub struct NormalModuleBody { pub body: S::ModuleBody, } impl From> for NormalModuleBody { fn from(value: NormalModuleBody) -> Self { let NormalModuleBody { body: BuilderModuleBody { mut blocks, mut annotations_map, memory_map: _, }, } = value; fn finalize_block( blocks: &mut [BuilderBlock], annotations_map: &mut HashMap, Vec>, block_id: BlockId, ) -> Block { let BuilderBlock { memories, stmts } = &mut blocks[block_id.as_usize()]; let memories = Interned::from_iter( memories .drain(..) .filter_map(|memory| memory.borrow().make_memory()), ); let stmts = std::mem::take(stmts); let stmts = Interned::from_iter(stmts.into_iter().map(|stmt| { match stmt { Stmt::Connect(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 From for ModuleBody { fn from(value: NormalModuleBody) -> Self { Self::Normal(value) } } #[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] pub struct ExternModuleBody< P: Deref = Interned<[ExternModuleParameter]>, > { pub verilog_name: Interned, pub parameters: P, } impl From>> for ExternModuleBody { fn from(value: ExternModuleBody>) -> Self { let ExternModuleBody { verilog_name, parameters, } = value; let parameters = Intern::intern_owned(parameters); Self { verilog_name, parameters, } } } impl From for ModuleBody { fn from(value: ExternModuleBody) -> Self { Self::Extern(value) } } #[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] pub enum ModuleBody { Normal(NormalModuleBody), Extern(ExternModuleBody), } impl ModuleBody { pub fn kind(&self) -> ModuleKind { match self { Self::Normal(_) => ModuleKind::Normal(NormalModule), Self::Extern(_) => ModuleKind::Extern(ExternModule), } } } /// 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(PartialEq, Eq, Hash)] pub struct Module where T::Type: BundleType, { name: NameId, source_location: SourceLocation, body: ModuleBody, io_ty: Interned, module_io: Interned<[AnnotatedModuleIO]>, module_annotations: Interned<[Annotation]>, } #[derive(Default)] struct DebugFmtModulesState { seen: HashSet, unwritten: VecDeque<(NameId, Box)>, } #[derive(Default)] struct DebugFmtState { modules_state: RefCell>, } impl DebugFmtState { fn with R, R>(f: F) -> R { thread_local! { static STATE: RefCell> = 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(&self, module: Module) -> bool where T::Type: BundleType, { 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(Module) where T::Type: BundleType; impl fmt::Debug for DebugModuleBody where T::Type: BundleType, { 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(Module) where T::Type: BundleType; impl fmt::Debug for ModuleDebugShort where T::Type: BundleType, { 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 Module where T::Type: BundleType, { pub fn debug_short(self) -> ModuleDebugShort { ModuleDebugShort(self) } } impl fmt::Debug for Module where T::Type: BundleType, { 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> = 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) } }) } } impl Clone for Module where T::Type: BundleType, { fn clone(&self) -> Self { *self } } impl Copy for Module where T::Type: BundleType {} #[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>, }, Decomposed { subtargets: HashMap, TargetState>, }, } #[derive(Debug)] struct TargetState { target: Interned, 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, ) { 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, declared_in_block: usize) -> Self { Self { target, inner: match target.canonical_ty().type_enum() { TypeEnum::BundleType(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(), }, TypeEnum::ArrayType(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(), }, TypeEnum::EnumType(_) | TypeEnum::UInt(_) | TypeEnum::SInt(_) | TypeEnum::Clock(_) | TypeEnum::AsyncReset(_) | TypeEnum::SyncReset(_) | TypeEnum::Reset(_) => TargetStateInner::Single { declared_in_block, written_in_blocks: RefCell::default(), }, }, } } } struct AssertValidityState { module: Module, blocks: Vec, target_states: HashMap, 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) -> Result<&TargetState, ()> { self.target_states.get(&target_base).ok_or(()) } #[track_caller] fn insert_new_base(&mut self, target_base: Interned, 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, 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, ) { 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(StmtConnect { lhs, rhs, source_location, }) => { self.set_connect_side_written(lhs, source_location, true, block); self.set_connect_side_written(rhs, source_location, false, block); } 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) => { 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 Module where T::Type: BundleType, { /// you generally should use the [`#[hdl_module]`][`crate::hdl_module`] proc-macro and [`ModuleBuilder`] instead pub fn new_unchecked( name_id: NameId, source_location: SourceLocation, body: ModuleBody, module_io: impl IntoIterator, module_annotations: impl IntoAnnotations, ) -> Module { 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::Type::from_canonical_type(DynBundleType::new(Intern::intern_owned( Vec::from_iter(module_io.iter().map(|io| io.module_io.field_type)), ))) .intern_sized(), module_io, module_annotations, }; retval.assert_validity(); retval } pub fn canonical(self) -> Module { Module { name: self.name, body: self.body, io_ty: self.io_ty.canonical().intern_sized(), module_io: self.module_io, source_location: self.source_location, module_annotations: self.module_annotations, } } pub fn from_canonical(canonical_module: Module) -> Self { Module { name: canonical_module.name, body: canonical_module.body, io_ty: T::Type::from_canonical_type(*canonical_module.io_ty).intern_sized(), module_io: canonical_module.module_io, source_location: canonical_module.source_location, module_annotations: canonical_module.module_annotations, } } pub fn top_block(self) -> Option { 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) -> Interned { self.io_ty } pub fn module_io(self) -> Interned<[AnnotatedModuleIO]> { self.module_io } pub fn name(self) -> Interned { 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<'a, CD, I, V: Value> { scoped_name: ScopedNameId, source_location: SourceLocation, clock_domain: CD, init: I, ty: V::Type, stmts: &'a mut Vec>, } impl<'a, CD> RegBuilder<'a, CD, (), ()> { pub fn no_reset(self, ty: V::Type) -> RegBuilder<'a, CD, Option>, V> { let Self { scoped_name, source_location, clock_domain, init: _, ty: _, stmts, } = self; RegBuilder { scoped_name, source_location, clock_domain, init: None, ty, stmts, } } pub fn reset_default(self) -> RegBuilder<'a, CD, Option>, V> where V::Type: FixedType, { self.reset(V::default().to_expr()) } pub fn opt_reset( self, ty: T, init: Option>, ) -> RegBuilder<'a, CD, Option>, T::Value> { let Self { scoped_name, source_location, clock_domain, init: _, ty: _, stmts, } = self; RegBuilder { scoped_name, source_location, clock_domain, init: init.map(|e| e.to_expr()), ty, stmts, } } pub fn reset(self, init: Expr) -> RegBuilder<'a, CD, Option>, V> where V::Type: Type, { let Self { scoped_name, source_location, clock_domain, init: _, ty: _, stmts, } = self; let ty = init.ty(); RegBuilder { scoped_name, source_location, clock_domain, init: Some(init), ty, stmts, } } } impl<'a, I, V: Value> RegBuilder<'a, (), I, V> { pub fn clock_domain( self, clock_domain: impl ToExpr::Type>, ) -> RegBuilder<'a, Expr, I, V> { let Self { scoped_name, source_location, clock_domain: _, init, ty, stmts, } = self; RegBuilder { scoped_name, source_location, clock_domain: clock_domain.to_expr(), init, ty, stmts, } } } impl RegBuilder<'_, Expr, Option>, V> where V::Type: Type, { #[track_caller] pub fn build(self) -> Expr { let Self { scoped_name, source_location, clock_domain, init, ty, stmts, } = self; let reg = Reg::::new_unchecked(scoped_name, source_location, ty, clock_domain, init); let retval = reg.to_expr(); stmts.push( StmtReg { annotations: (), reg: reg.to_dyn_canonical_reg(), } .into(), ); retval } } impl ModuleBuilder where T::Type: BundleType, { #[track_caller] pub fn io_with_loc( &mut self, name: &str, source_location: SourceLocation, is_input: bool, ty: IO::Type, ) -> Expr where IO::Type: Type, { let module_io = ModuleIO::::new_unchecked( self.name, name.intern(), source_location, is_input, ty, ); if let Err(e) = self.io_fields_hint.check_field( self.io.len(), module_io .field_type .as_ref_ty() .map_ty(|v: &IO::Type| -> &dyn DynType { v }), ) { panic!("error: {source_location}: {e}"); } let retval = module_io.to_expr(); let module_io = module_io.to_canonical_dyn_module_io(); self.io_indexes.insert(module_io.clone(), self.io.len()); self.io.push(AnnotatedModuleIO { annotations: vec![], module_io, }); retval } #[track_caller] pub fn io( &mut self, implicit_name: ImplicitName<'_>, is_input: bool, ty: IO::Type, ) -> Expr where IO::Type: Type, { self.io_with_loc(implicit_name.0, SourceLocation::caller(), is_input, ty) } #[track_caller] pub fn input_with_loc( &mut self, name: &str, source_location: SourceLocation, ty: IO::Type, ) -> Expr where IO::Type: Type, { self.io_with_loc(name, source_location, true, ty) } #[track_caller] pub fn output_with_loc( &mut self, name: &str, source_location: SourceLocation, ty: IO::Type, ) -> Expr where IO::Type: Type, { self.io_with_loc(name, source_location, false, ty) } #[track_caller] pub fn input(&mut self, implicit_name: ImplicitName<'_>, ty: IO::Type) -> Expr where IO::Type: Type, { self.input_with_loc(implicit_name.0, SourceLocation::caller(), ty) } #[track_caller] pub fn output(&mut self, implicit_name: ImplicitName<'_>, ty: IO::Type) -> Expr where IO::Type: Type, { self.output_with_loc(implicit_name.0, SourceLocation::caller(), ty) } #[track_caller] pub fn run(name: &str, f: F) -> Interned> { Self::run_with_loc(name, SourceLocation::caller(), f) } #[track_caller] pub fn try_run Result<(), E>, E>( name: &str, f: F, ) -> Result>, E> { Self::try_run_with_loc(name, SourceLocation::caller(), f) } #[track_caller] pub fn run_with_loc( name: &str, source_location: SourceLocation, f: F, ) -> Interned> { Self::try_run_with_loc(name, source_location, |this| -> Result<(), Infallible> { f(this); Ok(()) }) .unwrap() } #[track_caller] pub fn try_run_with_loc Result<(), E>, E>( name: &str, source_location: SourceLocation, f: F, ) -> Result>, E> { let name = NameIdGen::gen_module_name(name.intern()); let body = Kind::new_body(name); let mut builder = Self { name, body, block_stack: Rc::new(BlockStack::new(BlockId(0))), io: vec![], io_indexes: HashMap::new(), io_fields_hint: T::Type::fields_hint(), name_id_gen: NameIdGen::default(), module_annotations: vec![], _phantom: PhantomData, }; f(&mut builder)?; Ok(Module::new_unchecked( builder.name, source_location, Kind::ModuleBody::from(builder.body).into(), builder.io.into_iter().map( |AnnotatedModuleIO { annotations, module_io, }| AnnotatedModuleIO { annotations: Intern::intern_owned(annotations), module_io, }, ), builder.module_annotations, ) .intern_sized()) } #[track_caller] pub fn annotate_module(&mut self, annotations: impl IntoAnnotations) { self.module_annotations .extend(annotations.into_annotations()); } #[track_caller] pub fn annotate(&mut self, target: Expr, 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); let apply_annotations = |targeted_annotations: &mut Vec| { targeted_annotations.extend( annotations .into_annotations() .into_iter() .map(|annotation| TargetedAnnotation::new(target, annotation)), ) }; let decl = match TargetBase::clone(&target.base()) { TargetBase::ModuleIO(v) => { apply_annotations(&mut self.io[*unwrap!(self.io_indexes.get(&v))].annotations); return; } TargetBase::MemPort(v) => { apply_annotations( &mut unwrap!(unwrap!(Kind::builder_normal_body(&mut self.body)) .body .memory_map .get_mut(&v.mem_name())) .borrow_mut() .port_annotations, ); return; } TargetBase::Reg(reg) => StmtReg { annotations: (), reg, } .into(), TargetBase::Wire(wire) => StmtWire { annotations: (), wire, } .into(), TargetBase::Instance(instance) => StmtInstance { annotations: (), instance, } .into(), }; apply_annotations( unwrap!(Kind::builder_normal_body(&mut self.body)) .body .annotations_map .entry(decl) .or_default(), ); } } impl ModuleBuilder where T::Type: BundleType, { #[track_caller] pub fn verilog_name(&mut self, name: impl AsRef) { 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.body.verilog_name = name.intern(); } pub fn parameter(&mut self, name: impl AsRef, value: ExternModuleParameterValue) { self.body.parameters.push(ExternModuleParameter { name: name.as_ref().intern(), value, }); } pub fn parameter_int(&mut self, name: impl AsRef, value: impl Into) { self.body.parameters.push(ExternModuleParameter { name: name.as_ref().intern(), value: ExternModuleParameterValue::Integer(value.into()), }); } pub fn parameter_str(&mut self, name: impl AsRef, value: impl AsRef) { self.body.parameters.push(ExternModuleParameter { name: name.as_ref().intern(), value: ExternModuleParameterValue::String(value.as_ref().intern()), }); } pub fn parameter_raw_verilog(&mut self, name: impl AsRef, raw_verilog: impl AsRef) { self.body.parameters.push(ExternModuleParameter { name: name.as_ref().intern(), value: ExternModuleParameterValue::RawVerilog(raw_verilog.as_ref().intern()), }); } } impl ModuleBuilder where T::Type: BundleType, { fn block(&mut self, block_id: BlockId) -> &mut BuilderBlock { &mut self.body.body.blocks[block_id.as_usize()] } fn new_block(&mut self) -> BlockId { let index = self.body.body.blocks.len(); self.body.body.blocks.push(BuilderBlock { memories: vec![], stmts: vec![], }); BlockId(index) } pub fn wire_with_loc( &mut self, name: &str, source_location: SourceLocation, ty: V::Type, ) -> Expr where V::Type: Type, { let wire = Wire::::new_unchecked( ScopedNameId(self.name, self.name_id_gen.gen(name.intern())), source_location, ty, ); let retval = wire.to_expr(); self.block(self.block_stack.top()).stmts.push( StmtWire { annotations: (), wire: wire.to_dyn_canonical_wire(), } .into(), ); retval } #[track_caller] pub fn wire(&mut self, implicit_name: ImplicitName<'_>, ty: V::Type) -> Expr where V::Type: Type, { self.wire_with_loc(implicit_name.0, SourceLocation::caller(), ty) } #[track_caller] pub fn reg_builder_with_loc<'a>( &'a mut self, name: &str, source_location: SourceLocation, ) -> RegBuilder<'a, (), (), ()> { let scoped_name = ScopedNameId(self.name, self.name_id_gen.gen(name.intern())); let stmts = &mut self.block(self.block_stack.top()).stmts; RegBuilder { scoped_name, source_location, clock_domain: (), init: (), ty: (), stmts, } } #[track_caller] pub fn reg_builder<'a>( &'a mut self, implicit_name: ImplicitName<'_>, ) -> RegBuilder<'a, (), (), ()> { self.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_(&mut self, cond: Expr>) -> IfScope where Ty: FixedOrDynIntType< 1, Signed = ConstBool, CanonicalType = DynUIntType, CanonicalValue = DynUInt, >, { self.if_with_loc(cond, SourceLocation::caller()) } pub fn if_with_loc( &mut self, cond: Expr>, source_location: SourceLocation, ) -> IfScope where Ty: FixedOrDynIntType< 1, Signed = ConstBool, CanonicalType = DynUIntType, CanonicalValue = DynUInt, >, { let cond = cond.as_bool(); let outer_block = self.block_stack.top(); let then_block = self.new_block(); let else_block = self.new_block(); self.block(outer_block).stmts.push( StmtIf { cond, source_location, blocks: [then_block, else_block], } .into(), ); IfScope { then: self.block_stack.clone().push_scope(then_block), else_block, } } pub fn enum_match_variants_helper( &mut self, base: Expr, source_location: SourceLocation, ) -> EnumMatchVariantsIter where Enum::Type: EnumType, { let base = base.canonical(); let enum_ty = base.ty(); let outer_block = self.block_stack.top(); let blocks = Intern::intern_owned(Vec::from_iter( enum_ty.variants().iter().map(|_| self.new_block()), )); self.block(outer_block).stmts.push( StmtMatch { expr: base, source_location, blocks, } .into(), ); let block_stack = self.block_stack.clone(); EnumMatchVariantsIter { inner: EnumMatchVariantsIterImpl { block_stack, blocks, base, _phantom: PhantomData, }, variant_index: 0..blocks.len(), } } #[track_caller] pub fn match_(&mut self, expr: impl ToExpr) -> Ty::MatchVariantsIter { self.match_with_loc(expr, SourceLocation::caller()) } pub fn match_with_loc( &mut self, expr: impl ToExpr, source_location: SourceLocation, ) -> Ty::MatchVariantsIter { Ty::match_variants(expr.to_expr(), self, source_location) } #[track_caller] pub fn connect_any_with_loc( &mut self, lhs: Lhs, rhs: Rhs, source_location: SourceLocation, ) where Lhs::Type: Connect + Type< CanonicalType = ::CanonicalType, CanonicalValue = ::CanonicalValue, >, { let lhs = lhs.to_expr(); let rhs = rhs.to_expr(); assert!( matches!(lhs.flow(), Flow::Sink | Flow::Duplex), "can't connect to source, connect lhs must have sink or duplex flow" ); assert!(lhs.target().is_some(), "can't connect to non-target"); match rhs.flow() { Flow::Source | Flow::Duplex => {} Flow::Sink => assert!( rhs.ty().is_passive(), "can't connect from sink with non-passive type" ), } assert!( lhs.canonical_type().can_connect(&rhs.canonical_type()), "can't connect types that are not equivalent:\nlhs type:\n{:?}\nrhs type:\n{:?}", lhs.ty(), rhs.ty() ); self.block(self.block_stack.top()).stmts.push( StmtConnect { lhs: lhs.to_canonical_dyn(), rhs: rhs.to_canonical_dyn(), source_location, } .into(), ); } #[track_caller] pub fn connect_any(&mut self, lhs: Lhs, rhs: Rhs) where Lhs::Type: Connect + Type< CanonicalType = ::CanonicalType, CanonicalValue = ::CanonicalValue, >, { self.connect_any_with_loc(lhs, rhs, SourceLocation::caller()) } #[track_caller] pub fn connect(&mut self, lhs: Expr, rhs: Expr) where V::Type: Type, { self.connect_with_loc(lhs, rhs, SourceLocation::caller()) } #[track_caller] pub fn connect_with_loc( &mut self, lhs: Expr, rhs: Expr, source_location: SourceLocation, ) where V::Type: Type, { self.connect_any_with_loc(lhs, rhs, source_location) } pub fn instance_with_loc( &mut self, name: &str, instantiated: Interned>, source_location: SourceLocation, ) -> Expr where T2::Type: BundleType, { let instance = Instance:: { scoped_name: ScopedNameId(self.name, self.name_id_gen.gen(name.intern())), instantiated, source_location, }; self.block(self.block_stack.top()).stmts.push( StmtInstance { annotations: (), instance: instance.canonical(), } .into(), ); instance.to_expr() } #[track_caller] pub fn instance( &mut self, implicit_name: ImplicitName<'_>, instantiated: Interned>, ) -> Expr where T2::Type: BundleType, { self.instance_with_loc(implicit_name.0, instantiated, SourceLocation::caller()) } #[track_caller] fn memory_impl( &mut self, name: &str, mem_element_type: VA::ElementType, source_location: SourceLocation, ) -> MemBuilder { let scoped_name = ScopedNameId(self.name, self.name_id_gen.gen(name.intern())); let (retval, target_mem) = MemBuilder::new(scoped_name, source_location, mem_element_type); self.block(self.block_stack.top()) .memories .push(target_mem.clone()); self.body.body.memory_map.insert(scoped_name, target_mem); retval } #[track_caller] pub fn memory_array_with_loc( &mut self, name: &str, mem_array_type: ArrayType, source_location: SourceLocation, ) -> MemBuilder { let mut retval = self.memory_impl(name, mem_array_type.element().clone(), source_location); retval.depth(mem_array_type.len()); retval } #[track_caller] pub fn memory_array( &mut self, implicit_name: ImplicitName<'_>, mem_array_type: ArrayType, ) -> MemBuilder { self.memory_array_with_loc(implicit_name.0, mem_array_type, SourceLocation::caller()) } #[track_caller] pub fn memory_with_init_and_loc( &mut self, name: &str, initial_value: impl ToExpr>, source_location: SourceLocation, ) -> MemBuilder { let mut retval = self.memory_array_with_loc(name, initial_value.ty(), source_location); retval.initial_value(initial_value); retval } #[track_caller] pub fn memory_with_init( &mut self, implicit_name: ImplicitName<'_>, initial_value: impl ToExpr>, ) -> MemBuilder { self.memory_with_init_and_loc( implicit_name.0, initial_value.to_expr(), SourceLocation::caller(), ) } #[track_caller] pub fn memory_with_loc( &mut self, name: &str, element_type: Element::Type, source_location: SourceLocation, ) -> MemBuilder<[Element]> where Element::Type: Type, { self.memory_impl(name, element_type, source_location) } #[track_caller] pub fn memory( &mut self, implicit_name: ImplicitName<'_>, element_type: Element::Type, ) -> MemBuilder<[Element]> where Element::Type: Type, { self.memory_with_loc(implicit_name.0, element_type, SourceLocation::caller()) } } #[derive(Clone, Eq, PartialEq, Hash)] pub struct ModuleIO { containing_module_name: NameId, field_type: FieldType, source_location: SourceLocation, } impl fmt::Debug for ModuleIO { 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.field_type().ty) .finish_non_exhaustive() } } impl ModuleIO { pub fn canonical(&self) -> ModuleIO { let Self { containing_module_name, ref field_type, source_location, } = *self; ModuleIO { containing_module_name, field_type: field_type.canonical(), source_location, } } pub fn to_canonical_dyn_module_io(&self) -> ModuleIO> { let Self { containing_module_name, ref field_type, source_location, } = *self; ModuleIO { containing_module_name, field_type: field_type.canonical_dyn(), source_location, } } pub fn field_type(&self) -> &FieldType { &self.field_type } pub fn is_input(&self) -> bool { self.field_type.flipped } pub fn is_output(&self) -> bool { !self.field_type.flipped } pub fn containing_module_name(&self) -> Interned { self.containing_module_name.0 } pub fn containing_module_name_id(&self) -> NameId { self.containing_module_name } pub fn name(&self) -> Interned { self.field_type.name } pub fn name_id(&self) -> NameId { NameId(self.field_type.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, source_location: SourceLocation, is_input: bool, ty: T, ) -> Self { Self { containing_module_name, field_type: FieldType { name, flipped: is_input, 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!() } } }