calyx_ir/
component.rs

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