From 25cc2658bcb3ecd7281550ba70d593397857fe47 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Tue, 24 May 2022 19:14:48 -0700 Subject: [PATCH 1/2] updates derive Copy, Clone for Point. derive Default for Rect, Polygon, Path derive Default, Copy for BoundBox. impl BoundBoxTrait for Rect fix layout bbox fix bug add instance alignment methods format, fix bug in gds exporter add reflect_vert_anchored upgrade deps add some align modes bugfixes for empty bbox, more align modes upgrade chrono make Rect Copy Make outline in abstract view optional warning: tests not passing add shape and add port APIs add git dependency on vlsir Update to get layers by spec port set net add alignment methods align, bbox, data, geom update rect, span, ports GDS importer generates abstract text label reading, direction agnostic Rect APIs fix trailing whitespace, gds/geom updates update update layout 21 port merging Make GDS error Send + Sync Send + Sync error handling Send + Sync error handling update layers API to allow querying for layers and update gitignore add get_purps API for Layers add traits to LayerSpec add Ord and PartialOrd to LayerSpec Instance builder Instance builder does not take &self as parameter Add Point::zero method Add ability to find cells in a library Make Instance::loc default to origin when using InstanceBuilder Add empty check when converting Rect from BoundBox better error messages when getting ports from abstract views Span::expand method Add Cell::empty method cargo fmt AbstractPort::named method Update readme to point to dan-fritchman/Layout21 typo Create Layout::add_pin method Cell add pin methods Add Span::merge method Use IntoIterator Correct implementation of Span::merge using IntoIterator Rect::expanded method Rect::expand_dir Add Layout::draw_rect method Add ports_starting_with method to Instance Add Cell::layout getter method Add Cell::abs getter method Hack layout21 to preserve text elements on non-label layers Mark element nets if text is on pin or label Add print statement fix: import floating text elements correctly --- .gitignore | 60 +++- gds21/Cargo.toml | 6 +- gds21/src/lib.rs | 2 +- layout21converters/src/gds2proto.rs | 3 +- layout21protos/Cargo.toml | 1 + layout21protos/src/lib.rs | 8 +- layout21raw/Cargo.toml | 8 +- layout21raw/src/align.rs | 135 +++++++++ layout21raw/src/bbox.rs | 28 ++ layout21raw/src/data.rs | 435 +++++++++++++++++++++++++--- layout21raw/src/error.rs | 6 +- layout21raw/src/gds.rs | 158 ++++++---- layout21raw/src/geom.rs | 408 +++++++++++++++++++++++++- layout21raw/src/lef.rs | 10 +- layout21raw/src/lib.rs | 4 +- layout21raw/src/proto.rs | 19 +- layout21raw/src/translate.rs | 19 ++ layout21tetris/Cargo.toml | 4 +- layout21tetris/src/abs.rs | 2 +- layout21tetris/src/conv/raw.rs | 5 +- layout21tetris/src/placer.rs | 17 +- layout21tetris/src/stack.rs | 7 +- layout21tetris/src/tracks.rs | 7 +- layout21utils/Cargo.toml | 2 +- layout21utils/src/error.rs | 2 +- layout21utils/src/ptr.rs | 2 +- layout21utils/src/ser.rs | 2 +- lef21/Cargo.toml | 4 +- readme.md | 2 + 29 files changed, 1205 insertions(+), 161 deletions(-) create mode 100644 layout21raw/src/align.rs create mode 100644 layout21raw/src/translate.rs diff --git a/.gitignore b/.gitignore index c6ec141..74c182f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ - # Custom Additions **/resources/ scratch/ @@ -14,3 +13,62 @@ Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk + +# Created by https://www.toptal.com/developers/gitignore/api/vim,macos +# Edit at https://www.toptal.com/developers/gitignore?templates=vim,macos + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Vim ### +# Swap +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +*~ +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ + +# End of https://www.toptal.com/developers/gitignore/api/vim,macos diff --git a/gds21/Cargo.toml b/gds21/Cargo.toml index 825d8c6..9984668 100644 --- a/gds21/Cargo.toml +++ b/gds21/Cargo.toml @@ -15,12 +15,12 @@ exclude = ["resources",] layout21utils = {path = "../layout21utils", version="0.2.1"} # External dependencies byteorder = "1.3.4" -chrono = {version = "0.4", features = ["serde"]} +chrono = { version = "0.4.19", features = ["serde"] } derive_builder = "0.9.0" derive_more = "0.99.16" num-derive = "0.3" -num-traits = "0.2" -serde = {version = "1.0", features = ["derive"]} +num-traits = "0.2.15" +serde = { version = "1.0.137", features = ["derive"] } serde_derive = "1.0.88" serde_json = "1.0" tempfile = {version = "3", optional = true} diff --git a/gds21/src/lib.rs b/gds21/src/lib.rs index 741a855..66ef4af 100644 --- a/gds21/src/lib.rs +++ b/gds21/src/lib.rs @@ -1114,7 +1114,7 @@ pub enum GdsError { ctx: Vec, }, /// Boxed (External) Errors - Boxed(Box), + Boxed(Box), /// Other errors Str(String), } diff --git a/layout21converters/src/gds2proto.rs b/layout21converters/src/gds2proto.rs index 67488a3..1d0b884 100644 --- a/layout21converters/src/gds2proto.rs +++ b/layout21converters/src/gds2proto.rs @@ -7,7 +7,7 @@ use clap::Parser; use layout21raw as raw; use std::error::Error; -// => The doc-comment on `ProgramOptions` here is displayed by the `clap`-generated help docs => +// => The doc-comment on `ProgramOptions` here is displayed by the `clap`-generated help docs => /// GDSII to VLSIR Protobuf Schema Converter #[derive(Parser)] @@ -66,6 +66,7 @@ mod tests { use super::*; #[test] + #[ignore = "requires layer config info"] fn roundtrip_to_golden_file() { // The golden file was created by running the program: // $ cargo run -- \ diff --git a/layout21protos/Cargo.toml b/layout21protos/Cargo.toml index 4099f81..fc3586d 100644 --- a/layout21protos/Cargo.toml +++ b/layout21protos/Cargo.toml @@ -11,3 +11,4 @@ workspace = "../" [dependencies] vlsir = { path = "vlsir/bindings/rust", version = "2.0.1" } + diff --git a/layout21protos/src/lib.rs b/layout21protos/src/lib.rs index a4927aa..a90d768 100644 --- a/layout21protos/src/lib.rs +++ b/layout21protos/src/lib.rs @@ -1,7 +1,7 @@ -//! -//! # Layout21Protos -//! +//! +//! # Layout21Protos +//! //! A wrapper around the [VLSIR](https://crates.io/crates/vlsir) -//! +//! pub use vlsir::*; diff --git a/layout21raw/Cargo.toml b/layout21raw/Cargo.toml index d296d14..e36e501 100644 --- a/layout21raw/Cargo.toml +++ b/layout21raw/Cargo.toml @@ -19,10 +19,12 @@ lef21 = {path = "../lef21", version = "0.2.1", optional = true} # Crates.io enum_dispatch = "0.3.7" num-integer = "0.1" -num-traits = "0.2" -serde = {version = "1.0", features = ["derive"]} -serde_derive = "1.0.88" +num-traits = "0.2.15" +serde = { version = "1.0.137", features = ["derive"] } +serde_derive = "1.0" slotmap = {version = "1.0", features = ["serde"]} +derive_builder = "0.11" +anyhow = "1" [features] # Break out each import/export format as a feature diff --git a/layout21raw/src/align.rs b/layout21raw/src/align.rs new file mode 100644 index 0000000..cba1cc9 --- /dev/null +++ b/layout21raw/src/align.rs @@ -0,0 +1,135 @@ +use crate::{snap_to_grid, translate::Translate, BoundBox, BoundBoxTrait, Int, Point}; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum AlignMode { + Left, + Right, + Bottom, + Top, + CenterHorizontal, + CenterVertical, + ToTheRight, + ToTheLeft, + Beneath, + Above, +} + +pub trait AlignRect: Translate + BoundBoxTrait { + fn align(&mut self, mode: AlignMode, obox: BoundBox, space: Int) -> &mut Self { + let sbox = self.bbox(); + + match mode { + AlignMode::Left => { + self.translate(Point::new(obox.p0.x - sbox.p0.x + space, 0)); + } + AlignMode::Right => { + self.translate(Point::new(obox.p1.x - sbox.p1.x + space, 0)); + } + AlignMode::Bottom => { + self.translate(Point::new(0, obox.p0.y - sbox.p0.y + space)); + } + AlignMode::Top => { + self.translate(Point::new(0, obox.p1.y - sbox.p1.y + space)); + } + AlignMode::ToTheRight => { + self.translate(Point::new(obox.p1.x - sbox.p0.x + space, 0)); + } + AlignMode::ToTheLeft => { + self.translate(Point::new(obox.p0.x - sbox.p1.x - space, 0)); + } + AlignMode::CenterHorizontal => { + self.translate(Point::new( + ((obox.p0.x + obox.p1.x) - (sbox.p0.x + sbox.p1.x)) / 2 + space, + 0, + )); + } + AlignMode::CenterVertical => { + self.translate(Point::new( + 0, + ((obox.p0.y + obox.p1.y) - (sbox.p0.y + sbox.p1.y)) / 2 + space, + )); + } + AlignMode::Beneath => { + self.translate(Point::new(0, obox.p0.y - sbox.p1.y - space)); + } + AlignMode::Above => { + self.translate(Point::new(0, obox.p1.y - sbox.p0.y + space)); + } + } + + self + } + + fn align_left(&mut self, other: BoundBox) { + self.align(AlignMode::Left, other, 0); + } + + fn align_right(&mut self, other: BoundBox) { + self.align(AlignMode::Right, other, 0); + } + + fn align_bottom(&mut self, other: BoundBox) { + self.align(AlignMode::Bottom, other, 0); + } + + fn align_top(&mut self, other: BoundBox) { + self.align(AlignMode::Top, other, 0); + } + + fn align_to_the_right_of(&mut self, other: BoundBox, space: Int) { + self.align(AlignMode::ToTheRight, other, space); + } + + fn align_to_the_left_of(&mut self, other: BoundBox, space: Int) { + self.align(AlignMode::ToTheLeft, other, space); + } + + fn align_centers_horizontally(&mut self, other: BoundBox) { + self.align(AlignMode::CenterHorizontal, other, 0); + } + + fn align_centers_vertically(&mut self, other: BoundBox) { + self.align(AlignMode::CenterVertical, other, 0); + } + + fn align_centers(&mut self, other: BoundBox) { + self.align_centers_vertically(other); + self.align_centers_vertically(other); + } + + fn align_beneath(&mut self, other: BoundBox, space: Int) { + self.align(AlignMode::Beneath, other, space); + } + + fn align_above(&mut self, other: BoundBox, space: Int) { + self.align(AlignMode::Above, other, space); + } + + fn align_centers_horizontally_gridded(&mut self, other: BoundBox, grid: Int) { + // Align the center + self.align(AlignMode::CenterHorizontal, other, 0); + + // Then snap to the nearest grid location + let bbox = self.bbox(); + assert_eq!(bbox.width() % grid, 0); + let offset = snap_to_grid(bbox.p0.x, grid) - bbox.p0.x; + self.translate(Point::new(offset, 0)); + } + + fn align_centers_vertically_gridded(&mut self, other: BoundBox, grid: Int) { + // Align the center + self.align(AlignMode::CenterVertical, other, 0); + + // Then snap to the nearest grid location + let bbox = self.bbox(); + assert_eq!(bbox.height() % grid, 0); + let offset = snap_to_grid(bbox.p0.y, grid) - bbox.p0.y; + self.translate(Point::new(0, offset)); + } + + fn align_centers_gridded(&mut self, other: BoundBox, grid: Int) { + self.align_centers_horizontally_gridded(other, grid); + self.align_centers_vertically_gridded(other, grid); + } +} diff --git a/layout21raw/src/bbox.rs b/layout21raw/src/bbox.rs index 5ae222d..4ecc6f8 100644 --- a/layout21raw/src/bbox.rs +++ b/layout21raw/src/bbox.rs @@ -28,6 +28,14 @@ impl BoundBox { fn new(p0: Point, p1: Point) -> Self { Self { p0, p1 } } + #[inline] + pub fn width(&self) -> Int { + self.p1.x - self.p0.x + } + #[inline] + pub fn height(&self) -> Int { + self.p1.y - self.p0.y + } /// Create a new [BoundBox] from a single [Point]. /// The resultant [BoundBox] comprises solely the point, having zero area. pub fn from_point(pt: &Point) -> Self { @@ -73,6 +81,19 @@ impl BoundBox { pub fn center(&self) -> Point { Point::new((self.p0.x + self.p1.x) / 2, (self.p0.y + self.p1.y) / 2) } + + #[inline] + pub fn into_rect(self) -> Rect { + Rect::from(self) + } +} + +impl From for BoundBox { + fn from(r: Rect) -> Self { + debug_assert!(r.p0.x <= r.p1.x); + debug_assert!(r.p0.y <= r.p1.y); + Self { p0: r.p0, p1: r.p1 } + } } /// @@ -115,6 +136,12 @@ impl BoundBoxTrait for BoundBox { BoundBox::new(pmin, pmax) } fn union(&self, bbox: &BoundBox) -> BoundBox { + if bbox.is_empty() { + return *self; + } + if self.is_empty() { + return *bbox; + } // Take the minimum and maximum of the two bounding boxes BoundBox::new( Point::new(self.p0.x.min(bbox.p0.x), self.p0.y.min(bbox.p0.y)), @@ -146,6 +173,7 @@ impl BoundBoxTrait for Shape { Shape::Rect(ref r) => BoundBox::from_points(&r.p0, &r.p1), Shape::Polygon(ref p) => (&p.points).bbox(), Shape::Path(ref p) => (&p.points).bbox(), + Shape::Point(ref p) => BoundBox::from_point(p), } } } diff --git a/layout21raw/src/data.rs b/layout21raw/src/data.rs index 0f31b58..0bae9b7 100644 --- a/layout21raw/src/data.rs +++ b/layout21raw/src/data.rs @@ -9,10 +9,15 @@ use std::collections::{HashMap, HashSet}; use std::hash::Hash; +use gds21::GdsLayerSpec; // Crates.io +use derive_builder::Builder; use serde::{Deserialize, Serialize}; use slotmap::{new_key_type, SlotMap}; +use crate::align::AlignRect; +use crate::translate::Translate; +use crate::Rect; // Local Imports use crate::{ bbox::{BoundBox, BoundBoxTrait}, @@ -115,23 +120,162 @@ impl SiUnits { } /// Instance of another Cell -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Builder)] pub struct Instance { /// Instance Name + #[builder(setter(into))] pub inst_name: String, /// Cell Definition Reference + #[builder(setter(into))] pub cell: Ptr, /// Location of `cell` origin /// regardless of rotation or reflection + #[builder(default)] pub loc: Point, /// Vertical reflection, /// applied *before* rotation + #[builder(default)] pub reflect_vert: bool, /// Angle of rotation (degrees), /// Clockwise and applied *after* reflection + #[builder(default, setter(strip_option))] pub angle: Option, } +impl BoundBoxTrait for Instance { + fn bbox(&self) -> BoundBox { + let inner = { + let cell = self.cell.read().unwrap(); + cell.layout.as_ref().unwrap().bbox() + }; + + if inner.is_empty() { + return inner; + } + + let r = Rect { + p0: inner.p0, + p1: inner.p1, + }; + + let r = r.transform(&Transform::from_instance( + &self.loc, + self.reflect_vert, + self.angle, + )); + + BoundBox { p0: r.p0, p1: r.p1 } + } +} + +impl Translate for Instance { + fn translate(&mut self, v: Point) { + self.loc.translate(v); + } +} + +impl AlignRect for Instance {} + +impl Instance { + pub fn new(name: N, cell: C) -> Self + where + N: Into, + C: Into>, + { + Self { + cell: cell.into(), + inst_name: name.into(), + loc: Point::new(0, 0), + reflect_vert: false, + angle: None, + } + } + + #[inline] + pub fn builder() -> InstanceBuilder { + InstanceBuilder::default() + } + + #[inline] + pub fn transform(&self) -> Transform { + self._transform() + } + + #[inline] + fn _transform(&self) -> Transform { + Transform::from_instance(&self.loc, self.reflect_vert, self.angle) + } + + pub fn port(&self, net: impl Into) -> AbstractPort { + let net = net.into(); + let cell = self.cell.read().unwrap(); + let port = cell + .abs + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Cell `{}` does not have an abstract", &cell.name)) + .unwrap() + .ports + .iter() + .find(|p| p.net == net) + .ok_or_else(|| anyhow::anyhow!("No port named {net}")) + .unwrap(); + + port.transform(&self._transform()) + } + + pub fn ports_starting_with(&self, net: &str) -> Vec { + let cell = self.cell.read().unwrap(); + let xform = self._transform(); + let ports = cell + .abs + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Cell `{}` does not have an abstract", &cell.name)) + .unwrap() + .ports + .iter() + .filter(|p| p.net.starts_with(net)) + .map(|p| p.transform(&xform)) + .collect(); + + ports + } + + pub fn ports(&self) -> Vec { + let cell = self.cell.read().unwrap(); + let ports = &cell.abs.as_ref().unwrap().ports; + let xform = self._transform(); + ports.iter().map(|p| p.transform(&xform)).collect() + } + + pub fn has_abstract(&self) -> bool { + let cell = self.cell.read().unwrap(); + cell.has_abstract() + } + + pub fn reflect_vert_anchored(&mut self) -> &mut Self { + let box0 = self.bbox(); + self.reflect_vert = !self.reflect_vert; + let box1 = self.bbox(); + self.loc.y += box0.p0.y - box1.p0.y; + self + } + + pub fn reflect_horiz_anchored(&mut self) -> &mut Self { + let box0 = self.bbox(); + self.reflect_vert = !self.reflect_vert; + self.angle = Some(self.angle.unwrap_or(0f64) + 180f64); + + let box1 = self.bbox(); + self.loc.x += box0.p0.x - box1.p0.x; + self.loc.y += box0.p0.y - box1.p0.y; + + let final_box = self.bbox(); + assert_eq!(final_box, box0); + + self + } +} + /// # Layer Set & Manager /// /// Keep track of active layers, and index them by name and number. @@ -165,19 +309,10 @@ impl Layers { } LayoutError::fail("No more layer numbers available") } - /// Get a reference to the [LayerKey] for layer-number `num` - pub fn keynum(&self, num: i16) -> Option { - self.nums.get(&num).map(|x| x.clone()) - } /// Get a reference to the [LayerKey] layer-name `name` pub fn keyname(&self, name: impl Into) -> Option { self.names.get(&name.into()).map(|x| x.clone()) } - /// Get a reference to [Layer] number `num` - pub fn num(&self, num: i16) -> Option<&Layer> { - let key = self.nums.get(&num)?; - self.slots.get(*key) - } /// Get a reference to [Layer] name `name` pub fn name(&self, name: &str) -> Option<&Layer> { let key = self.names.get(name)?; @@ -192,41 +327,40 @@ impl Layers { pub fn get(&self, key: LayerKey) -> Option<&Layer> { self.slots.get(key) } - /// Get the ([LayerKey], [LayerPurpose]) objects for numbers (`layernum`, `purposenum`) if present. - /// Inserts a new [Layer] if `layernum` is not present. - /// Returns `LayerPurpose::Other(purposenum)` if `purposenum` is not present on that layer. - pub fn get_or_insert( - &mut self, - layernum: i16, - purposenum: i16, - ) -> LayoutResult<(LayerKey, LayerPurpose)> { - // Get the [LayerKey] for `layernum`, creating the [Layer] if it doesn't exist. - let key = match self.keynum(layernum) { - Some(key) => key.clone(), - None => self.add(Layer::from_num(layernum)), - }; - // Slightly awkwardly, get that [Layer] (back), so we can get or add a [LayerPurpose] - let layer = self - .slots - .get_mut(key) - .ok_or(LayoutError::msg("Layer Not Found"))?; - // Get or create the corresponding [LayerPurpose] - let purpose = match layer.purpose(purposenum) { - Some(purpose) => purpose.clone(), - None => { - // Create a new anonymous/ numbered layer-purpose - let purpose = LayerPurpose::Other(purposenum); - layer.add_purpose(purposenum, purpose.clone())?; - purpose - } - }; - Ok((key, purpose)) - } /// Get a shared reference to the internal <[LayerKey], [Layer]> map pub fn slots(&self) -> &SlotMap { &self.slots } + + pub fn get_new_purpose(&self, num: i16, purpose: i16, to: LayerPurpose) -> Option { + let lay = self.get_layer_from_spec(num, purpose)?; + let purpose = lay.num(&to)?; + let (key, _) = self.get_from_spec(num, purpose)?; + Some(key) + } + + pub fn get_layer_from_spec(&self, num: i16, purpose: i16) -> Option<&Layer> { + let (key, _) = self.get_from_spec(num, purpose)?; + self.get(key) + } + + pub fn get_from_spec(&self, num: i16, purpose: i16) -> Option<(LayerKey, LayerPurpose)> { + for (k, layer) in self.slots().iter() { + if layer.layernum != spec.0 { + continue; + } + if let Some(purpose) = layer.purpose(spec.1) { + return Some((k, purpose.clone())); + } + } + None + } + + pub fn get_layer_names(&self) -> Vec<&String> { + self.names.keys().collect() + } } + /// Layer-Purpose Enumeration /// Includes the common use-cases for each shape, /// and two "escape hatches", one named and one not. @@ -252,13 +386,18 @@ impl Default for LayerPurpose { /// # Layer Specification /// As in seemingly every layout system, this uses two numbers to identify each layer. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] pub struct LayerSpec(i16, i16); impl LayerSpec { pub fn new(n1: i16, n2: i16) -> Self { Self(n1, n2) } } +impl From for LayerSpec { + fn from(other: GdsLayerSpec) -> Self { + Self(other.layer, other.xtype) + } +} /// # Per-Layer Datatype Specification /// Includes the datatypes used for each category of element on layer `layernum` #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)] @@ -339,6 +478,10 @@ impl Layer { pub fn num(&self, purpose: &LayerPurpose) -> Option { self.nums.get(purpose).copied() } + /// Retrieve a list of purpose number-[LayerPurpose] tuples + pub fn get_purps(&self) -> Vec<(&i16, &LayerPurpose)> { + self.purps.iter().collect() + } } /// Raw Abstract-Layout @@ -350,7 +493,7 @@ pub struct Abstract { /// Cell Name pub name: String, /// Outline - pub outline: Polygon, + pub outline: Option, /// Ports pub ports: Vec, /// Blockages @@ -358,16 +501,26 @@ pub struct Abstract { } impl Abstract { /// Create a new [Abstract] with the given `name` - pub fn new(name: impl Into, outline: Polygon) -> Self { + pub fn new(name: impl Into) -> Self { let name = name.into(); Self { name, - outline, + outline: None, ports: Vec::new(), blockages: HashMap::new(), } } + + pub fn add_port(&mut self, port: AbstractPort) -> &mut Self { + self.ports.push(port); + self + } + + pub fn set_name(&mut self, name: impl Into) { + self.name = name.into(); + } } + /// # Port Element for [Abstract]s #[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct AbstractPort { @@ -385,6 +538,61 @@ impl AbstractPort { shapes: HashMap::new(), } } + + /// Renames this port + pub fn named(mut self, name: impl Into) -> Self { + self.net = name.into(); + self + } + + pub fn add_shape(&mut self, layer: LayerKey, shape: Shape) -> &mut Self { + let v = self.shapes.entry(layer).or_insert(Vec::with_capacity(1)); + v.push(shape); + self + } + + pub fn set_net(&mut self, net: impl Into) -> &mut Self { + self.net = net.into(); + self + } + + /// Adds the shapes of `other` to this [`AbstractPort`]. + pub fn merge(&mut self, other: Self) { + for (k, mut v) in other.shapes.into_iter() { + let shapes = self.shapes.entry(k).or_insert(Vec::new()); + shapes.append(&mut v); + } + } + + pub fn bbox(&self, layer: LayerKey) -> Option { + if let Some(shapes) = self.shapes.get(&layer) { + let mut bbox = BoundBox::empty(); + for s in shapes { + bbox = s.union(&bbox); + } + Some(bbox) + } else { + None + } + } + + pub fn largest_rect(&self, layer: LayerKey) -> Option { + let shapes = self.shapes.get(&layer)?; + let mut best = None; + let mut best_area = 0; + for s in shapes { + let r = match s { + Shape::Rect(r) => r, + _ => continue, + }; + let area = r.area(); + if area > best_area { + best_area = area; + best = Some(*r) + } + } + best + } } /// # Raw Layout Library @@ -400,6 +608,7 @@ pub struct Library { /// Cell Definitions pub cells: PtrList, } + impl Library { /// Create a new and empty Library pub fn new(name: impl Into, units: Units) -> Self { @@ -409,6 +618,20 @@ impl Library { ..Default::default() } } + + /// Finds the cell with the given name, if one exists. + /// + /// Note that this operation takes `O(n)` time in the worst + /// case, where `n` is the number of cells in the library. + pub fn cell(&self, name: &str) -> Option> { + self.cells + .iter() + .find(|c| { + let c = c.read().unwrap(); + c.name == name + }) + .cloned() + } } /// # Dependency-Orderer @@ -460,7 +683,7 @@ pub struct Cell { pub layout: Option, } impl Cell { - /// Create a new and empty Cell named `name` + /// Create a new and empty Cell named `name`. pub fn new(name: impl Into) -> Self { let name = name.into(); Self { @@ -468,7 +691,80 @@ impl Cell { ..Default::default() } } + + /// Creates a cell with empty abstract and layout views. + pub fn empty(name: S) -> Self + where + S: Clone + Into, + { + let abs = Some(Abstract::new(name.clone())); + let layout = Some(Layout::new(name.clone())); + let name = name.into(); + Self { name, abs, layout } + } + + #[inline] + pub fn has_abstract(&self) -> bool { + self.abs.is_some() + } + + /// Gets a mutable reference to this cell's [`Layout`]. + /// + /// Panics if the cell does not have a layout view. + #[inline] + pub fn layout_mut(&mut self) -> &mut Layout { + self.layout.as_mut().unwrap() + } + + /// Gets an immutable reference to this cell's [`Layout`]. + /// + /// Panics if the cell does not have a layout view. + #[inline] + pub fn layout(&self) -> &Layout { + self.layout.as_ref().unwrap() + } + + /// Gets an immutable reference to this cell's [`Abstract`]. + /// + /// Panics if the cell does not have an abstract view. + #[inline] + pub fn abs(&self) -> &Abstract { + self.abs.as_ref().unwrap() + } + + /// Gets a mutable reference to this cell's [`Abstract`]. + /// + /// Panics if the cell does not have an abstract view. + #[inline] + pub fn abs_mut(&mut self) -> &mut Abstract { + self.abs.as_mut().unwrap() + } + + /// Adds a new pin to the layout and abstract views of this cell, if they exist. + pub fn add_pin(&mut self, net: impl Into, layer: LayerKey, rect: Rect) { + let net = net.into(); + if let Some(ref mut layout) = self.layout { + layout.add_pin(net.clone(), layer, rect); + } + if let Some(ref mut abs) = self.abs { + let mut port = AbstractPort::new(net); + port.add_shape(layer, Shape::Rect(rect)); + abs.add_port(port); + } + } + + /// Adds a new pin to the layout and abstract views of this cell, if they exist. + pub fn add_pin_from_port(&mut self, port: AbstractPort, layer: LayerKey) { + if let Some(ref mut layout) = self.layout { + let rect = port.largest_rect(layer).unwrap(); + layout.add_pin(port.net.clone(), layer, rect); + } + if let Some(ref mut abs) = self.abs { + abs.add_port(port); + } + } } + impl From for Cell { fn from(src: Abstract) -> Self { Self { @@ -505,14 +801,43 @@ pub struct Layout { pub annotations: Vec, } impl Layout { + pub fn new(name: impl Into) -> Self { + Self { + name: name.into(), + ..Default::default() + } + } + /// Create a rectangular [BoundBox] surrounding all elements in the [Layout]. pub fn bbox(&self) -> BoundBox { let mut bbox = BoundBox::empty(); for elem in &self.elems { bbox = elem.inner.union(&bbox); } + for inst in &self.insts { + let b = inst.bbox(); + if !b.is_empty() { + let s = Shape::Rect(Rect { p0: b.p0, p1: b.p1 }); + bbox = s.union(&bbox); + } + } bbox } + + pub fn add_inst(&mut self, inst: T) + where + T: Into, + { + self.insts.push(inst.into()); + } + + pub fn add(&mut self, elem: T) + where + T: Into, + { + self.elems.push(elem.into()); + } + /// Flatten a [Layout], particularly its hierarchical instances, to a vector of [Element]s pub fn flatten(&self) -> LayoutResult> { // Kick off recursive calls, with the identity-transform applied for the top-level `layout` @@ -520,6 +845,26 @@ impl Layout { flatten_helper(self, &Transform::identity(), &mut elems)?; Ok(elems) } + + /// Creates a physical layout pin with the given layer, position and name. + pub fn add_pin(&mut self, net: impl Into, layer: LayerKey, rect: Rect) { + self.elems.push(Element { + net: Some(net.into()), + layer, + inner: Shape::Rect(rect), + purpose: LayerPurpose::Pin, + }); + } + + /// Draws a rectangle on the given layer. + pub fn draw_rect(&mut self, layer: LayerKey, rect: Rect) { + self.elems.push(Element { + net: None, + layer, + inner: Shape::Rect(rect), + purpose: LayerPurpose::Drawing, + }); + } } /// Internal helper and core logic for [Layout::flatten]. fn flatten_helper( diff --git a/layout21raw/src/error.rs b/layout21raw/src/error.rs index 2b2ad99..2187ca2 100644 --- a/layout21raw/src/error.rs +++ b/layout21raw/src/error.rs @@ -25,11 +25,11 @@ pub enum LayoutError { /// Conversion Errors, with Boxed External Error Conversion { message: String, - err: Box, + err: Box, stack: Vec, }, /// Boxed External Errors - Boxed(Box), + Boxed(Box), /// Uncategorized Error, with String Message Str(String), /// # [Ptr] Locking @@ -113,7 +113,7 @@ impl From> for LayoutError { Self::PtrLock } } -impl From> for LayoutError { +impl From> for LayoutError { fn from(e: Box) -> Self { Self::Boxed(e) } diff --git a/layout21raw/src/gds.rs b/layout21raw/src/gds.rs index 6468a58..0803232 100644 --- a/layout21raw/src/gds.rs +++ b/layout21raw/src/gds.rs @@ -13,6 +13,7 @@ use gds21::GdsElement; // Crates.io use slotmap::{new_key_type, SlotMap}; +use crate::LayerSpec; // Local imports use crate::{ bbox::BoundBoxTrait, @@ -136,7 +137,9 @@ impl<'lib> GdsExporter<'lib> { }); // Blockages do not map to GDSII elements. // Conversion includes the abstract's name, outline and ports. - elems.push(outline); + if let Some(outline) = abs.outline { + elems.push(outline); + } // Convert each [AbstractPort] for port in abs.ports.iter() { @@ -161,8 +164,12 @@ impl<'lib> GdsExporter<'lib> { let pin_spec = self.export_layerspec(&layerkey, &LayerPurpose::Pin)?; let label_spec = self.export_layerspec(&layerkey, &LayerPurpose::Label)?; for shape in shapes.iter() { - elems.push(self.export_shape(shape, &drawing_spec)?); - elems.push(self.export_shape(shape, &pin_spec)?); + if let Some(elem) = self.export_shape(shape, &drawing_spec)? { + elems.push(elem); + } + if let Some(elem) = self.export_shape(shape, &pin_spec)? { + elems.push(elem); + } elems.push(self.export_shape_label(&port.net, shape, &label_spec)?); } } @@ -246,11 +253,14 @@ impl<'lib> GdsExporter<'lib> { // Get the element's layer-numbers pair let layerspec = self.export_layerspec(&elem.layer, &elem.purpose)?; // Convert its core inner [Shape] - let mut gds_elems = vec![self.export_shape(&elem.inner, &layerspec)?]; + let mut gds_elems = match self.export_shape(&elem.inner, &layerspec)? { + Some(x) => vec![x], + None => Vec::new(), + }; // If there's an assigned net, create a corresponding text-element if let Some(name) = &elem.net { // Get the element's layer-numbers pair - let layerspec = self.export_layerspec(&elem.layer, &LayerPurpose::Label)?; + let layerspec = self.export_layerspec(&elem.layer, &elem.purpose)?; gds_elems.push(self.export_shape_label(name, &elem.inner, &layerspec)?); } Ok(gds_elems) @@ -265,7 +275,7 @@ impl<'lib> GdsExporter<'lib> { &mut self, shape: &Shape, layerspec: &gds21::GdsLayerSpec, - ) -> LayoutResult { + ) -> LayoutResult> { let elem = match shape { Shape::Rect(r) => { let (p0, p1) = (&r.p0, &r.p1); @@ -317,8 +327,9 @@ impl<'lib> GdsExporter<'lib> { } .into() } + Shape::Point(_) => return Ok(None), }; - Ok(elem) + Ok(Some(elem)) } /// Create a labeling [gds21::GdsElement] for [Shape] `shape` pub fn export_shape_label( @@ -386,9 +397,16 @@ impl PlaceLabels for Shape { Shape::Rect(ref r) => r.label_location(), Shape::Polygon(ref p) => p.label_location(), Shape::Path(ref p) => p.label_location(), + Shape::Point(ref p) => p.label_location(), } } } +impl PlaceLabels for Point { + fn label_location(&self) -> LayoutResult { + // Place rectangle-labels in the center of the rectangle + Ok(*self) + } +} impl PlaceLabels for Rect { fn label_location(&self) -> LayoutResult { // Place rectangle-labels in the center of the rectangle @@ -593,14 +611,20 @@ impl GdsImporter { /// Import a GDS Cell ([gds21::GdsStruct]) into a [Cell] fn import_cell(&mut self, strukt: &gds21::GdsStruct) -> LayoutResult { self.ctx.push(ErrorContext::Cell(strukt.name.clone())); - let cell = self.import_layout(strukt)?.into(); + let (layout, abs) = self.import_layout(strukt)?; + let cell = Cell { + name: layout.name.clone(), + layout: Some(layout), + abs: Some(abs), + }; self.ctx.pop(); Ok(cell) } /// Import a GDS Cell ([gds21::GdsStruct]) into a [Layout] - fn import_layout(&mut self, strukt: &gds21::GdsStruct) -> LayoutResult { + fn import_layout(&mut self, strukt: &gds21::GdsStruct) -> LayoutResult<(Layout, Abstract)> { let mut layout = Layout::default(); let name = strukt.name.clone(); + let mut abs = Abstract::new(&name); layout.name = name.clone(); self.ctx.push(ErrorContext::Impl); @@ -646,62 +670,96 @@ impl GdsImporter { } } } + let mut ports = HashMap::new(); // Pass two: sort out whether each [gds21::GdsTextElem] is a net-label, // And if so, assign it as a net-name on each intersecting [Element]. // Text elements which do not overlap a geometric element on the same layer // are converted to annotations. + let layer_map = Ptr::clone(&self.layers); + let layer_map = layer_map.read().unwrap(); for textelem in &texts { + let net_name = textelem.string.to_lowercase().to_string(); + let text_key = + layer_map.get_from_spec(LayerSpec::new(textelem.layer, textelem.texttype)); let loc = self.import_point(&textelem.xy)?; - if let Some(layer) = layers.get(&textelem.layer) { - // Layer exists in geometry; see which elements intersect with this text - let mut hit = false; - for ekey in layer.iter() { - let elem = elems.get_mut(*ekey).unwrap(); - if elem.inner.contains(&loc) { - // Label lands inside this element. - // Check whether we have an existing label. - // If so, it better be the same net name! - // FIXME: casing, as usual with all EDA crap. - // Here we support case *insensitive* GDSes, and lower-case everything. - // Many GDS seem to mix and match upper and lower case, - // essentially using the case-insensitivity for connections (bleh). - let lower_case_name = textelem.string.to_lowercase(); - if let Some(pname) = &elem.net { - if *pname != lower_case_name { - println!( - "Warning: GDSII labels shorting nets {} and {} on layer {}", + if text_key.is_none() { + println!( + "Ignoring text element on unknown GDS layer ({}, {}).", + textelem.layer, textelem.texttype + ); + continue; + } + let (text_key, purp) = text_key.unwrap(); + let draw_key = + layer_map.get_new_purpose(textelem.layer, textelem.texttype, LayerPurpose::Drawing); + + if (purp == LayerPurpose::Label || purp == LayerPurpose::Pin) && draw_key.is_some() { + let port = ports + .entry(net_name.clone()) + .or_insert(AbstractPort::new(&net_name)); + if let Some(layer) = layers.get(&textelem.layer) { + if draw_key.is_none() { + println!( + "No drawing layer found for GDS layer ({}, {}).", + textelem.layer, textelem.texttype + ); + } + // Layer exists in geometry; see which elements intersect with this text + for ekey in layer.iter() { + let elem = elems.get_mut(*ekey).unwrap(); + if elem.inner.contains(&loc) && Some(elem.layer) == draw_key { + let layer_key = draw_key.unwrap(); + // Label lands inside this element. + // Check whether we have an existing label. + // If so, it better be the same net name! + // FIXME: casing, as usual with all EDA crap. + // Here we support case *insensitive* GDSes, and lower-case everything. + // Many GDS seem to mix and match upper and lower case, + // essentially using the case-insensitivity for connections (bleh). + let lower_case_name = textelem.string.to_lowercase(); + if let Some(pname) = &elem.net { + if *pname != lower_case_name { + println!( + "Warning: GDSII labels shorting nets {} and {} on layer {} in cell {}", pname, textelem.string.clone(), - textelem.layer + textelem.layer, + &name, ); - // return self.fail(format!( - // "GDSII labels shorting nets {} and {} on layer {}", - // pname, - // textelem.string.clone(), - // textelem.layer - // )); + // return self.fail(format!( + // "GDSII labels shorting nets {} and {} on layer {}", + // pname, + // textelem.string.clone(), + // textelem.layer + // )); + } + } else { + elem.net = Some(lower_case_name); + port.add_shape(layer_key, elem.inner.clone()); } - } else { - elem.net = Some(lower_case_name); } - hit = true; } } - // If we've hit at least one, carry onto the next TextElement - if hit { - continue; - } + } else { + // Import the text element as is + println!("Importing unattached GDSII text element `{}`", &net_name); + elems.insert(Element { + net: Some(net_name), + layer: text_key, + purpose: purp, + inner: Shape::Point(loc), + }); } - // No hits (or a no-shape Layer). Create an annotation instead. - layout.annotations.push(TextElement { - string: textelem.string.clone(), - loc, - }); + } + drop(layer_map); + + for (_, port) in ports.into_iter() { + abs.add_port(port); } // Pull the elements out of the local slot-map, into the vector that [Layout] wants layout.elems = elems.drain().map(|(_k, v)| v).collect(); self.ctx.pop(); - Ok(layout) + Ok((layout, abs)) } /// Import a [gds21::GdsBoundary] into an [Element] fn import_boundary(&mut self, x: &gds21::GdsBoundary) -> LayoutResult { @@ -934,8 +992,10 @@ impl GdsImporter { elem: &impl gds21::HasLayer, ) -> LayoutResult<(LayerKey, LayerPurpose)> { let spec = elem.layerspec(); - let mut layers = self.layers.write()?; - layers.get_or_insert(spec.layer, spec.xtype) + let layers = self.layers.write()?; + layers + .get_from_spec(spec.into()) + .ok_or(LayoutError::msg("Layer Not Found")) } } impl ErrorHelper for GdsImporter { diff --git a/layout21raw/src/geom.rs b/layout21raw/src/geom.rs index f17828f..d525092 100644 --- a/layout21raw/src/geom.rs +++ b/layout21raw/src/geom.rs @@ -6,13 +6,13 @@ //! // Std-Lib -use std::convert::TryFrom; +use std::{collections::HashMap, convert::TryFrom, fmt::Display}; // Crates.io use serde::{Deserialize, Serialize}; // Local imports -use crate::{bbox::BoundBoxTrait, Int}; +use crate::{align::AlignRect, bbox::BoundBoxTrait, AbstractPort, BoundBox, Int}; /// # Point in two-dimensional layout-space #[derive(Debug, Copy, Clone, Default, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] @@ -25,6 +25,10 @@ impl Point { pub fn new(x: Int, y: Int) -> Self { Self { x, y } } + /// The origin, (0, 0). + pub fn zero() -> Self { + Self { x: 0, y: 0 } + } /// Create a new [Point] which serves as an offset in direction `dir` pub fn offset(val: Int, dir: Dir) -> Self { match dir { @@ -67,12 +71,139 @@ impl Point { } } } + +#[derive(Debug, Clone, Copy, Hash, Serialize, Deserialize, PartialEq, Eq)] +pub struct Span { + start: Int, + stop: Int, +} + +/// Snaps `pos` to the nearest multiple of `grid`. +pub fn snap_to_grid(pos: Int, grid: Int) -> Int { + assert!(grid > 0); + + let rem = pos.rem_euclid(grid); + assert!(rem >= 0); + assert!(rem < grid); + if rem <= grid / 2 { + pos - rem + } else { + pos + grid - rem + } +} + +impl Span { + pub fn new(start: Int, stop: Int) -> Self { + use std::cmp::{max, min}; + let lower = min(start, stop); + let upper = max(start, stop); + Self { + start: lower, + stop: upper, + } + } + + pub fn expand(&mut self, pos: bool, amount: Int) -> &mut Self { + if pos { + self.stop += amount; + } else { + self.start -= amount; + } + self + } + + pub fn edge(&self, pos: bool) -> Int { + if pos { + self.stop + } else { + self.start + } + } + + pub fn from_center_span(center: Int, span: Int) -> Self { + assert!(span >= 0); + assert_eq!(span % 2, 0); + + Self::new(center - (span / 2), center + (span / 2)) + } + + pub fn from_center_span_gridded(center: Int, span: Int, grid: Int) -> Self { + assert!(span >= 0); + assert_eq!(span % 2, 0); + assert_eq!(span % grid, 0); + + let start = snap_to_grid(center - (span / 2), grid); + + Self::new(start, start + span) + } + + #[inline] + pub fn center(&self) -> Int { + (self.start + self.stop) / 2 + } + + #[inline] + pub fn intersects(&self, other: &Self) -> bool { + !(other.stop < self.start || self.stop < other.start) + } + + #[inline] + pub fn length(&self) -> Int { + self.stop - self.start + } + + #[inline] + pub fn start(&self) -> Int { + self.start + } + + #[inline] + pub fn stop(&self) -> Int { + self.stop + } + + pub fn merge(spans: impl IntoIterator) -> Self { + use std::cmp::{max, min}; + let mut spans = spans.into_iter(); + let (mut start, mut stop) = spans + .next() + .expect("Span::merge requires at least one span") + .into(); + + for span in spans { + start = min(start, span.start); + stop = max(stop, span.stop); + } + + debug_assert!(start <= stop); + + Span { start, stop } + } +} + +impl From<(Int, Int)> for Span { + #[inline] + fn from(tup: (Int, Int)) -> Self { + Self::new(tup.0, tup.1) + } +} + +impl From for (Int, Int) { + #[inline] + fn from(s: Span) -> Self { + (s.start(), s.stop()) + } +} + /// Direction Enumeration -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, Hash, PartialEq, Eq)] pub enum Dir { + /// Horizontal. Horiz, + /// Vertical. Vert, } + impl Dir { /// Whichever direction we are, return the other one. pub fn other(self) -> Self { @@ -81,7 +212,30 @@ impl Dir { Self::Vert => Self::Horiz, } } + pub fn short_form(&self) -> &'static str { + match *self { + Self::Horiz => "h", + Self::Vert => "v", + } + } } + +impl Default for Dir { + #[inline] + fn default() -> Self { + Self::Horiz + } +} + +impl Display for Dir { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match *self { + Self::Horiz => write!(f, "horizontal"), + Self::Vert => write!(f, "vertical"), + } + } +} + impl std::ops::Not for Dir { type Output = Self; /// Exclamation Operator returns the opposite direction @@ -116,7 +270,7 @@ pub struct Polygon { /// /// Axis-aligned rectangle, specified by two opposite corners. /// -#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Default, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct Rect { pub p0: Point, pub p1: Point, @@ -126,6 +280,192 @@ impl Rect { pub fn center(&self) -> Point { Point::new((self.p0.x + self.p1.x) / 2, (self.p0.y + self.p1.y) / 2) } + + pub fn new(p0: Point, p1: Point) -> Self { + Self { p0, p1 } + } + + pub fn from_spans(h: Span, v: Span) -> Self { + Self { + p0: Point::new(h.start(), v.start()), + p1: Point::new(h.stop(), v.stop()), + } + } + + #[inline] + pub fn bottom(&self) -> Int { + self.p0.y + } + #[inline] + pub fn top(&self) -> Int { + self.p1.y + } + #[inline] + pub fn left(&self) -> Int { + self.p0.x + } + #[inline] + pub fn right(&self) -> Int { + self.p1.x + } + + pub fn hspan(&self) -> Span { + Span::new(self.p0.x, self.p1.x) + } + + pub fn vspan(&self) -> Span { + Span::new(self.p0.y, self.p1.y) + } + + #[inline] + pub fn width(&self) -> Int { + self.hspan().length() + } + + #[inline] + pub fn area(&self) -> Int { + self.width() * self.height() + } + + pub fn lower_edge(&self, dir: Dir) -> Int { + self.span(dir).start() + } + + pub fn upper_edge(&self, dir: Dir) -> Int { + self.span(dir).stop() + } + + pub fn span(&self, dir: Dir) -> Span { + match dir { + Dir::Horiz => self.hspan(), + Dir::Vert => self.vspan(), + } + } + + fn sorted_edges(&self, other: &Self, dir: Dir) -> [Int; 4] { + let mut edges = [ + self.lower_edge(dir), + self.upper_edge(dir), + other.lower_edge(dir), + other.upper_edge(dir), + ]; + edges.sort(); + edges + } + + #[inline] + pub fn inner_span(&self, other: &Self, dir: Dir) -> Span { + let edges = self.sorted_edges(other, dir); + Span::new(edges[1], edges[2]) + } + + #[inline] + pub fn outer_span(&self, other: &Self, dir: Dir) -> Span { + let edges = self.sorted_edges(other, dir); + Span::new(edges[0], edges[3]) + } + + pub fn edge_closer_to(&self, x: Int, dir: Dir) -> Int { + let (x0, x1) = self.span(dir).into(); + if (x - x0).abs() <= (x - x1).abs() { + x0 + } else { + x1 + } + } + + pub fn edge_farther_from(&self, x: Int, dir: Dir) -> Int { + let (x0, x1) = self.span(dir).into(); + if (x - x0).abs() <= (x - x1).abs() { + x1 + } else { + x0 + } + } + + #[inline] + pub fn span_builder() -> RectSpanBuilder { + RectSpanBuilder::new() + } + + #[inline] + pub fn height(&self) -> Int { + self.vspan().length() + } + + #[inline] + pub fn longer_dir(&self) -> Dir { + if self.width() > self.height() { + Dir::Horiz + } else { + Dir::Vert + } + } + + #[inline] + pub fn shorter_dir(&self) -> Dir { + !self.longer_dir() + } + + #[inline] + pub fn expand(&self, amount: Int) -> Self { + Self::new( + Point::new(self.p0.x - amount, self.p0.y - amount), + Point::new(self.p1.x + amount, self.p1.y + amount), + ) + } + + #[inline] + pub fn expand_dir(&self, dir: Dir, amount: Int) -> Self { + match dir { + Dir::Horiz => Self::new( + Point::new(self.p0.x - amount, self.p0.y), + Point::new(self.p1.x + amount, self.p1.y), + ), + Dir::Vert => Self::new( + Point::new(self.p0.x, self.p0.y - amount), + Point::new(self.p1.x, self.p1.y + amount), + ), + } + } +} + +impl AlignRect for Rect {} + +/// A helper struct for building [`Rect`]s from [`Span`]s. +#[derive(Clone, Default, Eq, PartialEq, Hash, Serialize, Deserialize)] +pub struct RectSpanBuilder { + hspan: Option, + vspan: Option, +} + +impl RectSpanBuilder { + pub fn new() -> Self { + Default::default() + } + + pub fn with(&mut self, dir: Dir, span: Span) -> &mut Self { + match dir { + Dir::Horiz => self.hspan = Some(span), + Dir::Vert => self.vspan = Some(span), + } + self + } + + /// Builds a Rect from the specified spans. Panics if one or more directions + /// were left unspecified. + pub fn build(&self) -> Rect { + Rect::from_spans(self.hspan.unwrap(), self.vspan.unwrap()) + } +} + +impl From for Rect { + fn from(r: BoundBox) -> Self { + debug_assert!(!r.is_empty()); + debug_assert!(r.p0.x <= r.p1.x); + debug_assert!(r.p0.y <= r.p1.y); + Self { p0: r.p0, p1: r.p1 } + } } /// # Shape @@ -139,6 +479,7 @@ pub enum Shape { Rect(Rect), Polygon(Polygon), Path(Path), + Point(Point), } impl Default for Shape { @@ -149,7 +490,7 @@ impl Default for Shape { impl Shape { /// Boolean indication of whether we intersect with [Shape] `other`. - pub fn intersects(&self, other: &Shape) -> bool { + pub fn intersects(&self, _other: &Shape) -> bool { todo!() // FIXME! } } @@ -346,6 +687,32 @@ impl ShapeTrait for Path { unimplemented!("Path::to_poly") } } +impl ShapeTrait for Point { + /// Retrieve our "origin", or first [Point] + fn point0(&self) -> &Point { + &self + } + /// Indicate whether this shape is (more or less) horizontal or vertical. + /// Primarily used for orienting label-text. + fn orientation(&self) -> Dir { + // FIXME: always horizontal, at least for now + Dir::Horiz + } + /// Shift coordinates by the (x,y) values specified in `pt` + fn shift(&mut self, pt: &Point) { + self.x += pt.x; + self.y += pt.y; + } + /// Boolean indication of whether the [Shape] contains [Point] `pt`. + /// Containment is *inclusive* for all [Shape] types. + /// [Point]s on their boundary, which generally include all points specifying the shape itself, are regarded throughout as "inside" the shape. + fn contains(&self, pt: &Point) -> bool { + *pt == *self + } + fn to_poly(&self) -> Polygon { + panic!("Cannot convert a Point to a Polygon") + } +} /// # Matrix-Vector Transformation /// @@ -460,6 +827,7 @@ impl TransformTrait for Shape { Shape::Rect(r) => Shape::Rect(r.transform(trans)), Shape::Polygon(p) => Shape::Polygon(p.transform(trans)), Shape::Path(p) => Shape::Path(p.transform(trans)), + Shape::Point(p) => Shape::Point(p.transform(trans)), } } } @@ -468,10 +836,13 @@ impl TransformTrait for Rect { /// Creates a new shape at a location equal to the transformation of our own. fn transform(&self, trans: &Transform) -> Self { let (p0, p1) = (&self.p0, &self.p1); - Rect { - p0: p0.transform(trans), - p1: p1.transform(trans), - } + let p0p = p0.transform(trans); + let p1p = p1.transform(trans); + + let p0 = Point::new(std::cmp::min(p0p.x, p1p.x), std::cmp::min(p0p.y, p1p.y)); + let p1 = Point::new(std::cmp::max(p0p.x, p1p.x), std::cmp::max(p0p.y, p1p.y)); + + Rect { p0, p1 } } } impl TransformTrait for Polygon { @@ -493,6 +864,25 @@ impl TransformTrait for Path { } } } +impl TransformTrait for AbstractPort { + /// Apply matrix-vector [Tranform] `trans`. + /// Creates a new shape at a location equal to the transformation of our own. + fn transform(&self, trans: &Transform) -> Self { + let shapes = self + .shapes + .iter() + .map(|(k, v)| { + let v = v.iter().map(|s| s.transform(trans)).collect::>(); + (k.clone(), v) + }) + .collect::>(); + + Self { + net: self.net.clone(), + shapes, + } + } +} #[cfg(test)] pub mod tests { diff --git a/layout21raw/src/lef.rs b/layout21raw/src/lef.rs index aaf766f..f6a2835 100644 --- a/layout21raw/src/lef.rs +++ b/layout21raw/src/lef.rs @@ -147,6 +147,9 @@ impl<'lib> LefExporter<'lib> { Shape::Path { .. } => { unimplemented!("LefExporter::PATH"); } + Shape::Point(_) => { + unimplemented!("LefExporter::POINT"); + } }; // Wrap it in the [LefGeometry] enum (which also includes repetitions) and return it Ok(lef21::LefGeometry::Shape(inner)) @@ -282,7 +285,8 @@ impl LefImporter { } }; // Create the [Abstract] to be returned - let mut abs = Abstract::new(&lefmacro.name, outline); + let mut abs = Abstract::new(&lefmacro.name); + abs.outline = Some(outline); // Import all pins for lefpin in &lefmacro.pins { let abs_port = self.import_pin(lefpin)?; @@ -483,14 +487,14 @@ mod tests { let layers = crate::tests::layers()?; let a = Abstract { name: "to_lef1".into(), - outline: Polygon { + outline: Some(Polygon { points: vec![ Point::new(0, 0), Point::new(11, 0), Point::new(11, 11), Point::new(0, 11), ], - }, + }), ports: vec![AbstractPort { net: "port1".into(), // Collect a hashmap of shapes from (LayerKey, Vec) pairs diff --git a/layout21raw/src/lib.rs b/layout21raw/src/lib.rs index b9c2f9d..4744d9c 100644 --- a/layout21raw/src/lib.rs +++ b/layout21raw/src/lib.rs @@ -6,15 +6,17 @@ //! much akin to nearly any legacy layout system. //! -// Crates.io dependencies, at crate-level for their macros +// Crates.io dependencies, at crate-level for their macros #[macro_use] extern crate enum_dispatch; // Internal modules +pub mod align; pub mod bbox; pub mod data; pub mod error; pub mod geom; +pub mod translate; // Re-exports #[doc(inline)] diff --git a/layout21raw/src/proto.rs b/layout21raw/src/proto.rs index 3864a0d..b8d4b37 100644 --- a/layout21raw/src/proto.rs +++ b/layout21raw/src/proto.rs @@ -13,6 +13,7 @@ use std::collections::HashMap; use std::convert::{TryFrom, TryInto}; +use crate::LayerSpec; // Local imports use crate::{ utils::{ErrorContext, ErrorHelper, Ptr}, @@ -106,7 +107,9 @@ impl<'lib> ProtoExporter<'lib> { .push(self.export_abstract_blockages(layerkey, shapes)?); } // Convert its outline - pabs.outline = Some(self.export_polygon(&abs.outline)?); + if let Some(ref outline) = abs.outline { + pabs.outline = Some(self.export_polygon(outline)?); + } // And we're done - pop the context and return self.ctx.pop(); Ok(pabs) @@ -233,6 +236,7 @@ impl<'lib> ProtoExporter<'lib> { Shape::Rect(ref r) => Ok(ProtoShape::Rect(self.export_rect(r)?)), Shape::Polygon(ref p) => Ok(ProtoShape::Poly(self.export_polygon(p)?)), Shape::Path(ref p) => Ok(ProtoShape::Path(self.export_path(p)?)), + Shape::Point(_) => self.fail("Cannot export point shapes as protos"), } } /// Export a [Rect] @@ -286,6 +290,7 @@ impl<'lib> ProtoExporter<'lib> { Shape::Rect(rect) => pshapes.rectangles.push(self.export_rect(rect)?), Shape::Polygon(poly) => pshapes.polygons.push(self.export_polygon(poly)?), Shape::Path(path) => pshapes.paths.push(self.export_path(path)?), + Shape::Point(_) => return self.fail("Cannot export point shapes as protos"), }; Ok(()) } @@ -674,10 +679,13 @@ impl ProtoImporter { // FIXME: maybe a bigger-size type for these layer numbers. let num = i16::try_from(player.number)?; let purpose = i16::try_from(player.purpose)?; - let mut layers = self.layers.write()?; - layers.get_or_insert(num, purpose) + let layers = self.layers.write()?; + layers + .get_from_spec(LayerSpec::new(num, purpose)) + .ok_or(LayoutError::msg("Layer Not Found")) } } + impl ErrorHelper for ProtoImporter { type Error = LayoutError; fn err(&self, msg: impl Into) -> LayoutError { @@ -729,8 +737,11 @@ fn proto1() -> LayoutResult<()> { // Round-trip through Layout21::Raw -> ProtoBuf -> Layout21::Raw let mut lib = Library::new("prt_lib", Units::Nano); let (layer, purpose) = { + use crate::Layer; + let mut layer = Layer::new(0, "prt_layer"); + layer.add_purpose(0, LayerPurpose::Drawing); let mut layers = lib.layers.write()?; - layers.get_or_insert(0, 0)? + (layers.add(layer), LayerPurpose::Drawing) }; let c1 = lib.cells.insert(Layout { name: "prt_cell".into(), diff --git a/layout21raw/src/translate.rs b/layout21raw/src/translate.rs new file mode 100644 index 0000000..e6f18d1 --- /dev/null +++ b/layout21raw/src/translate.rs @@ -0,0 +1,19 @@ +use crate::{Point, Rect}; + +pub trait Translate { + fn translate(&mut self, v: Point); +} + +impl Translate for Point { + fn translate(&mut self, v: Point) { + self.x += v.x; + self.y += v.y; + } +} + +impl Translate for Rect { + fn translate(&mut self, v: Point) { + self.p0.translate(v); + self.p1.translate(v); + } +} diff --git a/layout21tetris/Cargo.toml b/layout21tetris/Cargo.toml index 3ff0285..724293e 100644 --- a/layout21tetris/Cargo.toml +++ b/layout21tetris/Cargo.toml @@ -20,7 +20,7 @@ layout21protos = {path = "../layout21protos", version = "0.2.1"} derive_builder = "0.9" derive_more = "0.99.16" num-integer = "0.1" -num-traits = "0.2" -serde = {version = "1.0", features = ["derive"]} +num-traits = "0.2.15" +serde = { version = "1.0.137", features = ["derive"] } serde_derive = "1.0.88" slotmap = {version = "1.0", features = ["serde"]} diff --git a/layout21tetris/src/abs.rs b/layout21tetris/src/abs.rs index 3802ef5..2a12d3f 100644 --- a/layout21tetris/src/abs.rs +++ b/layout21tetris/src/abs.rs @@ -108,7 +108,7 @@ pub struct TopLoc { /// Note there are only two such sides: the "origin-side" [BottomOrLeft] and the "width-side" [TopOrRight]. /// Each [Layer]'s orientation ([Dir]) dictates between bottom/left and top/right. /// Also note the requirements on [Outline] shapes ensure each track has a unique left/right or top/bottom pair of edges. -/// +/// #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub enum Side { BottomOrLeft, diff --git a/layout21tetris/src/conv/raw.rs b/layout21tetris/src/conv/raw.rs index 96edd3a..7c78b18 100644 --- a/layout21tetris/src/conv/raw.rs +++ b/layout21tetris/src/conv/raw.rs @@ -440,7 +440,8 @@ impl<'lib> RawExporter { // Create the outline-element, and grab a copy of its inner shape let outline = self.export_outline(&abs.outline)?; // Create the raw abstract - let mut rawabs = raw::Abstract::new(&abs.name, outline.clone()); + let mut rawabs = raw::Abstract::new(&abs.name); + rawabs.outline = Some(outline); // Draw a blockage on each layer, equal to the shape of the outline for layerindex in 0..abs.metals { @@ -824,7 +825,7 @@ impl ErrorHelper for RawExporter { stack: self.ctx.clone(), } } - fn ok( + fn ok( &self, res: Result, msg: impl Into, diff --git a/layout21tetris/src/placer.rs b/layout21tetris/src/placer.rs index 2b6d7ba..687b84e 100644 --- a/layout21tetris/src/placer.rs +++ b/layout21tetris/src/placer.rs @@ -8,14 +8,11 @@ use std::convert::TryFrom; // Local imports +use crate::array::{Array, ArrayInstance, Arrayable}; use crate::bbox::HasBoundBox; -use crate::{instance::Instance, layout::Layout}; use crate::coords::{LayerPitches, PrimPitches, UnitSpeced, Xy}; use crate::library::Library; -use crate::array::{Array, ArrayInstance, Arrayable}; -use crate::placement::{ - Align, Place, Placeable, RelativePlace, SepBy, Side, -}; +use crate::placement::{Align, Place, Placeable, RelativePlace, SepBy, Side}; use crate::raw::{Dir, LayoutError, LayoutResult}; use crate::utils::{DepOrder, DepOrderer, ErrorContext, ErrorHelper, Ptr}; use crate::validate::ValidStack; @@ -23,6 +20,7 @@ use crate::{ abs, stack, tracks::{TrackCross, TrackRef}, }; +use crate::{instance::Instance, layout::Layout}; /// # Placer /// Converts all potentially-relatively-placed attributes to absolute positions. @@ -114,10 +112,7 @@ impl Placer { } /// Flatten an [ArrayInstance] to a vector of Cell Instances. /// Instance location must be absolute by call-time. - fn flatten_array_inst( - &mut self, - array_inst: &ArrayInstance, - ) -> LayoutResult> { + fn flatten_array_inst(&mut self, array_inst: &ArrayInstance) -> LayoutResult> { // Read the child-Instances from the underlying [Array] definition let mut children = { let array = array_inst.array.read()?; @@ -426,7 +421,7 @@ impl Placer { if inst.reflected(!dir) { period_tracks - track - 1 } else { - period_tracks + track + period_tracks + track } }; @@ -618,8 +613,8 @@ impl DepOrder for PlaceOrder { #[cfg(test)] mod tests { use super::*; - use crate::outline::Outline; use crate::cell::Cell; + use crate::outline::Outline; use crate::placement::{Place, Placeable, RelAssign, RelativePlace, SepBy, Separation, Side}; use crate::tests::{exports, stacks::SampleStacks}; diff --git a/layout21tetris/src/stack.rs b/layout21tetris/src/stack.rs index b7baf54..d8f0678 100644 --- a/layout21tetris/src/stack.rs +++ b/layout21tetris/src/stack.rs @@ -220,12 +220,7 @@ impl<'lib> LayerPeriod<'lib> { Ok(()) } /// Cut all [Track]s from `start` to `stop`, - pub fn cut( - &mut self, - start: DbUnits, - stop: DbUnits, - src: &'lib TrackCross, - ) -> TrackResult<()> { + pub fn cut(&mut self, start: DbUnits, stop: DbUnits, src: &'lib TrackCross) -> TrackResult<()> { for t in self.rails.iter_mut() { t.cut(start, stop, src)?; } diff --git a/layout21tetris/src/tracks.rs b/layout21tetris/src/tracks.rs index 172db9c..99767e0 100644 --- a/layout21tetris/src/tracks.rs +++ b/layout21tetris/src/tracks.rs @@ -246,12 +246,7 @@ impl<'lib> Track<'lib> { } /// Cut from `start` to `stop`. /// Fails if the region is not a contiguous wire segment. - pub fn cut( - &mut self, - start: DbUnits, - stop: DbUnits, - src: &'lib TrackCross, - ) -> TrackResult<()> { + pub fn cut(&mut self, start: DbUnits, stop: DbUnits, src: &'lib TrackCross) -> TrackResult<()> { self.cut_or_block(start, stop, TrackSegmentType::Cut { src }) } /// Set the stop position for our last [TrackSegment] to `stop` diff --git a/layout21utils/Cargo.toml b/layout21utils/Cargo.toml index 84b0046..48b10c8 100644 --- a/layout21utils/Cargo.toml +++ b/layout21utils/Cargo.toml @@ -11,7 +11,7 @@ workspace = "../" [dependencies] by_address = "1.0.4" -serde = {version = "1.0", features = ["derive"]} +serde = { version = "1.0.137", features = ["derive"] } serde_derive = "1.0.88" serde_json = "1.0" serde_yaml = "0.8" diff --git a/layout21utils/src/error.rs b/layout21utils/src/error.rs index 3af8e31..3a64d8b 100644 --- a/layout21utils/src/error.rs +++ b/layout21utils/src/error.rs @@ -34,7 +34,7 @@ pub trait ErrorHelper { /// The default implementation simply returns an error via `self.fail`. fn ok(&self, _res: Result, msg: impl Into) -> Result where - E: std::error::Error + 'static, + E: std::error::Error + Send + Sync + 'static, { self.fail(msg) // Default version always fails. } diff --git a/layout21utils/src/ptr.rs b/layout21utils/src/ptr.rs index beeae76..e565ef7 100644 --- a/layout21utils/src/ptr.rs +++ b/layout21utils/src/ptr.rs @@ -19,7 +19,7 @@ use by_address::ByAddress; /// Attribute access is largely forwarded through [Deref] calls, /// allowing for fairly natural syntax after grabbing `read()` or `write()` access. /// For example: -/// +/// /// ```text /// let data = ptr.read()?; /// data.some_function(); diff --git a/layout21utils/src/ser.rs b/layout21utils/src/ser.rs index 53089f1..0c06837 100644 --- a/layout21utils/src/ser.rs +++ b/layout21utils/src/ser.rs @@ -86,7 +86,7 @@ pub trait SerdeFile: Serialize + DeserializeOwned { /// Wrapper over other errors #[derive(Debug)] -pub struct Error(Box); +pub struct Error(Box); impl std::fmt::Display for Error { /// Delegate [std::fmt::Display] to the (derived) [std::fmt::Debug] implementation. /// Maybe more info that wanted in some cases. But certainly enough. diff --git a/lef21/Cargo.toml b/lef21/Cargo.toml index 71cd600..ff850c6 100644 --- a/lef21/Cargo.toml +++ b/lef21/Cargo.toml @@ -18,9 +18,9 @@ layout21utils = {path = "../layout21utils", version="0.2.1"} derive_builder = "0.9.0" enum_dispatch = "0.3.1" num-derive = "0.3" -num-traits = "0.2" +num-traits = "0.2.15" rust_decimal = {version = "1.14.3"} -serde = {version = "1.0", features = ["derive"]} +serde = { version = "1.0.137", features = ["derive"] } serde_derive = "1.0.88" derive_more = "0.99.16" once_cell = "1.8.0" diff --git a/readme.md b/readme.md index 22e63b2..44ac390 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,8 @@ # Layout21 +This repository is copied from [dan-fritchman/Layout21](https://github.com/dan-fritchman/Layout21). + Custom integrated circuit layout. [![test](https://github.com/dan-fritchman/Layout21/actions/workflows/test.yml/badge.svg)](https://github.com/dan-fritchman/Layout21/actions/workflows/test.yml) From cbd1209ef712d592b643345ea5535a545d43fb81 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 3 Nov 2022 15:35:37 -0700 Subject: [PATCH 2/2] cherry-pick Rahul's changes --- layout21raw/src/data.rs | 8 ++++---- layout21raw/src/gds.rs | 8 ++++---- layout21raw/src/proto.rs | 8 ++++---- layout21tetris/src/conv/raw.rs | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/layout21raw/src/data.rs b/layout21raw/src/data.rs index 0bae9b7..ae2e755 100644 --- a/layout21raw/src/data.rs +++ b/layout21raw/src/data.rs @@ -335,16 +335,16 @@ impl Layers { pub fn get_new_purpose(&self, num: i16, purpose: i16, to: LayerPurpose) -> Option { let lay = self.get_layer_from_spec(num, purpose)?; let purpose = lay.num(&to)?; - let (key, _) = self.get_from_spec(num, purpose)?; + let (key, _) = self.get_from_spec(LayerSpec(num, purpose))?; Some(key) } pub fn get_layer_from_spec(&self, num: i16, purpose: i16) -> Option<&Layer> { - let (key, _) = self.get_from_spec(num, purpose)?; + let (key, _) = self.get_from_spec(LayerSpec(num, purpose))?; self.get(key) } - pub fn get_from_spec(&self, num: i16, purpose: i16) -> Option<(LayerKey, LayerPurpose)> { + pub fn get_from_spec(&self, spec: LayerSpec) -> Option<(LayerKey, LayerPurpose)> { for (k, layer) in self.slots().iter() { if layer.layernum != spec.0 { continue; @@ -387,7 +387,7 @@ impl Default for LayerPurpose { /// # Layer Specification /// As in seemingly every layout system, this uses two numbers to identify each layer. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] -pub struct LayerSpec(i16, i16); +pub struct LayerSpec(pub i16, pub i16); impl LayerSpec { pub fn new(n1: i16, n2: i16) -> Self { Self(n1, n2) diff --git a/layout21raw/src/gds.rs b/layout21raw/src/gds.rs index 0803232..08210f7 100644 --- a/layout21raw/src/gds.rs +++ b/layout21raw/src/gds.rs @@ -123,12 +123,14 @@ impl<'lib> GdsExporter<'lib> { // Flatten our points-vec, converting to 32-bit along the way let mut xy = abs .outline + .as_ref() + .expect("tried to export an abstract with an outline") .points .iter() .map(|p| self.export_point(p)) .collect::, _>>()?; // Add the origin a second time, to "close" the polygon - xy.push(self.export_point(&abs.outline.points[0])?); + xy.push(self.export_point(&abs.outline.as_ref().unwrap().points[0])?); let outline = GdsElement::GdsBoundary(gds21::GdsBoundary { layer: i16::MAX, datatype: i16::MAX, @@ -137,9 +139,7 @@ impl<'lib> GdsExporter<'lib> { }); // Blockages do not map to GDSII elements. // Conversion includes the abstract's name, outline and ports. - if let Some(outline) = abs.outline { - elems.push(outline); - } + elems.push(outline); // Convert each [AbstractPort] for port in abs.ports.iter() { diff --git a/layout21raw/src/proto.rs b/layout21raw/src/proto.rs index b8d4b37..b0ae9b3 100644 --- a/layout21raw/src/proto.rs +++ b/layout21raw/src/proto.rs @@ -13,13 +13,13 @@ use std::collections::HashMap; use std::convert::{TryFrom, TryInto}; -use crate::LayerSpec; // Local imports use crate::{ utils::{ErrorContext, ErrorHelper, Ptr}, Abstract, AbstractPort, Cell, DepOrder, Element, Instance, Int, LayerKey, LayerPurpose, Layers, Layer, Layout, LayoutError, LayoutResult, Library, Path, Point, Polygon, Rect, Shape, TextElement, Units, + LayerSpec }; pub use layout21protos as proto; @@ -440,14 +440,14 @@ impl ProtoImporter { .layers .write() .unwrap() - .get_or_insert(*number as i16, *purpose as i16) + .get_from_spec(LayerSpec(*number as i16, *purpose as i16)) .unwrap(); let shapes = self.import_abstract_layer_shapes(layershapes)?; abs.blockages.insert(layerkey, shapes); } abs.outline = match self.import_polygon(&pabs.outline.as_ref().unwrap())? { - Shape::Polygon(p) => p, + Shape::Polygon(p) => Some(p), _ => unreachable!( "import_polygon only returns the Shape::Polygon(Polygon) enum variant." ), @@ -466,7 +466,7 @@ impl ProtoImporter { .layers .write() .unwrap() - .get_or_insert(*number as i16, *purpose as i16) + .get_from_spec(LayerSpec(*number as i16, *purpose as i16)) .unwrap(); let shapes = self.import_abstract_layer_shapes(layershapes)?; port.shapes.insert(layerkey, shapes); diff --git a/layout21tetris/src/conv/raw.rs b/layout21tetris/src/conv/raw.rs index 7c78b18..0f4b4a2 100644 --- a/layout21tetris/src/conv/raw.rs +++ b/layout21tetris/src/conv/raw.rs @@ -441,7 +441,7 @@ impl<'lib> RawExporter { let outline = self.export_outline(&abs.outline)?; // Create the raw abstract let mut rawabs = raw::Abstract::new(&abs.name); - rawabs.outline = Some(outline); + rawabs.outline = Some(outline.clone()); // Draw a blockage on each layer, equal to the shape of the outline for layerindex in 0..abs.metals {