calyx_opt/passes/
default_assigns.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
use crate::analysis::AssignmentAnalysis;
use crate::traversal::{Action, ConstructVisitor, Named, VisResult, Visitor};
use calyx_ir::{self as ir, LibrarySignatures};
use calyx_utils::{CalyxResult, Error};
use itertools::Itertools;
use std::collections::HashMap;

/// Adds default assignments to all non-`@data` ports of an instance.
pub struct DefaultAssigns {
    /// Mapping from component to data ports
    data_ports: HashMap<ir::Id, Vec<ir::Id>>,
}

impl Named for DefaultAssigns {
    fn name() -> &'static str {
        "default-assigns"
    }

    fn description() -> &'static str {
        "adds default assignments to all non-`@data` ports of an instance."
    }
}

impl ConstructVisitor for DefaultAssigns {
    fn from(ctx: &ir::Context) -> CalyxResult<Self>
    where
        Self: Sized,
    {
        let data_ports = ctx
            .lib
            .signatures()
            .map(|sig| {
                let ports = sig.signature.iter().filter_map(|p| {
                    if p.direction == ir::Direction::Input
                        && !p.attributes.has(ir::BoolAttr::Data)
                        && !p.attributes.has(ir::BoolAttr::Clk)
                        && !p.attributes.has(ir::BoolAttr::Reset)
                    {
                        Some(p.name())
                    } else {
                        None
                    }
                });
                (sig.name, ports.collect())
            })
            .collect();
        Ok(Self { data_ports })
    }

    fn clear_data(&mut self) {
        /* shared across components */
    }
}

impl Visitor for DefaultAssigns {
    fn start(
        &mut self,
        comp: &mut ir::Component,
        sigs: &LibrarySignatures,
        _comps: &[ir::Component],
    ) -> VisResult {
        if !comp.is_structural() {
            return Err(Error::pass_assumption(
                Self::name(),
                format!("component {} is not purely structural", comp.name),
            ));
        }

        // We only need to consider write set of the continuous assignments
        // and the register that controls an FSM
        let writes = comp
            .continuous_assignments
            .iter()
            .analysis()
            .writes()
            .group_by_cell();

        let mut assigns = Vec::new();

        let mt = vec![];
        let cells = comp.cells.iter().cloned().collect_vec();
        let mut builder = ir::Builder::new(comp, sigs);

        for cr in &cells {
            let cell = cr.borrow();
            let Some(typ) = cell.type_name() else {
                continue;
            };
            let Some(required) = self.data_ports.get(&typ) else {
                continue;
            };

            // For all the assignments not in the write set, add a default assignment
            // if the assignment does not write to an FSM-controlling register
            let mut cell_writes: Vec<ir::RRC<ir::Port>> = writes
                .get(&cell.name())
                .unwrap_or(&mt)
                .iter()
                .map(ir::RRC::clone)
                .collect();

            if cell.attributes.has(ir::BoolAttr::FSMControl) {
                cell_writes
                    .extend(cell.ports().into_iter().map(ir::RRC::clone));
            }

            let cell_writes = cell_writes
                .into_iter()
                .map(|p| {
                    let p = p.borrow();
                    p.name
                })
                .collect_vec();

            assigns.extend(
                required.iter().filter(|p| (!cell_writes.contains(p))).map(
                    |name| {
                        let port = cell.get(name);
                        let zero = builder.add_constant(0, port.borrow().width);
                        let assign: ir::Assignment<ir::Nothing> = builder
                            .build_assignment(
                                cell.get(name),
                                zero.borrow().get("out"),
                                ir::Guard::True,
                            );
                        log::info!(
                            "Adding {}",
                            ir::Printer::assignment_to_str(&assign)
                        );
                        assign
                    },
                ),
            );
        }

        comp.continuous_assignments.extend(assigns);

        // Purely structural pass
        Ok(Action::Stop)
    }
}