use std::collections::HashMap;
use crate::{
BsdlError,
string_helpers::{
find_first_comma_outside_paren, split_once_at_first_comma_outside_paren, strip_parenthesis,
strip_quotes,
},
};
#[derive(Debug, PartialEq, Clone)]
pub enum PortDirection {
In,
Out,
Buffer,
Inout,
Linkage,
Other(String),
}
#[derive(Debug)]
pub struct Port {
pub name: String,
pub pin: String,
pub dir: PortDirection,
pub grouped: Option<usize>,
pub aio_hp_time: Option<f32>,
pub aio_lp_time: Option<f32>,
pub cell_i: Option<usize>,
pub cell_o: Option<usize>,
}
impl Port {}
#[derive(Debug, Clone, Copy)]
struct VecRange {
begin: usize,
maxindex: usize,
reversed: bool,
}
impl VecRange {
fn from_str(s: &str) -> Result<VecRange, BsdlError> {
let bv = "bit_vector";
let pos = s.find(bv).ok_or(BsdlError::VecRangeError)?;
let range = strip_parenthesis(&s[pos + bv.len()..])?;
let reversed = range.find("downto").is_some();
let delimiter = match reversed {
true => "downto",
false => "to",
};
let (begin, end) = range
.split_once(delimiter)
.ok_or(BsdlError::VecRangeError)?;
let begin = begin.trim().parse::<usize>()?;
let end = end.trim().parse::<usize>()?;
let maxindex = match reversed {
true => begin.checked_sub(end),
false => end.checked_sub(begin),
}
.ok_or(BsdlError::VecRangeError)?;
Ok(VecRange {
begin,
maxindex,
reversed,
})
}
fn get_index(&self, i: usize) -> Result<usize, BsdlError> {
if i > self.maxindex {
return Err(BsdlError::VecRangeError);
}
if self.reversed {
Ok(self.begin - i)
} else {
Ok(self.begin + i)
}
}
}
#[cfg(test)]
mod test_vecrange {
use super::*;
#[test]
fn test_vecrange() {
let x = VecRange::from_str("bit_vector (3 downto 0)").unwrap();
assert!(x.get_index(0).unwrap() == 3);
assert!(x.get_index(3).unwrap() == 0);
assert!(x.get_index(4).is_err());
let x = VecRange::from_str("bit_vector (1 to 2)").unwrap();
assert!(x.get_index(0).unwrap() == 1);
assert!(x.get_index(1).unwrap() == 2);
assert!(x.get_index(2).is_err());
}
}
#[derive(Debug)]
pub struct Ports {
pub v: Vec<Port>,
pub d_port: HashMap<String, usize>,
pub d_pin: HashMap<String, usize>,
d_vecs: HashMap<String, VecRange>,
pub pinmap_applied: bool,
}
impl std::fmt::Display for Ports {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
for (i, v) in self.v.iter().enumerate() {
write!(f, "port{}: port = {:?}\n", i, v,)?;
}
write!(f, "")
}
}
impl Ports {
pub fn new() -> Ports {
Ports {
v: Vec::new(),
d_port: HashMap::new(),
d_pin: HashMap::new(),
d_vecs: HashMap::new(),
pinmap_applied: false,
}
}
pub fn from_str(s: &str) -> Result<Ports, BsdlError> {
let mut v = Vec::new();
let s = strip_parenthesis(s)?;
let mut d_vecs = HashMap::new();
for chunk in s.split(';') {
let chunk = chunk.trim();
let (name, rest) = chunk.split_once(':').ok_or(BsdlError::PortError)?;
let name = name.trim();
let (dir_str, rest) = rest
.trim()
.split_once(char::is_whitespace)
.ok_or_else(|| BsdlError::PortError)?;
let dir = match dir_str {
"in" => PortDirection::In,
"out" => PortDirection::Out,
"buffer" => PortDirection::Buffer,
"inout" => PortDirection::Inout,
"linkage" => PortDirection::Linkage,
_ => PortDirection::Other(dir_str.to_string()),
};
let rest = rest.trim();
if rest == "bit" {
for name in name.split(',') {
v.push(Port {
name: name.trim().to_uppercase(),
pin: String::new(),
dir: dir.clone(),
grouped: None,
aio_hp_time: None,
aio_lp_time: None,
cell_i: None,
cell_o: None,
});
}
} else {
let range = VecRange::from_str(rest)?;
let name = name.to_uppercase();
for i in 0..range.maxindex + 1 {
v.push(Port {
name: format!("{}({})", name, range.get_index(i)?),
pin: String::new(),
dir: dir.clone(),
grouped: None,
aio_hp_time: None,
aio_lp_time: None,
cell_i: None,
cell_o: None,
});
}
d_vecs.insert(name, range);
};
}
let mut d_port = HashMap::with_capacity(v.len());
let d_pin = HashMap::with_capacity(v.len());
for (i, val) in v.iter().enumerate() {
d_port.insert(val.name.clone(), i);
}
Ok(Ports {
v,
d_port,
d_pin,
d_vecs,
pinmap_applied: false,
})
}
pub fn apply_port_grouping(&mut self, s: &str) -> Result<(), BsdlError> {
let mut s = strip_quotes(s)?;
loop {
if s.len() == 0 {
return Ok(());
}
let comma = find_first_comma_outside_paren(s)?;
let (chunk, rest) = match comma {
None => (s, ""),
Some(comma) => (&s[..comma], &s[comma + 1..]),
};
s = rest.trim();
let openparen = chunk
.find('(')
.ok_or(BsdlError::ParseError(format!("PORT_GROUPING: expected (")))?;
let mut port_groups = strip_parenthesis(&chunk[openparen..])?;
loop {
if port_groups.is_empty() {
break;
}
let (port_group, rest) = split_once_at_first_comma_outside_paren(port_groups)?;
port_groups = rest;
let port_group = port_group.trim();
let port_group = strip_parenthesis(port_group)?;
let (a, b) = port_group.split_once(',').ok_or(BsdlError::NotFoundError)?;
let c = self.get_index_by_name_portgroup(&a.to_uppercase().as_str())?;
let d = self.get_index_by_name_portgroup(&b.to_uppercase().as_str())?;
self.v[c].grouped = Some(d);
self.v[d].grouped = Some(c);
}
}
}
fn get_index_by_name_portgroup(&self, name: &str) -> Result<usize, BsdlError> {
let name = name.trim();
let idx = self.d_port.get(name);
match idx {
Some(x) => Ok(*x),
_ => Err(BsdlError::ParseError(format!(
"PORT_GROUPING: {name} not found"
))),
}
}
fn apply_pinmap_scalar(&mut self, port: &str, pin: &str) -> Result<(), BsdlError> {
let portindex = *self.d_port.get(port).ok_or(BsdlError::ParseError(format!(
"PIN_MAP: port {port} not found"
)))?;
self.v[portindex].pin = pin.to_uppercase();
Ok(())
}
pub fn apply_pinmap(&mut self, s: &str) -> Result<(), BsdlError> {
let mut s = strip_quotes(s)?;
loop {
s = s.trim();
if s.len() == 0 {
break;
}
let (port, rest) = s.split_once(':').ok_or(BsdlError::PinMapErr)?;
let port = port.trim().to_uppercase();
let comma = find_first_comma_outside_paren(rest)?;
let pins = match comma {
Some(comma) => {
s = &rest[comma + 1..];
&rest[0..comma]
}
None => {
s = "";
rest
}
}
.trim();
if pins.ends_with(')') {
let pins = strip_parenthesis(pins)?;
let vecport = self
.d_vecs
.get(port.as_str())
.ok_or(BsdlError::VecRangeError)?;
let vecport = *vecport;
for (i, pin) in pins.split(',').enumerate() {
let idx = vecport.get_index(i)?;
let port = format!("{port}({idx})");
self.apply_pinmap_scalar(port.as_str(), pin.trim())?;
}
} else {
self.apply_pinmap_scalar(port.as_str(), pins)?;
}
}
self.pinmap_applied = true;
for (i, port) in self.v.iter().enumerate() {
self.d_pin.insert(port.pin.clone(), i);
}
Ok(())
}
}
#[cfg(test)]
mod test_ports {
use super::*;
#[test]
fn test_ports() {
let ports = Ports::from_str(
"(
VDd: linkage bit_vector (1 to 4);
GND: linkage bit;
CLOCK: in bit;
RESET_N: in bit;
D: inout bit_vector(0 to 3);
CSN: out bit;
SCK: out bit;
TMS: in bit;
TCK: in bit;
TDI: in bit;
TDO: out bit;
GT_RXN0: in bit;
GT_RXP0: in bit;
GT_TXN0: buffer bit;
GT_TXP0: buffer bit
)",
);
println!("port = {:?}", ports);
let mut ports = ports.unwrap();
let x = ports.apply_port_grouping(
"\"DIFFERENTIAL_VOLTAGE ((GT_RXP0, GT_RXN0), (GT_TXP0, GT_TXN0))\"",
);
ports.apply_pinmap("\"VDD: (1, 2, 18, 19), GND: 21, D: (3, 4, 5, 6), RESET_N: 13, GT_RXP0: 14, GT_RXP0: 15, GT_RXP0: 16, GT_RXP0: 17, CLOCK: 18, SCK: 7, CSN: 8, TMS: 9, TCK: 10, TDI: 11, TDO: 12\"").unwrap();
println!("x = {:?} port = {:?}", x, ports);
println!("d_pin: {:?}", ports.d_pin);
}
}