1use super::math_utilities::get_bit_width_from;
2use crate::analysis::GraphColoring;
3use crate::traversal::{Action, Named, VisResult, Visitor};
4use calyx_ir as ir;
5use calyx_ir::{guard, structure, GetAttributes};
6use calyx_utils::Error;
7use ir::{build_assignments, Nothing, StaticTiming, RRC};
8use itertools::Itertools;
9use std::collections::{HashMap, HashSet};
10use std::ops::Not;
11use std::rc::Rc;
12
13#[derive(Default)]
14pub struct CompileStatic {
16 reset_early_map: HashMap<ir::Id, ir::Id>,
18 wrapper_map: HashMap<ir::Id, ir::Id>,
20 signal_reg_map: HashMap<ir::Id, ir::Id>,
22 fsm_info_map: HashMap<ir::Id, (ir::Id, u64)>,
24 group_rewrite: ir::rewriter::PortRewriteMap,
26}
27
28impl Named for CompileStatic {
29 fn name() -> &'static str {
30 "compile-static"
31 }
32
33 fn description() -> &'static str {
34 "compiles static sub-programs into a dynamic group"
35 }
36}
37
38pub(super) fn make_guard_dyn(
43 guard: ir::Guard<StaticTiming>,
44 fsm: &ir::RRC<ir::Cell>,
45 fsm_size: u64,
46 builder: &mut ir::Builder,
47 is_static_comp: bool,
48 comp_sig: Option<RRC<ir::Cell>>,
49) -> Box<ir::Guard<Nothing>> {
50 match guard {
51 ir::Guard::Or(l, r) => Box::new(ir::Guard::Or(
52 make_guard_dyn(
53 *l,
54 fsm,
55 fsm_size,
56 builder,
57 is_static_comp,
58 comp_sig.clone(),
59 ),
60 make_guard_dyn(
61 *r,
62 fsm,
63 fsm_size,
64 builder,
65 is_static_comp,
66 comp_sig,
67 ),
68 )),
69 ir::Guard::And(l, r) => Box::new(ir::Guard::And(
70 make_guard_dyn(
71 *l,
72 fsm,
73 fsm_size,
74 builder,
75 is_static_comp,
76 comp_sig.clone(),
77 ),
78 make_guard_dyn(
79 *r,
80 fsm,
81 fsm_size,
82 builder,
83 is_static_comp,
84 comp_sig,
85 ),
86 )),
87 ir::Guard::Not(g) => Box::new(ir::Guard::Not(make_guard_dyn(
88 *g,
89 fsm,
90 fsm_size,
91 builder,
92 is_static_comp,
93 comp_sig,
94 ))),
95 ir::Guard::CompOp(op, l, r) => Box::new(ir::Guard::CompOp(op, l, r)),
96 ir::Guard::Port(p) => Box::new(ir::Guard::Port(p)),
97 ir::Guard::True => Box::new(ir::Guard::True),
98 ir::Guard::Info(static_timing) => {
99 let (beg, end) = static_timing.get_interval();
100 if is_static_comp && beg == 0 && end == 1 {
101 let interval_const = builder.add_constant(0, fsm_size);
102 let sig = comp_sig.unwrap();
103 let g1 = guard!(sig["go"]);
104 let g2 = guard!(fsm["out"] == interval_const["out"]);
105 let g = ir::Guard::And(Box::new(g1), Box::new(g2));
106 return Box::new(g);
107 }
108 if beg + 1 == end {
109 let interval_const = builder.add_constant(beg, fsm_size);
111 let g = guard!(fsm["out"] == interval_const["out"]);
112 Box::new(g)
113 } else if beg == 0 {
114 let end_const = builder.add_constant(end, fsm_size);
116 let lt: ir::Guard<Nothing> =
117 guard!(fsm["out"] < end_const["out"]);
118 Box::new(lt)
119 } else {
120 let beg_const = builder.add_constant(beg, fsm_size);
122 let end_const = builder.add_constant(end, fsm_size);
123 let beg_guard: ir::Guard<Nothing> =
124 guard!(fsm["out"] >= beg_const["out"]);
125 let end_guard: ir::Guard<Nothing> =
126 guard!(fsm["out"] < end_const["out"]);
127 Box::new(ir::Guard::And(
128 Box::new(beg_guard),
129 Box::new(end_guard),
130 ))
131 }
132 }
133 }
134}
135
136pub(super) fn make_assign_dyn(
139 assign: ir::Assignment<StaticTiming>,
140 fsm: &ir::RRC<ir::Cell>,
141 fsm_size: u64,
142 builder: &mut ir::Builder,
143 is_static_comp: bool,
144 comp_sig: Option<RRC<ir::Cell>>,
145) -> ir::Assignment<Nothing> {
146 ir::Assignment {
147 src: assign.src,
148 dst: assign.dst,
149 attributes: assign.attributes,
150 guard: make_guard_dyn(
151 *assign.guard,
152 fsm,
153 fsm_size,
154 builder,
155 is_static_comp,
156 comp_sig,
157 ),
158 }
159}
160
161fn find_static_group(
164 name: &ir::Id,
165 static_groups: &[ir::RRC<ir::StaticGroup>],
166) -> ir::RRC<ir::StaticGroup> {
167 Rc::clone(
168 static_groups
169 .iter()
170 .find(|static_group| static_group.borrow().name() == name)
171 .unwrap_or_else(|| {
172 unreachable!("couldn't find static group {name}")
173 }),
174 )
175}
176
177fn get_go_writes(sgroup: &ir::RRC<ir::StaticGroup>) -> HashSet<ir::Id> {
184 let mut uses = HashSet::new();
185 for asgn in &sgroup.borrow().assignments {
186 let dst = asgn.dst.borrow();
187 if dst.is_hole() && dst.name == "go" {
188 uses.insert(dst.get_parent_name());
189 }
190 }
191 uses
192}
193
194impl CompileStatic {
195 fn make_early_reset_group(
201 &mut self,
202 sgroup_assigns: &mut Vec<ir::Assignment<ir::StaticTiming>>,
203 sgroup_name: ir::Id,
204 latency: u64,
205 attributes: ir::Attributes,
206 fsm: ir::RRC<ir::Cell>,
207 builder: &mut ir::Builder,
208 ) -> ir::RRC<ir::Group> {
209 let fsm_name = fsm.borrow().name();
210 let fsm_size = fsm
211 .borrow()
212 .find("out")
213 .unwrap_or_else(|| unreachable!("no `out` port on {fsm_name}"))
214 .borrow()
215 .width;
216 structure!( builder;
217 let ud = prim undef(1);
219 let signal_on = constant(1,1);
220 let adder = prim std_add(fsm_size);
221 let const_one = constant(1, fsm_size);
222 let first_state = constant(0, fsm_size);
223 let penultimate_state = constant(latency-1, fsm_size);
224 );
225 let mut early_reset_name = sgroup_name.to_string();
227 early_reset_name.insert_str(0, "early_reset_");
228 let g = builder.add_group(early_reset_name);
229 let mut assigns = sgroup_assigns
231 .drain(..)
232 .map(|assign| {
233 make_assign_dyn(assign, &fsm, fsm_size, builder, false, None)
234 })
235 .collect_vec();
236 let not_penultimate_state_guard: ir::Guard<ir::Nothing> =
238 guard!(fsm["out"] != penultimate_state["out"]);
239 let penultimate_state_guard: ir::Guard<ir::Nothing> =
240 guard!(fsm["out"] == penultimate_state["out"]);
241 let fsm_incr_assigns = build_assignments!(
242 builder;
243 adder["left"] = ? fsm["out"];
245 adder["right"] = ? const_one["out"];
246 fsm["write_en"] = ? signal_on["out"];
247 fsm["in"] = not_penultimate_state_guard ? adder["out"];
248 fsm["in"] = penultimate_state_guard ? first_state["out"];
250 g["done"] = ? ud["out"];
253 );
254 assigns.extend(fsm_incr_assigns.to_vec());
255 self.fsm_info_map
258 .insert(g.borrow().name(), (fsm.borrow().name(), fsm_size));
259 g.borrow_mut().assignments = assigns;
262 g.borrow_mut().attributes = attributes;
263 g
264 }
265
266 fn build_wrapper_group(
267 fsm_name: &ir::Id,
268 fsm_width: u64,
269 group_name: &ir::Id,
270 signal_reg: ir::RRC<ir::Cell>,
271 builder: &mut ir::Builder,
272 add_continuous_assigns: bool,
273 ) -> ir::RRC<ir::Group> {
274 let early_reset_group = builder
276 .component
277 .get_groups()
278 .find(*group_name)
279 .unwrap_or_else(|| {
280 unreachable!(
281 "called build_wrapper_group with {}, which is not a group",
282 group_name
283 )
284 });
285 let early_reset_fsm =
286 builder.component.find_cell(*fsm_name).unwrap_or_else(|| {
287 unreachable!(
288 "called build_wrapper_group with {}, which is not an fsm",
289 fsm_name
290 )
291 });
292
293 structure!( builder;
294 let state_zero = constant(0, fsm_width);
295 let signal_on = constant(1, 1);
296 let signal_off = constant(0, 1);
297 );
298 let first_state: ir::Guard<ir::Nothing> =
301 guard!(early_reset_fsm["out"] == state_zero["out"]);
302 let signal_reg_guard: ir::Guard<ir::Nothing> =
304 guard!(signal_reg["out"]);
305 let not_signal_reg = signal_reg_guard.clone().not();
307 let first_state_and_signal = first_state.clone() & signal_reg_guard;
309 let first_state_and_not_signal = first_state & not_signal_reg;
311 let mut wrapper_name = group_name.clone().to_string();
313 wrapper_name.insert_str(0, "wrapper_");
314 let g = builder.add_group(wrapper_name);
315 let group_assigns = build_assignments!(
316 builder;
317 early_reset_group["go"] = ? signal_on["out"];
319 signal_reg["write_en"] = first_state_and_not_signal ? signal_on["out"];
321 signal_reg["in"] = first_state_and_not_signal ? signal_on["out"];
322 g["done"] = first_state_and_signal ? signal_on["out"];
324 );
325 if add_continuous_assigns {
326 let continuous_assigns = build_assignments!(
328 builder;
329 signal_reg["write_en"] = first_state_and_signal ? signal_on["out"];
332 signal_reg["in"] = first_state_and_signal ? signal_off["out"];
333 );
334 builder.add_continuous_assignments(continuous_assigns.to_vec());
335 }
336 g.borrow_mut().assignments = group_assigns.to_vec();
337 g.borrow_mut().attributes =
338 early_reset_group.borrow().attributes.clone();
339 g
340 }
341
342 fn get_reset_group_name(&self, sc: &mut ir::StaticControl) -> &ir::Id {
343 let ir::StaticControl::Enable(s) = sc else {
346 unreachable!("Non-Enable Static Control should have been compiled away. Run {} to do this", crate::passes::StaticInliner::name());
347 };
348
349 let sgroup = s.group.borrow_mut();
350 let sgroup_name = sgroup.name();
351 let early_reset_name =
354 self.reset_early_map.get(&sgroup_name).unwrap_or_else(|| {
355 unreachable!(
356 "group {} not in self.reset_early_map",
357 sgroup_name
358 )
359 });
360
361 early_reset_name
362 }
363
364 fn build_wrapper_group_while(
373 &self,
374 fsm_name: &ir::Id,
375 fsm_width: u64,
376 group_name: &ir::Id,
377 port: RRC<ir::Port>,
378 builder: &mut ir::Builder,
379 ) -> RRC<ir::Group> {
380 let reset_early_group = builder
381 .component
382 .find_group(*group_name)
383 .unwrap_or_else(|| {
384 unreachable!(
385 "called build_wrapper_group with {}, which is not a group",
386 group_name
387 )
388 });
389 let early_reset_fsm =
390 builder.component.find_cell(*fsm_name).unwrap_or_else(|| {
391 unreachable!(
392 "called build_wrapper_group with {}, which is not an fsm",
393 fsm_name
394 )
395 });
396
397 let wrapper_group =
398 builder.add_group(format!("while_wrapper_{}", group_name));
399
400 structure!(
401 builder;
402 let one = constant(1, 1);
403 let time_0 = constant(0, fsm_width);
404 );
405
406 let port_parent = port.borrow().cell_parent();
407 let port_name = port.borrow().name;
408 let done_guard = guard!(port_parent[port_name]).not()
409 & guard!(early_reset_fsm["out"] == time_0["out"]);
410
411 let assignments = build_assignments!(
412 builder;
413 reset_early_group["go"] = ? one["out"];
416 wrapper_group["done"] = done_guard ? one["out"];
417 );
418
419 wrapper_group.borrow_mut().assignments.extend(assignments);
420 wrapper_group
421 }
422
423 fn get_used_sgroups(
427 c: &ir::Control,
428 cur_names: &mut HashSet<ir::Id>,
429 sgroup_uses_map: &HashMap<ir::Id, HashSet<ir::Id>>,
430 ) {
431 match c {
432 ir::Control::Empty(_)
433 | ir::Control::Enable(_)
434 | ir::Control::Invoke(_) => (),
435 ir::Control::Static(sc) => {
436 let ir::StaticControl::Enable(s) = sc else {
437 unreachable!("Non-Enable Static Control should have been compiled away. Run {} to do this", crate::passes::StaticInliner::name());
438 };
439 let group_name = s.group.borrow().name();
440 if let Some(sgroup_uses) = sgroup_uses_map.get(&group_name) {
441 cur_names.extend(sgroup_uses);
442 }
443 cur_names.insert(group_name);
444 }
445 ir::Control::Par(ir::Par { stmts, .. })
446 | ir::Control::Seq(ir::Seq { stmts, .. }) => {
447 for stmt in stmts {
448 Self::get_used_sgroups(stmt, cur_names, sgroup_uses_map);
449 }
450 }
451 ir::Control::Repeat(ir::Repeat { body, .. })
452 | ir::Control::While(ir::While { body, .. }) => {
453 Self::get_used_sgroups(body, cur_names, sgroup_uses_map);
454 }
455 ir::Control::If(if_stmt) => {
456 Self::get_used_sgroups(
457 &if_stmt.tbranch,
458 cur_names,
459 sgroup_uses_map,
460 );
461 Self::get_used_sgroups(
462 &if_stmt.fbranch,
463 cur_names,
464 sgroup_uses_map,
465 );
466 }
467 }
468 }
469
470 fn add_par_conflicts(
478 c: &ir::Control,
479 sgroup_uses_map: &HashMap<ir::Id, HashSet<ir::Id>>,
480 conflict_graph: &mut GraphColoring<ir::Id>,
481 ) {
482 match c {
483 ir::Control::Empty(_)
484 | ir::Control::Enable(_)
485 | ir::Control::Invoke(_)
486 | ir::Control::Static(_) => (),
487 ir::Control::Seq(seq) => {
488 for stmt in &seq.stmts {
489 Self::add_par_conflicts(
490 stmt,
491 sgroup_uses_map,
492 conflict_graph,
493 );
494 }
495 }
496 ir::Control::Repeat(ir::Repeat { body, .. })
497 | ir::Control::While(ir::While { body, .. }) => {
498 Self::add_par_conflicts(body, sgroup_uses_map, conflict_graph)
499 }
500 ir::Control::If(if_stmt) => {
501 Self::add_par_conflicts(
502 &if_stmt.tbranch,
503 sgroup_uses_map,
504 conflict_graph,
505 );
506 Self::add_par_conflicts(
507 &if_stmt.fbranch,
508 sgroup_uses_map,
509 conflict_graph,
510 );
511 }
512 ir::Control::Par(par) => {
513 let mut sgroup_conflict_vec = Vec::new();
517 for stmt in &par.stmts {
518 let mut used_sgroups = HashSet::new();
519 Self::get_used_sgroups(
520 stmt,
521 &mut used_sgroups,
522 sgroup_uses_map,
523 );
524 sgroup_conflict_vec.push(used_sgroups);
525 }
526 for (thread1_sgroups, thread2_sgroups) in
527 sgroup_conflict_vec.iter().tuple_combinations()
528 {
529 for sgroup1 in thread1_sgroups {
530 for sgroup2 in thread2_sgroups {
531 conflict_graph.insert_conflict(sgroup1, sgroup2);
532 }
533 }
534 }
535 for stmt in &par.stmts {
537 Self::add_par_conflicts(
538 stmt,
539 sgroup_uses_map,
540 conflict_graph,
541 );
542 }
543 }
544 }
545 }
546
547 fn add_go_port_conflicts(
555 sgroup_uses_map: &HashMap<ir::Id, HashSet<ir::Id>>,
556 conflict_graph: &mut GraphColoring<ir::Id>,
557 ) {
558 for (sgroup, sgroup_uses) in sgroup_uses_map {
559 for sgroup_use in sgroup_uses {
560 conflict_graph.insert_conflict(sgroup_use, sgroup);
561 }
562 for (sgroup_use1, sgroup_use2) in
565 sgroup_uses.iter().tuple_combinations()
566 {
567 conflict_graph.insert_conflict(sgroup_use1, sgroup_use2);
568 }
569 }
570 }
571
572 fn build_fsm_mapping(
576 coloring: HashMap<ir::Id, ir::Id>,
577 static_groups: &[ir::RRC<ir::StaticGroup>],
578 builder: &mut ir::Builder,
579 ) -> HashMap<ir::Id, HashSet<ir::Id>> {
580 let mut color_to_groups: HashMap<ir::Id, HashSet<ir::Id>> =
582 HashMap::new();
583 for (group, color) in coloring {
584 color_to_groups.entry(color).or_default().insert(group);
585 }
586 let mut vec_color_to_groups: Vec<(ir::Id, HashSet<ir::Id>)> =
588 color_to_groups.into_iter().collect();
589 vec_color_to_groups
590 .sort_by(|(color1, _), (color2, _)| color1.cmp(color2));
591 vec_color_to_groups.into_iter().map(|(color, group_names)| {
592 let max_latency = group_names
595 .iter()
596 .map(|g| {
597 find_static_group(g, static_groups).borrow()
598 .latency
599 })
600 .max().unwrap_or_else(|| unreachable!("group {color} had no corresponding groups in its coloring map")
601 );
602 let fsm_size = get_bit_width_from(
603 max_latency + 1, );
605 structure!( builder;
606 let fsm = prim std_reg(fsm_size);
607 );
608 let fsm_name = fsm.borrow().name();
609 (fsm_name, group_names)
610 }).collect()
611 }
612
613 fn update_sgroup_uses_map(
623 parent_group: &ir::Id,
624 full_group_ancestry: &mut HashSet<ir::Id>,
625 cur_mapping: &mut HashMap<ir::Id, HashSet<ir::Id>>,
626 group_names: &mut HashSet<ir::Id>,
627 sgroups: &Vec<ir::RRC<ir::StaticGroup>>,
628 ) {
629 let group_uses =
630 get_go_writes(&find_static_group(parent_group, sgroups));
631 for group_use in group_uses {
632 for ancestor in full_group_ancestry.iter() {
633 cur_mapping.entry(*ancestor).or_default().insert(group_use);
634 }
635 full_group_ancestry.insert(group_use);
636 Self::update_sgroup_uses_map(
637 &group_use,
638 full_group_ancestry,
639 cur_mapping,
640 group_names,
641 sgroups,
642 );
643 full_group_ancestry.remove(&group_use);
644 }
645 group_names.remove(parent_group);
646 }
647
648 fn build_sgroup_uses_map(
654 sgroups: &Vec<ir::RRC<ir::StaticGroup>>,
655 ) -> HashMap<ir::Id, HashSet<ir::Id>> {
656 let mut names: HashSet<ir::Id> = sgroups
657 .iter()
658 .map(|sgroup| sgroup.borrow().name())
659 .collect();
660 let mut cur_mapping = HashMap::new();
661 while !names.is_empty() {
662 let random_group = *names.iter().next().unwrap();
663 Self::update_sgroup_uses_map(
664 &random_group,
665 &mut HashSet::from([random_group]),
666 &mut cur_mapping,
667 &mut names,
668 sgroups,
669 )
670 }
671 cur_mapping
672 }
673}
674
675impl Visitor for CompileStatic {
676 fn start(
677 &mut self,
678 comp: &mut ir::Component,
679 sigs: &ir::LibrarySignatures,
680 _comps: &[ir::Component],
681 ) -> VisResult {
682 let sgroups: Vec<ir::RRC<ir::StaticGroup>> =
683 comp.get_static_groups_mut().drain().collect();
684 let sgroup_uses_map = Self::build_sgroup_uses_map(&sgroups);
687 let mut conflict_graph: GraphColoring<ir::Id> =
689 GraphColoring::from(sgroups.iter().map(|g| g.borrow().name()));
690 Self::add_par_conflicts(
691 &comp.control.borrow(),
692 &sgroup_uses_map,
693 &mut conflict_graph,
694 );
695 Self::add_go_port_conflicts(&sgroup_uses_map, &mut conflict_graph);
696 let coloring = conflict_graph.color_greedy(None, true);
697 let mut builder = ir::Builder::new(comp, sigs);
698 let fsm_mappings =
700 Self::build_fsm_mapping(coloring, &sgroups, &mut builder);
701 let mut groups_to_fsms = HashMap::new();
702 for (fsm_name, group_names) in fsm_mappings {
704 let fsm = builder.component.find_guaranteed_cell(fsm_name);
705 for group_name in group_names {
706 groups_to_fsms.insert(group_name, Rc::clone(&fsm));
707 }
708 }
709
710 for sgroup in sgroups.iter() {
712 let mut sgroup_ref = sgroup.borrow_mut();
713 let sgroup_name = sgroup_ref.name();
714 let sgroup_latency = sgroup_ref.get_latency();
715 let sgroup_attributes = sgroup_ref.attributes.clone();
716 let sgroup_assigns = &mut sgroup_ref.assignments;
717 let g = self.make_early_reset_group(
718 sgroup_assigns,
719 sgroup_name,
720 sgroup_latency,
721 sgroup_attributes,
722 Rc::clone(groups_to_fsms.get(&sgroup_name).unwrap_or_else(
723 || unreachable!("{sgroup_name} has no corresponding fsm"),
724 )),
725 &mut builder,
726 );
727 self.reset_early_map.insert(sgroup_name, g.borrow().name());
730 self.group_rewrite.insert(
734 ir::Canonical::new(sgroup_name, ir::Id::from("go")),
735 g.borrow().find("go").unwrap_or_else(|| {
736 unreachable!("group {} has no go port", g.borrow().name())
737 }),
738 );
739 }
740
741 comp.for_each_assignment(|assign| {
745 assign.for_each_port(|port| {
746 self.group_rewrite
747 .get(&port.borrow().canonical())
748 .map(Rc::clone)
749 })
750 });
751
752 comp.get_static_groups_mut().append(sgroups.into_iter());
753
754 Ok(Action::Continue)
755 }
756
757 fn start_static_control(
759 &mut self,
760 sc: &mut ir::StaticControl,
761 comp: &mut ir::Component,
762 sigs: &ir::LibrarySignatures,
763 _comps: &[ir::Component],
764 ) -> VisResult {
765 let ir::StaticControl::Enable(s) = sc else {
768 return Err(Error::malformed_control(format!("Non-Enable Static Control should have been compiled away. Run {} to do this", crate::passes::StaticInliner::name())));
769 };
770
771 let sgroup = s.group.borrow_mut();
772 let sgroup_name = sgroup.name();
773 let early_reset_name =
776 self.reset_early_map.get(&sgroup_name).unwrap_or_else(|| {
777 unreachable!(
778 "group {} early reset has not been created",
779 sgroup_name
780 )
781 });
782 let group_choice = match self.wrapper_map.get(early_reset_name) {
785 None => {
786 let mut builder = ir::Builder::new(comp, sigs);
788 let (fsm_name, fsm_width )= self.fsm_info_map.get(early_reset_name).unwrap_or_else(|| unreachable!("group {} has no correspondoing fsm in self.fsm_map", early_reset_name));
789 let wrapper = match self.signal_reg_map.get(fsm_name) {
793 None => {
794 structure!( builder;
797 let signal_reg = prim std_reg(1);
798 );
799 self.signal_reg_map
800 .insert(*fsm_name, signal_reg.borrow().name());
801 Self::build_wrapper_group(
802 fsm_name,
803 *fsm_width,
804 early_reset_name,
805 signal_reg,
806 &mut builder,
807 true,
808 )
809 }
810 Some(reg_name) => {
811 let signal_reg = builder
815 .component
816 .find_cell(*reg_name)
817 .unwrap_or_else(|| {
818 unreachable!("signal reg {reg_name} found")
819 });
820 Self::build_wrapper_group(
821 fsm_name,
822 *fsm_width,
823 early_reset_name,
824 signal_reg,
825 &mut builder,
826 false,
827 )
828 }
829 };
830 self.wrapper_map
831 .insert(*early_reset_name, wrapper.borrow().name());
832 wrapper
833 }
834 Some(name) => comp.find_group(*name).unwrap(),
835 };
836
837 let mut e = ir::Control::enable(group_choice);
838 let attrs = std::mem::take(&mut s.attributes);
839 *e.get_mut_attributes() = attrs;
840 Ok(Action::Change(Box::new(e)))
841 }
842
843 fn start_while(
882 &mut self,
883 s: &mut ir::While,
884 comp: &mut ir::Component,
885 sigs: &ir::LibrarySignatures,
886 _comps: &[ir::Component],
887 ) -> VisResult {
888 if s.cond.is_none() {
889 if let ir::Control::Static(sc) = &mut *(s.body) {
890 let mut builder = ir::Builder::new(comp, sigs);
891 let reset_group_name = self.get_reset_group_name(sc);
892
893 let (fsm, fsm_width) = self.fsm_info_map.get(reset_group_name).unwrap_or_else(|| unreachable!("group {} has no correspondoing fsm in self.fsm_map", reset_group_name));
895 let wrapper_group = self.build_wrapper_group_while(
896 fsm,
897 *fsm_width,
898 reset_group_name,
899 Rc::clone(&s.port),
900 &mut builder,
901 );
902 let c = ir::Control::enable(wrapper_group);
903 return Ok(Action::change(c));
904 }
905 }
906
907 Ok(Action::Continue)
908 }
909
910 fn finish(
911 &mut self,
912 comp: &mut ir::Component,
913 _sigs: &ir::LibrarySignatures,
914 _comps: &[ir::Component],
915 ) -> VisResult {
916 for g in comp.get_static_groups() {
919 if !g.borrow().assignments.is_empty() {
920 unreachable!("Should have converted all static groups to dynamic. {} still has assignments in it", g.borrow().name());
921 }
922 }
923 comp.get_static_groups_mut().retain(|_| false);
925 Ok(Action::Continue)
926 }
927}