Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion fpga_arch_parser/src/arch.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@

pub struct Model {
pub struct ModelPort {
pub name: String,
pub is_clock: bool,
pub clock: Option<String>,
pub combinational_sink_ports: Vec<String>,
}

pub struct Model {
pub name: String,
pub never_prune: bool,
pub input_ports: Vec<ModelPort>,
pub output_ports: Vec<ModelPort>,
}

pub struct Metadata {
Expand Down
15 changes: 12 additions & 3 deletions fpga_arch_parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ mod arch;
mod parse_error;
mod parse_metadata;
mod parse_port;
mod parse_models;
mod parse_tiles;
mod parse_layouts;
mod parse_device;
Expand All @@ -22,6 +23,7 @@ mod parse_complex_block_list;
pub use crate::parse_error::FPGAArchParseError;
pub use crate::arch::*;

use crate::parse_models::parse_models;
use crate::parse_tiles::parse_tiles;
use crate::parse_layouts::parse_layouts;
use crate::parse_device::parse_device;
Expand All @@ -37,6 +39,7 @@ fn parse_architecture(name: &OwnedName,
return Err(FPGAArchParseError::UnknownAttribute(String::from("Expected to be empty"), parser.position()));
}

let mut models: Option<Vec<Model>> = None;
let mut tiles: Option<Vec<Tile>> = None;
let mut layouts: Option<Vec<Layout>> = None;
let mut device: Option<DeviceInfo> = None;
Expand All @@ -49,8 +52,10 @@ fn parse_architecture(name: &OwnedName,
Ok(XmlEvent::StartElement { name, attributes, .. }) => {
match name.to_string().as_str() {
"models" => {
// TODO: Implement.
let _ = parser.skip();
models = match models {
None => Some(parse_models(&name, &attributes, parser)?),
Some(_) => return Err(FPGAArchParseError::DuplicateTag(format!("<{name}>"), parser.position())),
}
},
"tiles" => {
tiles = match tiles {
Expand Down Expand Up @@ -128,6 +133,10 @@ fn parse_architecture(name: &OwnedName,
};
}

let models = match models {
Some(t) => t,
None => return Err(FPGAArchParseError::MissingRequiredTag("<models>".to_string())),
};
let tiles = match tiles {
Some(t) => t,
None => return Err(FPGAArchParseError::MissingRequiredTag("<tiles>".to_string())),
Expand All @@ -154,7 +163,7 @@ fn parse_architecture(name: &OwnedName,
};

Ok(FPGAArch {
models: Vec::new(),
models,
tiles,
layouts,
device,
Expand Down
4 changes: 2 additions & 2 deletions fpga_arch_parser/src/parse_complex_block_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -477,8 +477,8 @@ fn parse_pb_type(name: &OwnedName,
}

pub fn parse_complex_block_list(name: &OwnedName,
attributes: &[OwnedAttribute],
parser: &mut EventReader<BufReader<File>>) -> Result<Vec<PBType>, FPGAArchParseError> {
attributes: &[OwnedAttribute],
parser: &mut EventReader<BufReader<File>>) -> Result<Vec<PBType>, FPGAArchParseError> {
assert!(name.to_string() == "complexblocklist");
if !attributes.is_empty() {
return Err(FPGAArchParseError::UnknownAttribute(String::from("Expected to be empty"), parser.position()));
Expand Down
251 changes: 251 additions & 0 deletions fpga_arch_parser/src/parse_models.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
use std::fs::File;
use std::io::BufReader;

use xml::common::Position;
use xml::reader::{EventReader, XmlEvent};
use xml::name::OwnedName;
use xml::attribute::OwnedAttribute;

use crate::parse_error::*;
use crate::arch::*;

fn parse_model_port(tag_name: &OwnedName,
attributes: &[OwnedAttribute],
parser: &mut EventReader<BufReader<File>>) -> Result<ModelPort, FPGAArchParseError> {
assert!(tag_name.to_string() == "port");

let mut name: Option<String> = None;
let mut is_clock: Option<bool> = None;
let mut clock: Option<String> = None;
let mut combinational_sink_ports: Option<Vec<String>> = None;
for a in attributes {
match a.name.to_string().as_ref() {
"name" => {
name = match name {
None => Some(a.value.clone()),
Some(_) => return Err(FPGAArchParseError::DuplicateAttribute(a.to_string(), parser.position())),
}
},
"is_clock" => {
is_clock = match is_clock {
None => match a.value.as_ref() {
"0" => Some(false),
"1" => Some(true),
_ => return Err(FPGAArchParseError::AttributeParseError(format!("is_clock can only be 0 or 1, found: {}", a.value), parser.position())),
},
Some(_) => return Err(FPGAArchParseError::DuplicateAttribute(a.to_string(), parser.position())),
}
},
"clock" => {
clock = match clock {
None => Some(a.value.clone()),
Some(_) => return Err(FPGAArchParseError::DuplicateAttribute(a.to_string(), parser.position())),
}
},
"combinational_sink_ports" => {
combinational_sink_ports = match combinational_sink_ports {
None => Some(a.value.split_whitespace().map(str::to_string).collect()),
Some(_) => return Err(FPGAArchParseError::DuplicateAttribute(a.to_string(), parser.position())),
}
},
_ => return Err(FPGAArchParseError::UnknownAttribute(a.to_string(), parser.position())),
};
}
let name = match name {
Some(p) => p,
None => return Err(FPGAArchParseError::MissingRequiredAttribute("name".to_string(), parser.position())),
};
let is_clock = is_clock.unwrap_or(false);
let combinational_sink_ports = combinational_sink_ports.unwrap_or_default();

loop {
match parser.next() {
Ok(XmlEvent::StartElement { name, .. }) => {
return Err(FPGAArchParseError::InvalidTag(name.to_string(), parser.position()));
},
Ok(XmlEvent::EndElement { name }) => {
match name.to_string().as_ref() {
"port" => break,
_ => return Err(FPGAArchParseError::UnexpectedEndTag(name.to_string(), parser.position())),
}
},
Ok(XmlEvent::EndDocument) => {
return Err(FPGAArchParseError::UnexpectedEndOfDocument(name.to_string()));
},
Err(e) => {
return Err(FPGAArchParseError::XMLParseError(format!("{e:?}"), parser.position()));
},
_ => {},
};
}

Ok(ModelPort {
name,
is_clock,
clock,
combinational_sink_ports,
})
}

fn parse_model_port_list(tag_name: &OwnedName,
attributes: &[OwnedAttribute],
parser: &mut EventReader<BufReader<File>>) -> Result<Vec<ModelPort>, FPGAArchParseError> {
assert!(tag_name.to_string() == "input_ports" || tag_name.to_string() == "output_ports");
if !attributes.is_empty() {
return Err(FPGAArchParseError::UnknownAttribute(String::from("Expected to be empty"), parser.position()));
}

let mut ports: Vec<ModelPort> = Vec::new();
loop {
match parser.next() {
Ok(XmlEvent::StartElement { name, attributes, .. }) => {
match name.to_string().as_str() {
"port" => {
ports.push(parse_model_port(&name, &attributes, parser)?);
},
_ => return Err(FPGAArchParseError::InvalidTag(name.to_string(), parser.position())),
};
},
Ok(XmlEvent::EndElement { name }) => {
if name.to_string() == tag_name.to_string() {
break;
} else {
return Err(FPGAArchParseError::UnexpectedEndTag(name.to_string(), parser.position()));
}
},
Ok(XmlEvent::EndDocument) => {
return Err(FPGAArchParseError::UnexpectedEndOfDocument(tag_name.to_string()));
},
Err(e) => {
return Err(FPGAArchParseError::XMLParseError(format!("{e:?}"), parser.position()));
},
_ => {},
}
};

Ok(ports)
}

fn parse_model(tag_name: &OwnedName,
attributes: &[OwnedAttribute],
parser: &mut EventReader<BufReader<File>>) -> Result<Model, FPGAArchParseError> {
assert!(tag_name.to_string() == "model");

let mut name: Option<String> = None;
let mut never_prune: Option<bool> = None;
for a in attributes {
match a.name.to_string().as_ref() {
"name" => {
name = match name {
None => Some(a.value.clone()),
Some(_) => return Err(FPGAArchParseError::DuplicateAttribute(a.to_string(), parser.position())),
}
},
"never_prune" => {
never_prune = match never_prune {
None => match a.value.parse() {
Ok(v) => Some(v),
Err(e) => return Err(FPGAArchParseError::AttributeParseError(format!("{a}: {e}"), parser.position())),
},
Some(_) => return Err(FPGAArchParseError::DuplicateAttribute(a.to_string(), parser.position())),
}
},
_ => return Err(FPGAArchParseError::UnknownAttribute(a.to_string(), parser.position())),
};
}
let name = match name {
Some(p) => p,
None => return Err(FPGAArchParseError::MissingRequiredAttribute("name".to_string(), parser.position())),
};
let never_prune = never_prune.unwrap_or(false);

let mut input_ports: Option<Vec<ModelPort>> = None;
let mut output_ports: Option<Vec<ModelPort>> = None;
loop {
match parser.next() {
Ok(XmlEvent::StartElement { name, attributes, .. }) => {
match name.to_string().as_str() {
"input_ports" => {
input_ports = match input_ports {
None => Some(parse_model_port_list(&name, &attributes, parser)?),
Some(_) => return Err(FPGAArchParseError::DuplicateTag(name.to_string(), parser.position())),
}
},
"output_ports" => {
output_ports = match output_ports {
None => Some(parse_model_port_list(&name, &attributes, parser)?),
Some(_) => return Err(FPGAArchParseError::DuplicateTag(name.to_string(), parser.position())),
}
},
_ => return Err(FPGAArchParseError::InvalidTag(name.to_string(), parser.position())),
};
},
Ok(XmlEvent::EndElement { name }) => {
match name.to_string().as_str() {
"model" => break,
_ => return Err(FPGAArchParseError::UnexpectedEndTag(name.to_string(), parser.position())),
}
},
Ok(XmlEvent::EndDocument) => {
return Err(FPGAArchParseError::UnexpectedEndOfDocument(name.to_string()));
},
Err(e) => {
return Err(FPGAArchParseError::XMLParseError(format!("{e:?}"), parser.position()));
},
_ => {},
}
};
let input_ports = match input_ports {
Some(t) => t,
None => return Err(FPGAArchParseError::MissingRequiredTag("<input_ports>".to_string())),
};
let output_ports = match output_ports {
Some(t) => t,
None => return Err(FPGAArchParseError::MissingRequiredTag("<output_ports>".to_string())),
};

Ok(Model {
name,
never_prune,
input_ports,
output_ports,
})
}

pub fn parse_models(name: &OwnedName,
attributes: &[OwnedAttribute],
parser: &mut EventReader<BufReader<File>>) -> Result<Vec<Model>, FPGAArchParseError> {
assert!(name.to_string() == "models");
if !attributes.is_empty() {
return Err(FPGAArchParseError::UnknownAttribute(String::from("Expected to be empty"), parser.position()));
}

let mut models: Vec<Model> = Vec::new();
loop {
match parser.next() {
Ok(XmlEvent::StartElement { name, attributes, .. }) => {
match name.to_string().as_str() {
"model" => {
models.push(parse_model(&name, &attributes, parser)?);
},
_ => return Err(FPGAArchParseError::InvalidTag(name.to_string(), parser.position())),
};
},
Ok(XmlEvent::EndElement { name }) => {
match name.to_string().as_str() {
"models" => break,
_ => return Err(FPGAArchParseError::UnexpectedEndTag(name.to_string(), parser.position())),
}
},
Ok(XmlEvent::EndDocument) => {
return Err(FPGAArchParseError::UnexpectedEndOfDocument(name.to_string()));
},
Err(e) => {
return Err(FPGAArchParseError::XMLParseError(format!("{e:?}"), parser.position()));
},
_ => {},
}
};

Ok(models)
}
20 changes: 20 additions & 0 deletions fpga_arch_parser/tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ fn test_k4_n4_90nm_parse() -> Result<(), FPGAArchParseError> {

let res = fpga_arch_parser::parse(&input_xml)?;

// Check models.
assert_eq!(res.models.len(), 0);

// Check tiles.
assert_eq!(res.tiles.len(), 2);
assert_eq!(res.tiles[0].name,"io");
Expand Down Expand Up @@ -182,6 +185,23 @@ fn test_vtr_flagship_parse() -> Result<(), FPGAArchParseError> {

let res = fpga_arch_parser::parse(&input_xml)?;

// Check models.
assert_eq!(res.models.len(), 4);
let multiply_model = &res.models[0];
assert_eq!(multiply_model.name, "multiply");
assert_eq!(multiply_model.input_ports.len(), 2);
let multiply_model_port_a = &multiply_model.input_ports[0];
assert_eq!(multiply_model_port_a.name, "a");
assert_eq!(multiply_model_port_a.combinational_sink_ports.len(), 1);
assert_eq!(multiply_model_port_a.combinational_sink_ports[0], "out");
let multiply_model_port_b = &multiply_model.input_ports[1];
assert_eq!(multiply_model_port_b.name, "b");
assert_eq!(multiply_model_port_b.combinational_sink_ports.len(), 1);
assert_eq!(multiply_model_port_b.combinational_sink_ports[0], "out");
assert_eq!(multiply_model.output_ports.len(), 1);
let multiply_model_port_out = &multiply_model.output_ports[0];
assert_eq!(multiply_model_port_out.name, "out");

// Check tiles
let tiles = &res.tiles;
assert_eq!(tiles.len(), 4);
Expand Down