libreda-pnr 0.0.4

Algorithm interface definitions of the LibrEDA place-and-route framework.
Documentation
// Copyright (c) 2020-2021 Thomas Kramer.
// SPDX-FileCopyrightText: 2022 Thomas Kramer <code@tkramer.ch>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

//! Associate layout shapes with nets based on text labels.

use std::collections::HashMap;
use crate::db;
use db::{ToPolygon, WindingNumber};
use libreda_db::prelude::Geometry;
use log;

/// Find pin shapes based on a layer containing all polygon shapes and a layer containing text labels.
/// The text labels mark the shapes that belong to a pin. For each text label all this shapes are grouped together.
///
/// The naive algorithm used is very inefficient but good enough for standard-cells. Especially
/// if it is used in a pre-computation step which is not time-critical.
///
/// # Parameters
///
/// * `layout`: The layout data structure.
/// * `cell`: The cell of which pins should be found.
/// * `pin_shape_layer_id`: The layer which contains the pin shapes.
/// * `label_layer_id`: The layer which contains the text labels of the pins.
pub fn find_pin_geometries_from_text_labels<L: db::LayoutBase<Coord=db::Coord>>(
    layout: &L,
    cell: &L::CellId,
    pin_shape_layer: &L::LayerId,
    label_layer: &L::LayerId,
) -> HashMap<String, Vec<db::Geometry<L::Coord>>> {

    let shape_ids = find_pin_shapes_from_text_labels(layout, cell, pin_shape_layer, label_layer);

    shape_ids.into_iter()
        .map(|(name, ids)| {
            let geometries = ids.into_iter()
                .map(|id| layout.with_shape(&id, |_layer, geo| geo.clone()))
                .collect();
            (name, geometries)
        })
        .collect()
}

/// Associate pins and shapes based on a layer containing all polygon shapes and a layer containing text labels.
/// The text labels mark the shapes that belong to a pin. For each text label all this shapes are grouped together.
///
/// The naive algorithm used is very inefficient but good enough for standard-cells. Especially
/// if it is used in a pre-computation step which is not time-critical.
///
/// # Parameters
///
/// * `layout`: The layout and netlist data structure.
/// * `cell`: The cell of which pins should be found.
/// * `pin_shape_layer_id`: The layer which contains the pin shapes.
/// * `label_layer_id`: The layer which contains the text labels of the pins.
pub fn assign_pin_shapes_from_text_labels<L: db::L2NEdit<Coord=db::Coord>>(
    layout: &mut L,
    cell: &L::CellId,
    pin_shape_layer: &L::LayerId,
    label_layer: &L::LayerId,
) {
    // Find the shapes that are marked with a text label.
    let pin_shapes = find_pin_shapes_from_text_labels(layout, cell, pin_shape_layer, label_layer);

    // Create the links between shapes and pins.
    for (pin_name, shapes) in pin_shapes {
        if let Some(pin) = layout.pin_by_name(cell, &pin_name) {
            for shape in &shapes {
                let previous_pin = layout.set_pin_of_shape(shape, Some(pin.clone()));
                if let Some(previous_pin) = previous_pin {
                    if previous_pin != pin {
                        log::debug!("Overwriting pin of shape: '{}' -> '{}'",
                                    layout.pin_name(&previous_pin), layout.pin_name(&pin));
                    }
                }
            }
        } else {
            // Found a label that does not correspond to any pin name in this cell.
            let cell_name = layout.cell_name(cell);
            log::warn!("No such pin in netlist view of cell '{}': '{}'", cell_name, &pin_name);
        }
    }
}

/// Find pin shapes based on a layer containing all polygon shapes and a layer containing text labels.
/// The text labels mark the shapes that belong to a pin. For each text label all this shapes are grouped together.
///
/// The naive algorithm used is very inefficient but good enough for standard-cells. Especially
/// if it is used in a pre-computation step which is not time-critical.
///
/// # Parameters
///
/// * `layout`: The layout data structure.
/// * `cell`: The cell of which pins should be found.
/// * `pin_shape_layer_id`: The layer which contains the pin shapes.
/// * `label_layer_id`: The layer which contains the text labels of the pins.
pub fn find_pin_shapes_from_text_labels<L: db::LayoutBase<Coord=db::Coord>>(
    layout: &L,
    cell: &L::CellId,
    pin_shape_layer: &L::LayerId,
    label_layer: &L::LayerId,
) -> HashMap<String, Vec<L::ShapeId>> {

    // Find all labels.
    let mut labels = Vec::new();
    layout.for_each_shape(cell, label_layer, |_id, geometry| {
        match geometry {
            Geometry::Text(t) => labels.push(t.clone()),
            _ => { }
        }
    });


    // For each label find all touching shapes.
    let result = labels.into_iter()
        .filter_map(|label| {
            // Find all shapes on the pin layer that touch the label.

            let mut touching_shapes = Vec::new();
            layout.for_each_shape(cell, pin_shape_layer, |shape_id, geometry| {
                // TODO: Check if the label touches the shape without converting the shape to a polygon.
                let touches = geometry.to_polygon()
                    .contains_point(label.location());
                if touches {
                    touching_shapes.push(shape_id.clone())
                }
            });

            if touching_shapes.is_empty() {
                None
            } else {
                Some((label.text().clone(), touching_shapes))
            }
        })
        .collect();

    result
}