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 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
32pub struct GdsImporter<'a> {
34 lib: LibraryBuilder<GdsLayer>,
35 gds: &'a gds::GdsLibrary,
36 opts: GdsImportOpts,
37}
38
39#[derive(Debug, Clone)]
41pub struct GdsImportError;
42
43type Result<T> = std::result::Result<T, GdsImportError>;
44
45impl<'a> GdsImporter<'a> {
46 pub fn new(gds: &'a gds::GdsLibrary, opts: GdsImportOpts) -> Self {
48 Self {
49 lib: LibraryBuilder::new(),
50 gds,
51 opts,
52 }
53 }
54
55 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 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 fn run_preimport_checks(&mut self) -> Result<()> {
89 self.check_units(&self.gds.units)
104 }
105 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 fn import_and_add(&mut self, strukt: &gds::GdsStruct) -> Result<CellId> {
118 let name = &strukt.name;
119 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 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 GdsNode(elem) => {
165 tracing::warn!(?elem, "ignoring unsupported GDS Node element");
166 }
167 };
168 }
169 Ok(())
170 }
171 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 pts.pop();
190 let inner = if pts.len() == 4
192 && ((pts[0].x == pts[1].x && 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 && pts[1].x == pts[2].x
198 && pts[2].y == pts[3].y
199 && pts[3].x == pts[0].x))
200 {
201 geometry::shape::Shape::Rect(Rect::new(pts[0], pts[2]))
203 } else {
204 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 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 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 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 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 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 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 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 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 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 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 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 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 let xstep = (p1.x - p0.x) / i64::from(aref.cols);
368 let ystep = (p2.y - p0.y) / i64::from(aref.rows);
369
370 let mut orientation = Orientation::default();
372 if let Some(strans) = &aref.strans {
373 orientation = self.import_orientation(strans)?;
374 }
375
376 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 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 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 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 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#[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 fn new(gdslib: &'a gds::GdsLibrary) -> Self {
460 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 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 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 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}