diff --git a/Cargo.toml b/Cargo.toml index 9bd4f287..36d644df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,11 +25,12 @@ path = "src/bin/ro.rs" [workspace] members = [ + "src/langs/asm", + "src/langs/edif", "src/langs/ir", "src/langs/pat", - "src/langs/asm", - "src/langs/xir", "src/langs/xim", + "src/langs/xir", "src/langs/verilog", "src/tools/io", "src/tools/prettyprint", diff --git a/src/langs/edif/Cargo.toml b/src/langs/edif/Cargo.toml new file mode 100644 index 00000000..6907e492 --- /dev/null +++ b/src/langs/edif/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "edif" +version = "0.1.0" +authors = ["Pedro Torruella "] +license = "Apache-2.0" +edition = "2018" + +[dependencies] +serde = { version = "1.0", features = ["derive", "rc"] } +serde_sexpr = "0.1.0" +expect-test = "1.0.2" +derive_more = { version = "0.99.14" , features = ["from"] } diff --git a/src/langs/edif/README.md b/src/langs/edif/README.md new file mode 100644 index 00000000..30150b94 --- /dev/null +++ b/src/langs/edif/README.md @@ -0,0 +1,9 @@ +# Project Structure +The project has the following areas: +* examples: this is a good point of entry as it will quicky show you a template for your application. +* src: this is the place where we have stored the code, separated in crates. + * edif: This crate is the core of the project, provides generic edif utilities. + * platforms: This crate hosts a non-comprehensive list of platform specific support functions, mainly for unit-testing/example. +* others: random files to support development. + +# Still under construction diff --git a/src/langs/edif/src/ast.rs b/src/langs/edif/src/ast.rs new file mode 100644 index 00000000..e36d6c11 --- /dev/null +++ b/src/langs/edif/src/ast.rs @@ -0,0 +1,183 @@ +/* +Copyright 2021 Pedro M. Torruella N. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +use derive_more::{Deref, DerefMut, From}; +use serde::Serialize; + +#[derive(Debug)] +pub struct Rename { + pub from: String, + pub to: String, +} + +// TODO: probably create a module to allow these fields to +// be private +#[derive(Debug, Clone)] +pub struct GenericRef { + pub name: String, + pub reference: String, +} + +#[derive(Debug, From, Serialize)] +pub struct LibraryRef(pub GenericRef); + +#[derive(Debug, From, Serialize, Clone)] +pub struct InstanceRef(pub GenericRef); + +#[derive(Debug, From)] +pub enum PropertyValue { + Integer(i32), + String(String), +} + +#[derive(Debug)] +pub struct Property { + pub name: String, + pub property: PropertyValue, +} + +#[derive(Debug, Deref, DerefMut, From, Serialize)] +pub struct PropertyList(pub Vec); + +#[derive(Debug)] +pub struct PortMember { + pub name: String, + pub index: u32, +} + +#[derive(Debug, From)] +pub enum PortRefToken { + Name(String), + Member(PortMember), +} + +#[derive(Debug)] +pub struct PortRef { + pub token: PortRefToken, + pub instanceref: InstanceRef, +} + +#[derive(Debug, Deref, DerefMut)] +pub struct PortList(pub Vec); + +#[derive(Debug, From)] +pub enum StringToken { + Name(String), + Rename(Rename), +} + +#[derive(Debug)] +pub struct ContentNet { + pub token: StringToken, + pub portlist: PortList, +} + +#[derive(Debug)] +pub struct CellRef { + pub name: String, + pub libraryref: LibraryRef, +} + +#[derive(Debug)] +pub struct ContentInstance { + pub token: StringToken, + pub viewref: String, + pub cellref: CellRef, + pub properties: PropertyList, +} + +#[derive(Debug, From)] +pub enum ContentElement { + Instance(ContentInstance), + Net(ContentNet), +} + +#[derive(Debug, Deref, DerefMut)] +pub struct CellContents(pub Vec); + +#[derive(Debug, From)] +pub enum PortDirection { + Input, + Output, +} + +#[derive(Debug, From)] +pub struct PortArray { + pub rename: Rename, + pub length: i32, +} + +#[derive(Debug, From)] +pub enum PortToken { + Name(String), + Array(PortArray), +} + +#[derive(Debug)] +pub struct InterfacePort { + pub token: PortToken, + pub direction: PortDirection, +} + +#[derive(Debug, Deref, DerefMut)] +pub struct CellInterface(pub Vec); + +#[derive(Debug)] +pub struct CellView { + pub name: String, + pub interface: CellInterface, + pub contents: CellContents, + pub properties: PropertyList, +} + +#[derive(Debug, Deref, DerefMut, From)] +pub struct CellViews(pub Vec); + +#[derive(Debug)] +pub struct Cell { + pub name: String, + pub views: CellViews, +} + +#[derive(Debug, Deref, DerefMut, From, Serialize)] +pub struct Cells(pub Vec); + +#[derive(Debug)] +pub struct Library { + pub name: String, + pub elements: Cells, +} + +#[derive(Debug)] +pub struct Design { + pub name: String, + pub cellref: CellRef, + pub properties: PropertyList, +} + +#[derive(Debug, From)] +pub enum EdifElement { + Library(Library), + Design(Design), +} + +#[derive(Debug, Deref, DerefMut, From, Serialize)] +pub struct EdifElements(pub Vec); + +#[derive(Debug)] +pub struct Edif { + pub name: String, + pub elements: EdifElements, +} diff --git a/src/langs/edif/src/helpers.rs b/src/langs/edif/src/helpers.rs new file mode 100644 index 00000000..129b98ec --- /dev/null +++ b/src/langs/edif/src/helpers.rs @@ -0,0 +1,413 @@ +/* +Copyright 2021 Pedro M. Torruella N. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +use crate::ast::*; + +impl Rename { + pub fn new(from: S, to: S) -> Self + where + S: AsRef, + { + Rename { + from: from.as_ref().to_string(), + to: to.as_ref().to_string(), + } + } +} + +impl GenericRef { + pub fn new(name: S, reference: S) -> Self + where + S: AsRef, + { + GenericRef { + name: name.as_ref().to_string(), + reference: reference.as_ref().to_string(), + } + } + + pub fn is_empty(&self) -> bool { + self.reference.is_empty() + } +} + +impl LibraryRef { + pub fn new(reference: S) -> Self + where + S: AsRef, + { + LibraryRef::from(GenericRef::new("libraryref", reference.as_ref())) + } + + pub fn is_empty(&self) -> bool { + self.0.reference.is_empty() + } +} + +impl InstanceRef { + pub fn new(reference: S) -> Self + where + S: AsRef, + { + InstanceRef::from(GenericRef::new("instanceref", reference.as_ref())) + } + + pub fn is_empty(&self) -> bool { + self.0.reference.is_empty() + } +} + +impl CellRef { + pub fn new(name: S, libref: S) -> Self + where + S: AsRef, + { + CellRef { + name: name.as_ref().to_string(), + libraryref: LibraryRef::new(libref), + } + } +} + +impl Property { + // TODO: is there a way to have a single def of new for multiple types? + pub fn new_integer(name: S, val: i32) -> Self + where + S: AsRef, + { + Property { + name: name.as_ref().to_string(), + property: PropertyValue::from(val), + } + } + + pub fn new_string(name: S, val: S) -> Self + where + S: AsRef, + { + Property { + name: name.as_ref().to_string(), + property: PropertyValue::from(val.as_ref().to_string()), + } + } +} + +impl PortRefToken { + pub fn new(name: S) -> Self + where + S: AsRef, + { + PortRefToken::Name(name.as_ref().to_string()) + } +} + +impl PortRef { + pub fn new(name: S) -> Self + where + S: AsRef, + { + PortRef { + token: PortRefToken::new(name), + instanceref: InstanceRef::new(""), + } + } + + pub fn new_with_ref(name: S, instref: InstanceRef) -> Self + where + S: AsRef, + { + PortRef { + token: PortRefToken::new(name), + instanceref: instref, + } + } + + pub fn new_member(name: S, index: u32) -> Self + where + S: AsRef, + { + PortRef { + token: PortRefToken::Member(PortMember { + name: name.as_ref().to_string(), + index, + }), + instanceref: InstanceRef::new(""), + } + } + + pub fn new_member_with_ref(name: S, index: u32, instref: InstanceRef) -> Self + where + S: AsRef, + { + PortRef { + token: PortRefToken::Member(PortMember { + name: name.as_ref().to_string(), + index, + }), + instanceref: instref, + } + } +} + +impl ContentNet { + pub fn new(name: S) -> Self + where + S: AsRef, + { + ContentNet { + token: StringToken::from(name.as_ref().to_string()), + portlist: PortList(Vec::new()), + } + } + + pub fn new_with_ports(name: S, ports: PortList) -> Self + where + S: AsRef, + { + ContentNet { + token: StringToken::from(name.as_ref().to_string()), + portlist: ports, + } + } + + pub fn new_renamed(from: S, to: S) -> Self + where + S: AsRef, + { + ContentNet { + token: StringToken::from(Rename { + from: from.as_ref().to_string(), + to: to.as_ref().to_string(), + }), + portlist: PortList(Vec::new()), + } + } + + pub fn new_renamed_with_ports(from: S, to: S, ports: PortList) -> Self + where + S: AsRef, + { + ContentNet { + token: StringToken::from(Rename { + from: from.as_ref().to_string(), + to: to.as_ref().to_string(), + }), + portlist: ports, + } + } +} + +impl StringToken { + pub fn new(name: S) -> Self + where + S: AsRef, + { + StringToken::from(name.as_ref().to_string()) + } + + pub fn new_renamed(from: S, to: S) -> Self + where + S: AsRef, + { + StringToken::from(Rename { + from: from.as_ref().to_string(), + to: to.as_ref().to_string(), + }) + } +} + +impl PortArray { + pub fn new(from: S, to: S, len: i32) -> Self + where + S: AsRef, + { + PortArray { + rename: Rename::new(from, to), + length: len, + } + } +} + +impl PortToken { + pub fn new(name: S) -> Self + where + S: AsRef, + { + PortToken::from(name.as_ref().to_string()) + } + + // TODO: How to automatically define the type of len + // to be the same time as PortArray.length? + // TODO: turn len to u32? + pub fn new_array(from: S, to: S, len: i32) -> Self + where + S: AsRef, + { + PortToken::from(PortArray::new(from, to, len)) + } +} + +impl InterfacePort { + pub fn new(porttoken: PortToken, dir: PortDirection) -> Self { + InterfacePort { + token: porttoken, + direction: dir, + } + } + + pub fn new_input(name: S) -> Self + where + S: AsRef, + { + InterfacePort { + token: PortToken::new(name), + direction: PortDirection::Input, + } + } + + pub fn new_output(name: S) -> Self + where + S: AsRef, + { + InterfacePort { + token: PortToken::new(name), + direction: PortDirection::Output, + } + } + + pub fn new_input_array(from: S, to: S, len: i32) -> Self + where + S: AsRef, + { + InterfacePort { + token: PortToken::new_array(from, to, len), + direction: PortDirection::Input, + } + } + + pub fn new_output_array(from: S, to: S, len: i32) -> Self + where + S: AsRef, + { + InterfacePort { + token: PortToken::new_array(from, to, len), + direction: PortDirection::Output, + } + } +} + +impl CellView { + pub fn new(name: S) -> Self + where + S: AsRef, + { + CellView { + name: name.as_ref().to_string(), + interface: CellInterface(Vec::new()), + contents: CellContents(Vec::new()), + properties: PropertyList::from(Vec::new()), + } + } +} + +impl Cell { + pub fn new(name: S) -> Self + where + S: AsRef, + { + Cell { + name: name.as_ref().to_string(), + views: CellViews(Vec::new()), + } + } + + pub fn new_with_views(name: S, cells: CellViews) -> Self + where + S: AsRef, + { + Cell { + name: name.as_ref().to_string(), + views: cells, + } + } +} + +impl Design { + pub fn new(name: S, cell: CellRef) -> Self + where + S: AsRef, + { + Design { + name: name.as_ref().to_string(), + cellref: cell, + properties: PropertyList::from(Vec::new()), + } + } + + pub fn new_with_prop(name: S, cell: CellRef, props: PropertyList) -> Self + where + S: AsRef, + { + Design { + name: name.as_ref().to_string(), + cellref: cell, + properties: props, + } + } +} + +impl Library { + pub fn new(name: S) -> Self + where + S: AsRef, + { + Library { + name: name.as_ref().to_string(), + elements: Cells::from(Vec::new()), + } + } + + pub fn new_with_cells(name: S, cells: Cells) -> Self + where + S: AsRef, + { + Library { + name: name.as_ref().to_string(), + elements: cells, + } + } +} + +impl Edif { + pub fn new(name: S) -> Self + where + S: AsRef, + { + Edif { + name: name.as_ref().to_string(), + elements: EdifElements::from(Vec::new()), + } + } + + pub fn new_with_elems(name: S, elems: EdifElements) -> Self + where + S: AsRef, + { + Edif { + name: name.as_ref().to_string(), + elements: elems, + } + } +} diff --git a/src/langs/edif/src/lib.rs b/src/langs/edif/src/lib.rs new file mode 100644 index 00000000..a2e4dddb --- /dev/null +++ b/src/langs/edif/src/lib.rs @@ -0,0 +1,19 @@ +/* +Copyright 2021 Pedro M. Torruella N. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +pub mod ast; +pub mod helpers; +pub mod serialize; +pub mod string_helpers; diff --git a/src/langs/edif/src/main.rs b/src/langs/edif/src/main.rs new file mode 100644 index 00000000..f97a13fa --- /dev/null +++ b/src/langs/edif/src/main.rs @@ -0,0 +1,52 @@ +/* +Copyright 2021 Pedro M. Torruella N. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +use std::path::Path; + +use std::fs::File; +use std::io::Write; + +use edif::ast::*; + +static DOFILE: bool = false; + +fn main() { + let point = Edif { + name: r#"dsp2"#.to_string(), + elements: EdifElements::from(Vec::new()), + }; + + //point.libraries.push(mylib); + + let serialized = serde_sexpr::to_string(&point).unwrap(); + + println!("serialized = {}", serialized); + + let path = Path::new("test.edf"); + let display = path.display(); + + if DOFILE { + // Open a file in write-only mode, returns `io::Result` + let mut file = match File::create(&path) { + Err(why) => panic!("couldn't create {}: {}", display, why), + Ok(file) => file, + }; + + match file.write_all(serialized.as_bytes()) { + Err(why) => panic!("couldn't write to {}: {}", display, why), + Ok(_) => println!("successfully wrote to {}", display), + } + } +} diff --git a/src/langs/edif/src/serialize.rs b/src/langs/edif/src/serialize.rs new file mode 100644 index 00000000..875a22f7 --- /dev/null +++ b/src/langs/edif/src/serialize.rs @@ -0,0 +1,449 @@ +/* +Copyright 2021 Pedro M. Torruella N. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +use crate::ast::*; +use serde::ser::{SerializeSeq, Serializer}; +use serde::Serialize; + +impl Serialize for Rename { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(3))?; + seq.serialize_element(&"rename".to_string())?; + seq.serialize_element(&self.from)?; + let mut with_quotes = r#"""#.to_string(); + with_quotes.push_str(&self.to); + with_quotes.push_str(&r#"""#.to_string()); + seq.serialize_element(&with_quotes)?; + seq.end() + } +} + +impl Serialize for GenericRef { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(2))?; + seq.serialize_element(&self.name)?; + seq.serialize_element(&self.reference)?; + seq.end() + } +} + +impl Serialize for PropertyValue { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(2))?; + + match self { + PropertyValue::Integer(val) => { + seq.serialize_element(&"integer".to_string())?; + seq.serialize_element(&val)?; + seq.end() + } + PropertyValue::String(val) => { + seq.serialize_element(&"string".to_string())?; + let mut with_quotes = r#"""#.to_string(); + with_quotes.push_str(val); + with_quotes.push_str(&r#"""#.to_string()); + seq.serialize_element(&with_quotes)?; + seq.end() + } + } + } +} + +impl Serialize for Property { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(3))?; + seq.serialize_element(&"property".to_string())?; + seq.serialize_element(&self.name)?; + seq.serialize_element(&self.property)?; + seq.end() + } +} + +impl Serialize for PortMember { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(3))?; + seq.serialize_element(&"member".to_string())?; + seq.serialize_element(&self.name)?; + seq.serialize_element(&self.index)?; + seq.end() + } +} + +impl Serialize for PortRefToken { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + PortRefToken::Name(elem) => elem.serialize(serializer), + PortRefToken::Member(elem) => elem.serialize(serializer), + } + } +} + +impl Serialize for PortRef { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(3))?; + seq.serialize_element(&"portref".to_string())?; + seq.serialize_element(&self.token)?; + if !self.instanceref.is_empty() { + seq.serialize_element(&self.instanceref)? + } + seq.end() + } +} + +impl Serialize for PortList { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + if !self.is_empty() { + let mut seq = serializer.serialize_seq(Some(self.len() + 1))?; + seq.serialize_element(&"joined".to_string())?; + for portref in self.iter() { + seq.serialize_element(&portref)?; + } + seq.end() + } else { + serializer.serialize_none() + } + } +} + +impl Serialize for StringToken { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + StringToken::Name(elem) => elem.serialize(serializer), + StringToken::Rename(elem) => elem.serialize(serializer), + } + } +} + +impl Serialize for ContentNet { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(3))?; + seq.serialize_element(&"net".to_string())?; + seq.serialize_element(&self.token)?; + + if !self.portlist.is_empty() { + seq.serialize_element(&self.portlist)?; + } + seq.end() + } +} + +impl Serialize for CellRef { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(3))?; + seq.serialize_element(&"cellref".to_string())?; + seq.serialize_element(&self.name)?; + seq.serialize_element(&self.libraryref)?; + seq.end() + } +} + +impl Serialize for ContentInstance { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(3 + self.properties.len()))?; + let all = ("viewref", &self.viewref, &self.cellref); + + seq.serialize_element(&"instance".to_string())?; + seq.serialize_element(&self.token)?; + seq.serialize_element(&all)?; + if !self.properties.is_empty() { + for prop in self.properties.iter() { + seq.serialize_element(&prop)?; + } + } + seq.end() + } +} + +impl Serialize for ContentElement { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + ContentElement::Instance(elem) => elem.serialize(serializer), + ContentElement::Net(elem) => elem.serialize(serializer), + } + } +} + +impl Serialize for CellContents { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + if !self.is_empty() { + let mut seq = serializer.serialize_seq(Some(2))?; + seq.serialize_element(&"contents".to_string())?; + + for element in self.iter() { + seq.serialize_element(&element)?; + } + seq.end() + } else { + serializer.serialize_none() + } + } +} + +impl Serialize for PortDirection { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(2))?; + seq.serialize_element(&"direction".to_string())?; + match self { + PortDirection::Input => { + seq.serialize_element(&"INPUT".to_string())?; + } + PortDirection::Output => { + seq.serialize_element(&"OUTPUT".to_string())?; + } + } + seq.end() + } +} + +impl Serialize for PortArray { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(3))?; + + seq.serialize_element(&"array".to_string())?; + seq.serialize_element(&self.rename)?; + seq.serialize_element(&self.length)?; + seq.end() + } +} + +impl Serialize for PortToken { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + PortToken::Name(name) => name.serialize(serializer), + PortToken::Array(array) => array.serialize(serializer), + } + } +} + +impl Serialize for InterfacePort { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(3))?; + seq.serialize_element(&"port".to_string())?; + seq.serialize_element(&self.token)?; + seq.serialize_element(&self.direction)?; + seq.end() + } +} + +impl Serialize for CellInterface { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + if !self.is_empty() { + let mut seq = serializer.serialize_seq(Some(1 + self.len()))?; + seq.serialize_element(&"interface".to_string())?; + + for port in self.iter() { + seq.serialize_element(&port)?; + } + seq.end() + } else { + serializer.serialize_none() + } + } +} + +impl Serialize for CellView { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let len_interface = self.interface.len(); + let len_contents = self.contents.len(); + let len_properties = self.properties.len(); + let local_size = 2 + len_interface + len_contents + len_properties; + let mut seq = serializer.serialize_seq(Some(local_size))?; + let viewtype = ("viewtype", "NETLIST"); + + seq.serialize_element("view")?; + seq.serialize_element(&self.name)?; + seq.serialize_element(&viewtype)?; + + if !self.interface.is_empty() { + seq.serialize_element(&self.interface)?; + } + + if !self.contents.is_empty() { + seq.serialize_element(&self.contents)?; + } + + if !self.properties.is_empty() { + for prop in self.properties.iter() { + seq.serialize_element(&prop)?; + } + } + + seq.end() + } +} + +impl Serialize for Cell { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(4))?; + let celltype = ("celltype", "GENERIC"); + + seq.serialize_element("cell")?; + seq.serialize_element(&self.name)?; + seq.serialize_element(&celltype)?; + if !self.views.is_empty() { + for view in self.views.iter() { + seq.serialize_element(&view)?; + } + } + seq.end() + } +} + +impl Serialize for Library { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(3))?; + let fixed1 = ("edifLevel", "0"); + //Little hack to make serialize to insert braces around numberDefinition. + let fixed2 = ("technology", ("numberDefinition",)); + + seq.serialize_element("Library")?; + seq.serialize_element(&self.name)?; + seq.serialize_element(&fixed1)?; + seq.serialize_element(&fixed2)?; + if !self.elements.is_empty() { + for elem in self.elements.iter() { + seq.serialize_element(&elem)?; + } + } + seq.end() + } +} + +impl Serialize for Design { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(3))?; + + seq.serialize_element("design")?; + seq.serialize_element(&self.name)?; + seq.serialize_element(&self.cellref)?; + if !self.properties.is_empty() { + for prop in self.properties.iter() { + seq.serialize_element(&prop)?; + } + } + seq.end() + } +} + +impl Serialize for EdifElement { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + EdifElement::Library(lib) => lib.serialize(serializer), + EdifElement::Design(des) => des.serialize(serializer), + } + } +} + +impl Serialize for Edif { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + // 1) Need to count the number of fields we will have on the + // main branch. + // + // 3 is the number of fields in the struct. + let mut seq = serializer.serialize_seq(Some(6))?; + let version = ("edifversion", "2", "0", "0"); + let level = ("edifLevel", 0); + let kword = ("keywordmap", ("keywordlevel", 0)); + + seq.serialize_element("edif")?; + seq.serialize_element(&self.name)?; + seq.serialize_element(&version)?; + seq.serialize_element(&level)?; + seq.serialize_element(&kword)?; + + if !self.elements.is_empty() { + for elem in self.elements.iter() { + seq.serialize_element(&elem)?; + } + } + + seq.end() + } +} diff --git a/src/langs/edif/src/string_helpers.rs b/src/langs/edif/src/string_helpers.rs new file mode 100644 index 00000000..d76dac16 --- /dev/null +++ b/src/langs/edif/src/string_helpers.rs @@ -0,0 +1,52 @@ +use std::cmp::Ordering; + +pub fn match_check(incoming: String) -> i32 { + let mut counter: i32 = 0; + for c in incoming.chars() { + if c == '(' { + counter += 1; + } else if c == ')' { + counter -= 1; + } + } + match counter.cmp(&0) { + Ordering::Greater => println!("ERROR: Too many left parentheses."), + Ordering::Less => println!("ERROR: Too many right parentheses."), + Ordering::Equal => (), + } + counter +} + +pub fn add_new_lines(input: String, level: u32, dospaces: bool) -> String { + let mut output = String::new(); + let mut count = 0; + let mut first_pass = false; + for c in input.chars() { + match c { + '(' => { + if (count <= level) & (first_pass) { + if output.ends_with(' ') { + output.pop(); + } + output.push('\n'); + if dospaces { + for _i in 0..count { + output.push(' '); + output.push(' '); + } + } + } + output.push(c); + first_pass = true; + count += 1; + } + ')' => { + output.push(c); + count -= 1; + } + _ => output.push(c), + } + } + assert_eq!(0, match_check(output.clone())); + output +} diff --git a/src/langs/edif/tests/string_helpers_ut.rs b/src/langs/edif/tests/string_helpers_ut.rs new file mode 100644 index 00000000..10429649 --- /dev/null +++ b/src/langs/edif/tests/string_helpers_ut.rs @@ -0,0 +1,28 @@ +use edif::string_helpers::*; + +#[test] +fn simple_new_lines() { + let test = "Aqui ((hay espacios) estes) o no estes".to_string(); + let actual = add_new_lines(test, 2, false); + + assert_eq!(actual, "Aqui (\n(hay espacios) estes) o no estes") +} + +#[test] +fn onelevel_new_lines() { + let test = "(Aqui ((())) ((())) estes o no estes)".to_string(); + let actual = add_new_lines(test, 1, false); + + assert_eq!(actual, "(Aqui\n((()))\n((())) estes o no estes)") +} + +#[test] +fn complex_new_lines() { + let test = "(Aqui((() ))((())) estes o no estes)".to_string(); + let actual = add_new_lines(test, 2, true); + + assert_eq!( + actual, + "(Aqui\n (\n (() ))\n (\n (())) estes o no estes)" + ) +} diff --git a/src/langs/edif/tests/unit_tests.rs b/src/langs/edif/tests/unit_tests.rs new file mode 100644 index 00000000..b90bc867 --- /dev/null +++ b/src/langs/edif/tests/unit_tests.rs @@ -0,0 +1,348 @@ +/* +Copyright 2021 Pedro M. Torruella N. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +//use std::process::Command; +//use Edif; +use edif::ast::*; +use edif::string_helpers::*; + +/* +#[test] +fn runs_without_arguments() { + let mut cmd = Command::cargo_bin("ls").unwrap(); + cmd.assert().success(); +}*/ + +// Test 1: we should get a minimal edif with no elements +#[test] +fn empty_edif() { + let ed = Edif { + name: "ed".to_string(), + elements: EdifElements::from(Vec::new()), + }; + let actual = serde_sexpr::to_string(&ed).unwrap(); + + assert_eq!( + actual, + "(edif ed (edifversion 2 0 0) (edifLevel 0) (keywordmap (keywordlevel 0)))" + ); + assert_eq!(match_check(actual), 0); +} + +// Test 2: with library elements +#[test] +fn edif_lib() { + let libelem = EdifElement::from(Library::new("mylib")); + let ed = Edif { + name: "ed2".to_string(), + elements: EdifElements::from(vec![libelem]), + }; + let actual = serde_sexpr::to_string(&ed).unwrap(); + + assert_eq!(actual, + "(edif ed2 (edifversion 2 0 0) (edifLevel 0) (keywordmap (keywordlevel 0)) (Library mylib (edifLevel 0) (technology (numberDefinition))))" ); + assert_eq!(match_check(actual), 0); +} + +// Test 2.1: with library that has cells +#[test] +fn edif_lib_cells() { + let libelem = EdifElement::from(Library::new_with_cells( + "mylib", + Cells::from(vec![Cell::new_with_views( + "mycell", + CellViews::from(vec![CellView::new("myview")]), + )]), + )); + + let ed = Edif { + name: "ed2".to_string(), + elements: EdifElements::from(vec![libelem]), + }; + + let actual = serde_sexpr::to_string(&ed).unwrap(); + + assert_eq!(actual, + "(edif ed2 (edifversion 2 0 0) (edifLevel 0) (keywordmap (keywordlevel 0)) (Library mylib (edifLevel 0) (technology (numberDefinition)) (cell mycell (celltype GENERIC) (view myview (viewtype NETLIST)))))" ); + assert_eq!(match_check(actual), 0); +} + +// Test 3: cell with no elements +#[test] +fn cell_empty() { + let mycell = Cell::new("mycell"); + let actual = serde_sexpr::to_string(&mycell).unwrap(); + assert_eq!(actual, "(cell mycell (celltype GENERIC))"); + assert_eq!(match_check(actual), 0); +} + +// Test 4: cell view with no elements +#[test] +fn cellview_empty() { + let myview = CellView::new("myview"); + let actual = serde_sexpr::to_string(&myview).unwrap(); + assert_eq!(actual, "(view myview (viewtype NETLIST))"); + assert_eq!(match_check(actual), 0); +} + +// Test 4.1: cell view with properties +#[test] +fn cellview_w_props() { + let mut myview = CellView::new("myview"); + myview + .properties + .push(Property::new_string("usability", "very_high_please")); + let actual = serde_sexpr::to_string(&myview).unwrap(); + assert_eq!( + actual, + r#"(view myview (viewtype NETLIST) (property usability (string "very_high_please")))"# + ); + assert_eq!(match_check(actual), 0); +} + +// Test 5: interface with no elements +#[test] +fn interface_empty() { + let myinterface = CellInterface(Vec::new()); + let actual = serde_sexpr::to_string(&myinterface).unwrap(); + assert_eq!(actual, "()"); + assert_eq!(match_check(actual), 0); +} + +// Test 6: interface with 2 elements +#[test] +fn interface_some() { + let porta = InterfacePort::new_input("a"); + let portb = InterfacePort::new_input("b"); + let myinterface = CellInterface(vec![porta, portb]); + let actual = serde_sexpr::to_string(&myinterface).unwrap(); + assert_eq!( + actual, + "(interface (port a (direction INPUT)) (port b (direction INPUT)))" + ); + assert_eq!(match_check(actual), 0); +} + +// Test 7: contents empty +#[test] +fn contents_empty() { + let mycontent = CellContents(Vec::new()); + let actual = serde_sexpr::to_string(&mycontent).unwrap(); + assert_eq!(actual, "()"); + assert_eq!(match_check(actual), 0); +} + +//test 8: content instance with no properties +#[test] +fn contents_instance_simple() { + let myinstance = ContentInstance { + token: StringToken::new("lut4"), + viewref: "myview".to_string(), + cellref: CellRef::new("mycellref", "mylibref"), + properties: PropertyList(Vec::new()), + }; + let actual = serde_sexpr::to_string(&myinstance).unwrap(); + assert_eq!( + actual, + "(instance lut4 (viewref myview (cellref mycellref (libraryref mylibref))))" + ); + assert_eq!(match_check(actual), 0); +} + +//test 8.1: content instance with properties +#[test] +fn contents_instance_props() { + let props = PropertyList(vec![ + Property { + name: "adjustability".to_string(), + property: PropertyValue::Integer(11), + }, + Property { + name: "usability".to_string(), + property: PropertyValue::String("very_high_please".to_string()), + }, + ]); + let myinstance = ContentInstance { + token: StringToken::new("dsp1"), + viewref: "myview".to_string(), + cellref: CellRef::new("mycellref", "mylibref"), + properties: props, + }; + let actual = serde_sexpr::to_string(&myinstance).unwrap(); + assert_eq!( + actual, + r#"(instance dsp1 (viewref myview (cellref mycellref (libraryref mylibref))) (property adjustability (integer 11)) (property usability (string "very_high_please")))"# + ); + assert_eq!(match_check(actual), 0); +} + +//test 9: content net +#[test] +fn net_empty() { + let myinstance = ContentNet::new("y"); + let actual = serde_sexpr::to_string(&myinstance).unwrap(); + assert_eq!(actual, "(net y)"); + assert_eq!(match_check(actual), 0); +} + +//test 9.1: content net renamed +#[test] +fn net_renamed() { + let myinstance = ContentNet::new_renamed("u_u_sad_4", "u_u_sad[4]"); + let actual = serde_sexpr::to_string(&myinstance).unwrap(); + assert_eq!(actual, r#"(net (rename u_u_sad_4 "u_u_sad[4]"))"#); + assert_eq!(match_check(actual), 0); +} + +//test 10: port reference +#[test] +fn portref() { + let myport = PortRef { + token: PortRefToken::new("y"), + instanceref: InstanceRef::new("myinst"), + }; + let actual = serde_sexpr::to_string(&myport).unwrap(); + assert_eq!(actual, "(portref y (instanceref myinst))"); + assert_eq!(match_check(actual), 0); +} + +//test 11: multiple port reference +#[test] +fn multi_portref() { + let myport1 = PortRef { + token: PortRefToken::new("y"), + instanceref: InstanceRef::new("myinst"), + }; + let myport2 = PortRef { + token: PortRefToken::new("x"), + instanceref: InstanceRef::new(""), + }; + let myport3 = PortRef { + token: PortRefToken::Member(PortMember { + name: "some_other_port_".to_string(), + index: 9, + }), + instanceref: InstanceRef::new("the_inst"), + }; + let mut myinstance = ContentNet::new("y"); + myinstance.portlist = PortList(vec![myport1, myport2, myport3]); + + let actual = serde_sexpr::to_string(&myinstance).unwrap(); + assert_eq!( + actual, + "(net y (joined (portref y (instanceref myinst)) (portref x) (portref (member some_other_port_ 9) (instanceref the_inst))))" + ); + assert_eq!(match_check(actual), 0); +} + +// Test 12: contents with something inside. +#[test] +fn contents_elements() { + let mut mycontent = CellContents(Vec::new()); + mycontent.push(ContentElement::Net(ContentNet::new("a"))); + mycontent.push(ContentElement::Net(ContentNet::new("b"))); + + let actual = serde_sexpr::to_string(&mycontent).unwrap(); + assert_eq!(actual, "(contents (net a) (net b))"); + assert_eq!(match_check(actual), 0); +} + +// Test 13: property values +#[test] +fn property_values() { + let mypropint = PropertyValue::Integer(42); + let actual = serde_sexpr::to_string(&mypropint).unwrap(); + assert_eq!(actual, "(integer 42)"); + assert_eq!(match_check(actual), 0); + + let mypropstr = PropertyValue::from("64'h00AA00FF33CC0F00".to_string()); + let actual = serde_sexpr::to_string(&mypropstr).unwrap(); + assert_eq!(actual, r#"(string "64'h00AA00FF33CC0F00")"#); + assert_eq!(match_check(actual), 0); +} + +// Test 14: property +#[test] +fn property_complete() { + let mypropval = PropertyValue::from( + "256'h0000000000000000000000000000000000000000000000000000000000000000".to_string(), + ); + let myprop = Property { + name: "INITP_01".to_string(), + property: mypropval, + }; + let actual = serde_sexpr::to_string(&myprop).unwrap(); + assert_eq!( + actual, + r#"(property INITP_01 (string "256'h0000000000000000000000000000000000000000000000000000000000000000"))"# + ); + assert_eq!(match_check(actual), 0); +} + +// Test 15: rename +#[test] +fn rename() { + let myren = Rename::new("my_exp_0", r#"my_exp[0]"#); + let actual = serde_sexpr::to_string(&myren).unwrap(); + assert_eq!(actual, r#"(rename my_exp_0 "my_exp[0]")"#); + assert_eq!(match_check(actual), 0); +} + +// Test 16: References +#[test] +fn references() { + let libref = LibraryRef::new("anew_libref"); + let actual = serde_sexpr::to_string(&libref).unwrap(); + assert_eq!(actual, r#"(libraryref anew_libref)"#); + assert_eq!(match_check(actual), 0); + + let instref = InstanceRef::new("anew_instance"); + let actual = serde_sexpr::to_string(&instref).unwrap(); + assert_eq!(actual, r#"(instanceref anew_instance)"#); + assert_eq!(match_check(actual), 0); +} + +// Test 17: Designs +#[test] +fn design_empty() { + let design = Design::new("mydesign", CellRef::new("LUT4", "hdi_primitives")); + let actual = serde_sexpr::to_string(&design).unwrap(); + assert_eq!( + actual, + r#"(design mydesign (cellref LUT4 (libraryref hdi_primitives)))"# + ); + assert_eq!(match_check(actual), 0); +} + +// Test 18: Designs with properties +#[test] +fn design_props() { + let mut proplist = PropertyList(Vec::new()); + proplist.push(Property::new_string( + "XLNX_PROJ_DIR", + "/home/clavin/testRW/picoblaze", + )); + proplist.push(Property::new_string("part", "xcvu3p-ffvc1517-2-i")); + + let design = + Design::new_with_prop("mydesign", CellRef::new("LUT4", "hdi_primitives"), proplist); + let actual = serde_sexpr::to_string(&design).unwrap(); + assert_eq!( + actual, + r#"(design mydesign (cellref LUT4 (libraryref hdi_primitives)) (property XLNX_PROJ_DIR (string "/home/clavin/testRW/picoblaze")) (property part (string "xcvu3p-ffvc1517-2-i")))"# + ); + assert_eq!(match_check(actual), 0); +}