1use super::{
2 Assignment, Attributes, BackendConf, Builder, Cell, CellType, Component,
3 Context, Control, Direction, GetAttributes, Guard, Id, Invoke,
4 LibrarySignatures, Port, PortDef, RESERVED_NAMES, RRC, StaticControl,
5 StaticInvoke, Transition,
6};
7use crate::{Nothing, PortComp, StaticTiming};
8use calyx_frontend::{BoolAttr, NumAttr, Workspace, ast};
9use calyx_utils::{CalyxResult, Error, GPosIdx, WithPos};
10use itertools::Itertools;
11
12use std::collections::{HashMap, HashSet};
13use std::num::NonZeroU64;
14use std::rc::Rc;
15
16#[derive(Default)]
19struct SigCtx {
20 comp_sigs: HashMap<Id, (Vec<PortDef<u64>>, Option<NonZeroU64>)>,
22
23 lib: LibrarySignatures,
25}
26
27#[derive(Debug, Copy, Clone)]
28pub struct AstConversionConfig {
29 pub extend_signatures: bool,
30}
31
32impl Default for AstConversionConfig {
33 fn default() -> Self {
34 Self {
35 extend_signatures: true,
36 }
37 }
38}
39
40fn get_comp_latency(
44 sig_ctx: &SigCtx,
45 cell: RRC<Cell>,
46 attrs: &Attributes,
47) -> CalyxResult<Option<NonZeroU64>> {
48 let comp_name = cell
49 .borrow()
50 .type_name()
51 .unwrap_or_else(|| unreachable!("invoked component without a name"));
52 if let Some(prim) = sig_ctx.lib.find_primitive(comp_name) {
53 match prim.latency {
54 Some(val) => Ok(Some(val)),
55 None => {
56 let prim_sig = &prim.signature;
57 let interval_value = prim_sig
60 .iter()
61 .find(|port_def| port_def.attributes.has(NumAttr::Go))
62 .and_then(|go_port| {
63 go_port.attributes.get(NumAttr::Interval)
64 });
65 match interval_value {
67 Some(lat) => Ok(NonZeroU64::new(lat)),
68 None => Ok(None),
69 }
70 }
71 }
72 } else if let Some((comp_sig, latency)) = sig_ctx.comp_sigs.get(&comp_name)
73 {
74 match latency {
75 Some(val) => Ok(Some(*val)),
76 None => {
77 let interval_value = comp_sig
80 .iter()
81 .find(|port_def| port_def.attributes.has(NumAttr::Go))
82 .and_then(|go_port| {
83 go_port.attributes.get(NumAttr::Interval)
84 });
85 match interval_value {
87 Some(lat) => Ok(NonZeroU64::new(lat)),
88 None => Ok(None),
89 }
90 }
91 }
92 } else {
93 return Err(Error::undefined(
94 comp_name,
95 "primitive or component".to_string(),
96 )
97 .with_pos(attrs));
98 }
99}
100
101fn get_static_latency(
104 sig_ctx: &SigCtx,
105 cell: RRC<Cell>,
106 attrs: &Attributes,
107) -> CalyxResult<Option<NonZeroU64>> {
108 let comp_name = cell
109 .borrow()
110 .type_name()
111 .unwrap_or_else(|| unreachable!("invoked component without a name"));
112 if let Some(prim) = sig_ctx.lib.find_primitive(comp_name) {
113 Ok(prim.latency)
114 } else if let Some((_, latency)) = sig_ctx.comp_sigs.get(&comp_name) {
115 Ok(*latency)
116 } else {
117 return Err(Error::undefined(
118 comp_name,
119 "primitive or component".to_string(),
120 )
121 .with_pos(attrs));
122 }
123}
124
125fn check_valid_port(
128 cell: RRC<Cell>,
129 port_name: &Id,
130 attrs: &Attributes,
131 sig_ctx: &SigCtx,
132) -> CalyxResult<()> {
133 let cell_name = cell.borrow().name();
134 let comp_name = cell
135 .borrow()
136 .type_name()
137 .unwrap_or_else(|| unreachable!("invoked component without a name"));
138 let sig_ports: HashSet<_> =
139 if let Some(prim) = sig_ctx.lib.find_primitive(comp_name) {
140 prim.signature
141 .iter()
142 .map(|port_def| port_def.name())
143 .collect()
144 } else if let Some((comp_sigs, _)) = sig_ctx.comp_sigs.get(&comp_name) {
145 comp_sigs.iter().map(|port_def| port_def.name()).collect()
146 } else {
147 return Err(Error::undefined(
148 comp_name,
149 "primitive or component".to_string(),
150 )
151 .with_pos(attrs));
152 };
153 if !sig_ports.contains(port_name) {
154 return Err(Error::malformed_structure(format!(
155 "cell `{cell_name}` (which is an instance of {comp_name}) does not have port named `{port_name}`"
156 ))
157 .with_pos(attrs));
158 };
159 Ok(())
160}
161
162fn check_signature(pds: &[PortDef<u64>]) -> CalyxResult<()> {
164 let mut ports: HashSet<Id> = HashSet::new();
165 for pd in pds {
166 let name = pd.name();
167 match &pd.direction {
169 Direction::Input => {
170 if !ports.contains(&name) {
171 ports.insert(name);
172 } else {
173 return Err(Error::already_bound(name, "port".to_string()));
174 }
175 }
176 Direction::Output => {
177 if !ports.contains(&name) {
178 ports.insert(name);
179 } else {
180 return Err(Error::already_bound(name, "port".to_string()));
181 }
182 }
183 Direction::Inout => {
184 panic!("Components shouldn't have inout ports.")
185 }
186 }
187 }
188 Ok(())
189}
190
191pub fn ast_to_ir(
193 mut workspace: Workspace,
194 config: AstConversionConfig,
195) -> CalyxResult<Context> {
196 let prims = workspace.lib.signatures().collect_vec();
197 let mut all_names: HashSet<&Id> =
198 HashSet::with_capacity(workspace.components.len() + prims.len());
199
200 let prim_names = prims.iter().map(|p| (&p.name, p.attributes.copy_span()));
201
202 let comp_names = workspace
203 .components
204 .iter()
205 .map(|comp| (&comp.name, comp.attributes.copy_span()));
206
207 for (bound, span) in prim_names.chain(comp_names) {
208 if all_names.contains(bound) {
209 return Err(Error::already_bound(
210 *bound,
211 "component or primitive".to_string(),
212 )
213 .with_pos(&span));
214 }
215 all_names.insert(bound);
216 }
217
218 let mut sig_ctx = SigCtx {
220 lib: workspace.lib,
221 ..Default::default()
222 };
223
224 for comp in workspace
226 .declarations
227 .iter_mut()
228 .chain(workspace.components.iter_mut())
229 {
230 let sig = &mut comp.signature;
231 check_signature(&*sig)?;
232 if !comp.attributes.has(BoolAttr::NoInterface)
234 && !comp.is_comb
235 && config.extend_signatures
236 {
237 Component::extend_signature(sig);
238 }
239 sig_ctx
240 .comp_sigs
241 .insert(comp.name, (sig.clone(), comp.latency));
242 }
243
244 let comps: Vec<Component> = workspace
246 .components
247 .into_iter()
248 .map(|comp| build_component(comp, &mut sig_ctx, config))
249 .collect::<Result<_, _>>()?;
250
251 let entrypoint = comps
253 .iter()
254 .find(|c| c.attributes.has(BoolAttr::TopLevel))
255 .or_else(|| comps.iter().find(|c| c.name == "main"))
256 .map(|c| c.name)
257 .ok_or_else(|| Error::misc("No entry point for the program. Program needs to be either mark a component with the \"toplevel\" attribute or define a component named `main`".to_string()))?;
258
259 Ok(Context {
260 components: comps,
261 lib: sig_ctx.lib,
262 bc: BackendConf::default(),
263 entrypoint,
264 extra_opts: vec![],
265 metadata: workspace.metadata,
266 source_info_table: workspace.source_info_table,
267 })
268}
269
270fn validate_component(
271 comp: &ast::ComponentDef,
272 sig_ctx: &SigCtx,
273) -> CalyxResult<()> {
274 let mut cells: HashMap<Id, GPosIdx> = HashMap::new();
275 let mut groups: HashMap<Id, GPosIdx> = HashMap::new();
276
277 for cell in &comp.cells {
278 let attrs = &cell.attributes;
279 if let Some(pos) = cells.get(&cell.name) {
280 let prev =
281 pos.into_option().map(|s| s.format("Previous definition"));
282 return Err(Error::already_bound(cell.name, "cell".to_string())
283 .with_pos(attrs)
284 .with_post_msg(prev));
285 }
286 cells.insert(cell.name, cell.attributes.copy_span());
287
288 let proto_name = cell.prototype.name;
289
290 if sig_ctx.lib.find_primitive(proto_name).is_none()
291 && !sig_ctx.comp_sigs.contains_key(&proto_name)
292 {
293 return Err(Error::undefined(
294 proto_name,
295 "primitive or component".to_string(),
296 )
297 .with_pos(attrs));
298 }
299 }
300
301 for group in &comp.groups {
302 let name = &group.name;
303 let attrs = &group.attributes;
304 if let Some(pos) = groups.get(name) {
305 let prev =
306 pos.into_option().map(|s| s.format("Previous definition"));
307 return Err(Error::already_bound(*name, "group".to_string())
308 .with_pos(attrs)
309 .with_post_msg(prev));
310 }
311 if let Some(pos) = cells.get(name) {
312 let prev =
313 pos.into_option().map(|s| s.format("Previous definition"));
314 return Err(Error::already_bound(*name, "cell".to_string())
315 .with_pos(attrs)
316 .with_post_msg(prev));
317 }
318 if let Some(pos) = cells.get(name) {
319 let prev =
320 pos.into_option().map(|s| s.format("Previous definition"));
321 return Err(Error::already_bound(*name, "cell".to_string())
322 .with_pos(attrs)
323 .with_post_msg(prev));
324 }
325 groups.insert(*name, group.attributes.copy_span());
326 }
327
328 Ok(())
329}
330
331fn build_component(
333 comp: ast::ComponentDef,
334 sig_ctx: &mut SigCtx,
335 config: AstConversionConfig,
336) -> CalyxResult<Component> {
337 validate_component(&comp, sig_ctx)?;
339
340 let mut ir_component = Component::new(
341 comp.name,
342 comp.signature,
343 !comp.attributes.has(BoolAttr::NoInterface)
344 && !comp.is_comb
345 && config.extend_signatures,
346 comp.is_comb,
347 comp.latency,
350 );
351
352 let mut builder =
353 Builder::new(&mut ir_component, &sig_ctx.lib).not_generated();
354
355 comp.cells
358 .into_iter()
359 .try_for_each(|cell| add_cell(cell, sig_ctx, &mut builder))?;
360
361 comp.groups
362 .into_iter()
363 .try_for_each(|g| add_group(g, &mut builder))?;
364
365 comp.static_groups
366 .into_iter()
367 .try_for_each(|g| add_static_group(g, &mut builder))?;
368
369 comp.fsms
370 .into_iter()
371 .try_for_each(|f| add_fsm(f, &mut builder))?;
372
373 let continuous_assignments =
374 build_assignments(comp.continuous_assignments, &mut builder)?;
375 builder.component.continuous_assignments = continuous_assignments;
376
377 let control =
379 super::rrc(build_control(comp.control, sig_ctx, &mut builder)?);
380
381 builder.component.control = control;
382
383 ir_component.attributes = comp.attributes;
384
385 ir_component
388 .add_names(RESERVED_NAMES.iter().map(|s| Id::from(*s)).collect());
389
390 Ok(ir_component)
391}
392
393fn add_cell(
396 cell: ast::Cell,
397 sig_ctx: &SigCtx,
398 builder: &mut Builder,
399) -> CalyxResult<()> {
400 let proto_name = cell.prototype.name;
401
402 let res = if sig_ctx.lib.find_primitive(proto_name).is_some() {
403 let c = builder
404 .try_add_primitive(cell.name, proto_name, &cell.prototype.params)
405 .map_err(|e| e.with_pos(&cell.attributes))?;
406 c.borrow_mut().set_reference(cell.reference);
407 c
408 } else {
409 let name = builder.component.generate_name(cell.name);
412 let sig = &sig_ctx.comp_sigs[&proto_name].0;
413 let typ = CellType::Component { name: proto_name };
414 let reference = cell.reference;
415 let cell = Builder::cell_from_signature(name, typ, sig.clone());
417 cell.borrow_mut().set_reference(reference);
418 builder.component.cells.add(Rc::clone(&cell));
419 cell
420 };
421
422 res.borrow_mut().attributes = cell.attributes;
424
425 Ok(())
426}
427
428fn add_group(group: ast::Group, builder: &mut Builder) -> CalyxResult<()> {
433 if group.is_comb {
434 let ir_group = builder.add_comb_group(group.name);
435 let assigns = build_assignments(group.wires, builder)?;
436
437 ir_group.borrow_mut().attributes = group.attributes;
438 ir_group.borrow_mut().assignments = assigns;
439 } else {
440 let ir_group = builder.add_group(group.name);
441 let assigns = build_assignments(group.wires, builder)?;
442
443 ir_group.borrow_mut().attributes = group.attributes;
444 ir_group.borrow_mut().assignments = assigns;
445 };
446
447 Ok(())
448}
449
450fn add_static_group(
453 group: ast::StaticGroup,
454 builder: &mut Builder,
455) -> CalyxResult<()> {
456 if group.latency.get() == 0 {
457 return Err(Error::malformed_structure(
458 "static group with 0 latency".to_string(),
459 ));
460 }
461 let ir_group = builder.add_static_group(group.name, group.latency.get());
462 let assigns = build_static_assignments(group.wires, builder)?;
463
464 ir_group.borrow_mut().attributes = group.attributes;
465 ir_group.borrow_mut().assignments = assigns;
466
467 Ok(())
468}
469
470fn add_fsm(fsm: ast::Fsm, builder: &mut Builder) -> CalyxResult<()> {
475 let ir_fsm = builder.add_fsm(fsm.name);
476 let (state_assignments, state_transition): (
477 Vec<Vec<Assignment<Nothing>>>,
478 Vec<Transition>,
479 ) = fsm
480 .fsm_states
481 .into_iter()
482 .map(|rule| -> CalyxResult<_> {
483 Ok((
484 build_assignments(rule.assignments, builder)?,
485 build_transition(rule.transition, builder)?,
486 ))
487 })
488 .collect::<CalyxResult<Vec<_>>>()?
489 .into_iter()
490 .unzip();
491
492 {
493 let mut ir_fsm = ir_fsm.borrow_mut();
494 ir_fsm.attributes = fsm.attributes;
495 ir_fsm.assignments = state_assignments;
496 ir_fsm.transitions = state_transition;
497 }
498
499 Ok(())
500}
501
502fn get_port_ref(port: ast::Port, comp: &Component) -> CalyxResult<RRC<Port>> {
506 match port {
507 ast::Port::Comp { component, port } => comp
508 .find_cell(component)
509 .ok_or_else(|| Error::undefined(component, "cell".to_string()))?
510 .borrow()
511 .find(port)
512 .ok_or_else(|| {
513 Error::undefined(
514 Id::new(format!("{component}.{port}")),
515 "port".to_string(),
516 )
517 }),
518 ast::Port::This { port } => {
519 comp.signature.borrow().find(&port).ok_or_else(|| {
520 Error::undefined(port, "component port".to_string())
521 })
522 }
523 ast::Port::Hole {
524 struct_elem,
525 name: port,
526 } => {
527 if let Some(f) = comp.find_fsm(struct_elem) {
528 return f.borrow().find(port).ok_or_else(|| {
529 Error::undefined(
530 Id::new(format!("{}.{}", f.borrow().name(), port)),
531 "hole".to_string(),
532 )
533 });
534 } else if let Some(g) = comp.find_group(struct_elem) {
535 return g.borrow().find(port).ok_or_else(|| {
536 Error::undefined(
537 Id::new(format!("{}.{}", g.borrow().name(), port)),
538 "hole".to_string(),
539 )
540 });
541 } else {
542 return comp
543 .find_static_group(struct_elem)
544 .ok_or_else(|| {
545 Error::undefined(struct_elem, "group".to_string())
546 })?
547 .borrow()
548 .find(port)
549 .ok_or_else(|| Error::undefined(port, "hole".to_string()));
550 }
551 }
552 }
553}
554
555fn atom_to_port(
560 atom: ast::Atom,
561 builder: &mut Builder,
562) -> CalyxResult<RRC<Port>> {
563 match atom {
564 ast::Atom::Num(n) => {
565 let port = builder.add_constant(n.val, n.width).borrow().get("out");
566 Ok(Rc::clone(&port))
567 }
568 ast::Atom::Port(p) => get_port_ref(p, builder.component),
569 }
570}
571
572fn ensure_direction(pr: RRC<Port>, dir: Direction) -> CalyxResult<RRC<Port>> {
574 let port_dir = pr.borrow().direction.clone();
575 match (dir, port_dir) {
576 (Direction::Input, Direction::Output) => {
577 let name = pr.borrow().canonical();
578 Err(Error::malformed_structure(format!(
579 "Port `{name}` occurs in write position but is an output port",
580 )))
581 }
582 (Direction::Output, Direction::Input) => {
583 let name = pr.borrow().canonical();
584 Err(Error::malformed_structure(format!(
585 "Port `{name}` occurs in write position but is an output port",
586 )))
587 }
588 _ => Ok(pr),
589 }
590}
591
592fn build_assignment(
595 wire: ast::Wire,
596 builder: &mut Builder,
597) -> CalyxResult<Assignment<Nothing>> {
598 let src_port: RRC<Port> = ensure_direction(
599 atom_to_port(wire.src.expr, builder)?,
600 Direction::Output,
601 )?;
602 let dst_port: RRC<Port> = ensure_direction(
603 get_port_ref(wire.dest, builder.component)?,
604 Direction::Input,
605 )?;
606 if src_port.borrow().width != dst_port.borrow().width {
607 let msg = format!(
608 "Mismatched port widths. Source has size {} while destination requires {}.",
609 src_port.borrow().width,
610 dst_port.borrow().width,
611 );
612 return Err(Error::malformed_structure(msg).with_pos(&wire.attributes));
613 }
614 let guard = match wire.src.guard {
615 Some(g) => build_guard(g, builder)?,
616 None => Guard::True,
617 };
618
619 let mut assign = builder.build_assignment(dst_port, src_port, guard);
620 assign.attributes = wire.attributes;
621 Ok(assign)
622}
623
624fn build_static_assignment(
627 wire: ast::StaticWire,
628 builder: &mut Builder,
629) -> CalyxResult<Assignment<StaticTiming>> {
630 let src_port: RRC<Port> = ensure_direction(
631 atom_to_port(wire.src.expr, builder)?,
632 Direction::Output,
633 )?;
634 let dst_port: RRC<Port> = ensure_direction(
635 get_port_ref(wire.dest, builder.component)?,
636 Direction::Input,
637 )?;
638 if src_port.borrow().width != dst_port.borrow().width {
639 let msg = format!(
640 "Mismatched port widths. Source has size {} while destination requires {}.",
641 src_port.borrow().width,
642 dst_port.borrow().width,
643 );
644 return Err(Error::malformed_structure(msg).with_pos(&wire.attributes));
645 }
646 let guard = match wire.src.guard {
647 Some(g) => build_static_guard(g, builder)?,
648 None => Guard::True,
649 };
650
651 let mut assign = builder.build_assignment(dst_port, src_port, guard);
652 assign.attributes = wire.attributes;
653 Ok(assign)
654}
655
656fn build_assignments(
657 assigns: Vec<ast::Wire>,
658 builder: &mut Builder,
659) -> CalyxResult<Vec<Assignment<Nothing>>> {
660 assigns
661 .into_iter()
662 .map(|w| {
663 let attrs = w.attributes.clone();
664 build_assignment(w, builder).map_err(|err| err.with_pos(&attrs))
665 })
666 .collect::<CalyxResult<Vec<_>>>()
667}
668
669fn build_static_assignments(
670 assigns: Vec<ast::StaticWire>,
671 builder: &mut Builder,
672) -> CalyxResult<Vec<Assignment<StaticTiming>>> {
673 assigns
674 .into_iter()
675 .map(|w| {
676 let attrs = w.attributes.clone();
677 build_static_assignment(w, builder)
678 .map_err(|err| err.with_pos(&attrs))
679 })
680 .collect::<CalyxResult<Vec<_>>>()
681}
682
683fn build_transition(
686 transition: ast::Transition,
687 builder: &mut Builder,
688) -> CalyxResult<Transition> {
689 match transition {
690 ast::Transition::Unconditional(state_idx) => {
691 Ok(Transition::Unconditional(state_idx))
692 }
693 ast::Transition::Conditional(conditions) => {
694 let conds = conditions
695 .into_iter()
696 .map(|(condition, state_idx)| {
697 let guard = match condition {
698 Some(g) => build_guard(g, builder)?,
699 None => Guard::True,
700 };
701 Ok((guard, state_idx))
702 })
703 .collect::<CalyxResult<Vec<(Guard<Nothing>, u64)>>>()?;
704
705 Ok(Transition::Conditional(conds))
706 }
707 }
708}
709
710fn build_guard(
712 guard: ast::GuardExpr,
713 bd: &mut Builder,
714) -> CalyxResult<Guard<Nothing>> {
715 use ast::GuardExpr as GE;
716
717 let into_box_guard = |g: Box<GE>, bd: &mut Builder| -> CalyxResult<_> {
718 Ok(Box::new(build_guard(*g, bd)?))
719 };
720
721 Ok(match guard {
722 GE::Atom(atom) => Guard::port(ensure_direction(
723 atom_to_port(atom, bd)?,
724 Direction::Output,
725 )?),
726 GE::Or(l, r) => Guard::or(build_guard(*l, bd)?, build_guard(*r, bd)?),
727 GE::And(l, r) => Guard::and(build_guard(*l, bd)?, build_guard(*r, bd)?),
728 GE::Not(g) => Guard::Not(into_box_guard(g, bd)?),
729 GE::CompOp((op, l, r)) => {
730 let nl = ensure_direction(atom_to_port(l, bd)?, Direction::Output)?;
731 let nr = ensure_direction(atom_to_port(r, bd)?, Direction::Output)?;
732 let nop = match op {
733 ast::GuardComp::Eq => PortComp::Eq,
734 ast::GuardComp::Neq => PortComp::Neq,
735 ast::GuardComp::Gt => PortComp::Gt,
736 ast::GuardComp::Lt => PortComp::Lt,
737 ast::GuardComp::Geq => PortComp::Geq,
738 ast::GuardComp::Leq => PortComp::Leq,
739 };
740 Guard::CompOp(nop, nl, nr)
741 }
742 })
743}
744
745fn build_static_guard(
747 guard: ast::StaticGuardExpr,
748 bd: &mut Builder,
749) -> CalyxResult<Guard<StaticTiming>> {
750 use ast::StaticGuardExpr as SGE;
751
752 let into_box_guard = |g: Box<SGE>, bd: &mut Builder| -> CalyxResult<_> {
753 Ok(Box::new(build_static_guard(*g, bd)?))
754 };
755
756 Ok(match guard {
757 SGE::StaticInfo(interval) => Guard::Info(StaticTiming::new(interval)),
758 SGE::Atom(atom) => Guard::port(ensure_direction(
759 atom_to_port(atom, bd)?,
760 Direction::Output,
761 )?),
762 SGE::Or(l, r) => {
763 Guard::or(build_static_guard(*l, bd)?, build_static_guard(*r, bd)?)
764 }
765 SGE::And(l, r) => {
766 Guard::and(build_static_guard(*l, bd)?, build_static_guard(*r, bd)?)
767 }
768 SGE::Not(g) => Guard::Not(into_box_guard(g, bd)?),
769 SGE::CompOp((op, l, r)) => {
770 let nl = ensure_direction(atom_to_port(l, bd)?, Direction::Output)?;
771 let nr = ensure_direction(atom_to_port(r, bd)?, Direction::Output)?;
772 let nop = match op {
773 ast::GuardComp::Eq => PortComp::Eq,
774 ast::GuardComp::Neq => PortComp::Neq,
775 ast::GuardComp::Gt => PortComp::Gt,
776 ast::GuardComp::Lt => PortComp::Lt,
777 ast::GuardComp::Geq => PortComp::Geq,
778 ast::GuardComp::Leq => PortComp::Leq,
779 };
780 Guard::CompOp(nop, nl, nr)
781 }
782 })
783}
784
785fn assert_latencies_eq(
788 given_latency: Option<NonZeroU64>,
789 inferred_latency: u64,
790) {
791 if let Some(v) = given_latency {
792 assert_eq!(
793 v.get(),
794 inferred_latency,
795 "inferred latency: {inferred_latency}, given latency: {v}"
796 )
797 };
798}
799
800fn build_static_seq(
802 stmts: Vec<ast::Control>,
803 attributes: Attributes,
804 latency: Option<NonZeroU64>,
805 builder: &mut Builder,
806 sig_ctx: &SigCtx,
807) -> CalyxResult<StaticControl> {
808 let ir_stmts = stmts
809 .into_iter()
810 .map(|c| build_static_control(c, sig_ctx, builder))
811 .collect::<CalyxResult<Vec<_>>>()?;
812 let inferred_latency =
813 ir_stmts.iter().fold(0, |acc, s| acc + (s.get_latency()));
814 assert_latencies_eq(latency, inferred_latency);
815 let mut s = StaticControl::seq(ir_stmts, inferred_latency);
816 *s.get_mut_attributes() = attributes;
817 Ok(s)
818}
819
820fn build_static_par(
821 stmts: Vec<ast::Control>,
822 attributes: Attributes,
823 latency: Option<NonZeroU64>,
824 builder: &mut Builder,
825 sig_ctx: &SigCtx,
826) -> CalyxResult<StaticControl> {
827 let ir_stmts = stmts
828 .into_iter()
829 .map(|c| build_static_control(c, sig_ctx, builder))
830 .collect::<CalyxResult<Vec<_>>>()?;
831 let inferred_latency = match ir_stmts.iter().max_by_key(|s| s.get_latency())
832 {
833 Some(s) => s.get_latency(),
834 None => {
835 return Err(Error::malformed_control(
836 "empty par block".to_string(),
837 ));
838 }
839 };
840 assert_latencies_eq(latency, inferred_latency);
841 let mut p = StaticControl::par(ir_stmts, inferred_latency);
842 *p.get_mut_attributes() = attributes;
843 Ok(p)
844}
845
846fn build_static_if(
847 port: ast::Port,
848 tbranch: ast::Control,
849 fbranch: ast::Control,
850 attributes: Attributes,
851 latency: Option<NonZeroU64>,
852 builder: &mut Builder,
853 sig_ctx: &SigCtx,
854) -> CalyxResult<StaticControl> {
855 let ir_tbranch = build_static_control(tbranch, sig_ctx, builder)?;
856 let ir_fbranch = build_static_control(fbranch, sig_ctx, builder)?;
857 let inferred_latency =
858 std::cmp::max(ir_tbranch.get_latency(), ir_fbranch.get_latency());
859 assert_latencies_eq(latency, inferred_latency);
860 let mut con = StaticControl::static_if(
861 ensure_direction(
862 get_port_ref(port, builder.component)?,
863 Direction::Output,
864 )?,
865 Box::new(ir_tbranch),
866 Box::new(ir_fbranch),
867 inferred_latency,
868 );
869 *con.get_mut_attributes() = attributes;
870 Ok(con)
871}
872
873fn build_static_repeat(
874 num_repeats: u64,
875 body: ast::Control,
876 builder: &mut Builder,
877 attributes: Attributes,
878 sig_ctx: &SigCtx,
879) -> CalyxResult<StaticControl> {
880 let body = build_static_control(body, sig_ctx, builder)?;
881 let total_latency = body.get_latency() * num_repeats;
882 let mut scon =
883 StaticControl::repeat(num_repeats, total_latency, Box::new(body));
884 *scon.get_mut_attributes() = attributes;
885 Ok(scon)
886}
887
888fn build_static_control(
890 control: ast::Control,
891 sig_ctx: &SigCtx,
892 builder: &mut Builder,
893) -> CalyxResult<StaticControl> {
894 let sc = match control {
895 ast::Control::Enable {
896 comp: group,
897 attributes,
898 } => {
899 if builder.component.find_group(group).is_some() {
900 return Err(Error::malformed_control(
902 "found dynamic group in static context".to_string(),
903 )
904 .with_pos(&attributes));
905 };
906 let mut en = StaticControl::from(Rc::clone(
907 &builder.component.find_static_group(group).ok_or_else(
908 || {
909 Error::undefined(group, "group".to_string())
910 .with_pos(&attributes)
911 },
912 )?,
913 ));
914 *en.get_mut_attributes() = attributes;
915 en
916 }
917 ast::Control::StaticInvoke {
918 comp,
919 inputs,
920 outputs,
921 attributes,
922 ref_cells,
923 latency,
924 comb_group,
925 } => {
926 let cell = Rc::clone(
927 &builder.component.find_cell(comp).ok_or_else(|| {
928 Error::undefined(comp, "cell".to_string())
929 .with_pos(&attributes)
930 })?,
931 );
932 let comp_name = cell.borrow().type_name().unwrap_or_else(|| {
933 unreachable!("invoked component without a name")
934 });
935 let c_latency =
936 get_comp_latency(sig_ctx, Rc::clone(&cell), &attributes)?;
937 let unwrapped_latency = if let Some(v) = c_latency {
938 v
939 } else {
940 return Err(Error::malformed_control(format!(
941 "component {comp_name} is statically invoked, but is neither static nor does it have @interval attribute its @go port"
942 ))
943 .with_pos(&attributes));
944 };
945 assert_latencies_eq(latency, unwrapped_latency.into());
946
947 let inputs = inputs
948 .into_iter()
949 .map(|(id, port)| {
950 check_valid_port(
952 Rc::clone(&cell),
953 &id,
954 &attributes,
955 sig_ctx,
956 )?;
957 atom_to_port(port, builder)
958 .and_then(|pr| ensure_direction(pr, Direction::Output))
959 .map(|p| (id, p))
960 })
961 .collect::<Result<_, _>>()?;
962 let outputs = outputs
963 .into_iter()
964 .map(|(id, port)| {
965 check_valid_port(
967 Rc::clone(&cell),
968 &id,
969 &attributes,
970 sig_ctx,
971 )?;
972 atom_to_port(port, builder)
973 .and_then(|pr| ensure_direction(pr, Direction::Input))
974 .map(|p| (id, p))
975 })
976 .collect::<Result<_, _>>()?;
977 let mut inv = StaticInvoke {
978 comp: cell,
979 inputs,
980 outputs,
981 attributes: Attributes::default(),
982 ref_cells: Vec::new(),
983 latency: unwrapped_latency.into(),
984 comb_group: None,
985 };
986 if !ref_cells.is_empty() {
987 let mut ext_cell_tuples = Vec::new();
988 for (outcell, incell) in ref_cells {
989 let ext_cell_ref =
990 builder.component.find_cell(incell).ok_or_else(
991 || Error::undefined(incell, "cell".to_string()),
992 )?;
993 ext_cell_tuples.push((outcell, ext_cell_ref));
994 }
995 inv.ref_cells = ext_cell_tuples;
996 }
997 if let Some(cg) = comb_group {
998 let cg_ref =
999 builder.component.find_comb_group(cg).ok_or_else(|| {
1000 Error::undefined(cg, "combinational group".to_string())
1001 .with_pos(&inv.attributes)
1002 })?;
1003 inv.comb_group = Some(cg_ref);
1004 }
1005 let mut con = StaticControl::Invoke(inv);
1006 *con.get_mut_attributes() = attributes;
1007 return Ok(con);
1008 }
1009 ast::Control::StaticSeq {
1010 stmts,
1011 attributes,
1012 latency,
1013 } => {
1014 return build_static_seq(
1015 stmts, attributes, latency, builder, sig_ctx,
1016 );
1017 }
1018 ast::Control::StaticPar {
1019 stmts,
1020 attributes,
1021 latency,
1022 } => {
1023 return build_static_par(
1024 stmts, attributes, latency, builder, sig_ctx,
1025 );
1026 }
1027 ast::Control::StaticIf {
1028 port,
1029 tbranch,
1030 fbranch,
1031 attributes,
1032 latency,
1033 } => {
1034 return build_static_if(
1035 port, *tbranch, *fbranch, attributes, latency, builder, sig_ctx,
1036 );
1037 }
1038 ast::Control::StaticRepeat {
1039 attributes,
1040 num_repeats,
1041 body,
1042 } => {
1043 return build_static_repeat(
1044 num_repeats,
1045 *body,
1046 builder,
1047 attributes,
1048 sig_ctx,
1049 );
1050 }
1051 ast::Control::Par { .. }
1052 | ast::Control::If { .. }
1053 | ast::Control::While { .. }
1054 | ast::Control::Seq { .. }
1055 | ast::Control::Repeat { .. }
1056 | ast::Control::Invoke { .. } => {
1057 return Err(Error::malformed_control(
1058 "found dynamic control in static context".to_string(),
1059 )
1060 .with_pos(control.get_attributes()));
1061 }
1062 ast::Control::Empty { attributes } => {
1063 let mut emp = StaticControl::empty();
1064 *emp.get_mut_attributes() = attributes;
1065 emp
1066 }
1067 };
1068 Ok(sc)
1069}
1070
1071fn build_control(
1073 control: ast::Control,
1074 sig_ctx: &SigCtx,
1075 builder: &mut Builder,
1076) -> CalyxResult<Control> {
1077 let c = match control {
1078 ast::Control::Enable {
1079 comp: component,
1080 attributes,
1081 } => {
1082 if let Some(f) = builder.component.find_fsm(component) {
1083 let mut en = Control::fsm_enable(Rc::clone(&f));
1084 *en.get_mut_attributes() = attributes;
1085 en
1086 } else if let Some(g) = builder.component.find_group(component) {
1087 let mut en = Control::enable(Rc::clone(&g));
1088 *en.get_mut_attributes() = attributes;
1089 en
1090 } else {
1091 let mut en = Control::Static(StaticControl::from(Rc::clone(
1092 &builder
1093 .component
1094 .find_static_group(component)
1095 .ok_or_else(|| {
1096 Error::undefined(
1097 component,
1098 "group or fsm".to_string(),
1099 )
1100 .with_pos(&attributes)
1101 })?,
1102 )));
1103 *en.get_mut_attributes() = attributes;
1104 en
1105 }
1106 }
1107 ast::Control::StaticInvoke {
1108 comp,
1109 inputs,
1110 outputs,
1111 attributes,
1112 ref_cells,
1113 latency,
1114 comb_group,
1115 } => {
1116 let cell = Rc::clone(
1117 &builder.component.find_cell(comp).ok_or_else(|| {
1118 Error::undefined(comp, "cell".to_string())
1119 .with_pos(&attributes)
1120 })?,
1121 );
1122 let comp_name = cell.borrow().type_name().unwrap_or_else(|| {
1123 unreachable!("invoked component without a name")
1124 });
1125 let c_latency =
1126 get_comp_latency(sig_ctx, Rc::clone(&cell), &attributes)?;
1127 let unwrapped_latency = if let Some(v) = c_latency {
1128 v
1129 } else {
1130 return Err(Error::malformed_control(format!(
1131 "component {comp_name} is statically invoked, but is neither static nor does it have @interval attribute its @go port"
1132 ))
1133 .with_pos(&attributes));
1134 };
1135 assert_latencies_eq(latency, unwrapped_latency.into());
1136
1137 let inputs = inputs
1138 .into_iter()
1139 .map(|(id, port)| {
1140 check_valid_port(
1142 Rc::clone(&cell),
1143 &id,
1144 &attributes,
1145 sig_ctx,
1146 )?;
1147 atom_to_port(port, builder)
1148 .and_then(|pr| ensure_direction(pr, Direction::Output))
1149 .map(|p| (id, p))
1150 })
1151 .collect::<Result<_, _>>()?;
1152 let outputs = outputs
1153 .into_iter()
1154 .map(|(id, port)| {
1155 check_valid_port(
1157 Rc::clone(&cell),
1158 &id,
1159 &attributes,
1160 sig_ctx,
1161 )?;
1162 atom_to_port(port, builder)
1163 .and_then(|pr| ensure_direction(pr, Direction::Input))
1164 .map(|p| (id, p))
1165 })
1166 .collect::<Result<_, _>>()?;
1167 let mut inv = StaticInvoke {
1168 comp: cell,
1169 inputs,
1170 outputs,
1171 attributes: Attributes::default(),
1172 ref_cells: Vec::new(),
1173 latency: unwrapped_latency.into(),
1174 comb_group: None,
1175 };
1176 if !ref_cells.is_empty() {
1177 let mut ext_cell_tuples = Vec::new();
1178 for (outcell, incell) in ref_cells {
1179 let ext_cell_ref =
1180 builder.component.find_cell(incell).ok_or_else(
1181 || Error::undefined(incell, "cell".to_string()),
1182 )?;
1183 ext_cell_tuples.push((outcell, ext_cell_ref));
1184 }
1185 inv.ref_cells = ext_cell_tuples;
1186 }
1187 if let Some(cg) = comb_group {
1188 let cg_ref =
1189 builder.component.find_comb_group(cg).ok_or_else(|| {
1190 Error::undefined(cg, "combinational group".to_string())
1191 .with_pos(&inv.attributes)
1192 })?;
1193 inv.comb_group = Some(cg_ref);
1194 }
1195 let mut con = StaticControl::Invoke(inv);
1196 *con.get_mut_attributes() = attributes;
1197 Control::Static(con)
1198 }
1199 ast::Control::Invoke {
1200 comp: component,
1201 inputs,
1202 outputs,
1203 attributes,
1204 comb_group,
1205 ref_cells,
1206 } => {
1207 let cell = Rc::clone(
1208 &builder.component.find_cell(component).ok_or_else(|| {
1209 Error::undefined(component, "cell".to_string())
1210 .with_pos(&attributes)
1211 })?,
1212 );
1213
1214 let comp_name = cell.borrow().type_name().unwrap_or_else(|| {
1215 unreachable!("invoked component without a name")
1216 });
1217
1218 if get_static_latency(sig_ctx, Rc::clone(&cell), &attributes)?
1220 .is_some()
1221 {
1222 return Err(Error::malformed_control(format!(
1223 "static component {comp_name} is dynamically invoked"
1224 ))
1225 .with_pos(&attributes));
1226 }
1227
1228 let inputs = inputs
1229 .into_iter()
1230 .map(|(id, port)| {
1231 check_valid_port(
1233 Rc::clone(&cell),
1234 &id,
1235 &attributes,
1236 sig_ctx,
1237 )?;
1238 atom_to_port(port, builder)
1239 .and_then(|pr| ensure_direction(pr, Direction::Output))
1240 .map(|p| (id, p))
1241 })
1242 .collect::<Result<_, _>>()?;
1243 let outputs = outputs
1244 .into_iter()
1245 .map(|(id, port)| {
1246 check_valid_port(
1248 Rc::clone(&cell),
1249 &id,
1250 &attributes,
1251 sig_ctx,
1252 )?;
1253 atom_to_port(port, builder)
1254 .and_then(|pr| ensure_direction(pr, Direction::Input))
1255 .map(|p| (id, p))
1256 })
1257 .collect::<Result<_, _>>()?;
1258 let mut inv = Invoke {
1259 comp: cell,
1260 inputs,
1261 outputs,
1262 attributes,
1263 comb_group: None,
1264 ref_cells: Vec::new(),
1265 };
1266 if let Some(cg) = comb_group {
1267 let cg_ref =
1268 builder.component.find_comb_group(cg).ok_or_else(|| {
1269 Error::undefined(cg, "combinational group".to_string())
1270 .with_pos(&inv.attributes)
1271 })?;
1272 inv.comb_group = Some(cg_ref);
1273 }
1274 if !ref_cells.is_empty() {
1275 let mut ext_cell_tuples = Vec::new();
1276 for (outcell, incell) in ref_cells {
1277 let ext_cell_ref =
1278 builder.component.find_cell(incell).ok_or_else(
1279 || Error::undefined(incell, "cell".to_string()),
1280 )?;
1281 ext_cell_tuples.push((outcell, ext_cell_ref));
1282 }
1283 inv.ref_cells = ext_cell_tuples;
1284 }
1285 Control::Invoke(inv)
1286 }
1287 ast::Control::Seq { stmts, attributes } => {
1288 let mut s = Control::seq(
1289 stmts
1290 .into_iter()
1291 .map(|c| build_control(c, sig_ctx, builder))
1292 .collect::<CalyxResult<Vec<_>>>()?,
1293 );
1294 *s.get_mut_attributes() = attributes;
1295 s
1296 }
1297 ast::Control::StaticSeq {
1298 stmts,
1299 attributes,
1300 latency,
1301 } => {
1302 let s =
1303 build_static_seq(stmts, attributes, latency, builder, sig_ctx);
1304 Control::Static(s?)
1305 }
1306 ast::Control::StaticPar {
1307 stmts,
1308 attributes,
1309 latency,
1310 } => {
1311 let s =
1312 build_static_par(stmts, attributes, latency, builder, sig_ctx);
1313 Control::Static(s?)
1314 }
1315 ast::Control::StaticIf {
1316 port,
1317 tbranch,
1318 fbranch,
1319 attributes,
1320 latency,
1321 } => {
1322 let s = build_static_if(
1323 port, *tbranch, *fbranch, attributes, latency, builder, sig_ctx,
1324 );
1325 Control::Static(s?)
1326 }
1327 ast::Control::StaticRepeat {
1328 attributes,
1329 num_repeats,
1330 body,
1331 } => {
1332 let s = build_static_repeat(
1333 num_repeats,
1334 *body,
1335 builder,
1336 attributes,
1337 sig_ctx,
1338 );
1339 Control::Static(s?)
1340 }
1341 ast::Control::Par { stmts, attributes } => {
1342 let mut p = Control::par(
1343 stmts
1344 .into_iter()
1345 .map(|c| build_control(c, sig_ctx, builder))
1346 .collect::<CalyxResult<Vec<_>>>()?,
1347 );
1348 *p.get_mut_attributes() = attributes;
1349 p
1350 }
1351 ast::Control::If {
1352 port,
1353 cond: maybe_cond,
1354 tbranch,
1355 fbranch,
1356 attributes,
1357 } => {
1358 let group = maybe_cond
1359 .map(|cond| {
1360 builder.component.find_comb_group(cond).ok_or_else(|| {
1361 Error::undefined(
1362 cond,
1363 "combinational group".to_string(),
1364 )
1365 .with_pos(&attributes)
1366 })
1367 })
1368 .transpose()?;
1369 let mut con = Control::if_(
1370 ensure_direction(
1371 get_port_ref(port, builder.component)?,
1372 Direction::Output,
1373 )?,
1374 group,
1375 Box::new(build_control(*tbranch, sig_ctx, builder)?),
1376 Box::new(build_control(*fbranch, sig_ctx, builder)?),
1377 );
1378 *con.get_mut_attributes() = attributes;
1379 con
1380 }
1381 ast::Control::While {
1382 port,
1383 cond: maybe_cond,
1384 body,
1385 attributes,
1386 } => {
1387 let group = maybe_cond
1388 .map(|cond| {
1389 builder.component.find_comb_group(cond).ok_or_else(|| {
1390 Error::undefined(
1391 cond,
1392 "combinational group".to_string(),
1393 )
1394 .with_pos(&attributes)
1395 })
1396 })
1397 .transpose()?;
1398 let mut con = Control::while_(
1399 ensure_direction(
1400 get_port_ref(port, builder.component)?,
1401 Direction::Output,
1402 )?,
1403 group,
1404 Box::new(build_control(*body, sig_ctx, builder)?),
1405 );
1406 *con.get_mut_attributes() = attributes;
1407 con
1408 }
1409 ast::Control::Repeat {
1410 num_repeats,
1411 body,
1412 attributes,
1413 } => {
1414 let mut con = Control::repeat(
1415 num_repeats,
1416 Box::new(build_control(*body, sig_ctx, builder)?),
1417 );
1418 *con.get_mut_attributes() = attributes;
1419 con
1420 }
1421 ast::Control::Empty { attributes } => {
1422 let mut emp = Control::empty();
1423 *emp.get_mut_attributes() = attributes;
1424 emp
1425 }
1426 };
1427 Ok(c)
1428}