fayalite 0.2.0

Hardware Description Language embedded in Rust, using FIRRTL's semantics
Documentation
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
    expr::{Expr, Flow, ToExpr},
    intern::Interned,
    module::{IncompleteDeclaration, NameId, ScopedNameId, StmtDeclaration, StmtWire},
    source_location::SourceLocation,
    ty::{CanonicalType, Type},
};
use std::{cell::RefCell, fmt, rc::Rc};

#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct Wire<T: Type> {
    name: ScopedNameId,
    source_location: SourceLocation,
    ty: T,
}

impl<T: Type + fmt::Debug> fmt::Debug for Wire<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "Wire({:?}: ", self.name)?;
        self.ty.fmt(f)?;
        f.write_str(")")
    }
}

impl<T: Type> Wire<T> {
    pub fn canonical(&self) -> Wire<CanonicalType> {
        let Self {
            name,
            source_location,
            ref ty,
        } = *self;
        Wire {
            name,
            source_location,
            ty: ty.canonical(),
        }
    }
    pub fn ty(&self) -> T {
        self.ty
    }
    pub fn new_unchecked(
        scoped_name: ScopedNameId,
        source_location: SourceLocation,
        ty: T,
    ) -> Self {
        Self {
            name: scoped_name,
            source_location,
            ty,
        }
    }
    pub fn source_location(&self) -> SourceLocation {
        self.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.name.0
    }
    pub fn name(&self) -> Interned<str> {
        self.name_id().0
    }
    pub fn name_id(&self) -> NameId {
        self.name.1
    }
    pub fn scoped_name(&self) -> ScopedNameId {
        self.name
    }
    pub fn flow(&self) -> Flow {
        Flow::Duplex
    }
    pub fn must_connect_to(&self) -> bool {
        true
    }
}

#[derive(Clone)]
pub struct IncompleteWire {
    pub(crate) declaration: Rc<RefCell<IncompleteDeclaration>>,
}

impl IncompleteWire {
    #[track_caller]
    pub fn complete<T: Type>(&mut self, ty: T) -> Expr<T> {
        let canonical_type = ty.canonical();
        let mut declaration = self.declaration.borrow_mut();
        if let IncompleteDeclaration::Incomplete {
            name,
            source_location,
        } = *declaration
        {
            *declaration = IncompleteDeclaration::Complete(
                StmtWire {
                    annotations: (),
                    wire: Wire {
                        name,
                        source_location,
                        ty: canonical_type,
                    },
                }
                .into(),
            );
        }
        match *declaration {
            IncompleteDeclaration::Complete(StmtDeclaration::Wire(StmtWire {
                wire:
                    Wire {
                        name,
                        source_location,
                        ty: wire_ty,
                    },
                ..
            })) => {
                drop(declaration);
                assert_eq!(wire_ty, canonical_type, "type mismatch");
                Wire {
                    name,
                    source_location,
                    ty,
                }
                .to_expr()
            }
            IncompleteDeclaration::Taken => panic!("can't use wire outside of containing module"),
            IncompleteDeclaration::Complete(_) | IncompleteDeclaration::Incomplete { .. } => {
                unreachable!()
            }
        }
    }
}