1use crate::analysis::{IncompleteTransition, StaticSchedule};
2use crate::traversal::{Action, ConstructVisitor, Named, Visitor};
3use calyx_ir::{self as ir, BoolAttr, GetAttributes};
4use calyx_utils::CalyxResult;
5use core::ops::Not;
6use itertools::Itertools;
7
8pub struct StaticRepeatFSMAllocation {}
9
10impl Named for StaticRepeatFSMAllocation {
11 fn name() -> &'static str {
12 "static-repeat-fsm-alloc"
13 }
14 fn description() -> &'static str {
15 "compiles a static repeat into an FSM construct"
16 }
17}
18
19impl ConstructVisitor for StaticRepeatFSMAllocation {
20 fn from(_ctx: &ir::Context) -> CalyxResult<Self> {
21 Ok(StaticRepeatFSMAllocation {})
22 }
23 fn clear_data(&mut self) {}
24}
25
26impl StaticSchedule<'_, '_> {
27 fn build_abstract_fsm(
33 &mut self,
34 scon: &ir::StaticControl,
35 guard: ir::Guard<ir::Nothing>,
36 mut transitions_to_curr: Vec<IncompleteTransition>,
37 ) -> Vec<IncompleteTransition> {
38 match scon {
39 ir::StaticControl::Empty(_) => transitions_to_curr,
40 ir::StaticControl::Enable(sen) => {
41 self.register_transitions(
44 self.state,
45 &mut transitions_to_curr,
46 guard.clone(),
47 );
48
49 if sen.attributes.has(BoolAttr::OneState) {
52 let final_state_guard =
53 self.leave_one_state_condition(guard, sen);
54
55 self.state += 1;
56 vec![IncompleteTransition::new(
57 self.state - 1,
58 final_state_guard,
59 )]
60 } else {
61 sen.group.borrow().assignments.iter().for_each(|sassign| {
62 sassign
63 .guard
64 .compute_live_states(sen.group.borrow().latency)
65 .into_iter()
66 .for_each(|offset| {
67 let mut assign: ir::Assignment<ir::Nothing> =
69 ir::Assignment::from(sassign.clone());
70 assign.and_guard(guard.clone());
72 self.state2assigns
75 .entry(self.state + offset)
76 .and_modify(|other_assigns| {
77 other_assigns.push(assign.clone())
78 })
79 .or_insert(vec![assign]);
80 })
81 });
82 self.state += sen.group.borrow().latency;
83 vec![IncompleteTransition::new(
87 self.state - 1,
88 ir::Guard::True,
89 )]
90 }
91 }
92 ir::StaticControl::Seq(sseq) => sseq.stmts.iter().fold(
93 transitions_to_curr,
94 |transitions_to_this_stmt, stmt| {
95 self.build_abstract_fsm(
96 stmt,
97 guard.clone(),
98 transitions_to_this_stmt,
99 )
100 },
101 ),
102
103 ir::StaticControl::If(sif) => {
104 let build_branch_guard =
106 |is_true_branch: bool| -> ir::Guard<ir::Nothing> {
107 guard.clone().and({
108 if is_true_branch {
109 ir::Guard::port(sif.port.clone())
110 } else {
111 ir::Guard::not(ir::Guard::port(
112 sif.port.clone(),
113 ))
114 }
115 })
116 };
117 self.build_abstract_fsm(
118 &sif.tbranch,
119 guard.clone().and(build_branch_guard(true)),
120 transitions_to_curr.clone(),
121 )
122 .into_iter()
123 .chain(self.build_abstract_fsm(
124 &sif.fbranch,
125 guard.clone().and(build_branch_guard(false)),
126 transitions_to_curr.clone(),
127 ))
128 .collect()
129 }
130 ir::StaticControl::Repeat(srep) => {
131 (0..srep.num_repeats).fold(
133 transitions_to_curr,
134 |transitions_to_this_body, _| {
135 self.build_abstract_fsm(
136 &srep.body,
137 guard.clone(),
138 transitions_to_this_body,
139 )
140 },
141 )
142 }
143 ir::StaticControl::Par(_) => {
144 unreachable!(
145 "`construct_schedule` encountered a `static_par` node. \
146 Should have been compiled into a static group."
147 )
148 }
149 ir::StaticControl::Invoke(_) => {
150 unreachable!(
151 "`construct_schedule` encountered a `static_invoke` node. \
152 Should have been compiled away."
153 )
154 }
155 }
156 }
157
158 fn build_fsm(&mut self, control: &ir::StaticControl) -> ir::RRC<ir::FSM> {
161 let fsm = self.builder.add_fsm("fsm");
162
163 let mut remaining_assignments =
164 self.build_abstract_fsm(control, ir::Guard::True, vec![]);
165
166 self.register_transitions(
168 0,
169 &mut remaining_assignments,
170 ir::Guard::True,
171 );
172
173 let (assignments, transitions, state2wires) =
174 self.build_fsm_pieces(ir::RRC::clone(&fsm));
175
176 self.builder.add_continuous_assignments(
177 self.state2assigns
178 .drain()
179 .flat_map(|(state, mut assigns)| {
180 assigns.iter_mut().for_each(|assign| {
181 assign.and_guard(ir::Guard::port(
182 state2wires
183 .get(state as usize)
184 .unwrap()
185 .borrow()
186 .get("out"),
187 ));
188 });
189 assigns
190 })
191 .collect(),
192 );
193
194 fsm.borrow_mut().extend_fsm(assignments, transitions);
195 fsm
196 }
197}
198
199impl Visitor for StaticRepeatFSMAllocation {
200 fn finish_static_if(
201 &mut self,
202 s: &mut calyx_ir::StaticIf,
203 comp: &mut calyx_ir::Component,
204 sigs: &calyx_ir::LibrarySignatures,
205 _comps: &[calyx_ir::Component],
206 ) -> crate::traversal::VisResult {
207 let mut builder = ir::Builder::new(comp, sigs);
208 let signal_on = builder.add_constant(1, 1);
209
210 let mut sch_constructor_true = StaticSchedule::from(&mut builder);
212 let true_branch_fsm = sch_constructor_true.build_fsm(&s.tbranch);
213
214 let if_group = builder.add_static_group("if", s.latency);
216 let true_guard: ir::Guard<ir::StaticTiming> =
217 ir::Guard::port(ir::RRC::clone(&s.port));
218 let false_guard = ir::Guard::not(true_guard.clone());
219
220 let mut trigger_fsms_with_branch_latency = vec![(
222 builder.build_assignment(
223 true_branch_fsm.borrow().get("start"),
224 signal_on.borrow().get("out"),
225 true_guard,
226 ),
227 s.tbranch.get_latency(),
228 )];
229
230 if !(matches!(&*s.fbranch, ir::StaticControl::Empty(_))) {
232 let mut sch_constructor_false = StaticSchedule::from(&mut builder);
233 let false_branch_fsm = sch_constructor_false.build_fsm(&s.fbranch);
234 trigger_fsms_with_branch_latency.push((
235 builder.build_assignment(
236 false_branch_fsm.borrow().get("start"),
237 signal_on.borrow().get("out"),
238 false_guard,
239 ),
240 s.fbranch.get_latency(),
241 ));
242 }
243
244 let trigger_fsms = trigger_fsms_with_branch_latency
248 .into_iter()
249 .map(|(mut assign, latency)| {
250 assign
251 .guard
252 .add_interval(ir::StaticTiming::new((0, latency)));
253 assign
254 })
255 .collect_vec();
256
257 if_group.borrow_mut().assignments.extend(trigger_fsms);
258
259 let mut enable = ir::StaticControl::Enable(ir::StaticEnable {
262 group: if_group,
263 attributes: ir::Attributes::default(),
264 });
265 enable
266 .get_mut_attributes()
267 .insert(ir::BoolAttr::OneState, 1);
268
269 Ok(Action::static_change(enable))
270 }
271
272 fn finish_static_par(
273 &mut self,
274 s: &mut calyx_ir::StaticPar,
275 comp: &mut calyx_ir::Component,
276 sigs: &calyx_ir::LibrarySignatures,
277 _comps: &[calyx_ir::Component],
278 ) -> crate::traversal::VisResult {
279 let mut builder = ir::Builder::new(comp, sigs);
280 let signal_on = builder.add_constant(1, 1);
281 let par_group = builder.add_static_group("par", s.latency);
282 par_group
283 .borrow_mut()
284 .assignments
285 .extend(s.stmts.iter().map(|thread: &ir::StaticControl| {
286 let mut sch_generator = StaticSchedule::from(&mut builder);
287 let thread_latency = thread.get_latency();
288 let thread_fsm = sch_generator.build_fsm(thread);
289 let mut trigger_thread = builder.build_assignment(
290 thread_fsm.borrow().get("start"),
291 signal_on.borrow().get("out"),
292 ir::Guard::True,
293 );
294 trigger_thread
295 .guard
296 .add_interval(ir::StaticTiming::new((0, thread_latency)));
297 trigger_thread
298 }));
299
300 let mut enable = ir::StaticControl::Enable(ir::StaticEnable {
301 group: par_group,
302 attributes: ir::Attributes::default(),
303 });
304 enable
305 .get_mut_attributes()
306 .insert(ir::BoolAttr::OneState, 1);
307
308 Ok(Action::static_change(enable))
309 }
310
311 fn finish_static_repeat(
312 &mut self,
313 s: &mut calyx_ir::StaticRepeat,
314 comp: &mut calyx_ir::Component,
315 sigs: &calyx_ir::LibrarySignatures,
316 _comps: &[calyx_ir::Component],
317 ) -> crate::traversal::VisResult {
318 let mut builder = ir::Builder::new(comp, sigs);
319 let signal_on = builder.add_constant(1, 1);
320 let repeat_group = builder.add_static_group("repeat", s.latency);
321 let mut sch_generator = StaticSchedule::from(&mut builder);
322 let trigger_fsm = if false {
324 let dummy_repeat = ir::StaticRepeat {
331 attributes: ir::Attributes::default(),
332 body: Box::new(ir::StaticControl::empty()),
333 num_repeats: 0,
334 latency: 0,
335 };
336
337 let repeat_node = std::mem::replace(s, dummy_repeat);
338 let sc_wrapper = ir::StaticControl::Repeat(repeat_node);
339 let fsm = sch_generator.build_fsm(&sc_wrapper);
340 let mut trigger_thread = builder.build_assignment(
341 fsm.borrow().get("start"),
342 signal_on.borrow().get("out"),
343 ir::Guard::True,
344 );
345 trigger_thread
346 .guard
347 .add_interval(ir::StaticTiming::new((0, 1)));
348 trigger_thread
349 } else {
350 let fsm = sch_generator.build_fsm(&s.body);
352
353 let mut trigger_thread = builder.build_assignment(
354 fsm.borrow().get("start"),
355 signal_on.borrow().get("out"),
356 ir::Guard::True,
357 );
358 trigger_thread
362 .guard
363 .add_interval(ir::StaticTiming::new((0, s.latency)));
364 trigger_thread
365 };
366
367 repeat_group.borrow_mut().assignments.push(trigger_fsm);
368 let mut enable = ir::StaticControl::Enable(ir::StaticEnable {
369 group: repeat_group,
370 attributes: ir::Attributes::default(),
371 });
372 enable
373 .get_mut_attributes()
374 .insert(ir::BoolAttr::OneState, 1);
375
376 Ok(Action::static_change(enable))
377 }
378}