calyx_ir/
component.rs

1use super::{
2    Assignment, Attribute, Attributes, BoolAttr, Builder, Cell, CellType,
3    CombGroup, Control, Direction, FSM, GetName, Group, Id, NumAttr, PortDef,
4    RRC, StaticGroup,
5};
6use crate::Nothing;
7use crate::guard::StaticTiming;
8use calyx_utils::NameGenerator;
9use itertools::Itertools;
10use linked_hash_map::LinkedHashMap;
11use std::collections::HashSet;
12use std::iter::Extend;
13use std::num::NonZeroU64;
14use std::rc::Rc;
15
16/// The default name of the signature cell in a component.
17/// In general, this should not be used by anything.
18const THIS_ID: &str = "_this";
19
20/// Interface ports that must be present on every component
21const INTERFACE_PORTS: [(Attribute, u64, Direction); 4] = [
22    (Attribute::Num(NumAttr::Go), 1, Direction::Input),
23    (Attribute::Bool(BoolAttr::Clk), 1, Direction::Input),
24    (Attribute::Bool(BoolAttr::Reset), 1, Direction::Input),
25    (Attribute::Num(NumAttr::Done), 1, Direction::Output),
26];
27
28/// In memory representation of a Component.
29#[derive(Debug)]
30#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
31pub struct Component {
32    /// Name of the component.
33    pub name: Id,
34    /// The input/output signature of this component.
35    pub signature: RRC<Cell>,
36    /// The cells instantiated for this component.
37    pub cells: IdList<Cell>,
38    /// Groups of assignment wires.
39    pub groups: IdList<Group>,
40    /// FSMs generated during compilation.
41    pub fsms: IdList<FSM>,
42    /// Groups of assignment wires
43    pub static_groups: IdList<StaticGroup>,
44    /// Groups of assignment wires.
45    pub comb_groups: IdList<CombGroup>,
46    /// The set of "continuous assignments", i.e., assignments that are always
47    /// active.
48    pub continuous_assignments: Vec<Assignment<Nothing>>,
49    /// The control program for this component.
50    pub control: RRC<Control>,
51    /// Attributes for this component
52    pub attributes: Attributes,
53    /// True iff component is combinational
54    pub is_comb: bool,
55    /// (Optional) latency of component, if it is static
56    pub latency: Option<NonZeroU64>,
57
58    ///// Internal structures
59    /// Namegenerator that contains the names currently defined in this
60    /// component (cell and group names).
61    #[cfg_attr(feature = "serialize", serde(skip))]
62    namegen: NameGenerator,
63}
64
65/// Builder methods for extracting and construction IR nodes.
66/// The naming scheme for methods is consistent:
67/// - find_<construct>: Returns a reference to the construct with the given
68///   name.
69impl Component {
70    /// Extend the signature with interface ports if they are missing.
71    pub(super) fn extend_signature(sig: &mut Vec<PortDef<u64>>) {
72        let port_names: HashSet<_> = sig.iter().map(|pd| pd.name()).collect();
73        let mut namegen = NameGenerator::with_prev_defined_names(port_names);
74        for (attr, width, direction) in INTERFACE_PORTS.iter() {
75            // Check if there is already another interface port defined for the
76            // component
77            if !sig.iter().any(|pd| pd.attributes.has(*attr)) {
78                let mut attributes = Attributes::default();
79                attributes.insert(*attr, 1);
80                let name = Id::from(attr.to_string());
81                sig.push(PortDef::new(
82                    namegen.gen_name(name.to_string()),
83                    *width,
84                    direction.clone(),
85                    attributes,
86                ));
87            }
88        }
89    }
90
91    /// Construct a new Component with the given `name` and ports.
92    ///
93    /// * If `has_interface` is true, then we do not add `@go` and `@done` ports.
94    ///   This will usually happen with the component is marked with [super::BoolAttr::Nointerface].
95    /// * If `is_comb` is set, then this is a combinational component and cannot use `group` or `control` constructs.
96    /// * If `latency` is set, then this is a static component with the given latency. A combinational component cannot have a latency.
97    pub fn new<S>(
98        name: S,
99        mut ports: Vec<PortDef<u64>>,
100        has_interface: bool,
101        is_comb: bool,
102        latency: Option<NonZeroU64>,
103    ) -> Self
104    where
105        S: Into<Id>,
106    {
107        if has_interface {
108            // Add interface ports if missing
109            Self::extend_signature(&mut ports);
110        }
111
112        let prev_names: HashSet<_> = ports.iter().map(|pd| pd.name()).collect();
113
114        let this_sig = Builder::cell_from_signature(
115            THIS_ID.into(),
116            CellType::ThisComponent,
117            ports
118                .into_iter()
119                // Reverse the port directions inside the component.
120                .map(|pd| {
121                    PortDef::new(
122                        pd.name(),
123                        pd.width,
124                        pd.direction.reverse(),
125                        pd.attributes,
126                    )
127                })
128                .collect(),
129        );
130
131        Component {
132            name: name.into(),
133            signature: this_sig,
134            cells: IdList::default(),
135            groups: IdList::default(),
136            fsms: IdList::default(),
137            static_groups: IdList::default(),
138            comb_groups: IdList::default(),
139            continuous_assignments: vec![],
140            control: super::rrc(Control::empty()),
141            namegen: NameGenerator::with_prev_defined_names(prev_names),
142            attributes: Attributes::default(),
143            is_comb,
144            // converting from NonZeroU64 to u64. May want to keep permanently as NonZeroU64
145            // in the future, but rn it's probably easier to keep as u64
146            latency,
147        }
148    }
149
150    pub(super) fn add_names(&mut self, names: HashSet<Id>) {
151        self.namegen.add_names(names)
152    }
153
154    /// gets the component's groups
155    pub fn get_groups(&self) -> &IdList<Group> {
156        &self.groups
157    }
158
159    /// get the component's fsms
160    pub fn get_fsms(&self) -> &IdList<FSM> {
161        &self.fsms
162    }
163
164    /// gets the component's static groups
165    pub fn get_static_groups(&self) -> &IdList<StaticGroup> {
166        &self.static_groups
167    }
168
169    /// gets the component's groups
170    pub fn get_groups_mut(&mut self) -> &mut IdList<Group> {
171        &mut self.groups
172    }
173
174    /// gets mutable access to the component's fsms
175    pub fn get_fsms_mut(&mut self) -> &mut IdList<FSM> {
176        &mut self.fsms
177    }
178
179    /// gets the component's groups
180    pub fn get_static_groups_mut(&mut self) -> &mut IdList<StaticGroup> {
181        &mut self.static_groups
182    }
183
184    /// gets the component's groups
185    pub fn set_groups(&mut self, groups: IdList<Group>) {
186        self.groups = groups
187    }
188
189    /// gets the component's groups
190    pub fn set_static_groups(&mut self, static_groups: IdList<StaticGroup>) {
191        self.static_groups = static_groups
192    }
193
194    /// Return a reference to the group with `name` if present.
195    pub fn find_group<S>(&self, name: S) -> Option<RRC<Group>>
196    where
197        S: Into<Id>,
198    {
199        self.groups.find(name)
200    }
201
202    /// Return a reference to the group with `name` if present.
203    pub fn find_static_group<S>(&self, name: S) -> Option<RRC<StaticGroup>>
204    where
205        S: Into<Id>,
206    {
207        self.static_groups.find(name)
208    }
209
210    /// Return a reference to the fsm with `name` if present.
211    pub fn find_fsm<S>(&self, name: S) -> Option<RRC<FSM>>
212    where
213        S: Into<Id>,
214    {
215        self.fsms.find(name)
216    }
217
218    /// Return a refernece to a combination group with `name` if present.
219    pub fn find_comb_group<S>(&self, name: S) -> Option<RRC<CombGroup>>
220    where
221        S: Into<Id>,
222    {
223        self.comb_groups.find(name)
224    }
225
226    /// Return a reference to the cell with `name` if present.
227    pub fn find_cell<S>(&self, name: S) -> Option<RRC<Cell>>
228    where
229        S: Into<Id>,
230    {
231        self.cells.find(name)
232    }
233
234    /// Return a reference to the cell with `name` if present.
235    pub fn find_guaranteed_cell<S>(&self, name: S) -> RRC<Cell>
236    where
237        S: Into<Id> + std::fmt::Debug + Copy,
238    {
239        self.cells.find(name).unwrap_or_else(|| {
240            unreachable!(
241                "called find_certain_cell on {:?} but it wasn't found",
242                name
243            )
244        })
245    }
246
247    /// Construct a non-conflicting name using the Component's namegenerator.
248    pub fn generate_name<S>(&mut self, prefix: S) -> Id
249    where
250        S: Into<Id>,
251    {
252        self.namegen.gen_name(prefix)
253    }
254
255    /// Check whether this component is purely structural, i.e. has no groups or control
256    pub fn is_structural(&self) -> bool {
257        self.groups.is_empty()
258            && self.comb_groups.is_empty()
259            && self.static_groups.is_empty()
260            && self.control.borrow().is_empty()
261    }
262
263    /// Check whether this is a static component.
264    /// A static component is a component which has a latency field.
265    pub fn is_static(&self) -> bool {
266        self.latency.is_some()
267    }
268
269    /// Apply function to all assignments within static groups.
270    pub fn for_each_static_assignment<F>(&mut self, mut f: F)
271    where
272        F: FnMut(&mut Assignment<StaticTiming>),
273    {
274        for group_ref in self.get_static_groups().iter() {
275            let mut assigns =
276                group_ref.borrow_mut().assignments.drain(..).collect_vec();
277            for assign in &mut assigns {
278                f(assign)
279            }
280            group_ref.borrow_mut().assignments = assigns;
281        }
282    }
283
284    /// Apply function on all non-static assignments contained within the component.
285    pub fn for_each_assignment<F>(&mut self, mut f: F)
286    where
287        F: FnMut(&mut Assignment<Nothing>),
288    {
289        // Detach assignments from the group so that ports that use group
290        // `go` and `done` condition can access the parent group.
291        for group_ref in self.groups.iter() {
292            let mut assigns =
293                group_ref.borrow_mut().assignments.drain(..).collect_vec();
294            for assign in &mut assigns {
295                f(assign)
296            }
297            group_ref.borrow_mut().assignments = assigns;
298        }
299        for group_ref in self.comb_groups.iter() {
300            let mut assigns =
301                group_ref.borrow_mut().assignments.drain(..).collect_vec();
302            for assign in &mut assigns {
303                f(assign)
304            }
305            group_ref.borrow_mut().assignments = assigns;
306        }
307        self.continuous_assignments.iter_mut().for_each(f);
308    }
309
310    /// Iterate over all non-static assignments contained within the component.
311    pub fn iter_assignments<F>(&self, mut f: F)
312    where
313        F: FnMut(&Assignment<Nothing>),
314    {
315        for group_ref in self.groups.iter() {
316            for assign in &group_ref.borrow().assignments {
317                f(assign)
318            }
319        }
320        for group_ref in self.comb_groups.iter() {
321            for assign in &group_ref.borrow().assignments {
322                f(assign)
323            }
324        }
325        self.continuous_assignments.iter().for_each(f);
326    }
327
328    /// Iterate over all static assignments contained within the component
329    pub fn iter_static_assignments<F>(&self, mut f: F)
330    where
331        F: FnMut(&Assignment<StaticTiming>),
332    {
333        for group_ref in self.get_static_groups().iter() {
334            for assign in &group_ref.borrow().assignments {
335                f(assign)
336            }
337        }
338    }
339}
340
341/// A wrapper struct exposing an ordered collection of named entities within an
342/// RRC with deterministic iteration and constant-time look-up on names
343/// directly. The struct assumes that the name of an entity cannot change. Doing
344/// so will introduce incorrect results for look-ups.
345#[derive(Debug)]
346pub struct IdList<T: GetName>(LinkedHashMap<Id, RRC<T>>);
347
348/// Simple iter impl delegating to the [`Values`](linked_hash_map::Values).
349impl<'a, T: GetName> IntoIterator for &'a IdList<T> {
350    type Item = &'a RRC<T>;
351
352    type IntoIter = linked_hash_map::Values<'a, Id, RRC<T>>;
353
354    fn into_iter(self) -> Self::IntoIter {
355        self.0.values()
356    }
357}
358
359impl<T, F> From<F> for IdList<T>
360where
361    T: GetName,
362    F: IntoIterator<Item = RRC<T>>,
363{
364    fn from(list: F) -> Self {
365        IdList(
366            list.into_iter()
367                .map(|item| {
368                    let name = item.borrow().name();
369                    (name, item)
370                })
371                .collect::<LinkedHashMap<Id, RRC<T>>>(),
372        )
373    }
374}
375
376impl<T: GetName> IdList<T> {
377    /// Removes all elements from the collection
378    pub fn clear(&mut self) {
379        self.0.clear();
380    }
381
382    /// Returns true if there are no elements in the list.
383    pub fn is_empty(&self) -> bool {
384        self.0.is_empty()
385    }
386
387    // Length of the underlying storage.
388    pub fn len(&self) -> usize {
389        self.0.len()
390    }
391
392    /// Keep only the elements in the collection which satisfy the given predicate and return the
393    /// number of elements removed.
394    pub fn retain<F>(&mut self, mut f: F) -> u64
395    where
396        F: FnMut(&RRC<T>) -> bool,
397    {
398        let mut removed = 0;
399        for entry in self.0.entries() {
400            if !f(entry.get()) {
401                entry.remove();
402                removed += 1;
403            }
404        }
405        removed
406    }
407
408    /// Add a new element to the colleciton
409    pub fn add(&mut self, item: RRC<T>) {
410        let name = item.borrow().name();
411        self.0.insert(name, item);
412    }
413
414    // Remove and return the element with the given name.
415    pub fn remove<S>(&mut self, name: S) -> Option<RRC<T>>
416    where
417        S: Into<Id>,
418    {
419        self.0.remove(&name.into())
420    }
421
422    /// Add all elements to the collection
423    pub fn append(&mut self, items: impl Iterator<Item = RRC<T>>) {
424        let map = items.map(|i| {
425            let name = i.borrow().name();
426            (name, i)
427        });
428        self.0.extend(map);
429    }
430
431    /// Returns an iterator over immutable references
432    pub fn iter(&self) -> impl Clone + Iterator<Item = &RRC<T>> {
433        self.0.values()
434    }
435
436    /// Returns an iterator over mutable references. Likely a pointless method
437    /// as this is a collection of RRCs.
438    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut RRC<T>> {
439        self.0.iter_mut().map(|(_id, val)| val)
440    }
441
442    /// Removes all elements from the collection and returns an iterator over
443    /// the owned elements.
444    pub fn drain(&mut self) -> impl Iterator<Item = RRC<T>> + use<T> {
445        let drain = std::mem::take(&mut self.0);
446
447        drain.into_iter().map(|(_, cell)| cell)
448    }
449
450    /// Returns the element indicated by the name, if present, otherwise None.
451    pub fn find<S>(&self, name: S) -> Option<RRC<T>>
452    where
453        S: Into<Id>,
454    {
455        self.0.get(&name.into()).map(Rc::clone)
456    }
457}
458
459impl<T: GetName> Default for IdList<T> {
460    fn default() -> Self {
461        IdList(LinkedHashMap::new())
462    }
463}