calyx_opt/passes/
synthesis_papercut.rs1use crate::analysis::GraphAnalysis;
2use crate::traversal::{Action, Named, VisResult, Visitor};
3use calyx_ir::{self as ir, LibrarySignatures};
4use calyx_utils::Error;
5use std::collections::HashSet;
6
7const READ_PORT: &str = "read_data";
8const WRITE_PORT: &str = "write_data";
9
10pub struct SynthesisPapercut {
14 memories: HashSet<ir::Id>,
16}
17
18impl Default for SynthesisPapercut {
19 fn default() -> Self {
20 let memories =
21 ["comb_mem_d1", "comb_mem_d2", "comb_mem_d3", "comb_mem_d4"]
22 .iter()
23 .map(|&mem| mem.into())
24 .collect();
25 SynthesisPapercut { memories }
26 }
27}
28
29impl Named for SynthesisPapercut {
30 fn name() -> &'static str {
31 "synthesis-papercut"
32 }
33
34 fn description() -> &'static str {
35 "Detect common problems when targeting synthesis backends"
36 }
37}
38
39impl Visitor for SynthesisPapercut {
40 fn start(
41 &mut self,
42 comp: &mut ir::Component,
43 _ctx: &LibrarySignatures,
44 _comps: &[ir::Component],
45 ) -> VisResult {
46 let memory_cells = comp
48 .cells
49 .iter()
50 .filter_map(|cell| {
51 let cell = &cell.borrow();
52 if let Some(ref parent) = cell.type_name() {
53 if self.memories.contains(parent) {
54 let has_external =
55 cell.get_attribute(ir::BoolAttr::External);
56 if has_external.is_none() {
57 return Some(cell.name());
58 }
59 }
60 }
61 None
62 })
63 .collect::<HashSet<_>>();
64
65 if memory_cells.is_empty() {
67 return Ok(Action::Stop);
68 }
69
70 let has_mem_parent =
71 |p: &ir::Port| memory_cells.contains(&p.get_parent_name());
72 let analysis =
73 GraphAnalysis::from(&*comp).edge_induced_subgraph(|p1, p2| {
74 has_mem_parent(p1) || has_mem_parent(p2)
75 });
76
77 for mem in memory_cells {
78 let cell = comp.find_cell(mem).unwrap();
79 let read_port = cell.borrow().get(READ_PORT);
80 if analysis.reads_from(&read_port.borrow()).next().is_none() {
81 return Err(Error::papercut(
82 format!(
83 "Only writes performed on memory `{mem}'. Synthesis tools will remove this memory. Add @external to cell to turn this into an interface memory.",
84 ),
85 ));
86 }
87 let write_port = cell.borrow().get(WRITE_PORT);
88 if analysis.writes_to(&write_port.borrow()).next().is_none() {
89 return Err(Error::papercut(
90 format!(
91 "Only reads performed on memory `{mem}'. Synthesis tools will remove this memory. Add @external to cell to turn this into an interface memory.",
92 ),
93 ));
94 }
95 }
96 Ok(Action::Stop)
97 }
98}