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..ae2e755 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(LayerSpec(num, purpose))?; + Some(key) + } + + pub fn get_layer_from_spec(&self, num: i16, purpose: i16) -> Option<&Layer> { + let (key, _) = self.get_from_spec(LayerSpec(num, purpose))?; + self.get(key) + } + + pub fn get_from_spec(&self, spec: LayerSpec) -> 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)] -pub struct LayerSpec(i16, i16); +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +pub struct LayerSpec(pub i16, pub 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..08210f7 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, @@ -122,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, @@ -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..b0ae9b3 100644 --- a/layout21raw/src/proto.rs +++ b/layout21raw/src/proto.rs @@ -19,6 +19,7 @@ use crate::{ 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; @@ -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(()) } @@ -435,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." ), @@ -461,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); @@ -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..0f4b4a2 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.clone()); // 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)