1use crate::{self as ir, LibrarySignatures, Nothing, RRC, WRC};
4use calyx_frontend::BoolAttr;
5use calyx_utils::CalyxResult;
6use std::{cmp, rc::Rc};
7
8use super::{CellType, PortDef};
9
10pub struct Builder<'a> {
18 pub component: &'a mut ir::Component,
20 lib: &'a LibrarySignatures,
22 validate: bool,
25 generated: bool,
27}
28
29impl<'a> Builder<'a> {
30 pub fn new(
32 component: &'a mut ir::Component,
33 lib: &'a LibrarySignatures,
34 ) -> Self {
35 Self {
36 component,
37 lib,
38 validate: false,
39 generated: true,
41 }
42 }
43
44 pub fn validate(mut self) -> Self {
46 self.validate = true;
47 self
48 }
49
50 pub fn not_generated(mut self) -> Self {
52 self.generated = false;
53 self
54 }
55
56 pub fn add_continuous_assignments(
57 &mut self,
58 assigns: Vec<ir::Assignment<Nothing>>,
59 ) {
60 self.component.continuous_assignments.extend(assigns);
61 }
62
63 pub fn add_group<S>(&mut self, prefix: S) -> RRC<ir::Group>
67 where
68 S: Into<ir::Id>,
69 {
70 let prefix: ir::Id = prefix.into();
71 assert!(
72 prefix != "",
73 "Cannot construct group with empty name prefix"
74 );
75 let name = self.component.generate_name(prefix);
76
77 let group = ir::rrc(ir::Group::new(name));
79
80 for (name, width) in &[("go", 1), ("done", 1)] {
82 let hole = ir::rrc(ir::Port {
83 name: ir::Id::from(*name),
84 width: *width,
85 direction: ir::Direction::Inout,
86 parent: ir::PortParent::Group(WRC::from(&group)),
87 attributes: ir::Attributes::default(),
88 });
89 group.borrow_mut().holes.push(hole);
90 }
91
92 self.component.get_groups_mut().add(Rc::clone(&group));
94
95 group
96 }
97
98 pub fn add_static_group<S>(
102 &mut self,
103 prefix: S,
104 latency: u64,
105 ) -> RRC<ir::StaticGroup>
106 where
107 S: Into<ir::Id>,
108 {
109 let prefix: ir::Id = prefix.into();
110 assert!(
111 prefix != "",
112 "Cannot construct group with empty name prefix"
113 );
114 let name = self.component.generate_name(prefix);
115
116 let group = ir::rrc(ir::StaticGroup::new(name, latency));
118
119 let (name, width) = ("go", 1);
123 let hole = ir::rrc(ir::Port {
124 name: ir::Id::from(name),
125 width,
126 direction: ir::Direction::Inout,
127 parent: ir::PortParent::StaticGroup(WRC::from(&group)),
128 attributes: ir::Attributes::default(),
129 });
130 group.borrow_mut().holes.push(hole);
131
132 self.component
134 .get_static_groups_mut()
135 .add(Rc::clone(&group));
136
137 group
138 }
139
140 pub fn add_comb_group<S>(&mut self, prefix: S) -> RRC<ir::CombGroup>
142 where
143 S: Into<ir::Id> + ToString + Clone,
144 {
145 let name = self.component.generate_name(prefix);
146
147 let group = ir::rrc(ir::CombGroup {
149 name,
150 attributes: ir::Attributes::default(),
151 assignments: vec![],
152 });
153
154 self.component.comb_groups.add(Rc::clone(&group));
156
157 group
158 }
159
160 pub fn add_constant(&mut self, val: u64, width: u64) -> RRC<ir::Cell> {
164 assert!(
166 val < match width.cmp(&64) {
167 cmp::Ordering::Less => 1 << width,
168 cmp::Ordering::Equal => u64::MAX,
169 cmp::Ordering::Greater =>
170 panic!("Widths greater than 64 are not supported."),
171 },
172 "Constant value {} cannot fit in {} bits",
173 val,
174 width
175 );
176 let name = ir::Cell::constant_name(val, width);
177 if let Some(cell) = self.component.cells.find(name) {
180 return Rc::clone(&cell);
181 }
182
183 let cell = Self::cell_from_signature(
185 name,
186 ir::CellType::Constant { val, width },
187 vec![ir::PortDef::new(
188 ir::Id::from("out"),
189 width,
190 ir::Direction::Output,
191 ir::Attributes::default(),
192 )],
193 );
194
195 self.component.cells.add(Rc::clone(&cell));
197
198 cell
199 }
200
201 pub fn add_primitive<Pre, Prim>(
212 &mut self,
213 prefix: Pre,
214 primitive: Prim,
215 param_values: &[u64],
216 ) -> RRC<ir::Cell>
217 where
218 Pre: Into<ir::Id> + ToString + Clone,
219 Prim: Into<ir::Id>,
220 {
221 self.try_add_primitive(prefix, primitive, param_values)
222 .expect("failed to add primitive:")
223 }
224
225 pub fn try_add_primitive<Pre, Prim>(
227 &mut self,
228 prefix: Pre,
229 primitive: Prim,
230 param_values: &[u64],
231 ) -> CalyxResult<RRC<ir::Cell>>
232 where
233 Pre: Into<ir::Id> + ToString + Clone,
234 Prim: Into<ir::Id>,
235 {
236 let prim_id = primitive.into();
237 let prim = &self.lib.get_primitive(prim_id);
238 let (param_binding, ports) = prim.resolve(param_values)?;
239
240 let name = self.component.generate_name(prefix);
241 let cell = Self::cell_from_signature(
242 name,
243 ir::CellType::Primitive {
244 name: prim_id,
245 param_binding: Box::new(param_binding),
246 is_comb: prim.is_comb,
247 latency: prim.latency,
248 },
249 ports,
250 );
251 if self.generated {
252 cell.borrow_mut().add_attribute(BoolAttr::Generated, 1);
253 }
254 self.component.cells.add(Rc::clone(&cell));
255 Ok(cell)
256 }
257
258 pub fn add_component<Pre>(
261 &mut self,
262 prefix: Pre,
263 component: Pre,
264 sig: Vec<PortDef<u64>>,
265 ) -> RRC<ir::Cell>
266 where
267 Pre: Into<ir::Id> + ToString + Clone,
268 {
269 let name = self.component.generate_name(prefix);
270 let cell = Self::cell_from_signature(
271 name,
272 CellType::Component {
273 name: component.into(),
274 },
275 sig,
276 );
277 if self.generated {
278 cell.borrow_mut().add_attribute(BoolAttr::Generated, 1);
279 }
280 self.component.cells.add(Rc::clone(&cell));
281 cell
282 }
283
284 pub fn build_assignment<T>(
286 &self,
287 dst: RRC<ir::Port>,
288 src: RRC<ir::Port>,
289 guard: ir::Guard<T>,
290 ) -> ir::Assignment<T> {
291 if self.validate {
293 self.is_port_well_formed(&dst.borrow());
294 self.is_port_well_formed(&src.borrow());
295 guard
296 .all_ports()
297 .into_iter()
298 .for_each(|p| self.is_port_well_formed(&p.borrow()));
299 }
300 debug_assert!(
302 src.borrow().width == dst.borrow().width,
303 "Invalid assignment. `{}.{}' and `{}.{}' have different widths",
304 src.borrow().get_parent_name(),
305 src.borrow().name,
306 dst.borrow().get_parent_name(),
307 dst.borrow().name,
308 );
309 debug_assert!(
311 src.borrow().direction != ir::Direction::Input,
313 "Not an ouput port: {}.{}",
314 src.borrow().get_parent_name(),
315 src.borrow().name
316 );
317 debug_assert!(
318 dst.borrow().direction != ir::Direction::Output,
320 "Not an input port: {}.{}",
321 dst.borrow().get_parent_name(),
322 dst.borrow().name
323 );
324
325 ir::Assignment {
326 dst,
327 src,
328 guard: Box::new(guard),
329 attributes: ir::Attributes::default(),
330 }
331 }
332
333 fn is_port_well_formed(&self, port: &ir::Port) {
339 match &port.parent {
340 ir::PortParent::Cell(cell_wref) => {
341 let cell_ref = cell_wref.internal.upgrade().expect("Weak reference to port's parent cell points to nothing. This usually means that the Component did not retain a pointer to the Cell.");
342
343 let cell = &cell_ref.borrow();
344 self.component.find_cell(cell.name()).expect("Port's parent cell not present in the component. Add the cell to the component before using the Port.");
345 }
346 ir::PortParent::Group(group_wref) => {
347 let group_ref = group_wref.internal.upgrade().expect("Weak reference to hole's parent group points to nothing. This usually means that the Component did not retain a pointer to the Group.");
348
349 let group = &group_ref.borrow();
350 self.component.find_group(group.name()).expect("Hole's parent cell not present in the component. Add the group to the component before using the Hole.");
351 }
352 ir::PortParent::StaticGroup(group_wref) => {
353 let group_ref = group_wref.internal.upgrade().expect("Weak reference to hole's parent group points to nothing. This usually means that the Component did not retain a pointer to the Group.");
354
355 let group = &group_ref.borrow();
356 self.component.find_static_group(group.name()).expect("Hole's parent cell not present in the component. Add the static group to the component before using the Hole.");
357 }
358 };
359 }
360 pub(super) fn cell_from_signature(
363 name: ir::Id,
364 typ: ir::CellType,
365 ports: Vec<ir::PortDef<u64>>,
366 ) -> RRC<ir::Cell> {
367 let cell = ir::rrc(ir::Cell::new(name, typ));
368 ports.into_iter().for_each(|pd| {
369 let port = ir::rrc(ir::Port {
370 name: pd.name(),
371 width: pd.width,
372 direction: pd.direction,
373 parent: ir::PortParent::Cell(WRC::from(&cell)),
374 attributes: pd.attributes,
375 });
376 cell.borrow_mut().ports.push(port);
377 });
378 cell
379 }
380}