1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
// Copyright (c) 2020-2021 Thomas Kramer.
// SPDX-FileCopyrightText: 2022 Thomas Kramer <code@tkramer.ch>
//
// SPDX-License-Identifier: AGPL-3.0-or-later
//! Legalization (or detail-placement) of globally placed standard cells.
//! Legalization involves snapping the standard-cells to the rows in an overlap-free manner.
use libreda_db::prelude as db;
use std::collections::{HashMap, HashSet};
use db::{L2NEdit, NetlistBase};
use log::{debug, info, warn};
/// Find legal positions and move the cells to those.
///
/// * `legalizer`: Legalization engine that finds the legal positions of the movable cells.
/// * `top_cell`: The cell whose content should be legalized.
/// * `core_area`: The region to be used for the placement.
/// * `cell_outlines`: Abutment boxes of the standard-cells.
/// * `fixed_instances`: Cell instanced that can be moved.
pub fn legalize<L, LN>(
legalizer: &L,
chip: &mut LN,
top_cell: &LN::CellId,
core_area: &db::SimplePolygon<LN::Coord>,
cell_outlines: &HashMap<LN::CellId, db::Rect<LN::Coord>>,
movable_instances: &HashSet<LN::CellInstId>,
) where
L: SimpleStdCellLegalizer<LN>,
LN: L2NEdit<Coord = db::Coord>,
{
// Extract initial positions from the layout.
// Convert transformations to simple displacements.
let initial_positions = chip
.each_cell_instance(top_cell)
.map(|inst| {
let tf = chip.get_transform(&inst);
let point = tf.transform_point(db::Point::zero());
(inst, point)
})
.collect();
// Compute legal positions.
let legal_positions = legalizer.find_legal_positions(
chip,
top_cell,
core_area,
cell_outlines,
&initial_positions,
movable_instances,
);
// Apply legal positions.
let mut fixed_instance_moves = 0; // Count how many fixed instance the legalizer tries to move.
for (inst, tf) in legal_positions {
if movable_instances.contains(&inst) {
chip.set_transform(&inst, tf);
} else {
// Don't move fixed instances.
fixed_instance_moves += 1;
}
}
if fixed_instance_moves > 0 {
log::warn!(
"Legalizer tried to move fixed instances: {}",
fixed_instance_moves
);
}
}
/// Trait for a legalizer that is able to put globally placed standard-cells
/// into legal positions. A legal position must for instance be overlap free, snapped to a standard-cell row
/// and rotated or flipped correctly such that the power rails and wells are properly oriented.
pub trait SimpleStdCellLegalizer<N: NetlistBase> {
/// Find legal positions and rotations for the cells based on a global placement.
///
/// # Parameters
/// * `netlist`: The netlist containing the circuit to be placed.
/// * `circuit`: The circuit netlist which is being placed.
/// * `core_area`: The region which shall be used for the placement.
/// * `cell_outlines`: Outlines (abutment boxes) of the standard cells.
/// * `global_positions`: Result of the global placement: a location for each circuit instance.
/// * `movable_instances`: Circuit instances that can be moved.
fn find_legal_positions_impl(
&self,
netlist: &N,
circuit: &N::CellId,
core_area: &db::SimplePolygon<db::SInt>,
cell_outlines: &HashMap<N::CellId, db::Rect<db::SInt>>,
global_positions: &HashMap<N::CellInstId, db::Point<db::SInt>>,
movable_instances: &HashSet<N::CellInstId>,
) -> HashMap<N::CellInstId, db::SimpleTransform<db::SInt>>;
/// Wrapper around `legalize_impl(). Does additional logging and sanity checks.
///
/// # Parameters
/// * `netlist`: The netlist containing the circuit to be placed.
/// * `circuit`: The circuit netlist which is being placed.
/// * `core_area`: The region which shall be used for the placement.
/// * `cell_outlines`: Outlines (abutment boxes) of the standard cells.
/// * `global_positions`: Result of the global placement: a location for each circuit instance.
/// * `movable_instances`: Circuit instances that can be moved.
fn find_legal_positions(
&self,
netlist: &N,
circuit: &N::CellId,
core_area: &db::SimplePolygon<db::SInt>,
cell_outlines: &HashMap<N::CellId, db::Rect<db::SInt>>,
global_positions: &HashMap<N::CellInstId, db::Point<db::SInt>>,
movable_instances: &HashSet<N::CellInstId>,
) -> HashMap<N::CellInstId, db::SimpleTransform<db::SInt>> {
info!("Run legalization of the standard-cells.");
// Check that all circuit instances have a global position.
debug!("Check that all circuit instances have a global position.");
let without_position: Vec<_> = netlist
.each_cell_instance(circuit)
.filter(|c| !global_positions.contains_key(c))
.collect();
// Print debug information if there are instances without global position.
if without_position.len() > 0 {
warn!(
"{} circuit instances have no global position.",
without_position.len()
);
debug!(
"Circuit instances without global position: {:?}",
without_position
.iter()
.map(|c| netlist
.cell_instance_name(c)
.map(|n| n.into())
.unwrap_or(format!("ID={:?}", c)))
.collect::<Vec<_>>()
)
}
// Check that the shapes are defined for all used cells.
// Print a warning if the shapes are not known for some cells.
let mut unknown_shapes: Vec<_> = netlist
.each_cell_dependency(circuit)
.filter(|c| !cell_outlines.contains_key(c))
.map(|c| netlist.cell_name(&c))
.collect();
unknown_shapes.sort();
let unknown_shapes = unknown_shapes;
if !unknown_shapes.is_empty() {
warn!(
"Number of cells without known shape: {}",
unknown_shapes.len()
);
warn!("Cells without known shape: {}", unknown_shapes.join(", "))
}
// Call the legalizer.
debug!("Call legalize_impl().");
let result = self.find_legal_positions_impl(
netlist,
circuit,
core_area,
cell_outlines,
global_positions,
movable_instances,
);
// TODO: Check that the found positions are actually legal.
info!("Legalization finished.");
result
}
}