Skip to main content

tydi/generator/
common.rs

1//! Common hardware representation for back-ends.
2//!
3//! The goal of this module is to define a common representation of the hardware structure to be
4//! generated, before selecting a specific back-end to generate some language-specific sources.
5//!
6//! # Examples:
7//!
8//! ```
9//! use tydi::generator::{
10//!     chisel::ChiselBackEnd, vhdl::VHDLBackEnd,
11//!     common::Project,
12//!     GenerateProject
13//! };
14//!
15//! let tmpdir = tempfile::tempdir()?;
16//! let path = tmpdir.path().join("output");
17//!
18//! let proj = Project {
19//!     identifier: "MyProj".to_string(),
20//!     libraries: vec![ /*stuff*/]
21//! };
22//!
23//! let vhdl = VHDLBackEnd::default();
24//! //let chisel = ChiselBackEnd::default();
25//!
26//! vhdl.generate(&proj, &path.join("vhdl"));
27//! //chisel.generate(&proj, &path.join("chisel"));
28//! # Ok::<(), Box<dyn std::error::Error>>(())
29//! ```
30
31use crate::cat;
32use crate::traits::Identify;
33use crate::{NonNegative, Reversed};
34
35/// Inner struct for `Type::Array`
36#[derive(Debug, Clone, PartialEq)]
37pub struct Array {
38    /// VHDL identifier for this array type.
39    identifier: String,
40    /// The size of the array.
41    pub size: usize,
42    /// The type of the array elements.
43    pub typ: Box<Type>,
44}
45
46impl Identify for Array {
47    fn identifier(&self) -> &str {
48        self.identifier.as_str()
49    }
50}
51
52/// A field for a `Record`.
53#[derive(Debug, Clone, PartialEq)]
54pub struct Field {
55    /// Name of the field.
56    name: String,
57    /// Type of the field.
58    typ: Type,
59    /// Whether the direction of this field should be reversed
60    /// w.r.t. the other fields in the record for bulk connections.
61    reversed: bool,
62}
63
64impl Identify for Field {
65    fn identifier(&self) -> &str {
66        self.name.as_str()
67    }
68}
69
70impl Field {
71    /// Construct a new record field.
72    pub fn new(name: impl Into<String>, typ: Type, reversed: bool) -> Field {
73        Field {
74            name: name.into(),
75            typ,
76            reversed,
77        }
78    }
79
80    pub fn typ(&self) -> &Type {
81        &self.typ
82    }
83
84    pub fn is_reversed(&self) -> bool {
85        self.reversed
86    }
87}
88
89impl Reversed for Field {
90    fn reversed(&self) -> Self {
91        Field::new(self.name.clone(), self.typ.clone(), !self.reversed)
92    }
93}
94
95/// Inner struct for `Type::Record`
96#[derive(Debug, Clone, PartialEq)]
97pub struct Record {
98    /// VHDL identifier for this record type.
99    identifier: String,
100    /// The fields of the record.
101    fields: Vec<Field>,
102}
103
104impl Identify for Record {
105    fn identifier(&self) -> &str {
106        self.identifier.as_str()
107    }
108}
109
110impl Record {
111    /// Construct a new record.
112    pub fn new(name: impl Into<String>, fields: Vec<Field>) -> Record {
113        // TODO(johanpel): records should be constructed through a UniquelyNamedBuilder
114        Record {
115            identifier: name.into(),
116            fields,
117        }
118    }
119
120    /// Construct a new record without any fields.
121    pub fn new_empty(name: impl Into<String>) -> Record {
122        Record {
123            identifier: name.into(),
124            fields: vec![],
125        }
126    }
127
128    /// Construct a new record with a valid and ready bit.
129    pub fn new_empty_stream(name: impl Into<String>) -> Record {
130        Record {
131            identifier: name.into(),
132            fields: vec![
133                Field::new("valid", Type::Bit, false),
134                Field::new("ready", Type::Bit, true),
135            ],
136        }
137    }
138
139    /// Create a new field and add it to the record.
140    pub fn insert_new_field(&mut self, name: impl Into<String>, typ: Type, reversed: bool) {
141        self.fields.push(Field::new(name, typ, reversed));
142    }
143
144    /// Add a field to the record.
145    pub fn insert(&mut self, field: Field) {
146        self.fields.push(field);
147    }
148
149    /// Returns true if the record contains a field that is reversed.
150    pub fn has_reversed(&self) -> bool {
151        self.fields.iter().any(|i| i.reversed)
152    }
153
154    /// Returns an iterable over the fields.
155    pub fn fields(&self) -> impl Iterator<Item = &Field> {
156        self.fields.iter()
157    }
158
159    /// Returns true if record contains no fields.
160    pub fn is_empty(&self) -> bool {
161        self.fields.is_empty()
162    }
163
164    /// Append a string to the name of this record, and any nested records.
165    pub fn append_name_nested(&self, with: impl Into<String>) -> Self {
166        let p: String = with.into();
167        let mut result = Record::new_empty(cat!(self.identifier, p.clone()));
168        for f in self.fields() {
169            result.insert(match f.typ() {
170                Type::Record(r) => Field::new(
171                    f.identifier(),
172                    Type::Record(r.append_name_nested(p.clone())),
173                    f.reversed,
174                ),
175                _ => f.clone(),
176            });
177        }
178        result
179    }
180}
181
182/// Hardware types.
183#[derive(Debug, Clone, PartialEq)]
184pub enum Type {
185    /// A single bit.
186    Bit,
187    /// A vector of bits.
188    BitVec {
189        /// The width of the vector.
190        width: NonNegative,
191    },
192    /// A statically-sized array.
193    Array(Array),
194    /// A record.
195    Record(Record),
196}
197
198/// Bundle of names and types. Useful to represent flattened types.
199pub type TypeBundle = Vec<(Vec<String>, Type, bool)>;
200
201impl Type {
202    /// Construct a bit vector type.
203    pub fn bitvec(width: NonNegative) -> Type {
204        Type::BitVec { width }
205    }
206
207    /// Construct a record type.
208    pub fn record(name: impl Into<String>, fields: Vec<Field>) -> Type {
209        Type::Record(Record::new(name.into(), fields))
210    }
211
212    /// Flatten a type to a non-nested type bundle.
213    pub fn flatten(&self, prefix: Vec<String>, reversed: bool) -> TypeBundle {
214        let mut result: TypeBundle = vec![];
215        match self {
216            Type::Record(rec) => rec.fields.iter().for_each(|field| {
217                let mut new_prefix = prefix.clone();
218                new_prefix.push(field.name.clone());
219                result.extend(field.typ.flatten(new_prefix, field.reversed))
220            }),
221            _ => result.push((prefix, self.clone(), reversed)),
222        }
223        result
224    }
225
226    // Returns true if the type contains a reversed field.
227    pub fn has_reversed(&self) -> bool {
228        match self {
229            Type::Record(rec) => rec.has_reversed(),
230            _ => false,
231        }
232    }
233}
234
235/// A parameter for components.
236#[derive(Debug, Clone)]
237pub struct Parameter {
238    pub name: String,
239    pub typ: Type,
240}
241
242/// Modes for ports.
243#[derive(Copy, Clone, Debug, PartialEq)]
244pub enum Mode {
245    /// Input.
246    In,
247    /// Output.
248    Out,
249}
250
251impl Reversed for Mode {
252    fn reversed(&self) -> Self {
253        match self {
254            Mode::In => Mode::Out,
255            Mode::Out => Mode::In,
256        }
257    }
258}
259
260/// A port.
261#[derive(Debug, Clone, PartialEq)]
262pub struct Port {
263    /// Port identifier.
264    identifier: String,
265    /// Port mode.
266    mode: Mode,
267    /// Port type.
268    typ: Type,
269}
270
271impl Port {
272    pub fn new(name: impl Into<String>, mode: Mode, typ: Type) -> Port {
273        Port {
274            identifier: name.into(),
275            mode,
276            typ,
277        }
278    }
279
280    pub fn mode(&self) -> Mode {
281        self.mode
282    }
283
284    pub fn typ(&self) -> Type {
285        self.typ.clone()
286    }
287}
288
289impl Identify for Port {
290    fn identifier(&self) -> &str {
291        self.identifier.as_str()
292    }
293}
294
295/// A component.
296#[derive(Debug, Clone)]
297pub struct Component {
298    /// Component identifier.
299    identifier: String,
300    /// The parameters of the component..
301    parameters: Vec<Parameter>,
302    /// The ports of the component.
303    ports: Vec<Port>,
304}
305
306impl Identify for Component {
307    fn identifier(&self) -> &str {
308        self.identifier.as_str()
309    }
310}
311
312impl Component {
313    pub fn new(
314        identifier: impl Into<String>,
315        parameters: Vec<Parameter>,
316        ports: Vec<Port>,
317    ) -> Component {
318        Component {
319            identifier: identifier.into(),
320            parameters,
321            ports,
322        }
323    }
324
325    pub fn ports(&self) -> &Vec<Port> {
326        &self.ports
327    }
328
329    pub fn parameters(&self) -> &Vec<Parameter> {
330        &self.parameters
331    }
332
333    pub fn flatten_types(&mut self) {
334        let mut new_ports: Vec<Port> = Vec::new();
335        self.ports.iter().for_each(|port| {
336            let bundle = port
337                .typ
338                .flatten(vec![port.identifier.clone()], port.mode == Mode::Out);
339            for tup in bundle {
340                new_ports.push(Port::new(
341                    tup.0.join("_"),
342                    if tup.2 { Mode::Out } else { Mode::In },
343                    tup.1,
344                ));
345            }
346        });
347        self.ports = new_ports;
348    }
349}
350
351/// A library of components and types.
352#[derive(Debug)]
353pub struct Library {
354    /// The identifier.
355    pub identifier: String,
356    /// The components declared within the library.
357    pub components: Vec<Component>,
358}
359
360/// A project with libraries
361// TODO(johanpel): consider renaming this, because project might imply some EDA tool-specific
362//                 project
363#[derive(Debug)]
364pub struct Project {
365    /// The name of the project.
366    pub identifier: String,
367    /// The libraries contained within the projects.
368    pub libraries: Vec<Library>,
369}
370
371#[cfg(test)]
372pub(crate) mod test {
373
374    use super::*;
375    use crate::cat;
376
377    pub(crate) mod records {
378
379        use super::*;
380
381        pub(crate) fn prim(bits: u32) -> Type {
382            Type::bitvec(bits)
383        }
384
385        pub(crate) fn rec(name: impl Into<String>) -> Type {
386            Type::record(
387                cat!(name.into(), "type"),
388                vec![
389                    Field::new("a", Type::bitvec(42), false),
390                    Field::new("b", Type::bitvec(1337), false),
391                ],
392            )
393        }
394
395        pub(crate) fn rec_of_single(name: impl Into<String>) -> Type {
396            Type::record(
397                cat!(name.into(), "type"),
398                vec![Field::new("a", Type::bitvec(42), false)],
399            )
400        }
401
402        pub(crate) fn rec_nested(name: impl Into<String>) -> Type {
403            let n: String = name.into();
404            Type::record(
405                cat!(n, "type"),
406                vec![
407                    Field::new("c", rec(cat!(n, "c")), false),
408                    Field::new("d", rec(cat!(n, "d")), false),
409                ],
410            )
411        }
412    }
413
414    // Some structs from this mod to be used in tests:
415    pub fn test_rec() -> Type {
416        Type::record(
417            "rec",
418            vec![
419                Field::new("a", Type::Bit, false),
420                Field::new("b", Type::bitvec(4), true),
421            ],
422        )
423    }
424
425    pub fn test_rec_nested() -> Type {
426        Type::record(
427            cat!("rec", "nested"),
428            vec![
429                Field::new("a", Type::Bit, false),
430                Field::new("b", test_rec(), false),
431            ],
432        )
433    }
434
435    pub fn test_comp() -> Component {
436        Component {
437            identifier: "test_comp".to_string(),
438            parameters: vec![],
439            ports: vec![
440                Port::new("a", Mode::In, test_rec()),
441                Port::new("b", Mode::Out, test_rec_nested()),
442            ],
443        }
444    }
445
446    pub fn test_lib() -> Library {
447        Library {
448            identifier: "lib".to_string(),
449            components: vec![test_comp()],
450        }
451    }
452
453    pub fn test_proj() -> Project {
454        Project {
455            identifier: "proj".to_string(),
456            libraries: vec![test_lib()],
457        }
458    }
459
460    #[test]
461    fn test_flatten_rec() {
462        let flat = test_rec().flatten(vec![], false);
463        assert_eq!(flat[0].0, vec!["a".to_string()]);
464        assert_eq!(flat[0].1, Type::Bit);
465        assert_eq!(flat[0].2, false);
466        assert_eq!(flat[1].0, vec!["b".to_string()]);
467        assert_eq!(flat[1].1, Type::bitvec(4));
468        assert_eq!(flat[1].2, true);
469    }
470
471    #[test]
472    fn test_flatten_rec_nested() {
473        let flat = test_rec_nested().flatten(vec![], false);
474        assert_eq!(flat[0].0[0], "a".to_string());
475        assert_eq!(flat[0].1, Type::Bit);
476        assert_eq!(flat[0].2, false);
477        assert_eq!(flat[1].0[0], "b".to_string());
478        assert_eq!(flat[1].0[1], "a".to_string());
479        assert_eq!(flat[1].1, Type::Bit);
480        assert_eq!(flat[1].2, false);
481        assert_eq!(flat[2].0[0], "b".to_string());
482        assert_eq!(flat[2].0[1], "b".to_string());
483        assert_eq!(flat[2].1, Type::bitvec(4));
484        assert_eq!(flat[2].2, true);
485    }
486
487    #[test]
488    fn rec_has_reversed() {
489        assert!(test_rec().has_reversed());
490        assert!(!Type::record(
491            "test",
492            vec![
493                Field::new("a", Type::Bit, false),
494                Field::new("b", Type::Bit, false),
495            ],
496        )
497        .has_reversed());
498    }
499}