gdsconv/conv.rs
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
//! Converting GDS libraries.
use std::collections::HashMap;
use std::hash::Hash;
use crate::GdsLayer;
use arcstr::ArcStr;
use geometry::prelude::Contains;
use layir::{Cell, Direction, Element, Instance, LibraryBuilder, Port};
use thiserror::Error;
/// A layer type that can be constructed from a [`GdsLayer`].
pub trait FromGds: Sized {
/// Converts the given GDS layer to this layer type.
///
/// Returns [`None`] if the GDS layer has no valid mapping to the new layer type.
fn from_gds(layer: GdsLayer) -> Option<Self>;
/// Converts the given GDS layer to this layer type if the GDS layer is a pin-type layer.
///
/// Should return [`None`] if the GDS layer has no valid mapping to the new layer type,
/// or if the given GDS layer is not a pin layer.
fn from_gds_pin(layer: GdsLayer) -> Option<Self>;
/// Converts the given GDS layer to this layer type if the GDS layer is a label-type layer.
///
/// Should return [`None`] if the GDS layer has no valid mapping to the new layer type,
/// or if the given GDS layer is not a label layer.
fn from_gds_label(layer: GdsLayer) -> Option<Self>;
}
#[derive(Error, Debug)]
pub enum FromGdsError {
#[error("no layer mapping for layer {layer} in cell `{cell}`")]
NoLayerMapping { cell: ArcStr, layer: GdsLayer },
#[error("pin with multiple labels: `{label1}`, `{label2}` in cell `{cell}`")]
PinWithMultipleLabels {
cell: ArcStr,
label1: ArcStr,
label2: ArcStr,
},
#[error("pin with no label in cell `{cell}`")]
PinWithNoLabel { cell: ArcStr },
#[error("error building LayIR library")]
BuildError(#[from] layir::BuildError),
}
/// Convert a GDS layout library to a sky130 layout library.
pub fn from_gds<L: FromGds + Hash + Eq + Clone>(
lib: &layir::Library<GdsLayer>,
) -> Result<layir::Library<L>, FromGdsError> {
let mut olib = LibraryBuilder::<L>::new();
let cells = lib.topological_order();
for cell in cells {
let cell = lib.cell(cell);
let mut ocell = Cell::new(cell.name());
for elt in cell.elements() {
let layer = *elt.layer();
let l = L::from_gds(layer).ok_or_else(|| FromGdsError::NoLayerMapping {
cell: cell.name().clone(),
layer,
})?;
ocell.add_element(elt.with_layer(l));
}
for (_, inst) in cell.instances() {
let name = lib.cell(inst.child()).name();
let child_id = olib.cell_id_named(name);
ocell.add_instance(Instance::with_transformation(
child_id,
inst.name(),
inst.transformation(),
));
}
for (name, oport) in cell.ports() {
let port = oport.map_layer(|layer| L::from_gds(*layer).unwrap());
ocell.add_port(name, port);
}
let mut pin_correspondences: HashMap<L, (Vec<_>, Vec<_>)> = HashMap::new();
for elt in cell.elements() {
match elt {
Element::Shape(s) => {
if let Some(layer) = L::from_gds_pin(*s.layer()) {
let entry = pin_correspondences.entry(layer.clone()).or_default();
entry.0.push(s.with_layer(layer));
}
}
Element::Text(t) => {
if let Some(layer) =
L::from_gds_pin(*t.layer()).or_else(|| L::from_gds_label(*t.layer()))
{
let entry = pin_correspondences.entry(layer.clone()).or_default();
entry.1.push(t.with_layer(layer));
}
}
}
}
for (_, (shapes, texts)) in pin_correspondences {
for shape in shapes {
let mut name: Option<ArcStr> = None;
let mut pin_texts = Vec::new();
for text in texts.iter() {
if shape
.shape()
.contains(&text.transformation().offset_point())
.intersects()
{
// Identify pin shapes with multiple labels.
if let Some(ref name) = name {
if name != text.text() {
return Err(FromGdsError::PinWithMultipleLabels {
cell: cell.name().clone(),
label1: name.clone(),
label2: text.text().clone(),
});
}
}
name = Some(text.text().clone());
pin_texts.push(text.clone());
}
}
// If name is None, no label was found for this shape.
// We ignore shapes with missing labels.
// In the future, we can perform connectivity analysis
// to identify other pins to which this shape is connected,
// and then add this shape to the appropriate pin.
if let Some(name) = name {
if ocell.try_port(&name).is_none() {
ocell.add_port(&name, Port::new(Direction::InOut));
}
let port = ocell.port_mut(&name);
port.add_element(shape);
for text in pin_texts {
// If a text overlaps multiple pin shapes, the imported library will have
// multiple texts: one for each pin shape. We may want to change this
// behavior in the future.
port.add_element(text);
}
}
}
}
olib.add_cell(ocell);
}
Ok(olib.build()?)
}