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
// 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 crate::db;
use db::{ToPolygon, WindingNumber};
use libreda_db::prelude::Geometry;
use log;
use std::collections::HashMap;

/// 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
}