use indexmap::{IndexMap, map::Entry};
use parking_lot::RwLock;
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use crate::mod_def::dtypes::{PhysicalPin, Polygon, Range};
use crate::{ConvertibleToPortSlice, ModDef, Port, PortSlice, for_each_edge_direction};
macro_rules! place_pin_on_named_edge {
($edge_name:ident, $const_name:path) => {
paste::paste! {
#[doc = concat!(
"Places the specified pin bit on the ",
stringify!($edge_name),
" edge using the default track definition."
)]
pub fn [<place_pin_on_ $edge_name _edge>](
&self,
port_name: impl AsRef<str>,
bit: usize,
layer: impl AsRef<str>,
track_index: usize
) {
assert!(
self.shape_is_rectangular(),
"Cannot use cardinal direction names for edges for a non-rectangular shape"
);
self.place_pin_on_edge_index(port_name, bit, $const_name, layer, track_index);
}
}
};
}
macro_rules! place_pins_on_named_edge_index {
($edge_name:ident, $const_name:path) => {
paste::paste! {
#[doc = concat!(
"Places the provided pins on the ",
stringify!($edge_name),
" edge while honoring optional spacing and layer priorities."
)]
pub fn [<place_pins_on_ $edge_name _edge>]<L, S>(
&self,
pins: &[(impl AsRef<str>, usize)],
layers: L,
position_range: Range,
min_spacing: Option<f64>,
) -> Result<(), BatchPinPlacementError>
where
L: IntoIterator<Item = S>,
S: AsRef<str>,
{
self.place_pins_on_edge_index(
pins,
$const_name,
layers,
position_range,
min_spacing,
)
}
}
};
}
macro_rules! place_pins_on_named_edge_index_with_polygons {
($edge_name:ident, $const_name:path) => {
paste::paste! {
#[doc = concat!(
"Places the provided pins on the ",
stringify!($edge_name),
" edge using explicit pin and keepout polygons per layer."
)]
pub fn [<place_pins_on_ $edge_name _edge_with_polygons>](
&self,
pins: &[(impl AsRef<str>, usize)],
layers: IndexMap<String, (Polygon, Option<Polygon>)>,
position_range: Range,
min_spacing: Option<f64>,
) -> Result<(), BatchPinPlacementError> {
self.place_pins_on_edge_index_with_polygons(
pins,
$const_name,
layers,
position_range,
min_spacing,
).map(|_| ())
}
}
};
}
macro_rules! spread_pins_on_named_edge_index {
($edge_name:ident, $const_name:path) => {
paste::paste! {
#[doc = concat!(
"Evenly spreads the provided pins across the ",
stringify!($edge_name),
" edge using layer defaults."
)]
pub fn [<spread_pins_on_ $edge_name _edge>]<L, S>(
&self,
pins: &[(impl AsRef<str>, usize)],
layers: L,
options: SpreadPinsOptions,
) -> Result<(), BatchPinPlacementError>
where
L: IntoIterator<Item = S>,
S: AsRef<str>,
{
self.spread_pins_on_edge_index(
pins,
$const_name,
layers,
options,
)
}
}
};
}
macro_rules! spread_pins_on_named_edge_index_with_polygons {
($edge_name:ident, $const_name:path) => {
paste::paste! {
#[doc = concat!(
"Evenly spreads the provided pins across the ",
stringify!($edge_name),
" edge using custom per-layer pin polygons."
)]
pub fn [<spread_pins_on_ $edge_name _edge_with_polygons>](
&self,
pins: &[(impl AsRef<str>, usize)],
layers: IndexMap<String, (Polygon, Option<Polygon>)>,
options: SpreadPinsOptions,
) -> Result<(), BatchPinPlacementError> {
self.spread_pins_on_edge_index_with_polygons(
pins,
$const_name,
layers,
options,
)
}
}
};
}
macro_rules! spread_port_pins_on_named_edge {
($edge_name:ident, $const_name:path) => {
paste::paste! {
impl Port {
#[doc = concat!(
"Spreads the bits of this port on the ",
stringify!($edge_name),
" edge using layer defaults."
)]
pub fn [<spread_pins_on_ $edge_name _edge>]<L, S>(
&self,
layers: L,
options: SpreadPinsOptions,
) -> Result<(), BatchPinPlacementError>
where
L: IntoIterator<Item = S>,
S: AsRef<str>,
{
let mod_def = ModDef { core: self.get_mod_def_core() };
mod_def.[<spread_pins_on_ $edge_name _edge>](&self.to_bits(), layers, options)
}
}
}
};
}
macro_rules! spread_port_slice_pins_on_named_edge {
($edge_name:ident, $const_name:path) => {
paste::paste! {
#[doc = concat!(
"Spreads the bits of this `PortSlice` on the ",
stringify!($edge_name),
" edge using layer defaults."
)]
pub fn [<spread_pins_on_ $edge_name _edge>]<L, S>(
&self,
layers: L,
options: SpreadPinsOptions,
) -> Result<(), BatchPinPlacementError>
where
L: IntoIterator<Item = S>,
S: AsRef<str>,
{
self.get_mod_def().[<spread_pins_on_ $edge_name _edge>](
&self.to_bits(),
layers,
options,
)
}
}
};
}
#[derive(Debug, Clone)]
pub enum BatchPinPlacementError {
RanOutOfLayers { requested: usize, placed: usize },
EdgeOutOfBounds { edge_index: usize, num_edges: usize },
RequestOutOfBounds {
edge_index: usize,
edge_range: Range,
req_range: Range,
},
OffTrackRange {
layer: String,
req_range: Range,
edge_range: Range,
},
}
impl std::fmt::Display for BatchPinPlacementError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
BatchPinPlacementError::RanOutOfLayers { requested, placed } => write!(
f,
"unable to place all pins: requested {requested}, placed {placed} (ran out of layers)"
),
BatchPinPlacementError::EdgeOutOfBounds {
edge_index,
num_edges,
} => write!(
f,
"edge index {edge_index} is out of bounds ({num_edges} edges available)"
),
BatchPinPlacementError::RequestOutOfBounds {
edge_index,
edge_range,
req_range,
} => write!(
f,
"requested coordinate range {req_range} on edge {edge_index} lies outside edge span {edge_range}"
),
BatchPinPlacementError::OffTrackRange {
layer,
req_range,
edge_range,
} => write!(
f,
"requested absolute track range {req_range} on layer '{layer}' lies outside edge coverage {edge_range}"
),
}
}
}
impl std::error::Error for BatchPinPlacementError {}
#[derive(Debug, Clone, Copy)]
pub struct SpreadPinsOptions {
pub range: Range,
pub tolerance: f64,
}
impl Default for SpreadPinsOptions {
fn default() -> Self {
Self {
range: Range::default(),
tolerance: 1.0,
}
}
}
impl ModDef {
fn clone_for_pin_placement(&self) -> ModDef {
use crate::mod_def::tracks::{TrackOccupancies, TrackOccupancy};
let core = self.core.read();
let cloned_occupancies: Option<TrackOccupancies> =
core.track_occupancies.as_ref().map(|occ| {
let mut vec_maps: Vec<indexmap::IndexMap<String, TrackOccupancy>> =
Vec::with_capacity(occ.0.len());
for edge_map in occ.0.iter() {
let mut new_map = indexmap::IndexMap::new();
for (layer, o) in edge_map.iter() {
let mut no = TrackOccupancy::new(o.pin_occupancies.len());
no.pin_occupancies = o.pin_occupancies.clone();
no.keepout_occupancies = o.keepout_occupancies.clone();
new_map.insert(layer.clone(), no);
}
vec_maps.push(new_map);
}
TrackOccupancies(vec_maps)
});
let new_core = crate::mod_def::ModDefCore {
name: core.name.clone(),
ports: core.ports.clone(),
interfaces: IndexMap::new(),
instances: IndexMap::new(),
usage: core.usage.clone(),
verilog_import: None,
parameters: IndexMap::new(),
mod_inst_connections: IndexMap::new(),
mod_def_connections: IndexMap::new(),
enum_ports: IndexMap::new(),
mod_def_metadata: HashMap::new(),
mod_def_port_metadata: HashMap::new(),
mod_def_intf_metadata: HashMap::new(),
mod_inst_metadata: HashMap::new(),
mod_inst_port_metadata: HashMap::new(),
mod_inst_intf_metadata: HashMap::new(),
shape: core.shape.clone(),
layer: core.layer.clone(),
inst_placements: IndexMap::new(),
physical_pins: core.physical_pins.clone(),
port_max_distances: core.port_max_distances.clone(),
track_definitions: core.track_definitions.clone(),
track_occupancies: cloned_occupancies,
default_connection_max_distance: core.default_connection_max_distance,
specified_net_names: HashSet::new(),
pipeline_counter: 0..,
};
ModDef {
core: Arc::new(RwLock::new(new_core)),
}
}
pub fn place_pin(&self, port_name: impl AsRef<str>, bit: usize, pin: PhysicalPin) {
let mut core = self.core.write();
let io = core.ports.get(port_name.as_ref()).unwrap_or_else(|| {
panic!(
"Port {}.{} does not exist (adding physical pin)",
self.core.read().name,
port_name.as_ref()
)
});
let width = io.width();
if bit >= width {
panic!(
"Bit {} out of range for port {}.{} with width {}",
bit,
self.core.read().name,
port_name.as_ref(),
width
);
}
let pins_for_port = match core.physical_pins.entry(port_name.as_ref().to_string()) {
Entry::Occupied(e) => e.into_mut(),
Entry::Vacant(v) => v.insert(vec![None; width]),
};
pins_for_port[bit] = Some(pin);
}
pub fn unpinned_port_slices(&self) -> Vec<PortSlice> {
let core = self.core.read();
let mut missing = Vec::new();
for (port_name, io) in core.ports.iter() {
let width = io.width();
if width == 0 {
continue;
}
let port = Port::ModDef {
name: port_name.clone(),
mod_def_core: Arc::downgrade(&self.core),
};
let pins = if let Some(pins) = core.physical_pins.get(port_name) {
pins
} else {
missing.push(port.to_port_slice());
continue;
};
if pins.len() != width {
panic!(
"Physical pins for {}.{} have width {}, expected {}",
core.name,
port_name,
pins.len(),
width
);
}
let mut run = None;
for (bit, pin) in pins.iter().enumerate() {
if pin.is_none() {
match run {
Some((start, end)) => {
if bit == end + 1 {
run = Some((start, bit));
} else {
missing.push(port.slice(end, start));
run = Some((bit, bit));
}
}
None => {
run = Some((bit, bit));
}
}
}
}
if let Some((start, end)) = run {
missing.push(port.slice(end, start));
}
}
missing
}
for_each_edge_direction!(place_pin_on_named_edge);
for_each_edge_direction!(place_pins_on_named_edge_index);
for_each_edge_direction!(place_pins_on_named_edge_index_with_polygons);
for_each_edge_direction!(spread_pins_on_named_edge_index);
for_each_edge_direction!(spread_pins_on_named_edge_index_with_polygons);
pub fn place_pin_on_edge_index(
&self,
port_name: impl AsRef<str>,
bit: usize,
edge_index: usize,
layer: impl AsRef<str>,
track_index: usize,
) {
let track = self.get_track(layer.as_ref()).unwrap();
self.place_pin_on_edge_index_with_polygon(
port_name,
bit,
edge_index,
layer,
track_index,
track.pin_shape.as_ref(),
track.keepout_shape.as_ref(),
);
}
#[allow(clippy::too_many_arguments)]
pub fn place_pin_on_edge_index_with_polygon(
&self,
port_name: impl AsRef<str>,
bit: usize,
edge_index: usize,
layer: impl AsRef<str>,
track_index: usize,
pin_polygon: Option<&Polygon>,
keepout_polygon: Option<&Polygon>,
) {
let layer_ref = layer.as_ref();
if let Err(err) = self.check_pin_placement_on_edge_index_with_polygon(
edge_index,
layer_ref,
track_index,
pin_polygon,
keepout_polygon,
) {
panic!(
"Cannot place pin for {}.{}[{}] on edge {} (layer '{}', track {}): {}",
self.core.read().name,
port_name.as_ref(),
bit,
edge_index,
layer_ref,
track_index,
err
);
}
if let Some(pin_polygon) = pin_polygon {
let transform = self.track_index_to_transform(edge_index, layer_ref, track_index);
let transformed_polygon = pin_polygon.apply_transform(&transform);
let (pin_min_track, pin_max_track) =
self.track_range_for_polygon(edge_index, layer_ref, &transformed_polygon);
if let Some(keepout_polygon) = keepout_polygon {
let keepout_polygon = keepout_polygon.apply_transform(&transform);
let (keepout_min_track, keepout_max_track) =
self.track_range_for_polygon(edge_index, layer_ref, &keepout_polygon);
self.mark_pin_and_keepout_ranges(
edge_index,
layer_ref,
pin_min_track,
pin_max_track,
keepout_min_track,
keepout_max_track,
);
} else {
self.mark_pin_range(edge_index, layer_ref, pin_min_track, pin_max_track);
}
let physical_pin =
PhysicalPin::from_transform(layer_ref, pin_polygon.clone(), transform);
self.place_pin(port_name, bit, physical_pin);
} else if let Some(keepout_polygon) = keepout_polygon {
let transform = self.track_index_to_transform(edge_index, layer_ref, track_index);
let keepout_polygon = keepout_polygon.apply_transform(&transform);
let (keepout_min_track, keepout_max_track) =
self.track_range_for_polygon(edge_index, layer_ref, &keepout_polygon);
self.mark_keepout_range(edge_index, layer_ref, keepout_min_track, keepout_max_track);
}
}
pub fn place_pins_on_edge_index<L, S>(
&self,
pins: &[(impl AsRef<str>, usize)],
edge_index: usize,
layers: L,
position_range: Range,
min_spacing: Option<f64>,
) -> Result<(), BatchPinPlacementError>
where
L: IntoIterator<Item = S>,
S: AsRef<str>,
{
self.place_pins_on_edge_index_with_polygons(
pins,
edge_index,
self.get_default_layer_shapes(layers),
position_range,
min_spacing,
)
.map(|_| ())
}
pub fn place_pins_on_edge_index_with_polygons(
&self,
pins: &[(impl AsRef<str>, usize)],
edge_index: usize,
layers: IndexMap<String, (Polygon, Option<Polygon>)>,
position_range: Range,
min_spacing: Option<f64>,
) -> Result<usize, BatchPinPlacementError> {
let mut placed_count: usize = 0;
let edge = match self.get_edge(edge_index) {
Some(e) => e,
None => {
return Err(BatchPinPlacementError::EdgeOutOfBounds {
edge_index,
num_edges: self.get_num_edges(),
});
}
};
let edge_range = match edge.get_coord_range() {
Some(v) => v,
None => {
return Err(BatchPinPlacementError::EdgeOutOfBounds {
edge_index,
num_edges: self.get_num_edges(),
});
}
};
let edge_min = edge_range.min.unwrap();
let req_abs_range = Range {
min: position_range.min.map(|v| edge_min + v),
max: position_range.max.map(|v| edge_min + v),
};
if !req_abs_range.is_subset_of(&edge_range) {
return Err(BatchPinPlacementError::RequestOutOfBounds {
edge_index,
edge_range,
req_range: req_abs_range,
});
}
let req_range = req_abs_range;
struct Candidate {
position: i64,
layer_idx: usize,
track_index: usize,
}
let mut candidates: Vec<Candidate> = Vec::new();
let layer_names: Vec<&str> = layers.keys().map(|k| k.as_str()).collect();
let mut rel_min_by_layer: Vec<Option<usize>> = vec![None; layer_names.len()];
let mut period_by_layer: Vec<Option<i64>> = vec![None; layer_names.len()];
let edge_orientation = edge
.orientation()
.expect("Edge orientation must be rectilinear");
for (layer_idx, layer_name) in layer_names.iter().enumerate() {
let track = match self.get_track(layer_name) {
Some(t) => t,
None => continue,
};
if !track
.orientation
.is_compatible_with_edge_orientation(&edge_orientation)
{
continue;
}
let req_tracks = track.convert_coord_range_to_index_range(&req_range);
let edge_tracks = match edge.get_index_range(&track) {
Some(v) => v,
None => continue,
};
let edge_min_index = match edge_tracks.min {
Some(v) => v,
None => continue,
};
let (rel_min, rel_max) = match req_tracks.intersection(&edge_tracks) {
Some(Range {
min: Some(min),
max: Some(max),
}) => (min - edge_min_index, max - edge_min_index),
_ => continue,
};
assert!(rel_min >= 0);
let rel_min = rel_min as usize;
rel_min_by_layer[layer_idx] = Some(rel_min);
assert!(rel_max >= 0);
let rel_max = rel_max as usize;
period_by_layer[layer_idx] = Some(track.period);
let track_occupancy = match self.get_occupancy(edge_index, layer_name) {
Some(v) => v,
None => continue,
};
candidates.extend(
track_occupancy
.get_available_indices_in_range(rel_min, rel_max)
.ones()
.map(|track_index| {
let position = edge.get_position_on_edge(&track, track_index);
Candidate {
position,
layer_idx,
track_index,
}
}),
);
}
candidates.sort_by(|a, b| {
use std::cmp::Ordering;
match a.position.cmp(&b.position) {
Ordering::Equal => match a.layer_idx.cmp(&b.layer_idx) {
Ordering::Equal => a.track_index.cmp(&b.track_index),
other => other,
},
other => other,
}
});
let spacing = min_spacing.map(|s| if s.is_sign_negative() { 0.0 } else { s });
let mut pins_placed_by_layer: Vec<usize> = vec![0; layer_names.len()];
for c in candidates.into_iter() {
if placed_count >= pins.len() {
break;
}
if let (Some(sp), Some(layer_rel_min), Some(period)) = (
spacing,
rel_min_by_layer[c.layer_idx],
period_by_layer[c.layer_idx],
) {
let n_on_layer = pins_placed_by_layer[c.layer_idx] as i64;
let delta_tracks = (c.track_index as i64) - (layer_rel_min as i64);
let lhs = (delta_tracks as f64) * (period as f64);
let rhs = (n_on_layer as f64) * sp;
if lhs < rhs {
continue;
}
}
let (layer_name, _) = layers
.get_index(c.layer_idx)
.expect("layer index out of bounds");
let layer_shapes = layers.get(layer_name).unwrap();
if self
.check_pin_placement_on_edge_index_with_polygon(
edge_index,
layer_name,
c.track_index,
Some(&layer_shapes.0),
layer_shapes.1.as_ref(),
)
.is_err()
{
continue;
}
let (port_name, bit) = (
pins[placed_count].0.as_ref().to_string(),
pins[placed_count].1,
);
self.place_pin_on_edge_index(
port_name,
bit,
edge_index,
layer_name.as_str(),
c.track_index,
);
pins_placed_by_layer[c.layer_idx] += 1;
placed_count += 1;
}
if placed_count == pins.len() {
let max_on_layer = pins_placed_by_layer.into_iter().max().unwrap_or(0);
Ok(max_on_layer)
} else {
Err(BatchPinPlacementError::RanOutOfLayers {
requested: pins.len(),
placed: placed_count,
})
}
}
pub fn spread_pins_on_edge_index_with_polygons(
&self,
pins: &[(impl AsRef<str>, usize)],
edge_index: usize,
layers: IndexMap<String, (Polygon, Option<Polygon>)>,
options: SpreadPinsOptions,
) -> Result<(), BatchPinPlacementError> {
let tolerance = options.tolerance.max(0.0);
let position_range = options.range;
let search_span = match (position_range.min, position_range.max) {
(Some(a), Some(b)) => (b - a).max(0),
_ => {
let edge = self.get_edge(edge_index).unwrap();
let er = edge.get_coord_range().unwrap();
let a = position_range.min.unwrap_or(0);
let b = position_range
.max
.unwrap_or(er.max.unwrap() - er.min.unwrap());
(b - a).max(0)
}
};
let mut lo: f64 = 0.0;
let mut hi: f64 = (search_span as f64).max(0.0);
let mut last_max_per_layer = {
let sim = self.clone_for_pin_placement();
sim.place_pins_on_edge_index_with_polygons(
pins,
edge_index,
layers.clone(),
Range {
min: position_range.min,
max: position_range.max,
},
Some(0.0),
)?
};
while (hi - lo) * ((last_max_per_layer as f64 - 1.0).max(0.0)) > tolerance {
let mid = (lo + hi) / 2.0;
let sim = self.clone_for_pin_placement();
if let Ok(v) = sim.place_pins_on_edge_index_with_polygons(
pins,
edge_index,
layers.clone(),
Range {
min: position_range.min,
max: position_range.max,
},
Some(mid.max(0.0)),
) {
lo = mid;
last_max_per_layer = v;
} else {
hi = mid;
}
}
self.place_pins_on_edge_index_with_polygons(
pins,
edge_index,
layers,
position_range,
Some(lo.max(0.0)),
)
.map(|_| ())
}
pub fn spread_pins_on_edge_index<L, S>(
&self,
pins: &[(impl AsRef<str>, usize)],
edge_index: usize,
layers: L,
options: SpreadPinsOptions,
) -> Result<(), BatchPinPlacementError>
where
L: IntoIterator<Item = S>,
S: AsRef<str>,
{
self.spread_pins_on_edge_index_with_polygons(
pins,
edge_index,
self.get_default_layer_shapes(layers),
options,
)
}
fn get_default_layer_shapes<L, S>(
&self,
layers: L,
) -> IndexMap<String, (Polygon, Option<Polygon>)>
where
L: IntoIterator<Item = S>,
S: AsRef<str>,
{
let mut layers_map: IndexMap<String, (Polygon, Option<Polygon>)> = IndexMap::new();
for l in layers.into_iter() {
let name = l.as_ref();
if let Some(track) = self.get_track(name)
&& let Some(pin_shape) = track.pin_shape.clone()
{
layers_map.insert(name.to_string(), (pin_shape, track.keepout_shape.clone()));
}
}
layers_map
}
pub fn get_physical_pin(&self, port_name: &str, bit: usize) -> PhysicalPin {
let core = self.core.read();
core.get_physical_pin(port_name, bit)
}
}
macro_rules! place_port_slice_on_named_edge {
($fn_name:ident, $const_name:path) => {
paste::paste! {
#[doc = concat!(
"Places this single-bit slice on the ",
stringify!($fn_name),
" edge using the default track definition."
)]
pub fn [<place_on_ $fn_name _edge>](&self, layer: impl AsRef<str>, track_index: usize) {
let (port_name, bit) = self.get_port_name_and_bit();
self.get_mod_def().[<place_pin_on_ $fn_name _edge>](port_name, bit, layer, track_index);
}
}
};
}
impl PortSlice {
fn get_port_name_and_bit(&self) -> (String, usize) {
self.check_validity();
assert!(
self.width() == 1,
"define_physical_pin must be called on a single bit slice"
);
assert!(
matches!(self.port, crate::Port::ModDef { .. }),
"define_physical_pin must be called on a ModDef port"
);
let port_name = self.port.get_port_name();
let bit = self.lsb; (port_name, bit)
}
pub fn place(&self, pin: PhysicalPin) {
let (port_name, bit) = self.get_port_name_and_bit();
self.get_mod_def().place_pin(port_name, bit, pin);
}
for_each_edge_direction!(place_port_slice_on_named_edge);
for_each_edge_direction!(spread_port_slice_pins_on_named_edge);
pub fn place_on_edge_index(
&self,
edge_index: usize,
layer: impl AsRef<str>,
track_index: usize,
) {
let (port_name, bit) = self.get_port_name_and_bit();
self.get_mod_def()
.place_pin_on_edge_index(port_name, bit, edge_index, layer, track_index);
}
pub fn place_on_edge_index_with_polygon(
&self,
edge_index: usize,
layer: impl AsRef<str>,
track_index: usize,
pin_polygon: Option<&Polygon>,
keepout_polygon: Option<&Polygon>,
) {
let (port_name, bit) = self.get_port_name_and_bit();
self.get_mod_def().place_pin_on_edge_index_with_polygon(
port_name,
bit,
edge_index,
layer,
track_index,
pin_polygon,
keepout_polygon,
);
}
}
for_each_edge_direction!(spread_port_pins_on_named_edge);