gdsconv/
import.rs

1use std::collections::{HashMap, HashSet};
2
3use arcstr::ArcStr;
4use gds::{GdsLibrary, GdsUnits};
5use geometry::{
6    point::Point,
7    prelude::{Orientation, Polygon, Transformation},
8    rect::Rect,
9    span::Span,
10    transform::Rotation,
11};
12use layir::{Cell, CellId, Instance, Library, LibraryBuilder, Shape, Text};
13use slotmap::new_key_type;
14use tracing::{Level, span};
15
16use crate::GdsLayer;
17
18new_key_type! {
19    /// A key used for identifying elements when importing a GDSII file.
20    pub struct ElementKey;
21}
22
23pub struct GdsImportOpts {
24    pub units: Option<GdsUnits>,
25}
26
27pub fn import_gds(lib: &GdsLibrary, opts: GdsImportOpts) -> Result<Library<GdsLayer>> {
28    let importer = GdsImporter::new(lib, opts);
29    importer.import()
30}
31
32/// An importer for GDS files.
33pub struct GdsImporter<'a> {
34    lib: LibraryBuilder<GdsLayer>,
35    gds: &'a gds::GdsLibrary,
36    opts: GdsImportOpts,
37}
38
39/// An error encountered while converting a GDS library to LayIR.
40#[derive(Debug, Clone)]
41pub struct GdsImportError;
42
43type Result<T> = std::result::Result<T, GdsImportError>;
44
45impl<'a> GdsImporter<'a> {
46    /// Creates a new GDS importer.
47    pub fn new(gds: &'a gds::GdsLibrary, opts: GdsImportOpts) -> Self {
48        Self {
49            lib: LibraryBuilder::new(),
50            gds,
51            opts,
52        }
53    }
54
55    /// Imports a [`gds::GdsLibrary`].
56    pub fn import(mut self) -> Result<Library<GdsLayer>> {
57        self.run_preimport_checks()?;
58        for strukt in GdsDepOrder::new(self.gds).total_order() {
59            self.import_and_add(strukt)?;
60        }
61        let lib = self.lib.build().map_err(|_| GdsImportError)?;
62        Ok(lib)
63    }
64
65    /// Imports a single cell and all of its dependencies into the provided cell.
66    pub fn import_cell(mut self, name: impl Into<ArcStr>) -> Result<Library<GdsLayer>> {
67        let name = name.into();
68        self.run_preimport_checks()?;
69
70        let mut cell = None;
71        for strukt in GdsDepOrder::new(self.gds).cell_order(name.clone()) {
72            if strukt.name == name {
73                cell = Some(self.import_and_add(strukt)?);
74            } else {
75                self.import_and_add(strukt)?;
76            }
77        }
78
79        if cell.is_none() {
80            tracing::event!(Level::ERROR, cell_name = %name, "cell not found: `{}`", name);
81            return Err(GdsImportError);
82        }
83
84        let lib = self.lib.build().map_err(|_| GdsImportError)?;
85        Ok(lib)
86    }
87    /// Runs relevant checks before importing from a GDS library.
88    fn run_preimport_checks(&mut self) -> Result<()> {
89        // Unsupported GDSII features, if ever added, shall be imported here:
90        // if gdslib.libdirsize.is_some()
91        //     || gdslib.srfname.is_some()
92        //     || gdslib.libsecur.is_some()
93        //     || gdslib.reflibs.is_some()
94        //     || gdslib.fonts.is_some()
95        //     || gdslib.attrtable.is_some()
96        //     || gdslib.generations.is_some()
97        //     || gdslib.format_type.is_some()
98        // {
99        //     return self.fail("Unsupported GDSII Feature");
100        // }
101        // And convert each of its `structs` into our `cells`
102
103        self.check_units(&self.gds.units)
104    }
105    /// Checks that the database units match up with the units specified by the PDK.
106    fn check_units(&mut self, units: &gds::GdsUnits) -> Result<()> {
107        let gdsunit = units.db_unit();
108
109        if let Some(expected_units) = &self.opts.units
110            && (gdsunit - expected_units.db_unit()).abs() / expected_units.db_unit() > 1e-3
111        {
112            return Err(GdsImportError);
113        }
114        Ok(())
115    }
116    /// Imports and adds a cell if not already defined
117    fn import_and_add(&mut self, strukt: &gds::GdsStruct) -> Result<CellId> {
118        let name = &strukt.name;
119        // Check whether we're already defined, and bail if so
120        if self.lib.try_cell_id_named(name).is_some() {
121            tracing::event!(
122                Level::ERROR,
123                cell_name = %name,
124                "duplicate cell name: `{}`",
125                name
126            );
127            return Err(GdsImportError);
128        }
129
130        let mut cell = Cell::new(name);
131        self.import_gds_struct(strukt, &mut cell)?;
132        let id = self.lib.add_cell(cell);
133        Ok(id)
134    }
135    /// Imports a GDS Cell ([gds::GdsStruct]) into a [Cell]
136    fn import_gds_struct(
137        &mut self,
138        strukt: &gds::GdsStruct,
139        cell: &mut Cell<GdsLayer>,
140    ) -> Result<()> {
141        let span = span!(Level::INFO, "cell", name=%cell.name());
142        let _guard = span.enter();
143
144        for elem in &strukt.elems {
145            use gds::GdsElement::*;
146            match elem {
147                GdsBoundary(x) => cell.add_element(self.import_boundary(x)?),
148                GdsPath(x) => cell.add_element(self.import_path(x)?),
149                GdsBox(x) => cell.add_element(self.import_box(x)?),
150                GdsArrayRef(x) => {
151                    let elems = self.import_instance_array(x)?;
152                    for elem in elems {
153                        cell.add_instance(elem);
154                    }
155                }
156                GdsStructRef(x) => {
157                    cell.add_instance(self.import_instance(x)?);
158                }
159                GdsTextElem(x) => {
160                    cell.add_element(self.import_text_elem(x)?);
161                }
162                // GDSII "Node" elements are fairly rare, and are not supported.
163                // (Maybe some day we'll even learn what they are.)
164                GdsNode(elem) => {
165                    tracing::warn!(?elem, "ignoring unsupported GDS Node element");
166                }
167            };
168        }
169        Ok(())
170    }
171    /// Imports a [gds::GdsBoundary] into a [Shape]
172    fn import_boundary(&mut self, x: &gds::GdsBoundary) -> Result<Shape<GdsLayer>> {
173        let span = span!(Level::INFO, "boundary", value=?x);
174        let _guard = span.enter();
175
176        let mut pts: Vec<Point> = self.import_point_vec(&x.xy)?;
177        if pts.is_empty() {
178            tracing::event!(
179                Level::ERROR,
180                "cannot import empty GDS boundary: empty polygons are not permitted"
181            );
182            return Err(GdsImportError);
183        }
184        if pts[0] != *pts.last().unwrap() {
185            tracing::event!(Level::ERROR, first_pt=?pts[0], last_pt=?pts.last().unwrap(), "invalid GDS boundary: last point of polygon must be the same as the first point, or else the polygon is not closed");
186            return Err(GdsImportError);
187        }
188        // Pop the redundant last entry
189        pts.pop();
190        // Check for Rectangles; they help
191        let inner = if pts.len() == 4
192            && ((pts[0].x == pts[1].x // Clockwise
193            && pts[1].y == pts[2].y
194            && pts[2].x == pts[3].x
195            && pts[3].y == pts[0].y)
196                || (pts[0].y == pts[1].y // Counter-clockwise
197            && pts[1].x == pts[2].x
198            && pts[2].y == pts[3].y
199            && pts[3].x == pts[0].x))
200        {
201            // That makes this a Rectangle.
202            geometry::shape::Shape::Rect(Rect::new(pts[0], pts[2]))
203        } else {
204            // Otherwise, it's a polygon
205            geometry::shape::Shape::Polygon(Polygon::from_verts(pts))
206        };
207
208        let layer = self.import_element_layer(x)?;
209        let shape = Shape::new(layer, inner);
210        Ok(shape)
211    }
212    /// Imports a [gds::GdsBox] into a [Shape]
213    fn import_box(&mut self, gds_box: &gds::GdsBox) -> Result<Shape<GdsLayer>> {
214        let span = span!(Level::INFO, "box", value=?gds_box);
215        let _guard = span.enter();
216
217        // GDS stores *five* coordinates per box (for whatever reason).
218        // This does not check fox "box validity", and imports the
219        // first and third of those five coordinates,
220        // which are by necessity for a valid [GdsBox] located at opposite corners.
221        let inner = geometry::shape::Shape::Rect(Rect::new(
222            self.import_point(&gds_box.xy[0])?,
223            self.import_point(&gds_box.xy[2])?,
224        ));
225
226        let layer = self.import_element_layer(gds_box)?;
227        let shape = Shape::new(layer, inner);
228        Ok(shape)
229    }
230    /// Import a [gds::GdsPath] into an [Element]
231    fn import_path(&mut self, x: &gds::GdsPath) -> Result<Shape<GdsLayer>> {
232        let span = span!(Level::INFO, "path");
233        let _guard = span.enter();
234
235        let pts = self.import_point_vec(&x.xy)?;
236        let width = if let Some(w) = x.width {
237            w as i64
238        } else {
239            tracing::event!(Level::ERROR, "GDS path width must be specified");
240            return Err(GdsImportError);
241        };
242
243        let layer = self.import_element_layer(x)?;
244        let (begin_extn, end_extn) = match x.path_type {
245            Some(0) => (0, 0),
246            Some(2) => (width / 2, width / 2),
247            Some(4) => (
248                x.begin_extn.unwrap_or_default() as i64,
249                x.end_extn.unwrap_or_default() as i64,
250            ),
251            None => (0, 0),
252            _ => {
253                tracing::event!(
254                    Level::ERROR,
255                    "Only flush and square path ends are supported"
256                );
257                return Err(GdsImportError);
258            }
259        };
260
261        if pts.iter().all(|pt| pt.x == pts[0].x) {
262            Ok(Shape::new(
263                layer,
264                Rect::from_spans(
265                    Span::from_center_span(pts[0].x, width),
266                    Span::new(
267                        pts.iter().map(|pt| pt.y).min().unwrap(),
268                        pts.iter().map(|pt| pt.y).max().unwrap(),
269                    )
270                    .union(Span::from_point(pts[0].y).expand_all(begin_extn))
271                    .union(Span::from_point(pts[pts.len() - 1].y).expand_all(end_extn)),
272                ),
273            ))
274        } else if pts.iter().all(|pt| pt.y == pts[0].y) {
275            Ok(Shape::new(
276                layer,
277                Rect::from_spans(
278                    Span::new(
279                        pts.iter().map(|pt| pt.x).min().unwrap(),
280                        pts.iter().map(|pt| pt.x).max().unwrap(),
281                    )
282                    .union(Span::from_point(pts[0].x).expand_all(begin_extn))
283                    .union(Span::from_point(pts[pts.len() - 1].x).expand_all(end_extn)),
284                    Span::from_center_span(pts[0].y, width),
285                ),
286            ))
287        } else {
288            tracing::event!(Level::ERROR, "2D GDS paths not supported");
289            Err(GdsImportError)
290        }
291    }
292    /// Import a [gds::GdsTextElem] cell/struct-instance into an [TextElement].
293    fn import_text_elem(&mut self, sref: &gds::GdsTextElem) -> Result<Text<GdsLayer>> {
294        let string = ArcStr::from(sref.string.to_lowercase());
295        let span = span!(Level::INFO, "text element", text = %string);
296        let _guard = span.enter();
297
298        // Convert its location
299        let loc = self.import_point(&sref.xy)?;
300        let layer = self.import_element_layer(sref)?;
301        Ok(Text::with_transformation(
302            layer,
303            string,
304            Transformation::from_offset(loc),
305        ))
306    }
307    /// Import a [gds::GdsStructRef] cell/struct-instance into an [Instance]
308    fn import_instance(&mut self, sref: &gds::GdsStructRef) -> Result<Instance> {
309        let span = span!(Level::INFO, "instance", name = %sref.name, loc = %sref.xy);
310        let _guard = span.enter();
311
312        // Look up the cell-key, which must be imported by now
313        let cell = self.lib.try_cell_id_named(&sref.name).ok_or_else(|| {
314            tracing::event!(Level::ERROR, cell_name=%sref.name, "cell not found: `{}`", sref.name);
315            GdsImportError
316        })?;
317        // Convert its location
318        let loc = self.import_point(&sref.xy)?;
319        Ok(Instance::with_transformation(
320            cell,
321            sref.name.clone(),
322            Transformation::from_offset_and_orientation(
323                loc,
324                sref.strans
325                    .as_ref()
326                    .map(|value| self.import_orientation(value))
327                    .transpose()?
328                    .unwrap_or_default(),
329            ),
330        ))
331    }
332    /// Imports a (two-dimensional) [`gds::GdsArrayRef`] into [`Instance`]s.
333    ///
334    /// Returns the newly-created [`Instance`]s as a vector.
335    /// Instance names are of the form `{array.name}[{col}][{row}]`.
336    ///
337    /// GDSII arrays are described by three spatial points:
338    /// The origin, extent in "rows", and extent in "columns".
339    /// In principle these need not be the same as "x" and "y" spacing,
340    /// i.e. there might be "diamond-shaped" array specifications.
341    ///
342    /// Here, arrays are supported if they are "specified rectangular",
343    /// i.e. that (a) the first two points align in `y`, and (b) the second two points align in `x`.
344    ///
345    /// Further support for such "non-rectangular-specified" arrays may (or may not) become a future addition,
346    /// based on observed GDSII usage.
347    fn import_instance_array(&mut self, aref: &gds::GdsArrayRef) -> Result<Vec<Instance>> {
348        let span = span!(Level::INFO, "instance array");
349        let _guard = span.enter();
350
351        // Look up the cell, which must be imported by now
352        let cell = self.lib.try_cell_id_named(&aref.name).ok_or_else(|| {
353            tracing::event!(Level::ERROR, cell_name=%aref.name, "cell not found: `{}`", aref.name);
354            GdsImportError
355        })?;
356
357        // Convert its three (x,y) coordinates
358        let p0 = self.import_point(&aref.xy[0])?;
359        let p1 = self.import_point(&aref.xy[1])?;
360        let p2 = self.import_point(&aref.xy[2])?;
361        // Check for (thus far) unsupported non-rectangular arrays
362        if p0.y != p1.y || p0.x != p2.x {
363            tracing::event!(Level::ERROR, p0=?p0, p1=?p1, p2=?p2, "unsupported non-rectangular GDS instance array");
364            return Err(GdsImportError);
365        }
366        // Sort out the inter-element spacing
367        let xstep = (p1.x - p0.x) / i64::from(aref.cols);
368        let ystep = (p2.y - p0.y) / i64::from(aref.rows);
369
370        // Incorporate the reflection/ rotation settings
371        let mut orientation = Orientation::default();
372        if let Some(strans) = &aref.strans {
373            orientation = self.import_orientation(strans)?;
374        }
375
376        // Create the Instances
377        let mut insts = Vec::with_capacity((aref.rows * aref.cols) as usize);
378        for ix in 0..i64::from(aref.cols) {
379            let x = p0.x + ix * xstep;
380            for iy in 0..i64::from(aref.rows) {
381                let y = p0.y + iy * ystep;
382                insts.push(Instance::with_transformation(
383                    cell,
384                    arcstr::format!("{}_{}_{}", aref.name, ix, iy),
385                    Transformation::from_offset_and_orientation(Point::new(x, y), orientation),
386                ));
387            }
388        }
389        Ok(insts)
390    }
391    /// Imports a [`Point`].
392    fn import_point(&self, pt: &gds::GdsPoint) -> Result<Point> {
393        let x = pt.x.into();
394        let y = pt.y.into();
395        Ok(Point::new(x, y))
396    }
397    /// Imports a vector of [`Point`]s.
398    fn import_point_vec(&mut self, pts: &[gds::GdsPoint]) -> Result<Vec<Point>> {
399        pts.iter()
400            .map(|p| self.import_point(p))
401            .collect::<Result<Vec<_>>>()
402    }
403    /// Imports an orientation.
404    fn import_orientation(&mut self, strans: &gds::GdsStrans) -> Result<Orientation> {
405        let span = span!(Level::INFO, "orientation", value=?strans);
406        let _guard = span.enter();
407
408        if strans.abs_mag || strans.abs_angle {
409            tracing::event!(
410                Level::ERROR,
411                "absolute magnitude/absolute angle are unsupported"
412            );
413            return Err(GdsImportError);
414        }
415        if strans.mag.is_some() {
416            tracing::event!(Level::ERROR, "orientation magnitude unsupported");
417            return Err(GdsImportError);
418        }
419
420        let rotation = Rotation::try_from(strans.angle.unwrap_or_default()).map_err(|_| {
421            tracing::event!(Level::ERROR, "rotations must be in 90 degree increments");
422            GdsImportError
423        })?;
424        let orientation = Orientation::from_reflect_and_angle(strans.reflected, rotation);
425        Ok(orientation)
426    }
427    /// Gets the [`LayerSpec`] for a GDS element implementing its [`gds::HasLayer`] trait.
428    /// Layers are created if they do not already exist,
429    /// although this may eventually be a per-importer setting.
430    fn import_element_layer(&mut self, elem: &impl gds::HasLayer) -> Result<GdsLayer> {
431        let spec = elem.layerspec();
432        let span = span!(Level::INFO, "layer", spec=?spec);
433        let _guard = span.enter();
434        let first = u16::try_from(spec.layer).map_err(|_| {
435            tracing::event!(Level::ERROR, layer=%spec.layer, "failed to convert layer number to u16");
436            GdsImportError
437        })?;
438        let second = u16::try_from(spec.xtype).map_err(|_| {
439            tracing::event!(Level::ERROR, layer=%spec.layer, "failed to convert layer xtype number to u16");
440            GdsImportError
441        })?;
442        Ok(GdsLayer(first, second))
443    }
444}
445
446/// A helper for retrieving GDS dependencies in reverse topological order.
447///
448/// Creates a vector of references Gds structs, ordered by their instance dependencies.
449/// Each item in the ordered return value is guaranteed *not* to instantiate any item which comes later.
450#[derive(Debug)]
451pub struct GdsDepOrder<'a> {
452    strukts: HashMap<ArcStr, &'a gds::GdsStruct>,
453    stack: Vec<&'a gds::GdsStruct>,
454    seen: HashSet<ArcStr>,
455}
456
457impl<'a> GdsDepOrder<'a> {
458    /// Creates a new [`GdsDepOrder`] for a [`gds::GdsLibrary`].
459    fn new(gdslib: &'a gds::GdsLibrary) -> Self {
460        // First create a map from names to structs
461        let mut strukts = HashMap::new();
462        for s in &gdslib.structs {
463            strukts.insert(s.name.clone(), s);
464        }
465        Self {
466            strukts,
467            stack: Vec::new(),
468            seen: HashSet::new(),
469        }
470    }
471    /// Returns a reverse topological sort of all structs in `gdslib`.
472    fn total_order(mut self) -> Vec<&'a gds::GdsStruct> {
473        let strukts = self
474            .strukts
475            .values()
476            .copied()
477            .collect::<Vec<&gds::GdsStruct>>();
478        for s in strukts {
479            self.push(s)
480        }
481        self.stack
482    }
483    /// Returns all dependencies of a given cell in reverse topological order.
484    fn cell_order(mut self, cell: impl Into<ArcStr>) -> Vec<&'a gds::GdsStruct> {
485        if let Some(strukt) = self.strukts.get(&cell.into()) {
486            self.push(strukt);
487        }
488        self.stack
489    }
490    /// Adds all of `strukt`'s dependencies, and then `strukt` itself, to the stack.
491    fn push(&mut self, strukt: &'a gds::GdsStruct) {
492        if !self.seen.contains(&strukt.name) {
493            for elem in &strukt.elems {
494                use gds::GdsElement::*;
495                match elem {
496                    GdsStructRef(x) => self.push(self.strukts.get(&x.name).unwrap()),
497                    GdsArrayRef(x) => self.push(self.strukts.get(&x.name).unwrap()),
498                    _ => (),
499                };
500            }
501            self.seen.insert(strukt.name.clone());
502            self.stack.push(strukt);
503        }
504    }
505}