fayalite/crates/fayalite/src/module.rs

2635 lines
81 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, 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<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 {}
#[derive(Debug)]
pub struct BuilderBlock {
memories: Vec<Rc<RefCell<MemBuilderTarget>>>,
stmts: Vec<Stmt<ModuleBuilding>>,
}
#[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<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<DynCanonicalValue>,
pub rhs: Expr<DynCanonicalValue>,
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<S: ModuleBuildingStatus = ModuleBuilt> {
pub cond: Expr<UInt<1>>,
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<DynEnum>,
pub source_location: SourceLocation,
pub blocks: Interned<[S::Block]>,
}
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<Interned<dyn DynCanonicalType>>,
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct StmtReg<S: ModuleBuildingStatus = ModuleBuilt> {
pub annotations: S::StmtAnnotations,
pub reg: Reg<Interned<dyn DynCanonicalType>>,
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct StmtInstance<S: ModuleBuildingStatus = ModuleBuilt> {
pub annotations: S::StmtAnnotations,
pub instance: Instance<DynBundle>,
}
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) -> Interned<dyn DynCanonicalType> {
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((<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_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::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<DynBundle>) -> 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<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)
}
}
pub struct Instance<T: BundleValue>
where
T::Type: BundleType<Value = T>,
{
scoped_name: ScopedNameId,
instantiated: Interned<Module<T>>,
source_location: SourceLocation,
}
impl<T: BundleValue> Clone for Instance<T>
where
T::Type: BundleType<Value = T>,
{
fn clone(&self) -> Self {
*self
}
}
impl<T: BundleValue> Copy for Instance<T> where T::Type: BundleType<Value = T> {}
impl<T: BundleValue> Eq for Instance<T> where T::Type: BundleType<Value = T> {}
impl<T: BundleValue> fmt::Debug for Instance<T>
where
T::Type: BundleType<Value = 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: BundleValue> PartialEq for Instance<T>
where
T::Type: BundleType<Value = T>,
{
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<T: BundleValue> Hash for Instance<T>
where
T::Type: BundleType<Value = T>,
{
fn hash<H: Hasher>(&self, state: &mut H) {
let Self {
scoped_name,
instantiated,
source_location,
} = self;
scoped_name.hash(state);
instantiated.hash(state);
source_location.hash(state);
}
}
impl<T: BundleValue> Instance<T>
where
T::Type: BundleType<Value = T>,
{
pub fn canonical(self) -> Instance<DynBundle> {
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
}
}
mod module_kind_trait_sealed {
pub trait Sealed {}
}
pub trait ModuleKindTrait:
Into<ModuleKind>
+ '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<ModuleBody>
+ From<Self::ModuleBuilderBody>
+ 'static
+ Send
+ Sync
+ Copy
+ Eq
+ Hash
+ fmt::Debug;
fn builder_normal_body(
body: &mut Self::ModuleBuilderBody,
) -> Option<&mut NormalModuleBody<ModuleBuilding>>;
fn new_body(module_name: NameId) -> Self::ModuleBuilderBody;
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
pub struct ExternModule;
impl From<ExternModule> 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<Vec<ExternModuleParameter>>;
type ModuleBody = ExternModuleBody;
fn builder_normal_body(
_body: &mut Self::ModuleBuilderBody,
) -> Option<&mut NormalModuleBody<ModuleBuilding>> {
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<NormalModule> 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<ModuleBuilding>;
type ModuleBody = NormalModuleBody;
fn builder_normal_body(
body: &mut Self::ModuleBuilderBody,
) -> Option<&mut NormalModuleBody<ModuleBuilding>> {
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<S: ModuleBuildingStatus = ModuleBuilt> {
pub annotations: S::ModuleIOAnnotations,
pub module_io: ModuleIO<Interned<dyn DynCanonicalType>>,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum ModuleKind {
Extern(ExternModule),
Normal(NormalModule),
}
pub struct ModuleBuilder<T: BundleValue, Kind: ModuleKindTrait>
where
T::Type: BundleType<Value = T>,
{
name: NameId,
body: Kind::ModuleBuilderBody,
block_stack: Rc<BlockStack>,
io: Vec<AnnotatedModuleIO<ModuleBuilding>>,
io_indexes: HashMap<ModuleIO<Interned<dyn DynCanonicalType>>, usize>,
io_fields_hint: FieldsHint,
name_id_gen: NameIdGen,
module_annotations: Vec<Annotation>,
_phantom: PhantomData<fn(Kind) -> T::Type>,
}
#[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>
where
T::Value: EnumValue<Type = T>,
{
block_stack: Rc<BlockStack>,
match_arm_block_id: BlockId,
variant_access: Interned<VariantAccess<T, Interned<dyn DynCanonicalType>>>,
}
impl<T: EnumType> EnumMatchVariantAndInactiveScopeImpl<T>
where
T::Value: EnumValue<Type = T>,
{
pub(crate) fn activate(
self,
) -> (
Interned<VariantAccess<T, Interned<dyn DynCanonicalType>>>,
Scope,
) {
let scope = self.block_stack.push_scope(self.match_arm_block_id);
(self.variant_access, scope)
}
pub(crate) fn variant_access(
&self,
) -> Interned<VariantAccess<T, Interned<dyn DynCanonicalType>>> {
self.variant_access
}
}
#[derive(Clone)]
pub(crate) struct EnumMatchVariantsIterImpl<T> {
block_stack: Rc<BlockStack>,
blocks: Interned<[BlockId]>,
base: Expr<DynEnum>,
_phantom: PhantomData<T>,
}
impl<T: EnumType> EnumMatchVariantsIterImpl<T>
where
T::Value: EnumValue<Type = 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: Intern::intern_sized(VariantAccess::new_unchecked(
self.base,
variant_index,
)),
}
}
}
#[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, 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<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(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<T: BundleValue>
where
T::Type: BundleType<Value = T>,
{
name: NameId,
source_location: SourceLocation,
body: ModuleBody,
io_ty: Interned<T::Type>,
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: BundleValue>(&self, module: Module<T>) -> bool
where
T::Type: BundleType<Value = T>,
{
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: BundleValue>(Module<T>)
where
T::Type: BundleType<Value = T>;
impl<T: BundleValue> fmt::Debug for DebugModuleBody<T>
where
T::Type: BundleType<Value = 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: BundleValue>(Module<T>)
where
T::Type: BundleType<Value = T>;
impl<T: BundleValue> fmt::Debug for ModuleDebugShort<T>
where
T::Type: BundleType<Value = 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: BundleValue> Module<T>
where
T::Type: BundleType<Value = T>,
{
pub fn debug_short(self) -> ModuleDebugShort<T> {
ModuleDebugShort(self)
}
}
impl<T: BundleValue> fmt::Debug for Module<T>
where
T::Type: BundleType<Value = 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)
}
})
}
}
impl<T: BundleValue> Clone for Module<T>
where
T::Type: BundleType<Value = T>,
{
fn clone(&self) -> Self {
*self
}
}
impl<T: BundleValue> Copy for Module<T> where T::Type: BundleType<Value = T> {}
#[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().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<DynBundle>,
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<DynCanonicalValue>,
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(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<T: BundleValue> Module<T>
where
T::Type: BundleType<Value = T>,
{
/// 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<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::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<DynBundle> {
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<DynBundle>) -> 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<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) -> Interned<T::Type> {
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<'a, CD, I, V: Value> {
scoped_name: ScopedNameId,
source_location: SourceLocation,
clock_domain: CD,
init: I,
ty: V::Type,
stmts: &'a mut Vec<Stmt<ModuleBuilding>>,
}
impl<'a, CD> RegBuilder<'a, CD, (), ()> {
pub fn no_reset<V: Value>(self, ty: V::Type) -> RegBuilder<'a, CD, Option<Expr<V>>, 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<V: Value + Default>(self) -> RegBuilder<'a, CD, Option<Expr<V>>, V>
where
V::Type: FixedType<Value = V>,
{
self.reset(V::default().to_expr())
}
pub fn opt_reset<T: Type>(
self,
ty: T,
init: Option<impl ToExpr<Type = T>>,
) -> RegBuilder<'a, CD, Option<Expr<T::Value>>, 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<V: Value>(self, init: Expr<V>) -> RegBuilder<'a, CD, Option<Expr<V>>, V>
where
V::Type: Type<Value = V>,
{
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 = <ClockDomain as ToExpr>::Type>,
) -> RegBuilder<'a, Expr<ClockDomain>, 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<V: Value> RegBuilder<'_, Expr<ClockDomain>, Option<Expr<V>>, V>
where
V::Type: Type<Value = V>,
{
#[track_caller]
pub fn build(self) -> Expr<V> {
let Self {
scoped_name,
source_location,
clock_domain,
init,
ty,
stmts,
} = self;
let reg =
Reg::<V::Type>::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<T: BundleValue, Kind: ModuleKindTrait> ModuleBuilder<T, Kind>
where
T::Type: BundleType<Value = T>,
{
#[track_caller]
pub fn io_with_loc<IO: Value>(
&mut self,
name: &str,
source_location: SourceLocation,
is_input: bool,
ty: IO::Type,
) -> Expr<IO>
where
IO::Type: Type<Value = IO>,
{
let module_io = ModuleIO::<IO::Type>::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<IO: Value>(
&mut self,
implicit_name: ImplicitName<'_>,
is_input: bool,
ty: IO::Type,
) -> Expr<IO>
where
IO::Type: Type<Value = IO>,
{
self.io_with_loc(implicit_name.0, SourceLocation::caller(), is_input, ty)
}
#[track_caller]
pub fn input_with_loc<IO: Value>(
&mut self,
name: &str,
source_location: SourceLocation,
ty: IO::Type,
) -> Expr<IO>
where
IO::Type: Type<Value = IO>,
{
self.io_with_loc(name, source_location, true, ty)
}
#[track_caller]
pub fn output_with_loc<IO: Value>(
&mut self,
name: &str,
source_location: SourceLocation,
ty: IO::Type,
) -> Expr<IO>
where
IO::Type: Type<Value = IO>,
{
self.io_with_loc(name, source_location, false, ty)
}
#[track_caller]
pub fn input<IO: Value>(&mut self, implicit_name: ImplicitName<'_>, ty: IO::Type) -> Expr<IO>
where
IO::Type: Type<Value = IO>,
{
self.input_with_loc(implicit_name.0, SourceLocation::caller(), ty)
}
#[track_caller]
pub fn output<IO: Value>(&mut self, implicit_name: ImplicitName<'_>, ty: IO::Type) -> Expr<IO>
where
IO::Type: Type<Value = IO>,
{
self.output_with_loc(implicit_name.0, SourceLocation::caller(), ty)
}
#[track_caller]
pub fn run<F: FnOnce(&mut Self)>(name: &str, f: F) -> Interned<Module<T>> {
Self::run_with_loc(name, SourceLocation::caller(), f)
}
#[track_caller]
pub fn try_run<F: FnOnce(&mut Self) -> Result<(), E>, E>(
name: &str,
f: F,
) -> Result<Interned<Module<T>>, E> {
Self::try_run_with_loc(name, SourceLocation::caller(), f)
}
#[track_caller]
pub fn run_with_loc<F: FnOnce(&mut Self)>(
name: &str,
source_location: SourceLocation,
f: F,
) -> Interned<Module<T>> {
Self::try_run_with_loc(name, source_location, |this| -> Result<(), Infallible> {
f(this);
Ok(())
})
.unwrap()
}
#[track_caller]
pub fn try_run_with_loc<F: FnOnce(&mut Self) -> Result<(), E>, E>(
name: &str,
source_location: SourceLocation,
f: F,
) -> Result<Interned<Module<T>>, 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<V: Value>(&mut self, target: Expr<V>, 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<TargetedAnnotation>| {
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<T: BundleValue> ModuleBuilder<T, ExternModule>
where
T::Type: BundleType<Value = T>,
{
#[track_caller]
pub fn verilog_name(&mut 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.body.verilog_name = name.intern();
}
pub fn parameter(&mut self, name: impl AsRef<str>, value: ExternModuleParameterValue) {
self.body.parameters.push(ExternModuleParameter {
name: name.as_ref().intern(),
value,
});
}
pub fn parameter_int(&mut self, name: impl AsRef<str>, value: impl Into<BigInt>) {
self.body.parameters.push(ExternModuleParameter {
name: name.as_ref().intern(),
value: ExternModuleParameterValue::Integer(value.into()),
});
}
pub fn parameter_str(&mut self, name: impl AsRef<str>, value: impl AsRef<str>) {
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<str>, raw_verilog: impl AsRef<str>) {
self.body.parameters.push(ExternModuleParameter {
name: name.as_ref().intern(),
value: ExternModuleParameterValue::RawVerilog(raw_verilog.as_ref().intern()),
});
}
}
impl<T: BundleValue> ModuleBuilder<T, NormalModule>
where
T::Type: BundleType<Value = T>,
{
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<V: Value>(
&mut self,
name: &str,
source_location: SourceLocation,
ty: V::Type,
) -> Expr<V>
where
V::Type: Type<Value = V>,
{
let wire = Wire::<V::Type>::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<V: Value>(&mut self, implicit_name: ImplicitName<'_>, ty: V::Type) -> Expr<V>
where
V::Type: Type<Value = V>,
{
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_<Ty>(&mut self, cond: Expr<IntValue<Ty>>) -> IfScope
where
Ty: FixedOrDynIntType<
1,
Signed = ConstBool<false>,
CanonicalType = DynUIntType,
CanonicalValue = DynUInt,
>,
{
self.if_with_loc(cond, SourceLocation::caller())
}
pub fn if_with_loc<Ty>(
&mut self,
cond: Expr<IntValue<Ty>>,
source_location: SourceLocation,
) -> IfScope
where
Ty: FixedOrDynIntType<
1,
Signed = ConstBool<false>,
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<Enum: EnumValue>(
&mut self,
base: Expr<Enum>,
source_location: SourceLocation,
) -> EnumMatchVariantsIter<Enum::Type>
where
Enum::Type: EnumType<Value = Enum>,
{
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_<Ty: Type>(&mut self, expr: impl ToExpr<Type = Ty>) -> Ty::MatchVariantsIter {
self.match_with_loc(expr, SourceLocation::caller())
}
pub fn match_with_loc<Ty: Type>(
&mut self,
expr: impl ToExpr<Type = Ty>,
source_location: SourceLocation,
) -> Ty::MatchVariantsIter {
Ty::match_variants(expr.to_expr(), self, source_location)
}
#[track_caller]
pub fn connect_any_with_loc<Lhs: ToExpr, Rhs: ToExpr>(
&mut self,
lhs: Lhs,
rhs: Rhs,
source_location: SourceLocation,
) where
Lhs::Type: Connect<Rhs::Type>
+ Type<
CanonicalType = <Rhs::Type as Type>::CanonicalType,
CanonicalValue = <Rhs::Type as Type>::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<Lhs: ToExpr, Rhs: ToExpr>(&mut self, lhs: Lhs, rhs: Rhs)
where
Lhs::Type: Connect<Rhs::Type>
+ Type<
CanonicalType = <Rhs::Type as Type>::CanonicalType,
CanonicalValue = <Rhs::Type as Type>::CanonicalValue,
>,
{
self.connect_any_with_loc(lhs, rhs, SourceLocation::caller())
}
#[track_caller]
pub fn connect<V: Value>(&mut self, lhs: Expr<V>, rhs: Expr<V>)
where
V::Type: Type<Value = V>,
{
self.connect_with_loc(lhs, rhs, SourceLocation::caller())
}
#[track_caller]
pub fn connect_with_loc<V: Value>(
&mut self,
lhs: Expr<V>,
rhs: Expr<V>,
source_location: SourceLocation,
) where
V::Type: Type<Value = V>,
{
self.connect_any_with_loc(lhs, rhs, source_location)
}
pub fn instance_with_loc<T2: BundleValue>(
&mut self,
name: &str,
instantiated: Interned<Module<T2>>,
source_location: SourceLocation,
) -> Expr<T2>
where
T2::Type: BundleType<Value = T2>,
{
let instance = Instance::<T2> {
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<T2: BundleValue>(
&mut self,
implicit_name: ImplicitName<'_>,
instantiated: Interned<Module<T2>>,
) -> Expr<T2>
where
T2::Type: BundleType<Value = T2>,
{
self.instance_with_loc(implicit_name.0, instantiated, SourceLocation::caller())
}
#[track_caller]
fn memory_impl<VA: ValueArrayOrSlice + ?Sized>(
&mut self,
name: &str,
mem_element_type: VA::ElementType,
source_location: SourceLocation,
) -> MemBuilder<VA> {
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<VA: ValueArrayOrSlice + ?Sized>(
&mut self,
name: &str,
mem_array_type: ArrayType<VA>,
source_location: SourceLocation,
) -> MemBuilder<VA> {
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<VA: ValueArrayOrSlice + ?Sized>(
&mut self,
implicit_name: ImplicitName<'_>,
mem_array_type: ArrayType<VA>,
) -> MemBuilder<VA> {
self.memory_array_with_loc(implicit_name.0, mem_array_type, SourceLocation::caller())
}
#[track_caller]
pub fn memory_with_init_and_loc<VA: ValueArrayOrSlice + ?Sized>(
&mut self,
name: &str,
initial_value: impl ToExpr<Type = ArrayType<VA>>,
source_location: SourceLocation,
) -> MemBuilder<VA> {
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<VA: ValueArrayOrSlice + ?Sized>(
&mut self,
implicit_name: ImplicitName<'_>,
initial_value: impl ToExpr<Type = ArrayType<VA>>,
) -> MemBuilder<VA> {
self.memory_with_init_and_loc(
implicit_name.0,
initial_value.to_expr(),
SourceLocation::caller(),
)
}
#[track_caller]
pub fn memory_with_loc<Element: Value>(
&mut self,
name: &str,
element_type: Element::Type,
source_location: SourceLocation,
) -> MemBuilder<[Element]>
where
Element::Type: Type<Value = Element>,
{
self.memory_impl(name, element_type, source_location)
}
#[track_caller]
pub fn memory<Element: Value>(
&mut self,
implicit_name: ImplicitName<'_>,
element_type: Element::Type,
) -> MemBuilder<[Element]>
where
Element::Type: Type<Value = Element>,
{
self.memory_with_loc(implicit_name.0, element_type, SourceLocation::caller())
}
}
#[derive(Clone, Eq, PartialEq, Hash)]
pub struct ModuleIO<T: Type> {
containing_module_name: NameId,
field_type: FieldType<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.field_type().ty)
.finish_non_exhaustive()
}
}
impl<T: Type> ModuleIO<T> {
pub fn canonical(&self) -> ModuleIO<T::CanonicalType> {
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<Interned<dyn DynCanonicalType>> {
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<T> {
&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<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.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<str>,
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!()
}
}
}