gdsconv/
conv.rs

1//! Converting GDS libraries.
2
3use std::collections::HashMap;
4use std::hash::Hash;
5
6use crate::GdsLayer;
7use arcstr::ArcStr;
8use geometry::prelude::Contains;
9use layir::{Cell, Direction, Element, Instance, LibraryBuilder, Port};
10use thiserror::Error;
11
12/// A layer type that can be constructed from a [`GdsLayer`].
13pub trait FromGds: Sized {
14    /// Converts the given GDS layer to this layer type.
15    ///
16    /// Returns [`None`] if the GDS layer has no valid mapping to the new layer type.
17    fn from_gds(layer: GdsLayer) -> Option<Self>;
18    /// Converts the given GDS layer to this layer type if the GDS layer is a pin-type layer.
19    ///
20    /// Should return [`None`] if the GDS layer has no valid mapping to the new layer type,
21    /// or if the given GDS layer is not a pin layer.
22    fn from_gds_pin(layer: GdsLayer) -> Option<Self>;
23    /// Converts the given GDS layer to this layer type if the GDS layer is a label-type layer.
24    ///
25    /// Should return [`None`] if the GDS layer has no valid mapping to the new layer type,
26    /// or if the given GDS layer is not a label layer.
27    fn from_gds_label(layer: GdsLayer) -> Option<Self>;
28}
29
30#[derive(Error, Debug)]
31pub enum FromGdsError {
32    #[error("no layer mapping for layer {layer} in cell `{cell}`")]
33    NoLayerMapping { cell: ArcStr, layer: GdsLayer },
34    #[error("pin with multiple labels: `{label1}`, `{label2}` in cell `{cell}`")]
35    PinWithMultipleLabels {
36        cell: ArcStr,
37        label1: ArcStr,
38        label2: ArcStr,
39    },
40    #[error("pin with no label in cell `{cell}`")]
41    PinWithNoLabel { cell: ArcStr },
42    #[error("error building LayIR library")]
43    BuildError(#[from] layir::BuildError),
44}
45
46/// Convert a GDS layout library to a sky130 layout library.
47pub fn from_gds<L: FromGds + Hash + Eq + Clone>(
48    lib: &layir::Library<GdsLayer>,
49) -> Result<layir::Library<L>, FromGdsError> {
50    let mut olib = LibraryBuilder::<L>::new();
51    let cells = lib.topological_order();
52    for cell in cells {
53        let cell = lib.cell(cell);
54        let mut ocell = Cell::new(cell.name());
55        for elt in cell.elements() {
56            let layer = *elt.layer();
57            let l = L::from_gds(layer).ok_or_else(|| FromGdsError::NoLayerMapping {
58                cell: cell.name().clone(),
59                layer,
60            })?;
61            ocell.add_element(elt.with_layer(l));
62        }
63        for (_, inst) in cell.instances() {
64            let name = lib.cell(inst.child()).name();
65            let child_id = olib.cell_id_named(name);
66            ocell.add_instance(Instance::with_transformation(
67                child_id,
68                inst.name(),
69                inst.transformation(),
70            ));
71        }
72        for (name, oport) in cell.ports() {
73            let port = oport.map_layer(|layer| L::from_gds(*layer).unwrap());
74            ocell.add_port(name, port);
75        }
76        let mut pin_correspondences: HashMap<L, (Vec<_>, Vec<_>)> = HashMap::new();
77        for elt in cell.elements() {
78            match elt {
79                Element::Shape(s) => {
80                    if let Some(layer) = L::from_gds_pin(*s.layer()) {
81                        let entry = pin_correspondences.entry(layer.clone()).or_default();
82                        entry.0.push(s.with_layer(layer));
83                    }
84                }
85                Element::Text(t) => {
86                    if let Some(layer) =
87                        L::from_gds_pin(*t.layer()).or_else(|| L::from_gds_label(*t.layer()))
88                    {
89                        let entry = pin_correspondences.entry(layer.clone()).or_default();
90                        entry.1.push(t.with_layer(layer));
91                    }
92                }
93            }
94        }
95        for (_, (shapes, texts)) in pin_correspondences {
96            for shape in shapes {
97                let mut name: Option<ArcStr> = None;
98                let mut pin_texts = Vec::new();
99                for text in texts.iter() {
100                    if shape
101                        .shape()
102                        .contains(&text.transformation().offset_point())
103                        .intersects()
104                    {
105                        // Identify pin shapes with multiple labels.
106                        if let Some(ref name) = name
107                            && name != text.text()
108                        {
109                            return Err(FromGdsError::PinWithMultipleLabels {
110                                cell: cell.name().clone(),
111                                label1: name.clone(),
112                                label2: text.text().clone(),
113                            });
114                        }
115                        name = Some(text.text().clone());
116                        pin_texts.push(text.clone());
117                    }
118                }
119
120                // If name is None, no label was found for this shape.
121                // We ignore shapes with missing labels.
122                // In the future, we can perform connectivity analysis
123                // to identify other pins to which this shape is connected,
124                // and then add this shape to the appropriate pin.
125                if let Some(name) = name {
126                    if ocell.try_port(&name).is_none() {
127                        ocell.add_port(&name, Port::new(Direction::InOut));
128                    }
129                    let port = ocell.port_mut(&name);
130                    port.add_element(shape);
131                    for text in pin_texts {
132                        // If a text overlaps multiple pin shapes, the imported library will have
133                        // multiple texts: one for each pin shape. We may want to change this
134                        // behavior in the future.
135                        port.add_element(text);
136                    }
137                }
138            }
139        }
140        olib.add_cell(ocell);
141    }
142
143    Ok(olib.build()?)
144}