1use crate::analysis::{IncompleteTransition, StaticSchedule};
2use crate::traversal::{Action, ConstructVisitor, Named, Visitor};
3use calyx_ir::{self as ir};
4use calyx_utils::CalyxResult;
5
6pub struct StaticFSMAllocation {
7 non_promoted_static_component: bool,
8}
9
10impl Named for StaticFSMAllocation {
11 fn name() -> &'static str {
12 "static-fsm-alloc"
13 }
14 fn description() -> &'static str {
15 "compiles a static schedule into an FSM construct"
16 }
17}
18
19impl ConstructVisitor for StaticFSMAllocation {
20 fn from(_ctx: &ir::Context) -> CalyxResult<Self> {
21 Ok(StaticFSMAllocation {
22 non_promoted_static_component: false,
23 })
24 }
25 fn clear_data(&mut self) {
26 self.non_promoted_static_component = false
27 }
28}
29
30impl StaticSchedule<'_, '_> {
31 fn build_abstract_fsm_with_loop(
37 &mut self,
38 scon: &ir::StaticControl,
39 guard: ir::Guard<ir::Nothing>,
40 mut transitions_to_curr: Vec<IncompleteTransition>,
41 looped_once_guard: Option<ir::Guard<ir::Nothing>>,
42 ) -> (Vec<IncompleteTransition>, Option<ir::Guard<ir::Nothing>>) {
43 match scon {
44 ir::StaticControl::Empty(_) => {
45 (transitions_to_curr, looped_once_guard)
46 }
47 ir::StaticControl::Enable(sen) => {
48 self.register_transitions(
51 self.state,
52 &mut transitions_to_curr,
53 guard.clone(),
54 );
55
56 if sen.attributes.has(ir::BoolAttr::OneState) {
59 let final_state_guard =
60 self.leave_one_state_condition(guard, sen);
61
62 let new_looped_once_guard = match self.state {
63 0 => Some(final_state_guard.clone()),
64 _ => looped_once_guard,
65 };
66 self.state += 1;
67 (
68 vec![IncompleteTransition::new(
69 self.state - 1,
70 final_state_guard,
71 )],
72 new_looped_once_guard,
73 )
74 } else {
75 sen.group.borrow().assignments.iter().for_each(|sassign| {
76 sassign
77 .guard
78 .compute_live_states(sen.group.borrow().latency)
79 .into_iter()
80 .for_each(|offset| {
81 let mut assign: ir::Assignment<ir::Nothing> =
83 ir::Assignment::from(sassign.clone());
84 assign.and_guard(guard.clone());
86 self.state2assigns
89 .entry(self.state + offset)
90 .and_modify(|other_assigns| {
91 other_assigns.push(assign.clone())
92 })
93 .or_insert(vec![assign]);
94 })
95 });
96 self.state += sen.group.borrow().latency;
97 (
101 vec![IncompleteTransition::new(
102 self.state - 1,
103 ir::Guard::True,
104 )],
105 looped_once_guard,
106 )
107 }
108 }
109 ir::StaticControl::Seq(sseq) => sseq.stmts.iter().fold(
110 (transitions_to_curr, looped_once_guard),
111 |(transitions_to_this_stmt, looped_once_guard_this_stmt),
112 stmt| {
113 self.build_abstract_fsm_with_loop(
114 stmt,
115 guard.clone(),
116 transitions_to_this_stmt,
117 looped_once_guard_this_stmt,
118 )
119 },
120 ),
121
122 ir::StaticControl::If(_) => {
123 unreachable!(
124 "`construct_schedule` encountered a `static_if` node. \
125 Should have been compiled into a static group."
126 )
127 }
128 ir::StaticControl::Repeat(_) => {
129 unreachable!(
130 "`construct_schedule` encountered a `static_repeat` node. \
131 Should have been compiled into a static group."
132 )
133 }
134 ir::StaticControl::Par(_) => {
135 unreachable!(
136 "`construct_schedule` encountered a `static_par` node. \
137 Should have been compiled into a static group."
138 )
139 }
140 ir::StaticControl::Invoke(_) => {
141 unreachable!(
142 "`construct_schedule` encountered a `static_invoke` node. \
143 Should have been compiled away."
144 )
145 }
146 }
147 }
148
149 fn realize_fsm(
152 &mut self,
153 control: &ir::StaticControl,
154 non_promoted_static_component: bool,
155 ) -> ir::RRC<ir::FSM> {
156 let true_guard = ir::Guard::True;
157 let signal_on = self.builder.add_constant(1, 1);
158
159 let fsm = self.builder.add_fsm("fsm");
161
162 let (mut remaining_assignments, additional_looped_once_guard) = self
163 .build_abstract_fsm_with_loop(
164 control,
165 ir::Guard::True,
166 vec![],
167 None,
168 );
169
170 self.register_transitions(
172 0,
173 &mut remaining_assignments,
174 ir::Guard::True,
175 );
176
177 let (mut assignments, transitions, state2wires) =
178 self.build_fsm_pieces(ir::RRC::clone(&fsm));
179
180 if non_promoted_static_component {
181 let assign_fsm_start = self.builder.build_assignment(
189 fsm.borrow().get("start"),
190 self.builder
191 .component
192 .signature
193 .borrow()
194 .find_unique_with_attr(ir::NumAttr::Go)
195 .unwrap()
196 .unwrap(),
197 true_guard,
198 );
199 self.builder
200 .add_continuous_assignments(vec![assign_fsm_start]);
201 } else {
202 let looped_once: ir::RRC<ir::Cell> =
210 self.builder.add_primitive("looped_once", "std_reg", &[1]);
211
212 looped_once
213 .borrow_mut()
214 .add_attribute(ir::BoolAttr::FSMControl, 1);
215
216 let (assign_looped_once, assign_looped_once_we, fsm_done) = (
217 self.builder.build_assignment(
218 looped_once.borrow().get("in"),
219 signal_on.borrow().get("out"),
220 match additional_looped_once_guard {
221 None => ir::guard!(fsm["start"]),
222 Some(g) => ir::guard!(fsm["start"]).and(g),
223 },
224 ),
225 self.builder.build_assignment(
226 looped_once.borrow().get("write_en"),
227 signal_on.borrow().get("out"),
228 ir::Guard::True,
229 ),
230 self.builder.build_assignment(
231 fsm.borrow().get("done"),
232 looped_once.borrow().get("out"),
233 ir::Guard::True,
234 ),
235 );
236
237 assignments.first_mut().unwrap().extend(vec![
238 assign_looped_once,
239 assign_looped_once_we,
240 fsm_done,
241 ]);
242 }
243
244 self.builder.add_continuous_assignments(
245 self.state2assigns
246 .drain()
247 .flat_map(|(state, mut assigns)| {
248 assigns.iter_mut().for_each(|assign| {
249 assign.and_guard(ir::Guard::port(
250 state2wires
251 .get(state as usize)
252 .unwrap()
253 .borrow()
254 .get("out"),
255 ));
256 });
257 assigns
258 })
259 .collect(),
260 );
261
262 fsm.borrow_mut().extend_fsm(assignments, transitions);
264 fsm
265 }
266}
267
268impl Visitor for StaticFSMAllocation {
269 fn start_static_control(
270 &mut self,
271 s: &mut calyx_ir::StaticControl,
272 comp: &mut calyx_ir::Component,
273 sigs: &calyx_ir::LibrarySignatures,
274 _comps: &[calyx_ir::Component],
275 ) -> crate::traversal::VisResult {
276 self.non_promoted_static_component = comp.is_static()
277 && !(comp
278 .attributes
279 .has(ir::Attribute::Bool(ir::BoolAttr::Promoted)));
280 let mut builder = ir::Builder::new(comp, sigs);
281
282 let mut ssch = StaticSchedule::from(&mut builder);
283
284 Ok(Action::change(ir::Control::fsm_enable(
285 ssch.realize_fsm(s, self.non_promoted_static_component),
286 )))
287 }
288 fn finish(
289 &mut self,
290 _comp: &mut ir::Component,
291 _sigs: &ir::LibrarySignatures,
292 _comps: &[ir::Component],
293 ) -> crate::traversal::VisResult {
294 if self.non_promoted_static_component {
297 Ok(Action::Change(Box::new(ir::Control::empty())))
298 } else {
299 Ok(Action::Continue)
300 }
301 }
302}