calyx_ir/
structure.rs

1//! Representation for structure (wires and cells) in a Calyx program.
2
3use crate::Nothing;
4use crate::guard::StaticTiming;
5
6use super::{
7    Attributes, Direction, GetAttributes, Guard, Id, PortDef, RRC, WRC,
8};
9use calyx_frontend::{Attribute, BoolAttr};
10use calyx_utils::{CalyxResult, Error, GetName};
11use itertools::Itertools;
12use smallvec::{SmallVec, smallvec};
13use std::collections::HashMap;
14use std::hash::Hash;
15use std::rc::Rc;
16
17/// Ports can come from Cells or Groups
18#[derive(Debug, Clone)]
19pub enum PortParent {
20    Cell(WRC<Cell>),
21    Group(WRC<Group>),
22    StaticGroup(WRC<StaticGroup>),
23    FSM(WRC<FSM>),
24}
25
26/// Represents a port on a cell.
27#[derive(Debug, Clone)]
28#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
29pub struct Port {
30    /// Name of the port
31    pub name: Id,
32    /// Width of the port
33    pub width: u64,
34    /// Direction of the port
35    pub direction: Direction,
36    /// Weak pointer to this port's parent
37    pub parent: PortParent,
38    /// Attributes associated with this port.
39    pub attributes: Attributes,
40}
41
42/// Canonical name of a Port
43#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
44pub struct Canonical {
45    pub cell: Id,
46    pub port: Id,
47}
48
49impl Canonical {
50    pub const fn new(cell: Id, port: Id) -> Self {
51        Self { cell, port }
52    }
53}
54
55impl std::fmt::Display for Canonical {
56    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57        write!(f, "{}.{}", self.cell, self.port)
58    }
59}
60
61impl Port {
62    /// Checks if this port is a hole
63    pub fn is_hole(&self) -> bool {
64        matches!(&self.parent, PortParent::Group(_))
65            || matches!(&self.parent, PortParent::StaticGroup(_))
66    }
67
68    /// Returns the parent of the [Port] which must be [Cell]. Throws an error
69    /// otherwise.
70    pub fn cell_parent(&self) -> RRC<Cell> {
71        if let PortParent::Cell(cell_wref) = &self.parent {
72            return cell_wref.upgrade();
73        }
74        unreachable!("This port should have a cell parent")
75    }
76
77    /// Checks if this port is a constant of value: `val`.
78    pub fn is_constant(&self, val: u64, width: u64) -> bool {
79        if let PortParent::Cell(cell) = &self.parent {
80            match cell.upgrade().borrow().prototype {
81                CellType::Constant { val: v, width: w } => {
82                    v == val && width == w
83                }
84                _ => false,
85            }
86        } else {
87            false
88        }
89    }
90
91    /// Gets name of parent object.
92    pub fn get_parent_name(&self) -> Id {
93        match &self.parent {
94            PortParent::Cell(cell) => cell.upgrade().borrow().name,
95            PortParent::Group(group) => group.upgrade().borrow().name,
96            PortParent::FSM(fsm) => fsm.upgrade().borrow().name,
97            PortParent::StaticGroup(group) => group.upgrade().borrow().name,
98        }
99    }
100
101    /// Checks if parent is combinational component
102    pub fn parent_is_comb(&self) -> bool {
103        match &self.parent {
104            PortParent::Cell(cell) => cell.upgrade().borrow().is_comb_cell(),
105            _ => false,
106        }
107    }
108
109    /// Checks if parent is a protected cell
110    pub fn parent_is_protected(&self) -> bool {
111        match &self.parent {
112            PortParent::Cell(cell) => {
113                cell.upgrade().borrow().attributes.has(BoolAttr::Protected)
114            }
115            _ => false,
116        }
117    }
118
119    /// Checks if the parent is an FSM. Assignments to these always need to be maintained.
120    pub fn parent_is_fsm(&self) -> bool {
121        matches!(&self.parent, PortParent::FSM(..))
122    }
123
124    /// Get the canonical representation for this Port.
125    pub fn canonical(&self) -> Canonical {
126        Canonical {
127            cell: self.get_parent_name(),
128            port: self.name,
129        }
130    }
131
132    /// Returns the value of an attribute if present
133    pub fn get_attribute<A>(&self, attr: A) -> Option<u64>
134    where
135        A: Into<Attribute>,
136    {
137        self.get_attributes().get(attr)
138    }
139
140    /// Returns true if the node has a specific attribute
141    pub fn has_attribute<A>(&self, attr: A) -> bool
142    where
143        A: Into<Attribute>,
144    {
145        self.get_attributes().has(attr)
146    }
147
148    /// Returns true if the widths, name, direction, and attributes of 2 ports match.
149    /// This is different than `==` equivalence, which only looks at parent and name.
150    pub fn type_equivalent(&self, other: &Port) -> bool {
151        self.width == other.width
152            && self.direction == other.direction
153            && self.name == other.name
154            && self.attributes == other.attributes
155    }
156}
157
158impl GetAttributes for Port {
159    fn get_attributes(&self) -> &Attributes {
160        &self.attributes
161    }
162
163    fn get_mut_attributes(&mut self) -> &mut Attributes {
164        &mut self.attributes
165    }
166}
167
168impl PartialEq for Port {
169    fn eq(&self, other: &Self) -> bool {
170        self.get_parent_name() == other.get_parent_name()
171            && self.name == other.name
172    }
173}
174
175impl Eq for Port {}
176
177/// Wraps generic iterators over ports to allow functions to build and return port iterators in
178/// different ways.
179pub struct PortIterator<'a> {
180    port_iter: Box<dyn Iterator<Item = RRC<Port>> + 'a>,
181}
182
183impl<'a> PortIterator<'a> {
184    /// Construct a new PortIterator from an iterator over ports.
185    pub fn new<T>(iter: T) -> Self
186    where
187        T: Iterator<Item = RRC<Port>> + 'a,
188    {
189        PortIterator {
190            port_iter: Box::new(iter),
191        }
192    }
193
194    /// Returns an empty iterator over ports.
195    pub fn empty() -> Self {
196        PortIterator {
197            port_iter: Box::new(std::iter::empty()),
198        }
199    }
200}
201
202impl Iterator for PortIterator<'_> {
203    type Item = RRC<Port>;
204
205    fn next(&mut self) -> Option<Self::Item> {
206        self.port_iter.next()
207    }
208}
209
210/// Alias for bindings
211pub type Binding = SmallVec<[(Id, u64); 5]>;
212
213/// The type for a Cell
214#[derive(Debug, PartialEq, Eq, Hash, Clone)]
215#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
216pub enum CellType {
217    /// Cell constructed using a primitive definition
218    Primitive {
219        /// Name of the primitive cell used to instantiate this cell.
220        name: Id,
221        /// Bindings for the parameters. Uses Vec to retain the input order.
222        param_binding: Box<Binding>,
223        /// True iff this is a combinational primitive
224        is_comb: bool,
225        /// (Optional) latency of the primitive
226        latency: Option<std::num::NonZeroU64>,
227    },
228    /// Cell constructed using a Calyx component
229    Component {
230        /// Name of the component used to instantiate this cell.
231        name: Id,
232    },
233    /// This cell represents the current component
234    ThisComponent,
235    /// Cell representing a Constant
236    Constant {
237        /// Value of this constant
238        val: u64,
239        /// Width of this constant
240        width: u64,
241    },
242}
243
244impl CellType {
245    /// Return the name associated with this CellType if present
246    pub fn get_name(&self) -> Option<Id> {
247        match self {
248            CellType::Primitive { name, .. } | CellType::Component { name } => {
249                Some(*name)
250            }
251            CellType::ThisComponent | CellType::Constant { .. } => None,
252        }
253    }
254
255    /// Generate string representation of CellType appropriate for error messages.
256    pub fn surface_name(&self) -> Option<String> {
257        match self {
258            CellType::Primitive {
259                name,
260                param_binding,
261                ..
262            } => Some(format!(
263                "{}({})",
264                name,
265                param_binding.iter().map(|(_, v)| v.to_string()).join(", ")
266            )),
267            CellType::Component { name } => Some(name.to_string()),
268            CellType::ThisComponent | CellType::Constant { .. } => None,
269        }
270    }
271}
272
273/// Represents an instantiated cell.
274#[derive(Debug, Clone)]
275#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
276pub struct Cell {
277    /// Name of this cell.
278    name: Id,
279    /// Ports on this cell
280    pub ports: SmallVec<[RRC<Port>; 10]>,
281    /// Underlying type for this cell
282    pub prototype: CellType,
283    /// Attributes for this group.
284    pub attributes: Attributes,
285    /// Whether the cell is external
286    reference: bool,
287}
288
289impl GetAttributes for Cell {
290    fn get_attributes(&self) -> &Attributes {
291        &self.attributes
292    }
293
294    fn get_mut_attributes(&mut self) -> &mut Attributes {
295        &mut self.attributes
296    }
297}
298
299impl Cell {
300    /// Construct a cell
301    pub fn new(name: Id, prototype: CellType) -> Self {
302        Self {
303            name,
304            ports: smallvec![],
305            prototype,
306            attributes: Attributes::default(),
307            reference: false,
308        }
309    }
310
311    ///Get a boolean describing whether the cell is external.
312    pub fn is_reference(&self) -> bool {
313        self.reference
314    }
315
316    ///Set the external field
317    pub fn set_reference(&mut self, reference: bool) -> bool {
318        self.reference = reference;
319        self.reference
320    }
321
322    /// Get a reference to the named port if it exists.
323    pub fn find<S>(&self, name: S) -> Option<RRC<Port>>
324    where
325        S: std::fmt::Display + Clone,
326        Id: PartialEq<S>,
327    {
328        self.ports
329            .iter()
330            .find(|&g| g.borrow().name == name)
331            .map(Rc::clone)
332    }
333
334    /// Return all ports that have the attribute `attr`.
335    pub fn find_all_with_attr<A>(
336        &self,
337        attr: A,
338    ) -> impl Iterator<Item = RRC<Port>> + '_
339    where
340        A: Into<Attribute>,
341    {
342        let attr = attr.into();
343        self.ports
344            .iter()
345            .filter(move |&p| p.borrow().attributes.has(attr))
346            .map(Rc::clone)
347    }
348
349    /// Return the unique port with the given attribute.
350    /// If multiple ports have the same attribute, then we panic.
351    /// If there are not ports with the give attribute, then we return None.
352    pub fn find_unique_with_attr<A>(
353        &self,
354        attr: A,
355    ) -> CalyxResult<Option<RRC<Port>>>
356    where
357        A: Into<Attribute>,
358    {
359        let attr = attr.into();
360        let mut ports = self.find_all_with_attr(attr);
361        if let Some(port) = ports.next() {
362            if ports.next().is_some() {
363                Err(Error::malformed_structure(format!(
364                    "Multiple ports with attribute `{}` found on cell `{}`",
365                    attr, self.name
366                )))
367            } else {
368                Ok(Some(port))
369            }
370        } else {
371            Ok(None)
372        }
373    }
374
375    /// Get a reference to the named port and throw an error if it doesn't
376    /// exist.
377    pub fn get<S>(&self, name: S) -> RRC<Port>
378    where
379        S: std::fmt::Display + Clone,
380        Id: PartialEq<S>,
381    {
382        self.find(name.clone()).unwrap_or_else(|| {
383            panic!(
384                "Port `{name}' not found on cell `{}'. Known ports are: {}",
385                self.name,
386                self.ports
387                    .iter()
388                    .map(|p| p.borrow().name.to_string())
389                    .join(",")
390            )
391        })
392    }
393
394    /// Returns true iff this cell is an instance of a Calyx-defined component.
395    pub fn is_component(&self) -> bool {
396        matches!(&self.prototype, CellType::Component { .. })
397    }
398
399    /// Returns true iff this cell is the signature of the current component
400    pub fn is_this(&self) -> bool {
401        matches!(&self.prototype, CellType::ThisComponent)
402    }
403
404    /// Returns true if this is an instance of a primitive. If the optional name is provided then
405    /// only returns true if the primitive has the given name.
406    pub fn is_primitive<S>(&self, prim: Option<S>) -> bool
407    where
408        Id: PartialEq<S>,
409    {
410        match &self.prototype {
411            CellType::Primitive { name, .. } => {
412                prim.as_ref().map(|p| name == p).unwrap_or(true)
413            }
414            _ => false,
415        }
416    }
417
418    /// Get the unique port with the given attribute.
419    /// Panic if no port with the attribute is found and returns an error if multiple ports with the attribute are found.
420    pub fn get_unique_with_attr<A>(&self, attr: A) -> CalyxResult<RRC<Port>>
421    where
422        A: Into<Attribute> + std::fmt::Display + Copy,
423    {
424        Ok(self.find_unique_with_attr(attr)?.unwrap_or_else(|| {
425            panic!(
426                "Port with attribute `{attr}' not found on cell `{}'",
427                self.name,
428            )
429        }))
430    }
431
432    /// Returns the name of the component that is this cells type.
433    pub fn type_name(&self) -> Option<Id> {
434        self.prototype.get_name()
435    }
436
437    /// Get parameter binding from the prototype used to build this cell.
438    pub fn get_parameter<S>(&self, param: S) -> Option<u64>
439    where
440        Id: PartialEq<S>,
441    {
442        match &self.prototype {
443            CellType::Primitive { param_binding, .. } => param_binding
444                .iter()
445                .find(|(key, _)| *key == param)
446                .map(|(_, val)| *val),
447            CellType::Component { .. } => None,
448            CellType::ThisComponent => None,
449            CellType::Constant { .. } => None,
450        }
451    }
452
453    /// Return the canonical name for the cell generated to represent this
454    /// (val, width) constant.
455    pub fn constant_name(val: u64, width: u64) -> Id {
456        format!("_{val}_{width}").into()
457    }
458
459    /// Return the value associated with this attribute key.
460    pub fn get_attribute<A: Into<Attribute>>(&self, attr: A) -> Option<u64> {
461        self.attributes.get(attr.into())
462    }
463
464    /// Add a new attribute to the group.
465    pub fn add_attribute<A: Into<Attribute>>(&mut self, attr: A, value: u64) {
466        self.attributes.insert(attr.into(), value);
467    }
468
469    /// Grants immutable access to the name of this cell.
470    pub fn name(&self) -> Id {
471        self.name
472    }
473
474    /// Returns a reference to all [super::Port] attached to this cells.
475    pub fn ports(&self) -> &SmallVec<[RRC<Port>; 10]> {
476        &self.ports
477    }
478
479    // Get the signature of this cell as a vector. Each element corresponds to a port in the Cell.
480    pub fn get_signature(&self) -> Vec<PortDef<u64>> {
481        self.ports
482            .iter()
483            .map(|port_ref| {
484                let port = port_ref.borrow();
485                PortDef::new(
486                    port.name,
487                    port.width,
488                    port.direction.clone(),
489                    port.attributes.clone(),
490                )
491            })
492            .collect()
493    }
494
495    // returns true if cell is comb, false otherwise
496    // note that this component/component cannot be combinational
497    // XXX(rachit): Combinational components are now supported so this function returns
498    // the wrong answer when the parent is a combinational component
499    pub fn is_comb_cell(&self) -> bool {
500        match self.prototype {
501            CellType::Primitive { is_comb, .. } => is_comb,
502            _ => false,
503        }
504    }
505}
506
507/// Represents a guarded assignment in the program
508#[derive(Clone, Debug)]
509#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
510pub struct Assignment<T> {
511    /// The destination for the assignment.
512    pub dst: RRC<Port>,
513
514    /// The source for the assignment.
515    pub src: RRC<Port>,
516
517    /// The guard for this assignment.
518    pub guard: Box<Guard<T>>,
519
520    /// Attributes for this assignment.
521    pub attributes: Attributes,
522}
523
524impl<T> Assignment<T> {
525    /// Build a new unguarded assignment
526    pub fn new(dst: RRC<Port>, src: RRC<Port>) -> Self {
527        assert!(
528            dst.borrow().direction == Direction::Input,
529            "{} is not in input port",
530            dst.borrow().canonical()
531        );
532        assert!(
533            src.borrow().direction == Direction::Output,
534            "{} is not in output port",
535            src.borrow().canonical()
536        );
537        Self {
538            dst,
539            src,
540            guard: Box::new(Guard::True),
541            attributes: Attributes::default(),
542        }
543    }
544
545    /// Apply function `f` to each port contained within the assignment and
546    /// replace the port with the generated value if not None.
547    pub fn for_each_port<F>(&mut self, mut f: F)
548    where
549        F: FnMut(&RRC<Port>) -> Option<RRC<Port>>,
550    {
551        if let Some(new_src) = f(&self.src) {
552            self.src = new_src;
553        }
554        if let Some(new_dst) = f(&self.dst) {
555            self.dst = new_dst;
556        }
557        self.guard.for_each(&mut |port| f(&port).map(Guard::port))
558    }
559
560    /// Iterate through all ports contained within the assignment.
561    pub fn iter_ports(&self) -> impl Iterator<Item = RRC<Port>> {
562        self.guard
563            .all_ports()
564            .into_iter()
565            .chain(std::iter::once(Rc::clone(&self.dst)))
566            .chain(std::iter::once(Rc::clone(&self.src)))
567    }
568
569    /// Mutate the guard `g` of the assignment in-place to be `g AND addition`
570    pub fn and_guard(&mut self, addition: Guard<T>)
571    where
572        T: Eq,
573    {
574        if !(addition.is_true()) {
575            self.guard.update(|g| g.and(addition));
576        }
577    }
578}
579
580impl From<Assignment<Nothing>> for Assignment<StaticTiming> {
581    /// Turns a normal assignment into a static assignment
582    fn from(assgn: Assignment<Nothing>) -> Assignment<StaticTiming> {
583        Assignment {
584            dst: Rc::clone(&assgn.dst),
585            src: Rc::clone(&assgn.src),
586            guard: Box::new(Guard::from(*assgn.guard)),
587            attributes: assgn.attributes,
588        }
589    }
590}
591
592impl From<Assignment<StaticTiming>> for Assignment<Nothing> {
593    /// Turns a static assignment into a normal assignment by getting rid of
594    /// all `Info<StaticTiming>` leaves from the guard of the assignment.
595    fn from(mut assgn: Assignment<StaticTiming>) -> Assignment<Nothing> {
596        assgn.guard.as_mut().remove_static_timing_info();
597        Assignment {
598            dst: Rc::clone(&assgn.dst),
599            src: Rc::clone(&assgn.src),
600            guard: Box::new(Guard::from(*assgn.guard)),
601            attributes: assgn.attributes,
602        }
603    }
604}
605
606impl<StaticTiming> Assignment<StaticTiming> {
607    /// Apply function `f` to each port contained within the assignment and
608    /// replace the port with the generated value if not None.
609    pub fn for_each_interval<F>(&mut self, mut f: F)
610    where
611        F: FnMut(&mut StaticTiming) -> Option<Guard<StaticTiming>>,
612    {
613        self.guard.for_each_info(&mut |interval| f(interval))
614    }
615}
616
617#[derive(Debug, Clone)]
618#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
619pub enum Transition {
620    Unconditional(u64),
621    Conditional(Vec<(Guard<Nothing>, u64)>),
622}
623
624impl Transition {
625    pub fn new_uncond(s: u64) -> Self {
626        Self::Unconditional(s)
627    }
628}
629
630/// A Group of assignments that perform a logical action.
631#[derive(Debug)]
632#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
633pub struct Group {
634    /// Name of this group
635    name: Id,
636
637    /// The assignments used in this group
638    pub assignments: Vec<Assignment<Nothing>>,
639
640    /// Holes for this group
641    pub holes: SmallVec<[RRC<Port>; 3]>,
642
643    /// Attributes for this group.
644    pub attributes: Attributes,
645}
646impl Group {
647    pub fn new(name: Id) -> Self {
648        Self {
649            name,
650            assignments: vec![],
651            holes: smallvec![],
652            attributes: Attributes::default(),
653        }
654    }
655
656    /// Get a reference to the named hole if it exists.
657    pub fn find<S>(&self, name: S) -> Option<RRC<Port>>
658    where
659        S: std::fmt::Display,
660        Id: PartialEq<S>,
661    {
662        self.holes
663            .iter()
664            .find(|&g| g.borrow().name == name)
665            .map(Rc::clone)
666    }
667
668    /// Get a reference to the named hole or panic.
669    pub fn get<S>(&self, name: S) -> RRC<Port>
670    where
671        S: std::fmt::Display + Clone,
672        Id: PartialEq<S>,
673    {
674        self.find(name.clone()).unwrap_or_else(|| {
675            panic!("Hole `{name}' not found on group `{}'", self.name)
676        })
677    }
678
679    /// Returns the index to the done assignment in the group.
680    fn find_done_cond(&self) -> usize {
681        self.assignments
682            .iter()
683            .position(|assign| {
684                let dst = assign.dst.borrow();
685                dst.is_hole() && dst.name == "done"
686            })
687            .unwrap_or_else(|| {
688                panic!("Group `{}' has no done condition", self.name)
689            })
690    }
691
692    /// Returns a reference to the assignment in the group that writes to the done condition.
693    pub fn done_cond(&self) -> &Assignment<Nothing> {
694        let idx = self.find_done_cond();
695        &self.assignments[idx]
696    }
697
698    /// Returns a mutable reference to the assignment in the group that writes to the done
699    /// condition.
700    pub fn done_cond_mut(&mut self) -> &mut Assignment<Nothing> {
701        let idx = self.find_done_cond();
702        &mut self.assignments[idx]
703    }
704
705    /// The name of this group.
706    #[inline]
707    pub fn name(&self) -> Id {
708        self.name
709    }
710
711    /// The attributes of this group.
712    #[inline]
713    pub fn get_attributes(&self) -> Option<&Attributes> {
714        Some(&self.attributes)
715    }
716
717    pub fn remove_attribute(&mut self, attr: Attribute) {
718        self.attributes.remove(attr);
719    }
720}
721
722/// A Group of assignments that perform a logical action.
723#[derive(Debug)]
724#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
725pub struct StaticGroup {
726    /// Name of this group
727    name: Id,
728
729    /// The assignments used in this group
730    pub assignments: Vec<Assignment<StaticTiming>>,
731
732    /// Holes for this group
733    pub holes: SmallVec<[RRC<Port>; 3]>,
734
735    /// Attributes for this group.
736    pub attributes: Attributes,
737
738    /// Latency of static group
739    pub latency: u64,
740}
741
742///implement the StaticGroup struct
743impl StaticGroup {
744    pub fn new(name: Id, latency: u64) -> Self {
745        Self {
746            name,
747            assignments: vec![],
748            holes: smallvec![],
749            attributes: Attributes::default(),
750            latency,
751        }
752    }
753
754    pub fn get_latency(&self) -> u64 {
755        self.latency
756    }
757
758    /// Get a reference to the named hole if it exists.
759    pub fn find<S>(&self, name: S) -> Option<RRC<Port>>
760    where
761        S: std::fmt::Display,
762        Id: PartialEq<S>,
763    {
764        self.holes
765            .iter()
766            .find(|&g| g.borrow().name == name)
767            .map(Rc::clone)
768    }
769
770    /// Get a reference to the named hole or panic.
771    pub fn get<S>(&self, name: S) -> RRC<Port>
772    where
773        S: std::fmt::Display + Clone,
774        Id: PartialEq<S>,
775    {
776        self.find(name.clone()).unwrap_or_else(|| {
777            panic!("Hole `{name}' not found on group `{}'", self.name)
778        })
779    }
780
781    /// The name of this group.
782    #[inline]
783    pub fn name(&self) -> Id {
784        self.name
785    }
786
787    /// The attributes of this group.
788    #[inline]
789    pub fn get_attributes(&self) -> Option<&Attributes> {
790        Some(&self.attributes)
791    }
792
793    pub fn remove_attribute(&mut self, attr: Attribute) {
794        self.attributes.remove(attr);
795    }
796}
797
798/// A combinational group.
799/// A combinational group does not have any holes and should only contain assignments that should
800/// will be combinationally active
801#[derive(Debug)]
802#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
803pub struct CombGroup {
804    /// Name of this group
805    pub(super) name: Id,
806
807    /// The assignments used in this group
808    pub assignments: Vec<Assignment<Nothing>>,
809
810    /// Attributes for this group.
811    pub attributes: Attributes,
812}
813impl CombGroup {
814    /// The name of this group.
815    #[inline]
816    pub fn name(&self) -> Id {
817        self.name
818    }
819
820    /// The attributes of this group.
821    #[inline]
822    pub fn get_attributes(&self) -> Option<&Attributes> {
823        Some(&self.attributes)
824    }
825}
826
827#[derive(Debug)]
828#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
829pub struct FSM {
830    /// Name of this construct
831    pub(super) name: Id,
832    /// Attributes for this FSM
833    pub attributes: Attributes,
834    /// State indexes into assignments that are supposed to be enabled at that state
835    pub assignments: Vec<Vec<Assignment<Nothing>>>,
836    /// State indexes into (potentially guarded) next states
837    pub transitions: Vec<Transition>,
838    // Wire representing fsm output
839    pub wires: SmallVec<[RRC<Port>; 2]>,
840}
841
842impl FSM {
843    /// Grants immutable access to the name of this cell.
844    pub fn name(&self) -> Id {
845        self.name
846    }
847
848    /// Constructs a new FSM construct using a list of cases and a name
849    pub fn new(name: Id) -> Self {
850        Self {
851            name,
852            assignments: vec![],
853            transitions: vec![],
854            wires: SmallVec::new(),
855            attributes: Attributes::default(),
856        }
857    }
858
859    /// Gets the number of states in the FSM.
860    pub fn num_states(&self) -> u64 {
861        self.assignments.len().try_into().unwrap()
862    }
863
864    /// Get a reference to the named hole if it exists.
865    pub fn find<S>(&self, name: S) -> Option<RRC<Port>>
866    where
867        S: std::fmt::Display,
868        Id: PartialEq<S>,
869    {
870        self.wires
871            .iter()
872            .find(|&g| g.borrow().name == name)
873            .map(Rc::clone)
874    }
875
876    pub fn get<S>(&self, name: S) -> RRC<Port>
877    where
878        S: std::fmt::Display + Clone,
879        Id: PartialEq<S>,
880    {
881        self.find(name.clone()).unwrap_or_else(|| {
882            panic!("Wire `{name}' not found on group `{}'", self.name)
883        })
884    }
885
886    /// Extend the FSM with new transitions and assignments. Will panic if
887    /// the lengths are not consistent.
888    pub fn extend_fsm<A, T>(&mut self, assigns: A, transitions: T)
889    where
890        A: IntoIterator<Item = Vec<Assignment<Nothing>>>,
891        T: IntoIterator<Item = Transition>,
892    {
893        self.assignments.extend(assigns);
894        self.transitions.extend(transitions);
895    }
896
897    /// Extend the assignments that are supposed to be active at a given state.
898    pub fn extend_state_assignments<I>(&mut self, state: u64, assigns: I)
899    where
900        I: IntoIterator<Item = Assignment<Nothing>>,
901    {
902        let msg = format!("State {state} does not exist in FSM");
903        self.assignments
904            .get_mut(state as usize)
905            .expect(&msg)
906            .extend(assigns);
907    }
908
909    /// Returns a list of names of the groups, cells, or other port parents used
910    /// by the FSM. Requires as an argument a function that can
911    /// in-place update a Vec with the name of kinds of port parents you care about.
912    pub fn get_called_port_parents<F>(&self, push_parent_name: F) -> Vec<Id>
913    where
914        F: Fn(&mut Vec<Id>, &RRC<Port>),
915    {
916        self.transitions
917            .iter()
918            .zip(self.assignments.iter())
919            .flat_map(|(state_transitions, state_assignments)| {
920                let mut parent_names = vec![];
921
922                // if transitions uses the specified kind of port parent, add its Id
923                if let Transition::Conditional(conds) = state_transitions {
924                    for (guard, _) in conds.iter() {
925                        guard.all_ports().iter().for_each(|port| {
926                            push_parent_name(&mut parent_names, port)
927                        });
928                    }
929                }
930                // if assignments uses the specified kind of port parent, add its Id
931                for assign in state_assignments.iter() {
932                    assign.iter_ports().for_each(|port| {
933                        push_parent_name(&mut parent_names, &port);
934                    });
935                }
936                parent_names
937            })
938            .collect()
939    }
940
941    /// Each element of the resulting Vec is a collection of assignments writing
942    /// to the same destination port, where the `usize` value represents the
943    /// FSM state at which the assignment should take place.
944    pub fn merge_assignments(&self) -> Vec<Vec<(usize, Assignment<Nothing>)>> {
945        let mut assigns_by_port: HashMap<
946            Canonical,
947            Vec<(usize, Assignment<Nothing>)>,
948        > = HashMap::new();
949        for (case, assigns_at_state) in self.assignments.iter().enumerate() {
950            for assign in assigns_at_state.iter() {
951                let dest_port = assign.dst.borrow().canonical();
952                assigns_by_port
953                    .entry(dest_port)
954                    .and_modify(|assigns_at_port| {
955                        assigns_at_port.push((case, assign.clone()));
956                    })
957                    .or_insert(vec![(case, assign.clone())]);
958            }
959        }
960        assigns_by_port
961            .into_values()
962            // order by state, for better appearance when emitted
963            .sorted_by(|a, b| a.first().unwrap().0.cmp(&b.first().unwrap().0))
964            .collect()
965    }
966}
967
968impl GetName for Cell {
969    fn name(&self) -> Id {
970        self.name()
971    }
972}
973
974impl GetName for Group {
975    fn name(&self) -> Id {
976        self.name()
977    }
978}
979
980impl GetName for CombGroup {
981    fn name(&self) -> Id {
982        self.name()
983    }
984}
985
986impl GetName for FSM {
987    fn name(&self) -> Id {
988        self.name()
989    }
990}
991
992impl GetName for StaticGroup {
993    fn name(&self) -> Id {
994        self.name()
995    }
996}