diff --git a/layout21converters/resources/macro.golden.yaml b/layout21converters/resources/macro.golden.yaml index 51acae8..ae6a905 100644 --- a/layout21converters/resources/macro.golden.yaml +++ b/layout21converters/resources/macro.golden.yaml @@ -9,6 +9,7 @@ macros: geometries: - Shape: Rect: + - ~ - x: "88.4" y: "0.0" - x: "88.78" diff --git a/layout21converters/resources/macro.yaml b/layout21converters/resources/macro.yaml index 51acae8..ae6a905 100644 --- a/layout21converters/resources/macro.yaml +++ b/layout21converters/resources/macro.yaml @@ -9,6 +9,7 @@ macros: geometries: - Shape: Rect: + - ~ - x: "88.4" y: "0.0" - x: "88.78" diff --git a/layout21raw/src/lef.rs b/layout21raw/src/lef.rs index 1fce12b..30ec8a3 100644 --- a/layout21raw/src/lef.rs +++ b/layout21raw/src/lef.rs @@ -133,7 +133,7 @@ impl<'lib> LefExporter<'lib> { // Conver to a [LefShape] let inner: lef21::LefShape = match shape { Shape::Rect(ref r) => { - lef21::LefShape::Rect(self.export_point(&r.p0)?, self.export_point(&r.p1)?) + lef21::LefShape::Rect(None, self.export_point(&r.p0)?, self.export_point(&r.p1)?) } Shape::Polygon(ref poly) => { let points = poly @@ -379,7 +379,7 @@ impl LefImporter { ) -> LayoutResult { use lef21::LefShape::{Path, Polygon, Rect}; match lefshape { - Rect(ref p0, ref p1) => self.import_rect((p0, p1)), + Rect(_, ref p0, ref p1) => self.import_rect((p0, p1)), Polygon(ref pts) => self.import_polygon(pts), Path(ref pts) => self.import_path(pts, layer), } diff --git a/lef21/resources/geoms1.yaml b/lef21/resources/geoms1.yaml index 87321f4..734fcd6 100644 --- a/lef21/resources/geoms1.yaml +++ b/lef21/resources/geoms1.yaml @@ -2,58 +2,212 @@ layer_name: some_layers_name geometries: - Shape: - Rect: [["1.065000", "1.075000"], ["1.705000", "1.325000"]] + Rect: + - ~ + - x: "1.065000" + y: "1.075000" + - x: "1.705000" + y: "1.325000" + - Shape: + Rect: + - ~ + - x: "1.495000" + y: "0.615000" + - x: "3.335000" + y: "0.785000" + - Shape: + Rect: + - ~ + - x: "1.495000" + y: "0.785000" + - x: "1.705000" + y: "1.075000" + - Shape: + Rect: + - ~ + - x: "1.495000" + y: "1.325000" + - x: "1.705000" + y: "1.495000" + - Shape: + Rect: + - ~ + - x: "1.495000" + y: "1.495000" + - x: "1.785000" + y: "2.465000" + - Shape: + Rect: + - ~ + - x: "2.180000" + y: "0.255000" + - x: "2.420000" + y: "0.615000" + - Shape: + Rect: + - ~ + - x: "3.070000" + y: "1.915000" + - x: "4.515000" + y: "2.085000" + - Shape: + Rect: + - ~ + - x: "3.070000" + y: "2.085000" + - x: "3.400000" + y: "2.465000" + - Shape: + Rect: + - ~ + - x: "3.090000" + y: "0.255000" + - x: "3.335000" + y: "0.615000" + - Shape: + Rect: + - ~ + - x: "4.090000" + y: "2.085000" + - x: "4.515000" + y: "2.465000" + - Shape: + Rect: + - mask: "1" + - x: "1.065000" + y: "1.075000" + - x: "1.705000" + y: "1.325000" + - Shape: + Rect: + - mask: "1" + - x: "1.495000" + y: "0.615000" + - x: "3.335000" + y: "0.785000" + - Shape: + Rect: + - mask: "1" + - x: "1.495000" + y: "0.785000" + - x: "1.705000" + y: "1.075000" + - Shape: + Rect: + - mask: "1" + - x: "1.495000" + y: "1.325000" + - x: "1.705000" + y: "1.495000" + - Shape: + Rect: + - mask: "1" + - x: "1.495000" + y: "1.495000" + - x: "1.785000" + y: "2.465000" + - Shape: + Rect: + - mask: "1" + - x: "2.180000" + y: "0.255000" + - x: "2.420000" + y: "0.615000" + - Shape: + Rect: + - mask: "1" + - x: "3.070000" + y: "1.915000" + - x: "4.515000" + y: "2.085000" + - Shape: + Rect: + - mask: "1" + - x: "3.070000" + y: "2.085000" + - x: "3.400000" + y: "2.465000" + - Shape: + Rect: + - mask: "1" + - x: "3.090000" + y: "0.255000" + - x: "3.335000" + y: "0.615000" + - Shape: + Rect: + - mask: "1" + - x: "4.090000" + y: "2.085000" + - x: "4.515000" + y: "2.465000" + - Shape: + Rect: + - mask: "2" + - x: "1.065000" + y: "1.075000" + - x: "1.705000" + y: "1.325000" - Shape: Rect: - - - "1.495000" - - "0.615000" - - - "3.335000" - - "0.785000" + - mask: "2" + - x: "1.495000" + y: "0.615000" + - x: "3.335000" + y: "0.785000" - Shape: Rect: - - - "1.495000" - - "0.785000" - - - "1.705000" - - "1.075000" + - mask: "2" + - x: "1.495000" + y: "0.785000" + - x: "1.705000" + y: "1.075000" - Shape: Rect: - - - "1.495000" - - "1.325000" - - - "1.705000" - - "1.495000" + - mask: "2" + - x: "1.495000" + y: "1.325000" + - x: "1.705000" + y: "1.495000" - Shape: Rect: - - - "1.495000" - - "1.495000" - - - "1.785000" - - "2.465000" + - mask: "2" + - x: "1.495000" + y: "1.495000" + - x: "1.785000" + y: "2.465000" - Shape: Rect: - - - "2.180000" - - "0.255000" - - - "2.420000" - - "0.615000" + - mask: "2" + - x: "2.180000" + y: "0.255000" + - x: "2.420000" + y: "0.615000" - Shape: Rect: - - - "3.070000" - - "1.915000" - - - "4.515000" - - "2.085000" + - mask: "2" + - x: "3.070000" + y: "1.915000" + - x: "4.515000" + y: "2.085000" - Shape: Rect: - - - "3.070000" - - "2.085000" - - - "3.400000" - - "2.465000" + - mask: "2" + - x: "3.070000" + y: "2.085000" + - x: "3.400000" + y: "2.465000" - Shape: Rect: - - - "3.090000" - - "0.255000" - - - "3.335000" - - "0.615000" + - mask: "2" + - x: "3.090000" + y: "0.255000" + - x: "3.335000" + y: "0.615000" - Shape: Rect: - - - "4.090000" - - "2.085000" - - - "4.515000" - - "2.465000" + - mask: "2" + - x: "4.090000" + y: "2.085000" + - x: "4.515000" + y: "2.465000" diff --git a/lef21/resources/lef21.schema.json b/lef21/resources/lef21.schema.json index e984c60..6f5d3bf 100644 --- a/lef21/resources/lef21.schema.json +++ b/lef21/resources/lef21.schema.json @@ -170,7 +170,7 @@ "writeOnly": true, "anyOf": [ { - "$ref": "#/definitions/Unsupported" + "$ref": "#/definitions/LefOnOff" }, { "type": "null" @@ -846,6 +846,20 @@ } ] }, + "LefMask": { + "title": "Mask value", + "description": "Specifies which mask from double- or triple-patterning to use for this shape. Supports common mathematical operations (Add, Sub, increment, etc.).", + "type": "object", + "required": [ + "mask" + ], + "properties": { + "mask": { + "type": "string", + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" + } + } + }, "LefOnOff": { "description": "Binary On/Off Settings, Denoted by `ON` and `OFF`", "oneOf": [ @@ -1300,6 +1314,16 @@ "Rect": { "type": "array", "items": [ + { + "anyOf": [ + { + "$ref": "#/definitions/LefMask" + }, + { + "type": "null" + } + ] + }, { "$ref": "#/definitions/LefPoint" }, @@ -1307,8 +1331,8 @@ "$ref": "#/definitions/LefPoint" } ], - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 } }, "additionalProperties": false diff --git a/lef21/resources/lib2.yaml b/lef21/resources/lib2.yaml index 624dac3..2245a85 100644 --- a/lef21/resources/lib2.yaml +++ b/lef21/resources/lib2.yaml @@ -1,5 +1,4 @@ --- -version: "5.4" macros: - name: macro_name pins: @@ -10,11 +9,11 @@ macros: geometries: - Shape: Rect: - - - "88.4" - - "0.0" - - - "88.78" - - "1.06" - class: ~ + - ~ + - x: "88.4" + y: "0.0" + - x: "88.78" + y: "1.06" direction: Input class: Block: @@ -26,5 +25,7 @@ macros: - X - Y - R90 +version: "5.4" +use_min_spacing: "Off" units: database_microns: 2000 diff --git a/lef21/src/data.rs b/lef21/src/data.rs index 4fded17..42ad0df 100644 --- a/lef21/src/data.rs +++ b/lef21/src/data.rs @@ -94,7 +94,7 @@ pub struct LefLibrary { /// "Use Min Spacing" Option #[serde(default, skip_serializing)] #[builder(default)] - pub use_min_spacing: Option, + pub use_min_spacing: Option, /// Clearance Measure #[serde(default, skip_serializing)] #[builder(default)] @@ -427,7 +427,7 @@ pub enum LefGeometry { /// rectangles, polygons, and paths. #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq)] pub enum LefShape { - Rect(LefPoint, LefPoint), + Rect(Option, LefPoint, LefPoint), Polygon(Vec), Path(Vec), } @@ -467,6 +467,38 @@ impl std::fmt::Display for LefPoint { write!(f, "{} {}", self.x, self.y) } } +/// # Mask value +/// +/// Specifies which mask from double- or triple-patterning to use for this shape. +/// Supports common mathematical operations (Add, Sub, increment, etc.). +#[derive( + Clone, + Default, + Debug, + Deserialize, + Serialize, + JsonSchema, + PartialEq, + Eq, + Add, + AddAssign, + Sub, + SubAssign, +)] +pub struct LefMask { + pub mask: LefDecimal, +} +impl LefMask { + /// Create a new [LefMask] + pub fn new(mask: impl Into) -> Self { + Self { mask: mask.into() } + } +} +impl std::fmt::Display for LefMask { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", self.mask) + } +} /// # Lef Distance Units per Micron /// /// A constrained numeric type. Allowed values of [LefDbuPerMicron] are: @@ -613,7 +645,9 @@ enumstr!( Bump: "BUMP", Eeq: "EEQ", FixedMask: "FIXEDMASK", - + Mask: "MASK", + UseMinSpacing: "USEMINSPACING", + // UNITS Fields Units: "UNITS", Time: "TIME", @@ -653,7 +687,6 @@ enumstr!( MustJoin: "MUSTJOIN", Property: "PROPERTY", ManufacturingGrid: "MANUFACTURINGGRID", - UseMinSpacing: "USEMINSPACING", ClearanceMeasure: "CLEARANCEMEASURE", PropertyDefinitions: "PROPERTYDEFINITIONS", MaxViaStack: "MAXVIASTACK", diff --git a/lef21/src/read.rs b/lef21/src/read.rs index 78b5905..5f2a9ce 100644 --- a/lef21/src/read.rs +++ b/lef21/src/read.rs @@ -506,9 +506,15 @@ impl<'src> LefParser<'src> { self.expect_key(LefKey::Library)?; // Expect END LIBRARY break; } + LefKey::UseMinSpacing => { + self.advance()?; + self.expect_key(LefKey::Obs)?; + let e = self.parse_enum::()?; + self.expect(TokenType::SemiColon)?; + lib.use_min_spacing(e) + } LefKey::BeginExtension | LefKey::ManufacturingGrid - | LefKey::UseMinSpacing | LefKey::ClearanceMeasure | LefKey::PropertyDefinitions | LefKey::MaxViaStack @@ -873,16 +879,21 @@ impl<'src> LefParser<'src> { match self.peek_key()? { LefKey::Rect => { self.advance()?; + let mut mask = None; if self.matches(TokenType::Name) { - // The ITERATE construction would go here, but is not supported. - self.fail(LefParseErrorType::Unsupported)?; + if self.get_key()? == LefKey::Mask { + mask = Some(LefMask::new(self.parse_number()?)); + } else { + // The ITERATE construction would go here, but is not supported. + self.fail(LefParseErrorType::Unsupported)?; + } } // Parse the two points let p1 = self.parse_point()?; let p2 = self.parse_point()?; self.expect(TokenType::SemiColon)?; // And return the Rect - Ok(LefGeometry::Shape(LefShape::Rect(p1, p2))) + Ok(LefGeometry::Shape(LefShape::Rect(mask, p1, p2))) } LefKey::Polygon => { self.advance()?; diff --git a/lef21/src/tests.rs b/lef21/src/tests.rs index 99cd163..aca566b 100644 --- a/lef21/src/tests.rs +++ b/lef21/src/tests.rs @@ -1,8 +1,7 @@ -use super::*; use super::read::{parse_str, LefLexer, LefParser, Token}; +use super::*; +use crate::utils::SerializationFormat::{Json, Toml, Yaml}; use std::path::Path; -use crate::utils::SerializationFormat::{Yaml, Json, Toml}; - #[test] fn test_points() -> LefResult<()> { @@ -62,6 +61,26 @@ fn it_parses_layer_geoms1() -> LefResult<()> { RECT 3.070000 2.085000 3.400000 2.465000 ; RECT 3.090000 0.255000 3.335000 0.615000 ; RECT 4.090000 2.085000 4.515000 2.465000 ; + RECT MASK 1 1.065000 1.075000 1.705000 1.325000 ; + RECT MASK 1 1.495000 0.615000 3.335000 0.785000 ; + RECT MASK 1 1.495000 0.785000 1.705000 1.075000 ; + RECT MASK 1 1.495000 1.325000 1.705000 1.495000 ; + RECT MASK 1 1.495000 1.495000 1.785000 2.465000 ; + RECT MASK 1 2.180000 0.255000 2.420000 0.615000 ; + RECT MASK 1 3.070000 1.915000 4.515000 2.085000 ; + RECT MASK 1 3.070000 2.085000 3.400000 2.465000 ; + RECT MASK 1 3.090000 0.255000 3.335000 0.615000 ; + RECT MASK 1 4.090000 2.085000 4.515000 2.465000 ; + RECT MASK 2 1.065000 1.075000 1.705000 1.325000 ; + RECT MASK 2 1.495000 0.615000 3.335000 0.785000 ; + RECT MASK 2 1.495000 0.785000 1.705000 1.075000 ; + RECT MASK 2 1.495000 1.325000 1.705000 1.495000 ; + RECT MASK 2 1.495000 1.495000 1.785000 2.465000 ; + RECT MASK 2 2.180000 0.255000 2.420000 0.615000 ; + RECT MASK 2 3.070000 1.915000 4.515000 2.085000 ; + RECT MASK 2 3.070000 2.085000 3.400000 2.465000 ; + RECT MASK 2 3.090000 0.255000 3.335000 0.615000 ; + RECT MASK 2 4.090000 2.085000 4.515000 2.465000 ; "#; let mut parser = LefParser::new(src)?; let geoms = parser.parse_layer_geometries()?; @@ -73,6 +92,7 @@ fn it_parses_layer_geoms1() -> LefResult<()> { fn it_parses_lib2() -> LefResult<()> { let src = r#" VERSION 5.4 ; + USEMINSPACING OBS OFF ; UNITS DATABASE MICRONS 2000 ; END UNITS @@ -119,15 +139,18 @@ fn it_errors_no_end_library_5p5() -> LefResult<()> { #[test] fn empty_lib_to_yaml() { - Yaml.save(&LefLibrary::new(), &resource("empty_lib.lef.yaml")).unwrap(); + Yaml.save(&LefLibrary::new(), &resource("empty_lib.lef.yaml")) + .unwrap(); } #[test] fn empty_lib_to_json() { - Json.save(&LefLibrary::new(), &resource("empty_lib.lef.json")).unwrap(); + Json.save(&LefLibrary::new(), &resource("empty_lib.lef.json")) + .unwrap(); } #[test] fn empty_lib_to_toml() { - Toml.save(&LefLibrary::new(), &resource("empty_lib.lef.toml")).unwrap(); + Toml.save(&LefLibrary::new(), &resource("empty_lib.lef.toml")) + .unwrap(); } /// Helper function: Assert that `data` equals the content in YAML file `fname` diff --git a/lef21/src/write.rs b/lef21/src/write.rs index bdf0ede..c0baa01 100644 --- a/lef21/src/write.rs +++ b/lef21/src/write.rs @@ -53,8 +53,8 @@ impl<'wr> LefWriter<'wr> { /// Fields are written in the LEF-recommended order fn write_lib(&mut self, lib: &LefLibrary) -> LefResult<()> { use LefKey::{ - BusBitChars, DividerChar, End, Library, NamesCaseSensitive, NoWireExtensionAtPin, - Units, Version, + BusBitChars, DividerChar, End, Library, NamesCaseSensitive, NoWireExtensionAtPin, Obs, + Units, UseMinSpacing, Version, }; if let Some(ref v) = lib.version { // Save a copy in our session-state @@ -86,6 +86,9 @@ impl<'wr> LefWriter<'wr> { if let Some(ref v) = lib.divider_char { self.write_line(format_args_f!("{DividerChar} \"{}\" ; ", v))?; } + if let Some(ref v) = lib.use_min_spacing { + self.write_line(format_args_f!("{UseMinSpacing} {Obs} {} ; ", v))?; + } if let Some(ref v) = lib.units { self.write_line(format_args_f!("{Units} "))?; self.indent += 1; @@ -312,8 +315,13 @@ impl<'wr> LefWriter<'wr> { match geom { LefGeometry::Iterate { .. } => unimplemented!(), LefGeometry::Shape(ref shape) => match shape { - LefShape::Rect(p0, p1) => { - self.write_line(format_args_f!("{Rect} {p0} {p1} ; "))?; + LefShape::Rect(mask, p0, p1) => { + let mut line = format!("{Rect} "); + match mask { + Some(mask) => line.push_str(&format!("MASK {mask} ")), + None => (), + }; + self.write_line(format_args_f!("{line}{p0} {p1} ; "))?; } LefShape::Polygon(pts) => { let ptstr = pts