From 289a3b8f5b998e55556633214fe2a74430b9a4f4 Mon Sep 17 00:00:00 2001 From: Ben Bowers Date: Fri, 5 Jul 2024 18:23:05 -0400 Subject: [PATCH 01/13] add lef-read-write bin/ remove semicolon after macro write --- lef21/src/bin/lefrw.rs | 36 ++++++++++++++++++++++++++++++++++++ lef21/src/write.rs | 2 +- 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 lef21/src/bin/lefrw.rs diff --git a/lef21/src/bin/lefrw.rs b/lef21/src/bin/lefrw.rs new file mode 100644 index 0000000..d03fe0f --- /dev/null +++ b/lef21/src/bin/lefrw.rs @@ -0,0 +1,36 @@ +use std::error::Error; +use std::env; +use std::process; +use lef21::LefLibrary; + +struct Config { + inlef: String, + outlef: String +} + +impl Config { + fn new(args: &[String]) -> Result { + if args.len() < 3 { + return Err("Not enough arguments, expecting 2.") + } + let inlef = args[1].clone(); + let outlef = args[2].clone(); + Ok(Config { inlef, outlef} ) + } +} + +//fn parse_config(args: + +fn run() -> Result<(), Box> { + let args : Vec = env::args().collect(); + let cfg = Config::new(&args)?; + let lib = LefLibrary::open(cfg.inlef)?; + lib.save(cfg.outlef)?; + Ok(()) +} +fn main() { + run().unwrap_or_else(|err| { + println!("Problem in lefrw: {}", err); + process::exit(1); + }); +} \ No newline at end of file diff --git a/lef21/src/write.rs b/lef21/src/write.rs index e68aabf..3c61aaf 100644 --- a/lef21/src/write.rs +++ b/lef21/src/write.rs @@ -142,7 +142,7 @@ impl<'wr> LefWriter<'wr> { /// Write a [LefMacro], in recommended order of fields. fn write_macro(&mut self, mac: &LefMacro) -> LefResult<()> { use LefKey::{By, End, Foreign, Macro, Obs, Origin, Site, Size, Source}; - self.write_line(format_args_f!("{Macro} {mac.name} ; "))?; + self.write_line(format_args_f!("{Macro} {mac.name}"))?; self.indent += 1; if let Some(ref v) = mac.class { From d958c1236f5443c1c161dc23cfc9e918724630e3 Mon Sep 17 00:00:00 2001 From: Ben Bowers Date: Fri, 5 Jul 2024 21:21:58 -0400 Subject: [PATCH 02/13] add support for optional END LIBRARY for lef5.6 or higher --- lef21/src/data.rs | 1 + lef21/src/read.rs | 3 +++ lef21/src/tests.rs | 22 ++++++++++++++++++++++ 3 files changed, 26 insertions(+) diff --git a/lef21/src/data.rs b/lef21/src/data.rs index 56ed789..1737ca6 100644 --- a/lef21/src/data.rs +++ b/lef21/src/data.rs @@ -31,6 +31,7 @@ pub type LefDecimal = rust_decimal::Decimal; // Note [`once_cell`](https://docs.rs/once_cell/1.8.0/once_cell/#lazy-initialized-global-data) // demands these be `static`, not `const`, for reasons outside our grasp. pub(crate) static V5P4: Lazy = Lazy::new(|| LefDecimal::from_str("5.4").unwrap()); +pub(crate) static V5P6: Lazy = Lazy::new(|| LefDecimal::from_str("5.6").unwrap()); pub(crate) static V5P8: Lazy = Lazy::new(|| LefDecimal::from_str("5.8").unwrap()); /// # Lef Library diff --git a/lef21/src/read.rs b/lef21/src/read.rs index 385112a..0b7e18c 100644 --- a/lef21/src/read.rs +++ b/lef21/src/read.rs @@ -466,6 +466,9 @@ impl<'src> LefParser<'src> { let mut macros = Vec::new(); let mut sites = 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+ + } lib = match self.peek_key()? { LefKey::Macro => { macros.push(self.parse_macro()?); diff --git a/lef21/src/tests.rs b/lef21/src/tests.rs index 7e010d6..99cd163 100644 --- a/lef21/src/tests.rs +++ b/lef21/src/tests.rs @@ -95,6 +95,28 @@ fn it_parses_lib2() -> LefResult<()> { Ok(()) } +#[test] +fn it_parses_no_end_library_5p6() -> LefResult<()> { + let src = r#" + VERSION 5.6 ; + UNITS DATABASE MICRONS 2000 ; END UNITS + MACRO macro_name SIZE 2 BY 3 ; END macro_name + "#; + parse_str(src)?; + Ok(()) +} + +#[test] +fn it_errors_no_end_library_5p5() -> LefResult<()> { + let src = r#" + VERSION 5.5 ; + UNITS DATABASE MICRONS 2000 ; END UNITS + MACRO macro_name SIZE 2 BY 3 ; END macro_name + "#; + assert!(parse_str(src).is_err()); + Ok(()) +} + #[test] fn empty_lib_to_yaml() { Yaml.save(&LefLibrary::new(), &resource("empty_lib.lef.yaml")).unwrap(); From 99cc71b5fce22e878dac27b190abe8e8b1dcfd5d Mon Sep 17 00:00:00 2001 From: Ben Bowers Date: Fri, 5 Jul 2024 21:29:51 -0400 Subject: [PATCH 03/13] add write support for PATH --- lef21/src/write.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lef21/src/write.rs b/lef21/src/write.rs index 3c61aaf..86d2fff 100644 --- a/lef21/src/write.rs +++ b/lef21/src/write.rs @@ -290,7 +290,7 @@ impl<'wr> LefWriter<'wr> { Ok(()) // Note [LefLayerGeometries] have no "END" or other closing delimeter. } fn write_geom(&mut self, geom: &LefGeometry) -> LefResult<()> { - use LefKey::{Polygon, Rect}; + use LefKey::{Polygon, Rect, Path}; match geom { LefGeometry::Iterate { .. } => unimplemented!(), LefGeometry::Shape(ref shape) => match shape { @@ -305,8 +305,13 @@ impl<'wr> LefWriter<'wr> { .join(" "); self.write_line(format_args_f!("{Polygon} {ptstr} ;"))?; } - LefShape::Path(_) => { - self.fail(&format_f!("Unsupported Write: LefShape::Path"))?; + LefShape::Path(pts) => { + let ptstr = pts + .iter() + .map(|x| x.to_string()) + .collect::>() + .join(" "); + self.write_line(format_args_f!("{Path} {ptstr} ;"))?; } }, }; From 05cd61da9a7959cadd017fa6f7ab582928e6969f Mon Sep 17 00:00:00 2001 From: Ben Bowers Date: Fri, 5 Jul 2024 21:41:29 -0400 Subject: [PATCH 04/13] fixed parsing semicolon after ANTENNAMODEL --- lef21/src/read.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lef21/src/read.rs b/lef21/src/read.rs index 0b7e18c..f371df7 100644 --- a/lef21/src/read.rs +++ b/lef21/src/read.rs @@ -654,7 +654,9 @@ impl<'src> LefParser<'src> { } LefKey::AntennaModel => { self.advance()?; - pin.antenna_model(self.parse_enum::()?) + let e = self.parse_enum::()?; + self.expect(TokenType::SemiColon)?; + pin.antenna_model(e) } LefKey::AntennaDiffArea | LefKey::AntennaGateArea From c46d8353f8d9623f8c258ee6e716eda6c25e1547 Mon Sep 17 00:00:00 2001 From: Ben Bowers Date: Fri, 5 Jul 2024 21:59:16 -0400 Subject: [PATCH 05/13] add support for FIXEDMASK in MACRO --- lef21/src/data.rs | 3 ++- lef21/src/read.rs | 5 +++++ lef21/src/write.rs | 7 ++++--- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lef21/src/data.rs b/lef21/src/data.rs index 1737ca6..9aa5498 100644 --- a/lef21/src/data.rs +++ b/lef21/src/data.rs @@ -202,7 +202,7 @@ pub struct LefMacro { /// Fixed Mask Option (Unsupported) #[serde(default, skip_serializing)] #[builder(default)] - pub fixed_mask: Option, + pub fixed_mask: bool, //Option, /// Electrically-Equivalent Cell (Unsupported) #[serde(default, skip_serializing)] #[builder(default)] @@ -611,6 +611,7 @@ enumstr!( DesignRuleWidth: "DESIGNRULEWIDTH", Spacing: "SPACING", Bump: "BUMP", + FixedMask: "FIXEDMASK", // UNITS Fields Units: "UNITS", diff --git a/lef21/src/read.rs b/lef21/src/read.rs index f371df7..087f4d3 100644 --- a/lef21/src/read.rs +++ b/lef21/src/read.rs @@ -561,6 +561,11 @@ impl<'src> LefParser<'src> { self.expect(TokenType::SemiColon)?; mac.site(id) } + LefKey::FixedMask => { + self.advance()?; // Eat the FIXEDMASK key + self.expect(TokenType::SemiColon)?; + mac.fixed_mask(true) + } LefKey::Foreign => { self.advance()?; // Eat the FOREIGN key let cell_name = self.parse_ident()?; diff --git a/lef21/src/write.rs b/lef21/src/write.rs index 86d2fff..257b1c8 100644 --- a/lef21/src/write.rs +++ b/lef21/src/write.rs @@ -141,15 +141,16 @@ impl<'wr> LefWriter<'wr> { } /// Write a [LefMacro], in recommended order of fields. fn write_macro(&mut self, mac: &LefMacro) -> LefResult<()> { - use LefKey::{By, End, Foreign, Macro, Obs, Origin, Site, Size, Source}; + use LefKey::{By, End, FixedMask, Foreign, Macro, Obs, Origin, Site, Size, Source}; self.write_line(format_args_f!("{Macro} {mac.name}"))?; self.indent += 1; if let Some(ref v) = mac.class { self.write_macro_class(v)?; } - // FIXEDMASK would be written here - // if mac.fixed_mask.is_some() { } + if mac.fixed_mask { + self.write_line(format_args_f!("{FixedMask} ;"))?; + } if let Some(ref v) = mac.foreign { let pt = match v.pt { Some(ref p) => p.to_string(), From 35c5e4bd73085932a6676d5e5911f7949536229d Mon Sep 17 00:00:00 2001 From: Ben Bowers Date: Sat, 6 Jul 2024 08:07:17 -0400 Subject: [PATCH 06/13] add support for ORIENT on FOREIGN --- lef21/src/data.rs | 22 ++++++++++++++++------ lef21/src/read.rs | 9 +++++---- lef21/src/write.rs | 6 +++++- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/lef21/src/data.rs b/lef21/src/data.rs index 9aa5498..f45fcfd 100644 --- a/lef21/src/data.rs +++ b/lef21/src/data.rs @@ -240,18 +240,14 @@ pub enum LefMacroClass { /// /// 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. -/// The optional `ORIENT` feature is not supported. #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq)] pub struct LefForeign { /// Foreign Cell Name pub cell_name: String, /// Location pub pt: Option, - - // Unsupported Fields - /// Orientation (Unsupported) - #[serde(default, skip_serializing)] - pub orient: Option, + /// Orientation + pub orient: Option, } /// # Lef Pin Definition /// @@ -695,6 +691,20 @@ enumstr!( R90: "R90" } ); +enumstr!( + /// Specifies orientation for FOREIGN statement + LefOrient { + N: "N", + S: "S", + E: "E", + W: "W", + FN: "FN", + FS: "FS", + FE: "FE", + FW: "FW" + } +); + enumstr!( /// # Lef Pin-Usage /// diff --git a/lef21/src/read.rs b/lef21/src/read.rs index 087f4d3..1deb69c 100644 --- a/lef21/src/read.rs +++ b/lef21/src/read.rs @@ -569,19 +569,20 @@ 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()?); } - // The optional `ORIENT` field is not supported - if self.matches(TokenType::Name) { - self.fail(LefParseErrorType::Unsupported)?; + let mut orient = None; + if !self.matches(TokenType::SemiColon) { + orient = Some(self.parse_enum::()?); } self.expect(TokenType::SemiColon)?; mac.foreign(LefForeign { cell_name, pt, - orient: None, + orient: orient, }) } LefKey::Origin => { diff --git a/lef21/src/write.rs b/lef21/src/write.rs index 257b1c8..760a5b8 100644 --- a/lef21/src/write.rs +++ b/lef21/src/write.rs @@ -156,7 +156,11 @@ impl<'wr> LefWriter<'wr> { Some(ref p) => p.to_string(), None => "".into(), }; - self.write_line(format_args_f!("{Foreign} {v.cell_name} {pt} ;"))?; + let orient = match v.orient { + Some(ref o) => o.to_string(), + None => "".into(), + }; + self.write_line(format_args_f!("{Foreign} {v.cell_name} {pt} {orient} ;"))?; } if let Some(ref v) = mac.origin { self.write_line(format_args_f!("{Origin} {v} ;"))?; From 77e9d653483bb37a93217ee9ebcbc10d5e532c56 Mon Sep 17 00:00:00 2001 From: Ben Bowers Date: Sat, 6 Jul 2024 09:56:39 -0400 Subject: [PATCH 07/13] add support for EEQ in MACRO --- lef21/src/data.rs | 18 ++++++++++-------- lef21/src/read.rs | 6 ++++++ lef21/src/write.rs | 8 +++++--- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/lef21/src/data.rs b/lef21/src/data.rs index f45fcfd..e3a1865 100644 --- a/lef21/src/data.rs +++ b/lef21/src/data.rs @@ -198,15 +198,16 @@ pub struct LefMacro { #[builder(default, setter(strip_option))] pub source: Option, - // Unsupported - /// Fixed Mask Option (Unsupported) - #[serde(default, skip_serializing)] - #[builder(default)] - pub fixed_mask: bool, //Option, - /// Electrically-Equivalent Cell (Unsupported) + /// Electrically-Equivalent Cell + #[serde(default, skip_serializing_if = "Option::is_none")] + #[builder(default, setter(strip_option))] + pub eeq: Option, + #[serde(default, skip_serializing)] #[builder(default)] - pub eeq: Option, + pub fixed_mask: bool, + + // Unsupported /// Density Objects (Unsupported) #[serde(default, skip_serializing)] #[builder(default)] @@ -607,8 +608,9 @@ enumstr!( DesignRuleWidth: "DESIGNRULEWIDTH", Spacing: "SPACING", Bump: "BUMP", + Eeq: "EEQ", FixedMask: "FIXEDMASK", - + // UNITS Fields Units: "UNITS", Time: "TIME", diff --git a/lef21/src/read.rs b/lef21/src/read.rs index 1deb69c..7497770 100644 --- a/lef21/src/read.rs +++ b/lef21/src/read.rs @@ -561,6 +561,12 @@ impl<'src> LefParser<'src> { self.expect(TokenType::SemiColon)?; mac.site(id) } + LefKey::Eeq => { + self.advance()?; // Eat the EEQ key + let cell_name = self.parse_ident()?; + self.expect(TokenType::SemiColon)?; + mac.eeq(cell_name) + } LefKey::FixedMask => { self.advance()?; // Eat the FIXEDMASK key self.expect(TokenType::SemiColon)?; diff --git a/lef21/src/write.rs b/lef21/src/write.rs index 760a5b8..94f498b 100644 --- a/lef21/src/write.rs +++ b/lef21/src/write.rs @@ -141,7 +141,7 @@ impl<'wr> LefWriter<'wr> { } /// Write a [LefMacro], in recommended order of fields. fn write_macro(&mut self, mac: &LefMacro) -> LefResult<()> { - use LefKey::{By, End, FixedMask, Foreign, Macro, Obs, Origin, Site, Size, Source}; + use LefKey::{By, Eeq, End, FixedMask, Foreign, Macro, Obs, Origin, Site, Size, Source}; self.write_line(format_args_f!("{Macro} {mac.name}"))?; self.indent += 1; @@ -174,8 +174,10 @@ impl<'wr> LefWriter<'wr> { } self.write_line(format_args_f!("{Source} {v} ;"))?; } - // EEQ would be written here - // if mac.eeq.is_some() { } + + if let Some(ref cell) = mac.eeq { + self.write_line(format_args_f!("{Eeq} {cell} ;"))?; + } if let Some(ref v) = mac.size { self.write_line(format_args_f!("{Size} {v.0} {By} {v.1} ;"))?; } From d4fff024a55cbb59f3343646f1c23d1c27706ff1 Mon Sep 17 00:00:00 2001 From: Ben Bowers Date: Sat, 6 Jul 2024 14:19:02 -0400 Subject: [PATCH 08/13] adding support for TAPERRULE, MUSTJOIN, GROUNDSENSITIVITY and SUPPLYSENSITIVITY --- lef21/src/data.rs | 33 +++++++++++++++++---------------- lef21/src/read.rs | 30 +++++++++++++++++++++++++----- lef21/src/write.rs | 15 +++++++++++++-- 3 files changed, 55 insertions(+), 23 deletions(-) diff --git a/lef21/src/data.rs b/lef21/src/data.rs index e3a1865..3b15fab 100644 --- a/lef21/src/data.rs +++ b/lef21/src/data.rs @@ -284,27 +284,28 @@ pub struct LefPin { #[serde(default, skip_serializing_if = "Vec::is_empty")] pub antenna_attrs: Vec, + /// Taper Rule + #[serde(default, skip_serializing_if = "Option::is_none")] + #[builder(default, setter(strip_option))] + pub taper_rule: Option, + /// Supply Sensitivity + #[serde(default, skip_serializing_if = "Option::is_none")] + #[builder(default, setter(strip_option))] + pub supply_sensitivity: Option, + /// Ground Sensitivity + #[serde(default, skip_serializing_if = "Option::is_none")] + #[builder(default, setter(strip_option))] + pub ground_sensitivity: Option, + /// Must-Join + #[serde(default, skip_serializing_if = "Option::is_none")] + #[builder(default, setter(strip_option))] + pub must_join: Option, + // Unsupported - /// Taper Rule (Unsupported) - #[serde(default, skip_serializing)] - #[builder(default)] - pub taper_rule: Option, /// Net Expression (Unsupported) #[serde(default, skip_serializing)] #[builder(default)] pub net_expr: Option, - /// Supply Sensitivity (Unsupported) - #[serde(default, skip_serializing)] - #[builder(default)] - pub supply_sensitivity: Option, - /// Ground Sensitivity (Unsupported) - #[serde(default, skip_serializing)] - #[builder(default)] - pub ground_sensitivity: Option, - /// Must-Join (Unsupported) - #[serde(default, skip_serializing)] - #[builder(default)] - pub must_join: Option, /// Properties (Unsupported) #[serde(default, skip_serializing)] #[builder(default)] diff --git a/lef21/src/read.rs b/lef21/src/read.rs index 7497770..e14030e 100644 --- a/lef21/src/read.rs +++ b/lef21/src/read.rs @@ -691,11 +691,31 @@ impl<'src> LefParser<'src> { antenna_attrs.push(LefPinAntennaAttr { key, val, layer }); pin } - LefKey::TaperRule - | LefKey::NetExpr - | LefKey::SupplySensitivity - | LefKey::GroundSensitivity - | LefKey::MustJoin + LefKey::TaperRule => { + self.advance()?; + let value = self.parse_ident()?; + self.expect(TokenType::SemiColon)?; + pin.taper_rule(value) + } + LefKey::MustJoin => { + self.advance()?; + let value = self.parse_ident()?; + self.expect(TokenType::SemiColon)?; + pin.must_join(value) + } + LefKey::SupplySensitivity => { + self.advance()?; + let value = self.parse_ident()?; + self.expect(TokenType::SemiColon)?; + pin.supply_sensitivity(value) + } + LefKey::GroundSensitivity => { + self.advance()?; + let value = self.parse_ident()?; + self.expect(TokenType::SemiColon)?; + pin.ground_sensitivity(value) + } + LefKey::NetExpr | LefKey::Property => self.fail(LefParseErrorType::Unsupported)?, _ => self.fail(LefParseErrorType::InvalidKey)?, } diff --git a/lef21/src/write.rs b/lef21/src/write.rs index 94f498b..4ab1449 100644 --- a/lef21/src/write.rs +++ b/lef21/src/write.rs @@ -210,7 +210,7 @@ impl<'wr> LefWriter<'wr> { } /// Write a [LefPin] definition fn write_pin(&mut self, pin: &LefPin) -> LefResult<()> { - use LefKey::{AntennaModel, Direction, End, Layer, Pin, Shape, Use}; + use LefKey::{AntennaModel, Direction, End, GroundSensitivity, Layer, MustJoin, Pin, Shape, SupplySensitivity, TaperRule, Use}; self.write_line(format_args_f!("{Pin} {pin.name} "))?; self.indent += 1; if let Some(ref v) = pin.direction { @@ -233,7 +233,18 @@ impl<'wr> LefWriter<'wr> { }; self.write_line(format_args_f!("{attr.key} {attr.val} {layer} ;"))?; } - + if let Some(ref v) = pin.taper_rule { + self.write_line(format_args_f!("{TaperRule} {v} ; "))?; + } + if let Some(ref v) = pin.supply_sensitivity { + self.write_line(format_args_f!("{SupplySensitivity} {v} ; "))?; + } + if let Some(ref v) = pin.ground_sensitivity { + self.write_line(format_args_f!("{GroundSensitivity} {v} ; "))?; + } + if let Some(ref v) = pin.must_join { + self.write_line(format_args_f!("{MustJoin} {v} ; "))?; + } // Most unsupported PINS features *would* go here. // if pin.taper_rule.is_some() // || pin.net_expr.is_some() From 07b0c8f5a0eca1417e7d2f0fdf0934ca85a91e74 Mon Sep 17 00:00:00 2001 From: Ben Bowers Date: Sun, 7 Jul 2024 16:02:28 -0400 Subject: [PATCH 09/13] additional cleanup, fixing warnings for deadcode --- lef21/src/data.rs | 3 ++- lef21/src/tests.rs | 2 +- lef21/src/write.rs | 11 +++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lef21/src/data.rs b/lef21/src/data.rs index 3b15fab..abf3a4e 100644 --- a/lef21/src/data.rs +++ b/lef21/src/data.rs @@ -202,7 +202,8 @@ 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)] pub fixed_mask: bool, diff --git a/lef21/src/tests.rs b/lef21/src/tests.rs index 99cd163..223cf9b 100644 --- a/lef21/src/tests.rs +++ b/lef21/src/tests.rs @@ -154,7 +154,7 @@ fn it_writes_schema() -> LefResult<()> { let schema = schema_for!(LefLibrary); // NOTE: uncomment to overwrite golden data - // Json.save(&schema, resource("lef21.schema.json"))?; + Json.save(&schema, resource("lef21.schema.json"))?; // Load the golden version, and ensure they match let golden = Json.open(resource("lef21.schema.json"))?; diff --git a/lef21/src/write.rs b/lef21/src/write.rs index 4ab1449..3cb7589 100644 --- a/lef21/src/write.rs +++ b/lef21/src/write.rs @@ -9,7 +9,8 @@ use std::path::Path; // Layout21 Imports use layout21utils as utils; -pub use utils::{EnumStr, SerdeFile, SerializationFormat}; +// pub use utils::{EnumStr, SerdeFile, SerializationFormat}; +pub use utils::EnumStr; // Local imports use super::data::*; @@ -246,11 +247,7 @@ impl<'wr> LefWriter<'wr> { self.write_line(format_args_f!("{MustJoin} {v} ; "))?; } // Most unsupported PINS features *would* go here. - // if pin.taper_rule.is_some() - // || pin.net_expr.is_some() - // || pin.supply_sensitivity.is_some() - // || pin.ground_sensitivity.is_some() - // || pin.must_join.is_some() + // if pin.net_expr.is_some() // || pin.properties.is_some() // { // return Err(LefError::Str("Unsupported LefPin Attr".into())); @@ -377,11 +374,13 @@ impl<'wr> LefWriter<'wr> { fn write_line(&mut self, args: std::fmt::Arguments) -> std::io::Result<()> { writeln!(self.dest, "{}{}", self.indent.state, args) } + /* /// Failure Function /// Wraps error-message `msg` in a [LefError::Str]. fn fail(&mut self, msg: &str) -> LefResult<()> { Err(LefError::Str(msg.to_string())) } + */ } /// Helper function to call `T`'s [Display] method if `opt` is Some, or return an empty string if `opt` is None. fn display_option(opt: &Option) -> String { From 483964ffbb3809bf82c1e59efd8bf2edfb8e32b7 Mon Sep 17 00:00:00 2001 From: Ben Bowers Date: Sun, 7 Jul 2024 16:02:50 -0400 Subject: [PATCH 10/13] updating lef21 schema for new supported fields --- lef21/resources/lef21.schema.json | 216 ++++++++++++++++++------------ 1 file changed, 131 insertions(+), 85 deletions(-) diff --git a/lef21/resources/lef21.schema.json b/lef21/resources/lef21.schema.json index 50247fe..832c449 100644 --- a/lef21/resources/lef21.schema.json +++ b/lef21/resources/lef21.schema.json @@ -180,9 +180,10 @@ "version": { "description": "Lef Spec Version", "type": [ - "number", + "string", "null" - ] + ], + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" }, "via_rule_generators": { "description": "Via Rules Generators", @@ -411,7 +412,7 @@ }, "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. The optional `ORIENT` feature is not supported.", + "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.", "type": "object", "required": [ "cell_name" @@ -422,11 +423,10 @@ "type": "string" }, "orient": { - "description": "Orientation (Unsupported)", - "writeOnly": true, + "description": "Orientation", "anyOf": [ { - "$ref": "#/definitions/Unsupported" + "$ref": "#/definitions/LefOrient" }, { "type": "null" @@ -540,9 +540,10 @@ }, "width": { "type": [ - "number", + "string", "null" - ] + ], + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" } } }, @@ -557,7 +558,8 @@ ], "properties": { "Spacing": { - "type": "number" + "type": "string", + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" } }, "additionalProperties": false @@ -569,7 +571,8 @@ ], "properties": { "DesignRuleWidth": { - "type": "number" + "type": "string", + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" } }, "additionalProperties": false @@ -581,6 +584,7 @@ "description": "The primary block-level construct comprising each [LefLibrary]. Defines a hardware-block's physical abstract, including: * Pin definitions (`pins`) with locations, directions, and associated metadata * Required blockage-obstructions (`obs`) * A variety of other block-level metadata", "type": "object", "required": [ + "fixed_mask", "name" ], "properties": { @@ -608,28 +612,15 @@ ] }, "eeq": { - "description": "Electrically-Equivalent Cell (Unsupported)", - "writeOnly": true, - "anyOf": [ - { - "$ref": "#/definitions/Unsupported" - }, - { - "type": "null" - } + "description": "Electrically-Equivalent Cell", + "type": [ + "string", + "null" ] }, "fixed_mask": { - "description": "Fixed Mask Option (Unsupported)", "writeOnly": true, - "anyOf": [ - { - "$ref": "#/definitions/Unsupported" - }, - { - "type": "null" - } - ] + "type": "boolean" }, "foreign": { "description": "Foreign (i.e. GDSII, DEF) Cell", @@ -698,10 +689,12 @@ ], "items": [ { - "type": "number" + "type": "string", + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" }, { - "type": "number" + "type": "string", + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" } ], "maxItems": 2, @@ -872,6 +865,67 @@ } ] }, + "LefOrient": { + "description": "Specifies orientation for FOREIGN statement", + "oneOf": [ + { + "description": "N", + "type": "string", + "enum": [ + "N" + ] + }, + { + "description": "S", + "type": "string", + "enum": [ + "S" + ] + }, + { + "description": "E", + "type": "string", + "enum": [ + "E" + ] + }, + { + "description": "W", + "type": "string", + "enum": [ + "W" + ] + }, + { + "description": "FN", + "type": "string", + "enum": [ + "FN" + ] + }, + { + "description": "FS", + "type": "string", + "enum": [ + "FS" + ] + }, + { + "description": "FE", + "type": "string", + "enum": [ + "FE" + ] + }, + { + "description": "FW", + "type": "string", + "enum": [ + "FW" + ] + } + ] + }, "LefPadClassType": { "description": "Sub-Types for Macros of Class [LefMacroClass::Pad]", "oneOf": [ @@ -958,27 +1012,17 @@ ] }, "ground_sensitivity": { - "description": "Ground Sensitivity (Unsupported)", - "writeOnly": true, - "anyOf": [ - { - "$ref": "#/definitions/Unsupported" - }, - { - "type": "null" - } + "description": "Ground Sensitivity", + "type": [ + "string", + "null" ] }, "must_join": { - "description": "Must-Join (Unsupported)", - "writeOnly": true, - "anyOf": [ - { - "$ref": "#/definitions/Unsupported" - }, - { - "type": "null" - } + "description": "Must-Join", + "type": [ + "string", + "null" ] }, "name": { @@ -1028,27 +1072,17 @@ ] }, "supply_sensitivity": { - "description": "Supply Sensitivity (Unsupported)", - "writeOnly": true, - "anyOf": [ - { - "$ref": "#/definitions/Unsupported" - }, - { - "type": "null" - } + "description": "Supply Sensitivity", + "type": [ + "string", + "null" ] }, "taper_rule": { - "description": "Taper Rule (Unsupported)", - "writeOnly": true, - "anyOf": [ - { - "$ref": "#/definitions/Unsupported" - }, - { - "type": "null" - } + "description": "Taper Rule", + "type": [ + "string", + "null" ] }, "use": { @@ -1083,7 +1117,8 @@ ] }, "val": { - "type": "number" + "type": "string", + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" } } }, @@ -1197,10 +1232,12 @@ ], "properties": { "x": { - "type": "number" + "type": "string", + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" }, "y": { - "type": "number" + "type": "string", + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" } } }, @@ -1352,10 +1389,12 @@ "type": "array", "items": [ { - "type": "number" + "type": "string", + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" }, { - "type": "number" + "type": "string", + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" } ], "maxItems": 2, @@ -1425,15 +1464,17 @@ "properties": { "capacitance_pf": { "type": [ - "number", + "string", "null" - ] + ], + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" }, "current_ma": { "type": [ - "number", + "string", "null" - ] + ], + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" }, "database_microns": { "description": "Database Distance Units per Micron Defaults to 100, i.e. 1 DBU = 10nm", @@ -1448,33 +1489,38 @@ }, "frequency_mhz": { "type": [ - "number", + "string", "null" - ] + ], + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" }, "power_mw": { "type": [ - "number", + "string", "null" - ] + ], + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" }, "resistance_ohms": { "type": [ - "number", + "string", "null" - ] + ], + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" }, "time_ns": { "type": [ - "number", + "string", "null" - ] + ], + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" }, "voltage_volts": { "type": [ - "number", + "string", "null" - ] + ], + "pattern": "^-?[0-9]+(\\.[0-9]+)?$" } } }, From 07ee5d22ecdc032555bdce9532be5ce66c4af8ec Mon Sep 17 00:00:00 2001 From: Ben Bowers Date: Sun, 7 Jul 2024 16:11:19 -0400 Subject: [PATCH 11/13] fixed bug in gds21 empty.json tests where tests would occasionally fail due to threads using the same file --- gds21/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gds21/src/tests.rs b/gds21/src/tests.rs index ea46379..69a408f 100644 --- a/gds21/src/tests.rs +++ b/gds21/src/tests.rs @@ -197,7 +197,7 @@ fn empty_lib_roundtrip() -> GdsResult<()> { #[test] fn empty_lib_to_json() -> GdsResult<()> { let lib = empty_lib(); - Json.save(&lib, &resource("empty.gds.json")) + Json.save(&lib, &resource("empty1.gds.json")) .expect("save failed"); Ok(()) } From fe1a39e86163d45c62c25615e15b5304425c8d61 Mon Sep 17 00:00:00 2001 From: Ben Bowers Date: Mon, 8 Jul 2024 08:02:36 -0400 Subject: [PATCH 12/13] adding support for NETEXPR --- lef21/resources/lef21.schema.json | 13 ++++--------- lef21/src/data.rs | 9 +++++---- lef21/src/read.rs | 9 +++++++-- lef21/src/tests.rs | 2 +- lef21/src/write.rs | 11 +++++++---- 5 files changed, 24 insertions(+), 20 deletions(-) diff --git a/lef21/resources/lef21.schema.json b/lef21/resources/lef21.schema.json index 832c449..e984c60 100644 --- a/lef21/resources/lef21.schema.json +++ b/lef21/resources/lef21.schema.json @@ -1030,15 +1030,10 @@ "type": "string" }, "net_expr": { - "description": "Net Expression (Unsupported)", - "writeOnly": true, - "anyOf": [ - { - "$ref": "#/definitions/Unsupported" - }, - { - "type": "null" - } + "description": "Net Expression", + "type": [ + "string", + "null" ] }, "ports": { diff --git a/lef21/src/data.rs b/lef21/src/data.rs index abf3a4e..4fded17 100644 --- a/lef21/src/data.rs +++ b/lef21/src/data.rs @@ -301,12 +301,13 @@ 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))] + pub net_expr: Option, // Unsupported - /// Net Expression (Unsupported) - #[serde(default, skip_serializing)] - #[builder(default)] - pub net_expr: Option, /// Properties (Unsupported) #[serde(default, skip_serializing)] #[builder(default)] diff --git a/lef21/src/read.rs b/lef21/src/read.rs index e14030e..78b5905 100644 --- a/lef21/src/read.rs +++ b/lef21/src/read.rs @@ -715,8 +715,13 @@ impl<'src> LefParser<'src> { self.expect(TokenType::SemiColon)?; pin.ground_sensitivity(value) } - LefKey::NetExpr - | LefKey::Property => self.fail(LefParseErrorType::Unsupported)?, + LefKey::NetExpr => { + self.advance()?; + let value_token = self.expect(TokenType::StringLiteral)?; + self.expect(TokenType::SemiColon)?; + pin.net_expr(String::from(self.txt(&value_token))) + } + LefKey::Property => self.fail(LefParseErrorType::Unsupported)?, _ => self.fail(LefParseErrorType::InvalidKey)?, } } diff --git a/lef21/src/tests.rs b/lef21/src/tests.rs index 223cf9b..99cd163 100644 --- a/lef21/src/tests.rs +++ b/lef21/src/tests.rs @@ -154,7 +154,7 @@ fn it_writes_schema() -> LefResult<()> { let schema = schema_for!(LefLibrary); // NOTE: uncomment to overwrite golden data - Json.save(&schema, resource("lef21.schema.json"))?; + // Json.save(&schema, resource("lef21.schema.json"))?; // Load the golden version, and ensure they match let golden = Json.open(resource("lef21.schema.json"))?; diff --git a/lef21/src/write.rs b/lef21/src/write.rs index 3cb7589..bdf0ede 100644 --- a/lef21/src/write.rs +++ b/lef21/src/write.rs @@ -211,7 +211,8 @@ impl<'wr> LefWriter<'wr> { } /// Write a [LefPin] definition fn write_pin(&mut self, pin: &LefPin) -> LefResult<()> { - use LefKey::{AntennaModel, Direction, End, GroundSensitivity, Layer, MustJoin, 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 { @@ -246,10 +247,12 @@ impl<'wr> LefWriter<'wr> { if let Some(ref v) = pin.must_join { self.write_line(format_args_f!("{MustJoin} {v} ; "))?; } + 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.net_expr.is_some() - // || pin.properties.is_some() - // { + // if pin.properties.is_some() { // return Err(LefError::Str("Unsupported LefPin Attr".into())); // } From 39a3db6b76e18f18251307f04bf309c56c73c7e2 Mon Sep 17 00:00:00 2001 From: Ben Bowers Date: Mon, 8 Jul 2024 08:30:31 -0400 Subject: [PATCH 13/13] remove stray comment (cleanup) --- lef21/src/bin/lefrw.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/lef21/src/bin/lefrw.rs b/lef21/src/bin/lefrw.rs index d03fe0f..62d20f9 100644 --- a/lef21/src/bin/lefrw.rs +++ b/lef21/src/bin/lefrw.rs @@ -19,8 +19,6 @@ impl Config { } } -//fn parse_config(args: - fn run() -> Result<(), Box> { let args : Vec = env::args().collect(); let cfg = Config::new(&args)?;