use parking_lot::RwLock;
use std::hash::{Hash, Hasher};
use std::sync::{Arc, Weak};
use crate::connection::PortSliceConnections;
use crate::io::IO;
use crate::mod_inst::HierPathElem;
use crate::{
ConvertibleToPortSlice, Coordinate, MetadataKey, MetadataValue, ModDef, ModDefCore, ModInst,
PhysicalPin, PortSlice,
};
mod connect;
mod export;
mod feedthrough;
mod tieoff;
mod trace;
#[derive(Clone, Debug)]
pub enum PortDirectionality {
Driver,
Receiver,
InOut,
}
impl PortDirectionality {
pub(crate) fn compatible_with(&self, other: &PortDirectionality) -> bool {
matches!(
(self, other),
(PortDirectionality::InOut, _)
| (_, PortDirectionality::InOut)
| (PortDirectionality::Driver, PortDirectionality::Receiver)
| (PortDirectionality::Receiver, PortDirectionality::Driver)
)
}
}
#[derive(Clone, Debug)]
pub enum Port {
ModDef {
mod_def_core: Weak<RwLock<ModDefCore>>,
name: String,
},
ModInst {
hierarchy: Vec<HierPathElem>,
port_name: String,
},
}
impl PartialEq for Port {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(
Port::ModDef {
mod_def_core: a_core,
name: a_name,
},
Port::ModDef {
mod_def_core: b_core,
name: b_name,
},
) => match (a_core.upgrade(), b_core.upgrade()) {
(Some(a_rc), Some(b_rc)) => Arc::ptr_eq(&a_rc, &b_rc) && (a_name == b_name),
_ => false,
},
(
Port::ModInst {
hierarchy: a_hier,
port_name: a_port,
},
Port::ModInst {
hierarchy: b_hier,
port_name: b_port,
},
) => a_hier == b_hier && (a_port == b_port),
_ => false,
}
}
}
impl Eq for Port {}
impl Hash for Port {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
Port::ModDef { name, mod_def_core } => {
if let Some(rc) = mod_def_core.upgrade() {
Arc::as_ptr(&rc).hash(state);
} else {
(0usize).hash(state);
}
name.hash(state);
}
Port::ModInst {
port_name,
hierarchy,
} => {
for frame in hierarchy {
if let Some(rc) = frame.mod_def_core.upgrade() {
Arc::as_ptr(&rc).hash(state);
} else {
(0usize).hash(state);
}
frame.inst_name.hash(state);
}
port_name.hash(state);
}
}
}
}
impl Port {
pub fn name(&self) -> &str {
match self {
Port::ModDef { name, .. } => name,
Port::ModInst { port_name, .. } => port_name,
}
}
pub fn set_metadata(
&self,
key: impl Into<MetadataKey>,
value: impl Into<MetadataValue>,
) -> Self {
let key = key.into();
let value = value.into();
match self {
Port::ModDef { .. } => {
let core_rc = self.get_mod_def_core_where_declared();
let mut core = core_rc.write();
core.mod_def_port_metadata
.entry(self.name().to_string())
.or_default()
.insert(key, value);
}
Port::ModInst { .. } => {
let inst_name = self
.inst_name()
.expect("Port::ModInst hierarchy cannot be empty")
.to_string();
let core_rc = self.get_mod_def_core();
let mut core = core_rc.write();
core.mod_inst_port_metadata
.entry(inst_name)
.or_default()
.entry(self.name().to_string())
.or_default()
.insert(key, value);
}
}
self.clone()
}
pub fn get_metadata(&self, key: impl AsRef<str>) -> Option<MetadataValue> {
match self {
Port::ModDef { .. } => self
.get_mod_def_core_where_declared()
.read()
.mod_def_port_metadata
.get(self.name())
.and_then(|metadata| metadata.get(key.as_ref()).cloned()),
Port::ModInst { .. } => {
let inst_name = self
.inst_name()
.expect("Port::ModInst hierarchy cannot be empty");
self.get_mod_def_core()
.read()
.mod_inst_port_metadata
.get(inst_name)
.and_then(|ports| ports.get(self.name()))
.and_then(|metadata| metadata.get(key.as_ref()).cloned())
}
}
}
pub fn clear_metadata(&self, key: impl AsRef<str>) -> Self {
match self {
Port::ModDef { .. } => {
let core_rc = self.get_mod_def_core_where_declared();
let mut core = core_rc.write();
if let Some(metadata) = core.mod_def_port_metadata.get_mut(self.name()) {
metadata.remove(key.as_ref());
if metadata.is_empty() {
core.mod_def_port_metadata.remove(self.name());
}
}
}
Port::ModInst { .. } => {
let inst_name = self
.inst_name()
.expect("Port::ModInst hierarchy cannot be empty")
.to_string();
let core_rc = self.get_mod_def_core();
let mut core = core_rc.write();
if let Some(ports) = core.mod_inst_port_metadata.get_mut(&inst_name) {
if let Some(metadata) = ports.get_mut(self.name()) {
metadata.remove(key.as_ref());
if metadata.is_empty() {
ports.remove(self.name());
}
}
if ports.is_empty() {
core.mod_inst_port_metadata.remove(&inst_name);
}
}
}
}
self.clone()
}
pub fn io(&self) -> IO {
match self {
Port::ModDef { mod_def_core, name } => {
mod_def_core.upgrade().unwrap().read().ports[name].clone()
}
Port::ModInst {
hierarchy,
port_name,
..
} => {
let inst_frame = hierarchy
.last()
.expect("Port::ModInst hierarchy cannot be empty");
inst_frame.mod_def_core.upgrade().unwrap().read().instances
[inst_frame.inst_name.as_str()]
.read()
.ports[port_name.as_str()]
.clone()
}
}
}
pub(crate) fn assign_to_inst(&self, inst: &ModInst) -> Port {
match self {
Port::ModDef { name, .. } => Port::ModInst {
hierarchy: inst.hierarchy.clone(),
port_name: name.clone(),
},
_ => panic!("Already assigned to an instance."),
}
}
pub(crate) fn is_driver(&self) -> bool {
match self {
Port::ModDef { .. } => matches!(self.io(), IO::Input(_)),
Port::ModInst { .. } => matches!(self.io(), IO::Output(_)),
}
}
pub(crate) fn get_mod_def_core(&self) -> Arc<RwLock<ModDefCore>> {
match self {
Port::ModDef { mod_def_core, .. } => mod_def_core.upgrade().unwrap(),
Port::ModInst { hierarchy, .. } => hierarchy
.last()
.expect("Port::ModInst hierarchy cannot be empty")
.mod_def_core
.upgrade()
.expect("Containing ModDefCore has been dropped"),
}
}
pub(crate) fn get_mod_def_where_declared(&self) -> ModDef {
ModDef {
core: self.get_mod_def_core_where_declared(),
}
}
pub(crate) fn get_mod_def_core_where_declared(&self) -> Arc<RwLock<ModDefCore>> {
match self {
Port::ModDef { mod_def_core, .. } => mod_def_core.upgrade().unwrap(),
Port::ModInst { hierarchy, .. } => {
let last = hierarchy.last().unwrap();
last.mod_def_core
.upgrade()
.unwrap()
.read()
.instances
.get(last.inst_name.as_str())
.unwrap()
.clone()
}
}
}
pub(crate) fn as_mod_inst_port(&self, hierarchy: Vec<HierPathElem>) -> Port {
Port::ModInst {
hierarchy,
port_name: self.name().to_string(),
}
}
pub(crate) fn as_mod_def_port(&self) -> Port {
Port::ModDef {
mod_def_core: Arc::downgrade(&self.get_mod_def_core_where_declared()),
name: self.name().to_string(),
}
}
pub(crate) fn get_port_connections_define_if_missing(
&self,
) -> Arc<RwLock<PortSliceConnections>> {
let core_rc = self.get_mod_def_core();
let mut core = core_rc.write();
match self {
Port::ModDef { .. } => core
.mod_def_connections
.entry(self.name().to_string())
.or_default()
.clone(),
Port::ModInst { .. } => core
.mod_inst_connections
.entry(self.inst_name().unwrap().to_string())
.or_default()
.entry(self.name().to_string())
.or_default()
.clone(),
}
}
pub(crate) fn get_port_connections(&self) -> Option<Arc<RwLock<PortSliceConnections>>> {
let core_rc = self.get_mod_def_core();
let core = core_rc.read();
match self {
Port::ModDef { .. } => core
.mod_def_connections
.get(&self.name().to_string())
.cloned(),
Port::ModInst { .. } => core
.mod_inst_connections
.get(&self.inst_name().unwrap().to_string())
.and_then(|connections| connections.get(&self.name().to_string()).cloned()),
}
}
pub fn get_mod_inst(&self) -> Option<ModInst> {
match self {
Port::ModInst { hierarchy, .. } => Some(ModInst {
hierarchy: hierarchy.clone(),
}),
_ => None,
}
}
pub(crate) fn inst_name(&self) -> Option<&str> {
match self {
Port::ModInst { hierarchy, .. } => {
hierarchy.last().map(|frame| frame.inst_name.as_str())
}
_ => None,
}
}
pub(crate) fn get_port_name(&self) -> String {
match self {
Port::ModDef { name, .. } => name.clone(),
Port::ModInst { port_name, .. } => port_name.clone(),
}
}
pub(crate) fn debug_string(&self) -> String {
match self {
Port::ModDef { name, mod_def_core } => {
format!("{}.{}", mod_def_core.upgrade().unwrap().read().name, name)
}
Port::ModInst { port_name, .. } => {
let inst = self
.get_mod_inst()
.expect("Port::ModInst hierarchy cannot be empty");
format!("{}.{}", inst.debug_string(), port_name)
}
}
}
pub fn has_physical_pin(&self) -> bool {
self.to_port_slice().has_physical_pin()
}
pub fn get_physical_pin(&self) -> PhysicalPin {
self.to_port_slice().get_physical_pin()
}
pub fn get_coordinate(&self) -> Coordinate {
self.get_physical_pin().translation()
}
pub(crate) fn debug_string_with_width(&self) -> String {
format!("{}[{}:{}]", self.debug_string(), self.io().width() - 1, 0)
}
pub fn slice(&self, msb: usize, lsb: usize) -> PortSlice {
if msb >= self.io().width() || lsb > msb {
panic!(
"Invalid slice [{}:{}] of port {}",
msb,
lsb,
self.debug_string_with_width()
);
}
PortSlice {
port: self.clone(),
msb,
lsb,
}
}
pub fn bit(&self, index: usize) -> PortSlice {
self.slice(index, index)
}
pub fn subdivide(&self, n: usize) -> Vec<PortSlice> {
self.to_port_slice().subdivide(n)
}
pub fn to_bits(&self) -> Vec<(&str, usize)> {
let mut bits = Vec::new();
for i in 0..self.io().width() {
bits.push((self.name(), i));
}
bits
}
pub(crate) fn get_directionality(&self) -> PortDirectionality {
match self {
Port::ModDef { .. } => match self.io() {
IO::Input(_) => PortDirectionality::Driver,
IO::Output(_) => PortDirectionality::Receiver,
IO::InOut(_) => PortDirectionality::InOut,
},
Port::ModInst { .. } => match self.io() {
IO::Input(_) => PortDirectionality::Receiver,
IO::Output(_) => PortDirectionality::Driver,
IO::InOut(_) => PortDirectionality::InOut,
},
}
}
pub fn place_abutted(&self) {
self.to_port_slice().place_abutted();
}
pub fn place_abutted_to<T: ConvertibleToPortSlice>(&self, other: T) {
self.to_port_slice().place_abutted_to(other);
}
pub fn place_overlapped(&self, pin: &PhysicalPin) {
self.to_port_slice().place_overlapped(pin);
}
pub fn place_overlapped_with<T: ConvertibleToPortSlice>(&self, other: T, pin: &PhysicalPin) {
self.to_port_slice().place_overlapped_with(other, pin);
}
pub fn place_across(&self) {
self.to_port_slice().place_across();
}
pub fn place_across_from<T: ConvertibleToPortSlice>(&self, other: T) {
self.to_port_slice().place_across_from(other);
}
}
impl ConvertibleToPortSlice for Port {
fn to_port_slice(&self) -> PortSlice {
PortSlice {
port: self.clone(),
msb: self.io().width() - 1,
lsb: 0,
}
}
}