use crate::traits::Identify;
use crate::{util::log2_ceil, Error, NonNegative, PathName, Positive, Result};
use indexmap::IndexMap;
use std::str::FromStr;
use std::{
cmp::Ordering,
convert::{TryFrom, TryInto},
fmt,
};
pub type BitCount = Positive;
#[derive(Debug, Clone)]
pub struct Complexity {
level: Vec<NonNegative>,
}
impl Default for Complexity {
fn default() -> Self {
Complexity { level: vec![4] }
}
}
impl PartialEq for Complexity {
fn eq(&self, other: &Self) -> bool {
(0..self.level.len().max(other.level.len()))
.all(|idx| self.level.get(idx).unwrap_or(&0) == other.level.get(idx).unwrap_or(&0))
}
}
impl Eq for Complexity {}
impl PartialOrd for Complexity {
fn partial_cmp(&self, other: &Complexity) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Complexity {
fn cmp(&self, other: &Complexity) -> Ordering {
(0..self.level.len().max(other.level.len()))
.map(|idx| {
(
self.level.get(idx).unwrap_or(&0),
other.level.get(idx).unwrap_or(&0),
)
})
.fold(None, |ord, (i, j)| match ord {
Some(ord) => Some(ord),
None => {
if i == j {
None
} else {
Some(i.cmp(j))
}
}
})
.unwrap_or(Ordering::Equal)
}
}
impl From<NonNegative> for Complexity {
fn from(major: NonNegative) -> Self {
Complexity::new_major(major)
}
}
impl TryFrom<Vec<NonNegative>> for Complexity {
type Error = Error;
fn try_from(level: Vec<NonNegative>) -> Result<Self> {
Complexity::new(level)
}
}
impl FromStr for Complexity {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
Complexity::new(
s.split('.')
.map(|d| d.trim().parse::<NonNegative>())
.collect::<std::result::Result<Vec<_>, std::num::ParseIntError>>()
.map_err(|e| Error::InvalidArgument(e.to_string()))?,
)
}
}
impl Complexity {
pub fn new(level: impl IntoIterator<Item = NonNegative>) -> Result<Self> {
let level = level.into_iter().collect::<Vec<NonNegative>>();
if level.is_empty() {
Err(Error::InvalidArgument(
"complexity level cannot be empty".to_string(),
))
} else {
Ok(Complexity { level })
}
}
pub fn new_major(level: NonNegative) -> Self {
Complexity { level: vec![level] }
}
pub fn level(&self) -> &[NonNegative] {
self.level.as_ref()
}
pub fn major(&self) -> NonNegative {
self.level[0]
}
}
impl fmt::Display for Complexity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut result = String::new();
let mut level = self.level.iter().map(|x| x.to_string());
if let Some(x) = level.next() {
result.push_str(&x);
level.for_each(|x| {
result.push('.');
result.push_str(&x);
});
}
write!(f, "{}", result)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Fields(IndexMap<PathName, BitCount>);
impl Fields {
pub fn new(iter: impl IntoIterator<Item = (PathName, BitCount)>) -> Result<Self> {
let fields = iter.into_iter();
let (lower, upper) = fields.size_hint();
let mut map = IndexMap::with_capacity(upper.unwrap_or(lower));
for (path_name, bit_count) in fields {
map.insert(path_name, bit_count)
.map(|_| -> Result<()> { Err(Error::UnexpectedDuplicate) })
.transpose()?;
}
Ok(Fields(map))
}
pub(crate) fn new_empty() -> Self {
Fields(IndexMap::new())
}
pub(crate) fn insert(&mut self, path_name: PathName, bit_count: BitCount) -> Result<()> {
self.0
.insert(path_name, bit_count)
.map(|_| -> Result<()> { Err(Error::UnexpectedDuplicate) })
.transpose()?;
Ok(())
}
pub fn iter(&self) -> impl Iterator<Item = (&PathName, &BitCount)> {
self.0.iter()
}
pub fn keys(&self) -> impl Iterator<Item = &PathName> {
self.0.keys()
}
pub fn values(&self) -> impl Iterator<Item = &BitCount> {
self.0.values()
}
}
impl<'a> IntoIterator for &'a Fields {
type Item = (&'a PathName, &'a BitCount);
type IntoIter = indexmap::map::Iter<'a, PathName, BitCount>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct PhysicalStream {
element_fields: Fields,
element_lanes: Positive,
dimensionality: NonNegative,
complexity: Complexity,
user: Fields,
}
impl PhysicalStream {
pub fn try_new<T, U>(
element_fields: T,
element_lanes: usize,
dimensionality: usize,
complexity: impl Into<Complexity>,
user: T,
) -> Result<Self>
where
T: IntoIterator<Item = (U, usize)>,
U: TryInto<PathName, Error = Error>,
{
let element_fields = Fields::new(
element_fields
.into_iter()
.map(|(path_name, bit_count)| {
(
path_name.try_into(),
Positive::new(bit_count as NonNegative),
)
})
.map(|(path_name, bit_count)| match (path_name, bit_count) {
(Ok(path_name), Some(bit_count)) => Ok((path_name, bit_count)),
(Err(e), _) => Err(e),
(_, None) => Err(Error::InvalidArgument(
"element lanes cannot be zero".to_string(),
)),
})
.collect::<Result<Vec<_>>>()?,
)?;
let element_lanes = Positive::new(element_lanes as NonNegative)
.ok_or_else(|| Error::InvalidArgument("element lanes cannot be zero".to_string()))?;
let dimensionality = dimensionality as NonNegative;
let complexity = complexity.into();
let user = Fields::new(
user.into_iter()
.map(|(path_name, bit_count)| {
(
path_name.try_into(),
Positive::new(bit_count as NonNegative),
)
})
.map(|(path_name, bit_count)| match (path_name, bit_count) {
(Ok(path_name), Some(bit_count)) => Ok((path_name, bit_count)),
(Err(e), _) => Err(e),
(_, None) => Err(Error::InvalidArgument(
"element lanes cannot be zero".to_string(),
)),
})
.collect::<Result<Vec<_>>>()?,
)?;
Ok(PhysicalStream::new(
element_fields,
element_lanes,
dimensionality,
complexity,
user,
))
}
pub fn new(
element_fields: impl Into<Fields>,
element_lanes: Positive,
dimensionality: NonNegative,
complexity: impl Into<Complexity>,
user: impl Into<Fields>,
) -> Self {
PhysicalStream {
element_fields: element_fields.into(),
element_lanes,
dimensionality,
complexity: complexity.into(),
user: user.into(),
}
}
pub fn element_fields(&self) -> &Fields {
&self.element_fields
}
pub fn element_lanes(&self) -> Positive {
self.element_lanes
}
pub fn dimensionality(&self) -> NonNegative {
self.dimensionality
}
pub fn complexity(&self) -> &Complexity {
&self.complexity
}
pub fn user(&self) -> &Fields {
&self.user
}
pub fn data_bit_count(&self) -> NonNegative {
self.element_fields
.values()
.map(|b| b.get())
.sum::<NonNegative>()
* self.element_lanes.get()
}
pub fn last_bit_count(&self) -> NonNegative {
self.dimensionality
}
pub fn stai_bit_count(&self) -> NonNegative {
if self.complexity.major() >= 6 && self.element_lanes.get() > 1 {
log2_ceil(self.element_lanes)
} else {
0
}
}
pub fn endi_bit_count(&self) -> NonNegative {
if (self.complexity.major() >= 5 || self.dimensionality >= 1)
&& self.element_lanes.get() > 1
{
log2_ceil(self.element_lanes)
} else {
0
}
}
pub fn strb_bit_count(&self) -> NonNegative {
if self.complexity.major() >= 7 || self.dimensionality >= 1 {
self.element_lanes.get()
} else {
0
}
}
pub fn user_bit_count(&self) -> NonNegative {
self.user.values().map(|b| b.get()).sum::<NonNegative>()
}
pub fn signal_list(&self) -> SignalList {
let opt = |x| if x == 0 { None } else { Some(x) };
SignalList {
data: opt(self.data_bit_count()),
last: opt(self.last_bit_count()),
stai: opt(self.stai_bit_count()),
endi: opt(self.endi_bit_count()),
strb: opt(self.strb_bit_count()),
user: opt(self.user_bit_count()),
}
}
pub fn bit_count(&self) -> NonNegative {
self.data_bit_count()
+ self.last_bit_count()
+ self.stai_bit_count()
+ self.endi_bit_count()
+ self.strb_bit_count()
+ self.user_bit_count()
}
}
impl From<&PhysicalStream> for SignalList {
fn from(physical_stream: &PhysicalStream) -> SignalList {
physical_stream.signal_list()
}
}
impl From<PhysicalStream> for SignalList {
fn from(physical_stream: PhysicalStream) -> SignalList {
physical_stream.signal_list()
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Origin {
Source,
Sink,
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Width {
Scalar,
Vector(NonNegative),
}
#[derive(Debug, Clone, PartialEq)]
pub struct Signal {
name: String,
origin: Origin,
width: Width,
}
impl Identify for Signal {
fn identifier(&self) -> &str {
self.name.as_str()
}
}
impl Signal {
pub fn opt_vec(
name: impl Into<String>,
origin: Origin,
width: Option<NonNegative>,
) -> Option<Signal> {
match width {
None => None,
Some(w) => Some(Signal {
name: name.into(),
origin,
width: Width::Vector(w),
}),
}
}
pub fn vec(name: impl Into<String>, origin: Origin, width: Positive) -> Signal {
Signal {
name: name.into(),
origin,
width: Width::Vector(width.get()),
}
}
pub fn bit(name: impl Into<String>, origin: Origin) -> Signal {
Signal {
name: name.into(),
origin,
width: Width::Scalar,
}
}
pub fn reversed(&self) -> bool {
self.origin == Origin::Sink
}
pub fn origin(&self) -> Origin {
self.origin
}
pub fn width(&self) -> Width {
self.width
}
pub fn with_name(&self, name: String) -> Signal {
Signal {
name,
origin: self.origin,
width: self.width,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct SignalList {
data: Option<NonNegative>,
last: Option<NonNegative>,
stai: Option<NonNegative>,
endi: Option<NonNegative>,
strb: Option<NonNegative>,
user: Option<NonNegative>,
}
impl SignalList {
pub fn valid(&self) -> Signal {
Signal {
name: "valid".to_string(),
origin: Origin::Source,
width: Width::Scalar,
}
}
pub fn ready(&self) -> Signal {
Signal {
name: "ready".to_string(),
origin: Origin::Sink,
width: Width::Scalar,
}
}
pub fn data(&self) -> Option<Signal> {
Signal::opt_vec("data", Origin::Source, self.data)
}
pub fn last(&self) -> Option<Signal> {
Signal::opt_vec("last", Origin::Source, self.last)
}
pub fn stai(&self) -> Option<Signal> {
Signal::opt_vec("stai", Origin::Source, self.stai)
}
pub fn endi(&self) -> Option<Signal> {
Signal::opt_vec("endi", Origin::Source, self.endi)
}
pub fn strb(&self) -> Option<Signal> {
Signal::opt_vec("strb", Origin::Source, self.strb)
}
pub fn user(&self) -> Option<Signal> {
Signal::opt_vec("user", Origin::Source, self.user)
}
pub fn opt_bit_count(&self) -> Option<NonNegative> {
match self.data.unwrap_or(0)
+ self.last.unwrap_or(0)
+ self.stai.unwrap_or(0)
+ self.endi.unwrap_or(0)
+ self.strb.unwrap_or(0)
+ self.user.unwrap_or(0)
{
0 => None,
x => Some(x),
}
}
pub fn bit_count(&self) -> NonNegative {
self.opt_bit_count().unwrap_or(0)
}
}
impl<'a> IntoIterator for &'a SignalList {
type Item = Signal;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
[
Some(self.valid()),
Some(self.ready()),
self.data(),
self.last(),
self.stai(),
self.endi(),
self.strb(),
self.user(),
]
.iter()
.filter(|o| o.is_some())
.map(|s| s.clone().unwrap())
.collect::<Vec<_>>()
.into_iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::convert::TryInto;
#[test]
#[allow(clippy::cognitive_complexity)]
fn complexity() -> Result<()> {
use std::convert::TryInto;
let empty = Complexity::new(vec![]);
assert_eq!(
empty.unwrap_err().to_string(),
"Invalid argument: complexity level cannot be empty"
);
assert_eq!(
Complexity::try_from(vec![]).unwrap_err().to_string(),
"Invalid argument: complexity level cannot be empty"
);
let c = Complexity::new_major(0);
let c3 = Complexity::new_major(3);
let c30 = Complexity::new(vec![3, 0])?;
let c31 = Complexity::new(vec![3, 1])?;
let c311 = Complexity::new(vec![3, 1, 1])?;
let c32 = Complexity::new(vec![3, 2])?;
let c4 = Complexity::new_major(4);
let c400 = Complexity::new(vec![4, 0, 0])?;
let c401 = Complexity::new(vec![4, 0, 1])?;
assert!(c < c3);
assert!(c3 < c31);
assert!(!(c3 < c30));
assert!(!(c3 > c30));
assert_eq!(c3, c30);
assert!(c31 < c311);
assert!(c311 < c32);
assert!(c32 < c4);
assert_eq!(c4, c4);
assert_eq!(c4, c400);
assert_eq!(c400, c4);
assert!(!(c400 > c4));
assert!(!(c400 < c4));
assert!(c400 < c401);
assert!(c4 < c401);
assert_eq!(c3, 3.into());
assert_eq!(c401, vec![4, 0, 1].try_into()?);
assert_eq!(c3.to_string(), "3");
assert_eq!(c31.to_string(), "3.1");
assert_eq!(c3.major(), 3);
assert_eq!(c31.major(), 3);
assert_eq!(c4.major(), 4);
assert_eq!(c4.level(), &[4]);
assert_eq!(c400.level(), &[4, 0, 0]);
Ok(())
}
#[test]
#[allow(clippy::cognitive_complexity)]
fn physical_stream() -> Result<()> {
let physical_stream = PhysicalStream::new(
Fields::new(vec![
("a".try_into()?, BitCount::new(8).unwrap()),
("b".try_into()?, BitCount::new(16).unwrap()),
("c".try_into()?, BitCount::new(1).unwrap()),
])?,
Positive::new(3).unwrap(),
4,
8,
Fields::new(vec![("user".try_into()?, BitCount::new(1).unwrap())])?,
);
let mut element = physical_stream.element_fields().iter();
assert_eq!(
element.next(),
Some((&("a".try_into()?), &BitCount::new(8).unwrap()))
);
assert_eq!(
element.next(),
Some((&("b".try_into()?), &BitCount::new(16).unwrap()))
);
assert_eq!(
element.next(),
Some((&("c".try_into()?), &BitCount::new(1).unwrap()))
);
assert_eq!(element.next(), None);
assert_eq!(physical_stream.element_lanes(), Positive::new(3).unwrap());
assert_eq!(physical_stream.dimensionality(), 4);
assert_eq!(physical_stream.complexity(), &Complexity::new_major(8));
assert_eq!(
physical_stream.user().iter().next().unwrap(),
(&("user".try_into()?), &BitCount::new(1).unwrap())
);
assert_eq!(physical_stream.bit_count(), 87);
assert_eq!(physical_stream.data_bit_count(), (8 + 16 + 1) * 3);
assert_eq!(physical_stream.last_bit_count(), 4);
assert_eq!(physical_stream.stai_bit_count(), 2);
assert_eq!(physical_stream.endi_bit_count(), 2);
assert_eq!(physical_stream.strb_bit_count(), 3);
assert_eq!(physical_stream.user_bit_count(), 1);
assert_eq!(
physical_stream.signal_list(),
SignalList {
data: Some(75),
last: Some(4),
stai: Some(2),
endi: Some(2),
strb: Some(3),
user: Some(1)
}
);
let physical_stream = PhysicalStream::new(
Fields::new(vec![("a".try_into()?, BitCount::new(8).unwrap())])?,
Positive::new(1).unwrap(),
0,
0,
Fields::new(vec![])?,
);
assert_eq!(physical_stream.element_fields().iter().count(), 1);
assert_eq!(physical_stream.element_lanes(), Positive::new(1).unwrap());
assert_eq!(physical_stream.dimensionality(), 0);
assert_eq!(physical_stream.complexity(), &Complexity::new_major(0));
assert_eq!(physical_stream.user().iter().next(), None);
assert_eq!(physical_stream.bit_count(), 8);
assert_eq!(physical_stream.data_bit_count(), 8);
assert_eq!(physical_stream.last_bit_count(), 0);
assert_eq!(physical_stream.stai_bit_count(), 0);
assert_eq!(physical_stream.endi_bit_count(), 0);
assert_eq!(physical_stream.strb_bit_count(), 0);
assert_eq!(physical_stream.user_bit_count(), 0);
assert_eq!(
physical_stream.signal_list(),
SignalList {
data: Some(8),
last: None,
stai: None,
endi: None,
strb: None,
user: None
}
);
Ok(())
}
#[test]
fn signal_list() -> Result<()> {
let physical_stream = PhysicalStream::new(
Fields::new(vec![
("a".try_into()?, BitCount::new(3).unwrap()),
("b".try_into()?, BitCount::new(2).unwrap()),
])?,
Positive::new(2).unwrap(),
3,
8,
Fields::new(vec![])?,
);
let signal_list = SignalList::from(&physical_stream);
assert_eq!(physical_stream.bit_count(), 17);
assert_eq!(physical_stream.data_bit_count(), 2 * (3 + 2));
assert_eq!(physical_stream.last_bit_count(), 3);
assert_eq!(physical_stream.stai_bit_count(), 1);
assert_eq!(physical_stream.endi_bit_count(), 1);
assert_eq!(physical_stream.strb_bit_count(), 2);
assert_eq!(physical_stream.user_bit_count(), 0);
assert_eq!(
Width::Vector(physical_stream.data_bit_count()),
signal_list.data().unwrap().width
);
assert_eq!(
Width::Vector(physical_stream.last_bit_count()),
signal_list.last().unwrap().width
);
assert_eq!(
Width::Vector(physical_stream.stai_bit_count()),
signal_list.stai().unwrap().width
);
assert_eq!(
Width::Vector(physical_stream.endi_bit_count()),
signal_list.endi().unwrap().width
);
assert_eq!(
Width::Vector(physical_stream.strb_bit_count()),
signal_list.strb().unwrap().width
);
assert_eq!(
Width::Vector(physical_stream.user_bit_count()),
Width::Vector(0)
);
assert_eq!(signal_list.opt_bit_count(), Some(17));
assert_eq!(signal_list.bit_count(), 17);
assert_eq!(signal_list, SignalList::from(physical_stream));
assert_eq!(
signal_list.into_iter().collect::<Vec<_>>(),
vec![
Signal::bit("valid", Origin::Source),
Signal::bit("ready", Origin::Sink),
Signal::opt_vec("data", Origin::Source, Some(10)).unwrap(),
Signal::opt_vec("last", Origin::Source, Some(3)).unwrap(),
Signal::opt_vec("stai", Origin::Source, Some(1)).unwrap(),
Signal::opt_vec("endi", Origin::Source, Some(1)).unwrap(),
Signal::opt_vec("strb", Origin::Source, Some(2)).unwrap(),
]
);
Ok(())
}
}