1use calyx_ir::RRC;
4use pulsar_utils::environment::Environment;
5use std::{
6 collections::HashMap, fmt::Display, marker::PhantomData, path::PathBuf
7};
8
9pub mod macros;
10
11#[derive(Clone, PartialEq, Eq)]
13pub enum CalyxCellKind {
14 Register { size: usize },
16
17 CombMemoryD1 {
21 size: usize,
22 length: usize,
23 address_bits: usize
24 },
25
26 Primitive { name: String, params: Vec<u64> },
28
29 GoDoneComponent { component: String },
32
33 Constant { width: usize }
35}
36
37impl CalyxCellKind {
38 pub fn is_primitive(&self) -> bool {
42 matches!(
43 self,
44 Self::Register { size: _ }
45 | Self::CombMemoryD1 {
46 size: _,
47 length: _,
48 address_bits: _
49 }
50 | Self::Primitive { name: _, params: _ }
51 )
52 }
53
54 pub fn is_memory(&self) -> bool {
56 matches!(
57 self,
58 Self::CombMemoryD1 {
59 size: _,
60 length: _,
61 address_bits: _
62 }
63 )
64 }
65
66 pub(crate) fn primitive_params(&self) -> Vec<u64> {
70 match &self {
71 CalyxCellKind::Register { size } => vec![*size as u64],
72 CalyxCellKind::CombMemoryD1 {
73 size,
74 length,
75 address_bits
76 } => vec![*size as u64, *length as u64, *address_bits as u64],
77 CalyxCellKind::Primitive { name: _, params } => params.clone(),
78 CalyxCellKind::GoDoneComponent { component: _ } => {
79 panic!("Cell not a primitive")
80 }
81 CalyxCellKind::Constant { width } => vec![*width as u64]
82 }
83 }
84}
85
86impl Display for CalyxCellKind {
87 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88 match &self {
89 Self::Register { size: _ } => "std_reg",
90 Self::CombMemoryD1 {
91 size: _,
92 length: _,
93 address_bits: _
94 } => "comb_mem_d1",
95 Self::Primitive { name, params: _ } => name,
96 Self::GoDoneComponent { component } => component,
97 Self::Constant { width: _ } => "std_const"
98 }
99 .fmt(f)
100 }
101}
102
103pub type CalyxPort = RRC<calyx_ir::Port>;
105
106#[derive(Clone)]
109pub struct CalyxCell {
110 pub kind: CalyxCellKind,
111 pub value: RRC<calyx_ir::Cell>
112}
113
114impl CalyxCell {
115 pub fn get(&self, port: &str) -> CalyxPort {
117 self.value.borrow().get(port)
118 }
119}
120
121pub trait CalyxAssignmentContainer {
123 type AssignmentType;
124
125 fn add(&self, assignment: calyx_ir::Assignment<Self::AssignmentType>);
127
128 fn extend<
130 I: IntoIterator<Item = calyx_ir::Assignment<Self::AssignmentType>>
131 >(
132 &self, assignments: I
133 ) {
134 assignments.into_iter().for_each(|a| self.add(a));
135 }
136}
137
138pub struct CalyxGroup {
140 pub value: RRC<calyx_ir::Group>
141}
142
143impl CalyxAssignmentContainer for CalyxGroup {
144 type AssignmentType = calyx_ir::Nothing;
145
146 fn add(&self, assignment: calyx_ir::Assignment<Self::AssignmentType>) {
147 self.value.borrow_mut().assignments.push(assignment);
148 }
149}
150
151pub struct CalyxCombGroup {
153 pub value: RRC<calyx_ir::CombGroup>
154}
155
156impl CalyxAssignmentContainer for CalyxCombGroup {
157 type AssignmentType = calyx_ir::Nothing;
158
159 fn add(&self, assignment: calyx_ir::Assignment<Self::AssignmentType>) {
160 self.value.borrow_mut().assignments.push(assignment);
161 }
162}
163
164pub trait CalyxControlType {}
166
167pub struct Sequential;
169impl CalyxControlType for Sequential {}
170
171pub struct Parallel;
173impl CalyxControlType for Parallel {}
174
175pub struct CalyxControl<T: CalyxControlType> {
177 children: Vec<calyx_ir::Control>,
178 phantom: PhantomData<T>
179}
180
181impl<T: CalyxControlType> CalyxControl<T> {
182 pub fn seq<F>(&mut self, f: F)
199 where
200 F: FnOnce(&mut CalyxControl<Sequential>) {
201 let mut child = CalyxControl::<Sequential>::default();
202 f(&mut child);
203 self.children.push(calyx_ir::Control::seq(child.children));
204 }
205
206 pub fn par<F>(&mut self, f: F)
208 where
209 F: FnOnce(&mut CalyxControl<Parallel>) {
210 let mut child = CalyxControl::<Parallel>::default();
211 f(&mut child);
212 self.children.push(calyx_ir::Control::par(child.children));
213 }
214
215 pub fn if_<F>(
217 &mut self, port: CalyxPort, cond: Option<CalyxCombGroup>, true_f: F,
218 false_f: F
219 ) where
220 F: FnOnce(&mut CalyxControl<Sequential>) {
221 let mut true_branch = CalyxControl::<Sequential>::default();
222 let mut false_branch = CalyxControl::<Sequential>::default();
223 true_f(&mut true_branch);
224 false_f(&mut false_branch);
225 self.children.push(calyx_ir::Control::if_(
226 port,
227 cond.map(|cond| cond.value),
228 Box::new(true_branch.to_control()),
229 Box::new(false_branch.to_control())
230 ));
231 }
232
233 pub fn while_<F>(
235 &mut self, port: CalyxPort, cond: Option<CalyxCombGroup>, f: F
236 ) where
237 F: FnOnce(&mut CalyxControl<Sequential>) {
238 let mut body = CalyxControl::<Sequential>::default();
239 f(&mut body);
240 self.children.push(calyx_ir::Control::while_(
241 port,
242 cond.map(|cond| cond.value),
243 Box::new(body.to_control())
244 ));
245 }
246
247 }
249
250impl<T: CalyxControlType> Default for CalyxControl<T> {
251 fn default() -> Self {
252 Self {
253 children: vec![],
254 phantom: PhantomData
255 }
256 }
257}
258
259impl CalyxControl<Sequential> {
260 pub fn enable_next(&mut self, group: &CalyxGroup) {
262 self.children
263 .push(calyx_ir::Control::enable(group.value.clone()));
264 }
265
266 pub fn to_control(self) -> calyx_ir::Control {
268 if self.children.is_empty() {
269 calyx_ir::Control::empty()
270 } else {
271 calyx_ir::Control::seq(self.children)
272 }
273 }
274}
275
276impl CalyxControl<Parallel> {
277 pub fn enable(&mut self, group: &CalyxGroup) {
279 self.children
280 .push(calyx_ir::Control::enable(group.value.clone()));
281 }
282
283 pub fn to_control(self) -> calyx_ir::Control {
285 if self.children.is_empty() {
286 calyx_ir::Control::empty()
287 } else {
288 calyx_ir::Control::par(self.children)
289 }
290 }
291}
292
293pub struct CalyxComponent<'a, ComponentData: Default> {
303 ext_sigs: &'a HashMap<String, Vec<calyx_ir::PortDef<u64>>>,
304 lib_sig: &'a calyx_ir::LibrarySignatures,
305 env: Environment<String, CalyxCell>,
306 component: calyx_ir::Component,
307 cell_name_prefix: String,
308 unique_counter: usize,
309 user_data: ComponentData,
310 control_builder: CalyxControl<Sequential>
311}
312
313impl<'a, ComponentData: Default> CalyxComponent<'a, ComponentData> {
314 fn new(
315 component: calyx_ir::Component, cell_name_prefix: String,
316 ext_sigs: &'a HashMap<String, Vec<calyx_ir::PortDef<u64>>>,
317 lib_sig: &'a calyx_ir::LibrarySignatures
318 ) -> Self {
319 Self {
320 ext_sigs,
321 lib_sig,
322 env: Environment::new(),
323 component,
324 cell_name_prefix,
325 unique_counter: 0,
326 user_data: ComponentData::default(),
327 control_builder: CalyxControl::default()
328 }
329 }
330
331 pub fn user_data_ref(&self) -> &ComponentData {
333 &self.user_data
334 }
335
336 pub fn user_data_mut(&mut self) -> &mut ComponentData {
338 &mut self.user_data
339 }
340
341 pub fn signature(&mut self) -> CalyxCell {
343 CalyxCell {
344 kind: CalyxCellKind::GoDoneComponent {
345 component: self.component.name.to_string()
346 },
347 value: self.component.signature.clone()
348 }
349 }
350
351 pub fn control(&mut self) -> &mut CalyxControl<Sequential> {
353 &mut self.control_builder
354 }
355
356 pub fn with_calyx_builder<F, T>(&mut self, f: F) -> T
358 where
359 F: FnOnce(&mut calyx_ir::Builder) -> T {
360 let mut ir_builder =
364 calyx_ir::Builder::new(&mut self.component, self.lib_sig)
365 .not_generated();
366 f(&mut ir_builder)
367 }
368
369 pub fn new_reg(&mut self, name: String, width: usize) -> CalyxCell {
373 let mut bind_name = self.cell_name_prefix.clone();
374 bind_name.push_str(&name);
375 self.create_cell(bind_name, CalyxCellKind::Register { size: width })
376 }
377
378 pub fn named_mem(
382 &mut self, name: String, cell_size: usize, length: usize,
383 address_bits: usize
384 ) -> CalyxCell {
385 let mut bind_name = self.cell_name_prefix.clone();
386 bind_name.push_str(&name);
387 self.create_cell(
388 bind_name,
389 CalyxCellKind::CombMemoryD1 {
390 size: cell_size,
391 length,
392 address_bits
393 }
394 )
395 }
396
397 pub fn new_prim(
404 &mut self, name: &str, prim: &str, params: Vec<u64>
405 ) -> CalyxCell {
406 self.create_cell(
407 name.into(),
408 CalyxCellKind::Primitive {
409 name: prim.into(),
410 params
411 }
412 )
413 }
414
415 pub fn component_cell(
419 &mut self, prefix: String, component: String, instantiate_new: bool
420 ) -> (String, CalyxCell) {
421 let cell_name = if instantiate_new {
422 format!("{}{}", prefix, self.get_unique_number())
423 } else {
424 prefix
425 };
426 let cell = CalyxCell {
427 kind: CalyxCellKind::GoDoneComponent {
428 component: component.clone()
429 },
430 value: self._create_component_cell(cell_name.clone(), component)
431 };
432 (cell_name, cell)
433 }
434
435 pub fn new_unnamed_cell(&mut self, kind: CalyxCellKind) -> CalyxCell {
437 let cell_name = format!("t{}", self.get_unique_number());
438 self.create_cell(cell_name, kind)
439 }
440
441 pub fn constant(&mut self, value: i64, width: usize) -> CalyxCell {
443 CalyxCell {
444 kind: CalyxCellKind::Constant { width },
445 value: self.with_calyx_builder(|b| {
446 b.add_constant(value as u64, width as u64)
447 })
448 }
449 }
450
451 pub fn signal_out(&mut self) -> CalyxCell {
453 self.constant(1, 1)
454 }
455
456 pub fn alias_cell(&mut self, name: String, cell: CalyxCell) {
460 assert!(self
461 .env
462 .bind(format!("{}{}", self.cell_name_prefix, name), cell)
463 .is_none());
464 }
465
466 pub fn find(&mut self, name: String) -> CalyxCell {
470 self.env
471 .find(format!("{}{}", self.cell_name_prefix, name))
472 .expect("Did not find cell in component environment")
473 .clone()
474 }
475
476 pub fn begin_scope(&mut self) {
478 self.env.push();
479 }
480
481 pub fn end_scope(&mut self) -> bool {
483 self.env.pop()
484 }
485
486 pub fn add_group(&mut self, prefix: &str) -> CalyxGroup {
488 CalyxGroup {
489 value: self.with_calyx_builder(|b| b.add_group(prefix))
490 }
491 }
492
493 pub fn add_comb_group(&mut self, prefix: &str) -> CalyxCombGroup {
495 CalyxCombGroup {
496 value: self.with_calyx_builder(|b| b.add_comb_group(prefix))
497 }
498 }
499
500 pub fn finalize(self) -> calyx_ir::Component {
502 *self.component.control.borrow_mut() =
503 self.control_builder.to_control();
504 self.component
505 }
506
507 fn create_cell(&mut self, key: String, kind: CalyxCellKind) -> CalyxCell {
511 let calyx_cell = if kind.is_primitive() {
512 self._create_primitive(
513 key.clone(),
514 kind.to_string(),
515 kind.primitive_params()
516 )
517 } else if let CalyxCellKind::GoDoneComponent { component } = &kind {
518 self._create_component_cell(key.clone(), component.clone())
519 } else {
520 panic!("unknown cell kind")
521 };
522 let cell = CalyxCell {
523 kind,
524 value: calyx_cell
525 };
526 self.env.bind(key, cell.clone());
527 cell
528 }
529
530 fn get_unique_number(&mut self) -> usize {
533 let result = self.unique_counter;
534 self.unique_counter += 1;
535 result
536 }
537
538 fn _create_primitive(
540 &mut self, name: String, primitive: String, params: Vec<u64>
541 ) -> RRC<calyx_ir::Cell> {
542 self.with_calyx_builder(|b| b.add_primitive(name, primitive, ¶ms))
543 }
544
545 fn _create_component_cell(
547 &mut self, name: String, component: String
548 ) -> RRC<calyx_ir::Cell> {
549 let mut port_defs = self.ext_sigs.get(&component).unwrap().clone();
550
551 let mut go_attr = calyx_ir::Attributes::default();
552 go_attr.insert(calyx_ir::Attribute::Num(calyx_ir::NumAttr::Go), 1);
553 port_defs.push(calyx_ir::PortDef::new(
554 "go",
555 1,
556 calyx_ir::Direction::Input,
557 go_attr
558 ));
559
560 let mut done_attr = calyx_ir::Attributes::default();
561 done_attr.insert(calyx_ir::Attribute::Num(calyx_ir::NumAttr::Done), 1);
562 port_defs.push(calyx_ir::PortDef::new(
563 "done",
564 1,
565 calyx_ir::Direction::Output,
566 done_attr
567 ));
568
569 let mut clk_attr = calyx_ir::Attributes::default();
570 clk_attr.insert(calyx_ir::Attribute::Bool(calyx_ir::BoolAttr::Clk), 1);
571 port_defs.push(calyx_ir::PortDef::new(
572 "clk",
573 1,
574 calyx_ir::Direction::Input,
575 clk_attr
576 ));
577
578 let mut reset_attr = calyx_ir::Attributes::default();
579 reset_attr
580 .insert(calyx_ir::Attribute::Bool(calyx_ir::BoolAttr::Reset), 1);
581 port_defs.push(calyx_ir::PortDef::new(
582 "reset",
583 1,
584 calyx_ir::Direction::Input,
585 reset_attr
586 ));
587
588 let cell = self._cell_from_signature(
589 name.clone().into(),
590 calyx_ir::CellType::Component {
591 name: component.clone().into()
592 },
593 port_defs
594 );
595 self.component.cells.add(cell.clone());
596 cell
597 }
598
599 fn _cell_from_signature(
601 &self, name: calyx_ir::Id, typ: calyx_ir::CellType,
602 ports: Vec<calyx_ir::PortDef<u64>>
603 ) -> RRC<calyx_ir::Cell> {
604 let cell = calyx_ir::rrc(calyx_ir::Cell::new(name, typ));
605 ports.into_iter().for_each(|pd| {
606 let port = calyx_ir::rrc(calyx_ir::Port {
607 name: pd.name(),
608 width: pd.width,
609 direction: pd.direction,
610 parent: calyx_ir::PortParent::Cell(calyx_ir::WRC::from(&cell)),
611 attributes: pd.attributes
612 });
613 cell.borrow_mut().ports.push(port);
614 });
615 cell
616 }
617}
618
619pub struct CalyxBuilder {
622 ctx: calyx_ir::Context,
624
625 sigs: HashMap<String, Vec<calyx_ir::PortDef<u64>>>,
627
628 cell_name_prefix: String
630}
631
632impl CalyxBuilder {
633 pub fn new(
652 prelude: Option<PathBuf>, lib_path: PathBuf,
653 entrypoint: Option<String>, cell_name_prefix: String
654 ) -> Self {
655 assert!(!cell_name_prefix.is_empty());
656
657 let ws =
660 calyx_frontend::Workspace::construct(&prelude, &lib_path).unwrap();
661 let ctx = calyx_ir::Context {
662 components: vec![],
663 lib: ws.lib,
664 entrypoint: entrypoint.unwrap_or("main".into()).into(),
665 bc: calyx_ir::BackendConf::default(),
666 extra_opts: vec![],
667 metadata: None
668 };
669
670 Self {
671 ctx,
672 sigs: HashMap::new(),
673 cell_name_prefix
674 }
675 }
676
677 pub fn dummy() -> Self {
679 Self {
680 ctx: calyx_ir::Context {
681 components: vec![],
682 lib: calyx_ir::LibrarySignatures::default(),
683 entrypoint: "".into(),
684 bc: calyx_ir::BackendConf::default(),
685 extra_opts: vec![],
686 metadata: None
687 },
688 sigs: HashMap::new(),
689 cell_name_prefix: "".into()
690 }
691 }
692
693 pub fn register_component(
696 &mut self, name: String, ports: Vec<calyx_ir::PortDef<u64>>
697 ) {
698 self.sigs.insert(name, ports);
699 }
700
701 pub fn start_component<ComponentData: Default>(
707 &self, name: String
708 ) -> CalyxComponent<ComponentData> {
709 CalyxComponent::new(
710 calyx_ir::Component::new(
711 name.clone(),
712 self.sigs
713 .get(&name)
714 .expect("Use `register_component` first")
715 .clone(),
716 true,
717 false,
718 None
719 ),
720 self.cell_name_prefix.clone(),
721 &self.sigs,
722 &self.ctx.lib
723 )
724 }
725
726 pub fn _finish_component(&mut self, component: calyx_ir::Component) {
728 self.ctx.components.push(component);
729 }
730
731 pub fn set_entrypoint(&mut self, entrypoint: String) {
736 assert!(self.sigs.contains_key(&entrypoint));
737 self.ctx.entrypoint = entrypoint.into();
738 }
739
740 pub fn finalize(self) -> calyx_ir::Context {
745 self.ctx
746 }
747}
748
749#[macro_export]
752macro_rules! finish_component {
753 ($builder:expr, $component:expr) => {
754 $builder._finish_component($component.finalize())
755 };
756}