Skip to main content

tydi/generator/
mod.rs

1//! Generator methods and implementations for Tydi types.
2//!
3//! The generator module is enabled by the `generator` feature flag.
4
5use std::path::Path;
6
7use crate::design::{Interface, Streamlet};
8use crate::generator::common::{Component, Library, Mode, Port, Project, Record, Type};
9use crate::logical::{Group, LogicalStreamType, Stream, Union};
10use crate::physical::{Origin, Signal, Width};
11use crate::traits::Identify;
12use crate::Result;
13
14pub mod chisel;
15pub mod common;
16pub mod vhdl;
17
18// Generator-global constants:
19
20/// Suffix provided to the canonical representation of streamlet components.
21// TODO(johanpel): come up with a better suffix to make users understand to
22//                 preferably not touch the canonical component.
23pub const CANON_SUFFIX: Option<&str> = Some("com");
24
25/// Concatenate stuff using format with an underscore in between.
26/// Useful if the separator ever changes.
27#[macro_export]
28macro_rules! cat {
29    ($a:expr) => {{
30        format!("{}", $a)
31    }};
32
33    ($a:expr, $($b:expr),+) => {{
34        let left : String = format!("{}", $a);
35        let right : String = format!("{}", cat!($($b),+));
36        if left == "" {
37            right
38        } else if right == "" {
39            left
40        } else {
41            format!("{}_{}", left, right)
42        }
43    }};
44}
45
46/// Trait to generate back-end specific source files from the common hardware representation
47/// of a project.
48pub trait GenerateProject {
49    /// Generate source files from a [common::Project] and save them to [path].
50    fn generate(&self, project: &Project, path: impl AsRef<Path>) -> Result<()>;
51}
52
53/// Trait to create common representation types from things in the canonical
54/// way and user-friendly way.
55pub trait Typify {
56    fn user(&self, _prefix: impl Into<String>) -> Option<Type> {
57        None
58    }
59    fn canonical(&self, prefix: impl Into<String>) -> Vec<Signal>;
60}
61
62/// Trait to create common representation ports from things in the canonical
63/// way and user-friendly way.
64pub trait Portify {
65    fn user(
66        &self,
67        _port_name: impl Into<String>,
68        _port_type_prefix: impl Into<String>,
69    ) -> Vec<Port> {
70        Vec::new()
71    }
72    fn canonical(&self, name: impl Into<String>) -> Vec<Port>;
73}
74
75/// Trait to create common representation components from things in the canonical
76/// way and user-friendly way.
77pub trait Componentify {
78    fn user(&self, _suffix: Option<&str>) -> Option<Component> {
79        None
80    }
81    fn canonical(&self, suffix: Option<&str>) -> Component;
82}
83
84impl Typify for LogicalStreamType {
85    /// This implementation for LogicalStreamType assumes the LogicalStreamType has already been
86    /// flattened through synthesize.
87    fn user(&self, prefix: impl Into<String>) -> Option<Type> {
88        match self {
89            LogicalStreamType::Null => None,
90            LogicalStreamType::Bits(width) => Some(Type::bitvec(width.get())),
91            LogicalStreamType::Group(group) => group.user(prefix),
92            LogicalStreamType::Stream(stream) => stream.user(prefix),
93            LogicalStreamType::Union(union) => union.user(prefix),
94        }
95    }
96
97    fn canonical(&self, prefix: impl Into<String>) -> Vec<Signal> {
98        match self {
99            LogicalStreamType::Null => Vec::new(),
100            LogicalStreamType::Bits(width) => {
101                vec![Signal::vec(prefix.into(), Origin::Source, *width)]
102            }
103            LogicalStreamType::Group(group) => group.canonical(prefix),
104            LogicalStreamType::Stream(stream) => stream.canonical(prefix),
105            LogicalStreamType::Union(union) => union.canonical(prefix),
106        }
107    }
108}
109
110impl Typify for Group {
111    fn user(&self, prefix: impl Into<String>) -> Option<Type> {
112        let n: String = prefix.into();
113        let mut rec = Record::new_empty(cat!(n.clone(), "type"));
114        for (field_name, field_logical) in self.iter() {
115            if let Some(field_common_type) = field_logical.user(cat!(n.clone(), field_name)) {
116                rec.insert_new_field(field_name.to_string(), field_common_type, false)
117            }
118        }
119        Some(Type::Record(rec))
120    }
121
122    fn canonical(&self, prefix: impl Into<String>) -> Vec<Signal> {
123        let n: String = prefix.into();
124        let mut result = Vec::new();
125        for (field_name, field_logical) in self.iter() {
126            let field_result = field_logical.canonical(cat!(n.clone(), field_name));
127            result.extend(field_result);
128        }
129        result
130    }
131}
132
133impl Typify for Union {
134    fn user(&self, prefix: impl Into<String>) -> Option<Type> {
135        let n: String = prefix.into();
136        let mut rec = Record::new_empty(cat!(n.clone(), "type"));
137        if let Some((tag_name, tag_bc)) = self.tag() {
138            rec.insert_new_field(tag_name, Type::bitvec(tag_bc.get()), false);
139        }
140        for (field_name, field_logical) in self.iter() {
141            if let Some(field_common_type) = field_logical.user(cat!(n.clone(), field_name)) {
142                rec.insert_new_field(field_name, field_common_type, false);
143            }
144        }
145        Some(Type::Record(rec))
146    }
147
148    fn canonical(&self, prefix: impl Into<String>) -> Vec<Signal> {
149        let n: String = prefix.into();
150        let mut result = Vec::new();
151        if let Some((tag_name, tag_bc)) = self.tag() {
152            result.push(Signal::vec(
153                cat!(n.clone(), tag_name),
154                Origin::Source,
155                tag_bc,
156            ));
157        }
158        for (field_name, field_logical) in self.iter() {
159            let field_result = field_logical.canonical(cat!(n.clone(), field_name));
160            result.extend(field_result);
161        }
162        result
163    }
164}
165
166impl Typify for Stream {
167    /// This implementation for Stream assumes the parent LogicalStreamType has already been
168    /// flattened through synthesize.
169    fn user(&self, prefix: impl Into<String>) -> Option<Type> {
170        // We need to wrap the Stream back into a LogicalStreamType
171        // to be able to use various methods for checks and synthesize.
172        let logical = LogicalStreamType::from(self.clone());
173
174        // At this point, it should not be possible that this is a
175        // non-element-only LogicalStreamType.
176        assert!(logical.is_element_only());
177
178        // Check if the logical stream is null.
179        if !logical.is_null() {
180            // Synthesize the logical stream into physical streams.
181            let synth = logical.synthesize();
182
183            // Obtain the path name and signal map from the physical stream.
184            // There should only be one, since it is an element only stream.
185            // Therefore, it should be safe to unwrap.
186            let (name, physical) = synth.streams().next().unwrap();
187            let signals = physical.signal_list();
188
189            // Set up the resulting record.
190            let mut rec = Record::new_empty_stream(match name.len() {
191                0 => cat!(prefix.into(), "type"),
192                _ => cat!(prefix.into(), name, "type"),
193            });
194
195            // Insert data record. There must be something there since it is not null.
196            rec.insert_new_field("data", self.data().user("data").unwrap(), false);
197
198            // Check signals related to dimensionality, complexity, etc.
199            if let Some(sig) = signals.last() {
200                rec.insert_new_field("last", sig.width().into(), sig.reversed());
201            }
202            if let Some(sig) = signals.stai() {
203                rec.insert_new_field("stai", sig.width().into(), sig.reversed());
204            }
205            if let Some(sig) = signals.endi() {
206                rec.insert_new_field("endi", sig.width().into(), sig.reversed());
207            }
208            if let Some(sig) = signals.strb() {
209                rec.insert_new_field("strb", sig.width().into(), sig.reversed());
210            }
211
212            Some(Type::Record(rec))
213        } else {
214            None
215        }
216    }
217
218    /// This implementation for Stream assumes the parent LogicalStreamType has already been
219    /// flattened through synthesize.
220    fn canonical(&self, prefix: impl Into<String>) -> Vec<Signal> {
221        let n: String = prefix.into();
222        let mut result = Vec::new();
223
224        let logical = LogicalStreamType::from(self.clone());
225        assert!(logical.is_element_only());
226        if !logical.is_null() {
227            let synth = logical.synthesize();
228            let (path, phys) = synth.streams().next().unwrap();
229            for signal in phys.signal_list().into_iter() {
230                let n = cat!(n.clone(), path, signal.identifier());
231                result.push(signal.with_name(n));
232            }
233        }
234
235        result
236    }
237}
238
239impl From<Width> for Type {
240    fn from(width: Width) -> Self {
241        match width {
242            Width::Scalar => Type::Bit,
243            Width::Vector(w) => Type::bitvec(w),
244        }
245    }
246}
247
248/// Trait that helps to determine the common representation port mode given a streamlet interface
249/// mode.
250pub trait ModeFor {
251    /// Return the port mode of self, given a streamlet interface mode.
252    fn mode_for(&self, streamlet_mode: crate::design::Mode) -> Mode;
253}
254
255impl ModeFor for Origin {
256    /// Return the common representation port mode for this signal origin, given the interface mode.
257    fn mode_for(&self, streamlet_mode: crate::design::Mode) -> Mode {
258        match self {
259            Origin::Sink => match streamlet_mode {
260                crate::design::Mode::In => Mode::Out,
261                crate::design::Mode::Out => Mode::In,
262            },
263            Origin::Source => match streamlet_mode {
264                crate::design::Mode::In => Mode::In,
265                crate::design::Mode::Out => Mode::Out,
266            },
267        }
268    }
269}
270
271impl Portify for Interface {
272    fn user(&self, name: impl Into<String>, type_name: impl Into<String>) -> Vec<Port> {
273        let n: String = name.into();
274        let tn: String = type_name.into();
275
276        let mut result = Vec::new();
277
278        let split = self.typ().split();
279
280        if let Some(sig_type) = split.signal().user(tn.clone()) {
281            result.push(Port::new(cat!(n.clone()), self.mode().into(), sig_type));
282        }
283
284        // Split the LogicalStreamType up into discrete, simple streams.
285        for (path, simple_stream) in self.typ().split().streams() {
286            if let Some(typ) = simple_stream.user(cat!(tn.clone(), path)) {
287                result.push(Port::new(cat!(n.clone(), path), self.mode().into(), typ));
288            }
289        }
290
291        result
292    }
293
294    fn canonical(&self, prefix: impl Into<String>) -> Vec<Port> {
295        let signals = self.typ().canonical(prefix.into());
296        signals
297            .iter()
298            .map(|s| {
299                Port::new(
300                    s.identifier(),
301                    s.origin().mode_for(self.mode()),
302                    s.width().into(),
303                )
304            })
305            .collect()
306    }
307}
308
309impl From<crate::design::Mode> for Mode {
310    fn from(m: crate::design::Mode) -> Self {
311        match m {
312            crate::design::Mode::Out => Mode::Out,
313            crate::design::Mode::In => Mode::In,
314        }
315    }
316}
317
318impl Componentify for Streamlet {
319    fn user(&self, suffix: Option<&str>) -> Option<Component> {
320        Some(Component::new(
321            cat!(self.identifier().to_string(), suffix.unwrap_or("")),
322            vec![],
323            self.interfaces()
324                .into_iter()
325                .flat_map(|interface| {
326                    interface.user(
327                        interface.identifier(),
328                        cat!(self.identifier().to_string(), interface.identifier()),
329                    )
330                })
331                .collect(),
332        ))
333    }
334
335    fn canonical(&self, suffix: Option<&str>) -> Component {
336        Component::new(
337            cat!(self.identifier().to_string(), suffix.unwrap_or("")),
338            vec![],
339            {
340                let mut all_ports = Vec::new();
341                self.interfaces().into_iter().for_each(|interface| {
342                    all_ports.extend(interface.canonical(interface.identifier()));
343                });
344                all_ports
345            },
346        )
347    }
348}
349
350impl From<crate::design::Library> for Library {
351    fn from(l: crate::design::Library) -> Self {
352        Library {
353            identifier: l.identifier().to_string(),
354            components: l
355                .streamlets()
356                .into_iter()
357                .flat_map(|s| {
358                    let mut result = vec![s.canonical(CANON_SUFFIX)];
359                    if let Some(user) = s.user(None) {
360                        result.push(user);
361                    }
362                    result
363                })
364                .collect(),
365        }
366    }
367}
368
369impl From<crate::design::Project> for Project {
370    fn from(p: crate::design::Project) -> Self {
371        Project {
372            identifier: p.identifier().to_string(),
373            libraries: p.libraries().into_iter().map(|l| l.into()).collect(),
374        }
375    }
376}
377
378#[cfg(test)]
379pub(crate) mod tests {
380    use super::*;
381    use crate::design::{Interface, Streamlet};
382    use crate::generator::common::test::records;
383    use crate::generator::vhdl::Declare;
384    use crate::logical::tests::{elements, streams};
385    use crate::{Name, Positive, UniquelyNamedBuilder};
386
387    #[test]
388    fn test_cat() {
389        assert_eq!(cat!("ok"), "ok");
390        assert_eq!(cat!("ok", "tydi"), "ok_tydi");
391        assert_eq!(cat!("ok", "tydi", ""), "ok_tydi");
392        assert_eq!(cat!("", ""), "");
393    }
394
395    mod canonical {
396        use super::*;
397
398        #[test]
399        fn logical_to_common_prim() {
400            let typ = elements::prim(8).canonical("test");
401            assert_eq!(
402                typ,
403                vec![Signal::vec(
404                    "test".to_string(),
405                    Origin::Source,
406                    Positive::new(8).unwrap()
407                )]
408            )
409        }
410
411        #[test]
412        fn logical_to_common_groups() {
413            let typ0 = elements::group().canonical("test");
414            assert_eq!(
415                typ0,
416                vec![
417                    Signal::vec(
418                        "test_a".to_string(),
419                        Origin::Source,
420                        Positive::new(42).unwrap()
421                    ),
422                    Signal::vec(
423                        "test_b".to_string(),
424                        Origin::Source,
425                        Positive::new(1337).unwrap()
426                    )
427                ]
428            );
429
430            let typ1 = elements::group_nested().canonical("test");
431            assert_eq!(
432                typ1,
433                vec![
434                    Signal::vec(
435                        "test_c_a".to_string(),
436                        Origin::Source,
437                        Positive::new(42).unwrap()
438                    ),
439                    Signal::vec(
440                        "test_c_b".to_string(),
441                        Origin::Source,
442                        Positive::new(1337).unwrap()
443                    ),
444                    Signal::vec(
445                        "test_d_a".to_string(),
446                        Origin::Source,
447                        Positive::new(42).unwrap()
448                    ),
449                    Signal::vec(
450                        "test_d_b".to_string(),
451                        Origin::Source,
452                        Positive::new(1337).unwrap()
453                    ),
454                ]
455            );
456
457            let typ2 = elements::group_of_single().canonical("test");
458            assert_eq!(
459                typ2,
460                vec![Signal::vec(
461                    "test_a".to_string(),
462                    Origin::Source,
463                    Positive::new(42).unwrap()
464                ),]
465            );
466        }
467
468        #[test]
469        fn logical_to_common_streams() {
470            let typ0 = streams::prim(8).canonical("test");
471            dbg!(&typ0);
472
473            let typ1 = streams::group().canonical("test");
474            dbg!(&typ1);
475            // TODO(johanpel): implement actual test
476        }
477
478        #[test]
479        fn interface_to_port() {
480            let if0 =
481                Interface::try_new("test", crate::design::Mode::In, streams::prim(8)).unwrap();
482            dbg!(if0.canonical("test"));
483            let if1 =
484                Interface::try_new("test", crate::design::Mode::Out, streams::group()).unwrap();
485            dbg!(if1.canonical("test"));
486            // TODO(johanpel): implement actual test
487        }
488    }
489
490    mod user {
491        use super::*;
492        use crate::generator::common::Field;
493
494        #[test]
495        fn logical_to_common_prim() {
496            let typ: Type = elements::prim(8).user("test").unwrap();
497            assert_eq!(typ, records::prim(8));
498        }
499
500        #[test]
501        fn logical_to_common_groups() {
502            let typ0: Type = elements::group().user("test").unwrap();
503            assert_eq!(typ0, records::rec("test"));
504
505            let typ1: Type = elements::group_nested().user("test").unwrap();
506            assert_eq!(typ1, records::rec_nested("test"));
507
508            let typ2: Type = elements::group_of_single().user("test").unwrap();
509            assert_eq!(typ2, records::rec_of_single("test"));
510        }
511
512        #[test]
513        fn logical_to_common_streams() {
514            let typ0: Type = streams::prim(8).user("test").unwrap();
515            assert_eq!(
516                typ0,
517                Type::record(
518                    "test_type",
519                    vec![
520                        Field::new("valid", Type::Bit, false),
521                        Field::new("ready", Type::Bit, true),
522                        Field::new("data", Type::bitvec(8), false)
523                    ]
524                )
525            );
526
527            let typ1: Type = streams::group().user("test").unwrap();
528            assert_eq!(
529                typ1,
530                Type::record(
531                    "test_type",
532                    vec![
533                        Field::new(
534                            "a",
535                            Type::record(
536                                "test_a_type",
537                                vec![
538                                    Field::new("valid", Type::Bit, false),
539                                    Field::new("ready", Type::Bit, true),
540                                    Field::new("data", Type::bitvec(42), false)
541                                ]
542                            ),
543                            false
544                        ),
545                        Field::new(
546                            "b",
547                            Type::record(
548                                "test_b_type",
549                                vec![
550                                    Field::new("valid", Type::Bit, false),
551                                    Field::new("ready", Type::Bit, true),
552                                    Field::new("data", Type::bitvec(1337), false)
553                                ]
554                            ),
555                            false
556                        )
557                    ]
558                )
559            );
560        }
561
562        #[test]
563        fn interface_to_port() {
564            let if0 =
565                Interface::try_new("test", crate::design::Mode::In, streams::prim(8)).unwrap();
566            dbg!(if0.user("test", "test"));
567            let if1 =
568                Interface::try_new("test", crate::design::Mode::Out, streams::group()).unwrap();
569            dbg!(if1.user("test", "test"));
570            // TODO(johanpel): write actual test
571        }
572    }
573
574    #[test]
575    pub(crate) fn simple_streamlet() -> Result<()> {
576        let streamlet = Streamlet::from_builder(
577            Name::try_new("test")?,
578            UniquelyNamedBuilder::new().with_items(vec![
579                Interface::try_new("x", crate::design::Mode::In, streams::prim(8))?,
580                Interface::try_new("y", crate::design::Mode::Out, streams::group())?,
581            ]),
582        )?;
583        // TODO(johanpel): write actual test
584        let common_streamlet = streamlet.user(None).unwrap();
585        let pkg = Library {
586            identifier: "boomer".to_string(),
587            components: vec![common_streamlet],
588        };
589        println!("{}", pkg.declare()?);
590        Ok(())
591    }
592
593    #[test]
594    pub(crate) fn nested_streams_streamlet() -> Result<()> {
595        let streamlet = Streamlet::from_builder(
596            Name::try_new("test")?,
597            UniquelyNamedBuilder::new().with_items(vec![
598                Interface::try_new("x", crate::design::Mode::In, streams::prim(8))?,
599                Interface::try_new("y", crate::design::Mode::Out, streams::nested())?,
600            ]),
601        )?;
602        // TODO(johanpel): write actual test
603        let common_streamlet = streamlet.user(None).unwrap();
604        let pkg = Library {
605            identifier: "testing".to_string(),
606            components: vec![common_streamlet],
607        };
608        println!("{}", pkg.declare()?);
609        Ok(())
610    }
611
612    //
613    // #[test]
614    // fn interface_to_canonical() -> Result<()> {
615    //     let interface = Interface::try_new(
616    //         "x",
617    //         crate::design::Mode::Out,
618    //         crate::logical::tests::streams::single_element(),
619    //     )?;
620    //     let ports: Vec<Port> = interface.into();
621    //     // TODO(johanpel): write actual test
622    //     dbg!(ports);
623    //     Ok(())
624    // }
625
626    // #[test]
627    // fn physical_low_complexity() -> Result<()> {
628    //     let phys = PhysicalStream::try_new(vec![("a", 4), ("b", 8)], 2, 0, 2, vec![])?;
629    //     let common_type = phys.synthesize("test");
630    //     // TODO(johanpel): write actual test
631    //     println!("{}", common_type.declare().unwrap());
632    //     Ok(())
633    // }
634
635    // #[test]
636    // fn physical_high_complexity() -> Result<()> {
637    //     let phys = PhysicalStream::try_new(
638    //         vec![("a", 4), ("b", 8)],
639    //         4,
640    //         3,
641    //         8,
642    //         vec![("muh", 3), ("foo", 4)],
643    //     )?;
644    //
645    //     let common_type = phys.synthesize("test");
646    //
647    //     let mut comp = Component {
648    //         identifier: "MyComp".to_string(),
649    //         parameters: vec![],
650    //         ports: vec![Port::new("x", Mode::In, common_type)],
651    //     };
652    //
653    //     comp.flatten_types();
654    //     // TODO(johanpel): write actual test
655    //     println!("{}", comp.declare().unwrap());
656    //     Ok(())
657    // }
658}