From 0af3bf2ccffcc128bfdc273d02d42b1f03cd255f Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Tue, 18 Jun 2024 01:30:23 -0400 Subject: [PATCH 01/14] Start calyx writer --- Cargo.lock | 4 + Cargo.toml | 1 + tools/calyx_writer/Cargo.toml | 19 + tools/calyx_writer/lib.rs | 829 ++++++++++++++++++++++++++++++++++ 4 files changed, 853 insertions(+) create mode 100644 tools/calyx_writer/Cargo.toml create mode 100644 tools/calyx_writer/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 331f2dd7e7..69dc5ef592 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -519,6 +519,10 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "calyx_writer" +version = "0.7.1" + [[package]] name = "camino" version = "1.1.6" diff --git a/Cargo.toml b/Cargo.toml index 4112148e13..34010000c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ members = [ "tools/cider-data-converter", "tools/calyx-pass-explorer", "tools/yxi", + "tools/calyx_writer", ] exclude = ["site"] diff --git a/tools/calyx_writer/Cargo.toml b/tools/calyx_writer/Cargo.toml new file mode 100644 index 0000000000..2174a1116a --- /dev/null +++ b/tools/calyx_writer/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "calyx_writer" +authors.workspace = true +license-file.workspace = true +keywords.workspace = true +repository.workspace = true +readme.workspace = true +description.workspace = true +categories.workspace = true +homepage.workspace = true +edition.workspace = true +version.workspace = true +rust-version.workspace = true + +[dependencies] + +[lib] +name = "calyx_writer" +path = "lib.rs" diff --git a/tools/calyx_writer/lib.rs b/tools/calyx_writer/lib.rs new file mode 100644 index 0000000000..cc68e15ed0 --- /dev/null +++ b/tools/calyx_writer/lib.rs @@ -0,0 +1,829 @@ +// TODO(ethan): document everything & make code good + +use std::{ + cell::RefCell, + fmt::{self, Display, Write}, + rc::Rc, +}; + +pub type RRC = Rc>; + +fn rrc(value: T) -> RRC { + Rc::new(RefCell::new(value)) +} + +// TODO(ethan): too lazy to find a crate that handles this +struct Indent { + current: String, + add: usize, +} + +impl Indent { + fn push(&mut self) { + for _ in 0..self.add { + self.current.push(' '); + } + } + + fn pop(&mut self) { + for _ in 0..self.add { + self.current.pop(); + } + } + + fn current(&self) -> &str { + &self.current + } +} + +impl Default for Indent { + fn default() -> Self { + Indent { + current: String::new(), + add: 2, + } + } +} + +struct Marker { + name: String, + id: Option, +} + +impl Marker { + pub fn general(name: S) -> Marker { + Marker { + name: name.to_string(), + id: None, + } + } + + pub fn unique(name: S, id: T) -> Marker { + Marker { + name: name.to_string(), + id: Some(id.to_string()), + } + } + + pub fn to_string(&self, descriptor: S) -> String { + format!( + "// {} {}{}\n", + self.name.to_ascii_uppercase(), + descriptor.to_string(), + self.id + .as_ref() + .map(|id| format!(": {}", id)) + .unwrap_or_default() + ) + } +} + +trait CalyxWriter { + fn marker(&self) -> Option { + return None; + } + + fn write( + &self, + f: &mut fmt::Formatter<'_>, + indent: &mut Indent, + ) -> fmt::Result; + + fn writeln( + &self, + f: &mut fmt::Formatter<'_>, + indent: &mut Indent, + ) -> fmt::Result { + if let Some(marker) = self.marker() { + write!(f, "{}", marker.to_string("START"))?; + self.write(f, indent)?; + f.write_char('\n')?; + write!(f, "{}", marker.to_string("END"))?; + } else { + self.write(f, indent)?; + f.write_char('\n')?; + } + Ok(()) + } +} + +struct Import { + path: String, +} + +impl CalyxWriter for Import { + fn write( + &self, + f: &mut fmt::Formatter<'_>, + _indent: &mut Indent, + ) -> fmt::Result { + write!(f, "import \"{}\";", self.path) + } +} + +#[derive(PartialEq, Eq, Clone)] +pub struct Attribute { + name: String, + value: Option, +} + +impl Attribute { + pub fn bool(name: S) -> Self { + Self { + name: name.to_string(), + value: None, + } + } + + pub fn num(name: S, value: u64) -> Self { + Self { + name: name.to_string(), + value: Some(value), + } + } +} + +impl CalyxWriter for Attribute { + fn write( + &self, + f: &mut fmt::Formatter<'_>, + _indent: &mut Indent, + ) -> fmt::Result { + write!(f, "@{}", self.name)?; + if let Some(value) = self.value { + write!(f, "({})", value)?; + } + Ok(()) + } +} + +type Attributes = Vec; + +impl CalyxWriter for Attributes { + fn write( + &self, + f: &mut fmt::Formatter<'_>, + indent: &mut Indent, + ) -> fmt::Result { + for attr in self { + attr.write(f, indent)?; + f.write_char(' ')?; + } + Ok(()) + } +} + +#[derive(PartialEq, Eq, Clone)] +pub struct Port { + attributes: Vec, + parent: Option, + name: String, + // -1 = unkown + width: isize, +} + +impl Port { + pub fn new( + parent: Option, + name: T, + width: usize, + ) -> Self { + Self { + attributes: vec![], + parent: parent.map(|s| s.to_string()), + name: name.to_string(), + width: width as isize, + } + } + + pub fn inferred( + parent: Option, + name: T, + ) -> Self { + Self { + attributes: vec![], + parent: parent.map(|s| s.to_string()), + name: name.to_string(), + width: -1, + } + } + + pub fn add_attribute(&mut self, attr: Attribute) { + self.attributes.push(attr); + } +} + +type Ports = Vec; + +pub trait PortProvider { + fn get(&self, port: String) -> Port; +} + +impl CalyxWriter for Ports { + fn write( + &self, + f: &mut fmt::Formatter<'_>, + indent: &mut Indent, + ) -> fmt::Result { + for (i, port) in self.iter().enumerate() { + if i > 0 { + f.write_str(", ")?; + } + port.write(f, indent)?; + } + Ok(()) + } +} + +impl CalyxWriter for Port { + fn write( + &self, + f: &mut fmt::Formatter<'_>, + indent: &mut Indent, + ) -> fmt::Result { + self.attributes.write(f, indent)?; + write!(f, "{}: {}", self.name, self.width) + } +} + +impl Display for Port { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(parent) = &self.parent { + write!(f, "{}.", parent)?; + } + write!(f, "{}", self.name) + } +} + +pub struct Cell { + is_ref: bool, + attributes: Vec, + name: String, + inst: String, + args: Vec, +} + +impl PortProvider for Cell { + fn get(&self, port: String) -> Port { + Port::inferred(Some(self.name.clone()), port) + } +} + +impl CalyxWriter for Cell { + fn write( + &self, + f: &mut fmt::Formatter<'_>, + indent: &mut Indent, + ) -> fmt::Result { + self.attributes.write(f, indent)?; + write!( + f, + "{}{} = {}({});", + if self.is_ref { "ref " } else { "" }, + self.name, + self.inst, + self.args + .iter() + .map(u64::to_string) + .collect::>() + .join(", ") + ) + } +} + +#[derive(PartialEq, Eq)] +pub enum Guard { + None, + Constant(u64), + Port(Port), + And(Box, Box), +} + +impl CalyxWriter for Guard { + fn write( + &self, + _f: &mut fmt::Formatter<'_>, + _indent: &mut Indent, + ) -> fmt::Result { + // TODO + Ok(()) + } +} + +pub struct Assignment { + lhs: Port, + rhs: Port, + guard: Guard, +} + +impl CalyxWriter for Assignment { + fn write( + &self, + f: &mut fmt::Formatter<'_>, + indent: &mut Indent, + ) -> fmt::Result { + write!(f, "{} = ", self.lhs)?; + if self.guard != Guard::None { + self.guard.write(f, indent)?; + write!(f, " ? ")?; + } + write!(f, "{};", self.rhs) + } +} + +pub struct Group { + name: String, + description: Option, + is_comb: bool, + latency: Option, + assignments: Vec, +} + +impl Group { + fn new(name: S, is_comb: bool) -> Self { + Self { + name: name.to_string(), + description: None, + is_comb, + latency: None, + assignments: vec![], + } + } + + pub fn describe(&mut self, description: String) { + self.description = Some(description); + } + + pub fn set_latency(&mut self, latency: usize) { + self.latency = Some(latency); + } + + pub fn is_comb(&self) -> bool { + self.is_comb + } + + pub fn assign(&mut self, lhs: Port, rhs: Port) { + self.assign_guarded(lhs, rhs, Guard::None); + } + + pub fn assign_guarded(&mut self, lhs: Port, rhs: Port, guard: Guard) { + self.assignments.push(Assignment { lhs, rhs, guard }); + } +} + +impl PortProvider for Group { + fn get(&self, port: String) -> Port { + Port::inferred(Some(self.name.clone()), port) + } +} + +impl CalyxWriter for Group { + fn write( + &self, + f: &mut fmt::Formatter<'_>, + indent: &mut Indent, + ) -> fmt::Result { + if let Some(description) = &self.description { + for line in description.lines() { + write!(f, "// {}\n{}", line, indent.current())?; + } + } + if let Some(latency) = self.latency { + write!(f, "static<{}> ", latency)?; + } + if self.is_comb { + write!(f, "comb ")?; + } + writeln!(f, "group {} {{", self.name)?; + indent.push(); + for assignment in &self.assignments { + write!(f, "{}", indent.current())?; + assignment.writeln(f, indent)?; + } + indent.pop(); + write!(f, "{}}}", indent.current()) + } +} + +pub enum ControlValue { + Empty, + Enable(RRC), + Seq(Vec), + Par(Vec), + While(Port, Option>, Vec), + If(Port, Option>, Vec, Vec), +} + +pub struct Control { + attributes: Attributes, + value: ControlValue, +} + +impl Control { + pub fn enable(group: RRC) -> Self { + Control { + attributes: Attributes::new(), + value: ControlValue::Enable(group), + } + } + + pub fn seq(controls: Vec) -> Self { + Control { + attributes: Attributes::new(), + value: ControlValue::Seq(controls), + } + } + + pub fn par(controls: Vec) -> Self { + Control { + attributes: Attributes::new(), + value: ControlValue::Par(controls), + } + } + + pub fn while_( + port: Port, + group: Option>, + controls: Vec, + ) -> Self { + Control { + attributes: Attributes::new(), + value: ControlValue::While(port, group, controls), + } + } + + pub fn if_( + port: Port, + group: Option>, + then_controls: Vec, + else_controls: Vec, + ) -> Self { + Control { + attributes: Attributes::new(), + value: ControlValue::If(port, group, then_controls, else_controls), + } + } + + pub fn add_attribute(&mut self, attribute: Attribute) { + self.attributes.push(attribute); + } +} + +impl CalyxWriter for Control { + fn write( + &self, + f: &mut fmt::Formatter<'_>, + indent: &mut Indent, + ) -> fmt::Result { + self.attributes.write(f, indent)?; + match &self.value { + ControlValue::Empty => {} + ControlValue::Enable(group) => { + write!(f, "{};", group.borrow().name)?; + } + ControlValue::Seq(body) => { + writeln!(f, "seq {{")?; + indent.push(); + for node in body { + write!(f, "{}", indent.current())?; + node.writeln(f, indent)?; + } + indent.pop(); + write!(f, "{}}}", indent.current())?; + } + ControlValue::Par(body) => { + writeln!(f, "par {{")?; + indent.push(); + for node in body { + write!(f, "{}", indent.current())?; + node.writeln(f, indent)?; + } + indent.pop(); + write!(f, "{}}}", indent.current())?; + } + ControlValue::While(cond, group, body) => { + writeln!( + f, + "while {}{} {{", + cond, + if let Some(group) = group { + format!(" with {}", group.borrow().name) + } else { + "".into() + } + )?; + indent.push(); + for node in body { + write!(f, "{}", indent.current())?; + node.writeln(f, indent)?; + } + indent.pop(); + write!(f, "{}}}", indent.current())?; + } + ControlValue::If(cond, group, body_true, body_false) => { + writeln!( + f, + "if {}{} {{", + cond, + if let Some(group) = group { + format!(" with {}", group.borrow().name) + } else { + "".into() + } + )?; + indent.push(); + for node in body_true { + write!(f, "{}", indent.current())?; + node.writeln(f, indent)?; + } + indent.pop(); + writeln!(f, "{}}} else {{", indent.current())?; + indent.push(); + for node in body_false { + write!(f, "{}", indent.current())?; + node.writeln(f, indent)?; + } + indent.pop(); + write!(f, "{}}}", indent.current())?; + } + } + Ok(()) + } +} + +pub struct Component { + is_comb: bool, + name: String, + inputs: Ports, + outputs: Ports, + cells: Vec>, + groups: Vec>, + control: Control, + continuous_assignments: Vec, +} + +impl Component { + pub fn set_comb(&mut self, is_comb: bool) { + self.is_comb = is_comb; + } + + pub fn add_input(&mut self, name: S, width: usize) { + self.add_port(Port::new::(None, name, width), true); + } + + pub fn add_output(&mut self, name: S, width: usize) { + self.add_port(Port::new::(None, name, width), false); + } + + pub fn add_port(&mut self, port: Port, is_input: bool) { + if is_input { + self.inputs.push(port); + } else { + self.outputs.push(port); + } + } + + pub fn cell( + &mut self, + name: S, + inst: T, + args: Vec, + ) -> RRC { + let cell = rrc(Cell { + is_ref: false, + attributes: vec![], + name: name.to_string(), + inst: inst.to_string(), + args, + }); + self.cells.push(cell.clone()); + cell + } + + pub fn assign(&mut self, lhs: Port, rhs: Port, guard: Guard) { + self.continuous_assignments + .push(Assignment { lhs, rhs, guard }); + } + + pub fn group(&mut self, name: S) -> RRC { + let group = rrc(Group::new(name, false)); + self.groups.push(group.clone()); + group + } + + pub fn comb_group(&mut self, name: S) -> RRC { + let group = rrc(Group::new(name, true)); + self.groups.push(group.clone()); + group + } + + pub fn set_control(&mut self, control: Control) { + self.control = control; + } + + fn brace_section( + &self, + f: &mut fmt::Formatter, + indent: &mut Indent, + name: S, + body: F, + ) -> fmt::Result + where + F: FnOnce(&mut fmt::Formatter, &mut Indent) -> fmt::Result, + { + indent.push(); + writeln!(f, "{}{} {{", indent.current(), name.to_string(),)?; + indent.push(); + let result = body(f, indent); + indent.pop(); + writeln!(f, "{}}}", indent.current())?; + indent.pop(); + result + } +} + +impl PortProvider for Component { + fn get(&self, port: String) -> Port { + self.inputs + .iter() + .chain(self.outputs.iter()) + .find(|p| p.name == port) + .expect("port does not exist, violating precondition".into()) + .clone() + } +} + +impl CalyxWriter for Component { + fn marker(&self) -> Option { + Some(Marker::unique("component", self.name.clone())) + } + + fn write( + &self, + f: &mut fmt::Formatter<'_>, + indent: &mut Indent, + ) -> fmt::Result { + write!( + f, + "{}component {}(", + if self.is_comb { "comb " } else { "" }, + self.name + )?; + self.inputs.write(f, indent)?; + write!(f, ") -> (")?; + self.outputs.write(f, indent)?; + writeln!(f, ") {{")?; + self.brace_section(f, indent, "cells", |f, indent| { + for cell in &self.cells { + write!(f, "{}", indent.current())?; + cell.borrow().writeln(f, indent)?; + } + Ok(()) + })?; + self.brace_section(f, indent, "wires", |f, indent| { + for group in &self.groups { + write!(f, "{}", indent.current())?; + group.borrow().writeln(f, indent)?; + } + for assignment in &self.continuous_assignments { + write!(f, "{}", indent.current())?; + assignment.writeln(f, indent)?; + } + Ok(()) + })?; + self.brace_section(f, indent, "control", |f, indent| { + write!(f, "{}", indent.current())?; + self.control.writeln(f, indent) + })?; + write!(f, "}}") + } +} + +#[derive(Default)] +pub struct Program { + imports: Vec, + // inv: no element is removed from this array + comps: Vec>>, +} + +impl Program { + pub fn new() -> Self { + Self { + imports: vec![], + comps: vec![], + } + } + + pub fn import(&mut self, path: S) { + self.imports.push(Import { + path: path.to_string(), + }); + } + + pub fn comp(&mut self, name: S) -> RRC { + let comp = rrc(Component { + is_comb: false, + name: name.to_string(), + inputs: vec![], + outputs: vec![], + cells: vec![], + groups: vec![], + control: Control::seq(vec![]), + continuous_assignments: vec![], + }); + self.comps.push(comp.clone()); + comp + } +} + +impl CalyxWriter for Program { + fn write( + &self, + f: &mut fmt::Formatter<'_>, + indent: &mut Indent, + ) -> fmt::Result { + for import in &self.imports { + import.writeln(f, indent)?; + } + for comp in &self.comps { + comp.borrow().writeln(f, indent)?; + } + Ok(()) + } +} + +impl Display for Program { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.write(f, &mut Indent::default()) + } +} + +#[macro_export] +macro_rules! build_cells { + ($comp:ident; $($name:ident = $cell:ident($($args:expr),*);)*) => { + $( + let $name = $comp.borrow_mut().cell(stringify!($name), stringify!($cell), vec![$($args as u64),*]); + )* + }; +} + +#[macro_export] +macro_rules! declare_group { + ($comp:expr; group $group:ident) => { + let $group = $comp.borrow_mut().group(stringify!($group)); + }; + ($comp:expr; group $group:ident: $desc:expr) => { + let $group = $comp.borrow_mut().group(stringify!($group)); + $group.borrow_mut().describe($desc.to_string()); + }; + ($comp:expr; comb group $group:ident) => { + let $group = $comp.borrow_mut().comb_group(stringify!($group)); + }; + ($comp:expr; comb group $group:ident: $desc:expr) => { + let $group = $comp.borrow_mut().comb_group(stringify!($group)); + $group.borrow_mut().describe($desc.to_string()); + }; +} + +#[macro_export] +macro_rules! build_group { + ($group:ident; $($lhs:ident.$lhs_port:ident = $rhs:ident.$rhs_port:ident;)*) => { + $( + $group.borrow_mut().assign( + $lhs.borrow().get(stringify!($lhs_port).to_string()), + $rhs.borrow().get(stringify!($rhs_port).to_string()), + ); + )* + }; +} + +#[macro_export] +macro_rules! build_control { + ([$x:ident]) => { + Control::enable($x.clone()) + }; + ([seq { $($x:tt),+ }]) => { + Control::seq(vec![$(build_control!($x)),*]) + }; + ([par { $($x:tt),+ }]) => { + Control::par(vec![$(build_control!($x)),*]) + }; + ([while $cond:ident.$port:ident { $($x:tt),* }]) => { + Control::while_($cond.borrow().get(stringify!($port).into()), None, vec![$(build_control!($x)),*]) + }; + ([while $cond:ident.$port:ident with $comb_group:ident { $($x:tt),* }]) => { + Control::while_($cond.borrow().get(stringify!($port).into()), Some($comb_group), vec![$(build_control!($x)),*]) + }; + ([if $cond:ident.$port:ident { $($x_true:tt),* }]) => { + Control::if_($cond.borrow().get(stringify!($port).into()), None, vec![$(build_control!($x_true)),*], vec![]) + }; + ([if $cond:ident.$port:ident { $($x_true:tt),* } else { $($x_false:tt),* }]) => { + Control::if_($cond.borrow().get(stringify!($port).into()), None, vec![$(build_control!($x_true)),*], vec![$(build_control!($x_false)),*]) + }; + ([if $cond:ident.$port:ident with $comb_group:ident { $($x_true:tt),* }=]) => { + Control::if_($cond.borrow().get(stringify!($port).into()), Some($comb_group.clone()), vec![$(build_control!($x_true)),*], vec![]) + }; + ([if $cond:ident.$port:ident with $comb_group:ident { $($x_true:tt),* } else { $($x_false:tt),* }]) => { + Control::if_($cond.borrow().get(stringify!($port).into()), Some($comb_group.clone()), vec![$(build_control!($x_true)),*], vec![$(build_control!($x_false)),*]) + }; +} From 03cdf896e2c1872d236a9559bf249c6bed66b6e4 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Tue, 18 Jun 2024 02:22:11 -0400 Subject: [PATCH 02/14] Add documentation --- tools/calyx_writer/lib.rs | 230 +++++++++++++++++++++++++++++--------- 1 file changed, 176 insertions(+), 54 deletions(-) diff --git a/tools/calyx_writer/lib.rs b/tools/calyx_writer/lib.rs index cc68e15ed0..3ddb57d0ab 100644 --- a/tools/calyx_writer/lib.rs +++ b/tools/calyx_writer/lib.rs @@ -1,4 +1,11 @@ -// TODO(ethan): document everything & make code good +// Author: Ethan Uppal + +//! This library serves as a rust version of calyx-py. The main builder API is +//! via [`Program`], from where you can create [`Component`]s. Most `struct`s +//! here are [`CalyxWriter`]s, so you can easily obtain their calyx +//! representations (although, of course, that of [`Program`] is arguably the +//! most useful). Macros have been developed for creating cells, groups, and +//! control; they are the *intended* way of constructing these elements. use std::{ cell::RefCell, @@ -6,8 +13,10 @@ use std::{ rc::Rc, }; +/// Shorthand for the `Rc>` pattern. pub type RRC = Rc>; +/// Convenience constructor for [`RRC`]. fn rrc(value: T) -> RRC { Rc::new(RefCell::new(value)) } @@ -45,31 +54,43 @@ impl Default for Indent { } } +/// A location in the source code for source element `element` and optionally a +/// unique identifier `id` for that element. For example, +/// ``` +/// // COMPONENT END: main +/// ``` +/// would be a marker for the "component" element and with a unique identifier +/// of "main". struct Marker { - name: String, + element: String, id: Option, } impl Marker { - pub fn general(name: S) -> Marker { + /// A marker for an `element`. + #[allow(dead_code)] + pub fn general(element: S) -> Marker { Marker { - name: name.to_string(), + element: element.to_string(), id: None, } } - pub fn unique(name: S, id: T) -> Marker { + /// A marker for a unique `element` identified by `id`. + pub fn unique(element: S, id: T) -> Marker { Marker { - name: name.to_string(), + element: element.to_string(), id: Some(id.to_string()), } } - pub fn to_string(&self, descriptor: S) -> String { + /// Constructs a comment string for the marker at a given `location`. For + /// example, `marker.to_string("end")`. + pub fn to_string(&self, location: S) -> String { format!( "// {} {}{}\n", - self.name.to_ascii_uppercase(), - descriptor.to_string(), + self.element.to_ascii_uppercase(), + location.to_string().to_ascii_uppercase(), self.id .as_ref() .map(|id| format!(": {}", id)) @@ -78,27 +99,38 @@ impl Marker { } } +/// An element of calyx source code that MUST implement [`CalyxWriter::write`], +/// can OPTIONALLY implement [`CalyxWriter::marker`], and must NEVER override +/// [`CalyxWriter::writeln`]. trait CalyxWriter { fn marker(&self) -> Option { - return None; + None } + /// Writes this element to `f`. It may be assumed that `indent.current()` + /// spaces have already been written, but on any further line, these number + /// of spaces must be effected. See also [`CalyxWriter::writeln`]. fn write( &self, f: &mut fmt::Formatter<'_>, indent: &mut Indent, ) -> fmt::Result; + /// Writes this element followed by a newline and wrapped with markers if + /// the element provides them. See more information at + /// [`CalyxWriter::write`]. + /// + /// Do NOT override this function. See details at [`CalyxWriter`]. fn writeln( &self, f: &mut fmt::Formatter<'_>, indent: &mut Indent, ) -> fmt::Result { if let Some(marker) = self.marker() { - write!(f, "{}", marker.to_string("START"))?; + write!(f, "{}", marker.to_string("start"))?; self.write(f, indent)?; f.write_char('\n')?; - write!(f, "{}", marker.to_string("END"))?; + write!(f, "{}", marker.to_string("end"))?; } else { self.write(f, indent)?; f.write_char('\n')?; @@ -107,6 +139,7 @@ trait CalyxWriter { } } +/// A calyx import statement. struct Import { path: String, } @@ -121,6 +154,7 @@ impl CalyxWriter for Import { } } +/// A clyx attribute. #[derive(PartialEq, Eq, Clone)] pub struct Attribute { name: String, @@ -128,6 +162,8 @@ pub struct Attribute { } impl Attribute { + /// Constructs a `calyx_frontend::BoolAttr`, such as `@external`, named + /// `name`. pub fn bool(name: S) -> Self { Self { name: name.to_string(), @@ -135,6 +171,7 @@ impl Attribute { } } + /// Constructs a `calyx_frontend::NumAttr`, such as `@go`, named `name`. pub fn num(name: S, value: u64) -> Self { Self { name: name.to_string(), @@ -157,6 +194,7 @@ impl CalyxWriter for Attribute { } } +/// A list of attributes. type Attributes = Vec; impl CalyxWriter for Attributes { @@ -173,21 +211,28 @@ impl CalyxWriter for Attributes { } } +/// A calyx port on a cell or a component. #[derive(PartialEq, Eq, Clone)] pub struct Port { attributes: Vec, + /// The cell that this port belongs to, or `None` if it is in a component + /// signature. parent: Option, name: String, - // -1 = unkown + /// A width of `-1` means the actual width is unknown/irrelevant/inferred. width: isize, } impl Port { + /// Constructs a new port with the given `parent` and `name`. + /// + /// Requires: `width > 0`. pub fn new( parent: Option, name: T, width: usize, ) -> Self { + assert!(width > 0); Self { attributes: vec![], parent: parent.map(|s| s.to_string()), @@ -196,6 +241,8 @@ impl Port { } } + /// Constructs a new port with the given `parent` and `name` and with + /// inferred width. pub fn inferred( parent: Option, name: T, @@ -211,54 +258,48 @@ impl Port { pub fn add_attribute(&mut self, attr: Attribute) { self.attributes.push(attr); } -} - -type Ports = Vec; -pub trait PortProvider { - fn get(&self, port: String) -> Port; + pub fn has_inferred_width(&self) -> bool { + self.width == -1 + } } -impl CalyxWriter for Ports { - fn write( - &self, - f: &mut fmt::Formatter<'_>, - indent: &mut Indent, - ) -> fmt::Result { - for (i, port) in self.iter().enumerate() { - if i > 0 { - f.write_str(", ")?; - } - port.write(f, indent)?; +impl Display for Port { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(parent) = &self.parent { + write!(f, "{}.", parent)?; } - Ok(()) + write!(f, "{}", self.name) } } impl CalyxWriter for Port { + /// Behaves identically to [`Port::fmt`]. Please read [`CalyxWriter::write`] + /// for documentation on this function. fn write( &self, f: &mut fmt::Formatter<'_>, - indent: &mut Indent, + _indent: &mut Indent, ) -> fmt::Result { - self.attributes.write(f, indent)?; - write!(f, "{}: {}", self.name, self.width) + self.fmt(f) } } -impl Display for Port { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(parent) = &self.parent { - write!(f, "{}.", parent)?; - } - write!(f, "{}", self.name) - } +/// Abstracts port functionality over components and cells. +pub trait PortProvider { + /// Retrieves the port on this provider named `port`. + /// + /// Requires: `port` exists on this provider. + fn get(&self, port: String) -> Port; } +/// A calyx cell. pub struct Cell { is_ref: bool, attributes: Vec, name: String, + /// The name of the component or primitive instantiated, e.g., "std_reg" + /// for the register primitive. inst: String, args: Vec, } @@ -291,6 +332,7 @@ impl CalyxWriter for Cell { } } +/// A guard for a calyx [`Assignment`]. #[derive(PartialEq, Eq)] pub enum Guard { None, @@ -310,6 +352,7 @@ impl CalyxWriter for Guard { } } +/// A calyx assignment. pub struct Assignment { lhs: Port, rhs: Port, @@ -331,15 +374,19 @@ impl CalyxWriter for Assignment { } } +/// A calyx group. pub struct Group { name: String, description: Option, is_comb: bool, + /// `None` if dynamic, `Some(latency)` if static and taking `latency cycles.` latency: Option, assignments: Vec, } impl Group { + /// Constructs an empty group named `name`, combinational if and only if + /// `is_comb`. fn new(name: S, is_comb: bool) -> Self { Self { name: name.to_string(), @@ -350,6 +397,8 @@ impl Group { } } + /// Sets a human-readable description of the group. This description may + /// take multiple lines. pub fn describe(&mut self, description: String) { self.description = Some(description); } @@ -362,10 +411,13 @@ impl Group { self.is_comb } + /// Adds an unguarded assignment between `lhs` and `rhs`. Behaves like + /// [`Group::assign_guarded`] with [`Guard::None`] passed for the guard. pub fn assign(&mut self, lhs: Port, rhs: Port) { self.assign_guarded(lhs, rhs, Guard::None); } + /// Adds an assignment guarded by `guard` between `lhs` and `rhs`. pub fn assign_guarded(&mut self, lhs: Port, rhs: Port, guard: Guard) { self.assignments.push(Assignment { lhs, rhs, guard }); } @@ -405,7 +457,8 @@ impl CalyxWriter for Group { } } -pub enum ControlValue { +/// Helper variant with [`Control`]. +enum ControlValue { Empty, Enable(RRC), Seq(Vec), @@ -414,12 +467,24 @@ pub enum ControlValue { If(Port, Option>, Vec, Vec), } +/// Structured calyx control with attributes. pub struct Control { attributes: Attributes, value: ControlValue, } impl Control { + /// Constructs an empty control node. + pub fn empty() -> Self { + Control { + attributes: Attributes::new(), + value: ControlValue::Empty, + } + } + + /// Constructs a control node that enables `group` in a context-dependent + /// way. For example, in a sequence control, this control will enable the + /// group after all previous nodes in sequence. pub fn enable(group: RRC) -> Self { Control { attributes: Attributes::new(), @@ -427,31 +492,37 @@ impl Control { } } - pub fn seq(controls: Vec) -> Self { + /// Constructs a sequential control node. + pub fn seq(nodes: Vec) -> Self { Control { attributes: Attributes::new(), - value: ControlValue::Seq(controls), + value: ControlValue::Seq(nodes), } } - pub fn par(controls: Vec) -> Self { + /// Constructs a parallel control node. + pub fn par(nodes: Vec) -> Self { Control { attributes: Attributes::new(), - value: ControlValue::Par(controls), + value: ControlValue::Par(nodes), } } + /// Constructs a while loop control node. A combinational group `group` may + /// optionally be activated for the condition `cond`. pub fn while_( - port: Port, + cond: Port, group: Option>, controls: Vec, ) -> Self { Control { attributes: Attributes::new(), - value: ControlValue::While(port, group, controls), + value: ControlValue::While(cond, group, controls), } } + /// Constructs a conditional branching control node. A combinational group + /// `group` may optionally be activated for the condition `cond`. pub fn if_( port: Port, group: Option>, @@ -551,11 +622,12 @@ impl CalyxWriter for Control { } } +/// A calyx component. pub struct Component { is_comb: bool, name: String, - inputs: Ports, - outputs: Ports, + inputs: Vec, + outputs: Vec, cells: Vec>, groups: Vec>, control: Control, @@ -563,18 +635,23 @@ pub struct Component { } impl Component { + /// Sets whether the component is combinational or not. pub fn set_comb(&mut self, is_comb: bool) { self.is_comb = is_comb; } + /// Adds an input port `name` to this component. pub fn add_input(&mut self, name: S, width: usize) { self.add_port(Port::new::(None, name, width), true); } + /// Adds an output port `name` to this component. pub fn add_output(&mut self, name: S, width: usize) { self.add_port(Port::new::(None, name, width), false); } + /// [`Component::add_input`] or [`Component::add_output`] may be more + /// useful. pub fn add_port(&mut self, port: Port, is_input: bool) { if is_input { self.inputs.push(port); @@ -583,6 +660,8 @@ impl Component { } } + /// Constructs a new cell named `name` that instatiates `inst` with + /// arguments `args`. pub fn cell( &mut self, name: S, @@ -600,27 +679,43 @@ impl Component { cell } + /// Establishes a continuous assignment guarded by `guard` between `lhs` and + /// `rhs`. pub fn assign(&mut self, lhs: Port, rhs: Port, guard: Guard) { self.continuous_assignments .push(Assignment { lhs, rhs, guard }); } + /// Use [`declare_group!`] and [`build_group!`] instead. pub fn group(&mut self, name: S) -> RRC { let group = rrc(Group::new(name, false)); self.groups.push(group.clone()); group } + /// Use [`declare_group!`] and [`build_group!`] instead. pub fn comb_group(&mut self, name: S) -> RRC { let group = rrc(Group::new(name, true)); self.groups.push(group.clone()); group } + /// Sets the root control node for this component. Use [`build_control!`] to + /// construct this node. pub fn set_control(&mut self, control: Control) { self.control = control; } + /// Opens an indented section within a component and runs a callback after + /// applying another indent. For instance, when called with a `name` of + /// "cells", this function allows formatting of: + /// ``` + /// ... + /// cells { + /// ... + /// } + /// ... + /// ``` fn brace_section( &self, f: &mut fmt::Formatter, @@ -640,6 +735,26 @@ impl Component { indent.pop(); result } + + /// Formats a list of ports as an input or output signature. For example, + /// the syntax for ports in the declaration of a component with name-width + /// pairs separated by colons is such a signature. + /// + /// Requires: all ports in `ports` must have definite widths. + fn write_ports_sig( + &self, + f: &mut fmt::Formatter, + ports: &Vec, + ) -> fmt::Result { + for (i, port) in ports.iter().enumerate() { + if i > 0 { + f.write_str(", ")?; + } + assert!(!port.has_inferred_width()); + write!(f, "{}: {}", port.name, port.width)?; + } + Ok(()) + } } impl PortProvider for Component { @@ -648,7 +763,9 @@ impl PortProvider for Component { .iter() .chain(self.outputs.iter()) .find(|p| p.name == port) - .expect("port does not exist, violating precondition".into()) + .unwrap_or_else(|| { + panic!("{}", "port does not exist, violating precondition") + }) .clone() } } @@ -669,9 +786,9 @@ impl CalyxWriter for Component { if self.is_comb { "comb " } else { "" }, self.name )?; - self.inputs.write(f, indent)?; + self.write_ports_sig(f, &self.inputs)?; write!(f, ") -> (")?; - self.outputs.write(f, indent)?; + self.write_ports_sig(f, &self.outputs)?; writeln!(f, ") {{")?; self.brace_section(f, indent, "cells", |f, indent| { for cell in &self.cells { @@ -699,14 +816,16 @@ impl CalyxWriter for Component { } } +/// A complete calyx program containing imports and components. #[derive(Default)] pub struct Program { imports: Vec, - // inv: no element is removed from this array + /// inv: no element is removed from this array comps: Vec>>, } impl Program { + /// Constructs an empty program. pub fn new() -> Self { Self { imports: vec![], @@ -714,12 +833,14 @@ impl Program { } } + /// Imports the calyx standard library file `path`. pub fn import(&mut self, path: S) { self.imports.push(Import { path: path.to_string(), }); } + /// Constructs an empty component named `name`. pub fn comp(&mut self, name: S) -> RRC { let comp = rrc(Component { is_comb: false, @@ -728,7 +849,7 @@ impl Program { outputs: vec![], cells: vec![], groups: vec![], - control: Control::seq(vec![]), + control: Control::empty(), continuous_assignments: vec![], }); self.comps.push(comp.clone()); @@ -758,6 +879,7 @@ impl Display for Program { } } +/// Similar to the `structure!` macro in `calyx_ir`. #[macro_export] macro_rules! build_cells { ($comp:ident; $($name:ident = $cell:ident($($args:expr),*);)*) => { From c987cf485e4a5f2de7b77688258506bc205318fe Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Tue, 18 Jun 2024 02:27:50 -0400 Subject: [PATCH 03/14] Clippy --- tools/cache/cache.futil | 58 +++++++++++++++++++++++++++++++++++++++ tools/calyx_writer/lib.rs | 2 +- 2 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 tools/cache/cache.futil diff --git a/tools/cache/cache.futil b/tools/cache/cache.futil new file mode 100644 index 0000000000..223249ef26 --- /dev/null +++ b/tools/cache/cache.futil @@ -0,0 +1,58 @@ +import "primitives/core.futil"; +import "primitives/memories/comb.futil"; +// COMPONENT START: main +component main() -> () { + cells { + } + wires { + } + control { + seq { + } + } +} +// COMPONENT END: main +// COMPONENT START: cache_level_L1 +component cache_level_L1(read_en: 1, write_en: 1, in: 8, addr: 16, fetch_en: 1, fetch_in: 128) -> (out: 8, fetch_addr: 16) { + cells { + entries = comb_mem_d1(138, 64, 6); + addr_to_index = std_bit_slice(16, 2, 8, 6); + addr_to_tag = std_bit_slice(16, 8, 16, 8); + entry_to_tag = std_bit_slice(138, 128, 136, 8); + tag_matches = std_eq(8); + } + wires { + // checks if the given `addr` exists in the cache + comb group check_tag_matches { + addr_to_index.in = addr; + addr_to_tag.in = addr; + entry_to_tag.in = entries.read_data; + entries.addr0 = addr_to_index.out; + tag_matches.left = addr_to_tag.out; + tag_matches.right = entry_to_tag.out; + } + // we've cached this, so we can just look it up + group read_cached { + } + // we need to ask the level below for this data + group read_uncached { + } + } + control { + par { + if read_en { + if tag_matches.out with check_tag_matches { + read_cached; + } else { + read_uncached; + } + } else { + } + if write_en { + } else { + } + } + } +} +// COMPONENT END: cache_level_L1 + diff --git a/tools/calyx_writer/lib.rs b/tools/calyx_writer/lib.rs index 3ddb57d0ab..2bfa08ebe9 100644 --- a/tools/calyx_writer/lib.rs +++ b/tools/calyx_writer/lib.rs @@ -744,7 +744,7 @@ impl Component { fn write_ports_sig( &self, f: &mut fmt::Formatter, - ports: &Vec, + ports: &[Port], ) -> fmt::Result { for (i, port) in ports.iter().enumerate() { if i > 0 { From cb104b9c8493a33ab48b0dab472beea8f9966807 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Tue, 18 Jun 2024 03:14:07 -0400 Subject: [PATCH 04/14] Improve documentation --- tools/cache/cache.futil | 58 ------------------------------------- tools/calyx_writer/lib.rs | 60 ++++++++++++++++++++++++++++++++------- 2 files changed, 49 insertions(+), 69 deletions(-) delete mode 100644 tools/cache/cache.futil diff --git a/tools/cache/cache.futil b/tools/cache/cache.futil deleted file mode 100644 index 223249ef26..0000000000 --- a/tools/cache/cache.futil +++ /dev/null @@ -1,58 +0,0 @@ -import "primitives/core.futil"; -import "primitives/memories/comb.futil"; -// COMPONENT START: main -component main() -> () { - cells { - } - wires { - } - control { - seq { - } - } -} -// COMPONENT END: main -// COMPONENT START: cache_level_L1 -component cache_level_L1(read_en: 1, write_en: 1, in: 8, addr: 16, fetch_en: 1, fetch_in: 128) -> (out: 8, fetch_addr: 16) { - cells { - entries = comb_mem_d1(138, 64, 6); - addr_to_index = std_bit_slice(16, 2, 8, 6); - addr_to_tag = std_bit_slice(16, 8, 16, 8); - entry_to_tag = std_bit_slice(138, 128, 136, 8); - tag_matches = std_eq(8); - } - wires { - // checks if the given `addr` exists in the cache - comb group check_tag_matches { - addr_to_index.in = addr; - addr_to_tag.in = addr; - entry_to_tag.in = entries.read_data; - entries.addr0 = addr_to_index.out; - tag_matches.left = addr_to_tag.out; - tag_matches.right = entry_to_tag.out; - } - // we've cached this, so we can just look it up - group read_cached { - } - // we need to ask the level below for this data - group read_uncached { - } - } - control { - par { - if read_en { - if tag_matches.out with check_tag_matches { - read_cached; - } else { - read_uncached; - } - } else { - } - if write_en { - } else { - } - } - } -} -// COMPONENT END: cache_level_L1 - diff --git a/tools/calyx_writer/lib.rs b/tools/calyx_writer/lib.rs index 2bfa08ebe9..d8492ded4f 100644 --- a/tools/calyx_writer/lib.rs +++ b/tools/calyx_writer/lib.rs @@ -139,7 +139,7 @@ trait CalyxWriter { } } -/// A calyx import statement. +/// Imports a calyx file from the standard library. struct Import { path: String, } @@ -154,7 +154,7 @@ impl CalyxWriter for Import { } } -/// A clyx attribute. +/// See `calyx_frontend::Attribute`. #[derive(PartialEq, Eq, Clone)] pub struct Attribute { name: String, @@ -211,7 +211,7 @@ impl CalyxWriter for Attributes { } } -/// A calyx port on a cell or a component. +/// A port on a cell or a component. #[derive(PartialEq, Eq, Clone)] pub struct Port { attributes: Vec, @@ -293,7 +293,7 @@ pub trait PortProvider { fn get(&self, port: String) -> Port; } -/// A calyx cell. +/// See `calyx_ir::Cell`. pub struct Cell { is_ref: bool, attributes: Vec, @@ -332,7 +332,7 @@ impl CalyxWriter for Cell { } } -/// A guard for a calyx [`Assignment`]. +/// A guard for an [`Assignment`]. #[derive(PartialEq, Eq)] pub enum Guard { None, @@ -352,7 +352,7 @@ impl CalyxWriter for Guard { } } -/// A calyx assignment. +/// See `calyx_ir::Assignment`. pub struct Assignment { lhs: Port, rhs: Port, @@ -374,7 +374,7 @@ impl CalyxWriter for Assignment { } } -/// A calyx group. +/// See `calyx_ir::Group`. Contains optional documentation. pub struct Group { name: String, description: Option, @@ -622,7 +622,7 @@ impl CalyxWriter for Control { } } -/// A calyx component. +/// See `calyx_ir::Component`. pub struct Component { is_comb: bool, name: String, @@ -816,7 +816,8 @@ impl CalyxWriter for Component { } } -/// A complete calyx program containing imports and components. +/// A complete calyx program containing imports and components. To obtain the +/// generated calyx as text, use the [`Program::to_string`] function. #[derive(Default)] pub struct Program { imports: Vec, @@ -879,7 +880,14 @@ impl Display for Program { } } -/// Similar to the `structure!` macro in `calyx_ir`. +/// Similar to the `structure!` macro in `calyx_ir`. For example, +/// ``` +/// build_cells!(comp; +/// a = std_reg(32); +/// b = std_add(32); +/// ) +/// ``` +/// Remember to import (via [`Program::import`]) the necessary primitives. #[macro_export] macro_rules! build_cells { ($comp:ident; $($name:ident = $cell:ident($($args:expr),*);)*) => { @@ -889,6 +897,10 @@ macro_rules! build_cells { }; } +/// `declare_group!(comp; group name)` declares `name` as a group and binds it +/// as a variable. `declare_group!(comp; comb group name)` behaves identically +/// but constructs a combinational group. In both cases, an optional `: "..."` +/// can be places after the group name (`name`) to provide it a description. #[macro_export] macro_rules! declare_group { ($comp:expr; group $group:ident) => { @@ -907,9 +919,18 @@ macro_rules! declare_group { }; } +/// Adds assignments to a group. For example, +/// ``` +/// build_group!(group; +/// a.addr0 = b.out; +/// b.foo = a.bar; +/// comp.in_port = x.foo; +/// ) +/// ``` +/// Currently, only unguarded assignments are supported. #[macro_export] macro_rules! build_group { - ($group:ident; $($lhs:ident.$lhs_port:ident = $rhs:ident.$rhs_port:ident;)*) => { + ($group:expr; $($lhs:ident.$lhs_port:ident = $rhs:ident.$rhs_port:ident;)*) => { $( $group.borrow_mut().assign( $lhs.borrow().get(stringify!($lhs_port).to_string()), @@ -919,6 +940,23 @@ macro_rules! build_group { }; } +/// Recursively constructs control nodes. For example, +/// ``` +/// let control = build_control!( +/// [par { +/// [if comp.read_en { +/// [if tag_matches.out with check_tag_matches { +/// [read_cached] +/// } else { +/// [read_uncached] +/// }] +/// }], +/// [if comp.write_en { +/// +/// }] +/// }] +/// ); +/// ``` #[macro_export] macro_rules! build_control { ([$x:ident]) => { From a4e4a6ef1c085edb31f17c78f838567224818ab0 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Tue, 18 Jun 2024 12:33:44 -0400 Subject: [PATCH 05/14] Add better attribute support --- tools/calyx_writer/lib.rs | 107 ++++++++++++++++++++++++++++++-------- 1 file changed, 86 insertions(+), 21 deletions(-) diff --git a/tools/calyx_writer/lib.rs b/tools/calyx_writer/lib.rs index d8492ded4f..402e33653f 100644 --- a/tools/calyx_writer/lib.rs +++ b/tools/calyx_writer/lib.rs @@ -197,6 +197,19 @@ impl CalyxWriter for Attribute { /// A list of attributes. type Attributes = Vec; +/// Abstracts attribute functionality. +trait AttributeProvider { + fn add_attribute(&mut self, attr: Attribute); + + fn with_attribute(mut self, attr: Attribute) -> Self + where + Self: Sized, + { + self.add_attribute(attr); + self + } +} + impl CalyxWriter for Attributes { fn write( &self, @@ -241,6 +254,13 @@ impl Port { } } + /// Constructs a new port with the given `name` in a component. + /// + /// Requires: `width > 0`. + pub fn new_in_comp(name: S, width: usize) -> Self { + Self::new::(None, name, width) + } + /// Constructs a new port with the given `parent` and `name` and with /// inferred width. pub fn inferred( @@ -255,15 +275,17 @@ impl Port { } } - pub fn add_attribute(&mut self, attr: Attribute) { - self.attributes.push(attr); - } - pub fn has_inferred_width(&self) -> bool { self.width == -1 } } +impl AttributeProvider for Port { + fn add_attribute(&mut self, attr: Attribute) { + self.attributes.push(attr); + } +} + impl Display for Port { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if let Some(parent) = &self.parent { @@ -310,6 +332,12 @@ impl PortProvider for Cell { } } +impl AttributeProvider for Cell { + fn add_attribute(&mut self, attr: Attribute) { + self.attributes.push(attr); + } +} + impl CalyxWriter for Cell { fn write( &self, @@ -359,6 +387,18 @@ pub struct Assignment { guard: Guard, } +impl Assignment { + /// Constructs a new assignment guarded by `guard` between `lhs` and `rhs`. + /// + /// Requires: the assignment is well-formed. + fn new(lhs: Port, rhs: Port, guard: Guard) -> Self { + if !lhs.has_inferred_width() && !rhs.has_inferred_width() { + assert!(lhs.width == rhs.width, "port width mismatch"); + } + Self { lhs, rhs, guard } + } +} + impl CalyxWriter for Assignment { fn write( &self, @@ -419,7 +459,7 @@ impl Group { /// Adds an assignment guarded by `guard` between `lhs` and `rhs`. pub fn assign_guarded(&mut self, lhs: Port, rhs: Port, guard: Guard) { - self.assignments.push(Assignment { lhs, rhs, guard }); + self.assignments.push(Assignment::new(lhs, rhs, guard)); } } @@ -534,8 +574,10 @@ impl Control { value: ControlValue::If(port, group, then_controls, else_controls), } } +} - pub fn add_attribute(&mut self, attribute: Attribute) { +impl AttributeProvider for Control { + fn add_attribute(&mut self, attribute: Attribute) { self.attributes.push(attribute); } } @@ -624,6 +666,7 @@ impl CalyxWriter for Control { /// See `calyx_ir::Component`. pub struct Component { + attributes: Attributes, is_comb: bool, name: String, inputs: Vec, @@ -635,6 +678,28 @@ pub struct Component { } impl Component { + fn new(name: S, is_comb: bool) -> Self { + Self { + attributes: vec![], + is_comb, + name: name.to_string(), + inputs: vec![ + Port::new_in_comp("go", 1) + .with_attribute(Attribute::bool("go")), + Port::new_in_comp("clk", 1) + .with_attribute(Attribute::bool("clk")), + Port::new_in_comp("reset", 1) + .with_attribute(Attribute::bool("reset")), + ], + outputs: vec![Port::new_in_comp("done", 1) + .with_attribute(Attribute::bool("done"))], + cells: vec![], + groups: vec![], + control: Control::empty(), + continuous_assignments: vec![], + } + } + /// Sets whether the component is combinational or not. pub fn set_comb(&mut self, is_comb: bool) { self.is_comb = is_comb; @@ -642,12 +707,12 @@ impl Component { /// Adds an input port `name` to this component. pub fn add_input(&mut self, name: S, width: usize) { - self.add_port(Port::new::(None, name, width), true); + self.add_port(Port::new_in_comp(name, width), true); } /// Adds an output port `name` to this component. pub fn add_output(&mut self, name: S, width: usize) { - self.add_port(Port::new::(None, name, width), false); + self.add_port(Port::new_in_comp(name, width), false); } /// [`Component::add_input`] or [`Component::add_output`] may be more @@ -683,7 +748,7 @@ impl Component { /// `rhs`. pub fn assign(&mut self, lhs: Port, rhs: Port, guard: Guard) { self.continuous_assignments - .push(Assignment { lhs, rhs, guard }); + .push(Assignment::new(lhs, rhs, guard)); } /// Use [`declare_group!`] and [`build_group!`] instead. @@ -744,6 +809,7 @@ impl Component { fn write_ports_sig( &self, f: &mut fmt::Formatter, + indent: &mut Indent, ports: &[Port], ) -> fmt::Result { for (i, port) in ports.iter().enumerate() { @@ -751,6 +817,7 @@ impl Component { f.write_str(", ")?; } assert!(!port.has_inferred_width()); + port.attributes.write(f, indent)?; write!(f, "{}: {}", port.name, port.width)?; } Ok(()) @@ -770,6 +837,12 @@ impl PortProvider for Component { } } +impl AttributeProvider for Component { + fn add_attribute(&mut self, attribute: Attribute) { + self.attributes.push(attribute); + } +} + impl CalyxWriter for Component { fn marker(&self) -> Option { Some(Marker::unique("component", self.name.clone())) @@ -780,15 +853,16 @@ impl CalyxWriter for Component { f: &mut fmt::Formatter<'_>, indent: &mut Indent, ) -> fmt::Result { + self.attributes.write(f, indent)?; write!( f, "{}component {}(", if self.is_comb { "comb " } else { "" }, self.name )?; - self.write_ports_sig(f, &self.inputs)?; + self.write_ports_sig(f, indent, &self.inputs)?; write!(f, ") -> (")?; - self.write_ports_sig(f, &self.outputs)?; + self.write_ports_sig(f, indent, &self.outputs)?; writeln!(f, ") {{")?; self.brace_section(f, indent, "cells", |f, indent| { for cell in &self.cells { @@ -843,16 +917,7 @@ impl Program { /// Constructs an empty component named `name`. pub fn comp(&mut self, name: S) -> RRC { - let comp = rrc(Component { - is_comb: false, - name: name.to_string(), - inputs: vec![], - outputs: vec![], - cells: vec![], - groups: vec![], - control: Control::empty(), - continuous_assignments: vec![], - }); + let comp = rrc(Component::new(name, false)); self.comps.push(comp.clone()); comp } From 0494d8b4e290d31a8eff820ae135512f82da06af Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Wed, 19 Jun 2024 12:53:44 -0400 Subject: [PATCH 06/14] Refactor formatting to be more robust --- tools/calyx_writer/lib.rs | 355 ++++++++++++++++++++------------------ 1 file changed, 189 insertions(+), 166 deletions(-) diff --git a/tools/calyx_writer/lib.rs b/tools/calyx_writer/lib.rs index 402e33653f..7b06b01f08 100644 --- a/tools/calyx_writer/lib.rs +++ b/tools/calyx_writer/lib.rs @@ -21,36 +21,50 @@ fn rrc(value: T) -> RRC { Rc::new(RefCell::new(value)) } -// TODO(ethan): too lazy to find a crate that handles this -struct Indent { - current: String, - add: usize, +struct IndentFormatter<'a, 'b: 'a> { + current_indent: String, + add_indent: usize, + last_was_newline: bool, + formatter: &'a mut fmt::Formatter<'b>, } -impl Indent { - fn push(&mut self) { - for _ in 0..self.add { - self.current.push(' '); +impl<'a, 'b: 'a> IndentFormatter<'a, 'b> { + /// Constructs a new formatter managing indents of `indent` spaces in the + /// wrapped formatter `formatter`. + pub fn new(formatter: &'a mut fmt::Formatter<'b>, indent: usize) -> Self { + Self { + current_indent: String::new(), + add_indent: indent, + last_was_newline: false, + formatter, } } - fn pop(&mut self) { - for _ in 0..self.add { - self.current.pop(); + /// Adds a level of indentation. + pub fn push(&mut self) { + for _ in 0..self.add_indent { + self.current_indent.push(' '); } } - fn current(&self) -> &str { - &self.current + /// Removes a level of indentation. + pub fn pop(&mut self) { + for _ in 0..self.add_indent { + self.current_indent.pop(); + } } } -impl Default for Indent { - fn default() -> Self { - Indent { - current: String::new(), - add: 2, +impl fmt::Write for IndentFormatter<'_, '_> { + fn write_str(&mut self, s: &str) -> fmt::Result { + for c in s.chars() { + if self.last_was_newline { + self.formatter.write_str(&self.current_indent)?; + } + self.formatter.write_char(c)?; + self.last_was_newline = c == '\n'; } + Ok(()) } } @@ -107,32 +121,21 @@ trait CalyxWriter { None } - /// Writes this element to `f`. It may be assumed that `indent.current()` - /// spaces have already been written, but on any further line, these number - /// of spaces must be effected. See also [`CalyxWriter::writeln`]. - fn write( - &self, - f: &mut fmt::Formatter<'_>, - indent: &mut Indent, - ) -> fmt::Result; + /// Writes this element to `f`. See also [`CalyxWriter::writeln`]. + fn write(&self, f: &mut IndentFormatter<'_, '_>) -> fmt::Result; /// Writes this element followed by a newline and wrapped with markers if - /// the element provides them. See more information at - /// [`CalyxWriter::write`]. + /// the element provides them. See also [`CalyxWriter::write`]. /// /// Do NOT override this function. See details at [`CalyxWriter`]. - fn writeln( - &self, - f: &mut fmt::Formatter<'_>, - indent: &mut Indent, - ) -> fmt::Result { + fn writeln(&self, f: &mut IndentFormatter<'_, '_>) -> fmt::Result { if let Some(marker) = self.marker() { write!(f, "{}", marker.to_string("start"))?; - self.write(f, indent)?; + self.write(f)?; f.write_char('\n')?; write!(f, "{}", marker.to_string("end"))?; } else { - self.write(f, indent)?; + self.write(f)?; f.write_char('\n')?; } Ok(()) @@ -145,17 +148,13 @@ struct Import { } impl CalyxWriter for Import { - fn write( - &self, - f: &mut fmt::Formatter<'_>, - _indent: &mut Indent, - ) -> fmt::Result { + fn write(&self, f: &mut IndentFormatter<'_, '_>) -> fmt::Result { write!(f, "import \"{}\";", self.path) } } /// See `calyx_frontend::Attribute`. -#[derive(PartialEq, Eq, Clone)] +#[derive(PartialEq, Eq, Clone, Debug)] pub struct Attribute { name: String, value: Option, @@ -181,11 +180,7 @@ impl Attribute { } impl CalyxWriter for Attribute { - fn write( - &self, - f: &mut fmt::Formatter<'_>, - _indent: &mut Indent, - ) -> fmt::Result { + fn write(&self, f: &mut IndentFormatter<'_, '_>) -> fmt::Result { write!(f, "@{}", self.name)?; if let Some(value) = self.value { write!(f, "({})", value)?; @@ -211,13 +206,9 @@ trait AttributeProvider { } impl CalyxWriter for Attributes { - fn write( - &self, - f: &mut fmt::Formatter<'_>, - indent: &mut Indent, - ) -> fmt::Result { + fn write(&self, f: &mut IndentFormatter<'_, '_>) -> fmt::Result { for attr in self { - attr.write(f, indent)?; + attr.write(f)?; f.write_char(' ')?; } Ok(()) @@ -225,7 +216,7 @@ impl CalyxWriter for Attributes { } /// A port on a cell or a component. -#[derive(PartialEq, Eq, Clone)] +#[derive(PartialEq, Eq, Clone, Debug)] pub struct Port { attributes: Vec, /// The cell that this port belongs to, or `None` if it is in a component @@ -298,12 +289,8 @@ impl Display for Port { impl CalyxWriter for Port { /// Behaves identically to [`Port::fmt`]. Please read [`CalyxWriter::write`] /// for documentation on this function. - fn write( - &self, - f: &mut fmt::Formatter<'_>, - _indent: &mut Indent, - ) -> fmt::Result { - self.fmt(f) + fn write(&self, f: &mut IndentFormatter<'_, '_>) -> fmt::Result { + write!(f, "{}", self) } } @@ -326,6 +313,13 @@ pub struct Cell { args: Vec, } +impl Cell { + /// Whether this cell is a ref cell. + pub fn is_ref(&self) -> bool { + self.is_ref + } +} + impl PortProvider for Cell { fn get(&self, port: String) -> Port { Port::inferred(Some(self.name.clone()), port) @@ -339,12 +333,8 @@ impl AttributeProvider for Cell { } impl CalyxWriter for Cell { - fn write( - &self, - f: &mut fmt::Formatter<'_>, - indent: &mut Indent, - ) -> fmt::Result { - self.attributes.write(f, indent)?; + fn write(&self, f: &mut IndentFormatter<'_, '_>) -> fmt::Result { + self.attributes.write(f)?; write!( f, "{}{} = {}({});", @@ -361,7 +351,7 @@ impl CalyxWriter for Cell { } /// A guard for an [`Assignment`]. -#[derive(PartialEq, Eq)] +#[derive(PartialEq, Eq, Debug)] pub enum Guard { None, Constant(u64), @@ -370,17 +360,14 @@ pub enum Guard { } impl CalyxWriter for Guard { - fn write( - &self, - _f: &mut fmt::Formatter<'_>, - _indent: &mut Indent, - ) -> fmt::Result { + fn write(&self, _f: &mut IndentFormatter<'_, '_>) -> fmt::Result { // TODO Ok(()) } } /// See `calyx_ir::Assignment`. +#[derive(PartialEq, Eq, Debug)] pub struct Assignment { lhs: Port, rhs: Port, @@ -400,14 +387,10 @@ impl Assignment { } impl CalyxWriter for Assignment { - fn write( - &self, - f: &mut fmt::Formatter<'_>, - indent: &mut Indent, - ) -> fmt::Result { + fn write(&self, f: &mut IndentFormatter<'_, '_>) -> fmt::Result { write!(f, "{} = ", self.lhs)?; if self.guard != Guard::None { - self.guard.write(f, indent)?; + self.guard.write(f)?; write!(f, " ? ")?; } write!(f, "{};", self.rhs) @@ -415,6 +398,7 @@ impl CalyxWriter for Assignment { } /// See `calyx_ir::Group`. Contains optional documentation. +#[derive(PartialEq, Eq, Debug)] pub struct Group { name: String, description: Option, @@ -470,14 +454,10 @@ impl PortProvider for Group { } impl CalyxWriter for Group { - fn write( - &self, - f: &mut fmt::Formatter<'_>, - indent: &mut Indent, - ) -> fmt::Result { + fn write(&self, f: &mut IndentFormatter<'_, '_>) -> fmt::Result { if let Some(description) = &self.description { for line in description.lines() { - write!(f, "// {}\n{}", line, indent.current())?; + writeln!(f, "// {}", line)?; } } if let Some(latency) = self.latency { @@ -487,17 +467,17 @@ impl CalyxWriter for Group { write!(f, "comb ")?; } writeln!(f, "group {} {{", self.name)?; - indent.push(); + f.push(); for assignment in &self.assignments { - write!(f, "{}", indent.current())?; - assignment.writeln(f, indent)?; + assignment.writeln(f)?; } - indent.pop(); - write!(f, "{}}}", indent.current()) + f.pop(); + write!(f, "}}") } } /// Helper variant with [`Control`]. +#[derive(PartialEq, Eq, Debug)] enum ControlValue { Empty, Enable(RRC), @@ -508,6 +488,7 @@ enum ControlValue { } /// Structured calyx control with attributes. +#[derive(PartialEq, Eq, Debug)] pub struct Control { attributes: Attributes, value: ControlValue, @@ -583,12 +564,8 @@ impl AttributeProvider for Control { } impl CalyxWriter for Control { - fn write( - &self, - f: &mut fmt::Formatter<'_>, - indent: &mut Indent, - ) -> fmt::Result { - self.attributes.write(f, indent)?; + fn write(&self, f: &mut IndentFormatter<'_, '_>) -> fmt::Result { + self.attributes.write(f)?; match &self.value { ControlValue::Empty => {} ControlValue::Enable(group) => { @@ -596,23 +573,21 @@ impl CalyxWriter for Control { } ControlValue::Seq(body) => { writeln!(f, "seq {{")?; - indent.push(); + f.push(); for node in body { - write!(f, "{}", indent.current())?; - node.writeln(f, indent)?; + node.writeln(f)?; } - indent.pop(); - write!(f, "{}}}", indent.current())?; + f.pop(); + write!(f, "}}")?; } ControlValue::Par(body) => { writeln!(f, "par {{")?; - indent.push(); + f.push(); for node in body { - write!(f, "{}", indent.current())?; - node.writeln(f, indent)?; + node.writeln(f)?; } - indent.pop(); - write!(f, "{}}}", indent.current())?; + f.pop(); + write!(f, "}}")?; } ControlValue::While(cond, group, body) => { writeln!( @@ -625,13 +600,12 @@ impl CalyxWriter for Control { "".into() } )?; - indent.push(); + f.push(); for node in body { - write!(f, "{}", indent.current())?; - node.writeln(f, indent)?; + node.writeln(f)?; } - indent.pop(); - write!(f, "{}}}", indent.current())?; + f.pop(); + write!(f, "}}")?; } ControlValue::If(cond, group, body_true, body_false) => { writeln!( @@ -644,20 +618,18 @@ impl CalyxWriter for Control { "".into() } )?; - indent.push(); + f.push(); for node in body_true { - write!(f, "{}", indent.current())?; - node.writeln(f, indent)?; + node.writeln(f)?; } - indent.pop(); - writeln!(f, "{}}} else {{", indent.current())?; - indent.push(); + f.pop(); + writeln!(f, "}} else {{")?; + f.push(); for node in body_false { - write!(f, "{}", indent.current())?; - node.writeln(f, indent)?; + node.writeln(f)?; } - indent.pop(); - write!(f, "{}}}", indent.current())?; + f.pop(); + write!(f, "}}")?; } } Ok(()) @@ -729,12 +701,13 @@ impl Component { /// arguments `args`. pub fn cell( &mut self, + is_ref: bool, name: S, inst: T, args: Vec, ) -> RRC { let cell = rrc(Cell { - is_ref: false, + is_ref, attributes: vec![], name: name.to_string(), inst: inst.to_string(), @@ -783,21 +756,20 @@ impl Component { /// ``` fn brace_section( &self, - f: &mut fmt::Formatter, - indent: &mut Indent, + f: &mut IndentFormatter<'_, '_>, name: S, body: F, ) -> fmt::Result where - F: FnOnce(&mut fmt::Formatter, &mut Indent) -> fmt::Result, + F: FnOnce(&mut IndentFormatter<'_, '_>) -> fmt::Result, { - indent.push(); - writeln!(f, "{}{} {{", indent.current(), name.to_string(),)?; - indent.push(); - let result = body(f, indent); - indent.pop(); - writeln!(f, "{}}}", indent.current())?; - indent.pop(); + f.push(); + writeln!(f, "{} {{", name.to_string(),)?; + f.push(); + let result = body(f); + f.pop(); + writeln!(f, "}}")?; + f.pop(); result } @@ -808,8 +780,7 @@ impl Component { /// Requires: all ports in `ports` must have definite widths. fn write_ports_sig( &self, - f: &mut fmt::Formatter, - indent: &mut Indent, + f: &mut IndentFormatter<'_, '_>, ports: &[Port], ) -> fmt::Result { for (i, port) in ports.iter().enumerate() { @@ -817,7 +788,7 @@ impl Component { f.write_str(", ")?; } assert!(!port.has_inferred_width()); - port.attributes.write(f, indent)?; + port.attributes.write(f)?; write!(f, "{}: {}", port.name, port.width)?; } Ok(()) @@ -848,44 +819,34 @@ impl CalyxWriter for Component { Some(Marker::unique("component", self.name.clone())) } - fn write( - &self, - f: &mut fmt::Formatter<'_>, - indent: &mut Indent, - ) -> fmt::Result { - self.attributes.write(f, indent)?; + fn write(&self, f: &mut IndentFormatter<'_, '_>) -> fmt::Result { + self.attributes.write(f)?; write!( f, "{}component {}(", if self.is_comb { "comb " } else { "" }, self.name )?; - self.write_ports_sig(f, indent, &self.inputs)?; + self.write_ports_sig(f, &self.inputs)?; write!(f, ") -> (")?; - self.write_ports_sig(f, indent, &self.outputs)?; + self.write_ports_sig(f, &self.outputs)?; writeln!(f, ") {{")?; - self.brace_section(f, indent, "cells", |f, indent| { + self.brace_section(f, "cells", |f| { for cell in &self.cells { - write!(f, "{}", indent.current())?; - cell.borrow().writeln(f, indent)?; + cell.borrow().writeln(f)?; } Ok(()) })?; - self.brace_section(f, indent, "wires", |f, indent| { + self.brace_section(f, "wires", |f| { for group in &self.groups { - write!(f, "{}", indent.current())?; - group.borrow().writeln(f, indent)?; + group.borrow().writeln(f)?; } for assignment in &self.continuous_assignments { - write!(f, "{}", indent.current())?; - assignment.writeln(f, indent)?; + assignment.writeln(f)?; } Ok(()) })?; - self.brace_section(f, indent, "control", |f, indent| { - write!(f, "{}", indent.current())?; - self.control.writeln(f, indent) - })?; + self.brace_section(f, "control", |f| self.control.writeln(f))?; write!(f, "}}") } } @@ -924,16 +885,12 @@ impl Program { } impl CalyxWriter for Program { - fn write( - &self, - f: &mut fmt::Formatter<'_>, - indent: &mut Indent, - ) -> fmt::Result { + fn write(&self, f: &mut IndentFormatter<'_, '_>) -> fmt::Result { for import in &self.imports { - import.writeln(f, indent)?; + import.writeln(f)?; } for comp in &self.comps { - comp.borrow().writeln(f, indent)?; + comp.borrow().writeln(f)?; } Ok(()) } @@ -941,7 +898,8 @@ impl CalyxWriter for Program { impl Display for Program { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.write(f, &mut Indent::default()) + let mut f = IndentFormatter::new(f, 2); + self.write(&mut f) } } @@ -949,17 +907,21 @@ impl Display for Program { /// ``` /// build_cells!(comp; /// a = std_reg(32); -/// b = std_add(32); +/// ref b = std_add(32); /// ) /// ``` /// Remember to import (via [`Program::import`]) the necessary primitives. #[macro_export] macro_rules! build_cells { - ($comp:ident; $($name:ident = $cell:ident($($args:expr),*);)*) => { - $( - let $name = $comp.borrow_mut().cell(stringify!($name), stringify!($cell), vec![$($args as u64),*]); - )* + ($comp:ident; ref $name:ident = $cell:ident($($args:expr),*); $($rest:tt)*) => { + let $name = $comp.borrow_mut().cell(true, stringify!($name), stringify!($cell), vec![$($args as u64),*]); + build_cells!($comp; $($rest)*); + }; + ($comp:ident; $name:ident = $cell:ident($($args:expr),*); $($rest:tt)*) => { + let $name = $comp.borrow_mut().cell(false, stringify!($name), stringify!($cell), vec![$($args as u64),*]); + build_cells!($comp; $($rest)*); }; + ($comp:ident;) => {}; } /// `declare_group!(comp; group name)` declares `name` as a group and binds it @@ -1027,6 +989,9 @@ macro_rules! build_control { ([$x:ident]) => { Control::enable($x.clone()) }; + (($c:expr)) => { + $c + }; ([seq { $($x:tt),+ }]) => { Control::seq(vec![$(build_control!($x)),*]) }; @@ -1052,3 +1017,61 @@ macro_rules! build_control { Control::if_($cond.borrow().get(stringify!($port).into()), Some($comb_group.clone()), vec![$(build_control!($x_true)),*], vec![$(build_control!($x_false)),*]) }; } + +#[cfg(test)] +mod tests { + use crate::*; + + struct TestIndentFormatter; + impl Display for TestIndentFormatter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut f = IndentFormatter::new(f, 2); + writeln!(f, "hello\ngoodbye")?; + f.push(); + writeln!(f, "hello\ngoodbye")?; + f.pop(); + writeln!(f, "hello\ngoodbye") + } + } + + #[test] + fn test_indent_formatter() { + assert_eq!( + "hello\ngoodbye\n hello\n goodbye\nhello\ngoodbye\n", + TestIndentFormatter.to_string() + ); + } + + #[test] + fn test_build_cell() { + let mut prog = Program::new(); + let comp = prog.comp("test"); + build_cells!(comp; + x = std_reg(32); + ref y = std_ref(32); + ); + assert!(!x.borrow().is_ref()); + assert!(y.borrow().is_ref()); + } + + #[test] + fn test_build_control() { + let mut prog = Program::new(); + let comp = prog.comp("test"); + declare_group!(comp; group foo); + declare_group!(comp; group bar); + let control = build_control! { + [seq { + [foo], + (Control::enable(bar.clone())) + }] + }; + assert_eq!( + Control::seq(vec![ + Control::enable(foo.clone()), + Control::enable(bar.clone()) + ]), + control + ); + } +} From 117345a5d0f852717ea8b02f344c518c7dbff634 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Wed, 19 Jun 2024 12:54:11 -0400 Subject: [PATCH 07/14] Rename --- Cargo.toml | 2 +- tools/{calyx_writer => calyx-writer}/Cargo.toml | 0 tools/{calyx_writer => calyx-writer}/lib.rs | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename tools/{calyx_writer => calyx-writer}/Cargo.toml (100%) rename tools/{calyx_writer => calyx-writer}/lib.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 34010000c8..4bcf21fb4d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ members = [ "tools/cider-data-converter", "tools/calyx-pass-explorer", "tools/yxi", - "tools/calyx_writer", + "tools/calyx-writer", ] exclude = ["site"] diff --git a/tools/calyx_writer/Cargo.toml b/tools/calyx-writer/Cargo.toml similarity index 100% rename from tools/calyx_writer/Cargo.toml rename to tools/calyx-writer/Cargo.toml diff --git a/tools/calyx_writer/lib.rs b/tools/calyx-writer/lib.rs similarity index 100% rename from tools/calyx_writer/lib.rs rename to tools/calyx-writer/lib.rs From 8515bbc1b2fdbeefc379e110cc5ba888c5b881f0 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Wed, 19 Jun 2024 23:29:20 -0400 Subject: [PATCH 08/14] Implement Griffin's review --- tools/calyx-writer/lib.rs | 341 ++++++++++++++++--------------- tools/calyx-writer/tests/unit.rs | 62 ++++++ 2 files changed, 241 insertions(+), 162 deletions(-) create mode 100644 tools/calyx-writer/tests/unit.rs diff --git a/tools/calyx-writer/lib.rs b/tools/calyx-writer/lib.rs index 7b06b01f08..817b98a2d8 100644 --- a/tools/calyx-writer/lib.rs +++ b/tools/calyx-writer/lib.rs @@ -10,7 +10,10 @@ use std::{ cell::RefCell, fmt::{self, Display, Write}, + num::NonZeroU64, + path::PathBuf, rc::Rc, + str::FromStr, }; /// Shorthand for the `Rc>` pattern. @@ -21,7 +24,7 @@ fn rrc(value: T) -> RRC { Rc::new(RefCell::new(value)) } -struct IndentFormatter<'a, 'b: 'a> { +pub struct IndentFormatter<'a, 'b: 'a> { current_indent: String, add_indent: usize, last_was_newline: bool, @@ -41,14 +44,14 @@ impl<'a, 'b: 'a> IndentFormatter<'a, 'b> { } /// Adds a level of indentation. - pub fn push(&mut self) { + pub fn increase_indent(&mut self) { for _ in 0..self.add_indent { self.current_indent.push(' '); } } /// Removes a level of indentation. - pub fn pop(&mut self) { + pub fn decrease_indent(&mut self) { for _ in 0..self.add_indent { self.current_indent.pop(); } @@ -144,20 +147,24 @@ trait CalyxWriter { /// Imports a calyx file from the standard library. struct Import { - path: String, + path: PathBuf, } impl CalyxWriter for Import { fn write(&self, f: &mut IndentFormatter<'_, '_>) -> fmt::Result { - write!(f, "import \"{}\";", self.path) + write!( + f, + "import \"{}\";", + self.path.to_str().expect("invalid path") + ) } } -/// See `calyx_frontend::Attribute`. +/// See [`calyx_frontend::Attributes`](https://docs.calyxir.org/source/calyx_frontend/struct.Attributes.html). #[derive(PartialEq, Eq, Clone, Debug)] pub struct Attribute { name: String, - value: Option, + value: u64, } impl Attribute { @@ -166,33 +173,31 @@ impl Attribute { pub fn bool(name: S) -> Self { Self { name: name.to_string(), - value: None, + value: 1, } } - /// Constructs a `calyx_frontend::NumAttr`, such as `@go`, named `name`. + /// Constructs a `calyx_frontend::NumAttr`, such as `@go`, named `name`. pub fn num(name: S, value: u64) -> Self { Self { name: name.to_string(), - value: Some(value), + value, } } } impl CalyxWriter for Attribute { fn write(&self, f: &mut IndentFormatter<'_, '_>) -> fmt::Result { - write!(f, "@{}", self.name)?; - if let Some(value) = self.value { - write!(f, "({})", value)?; - } - Ok(()) + write!(f, "@{}({})", self.name, self.value) } } /// A list of attributes. type Attributes = Vec; -/// Abstracts attribute functionality. +/// Abstracts attribute functionality. This trait doesn't actually do anything, +/// but it ties together all the implementations via the type system to make +/// them more maintainable in response to API changes. trait AttributeProvider { fn add_attribute(&mut self, attr: Attribute); @@ -223,32 +228,31 @@ pub struct Port { /// signature. parent: Option, name: String, - /// A width of `-1` means the actual width is unknown/irrelevant/inferred. - width: isize, + width: Option, } impl Port { /// Constructs a new port with the given `parent` and `name`. /// - /// Requires: `width > 0`. + /// Requires: `width` is nonzero. pub fn new( parent: Option, name: T, - width: usize, + width: u64, ) -> Self { - assert!(width > 0); + assert!(width != 0); Self { attributes: vec![], parent: parent.map(|s| s.to_string()), name: name.to_string(), - width: width as isize, + width: Some(unsafe { NonZeroU64::new_unchecked(width) }), } } /// Constructs a new port with the given `name` in a component. /// /// Requires: `width > 0`. - pub fn new_in_comp(name: S, width: usize) -> Self { + pub fn new_in_comp(name: S, width: u64) -> Self { Self::new::(None, name, width) } @@ -262,12 +266,12 @@ impl Port { attributes: vec![], parent: parent.map(|s| s.to_string()), name: name.to_string(), - width: -1, + width: None, } } pub fn has_inferred_width(&self) -> bool { - self.width == -1 + self.width.is_none() } } @@ -294,12 +298,17 @@ impl CalyxWriter for Port { } } -/// Abstracts port functionality over components and cells. +/// Abstracts port functionality over components and cells. This trait doesn't +/// actually do anything, but it ties together all the implementations via the +/// type system to make them more maintainable in response to API changes. For +/// example, Griffin suggested I change the name of the member function from +/// `get` to `get_port`, which I could achieve everywhere with a simple +/// refactor. pub trait PortProvider { /// Retrieves the port on this provider named `port`. /// /// Requires: `port` exists on this provider. - fn get(&self, port: String) -> Port; + fn get_port(&self, port: String) -> Port; } /// See `calyx_ir::Cell`. @@ -321,7 +330,7 @@ impl Cell { } impl PortProvider for Cell { - fn get(&self, port: String) -> Port { + fn get_port(&self, port: String) -> Port { Port::inferred(Some(self.name.clone()), port) } } @@ -353,15 +362,22 @@ impl CalyxWriter for Cell { /// A guard for an [`Assignment`]. #[derive(PartialEq, Eq, Debug)] pub enum Guard { - None, - Constant(u64), + True, Port(Port), And(Box, Box), } impl CalyxWriter for Guard { - fn write(&self, _f: &mut IndentFormatter<'_, '_>) -> fmt::Result { - // TODO + fn write(&self, f: &mut IndentFormatter<'_, '_>) -> fmt::Result { + match self { + Guard::True => {} + Guard::Port(port) => port.write(f)?, + Guard::And(lhs, rhs) => { + lhs.write(f)?; + write!(f, " & ")?; + rhs.write(f)?; + } + } Ok(()) } } @@ -389,7 +405,7 @@ impl Assignment { impl CalyxWriter for Assignment { fn write(&self, f: &mut IndentFormatter<'_, '_>) -> fmt::Result { write!(f, "{} = ", self.lhs)?; - if self.guard != Guard::None { + if self.guard != Guard::True { self.guard.write(f)?; write!(f, " ? ")?; } @@ -438,7 +454,7 @@ impl Group { /// Adds an unguarded assignment between `lhs` and `rhs`. Behaves like /// [`Group::assign_guarded`] with [`Guard::None`] passed for the guard. pub fn assign(&mut self, lhs: Port, rhs: Port) { - self.assign_guarded(lhs, rhs, Guard::None); + self.assign_guarded(lhs, rhs, Guard::True); } /// Adds an assignment guarded by `guard` between `lhs` and `rhs`. @@ -448,7 +464,7 @@ impl Group { } impl PortProvider for Group { - fn get(&self, port: String) -> Port { + fn get_port(&self, port: String) -> Port { Port::inferred(Some(self.name.clone()), port) } } @@ -467,11 +483,11 @@ impl CalyxWriter for Group { write!(f, "comb ")?; } writeln!(f, "group {} {{", self.name)?; - f.push(); + f.increase_indent(); for assignment in &self.assignments { assignment.writeln(f)?; } - f.pop(); + f.decrease_indent(); write!(f, "}}") } } @@ -573,20 +589,20 @@ impl CalyxWriter for Control { } ControlValue::Seq(body) => { writeln!(f, "seq {{")?; - f.push(); + f.increase_indent(); for node in body { node.writeln(f)?; } - f.pop(); + f.decrease_indent(); write!(f, "}}")?; } ControlValue::Par(body) => { writeln!(f, "par {{")?; - f.push(); + f.increase_indent(); for node in body { node.writeln(f)?; } - f.pop(); + f.decrease_indent(); write!(f, "}}")?; } ControlValue::While(cond, group, body) => { @@ -600,11 +616,11 @@ impl CalyxWriter for Control { "".into() } )?; - f.push(); + f.increase_indent(); for node in body { node.writeln(f)?; } - f.pop(); + f.decrease_indent(); write!(f, "}}")?; } ControlValue::If(cond, group, body_true, body_false) => { @@ -618,17 +634,17 @@ impl CalyxWriter for Control { "".into() } )?; - f.push(); + f.increase_indent(); for node in body_true { node.writeln(f)?; } - f.pop(); + f.decrease_indent(); writeln!(f, "}} else {{")?; - f.push(); + f.increase_indent(); for node in body_false { node.writeln(f)?; } - f.pop(); + f.decrease_indent(); write!(f, "}}")?; } } @@ -651,53 +667,48 @@ pub struct Component { impl Component { fn new(name: S, is_comb: bool) -> Self { - Self { + let mut new_self = Self { attributes: vec![], is_comb, name: name.to_string(), - inputs: vec![ + inputs: vec![], + outputs: vec![], + cells: vec![], + groups: vec![], + control: Control::empty(), + continuous_assignments: vec![], + }; + if is_comb { + for input in [ Port::new_in_comp("go", 1) - .with_attribute(Attribute::bool("go")), + .with_attribute(Attribute::num("go", 1)), Port::new_in_comp("clk", 1) .with_attribute(Attribute::bool("clk")), Port::new_in_comp("reset", 1) .with_attribute(Attribute::bool("reset")), - ], - outputs: vec![Port::new_in_comp("done", 1) - .with_attribute(Attribute::bool("done"))], - cells: vec![], - groups: vec![], - control: Control::empty(), - continuous_assignments: vec![], + ] { + new_self.inputs.push(input); + } + for output in [Port::new_in_comp("done", 1) + .with_attribute(Attribute::num("done", 1))] + { + new_self.outputs.push(output); + } } - } - - /// Sets whether the component is combinational or not. - pub fn set_comb(&mut self, is_comb: bool) { - self.is_comb = is_comb; + new_self } /// Adds an input port `name` to this component. - pub fn add_input(&mut self, name: S, width: usize) { - self.add_port(Port::new_in_comp(name, width), true); + pub fn add_input(&mut self, name: S, width: u64) { + self.inputs.push(Port::new_in_comp(name, width)); } /// Adds an output port `name` to this component. - pub fn add_output(&mut self, name: S, width: usize) { - self.add_port(Port::new_in_comp(name, width), false); - } - - /// [`Component::add_input`] or [`Component::add_output`] may be more - /// useful. - pub fn add_port(&mut self, port: Port, is_input: bool) { - if is_input { - self.inputs.push(port); - } else { - self.outputs.push(port); - } + pub fn add_output(&mut self, name: S, width: u64) { + self.outputs.push(Port::new_in_comp(name, width)); } - /// Constructs a new cell named `name` that instatiates `inst` with + /// Constructs a new cell named `name` that instantiates `inst` with /// arguments `args`. pub fn cell( &mut self, @@ -763,14 +774,14 @@ impl Component { where F: FnOnce(&mut IndentFormatter<'_, '_>) -> fmt::Result, { - f.push(); + f.increase_indent(); writeln!(f, "{} {{", name.to_string(),)?; - f.push(); - let result = body(f); - f.pop(); + f.increase_indent(); + body(f)?; + f.decrease_indent(); writeln!(f, "}}")?; - f.pop(); - result + f.decrease_indent(); + Ok(()) } /// Formats a list of ports as an input or output signature. For example, @@ -789,14 +800,20 @@ impl Component { } assert!(!port.has_inferred_width()); port.attributes.write(f)?; - write!(f, "{}: {}", port.name, port.width)?; + write!( + f, + "{}: {}", + port.name, + port.width + .expect("we just checked that port has concrete width") + )?; } Ok(()) } } impl PortProvider for Component { - fn get(&self, port: String) -> Port { + fn get_port(&self, port: String) -> Port { self.inputs .iter() .chain(self.outputs.iter()) @@ -857,22 +874,21 @@ impl CalyxWriter for Component { pub struct Program { imports: Vec, /// inv: no element is removed from this array - comps: Vec>>, + comps: Vec>, } impl Program { /// Constructs an empty program. pub fn new() -> Self { - Self { - imports: vec![], - comps: vec![], - } + Self::default() } /// Imports the calyx standard library file `path`. - pub fn import(&mut self, path: S) { + /// + /// Requires: `path` is a well-formed path. + pub fn import>(&mut self, path: S) { self.imports.push(Import { - path: path.to_string(), + path: PathBuf::from_str(path.as_ref()).expect("precondition"), }); } @@ -882,6 +898,13 @@ impl Program { self.comps.push(comp.clone()); comp } + + /// Constructs an empty combinational component named `name`. + pub fn comb_comp(&mut self, name: S) -> RRC { + let comp = rrc(Component::new(name, true)); + self.comps.push(comp.clone()); + comp + } } impl CalyxWriter for Program { @@ -903,6 +926,55 @@ impl Display for Program { } } +/// Static assertion. +#[macro_export] +macro_rules! const_assert { + ($($tt:tt)*) => { + const _: () = assert!($($tt)*); + } +} + +/// Constructs static and dynamic assertions for the given primitive, if a +/// verification case exists. Due to limiations (https://github.com/rust-lang/rust/issues/85077) +/// we have to add a dummy value to the beginning of the input array, so the +/// logic must be modified accordingly (and annoyingly). +#[macro_export] +macro_rules! _validate_primitive { + (std_reg($arg_arr:expr)) => { + const_assert!( + $arg_arr.len() - 1 == 1, + "Invalid std_reg instantiation: std_reg takes 1 argument" + ); + }; + (std_add($arg_arr:expr)) => { + const_assert!( + $arg_arr.len() - 1 == 1, + "Invalid std_add instantiation: std_add takes 1 argument" + ); + }; + (comb_mem_d1($arg_arr:expr)) => { + const_assert!( + $arg_arr.len() - 1 == 3, + "Invalid comb_mem_d1 instantiation: comb_mem_d1 takes 3 arguments" + ); + }; + (seq_mem_d1($arg_arr:expr)) => { + const_assert!( + $arg_arr.len() - 1 == 3, + "Invalid seq_mem_d1 instantiation: seq_mem_d1 takes 3 arguments" + ); + }; + (std_bit_slice($arg_arr:expr)) => { + const_assert!( + $arg_arr.len() - 1 == 4, + "Invalid std_bit_slice instantiation: std_bit_slice takes 4 arguments" + ); + assert!($arg_arr[4] >= $arg_arr[1], "Invalid std_bit_slice instantiation: out_width <= in_width"); + assert!($arg_arr[4] == $arg_arr[3] - $arg_arr[2], "Invalid std_bit_slice instantiation: out_width must be end_idx - start_idx") + }; + ($idc:ident($idc2:expr)) => {}; +} + /// Similar to the `structure!` macro in `calyx_ir`. For example, /// ``` /// build_cells!(comp; @@ -914,10 +986,12 @@ impl Display for Program { #[macro_export] macro_rules! build_cells { ($comp:ident; ref $name:ident = $cell:ident($($args:expr),*); $($rest:tt)*) => { + _validate_primitive!($cell([0 as u64, $($args as u64),*])); let $name = $comp.borrow_mut().cell(true, stringify!($name), stringify!($cell), vec![$($args as u64),*]); build_cells!($comp; $($rest)*); }; ($comp:ident; $name:ident = $cell:ident($($args:expr),*); $($rest:tt)*) => { + _validate_primitive!($cell([0 as u64, $($args as u64),*])); let $name = $comp.borrow_mut().cell(false, stringify!($name), stringify!($cell), vec![$($args as u64),*]); build_cells!($comp; $($rest)*); }; @@ -954,7 +1028,7 @@ macro_rules! declare_group { /// comp.in_port = x.foo; /// ) /// ``` -/// Currently, only unguarded assignments are supported. +/// Currently, only unguarded assignments are supported. (TODO(ethan)) #[macro_export] macro_rules! build_group { ($group:expr; $($lhs:ident.$lhs_port:ident = $rhs:ident.$rhs_port:ident;)*) => { @@ -967,7 +1041,8 @@ macro_rules! build_group { }; } -/// Recursively constructs control nodes. For example, +/// Recursively constructs control nodes. You can use calyx syntax or embed +/// arbitrary expressions that are typed `Control`. For example, /// ``` /// let control = build_control!( /// [par { @@ -979,7 +1054,7 @@ macro_rules! build_group { /// }] /// }], /// [if comp.write_en { -/// +/// [(Control::empty())] /// }] /// }] /// ); @@ -987,91 +1062,33 @@ macro_rules! build_group { #[macro_export] macro_rules! build_control { ([$x:ident]) => { - Control::enable($x.clone()) + $crate::Control::enable($x.clone()) }; (($c:expr)) => { $c }; ([seq { $($x:tt),+ }]) => { - Control::seq(vec![$(build_control!($x)),*]) + $crate::Control::seq(vec![$(build_control!($x)),*]) }; ([par { $($x:tt),+ }]) => { - Control::par(vec![$(build_control!($x)),*]) + $crate::Control::par(vec![$(build_control!($x)),*]) }; ([while $cond:ident.$port:ident { $($x:tt),* }]) => { - Control::while_($cond.borrow().get(stringify!($port).into()), None, vec![$(build_control!($x)),*]) + $crate::Control::while_($cond.borrow().get(stringify!($port).into()), None, vec![$(build_control!($x)),*]) }; ([while $cond:ident.$port:ident with $comb_group:ident { $($x:tt),* }]) => { - Control::while_($cond.borrow().get(stringify!($port).into()), Some($comb_group), vec![$(build_control!($x)),*]) + $crate::Control::while_($cond.borrow().get(stringify!($port).into()), Some($comb_group), vec![$(build_control!($x)),*]) }; ([if $cond:ident.$port:ident { $($x_true:tt),* }]) => { - Control::if_($cond.borrow().get(stringify!($port).into()), None, vec![$(build_control!($x_true)),*], vec![]) + $crate::Control::if_($cond.borrow().get(stringify!($port).into()), None, vec![$(build_control!($x_true)),*], vec![]) }; ([if $cond:ident.$port:ident { $($x_true:tt),* } else { $($x_false:tt),* }]) => { - Control::if_($cond.borrow().get(stringify!($port).into()), None, vec![$(build_control!($x_true)),*], vec![$(build_control!($x_false)),*]) + $crate::Control::if_($cond.borrow().get(stringify!($port).into()), None, vec![$(build_control!($x_true)),*], vec![$(build_control!($x_false)),*]) }; ([if $cond:ident.$port:ident with $comb_group:ident { $($x_true:tt),* }=]) => { - Control::if_($cond.borrow().get(stringify!($port).into()), Some($comb_group.clone()), vec![$(build_control!($x_true)),*], vec![]) + $crate::Control::if_($cond.borrow().get(stringify!($port).into()), Some($comb_group.clone()), vec![$(build_control!($x_true)),*], vec![]) }; ([if $cond:ident.$port:ident with $comb_group:ident { $($x_true:tt),* } else { $($x_false:tt),* }]) => { - Control::if_($cond.borrow().get(stringify!($port).into()), Some($comb_group.clone()), vec![$(build_control!($x_true)),*], vec![$(build_control!($x_false)),*]) + $crate::Control::if_($cond.borrow().get(stringify!($port).into()), Some($comb_group.clone()), vec![$(build_control!($x_true)),*], vec![$(build_control!($x_false)),*]) }; } - -#[cfg(test)] -mod tests { - use crate::*; - - struct TestIndentFormatter; - impl Display for TestIndentFormatter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut f = IndentFormatter::new(f, 2); - writeln!(f, "hello\ngoodbye")?; - f.push(); - writeln!(f, "hello\ngoodbye")?; - f.pop(); - writeln!(f, "hello\ngoodbye") - } - } - - #[test] - fn test_indent_formatter() { - assert_eq!( - "hello\ngoodbye\n hello\n goodbye\nhello\ngoodbye\n", - TestIndentFormatter.to_string() - ); - } - - #[test] - fn test_build_cell() { - let mut prog = Program::new(); - let comp = prog.comp("test"); - build_cells!(comp; - x = std_reg(32); - ref y = std_ref(32); - ); - assert!(!x.borrow().is_ref()); - assert!(y.borrow().is_ref()); - } - - #[test] - fn test_build_control() { - let mut prog = Program::new(); - let comp = prog.comp("test"); - declare_group!(comp; group foo); - declare_group!(comp; group bar); - let control = build_control! { - [seq { - [foo], - (Control::enable(bar.clone())) - }] - }; - assert_eq!( - Control::seq(vec![ - Control::enable(foo.clone()), - Control::enable(bar.clone()) - ]), - control - ); - } -} diff --git a/tools/calyx-writer/tests/unit.rs b/tools/calyx-writer/tests/unit.rs new file mode 100644 index 0000000000..6915abb0c0 --- /dev/null +++ b/tools/calyx-writer/tests/unit.rs @@ -0,0 +1,62 @@ +#[cfg(test)] +mod tests { + use calyx_writer::*; + use std::fmt::{self, Display, Write}; + + struct TestIndentFormatter; + impl Display for TestIndentFormatter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut f = IndentFormatter::new(f, 2); + writeln!(f, "hello\ngoodbye")?; + f.increase_indent(); + writeln!(f, "hello\ngoodbye")?; + f.decrease_indent(); + writeln!(f, "hello\ngoodbye") + } + } + + #[test] + fn test_indent_formatter() { + assert_eq!( + "hello\ngoodbye\n hello\n goodbye\nhello\ngoodbye\n", + TestIndentFormatter.to_string() + ); + } + + #[test] + fn test_build_cell() { + let mut prog = Program::new(); + let comp = prog.comp("test"); + build_cells!(comp; + x = std_reg(32); + ref y = std_reg(32); + mem = comb_mem_d1(1, 2, 3); + le = std_le(); + ); + assert!(!x.borrow().is_ref()); + assert!(y.borrow().is_ref()); + assert!(!mem.borrow().is_ref()); + assert!(!le.borrow().is_ref()); + } + + #[test] + fn test_build_control() { + let mut prog = Program::new(); + let comp = prog.comp("test"); + declare_group!(comp; group foo); + declare_group!(comp; group bar); + let control = build_control! { + [seq { + [foo], + (Control::enable(bar.clone())) + }] + }; + assert_eq!( + Control::seq(vec![ + Control::enable(foo.clone()), + Control::enable(bar.clone()) + ]), + control + ); + } +} From e61ddab02aa5fdd547e562f0d63daa1b66949bb6 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Wed, 19 Jun 2024 23:30:00 -0400 Subject: [PATCH 09/14] Clippy --- tools/calyx-writer/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/calyx-writer/lib.rs b/tools/calyx-writer/lib.rs index 817b98a2d8..22a28a7565 100644 --- a/tools/calyx-writer/lib.rs +++ b/tools/calyx-writer/lib.rs @@ -666,6 +666,7 @@ pub struct Component { } impl Component { + #[allow(clippy::single_element_loop)] fn new(name: S, is_comb: bool) -> Self { let mut new_self = Self { attributes: vec![], From fb52eb266f75dbaa4afa561469f1df0f46e2554a Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Thu, 20 Jun 2024 16:05:48 -0400 Subject: [PATCH 10/14] Implement more Griffin changes --- tools/calyx-writer/lib.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tools/calyx-writer/lib.rs b/tools/calyx-writer/lib.rs index 22a28a7565..c231f6dfe5 100644 --- a/tools/calyx-writer/lib.rs +++ b/tools/calyx-writer/lib.rs @@ -240,12 +240,11 @@ impl Port { name: T, width: u64, ) -> Self { - assert!(width != 0); Self { attributes: vec![], parent: parent.map(|s| s.to_string()), name: name.to_string(), - width: Some(unsafe { NonZeroU64::new_unchecked(width) }), + width: Some(NonZeroU64::new(width).expect("width cannot be zero")), } } @@ -359,12 +358,16 @@ impl CalyxWriter for Cell { } } +// Griffin: To state the obvious, which I'm sure you'll add later. This is missing Or, Not, and BinOps. Also worth noting that Port's are only allowed as individual guard elements when they are 1bit values, otherwise they have to be part of some comparison operator + /// A guard for an [`Assignment`]. #[derive(PartialEq, Eq, Debug)] pub enum Guard { True, Port(Port), + Not(Box), And(Box, Box), + Or(Box, Box), } impl CalyxWriter for Guard { @@ -373,10 +376,13 @@ impl CalyxWriter for Guard { Guard::True => {} Guard::Port(port) => port.write(f)?, Guard::And(lhs, rhs) => { + write!(f, "(")?; lhs.write(f)?; write!(f, " & ")?; rhs.write(f)?; + write!(f, "(")?; } + _ => todo!("not all guards implemented") } Ok(()) } @@ -805,8 +811,7 @@ impl Component { f, "{}: {}", port.name, - port.width - .expect("we just checked that port has concrete width") + port.width.unwrap() )?; } Ok(()) @@ -889,7 +894,7 @@ impl Program { /// Requires: `path` is a well-formed path. pub fn import>(&mut self, path: S) { self.imports.push(Import { - path: PathBuf::from_str(path.as_ref()).expect("precondition"), + path: PathBuf::from_str(path.as_ref()).expect("malformed input path"), }); } From a469b166792e8362f7ca1c9ec0d1d548b9c85f70 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Thu, 20 Jun 2024 16:08:07 -0400 Subject: [PATCH 11/14] Formatting --- tools/calyx-writer/lib.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/tools/calyx-writer/lib.rs b/tools/calyx-writer/lib.rs index c231f6dfe5..06763e61b3 100644 --- a/tools/calyx-writer/lib.rs +++ b/tools/calyx-writer/lib.rs @@ -382,7 +382,7 @@ impl CalyxWriter for Guard { rhs.write(f)?; write!(f, "(")?; } - _ => todo!("not all guards implemented") + _ => todo!("not all guards implemented"), } Ok(()) } @@ -807,12 +807,7 @@ impl Component { } assert!(!port.has_inferred_width()); port.attributes.write(f)?; - write!( - f, - "{}: {}", - port.name, - port.width.unwrap() - )?; + write!(f, "{}: {}", port.name, port.width.unwrap())?; } Ok(()) } @@ -894,7 +889,8 @@ impl Program { /// Requires: `path` is a well-formed path. pub fn import>(&mut self, path: S) { self.imports.push(Import { - path: PathBuf::from_str(path.as_ref()).expect("malformed input path"), + path: PathBuf::from_str(path.as_ref()) + .expect("malformed input path"), }); } From 16ea5ce6c3c4ef70a3f8f55bf4b7ac78ebe2d62a Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Thu, 20 Jun 2024 16:10:03 -0400 Subject: [PATCH 12/14] AsRef --- tools/calyx-writer/lib.rs | 66 +++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/tools/calyx-writer/lib.rs b/tools/calyx-writer/lib.rs index 06763e61b3..d3aa524daf 100644 --- a/tools/calyx-writer/lib.rs +++ b/tools/calyx-writer/lib.rs @@ -86,28 +86,28 @@ struct Marker { impl Marker { /// A marker for an `element`. #[allow(dead_code)] - pub fn general(element: S) -> Marker { + pub fn general>(element: S) -> Marker { Marker { - element: element.to_string(), + element: element.as_ref().to_string(), id: None, } } /// A marker for a unique `element` identified by `id`. - pub fn unique(element: S, id: T) -> Marker { + pub fn unique, T: AsRef>(element: S, id: T) -> Marker { Marker { - element: element.to_string(), - id: Some(id.to_string()), + element: element.as_ref().to_string(), + id: Some(id.as_ref().to_string()), } } /// Constructs a comment string for the marker at a given `location`. For /// example, `marker.to_string("end")`. - pub fn to_string(&self, location: S) -> String { + pub fn to_string>(&self, location: S) -> String { format!( "// {} {}{}\n", self.element.to_ascii_uppercase(), - location.to_string().to_ascii_uppercase(), + location.as_ref().to_string().to_ascii_uppercase(), self.id .as_ref() .map(|id| format!(": {}", id)) @@ -170,17 +170,17 @@ pub struct Attribute { impl Attribute { /// Constructs a `calyx_frontend::BoolAttr`, such as `@external`, named /// `name`. - pub fn bool(name: S) -> Self { + pub fn bool>(name: S) -> Self { Self { - name: name.to_string(), + name: name.as_ref().to_string(), value: 1, } } /// Constructs a `calyx_frontend::NumAttr`, such as `@go`, named `name`. - pub fn num(name: S, value: u64) -> Self { + pub fn num>(name: S, value: u64) -> Self { Self { - name: name.to_string(), + name: name.as_ref().to_string(), value, } } @@ -235,15 +235,15 @@ impl Port { /// Constructs a new port with the given `parent` and `name`. /// /// Requires: `width` is nonzero. - pub fn new( + pub fn new, T: AsRef>( parent: Option, name: T, width: u64, ) -> Self { Self { attributes: vec![], - parent: parent.map(|s| s.to_string()), - name: name.to_string(), + parent: parent.map(|s| s.as_ref().to_string()), + name: name.as_ref().to_string(), width: Some(NonZeroU64::new(width).expect("width cannot be zero")), } } @@ -251,20 +251,20 @@ impl Port { /// Constructs a new port with the given `name` in a component. /// /// Requires: `width > 0`. - pub fn new_in_comp(name: S, width: u64) -> Self { + pub fn new_in_comp>(name: S, width: u64) -> Self { Self::new::(None, name, width) } /// Constructs a new port with the given `parent` and `name` and with /// inferred width. - pub fn inferred( + pub fn inferred, T: AsRef>( parent: Option, name: T, ) -> Self { Self { attributes: vec![], - parent: parent.map(|s| s.to_string()), - name: name.to_string(), + parent: parent.map(|s| s.as_ref().to_string()), + name: name.as_ref().to_string(), width: None, } } @@ -433,9 +433,9 @@ pub struct Group { impl Group { /// Constructs an empty group named `name`, combinational if and only if /// `is_comb`. - fn new(name: S, is_comb: bool) -> Self { + fn new>(name: S, is_comb: bool) -> Self { Self { - name: name.to_string(), + name: name.as_ref().to_string(), description: None, is_comb, latency: None, @@ -673,11 +673,11 @@ pub struct Component { impl Component { #[allow(clippy::single_element_loop)] - fn new(name: S, is_comb: bool) -> Self { + fn new>(name: S, is_comb: bool) -> Self { let mut new_self = Self { attributes: vec![], is_comb, - name: name.to_string(), + name: name.as_ref().to_string(), inputs: vec![], outputs: vec![], cells: vec![], @@ -706,18 +706,18 @@ impl Component { } /// Adds an input port `name` to this component. - pub fn add_input(&mut self, name: S, width: u64) { + pub fn add_input>(&mut self, name: S, width: u64) { self.inputs.push(Port::new_in_comp(name, width)); } /// Adds an output port `name` to this component. - pub fn add_output(&mut self, name: S, width: u64) { + pub fn add_output>(&mut self, name: S, width: u64) { self.outputs.push(Port::new_in_comp(name, width)); } /// Constructs a new cell named `name` that instantiates `inst` with /// arguments `args`. - pub fn cell( + pub fn cell, T: AsRef>( &mut self, is_ref: bool, name: S, @@ -727,8 +727,8 @@ impl Component { let cell = rrc(Cell { is_ref, attributes: vec![], - name: name.to_string(), - inst: inst.to_string(), + name: name.as_ref().to_string(), + inst: inst.as_ref().to_string(), args, }); self.cells.push(cell.clone()); @@ -743,14 +743,14 @@ impl Component { } /// Use [`declare_group!`] and [`build_group!`] instead. - pub fn group(&mut self, name: S) -> RRC { + pub fn group>(&mut self, name: S) -> RRC { let group = rrc(Group::new(name, false)); self.groups.push(group.clone()); group } /// Use [`declare_group!`] and [`build_group!`] instead. - pub fn comb_group(&mut self, name: S) -> RRC { + pub fn comb_group>(&mut self, name: S) -> RRC { let group = rrc(Group::new(name, true)); self.groups.push(group.clone()); group @@ -772,7 +772,7 @@ impl Component { /// } /// ... /// ``` - fn brace_section( + fn brace_section, F>( &self, f: &mut IndentFormatter<'_, '_>, name: S, @@ -782,7 +782,7 @@ impl Component { F: FnOnce(&mut IndentFormatter<'_, '_>) -> fmt::Result, { f.increase_indent(); - writeln!(f, "{} {{", name.to_string(),)?; + writeln!(f, "{} {{", name.as_ref().to_string(),)?; f.increase_indent(); body(f)?; f.decrease_indent(); @@ -895,14 +895,14 @@ impl Program { } /// Constructs an empty component named `name`. - pub fn comp(&mut self, name: S) -> RRC { + pub fn comp>(&mut self, name: S) -> RRC { let comp = rrc(Component::new(name, false)); self.comps.push(comp.clone()); comp } /// Constructs an empty combinational component named `name`. - pub fn comb_comp(&mut self, name: S) -> RRC { + pub fn comb_comp>(&mut self, name: S) -> RRC { let comp = rrc(Component::new(name, true)); self.comps.push(comp.clone()); comp From 7913374f40efdd3c3994cb2c313fd69501b83c1e Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Thu, 20 Jun 2024 16:16:55 -0400 Subject: [PATCH 13/14] Clippy --- tools/calyx-writer/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/calyx-writer/lib.rs b/tools/calyx-writer/lib.rs index d3aa524daf..cdcb6a77a3 100644 --- a/tools/calyx-writer/lib.rs +++ b/tools/calyx-writer/lib.rs @@ -782,7 +782,7 @@ impl Component { F: FnOnce(&mut IndentFormatter<'_, '_>) -> fmt::Result, { f.increase_indent(); - writeln!(f, "{} {{", name.as_ref().to_string(),)?; + writeln!(f, "{} {{", name.as_ref(),)?; f.increase_indent(); body(f)?; f.decrease_indent(); From 73267a9975a0d020b7c54cbb20e8b53bd1aa7bf1 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Fri, 21 Jun 2024 16:33:16 -0400 Subject: [PATCH 14/14] My bad --- tools/calyx-writer/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/calyx-writer/lib.rs b/tools/calyx-writer/lib.rs index cdcb6a77a3..34de6822ee 100644 --- a/tools/calyx-writer/lib.rs +++ b/tools/calyx-writer/lib.rs @@ -380,7 +380,7 @@ impl CalyxWriter for Guard { lhs.write(f)?; write!(f, " & ")?; rhs.write(f)?; - write!(f, "(")?; + write!(f, ")")?; } _ => todo!("not all guards implemented"), }