use crate::port::PortDirectionality;
use crate::{
ConvertibleToPortSlice, ConvertibleToPortSliceVec, IO, ModInst, PipelineConfig, Port, PortSlice,
};
use std::sync::Arc;
impl PortSlice {
pub fn specify_net_name(&self, net: &str) {
if let Port::ModInst { .. } = &self.port {
{
let core_rc = self.get_mod_def_core();
let mut core = core_rc.write();
if !core.specified_net_names.insert(net.to_string()) {
panic!(
"Net \"{}\" has already been manually specified in module {}.",
net, core.name
);
};
}
let this = self.to_port_slice();
let width = this.port.io().width();
let other = crate::connection::connected_item::Wire {
name: net.to_string(),
width,
msb: self.msb,
lsb: self.lsb,
};
self.port
.get_port_connections_define_if_missing()
.write()
.add(this, other);
} else {
panic!(
"{} only works on ports (or slices of ports) on module instances",
stringify!(specify_net_name)
);
}
}
pub fn connect<T: ConvertibleToPortSlice>(&self, other: &T) {
self.connect_generic(other, None);
}
pub fn connect_pipeline<T: ConvertibleToPortSlice>(&self, other: &T, pipeline: PipelineConfig) {
self.connect_generic(other, Some(pipeline));
}
pub fn todo_jam_connect<T: ConvertibleToPortSliceVec>(&self, other: &T) {
let left = self.to_port_slice_vec();
let right = other.to_port_slice_vec();
Self::todo_jam_connect_port_slices(&left, &right);
}
pub(crate) fn todo_jam_connect_port_slices(left: &[PortSlice], right: &[PortSlice]) {
let mut left_idx = 0usize;
let mut right_idx = 0usize;
let mut left_lsb = 0usize;
let mut right_lsb = 0usize;
while left_idx < left.len() && right_idx < right.len() {
let left_width = left[left_idx].width();
let right_width = right[right_idx].width();
if left_lsb == left_width {
left_idx += 1;
left_lsb = 0;
continue;
}
if right_lsb == right_width {
right_idx += 1;
right_lsb = 0;
continue;
}
let chunk_width = usize::min(left_width - left_lsb, right_width - right_lsb);
left[left_idx]
.slice_with_offset_and_width(left_lsb, chunk_width)
.connect(&right[right_idx].slice_with_offset_and_width(right_lsb, chunk_width));
left_lsb += chunk_width;
right_lsb += chunk_width;
if left_lsb == left_width {
left_idx += 1;
left_lsb = 0;
}
if right_lsb == right_width {
right_idx += 1;
right_lsb = 0;
}
}
mark_jam_remainder(left, left_idx, left_lsb);
mark_jam_remainder(right, right_idx, right_lsb);
}
pub(crate) fn connect_generic<T: ConvertibleToPortSlice>(
&self,
other: &T,
pipeline: Option<PipelineConfig>,
) {
let other_as_slice = other.to_port_slice();
if !Arc::ptr_eq(&self.get_mod_def_core(), &other_as_slice.get_mod_def_core()) {
panic!(
"Cannot connect {} and {} because they are in different module definitions",
self.debug_string(),
other_as_slice.debug_string()
);
}
if self.width() != other_as_slice.width() {
panic!(
"Width mismatch when connecting {} and {}",
self.debug_string(),
other_as_slice.debug_string()
);
}
let self_directionality = self.get_directionality();
let other_directionality = other_as_slice.get_directionality();
let generate_incompatibility_mesage = || {
format!(
"Cannot connect {} and {} because they have incompatible directions.",
self.debug_string(),
other_as_slice.debug_string()
)
};
if !self_directionality.compatible_with(&other_directionality) {
panic!("{}", generate_incompatibility_mesage());
}
if let Some(pipeline) = pipeline {
let repeater_mod_def = pipeline.to_mod_def(self.width());
let mod_def = self.get_mod_def();
let repeater_inst_name = mod_def.resolve_pipeline_instance_name(&pipeline);
let (driver, receiver) = match (self_directionality, other_directionality) {
(PortDirectionality::Driver, PortDirectionality::Receiver) => {
(self, &other_as_slice)
}
(PortDirectionality::Receiver, PortDirectionality::Driver) => {
(&other_as_slice, self)
}
(PortDirectionality::InOut, _) => {
panic!("Cannot pipeline InOut port {}", self.debug_string())
}
(_, PortDirectionality::InOut) => panic!(
"Cannot pipeline InOut port {}",
other_as_slice.debug_string()
),
_ => {
panic!("{}", generate_incompatibility_mesage());
}
};
let repeater_instance =
mod_def.instantiate(&repeater_mod_def, Some(&repeater_inst_name), None);
let mod_def_clk = if mod_def.has_port(&pipeline.clk) {
mod_def.get_port(&pipeline.clk)
} else {
mod_def.add_port(&pipeline.clk, IO::Input(1))
};
repeater_instance.get_port("clk").connect(&mod_def_clk);
repeater_instance.get_port("in").connect(driver);
repeater_instance.get_port("out").connect(receiver);
repeater_instance.get_port("out_stages").unused();
} else {
self.port
.get_port_connections_define_if_missing()
.write()
.add(self.clone(), other_as_slice.clone());
other_as_slice
.port
.get_port_connections_define_if_missing()
.write()
.add(other_as_slice.clone(), self.clone());
}
}
pub fn connect_through<T: ConvertibleToPortSlice>(
&self,
other: &T,
through: &[&ModInst],
prefix: impl AsRef<str>,
) {
let mut through_generic = Vec::new();
for inst in through {
through_generic.push((*inst, None));
}
self.connect_through_generic(other, &through_generic, prefix);
}
pub fn connect_through_generic<T: ConvertibleToPortSlice>(
&self,
other: &T,
through: &[(&ModInst, Option<PipelineConfig>)],
prefix: impl AsRef<str>,
) {
if through.is_empty() {
self.connect(other);
return;
}
let flipped = format!("{}_flipped", prefix.as_ref());
let original = format!("{}_original", prefix.as_ref());
for (i, (inst, pipeline)) in through.iter().enumerate() {
let (flipped_port, original_port) = self.feedthrough_generic(
&inst.get_mod_def(),
&flipped,
&original,
pipeline.as_ref().cloned(),
);
let flipped_port = flipped_port.assign_to_inst(inst);
let original_port = original_port.assign_to_inst(inst);
if i == 0 {
self.connect(&flipped_port);
} else {
through[i - 1].0.get_port(&original).connect(&flipped_port);
}
if i == through.len() - 1 {
other.to_port_slice().connect(&original_port);
}
}
}
}
fn mark_jam_remainder(port_slices: &[PortSlice], mut idx: usize, lsb: usize) {
if idx >= port_slices.len() {
return;
}
if lsb > 0 {
let width = port_slices[idx].width();
port_slices[idx]
.slice_with_offset_and_width(lsb, width - lsb)
.unused_or_tieoff(0);
idx += 1;
}
for port_slice in port_slices.iter().skip(idx) {
port_slice.unused_or_tieoff(0);
}
}