forked from libre-chip/fayalite
498 lines
14 KiB
Rust
498 lines
14 KiB
Rust
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
// See Notices.txt for copyright information
|
|
use crate::{
|
|
array::Array,
|
|
bundle::{Bundle, BundleField},
|
|
expr::{Expr, Flow, ToExpr, ValueType, value_category::ValueCategoryExpr},
|
|
intern::{Intern, Interned},
|
|
memory::{DynPortType, MemPort},
|
|
module::{Instance, ModuleIO, TargetName},
|
|
reg::Reg,
|
|
reset::{AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset},
|
|
source_location::SourceLocation,
|
|
ty::{CanonicalType, Type},
|
|
wire::Wire,
|
|
};
|
|
use std::fmt;
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
|
pub struct TargetPathBundleField {
|
|
pub name: Interned<str>,
|
|
}
|
|
|
|
impl fmt::Display for TargetPathBundleField {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, ".{}", self.name)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
|
pub struct TargetPathArrayElement {
|
|
pub index: usize,
|
|
}
|
|
|
|
impl fmt::Display for TargetPathArrayElement {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "[{}]", self.index)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
|
pub struct TargetPathDynArrayElement {}
|
|
|
|
impl fmt::Display for TargetPathDynArrayElement {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "[<dyn>]")
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
|
pub enum TargetPathElement {
|
|
BundleField(TargetPathBundleField),
|
|
ArrayElement(TargetPathArrayElement),
|
|
DynArrayElement(TargetPathDynArrayElement),
|
|
}
|
|
|
|
impl From<TargetPathBundleField> for TargetPathElement {
|
|
fn from(value: TargetPathBundleField) -> Self {
|
|
Self::BundleField(value)
|
|
}
|
|
}
|
|
|
|
impl From<TargetPathArrayElement> for TargetPathElement {
|
|
fn from(value: TargetPathArrayElement) -> Self {
|
|
Self::ArrayElement(value)
|
|
}
|
|
}
|
|
|
|
impl From<TargetPathDynArrayElement> for TargetPathElement {
|
|
fn from(value: TargetPathDynArrayElement) -> Self {
|
|
Self::DynArrayElement(value)
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for TargetPathElement {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::BundleField(v) => v.fmt(f),
|
|
Self::ArrayElement(v) => v.fmt(f),
|
|
Self::DynArrayElement(v) => v.fmt(f),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TargetPathElement {
|
|
pub fn canonical_ty(&self, parent: Interned<Target>) -> CanonicalType {
|
|
match self {
|
|
&Self::BundleField(TargetPathBundleField { name }) => {
|
|
let parent_ty = Bundle::from_canonical(parent.canonical_ty());
|
|
let field = parent_ty
|
|
.field_by_name(name)
|
|
.expect("field name is known to be a valid field of parent type");
|
|
field.ty
|
|
}
|
|
&Self::ArrayElement(TargetPathArrayElement { index }) => {
|
|
let parent_ty = Array::<CanonicalType>::from_canonical(parent.canonical_ty());
|
|
assert!(index < parent_ty.len());
|
|
parent_ty.element()
|
|
}
|
|
Self::DynArrayElement(_) => {
|
|
let parent_ty = Array::<CanonicalType>::from_canonical(parent.canonical_ty());
|
|
parent_ty.element()
|
|
}
|
|
}
|
|
}
|
|
pub fn flow(&self, parent: Interned<Target>) -> Flow {
|
|
match self {
|
|
Self::BundleField(v) => {
|
|
let parent_ty = Bundle::from_canonical(parent.canonical_ty());
|
|
let field = parent_ty
|
|
.field_by_name(v.name)
|
|
.expect("field name is known to be a valid field of parent type");
|
|
parent.flow().flip_if(field.flipped)
|
|
}
|
|
Self::ArrayElement(_) => parent.flow(),
|
|
Self::DynArrayElement(_) => parent.flow(),
|
|
}
|
|
}
|
|
pub fn is_static(&self) -> bool {
|
|
match self {
|
|
Self::BundleField(_) | Self::ArrayElement(_) => true,
|
|
Self::DynArrayElement(_) => false,
|
|
}
|
|
}
|
|
}
|
|
|
|
macro_rules! impl_target_base {
|
|
(
|
|
$(#[$enum_meta:meta])*
|
|
$enum_vis:vis enum $TargetBase:ident {
|
|
$(
|
|
$(#[from = $from:ident])?
|
|
#[is = $is_fn:ident]
|
|
#[to = $to_fn:ident]
|
|
$(#[$variant_meta:meta])*
|
|
$Variant:ident($VariantTy:ty),
|
|
)*
|
|
}
|
|
) => {
|
|
$(#[$enum_meta])*
|
|
$enum_vis enum $TargetBase {
|
|
$(
|
|
$(#[$variant_meta])*
|
|
$Variant($VariantTy),
|
|
)*
|
|
}
|
|
|
|
impl fmt::Debug for $TargetBase {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
$(Self::$Variant(v) => v.fmt(f),)*
|
|
}
|
|
}
|
|
}
|
|
|
|
$($(
|
|
impl From<$VariantTy> for $TargetBase {
|
|
fn $from(value: $VariantTy) -> Self {
|
|
Self::$Variant(value)
|
|
}
|
|
}
|
|
|
|
impl From<$VariantTy> for Target {
|
|
fn $from(value: $VariantTy) -> Self {
|
|
$TargetBase::$Variant(value).into()
|
|
}
|
|
}
|
|
)*)?
|
|
|
|
impl $TargetBase {
|
|
$(
|
|
$enum_vis fn $is_fn(&self) -> bool {
|
|
self.$to_fn().is_some()
|
|
}
|
|
$enum_vis fn $to_fn(&self) -> Option<&$VariantTy> {
|
|
if let Self::$Variant(retval) = self {
|
|
Some(retval)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
)*
|
|
$enum_vis fn must_connect_to(&self) -> bool {
|
|
match self {
|
|
$(Self::$Variant(v) => v.must_connect_to(),)*
|
|
}
|
|
}
|
|
$enum_vis fn flow(&self) -> Flow {
|
|
match self {
|
|
$(Self::$Variant(v) => v.flow(),)*
|
|
}
|
|
}
|
|
$enum_vis fn source_location(&self) -> SourceLocation {
|
|
match self {
|
|
$(Self::$Variant(v) => v.source_location(),)*
|
|
}
|
|
}
|
|
}
|
|
|
|
impl ValueType for $TargetBase {
|
|
type Type = CanonicalType;
|
|
type ValueCategory = ValueCategoryExpr;
|
|
|
|
fn ty(&self) -> Self::Type {
|
|
match self {
|
|
$(Self::$Variant(v) => v.ty().canonical(),)*
|
|
}
|
|
}
|
|
}
|
|
|
|
impl ToExpr for $TargetBase {
|
|
fn to_expr(&self) -> Expr<Self::Type> {
|
|
match self {
|
|
$(Self::$Variant(v) => Expr::canonical(v.to_expr()),)*
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
impl_target_base! {
|
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
|
pub enum TargetBase {
|
|
#[from = from]
|
|
#[is = is_module_io]
|
|
#[to = module_io]
|
|
ModuleIO(ModuleIO<CanonicalType>),
|
|
#[from = from]
|
|
#[is = is_mem_port]
|
|
#[to = mem_port]
|
|
MemPort(MemPort<DynPortType>),
|
|
#[is = is_reg]
|
|
#[to = reg]
|
|
Reg(Reg<CanonicalType, Reset>),
|
|
#[is = is_reg_sync]
|
|
#[to = reg_sync]
|
|
RegSync(Reg<CanonicalType, SyncReset>),
|
|
#[is = is_reg_async]
|
|
#[to = reg_async]
|
|
RegAsync(Reg<CanonicalType, AsyncReset>),
|
|
#[from = from]
|
|
#[is = is_wire]
|
|
#[to = wire]
|
|
Wire(Wire<CanonicalType>),
|
|
#[from = from]
|
|
#[is = is_instance]
|
|
#[to = instance]
|
|
Instance(Instance<Bundle>),
|
|
}
|
|
}
|
|
|
|
impl<R: ResetType> From<Reg<CanonicalType, R>> for TargetBase {
|
|
fn from(value: Reg<CanonicalType, R>) -> Self {
|
|
struct Dispatch;
|
|
impl ResetTypeDispatch for Dispatch {
|
|
type Input<T: ResetType> = Reg<CanonicalType, T>;
|
|
type Output<T: ResetType> = TargetBase;
|
|
|
|
fn reset(self, input: Self::Input<Reset>) -> Self::Output<Reset> {
|
|
TargetBase::Reg(input)
|
|
}
|
|
|
|
fn sync_reset(self, input: Self::Input<SyncReset>) -> Self::Output<SyncReset> {
|
|
TargetBase::RegSync(input)
|
|
}
|
|
|
|
fn async_reset(self, input: Self::Input<AsyncReset>) -> Self::Output<AsyncReset> {
|
|
TargetBase::RegAsync(input)
|
|
}
|
|
}
|
|
R::dispatch(value, Dispatch)
|
|
}
|
|
}
|
|
|
|
impl<R: ResetType> From<Reg<CanonicalType, R>> for Target {
|
|
fn from(value: Reg<CanonicalType, R>) -> Self {
|
|
TargetBase::from(value).into()
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for TargetBase {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "{:?}", self.target_name())
|
|
}
|
|
}
|
|
|
|
impl TargetBase {
|
|
pub fn target_name(&self) -> TargetName {
|
|
match self {
|
|
TargetBase::ModuleIO(v) => TargetName(v.scoped_name(), None),
|
|
TargetBase::MemPort(v) => TargetName(v.mem_name(), Some(v.port_name())),
|
|
TargetBase::Reg(v) => TargetName(v.scoped_name(), None),
|
|
TargetBase::RegSync(v) => TargetName(v.scoped_name(), None),
|
|
TargetBase::RegAsync(v) => TargetName(v.scoped_name(), None),
|
|
TargetBase::Wire(v) => TargetName(v.scoped_name(), None),
|
|
TargetBase::Instance(v) => TargetName(v.scoped_name(), None),
|
|
}
|
|
}
|
|
pub fn canonical_ty(&self) -> CanonicalType {
|
|
match self {
|
|
TargetBase::ModuleIO(v) => v.ty(),
|
|
TargetBase::MemPort(v) => v.ty().canonical(),
|
|
TargetBase::Reg(v) => v.ty(),
|
|
TargetBase::RegSync(v) => v.ty(),
|
|
TargetBase::RegAsync(v) => v.ty(),
|
|
TargetBase::Wire(v) => v.ty(),
|
|
TargetBase::Instance(v) => v.ty().canonical(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
|
pub struct TargetChild {
|
|
parent: Interned<Target>,
|
|
path_element: Interned<TargetPathElement>,
|
|
canonical_ty: CanonicalType,
|
|
flow: Flow,
|
|
}
|
|
|
|
impl fmt::Debug for TargetChild {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let Self {
|
|
parent,
|
|
path_element,
|
|
canonical_ty: _,
|
|
flow: _,
|
|
} = self;
|
|
parent.fmt(f)?;
|
|
fmt::Display::fmt(path_element, f)
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for TargetChild {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let Self {
|
|
parent,
|
|
path_element,
|
|
canonical_ty: _,
|
|
flow: _,
|
|
} = self;
|
|
parent.fmt(f)?;
|
|
path_element.fmt(f)
|
|
}
|
|
}
|
|
|
|
impl TargetChild {
|
|
pub fn new(parent: Interned<Target>, path_element: Interned<TargetPathElement>) -> Self {
|
|
Self {
|
|
parent,
|
|
path_element,
|
|
canonical_ty: path_element.canonical_ty(parent),
|
|
flow: path_element.flow(parent),
|
|
}
|
|
}
|
|
pub fn parent(self) -> Interned<Target> {
|
|
self.parent
|
|
}
|
|
pub fn path_element(self) -> Interned<TargetPathElement> {
|
|
self.path_element
|
|
}
|
|
pub fn canonical_ty(self) -> CanonicalType {
|
|
self.canonical_ty
|
|
}
|
|
pub fn flow(self) -> Flow {
|
|
self.flow
|
|
}
|
|
pub fn bundle_field(self) -> Option<BundleField> {
|
|
if let TargetPathElement::BundleField(TargetPathBundleField { name }) = *self.path_element {
|
|
let parent_ty = Bundle::from_canonical(self.parent.canonical_ty());
|
|
Some(
|
|
parent_ty
|
|
.field_by_name(name)
|
|
.expect("field name known to be a valid field of parent"),
|
|
)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
|
pub enum Target {
|
|
Base(Interned<TargetBase>),
|
|
Child(TargetChild),
|
|
}
|
|
|
|
impl From<TargetBase> for Target {
|
|
fn from(value: TargetBase) -> Self {
|
|
Self::Base(Intern::intern_sized(value))
|
|
}
|
|
}
|
|
|
|
impl From<TargetChild> for Target {
|
|
fn from(value: TargetChild) -> Self {
|
|
Self::Child(value)
|
|
}
|
|
}
|
|
|
|
impl From<Interned<TargetBase>> for Target {
|
|
fn from(value: Interned<TargetBase>) -> Self {
|
|
Self::Base(value)
|
|
}
|
|
}
|
|
|
|
impl Target {
|
|
pub fn base(&self) -> Interned<TargetBase> {
|
|
let mut target = self;
|
|
loop {
|
|
match target {
|
|
Self::Base(v) => break *v,
|
|
Self::Child(v) => target = &v.parent,
|
|
}
|
|
}
|
|
}
|
|
pub fn child(&self) -> Option<TargetChild> {
|
|
match *self {
|
|
Target::Base(_) => None,
|
|
Target::Child(v) => Some(v),
|
|
}
|
|
}
|
|
pub fn is_static(&self) -> bool {
|
|
let mut target = self;
|
|
loop {
|
|
match target {
|
|
Self::Base(_) => return true,
|
|
Self::Child(v) if !v.path_element().is_static() => return false,
|
|
Self::Child(v) => target = &v.parent,
|
|
}
|
|
}
|
|
}
|
|
#[must_use]
|
|
pub fn join(&self, path_element: Interned<TargetPathElement>) -> Self {
|
|
TargetChild::new(self.intern(), path_element).into()
|
|
}
|
|
pub fn flow(&self) -> Flow {
|
|
match self {
|
|
Self::Base(v) => v.flow(),
|
|
Self::Child(v) => v.flow(),
|
|
}
|
|
}
|
|
pub fn canonical_ty(&self) -> CanonicalType {
|
|
match self {
|
|
Target::Base(v) => v.canonical_ty(),
|
|
Target::Child(v) => v.canonical_ty(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Target {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::Base(v) => v.fmt(f),
|
|
Self::Child(v) => v.fmt(f),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for Target {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::Base(v) => v.fmt(f),
|
|
Self::Child(v) => v.fmt(f),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub trait GetTarget {
|
|
fn target(&self) -> Option<Interned<Target>>;
|
|
}
|
|
|
|
impl GetTarget for bool {
|
|
fn target(&self) -> Option<Interned<Target>> {
|
|
None
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized + GetTarget + Send + Sync + 'static> GetTarget for Interned<T> {
|
|
fn target(&self) -> Option<Interned<Target>> {
|
|
T::target(self)
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized + GetTarget> GetTarget for &'_ T {
|
|
fn target(&self) -> Option<Interned<Target>> {
|
|
T::target(self)
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized + GetTarget> GetTarget for &'_ mut T {
|
|
fn target(&self) -> Option<Interned<Target>> {
|
|
T::target(self)
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized + GetTarget> GetTarget for Box<T> {
|
|
fn target(&self) -> Option<Interned<Target>> {
|
|
T::target(self)
|
|
}
|
|
}
|