use crate::db;
use db::traits::*;
use db::{Direction, CoordinateType, TerminalId};
use num_traits::{PrimInt, NumCast};
#[derive(Debug, Clone)]
pub struct SimpleTieCellInsertionEngine {
pub tie_cell_low: String,
pub tie_cell_high: String,
pub max_fanout: u32,
pub max_distance: db::Coord,
}
impl SimpleTieCellInsertionEngine {
pub fn insert_tie_cells_on_net<LN: NetlistEdit + LayoutEdit>(
&self,
chip: &mut LN,
net: &LN::NetId,
) -> Result<(Vec<LN::CellInstId>, Vec<LN::NetId>), ()>
where LN::Coord: PrimInt {
if !chip.is_constant_net(&net) {
let parent_name = chip.cell_name(&chip.parent_cell_of_net(&net));
log::warn!("Net '{:?}' in '{}' is neither a constant LOW nor HIGH net.", &net, parent_name);
}
let mut sinks = vec![];
for t in chip.each_terminal_of_net(&net) {
match &t {
TerminalId::PinId(p) => {
match chip.pin_direction(p) {
Direction::Output => sinks.push(t),
d => {
let cell_name = chip.cell_name(&chip.parent_cell_of_pin(&p));
let pin_name = chip.pin_name(p);
panic!("Cannot handle pin direction of pin '{}' in cell '{}': {:?} (must be an output)", pin_name, cell_name, d)
}
}
}
TerminalId::PinInstId(p) => {
match chip.pin_direction(&chip.template_pin(p)) {
Direction::Input => sinks.push(t),
d => {
let pin = chip.template_pin(p);
let cell_name = chip.cell_name(&chip.parent_cell_of_pin(&pin));
let pin_name = chip.pin_name(&pin);
panic!("Cannot handle pin direction of pin '{}' in cell '{}': {:?} (must be an input)", pin_name, cell_name, d)
}
}
}
}
}
log::debug!("Number of sinks: {}", sinks.len());
self.insert_tie_cells(chip, &sinks)
}
fn insert_tie_cells<LN: NetlistEdit + LayoutEdit>(
&self,
chip: &mut LN,
signal_sinks: &Vec<TerminalId<LN>>,
) -> Result<(Vec<LN::CellInstId>, Vec<LN::NetId>), ()>
where LN::Coord: PrimInt {
log::debug!("Insert tie-cells for {} sinks.", signal_sinks.len());
let mut added_tie_cells = vec![];
let mut added_nets = vec![];
if signal_sinks.is_empty() {
return Ok((added_tie_cells, added_nets));
}
let parent_cell = match &signal_sinks[0] {
TerminalId::PinId(p) => chip.parent_cell_of_pin(p),
TerminalId::PinInstId(p) => chip.parent_cell(&chip.parent_of_pin_instance(p))
};
let source_net = chip.net_of_terminal(&signal_sinks[0]);
if source_net.is_none() {
log::error!("Sink terminal is not connected to any net.");
panic!("Source terminal is not connected to any net.");
}
{
let is_connected_to_correct_nets = signal_sinks.iter()
.all(|sink| chip.net_of_terminal(sink) == source_net);
if !is_connected_to_correct_nets {
log::error!("Sinks are not all connected to the same net as the source.");
panic!("Sinks are not all connected to the same net as the source.");
}
}
let source_net = source_net.unwrap();
assert!(chip.is_constant_net(&source_net), "Net must be either constant LOW or HIGH for tie-cell insertion.");
let net_value = source_net == chip.net_one(&parent_cell);
let tie_cell_name = if net_value {
&self.tie_cell_high
} else {
&self.tie_cell_low
};
log::info!("Use tie-cell '{}'.", &tie_cell_name);
let tie_cell = chip.cell_by_name(tie_cell_name)
.expect("Tie-cell not found.");
let inputs: Vec<_> = chip.each_pin(&tie_cell)
.filter(|pin| chip.pin_direction(&pin).is_input())
.collect();
assert_eq!(inputs.len(), 0, "Tie-cell must have no input pins.");
let outputs: Vec<_> = chip.each_pin(&tie_cell)
.filter(|pin| chip.pin_direction(&pin).is_output())
.collect();
assert_eq!(outputs.len(), 1, "Tie-cell must have exactly one output pin.");
let tie_cell_output = outputs[0].clone();
let terminal_location = |t: &TerminalId<LN>| {
match t {
TerminalId::PinId(_t) => {
db::Point::zero()
}
TerminalId::PinInstId(t) => {
let inst = chip.parent_of_pin_instance(t);
let tf = chip.get_transform(&inst);
let location = tf.transform_point(db::Point::zero());
location
}
}
};
let clusters = {
let sink_locations: Vec<_> = signal_sinks.iter()
.map(terminal_location)
.collect();
let (cluster_ids, num_clusters) = find_clusters(
&sink_locations,
self.max_fanout,
NumCast::from(self.max_distance).unwrap());
let mut clusters = vec![vec![]; num_clusters as usize];
for (i, cluster_id) in cluster_ids.iter().enumerate() {
clusters[*cluster_id as usize].push((signal_sinks[i].clone(), sink_locations[i]));
}
clusters
};
let mut name_counter = 0; for cluster in clusters {
let (sinks, positions): (Vec<_>, Vec<_>) = cluster.into_iter().unzip();
let center = center_of_mass(&positions);
let instance_name = loop {
let name = format!("_tie_cell_{}", name_counter);
name_counter += 1;
if chip.cell_instance_by_name(&parent_cell, &name).is_none() {
break name;
}
};
let tie_cell_inst = chip.create_cell_instance(&parent_cell, &tie_cell, Some(instance_name.into()));
let new_net = chip.create_net(&parent_cell, None);
let tie_cell_output_pin = chip.pin_instance(&tie_cell_inst, &tie_cell_output);
chip.connect_pin_instance(&tie_cell_output_pin, Some(new_net.clone()));
chip.set_transform(&tie_cell_inst, db::SimpleTransform::translate(center));
for sink in &sinks {
let old_net = chip.connect_terminal(sink, Some(new_net.clone()));
debug_assert_eq!(old_net.as_ref(), Some(&source_net), "Sink is expected to be connected to the source net.");
}
added_tie_cells.push(tie_cell_inst);
added_nets.push(new_net);
}
Ok((added_tie_cells, added_nets))
}
}
fn find_clusters<C: CoordinateType + PrimInt>(points: &Vec<db::Point<C>>,
max_cluster_size: u32,
max_cluster_span: C) -> (Vec<u32>, u32) {
let mut points_with_cluster_id: Vec<(db::Point<_>, usize, u32)> = points.iter()
.enumerate()
.map(|(idx, p)| (*p, idx, 0))
.collect();
points_with_cluster_id.sort_by_key(|(p, _, _)| (p.x, p.y));
let mut cluster_id = 0u32;
let mut current_cluster_points = vec![];
while let Some((first_point_idx, _)) = points_with_cluster_id.iter()
.enumerate()
.find(|(_pos, (_, _, cluster))| *cluster == 0) {
cluster_id += 1;
points_with_cluster_id[first_point_idx].2 = cluster_id;
let start = points_with_cluster_id[first_point_idx].0;
let mut bbox = db::Rect::new(start, start);
current_cluster_points.clear();
current_cluster_points.push(start);
for _ in 1..max_cluster_size {
let center = center_of_mass(¤t_cluster_points);
let closest = points_with_cluster_id.iter()
.enumerate()
.filter(|(_, (_, _, cluster))| *cluster == 0) .filter(|(_, (p, _, _))| {
let new_bbox = bbox.add_point(*p);
new_bbox.height() < max_cluster_span && new_bbox.width() < max_cluster_span
})
.min_by_key(|(_, (pos, _, _))| (center - *pos).norm1())
.map(|(idx, _)| idx);
if let Some(closest) = closest {
points_with_cluster_id[closest].2 = cluster_id;
let p = points_with_cluster_id[closest].0;
current_cluster_points.push(p);
bbox = bbox.add_point(p);
} else {
break;
}
}
}
let num_clusters = cluster_id;
let mut result = vec![0u32; points_with_cluster_id.len()];
for (_p, index, cluster_id) in points_with_cluster_id {
result[index] = cluster_id - 1; }
(result, num_clusters)
}
fn center_of_mass<C: PrimInt>(points: &Vec<db::Point<C>>) -> db::Point<C> {
points.iter()
.fold(db::Point::zero(), |a, b| a + b)
/ NumCast::from(points.len()).unwrap()
}