1use crate::{traits::Backend, VerilogBackend};
7use calyx_ir::{self as ir, Binding, RRC};
8use calyx_utils::{CalyxResult, Id, OutputFile};
9use ir::Port;
10use std::collections::HashSet;
11use std::io;
12
13pub(super) const SPACING: &str = " ";
14
15#[derive(Default)]
18pub struct FirrtlBackend;
19
20impl Backend for FirrtlBackend {
21 fn name(&self) -> &'static str {
22 "firrtl"
23 }
24
25 fn link_externs(
26 _prog: &calyx_ir::Context,
27 _write: &mut calyx_utils::OutputFile,
28 ) -> calyx_utils::CalyxResult<()> {
29 Ok(()) }
31
32 fn validate(prog: &calyx_ir::Context) -> calyx_utils::CalyxResult<()> {
33 VerilogBackend::validate(prog) }
35
36 fn emit(ctx: &ir::Context, file: &mut OutputFile) -> CalyxResult<()> {
37 let out = &mut file.get_write();
38 writeln!(out, "circuit {}:", ctx.entrypoint)?;
39 if ctx.bc.emit_primitive_extmodules {
40 emit_extmodules(ctx, out)?;
41 }
42 for comp in ctx.components.iter() {
43 emit_component(comp, out)?
44 }
45 Ok(())
46 }
47}
48
49fn emit_extmodules<F: io::Write>(
50 ctx: &ir::Context,
51 out: &mut F,
52) -> Result<(), calyx_utils::Error> {
53 let mut extmodule_set: HashSet<String> = HashSet::new();
54 for comp in &ctx.components {
55 for cell in comp.cells.iter() {
56 let cell_borrowed = cell.as_ref().borrow();
57 if let ir::CellType::Primitive {
58 name,
59 param_binding,
60 ..
61 } = &cell_borrowed.prototype
62 {
63 let curr_module_name =
64 get_primitive_module_name(name, param_binding);
65 if extmodule_set.insert(curr_module_name.clone()) {
66 emit_primitive_extmodule(
67 cell.borrow().ports(),
68 &curr_module_name,
69 name,
70 param_binding,
71 out,
72 )?;
73 }
74 };
75 }
76 }
77 Ok(())
78}
79
80fn emit_component<F: io::Write>(
82 comp: &ir::Component,
83 f: &mut F,
84) -> io::Result<()> {
85 writeln!(f, "{}module {}:", SPACING, comp.name)?;
86
87 let sig = comp.signature.borrow();
89 for port_ref in &sig.ports {
90 let port = port_ref.borrow();
91 emit_port(port, true, f)?;
92 }
93
94 writeln!(f, "{}; COMPONENT START: {}", SPACING.repeat(2), comp.name)?;
96
97 for cell in comp.cells.iter() {
99 let cell_borrowed = cell.as_ref().borrow();
100 if cell_borrowed.type_name().is_some() {
101 let module_name = match &cell_borrowed.prototype {
102 ir::CellType::Primitive {
103 name,
104 param_binding,
105 is_comb: _,
106 latency: _,
107 } => get_primitive_module_name(name, param_binding),
108 ir::CellType::Component { name } => name.to_string(),
109 _ => unreachable!(),
110 };
111 writeln!(
112 f,
113 "{}inst {} of {}",
114 SPACING.repeat(2),
115 cell_borrowed.name(),
116 module_name
117 )?;
118 }
119 }
120
121 let mut dst_set: HashSet<ir::Canonical> = HashSet::new();
122 for asgn in &comp.continuous_assignments {
124 match asgn.guard.as_ref() {
125 ir::Guard::True => {
126 let _ = write_assignment(asgn, f, 2);
128 }
129 _ => {
130 let dst_canonical = asgn.dst.as_ref().borrow().canonical();
131 if !dst_set.contains(&dst_canonical) {
132 write_invalid_initialization(&asgn.dst, f)?;
136 dst_set.insert(dst_canonical);
137 }
138 let guard_string = get_guard_string(asgn.guard.as_ref());
140 writeln!(f, "{}when {}:", SPACING.repeat(2), guard_string)?;
141 write_assignment(asgn, f, 3)?;
142 }
143 }
144 }
145
146 writeln!(f, "{}; COMPONENT END: {}\n", SPACING.repeat(2), comp.name)?;
148
149 Ok(())
150}
151
152fn get_primitive_module_name(name: &Id, param_binding: &Binding) -> String {
154 let mut primitive_string = name.to_string();
155 for (_, size) in param_binding.as_ref().iter() {
156 primitive_string.push('_');
157 primitive_string.push_str(&size.to_string());
158 }
159 primitive_string
160}
161
162fn emit_primitive_extmodule<F: io::Write>(
163 ports: &[RRC<Port>],
164 curr_module_name: &String,
165 name: &Id,
166 param_binding: &Binding,
167 f: &mut F,
168) -> io::Result<()> {
169 writeln!(f, "{}extmodule {}:", SPACING, curr_module_name)?;
170 for port in ports {
171 let port_borrowed = port.borrow();
172 emit_port(port_borrowed, false, f)?;
173 }
174 writeln!(f, "{}defname = {}", SPACING.repeat(2), name)?;
175 for (id, size) in param_binding.as_ref().iter() {
176 writeln!(f, "{}parameter {} = {}", SPACING.repeat(2), id, size)?;
177 }
178 writeln!(f)?;
179 Ok(())
180}
181
182fn emit_port<F: io::Write>(
183 port: std::cell::Ref<'_, Port>,
184 reverse_direction: bool,
185 f: &mut F,
186) -> Result<(), io::Error> {
187 let direction_string = match port.direction {
188 calyx_frontend::Direction::Input => {
189 if reverse_direction {
190 "output"
191 } else {
192 "input"
193 }
194 }
195 calyx_frontend::Direction::Output => {
196 if reverse_direction {
197 "input"
198 } else {
199 "output"
200 }
201 }
202 calyx_frontend::Direction::Inout => {
203 panic!("Unexpected Inout port on Component: {}", port.name)
204 }
205 };
206 if port.has_attribute(ir::BoolAttr::Clk) {
207 writeln!(
208 f,
209 "{}{} {}: Clock",
210 SPACING.repeat(2),
211 direction_string,
212 port.name
213 )?;
214 } else {
215 writeln!(
216 f,
217 "{}{} {}: UInt<{}>",
218 SPACING.repeat(2),
219 direction_string,
220 port.name,
221 port.width
222 )?;
223 };
224 Ok(())
225}
226
227fn get_guard_string(guard: &ir::Guard<ir::Nothing>) -> String {
231 match guard {
232 ir::Guard::Or(l, r) => {
233 let l_str = get_guard_string(l.as_ref());
234 let r_str = get_guard_string(r.as_ref());
235 format!("or({}, {})", l_str, r_str)
236 }
237 ir::Guard::And(l, r) => {
238 let l_str = get_guard_string(l.as_ref());
239 let r_str = get_guard_string(r.as_ref());
240 format!("and({}, {})", l_str, r_str)
241 }
242 ir::Guard::Not(g) => {
243 let g_str = get_guard_string(g);
244 format!("not({})", g_str)
245 }
246 ir::Guard::True => String::from(""),
247 ir::Guard::CompOp(op, l, r) => {
248 let l_str = get_port_string(&l.borrow(), false);
249 let r_str = get_port_string(&r.borrow(), false);
250 let op_str = match op {
251 ir::PortComp::Eq => "eq",
252 ir::PortComp::Neq => "neq",
253 ir::PortComp::Gt => "gt",
254 ir::PortComp::Lt => "lt",
255 ir::PortComp::Geq => "geq",
256 ir::PortComp::Leq => "leq",
257 };
258 format!("{}({}, {})", op_str, l_str, r_str)
259 }
260 ir::Guard::Port(port) => get_port_string(&port.borrow(), false),
261 ir::Guard::Info(_) => {
262 panic!("guard should not have info")
263 }
264 }
265}
266
267fn get_port_string(port: &calyx_ir::Port, is_dst: bool) -> String {
270 match &port.parent {
271 ir::PortParent::Cell(cell) => {
272 let parent_ref = cell.upgrade();
273 let parent = parent_ref.borrow();
274 match parent.prototype {
275 ir::CellType::Constant { val, width: _ } => {
276 if !is_dst {
277 format!("UInt({})", val)
278 } else {
279 unreachable!()
280 }
281 }
282 ir::CellType::ThisComponent => String::from(port.name.as_ref()),
283 _ => {
284 format!("{}.{}", parent.name().as_ref(), port.name.as_ref())
285 }
286 }
287 }
288 _ => {
289 unreachable!("Groups should not be parents as this backend takes place after compiler passes.")
290 }
291 }
292}
293
294fn write_invalid_initialization<F: io::Write>(
296 port: &RRC<ir::Port>,
297 f: &mut F,
298) -> io::Result<()> {
299 let default_initialization_str = "; default initialization";
300 let dst_string = get_port_string(&port.borrow(), true);
301 if port.borrow().attributes.has(ir::BoolAttr::Control) {
302 writeln!(
303 f,
304 "{}{} <= UInt(0) {}",
305 SPACING.repeat(2),
306 dst_string,
307 default_initialization_str
308 )
309 } else {
310 writeln!(
311 f,
312 "{}{} is invalid {}",
313 SPACING.repeat(2),
314 dst_string,
315 default_initialization_str
316 )?;
317 writeln!(f, "{}{} <= UInt(0)", SPACING.repeat(2), dst_string)
318 }
319}
320
321fn write_assignment<F: io::Write>(
323 asgn: &ir::Assignment<ir::Nothing>,
324 f: &mut F,
325 num_indent: usize,
326) -> io::Result<()> {
327 let dest_port = asgn.dst.borrow();
328 let dest_string = get_port_string(&dest_port, true);
329 let source_port = asgn.src.borrow();
330 let src_string = get_port_string(&source_port, false);
331 writeln!(
332 f,
333 "{}{} <= {}",
334 SPACING.repeat(num_indent),
335 dest_string,
336 src_string
337 )?;
338 Ok(())
339}