calyx_opt/passes/
wrap_main.rs

1use std::borrow::BorrowMut;
2
3use crate::traversal::{Action, Named, VisResult, Visitor};
4use calyx_ir as ir;
5use ir::build_assignments;
6
7#[derive(Default)]
8/// If the top-level component is not named `main`, adds a new `main` component
9/// and makes it the top-level component.
10/// This is useful because a lot of our tools rely on the name `main` being the design under test (DUT).
11///
12/// For more information, see https://github.com/calyxir/calyx/issues/1376
13pub struct WrapMain;
14
15impl Named for WrapMain {
16    fn name() -> &'static str {
17        "wrap-main"
18    }
19
20    fn description() -> &'static str {
21        "If the top-level component is not named `main`, adds a new `main` component and makes it the top-level component."
22    }
23}
24
25impl Visitor for WrapMain {
26    fn precondition(ctx: &calyx_ir::Context) -> Option<String>
27    where
28        Self: Sized,
29    {
30        if ctx.entrypoint().name == "main" {
31            Some("Top-level component is already named `main'".to_string())
32        } else {
33            None
34        }
35    }
36
37    fn start_context(&mut self, ctx: &mut ir::Context) -> VisResult {
38        let entry = ctx.entrypoint();
39        let sig = entry.signature.borrow();
40
41        // If any of the ports in the entrypoint component are non-interface ports, refuse to run this pass.
42        if let Some(p) = sig.ports.iter().find(|p| {
43            let port = p.borrow();
44            let attr = &port.attributes;
45            !(attr.has(ir::BoolAttr::Clk)
46                || attr.has(ir::BoolAttr::Reset)
47                || attr.has(ir::NumAttr::Go)
48                || attr.has(ir::NumAttr::Done))
49        }) {
50            let pn = p.borrow().name;
51            log::warn!(
52                "Entrypoint component `{}' has non-interface port `{}'. Cannot wrap it in `main' component. The component might not simulate with the Calyx test bench or generate results with the synthesis scripts without modification.",
53                entry.name,
54                pn
55            );
56            return Ok(Action::Stop);
57        }
58        let entry_name = entry.name;
59        let mut ports = sig.get_signature();
60        ports
61            .iter_mut()
62            .for_each(|pd| pd.direction = pd.direction.reverse());
63        drop(sig);
64
65        // Remove top-level attribute from previous component
66        ctx.entrypoint_mut()
67            .attributes
68            .remove(ir::BoolAttr::TopLevel);
69
70        // Create a new `main' component
71        let mut main = ir::Component::new("main", vec![], true, false, None);
72        main.borrow_mut()
73            .attributes
74            .insert(ir::BoolAttr::TopLevel, 1);
75
76        // Add the original top-level component as a cell to the main component.
77        {
78            let mut builder = ir::Builder::new(&mut main, &ctx.lib);
79            let comp = builder.add_component(entry_name, entry_name, ports);
80            let main_sig = builder.component.signature.clone();
81            let cont_assigns = build_assignments!(builder;
82                comp["go"] = ? main_sig["go"];
83                main_sig["done"] = ? comp["done"];
84            );
85            builder
86                .component
87                .continuous_assignments
88                .extend(cont_assigns);
89        }
90
91        // Update the context entrypoint to be the main component
92        ctx.entrypoint = main.name;
93        ctx.components.push(main);
94
95        // Purely context directed pass
96        Ok(Action::Stop)
97    }
98}