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(()) } diff --git a/lef21/resources/lef21.schema.json b/lef21/resources/lef21.schema.json index 6c1290f..6f5d3bf 100644 --- a/lef21/resources/lef21.schema.json +++ b/lef21/resources/lef21.schema.json @@ -412,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" @@ -423,11 +423,10 @@ "type": "string" }, "orient": { - "description": "Orientation (Unsupported)", - "writeOnly": true, + "description": "Orientation", "anyOf": [ { - "$ref": "#/definitions/Unsupported" + "$ref": "#/definitions/LefOrient" }, { "type": "null" @@ -585,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": { @@ -612,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", @@ -892,6 +879,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": [ @@ -978,27 +1026,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": { @@ -1006,15 +1044,10 @@ "type": "string" }, "net_expr": { - "description": "Net Expression (Unsupported)", - "writeOnly": true, - "anyOf": [ - { - "$ref": "#/definitions/Unsupported" - }, - { - "type": "null" - } + "description": "Net Expression", + "type": [ + "string", + "null" ] }, "ports": { @@ -1048,27 +1081,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": { diff --git a/lef21/src/bin/lefrw.rs b/lef21/src/bin/lefrw.rs new file mode 100644 index 0000000..62d20f9 --- /dev/null +++ b/lef21/src/bin/lefrw.rs @@ -0,0 +1,34 @@ +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 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/data.rs b/lef21/src/data.rs index 0d11326..42ad0df 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 @@ -197,15 +198,17 @@ 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: Option, - /// Electrically-Equivalent Cell (Unsupported) + /// Electrically-Equivalent Cell + #[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 eeq: Option, + pub fixed_mask: bool, + + // Unsupported /// Density Objects (Unsupported) #[serde(default, skip_serializing)] #[builder(default)] @@ -239,18 +242,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 /// @@ -286,27 +285,29 @@ 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, + + /// Net Expression + #[serde(default, skip_serializing_if = "Option::is_none")] + #[builder(default, setter(strip_option))] + pub net_expr: 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)] @@ -642,6 +643,8 @@ enumstr!( DesignRuleWidth: "DESIGNRULEWIDTH", Spacing: "SPACING", Bump: "BUMP", + Eeq: "EEQ", + FixedMask: "FIXEDMASK", Mask: "MASK", UseMinSpacing: "USEMINSPACING", @@ -726,6 +729,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 8425e1b..5f2a9ce 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()?); @@ -564,22 +567,34 @@ 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)?; + mac.fixed_mask(true) + } 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 => { @@ -657,7 +672,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 @@ -680,12 +697,37 @@ impl<'src> LefParser<'src> { antenna_attrs.push(LefPinAntennaAttr { key, val, layer }); pin } - LefKey::TaperRule - | LefKey::NetExpr - | LefKey::SupplySensitivity - | LefKey::GroundSensitivity - | LefKey::MustJoin - | LefKey::Property => self.fail(LefParseErrorType::Unsupported)?, + 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 => { + 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 40cd4f5..aca566b 100644 --- a/lef21/src/tests.rs +++ b/lef21/src/tests.rs @@ -115,6 +115,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")) diff --git a/lef21/src/write.rs b/lef21/src/write.rs index 166a578..c0baa01 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::*; @@ -144,21 +145,26 @@ 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} ; "))?; + 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; 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(), 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} ;"))?; @@ -172,8 +178,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} ;"))?; } @@ -206,7 +214,8 @@ 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, + 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 { @@ -229,15 +238,24 @@ 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} ; "))?; + } + 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.taper_rule.is_some() - // || pin.net_expr.is_some() - // || pin.supply_sensitivity.is_some() - // || pin.ground_sensitivity.is_some() - // || pin.must_join.is_some() - // || pin.properties.is_some() - // { + // if pin.properties.is_some() { // return Err(LefError::Str("Unsupported LefPin Attr".into())); // } @@ -293,7 +311,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 { @@ -313,8 +331,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} ;"))?; } }, }; @@ -362,11 +385,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 {