diff --git a/lef21/resources/lef21.schema.json b/lef21/resources/lef21.schema.json index dc8d8dc..0bddc5d 100644 --- a/lef21/resources/lef21.schema.json +++ b/lef21/resources/lef21.schema.json @@ -201,16 +201,11 @@ ] }, "vias": { - "description": "Via Definitions (Unsupported)", - "writeOnly": true, - "anyOf": [ - { - "$ref": "#/definitions/Unsupported" - }, - { - "type": "null" - } - ] + "description": "Via Definitions", + "type": "array", + "items": { + "$ref": "#/definitions/LefViaDef" + } } }, "definitions": { @@ -486,6 +481,27 @@ } } }, + "LefFixedViaDef": { + "title": "Lef Fixed Via Definition", + "type": "object", + "properties": { + "layers": { + "description": "Layers & Geometries", + "type": "array", + "items": { + "$ref": "#/definitions/LefViaLayerGeometries" + } + }, + "resistance_ohms": { + "description": "Resistance of the via.\n\nUsing this field is not recommended.", + "type": [ + "string", + "null" + ], + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" + } + } + }, "LefForeign": { "title": "Lef Foreign Cell Declaration", "description": "Declares the linkage to another cell, commonly in DEF or GDSII format. Foreign-cell references are stored exacty as in the LEF format: as a string cell-name.", @@ -522,6 +538,126 @@ } } }, + "LefGeneratedViaDef": { + "title": "Lef Generated Via Definition", + "type": "object", + "required": [ + "bot_enc_x", + "bot_enc_y", + "bot_metal_layer", + "cut_layer", + "cut_size_x", + "cut_size_y", + "cut_spacing_x", + "cut_spacing_y", + "top_enc_x", + "top_enc_y", + "top_metal_layer", + "via_rule_name" + ], + "properties": { + "bot_enc_x": { + "description": "Horizontal enclosure of vias by bottom metal layer.", + "type": "string", + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" + }, + "bot_enc_y": { + "description": "Vertical enclosure of vias by bottom metal layer.", + "type": "string", + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" + }, + "bot_metal_layer": { + "description": "The bottom metal layer.", + "type": "string" + }, + "cut_layer": { + "description": "The cut (via) layer.", + "type": "string" + }, + "cut_size_x": { + "description": "The width of the via rectangles.", + "type": "string", + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" + }, + "cut_size_y": { + "description": "The height of the via rectangles.", + "type": "string", + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" + }, + "cut_spacing_x": { + "description": "The horizontal spacing (right edge to next left edge) between cuts.", + "type": "string", + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" + }, + "cut_spacing_y": { + "description": "The vertical spacing (top edge to bottom edge of cut above) between cuts.", + "type": "string", + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" + }, + "offset": { + "description": "Offsets of top and bottom metal layers.", + "anyOf": [ + { + "$ref": "#/definitions/LefOffset" + }, + { + "type": "null" + } + ] + }, + "origin": { + "description": "The origin of the coordinate system specifying all of the via's shapes.", + "anyOf": [ + { + "$ref": "#/definitions/LefPoint" + }, + { + "type": "null" + } + ] + }, + "pattern": { + "description": "Specifies a pattern identifying which cuts are missing from the array.\n\nIn the absence of a specified pattern, all cuts are present. This field is currently unsupported.", + "anyOf": [ + { + "$ref": "#/definitions/Unsupported" + }, + { + "type": "null" + } + ] + }, + "rowcol": { + "description": "The via array's number of rows and columns.", + "anyOf": [ + { + "$ref": "#/definitions/LefRowCol" + }, + { + "type": "null" + } + ] + }, + "top_enc_x": { + "description": "Horizontal enclosure of vias by top metal layer.", + "type": "string", + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" + }, + "top_enc_y": { + "description": "Vertical enclosure of vias by top metal layer.", + "type": "string", + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" + }, + "top_metal_layer": { + "description": "The top metal layer.", + "type": "string" + }, + "via_rule_name": { + "description": "The name of the VIARULE.\n\nMust refer to a previously defined VIARULE GENERATE statement.", + "type": "string" + } + } + }, "LefGeometry": { "title": "Lef Geometric Object Enumeration", "description": "Includes [LefShape]s and Iterators thereof", @@ -923,6 +1059,34 @@ } } }, + "LefOffset": { + "title": "Lef Offset", + "type": "object", + "required": [ + "bot_x", + "bot_y", + "top_x", + "top_y" + ], + "properties": { + "bot_x": { + "type": "string", + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" + }, + "bot_y": { + "type": "string", + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" + }, + "top_x": { + "type": "string", + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" + }, + "top_y": { + "type": "string", + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" + } + } + }, "LefOnOff": { "description": "Binary On/Off Settings, Denoted by `ON` and `OFF`", "oneOf": [ @@ -1558,6 +1722,24 @@ } } }, + "LefRowCol": { + "title": "Lef Row and Column", + "type": "object", + "required": [ + "cols", + "rows" + ], + "properties": { + "cols": { + "type": "string", + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" + }, + "rows": { + "type": "string", + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" + } + } + }, "LefShape": { "title": "Lef Shape Enumeration", "description": "Includes each of LEF's individual geometric primitives: rectangles, polygons, and paths.", @@ -1885,6 +2067,166 @@ } } }, + "LefViaDef": { + "title": "Lef Via Definition", + "description": "LEF supports two kinds of vias: fixed vias and generated vias. Fixed vias contain a set of shapes (rectangles and/or polygons). Generated vias use a VIARULE statement to define via parameters.", + "type": "object", + "required": [ + "data", + "name" + ], + "properties": { + "data": { + "description": "The actual content of the via definition, which may be a fixed via or a generated via.", + "allOf": [ + { + "$ref": "#/definitions/LefViaDefData" + } + ] + }, + "default": { + "description": "Indicates that this via is a default via for connecting a pair of metal layers.\n\nThe metal layers connected are inferrred from the via's [`LefViaLayerGeometries`], which normally contain geometry on two metal layers and one cut layer.", + "default": false, + "type": "boolean" + }, + "name": { + "description": "The name of the via.", + "type": "string" + }, + "properties": { + "description": "Properties (Unsupported)", + "writeOnly": true, + "anyOf": [ + { + "$ref": "#/definitions/Unsupported" + }, + { + "type": "null" + } + ] + } + } + }, + "LefViaDefData": { + "title": "Lef Via Definition Data", + "oneOf": [ + { + "type": "object", + "required": [ + "Fixed" + ], + "properties": { + "Fixed": { + "$ref": "#/definitions/LefFixedViaDef" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "Generated" + ], + "properties": { + "Generated": { + "$ref": "#/definitions/LefGeneratedViaDef" + } + }, + "additionalProperties": false + } + ] + }, + "LefViaLayerGeometries": { + "title": "Lef Single-Layer Geometry Store for Vias", + "description": "[LefViaLayerGeometries] stores the combination of a layer (name) and suite of geometric primitives (rectangles and/or polygons) on that layer.\n\n[LefViaLayerGeometries] are the primary building block of [LefFixedViaDef]s.", + "type": "object", + "required": [ + "layer_name" + ], + "properties": { + "layer_name": { + "description": "Layer Name", + "type": "string" + }, + "shapes": { + "description": "Geometries", + "type": "array", + "items": { + "$ref": "#/definitions/LefViaShape" + } + } + } + }, + "LefViaShape": { + "title": "Lef Via Shape Enumeration", + "description": "These are the geometric primitives that can be included in a via definition.", + "oneOf": [ + { + "type": "object", + "required": [ + "Rect" + ], + "properties": { + "Rect": { + "type": "array", + "items": [ + { + "anyOf": [ + { + "$ref": "#/definitions/LefMask" + }, + { + "type": "null" + } + ] + }, + { + "$ref": "#/definitions/LefPoint" + }, + { + "$ref": "#/definitions/LefPoint" + } + ], + "maxItems": 3, + "minItems": 3 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "Polygon" + ], + "properties": { + "Polygon": { + "type": "array", + "items": [ + { + "anyOf": [ + { + "$ref": "#/definitions/LefMask" + }, + { + "type": "null" + } + ] + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/LefPoint" + } + } + ], + "maxItems": 2, + "minItems": 2 + } + }, + "additionalProperties": false + } + ] + }, "Unsupported": { "title": "Unsupported Feature", "description": "Empty placeholder struct for unsupported LEF features. Primarily included for documentation purposes. Most [`Unsupported`] fields are of type [`Optional`] for sake of serialization, so that they can take on the `null` value of many data formats. Setting these fields to [`Some(Unsupported)`] instead of [`None`] is largely a distinction without a difference.", diff --git a/lef21/src/bin/lefrw.rs b/lef21/src/bin/lefrw.rs index 62d20f9..f1848bc 100644 --- a/lef21/src/bin/lefrw.rs +++ b/lef21/src/bin/lefrw.rs @@ -1,26 +1,26 @@ -use std::error::Error; +use lef21::LefLibrary; use std::env; +use std::error::Error; use std::process; -use lef21::LefLibrary; struct Config { inlef: String, - outlef: String + outlef: String, } impl Config { - fn new(args: &[String]) -> Result { + fn new(args: &[String]) -> Result { if args.len() < 3 { - return Err("Not enough arguments, expecting 2.") + return Err("Not enough arguments, expecting 2."); } let inlef = args[1].clone(); let outlef = args[2].clone(); - Ok(Config { inlef, outlef} ) + Ok(Config { inlef, outlef }) } } fn run() -> Result<(), Box> { - let args : Vec = env::args().collect(); + let args: Vec = env::args().collect(); let cfg = Config::new(&args)?; let lib = LefLibrary::open(cfg.inlef)?; lib.save(cfg.outlef)?; @@ -31,4 +31,4 @@ fn main() { println!("Problem in lefrw: {}", err); process::exit(1); }); -} \ No newline at end of file +} diff --git a/lef21/src/data.rs b/lef21/src/data.rs index cf6c9d4..f1d910d 100644 --- a/lef21/src/data.rs +++ b/lef21/src/data.rs @@ -47,6 +47,10 @@ pub struct LefLibrary { /// Site Definitions #[serde(default, skip_serializing_if = "Vec::is_empty")] pub sites: Vec, + /// Via Definitions + #[serde(default, skip_serializing_if = "Vec::is_empty")] + #[builder(default)] + pub vias: Vec, // Optional /// Lef Spec Version @@ -76,26 +80,20 @@ pub struct LefLibrary { #[serde(default, skip_serializing_if = "Option::is_none")] #[builder(default, setter(strip_option))] pub units: Option, - // Fixed-Mask attribute #[serde(default, skip_serializing)] #[builder(default)] pub fixed_mask: bool, - /// Clearance Measure #[serde(default, skip_serializing_if = "Option::is_none")] #[builder(default)] pub clearance_measure: Option, - - // Unsupported fields recommended for *either* LEF "cell libraries" or "technologies" - /// Via Definitions (Unsupported) - #[serde(default, skip_serializing)] - #[builder(default)] - pub vias: Option, /// Syntax Extensions #[serde(default, skip_serializing_if = "Vec::is_empty")] #[builder(default)] pub extensions: Vec, + + // Unsupported fields recommended for *either* LEF "cell libraries" or "technologies" // Fields recommended for LEF technology descriptions, AKA "tech-lefs" /// Manufacturing Grid #[serde(default, skip_serializing_if = "Option::is_none")] @@ -207,7 +205,7 @@ pub struct LefMacro { #[serde(default, skip_serializing_if = "Option::is_none")] #[builder(default, setter(strip_option))] pub eeq: Option, - + // Fixed-Mask #[serde(default, skip_serializing)] #[builder(default)] @@ -316,7 +314,7 @@ pub struct LefPin { #[serde(default, skip_serializing_if = "Option::is_none")] #[builder(default, setter(strip_option))] pub must_join: Option, - + /// Net Expression #[serde(default, skip_serializing_if = "Option::is_none")] #[builder(default, setter(strip_option))] @@ -326,7 +324,6 @@ pub struct LefPin { #[serde(default, skip_serializing_if = "Vec::is_empty")] #[builder(default)] pub properties: Vec, - } /// # Lef Pin Direction #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq)] @@ -449,6 +446,142 @@ pub struct LefVia { /// Location pub pt: LefPoint, } +/// # Lef Via Definition +/// +/// LEF supports two kinds of vias: fixed vias and generated vias. +/// Fixed vias contain a set of shapes (rectangles and/or polygons). +/// Generated vias use a VIARULE statement to define via parameters. +#[derive(Builder, Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq)] +#[builder(pattern = "owned", setter(into))] +pub struct LefViaDef { + /// The name of the via. + pub name: String, + /// Indicates that this via is a default via for connecting a pair of metal layers. + /// + /// The metal layers connected are inferrred from the via's [`LefViaLayerGeometries`], + /// which normally contain geometry on two metal layers and one cut layer. + #[serde(default)] + #[builder(default)] + pub default: bool, + /// The actual content of the via definition, which may be a fixed via or a generated via. + pub data: LefViaDefData, + // Unsupported + /// Properties (Unsupported) + #[serde(default, skip_serializing)] + #[builder(default)] + pub properties: Option, +} +/// # Lef Via Definition Data +#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq)] +pub enum LefViaDefData { + Fixed(LefFixedViaDef), + Generated(LefGeneratedViaDef), +} +/// # Lef Fixed Via Definition +#[derive(Builder, Clone, Debug, Default, Deserialize, Serialize, JsonSchema, PartialEq, Eq)] +#[builder(pattern = "owned", setter(into))] +pub struct LefFixedViaDef { + /// Resistance of the via. + /// + /// Using this field is not recommended. + #[serde(default, skip_serializing_if = "Option::is_none")] + #[builder(default)] + pub resistance_ohms: Option, + /// Layers & Geometries + #[serde(default, skip_serializing_if = "Vec::is_empty")] + #[builder(default)] + pub layers: Vec, +} +/// # Lef Generated Via Definition +#[derive(Builder, Clone, Debug, Default, Deserialize, Serialize, JsonSchema, PartialEq, Eq)] +#[builder(pattern = "owned", setter(into))] +pub struct LefGeneratedViaDef { + /// The name of the VIARULE. + /// + /// Must refer to a previously defined VIARULE GENERATE statement. + pub via_rule_name: String, + /// The width of the via rectangles. + pub cut_size_x: LefDecimal, + /// The height of the via rectangles. + pub cut_size_y: LefDecimal, + /// The bottom metal layer. + pub bot_metal_layer: String, + /// The cut (via) layer. + pub cut_layer: String, + /// The top metal layer. + pub top_metal_layer: String, + /// The horizontal spacing (right edge to next left edge) between cuts. + pub cut_spacing_x: LefDecimal, + /// The vertical spacing (top edge to bottom edge of cut above) between cuts. + pub cut_spacing_y: LefDecimal, + /// Horizontal enclosure of vias by bottom metal layer. + pub bot_enc_x: LefDecimal, + /// Vertical enclosure of vias by bottom metal layer. + pub bot_enc_y: LefDecimal, + /// Horizontal enclosure of vias by top metal layer. + pub top_enc_x: LefDecimal, + /// Vertical enclosure of vias by top metal layer. + pub top_enc_y: LefDecimal, + /// The via array's number of rows and columns. + #[serde(default, skip_serializing_if = "Option::is_none")] + #[builder(default, setter(strip_option))] + pub rowcol: Option, + /// The origin of the coordinate system specifying all of the via's shapes. + #[serde(default, skip_serializing_if = "Option::is_none")] + #[builder(default, setter(strip_option))] + pub origin: Option, + /// Offsets of top and bottom metal layers. + #[serde(default, skip_serializing_if = "Option::is_none")] + #[builder(default, setter(strip_option))] + pub offset: Option, + /// Specifies a pattern identifying which cuts are missing from the array. + /// + /// In the absence of a specified pattern, all cuts are present. + /// This field is currently unsupported. + #[serde(default, skip_serializing_if = "Option::is_none")] + #[builder(default, setter(strip_option))] + pub pattern: Option, +} +/// # Lef Row and Column +#[derive(Builder, Clone, Debug, Default, Deserialize, Serialize, JsonSchema, PartialEq, Eq)] +#[builder(pattern = "owned", setter(into))] +pub struct LefRowCol { + pub rows: LefDecimal, + pub cols: LefDecimal, +} +/// # Lef Offset +#[derive(Builder, Clone, Debug, Default, Deserialize, Serialize, JsonSchema, PartialEq, Eq)] +#[builder(pattern = "owned", setter(into))] +pub struct LefOffset { + pub bot_x: LefDecimal, + pub bot_y: LefDecimal, + pub top_x: LefDecimal, + pub top_y: LefDecimal, +} +/// # Lef Single-Layer Geometry Store for Vias +/// +/// [LefViaLayerGeometries] stores the combination of a layer (name) +/// and suite of geometric primitives (rectangles and/or polygons) on that layer. +/// +/// [LefViaLayerGeometries] are the primary building block of [LefFixedViaDef]s. +/// +#[derive(Clone, Default, Builder, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq)] +#[builder(pattern = "owned", setter(into))] +pub struct LefViaLayerGeometries { + // Required + /// Layer Name + pub layer_name: String, + /// Geometries + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub shapes: Vec, +} +/// # Lef Via Shape Enumeration +/// These are the geometric primitives that can be included in a via definition. +#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq)] +pub enum LefViaShape { + Rect(Option, LefPoint, LefPoint), + Polygon(Option, Vec), +} /// # Enumerated Layer-Spacing Options /// Includes absolute spacing and design-rule-width modifiers. #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq)] @@ -475,8 +608,18 @@ pub struct LefPropertyRange { #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq)] pub enum LefPropertyDefinition { LefString(LefPropertyDefinitionObjectType, String, Option), - LefReal(LefPropertyDefinitionObjectType, String, Option, Option), - LefInteger(LefPropertyDefinitionObjectType, String, Option, Option), + LefReal( + LefPropertyDefinitionObjectType, + String, + Option, + Option, + ), + LefInteger( + LefPropertyDefinitionObjectType, + String, + Option, + Option, + ), } /// # Lef Geometric Object Enumeration /// Includes [LefShape]s and Iterators thereof @@ -737,7 +880,7 @@ enumstr!( ManufacturingGrid: "MANUFACTURINGGRID", ClearanceMeasure: "CLEARANCEMEASURE", Density: "DENSITY", - + // UNITS Fields Units: "UNITS", Time: "TIME", @@ -769,6 +912,17 @@ enumstr!( AntennaMaxSideAreaCar: "ANTENNAMAXSIDEAREACAR", AntennaMaxCutCar: "ANTENNAMAXCUTCAR", + // VIA Fields + Default: "DEFAULT", + ViaRule: "VIARULE", + CutSize: "CUTSIZE", + Layers: "LAYERS", + CutSpacing: "CUTSPACING", + Enclosure: "ENCLOSURE", + RowCol: "ROWCOL", + Offset: "OFFSET", + Pattern: "PATTERN", + // PropertyDefinitions PropertyDefinitions: "PROPERTYDEFINITIONS", String: "STRING", @@ -778,7 +932,6 @@ enumstr!( // Unsupported MaxViaStack: "MAXVIASTACK", - ViaRule: "VIARULE", Generate: "GENERATE", NonDefaultRule: "NONDEFAULTRULE", } diff --git a/lef21/src/read.rs b/lef21/src/read.rs index 9914423..4480b20 100644 --- a/lef21/src/read.rs +++ b/lef21/src/read.rs @@ -7,7 +7,6 @@ // Standard Lib Imports use std::io::Read; -use std::num::FpCategory; use std::path::Path; use std::str::Chars; @@ -216,7 +215,7 @@ impl<'src> LefLexer<'src> { continue; } // add the rest of the numberish &str to the String - let subbuf = &buf[0.. self.pos - self.start - 1]; + let subbuf = &buf[0..self.pos - self.start - 1]; nstring.push_str(subbuf); let numslice = nstring.as_str(); let tok = if i32::from_str(numslice).is_ok() || f64::from_str(numslice).is_ok() { @@ -324,6 +323,7 @@ pub enum LefParseContext { Site, Units, Density, + Via, Unknown, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -485,6 +485,7 @@ impl<'src> LefParser<'src> { let mut lib = LefLibraryBuilder::default(); let mut macros = Vec::new(); let mut sites = Vec::new(); + let mut vias = Vec::new(); let mut extensions = Vec::new(); let mut property_definitions = Vec::new(); loop { @@ -540,6 +541,10 @@ impl<'src> LefParser<'src> { self.expect(TokenType::SemiColon)?; lib.use_min_spacing(e) } + LefKey::Via => { + vias.push(self.parse_via()?); + lib + } LefKey::ClearanceMeasure => { self.advance()?; let e = self.parse_enum::()?; @@ -571,28 +576,29 @@ impl<'src> LefParser<'src> { break; } () - }, + } None => { - let _ignore = self.fail::(LefParseErrorType::InvalidKey); + let _ignore = self + .fail::(LefParseErrorType::InvalidKey); () } } } data.push_str(self.txt(&tok)); data.push_str(" ") - }, + } None => { self.fail(LefParseErrorType::InvalidKey)?; } } } - extensions.push(LefExtension {name, data}); + extensions.push(LefExtension { name, data }); lib } LefKey::PropertyDefinitions => { property_definitions.extend(self.parse_property_definitions()?); lib - }, + } LefKey::MaxViaStack | LefKey::ViaRule | LefKey::Generate @@ -602,6 +608,7 @@ impl<'src> LefParser<'src> { } lib = lib.macros(macros); lib = lib.sites(sites); + lib = lib.vias(vias); lib = lib.extensions(extensions); lib = lib.property_definitions(property_definitions); self.ctx.pop(); @@ -660,7 +667,7 @@ impl<'src> LefParser<'src> { LefKey::Foreign => { self.advance()?; // Eat the FOREIGN key let cell_name = self.parse_ident()?; - + let mut pt = None; if !self.matches(TokenType::SemiColon) { pt = Some(self.parse_point()?); @@ -706,9 +713,7 @@ impl<'src> LefParser<'src> { self.expect(TokenType::SemiColon)?; mac.source(e) } - LefKey::Density => { - mac.density(self.parse_density()?) - } + LefKey::Density => mac.density(self.parse_density()?), LefKey::End => { self.advance()?; // End of Macro. Eat the END key break; @@ -908,7 +913,11 @@ impl<'src> LefParser<'src> { let p1: LefPoint = self.parse_point()?; let p2: LefPoint = self.parse_point()?; let dens_value: LefDecimal = self.parse_number()?; - rects.push(LefDensityRectangle { pt1: p1, pt2: p2, density_value: dens_value }); + rects.push(LefDensityRectangle { + pt1: p1, + pt2: p2, + density_value: dens_value, + }); self.expect(TokenType::SemiColon)?; } _ => self.fail(LefParseErrorType::InvalidKey)?, @@ -1011,13 +1020,81 @@ impl<'src> LefParser<'src> { self.ctx.pop(); Ok(layer) } + /// Parse a set of via geometries on a single layer. + pub(crate) fn parse_via_layer_geometries(&mut self) -> LefResult { + self.ctx.push(LefParseContext::Geometry); + self.expect_key(LefKey::Layer)?; // Eat the opening LAYER keyword + let mut layer = LefViaLayerGeometriesBuilder::default(); + layer = layer.layer_name(self.parse_ident()?); // Parse the layer-name + self.expect(TokenType::SemiColon)?; + + let mut shapes: Vec = Vec::new(); + loop { + if self.peek_token().is_none() { + break; // End of input, which is valid here. + } + + // Anything else that shows up here must be one of the following keys: + match self.peek_key()? { + LefKey::Layer | LefKey::Property | LefKey::End => break, // End of geometries. (Really start/end of something else.) + LefKey::Polygon | LefKey::Rect => { + shapes.push(self.parse_via_shape()?); + } + _ => self.fail(LefParseErrorType::InvalidKey)?, + } + } + layer = layer.shapes(shapes); + let layer = layer.build()?; + self.ctx.pop(); + Ok(layer) + } + /// Parse a [LefViaShape] statement + fn parse_via_shape(&mut self) -> LefResult { + match self.peek_key()? { + LefKey::Rect => { + self.advance()?; + let mut mask = None; + if self.matches(TokenType::Name) { + if self.get_key()? == LefKey::Mask { + mask = Some(LefMask::new(self.parse_number()?)); + } else { + self.fail(LefParseErrorType::Unsupported)?; + } + } + // Parse the two points + let p1 = self.parse_point()?; + let p2: LefPoint = self.parse_point()?; + self.expect(TokenType::SemiColon)?; + // And return the Rect + Ok(LefViaShape::Rect(mask, p1, p2)) + } + LefKey::Polygon => { + self.advance()?; + let mut mask = None; + if self.matches(TokenType::Name) { + if self.get_key()? == LefKey::Mask { + mask = Some(LefMask::new(self.parse_number()?)); + } else { + self.fail(LefParseErrorType::Unsupported)?; + } + } + let points = self.parse_point_list()?; + if points.len() < 3 { + self.fail(LefParseErrorType::InvalidValue)?; + } + self.expect(TokenType::SemiColon)?; + Ok(LefViaShape::Polygon(mask, points)) + } + _ => self.fail(LefParseErrorType::InvalidKey)?, + } + } /// Parse optional ITERATE and return a wrapper boolean indicating success fn parse_iterate(&mut self) -> LefResult { if self.matches(TokenType::Name) { if self.peek_key()? == LefKey::Iterate { self.advance()?; - return Ok(true) + return Ok(true); } } Ok(false) @@ -1041,7 +1118,12 @@ impl<'src> LefParser<'src> { self.expect_key(LefKey::Step)?; let spacex = self.parse_number()?; let spacey = self.parse_number()?; - Ok(LefStepPattern {numx, numy, spacex, spacey}) + Ok(LefStepPattern { + numx, + numy, + spacex, + spacey, + }) } /// Parse a [LefGeometry] statement /// Each can be a shape or iteration thereof @@ -1110,13 +1192,20 @@ impl<'src> LefParser<'src> { while !self.matches(TokenType::SemiColon) { let name = self.parse_ident()?; match self.peek_token() { - Some(t) if (t.ttype == TokenType::Name || t.ttype == TokenType::Number || t.ttype ==TokenType::StringLiteral) => { + Some(t) + if (t.ttype == TokenType::Name + || t.ttype == TokenType::Number + || t.ttype == TokenType::StringLiteral) => + { let value = String::from(self.txt(&t)); self.advance()?; - propvec.push(LefProperty {name: name, value: value}) + propvec.push(LefProperty { name, value }) } None | Some(_) => { - self.fail_msg(LefParseErrorType::InvalidValue, "Unexpected token while parsing PROPERTY, must be string/number/name")?; + self.fail_msg( + LefParseErrorType::InvalidValue, + "Unexpected token while parsing PROPERTY, must be string/number/name", + )?; //self.fail(LefParseErrorType::InvalidValue)?; } } @@ -1126,14 +1215,19 @@ impl<'src> LefParser<'src> { } // get the tail portion of a numeric PROPERTYDEFINITION entry which has an optional value and range - fn parse_property_definition_tail(&mut self) -> LefResult<(Option, Option)> { + fn parse_property_definition_tail( + &mut self, + ) -> LefResult<(Option, Option)> { let mut lvalue: Option = None; let mut lrange: Option = None; if self.matches(TokenType::Name) { self.expect_key(LefKey::Range)?; let rbegin = self.parse_number()?; let rend = self.parse_number()?; - lrange = Some(LefPropertyRange {begin: rbegin, end: rend}); + lrange = Some(LefPropertyRange { + begin: rbegin, + end: rend, + }); } if self.matches(TokenType::Number) { lvalue = Some(self.parse_number()?); @@ -1144,7 +1238,9 @@ impl<'src> LefParser<'src> { /// Parse [LefPropertyDefinition]s fn parse_property_definitions(&mut self) -> LefResult> { - use LefKey::{End, Layer, Library, Macro, NonDefaultRule, Pin, PropertyDefinitions, Via, ViaRule}; + use LefKey::{ + End, Layer, Library, Macro, NonDefaultRule, Pin, PropertyDefinitions, Via, ViaRule, + }; self.ctx.push(LefParseContext::PropertyDefinitions); self.expect_key(PropertyDefinitions)?; let mut propdefs = Vec::new(); @@ -1152,7 +1248,7 @@ impl<'src> LefParser<'src> { match self.peek_key()? { Layer | Library | Macro | NonDefaultRule | Pin | Via | ViaRule => { let objtype = self.parse_enum::()?; - let propname = String::from(self.get_name()?); + let propname = String::from(self.get_name()?); match self.get_key()? { LefKey::String => { let value = if self.matches(TokenType::SemiColon) { @@ -1163,15 +1259,20 @@ impl<'src> LefParser<'src> { Some(String::from(txt)) }; self.expect(TokenType::SemiColon)?; - propdefs.push(LefPropertyDefinition::LefString(objtype, propname, value)); + propdefs + .push(LefPropertyDefinition::LefString(objtype, propname, value)); } LefKey::Real => { let (optval, optrange) = self.parse_property_definition_tail()?; - propdefs.push(LefPropertyDefinition::LefReal(objtype, propname, optval, optrange)); + propdefs.push(LefPropertyDefinition::LefReal( + objtype, propname, optval, optrange, + )); } LefKey::Integer => { let (optval, optrange) = self.parse_property_definition_tail()?; - propdefs.push(LefPropertyDefinition::LefInteger(objtype, propname, optval, optrange)); + propdefs.push(LefPropertyDefinition::LefInteger( + objtype, propname, optval, optrange, + )); } _ => self.fail(LefParseErrorType::InvalidKey)?, } @@ -1188,7 +1289,6 @@ impl<'src> LefParser<'src> { Ok(propdefs) } - /// Parse [LefUnits] definitions fn parse_units(&mut self) -> LefResult { use LefKey::{ @@ -1365,6 +1465,115 @@ impl<'src> LefParser<'src> { self.expect(TokenType::SemiColon)?; Ok((x, y)) } + /// Parse a Lef VIA definition + fn parse_via(&mut self) -> LefResult { + self.ctx.push(LefParseContext::Via); + self.expect_key(LefKey::Via)?; + let mut via: LefViaDefBuilder = Default::default(); + let name = self.parse_ident()?; + via = via.name(name.clone()); + + if let LefKey::Default = self.peek_key()? { + self.advance()?; // Consume the DEFAULT + via = via.default(true); + } + + if let LefKey::ViaRule = self.peek_key()? { + self.advance()?; // Eat the VIARULE key + let mut data = LefGeneratedViaDefBuilder::default(); + data = data.via_rule_name(self.parse_ident()?); + self.expect(TokenType::SemiColon)?; + loop { + match self.peek_key()? { + LefKey::CutSize => { + self.advance()?; // Eat the CUTSIZE key + data = data.cut_size_x(self.parse_number()?); + data = data.cut_size_y(self.parse_number()?); + self.expect(TokenType::SemiColon)?; + } + LefKey::Layers => { + self.advance()?; // Eat the LAYERS key + data = data.bot_metal_layer(self.parse_ident()?); + data = data.cut_layer(self.parse_ident()?); + data = data.top_metal_layer(self.parse_ident()?); + self.expect(TokenType::SemiColon)?; + } + LefKey::CutSpacing => { + self.advance()?; // Eat the CUTSPACING key + data = data.cut_spacing_x(self.parse_number()?); + data = data.cut_spacing_y(self.parse_number()?); + self.expect(TokenType::SemiColon)?; + } + LefKey::Enclosure => { + self.advance()?; // Eat the ENCLOSURE key + data = data.bot_enc_x(self.parse_number()?); + data = data.bot_enc_y(self.parse_number()?); + data = data.top_enc_x(self.parse_number()?); + data = data.top_enc_y(self.parse_number()?); + self.expect(TokenType::SemiColon)?; + } + LefKey::RowCol => { + self.advance()?; // Eat the ROWCOL key + data = data.rowcol(LefRowCol { + rows: self.parse_number()?, + cols: self.parse_number()?, + }); + self.expect(TokenType::SemiColon)?; + } + LefKey::Origin => { + self.advance()?; // Eat the ORIGIN key + data = data.origin(self.parse_point()?); + self.expect(TokenType::SemiColon)?; + } + LefKey::Offset => { + self.advance()?; // Eat the OFFSET key + data = data.offset(LefOffset { + bot_x: self.parse_number()?, + bot_y: self.parse_number()?, + top_x: self.parse_number()?, + top_y: self.parse_number()?, + }); + self.expect(TokenType::SemiColon)?; + } + LefKey::Pattern | LefKey::Property => { + self.fail(LefParseErrorType::Unsupported)? + } + LefKey::End => { + break; + } + _ => self.fail(LefParseErrorType::InvalidKey)?, + } + } + via = via.data(LefViaDefData::Generated(data.build()?)); + } else { + let mut data = LefFixedViaDefBuilder::default(); + + if let LefKey::Resistance = self.peek_key()? { + self.advance()?; // Eat the RESISTANCE key + data = data.resistance_ohms(self.parse_number()?); + self.expect(TokenType::SemiColon)?; + } + + let mut layers = Vec::new(); + while let LefKey::Layer = self.peek_key()? { + layers.push(self.parse_via_layer_geometries()?); + } + data = data.layers(layers); + via = via.data(LefViaDefData::Fixed(data.build()?)); + } + + match self.peek_key()? { + LefKey::Property => self.fail(LefParseErrorType::Unsupported)?, + LefKey::End => { + self.advance()?; // End of Via. Eat the END key + } + _ => self.fail(LefParseErrorType::InvalidKey)?, + } + // Parse the END-enclosing via-name + self.expect_ident(&name)?; + self.ctx.pop(); + Ok(via.build()?) + } /// Parse the next token into a [LefDecimal] number fn parse_number(&mut self) -> LefResult { let tok = self.expect(TokenType::Number)?; @@ -1475,10 +1684,26 @@ mod tests { let lex = LefLexer::new(src)?; let toks_vec: Vec = lex.collect(); // Collect up all tokens let tok_strs: Vec<&str> = toks_vec.iter().map(|t| t.substr(src)).collect(); - assert_eq!(tok_strs, vec!["STUFF", "101", "-98765", ".01", "1.23", "-0.87", "1e6", "1.06E-7", "18T", ";"]); + assert_eq!( + tok_strs, + vec!["STUFF", "101", "-98765", ".01", "1.23", "-0.87", "1e6", "1.06E-7", "18T", ";"] + ); let tok_types: Vec = toks_vec.iter().map(|t| t.ttype).collect(); - assert_eq!(tok_types, vec![TokenType::Name, TokenType::Number, TokenType::Number, TokenType::Number, TokenType::Number, - TokenType::Number, TokenType::Number, TokenType::Number, TokenType::Name, TokenType::SemiColon]); + assert_eq!( + tok_types, + vec![ + TokenType::Name, + TokenType::Number, + TokenType::Number, + TokenType::Number, + TokenType::Number, + TokenType::Number, + TokenType::Number, + TokenType::Number, + TokenType::Name, + TokenType::SemiColon + ] + ); Ok(()) } #[test] diff --git a/lef21/src/tests.rs b/lef21/src/tests.rs index 7147dae..27e16fc 100644 --- a/lef21/src/tests.rs +++ b/lef21/src/tests.rs @@ -1,3 +1,5 @@ +use rust_decimal::Decimal; + use super::read::{parse_str, LefLexer, LefParser, Token}; use super::*; use crate::utils::SerializationFormat::{Json, Toml, Yaml}; @@ -163,6 +165,142 @@ fn it_parses_density_lib() -> LefResult<()> { Ok(()) } +#[test] +fn it_parses_via_lib() -> LefResult<()> { + let src = r#" + VERSION 5.8 ; + UNITS DATABASE MICRONS 2000 ; END UNITS + VIA via1 DEFAULT + RESISTANCE 21.0 ; + LAYER met1 ; + RECT MASK 0 -0.5 -0.5 0.5 0.5 ; + LAYER cut1 ; + POLYGON -0.2 -0.2 -0.2 0.2 0.3 0.2 0.2 -0.2 ; + RECT -0.3 -0.3 0.3 0.3 ; + LAYER met2 ; + POLYGON MASK 1 -0.3 -0.3 -0.3 0.3 0.4 0.3 0.3 -0.3 ; + POLYGON -0.4 -0.2 -0.2 0.1 0.2 0.2 0.1 -0.4 ; + END via1 + VIA via2 + LAYER met2 ; + RECT MASK 0 -0.5 -0.5 0.5 0.5 ; + LAYER cut2 ; + RECT -0.3 -0.3 0.3 0.3 ; + LAYER met3 ; + RECT -0.4 -0.4 0.4 0.4 ; + END via2 + VIA via3 DEFAULT + VIARULE genvia3 ; + CUTSIZE 0.2 0.2 ; + LAYERS met3 via3 met4 ; + CUTSPACING 0.1 0.1 ; + ENCLOSURE 0.1 0.2 0.4 0.2 ; + ROWCOL 2 3 ; + END via3 + VIA via4 + # comment + DEFAULT + # another comment + RESISTANCE 2.00 ; + LAYER met4 ; RECT -1 -1 1 1 ; + # yet another comment + LAYER via4 ; RECT -0.5 -0.5 0.5 0.5 ; + LAYER met5 ; RECT -2 -2 2 2 ; + END via4 + "#; + let lib = parse_str(src)?; + assert_eq!(lib.vias.len(), 4); + let via2 = &lib.vias[1]; + assert_eq!( + *via2, + LefViaDef { + name: "via2".into(), + default: false, + data: LefViaDefData::Fixed(LefFixedViaDef { + resistance_ohms: None, + layers: vec![ + LefViaLayerGeometries { + layer_name: "met2".into(), + shapes: vec![LefViaShape::Rect( + Some(LefMask { + mask: Decimal::new(0, 0), + }), + LefPoint { + x: Decimal::new(-5, 1), + y: Decimal::new(-5, 1) + }, + LefPoint { + x: Decimal::new(5, 1), + y: Decimal::new(5, 1) + } + )], + }, + LefViaLayerGeometries { + layer_name: "cut2".into(), + shapes: vec![LefViaShape::Rect( + None, + LefPoint { + x: Decimal::new(-3, 1), + y: Decimal::new(-3, 1) + }, + LefPoint { + x: Decimal::new(3, 1), + y: Decimal::new(3, 1) + } + )], + }, + LefViaLayerGeometries { + layer_name: "met3".into(), + shapes: vec![LefViaShape::Rect( + None, + LefPoint { + x: Decimal::new(-4, 1), + y: Decimal::new(-4, 1) + }, + LefPoint { + x: Decimal::new(4, 1), + y: Decimal::new(4, 1) + } + )], + }, + ], + }), + properties: None, + } + ); + let via3 = &lib.vias[2]; + assert_eq!( + *via3, + LefViaDef { + name: "via3".into(), + default: true, + data: LefViaDefData::Generated(LefGeneratedViaDef { + via_rule_name: "genvia3".into(), + cut_size_x: Decimal::new(2, 1), + cut_size_y: Decimal::new(2, 1), + bot_metal_layer: "met3".into(), + cut_layer: "via3".into(), + top_metal_layer: "met4".into(), + cut_spacing_x: Decimal::new(1, 1), + cut_spacing_y: Decimal::new(1, 1), + bot_enc_x: Decimal::new(1, 1), + bot_enc_y: Decimal::new(2, 1), + top_enc_x: Decimal::new(4, 1), + top_enc_y: Decimal::new(2, 1), + rowcol: Some(LefRowCol { + rows: Decimal::new(2, 0), + cols: Decimal::new(3, 0), + }), + origin: None, + offset: None, + pattern: None, + }), + properties: None, + } + ); + Ok(()) +} + #[test] fn it_parses_no_end_library_5p6() -> LefResult<()> { let src = r#" diff --git a/lef21/src/write.rs b/lef21/src/write.rs index 2b45c28..be211fd 100644 --- a/lef21/src/write.rs +++ b/lef21/src/write.rs @@ -54,8 +54,8 @@ impl<'wr> LefWriter<'wr> { fn write_lib(&mut self, lib: &LefLibrary) -> LefResult<()> { use LefKey::{ BusBitChars, ClearanceMeasure, DividerChar, End, FixedMask, Library, ManufacturingGrid, - NamesCaseSensitive, NoWireExtensionAtPin, Obs, PropertyDefinitions, - UseMinSpacing, Version, + NamesCaseSensitive, NoWireExtensionAtPin, Obs, PropertyDefinitions, UseMinSpacing, + Version, }; if let Some(ref v) = lib.version { // Save a copy in our session-state @@ -104,19 +104,19 @@ impl<'wr> LefWriter<'wr> { } // PROPERTYDEFINITIONS if lib.property_definitions.len() > 0 { - self.write_line(format_args_f!("{PropertyDefinitions} "))?; + self.write_line(format_args_f!("{PropertyDefinitions} "))?; self.indent += 1; for propdef in lib.property_definitions.iter() { let propdef_str: String = match propdef { LefPropertyDefinition::LefString(objtype, name, None) => { format!("{objtype} {name} {}", LefKey::String) - }, + } LefPropertyDefinition::LefString(objtype, name, Some(val)) => { format!("{objtype} {name} {} {}", LefKey::String, val) - }, + } LefPropertyDefinition::LefReal(objtype, name, value, range) => { self.format_numeric_prop_def(objtype, name, LefKey::Real, value, range)? - }, + } LefPropertyDefinition::LefInteger(objtype, name, value, range) => { self.format_numeric_prop_def(objtype, name, LefKey::Integer, value, range)? } @@ -127,16 +127,19 @@ impl<'wr> LefWriter<'wr> { self.write_line(format_args_f!("{End} {PropertyDefinitions} "))?; } // FIXEDMASK - if lib.fixed_mask { + if lib.fixed_mask { self.write_line(format_args_f!("{FixedMask} ;"))?; } // TODO: LAYER // TODO: MAXVIASTACK - // TODO: VIARULE GENERATE - // TODO: VIA - // if let Some(ref v) = lib.vias { } - // TODO: VIARULE + + // Write each via definition + for via in lib.vias.iter() { + self.write_via(via)?; + } + + // TODO: VIARULE [GENERATE] // TODO: NONDEFAULTRULE // Write each SITE definition @@ -150,7 +153,9 @@ impl<'wr> LefWriter<'wr> { for ext in lib.extensions.iter() { use LefKey::{BeginExtension, EndExtension}; - self.write_line(format_args_f!("{BeginExtension} {ext.name} {ext.data} {EndExtension}"))?; + self.write_line(format_args_f!( + "{BeginExtension} {ext.name} {ext.data} {EndExtension}" + ))?; } self.write_line(format_args_f!("{End} {Library} \n"))?; @@ -158,9 +163,75 @@ impl<'wr> LefWriter<'wr> { Ok(()) } + /// Write a [LefViaDef]. + fn write_via(&mut self, via: &LefViaDef) -> LefResult<()> { + use LefKey::{ + CutSize, CutSpacing, Default, Enclosure, End, Layers, Offset, Origin, Resistance, + RowCol, Via, ViaRule, + }; + + if via.default { + self.write_line(format_args_f!("{Via} {via.name} {Default}"))?; + } else { + self.write_line(format_args_f!("{Via} {via.name}"))?; + } + self.indent += 1; + + match &via.data { + LefViaDefData::Fixed(via) => { + if let Some(r) = via.resistance_ohms { + self.write_line(format_args_f!("{Resistance} {r} ; "))?; + } + for layer in via.layers.iter() { + self.write_via_layer_geom(layer)?; + } + } + LefViaDefData::Generated(via) => { + self.write_line(format_args_f!("{ViaRule} {via.via_rule_name} ; "))?; + self.write_line(format_args_f!( + "{CutSize} {via.cut_size_x} {via.cut_size_y} ; " + ))?; + self.write_line(format_args_f!( + "{Layers} {via.bot_metal_layer} {via.cut_layer} {via.top_metal_layer} ; " + ))?; + self.write_line(format_args_f!( + "{CutSpacing} {via.cut_spacing_x} {via.cut_spacing_y} ; " + ))?; + self.write_line(format_args_f!( + "{Enclosure} {via.bot_enc_x} {via.bot_enc_y} {via.top_enc_x} {via.top_enc_y} ; " + ))?; + if let Some(ref rowcol) = via.rowcol { + self.write_line(format_args_f!("{RowCol} {rowcol.rows} {rowcol.cols} ; "))?; + } + if let Some(ref origin) = via.origin { + self.write_line(format_args_f!("{Origin} {origin.x} {origin.y} ; "))?; + } + if let Some(ref offset) = via.offset { + self.write_line(format_args_f!( + "{Offset} {offset.bot_x} {offset.bot_y} {offset.top_x} {offset.top_y} ; " + ))?; + } + // PATTERN would go here + } + } + + // PROPERTIES would go here + + self.indent -= 1; + self.write_line(format_args_f!("{End} {} ", via.name))?; + Ok(()) + } + // helper function to format numeric PROPERTYDEFINITION entries // for "objType propName [RANGE begin end] [value]" - fn format_numeric_prop_def(&mut self, objtype: &LefPropertyDefinitionObjectType, name: &String, key: LefKey, value: &Option, range: &Option) -> LefResult { + fn format_numeric_prop_def( + &mut self, + objtype: &LefPropertyDefinitionObjectType, + name: &String, + key: LefKey, + value: &Option, + range: &Option, + ) -> LefResult { use LefKey::Range; let mut string_list: Vec = Vec::new(); string_list.push(objtype.to_string()); @@ -168,11 +239,11 @@ impl<'wr> LefWriter<'wr> { string_list.push(key.to_string()); match range { Some(r) => string_list.push(format!("{Range} {} {}", r.begin, r.end)), - None => () + None => (), } match value { Some(v) => string_list.push(v.to_string()), - None => () + None => (), } Ok(string_list.join(" ")) } @@ -196,8 +267,11 @@ impl<'wr> LefWriter<'wr> { /// Write a [LefUnits] block. fn write_units(&mut self, units: &LefUnits) -> LefResult<()> { - use LefKey::{Capacitance, Current, Database, End, Frequency, Megahertz, Microns, Milliamps, - Milliwatts, Nanoseconds, Ohms, Picofarads, Power, Resistance, Time, Volts, Voltage, Units}; + use LefKey::{ + Capacitance, Current, Database, End, Frequency, Megahertz, Microns, Milliamps, + Milliwatts, Nanoseconds, Ohms, Picofarads, Power, Resistance, Time, Units, Voltage, + Volts, + }; self.write_line(format_args_f!("{Units} "))?; self.indent += 1; if let Some(ref val) = units.time_ns { @@ -238,7 +312,7 @@ impl<'wr> LefWriter<'wr> { if let Some(ref v) = mac.class { self.write_macro_class(v)?; } - if mac.fixed_mask { + if mac.fixed_mask { self.write_line(format_args_f!("{FixedMask} ;"))?; } if let Some(ref v) = mac.foreign { @@ -264,7 +338,7 @@ impl<'wr> LefWriter<'wr> { } self.write_line(format_args_f!("{Source} {v} ;"))?; } - + if let Some(ref cell) = mac.eeq { self.write_line(format_args_f!("{Eeq} {cell} ;"))?; } @@ -309,8 +383,10 @@ impl<'wr> LefWriter<'wr> { /// Write a [LefPin] definition fn write_pin(&mut self, pin: &LefPin) -> LefResult<()> { - use LefKey::{AntennaModel, Direction, End, GroundSensitivity, Layer, MustJoin, - NetExpr, Pin, Shape, SupplySensitivity, TaperRule, Use}; + use LefKey::{ + AntennaModel, Direction, End, GroundSensitivity, Layer, MustJoin, NetExpr, Pin, Shape, + SupplySensitivity, TaperRule, Use, + }; self.write_line(format_args_f!("{Pin} {pin.name} "))?; self.indent += 1; if let Some(ref v) = pin.direction { @@ -408,20 +484,62 @@ impl<'wr> LefWriter<'wr> { self.indent -= 1; Ok(()) // Note [LefLayerGeometries] have no "END" or other closing delimeter. } + /// Write [LefViaLayerGeometries]. + fn write_via_layer_geom(&mut self, layer: &LefViaLayerGeometries) -> LefResult<()> { + use LefKey::Layer; + self.write_line(format_args_f!("{Layer} {layer.layer_name} ;"))?; + self.indent += 1; + + for shape in layer.shapes.iter() { + self.write_via_shape(shape)?; + } + self.indent -= 1; + Ok(()) // No END token. + } + /// Writes a [`LefViaShape`]. + fn write_via_shape(&mut self, shape: &LefViaShape) -> LefResult<()> { + use LefKey::{Mask, Polygon, Rect}; + match shape { + LefViaShape::Rect(mask, p0, p1) => { + let mut line = format!("{Rect} "); + if let Some(mask) = mask { + line.push_str(&format!("{Mask} {mask} ")); + } + self.write_line(format_args_f!("{line}{p0} {p1} ; "))?; + } + LefViaShape::Polygon(mask, pts) => { + let mut line = format!("{Polygon} "); + if let Some(mask) = mask { + line.push_str(&format!("{Mask} {mask} ")); + } + let ptstr = pts + .iter() + .map(|x| x.to_string()) + .collect::>() + .join(" "); + self.write_line(format_args_f!("{line}{ptstr} ;"))?; + } + }; + Ok(()) + } fn write_geom(&mut self, geom: &LefGeometry) -> LefResult<()> { let mut wordlist: Vec = Vec::new(); match geom { - LefGeometry::Iterate { shape, pattern } => - wordlist.extend(self.format_geom(shape, Some(pattern))), - LefGeometry::Shape(ref shape) => - wordlist.extend(self.format_geom(shape, None)), + LefGeometry::Iterate { shape, pattern } => { + wordlist.extend(self.format_geom(shape, Some(pattern))) + } + LefGeometry::Shape(ref shape) => wordlist.extend(self.format_geom(shape, None)), }; let linestr = wordlist.join(" "); self.write_line(format_args_f!("{linestr} ;"))?; Ok(()) } - fn format_geom(&mut self, shape: &LefShape, step_pattern: Option<&LefStepPattern>) -> Vec { + fn format_geom( + &mut self, + shape: &LefShape, + step_pattern: Option<&LefStepPattern>, + ) -> Vec { let mut wordlist: Vec = Vec::new(); match shape { LefShape::Rect(mask, p0, p1) => { @@ -459,10 +577,10 @@ impl<'wr> LefWriter<'wr> { wordlist.push(LefKey::Step.to_string()); wordlist.push(pattern.spacex.to_string()); wordlist.push(pattern.spacey.to_string()); - }, - _ => {}, + } + _ => {} } - + wordlist } /// Format mask @@ -493,7 +611,9 @@ impl<'wr> LefWriter<'wr> { for layer_geom_set in dens_geoms.iter() { self.write_line(format_args_f!("{Layer} {layer_geom_set.layer_name} ; "))?; for dens_rect in layer_geom_set.geometries.iter() { - self.write_line(format_args_f!("{Rect} {dens_rect.pt1} {dens_rect.pt2} {dens_rect.density_value} ; "))?; + self.write_line(format_args_f!( + "{Rect} {dens_rect.pt1} {dens_rect.pt2} {dens_rect.density_value} ; " + ))?; } } self.indent -= 1;