1use super::compile_static::make_assign_dyn;
2use crate::passes::math_utilities::get_bit_width_from;
3use crate::traversal::{Action, Named, VisResult, Visitor};
4use calyx_ir as ir;
5use ir::{
6 build_assignments, guard, structure, Attributes, Guard, Nothing,
7 StaticTiming, RRC,
8};
9use itertools::Itertools;
10use std::cell::RefCell;
11use std::rc::Rc;
12
13#[derive(Default)]
14pub struct CompileStaticInterface;
15
16impl Named for CompileStaticInterface {
17 fn name() -> &'static str {
18 "compile-static-interface"
19 }
20
21 fn description() -> &'static str {
22 "Compiles Static Component Interface"
23 }
24}
25
26fn separate_first_cycle(
28 guard: ir::Guard<StaticTiming>,
29) -> ir::Guard<StaticTiming> {
30 match guard {
31 ir::Guard::Info(st) => {
32 let (beg, end) = st.get_interval();
33 if beg == 0 && end != 1 {
34 let first_cycle =
35 ir::Guard::Info(ir::StaticTiming::new((0, 1)));
36 let after = ir::Guard::Info(ir::StaticTiming::new((1, end)));
37 let cong = ir::Guard::or(first_cycle, after);
38 return cong;
39 }
40 guard
41 }
42 ir::Guard::And(l, r) => {
43 let left = separate_first_cycle(*l);
44 let right = separate_first_cycle(*r);
45 ir::Guard::and(left, right)
46 }
47 ir::Guard::Or(l, r) => {
48 let left = separate_first_cycle(*l);
49 let right = separate_first_cycle(*r);
50 ir::Guard::or(left, right)
51 }
52 ir::Guard::Not(g) => {
53 let a = separate_first_cycle(*g);
54 ir::Guard::Not(Box::new(a))
55 }
56 _ => guard,
57 }
58}
59
60fn separate_first_cycle_assign(
62 assign: ir::Assignment<StaticTiming>,
63) -> ir::Assignment<StaticTiming> {
64 ir::Assignment {
65 src: assign.src,
66 dst: assign.dst,
67 attributes: assign.attributes,
68 guard: Box::new(separate_first_cycle(*assign.guard)),
69 }
70}
71
72fn make_guard_dyn_one_cycle_static_comp(
75 guard: ir::Guard<StaticTiming>,
76 comp_sig: RRC<ir::Cell>,
77) -> ir::Guard<Nothing> {
78 match guard {
79 ir::Guard::Or(l, r) => {
80 let left =
81 make_guard_dyn_one_cycle_static_comp(*l, Rc::clone(&comp_sig));
82 let right =
83 make_guard_dyn_one_cycle_static_comp(*r, Rc::clone(&comp_sig));
84 ir::Guard::or(left, right)
85 }
86 ir::Guard::And(l, r) => {
87 let left =
88 make_guard_dyn_one_cycle_static_comp(*l, Rc::clone(&comp_sig));
89 let right =
90 make_guard_dyn_one_cycle_static_comp(*r, Rc::clone(&comp_sig));
91 ir::Guard::and(left, right)
92 }
93 ir::Guard::Not(g) => {
94 let f =
95 make_guard_dyn_one_cycle_static_comp(*g, Rc::clone(&comp_sig));
96 ir::Guard::Not(Box::new(f))
97 }
98 ir::Guard::Info(t) => {
99 match t.get_interval() {
100 (0, 1) => guard!(comp_sig["go"]),
101 _ => unreachable!("This function is implemented for 1 cycle static components, only %0 can exist as timing guard"),
102
103 }
104 }
105 ir::Guard::CompOp(op, l, r) => ir::Guard::CompOp(op, l, r),
106 ir::Guard::Port(p) => ir::Guard::Port(p),
107 ir::Guard::True => ir::Guard::True,
108 }
109}
110
111fn make_assign_dyn_one_cycle_static_comp(
114 assign: ir::Assignment<StaticTiming>,
115 comp_sig: RRC<ir::Cell>,
116) -> ir::Assignment<Nothing> {
117 ir::Assignment {
118 src: assign.src,
119 dst: assign.dst,
120 attributes: assign.attributes,
121 guard: Box::new(make_guard_dyn_one_cycle_static_comp(
122 *assign.guard,
123 comp_sig,
124 )),
125 }
126}
127
128impl CompileStaticInterface {
129 fn make_early_reset_assigns_static_component(
133 &mut self,
134 sgroup_assigns: &mut Vec<ir::Assignment<ir::StaticTiming>>,
135 latency: u64,
136 fsm: ir::RRC<ir::Cell>,
137 builder: &mut ir::Builder,
138 comp_sig: RRC<ir::Cell>,
139 ) -> Vec<ir::Assignment<Nothing>> {
140 let fsm_name = fsm.borrow().name();
141 let fsm_size = fsm
142 .borrow()
143 .find("out")
144 .unwrap_or_else(|| unreachable!("no `out` port on {fsm_name}"))
145 .borrow()
146 .width;
147 let mut assigns = sgroup_assigns
148 .drain(..)
149 .map(separate_first_cycle_assign)
150 .collect_vec();
151 let mut dyn_assigns = assigns
152 .drain(..)
153 .map(|assign| {
154 make_assign_dyn(
155 assign,
156 &fsm,
157 fsm_size,
158 builder,
159 true,
160 Some(Rc::clone(&comp_sig)),
161 )
162 })
163 .collect_vec();
164 let this = Rc::clone(&comp_sig);
165 structure!( builder;
166 let signal_on = constant(1,1);
168 let adder = prim std_add(fsm_size);
169 let const_one = constant(1, fsm_size);
170 let first_state = constant(0, fsm_size);
171 let final_state = constant(latency-1, fsm_size);
172 );
173 let g1: Guard<Nothing> = guard!(this["go"]);
174 let g2: Guard<Nothing> = guard!(fsm["out"] == first_state["out"]);
175 let trigger_guard = ir::Guard::and(g1, g2);
176 let g3: Guard<Nothing> = guard!(fsm["out"] != first_state["out"]);
177 let g4: Guard<Nothing> = guard!(fsm["out"] != final_state["out"]);
178 let incr_guard = ir::Guard::and(g3, g4);
179 let stop_guard: Guard<Nothing> =
180 guard!(fsm["out"] == final_state["out"]);
181 let fsm_incr_assigns = build_assignments!(
182 builder;
183 adder["left"] = ? fsm["out"];
185 adder["right"] = ? const_one["out"];
186 fsm["write_en"] = ? signal_on["out"];
188 fsm["in"] = trigger_guard ? const_one["out"];
190 fsm["in"] = incr_guard ? adder["out"];
192 fsm["in"] = stop_guard ? first_state["out"];
194 );
198 dyn_assigns.extend(fsm_incr_assigns);
199
200 dyn_assigns
201 }
202
203 fn make_done_signal_for_promoted_component(
205 &mut self,
206 fsm: ir::RRC<ir::Cell>,
207 builder: &mut ir::Builder,
208 comp_sig: RRC<ir::Cell>,
209 ) -> Vec<ir::Assignment<Nothing>> {
210 let fsm_size = fsm
211 .borrow()
212 .find("out")
213 .unwrap_or_else(|| {
214 unreachable!("no `out` port on {}", fsm.borrow().name())
215 })
216 .borrow()
217 .width;
218 structure!(builder;
219 let sig_reg = prim std_reg(1);
220 let one = constant(1, 1);
221 let zero = constant(0, 1);
222 let first_state = constant(0, fsm_size);
223 );
224 let go_guard = guard!(comp_sig["go"]);
225 let not_go_guard = !guard!(comp_sig["go"]);
226 let first_state_guard = guard!(fsm["out"] == first_state["out"]);
227 let comp_done_guard =
228 guard!(fsm["out"] == first_state["out"]) & guard!(sig_reg["out"]);
229 let assigns = build_assignments!(builder;
230 sig_reg["write_en"] = first_state_guard ? one["out"];
232 sig_reg["in"] = go_guard ? one["out"];
236 sig_reg["in"] = not_go_guard ? zero["out"];
238 comp_sig["done"] = comp_done_guard ? one["out"];
241 );
242 assigns.to_vec()
243 }
244
245 fn make_done_signal_for_promoted_component_one_cycle(
246 &mut self,
247 builder: &mut ir::Builder,
248 comp_sig: RRC<ir::Cell>,
249 ) -> Vec<ir::Assignment<Nothing>> {
250 structure!(builder;
251 let sig_reg = prim std_reg(1);
252 let one = constant(1, 1);
253 let zero = constant(0, 1);
254 );
255 let go_guard = guard!(comp_sig["go"]);
256 let not_go = !guard!(comp_sig["go"]);
257 let signal_on_guard = guard!(sig_reg["out"]);
258 let assigns = build_assignments!(builder;
259 sig_reg["in"] = go_guard ? one["out"];
264 sig_reg["in"] = not_go ? zero["out"];
265 sig_reg["write_en"] = ? one["out"];
266 comp_sig["done"] = signal_on_guard ? one["out"];
267 );
268 assigns.to_vec()
269 }
270}
271
272impl Visitor for CompileStaticInterface {
273 fn start_static_control(
274 &mut self,
275 s: &mut ir::StaticControl,
276 comp: &mut ir::Component,
277 sigs: &ir::LibrarySignatures,
278 _comps: &[ir::Component],
279 ) -> VisResult {
280 if comp.is_static() && s.get_latency() > 1 {
281 let latency = s.get_latency();
283 if let ir::StaticControl::Enable(sen) = s {
284 let mut builder = ir::Builder::new(comp, sigs);
285 let fsm_size = get_bit_width_from(latency + 1);
286 structure!( builder;
287 let fsm = prim std_reg(fsm_size);
288 );
289 let mut assignments =
290 std::mem::take(&mut sen.group.borrow_mut().assignments);
291 let comp_sig = Rc::clone(&builder.component.signature);
292 let dyn_assigns = self
293 .make_early_reset_assigns_static_component(
294 &mut assignments,
295 s.get_latency(),
296 Rc::clone(&fsm),
297 &mut builder,
298 Rc::clone(&comp_sig),
299 );
300 builder.component.continuous_assignments.extend(dyn_assigns);
301 if builder.component.attributes.has(ir::BoolAttr::Promoted) {
302 let done_assigns = self
303 .make_done_signal_for_promoted_component(
304 Rc::clone(&fsm),
305 &mut builder,
306 Rc::clone(&comp_sig),
307 );
308 builder
309 .component
310 .continuous_assignments
311 .extend(done_assigns);
312 }
313 }
314 } else if comp.is_static() && s.get_latency() == 1 {
315 if let ir::StaticControl::Enable(sen) = s {
317 let assignments =
318 std::mem::take(&mut sen.group.borrow_mut().assignments);
319 for assign in assignments {
320 let comp_sig = Rc::clone(&comp.signature);
321 comp.continuous_assignments.push(
322 make_assign_dyn_one_cycle_static_comp(assign, comp_sig),
323 );
324 }
325 if comp.attributes.has(ir::BoolAttr::Promoted) {
326 let mut builder = ir::Builder::new(comp, sigs);
327 let comp_sig = Rc::clone(&builder.component.signature);
328 let done_assigns = self
329 .make_done_signal_for_promoted_component_one_cycle(
330 &mut builder,
331 comp_sig,
332 );
333 builder
334 .component
335 .continuous_assignments
336 .extend(done_assigns);
337 }
338 }
339 }
340 Ok(Action::Continue)
341 }
342
343 fn finish(
344 &mut self,
345 comp: &mut ir::Component,
346 _sigs: &ir::LibrarySignatures,
347 _comps: &[ir::Component],
348 ) -> VisResult {
349 if comp.is_static() {
351 let _c = std::mem::replace(
352 &mut comp.control,
353 Rc::new(RefCell::new(ir::Control::Empty(ir::Empty {
354 attributes: Attributes::default(),
355 }))),
356 );
357 }
358 Ok(Action::Stop)
359 }
360}