1use super::PulsarBackend;
4use crate::{build_assignments_2, finish_component, Output};
5use builder::{
6 CalyxAssignmentContainer, CalyxBuilder, CalyxCell, CalyxCellKind,
7 CalyxComponent, CalyxControl, Sequential
8};
9use calyx_backend::Backend;
10use pulsar_frontend::ty::Type;
11use pulsar_ir::{
12 basic_block::BasicBlockCell,
13 control_flow_graph::ControlFlowGraph,
14 generator::GeneratedTopLevel,
15 label::{Label, LabelName, MAIN_SYMBOL_PREFIX},
16 operand::Operand,
17 variable::Variable,
18 Ir
19};
20use std::{io::stderr, path::PathBuf};
21
22pub mod builder;
23
24#[derive(Default)]
33struct FunctionContext {
34 ret_cell: Option<CalyxCell>,
35 param_env: usize
36}
37
38pub struct CalyxBackend {
39 builder: CalyxBuilder
40}
41
42impl CalyxBackend {
43 fn make_cell_for_array(
47 &self, component: &mut CalyxComponent<FunctionContext>, var: Variable,
48 cell_size: usize, length: usize
49 ) -> CalyxCell {
50 component.named_mem(var.to_string(), cell_size, length, 64)
51 }
52
53 fn find_operand_cell(
56 &self, component: &mut CalyxComponent<FunctionContext>,
57 operand: &Operand
58 ) -> CalyxCell {
59 match &operand {
60 Operand::Constant(value) => component.constant(*value, 64),
61 Operand::Variable(var) => component.find(var.to_string())
62 }
63 }
64
65 fn register_func(&mut self, label: &Label, args: &[Type], ret: &Type) {
66 let mut comp_ports = vec![];
67 for (i, arg) in args.iter().enumerate() {
68 let width = arg.size();
69 let name = format!("arg{}", i);
70 comp_ports.push(calyx_ir::PortDef::new(
71 name,
72 (width * 8) as u64,
73 calyx_ir::Direction::Input,
74 calyx_ir::Attributes::default()
75 ));
76 }
77 if *ret != Type::Unit {
78 comp_ports.push(calyx_ir::PortDef::new(
79 "ret",
80 (ret.size() * 8) as u64,
81 calyx_ir::Direction::Output,
82 calyx_ir::Attributes::default()
83 ));
84 }
85 self.builder
86 .register_component(label.name.mangle().clone(), comp_ports);
87
88 if label.name.mangle().starts_with(MAIN_SYMBOL_PREFIX) {
89 self.builder.set_entrypoint(label.name.mangle().clone());
90 }
91 }
92
93 fn cell_for_call(
96 &self, component: &mut CalyxComponent<FunctionContext>,
97 call: &LabelName, unique: bool
98 ) -> (String, CalyxCell) {
99 let callee_name = call.mangle().clone();
100 let cell_name = format!("call{}", callee_name);
101 component.component_cell(cell_name, callee_name, unique)
102 }
103
104 fn new_unnamed_reg(
107 &self, component: &mut CalyxComponent<FunctionContext>
108 ) -> CalyxCell {
109 component.new_unnamed_cell(CalyxCellKind::Register { size: 64 })
110 }
111
112 fn emit_ir(
113 &self, component: &mut CalyxComponent<FunctionContext>,
114 parent: &mut CalyxControl<Sequential>, ir: &Ir
115 ) {
116 let signal_out = component.signal_out();
117 match ir {
118 Ir::Add(result, lhs, rhs) => {
119 let lhs_cell = self.find_operand_cell(component, lhs);
120 let rhs_cell = self.find_operand_cell(component, rhs);
121 let result_cell = component.new_reg(result.to_string(), 64);
122 let adder = component.new_prim("adder", "std_add", vec![64]);
123 let add_group = component.add_group("add");
124 add_group.extend(build_assignments_2!(component;
125 adder["left"] = ? lhs_cell["out"];
126 adder["right"] = ? rhs_cell["out"];
127 result_cell["in"] = ? adder["out"];
128 result_cell["write_en"] = ? signal_out["out"];
129 add_group["done"] = ? result_cell["done"];
130 ));
131 parent.enable_next(&add_group);
132 }
133 Ir::Mul(result, lhs, rhs) => {
134 let lhs_cell = self.find_operand_cell(component, lhs);
135 let rhs_cell = self.find_operand_cell(component, rhs);
136 let result_cell = component.new_reg(result.to_string(), 64);
137 let mult =
138 component.new_prim("mult", "std_mult_pipe", vec![64]);
139 let mult_group = component.add_group("multiply");
140 mult_group.extend(build_assignments_2!(component;
141 mult["left"] = ? lhs_cell["out"];
142 mult["right"] = ? rhs_cell["out"];
143 mult["go"] = ? signal_out["out"];
144 result_cell["in"] = ? mult["out"];
145 result_cell["write_en"] = ? mult["done"];
146 mult_group["done"] = ? result_cell["done"];
147 ));
148 parent.enable_next(&mult_group);
149 }
150 Ir::Assign(result, value) => {
151 let value_cell = self.find_operand_cell(component, value);
152 if value_cell.kind.is_memory() {
153 component.alias_cell(result.to_string(), value_cell);
155 return;
156 }
157 let result_cell = component.new_reg(result.to_string(), 64);
158 let assign_group = component.add_group("assign");
159 assign_group.extend(build_assignments_2!(component;
160 result_cell["in"] = ? value_cell["out"];
161 result_cell["write_en"] = ? signal_out["out"];
162 assign_group["done"] = ? result_cell["done"];
163 ));
164 parent.enable_next(&assign_group);
165 }
166 Ir::GetParam(result) => {
167 let func = component.signature();
168 let result_cell = component.new_reg(result.to_string(), 64);
170 let get_param_group = component.add_group("get_param");
171 let param_port =
172 format!("arg{}", component.user_data_ref().param_env);
173 get_param_group.extend(build_assignments_2!(component;
174 result_cell["in"] = ? func[param_port];
175 result_cell["write_en"] = ? signal_out["out"];
176 get_param_group["done"] = ? result_cell["done"];
177 ));
178 parent.enable_next(&get_param_group);
179 component.user_data_mut().param_env += 1;
180 }
181 Ir::Return(value_opt) => {
182 if let Some(value) = value_opt {
186 let return_group = component.add_group("return");
187 let mut value_cell =
188 self.find_operand_cell(component, value);
189
190 if let Operand::Constant(_) = value {
194 let temp_cell = self.new_unnamed_reg(component);
195 return_group.extend(build_assignments_2!(component;
196 temp_cell["in"] = ? value_cell["out"];
197 ));
198 value_cell = temp_cell;
199 }
200
201 let ret_cell = component
202 .user_data_ref()
203 .ret_cell
204 .as_ref()
205 .cloned()
206 .unwrap();
207 return_group.extend(build_assignments_2!(component;
208 ret_cell["in"] = ? value_cell["out"];
209 ret_cell["write_en"] = ? signal_out["out"];
210 return_group["done"] = ? ret_cell["done"];
211 ));
212 parent.enable_next(&return_group);
213 } else {
214 }
216 }
217 Ir::LocalAlloc(result, size, count) => {
218 self.make_cell_for_array(component, *result, *size * 8, *count);
219 }
220 Ir::Store {
221 result,
222 value,
223 index
224 } => {
225 let store_group = component.add_group("store");
226 let result_cell = component.find(result.to_string());
227 let value_cell = self.find_operand_cell(component, value);
228 let index_cell = self.find_operand_cell(component, index);
229 assert!(
230 result_cell.kind.is_memory(),
231 "Ir::Store should take a memory result cell"
232 );
233 store_group.extend(build_assignments_2!(component;
234 result_cell["addr0"] = ? index_cell["out"];
235 result_cell["write_data"] = ? value_cell["out"];
236 result_cell["write_en"] = ? signal_out["out"];
237 store_group["done"] = ? result_cell["done"];
238 ));
239 parent.enable_next(&store_group);
240 }
241 Ir::Load {
242 result,
243 value,
244 index
245 } => {
246 let load_group = component.add_group("load");
247 let result_cell = component.new_reg(result.to_string(), 64);
248 let value_cell = self.find_operand_cell(component, value);
249 assert!(
250 value_cell.kind.is_memory(),
251 "Ir::Load should take a memory result cell"
252 );
253 let index_cell = self.find_operand_cell(component, index);
254 load_group.extend(build_assignments_2!(component;
255 value_cell["addr0"] = ? index_cell["out"];
256 result_cell["in"] = ? value_cell["read_data"];
257 result_cell["write_en"] = ? signal_out["out"];
258 load_group["done"] = ? result_cell["done"];
259 ));
260 parent.enable_next(&load_group);
261 }
262 Ir::Map {
263 result,
264 parallel_factor,
265 f,
266 input,
267 length
268 } => {
269 assert!(length % parallel_factor == 0, "parallel_factor must divide length. figure out a better place to assert this, probably in the type checker fix");
270 let index_cell = self.new_unnamed_reg(component);
271
272 let init_group = component.add_group("init");
273 let zero = component.constant(0, 64);
274 init_group.extend(build_assignments_2!(component;
275 index_cell["in"] = ? zero["out"];
276 index_cell["write_en"] = ? signal_out["out"];
277 init_group["done"] = ? index_cell["done"];
278 ));
279
280 let cond_group = component.add_comb_group("cond");
281 let array_size_cell = component.constant(*length as i64, 64);
282 let lt_cell = component.new_prim("lt", "std_lt", vec![64]);
283 cond_group.extend(build_assignments_2!(component;
284 lt_cell["left"] = ? index_cell["out"];
285 lt_cell["right"] = ? array_size_cell["out"];
286 ));
287
288 let read_group = component.add_group("read");
289 let write_group = component.add_group("write");
290
291 let input_cell = self.find_operand_cell(component, input); assert!(
293 input_cell.kind.is_memory(),
294 "Ir::Map should take a memory input cell"
295 );
296 let result_cell = component.find(result.to_string());
297 assert!(
298 result_cell.kind.is_memory(),
299 "Ir::Map should take a memory result cell"
300 );
301 let (_, call_cell) = self.cell_for_call(component, f, true);
302
303 read_group.extend(build_assignments_2!(component;
304 input_cell["addr0"] = ? index_cell["out"];
305 call_cell["arg0"] = ? input_cell["read_data"];
306 call_cell["go"] = ? signal_out["out"];
307 read_group["done"] = ? call_cell["done"];
308 ));
309
310 write_group.extend(build_assignments_2!(component;
311 result_cell["addr0"] = ? index_cell["out"];
312 result_cell["write_data"] = ? call_cell["ret"];
313 result_cell["write_en"] = ? call_cell["done"];
314 write_group["done"] = ? result_cell["done"];
315 ));
316
317 let incr_group = component.add_group("incr");
318 let adder = component.new_prim("adder", "std_add", vec![64]);
319 let one = component.constant(1, 64);
320 incr_group.extend(build_assignments_2!(component;
321 adder["left"] = ? index_cell["out"];
322 adder["right"] = ? one["out"];
323 index_cell["in"] = ? adder["out"];
324 index_cell["write_en"] = ? signal_out["out"];
325 incr_group["done"] = ? index_cell["done"];
326 ));
327
328 parent.enable_next(&init_group);
329 parent.while_(lt_cell.get("out"), Some(cond_group), |s| {
330 s.enable_next(&read_group);
331 s.enable_next(&write_group);
332 s.enable_next(&incr_group);
333 });
334 }
335 Ir::Call(result_opt, func_name, args) => {
336 let (_, call_cell) =
337 self.cell_for_call(component, func_name, false);
338 let call_group = component.add_group("call");
339 for (i, arg) in args.iter().enumerate() {
340 let arg_port =
341 self.find_operand_cell(component, arg).get("out");
342 call_group.add(component.with_calyx_builder(|b| {
343 b.build_assignment(
344 call_cell.get(&format!("arg{}", i)),
345 arg_port,
346 calyx_ir::Guard::True
347 )
348 }));
349 }
350 call_group.extend(build_assignments_2!(component;
351 call_cell["go"] = ? signal_out["out"];
352 call_group["done"] = ? call_cell["done"];
353 ));
354 parent.enable_next(&call_group);
355
356 if let Some(result) = result_opt {
357 let use_call_group = component.add_group("use_call");
358 let result_cell = component.new_reg(result.to_string(), 64);
359 use_call_group.extend(build_assignments_2!(component;
360 result_cell["in"] = ? call_cell["ret"];
361 result_cell["write_en"] = ? signal_out["out"];
362 use_call_group["done"] = ? result_cell["done"];
363 ));
364 parent.enable_next(&use_call_group);
365 }
366 }
367 }
368 }
369
370 fn emit_block(
371 &self, component: &mut CalyxComponent<FunctionContext>,
372 parent: &mut CalyxControl<Sequential>, block: BasicBlockCell
373 ) {
374 parent.seq(|s| {
375 for ir in block.as_ref().into_iter() {
376 self.emit_ir(component, s, ir);
377 }
378 });
379 }
380
381 fn emit_func(
382 &mut self, label: &Label, _args: &[Type], ret: &Type, _is_pure: bool,
383 cfg: &ControlFlowGraph
384 ) {
385 let mut component: CalyxComponent<FunctionContext> =
386 self.builder.start_component(label.name.mangle().clone());
387
388 if *ret != Type::Unit {
389 let func = component.signature();
390 let ret_cell =
391 component.new_unnamed_cell(builder::CalyxCellKind::Register {
392 size: ret.size() * 8
393 });
394 component.user_data_mut().ret_cell = Some(ret_cell.clone());
395 let always = build_assignments_2!(component;
396 func["ret"] = ? ret_cell["out"];
397 )
398 .to_vec();
399 component.with_calyx_builder(|b| {
400 b.add_continuous_assignments(always);
401 });
402 }
403
404 assert_eq!(1, cfg.size(), "CalyxBackend requires structured IR only in the entry block, but other blocks were found in the CFG");
408 let mut root_control = CalyxControl::default();
409 self.emit_block(&mut component, &mut root_control, cfg.entry());
410 *component.control() = root_control;
411
412 finish_component!(self.builder, component);
413 }
414}
415
416pub struct CalyxBackendInput {
417 pub lib_path: PathBuf
418}
419
420impl PulsarBackend for CalyxBackend {
421 type InitInput = CalyxBackendInput;
422 type Error = calyx_utils::Error;
423
424 fn new(input: Self::InitInput) -> Self {
425 let mut prelude_file_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
426 prelude_file_path.push("resources");
427 prelude_file_path.push("prelude.futil");
428
429 Self {
430 builder: CalyxBuilder::new(
431 Some(prelude_file_path),
432 input.lib_path,
433 None,
434 "_".into()
435 )
436 }
437 }
438
439 fn run(
440 mut self, code: Vec<GeneratedTopLevel>, output: Output
441 ) -> Result<(), Self::Error> {
442 for generated_top_level in &code {
445 match generated_top_level {
446 GeneratedTopLevel::Function {
447 label,
448 args,
449 ret,
450 is_pure: _,
451 cfg: _
452 } => {
453 self.register_func(label, args, ret);
454 }
455 }
456 }
457 for generated_top_level in &code {
459 match generated_top_level {
460 GeneratedTopLevel::Function {
461 label,
462 args,
463 ret,
464 is_pure,
465 cfg
466 } => self.emit_func(label, args, ret, *is_pure, cfg)
467 }
468 }
469
470 let mut builder = CalyxBuilder::dummy();
472 std::mem::swap(&mut builder, &mut self.builder);
473 let mut calyx_ctx = builder.finalize();
474
475 calyx_ir::Printer::write_context(&calyx_ctx, false, &mut stderr())
477 .unwrap();
478
479 let pm = calyx_opt::pass_manager::PassManager::default_passes()?;
481 let backend_conf = calyx_ir::BackendConf {
482 synthesis_mode: false,
483 enable_verification: false,
484 flat_assign: true,
485 emit_primitive_extmodules: false
486 };
487 calyx_ctx.bc = backend_conf;
488 pm.execute_plan(
489 &mut calyx_ctx,
490 &["all".to_string()],
491 &["canonicalize".to_string()],
492 false
493 )?;
494
495 let backend = calyx_backend::VerilogBackend;
497 backend.run(calyx_ctx, output.into())?;
498 Ok(())
499 }
500}
501
502impl From<Output> for calyx_utils::OutputFile {
503 fn from(output: Output) -> Self {
504 match output {
505 Output::Stdout => calyx_utils::OutputFile::Stdout,
506 Output::Stderr => calyx_utils::OutputFile::Stderr,
507 Output::File(path) => calyx_utils::OutputFile::File(path)
508 }
509 }
510}