Skip to main content

tydi/
physical.rs

1//! Physical streams.
2//!
3//! This modules defines the components of physical streams as described in the
4//! [Tydi specification].
5//!
6//! This modules defines the following types:
7//! - [`Complexity`] the interface complexity level.
8//!   [Reference](https://abs-tudelft.github.io/tydi/specification/physical.html#complexity-c)
9//! - [`Fields`] the fields of a physical stream.
10//!   [Reference](https://abs-tudelft.github.io/tydi/specification/physical.html#element-content-e-and-usertransfer-content-u)
11//! - [`PhysicalStream`] a physical stream.
12//!   [Reference](https://abs-tudelft.github.io/tydi/specification/physical.html#physical-stream-specification)
13//! - [`SignalList`] a signal list for the signals in a physical stream.
14//!   [Reference](https://abs-tudelft.github.io/tydi/specification/physical.html#signals)
15//!
16//! # Examples
17//!
18//! ## Minimal example
19//!
20//! ```rust
21//! use tydi::physical::{PhysicalStream, SignalList};
22//!
23//! // Construct a new physical stream with two elements, named "a" and "b".
24//! // The stream has two elements lanes, no dimensionality data, a complexity
25//! // of (major) level 2, and no user fields.
26//! let physical_stream =
27//!     PhysicalStream::try_new(vec![("a", 4), ("b", 8)], 2, 0, 2, vec![])?;
28//!
29//! // Get the signal list for the physical stream.
30//! let signal_list = physical_stream.signal_list();
31//!
32//! // Validate the signal list bit count. It should equal to (4 + 8) * 2.
33//! assert_eq!(signal_list.bit_count(), 24);
34//!
35//! // For a complexity level of 8 there should be 4 additional signals.
36//! // (2 `strb`, 1 `stai`, 1 `endi`).
37//! let signal_list =
38//!     SignalList::from(
39//!         PhysicalStream::try_new(vec![("a", 4), ("b", 8)], 2, 0, 8, vec![])?
40//!     );
41//! assert_eq!(signal_list.bit_count(), 28);
42//!
43//! # Ok::<(), Box<dyn std::error::Error>>(())
44//! ```
45//!
46//! [`Complexity`]: ./struct.Complexity.html
47//! [`Fields`]: ./struct.Fields.html
48//! [`PhysicalStream`]: ./struct.PhysicalStream.html
49//! [`SignalMap`]: ./struct.SignalMap.html
50//! [Tydi specification]: https://abs-tudelft.github.io/tydi/specification/physical.html
51
52use crate::traits::Identify;
53use crate::{util::log2_ceil, Error, NonNegative, PathName, Positive, Result};
54use indexmap::IndexMap;
55use std::str::FromStr;
56use std::{
57    cmp::Ordering,
58    convert::{TryFrom, TryInto},
59    fmt,
60};
61
62/// Positive number of bits.
63pub type BitCount = Positive;
64
65/// Interface complexity level.
66///
67/// This logical stream parameter specifies the guarantees a source makes about
68/// how elements are transferred. Equivalently, it specifies the assumptions a
69/// sink can safely make.
70///
71/// # Examples
72///
73/// ```rust
74/// use tydi::physical::Complexity;
75///
76/// let c3 = Complexity::new_major(3);
77/// let c30 = Complexity::new(vec![3, 0])?;
78/// let c31 = Complexity::new(vec![3, 1])?;
79/// let c4 = Complexity::new_major(4);
80///
81/// assert_eq!(c3, c30);
82/// assert!(c3 < c31);
83/// assert!(c31 < c4);
84///
85/// assert_eq!(c31.to_string(), "3.1");
86/// # Ok::<(), Box<dyn std::error::Error>>(())
87/// ```
88///
89/// [Reference]
90///
91/// [Reference]: https://abs-tudelft.github.io/tydi/specification/physical.html#complexity-c
92#[derive(Debug, Clone)]
93pub struct Complexity {
94    /// The complexity level.
95    level: Vec<NonNegative>,
96}
97
98impl Default for Complexity {
99    fn default() -> Self {
100        Complexity { level: vec![4] }
101    }
102}
103
104impl PartialEq for Complexity {
105    /// A complexity number is higher than another when the leftmost integer is
106    /// greater, and lower when the leftmost integer is lower. If the leftmost
107    /// integer is equal, the next integer is checked recursively. If one
108    /// complexity number has more entries than another, the shorter number is
109    /// padded with zeros on the right.
110    fn eq(&self, other: &Self) -> bool {
111        (0..self.level.len().max(other.level.len()))
112            .all(|idx| self.level.get(idx).unwrap_or(&0) == other.level.get(idx).unwrap_or(&0))
113    }
114}
115
116impl Eq for Complexity {}
117
118impl PartialOrd for Complexity {
119    fn partial_cmp(&self, other: &Complexity) -> Option<Ordering> {
120        Some(self.cmp(other))
121    }
122}
123
124impl Ord for Complexity {
125    /// A complexity number is higher than another when the leftmost integer is
126    /// greater, and lower when the leftmost integer is lower. If the leftmost
127    /// integer is equal, the next integer is checked recursively. If one
128    /// complexity number has more entries than another, the shorter number is
129    /// padded with zeros on the right.
130    fn cmp(&self, other: &Complexity) -> Ordering {
131        (0..self.level.len().max(other.level.len()))
132            .map(|idx| {
133                (
134                    self.level.get(idx).unwrap_or(&0),
135                    other.level.get(idx).unwrap_or(&0),
136                )
137            })
138            .fold(None, |ord, (i, j)| match ord {
139                Some(ord) => Some(ord),
140                None => {
141                    if i == j {
142                        None
143                    } else {
144                        Some(i.cmp(j))
145                    }
146                }
147            })
148            .unwrap_or(Ordering::Equal)
149    }
150}
151
152impl From<NonNegative> for Complexity {
153    /// Convert a NonNegative into complexity with the NonNegative as major version.
154    fn from(major: NonNegative) -> Self {
155        Complexity::new_major(major)
156    }
157}
158
159impl TryFrom<Vec<NonNegative>> for Complexity {
160    type Error = Error;
161    /// Try to convert a vector of NonNegative into a complexity. Returns an
162    /// error when the provided vector is empty.
163    fn try_from(level: Vec<NonNegative>) -> Result<Self> {
164        Complexity::new(level)
165    }
166}
167
168impl FromStr for Complexity {
169    type Err = Error;
170
171    fn from_str(s: &str) -> Result<Self> {
172        Complexity::new(
173            // split string into string slices
174            s.split('.')
175                // convert slices to nonnegatives after trimming whitespace
176                .map(|d| d.trim().parse::<NonNegative>())
177                // convert to result with vector of nonnegatives
178                .collect::<std::result::Result<Vec<_>, std::num::ParseIntError>>()
179                // convert potential error to tydi error
180                .map_err(|e| Error::InvalidArgument(e.to_string()))?,
181        )
182    }
183}
184
185impl Complexity {
186    /// Constructs a new Complexity with provided level. Returns an error when
187    /// the provided level iterator is empty.
188    ///
189    /// # Examples
190    ///
191    /// ```rust
192    /// use tydi::physical::Complexity;
193    ///
194    /// let c = Complexity::new(vec![1, 2, 3, 4])?;
195    /// assert!(Complexity::new(vec![]).is_err());
196    /// # Ok::<(), Box<dyn std::error::Error>>(())
197    /// ```
198    pub fn new(level: impl IntoIterator<Item = NonNegative>) -> Result<Self> {
199        let level = level.into_iter().collect::<Vec<NonNegative>>();
200        if level.is_empty() {
201            Err(Error::InvalidArgument(
202                "complexity level cannot be empty".to_string(),
203            ))
204        } else {
205            Ok(Complexity { level })
206        }
207    }
208
209    /// Constructs a new Complexity with provided level as major version.
210    ///
211    /// # Examples
212    ///
213    /// ```rust
214    /// use tydi::physical::Complexity;
215    ///
216    /// let c = Complexity::new_major(4);
217    ///
218    /// assert_eq!(c, Complexity::new(vec![4])?);
219    /// # Ok::<(), Box<dyn std::error::Error>>(())
220    /// ```
221    pub fn new_major(level: NonNegative) -> Self {
222        Complexity { level: vec![level] }
223    }
224
225    /// Returns the level of this Complexity.
226    ///
227    /// # Examples
228    ///
229    /// ```rust
230    /// use tydi::physical::Complexity;
231    ///
232    /// let c = Complexity::new(vec![3, 14])?;
233    /// assert_eq!(c.level(), &[3, 14]);
234    /// # Ok::<(), Box<dyn std::error::Error>>(())
235    /// ```
236    pub fn level(&self) -> &[NonNegative] {
237        self.level.as_ref()
238    }
239
240    /// Returns the major version of this Complexity level.
241    ///
242    /// # Examples
243    ///
244    /// ```rust
245    /// use tydi::physical::Complexity;
246    ///
247    /// let c = Complexity::new(vec![3, 14])?;
248    /// assert_eq!(c.major(), 3);
249    /// # Ok::<(), Box<dyn std::error::Error>>(())
250    pub fn major(&self) -> NonNegative {
251        self.level[0]
252    }
253}
254
255impl fmt::Display for Complexity {
256    /// Display a complexity level as a version number. The levels are
257    /// separated by periods.
258    ///
259    /// # Examples
260    ///
261    /// ```rust
262    /// use tydi::physical::Complexity;
263    ///
264    /// let c = Complexity::new(vec![3, 14])?;
265    /// assert_eq!(c.to_string(), "3.14");
266    /// # Ok::<(), Box<dyn std::error::Error>>(())
267    /// ```
268    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269        let mut result = String::new();
270        let mut level = self.level.iter().map(|x| x.to_string());
271        if let Some(x) = level.next() {
272            result.push_str(&x);
273            level.for_each(|x| {
274                result.push('.');
275                result.push_str(&x);
276            });
277        }
278        write!(f, "{}", result)
279    }
280}
281
282#[derive(Debug, Clone, PartialEq)]
283pub struct Fields(IndexMap<PathName, BitCount>);
284
285impl Fields {
286    pub fn new(iter: impl IntoIterator<Item = (PathName, BitCount)>) -> Result<Self> {
287        let fields = iter.into_iter();
288        let (lower, upper) = fields.size_hint();
289        let mut map = IndexMap::with_capacity(upper.unwrap_or(lower));
290
291        for (path_name, bit_count) in fields {
292            map.insert(path_name, bit_count)
293                .map(|_| -> Result<()> { Err(Error::UnexpectedDuplicate) })
294                .transpose()?;
295        }
296
297        Ok(Fields(map))
298    }
299
300    pub(crate) fn new_empty() -> Self {
301        Fields(IndexMap::new())
302    }
303
304    pub(crate) fn insert(&mut self, path_name: PathName, bit_count: BitCount) -> Result<()> {
305        self.0
306            .insert(path_name, bit_count)
307            .map(|_| -> Result<()> { Err(Error::UnexpectedDuplicate) })
308            .transpose()?;
309        Ok(())
310    }
311
312    pub fn iter(&self) -> impl Iterator<Item = (&PathName, &BitCount)> {
313        self.0.iter()
314    }
315
316    pub fn keys(&self) -> impl Iterator<Item = &PathName> {
317        self.0.keys()
318    }
319
320    pub fn values(&self) -> impl Iterator<Item = &BitCount> {
321        self.0.values()
322    }
323}
324
325impl<'a> IntoIterator for &'a Fields {
326    type Item = (&'a PathName, &'a BitCount);
327    type IntoIter = indexmap::map::Iter<'a, PathName, BitCount>;
328
329    fn into_iter(self) -> Self::IntoIter {
330        self.0.iter()
331    }
332}
333
334/// Physical stream.
335///
336/// A physical stream carries a stream of elements, dimensionality information
337/// for said elements, and (optionally) user-defined transfer information from
338/// a source to a sink.
339///
340/// [Reference]
341///
342/// [Reference]: https://abs-tudelft.github.io/tydi/specification/physical.html#physical-stream-specification
343#[derive(Debug, Clone, PartialEq)]
344pub struct PhysicalStream {
345    /// Element content.
346    element_fields: Fields,
347    /// Number of element lanes.
348    element_lanes: Positive,
349    /// Dimensionality.
350    dimensionality: NonNegative,
351    /// Complexity.
352    complexity: Complexity,
353    /// User-defined transfer content.
354    user: Fields,
355}
356
357impl PhysicalStream {
358    pub fn try_new<T, U>(
359        element_fields: T,
360        element_lanes: usize,
361        dimensionality: usize,
362        complexity: impl Into<Complexity>,
363        user: T,
364    ) -> Result<Self>
365    where
366        T: IntoIterator<Item = (U, usize)>,
367        U: TryInto<PathName, Error = Error>,
368    {
369        let element_fields = Fields::new(
370            element_fields
371                .into_iter()
372                .map(|(path_name, bit_count)| {
373                    (
374                        path_name.try_into(),
375                        Positive::new(bit_count as NonNegative),
376                    )
377                })
378                .map(|(path_name, bit_count)| match (path_name, bit_count) {
379                    (Ok(path_name), Some(bit_count)) => Ok((path_name, bit_count)),
380                    (Err(e), _) => Err(e),
381                    (_, None) => Err(Error::InvalidArgument(
382                        "element lanes cannot be zero".to_string(),
383                    )),
384                })
385                .collect::<Result<Vec<_>>>()?,
386        )?;
387        let element_lanes = Positive::new(element_lanes as NonNegative)
388            .ok_or_else(|| Error::InvalidArgument("element lanes cannot be zero".to_string()))?;
389        let dimensionality = dimensionality as NonNegative;
390        let complexity = complexity.into();
391        let user = Fields::new(
392            user.into_iter()
393                .map(|(path_name, bit_count)| {
394                    (
395                        path_name.try_into(),
396                        Positive::new(bit_count as NonNegative),
397                    )
398                })
399                .map(|(path_name, bit_count)| match (path_name, bit_count) {
400                    (Ok(path_name), Some(bit_count)) => Ok((path_name, bit_count)),
401                    (Err(e), _) => Err(e),
402                    (_, None) => Err(Error::InvalidArgument(
403                        "element lanes cannot be zero".to_string(),
404                    )),
405                })
406                .collect::<Result<Vec<_>>>()?,
407        )?;
408        Ok(PhysicalStream::new(
409            element_fields,
410            element_lanes,
411            dimensionality,
412            complexity,
413            user,
414        ))
415    }
416    /// Constructs a new PhysicalStream using provided arguments. Returns an
417    /// error when provided argument are not valid.
418    pub fn new(
419        element_fields: impl Into<Fields>,
420        element_lanes: Positive,
421        dimensionality: NonNegative,
422        complexity: impl Into<Complexity>,
423        user: impl Into<Fields>,
424    ) -> Self {
425        PhysicalStream {
426            element_fields: element_fields.into(),
427            element_lanes,
428            dimensionality,
429            complexity: complexity.into(),
430            user: user.into(),
431        }
432    }
433
434    /// Returns the element fields in this physical stream.
435    pub fn element_fields(&self) -> &Fields {
436        &self.element_fields
437    }
438
439    /// Returns the number of element lanes in this physical stream.
440    pub fn element_lanes(&self) -> Positive {
441        self.element_lanes
442    }
443
444    /// Returns the dimensionality of this physical stream.
445    pub fn dimensionality(&self) -> NonNegative {
446        self.dimensionality
447    }
448
449    /// Returns the complexity of this physical stream.
450    pub fn complexity(&self) -> &Complexity {
451        &self.complexity
452    }
453
454    /// Returns the user fields in this physical stream.
455    pub fn user(&self) -> &Fields {
456        &self.user
457    }
458
459    /// Returns the bit count of the data (element) fields in this physical
460    /// stream. The bit count is equal to the combined bit count of all fields
461    /// multiplied by the number of lanes.
462    pub fn data_bit_count(&self) -> NonNegative {
463        self.element_fields
464            .values()
465            .map(|b| b.get())
466            .sum::<NonNegative>()
467            * self.element_lanes.get()
468    }
469
470    /// Returns the number of last bits in this physical stream. The number of
471    /// last bits equals the dimensionality.
472    pub fn last_bit_count(&self) -> NonNegative {
473        self.dimensionality
474    }
475
476    /// Returns the number of `stai` (start index) bits in this physical
477    /// stream.
478    pub fn stai_bit_count(&self) -> NonNegative {
479        if self.complexity.major() >= 6 && self.element_lanes.get() > 1 {
480            log2_ceil(self.element_lanes)
481        } else {
482            0
483        }
484    }
485
486    /// Returns the number of `endi` (end index) bits in this physical stream.
487    pub fn endi_bit_count(&self) -> NonNegative {
488        if (self.complexity.major() >= 5 || self.dimensionality >= 1)
489            && self.element_lanes.get() > 1
490        {
491            log2_ceil(self.element_lanes)
492        } else {
493            0
494        }
495    }
496
497    /// Returns the number of `strb` (strobe) bits in this physical stream.
498    pub fn strb_bit_count(&self) -> NonNegative {
499        if self.complexity.major() >= 7 || self.dimensionality >= 1 {
500            self.element_lanes.get()
501        } else {
502            0
503        }
504    }
505
506    /// Returns the bit count of the user fields in this physical stream.
507    pub fn user_bit_count(&self) -> NonNegative {
508        self.user.values().map(|b| b.get()).sum::<NonNegative>()
509    }
510
511    /// Returns the signal list for this physical stream.
512    pub fn signal_list(&self) -> SignalList {
513        let opt = |x| if x == 0 { None } else { Some(x) };
514        SignalList {
515            data: opt(self.data_bit_count()),
516            last: opt(self.last_bit_count()),
517            stai: opt(self.stai_bit_count()),
518            endi: opt(self.endi_bit_count()),
519            strb: opt(self.strb_bit_count()),
520            user: opt(self.user_bit_count()),
521        }
522    }
523
524    /// Returns the combined bit count of all signals in this physical stream.
525    /// This excludes the `valid` and `ready` signals.
526    pub fn bit_count(&self) -> NonNegative {
527        self.data_bit_count()
528            + self.last_bit_count()
529            + self.stai_bit_count()
530            + self.endi_bit_count()
531            + self.strb_bit_count()
532            + self.user_bit_count()
533    }
534}
535
536impl From<&PhysicalStream> for SignalList {
537    fn from(physical_stream: &PhysicalStream) -> SignalList {
538        physical_stream.signal_list()
539    }
540}
541
542impl From<PhysicalStream> for SignalList {
543    fn from(physical_stream: PhysicalStream) -> SignalList {
544        physical_stream.signal_list()
545    }
546}
547
548#[derive(Debug, Copy, Clone, PartialEq)]
549pub enum Origin {
550    Source,
551    Sink,
552}
553
554#[derive(Debug, Copy, Clone, PartialEq)]
555pub enum Width {
556    /// Non-vectorized single bit.
557    Scalar,
558    /// Vectorized multiple bits.
559    Vector(NonNegative),
560}
561
562#[derive(Debug, Clone, PartialEq)]
563pub struct Signal {
564    name: String,
565    origin: Origin,
566    width: Width,
567}
568
569impl Identify for Signal {
570    fn identifier(&self) -> &str {
571        self.name.as_str()
572    }
573}
574
575impl Signal {
576    /// Returns a vector-style signal if the input width is Some(NonNegative)
577    pub fn opt_vec(
578        name: impl Into<String>,
579        origin: Origin,
580        width: Option<NonNegative>,
581    ) -> Option<Signal> {
582        match width {
583            None => None,
584            Some(w) => Some(Signal {
585                name: name.into(),
586                origin,
587                width: Width::Vector(w),
588            }),
589        }
590    }
591
592    /// Returns a vector-style signal.
593    pub fn vec(name: impl Into<String>, origin: Origin, width: Positive) -> Signal {
594        Signal {
595            name: name.into(),
596            origin,
597            width: Width::Vector(width.get()),
598        }
599    }
600
601    /// Returns a single bit non-vector style signal.
602    pub fn bit(name: impl Into<String>, origin: Origin) -> Signal {
603        Signal {
604            name: name.into(),
605            origin,
606            width: Width::Scalar,
607        }
608    }
609
610    /// Returns whether the signal is reversed w.r.t. the source
611    pub fn reversed(&self) -> bool {
612        self.origin == Origin::Sink
613    }
614
615    pub fn origin(&self) -> Origin {
616        self.origin
617    }
618
619    pub fn width(&self) -> Width {
620        self.width
621    }
622
623    pub fn with_name(&self, name: String) -> Signal {
624        Signal {
625            name,
626            origin: self.origin,
627            width: self.width,
628        }
629    }
630}
631
632/// Signal list for the signals in a physical stream.
633///
634/// A signal list can be constructed from a [`PhysicalStream`] using the
635/// [`signal_list`] method or using the `From`/`Into` trait implementation.
636///
637/// [Reference]
638///
639/// [`PhysicalStream`]: ./struct.PhysicalStream.html
640/// [`signal_list`]: ./struct.PhysicalStream.html#method.signal_list
641/// [Reference]: https://abs-tudelft.github.io/tydi/specification/physical.html#signals
642#[derive(Debug, Copy, Clone, PartialEq)]
643pub struct SignalList {
644    data: Option<NonNegative>,
645    last: Option<NonNegative>,
646    stai: Option<NonNegative>,
647    endi: Option<NonNegative>,
648    strb: Option<NonNegative>,
649    user: Option<NonNegative>,
650}
651
652impl SignalList {
653    /// Returns the valid signal.
654    pub fn valid(&self) -> Signal {
655        Signal {
656            name: "valid".to_string(),
657            origin: Origin::Source,
658            width: Width::Scalar,
659        }
660    }
661
662    /// Returns the ready signal.
663    pub fn ready(&self) -> Signal {
664        Signal {
665            name: "ready".to_string(),
666            origin: Origin::Sink,
667            width: Width::Scalar,
668        }
669    }
670
671    /// Returns the `data` signal, if applicable for this PhysicalStream.
672    pub fn data(&self) -> Option<Signal> {
673        Signal::opt_vec("data", Origin::Source, self.data)
674    }
675
676    /// Returns the `last` signal, if applicable for this PhysicalStream.
677    pub fn last(&self) -> Option<Signal> {
678        Signal::opt_vec("last", Origin::Source, self.last)
679    }
680
681    /// Returns the `stai` signal, if applicable for this PhysicalStream.
682    pub fn stai(&self) -> Option<Signal> {
683        Signal::opt_vec("stai", Origin::Source, self.stai)
684    }
685
686    /// Returns the `endi` signal, if applicable for this PhysicalStream.
687    pub fn endi(&self) -> Option<Signal> {
688        Signal::opt_vec("endi", Origin::Source, self.endi)
689    }
690
691    /// Returns the `strb` signal, if applicable for this PhysicalStream.
692    pub fn strb(&self) -> Option<Signal> {
693        Signal::opt_vec("strb", Origin::Source, self.strb)
694    }
695
696    /// Returns the `user` signal, if applicable for this PhysicalStream.
697    pub fn user(&self) -> Option<Signal> {
698        Signal::opt_vec("user", Origin::Source, self.user)
699    }
700
701    /// Returns the bit count of all combined signals in this map.
702    pub fn opt_bit_count(&self) -> Option<NonNegative> {
703        match self.data.unwrap_or(0)
704            + self.last.unwrap_or(0)
705            + self.stai.unwrap_or(0)
706            + self.endi.unwrap_or(0)
707            + self.strb.unwrap_or(0)
708            + self.user.unwrap_or(0)
709        {
710            0 => None,
711            x => Some(x),
712        }
713    }
714
715    /// Returns the bit count of all combined signals in this map.
716    pub fn bit_count(&self) -> NonNegative {
717        self.opt_bit_count().unwrap_or(0)
718    }
719}
720
721impl<'a> IntoIterator for &'a SignalList {
722    type Item = Signal;
723    type IntoIter = std::vec::IntoIter<Self::Item>;
724
725    fn into_iter(self) -> Self::IntoIter {
726        [
727            Some(self.valid()),
728            Some(self.ready()),
729            self.data(),
730            self.last(),
731            self.stai(),
732            self.endi(),
733            self.strb(),
734            self.user(),
735        ]
736        .iter()
737        .filter(|o| o.is_some())
738        .map(|s| s.clone().unwrap())
739        .collect::<Vec<_>>()
740        .into_iter()
741    }
742}
743
744#[cfg(test)]
745mod tests {
746    use super::*;
747    use std::convert::TryInto;
748
749    #[test]
750    #[allow(clippy::cognitive_complexity)]
751    fn complexity() -> Result<()> {
752        use std::convert::TryInto;
753
754        let empty = Complexity::new(vec![]);
755        assert_eq!(
756            empty.unwrap_err().to_string(),
757            "Invalid argument: complexity level cannot be empty"
758        );
759        assert_eq!(
760            Complexity::try_from(vec![]).unwrap_err().to_string(),
761            "Invalid argument: complexity level cannot be empty"
762        );
763
764        let c = Complexity::new_major(0);
765        let c3 = Complexity::new_major(3);
766        let c30 = Complexity::new(vec![3, 0])?;
767        let c31 = Complexity::new(vec![3, 1])?;
768        let c311 = Complexity::new(vec![3, 1, 1])?;
769        let c32 = Complexity::new(vec![3, 2])?;
770        let c4 = Complexity::new_major(4);
771        let c400 = Complexity::new(vec![4, 0, 0])?;
772        let c401 = Complexity::new(vec![4, 0, 1])?;
773        assert!(c < c3);
774        assert!(c3 < c31);
775        assert!(!(c3 < c30));
776        assert!(!(c3 > c30));
777        assert_eq!(c3, c30);
778        assert!(c31 < c311);
779        assert!(c311 < c32);
780        assert!(c32 < c4);
781        assert_eq!(c4, c4);
782        assert_eq!(c4, c400);
783        assert_eq!(c400, c4);
784        assert!(!(c400 > c4));
785        assert!(!(c400 < c4));
786        assert!(c400 < c401);
787        assert!(c4 < c401);
788        assert_eq!(c3, 3.into());
789        assert_eq!(c401, vec![4, 0, 1].try_into()?);
790
791        assert_eq!(c3.to_string(), "3");
792        assert_eq!(c31.to_string(), "3.1");
793
794        assert_eq!(c3.major(), 3);
795        assert_eq!(c31.major(), 3);
796        assert_eq!(c4.major(), 4);
797
798        assert_eq!(c4.level(), &[4]);
799        assert_eq!(c400.level(), &[4, 0, 0]);
800        Ok(())
801    }
802
803    #[test]
804    #[allow(clippy::cognitive_complexity)]
805    fn physical_stream() -> Result<()> {
806        let physical_stream = PhysicalStream::new(
807            Fields::new(vec![
808                ("a".try_into()?, BitCount::new(8).unwrap()),
809                ("b".try_into()?, BitCount::new(16).unwrap()),
810                ("c".try_into()?, BitCount::new(1).unwrap()),
811            ])?,
812            Positive::new(3).unwrap(),
813            4,
814            8,
815            Fields::new(vec![("user".try_into()?, BitCount::new(1).unwrap())])?,
816        );
817
818        let mut element = physical_stream.element_fields().iter();
819        assert_eq!(
820            element.next(),
821            Some((&("a".try_into()?), &BitCount::new(8).unwrap()))
822        );
823        assert_eq!(
824            element.next(),
825            Some((&("b".try_into()?), &BitCount::new(16).unwrap()))
826        );
827        assert_eq!(
828            element.next(),
829            Some((&("c".try_into()?), &BitCount::new(1).unwrap()))
830        );
831        assert_eq!(element.next(), None);
832        assert_eq!(physical_stream.element_lanes(), Positive::new(3).unwrap());
833        assert_eq!(physical_stream.dimensionality(), 4);
834        assert_eq!(physical_stream.complexity(), &Complexity::new_major(8));
835        assert_eq!(
836            physical_stream.user().iter().next().unwrap(),
837            (&("user".try_into()?), &BitCount::new(1).unwrap())
838        );
839        assert_eq!(physical_stream.bit_count(), 87);
840        assert_eq!(physical_stream.data_bit_count(), (8 + 16 + 1) * 3);
841        assert_eq!(physical_stream.last_bit_count(), 4);
842        assert_eq!(physical_stream.stai_bit_count(), 2);
843        assert_eq!(physical_stream.endi_bit_count(), 2);
844        assert_eq!(physical_stream.strb_bit_count(), 3);
845        assert_eq!(physical_stream.user_bit_count(), 1);
846        assert_eq!(
847            physical_stream.signal_list(),
848            SignalList {
849                data: Some(75),
850                last: Some(4),
851                stai: Some(2),
852                endi: Some(2),
853                strb: Some(3),
854                user: Some(1)
855            }
856        );
857
858        // let physical_stream = PhysicalStream::new(vec![(Some("a"), 8)], 1, 0, 0, vec![])?;
859        let physical_stream = PhysicalStream::new(
860            Fields::new(vec![("a".try_into()?, BitCount::new(8).unwrap())])?,
861            Positive::new(1).unwrap(),
862            0,
863            0,
864            Fields::new(vec![])?,
865        );
866
867        assert_eq!(physical_stream.element_fields().iter().count(), 1);
868        assert_eq!(physical_stream.element_lanes(), Positive::new(1).unwrap());
869        assert_eq!(physical_stream.dimensionality(), 0);
870        assert_eq!(physical_stream.complexity(), &Complexity::new_major(0));
871        assert_eq!(physical_stream.user().iter().next(), None);
872        assert_eq!(physical_stream.bit_count(), 8);
873        assert_eq!(physical_stream.data_bit_count(), 8);
874        assert_eq!(physical_stream.last_bit_count(), 0);
875        assert_eq!(physical_stream.stai_bit_count(), 0);
876        assert_eq!(physical_stream.endi_bit_count(), 0);
877        assert_eq!(physical_stream.strb_bit_count(), 0);
878        assert_eq!(physical_stream.user_bit_count(), 0);
879        assert_eq!(
880            physical_stream.signal_list(),
881            SignalList {
882                data: Some(8),
883                last: None,
884                stai: None,
885                endi: None,
886                strb: None,
887                user: None
888            }
889        );
890
891        Ok(())
892    }
893
894    #[test]
895    fn signal_list() -> Result<()> {
896        let physical_stream = PhysicalStream::new(
897            Fields::new(vec![
898                ("a".try_into()?, BitCount::new(3).unwrap()),
899                ("b".try_into()?, BitCount::new(2).unwrap()),
900            ])?,
901            Positive::new(2).unwrap(),
902            3,
903            8,
904            Fields::new(vec![])?,
905        );
906
907        let signal_list = SignalList::from(&physical_stream);
908        assert_eq!(physical_stream.bit_count(), 17);
909        assert_eq!(physical_stream.data_bit_count(), 2 * (3 + 2));
910        assert_eq!(physical_stream.last_bit_count(), 3);
911        assert_eq!(physical_stream.stai_bit_count(), 1);
912        assert_eq!(physical_stream.endi_bit_count(), 1);
913        assert_eq!(physical_stream.strb_bit_count(), 2);
914        assert_eq!(physical_stream.user_bit_count(), 0);
915
916        assert_eq!(
917            Width::Vector(physical_stream.data_bit_count()),
918            signal_list.data().unwrap().width
919        );
920        assert_eq!(
921            Width::Vector(physical_stream.last_bit_count()),
922            signal_list.last().unwrap().width
923        );
924        assert_eq!(
925            Width::Vector(physical_stream.stai_bit_count()),
926            signal_list.stai().unwrap().width
927        );
928        assert_eq!(
929            Width::Vector(physical_stream.endi_bit_count()),
930            signal_list.endi().unwrap().width
931        );
932        assert_eq!(
933            Width::Vector(physical_stream.strb_bit_count()),
934            signal_list.strb().unwrap().width
935        );
936        assert_eq!(
937            Width::Vector(physical_stream.user_bit_count()),
938            Width::Vector(0)
939        );
940
941        assert_eq!(signal_list.opt_bit_count(), Some(17));
942        assert_eq!(signal_list.bit_count(), 17);
943        assert_eq!(signal_list, SignalList::from(physical_stream));
944
945        assert_eq!(
946            signal_list.into_iter().collect::<Vec<_>>(),
947            vec![
948                Signal::bit("valid", Origin::Source),
949                Signal::bit("ready", Origin::Sink),
950                Signal::opt_vec("data", Origin::Source, Some(10)).unwrap(),
951                Signal::opt_vec("last", Origin::Source, Some(3)).unwrap(),
952                Signal::opt_vec("stai", Origin::Source, Some(1)).unwrap(),
953                Signal::opt_vec("endi", Origin::Source, Some(1)).unwrap(),
954                Signal::opt_vec("strb", Origin::Source, Some(2)).unwrap(),
955                // ("user", 0) ommitted
956            ]
957        );
958
959        Ok(())
960    }
961}