1use crate::{self as ir, RRC};
5use calyx_frontend::{PrimitiveInfo, source_info::SourceInfoTable};
6use calyx_utils::float;
7use itertools::Itertools;
8use std::io;
9use std::path::Path;
10use std::rc::Rc;
11
12pub struct Printer;
14
15impl Printer {
16 pub fn format_at_attributes(attrs: &ir::Attributes) -> String {
19 let mut buf = attrs.to_string_with(
20 " ",
21 |name, val| {
22 if val == 1 {
23 format!("@{name}")
24 } else {
25 format!("@{name}({val})")
26 }
27 },
28 |name, vals| format!("@{}{{{}}}", name, vals.iter().join(", ")),
29 );
30 if !attrs.is_empty() {
31 buf.push(' ');
32 }
33 buf
34 }
35
36 pub fn format_attributes(attrs: &ir::Attributes) -> String {
39 if attrs.is_empty() {
40 "".to_string()
41 } else {
42 format!(
43 "<{}>",
44 attrs.to_string_with(
45 ", ",
46 |name, val| { format!("\"{name}\"={val}") },
47 |name, vals| {
48 format!("\"{}\"={{{}}}", name, vals.iter().join(", "))
49 },
50 )
51 )
52 }
53 }
54
55 pub fn format_ports(ports: &[RRC<ir::Port>]) -> String {
57 ports
58 .iter()
59 .map(|p| {
60 format!(
61 "{}{}: {}",
62 Self::format_at_attributes(&p.borrow().attributes),
63 p.borrow().name.id,
64 p.borrow().width
65 )
66 })
67 .collect::<Vec<_>>()
68 .join(", ")
69 }
70
71 pub fn format_port_def<W: std::fmt::Display>(
72 port_defs: &[&ir::PortDef<W>],
73 ) -> String {
74 port_defs
75 .iter()
76 .map(|pd| {
77 format!(
78 "{}{}: {}",
79 Self::format_at_attributes(&pd.attributes),
80 pd.name(),
81 pd.width
82 )
83 })
84 .collect_vec()
85 .join(", ")
86 }
87
88 pub fn write_context<F: io::Write>(
91 ctx: &ir::Context,
92 skip_primitives: bool,
93 f: &mut F,
94 ) -> io::Result<()> {
95 for prim_info in ctx.lib.prim_infos() {
96 if skip_primitives && !prim_info.is_source() {
97 continue;
98 }
99 match prim_info {
100 PrimitiveInfo::Extern {
101 path, primitives, ..
102 } => {
103 ir::Printer::write_externs(
104 (path, primitives.into_iter().map(|(_, v)| v)),
105 f,
106 )?;
107 }
108 PrimitiveInfo::Inline { primitive, .. } => {
109 ir::Printer::write_primitive(primitive, 0, f)?;
110 }
111 }
112 }
113
114 for comp in &ctx.components {
115 ir::Printer::write_component(comp, f)?;
116 writeln!(f)?
117 }
118 write!(f, "{}", ir::Printer::format_metadata(&ctx.metadata))?;
119
120 Printer::write_source_info_table(f, &ctx.source_info_table)
121 }
122
123 pub fn write_externs<'a, F, I>(
125 (path, prims): (&Path, I),
126 f: &mut F,
127 ) -> io::Result<()>
128 where
129 F: io::Write,
130 I: Iterator<Item = &'a ir::Primitive>,
131 {
132 writeln!(f, "extern \"{}\" {{", path.to_string_lossy())?;
133 for prim in prims {
134 Self::write_primitive(prim, 2, f)?;
135 }
136 writeln!(f, "}}")
137 }
138
139 pub fn write_primitive<F: io::Write>(
140 prim: &ir::Primitive,
141 indent: usize,
142 f: &mut F,
143 ) -> io::Result<()> {
144 write!(f, "{}", " ".repeat(indent))?;
145 if prim.is_comb {
146 write!(f, "comb ")?;
147 }
148 if let Some(latency_val) = prim.latency {
149 write!(f, "static<{latency_val}> ")?;
150 }
151 write!(
152 f,
153 "primitive {}{}",
154 prim.name,
155 Self::format_attributes(&prim.attributes)
156 )?;
157 if !prim.params.is_empty() {
158 write!(
159 f,
160 "[{}]",
161 prim.params
162 .iter()
163 .map(|p| p.to_string())
164 .collect_vec()
165 .join(", ")
166 )?
167 }
168 let (mut inputs, mut outputs) = (vec![], vec![]);
169 for pd in &prim.signature {
170 if pd.direction == ir::Direction::Input {
171 inputs.push(pd)
172 } else {
173 outputs.push(pd)
174 }
175 }
176 write!(
177 f,
178 "({}) -> ({})",
179 Self::format_port_def(&inputs),
180 Self::format_port_def(&outputs)
181 )?;
182 if let Some(b) = prim.body.as_ref() {
183 writeln!(f, " {{")?;
184 writeln!(f, "{:indent$}{b}", "", indent = indent + 2)?;
185 writeln!(f, "}}")
186 } else {
187 writeln!(f, ";")
188 }
189 }
190
191 pub fn write_component<F: io::Write>(
193 comp: &ir::Component,
194 f: &mut F,
195 ) -> io::Result<()> {
196 let sig = comp.signature.borrow();
197 let (inputs, outputs): (Vec<_>, Vec<_>) =
198 sig.ports.iter().map(Rc::clone).partition(|p| {
199 matches!(p.borrow().direction, ir::Direction::Output)
201 });
202
203 let pre = if comp.is_comb {
204 "comb ".to_string()
205 } else if comp.latency.is_some() {
206 format!("static<{}> ", comp.latency.unwrap())
207 } else {
208 "".to_string()
209 };
210
211 writeln!(
212 f,
213 "{}component {}{}({}) -> ({}) {{",
214 pre,
215 comp.name.id,
216 Self::format_attributes(&comp.attributes),
217 Self::format_ports(&inputs),
218 Self::format_ports(&outputs),
219 )?;
220
221 write!(f, " cells {{")?;
223 if !comp.cells.is_empty() {
224 writeln!(f)?;
225 }
226 for cell in comp.cells.iter() {
227 Self::write_cell(&cell.borrow(), 4, f)?;
228 }
229 if !comp.cells.is_empty() {
230 writeln!(f, " }}")?;
231 } else {
232 writeln!(f, "}}")?;
233 }
234
235 let empty_wires = comp.groups.is_empty()
237 && comp.static_groups.is_empty()
238 && comp.comb_groups.is_empty()
239 && comp.continuous_assignments.is_empty();
240 write!(f, " wires {{")?;
241 if !empty_wires {
242 writeln!(f)?;
243 }
244 for group in comp.get_groups().iter() {
245 Self::write_group(&group.borrow(), 4, f)?;
246 writeln!(f)?;
247 }
248 for group in comp.get_static_groups().iter() {
249 Self::write_static_group(&group.borrow(), 4, f)?;
250 writeln!(f)?;
251 }
252 for fsm in comp.get_fsms().iter() {
253 Self::write_fsm(&fsm.borrow(), 4, f)?;
254 writeln!(f)?;
255 }
256 for comb_group in comp.comb_groups.iter() {
257 Self::write_comb_group(&comb_group.borrow(), 4, f)?;
258 writeln!(f)?;
259 }
260 for assign in &comp.continuous_assignments {
262 Self::write_assignment(assign, 4, f)?;
263 writeln!(f)?;
264 }
265 if !empty_wires {
266 writeln!(f, " }}")?;
267 } else {
268 writeln!(f, "}}")?;
269 }
270
271 if !comp.is_comb {
274 let con = &*comp.control.borrow();
275 match con {
276 ir::Control::Empty(ir::Empty { attributes })
277 if attributes.is_empty() =>
278 {
279 writeln!(f, " control {{}}")?;
280 }
281 _ => {
282 writeln!(f, " control {{")?;
283 Self::write_control(&comp.control.borrow(), 4, f)?;
284 writeln!(f, " }}")?;
285 }
286 }
287 }
288
289 write!(f, "}}")
290 }
291
292 pub fn write_float_const<F: io::Write>(
293 cell: &ir::Cell,
294 indent_level: usize,
295 f: &mut F,
296 ) -> io::Result<()> {
297 let ir::CellType::Primitive {
298 name: prim,
299 param_binding,
300 ..
301 } = &cell.prototype
302 else {
303 unreachable!("Expected std_float_const cell")
304 };
305
306 let (rep, width, val) =
307 (param_binding[0].1, param_binding[1].1, param_binding[2].1);
308
309 let fl = match float::emit(val, width) {
310 Ok(fl) => fl,
311 Err(e) => {
312 panic!("Error emitting float constant: {e:?}")
313 }
314 };
315
316 write!(f, "{}", " ".repeat(indent_level))?;
317 write!(f, "{}", Self::format_at_attributes(&cell.attributes))?;
318 if cell.is_reference() {
319 write!(f, "ref ")?;
320 }
321 writeln!(f, "{} = {prim}({rep}, {width}, {fl});", cell.name().id)?;
322 Ok(())
323 }
324
325 pub fn write_cell<F: io::Write>(
327 cell: &ir::Cell,
328 indent_level: usize,
329 f: &mut F,
330 ) -> io::Result<()> {
331 match &cell.prototype {
332 ir::CellType::Primitive {
333 name,
334 param_binding,
335 ..
336 } => {
337 if name == "std_float_const" {
338 return Self::write_float_const(cell, indent_level, f);
339 }
340
341 write!(f, "{}", " ".repeat(indent_level))?;
342 write!(f, "{}", Self::format_at_attributes(&cell.attributes))?;
343 if cell.is_reference() {
344 write!(f, "ref ")?
345 }
346 write!(f, "{} = ", cell.name().id)?;
347 writeln!(
348 f,
349 "{}({});",
350 name.id,
351 param_binding
352 .iter()
353 .map(|(_, v)| v.to_string())
354 .collect::<Vec<_>>()
355 .join(", ")
356 )
357 }
358 ir::CellType::Component { name, .. } => {
359 write!(f, "{}", " ".repeat(indent_level))?;
360 write!(f, "{}", Self::format_at_attributes(&cell.attributes))?;
361 if cell.is_reference() {
362 write!(f, "ref ")?
363 }
364 writeln!(f, "{} = {}();", cell.name().id, name)
365 }
366 ir::CellType::Constant { .. } => Ok(()),
367 _ => unimplemented!(),
368 }
369 }
370
371 pub fn write_assignment<F: io::Write, T: Clone + ToString + Eq>(
373 assign: &ir::Assignment<T>,
374 indent_level: usize,
375 f: &mut F,
376 ) -> io::Result<()> {
377 write!(f, "{}", " ".repeat(indent_level))?;
378 write!(f, "{}", Self::format_at_attributes(&assign.attributes))?;
379 write!(f, "{} = ", Self::port_to_str(&assign.dst.borrow()))?;
380 if !matches!(&*assign.guard, ir::Guard::True) {
381 write!(f, "{} ? ", Self::guard_str(&assign.guard.clone()))?;
382 }
383 write!(f, "{};", Self::port_to_str(&assign.src.borrow()))
384 }
385
386 pub fn assignment_to_str<T>(assign: &ir::Assignment<T>) -> String
388 where
389 T: ToString + Clone + Eq,
390 {
391 let mut buf = Vec::new();
392 Self::write_assignment(assign, 0, &mut buf).ok();
393 String::from_utf8_lossy(buf.as_slice()).to_string()
394 }
395
396 pub fn control_to_str(assign: &ir::Control) -> String {
398 let mut buf = Vec::new();
399 Self::write_control(assign, 0, &mut buf).ok();
400 String::from_utf8_lossy(buf.as_slice()).to_string()
401 }
402
403 pub fn write_comb_group<F: io::Write>(
405 group: &ir::CombGroup,
406 indent_level: usize,
407 f: &mut F,
408 ) -> io::Result<()> {
409 write!(f, "{}", " ".repeat(indent_level))?;
410 write!(f, "comb group {}", group.name().id)?;
411 if !group.attributes.is_empty() {
412 write!(f, "{}", Self::format_attributes(&group.attributes))?;
413 }
414 writeln!(f, " {{")?;
415
416 for assign in &group.assignments {
417 Self::write_assignment(assign, indent_level + 2, f)?;
418 writeln!(f)?;
419 }
420 write!(f, "{}}}", " ".repeat(indent_level))
421 }
422
423 pub fn write_group<F: io::Write>(
425 group: &ir::Group,
426 indent_level: usize,
427 f: &mut F,
428 ) -> io::Result<()> {
429 write!(f, "{}", " ".repeat(indent_level))?;
430 write!(f, "group {}", group.name().id)?;
431 if !group.attributes.is_empty() {
432 write!(f, "{}", Self::format_attributes(&group.attributes))?;
433 }
434 writeln!(f, " {{")?;
435
436 for assign in &group.assignments {
437 Self::write_assignment(assign, indent_level + 2, f)?;
438 writeln!(f)?;
439 }
440 write!(f, "{}}}", " ".repeat(indent_level))
441 }
442
443 pub fn write_static_group<F: io::Write>(
445 group: &ir::StaticGroup,
446 indent_level: usize,
447 f: &mut F,
448 ) -> io::Result<()> {
449 write!(f, "{}", " ".repeat(indent_level))?;
450 write!(
451 f,
452 "static<{}> group {}",
453 group.get_latency(),
454 group.name().id,
455 )?;
456 if !group.attributes.is_empty() {
457 write!(f, "{}", Self::format_attributes(&group.attributes))?;
458 }
459 writeln!(f, " {{")?;
460
461 for assign in &group.assignments {
462 Self::write_assignment(assign, indent_level + 2, f)?;
463 writeln!(f)?;
464 }
465 write!(f, "{}}}", " ".repeat(indent_level))
466 }
467
468 pub fn write_fsm<F: io::Write>(
470 fsm: &ir::FSM,
471 indent_level: usize,
472 f: &mut F,
473 ) -> io::Result<()> {
474 write!(f, "{}", " ".repeat(indent_level))?;
475 write!(f, "fsm {}", fsm.name().id)?;
476 if !fsm.attributes.is_empty() {
477 write!(f, "{}", Self::format_attributes(&fsm.attributes))?;
478 }
479 writeln!(f, " {{")?;
480 for (i, (assigns, trans)) in
481 fsm.assignments.iter().zip(&fsm.transitions).enumerate()
482 {
483 write!(f, "{}", " ".repeat(indent_level + 2))?;
485 write!(f, "{i} : ")?;
486 match assigns.is_empty() {
487 true => {
488 write!(f, "{{")?;
490 write!(f, "}} ")?;
491 write!(f, "=> ")?;
492 }
493 false => {
494 writeln!(f, "{{")?;
495 for assign in assigns {
496 Self::write_assignment(assign, indent_level + 4, f)?;
497 writeln!(f)?;
498 }
499 write!(f, "{}", " ".repeat(indent_level + 2))?;
500 write!(f, "}} => ")?;
501 }
502 }
503
504 match trans {
506 ir::Transition::Unconditional(s) => {
507 writeln!(f, "{s},")?;
508 }
509 ir::Transition::Conditional(cond_dsts) => {
510 writeln!(f, "{{")?;
511 for (i, (g, dst)) in cond_dsts.iter().enumerate() {
512 write!(f, "{}", " ".repeat(indent_level + 4))?;
513 if i == cond_dsts.len() - 1 {
514 writeln!(f, "default -> {dst},")?;
515 } else {
516 writeln!(f, "{} -> {},", Self::guard_str(g), dst)?;
517 }
518 }
519 write!(f, "{}", " ".repeat(indent_level + 2))?;
520 writeln!(f, "}},")?;
521 }
522 }
523 }
524 write!(f, "{}}}", " ".repeat(indent_level))
525 }
526
527 pub fn write_static_control<F: io::Write>(
529 scontrol: &ir::StaticControl,
530 indent_level: usize,
531 f: &mut F,
532 ) -> io::Result<()> {
533 write!(f, "{}", " ".repeat(indent_level))?;
534 match scontrol {
535 ir::StaticControl::Enable(ir::StaticEnable {
536 group,
537 attributes,
538 }) => {
539 write!(f, "{}", Self::format_at_attributes(attributes))?;
540 writeln!(f, "{};", group.borrow().name().id)
541 }
542 ir::StaticControl::Repeat(ir::StaticRepeat {
543 num_repeats,
544 attributes,
545 body,
546 ..
547 }) => {
548 write!(f, "{}", Self::format_at_attributes(attributes))?;
549 write!(f, "static repeat {num_repeats} ")?;
550 writeln!(f, "{{")?;
551 Self::write_static_control(body, indent_level + 2, f)?;
552 writeln!(f, "{}}}", " ".repeat(indent_level))
553 }
554 ir::StaticControl::Seq(ir::StaticSeq {
555 stmts,
556 attributes,
557 latency,
558 }) => {
559 write!(f, "{}", Self::format_at_attributes(attributes))?;
560 writeln!(f, "static<{latency}> seq {{")?;
561 for stmt in stmts {
562 Self::write_static_control(stmt, indent_level + 2, f)?;
563 }
564 writeln!(f, "{}}}", " ".repeat(indent_level))
565 }
566 ir::StaticControl::Par(ir::StaticPar {
567 stmts,
568 attributes,
569 latency,
570 }) => {
571 write!(f, "{}", Self::format_at_attributes(attributes))?;
572 writeln!(f, "static<{latency}> par {{")?;
573 for stmt in stmts {
574 Self::write_static_control(stmt, indent_level + 2, f)?;
575 }
576 writeln!(f, "{}}}", " ".repeat(indent_level))
577 }
578 ir::StaticControl::Empty(ir::Empty { attributes }) => {
579 if !attributes.is_empty() {
580 writeln!(f, "{};", Self::format_at_attributes(attributes))
581 } else {
582 writeln!(f)
583 }
584 }
585 ir::StaticControl::If(ir::StaticIf {
586 port,
587 latency,
588 tbranch,
589 fbranch,
590 attributes,
591 }) => {
592 write!(f, "{}", Self::format_at_attributes(attributes))?;
593 write!(
594 f,
595 "static<{}> if {} ",
596 latency,
597 Self::port_to_str(&port.borrow()),
598 )?;
599 writeln!(f, "{{")?;
600 Self::write_static_control(tbranch, indent_level + 2, f)?;
601 write!(f, "{}}}", " ".repeat(indent_level))?;
602 if let ir::StaticControl::Empty(_) = **fbranch {
603 writeln!(f)
604 } else {
605 writeln!(f, " else {{")?;
606 Self::write_static_control(fbranch, indent_level + 2, f)?;
607 writeln!(f, "{}}}", " ".repeat(indent_level))
608 }
609 }
610 ir::StaticControl::Invoke(ir::StaticInvoke {
611 comp,
612 latency,
613 inputs,
614 outputs,
615 attributes,
616 ref_cells,
617 comb_group,
618 }) => {
619 write!(f, "{}", Self::format_at_attributes(attributes))?;
620 write!(
621 f,
622 "static<{}> invoke {}",
623 latency,
624 comp.borrow().name()
625 )?;
626 if !ref_cells.is_empty() {
627 write!(f, "[")?;
628 for (i, (outcell, incell)) in ref_cells.iter().enumerate() {
629 write!(
630 f,
631 "{}{} = {}",
632 if i == 0 { "" } else { "," },
633 outcell,
634 incell.borrow().name()
635 )?
636 }
637 write!(f, "]")?;
638 }
639 write!(f, "(")?;
640 for (i, (arg, port)) in inputs.iter().enumerate() {
641 write!(
642 f,
643 "{}\n{}{} = {}",
644 if i == 0 { "" } else { "," },
645 " ".repeat(indent_level + 2),
646 arg,
647 Self::port_to_str(&port.borrow())
648 )?;
649 }
650 if inputs.is_empty() {
651 write!(f, ")(")?;
652 } else {
653 write!(f, "\n{})(", " ".repeat(indent_level))?;
654 }
655 for (i, (arg, port)) in outputs.iter().enumerate() {
656 write!(
657 f,
658 "{}\n{}{} = {}",
659 if i == 0 { "" } else { "," },
660 " ".repeat(indent_level + 2),
661 arg,
662 Self::port_to_str(&port.borrow())
663 )?;
664 }
665 if outputs.is_empty() {
666 write!(f, ")")?;
667 } else {
668 write!(f, "\n{})", " ".repeat(indent_level))?;
669 }
670 if let Some(group) = comb_group {
671 write!(f, " with {}", group.borrow().name)?;
672 }
673 writeln!(f, ";")
674 }
675 }
676 }
677
678 pub fn write_control<F: io::Write>(
680 control: &ir::Control,
681 indent_level: usize,
682 f: &mut F,
683 ) -> io::Result<()> {
684 if !matches!(control, ir::Control::Static(_)) {
686 write!(f, "{}", " ".repeat(indent_level))?;
687 }
688 match control {
689 ir::Control::Enable(ir::Enable { group, attributes }) => {
690 write!(f, "{}", Self::format_at_attributes(attributes))?;
691 writeln!(f, "{};", group.borrow().name().id)
692 }
693 ir::Control::FSMEnable(ir::FSMEnable { fsm, attributes }) => {
694 write!(f, "{}", Self::format_at_attributes(attributes))?;
695 writeln!(f, "{};", fsm.borrow().name().id)
696 }
697 ir::Control::Invoke(ir::Invoke {
698 comp,
699 inputs,
700 outputs,
701 attributes,
702 comb_group,
703 ref_cells,
704 }) => {
705 if !attributes.is_empty() {
706 write!(f, "{}", Self::format_at_attributes(attributes))?
707 }
708 write!(f, "invoke {}", comp.borrow().name())?;
709 if !ref_cells.is_empty() {
710 write!(f, "[")?;
711 for (i, (outcell, incell)) in ref_cells.iter().enumerate() {
712 write!(
713 f,
714 "{}{} = {}",
715 if i == 0 { "" } else { "," },
716 outcell,
717 incell.borrow().name()
718 )?
719 }
720 write!(f, "]")?;
721 }
722 write!(f, "(")?;
723 for (i, (arg, port)) in inputs.iter().enumerate() {
724 write!(
725 f,
726 "{}\n{}{} = {}",
727 if i == 0 { "" } else { "," },
728 " ".repeat(indent_level + 2),
729 arg,
730 Self::port_to_str(&port.borrow())
731 )?;
732 }
733 if inputs.is_empty() {
734 write!(f, ")(")?;
735 } else {
736 write!(f, "\n{})(", " ".repeat(indent_level))?;
737 }
738 for (i, (arg, port)) in outputs.iter().enumerate() {
739 write!(
740 f,
741 "{}\n{}{} = {}",
742 if i == 0 { "" } else { "," },
743 " ".repeat(indent_level + 2),
744 arg,
745 Self::port_to_str(&port.borrow())
746 )?;
747 }
748 if outputs.is_empty() {
749 write!(f, ")")?;
750 } else {
751 write!(f, "\n{})", " ".repeat(indent_level))?;
752 }
753 if let Some(group) = comb_group {
754 writeln!(f, " with {};", group.borrow().name)
755 } else {
756 writeln!(f, ";")
757 }
758 }
759 ir::Control::Seq(ir::Seq { stmts, attributes }) => {
760 write!(f, "{}", Self::format_at_attributes(attributes))?;
761 writeln!(f, "seq {{")?;
762 for stmt in stmts {
763 Self::write_control(stmt, indent_level + 2, f)?;
764 }
765 writeln!(f, "{}}}", " ".repeat(indent_level))
766 }
767 ir::Control::Repeat(ir::Repeat {
768 num_repeats,
769 attributes,
770 body,
771 ..
772 }) => {
773 write!(f, "{}", Self::format_at_attributes(attributes))?;
774 write!(f, "repeat {num_repeats} ")?;
775 writeln!(f, "{{")?;
776 Self::write_control(body, indent_level + 2, f)?;
777 writeln!(f, "{}}}", " ".repeat(indent_level))
778 }
779 ir::Control::Par(ir::Par { stmts, attributes }) => {
780 write!(f, "{}", Self::format_at_attributes(attributes))?;
781 writeln!(f, "par {{")?;
782 for stmt in stmts {
783 Self::write_control(stmt, indent_level + 2, f)?;
784 }
785 writeln!(f, "{}}}", " ".repeat(indent_level))
786 }
787 ir::Control::If(ir::If {
788 port,
789 cond,
790 tbranch,
791 fbranch,
792 attributes,
793 }) => {
794 write!(f, "{}", Self::format_at_attributes(attributes))?;
795 write!(f, "if {} ", Self::port_to_str(&port.borrow()),)?;
796 if let Some(c) = cond {
797 write!(f, "with {} ", c.borrow().name.id)?;
798 }
799 writeln!(f, "{{")?;
800 Self::write_control(tbranch, indent_level + 2, f)?;
801 write!(f, "{}}}", " ".repeat(indent_level))?;
802 if let ir::Control::Empty(_) = **fbranch {
803 writeln!(f)
804 } else {
805 writeln!(f, " else {{")?;
806 Self::write_control(fbranch, indent_level + 2, f)?;
807 writeln!(f, "{}}}", " ".repeat(indent_level))
808 }
809 }
810 ir::Control::While(ir::While {
811 port,
812 cond,
813 body,
814 attributes,
815 }) => {
816 write!(f, "{}", Self::format_at_attributes(attributes))?;
817 write!(f, "while {} ", Self::port_to_str(&port.borrow()),)?;
818 if let Some(c) = cond {
819 write!(f, "with {} ", c.borrow().name.id)?;
820 }
821 writeln!(f, "{{")?;
822 Self::write_control(body, indent_level + 2, f)?;
823 writeln!(f, "{}}}", " ".repeat(indent_level))
824 }
825 ir::Control::Empty(ir::Empty { attributes }) => {
826 if !attributes.is_empty() {
827 writeln!(f, "{};", Self::format_at_attributes(attributes))
828 } else {
829 writeln!(f)
830 }
831 }
832 ir::Control::Static(sc) => {
833 Self::write_static_control(sc, indent_level, f)
834 }
835 }
836 }
837
838 pub fn guard_str<T>(guard: &ir::Guard<T>) -> String
840 where
841 T: Eq + ToString,
842 {
843 match &guard {
844 ir::Guard::And(l, r) | ir::Guard::Or(l, r) => {
845 let left = if &**l > guard {
846 format!("({})", Self::guard_str(l))
847 } else {
848 Self::guard_str(l)
849 };
850 let right = if &**r > guard {
851 format!("({})", Self::guard_str(r))
852 } else {
853 Self::guard_str(r)
854 };
855 format!("{} {} {}", left, &guard.op_str(), right)
856 }
857 ir::Guard::CompOp(_, l, r) => {
858 format!(
859 "{} {} {}",
860 Self::port_to_str(&l.borrow()),
861 &guard.op_str(),
862 Self::port_to_str(&r.borrow())
863 )
864 }
865 ir::Guard::Not(g) => {
866 let s = if &**g > guard {
867 format!("({})", Self::guard_str(g))
868 } else {
869 Self::guard_str(g)
870 };
871 format!("!{s}")
872 }
873 ir::Guard::Port(port_ref) => Self::port_to_str(&port_ref.borrow()),
874 ir::Guard::True => "1'b1".to_string(),
875 ir::Guard::Info(i) => i.to_string(),
876 }
877 }
878
879 pub fn port_to_str(port: &ir::Port) -> String {
881 match &port.parent {
882 ir::PortParent::Cell(cell_wref) => {
883 let cell_ref =
884 cell_wref.internal.upgrade().unwrap_or_else(|| {
885 panic!(
886 "Malformed AST: No reference to Cell for port `{}'",
887 port.name
888 )
889 });
890 let cell = cell_ref.borrow();
891 match cell.prototype {
892 ir::CellType::Constant { val, width } => {
893 format!("{width}'d{val}")
894 }
895 ir::CellType::ThisComponent => port.name.to_string(),
896 _ => format!("{}.{}", cell.name().id, port.name.id),
897 }
898 }
899 ir::PortParent::Group(group_wref) => format!(
900 "{}[{}]",
901 group_wref
902 .internal
903 .upgrade()
904 .unwrap_or_else(|| panic!(
905 "Malformed AST: No reference to Group for port `{port:#?}'"
906 ))
907 .borrow()
908 .name()
909 .id,
910 port.name.id
911 ),
912 ir::PortParent::FSM(fsm_wref) => format!(
913 "{}[{}]",
914 fsm_wref
915 .internal
916 .upgrade()
917 .unwrap_or_else(|| panic!(
918 "Malformed AST: No reference to FSM for port `{port:#?}'"
919 ))
920 .borrow()
921 .name()
922 .id,
923 port.name.id
924 ),
925 ir::PortParent::StaticGroup(group_wref) => format!(
926 "{}[{}]",
927 group_wref
928 .internal
929 .upgrade()
930 .unwrap_or_else(|| panic!(
931 "Malformed AST: No reference to Group for port `{port:#?}'"
932 ))
933 .borrow()
934 .name()
935 .id,
936 port.name.id
937 ),
938 }
939 }
940
941 pub fn format_metadata(metadata: &Option<String>) -> String {
943 if let Some(metadata_str) = metadata {
944 format!("metadata #{{\n{metadata_str}\n}}#\n")
945 } else {
946 String::new()
947 }
948 }
949
950 pub fn write_source_info_table<W: io::Write>(
951 f: &mut W,
952 metadata: &Option<SourceInfoTable>,
953 ) -> Result<(), std::io::Error> {
954 if let Some(metadata) = metadata {
955 metadata.serialize(f)
956 } else {
957 Ok(())
958 }
959 }
960}