use calyx_ir::RRC;
use pulsar_utils::environment::Environment;
use std::{
collections::HashMap, fmt::Display, marker::PhantomData, path::PathBuf
};
pub mod macros;
#[derive(Clone, PartialEq, Eq)]
pub enum CalyxCellKind {
Register { size: usize },
CombMemoryD1 {
size: usize,
length: usize,
address_bits: usize
},
Primitive { name: String, params: Vec<u64> },
GoDoneComponent { component: String },
Constant { width: usize }
}
impl CalyxCellKind {
pub fn is_primitive(&self) -> bool {
matches!(
self,
Self::Register { size: _ }
| Self::CombMemoryD1 {
size: _,
length: _,
address_bits: _
}
| Self::Primitive { name: _, params: _ }
)
}
pub fn is_memory(&self) -> bool {
matches!(
self,
Self::CombMemoryD1 {
size: _,
length: _,
address_bits: _
}
)
}
pub(crate) fn primitive_params(&self) -> Vec<u64> {
match &self {
CalyxCellKind::Register { size } => vec![*size as u64],
CalyxCellKind::CombMemoryD1 {
size,
length,
address_bits
} => vec![*size as u64, *length as u64, *address_bits as u64],
CalyxCellKind::Primitive { name: _, params } => params.clone(),
CalyxCellKind::GoDoneComponent { component: _ } => {
panic!("Cell not a primitive")
}
CalyxCellKind::Constant { width } => vec![*width as u64]
}
}
}
impl Display for CalyxCellKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self {
Self::Register { size: _ } => "std_reg",
Self::CombMemoryD1 {
size: _,
length: _,
address_bits: _
} => "comb_mem_d1",
Self::Primitive { name, params: _ } => name,
Self::GoDoneComponent { component } => component,
Self::Constant { width: _ } => "std_const"
}
.fmt(f)
}
}
pub type CalyxPort = RRC<calyx_ir::Port>;
#[derive(Clone)]
pub struct CalyxCell {
pub kind: CalyxCellKind,
pub value: RRC<calyx_ir::Cell>
}
impl CalyxCell {
pub fn get(&self, port: &str) -> CalyxPort {
self.value.borrow().get(port)
}
}
pub trait CalyxAssignmentContainer {
type AssignmentType;
fn add(&self, assignment: calyx_ir::Assignment<Self::AssignmentType>);
fn extend<
I: IntoIterator<Item = calyx_ir::Assignment<Self::AssignmentType>>
>(
&self, assignments: I
) {
assignments.into_iter().for_each(|a| self.add(a));
}
}
pub struct CalyxGroup {
pub value: RRC<calyx_ir::Group>
}
impl CalyxAssignmentContainer for CalyxGroup {
type AssignmentType = calyx_ir::Nothing;
fn add(&self, assignment: calyx_ir::Assignment<Self::AssignmentType>) {
self.value.borrow_mut().assignments.push(assignment);
}
}
pub struct CalyxCombGroup {
pub value: RRC<calyx_ir::CombGroup>
}
impl CalyxAssignmentContainer for CalyxCombGroup {
type AssignmentType = calyx_ir::Nothing;
fn add(&self, assignment: calyx_ir::Assignment<Self::AssignmentType>) {
self.value.borrow_mut().assignments.push(assignment);
}
}
pub trait CalyxControlType {}
pub struct Sequential;
impl CalyxControlType for Sequential {}
pub struct Parallel;
impl CalyxControlType for Parallel {}
pub struct CalyxControl<T: CalyxControlType> {
children: Vec<calyx_ir::Control>,
phantom: PhantomData<T>
}
impl<T: CalyxControlType> CalyxControl<T> {
pub fn seq<F>(&mut self, f: F)
where
F: FnOnce(&mut CalyxControl<Sequential>) {
let mut child = CalyxControl::<Sequential>::default();
f(&mut child);
self.children.push(calyx_ir::Control::seq(child.children));
}
pub fn par<F>(&mut self, f: F)
where
F: FnOnce(&mut CalyxControl<Parallel>) {
let mut child = CalyxControl::<Parallel>::default();
f(&mut child);
self.children.push(calyx_ir::Control::par(child.children));
}
pub fn if_<F>(
&mut self, port: CalyxPort, cond: Option<CalyxCombGroup>, true_f: F,
false_f: F
) where
F: FnOnce(&mut CalyxControl<Sequential>) {
let mut true_branch = CalyxControl::<Sequential>::default();
let mut false_branch = CalyxControl::<Sequential>::default();
true_f(&mut true_branch);
false_f(&mut false_branch);
self.children.push(calyx_ir::Control::if_(
port,
cond.map(|cond| cond.value),
Box::new(true_branch.to_control()),
Box::new(false_branch.to_control())
));
}
pub fn while_<F>(
&mut self, port: CalyxPort, cond: Option<CalyxCombGroup>, f: F
) where
F: FnOnce(&mut CalyxControl<Sequential>) {
let mut body = CalyxControl::<Sequential>::default();
f(&mut body);
self.children.push(calyx_ir::Control::while_(
port,
cond.map(|cond| cond.value),
Box::new(body.to_control())
));
}
}
impl<T: CalyxControlType> Default for CalyxControl<T> {
fn default() -> Self {
Self {
children: vec![],
phantom: PhantomData
}
}
}
impl CalyxControl<Sequential> {
pub fn enable_next(&mut self, group: &CalyxGroup) {
self.children
.push(calyx_ir::Control::enable(group.value.clone()));
}
pub fn to_control(self) -> calyx_ir::Control {
if self.children.is_empty() {
calyx_ir::Control::empty()
} else {
calyx_ir::Control::seq(self.children)
}
}
}
impl CalyxControl<Parallel> {
pub fn enable(&mut self, group: &CalyxGroup) {
self.children
.push(calyx_ir::Control::enable(group.value.clone()));
}
pub fn to_control(self) -> calyx_ir::Control {
if self.children.is_empty() {
calyx_ir::Control::empty()
} else {
calyx_ir::Control::par(self.children)
}
}
}
pub struct CalyxComponent<'a, ComponentData: Default> {
ext_sigs: &'a HashMap<String, Vec<calyx_ir::PortDef<u64>>>,
lib_sig: &'a calyx_ir::LibrarySignatures,
env: Environment<String, CalyxCell>,
component: calyx_ir::Component,
cell_name_prefix: String,
unique_counter: usize,
user_data: ComponentData,
control_builder: CalyxControl<Sequential>
}
impl<'a, ComponentData: Default> CalyxComponent<'a, ComponentData> {
fn new(
component: calyx_ir::Component, cell_name_prefix: String,
ext_sigs: &'a HashMap<String, Vec<calyx_ir::PortDef<u64>>>,
lib_sig: &'a calyx_ir::LibrarySignatures
) -> Self {
Self {
ext_sigs,
lib_sig,
env: Environment::new(),
component,
cell_name_prefix,
unique_counter: 0,
user_data: ComponentData::default(),
control_builder: CalyxControl::default()
}
}
pub fn user_data_ref(&self) -> &ComponentData {
&self.user_data
}
pub fn user_data_mut(&mut self) -> &mut ComponentData {
&mut self.user_data
}
pub fn signature(&mut self) -> CalyxCell {
CalyxCell {
kind: CalyxCellKind::GoDoneComponent {
component: self.component.name.to_string()
},
value: self.component.signature.clone()
}
}
pub fn control(&mut self) -> &mut CalyxControl<Sequential> {
&mut self.control_builder
}
pub fn with_calyx_builder<F, T>(&mut self, f: F) -> T
where
F: FnOnce(&mut calyx_ir::Builder) -> T {
let mut ir_builder =
calyx_ir::Builder::new(&mut self.component, self.lib_sig)
.not_generated();
f(&mut ir_builder)
}
pub fn new_reg(&mut self, name: String, width: usize) -> CalyxCell {
let mut bind_name = self.cell_name_prefix.clone();
bind_name.push_str(&name);
self.create_cell(bind_name, CalyxCellKind::Register { size: width })
}
pub fn named_mem(
&mut self, name: String, cell_size: usize, length: usize,
address_bits: usize
) -> CalyxCell {
let mut bind_name = self.cell_name_prefix.clone();
bind_name.push_str(&name);
self.create_cell(
bind_name,
CalyxCellKind::CombMemoryD1 {
size: cell_size,
length,
address_bits
}
)
}
pub fn new_prim(
&mut self, name: &str, prim: &str, params: Vec<u64>
) -> CalyxCell {
self.create_cell(
name.into(),
CalyxCellKind::Primitive {
name: prim.into(),
params
}
)
}
pub fn component_cell(
&mut self, prefix: String, component: String, instantiate_new: bool
) -> (String, CalyxCell) {
let cell_name = if instantiate_new {
format!("{}{}", prefix, self.get_unique_number())
} else {
prefix
};
let cell = CalyxCell {
kind: CalyxCellKind::GoDoneComponent {
component: component.clone()
},
value: self._create_component_cell(cell_name.clone(), component)
};
(cell_name, cell)
}
pub fn new_unnamed_cell(&mut self, kind: CalyxCellKind) -> CalyxCell {
let cell_name = format!("t{}", self.get_unique_number());
self.create_cell(cell_name, kind)
}
pub fn constant(&mut self, value: i64, width: usize) -> CalyxCell {
CalyxCell {
kind: CalyxCellKind::Constant { width },
value: self.with_calyx_builder(|b| {
b.add_constant(value as u64, width as u64)
})
}
}
pub fn signal_out(&mut self) -> CalyxCell {
self.constant(1, 1)
}
pub fn alias_cell(&mut self, name: String, cell: CalyxCell) {
assert!(self
.env
.bind(format!("{}{}", self.cell_name_prefix, name), cell)
.is_none());
}
pub fn find(&mut self, name: String) -> CalyxCell {
self.env
.find(format!("{}{}", self.cell_name_prefix, name))
.expect("Did not find cell in component environment")
.clone()
}
pub fn begin_scope(&mut self) {
self.env.push();
}
pub fn end_scope(&mut self) -> bool {
self.env.pop()
}
pub fn add_group(&mut self, prefix: &str) -> CalyxGroup {
CalyxGroup {
value: self.with_calyx_builder(|b| b.add_group(prefix))
}
}
pub fn add_comb_group(&mut self, prefix: &str) -> CalyxCombGroup {
CalyxCombGroup {
value: self.with_calyx_builder(|b| b.add_comb_group(prefix))
}
}
pub fn finalize(self) -> calyx_ir::Component {
*self.component.control.borrow_mut() =
self.control_builder.to_control();
self.component
}
fn create_cell(&mut self, key: String, kind: CalyxCellKind) -> CalyxCell {
let calyx_cell = if kind.is_primitive() {
self._create_primitive(
key.clone(),
kind.to_string(),
kind.primitive_params()
)
} else if let CalyxCellKind::GoDoneComponent { component } = &kind {
self._create_component_cell(key.clone(), component.clone())
} else {
panic!("unknown cell kind")
};
let cell = CalyxCell {
kind,
value: calyx_cell
};
self.env.bind(key, cell.clone());
cell
}
fn get_unique_number(&mut self) -> usize {
let result = self.unique_counter;
self.unique_counter += 1;
result
}
fn _create_primitive(
&mut self, name: String, primitive: String, params: Vec<u64>
) -> RRC<calyx_ir::Cell> {
self.with_calyx_builder(|b| b.add_primitive(name, primitive, ¶ms))
}
fn _create_component_cell(
&mut self, name: String, component: String
) -> RRC<calyx_ir::Cell> {
let mut port_defs = self.ext_sigs.get(&component).unwrap().clone();
let mut go_attr = calyx_ir::Attributes::default();
go_attr.insert(calyx_ir::Attribute::Num(calyx_ir::NumAttr::Go), 1);
port_defs.push(calyx_ir::PortDef::new(
"go",
1,
calyx_ir::Direction::Input,
go_attr
));
let mut done_attr = calyx_ir::Attributes::default();
done_attr.insert(calyx_ir::Attribute::Num(calyx_ir::NumAttr::Done), 1);
port_defs.push(calyx_ir::PortDef::new(
"done",
1,
calyx_ir::Direction::Output,
done_attr
));
let mut clk_attr = calyx_ir::Attributes::default();
clk_attr.insert(calyx_ir::Attribute::Bool(calyx_ir::BoolAttr::Clk), 1);
port_defs.push(calyx_ir::PortDef::new(
"clk",
1,
calyx_ir::Direction::Input,
clk_attr
));
let mut reset_attr = calyx_ir::Attributes::default();
reset_attr
.insert(calyx_ir::Attribute::Bool(calyx_ir::BoolAttr::Reset), 1);
port_defs.push(calyx_ir::PortDef::new(
"reset",
1,
calyx_ir::Direction::Input,
reset_attr
));
let cell = self._cell_from_signature(
name.clone().into(),
calyx_ir::CellType::Component {
name: component.clone().into()
},
port_defs
);
self.component.cells.add(cell.clone());
cell
}
fn _cell_from_signature(
&self, name: calyx_ir::Id, typ: calyx_ir::CellType,
ports: Vec<calyx_ir::PortDef<u64>>
) -> RRC<calyx_ir::Cell> {
let cell = calyx_ir::rrc(calyx_ir::Cell::new(name, typ));
ports.into_iter().for_each(|pd| {
let port = calyx_ir::rrc(calyx_ir::Port {
name: pd.name(),
width: pd.width,
direction: pd.direction,
parent: calyx_ir::PortParent::Cell(calyx_ir::WRC::from(&cell)),
attributes: pd.attributes
});
cell.borrow_mut().ports.push(port);
});
cell
}
}
pub struct CalyxBuilder {
ctx: calyx_ir::Context,
sigs: HashMap<String, Vec<calyx_ir::PortDef<u64>>>,
cell_name_prefix: String
}
impl CalyxBuilder {
pub fn new(
prelude: Option<PathBuf>, lib_path: PathBuf,
entrypoint: Option<String>, cell_name_prefix: String
) -> Self {
assert!(!cell_name_prefix.is_empty());
let ws =
calyx_frontend::Workspace::construct(&prelude, &lib_path).unwrap();
let ctx = calyx_ir::Context {
components: vec![],
lib: ws.lib,
entrypoint: entrypoint.unwrap_or("main".into()).into(),
bc: calyx_ir::BackendConf::default(),
extra_opts: vec![],
metadata: None
};
Self {
ctx,
sigs: HashMap::new(),
cell_name_prefix
}
}
pub fn dummy() -> Self {
Self {
ctx: calyx_ir::Context {
components: vec![],
lib: calyx_ir::LibrarySignatures::default(),
entrypoint: "".into(),
bc: calyx_ir::BackendConf::default(),
extra_opts: vec![],
metadata: None
},
sigs: HashMap::new(),
cell_name_prefix: "".into()
}
}
pub fn register_component(
&mut self, name: String, ports: Vec<calyx_ir::PortDef<u64>>
) {
self.sigs.insert(name, ports);
}
pub fn start_component<ComponentData: Default>(
&self, name: String
) -> CalyxComponent<ComponentData> {
CalyxComponent::new(
calyx_ir::Component::new(
name.clone(),
self.sigs
.get(&name)
.expect("Use `register_component` first")
.clone(),
true,
false,
None
),
self.cell_name_prefix.clone(),
&self.sigs,
&self.ctx.lib
)
}
pub fn _finish_component(&mut self, component: calyx_ir::Component) {
self.ctx.components.push(component);
}
pub fn set_entrypoint(&mut self, entrypoint: String) {
assert!(self.sigs.contains_key(&entrypoint));
self.ctx.entrypoint = entrypoint.into();
}
pub fn finalize(self) -> calyx_ir::Context {
self.ctx
}
}
#[macro_export]
macro_rules! finish_component {
($builder:expr, $component:expr) => {
$builder._finish_component($component.finalize())
};
}