From e9e25d7ee452de85e652e9a58ece32b5ac2d5c21 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sun, 9 Feb 2025 13:26:42 -0800 Subject: [PATCH 1/8] support parsing fixed via definitions --- lef21/src/bin/lefrw.rs | 16 ++--- lef21/src/data.rs | 89 +++++++++++++++++++++++--- lef21/src/read.rs | 139 +++++++++++++++++++++++++++++++++++++++-- lef21/src/tests.rs | 90 ++++++++++++++++++++++++++ lef21/src/write.rs | 92 ++++++++++++++++++++++++--- 5 files changed, 399 insertions(+), 27 deletions(-) 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 7073f94..2006543 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 @@ -78,10 +82,6 @@ pub struct LefLibrary { pub units: 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 (Unsupported) #[serde(default, skip_serializing)] #[builder(default)] @@ -202,7 +202,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)] @@ -302,7 +302,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))] @@ -435,6 +435,78 @@ 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. +/// +/// Generated vias are currently unsupported. +#[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(Unsupported), +} +/// # 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 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)] @@ -686,7 +758,7 @@ enumstr!( SupplySensitivity: "SUPPLYSENSITIVITY", GroundSensitivity: "GROUNDSENSITIVITY", MustJoin: "MUSTJOIN", - + // UNITS Fields Units: "UNITS", Time: "TIME", @@ -718,6 +790,9 @@ enumstr!( AntennaMaxSideAreaCar: "ANTENNAMAXSIDEAREACAR", AntennaMaxCutCar: "ANTENNAMAXCUTCAR", + // VIA Fields + Default: "DEFAULT", + // Unsupported Property: "PROPERTY", ManufacturingGrid: "MANUFACTURINGGRID", diff --git a/lef21/src/read.rs b/lef21/src/read.rs index 9964ed0..002ccbf 100644 --- a/lef21/src/read.rs +++ b/lef21/src/read.rs @@ -306,6 +306,7 @@ pub enum LefParseContext { Site, Units, Density, + Via, Unknown, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -466,6 +467,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(); loop { if self.peek_token().is_none() && self.session.lef_version >= *V5P6 { break; // End of input (without END LIBRARY), which is valid for lef 5.6+ @@ -514,6 +516,10 @@ impl<'src> LefParser<'src> { self.expect(TokenType::SemiColon)?; lib.use_min_spacing(e) } + LefKey::Via => { + vias.push(self.parse_via()?); + lib + } LefKey::BeginExtension | LefKey::ManufacturingGrid | LefKey::ClearanceMeasure @@ -527,6 +533,7 @@ impl<'src> LefParser<'src> { } lib = lib.macros(macros); lib = lib.sites(sites); + lib = lib.vias(vias); self.ctx.pop(); Ok(lib.build()?) } @@ -582,7 +589,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()?); @@ -624,9 +631,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; @@ -822,7 +827,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)?, @@ -925,6 +934,80 @@ 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)?; + + // Now parse the layer-geom body. + // + // LayerGeometries don't have an END card, so this needs to peek at the next token, + // and exit when another LAYER or END (of a higher-level thing) turn up. + // Note that on end-of-file, i.e. `peek_token` returning `None`, this will exit and return a valid [LefLayerGeometries]. + // (Objects above it in the tree may error instead.) + 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 a [LefGeometry] statement /// Each can be a shape or iteration thereof fn parse_geometry(&mut self) -> LefResult { @@ -1156,6 +1239,52 @@ impl<'src> LefParser<'src> { self.expect(TokenType::SemiColon)?; Ok((x, y)) } + /// Parse a Lef VIA definition + /// + /// This parser currently only supports fixed vias. + 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.fail(LefParseErrorType::Unsupported)?; + } + + let mut data = LefFixedViaDefBuilder::default(); + + if let LefKey::Resistance = self.peek_key()? { + self.advance()?; // Consume the DEFAULT + data = data.resistance_ohms(self.parse_number()?); + } + + 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 Macro. Eat the END key + } + _ => self.fail(LefParseErrorType::InvalidKey)?, + } + // Parse the END-enclosing macro-name + self.expect_ident(&name)?; + // Set the pins, build our struct and return it + 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)?; diff --git a/lef21/src/tests.rs b/lef21/src/tests.rs index 214b464..49a1cef 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}; @@ -137,6 +139,94 @@ 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 + "#; + let lib = parse_str(src)?; + assert_eq!(lib.vias.len(), 2); + 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, + } + ); + 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 baaaac7..931e308 100644 --- a/lef21/src/write.rs +++ b/lef21/src/write.rs @@ -110,6 +110,9 @@ impl<'wr> LefWriter<'wr> { // VIAS would be written here // if let Some(ref v) = lib.vias { } + for via in lib.vias.iter() { + self.write_via(via)?; + } // Write each site definition for site in lib.sites.iter() { @@ -127,6 +130,38 @@ impl<'wr> LefWriter<'wr> { self.dest.flush()?; Ok(()) } + /// Write a [LefViaDef], in recommended order of fields. + fn write_via(&mut self, via: &LefViaDef) -> LefResult<()> { + use LefKey::{Default, End, Resistance, Via}; + + 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)?; + } + } + _ => { + return Err(LefError::Str("Generated vias are unsupported".into())); + } + } + + // PROPERTIES would go here + // if via.properties.is_some() { } + + self.indent -= 1; + self.write_line(format_args_f!("{End} {} ", via.name))?; + Ok(()) + } /// Write a [LefSite] definition fn write_site(&mut self, site: &LefSite) -> LefResult<()> { use LefKey::{By, Class, End, Site, Size}; @@ -152,7 +187,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 { @@ -178,7 +213,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} ;"))?; } @@ -215,8 +250,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 { @@ -254,7 +291,7 @@ impl<'wr> LefWriter<'wr> { if let Some(ref v) = pin.net_expr { self.write_line(format_args_f!("{NetExpr} {v} ; "))?; } - + // Most unsupported PINS features *would* go here. // if pin.properties.is_some() { // return Err(LefError::Str("Unsupported LefPin Attr".into())); @@ -311,8 +348,47 @@ impl<'wr> LefWriter<'wr> { self.indent -= 1; Ok(()) // Note [LefLayerGeometries] have no "END" or other closing delimeter. } + /// Write the [LefLayerGeometries], common to both ports and obstructions + 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(()) // Note [LefLayerGeometries] have no "END" or other closing delimeter. + } + fn write_via_shape(&mut self, shape: &LefViaShape) -> LefResult<()> { + use LefKey::{Polygon, Rect}; + match shape { + LefViaShape::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} ; "))?; + } + LefViaShape::Polygon(mask, pts) => { + let mut line = format!("{Polygon} "); + match mask { + Some(mask) => line.push_str(&format!("MASK {mask} ")), + None => (), + }; + 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<()> { - use LefKey::{Polygon, Rect, Path}; + use LefKey::{Path, Polygon, Rect}; match geom { LefGeometry::Iterate { .. } => unimplemented!(), LefGeometry::Shape(ref shape) => match shape { @@ -364,7 +440,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; From 2ab280f729c30d5042bcab895fdfd99c7c219369 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sun, 9 Feb 2025 13:32:34 -0800 Subject: [PATCH 2/8] update comments --- lef21/src/read.rs | 7 ------- lef21/src/write.rs | 7 ++++--- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/lef21/src/read.rs b/lef21/src/read.rs index 002ccbf..13bde42 100644 --- a/lef21/src/read.rs +++ b/lef21/src/read.rs @@ -942,12 +942,6 @@ impl<'src> LefParser<'src> { layer = layer.layer_name(self.parse_ident()?); // Parse the layer-name self.expect(TokenType::SemiColon)?; - // Now parse the layer-geom body. - // - // LayerGeometries don't have an END card, so this needs to peek at the next token, - // and exit when another LAYER or END (of a higher-level thing) turn up. - // Note that on end-of-file, i.e. `peek_token` returning `None`, this will exit and return a valid [LefLayerGeometries]. - // (Objects above it in the tree may error instead.) let mut shapes: Vec = Vec::new(); loop { if self.peek_token().is_none() { @@ -1281,7 +1275,6 @@ impl<'src> LefParser<'src> { } // Parse the END-enclosing macro-name self.expect_ident(&name)?; - // Set the pins, build our struct and return it self.ctx.pop(); Ok(via.build()?) } diff --git a/lef21/src/write.rs b/lef21/src/write.rs index 931e308..692ed27 100644 --- a/lef21/src/write.rs +++ b/lef21/src/write.rs @@ -130,7 +130,7 @@ impl<'wr> LefWriter<'wr> { self.dest.flush()?; Ok(()) } - /// Write a [LefViaDef], in recommended order of fields. + /// Write a [LefViaDef]. fn write_via(&mut self, via: &LefViaDef) -> LefResult<()> { use LefKey::{Default, End, Resistance, Via}; @@ -348,7 +348,7 @@ impl<'wr> LefWriter<'wr> { self.indent -= 1; Ok(()) // Note [LefLayerGeometries] have no "END" or other closing delimeter. } - /// Write the [LefLayerGeometries], common to both ports and obstructions + /// 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} ;"))?; @@ -358,8 +358,9 @@ impl<'wr> LefWriter<'wr> { self.write_via_shape(shape)?; } self.indent -= 1; - Ok(()) // Note [LefLayerGeometries] have no "END" or other closing delimeter. + Ok(()) // No END token. } + /// Writes a [`LefViaShape`]. fn write_via_shape(&mut self, shape: &LefViaShape) -> LefResult<()> { use LefKey::{Polygon, Rect}; match shape { From aa3d08280e2092ac529025d79bf146558a1b94da Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sun, 9 Feb 2025 18:18:00 -0800 Subject: [PATCH 3/8] support viarule/generated vias --- lef21/resources/lef21.schema.json | 362 +++++++++++++++++++++++++++++- lef21/src/data.rs | 79 ++++++- lef21/src/read.rs | 91 ++++++-- lef21/src/tests.rs | 40 +++- 4 files changed, 544 insertions(+), 28 deletions(-) diff --git a/lef21/resources/lef21.schema.json b/lef21/resources/lef21.schema.json index 983e52c..43907b8 100644 --- a/lef21/resources/lef21.schema.json +++ b/lef21/resources/lef21.schema.json @@ -210,16 +210,11 @@ ] }, "vias": { - "description": "Via Definitions (Unsupported)", - "writeOnly": true, - "anyOf": [ - { - "$ref": "#/definitions/Unsupported" - }, - { - "type": "null" - } - ] + "description": "Via Definitions", + "type": "array", + "items": { + "$ref": "#/definitions/LefViaDef" + } } }, "definitions": { @@ -459,6 +454,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.", @@ -495,6 +511,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", @@ -907,6 +1043,34 @@ } } }, + "LefOffset": { + "title": "Lef Offset", + "type": "object", + "required": [ + "x_bot", + "x_top", + "y_bot", + "y_top" + ], + "properties": { + "x_bot": { + "type": "string", + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" + }, + "x_top": { + "type": "string", + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" + }, + "y_bot": { + "type": "string", + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" + }, + "y_top": { + "type": "string", + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" + } + } + }, "LefOnOff": { "description": "Binary On/Off Settings, Denoted by `ON` and `OFF`", "oneOf": [ @@ -1348,6 +1512,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.", @@ -1613,6 +1795,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/data.rs b/lef21/src/data.rs index 2006543..54d9c80 100644 --- a/lef21/src/data.rs +++ b/lef21/src/data.rs @@ -440,8 +440,6 @@ pub struct LefVia { /// 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. -/// -/// Generated vias are currently unsupported. #[derive(Builder, Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq)] #[builder(pattern = "owned", setter(into))] pub struct LefViaDef { @@ -466,7 +464,7 @@ pub struct LefViaDef { #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq)] pub enum LefViaDefData { Fixed(LefFixedViaDef), - Generated(Unsupported), + Generated(LefGeneratedViaDef), } /// # Lef Fixed Via Definition #[derive(Builder, Clone, Debug, Default, Deserialize, Serialize, JsonSchema, PartialEq, Eq)] @@ -483,6 +481,72 @@ pub struct LefFixedViaDef { #[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 x_bot: LefDecimal, + pub y_bot: LefDecimal, + pub x_top: LefDecimal, + pub y_top: LefDecimal, +} /// # Lef Single-Layer Geometry Store for Vias /// /// [LefViaLayerGeometries] stores the combination of a layer (name) @@ -792,6 +856,14 @@ enumstr!( // VIA Fields Default: "DEFAULT", + ViaRule: "VIARULE", + CutSize: "CUTSIZE", + Layers: "LAYERS", + CutSpacing: "CUTSPACING", + Enclosure: "ENCLOSURE", + RowCol: "ROWCOL", + Offset: "OFFSET", + Pattern: "PATTERN", // Unsupported Property: "PROPERTY", @@ -799,7 +871,6 @@ enumstr!( ClearanceMeasure: "CLEARANCEMEASURE", PropertyDefinitions: "PROPERTYDEFINITIONS", MaxViaStack: "MAXVIASTACK", - ViaRule: "VIARULE", Generate: "GENERATE", NonDefaultRule: "NONDEFAULTRULE", } diff --git a/lef21/src/read.rs b/lef21/src/read.rs index 13bde42..7c2ece8 100644 --- a/lef21/src/read.rs +++ b/lef21/src/read.rs @@ -1249,22 +1249,87 @@ impl<'src> LefParser<'src> { } if let LefKey::ViaRule = self.peek_key()? { - self.fail(LefParseErrorType::Unsupported)?; - } - - let mut data = LefFixedViaDefBuilder::default(); + 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 { + x_bot: self.parse_number()?, + y_bot: self.parse_number()?, + x_top: self.parse_number()?, + y_top: 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()?; // Consume the DEFAULT - data = data.resistance_ohms(self.parse_number()?); - } + if let LefKey::Resistance = self.peek_key()? { + self.advance()?; // Eat the RESISTANCE key + data = data.resistance_ohms(self.parse_number()?); + } - let mut layers = Vec::new(); - while let LefKey::Layer = self.peek_key()? { - layers.push(self.parse_via_layer_geometries()?); + 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()?)); } - data = data.layers(layers); - via = via.data(LefViaDefData::Fixed(data.build()?)); match self.peek_key()? { LefKey::Property => self.fail(LefParseErrorType::Unsupported)?, diff --git a/lef21/src/tests.rs b/lef21/src/tests.rs index 49a1cef..de815ad 100644 --- a/lef21/src/tests.rs +++ b/lef21/src/tests.rs @@ -163,9 +163,17 @@ fn it_parses_via_lib() -> LefResult<()> { 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 "#; let lib = parse_str(src)?; - assert_eq!(lib.vias.len(), 2); + assert_eq!(lib.vias.len(), 3); let via2 = &lib.vias[1]; assert_eq!( *via2, @@ -224,6 +232,36 @@ fn it_parses_via_lib() -> LefResult<()> { 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(()) } From 20e457f28c99b6f2a7c9f35f9bd5f471ae261a79 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sun, 9 Feb 2025 18:32:12 -0800 Subject: [PATCH 4/8] support writing generated vias --- lef21/resources/lef21.schema.json | 16 +++++++-------- lef21/src/data.rs | 8 ++++---- lef21/src/read.rs | 8 ++++---- lef21/src/write.rs | 34 +++++++++++++++++++++++++++---- 4 files changed, 46 insertions(+), 20 deletions(-) diff --git a/lef21/resources/lef21.schema.json b/lef21/resources/lef21.schema.json index 43907b8..5f710ad 100644 --- a/lef21/resources/lef21.schema.json +++ b/lef21/resources/lef21.schema.json @@ -1047,25 +1047,25 @@ "title": "Lef Offset", "type": "object", "required": [ - "x_bot", - "x_top", - "y_bot", - "y_top" + "bot_x", + "bot_y", + "top_x", + "top_y" ], "properties": { - "x_bot": { + "bot_x": { "type": "string", "pattern": "^-?[0-9]+(\\.[0-9]+)?$" }, - "x_top": { + "bot_y": { "type": "string", "pattern": "^-?[0-9]+(\\.[0-9]+)?$" }, - "y_bot": { + "top_x": { "type": "string", "pattern": "^-?[0-9]+(\\.[0-9]+)?$" }, - "y_top": { + "top_y": { "type": "string", "pattern": "^-?[0-9]+(\\.[0-9]+)?$" } diff --git a/lef21/src/data.rs b/lef21/src/data.rs index 54d9c80..d8f8f1e 100644 --- a/lef21/src/data.rs +++ b/lef21/src/data.rs @@ -542,10 +542,10 @@ pub struct LefRowCol { #[derive(Builder, Clone, Debug, Default, Deserialize, Serialize, JsonSchema, PartialEq, Eq)] #[builder(pattern = "owned", setter(into))] pub struct LefOffset { - pub x_bot: LefDecimal, - pub y_bot: LefDecimal, - pub x_top: LefDecimal, - pub y_top: LefDecimal, + pub bot_x: LefDecimal, + pub bot_y: LefDecimal, + pub top_x: LefDecimal, + pub top_y: LefDecimal, } /// # Lef Single-Layer Geometry Store for Vias /// diff --git a/lef21/src/read.rs b/lef21/src/read.rs index 7c2ece8..7271a70 100644 --- a/lef21/src/read.rs +++ b/lef21/src/read.rs @@ -1298,10 +1298,10 @@ impl<'src> LefParser<'src> { LefKey::Offset => { self.advance()?; // Eat the OFFSET key data = data.offset(LefOffset { - x_bot: self.parse_number()?, - y_bot: self.parse_number()?, - x_top: self.parse_number()?, - y_top: self.parse_number()?, + bot_x: self.parse_number()?, + bot_y: self.parse_number()?, + top_x: self.parse_number()?, + top_y: self.parse_number()?, }); self.expect(TokenType::SemiColon)?; } diff --git a/lef21/src/write.rs b/lef21/src/write.rs index 692ed27..32ae618 100644 --- a/lef21/src/write.rs +++ b/lef21/src/write.rs @@ -132,7 +132,10 @@ impl<'wr> LefWriter<'wr> { } /// Write a [LefViaDef]. fn write_via(&mut self, via: &LefViaDef) -> LefResult<()> { - use LefKey::{Default, End, Resistance, Via}; + 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}"))?; @@ -150,13 +153,36 @@ impl<'wr> LefWriter<'wr> { self.write_via_layer_geom(layer)?; } } - _ => { - return Err(LefError::Str("Generated vias are unsupported".into())); + 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 - // if via.properties.is_some() { } self.indent -= 1; self.write_line(format_args_f!("{End} {} ", via.name))?; From 761632bbdca1e614b39aa417d94c4b2cde15f291 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Mon, 10 Feb 2025 13:13:17 -0800 Subject: [PATCH 5/8] consume the semicolon after RESISTANCE --- lef21/src/read.rs | 1 + lef21/src/tests.rs | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lef21/src/read.rs b/lef21/src/read.rs index 7271a70..5a2bc36 100644 --- a/lef21/src/read.rs +++ b/lef21/src/read.rs @@ -1321,6 +1321,7 @@ impl<'src> LefParser<'src> { 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(); diff --git a/lef21/src/tests.rs b/lef21/src/tests.rs index de815ad..73f1e4d 100644 --- a/lef21/src/tests.rs +++ b/lef21/src/tests.rs @@ -145,7 +145,7 @@ fn it_parses_via_lib() -> LefResult<()> { VERSION 5.8 ; UNITS DATABASE MICRONS 2000 ; END UNITS VIA via1 DEFAULT - RESISTANCE 21.0 + RESISTANCE 21.0 ; LAYER met1 ; RECT MASK 0 -0.5 -0.5 0.5 0.5 ; LAYER cut1 ; @@ -171,9 +171,19 @@ fn it_parses_via_lib() -> LefResult<()> { 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(), 3); + assert_eq!(lib.vias.len(), 4); let via2 = &lib.vias[1]; assert_eq!( *via2, From ec74c0598a5fd81a989058cb6730e72d32ac2f1d Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 12 Feb 2025 23:08:22 -0800 Subject: [PATCH 6/8] update comments --- lef21/src/read.rs | 6 ++---- lef21/src/write.rs | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/lef21/src/read.rs b/lef21/src/read.rs index 5a2bc36..4b0dc23 100644 --- a/lef21/src/read.rs +++ b/lef21/src/read.rs @@ -1234,8 +1234,6 @@ impl<'src> LefParser<'src> { Ok((x, y)) } /// Parse a Lef VIA definition - /// - /// This parser currently only supports fixed vias. fn parse_via(&mut self) -> LefResult { self.ctx.push(LefParseContext::Via); self.expect_key(LefKey::Via)?; @@ -1335,11 +1333,11 @@ impl<'src> LefParser<'src> { match self.peek_key()? { LefKey::Property => self.fail(LefParseErrorType::Unsupported)?, LefKey::End => { - self.advance()?; // End of Macro. Eat the END key + self.advance()?; // End of Via. Eat the END key } _ => self.fail(LefParseErrorType::InvalidKey)?, } - // Parse the END-enclosing macro-name + // Parse the END-enclosing via-name self.expect_ident(&name)?; self.ctx.pop(); Ok(via.build()?) diff --git a/lef21/src/write.rs b/lef21/src/write.rs index 32ae618..5330984 100644 --- a/lef21/src/write.rs +++ b/lef21/src/write.rs @@ -108,8 +108,7 @@ impl<'wr> LefWriter<'wr> { self.write_line(format_args_f!("{End} {Units} "))?; } - // VIAS would be written here - // if let Some(ref v) = lib.vias { } + // Write each via definition for via in lib.vias.iter() { self.write_via(via)?; } From c086bba265c7b9cfa9ce63e642515edfaa913248 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 13 Feb 2025 10:51:30 -0800 Subject: [PATCH 7/8] organize fields, edit comments --- lef21/resources/lef21.schema.json | 4 +--- lef21/src/data.rs | 10 ++++------ 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/lef21/resources/lef21.schema.json b/lef21/resources/lef21.schema.json index 25cd312..0bddc5d 100644 --- a/lef21/resources/lef21.schema.json +++ b/lef21/resources/lef21.schema.json @@ -4,7 +4,6 @@ "description": "LEF's primary design-content container, including a set of macro/cell definitions and associated metadata.", "type": "object", "required": [ - "extensions", "fixed_mask" ], "properties": { @@ -50,8 +49,7 @@ "minLength": 1 }, "extensions": { - "description": "Syntax Extensions (Unsupported)", - "writeOnly": true, + "description": "Syntax Extensions", "type": "array", "items": { "$ref": "#/definitions/LefExtension" diff --git a/lef21/src/data.rs b/lef21/src/data.rs index 4bdf4f2..f1d910d 100644 --- a/lef21/src/data.rs +++ b/lef21/src/data.rs @@ -80,22 +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" - /// Syntax Extensions (Unsupported) - #[serde(default, skip_serializing)] + /// 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")] From 2bc980285cf11b3a1cf237f1646559f304c50be9 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 13 Feb 2025 10:53:39 -0800 Subject: [PATCH 8/8] add TODO: VIARULE --- lef21/src/write.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/lef21/src/write.rs b/lef21/src/write.rs index 3837c98..be211fd 100644 --- a/lef21/src/write.rs +++ b/lef21/src/write.rs @@ -139,6 +139,7 @@ impl<'wr> LefWriter<'wr> { self.write_via(via)?; } + // TODO: VIARULE [GENERATE] // TODO: NONDEFAULTRULE // Write each SITE definition