diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 22b1a6100c..077c06342f 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -193,10 +193,10 @@ jobs: - name: Build Dahlia if: steps.dahlia-cache.outputs.cache-hit != 'true' run: | - cd ./dahlia && sbt assembly + cd ./dahlia && sbt "; getHeaders; assembly" shell: bash - - name: Cache Futil dependencies + - name: Cache Calyx dependencies uses: actions/cache@v2 with: path: | @@ -228,7 +228,6 @@ jobs: uses: actions-rs/cargo@v1 with: command: build - args: --all - name: Test run: | diff --git a/.gitignore b/.gitignore index 46e0753dc2..318bdd65c6 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,5 @@ __pycache__ !.vscode/settings.json !.vscode/launch.json !.vscode/tasks.json + +./tests/correctness/exp/*.futil diff --git a/calyx-py/calyx/gen_exp.py b/calyx-py/calyx/gen_exp.py index 6e9cbbab39..2f1dbf8078 100644 --- a/calyx-py/calyx/gen_exp.py +++ b/calyx-py/calyx/gen_exp.py @@ -1,11 +1,17 @@ -from calyx.py_ast import * +from calyx.py_ast import ( + Connect, CompVar, Cell, Group, ConstantPort, CompPort, Stdlib, + Component, ThisPort, And, HolePort, Atom, Not, PortDef, SeqComp, + Enable, While, ParComp, Structure, CompInst, Invoke, Program, Control, + If, Import, CombGroup +) from calyx.utils import float_to_fixed_point from math import factorial, log2 from typing import List from fud.stages.verilator import numeric_types -def generate_fp_pow_component(width: int, int_width: int, is_signed: bool) -> Component: +def generate_fp_pow_component( + width: int, int_width: int, is_signed: bool) -> Component: """Generates a fixed point `pow` component, which computes the value x**y, where y must be an integer. """ @@ -87,12 +93,12 @@ def generate_fp_pow_component(width: int, int_width: int, is_signed: bool) -> Co ), ], ), - Group( + CombGroup( id=CompVar("cond"), connections=[ Connect(CompPort(count, "out"), CompPort(lt, "left")), - Connect(ThisPort(CompVar("integer_exp")), CompPort(lt, "right")), - Connect(ConstantPort(1, 1), HolePort(CompVar("cond"), "done")), + Connect(ThisPort(CompVar("integer_exp")), + CompPort(lt, "right")), ], ), Connect(CompPort(CompVar("pow"), "out"), ThisPort(CompVar("out"))), @@ -117,6 +123,7 @@ def generate_fp_pow_component(width: int, int_width: int, is_signed: bool) -> Co ), ) + def generate_cells( degree: int, width: int, int_width: int, is_signed: bool ) -> List[Cell]: @@ -137,7 +144,10 @@ def generate_cells( ) pow_registers = [ - Cell(CompVar(f"p{i}"), stdlib.register(width)) for i in range(2, degree + 1) + Cell( + CompVar(f"p{i}"), + stdlib.register(width) + ) for i in range(2, degree + 1) ] product_registers = [ Cell(CompVar(f"product{i}"), stdlib.register(width)) @@ -167,20 +177,26 @@ def generate_cells( ] # One extra `fp_pow` instance to compute e^{int_value}. pows = [ - Cell(CompVar(f"pow{i}"), CompInst("fp_pow", [])) for i in range(1, degree + 1) + Cell( + CompVar(f"pow{i}"), CompInst("fp_pow", []) + ) for i in range(1, degree + 1) ] reciprocal_factorials = [] for i in range(2, degree + 1): - fixed_point_value = float_to_fixed_point(1.0 / factorial(i), frac_width) + fixed_point_value = float_to_fixed_point( + 1.0 / factorial(i), frac_width) value = numeric_types.FixedPoint( str(fixed_point_value), width, int_width, is_signed=is_signed ).unsigned_integer() reciprocal_factorials.append( - Cell(CompVar(f"reciprocal_factorial{i}"), stdlib.constant(width, value)) + Cell(CompVar(f"reciprocal_factorial{i}"), stdlib.constant( + width, value)) ) # Constant values for the exponent to the fixed point `pow` function. constants = [ - Cell(CompVar(f"c{i}"), stdlib.constant(width, i)) for i in range(2, degree + 1) + Cell( + CompVar(f"c{i}"), stdlib.constant(width, i) + ) for i in range(2, degree + 1) ] + [ Cell( CompVar("one"), @@ -218,7 +234,7 @@ def generate_cells( ) pipes.append( Cell( - CompVar(f"div_pipe"), + CompVar("div_pipe"), stdlib.fixed_point_op( "div_pipe", width, int_width, frac_width, signed=is_signed ), @@ -277,7 +293,8 @@ def divide_and_conquer_sums(degree: int) -> List[Structure]: reg_rhs = CompVar(f"{register_name}{rhs}") sum = CompVar(f"sum{i + 1}") - # In the first round and first group, we add the 1st degree, the value `x` itself. + # In the first round and first group, we add the 1st degree, the + # value `x` itself. lhs = ( CompPort(CompVar("frac_x"), "out") if round == 1 and i == 0 @@ -296,7 +313,7 @@ def divide_and_conquer_sums(degree: int) -> List[Structure]: # Sums the 0th degree value, 1, and the final # sum of the divide-and-conquer. - group_name = CompVar(f"add_degree_zero") + group_name = CompVar("add_degree_zero") adder = CompVar("add1") reg = CompVar("sum1") groups.append( @@ -329,7 +346,8 @@ def generate_groups( connections=[ Connect(ConstantPort(1, 1), CompPort(input, "write_en")), Connect(ThisPort(CompVar("x")), CompPort(input, "in")), - Connect(CompPort(input, "done"), HolePort(CompVar("init"), "done")), + Connect(CompPort(input, "done"), + HolePort(CompVar("init"), "done")), ], static_delay=1, ) @@ -349,13 +367,16 @@ def generate_groups( CompPort(mult_pipe, "go"), Not(Atom(CompPort(mult_pipe, "done"))), ), - Connect(CompPort(mult_pipe, "done"), CompPort(input, "write_en")), + Connect(CompPort(mult_pipe, "done"), + CompPort(input, "write_en")), Connect(CompPort(mult_pipe, "out"), CompPort(input, "in")), - Connect(CompPort(input, "done"), HolePort(CompVar("negate"), "done")), + Connect(CompPort(input, "done"), HolePort( + CompVar("negate"), "done")), ], ) - # Initialization: split up the value `x` into its integer and fractional values. + # Initialization: split up the value `x` into its integer and fractional + # values. split_bits = Group( id=CompVar("split_bits"), connections=[ @@ -440,7 +461,8 @@ def multiply_by_reciprocal_factorial(i: int) -> Group: CompPort(mult_pipe, "go"), Not(Atom(CompPort(mult_pipe, "done"))), ), - Connect(CompPort(mult_pipe, "done"), CompPort(product, "write_en")), + Connect(CompPort(mult_pipe, "done"), + CompPort(product, "write_en")), Connect(CompPort(mult_pipe, "out"), CompPort(product, "in")), Connect(CompPort(product, "done"), HolePort(group_name, "done")), ] @@ -469,9 +491,11 @@ def final_multiply(register_id: CompVar) -> List[Group]: CompPort(mult_pipe, "go"), Not(Atom(CompPort(mult_pipe, "done"))), ), - Connect(CompPort(mult_pipe, "done"), CompPort(reg, "write_en")), + Connect(CompPort(mult_pipe, "done"), + CompPort(reg, "write_en")), Connect(CompPort(mult_pipe, "out"), CompPort(reg, "in")), - Connect(CompPort(reg, "done"), HolePort(group_name, "done")), + Connect(CompPort(reg, "done"), + HolePort(group_name, "done")), ], ) ] @@ -483,28 +507,32 @@ def final_multiply(register_id: CompVar) -> List[Group]: reciprocal = Group( id=CompVar("reciprocal"), connections=[ - Connect(CompPort(CompVar("one"), "out"), CompPort(div_pipe, "left")), + Connect(CompPort(CompVar("one"), "out"), + CompPort(div_pipe, "left")), Connect(CompPort(input, "out"), CompPort(div_pipe, "right")), Connect( ConstantPort(1, 1), CompPort(div_pipe, "go"), Not(Atom(CompPort(div_pipe, "done"))), ), - Connect(CompPort(div_pipe, "done"), CompPort(input, "write_en")), - Connect(CompPort(div_pipe, "out_quotient"), CompPort(input, "in")), + Connect(CompPort(div_pipe, "done"), + CompPort(input, "write_en")), + Connect(CompPort(div_pipe, "out_quotient"), + CompPort(input, "in")), Connect( - CompPort(input, "done"), HolePort(CompVar("reciprocal"), "done") + CompPort(input, "done"), HolePort( + CompVar("reciprocal"), "done") ), ], ) - is_negative = Group( + is_negative = CombGroup( id=CompVar("is_negative"), connections=[ - Connect(ThisPort(CompVar("x")), CompPort(CompVar("lt"), "left")), - Connect(ConstantPort(width, 0), CompPort(CompVar("lt"), "right")), - Connect(ConstantPort(1, 1), HolePort(CompVar("is_negative"), "done")), - ], - static_delay=0, + Connect(ThisPort(CompVar("x")), + CompPort(CompVar("lt"), "left")), + Connect(ConstantPort(width, 0), + CompPort(CompVar("lt"), "right")), + ] ) # Connect final value to the `out` signal of the component. @@ -547,10 +575,12 @@ def generate_control(degree: int, is_signed: bool) -> Control: ] ) ] - consume_pow = [ParComp([Enable(f"consume_pow{i}") for i in range(2, degree + 1)])] + consume_pow = [ + ParComp([Enable(f"consume_pow{i}") for i in range(2, degree + 1)])] mult_by_reciprocal = [ ParComp( - [Enable(f"mult_by_reciprocal_factorial{i}") for i in range(2, degree + 1)] + [Enable(f"mult_by_reciprocal_factorial{i}") + for i in range(2, degree + 1)] ) ] @@ -558,7 +588,8 @@ def generate_control(degree: int, is_signed: bool) -> Control: Enable_count = degree >> 1 for r in range(1, int(log2(degree) + 1)): divide_and_conquer.append( - ParComp([Enable(f"sum_round{r}_{i}") for i in range(1, Enable_count + 1)]) + ParComp([Enable(f"sum_round{r}_{i}") + for i in range(1, Enable_count + 1)]) ) Enable_count >>= 1 @@ -631,7 +662,8 @@ def generate_exp_taylor_series_approximation( if __name__ == "__main__": - import argparse, json + import argparse + import json parser = argparse.ArgumentParser( description="`exp` using a Taylor Series approximation" @@ -677,7 +709,8 @@ def generate_exp_taylor_series_approximation( outputs=[], structs=[ Cell(CompVar("t"), Stdlib().register(width)), - Cell(CompVar("x"), Stdlib().mem_d1(width, 1, 1), is_external=True), + Cell(CompVar("x"), Stdlib().mem_d1( + width, 1, 1), is_external=True), Cell( CompVar("ret"), Stdlib().mem_d1(width, 1, 1), diff --git a/calyx-py/calyx/py_ast.py b/calyx-py/calyx/py_ast.py index 244fb6968a..09f997495e 100644 --- a/calyx-py/calyx/py_ast.py +++ b/calyx-py/calyx/py_ast.py @@ -166,7 +166,7 @@ class Connect(Structure): def doc(self) -> str: source = ( self.src.doc() - if self.guard == None + if self.guard is None else f"{self.guard.doc()} ? {self.src.doc()}" ) return f"{self.dest.doc()} = {source};" @@ -180,7 +180,7 @@ class Group(Structure): def doc(self) -> str: static_delay_attr = ( - "" if self.static_delay == None else f'<"static"={self.static_delay}>' + "" if self.static_delay is None else f'<"static"={self.static_delay}>' ) return block( f"group {self.id.doc()}{static_delay_attr}", @@ -188,6 +188,18 @@ def doc(self) -> str: ) +@dataclass +class CombGroup(Structure): + id: CompVar + connections: list[Connect] + + def doc(self) -> str: + return block( + f"comb group {self.id.doc()}", + [c.doc() for c in self.connections], + ) + + @dataclass class CompInst(Emittable): id: str diff --git a/calyx/src/analysis/control_ports.rs b/calyx/src/analysis/control_ports.rs index 4c12d947c5..1a0fe3cc1a 100644 --- a/calyx/src/analysis/control_ports.rs +++ b/calyx/src/analysis/control_ports.rs @@ -1,9 +1,12 @@ use std::{collections::HashMap, rc::Rc}; +use itertools::Itertools; + use crate::ir::{self, RRC}; /// Contains a mapping from name of groups to the ports read by the control /// program. +/// The vector of ports is guaranteed to only contain unique ports. pub struct ControlPorts { used_ports: HashMap>>, } @@ -36,10 +39,12 @@ fn construct( fbranch, .. }) => { - used_ports - .entry(cond.borrow().name().clone()) - .or_default() - .push(Rc::clone(port)); + if let Some(c) = cond { + used_ports + .entry(c.borrow().name().clone()) + .or_default() + .push(Rc::clone(port)); + } construct(tbranch, used_ports); construct(fbranch, used_ports); @@ -47,10 +52,12 @@ fn construct( ir::Control::While(ir::While { cond, port, body, .. }) => { - used_ports - .entry(cond.borrow().name().clone()) - .or_default() - .push(Rc::clone(port)); + if let Some(c) = cond { + used_ports + .entry(c.borrow().name().clone()) + .or_default() + .push(Rc::clone(port)); + } construct(body, used_ports); } ir::Control::Seq(ir::Seq { stmts, .. }) @@ -64,6 +71,10 @@ impl From<&ir::Control> for ControlPorts { fn from(con: &ir::Control) -> Self { let mut used_ports = HashMap::default(); construct(con, &mut used_ports); + // Deduplicate all vectors + used_ports.values_mut().for_each(|v| { + *v = v.drain(..).unique_by(|p| p.borrow().canonical()).collect() + }); ControlPorts { used_ports } } } diff --git a/calyx/src/analysis/live_range_analysis.rs b/calyx/src/analysis/live_range_analysis.rs index 355eb9b9bc..a12b1038cc 100644 --- a/calyx/src/analysis/live_range_analysis.rs +++ b/calyx/src/analysis/live_range_analysis.rs @@ -60,6 +60,11 @@ impl Prop { fn transfer(self, gen: &Prop, kill: &Prop) -> Self { &(&self - kill) | gen } + + /// Add an element to Prop. + fn insert(&mut self, id: ir::Id) { + self.set.insert(id); + } } /// This analysis implements a parallel version of a classic liveness analysis. @@ -340,29 +345,29 @@ impl LiveRangeAnalysis { } } - fn find_gen_kill_invoke(invoke: &ir::Invoke) -> (Prop, Prop) { - let register_filter = |port: &RRC| { - if let ir::PortParent::Cell(cell_wref) = &port.borrow().parent { - cell_wref.upgrade().borrow().type_name() - == Some(&"std_reg".into()) - } else { - false + fn port_to_cell_name(port: &RRC) -> Option { + if let ir::PortParent::Cell(cell_wref) = &port.borrow().parent { + let cell = cell_wref.upgrade(); + if cell.borrow().type_name() == Some(&"std_reg".into()) { + return Some(cell.borrow().clone_name()); } - }; + } + None + } + /// Returns (reads, writes) that occur in the [ir::Invoke] statement. + fn find_gen_kill_invoke(invoke: &ir::Invoke) -> (Prop, Prop) { let reads: Prop = invoke .inputs .iter() - .filter(|(_, src)| register_filter(src)) - .map(|(_, src)| src.borrow().get_parent_name()) + .filter_map(|(_, src)| Self::port_to_cell_name(src)) .collect::>() .into(); let writes: Prop = invoke .outputs .iter() - .filter(|(_, src)| register_filter(src)) - .map(|(_, dest)| dest.borrow().get_parent_name()) + .filter_map(|(_, src)| Self::port_to_cell_name(src)) .collect::>() .into(); @@ -406,9 +411,9 @@ fn build_live_ranges( }, ), ir::Control::If(ir::If { - cond, tbranch, fbranch, + port, .. }) => { // compute each branch @@ -423,18 +428,15 @@ fn build_live_ranges( build_live_ranges(fbranch, alive, gens, kills, lr); // take union - let alive = &t_alive | &f_alive; + let mut alive = &t_alive | &f_alive; let gens = &t_gens | &f_gens; let kills = &t_kills | &f_kills; // feed to condition to compute - build_live_ranges( - &ir::Control::enable(cond.clone()), - alive, - gens, - kills, - lr, - ) + if let Some(cell) = LiveRangeAnalysis::port_to_cell_name(port) { + alive.insert(cell) + } + (alive, gens, kills) } ir::Control::Par(ir::Par { stmts, .. }) => { let (alive, gens, kills) = stmts @@ -463,16 +465,12 @@ fn build_live_ranges( let alive = alive.transfer(&gens, &kills); (alive, gens, kills) } - ir::Control::While(ir::While { body, cond, .. }) => { - let (alive, gens, kills) = + ir::Control::While(ir::While { body, port, .. }) => { + let (mut alive, gens, kills) = build_live_ranges(body, alive, gens, kills, lr); - let (alive, gens, kills) = build_live_ranges( - &ir::Control::enable(cond.clone()), - alive, - gens, - kills, - lr, - ); + if let Some(cell) = LiveRangeAnalysis::port_to_cell_name(port) { + alive.insert(cell) + } build_live_ranges(body, alive, gens, kills, lr) } } diff --git a/calyx/src/analysis/reaching_defns.rs b/calyx/src/analysis/reaching_defns.rs index 80baa9f14e..01464a798e 100644 --- a/calyx/src/analysis/reaching_defns.rs +++ b/calyx/src/analysis/reaching_defns.rs @@ -6,7 +6,6 @@ use std::cmp::{Ord, PartialOrd}; use std::{ collections::{BTreeMap, BTreeSet, HashMap}, ops::BitOr, - rc::Rc, }; const INVOKE_PREFIX: &str = "__invoke_"; @@ -326,17 +325,15 @@ fn build_reaching_def( (par_exit_defs, &global_killed | &killed) } ir::Control::If(ir::If { - tbranch, - fbranch, - cond, - .. + tbranch, fbranch, .. }) => { - let fake_enable = ir::Control::Enable(ir::Enable { - attributes: ir::Attributes::default(), - group: Rc::clone(cond), - }); - let (post_cond_def, post_cond_killed) = - build_reaching_def(&fake_enable, reach, killed, rd, counter); + let (post_cond_def, post_cond_killed) = build_reaching_def( + &ir::Control::empty(), + reach, + killed, + rd, + counter, + ); let (t_case_def, t_case_killed) = build_reaching_def( tbranch, post_cond_def.clone(), @@ -353,13 +350,9 @@ fn build_reaching_def( ); (&t_case_def | &f_case_def, &t_case_killed | &f_case_killed) } - ir::Control::While(ir::While { cond, body, .. }) => { - let fake_enable = ir::Control::Enable(ir::Enable { - attributes: ir::Attributes::default(), - group: Rc::clone(cond), - }); + ir::Control::While(ir::While { body, .. }) => { let (post_cond_def, post_cond_killed) = build_reaching_def( - &fake_enable, + &ir::Control::empty(), reach.clone(), killed, rd, @@ -377,7 +370,7 @@ fn build_reaching_def( remove_entries_defined_by(&mut round_1_killed, &reach); let (post_cond2_def, post_cond2_killed) = build_reaching_def( - &fake_enable, + &ir::Control::empty(), &round_1_def | &reach, round_1_killed, rd, diff --git a/calyx/src/analysis/schedule_conflicts.rs b/calyx/src/analysis/schedule_conflicts.rs index 226c30322e..d0b8da7220 100644 --- a/calyx/src/analysis/schedule_conflicts.rs +++ b/calyx/src/analysis/schedule_conflicts.rs @@ -121,14 +121,22 @@ fn build_conflict_graph( fbranch, .. }) => { - all_enables.push(cond.clone_name()); - confs.add_node(cond.borrow().name()); + // XXX (rachit): This might be incorrect since cond is a combinational + // group + if let Some(c) = cond { + all_enables.push(c.clone_name()); + confs.add_node(c.borrow().name()); + } build_conflict_graph(tbranch, confs, all_enables); build_conflict_graph(fbranch, confs, all_enables); } ir::Control::While(ir::While { cond, body, .. }) => { - all_enables.push(cond.clone_name()); - confs.add_node(cond.borrow().name()); + // XXX (rachit): This might be incorrect since cond is a combinational + // group + if let Some(c) = cond { + all_enables.push(c.clone_name()); + confs.add_node(c.borrow().name()); + } build_conflict_graph(body, confs, all_enables); } ir::Control::Par(ir::Par { stmts, .. }) => { diff --git a/calyx/src/default_passes.rs b/calyx/src/default_passes.rs index b808a5573b..21e60500b6 100644 --- a/calyx/src/default_passes.rs +++ b/calyx/src/default_passes.rs @@ -1,10 +1,10 @@ //! Defines the default passes available to [PassManager]. use crate::passes::{ - ClkInsertion, CollapseControl, CompileControl, CompileEmpty, CompileInvoke, - ComponentInterface, DeadCellRemoval, Externalize, GoInsertion, - GuardCanonical, InferStaticTiming, Inliner, LowerGuards, MergeAssign, - MinimizeRegs, Papercut, ParToSeq, RegisterUnsharing, RemoveCombGroups, - ResetInsertion, ResourceSharing, SimplifyGuards, StaticTiming, + ClkInsertion, CollapseControl, CompileEmpty, CompileInvoke, + ComponentInterface, DeadCellRemoval, DeadGroupRemoval, Externalize, + GoInsertion, GuardCanonical, InferStaticTiming, Inliner, LowerGuards, + MergeAssign, MinimizeRegs, Papercut, ParToSeq, RegisterUnsharing, + RemoveCombGroups, ResetInsertion, ResourceSharing, SimplifyGuards, SynthesisPapercut, TopDownCompileControl, WellFormed, }; use crate::{ @@ -19,8 +19,8 @@ impl PassManager { // Register passes. pm.register_pass::()?; - pm.register_pass::()?; - pm.register_pass::()?; + // pm.register_pass::()?; + // pm.register_pass::()?; pm.register_pass::()?; pm.register_pass::()?; pm.register_pass::()?; @@ -33,11 +33,13 @@ impl PassManager { pm.register_pass::()?; pm.register_pass::()?; pm.register_pass::()?; + pm.register_pass::()?; pm.register_pass::()?; pm.register_pass::()?; pm.register_pass::()?; pm.register_pass::()?; pm.register_pass::()?; + // pm.register_pass::()?; pm.register_pass::()?; pm.register_pass::()?; pm.register_pass::()?; @@ -55,13 +57,17 @@ impl PassManager { CollapseControl, ResourceSharing, MinimizeRegs, - CompileInvoke, ] ); register_alias!( pm, "compile", - [CompileEmpty, StaticTiming, TopDownCompileControl] + [ + CompileInvoke, + CompileEmpty, + // StaticTiming, + TopDownCompileControl + ] ); register_alias!(pm, "post-opt", [DeadCellRemoval]); register_alias!( @@ -89,6 +95,7 @@ impl PassManager { "external", [ "validate", + SynthesisPapercut, "pre-opt", "compile", "post-opt", diff --git a/calyx/src/frontend/ast.rs b/calyx/src/frontend/ast.rs index 1644eb457d..eb7f79d8a2 100644 --- a/calyx/src/frontend/ast.rs +++ b/calyx/src/frontend/ast.rs @@ -251,6 +251,7 @@ pub struct Group { pub name: ir::Id, pub wires: Vec, pub attributes: ir::Attributes, + pub is_comb: bool, } /// Data for the `->` structure statement. @@ -286,7 +287,7 @@ pub enum Control { port: Port, /// Modules that need to be enabled to send signal on `port`. - cond: ir::Id, + cond: Option, /// Control for the true branch. tbranch: Box, @@ -303,7 +304,7 @@ pub enum Control { port: Port, /// Modules that need to be enabled to send signal on `port`. - cond: ir::Id, + cond: Option, /// Control for the loop body. body: Box, diff --git a/calyx/src/frontend/parser.rs b/calyx/src/frontend/parser.rs index 880a01d2ef..f9c1cf32b9 100644 --- a/calyx/src/frontend/parser.rs +++ b/calyx/src/frontend/parser.rs @@ -91,6 +91,10 @@ impl CalyxParser { Ok(()) } + fn comb(_input: Node) -> ParseResult<()> { + Ok(()) + } + // ================ Literals ===================== fn identifier(input: Node) -> ParseResult { Ok(ir::Id::new( @@ -209,13 +213,21 @@ impl CalyxParser { [string_lit(key), bitwidth(num)] => (key, num) )) } - fn attributes(input: Node) -> ParseResult { Ok(match_nodes!( input.into_children(); [attribute(kvs)..] => kvs.collect::>().into() )) } + fn name_with_attribute( + input: Node, + ) -> ParseResult<(ir::Id, ir::Attributes)> { + Ok(match_nodes!( + input.into_children(); + [identifier(name), attributes(attrs)] => (name, attrs), + [identifier(name)] => (name, vec![].into()), + )) + } fn attr_val(input: Node) -> ParseResult { Ok(match_nodes!( @@ -292,7 +304,7 @@ impl CalyxParser { fn signature(input: Node) -> ParseResult> { Ok(match_nodes!( input.into_children(); - // XXX(rachit): We expect the signature to be extended to have `go`, + // NOTE(rachit): We expect the signature to be extended to have `go`, // `done`, and `clk`. [] => Vec::with_capacity(3), [inputs(ins)] => ins , @@ -303,34 +315,33 @@ impl CalyxParser { )) } - // ==============PortDeftives ===================== + // ==============Primitives===================== + fn sig_with_params( + input: Node, + ) -> ParseResult<(Vec, Vec)> { + Ok(match_nodes!( + input.into_children(); + [params(p), signature(s)] => (p, s), + [signature(s)] => (vec![], s), + )) + } fn primitive(input: Node) -> ParseResult { Ok(match_nodes!( input.into_children(); - [identifier(name), attributes(attrs), params(p), signature(s)] => ir::Primitive { + [name_with_attribute((name, attrs)), sig_with_params((p, s))] => ir::Primitive { name, params: p, signature: s, attributes: attrs, + is_comb: false, }, - [identifier(name), attributes(attrs), signature(s)] => ir::Primitive { - name, - params: Vec::with_capacity(0), - signature: s, - attributes: attrs, - }, - [identifier(name), params(p), signature(s)] => ir::Primitive { + [comb(_), name_with_attribute((name, attrs)), sig_with_params((p, s))] => ir::Primitive { name, params: p, signature: s, - attributes: ir::Attributes::default() + attributes: attrs, + is_comb: true, }, - [identifier(name), signature(s)] => ir::Primitive { - name, - params: Vec::with_capacity(0), - signature: s, - attributes: ir::Attributes::default() - } )) } @@ -488,15 +499,17 @@ impl CalyxParser { fn group(input: Node) -> ParseResult { Ok(match_nodes!( input.into_children(); - [identifier(name), attributes(attrs), wire(wire)..] => ast::Group { + [name_with_attribute((name, attrs)), wire(wire)..] => ast::Group { name, attributes: attrs, - wires: wire.collect() + wires: wire.collect(), + is_comb: false, }, - [identifier(name), wire(wire)..] => ast::Group { + [comb(_), name_with_attribute((name, attrs)), wire(wire)..] => ast::Group { name, - attributes: ir::Attributes::default(), - wires: wire.collect() + attributes: attrs, + wires: wire.collect(), + is_comb: true, } )) } @@ -576,17 +589,25 @@ impl CalyxParser { )) } + fn port_with(input: Node) -> ParseResult<(ast::Port, Option)> { + Ok(match_nodes!( + input.into_children(); + [port(port), identifier(cond)] => (port, Some(cond)), + [port(port)] => (port, None), + )) + } + fn if_stmt(input: Node) -> ParseResult { Ok(match_nodes!( input.into_children(); - [at_attributes(attrs), port(port), identifier(cond), block(stmt)] => ast::Control::If { + [at_attributes(attrs), port_with((port, cond)), block(stmt)] => ast::Control::If { port, cond, tbranch: Box::new(stmt), fbranch: Box::new(ast::Control::Empty{}), attributes: attrs, }, - [at_attributes(attrs), port(port), identifier(cond), block(tbranch), block(fbranch)] => + [at_attributes(attrs), port_with((port, cond)), block(tbranch), block(fbranch)] => ast::Control::If { port, cond, @@ -594,7 +615,7 @@ impl CalyxParser { fbranch: Box::new(fbranch), attributes: attrs, }, - [at_attributes(attrs), port(port), identifier(cond), block(tbranch), if_stmt(fbranch)] => + [at_attributes(attrs), port_with((port, cond)), block(tbranch), if_stmt(fbranch)] => ast::Control::If { port, cond, @@ -609,7 +630,7 @@ impl CalyxParser { fn while_stmt(input: Node) -> ParseResult { Ok(match_nodes!( input.into_children(); - [at_attributes(attrs), port(port), identifier(cond), block(stmt)] => ast::Control::While { + [at_attributes(attrs), port_with((port, cond)), block(stmt)] => ast::Control::While { port, cond, body: Box::new(stmt), @@ -658,26 +679,7 @@ impl CalyxParser { Ok(match_nodes!( input.into_children(); [ - identifier(id), - signature(sig), - cells(cells), - connections(connections), - control(control) - ] => { - let (continuous_assignments, groups) = connections; - ast::ComponentDef { - name: id, - signature: sig, - cells, - groups, - continuous_assignments, - control, - attributes: ir::Attributes::default() - } - }, - [ - identifier(id), - attributes(attributes), + name_with_attribute((name, attributes)), signature(sig), cells(cells), connections(connections), @@ -685,7 +687,7 @@ impl CalyxParser { ] => { let (continuous_assignments, groups) = connections; ast::ComponentDef { - name: id, + name, signature: sig, cells, groups, diff --git a/calyx/src/frontend/syntax.pest b/calyx/src/frontend/syntax.pest index 2f2149e098..616c078856 100644 --- a/calyx/src/frontend/syntax.pest +++ b/calyx/src/frontend/syntax.pest @@ -37,6 +37,7 @@ bad_num = @{ ASCII_DIGIT ~ ('a'..'z' | 'A'..'Z' | '0'..'9' | "'")* } // ====== toplevel ====== +comb = { "comb" } file = { SOI @@ -50,7 +51,7 @@ extern_or_component = { } component = { - "component" ~ identifier ~ attributes? ~ signature + "component" ~ name_with_attribute ~ signature ~ "{" ~ cells ~ connections @@ -91,8 +92,11 @@ params = { "[" ~ (identifier ~ ("," ~ identifier)*)? ~ "]" } +sig_with_params = { + params? ~ signature +} primitive = { - "primitive" ~ identifier ~ attributes? ~ params? ~ signature ~ ";" + comb? ~ "primitive" ~ name_with_attribute ~ sig_with_params ~ ";" } ext = { @@ -188,6 +192,9 @@ attribute = { attributes = { "<" ~ (attribute ~ ("," ~ attribute)*) ~ ">" } +name_with_attribute = { + identifier ~ attributes? +} // @static(1) style annotation attr_val = { @@ -201,7 +208,7 @@ at_attributes = { } group = { - "group" ~ identifier ~ attributes? ~ "{" + comb? ~ "group" ~ name_with_attribute ~ "{" ~ wire* ~ "}" } @@ -243,12 +250,15 @@ block = { | stmts_without_block } +port_with = { + port ~ ("with" ~ identifier)? +} if_stmt = { - at_attributes ~ "if" ~ port ~ "with" ~ identifier ~ block ~ ("else" ~ (if_stmt | block))? + at_attributes ~ "if" ~ port_with ~ block ~ ("else" ~ (if_stmt | block))? } while_stmt = { - at_attributes ~ "while" ~ port ~ "with" ~ identifier ~ block + at_attributes ~ "while" ~ port_with ~ block } stmt = { diff --git a/calyx/src/ir/builder.rs b/calyx/src/ir/builder.rs index 650f83536a..c65df414bd 100644 --- a/calyx/src/ir/builder.rs +++ b/calyx/src/ir/builder.rs @@ -86,6 +86,26 @@ impl<'a> Builder<'a> { group } + /// Construct a combinational group + pub fn add_comb_group(&mut self, prefix: S) -> RRC + where + S: Into + ToString + Clone, + { + let name = self.component.generate_name(prefix); + + // Check if there is a group with the same name. + let group = Rc::new(RefCell::new(ir::CombGroup { + name, + attributes: ir::Attributes::default(), + assignments: vec![], + })); + + // Add the group to the component. + self.component.comb_groups.add(Rc::clone(&group)); + + group + } + /// Return reference for a constant cell associated with the (val, width) /// pair, building and adding it to the component if needed.. /// If the constant does not exist, it is added to the Context. @@ -152,6 +172,7 @@ impl<'a> Builder<'a> { ir::CellType::Primitive { name: prim_id, param_binding, + is_comb: prim.is_comb, }, ports, ); diff --git a/calyx/src/ir/component.rs b/calyx/src/ir/component.rs index 9666c28ab9..3089263c99 100644 --- a/calyx/src/ir/component.rs +++ b/calyx/src/ir/component.rs @@ -1,6 +1,6 @@ use super::{ - Assignment, Attributes, Builder, Cell, CellType, CloneName, Control, - Direction, GetName, Group, Id, RRC, + Assignment, Attributes, Builder, Cell, CellType, CloneName, CombGroup, + Control, Direction, GetName, Group, Id, RRC, }; use crate::ir::RESERVED_NAMES; use crate::utils; @@ -25,6 +25,8 @@ pub struct Component { pub cells: IdList, /// Groups of assignment wires. pub groups: IdList, + /// Groups of assignment wires. + pub comb_groups: IdList, /// The set of "continuous assignments", i.e., assignments that are always /// active. pub continuous_assignments: Vec, @@ -76,6 +78,7 @@ impl Component { signature: this_sig, cells: IdList::default(), groups: IdList::default(), + comb_groups: IdList::default(), continuous_assignments: vec![], control: Rc::new(RefCell::new(Control::empty())), namegen: utils::NameGenerator::with_prev_defined_names(prev_names), @@ -91,6 +94,14 @@ impl Component { self.groups.find(name) } + /// Return a refernece to a combination group with `name` if present. + pub fn find_comb_group(&self, name: &S) -> Option> + where + S: Clone + AsRef, + { + self.comb_groups.find(name) + } + /// Return a reference to the cell with `name` if present. pub fn find_cell(&self, name: &S) -> Option> where @@ -138,6 +149,11 @@ impl IdList { self.0.clear(); } + /// Returns true if there are no elements in the list. + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + /// Keep only the elements in the collection which satisfy the given /// predicate pub fn retain(&mut self, mut f: F) diff --git a/calyx/src/ir/context.rs b/calyx/src/ir/context.rs index f7eca6e7c2..4e0dc3c0e4 100644 --- a/calyx/src/ir/context.rs +++ b/calyx/src/ir/context.rs @@ -28,7 +28,7 @@ impl LibrarySignatures { where S: AsRef, { - &self.sigs.get(&name.as_ref().into()).unwrap_or_else(|| { + self.sigs.get(&name.as_ref().into()).unwrap_or_else(|| { panic!( "Primitive `{}` is not defined in the context.", name.as_ref() @@ -62,4 +62,7 @@ pub struct Context { pub enable_verification: bool, /// Original import statements. pub imports: Vec, + /// Extra options provided to the command line. Interperted by individual + /// passes + pub extra_opts: Vec, } diff --git a/calyx/src/ir/control.rs b/calyx/src/ir/control.rs index e9fda0b570..bb11756704 100644 --- a/calyx/src/ir/control.rs +++ b/calyx/src/ir/control.rs @@ -1,4 +1,4 @@ -use super::{Attributes, Cell, GetAttributes, Group, Id, Port, RRC}; +use super::{Attributes, Cell, CombGroup, GetAttributes, Group, Id, Port, RRC}; /// Data for the `seq` control statement. #[derive(Debug)] @@ -24,8 +24,8 @@ pub struct If { /// Port that connects the conditional check. pub port: RRC, - /// Group that makes the signal on the conditional port valid. - pub cond: RRC, + /// Optional combinational group attached using `with`. + pub cond: Option>, /// Control for the true branch. pub tbranch: Box, @@ -44,7 +44,7 @@ pub struct While { pub port: RRC, /// Group that makes the signal on the conditional port valid. - pub cond: RRC, + pub cond: Option>, /// Control for the loop body. pub body: Box, @@ -169,7 +169,7 @@ impl Control { /// Convience constructor for if pub fn if_( port: RRC, - cond: RRC, + cond: Option>, tbranch: Box, fbranch: Box, ) -> Self { @@ -185,7 +185,7 @@ impl Control { /// Convience constructor for while pub fn while_( port: RRC, - cond: RRC, + cond: Option>, body: Box, ) -> Self { Control::While(While { diff --git a/calyx/src/ir/from_ast.rs b/calyx/src/ir/from_ast.rs index b4006dd2c1..e89a196c49 100644 --- a/calyx/src/ir/from_ast.rs +++ b/calyx/src/ir/from_ast.rs @@ -142,6 +142,7 @@ pub fn ast_to_ir( imports: namespace.imports, enable_verification, synthesis_mode, + extra_opts: vec![], }) } @@ -276,16 +277,30 @@ fn add_cell(cell: ast::Cell, sig_ctx: &SigCtx, builder: &mut Builder) { ///////////////// Group Construction ///////////////////////// -/// Build an IR group using the AST Group. +/// Build an [ir::Group] from an [ast::Group] and attach it to the [ir::Compoennt] +/// associated with the [ir::Builder] fn add_group(group: ast::Group, builder: &mut Builder) -> CalyxResult<()> { - let ir_group = builder.add_group(group.name); - ir_group.borrow_mut().attributes = group.attributes; + if group.is_comb { + let ir_group = builder.add_comb_group(group.name); + let assigns = group + .wires + .into_iter() + .map(|assign| build_assignment(assign, builder)) + .collect::>>()?; - // Add assignemnts to the group - for wire in group.wires { - let assign = build_assignment(wire, builder)?; - ir_group.borrow_mut().assignments.push(assign) - } + ir_group.borrow_mut().attributes = group.attributes; + ir_group.borrow_mut().assignments = assigns; + } else { + let ir_group = builder.add_group(group.name); + let assigns = group + .wires + .into_iter() + .map(|assign| build_assignment(assign, builder)) + .collect::>>()?; + + ir_group.borrow_mut().attributes = group.attributes; + ir_group.borrow_mut().assignments = assigns; + }; Ok(()) } @@ -437,16 +452,24 @@ fn build_control( } ast::Control::If { port, - cond, + cond: maybe_cond, tbranch, fbranch, attributes, } => { + let group = maybe_cond + .map(|cond| { + builder.component.find_comb_group(&cond).ok_or_else(|| { + Error::Undefined( + cond.clone(), + "combinational group".to_string(), + ) + }) + }) + .transpose()?; let mut con = Control::if_( get_port_ref(port, builder.component)?, - Rc::clone(&builder.component.find_group(&cond).ok_or_else( - || Error::Undefined(cond.clone(), "group".to_string()), - )?), + group, Box::new(build_control(*tbranch, builder)?), Box::new(build_control(*fbranch, builder)?), ); @@ -455,15 +478,23 @@ fn build_control( } ast::Control::While { port, - cond, + cond: maybe_cond, body, attributes, } => { + let group = maybe_cond + .map(|cond| { + builder.component.find_comb_group(&cond).ok_or_else(|| { + Error::Undefined( + cond.clone(), + "combinational group".to_string(), + ) + }) + }) + .transpose()?; let mut con = Control::while_( get_port_ref(port, builder.component)?, - Rc::clone(&builder.component.find_group(&cond).ok_or_else( - || Error::Undefined(cond.clone(), "group".to_string()), - )?), + group, Box::new(build_control(*body, builder)?), ); *(con.get_mut_attributes().unwrap()) = attributes; diff --git a/calyx/src/ir/mod.rs b/calyx/src/ir/mod.rs index 6291f4bdc0..cc71b375ca 100644 --- a/calyx/src/ir/mod.rs +++ b/calyx/src/ir/mod.rs @@ -33,8 +33,8 @@ pub use primitives::{PortDef, Primitive, Width}; pub use printer::IRPrinter; pub use reserved_names::RESERVED_NAMES; pub use structure::{ - Assignment, Binding, Cell, CellIterator, CellType, CloneName, Direction, - GetName, Group, Port, PortIterator, PortParent, + Assignment, Binding, Cell, CellIterator, CellType, CloneName, CombGroup, + Direction, GetName, Group, Port, PortIterator, PortParent, }; /// Visitor to traverse a control program. diff --git a/calyx/src/ir/primitives.rs b/calyx/src/ir/primitives.rs index 15021159d9..9bb353ce49 100644 --- a/calyx/src/ir/primitives.rs +++ b/calyx/src/ir/primitives.rs @@ -14,7 +14,7 @@ use smallvec::SmallVec; /// ); /// ``` /// -/// The signature of a port is represented using [`PortDef`] which also specify +/// The signature of a port is represented using [PortDef] which also specify /// the direction of the port. #[derive(Clone, Debug)] pub struct Primitive { @@ -26,6 +26,8 @@ pub struct Primitive { pub signature: Vec, /// Key-value attributes for this primitive. pub attributes: Attributes, + /// True iff this is a combinational primitive + pub is_comb: bool, } impl Primitive { diff --git a/calyx/src/ir/printer.rs b/calyx/src/ir/printer.rs index 5707b6e85c..0e8f99505e 100644 --- a/calyx/src/ir/printer.rs +++ b/calyx/src/ir/printer.rs @@ -101,6 +101,10 @@ impl IRPrinter { Self::write_group(&group.borrow(), 4, f)?; writeln!(f)?; } + for comb_group in comp.comb_groups.iter() { + Self::write_comb_group(&comb_group.borrow(), 4, f)?; + writeln!(f)?; + } // Write the continuous assignments for assign in &comp.continuous_assignments { Self::write_assignment(assign, 4, f)?; @@ -182,6 +186,26 @@ impl IRPrinter { write!(f, "{};", Self::get_port_access(&assign.src.borrow())) } + /// Format and write a combinational group. + pub fn write_comb_group( + group: &ir::CombGroup, + indent_level: usize, + f: &mut F, + ) -> io::Result<()> { + write!(f, "{}", " ".repeat(indent_level))?; + write!(f, "comb group {}", group.name().id)?; + if !group.attributes.is_empty() { + write!(f, "{}", Self::format_attributes(&group.attributes))?; + } + writeln!(f, " {{")?; + + for assign in &group.assignments { + Self::write_assignment(assign, indent_level + 2, f)?; + writeln!(f)?; + } + write!(f, "{}}}", " ".repeat(indent_level)) + } + /// Format and write a group. pub fn write_group( group: &ir::Group, @@ -287,12 +311,11 @@ impl IRPrinter { if !attributes.is_empty() { write!(f, "{} ", Self::format_at_attributes(attributes))? } - writeln!( - f, - "if {} with {} {{", - Self::get_port_access(&port.borrow()), - cond.borrow().name().id - )?; + write!(f, "if {} ", Self::get_port_access(&port.borrow()),)?; + if let Some(c) = cond { + write!(f, "with {} ", c.borrow().name.id)?; + } + writeln!(f, "{{")?; Self::write_control(tbranch, indent_level + 2, f)?; write!(f, "{}}}", " ".repeat(indent_level))?; if let ir::Control::Empty(_) = **fbranch { @@ -312,12 +335,11 @@ impl IRPrinter { if !attributes.is_empty() { write!(f, "{} ", Self::format_at_attributes(attributes))? } - writeln!( - f, - "while {} with {} {{", - Self::get_port_access(&port.borrow()), - cond.borrow().name().id - )?; + write!(f, "while {} ", Self::get_port_access(&port.borrow()),)?; + if let Some(c) = cond { + write!(f, "with {} ", c.borrow().name.id)?; + } + writeln!(f, "{{")?; Self::write_control(body, indent_level + 2, f)?; writeln!(f, "{}}}", " ".repeat(indent_level)) } diff --git a/calyx/src/ir/structure.rs b/calyx/src/ir/structure.rs index c74b5199e8..03ba84662b 100644 --- a/calyx/src/ir/structure.rs +++ b/calyx/src/ir/structure.rs @@ -126,6 +126,8 @@ pub enum CellType { name: Id, /// Bindings for the parameters. Uses Vec to retain the input order. param_binding: Binding, + /// True iff this is a combinational primitive + is_comb: bool, }, /// Cell constructed using a Calyx component Component { @@ -153,7 +155,7 @@ pub struct Cell { /// Underlying type for this cell pub prototype: CellType, /// Attributes for this group. - pub(super) attributes: Attributes, + pub attributes: Attributes, } impl GetAttributes for Cell { @@ -273,13 +275,13 @@ impl Cell { &self.name } - /// Returns a reference to all [ir::Port] attached to this cells. + /// Returns a reference to all [super::Port] attached to this cells. pub fn ports(&self) -> &SmallVec<[RRC; 10]> { &self.ports } } -/// Generic wrapper for iterators that return [RRC] of [ir::Cell]. +/// Generic wrapper for iterators that return [RRC] of [super::Cell]. pub struct CellIterator<'a> { pub port_iter: Box> + 'a>, } @@ -346,6 +348,29 @@ impl Group { }) } + /// The name of this group. + #[inline] + pub fn name(&self) -> &Id { + &self.name + } +} + +/// A combinational group. +/// A combinational group does not have any holes and should only contain assignments that should +/// will be combinationally active +#[derive(Debug)] +pub struct CombGroup { + /// Name of this group + pub(super) name: Id, + + /// The assignments used in this group + pub assignments: Vec, + + /// Attributes for this group. + pub attributes: Attributes, +} +impl CombGroup { + #[inline] pub fn name(&self) -> &Id { &self.name } @@ -369,6 +394,12 @@ impl GetName for Group { } } +impl GetName for CombGroup { + fn name(&self) -> &Id { + self.name() + } +} + /// A utility trait representing the ability to clone the name of an object. /// Automatically definied for anything that implements GetName pub trait CloneName { diff --git a/calyx/src/passes/compile_control.rs b/calyx/src/passes/compile_control.rs index 0c557d260a..049219aad7 100644 --- a/calyx/src/passes/compile_control.rs +++ b/calyx/src/passes/compile_control.rs @@ -5,6 +5,7 @@ use crate::ir::{ traversal::{Action, Named, VisResult, Visitor}, LibrarySignatures, }; +use crate::passes::TopDownCompileControl; use crate::{build_assignments, guard, structure}; use std::convert::TryInto; use std::rc::Rc; @@ -73,12 +74,22 @@ impl Visitor for CompileControl { comp: &mut ir::Component, ctx: &LibrarySignatures, ) -> VisResult { - let mut builder = ir::Builder::new(comp, ctx); + todo!("compile-control support for if-with") + + /* let mut builder = ir::Builder::new(comp, ctx); // create a new group for if related structure let if_group = builder.add_group("if"); - let cond_group = Rc::clone(&cif.cond); + if cif.cond.is_some() { + return Err(Error::MalformedStructure(format!( + "{}: if without `with` is not supported. Use `{}` instead", + Self::name(), + TopDownCompileControl::name() + ))); + } + + let cond_group = Rc::clone(cif.cond.as_ref().unwrap()); let cond = Rc::clone(&cif.port); // extract group names from control statement @@ -161,7 +172,7 @@ impl Visitor for CompileControl { ); comp.continuous_assignments.append(&mut cleanup_assigns); - Ok(Action::Change(ir::Control::enable(if_group))) + Ok(Action::Change(ir::Control::enable(if_group))) */ } /// XXX(rachit): The explanation is not consistent with the code. @@ -173,7 +184,8 @@ impl Visitor for CompileControl { comp: &mut ir::Component, ctx: &LibrarySignatures, ) -> VisResult { - let mut builder = ir::Builder::new(comp, ctx); + todo!() + /* let mut builder = ir::Builder::new(comp, ctx); // create group let while_group = builder.add_group("while"); @@ -253,7 +265,7 @@ impl Visitor for CompileControl { ); comp.continuous_assignments.append(&mut clean_assigns); - Ok(Action::Change(ir::Control::enable(while_group))) + Ok(Action::Change(ir::Control::enable(while_group))) */ } fn finish_seq( diff --git a/calyx/src/passes/component_interface.rs b/calyx/src/passes/component_interface.rs index 6b89417fa1..cd834d04a2 100644 --- a/calyx/src/passes/component_interface.rs +++ b/calyx/src/passes/component_interface.rs @@ -70,10 +70,10 @@ impl Visitor for ComponentInterface { } else if let ir::Control::Empty(..) = &*control { Ok(Action::Stop) } else { - Err(Error::MalformedControl( - "ComponentInterface: Structure has more than one group" - .to_string(), - )) + Err(Error::MalformedControl(format!( + "{}: Structure has more than one group", + Self::name() + ))) } } } diff --git a/calyx/src/passes/dead_cell_removal.rs b/calyx/src/passes/dead_cell_removal.rs index 236414b758..847cf02b27 100644 --- a/calyx/src/passes/dead_cell_removal.rs +++ b/calyx/src/passes/dead_cell_removal.rs @@ -58,6 +58,12 @@ impl Visitor for DeadCellRemoval { .map(|c| c.clone_name()), ) } + for cg in comp.comb_groups.iter() { + self.used_cells.extend( + &mut analysis::ReadWriteSet::uses(&cg.borrow().assignments) + .map(|c| c.clone_name()), + ) + } // All cells used in continuous assignments. self.used_cells.extend( @@ -66,8 +72,11 @@ impl Visitor for DeadCellRemoval { ); // Remove cells that are not used. - comp.cells - .retain(|c| self.used_cells.contains(c.borrow().name())); + comp.cells.retain(|c| { + let cell = c.borrow(); + cell.attributes.has("external") + || self.used_cells.contains(cell.name()) + }); Ok(Action::Stop) } diff --git a/calyx/src/passes/dead_group_removal.rs b/calyx/src/passes/dead_group_removal.rs new file mode 100644 index 0000000000..18288a4148 --- /dev/null +++ b/calyx/src/passes/dead_group_removal.rs @@ -0,0 +1,76 @@ +use crate::ir::{ + self, + traversal::{Action, Named, VisResult, Visitor}, + CloneName, LibrarySignatures, +}; +use std::collections::HashSet; + +/// Removes unused groups and combinational groups from components. +/// A group is considered in use when it shows up in an [ir::Enable]. +/// A combinational group is considered in use when it is a part of an +/// [ir::If] or [ir::While]. +#[derive(Default)] +pub struct DeadGroupRemoval { + used_groups: HashSet, + used_comb_groups: HashSet, +} + +impl Named for DeadGroupRemoval { + fn name() -> &'static str { + "dead-group-removal" + } + + fn description() -> &'static str { + "removes unsed groups from components" + } +} + +impl Visitor for DeadGroupRemoval { + fn enable( + &mut self, + s: &mut ir::Enable, + _comp: &mut ir::Component, + _sigs: &ir::LibrarySignatures, + ) -> VisResult { + self.used_groups.insert(s.group.borrow().clone_name()); + Ok(Action::Continue) + } + + fn finish_if( + &mut self, + s: &mut ir::If, + _comp: &mut ir::Component, + _sigs: &ir::LibrarySignatures, + ) -> VisResult { + if let Some(cg) = &s.cond { + self.used_comb_groups.insert(cg.borrow().clone_name()); + } + Ok(Action::Continue) + } + + fn finish_while( + &mut self, + s: &mut ir::While, + _comp: &mut ir::Component, + _sigs: &ir::LibrarySignatures, + ) -> VisResult { + if let Some(cg) = &s.cond { + self.used_comb_groups.insert(cg.borrow().clone_name()); + } + Ok(Action::Continue) + } + + fn finish( + &mut self, + comp: &mut ir::Component, + _sigs: &LibrarySignatures, + ) -> VisResult { + // Remove Groups that are not used + comp.groups + .retain(|g| self.used_groups.contains(g.borrow().name())); + comp.comb_groups + .retain(|cg| self.used_comb_groups.contains(cg.borrow().name())); + + Ok(Action::Stop) + } +} diff --git a/calyx/src/passes/infer_static_timing.rs b/calyx/src/passes/infer_static_timing.rs index fdf917f3f4..458c9cdc35 100644 --- a/calyx/src/passes/infer_static_timing.rs +++ b/calyx/src/passes/infer_static_timing.rs @@ -407,13 +407,11 @@ impl Visitor for InferStaticTiming { _comp: &mut ir::Component, _sigs: &LibrarySignatures, ) -> VisResult { - if let (Some(bound), Some(cond_time), Some(body_time)) = ( + if let (Some(bound), Some(body_time)) = ( s.attributes.get("bound").cloned(), - s.cond.borrow().attributes.get("static"), s.body.get_attributes().and_then(|attr| attr.get("static")), ) { - s.attributes - .insert("static", bound * body_time + (bound + 1) * cond_time); + s.attributes.insert("static", bound * body_time); } Ok(Action::Continue) } @@ -424,8 +422,7 @@ impl Visitor for InferStaticTiming { _comp: &mut ir::Component, _sigs: &LibrarySignatures, ) -> VisResult { - if let (Some(ctime), Some(ttime), Some(ftime)) = ( - s.cond.borrow().attributes.get("static"), + if let (Some(ttime), Some(ftime)) = ( s.tbranch .get_attributes() .and_then(|attr| attr.get("static")), @@ -433,8 +430,7 @@ impl Visitor for InferStaticTiming { .get_attributes() .and_then(|attr| attr.get("static")), ) { - s.attributes - .insert("static", ctime + 1 + cmp::max(ttime, ftime)); + s.attributes.insert("static", 1 + cmp::max(ttime, ftime)); } Ok(Action::Continue) diff --git a/calyx/src/passes/inliner.rs b/calyx/src/passes/inliner.rs index 6be75941ac..bdf600dc03 100644 --- a/calyx/src/passes/inliner.rs +++ b/calyx/src/passes/inliner.rs @@ -113,7 +113,8 @@ impl Visitor for Inliner { ir::Control::Empty(_) => return Ok(Action::Stop), ir::Control::Enable(en) => Rc::clone(&en.group), _ => return Err(Error::MalformedControl(format!( - "The hole inliner requires control to be a single enable. Try running `{}` before inlining.", + "{}: Control shoudl be a single enable. Try running `{}` before inlining.", + Self::name(), TopDownCompileControl::name())) ) }; diff --git a/calyx/src/passes/mod.rs b/calyx/src/passes/mod.rs index 14a15aaf6f..0f06fa157e 100644 --- a/calyx/src/passes/mod.rs +++ b/calyx/src/passes/mod.rs @@ -1,11 +1,11 @@ //! Passes for the Calyx compiler. mod clk_insertion; mod collapse_control; -mod compile_control; mod compile_empty; mod compile_invoke; mod component_interface; mod dead_cell_removal; +mod dead_group_removal; mod externalize; mod go_insertion; mod guard_canonical; @@ -23,18 +23,17 @@ mod reset_insertion; mod resource_sharing; mod sharing_components; mod simplify_guards; -mod static_timing; mod synthesis_papercut; mod top_down_compile_control; mod well_formed; pub use clk_insertion::ClkInsertion; pub use collapse_control::CollapseControl; -pub use compile_control::CompileControl; pub use compile_empty::CompileEmpty; pub use compile_invoke::CompileInvoke; pub use component_interface::ComponentInterface; pub use dead_cell_removal::DeadCellRemoval; +pub use dead_group_removal::DeadGroupRemoval; pub use externalize::Externalize; pub use go_insertion::GoInsertion; pub use guard_canonical::GuardCanonical; @@ -50,7 +49,6 @@ pub use remove_comb_groups::RemoveCombGroups; pub use reset_insertion::ResetInsertion; pub use resource_sharing::ResourceSharing; pub use simplify_guards::SimplifyGuards; -pub use static_timing::StaticTiming; pub use synthesis_papercut::SynthesisPapercut; pub use top_down_compile_control::TopDownCompileControl; pub use well_formed::WellFormed; diff --git a/calyx/src/passes/papercut.rs b/calyx/src/passes/papercut.rs index a41fa352c6..a500e2f3a4 100644 --- a/calyx/src/passes/papercut.rs +++ b/calyx/src/passes/papercut.rs @@ -246,6 +246,60 @@ impl Visitor for Papercut { } } - Ok(Action::Stop) + Ok(Action::Continue) + } + + fn start_while( + &mut self, + s: &mut ir::While, + _comp: &mut ir::Component, + _ctx: &LibrarySignatures, + ) -> VisResult { + if s.cond.is_none() { + let port = s.port.borrow(); + if let ir::PortParent::Cell(cell_wref) = &port.parent { + let cell_ref = cell_wref.upgrade(); + let cell = cell_ref.borrow(); + if let ir::CellType::Primitive { + is_comb, + name: prim_name, + .. + } = &cell.prototype + { + if *is_comb { + let msg = format!("Port `{}.{}` is an output port on combinational primitive `{}` and will always output 0. Add a `with` statement to the `while` statement to ensure it has a valid value during execution.", cell.name(), port.name, prim_name); + return Err(Error::Papercut(msg, cell.name().clone())); + } + } + } + } + Ok(Action::Continue) + } + + fn start_if( + &mut self, + s: &mut ir::If, + _comp: &mut ir::Component, + _ctx: &LibrarySignatures, + ) -> VisResult { + if s.cond.is_none() { + let port = s.port.borrow(); + if let ir::PortParent::Cell(cell_wref) = &port.parent { + let cell_ref = cell_wref.upgrade(); + let cell = cell_ref.borrow(); + if let ir::CellType::Primitive { + is_comb, + name: prim_name, + .. + } = &cell.prototype + { + if *is_comb { + let msg = format!("Port `{}.{}` is an output port on combinational primitive `{}` and will always output 0. Add a `with` statement to the `if` statement to ensure it has a valid value during execution.", cell.name(), port.name, prim_name); + return Err(Error::Papercut(msg, cell.name().clone())); + } + } + } + } + Ok(Action::Continue) } } diff --git a/calyx/src/passes/remove_comb_groups.rs b/calyx/src/passes/remove_comb_groups.rs index 206020e17f..2df372418f 100644 --- a/calyx/src/passes/remove_comb_groups.rs +++ b/calyx/src/passes/remove_comb_groups.rs @@ -1,9 +1,8 @@ use std::collections::HashMap; use std::rc::Rc; -use itertools::Itertools; - -use crate::errors::Error; +use crate::errors::{CalyxResult, Error}; +use crate::ir::GetAttributes; use crate::ir::{ self, traversal::{Action, Named, VisResult, Visitor}, @@ -16,6 +15,9 @@ use crate::{analysis, guard, structure}; /// into proper groups by registering the values read from the ports of cells /// used within the combinational group. /// +/// It also transforms if-with and while-with into simple `if` and `while` operators that first +/// execute the respective cond group and then execute the control operator. +/// /// # Example /// ``` /// group comb_cond<"static"=0> { @@ -48,21 +50,31 @@ use crate::{analysis, guard, structure}; /// comb_cond[done] = lt_reg.done & eq_reg.done ? 1'd1; /// } /// control { -/// if lt_reg.out with comb_cond { -/// ... +/// seq { +/// comb_cond; +/// if lt_reg.out { +/// ... +/// } /// } -/// while eq_reg.out with comb_cond { -/// ... +/// seq { +/// comb_cond; +/// while eq_reg.out { +/// ... +/// comb_cond; +/// } /// } /// } /// ``` pub struct RemoveCombGroups { - // Mapping from (group_name, (cell_name, port_name)) -> port. - port_rewrite: HashMap<(ir::Id, (ir::Id, ir::Id)), RRC>, + // Mapping from (group_name, (cell_name, port_name)) -> (port, group). + port_rewrite: HashMap, RRC)>, // The pass updated combinational groups for this component. updated: bool, } +/// Represents (group_name, (cell_name, port_name)) +type PortInGroup = (ir::Id, (ir::Id, ir::Id)); + impl Named for RemoveCombGroups { fn name() -> &'static str { "remove-comb-groups" @@ -84,129 +96,179 @@ impl Visitor for RemoveCombGroups { let mut used_ports = analysis::ControlPorts::from(&*comp.control.borrow()); - // Detach groups from the component - let groups = comp.groups.drain().collect_vec(); - let mut builder = ir::Builder::new(comp, sigs); - for group_ref in &groups { - let group = group_ref.borrow(); - - // Is this group combinational - let done_assign = group - .assignments - .iter() - .find(|assign| { - let dst = assign.dst.borrow(); - dst.is_hole() && *group.name() == dst.get_parent_name() - }) - .map(|asgn| { - asgn.guard.is_true() && asgn.src.borrow().is_constant(1, 1) - }); - let is_comb = group - .attributes - .get("static") - .map(|v| *v == 0) - .unwrap_or(false) - || done_assign.unwrap_or(false); - - if !is_comb { - continue; - } - - // If any groups were updated, tell the pass that updates - // were made. + // If any groups were updated, tell the pass that updates + // were made. + if !comp.comb_groups.is_empty() { self.updated = true; + } else { + return Ok(Action::Continue); + } + + let mut builder = ir::Builder::new(comp, sigs); - // Register the ports read by the combinational group's usages. - let used_ports = - used_ports.remove(group.name()).ok_or_else(|| { + // Groups generated by transforming combinational groups + let groups = builder + .component + .comb_groups + .drain() + .map(|cg_ref| { + let name = cg_ref.borrow().name().clone(); + // Register the ports read by the combinational group's usages. + let used_ports = used_ports.remove(&name).ok_or_else(|| { Error::MalformedStructure(format!( "Values from combinational group {} never used", - group.name() + name )) })?; - let mut save_regs = Vec::with_capacity(used_ports.len()); + // Group generated to replace this comb group. + let group_ref = builder.add_group(name.as_ref()); + let mut group = group_ref.borrow_mut(); + // Attach assignmens from comb group + group.assignments = + cg_ref.borrow_mut().assignments.drain(..).collect(); + + // Registers to save value for the group + let mut save_regs = Vec::with_capacity(used_ports.len()); + for port in used_ports { + // Register to save port value + structure!(builder; + let comb_reg = prim std_reg(port.borrow().width); + let signal_on = constant(1, 1); + ); + let write = builder.build_assignment( + comb_reg.borrow().get("in"), + Rc::clone(&port), + ir::Guard::True, + ); + let en = builder.build_assignment( + comb_reg.borrow().get("write_en"), + signal_on.borrow().get("out"), + ir::Guard::True, + ); + group.assignments.push(write); + group.assignments.push(en); + + // Define mapping from this port to the register's output + // value. + self.port_rewrite.insert( + (name.clone(), port.borrow().canonical().clone()), + ( + Rc::clone(&comb_reg.borrow().get("out")), + Rc::clone(&group_ref), + ), + ); - // Explicitly drop group to avoid Borrow Error. - drop(group); - let mut group = group_ref.borrow_mut(); + save_regs.push(comb_reg); + } - for port in used_ports { - // Register to save port value structure!(builder; - let comb_reg = prim std_reg(port.borrow().width); let signal_on = constant(1, 1); ); - let write = builder.build_assignment( - comb_reg.borrow().get("in"), - Rc::clone(&port), - ir::Guard::True, - ); - let en = builder.build_assignment( - comb_reg.borrow().get("write_en"), + + // Create a done condition + let done_guard = save_regs + .drain(..) + .map(|reg| guard!(reg["done"])) + .fold(ir::Guard::True, ir::Guard::and); + let done_assign = builder.build_assignment( + group.get("done"), signal_on.borrow().get("out"), - ir::Guard::True, - ); - group.assignments.push(write); - group.assignments.push(en); - - // Define mapping from this port to the register's output - // value. - self.port_rewrite.insert( - (group.name().clone(), port.borrow().canonical().clone()), - Rc::clone(&comb_reg.borrow().get("out")), + done_guard, ); + group.assignments.push(done_assign); - save_regs.push(comb_reg); - } + // Add a "static" attribute + group.attributes.insert("static", 1); + drop(group); - // Update the done condition - for mut assign in group.assignments.iter_mut() { - let dst = assign.dst.borrow(); - if dst.is_hole() && dst.name == "done" { - // The source should be the constant 1 since this is a combinational group. - debug_assert!(assign.src.borrow().is_constant(1, 1)); - assign.guard = Box::new( - save_regs - .drain(..) - .map(|reg| guard!(reg["done"])) - .fold(ir::Guard::True, ir::Guard::and), - ); - } - } + Ok(group_ref) + }) + .collect::>>()?; - // Update the "static" attribute - group.attributes.insert("static", 1); + for group in groups { + comp.groups.add(group) } - comp.groups = groups.into(); Ok(Action::Continue) } - fn start_while( + fn finish_while( &mut self, s: &mut ir::While, _comp: &mut ir::Component, _sigs: &LibrarySignatures, ) -> VisResult { - let key = (s.cond.borrow().name().clone(), s.port.borrow().canonical()); - if let Some(new_port) = self.port_rewrite.get(&key) { - s.port = Rc::clone(new_port); + if s.cond.is_some() { + // Construct a new `while` statement + let key = ( + s.cond.as_ref().unwrap().borrow().name().clone(), + s.port.borrow().canonical(), + ); + let (port_ref, cond_ref) = self.port_rewrite.get(&key).unwrap(); + let cond_in_body = ir::Control::enable(Rc::clone(cond_ref)); + let body = std::mem::replace(s.body.as_mut(), ir::Control::empty()); + let new_body = ir::Control::seq(vec![body, cond_in_body]); + let mut while_ = ir::Control::while_( + Rc::clone(port_ref), + None, + Box::new(new_body), + ); + if let Some(attrs) = while_.get_mut_attributes() { + *attrs = std::mem::take(&mut s.attributes); + } + let cond_before_body = ir::Control::enable(Rc::clone(cond_ref)); + Ok(Action::Change(ir::Control::seq(vec![ + cond_before_body, + while_, + ]))) + } else { + Ok(Action::Continue) } - Ok(Action::Continue) } - fn start_if( + /// Transforms a `if-with` into a `seq-if` which first runs the cond group + /// and then the branch. + fn finish_if( &mut self, s: &mut ir::If, _comp: &mut ir::Component, _sigs: &LibrarySignatures, ) -> VisResult { - let key = (s.cond.borrow().name().clone(), s.port.borrow().canonical()); - if let Some(new_port) = self.port_rewrite.get(&key) { - s.port = Rc::clone(new_port); + if s.cond.is_some() { + // Construct a new `if` statement + let key = ( + s.cond.as_ref().unwrap().borrow().name().clone(), + s.port.borrow().canonical(), + ); + let (port_ref, cond_ref) = + self.port_rewrite.get(&key).unwrap_or_else(|| { + panic!( + "{}: Port `{}.{}` in group `{}` doesn't have a rewrite", + Self::name(), + key.1 .0, + key.1 .1, + key.0 + ) + }); + let port = Rc::clone(port_ref); + // Add @stable annotation to port + port.borrow_mut().attributes.insert("stable", 1); + let tbranch = + std::mem::replace(s.tbranch.as_mut(), ir::Control::empty()); + let fbranch = + std::mem::replace(s.fbranch.as_mut(), ir::Control::empty()); + let if_ = ir::Control::if_( + Rc::clone(port_ref), + None, + Box::new(tbranch), + Box::new(fbranch), + ); + let cond = ir::Control::enable(Rc::clone(cond_ref)); + Ok(Action::Change(ir::Control::seq(vec![cond, if_]))) + } else { + Ok(Action::Continue) } - Ok(Action::Continue) } fn finish( diff --git a/calyx/src/passes/static_timing.rs b/calyx/src/passes/static_timing.rs index b602d0e0d7..cf96869f4d 100644 --- a/calyx/src/passes/static_timing.rs +++ b/calyx/src/passes/static_timing.rs @@ -65,9 +65,8 @@ impl Visitor for StaticTiming { comp: &mut ir::Component, ctx: &LibrarySignatures, ) -> VisResult { - // let st = &mut comp.structure; - - if let ir::Control::Enable(data) = &*wh.body { + todo!() + /* if let ir::Control::Enable(data) = &*wh.body { let cond = &wh.cond; let port = &wh.port; let body = &data.group; @@ -183,6 +182,7 @@ impl Visitor for StaticTiming { } Ok(Action::Continue) + */ } fn finish_if( @@ -194,24 +194,26 @@ impl Visitor for StaticTiming { if let (ir::Control::Enable(tdata), ir::Control::Enable(fdata)) = (&*s.tbranch, &*s.fbranch) { - let cond = &s.cond; let tru = &tdata.group; let fal = &fdata.group; - if let (Some(ctime), Some(ttime), Some(ftime)) = ( - check_not_comb(cond)?, - check_not_comb(tru)?, - check_not_comb(fal)?, - ) { - let mut builder = ir::Builder::new(comp, ctx); + if s.cond.is_some() { + return Err(Error::MalformedStructure(format!("{}: condition group should be removed from if. Run `{}` before this pass.", Self::name(), RemoveCombGroups::name()))); + } + + if let (Some(ttime), Some(ftime)) = + (check_not_comb(tru)?, check_not_comb(fal)?) + { + todo!() + /* let mut builder = ir::Builder::new(comp, ctx); let if_group = builder.add_group("static_if"); if_group .borrow_mut() .attributes - .insert("static", ctime + 1 + cmp::max(ttime, ftime)); + .insert("static", 1 + cmp::max(ttime, ftime)); - let end_true_time = ttime + ctime + 1; - let end_false_time = ftime + ctime + 1; + let end_true_time = ttime + 1; + let end_false_time = ftime + 1; // `0` state + (ctime + max(ttime, ftime) + 1) states. let fsm_size = get_bit_width_from( 1 + cmp::max(end_true_time, end_false_time), @@ -223,9 +225,6 @@ impl Visitor for StaticTiming { let cond_stored = prim std_reg(1); let reset_val = constant(0, fsm_size); - let cond_time_const = constant(ctime, fsm_size); - let cond_done_time_const = constant(ctime, fsm_size); - let true_end_const = constant(end_true_time, fsm_size); let false_end_const = constant(end_false_time, fsm_size); @@ -302,6 +301,7 @@ impl Visitor for StaticTiming { comp.continuous_assignments.append(&mut clean_assigns); return Ok(Action::Change(ir::Control::enable(if_group))); + */ } } diff --git a/calyx/src/passes/top_down_compile_control.rs b/calyx/src/passes/top_down_compile_control.rs index 44d39e98c7..8b13df3fb6 100644 --- a/calyx/src/passes/top_down_compile_control.rs +++ b/calyx/src/passes/top_down_compile_control.rs @@ -1,17 +1,23 @@ use super::math_utilities::get_bit_width_from; -use crate::ir::{ - self, - traversal::{Action, Named, VisResult, Visitor}, - LibrarySignatures, RRC, +use crate::errors::CalyxResult; +use crate::ir::traversal::ConstructVisitor; +use crate::{build_assignments, guard, passes, structure}; +use crate::{ + errors::Error, + ir::{ + self, + traversal::{Action, Named, VisResult, Visitor}, + LibrarySignatures, RRC, + }, }; -use crate::{build_assignments, guard, structure}; use ir::IRPrinter; use itertools::Itertools; -use petgraph::{algo::connected_components, graph::DiGraph}; +use petgraph::graph::DiGraph; use std::collections::HashMap; +use std::io::Write; use std::rc::Rc; -/// Represents the execution schedule of a control program. +/// Represents the dyanmic execution schedule of a control program. #[derive(Default)] struct Schedule { /// Assigments that should be enabled in a given state. @@ -32,7 +38,7 @@ impl Schedule { ); debug_assert!( - connected_components(&graph) == 1, + petgraph::algo::connected_components(&graph) == 1, "State transition graph has unreachable states (graph has more than one connected component)."); } @@ -46,85 +52,263 @@ impl Schedule { } /// Print out the current schedule - #[allow(dead_code)] - fn display(&self) { + fn display(&self, group: String) { + let out = &mut std::io::stdout(); + writeln!(out, "======== {} =========", group).unwrap(); self.enables .iter() .sorted_by(|(k1, _), (k2, _)| k1.cmp(k2)) .for_each(|(state, assigns)| { - eprintln!("======== {} =========", state); + writeln!(out, "{}:", state).unwrap(); assigns.iter().for_each(|assign| { - IRPrinter::write_assignment( - assign, - 0, - &mut std::io::stderr(), - ) - .expect("Printing failed!"); - eprintln!(); + IRPrinter::write_assignment(assign, 2, out).unwrap(); + writeln!(out).unwrap(); }) }); - eprintln!("------------"); + writeln!(out, "{}:\n ", self.last_state()).unwrap(); + writeln!(out, "transitions:").unwrap(); self.transitions .iter() .sorted_by(|(k1, _, _), (k2, _, _)| k1.cmp(k2)) .for_each(|(i, f, g)| { - eprintln!("({}, {}): {}", i, f, IRPrinter::guard_str(g)); - }) + writeln!(out, " ({}, {}): {}", i, f, IRPrinter::guard_str(g)) + .unwrap(); + }); + } + + /// Implement a given [Schedule] and return the name of the [ir::Group] that + /// implements it. + fn realize_schedule( + self, + group: RRC, + builder: &mut ir::Builder, + ) -> RRC { + self.validate(); + let final_state = self.last_state(); + let fsm_size = get_bit_width_from( + final_state + 1, /* represent 0..final_state */ + ); + structure!(builder; + let fsm = prim std_reg(fsm_size); + let signal_on = constant(1, 1); + let last_state = constant(final_state, fsm_size); + let first_state = constant(0, fsm_size); + ); + + // Enable assignments + group.borrow_mut().assignments.extend( + self.enables + .into_iter() + .sorted_by(|(k1, _), (k2, _)| k1.cmp(k2)) + .flat_map(|(state, mut assigns)| { + let state_const = builder.add_constant(state, fsm_size); + let state_guard = + guard!(fsm["out"]).eq(guard!(state_const["out"])); + assigns.iter_mut().for_each(|asgn| { + asgn.guard.update(|g| g.and(state_guard.clone())) + }); + assigns + }), + ); + + // Transition assignments + group.borrow_mut().assignments.extend( + self.transitions.into_iter().flat_map(|(s, e, guard)| { + structure!(builder; + let end_const = constant(e, fsm_size); + let start_const = constant(s, fsm_size); + ); + let ec_borrow = end_const.borrow(); + let trans_guard = + guard!(fsm["out"]).eq(guard!(start_const["out"])) & guard; + + vec![ + builder.build_assignment( + fsm.borrow().get("in"), + ec_borrow.get("out"), + trans_guard.clone(), + ), + builder.build_assignment( + fsm.borrow().get("write_en"), + signal_on.borrow().get("out"), + trans_guard, + ), + ] + }), + ); + + // Done condition for group + let last_guard = guard!(fsm["out"]).eq(guard!(last_state["out"])); + let done_assign = builder.build_assignment( + group.borrow().get("done"), + signal_on.borrow().get("out"), + last_guard.clone(), + ); + group.borrow_mut().assignments.push(done_assign); + + // Cleanup: Add a transition from last state to the first state. + let mut reset_fsm = build_assignments!(builder; + fsm["in"] = last_guard ? first_state["out"]; + fsm["write_en"] = last_guard ? signal_on["out"]; + ); + builder + .component + .continuous_assignments + .append(&mut reset_fsm); + + group } } -/// Recursively calcuate the states for each child in a control sub-program. -fn calculate_states( +/// Computes the entry and exit points of a given [ir::Control] program. +/// +/// ## Example +/// In the following Calyx program: +/// ``` +/// while comb_reg.out { +/// seq { +/// incr; +/// cond0; +/// } +/// } +/// ``` +/// The exit point is `cond0`. +/// +/// Multiple exit points are created when conditions are used: +/// ``` +/// while comb_reg.out { +/// incr; +/// if comb_reg2.out { +/// true; +/// } else { +/// false; +/// } +/// } +/// ``` +/// The exit set is `[true, false]`. +fn control_exits( + con: &ir::Control, + cur_state: u64, + is_exit: bool, + exits: &mut Vec<(u64, RRC)>, +) -> u64 { + match con { + ir::Control::Enable(ir::Enable { group, .. }) => { + if is_exit { + exits.push((cur_state, Rc::clone(group))) + } + cur_state + 1 + } + ir::Control::Seq(ir::Seq { stmts, .. }) => { + let len = stmts.len(); + let mut cur = cur_state; + for (idx, stmt) in stmts.iter().enumerate() { + let exit = idx == len - 1 && is_exit; + cur = control_exits(stmt, cur, exit, exits); + } + cur + } + ir::Control::If(ir::If { + tbranch, fbranch, .. + }) => { + let tru_nxt = control_exits( + tbranch, cur_state, is_exit, exits, + ); + control_exits( + fbranch, tru_nxt, is_exit, exits, + ) + } + ir::Control::While(ir::While { body, .. }) => control_exits( + body, cur_state, is_exit, exits, + ), + ir::Control::Invoke(_) => unreachable!("`invoke` statements should have been compiled away. Run `{}` before this pass.", passes::CompileInvoke::name()), + ir::Control::Empty(_) => unreachable!("`invoke` statements should have been compiled away. Run `{}` before this pass.", passes::CompileEmpty::name()), + ir::Control::Par(_) => unreachable!(), + } +} + +/// Represents an edge from a predeccesor to the current control node. +/// The `u64` represents the FSM state of the predeccesor and the guard needs +/// to be true for the predeccesor to transition to the current state. +type PredEdge = (u64, ir::Guard); + +/// Recursively build an dynamic finite state machine represented by a [Schedule]. +/// Does the following, given an [ir::Control]: +/// 1. If needed, add transitions from predeccesors to the current state. +/// 2. Enable the groups in the current state +/// 3. Calculate [PredEdge] implied by this state +/// 4. Return [PredEdge] and the next state. +fn calculate_states_recur( con: &ir::Control, // The current state cur_state: u64, - // Additional guard for this condition. - pre_guard: &ir::Guard, + // The set of previous states that want to transition into cur_state + preds: Vec<(u64, ir::Guard)>, // Current schedule. schedule: &mut Schedule, // Component builder builder: &mut ir::Builder, -) -> u64 { +) -> CalyxResult<(Vec, u64)> { match con { - // Compiled to: - // ``` - // group[go] = (fsm.out == cur_state) & !group[done] & pre_guard ? 1'd1; - // fsm.in = (fsm.out == cur_state) & group[done] & pre_guard ? nxt_state; - // ``` + // See explanation of FSM states generated in [ir::TopDownCompileControl]. ir::Control::Enable(ir::Enable { group, .. }) => { - let done_cond = guard!(group["done"]) & pre_guard.clone(); - let not_done = !guard!(group["done"]) & pre_guard.clone(); + // If there is exactly one previous transition state with a `true` + // guard, then merge this state into previous state. + let (cur_state, prev_states) = if preds.len() == 1 && preds[0].1.is_true() { + (preds[0].0, vec![]) + } else { + (cur_state, preds) + }; + + let not_done = !guard!(group["done"]); let signal_on = builder.add_constant(1, 1); + + // Activate this group in the current state let mut en_go = build_assignments!(builder; group["go"] = not_done ? signal_on["out"]; ); - let nxt_state = cur_state + 1; schedule .enables .entry(cur_state) .or_default() .append(&mut en_go); - schedule.transitions.push((cur_state, nxt_state, done_cond)); - nxt_state + // Activate group in the cycle when previous state signals done. + // NOTE: We explicilty do not add `not_done` to the guard. + // See explanation in [ir::TopDownCompileControl] to understand + // why. + for (st, g) in &prev_states { + let mut early_go = build_assignments!(builder; + group["go"] = g ? signal_on["out"]; + ); + schedule.enables.entry(*st).or_default().append(&mut early_go); + } + + let transitions = prev_states + .into_iter() + .map(|(st, guard)| (st, cur_state, guard)); + schedule.transitions.extend(transitions); + + let done_cond = guard!(group["done"]); + let nxt = cur_state + 1; + Ok((vec![(cur_state, done_cond)], nxt)) } - // Give children the states `cur`, `cur + 1`, `cur + 2`, ... ir::Control::Seq(ir::Seq { stmts, .. }) => { + let mut prev = preds; let mut cur = cur_state; for stmt in stmts { - cur = calculate_states(stmt, cur, pre_guard, schedule, builder); + let res = calculate_states_recur( + stmt, + cur, + prev, + schedule, + builder, + )?; + prev = res.0; + cur = res.1; } - cur + Ok((prev, cur)) } - // Generate the following transitions: - // 1. cur -> cur + 1: Compute the condition and store the generated value. - // 2. (cur + 1 -> cur + t): Compute the true branch when stored condition - // is true. - // 3. (cur + 1 -> cur + f): Compute the true branch when stored condition - // is false. - // 4. (cur + t -> cur + max(t, f) + 1) - // (cur + f -> cur + max(t, f) + 1): Transition to a "join" stage - // after running the branch. ir::Control::If(ir::If { port, cond, @@ -132,299 +316,226 @@ fn calculate_states( fbranch, .. }) => { - structure!(builder; - let signal_on = constant(1, 1); - let signal_off = constant(0, 1); - let cs_if = prim std_reg(1); - ); - - // Compute the condition and save its value in cs_if - let mut cond_save_assigns = vec![ - builder.build_assignment( - cs_if.borrow().get("in"), - Rc::clone(port), - pre_guard.clone(), - ), - builder.build_assignment( - cs_if.borrow().get("write_en"), - signal_on.borrow().get("out"), - pre_guard.clone(), - ), - builder.build_assignment( - cond.borrow().get("go"), - signal_on.borrow().get("out"), - pre_guard.clone(), - ), - ]; - - // Schedule the condition computation first and transition to next - // state. - let after_cond_compute = cur_state + 1; - schedule - .enables - .entry(cur_state) - .or_default() - .append(&mut cond_save_assigns); - schedule.transitions.push(( - cur_state, - after_cond_compute, - guard!(cond["done"]), - )); - - // Computation for true branch - let true_go = guard!(cs_if["out"]) & pre_guard.clone(); - let after_true = calculate_states( + if cond.is_some() { + return Err(Error::MalformedStructure(format!("{}: Found group `{}` in with position of if. This should have compiled away.", TopDownCompileControl::name(), cond.as_ref().unwrap().borrow().name()))); + } + let port_guard: ir::Guard = Rc::clone(port).into(); + // Previous states transitioning into true branch need the conditional + // to be true. + let tru_transitions = preds.clone().into_iter().map(|(s, g)| (s, g & port_guard.clone())).collect(); + let (tru_prev, tru_nxt) = calculate_states_recur( tbranch, - after_cond_compute, - &true_go, + cur_state, + tru_transitions, schedule, builder, - ); - // Computation for false branch - let false_go = !guard!(cs_if["out"]) & pre_guard.clone(); - let after_false = calculate_states( + )?; + // Previous states transitioning into false branch need the conditional + // to be false. + let fal_transitions = preds.into_iter().map(|(s, g)| (s, g & !port_guard.clone())).collect(); + let (fal_prev, fal_nxt) = calculate_states_recur( fbranch, - after_cond_compute, - &false_go, + tru_nxt, + fal_transitions, schedule, builder, - ); - - // Transition to a join stage - let next = std::cmp::max(after_true, after_false) + 1; - schedule.transitions.push((after_true, next, true_go)); - schedule.transitions.push((after_false, next, false_go)); - - // Cleanup: Reset cs_if in the join stage. - let mut cleanup = build_assignments!(builder; - cs_if["in"] = pre_guard ? signal_off["out"]; - cs_if["write_en"] = pre_guard ? signal_on["out"]; - ); - schedule - .enables - .entry(next) - .or_default() - .append(&mut cleanup); - - next + )?; + let prevs = + tru_prev.into_iter().chain(fal_prev.into_iter()).collect(); + Ok((prevs, fal_nxt)) } - // Compile in three stage: - // 1. cur -> cur + 1: Compute the condition and store the generated value. - // 2. cur + 1 -> cur + b: Compute the body when the stored condition - // is true. - // 3. cur + b -> cur: Jump to the start state when stored condition was true. - // 4. cur + 1 -> cur + b + 1: Exit stage ir::Control::While(ir::While { cond, port, body, .. }) => { - structure!(builder; - let signal_on = constant(1, 1); - let signal_off = constant(0, 1); - let cs_wh = prim std_reg(1); - ); + if cond.is_some() { + return Err(Error::MalformedStructure(format!("{}: Found group `{}` in with position of if. This should have compiled away.", TopDownCompileControl::name(), cond.as_ref().unwrap().borrow().name()))); + } - // Compute the condition first and save its value. - let mut cond_save_assigns = vec![ - builder.build_assignment( - cs_wh.borrow().get("in"), - Rc::clone(port), - pre_guard.clone(), - ), - builder.build_assignment( - cs_wh.borrow().get("write_en"), - signal_on.borrow().get("out"), - pre_guard.clone(), - ), - builder.build_assignment( - cond.borrow().get("go"), - signal_on.borrow().get("out"), - pre_guard.clone(), - ), - ]; - - // Compute the condition first - let after_cond_compute = cur_state + 1; - schedule - .enables - .entry(cur_state) - .or_default() - .append(&mut cond_save_assigns); - schedule.transitions.push(( - cur_state, - after_cond_compute, - guard!(cond["done"]), - )); + let port_guard: ir::Guard = Rc::clone(port).into(); - // Build the FSM for the body - let body_go = guard!(cs_wh["out"]) & pre_guard.clone(); - let nxt = calculate_states( + // Step 1: Generate the backward edges + // First compute the entry and exit points. + let mut exits = vec![]; + control_exits( + body, + cur_state, + true, + &mut exits, + ); + let back_edge_prevs = exits.into_iter().map(|(st, group)| (st, group.borrow().get("done").into())); + + // Step 2: Generate the forward edges normally. + // Previous transitions into the body require the condition to be + // true. + let transitions: Vec<(u64, ir::Guard)> = preds + .clone() + .into_iter() + .chain(back_edge_prevs) + .map(|(s, g)| (s, g & port_guard.clone())) + .collect(); + let (prevs, nxt) = calculate_states_recur( body, - after_cond_compute, - &body_go, + cur_state, + transitions, schedule, builder, - ); - - // Back edge jump when condition was true - schedule.transitions.push((nxt, cur_state, body_go)); - - // Exit state: Jump to this when the condition is false. - let wh_done = !guard!(cs_wh["out"]) & pre_guard.clone(); - let exit = nxt + 1; - schedule - .transitions - .push((after_cond_compute, exit, wh_done)); - - // Cleanup state registers in exit stage - let mut cleanup = build_assignments!(builder; - cs_wh["in"] = pre_guard ? signal_off["out"]; - cs_wh["write_en"] = pre_guard ? signal_on["out"]; - ); - schedule - .enables - .entry(exit) - .or_default() - .append(&mut cleanup); - - exit - } - // `par` sub-programs should already be compiled - ir::Control::Par(..) => { - unreachable!("par should be compiled away!") - } - ir::Control::Empty(..) => { - unreachable!("empty control should have been compiled away!") - } - ir::Control::Invoke(..) => { - unreachable!("invoke should have been compiled away!") + )?; + + // Step 3: The final out edges from the while come from: + // - Before the body when the condition is false + // - Inside the body when the condition is false + let not_port_guard = !port_guard; + let all_prevs = preds + .into_iter() + .chain(prevs.into_iter()) + .map(|(st, guard)| (st, guard & not_port_guard.clone())) + .collect(); + + Ok((all_prevs, nxt)) } + ir::Control::Invoke(_) => unreachable!("`invoke` statements should have been compiled away. Run `{}` before this pass.", passes::CompileInvoke::name()), + ir::Control::Empty(_) => unreachable!("`invoke` statements should have been compiled away. Run `{}` before this pass.", passes::CompileEmpty::name()), + ir::Control::Par(_) => unreachable!(), } } -/// Implement a given [Schedule] and return the name of the [`ir::Group`](crate::ir::Group) that -/// implements it. -fn realize_schedule( - schedule: Schedule, +fn calculate_states( + con: &ir::Control, builder: &mut ir::Builder, -) -> RRC { - schedule.validate(); - let final_state = schedule.last_state(); - let fsm_size = - get_bit_width_from(final_state + 1 /* represent 0..final_state */); - structure!(builder; - let fsm = prim std_reg(fsm_size); - let signal_on = constant(1, 1); - let last_state = constant(final_state, fsm_size); - let first_state = constant(0, fsm_size); - ); - - // The compilation group - let group = builder.add_group("tdcc"); - - // Enable assignments - group.borrow_mut().assignments.extend( - schedule - .enables - .into_iter() - .sorted_by(|(k1, _), (k2, _)| k1.cmp(k2)) - .flat_map(|(state, mut assigns)| { - let state_const = builder.add_constant(state, fsm_size); - let state_guard = - guard!(fsm["out"]).eq(guard!(state_const["out"])); - assigns.iter_mut().for_each(|asgn| { - asgn.guard.update(|g| g.and(state_guard.clone())) - }); - assigns - }), - ); - - // Transition assignments - group.borrow_mut().assignments.extend( - schedule.transitions.into_iter().flat_map(|(s, e, guard)| { - structure!(builder; - let end_const = constant(e, fsm_size); - let start_const = constant(s, fsm_size); - ); - let ec_borrow = end_const.borrow(); - let trans_guard = - guard!(fsm["out"]).eq(guard!(start_const["out"])) & guard; - - vec![ - builder.build_assignment( - fsm.borrow().get("in"), - ec_borrow.get("out"), - trans_guard.clone(), - ), - builder.build_assignment( - fsm.borrow().get("write_en"), - signal_on.borrow().get("out"), - trans_guard, - ), - ] - }), - ); - - // Done condition for group - let last_guard = guard!(fsm["out"]).eq(guard!(last_state["out"])); - let done_assign = builder.build_assignment( - group.borrow().get("done"), - signal_on.borrow().get("out"), - last_guard.clone(), - ); - group.borrow_mut().assignments.push(done_assign); - - // Cleanup: Add a transition from last state to the first state. - let mut reset_fsm = build_assignments!(builder; - fsm["in"] = last_guard ? first_state["out"]; - fsm["write_en"] = last_guard ? signal_on["out"]; - ); - builder - .component - .continuous_assignments - .append(&mut reset_fsm); - - group +) -> CalyxResult { + let mut schedule = Schedule::default(); + let first_state = (0, ir::Guard::True); + // We create an empty first state in case the control program starts with + // a branch (if, while). + // If the program doesn't branch, then the initial state is merged into + // the first group. + let (prev, nxt) = calculate_states_recur( + con, + 1, + vec![first_state], + &mut schedule, + builder, + )?; + let transitions = prev.into_iter().map(|(st, guard)| (st, nxt, guard)); + schedule.transitions.extend(transitions); + Ok(schedule) } /// **Core lowering pass.** -/// Compiles away the control programs in components into purely structural -/// code using an finite-state machine (FSM). +/// Compiles away the control programs in components into purely structural code using an +/// finite-state machine (FSM). /// /// Lowering operates in two steps: -/// 1. Compile all [`ir::Par`](crate::ir::Par) control sub-programs into a -/// single [`ir::Enable`][enable] of a group that runs all children -/// to completion. -/// 2. Compile the top-level control program into a single [`ir::Enable`][enable]. +/// 1. Compile all [ir::Par] control sub-programs into a single [ir::Enable] of a group that runs +/// all children to completion. +/// 2. Compile the top-level control program into a single [ir::Enable]. /// /// ## Compiling non-`par` programs -/// Assuming all `par` statements have already been compiled in a control -/// sub-program, we can build a schedule for executing it. We calculate a -/// schedule by assigning an FSM state to each leaf node (an [`ir::Enable`][enable]) -/// as a guard condition. Each control program node also defines a transition -/// function over the states calculated for its children. +/// At very high-level, the pass assigns an FSM state to each [ir::Enable] in the program and +/// generates transitions to the state to activate the groups contained within the [ir::Enable]. +/// +/// The compilation process calculates all predeccesors of the [ir::Enable] while walking over the +/// control program. A predeccesor is any enable statement that can directly "jump" to the current +/// [ir::Enable]. The compilation process computes all such predeccesors and the guards that need +/// to be true for the predeccesor to jump into this enable statement. +/// +/// ``` +/// cond0; +/// while lt.out { +/// if gt.out { true } else { false } +/// } +/// next; +/// ``` +/// The predeccesor sets are: +/// ``` +/// cond0 -> [] +/// true -> [(cond0, lt.out & gt.out); (true; lt.out & gt.out); (false, lt.out & !gt.out)] +/// false -> [(cond0, lt.out & !gt.out); (true; lt.out & gt.out); (false, lt.out & !gt.out)] +/// next -> [(cond0, !lt.out); (true, !lt.out); (false, !lt.out)] +/// ``` +/// +/// ### Compiling [ir::Enable] +/// The process first takes all edges from predeccesors and transitions to the state for this +/// enable and enables the group in this state: +/// ```text +/// let cur_state; // state of this enable +/// for (state, guard) in predeccesors: +/// transitions.insert(state, cur_state, guard) +/// enables.insert(cur_state, group) +/// ``` +/// +/// While this process will generate a functioning FSM, the FSM takes unnecessary cycles for FSM +/// transitions. /// -/// At the end of schedule generation, each FSM state has a set of groups to -/// enable as well as a transition function. -/// This FSM is realized into an implementation using a new group that implements -/// the group enables and the transitions. +/// For example: +/// ``` +/// seq { one; two; } +/// ``` +/// The FSM generated will look like this (where `f` is the FSM register): +/// ``` +/// f.in = one[done] ? 1; +/// f.in = two[done] ? 2; +/// one[go] = !one[done] & f.out == 0; +/// two[go] = !two[done] & f.out == 1; +/// ``` +/// +/// The cycle-level timing for this FSM will look like: +/// - cycle 0: (f.out == 0), enable one +/// - cycle t: (f.out == 0), (one[done] == 1), disable one +/// - cycle t+1: (f.out == 1), enable two +/// - cycle t+l: (f.out == 1), (two[done] == 1), disable two +/// - cycle t+l+1: finish +/// +/// The transition t -> t+1 represents one where group one is done but group two hasn't started +/// executing. +/// +/// To address this specific problem, there is an additional enable added to run all groups within +/// an enable *while the FSM is transitioning*. +/// The final transition will look like this: +/// ``` +/// f.in = one[done] ? 1; +/// f.in = two[done] ? 2; +/// one[go] = !one[done] & f.out == 0; +/// two[go] = (!two[done] & f.out == 1) || (one[done] & f.out == 0); +/// ``` +/// +/// Note that `!two[done]` isn't present in the second disjunct because all groups are guaranteed +/// to run for at least one cycle and the second disjunct will only be true for one cycle before +/// the first disjunct becomes true. /// /// ## Compiling `par` programs -/// We have to generate new FSM-based controller for each child of a `par` node -/// so that each child can indepdendently make progress. -/// If we tie the children to one top-level FSM, their transitions would become -/// interdependent and reduce available concurrency. +/// We have to generate new FSM-based controller for each child of a `par` node so that each child +/// can indepdendently make progress. +/// If we tie the children to one top-level FSM, their transitions would become interdependent and +/// reduce available concurrency. /// /// ## Compilation guarantee /// At the end of this pass, the control program will have no more than one /// group enable in it. -/// -/// [enable]: crate::ir::Enable -#[derive(Default)] -pub struct TopDownCompileControl; +pub struct TopDownCompileControl { + dump_fsm: bool, +} + +impl ConstructVisitor for TopDownCompileControl { + fn from(ctx: &ir::Context) -> CalyxResult + where + Self: Sized + Named, + { + let dump_fsm = ctx + .extra_opts + .iter() + .any(|opt| opt == &format!("{}:{}", Self::name(), "dump-fsm")); + Ok(TopDownCompileControl { dump_fsm }) + } + + fn clear_data(&mut self) { + /* All data can be transferred between components */ + } +} impl Named for TopDownCompileControl { fn name() -> &'static str { - "top-down-cc" + "tdcc" } fn description() -> &'static str { @@ -462,15 +573,16 @@ impl Visitor for TopDownCompileControl { } // Compile complex schedule and return the group. _ => { - let mut schedule = Schedule::default(); - calculate_states( - con, - 0, - &ir::Guard::True, - &mut schedule, - &mut builder, - ); - realize_schedule(schedule, &mut builder) + let schedule = calculate_states(con, &mut builder)?; + let group = builder.add_group("tdcc"); + if self.dump_fsm { + schedule.display(format!( + "{}:{}", + builder.component.name, + group.borrow().name() + )); + } + schedule.realize_schedule(group, &mut builder) } }; @@ -539,15 +651,17 @@ impl Visitor for TopDownCompileControl { let control = Rc::clone(&comp.control); let mut builder = ir::Builder::new(comp, sigs); - let mut schedule = Schedule::default(); - calculate_states( - &control.borrow(), - 0, - &ir::Guard::True, - &mut schedule, - &mut builder, - ); - let comp_group = realize_schedule(schedule, &mut builder); + // Add assignments for the final states + let schedule = calculate_states(&control.borrow(), &mut builder)?; + let group = builder.add_group("tdcc"); + if self.dump_fsm { + schedule.display(format!( + "{}:{}", + builder.component.name, + group.borrow().name() + )); + } + let comp_group = schedule.realize_schedule(group, &mut builder); Ok(Action::Change(ir::Control::enable(comp_group))) } diff --git a/calyx/src/passes/top_down_static_timing.rs b/calyx/src/passes/top_down_static_timing.rs index 206146f678..ab32a10ae0 100644 --- a/calyx/src/passes/top_down_static_timing.rs +++ b/calyx/src/passes/top_down_static_timing.rs @@ -3,7 +3,7 @@ use crate::ir::{ traversal::{Action, Named, VisResult, Visitor}, IRPrinter, LibrarySignatures, }; -use crate::{build_assignments, guard, structure}; +use crate::{build_assignments, structure}; use itertools::Itertools; use std::collections::HashMap; diff --git a/calyx/src/passes/well_formed.rs b/calyx/src/passes/well_formed.rs index 071f53d66d..6a71701e6b 100644 --- a/calyx/src/passes/well_formed.rs +++ b/calyx/src/passes/well_formed.rs @@ -1,33 +1,20 @@ use crate::errors::Error; use crate::ir::traversal::{Action, Named, VisResult, Visitor}; -use crate::ir::{ - self, CloneName, Component, LibrarySignatures, RESERVED_NAMES, -}; +use crate::ir::{self, CloneName, Component, LibrarySignatures}; use std::collections::HashSet; /// Pass to check if the program is well-formed. /// /// Catches the following errors: -/// 1. Programs that use reserved SystemVerilog keywords as identifiers. -/// 2. Programs that don't use a defined group. +/// 1. Programs that don't use a defined group or combinational group. +/// 2. Groups that don't write to their done signal. +/// 3. Groups that write to another group's done signal. +#[derive(Default)] pub struct WellFormed { - /// Set of names that components and cells are not allowed to have. - reserved_names: HashSet, - /// Names of the groups that have been used in the control. used_groups: HashSet, -} - -impl Default for WellFormed { - fn default() -> Self { - let reserved_names = - RESERVED_NAMES.iter().map(|s| s.to_string()).collect(); - - WellFormed { - reserved_names, - used_groups: HashSet::new(), - } - } + /// Names of combinational groups used in the control. + used_comb_groups: HashSet, } impl Named for WellFormed { @@ -46,18 +33,10 @@ impl Visitor for WellFormed { comp: &mut Component, _ctx: &LibrarySignatures, ) -> VisResult { - // Check if any of the cells use a reserved name. - for cell_ref in comp.cells.iter() { - let cell = cell_ref.borrow(); - if self.reserved_names.contains(&cell.name().id) { - return Err(Error::ReservedName(cell.clone_name())); - } - } - - // For each group, check if there is at least one write to the done + // For each non-combinational group, check if there is at least one write to the done // signal of that group. // Names of the groups whose `done` hole has been written to. - for group_ref in comp.groups.iter() { + comp.groups.iter().try_for_each(|group_ref| { let group = group_ref.borrow(); let gname = group.name(); // Find an assignment writing to this group's done condition. @@ -68,14 +47,14 @@ impl Visitor for WellFormed { && dst.get_parent_name() == gname }); if done.is_none() { - return Err(Error::MalformedStructure(gname.fmt_err( - &format!( - "No writes to the `done' hole for group `{}'", - gname.to_string() - ), - ))); + Err(Error::MalformedStructure(gname.fmt_err(&format!( + "No writes to the `done' hole for group `{}'", + gname.to_string() + )))) + } else { + Ok(()) } - } + })?; // Check if any groups refer to another group's done signal. for group_ref in comp.groups.iter() { @@ -127,7 +106,7 @@ impl Visitor for WellFormed { .unwrap_or(false) || done_assign.unwrap_or(false) { - return Err(Error::MalformedStructure(group.name().fmt_err("Group with constant done condition not allowed inside normal control operators"))); + return Err(Error::MalformedStructure(group.name().fmt_err("Group with constant done condition are invalid. Use `comb group` instead to define a combinational group."))); } Ok(Action::Continue) @@ -167,7 +146,9 @@ impl Visitor for WellFormed { _ctx: &LibrarySignatures, ) -> VisResult { // Add cond group as a used port. - self.used_groups.insert(s.cond.clone_name()); + if let Some(cond) = &s.cond { + self.used_comb_groups.insert(cond.clone_name()); + } Ok(Action::Continue) } @@ -178,7 +159,9 @@ impl Visitor for WellFormed { _ctx: &LibrarySignatures, ) -> VisResult { // Add cond group as a used port. - self.used_groups.insert(s.cond.clone_name()); + if let Some(cond) = &s.cond { + self.used_comb_groups.insert(cond.clone_name()); + } Ok(Action::Continue) } @@ -189,11 +172,21 @@ impl Visitor for WellFormed { ) -> VisResult { let all_groups: HashSet = comp.groups.iter().map(|g| g.clone_name()).collect(); - let unused_group = - all_groups.difference(&self.used_groups).into_iter().next(); - match unused_group { - Some(group) => Err(Error::UnusedGroup(group.clone())), - None => Ok(Action::Continue), + if let Some(group) = + all_groups.difference(&self.used_groups).into_iter().next() + { + return Err(Error::UnusedGroup(group.clone())); + }; + + let all_comb_groups: HashSet = + comp.comb_groups.iter().map(|g| g.clone_name()).collect(); + if let Some(group) = all_comb_groups + .difference(&self.used_comb_groups) + .into_iter() + .next() + { + return Err(Error::UnusedGroup(group.clone())); } + Ok(Action::Continue) } } diff --git a/docs/tutorial/language-tut.md b/docs/tutorial/language-tut.md index cc4b1af22f..f02946f5ff 100644 --- a/docs/tutorial/language-tut.md +++ b/docs/tutorial/language-tut.md @@ -247,8 +247,9 @@ Next, the `incr` group adds one to the value in `counter` using `add2`: {{#include ../../examples/tutorial/language-tutorial-iterate.futil:incr}} ``` -And finally, `cond` uses our comparator `lt` to compute the signal we need for our `while` loop: - +And finally, `cond` uses our comparator `lt` to compute the signal we need for +our `while` loop. We use a `comb group` to denote that the assignments inside +the condition can be run combinationally: ``` {{#include ../../examples/tutorial/language-tutorial-iterate.futil:cond}} ``` diff --git a/examples/dahlia/dot-product.expect b/examples/dahlia/dot-product.expect index f6100fc202..5bc1d4f672 100644 --- a/examples/dahlia/dot-product.expect +++ b/examples/dahlia/dot-product.expect @@ -1,5 +1,5 @@ { - "cycles": 108, + "cycles": 98, "memories": { "A0": [ 27, diff --git a/examples/dahlia/vectorized-add.expect b/examples/dahlia/vectorized-add.expect index 6b55ee7372..9d7d382e6d 100644 --- a/examples/dahlia/vectorized-add.expect +++ b/examples/dahlia/vectorized-add.expect @@ -1,5 +1,5 @@ { - "cycles": 60, + "cycles": 50, "memories": { "A0": [ 1, diff --git a/examples/futil/dot-product.expect b/examples/futil/dot-product.expect index 955fdaeeab..288544db51 100644 --- a/examples/futil/dot-product.expect +++ b/examples/futil/dot-product.expect @@ -18,85 +18,71 @@ component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { mult_pipe0 = std_mult_pipe(32); @external v0 = std_mem_d1(32, 1, 1); @generated comb_reg = std_reg(1); - @generated fsm = std_reg(1); - @generated incr = std_add(1); - @generated fsm0 = std_reg(4); - @generated incr0 = std_add(4); - @generated fsm1 = std_reg(4); - @generated cond_stored = std_reg(1); - @generated incr1 = std_add(4); - @generated fsm2 = std_reg(2); + @generated pd = std_reg(1); + @generated pd0 = std_reg(1); + @generated fsm = std_reg(4); } wires { - A0.addr0 = fsm.out < 1'd1 & fsm0.out == 4'd0 & cond_stored.out & fsm1.out >= 4'd2 & fsm1.out < 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? i0.out; + A0.addr0 = !(pd.out | A_read0_0.done) & (comb_reg.done & comb_reg.out & fsm.out == 4'd1 & go | !(pd.out & pd0.out) & fsm.out == 4'd2 & go | comb_reg.done & comb_reg.out & fsm.out == 4'd7 & go) ? i0.out; A0.clk = clk; A_read0_0.clk = clk; - A_read0_0.in = fsm.out < 1'd1 & fsm0.out == 4'd0 & cond_stored.out & fsm1.out >= 4'd2 & fsm1.out < 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? A0.read_data; - A_read0_0.in = fsm0.out == 4'd5 & cond_stored.out & fsm1.out >= 4'd2 & fsm1.out < 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? bin_read0_0.out; - A_read0_0.write_en = fsm0.out == 4'd5 & cond_stored.out & fsm1.out >= 4'd2 & fsm1.out < 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go | fsm.out < 1'd1 & fsm0.out == 4'd0 & cond_stored.out & fsm1.out >= 4'd2 & fsm1.out < 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? 1'd1; - B0.addr0 = fsm.out < 1'd1 & fsm0.out == 4'd0 & cond_stored.out & fsm1.out >= 4'd2 & fsm1.out < 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? i0.out; + A_read0_0.in = !(pd.out | A_read0_0.done) & (comb_reg.done & comb_reg.out & fsm.out == 4'd1 & go | !(pd.out & pd0.out) & fsm.out == 4'd2 & go | comb_reg.done & comb_reg.out & fsm.out == 4'd7 & go) ? A0.read_data; + A_read0_0.in = bin_read0_0.done & fsm.out == 4'd3 & go | !A_read0_0.done & fsm.out == 4'd4 & go ? bin_read0_0.out; + A_read0_0.write_en = bin_read0_0.done & fsm.out == 4'd3 & go | !A_read0_0.done & fsm.out == 4'd4 & go | !(pd.out | A_read0_0.done) & (comb_reg.done & comb_reg.out & fsm.out == 4'd1 & go | !(pd.out & pd0.out) & fsm.out == 4'd2 & go | comb_reg.done & comb_reg.out & fsm.out == 4'd7 & go) ? 1'd1; + B0.addr0 = !(pd0.out | B_read0_0.done) & (comb_reg.done & comb_reg.out & fsm.out == 4'd1 & go | !(pd.out & pd0.out) & fsm.out == 4'd2 & go | comb_reg.done & comb_reg.out & fsm.out == 4'd7 & go) ? i0.out; B0.clk = clk; B_read0_0.clk = clk; - B_read0_0.in = fsm.out < 1'd1 & fsm0.out == 4'd0 & cond_stored.out & fsm1.out >= 4'd2 & fsm1.out < 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? B0.read_data; - B_read0_0.write_en = fsm.out < 1'd1 & fsm0.out == 4'd0 & cond_stored.out & fsm1.out >= 4'd2 & fsm1.out < 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? 1'd1; - done = fsm2.out == 2'd2 ? 1'd1; - add0.left = fsm0.out == 4'd6 & cond_stored.out & fsm1.out >= 4'd2 & fsm1.out < 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? v0.read_data; - add0.right = fsm0.out == 4'd6 & cond_stored.out & fsm1.out >= 4'd2 & fsm1.out < 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? A_read0_0.out; - add1.left = fsm0.out == 4'd7 & cond_stored.out & fsm1.out >= 4'd2 & fsm1.out < 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? i0.out; - add1.right = fsm0.out == 4'd7 & cond_stored.out & fsm1.out >= 4'd2 & fsm1.out < 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? const4.out; + B_read0_0.in = !(pd0.out | B_read0_0.done) & (comb_reg.done & comb_reg.out & fsm.out == 4'd1 & go | !(pd.out & pd0.out) & fsm.out == 4'd2 & go | comb_reg.done & comb_reg.out & fsm.out == 4'd7 & go) ? B0.read_data; + B_read0_0.write_en = !(pd0.out | B_read0_0.done) & (comb_reg.done & comb_reg.out & fsm.out == 4'd1 & go | !(pd.out & pd0.out) & fsm.out == 4'd2 & go | comb_reg.done & comb_reg.out & fsm.out == 4'd7 & go) ? 1'd1; + done = fsm.out == 4'd8 ? 1'd1; + add0.left = A_read0_0.done & fsm.out == 4'd4 & go | !v0.done & fsm.out == 4'd5 & go ? v0.read_data; + add0.right = A_read0_0.done & fsm.out == 4'd4 & go | !v0.done & fsm.out == 4'd5 & go ? A_read0_0.out; + add1.left = v0.done & fsm.out == 4'd5 & go | !i0.done & fsm.out == 4'd6 & go ? i0.out; + add1.right = v0.done & fsm.out == 4'd5 & go | !i0.done & fsm.out == 4'd6 & go ? const4.out; bin_read0_0.clk = clk; - bin_read0_0.in = fsm0.out >= 4'd1 & fsm0.out < 4'd5 & cond_stored.out & fsm1.out >= 4'd2 & fsm1.out < 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? mult_pipe0.out; - bin_read0_0.write_en = fsm0.out >= 4'd1 & fsm0.out < 4'd5 & cond_stored.out & fsm1.out >= 4'd2 & fsm1.out < 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? mult_pipe0.done; + bin_read0_0.in = pd.out & pd0.out & fsm.out == 4'd2 & go | !bin_read0_0.done & fsm.out == 4'd3 & go ? mult_pipe0.out; + bin_read0_0.write_en = pd.out & pd0.out & fsm.out == 4'd2 & go | !bin_read0_0.done & fsm.out == 4'd3 & go ? mult_pipe0.done; comb_reg.clk = clk; - comb_reg.in = fsm1.out < 4'd1 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? le0.out; + comb_reg.in = i0.done & fsm.out == 4'd0 & go | !comb_reg.done & fsm.out == 4'd1 & go | i0.done & fsm.out == 4'd6 & go | !comb_reg.done & fsm.out == 4'd7 & go ? le0.out; comb_reg.reset = reset; - comb_reg.write_en = fsm1.out < 4'd1 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? 1'd1; - cond_stored.clk = clk; - cond_stored.in = fsm1.out == 4'd1 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? comb_reg.out; - cond_stored.reset = reset; - cond_stored.write_en = fsm1.out == 4'd1 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? 1'd1; + comb_reg.write_en = i0.done & fsm.out == 4'd0 & go | !comb_reg.done & fsm.out == 4'd1 & go | i0.done & fsm.out == 4'd6 & go | !comb_reg.done & fsm.out == 4'd7 & go ? 1'd1; fsm.clk = clk; - fsm.in = fsm.out == 1'd1 ? 1'd0; - fsm.in = fsm.out != 1'd1 & fsm0.out == 4'd0 & cond_stored.out & fsm1.out >= 4'd2 & fsm1.out < 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? incr.out; + fsm.in = fsm.out == 4'd8 ? 4'd0; + fsm.in = fsm.out == 4'd0 & i0.done & go ? 4'd1; + fsm.in = fsm.out == 4'd1 & comb_reg.done & comb_reg.out & go | fsm.out == 4'd7 & comb_reg.done & comb_reg.out & go ? 4'd2; + fsm.in = fsm.out == 4'd2 & pd.out & pd0.out & go ? 4'd3; + fsm.in = fsm.out == 4'd3 & bin_read0_0.done & go ? 4'd4; + fsm.in = fsm.out == 4'd4 & A_read0_0.done & go ? 4'd5; + fsm.in = fsm.out == 4'd5 & v0.done & go ? 4'd6; + fsm.in = fsm.out == 4'd6 & i0.done & go ? 4'd7; + fsm.in = fsm.out == 4'd1 & comb_reg.done & !comb_reg.out & go | fsm.out == 4'd7 & comb_reg.done & !comb_reg.out & go ? 4'd8; fsm.reset = reset; - fsm.write_en = fsm.out != 1'd1 & fsm0.out == 4'd0 & cond_stored.out & fsm1.out >= 4'd2 & fsm1.out < 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go | fsm.out == 1'd1 ? 1'd1; - fsm0.clk = clk; - fsm0.in = fsm0.out == 4'd8 ? 4'd0; - fsm0.in = fsm0.out != 4'd8 & cond_stored.out & fsm1.out >= 4'd2 & fsm1.out < 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? incr0.out; - fsm0.reset = reset; - fsm0.write_en = fsm0.out != 4'd8 & cond_stored.out & fsm1.out >= 4'd2 & fsm1.out < 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go | fsm0.out == 4'd8 ? 1'd1; - fsm1.clk = clk; - fsm1.in = fsm1.out == 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go | fsm1.out == 4'd2 & !cond_stored.out ? 4'd0; - fsm1.in = (fsm1.out < 4'd2 | fsm1.out >= 4'd2 & fsm1.out < 4'd10 & cond_stored.out) & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? incr1.out; - fsm1.reset = reset; - fsm1.write_en = (fsm1.out < 4'd2 | fsm1.out >= 4'd2 & fsm1.out < 4'd10 & cond_stored.out) & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go | fsm1.out == 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go | fsm1.out == 4'd2 & !cond_stored.out ? 1'd1; - fsm2.clk = clk; - fsm2.in = fsm2.out == 2'd2 ? 2'd0; - fsm2.in = fsm2.out == 2'd0 & i0.done & go ? 2'd1; - fsm2.in = fsm2.out == 2'd1 & fsm1.out == 4'd2 & !cond_stored.out & go ? 2'd2; - fsm2.reset = reset; - fsm2.write_en = fsm2.out == 2'd0 & i0.done & go | fsm2.out == 2'd1 & fsm1.out == 4'd2 & !cond_stored.out & go | fsm2.out == 2'd2 ? 1'd1; + fsm.write_en = fsm.out == 4'd0 & i0.done & go | fsm.out == 4'd1 & comb_reg.done & comb_reg.out & go | fsm.out == 4'd7 & comb_reg.done & comb_reg.out & go | fsm.out == 4'd2 & pd.out & pd0.out & go | fsm.out == 4'd3 & bin_read0_0.done & go | fsm.out == 4'd4 & A_read0_0.done & go | fsm.out == 4'd5 & v0.done & go | fsm.out == 4'd6 & i0.done & go | fsm.out == 4'd1 & comb_reg.done & !comb_reg.out & go | fsm.out == 4'd7 & comb_reg.done & !comb_reg.out & go | fsm.out == 4'd8 ? 1'd1; i0.clk = clk; - i0.in = fsm0.out == 4'd7 & cond_stored.out & fsm1.out >= 4'd2 & fsm1.out < 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? add1.out; - i0.in = !i0.done & fsm2.out == 2'd0 & go ? const0.out; - i0.write_en = !i0.done & fsm2.out == 2'd0 & go | fsm0.out == 4'd7 & cond_stored.out & fsm1.out >= 4'd2 & fsm1.out < 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? 1'd1; - incr.left = fsm0.out == 4'd0 & cond_stored.out & fsm1.out >= 4'd2 & fsm1.out < 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? 1'd1; - incr.right = fsm0.out == 4'd0 & cond_stored.out & fsm1.out >= 4'd2 & fsm1.out < 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? fsm.out; - incr0.left = cond_stored.out & fsm1.out >= 4'd2 & fsm1.out < 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? 4'd1; - incr0.right = cond_stored.out & fsm1.out >= 4'd2 & fsm1.out < 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? fsm0.out; - incr1.left = !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? fsm1.out; - incr1.right = !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? 4'd1; - le0.left = fsm1.out < 4'd1 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? i0.out; - le0.right = fsm1.out < 4'd1 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? const1.out; + i0.in = v0.done & fsm.out == 4'd5 & go | !i0.done & fsm.out == 4'd6 & go ? add1.out; + i0.in = !i0.done & fsm.out == 4'd0 & go ? const0.out; + i0.write_en = !i0.done & fsm.out == 4'd0 & go | v0.done & fsm.out == 4'd5 & go | !i0.done & fsm.out == 4'd6 & go ? 1'd1; + le0.left = i0.done & fsm.out == 4'd0 & go | !comb_reg.done & fsm.out == 4'd1 & go | i0.done & fsm.out == 4'd6 & go | !comb_reg.done & fsm.out == 4'd7 & go ? i0.out; + le0.right = i0.done & fsm.out == 4'd0 & go | !comb_reg.done & fsm.out == 4'd1 & go | i0.done & fsm.out == 4'd6 & go | !comb_reg.done & fsm.out == 4'd7 & go ? const1.out; mult_pipe0.clk = clk; - mult_pipe0.go = !mult_pipe0.done & fsm0.out >= 4'd1 & fsm0.out < 4'd5 & cond_stored.out & fsm1.out >= 4'd2 & fsm1.out < 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? 1'd1; - mult_pipe0.left = fsm0.out >= 4'd1 & fsm0.out < 4'd5 & cond_stored.out & fsm1.out >= 4'd2 & fsm1.out < 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? A_read0_0.out; - mult_pipe0.right = fsm0.out >= 4'd1 & fsm0.out < 4'd5 & cond_stored.out & fsm1.out >= 4'd2 & fsm1.out < 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? B_read0_0.out; - v0.addr0 = fsm0.out == 4'd6 & cond_stored.out & fsm1.out >= 4'd2 & fsm1.out < 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? const2.out; - v0.addr0 = fsm0.out == 4'd6 & cond_stored.out & fsm1.out >= 4'd2 & fsm1.out < 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? const3.out; + mult_pipe0.go = !mult_pipe0.done & (pd.out & pd0.out & fsm.out == 4'd2 & go | !bin_read0_0.done & fsm.out == 4'd3 & go) ? 1'd1; + mult_pipe0.left = pd.out & pd0.out & fsm.out == 4'd2 & go | !bin_read0_0.done & fsm.out == 4'd3 & go ? A_read0_0.out; + mult_pipe0.right = pd.out & pd0.out & fsm.out == 4'd2 & go | !bin_read0_0.done & fsm.out == 4'd3 & go ? B_read0_0.out; + pd.clk = clk; + pd.in = pd.out & pd0.out ? 1'd0; + pd.in = A_read0_0.done & (comb_reg.done & comb_reg.out & fsm.out == 4'd1 & go | !(pd.out & pd0.out) & fsm.out == 4'd2 & go | comb_reg.done & comb_reg.out & fsm.out == 4'd7 & go) ? 1'd1; + pd.reset = reset; + pd.write_en = A_read0_0.done & (comb_reg.done & comb_reg.out & fsm.out == 4'd1 & go | !(pd.out & pd0.out) & fsm.out == 4'd2 & go | comb_reg.done & comb_reg.out & fsm.out == 4'd7 & go) | pd.out & pd0.out ? 1'd1; + pd0.clk = clk; + pd0.in = pd.out & pd0.out ? 1'd0; + pd0.in = B_read0_0.done & (comb_reg.done & comb_reg.out & fsm.out == 4'd1 & go | !(pd.out & pd0.out) & fsm.out == 4'd2 & go | comb_reg.done & comb_reg.out & fsm.out == 4'd7 & go) ? 1'd1; + pd0.reset = reset; + pd0.write_en = B_read0_0.done & (comb_reg.done & comb_reg.out & fsm.out == 4'd1 & go | !(pd.out & pd0.out) & fsm.out == 4'd2 & go | comb_reg.done & comb_reg.out & fsm.out == 4'd7 & go) | pd.out & pd0.out ? 1'd1; + v0.addr0 = A_read0_0.done & fsm.out == 4'd4 & go | !v0.done & fsm.out == 4'd5 & go ? const2.out; + v0.addr0 = A_read0_0.done & fsm.out == 4'd4 & go | !v0.done & fsm.out == 4'd5 & go ? const3.out; v0.clk = clk; - v0.write_data = fsm0.out == 4'd6 & cond_stored.out & fsm1.out >= 4'd2 & fsm1.out < 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? add0.out; - v0.write_en = fsm0.out == 4'd6 & cond_stored.out & fsm1.out >= 4'd2 & fsm1.out < 4'd10 & !(fsm1.out == 4'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? 1'd1; + v0.write_data = A_read0_0.done & fsm.out == 4'd4 & go | !v0.done & fsm.out == 4'd5 & go ? add0.out; + v0.write_en = A_read0_0.done & fsm.out == 4'd4 & go | !v0.done & fsm.out == 4'd5 & go ? 1'd1; } control {} diff --git a/examples/futil/dot-product.futil b/examples/futil/dot-product.futil index 5cd9ab0c63..8a79ff7988 100644 --- a/examples/futil/dot-product.futil +++ b/examples/futil/dot-product.futil @@ -20,8 +20,7 @@ component main() -> () { @external(1) v0 = std_mem_d1(32,1,1); } wires { - group cond0<"static"=0> { - cond0[done] = 1'd1; + comb group cond0 { le0.left = i0.out; le0.right = const1.out; } diff --git a/examples/futil/memory-by-reference/memory-by-reference.expect b/examples/futil/memory-by-reference/memory-by-reference.expect index e34fd15047..b768481a0f 100644 --- a/examples/futil/memory-by-reference/memory-by-reference.expect +++ b/examples/futil/memory-by-reference/memory-by-reference.expect @@ -1,5 +1,5 @@ { - "cycles": 11, + "cycles": 10, "memories": { "x": [ 1 diff --git a/examples/futil/multi-component.expect b/examples/futil/multi-component.expect index 1f939831d3..85a8530ca1 100644 --- a/examples/futil/multi-component.expect +++ b/examples/futil/multi-component.expect @@ -22,8 +22,8 @@ component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { wires { done = fsm.out == 2'd2 ? 1'd1; current_value.clk = clk; - current_value.in = !current_value.done & fsm.out == 2'd1 & go ? id.out; - current_value.write_en = !current_value.done & fsm.out == 2'd1 & go ? 1'd1; + current_value.in = id.done & fsm.out == 2'd0 & go | !current_value.done & fsm.out == 2'd1 & go ? id.out; + current_value.write_en = id.done & fsm.out == 2'd0 & go | !current_value.done & fsm.out == 2'd1 & go ? 1'd1; fsm.clk = clk; fsm.in = fsm.out == 2'd2 ? 2'd0; fsm.in = fsm.out == 2'd0 & id.done & go ? 2'd1; diff --git a/examples/futil/pass-in-register.expect b/examples/futil/pass-in-register.expect index e082c07b1f..66b3b7c11c 100644 --- a/examples/futil/pass-in-register.expect +++ b/examples/futil/pass-in-register.expect @@ -32,14 +32,14 @@ component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { fsm.reset = reset; fsm.write_en = fsm.out == 2'd0 & r.done & go | fsm.out == 2'd1 & op.done & go | fsm.out == 2'd2 ? 1'd1; op.clk = clk; - op.go = !op.done & fsm.out == 2'd1 & go ? 1'd1; - op.reg_done = !op.done & fsm.out == 2'd1 & go ? r.done; - op.reg_out = !op.done & fsm.out == 2'd1 & go ? r.out; + op.go = r.done & fsm.out == 2'd0 & go | !op.done & fsm.out == 2'd1 & go ? 1'd1; + op.reg_done = r.done & fsm.out == 2'd0 & go | !op.done & fsm.out == 2'd1 & go ? r.done; + op.reg_out = r.done & fsm.out == 2'd0 & go | !op.done & fsm.out == 2'd1 & go ? r.out; r.clk = clk; r.in = !r.done & fsm.out == 2'd0 & go ? 32'd15; - r.in = !op.done & fsm.out == 2'd1 & go ? op.reg_in; + r.in = r.done & fsm.out == 2'd0 & go | !op.done & fsm.out == 2'd1 & go ? op.reg_in; r.write_en = !r.done & fsm.out == 2'd0 & go ? 1'd1; - r.write_en = !op.done & fsm.out == 2'd1 & go ? op.reg_write_en; + r.write_en = r.done & fsm.out == 2'd0 & go | !op.done & fsm.out == 2'd1 & go ? op.reg_write_en; } control {} diff --git a/examples/futil/simple.expect b/examples/futil/simple.expect index 4ad41d24e5..8bf5d7c42e 100644 --- a/examples/futil/simple.expect +++ b/examples/futil/simple.expect @@ -20,15 +20,15 @@ component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { fsm.reset = reset; fsm.write_en = fsm.out == 2'd0 & reg0.done & go | fsm.out == 2'd1 & reg1.done & go | fsm.out == 2'd2 ? 1'd1; mult.clk = clk; - mult.go = !mult.done & !reg1.done & fsm.out == 2'd1 & go ? 1'd1; - mult.left = !reg1.done & fsm.out == 2'd1 & go ? const0.out; - mult.right = !reg1.done & fsm.out == 2'd1 & go ? const1.out; + mult.go = !mult.done & (reg0.done & fsm.out == 2'd0 & go | !reg1.done & fsm.out == 2'd1 & go) ? 1'd1; + mult.left = reg0.done & fsm.out == 2'd0 & go | !reg1.done & fsm.out == 2'd1 & go ? const0.out; + mult.right = reg0.done & fsm.out == 2'd0 & go | !reg1.done & fsm.out == 2'd1 & go ? const1.out; reg0.clk = clk; reg0.in = !reg0.done & fsm.out == 2'd0 & go ? add.out; reg0.write_en = !reg0.done & fsm.out == 2'd0 & go ? 1'd1; reg1.clk = clk; - reg1.in = !reg1.done & fsm.out == 2'd1 & go ? mult.out; - reg1.write_en = !reg1.done & fsm.out == 2'd1 & go ? mult.done; + reg1.in = reg0.done & fsm.out == 2'd0 & go | !reg1.done & fsm.out == 2'd1 & go ? mult.out; + reg1.write_en = reg0.done & fsm.out == 2'd0 & go | !reg1.done & fsm.out == 2'd1 & go ? mult.done; } control {} diff --git a/examples/futil/vectorized-add.expect b/examples/futil/vectorized-add.expect index cd65c651b6..bcbb28441b 100644 --- a/examples/futil/vectorized-add.expect +++ b/examples/futil/vectorized-add.expect @@ -14,76 +14,60 @@ component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { i0 = std_reg(4); le0 = std_le(4); @generated comb_reg = std_reg(1); - @generated fsm = std_reg(1); - @generated incr = std_add(1); - @generated fsm0 = std_reg(2); - @generated incr0 = std_add(2); - @generated fsm1 = std_reg(3); - @generated cond_stored = std_reg(1); - @generated incr1 = std_add(3); - @generated fsm2 = std_reg(2); + @generated pd = std_reg(1); + @generated pd0 = std_reg(1); + @generated fsm = std_reg(3); } wires { - A0.addr0 = fsm.out < 1'd1 & fsm0.out == 2'd0 & cond_stored.out & fsm1.out >= 3'd2 & fsm1.out < 3'd5 & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? i0.out; + A0.addr0 = !(pd.out | A_read0_0.done) & (comb_reg.done & comb_reg.out & fsm.out == 3'd1 & go | !(pd.out & pd0.out) & fsm.out == 3'd2 & go | comb_reg.done & comb_reg.out & fsm.out == 3'd5 & go) ? i0.out; A0.clk = clk; A_read0_0.clk = clk; - A_read0_0.in = fsm.out < 1'd1 & fsm0.out == 2'd0 & cond_stored.out & fsm1.out >= 3'd2 & fsm1.out < 3'd5 & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? A0.read_data; - A_read0_0.write_en = fsm.out < 1'd1 & fsm0.out == 2'd0 & cond_stored.out & fsm1.out >= 3'd2 & fsm1.out < 3'd5 & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? 1'd1; - B0.addr0 = fsm.out < 1'd1 & fsm0.out == 2'd0 & cond_stored.out & fsm1.out >= 3'd2 & fsm1.out < 3'd5 & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? i0.out; + A_read0_0.in = !(pd.out | A_read0_0.done) & (comb_reg.done & comb_reg.out & fsm.out == 3'd1 & go | !(pd.out & pd0.out) & fsm.out == 3'd2 & go | comb_reg.done & comb_reg.out & fsm.out == 3'd5 & go) ? A0.read_data; + A_read0_0.write_en = !(pd.out | A_read0_0.done) & (comb_reg.done & comb_reg.out & fsm.out == 3'd1 & go | !(pd.out & pd0.out) & fsm.out == 3'd2 & go | comb_reg.done & comb_reg.out & fsm.out == 3'd5 & go) ? 1'd1; + B0.addr0 = !(pd0.out | B_read0_0.done) & (comb_reg.done & comb_reg.out & fsm.out == 3'd1 & go | !(pd.out & pd0.out) & fsm.out == 3'd2 & go | comb_reg.done & comb_reg.out & fsm.out == 3'd5 & go) ? i0.out; B0.clk = clk; B_read0_0.clk = clk; - B_read0_0.in = fsm.out < 1'd1 & fsm0.out == 2'd0 & cond_stored.out & fsm1.out >= 3'd2 & fsm1.out < 3'd5 & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? B0.read_data; - B_read0_0.write_en = fsm.out < 1'd1 & fsm0.out == 2'd0 & cond_stored.out & fsm1.out >= 3'd2 & fsm1.out < 3'd5 & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? 1'd1; - Sum0.addr0 = fsm0.out == 2'd1 & cond_stored.out & fsm1.out >= 3'd2 & fsm1.out < 3'd5 & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? i0.out; + B_read0_0.in = !(pd0.out | B_read0_0.done) & (comb_reg.done & comb_reg.out & fsm.out == 3'd1 & go | !(pd.out & pd0.out) & fsm.out == 3'd2 & go | comb_reg.done & comb_reg.out & fsm.out == 3'd5 & go) ? B0.read_data; + B_read0_0.write_en = !(pd0.out | B_read0_0.done) & (comb_reg.done & comb_reg.out & fsm.out == 3'd1 & go | !(pd.out & pd0.out) & fsm.out == 3'd2 & go | comb_reg.done & comb_reg.out & fsm.out == 3'd5 & go) ? 1'd1; + Sum0.addr0 = pd.out & pd0.out & fsm.out == 3'd2 & go | !Sum0.done & fsm.out == 3'd3 & go ? i0.out; Sum0.clk = clk; - Sum0.write_data = fsm0.out == 2'd1 & cond_stored.out & fsm1.out >= 3'd2 & fsm1.out < 3'd5 & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? add0.out; - Sum0.write_en = fsm0.out == 2'd1 & cond_stored.out & fsm1.out >= 3'd2 & fsm1.out < 3'd5 & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? 1'd1; - done = fsm2.out == 2'd2 ? 1'd1; - add0.left = fsm0.out == 2'd1 & cond_stored.out & fsm1.out >= 3'd2 & fsm1.out < 3'd5 & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? A_read0_0.out; - add0.right = fsm0.out == 2'd1 & cond_stored.out & fsm1.out >= 3'd2 & fsm1.out < 3'd5 & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? B_read0_0.out; - add1.left = fsm0.out == 2'd2 & cond_stored.out & fsm1.out >= 3'd2 & fsm1.out < 3'd5 & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? i0.out; - add1.right = fsm0.out == 2'd2 & cond_stored.out & fsm1.out >= 3'd2 & fsm1.out < 3'd5 & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? const2.out; + Sum0.write_data = pd.out & pd0.out & fsm.out == 3'd2 & go | !Sum0.done & fsm.out == 3'd3 & go ? add0.out; + Sum0.write_en = pd.out & pd0.out & fsm.out == 3'd2 & go | !Sum0.done & fsm.out == 3'd3 & go ? 1'd1; + done = fsm.out == 3'd6 ? 1'd1; + add0.left = pd.out & pd0.out & fsm.out == 3'd2 & go | !Sum0.done & fsm.out == 3'd3 & go ? A_read0_0.out; + add0.right = pd.out & pd0.out & fsm.out == 3'd2 & go | !Sum0.done & fsm.out == 3'd3 & go ? B_read0_0.out; + add1.left = Sum0.done & fsm.out == 3'd3 & go | !i0.done & fsm.out == 3'd4 & go ? i0.out; + add1.right = Sum0.done & fsm.out == 3'd3 & go | !i0.done & fsm.out == 3'd4 & go ? const2.out; comb_reg.clk = clk; - comb_reg.in = fsm1.out < 3'd1 & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? le0.out; + comb_reg.in = i0.done & fsm.out == 3'd0 & go | !comb_reg.done & fsm.out == 3'd1 & go | i0.done & fsm.out == 3'd4 & go | !comb_reg.done & fsm.out == 3'd5 & go ? le0.out; comb_reg.reset = reset; - comb_reg.write_en = fsm1.out < 3'd1 & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? 1'd1; - cond_stored.clk = clk; - cond_stored.in = fsm1.out == 3'd1 & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? comb_reg.out; - cond_stored.reset = reset; - cond_stored.write_en = fsm1.out == 3'd1 & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? 1'd1; + comb_reg.write_en = i0.done & fsm.out == 3'd0 & go | !comb_reg.done & fsm.out == 3'd1 & go | i0.done & fsm.out == 3'd4 & go | !comb_reg.done & fsm.out == 3'd5 & go ? 1'd1; fsm.clk = clk; - fsm.in = fsm.out == 1'd1 ? 1'd0; - fsm.in = fsm.out != 1'd1 & fsm0.out == 2'd0 & cond_stored.out & fsm1.out >= 3'd2 & fsm1.out < 3'd5 & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? incr.out; + fsm.in = fsm.out == 3'd6 ? 3'd0; + fsm.in = fsm.out == 3'd0 & i0.done & go ? 3'd1; + fsm.in = fsm.out == 3'd1 & comb_reg.done & comb_reg.out & go | fsm.out == 3'd5 & comb_reg.done & comb_reg.out & go ? 3'd2; + fsm.in = fsm.out == 3'd2 & pd.out & pd0.out & go ? 3'd3; + fsm.in = fsm.out == 3'd3 & Sum0.done & go ? 3'd4; + fsm.in = fsm.out == 3'd4 & i0.done & go ? 3'd5; + fsm.in = fsm.out == 3'd1 & comb_reg.done & !comb_reg.out & go | fsm.out == 3'd5 & comb_reg.done & !comb_reg.out & go ? 3'd6; fsm.reset = reset; - fsm.write_en = fsm.out != 1'd1 & fsm0.out == 2'd0 & cond_stored.out & fsm1.out >= 3'd2 & fsm1.out < 3'd5 & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go | fsm.out == 1'd1 ? 1'd1; - fsm0.clk = clk; - fsm0.in = fsm0.out == 2'd3 ? 2'd0; - fsm0.in = fsm0.out != 2'd3 & cond_stored.out & fsm1.out >= 3'd2 & fsm1.out < 3'd5 & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? incr0.out; - fsm0.reset = reset; - fsm0.write_en = fsm0.out != 2'd3 & cond_stored.out & fsm1.out >= 3'd2 & fsm1.out < 3'd5 & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go | fsm0.out == 2'd3 ? 1'd1; - fsm1.clk = clk; - fsm1.in = fsm1.out == 3'd5 & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go | fsm1.out == 3'd2 & !cond_stored.out ? 3'd0; - fsm1.in = (fsm1.out < 3'd2 | fsm1.out >= 3'd2 & fsm1.out < 3'd5 & cond_stored.out) & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? incr1.out; - fsm1.reset = reset; - fsm1.write_en = (fsm1.out < 3'd2 | fsm1.out >= 3'd2 & fsm1.out < 3'd5 & cond_stored.out) & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go | fsm1.out == 3'd5 & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go | fsm1.out == 3'd2 & !cond_stored.out ? 1'd1; - fsm2.clk = clk; - fsm2.in = fsm2.out == 2'd2 ? 2'd0; - fsm2.in = fsm2.out == 2'd0 & i0.done & go ? 2'd1; - fsm2.in = fsm2.out == 2'd1 & fsm1.out == 3'd2 & !cond_stored.out & go ? 2'd2; - fsm2.reset = reset; - fsm2.write_en = fsm2.out == 2'd0 & i0.done & go | fsm2.out == 2'd1 & fsm1.out == 3'd2 & !cond_stored.out & go | fsm2.out == 2'd2 ? 1'd1; + fsm.write_en = fsm.out == 3'd0 & i0.done & go | fsm.out == 3'd1 & comb_reg.done & comb_reg.out & go | fsm.out == 3'd5 & comb_reg.done & comb_reg.out & go | fsm.out == 3'd2 & pd.out & pd0.out & go | fsm.out == 3'd3 & Sum0.done & go | fsm.out == 3'd4 & i0.done & go | fsm.out == 3'd1 & comb_reg.done & !comb_reg.out & go | fsm.out == 3'd5 & comb_reg.done & !comb_reg.out & go | fsm.out == 3'd6 ? 1'd1; i0.clk = clk; - i0.in = fsm0.out == 2'd2 & cond_stored.out & fsm1.out >= 3'd2 & fsm1.out < 3'd5 & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? add1.out; - i0.in = !i0.done & fsm2.out == 2'd0 & go ? const0.out; - i0.write_en = !i0.done & fsm2.out == 2'd0 & go | fsm0.out == 2'd2 & cond_stored.out & fsm1.out >= 3'd2 & fsm1.out < 3'd5 & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? 1'd1; - incr.left = fsm0.out == 2'd0 & cond_stored.out & fsm1.out >= 3'd2 & fsm1.out < 3'd5 & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? 1'd1; - incr.right = fsm0.out == 2'd0 & cond_stored.out & fsm1.out >= 3'd2 & fsm1.out < 3'd5 & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? fsm.out; - incr0.left = cond_stored.out & fsm1.out >= 3'd2 & fsm1.out < 3'd5 & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? 2'd1; - incr0.right = cond_stored.out & fsm1.out >= 3'd2 & fsm1.out < 3'd5 & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? fsm0.out; - incr1.left = !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? fsm1.out; - incr1.right = !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? 3'd1; - le0.left = fsm1.out < 3'd1 & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? i0.out; - le0.right = fsm1.out < 3'd1 & !(fsm1.out == 3'd2 & !cond_stored.out) & fsm2.out == 2'd1 & go ? const1.out; + i0.in = Sum0.done & fsm.out == 3'd3 & go | !i0.done & fsm.out == 3'd4 & go ? add1.out; + i0.in = !i0.done & fsm.out == 3'd0 & go ? const0.out; + i0.write_en = !i0.done & fsm.out == 3'd0 & go | Sum0.done & fsm.out == 3'd3 & go | !i0.done & fsm.out == 3'd4 & go ? 1'd1; + le0.left = i0.done & fsm.out == 3'd0 & go | !comb_reg.done & fsm.out == 3'd1 & go | i0.done & fsm.out == 3'd4 & go | !comb_reg.done & fsm.out == 3'd5 & go ? i0.out; + le0.right = i0.done & fsm.out == 3'd0 & go | !comb_reg.done & fsm.out == 3'd1 & go | i0.done & fsm.out == 3'd4 & go | !comb_reg.done & fsm.out == 3'd5 & go ? const1.out; + pd.clk = clk; + pd.in = pd.out & pd0.out ? 1'd0; + pd.in = A_read0_0.done & (comb_reg.done & comb_reg.out & fsm.out == 3'd1 & go | !(pd.out & pd0.out) & fsm.out == 3'd2 & go | comb_reg.done & comb_reg.out & fsm.out == 3'd5 & go) ? 1'd1; + pd.reset = reset; + pd.write_en = A_read0_0.done & (comb_reg.done & comb_reg.out & fsm.out == 3'd1 & go | !(pd.out & pd0.out) & fsm.out == 3'd2 & go | comb_reg.done & comb_reg.out & fsm.out == 3'd5 & go) | pd.out & pd0.out ? 1'd1; + pd0.clk = clk; + pd0.in = pd.out & pd0.out ? 1'd0; + pd0.in = B_read0_0.done & (comb_reg.done & comb_reg.out & fsm.out == 3'd1 & go | !(pd.out & pd0.out) & fsm.out == 3'd2 & go | comb_reg.done & comb_reg.out & fsm.out == 3'd5 & go) ? 1'd1; + pd0.reset = reset; + pd0.write_en = B_read0_0.done & (comb_reg.done & comb_reg.out & fsm.out == 3'd1 & go | !(pd.out & pd0.out) & fsm.out == 3'd2 & go | comb_reg.done & comb_reg.out & fsm.out == 3'd5 & go) | pd.out & pd0.out ? 1'd1; } control {} diff --git a/examples/futil/vectorized-add.futil b/examples/futil/vectorized-add.futil index 81f6e53013..51804c6270 100644 --- a/examples/futil/vectorized-add.futil +++ b/examples/futil/vectorized-add.futil @@ -15,8 +15,7 @@ component main() -> () { le0 = std_le(4); } wires { - group cond0<"static"=0> { - cond0[done] = 1'd1; + comb group cond0 { le0.left = i0.out; le0.right = const1.out; } diff --git a/examples/tutorial/language-tutorial-compute.expect b/examples/tutorial/language-tutorial-compute.expect index e4da1ded0f..7ad777d7f0 100644 --- a/examples/tutorial/language-tutorial-compute.expect +++ b/examples/tutorial/language-tutorial-compute.expect @@ -1,5 +1,5 @@ { - "cycles": 10, + "cycles": 11, "memories": { "mem": [ 14 diff --git a/examples/tutorial/language-tutorial-iterate.expect b/examples/tutorial/language-tutorial-iterate.expect index d6f0b01714..bcfbbc2b40 100644 --- a/examples/tutorial/language-tutorial-iterate.expect +++ b/examples/tutorial/language-tutorial-iterate.expect @@ -1,5 +1,5 @@ { - "cycles": 60, + "cycles": 58, "memories": { "mem": [ 42 diff --git a/examples/tutorial/language-tutorial-iterate.futil b/examples/tutorial/language-tutorial-iterate.futil index aa06563ada..bbd04fb783 100644 --- a/examples/tutorial/language-tutorial-iterate.futil +++ b/examples/tutorial/language-tutorial-iterate.futil @@ -55,10 +55,9 @@ component main() -> () { // ANCHOR_END: incr // ANCHOR: cond - group cond { + comb group cond { lt.left = counter.out; lt.right = 32'd8; - cond[done] = 1'b1; } // ANCHOR_END: cond } diff --git a/frontends/mrxl/mrxl/gen_futil.py b/frontends/mrxl/mrxl/gen_futil.py index 2e009a9541..f179d7798b 100644 --- a/frontends/mrxl/mrxl/gen_futil.py +++ b/frontends/mrxl/mrxl/gen_futil.py @@ -1,5 +1,7 @@ from . import ast -from calyx.py_ast import * +from calyx.py_ast import ( + Connect, Group, CompVar, Stdlib, Cell, Program, Component, Import, SeqComp, + ConstantPort, HolePort, CompPort, Enable, While, ParComp, CombGroup) def emit_mem_decl(name, size, par): @@ -33,12 +35,11 @@ def emit_cond_group(suffix, arr_size, b=None): group_id = CompVar(f"cond{bank_suffix}{suffix}") le = CompVar(f"le{bank_suffix}{suffix}") idx = CompVar(f"idx{bank_suffix}{suffix}") - return Group( + return CombGroup( id=group_id, connections=[ Connect(CompPort(idx, "out"), CompPort(le, "left")), Connect(ConstantPort(32, arr_size), CompPort(le, "right")), - Connect(ConstantPort(1, 1), HolePort(group_id, "done")), ], ) @@ -103,12 +104,14 @@ def emit_eval_body_group(s_idx, stmt, b=None): src = CompVar(f"{bi.src}{bank_suffix}") dest = CompVar(f"idx{bank_suffix}_{s_idx}") - mem_offsets.append(Connect(CompPort(dest, "out"), CompPort(src, "addr0"))) + mem_offsets.append( + Connect(CompPort(dest, "out"), CompPort(src, "addr0"))) if isinstance(stmt.op, ast.Map): src = CompVar(f"{stmt.dest}{bank_suffix}") dest = CompVar(f"idx{bank_suffix}_{s_idx}") - mem_offsets.append(Connect(CompPort(dest, "out"), CompPort(src, "addr0"))) + mem_offsets.append( + Connect(CompPort(dest, "out"), CompPort(src, "addr0"))) compute_left_op = emit_compute_op( stmt.op.body.lhs, stmt.op, stmt.dest, name2arr, s_idx, bank_suffix @@ -153,14 +156,14 @@ def gen_reduce_impl(stmt, arr_size, s_idx): of the `reduce` statement instead of an implementation of a `map` statement. """ - result = dict() stdlib = Stdlib() op_name = "mult" if stmt.op.body.op == "mul" else "add" cells = [ Cell(CompVar(f"le{s_idx}"), stdlib.op("lt", 32, signed=False)), Cell(CompVar(f"idx{s_idx}"), stdlib.register(32)), Cell(CompVar(f"adder_idx{s_idx}"), stdlib.op("add", 32, signed=False)), - Cell(CompVar(f"adder_op{s_idx}"), stdlib.op(f"{op_name}", 32, signed=False)), + Cell(CompVar(f"adder_op{s_idx}"), stdlib.op( + f"{op_name}", 32, signed=False)), ] wires = [ emit_cond_group(s_idx, arr_size), @@ -170,7 +173,8 @@ def gen_reduce_impl(stmt, arr_size, s_idx): control = While( port=CompPort(CompVar(f"le{s_idx}"), "out"), cond=CompVar(f"cond{s_idx}"), - body=SeqComp([Enable(f"eval_body{s_idx}"), Enable(f"incr_idx{s_idx}")]), + body=SeqComp( + [Enable(f"eval_body{s_idx}"), Enable(f"incr_idx{s_idx}")]), ) return {"cells": cells, "wires": wires, "control": control} @@ -188,14 +192,14 @@ def gen_map_impl(stmt, arr_size, bank_factor, s_idx): - a group that implements the loop condition, checking if the index has reached the end of the input array """ - result = dict() stdlib = Stdlib() cells = [] for b in range(bank_factor): cells.extend( [ - Cell(CompVar(f"le_b{b}_{s_idx}"), stdlib.op("lt", 32, signed=False)), + Cell(CompVar(f"le_b{b}_{s_idx}"), + stdlib.op("lt", 32, signed=False)), Cell(CompVar(f"idx_b{b}_{s_idx}"), stdlib.register(32)), Cell( CompVar(f"adder_idx_b{b}_{s_idx}"), @@ -294,7 +298,8 @@ def emit(prog): used_names.append(decl.name) if decl.type.size: # A memory arr_size = decl.type.size - cells.extend(emit_mem_decl(decl.name, decl.type.size, name2par[decl.name])) + cells.extend(emit_mem_decl( + decl.name, decl.type.size, name2par[decl.name])) else: # A register cells.append(Cell(CompVar(decl.name), stdlib.register(32))) @@ -302,9 +307,11 @@ def emit(prog): for stmt in prog.stmts: if stmt.dest not in used_names: if isinstance(stmt.op, ast.Map): - cells.extend(emit_mem_decl(stmt.dest, arr_size, name2par[stmt.dest])) + cells.extend(emit_mem_decl( + stmt.dest, arr_size, name2par[stmt.dest])) else: - cells.append(emit_reg_decl(stmt.dest, 32)) + raise NotImplementedError("Generating register declarations") + # cells.append(emit_reg_decl(stmt.dest, 32)) used_names.append(stmt.dest) # Generate Calyx. diff --git a/frontends/ntt-pipeline/gen-ntt-pipeline.py b/frontends/ntt-pipeline/gen-ntt-pipeline.py index 4f17ad5082..e558d7c944 100644 --- a/frontends/ntt-pipeline/gen-ntt-pipeline.py +++ b/frontends/ntt-pipeline/gen-ntt-pipeline.py @@ -1,6 +1,9 @@ from prettytable import PrettyTable import numpy as np -from calyx.py_ast import * +from calyx.py_ast import ( + ParComp, SeqComp, Enable, CompVar, Connect, Atom, Not, Group, HolePort, + Component, Program, CompPort, Cell, ConstantPort, And, Import, Stdlib +) from calyx.utils import bits_needed @@ -133,7 +136,8 @@ def generate_ntt_pipeline(input_bitwidth: int, n: int, q: int): Reference: https://www.microsoft.com/en-us/research/wp-content/uploads/2016/05/RLWE-1.pdf """ - assert n > 0 and (not (n & (n - 1))), f"Input length: {n} must be a power of 2." + assert n > 0 and ( + not (n & (n - 1))), f"Input length: {n} must be a power of 2." bitwidth = bits_needed(n) num_stages = bitwidth - 1 @@ -198,7 +202,8 @@ def op_mod_group(stage, row, operations_tuple): Connect(CompPort(reg, "out"), CompPort(op, "left")), Connect(CompPort(mul, "out"), CompPort(op, "right")), Connect(CompPort(op, "out"), CompPort(mod_pipe, "left")), - Connect(ConstantPort(input_bitwidth, q), CompPort(mod_pipe, "right")), + Connect(ConstantPort(input_bitwidth, q), + CompPort(mod_pipe, "right")), Connect( ConstantPort(1, 1), CompPort(mod_pipe, "go"), @@ -255,8 +260,10 @@ def cells(): stdlib = Stdlib() memories = [ - Cell(input, stdlib.mem_d1(input_bitwidth, n, bitwidth), is_external=True), - Cell(phis, stdlib.mem_d1(input_bitwidth, n, bitwidth), is_external=True), + Cell(input, stdlib.mem_d1(input_bitwidth, + n, bitwidth), is_external=True), + Cell(phis, stdlib.mem_d1(input_bitwidth, + n, bitwidth), is_external=True), ] r_regs = [ Cell(CompVar(f"r{r}"), stdlib.register(input_bitwidth)) for r in range(n) @@ -286,11 +293,13 @@ def cells(): for i in range(n // 2) ] adds = [ - Cell(CompVar(f"add{i}"), stdlib.op("add", input_bitwidth, signed=True)) + Cell(CompVar(f"add{i}"), stdlib.op( + "add", input_bitwidth, signed=True)) for i in range(n // 2) ] subs = [ - Cell(CompVar(f"sub{i}"), stdlib.op("sub", input_bitwidth, signed=True)) + Cell(CompVar(f"sub{i}"), stdlib.op( + "sub", input_bitwidth, signed=True)) for i in range(n // 2) ] @@ -330,11 +339,14 @@ def control(): for s in range(num_stages): if s != 0: # Only append precursors if this is not the first stage. - ntt_stages.append(ParComp([Enable(f"precursor_{r}") for r in range(n)])) + ntt_stages.append( + ParComp([Enable(f"precursor_{r}") for r in range(n)])) # Multiply - ntt_stages.append(ParComp([Enable(f"s{s}_mul{i}") for i in range(n // 2)])) + ntt_stages.append( + ParComp([Enable(f"s{s}_mul{i}") for i in range(n // 2)])) # Addition or subtraction mod `q` - ntt_stages.append(ParComp([Enable(f"s{s}_r{r}_op_mod") for r in range(n)])) + ntt_stages.append( + ParComp([Enable(f"s{s}_r{r}_op_mod") for r in range(n)])) return SeqComp(preambles + ntt_stages + epilogues) pp_table(operations, multiplies, n, num_stages) @@ -353,7 +365,8 @@ def control(): if __name__ == "__main__": - import argparse, json + import argparse + import json parser = argparse.ArgumentParser(description="NTT") parser.add_argument("file", nargs="?", type=str) diff --git a/interp/runt.toml b/interp/runt.toml index ea69914fe8..b4417b927b 100644 --- a/interp/runt.toml +++ b/interp/runt.toml @@ -9,6 +9,7 @@ paths = [ cmd = """ ../target/debug/interp {} | jq .memories """ +timeout = 3 [[tests]] name = "errors" @@ -19,6 +20,7 @@ paths = [ cmd = """ ../target/debug/interp {} """ +timeout = 3 [[tests]] name = "complex" @@ -29,6 +31,7 @@ paths = [ cmd = """ ../target/debug/interp {} """ +timeout = 3 [[tests]] name = "primitives" @@ -38,6 +41,7 @@ paths = [ cmd = """ ../target/debug/interp {} | jq .memories """ +timeout = 3 [[tests]] name = "par to seq" @@ -48,6 +52,7 @@ paths = [ cmd = """ ../target/debug/futil {} -p par-to-seq -l ../ | ../target/debug/interp | jq .memories """ +timeout = 3 [[tests]] name = "control" @@ -59,6 +64,7 @@ paths = [ cmd = """ ../target/debug/interp {} | jq .memories """ +timeout = 3 [[tests]] name = "fully structural" @@ -68,10 +74,10 @@ paths = [ # "tests/control/iteration/*.futil" ] cmd = """ -../target/debug/futil {} -d static-timing -d pre-opt -d post-opt -l ../ | ../target/debug/interp | jq .memories +../target/debug/futil {} -d pre-opt -d post-opt -p remove-comb-groups -l ../ | ../target/debug/interp | jq .memories """ expect_dir = "tests/lowered/" - +timeout = 3 [[tests]] @@ -82,6 +88,7 @@ paths = [ cmd = """ ../target/debug/interp {} debug -p | jq .memories """ +timeout = 3 [[tests]] name = "primitives (CIDR)" @@ -91,6 +98,7 @@ paths = [ cmd = """ ../target/debug/interp {} debug -p | jq .memories """ +timeout = 3 [[tests]] name = "control (CIDR)" @@ -101,6 +109,7 @@ paths = [ cmd = """ ../target/debug/interp {} debug -p | jq .memories """ +timeout = 3 [[tests]] name = "fully structural (CIDR)" @@ -110,8 +119,9 @@ paths = [ # "tests/control/iteration/*.futil" ] cmd = """ -../target/debug/futil {} -d static-timing -d pre-opt -d post-opt -l ../ | ../target/debug/interp debug -p | jq .memories +../target/debug/futil {} -d pre-opt -d post-opt -p remove-comb-groups -l ../ | ../target/debug/interp debug -p | jq .memories """ +timeout = 3 [[tests]] name = "fully structural static" @@ -119,6 +129,7 @@ paths = [ "tests/control/static*.futil" ] cmd = """ -../target/debug/futil {} -d pre-opt -d post-opt -l ../ | ../target/debug/interp | jq .memories +../target/debug/futil {} -d pre-opt -d post-opt -l ../ | ../target/debug/interp | jq .memories """ expect_dir = "tests/lowered/" +timeout = 3 diff --git a/interp/src/environment.rs b/interp/src/environment.rs index 3bb0fa4bdd..af8e441d0a 100644 --- a/interp/src/environment.rs +++ b/interp/src/environment.rs @@ -156,6 +156,7 @@ impl InterpreterState { if let ir::CellType::Primitive { name, param_binding, + is_comb: _, } = cl.prototype.clone() { let cell_name = match name.as_ref() { diff --git a/interp/src/interpreter/interpret_control.rs b/interp/src/interpreter/interpret_control.rs index be0652a40f..ad34c65f5b 100644 --- a/interp/src/interpreter/interpret_control.rs +++ b/interp/src/interpreter/interpret_control.rs @@ -1,7 +1,8 @@ //! Inteprets a control in a component. use super::interpret_group::{ - finish_group_interpretation, interp_cont, interpret_group, + finish_comb_group_interpretation, finish_group_interpretation, interp_cont, + interpret_comb_group, interpret_group, }; use crate::environment::InterpreterState; use crate::errors::InterpreterResult; @@ -97,13 +98,19 @@ fn eval_if( mut env: InterpreterState, comp: &ir::Component, ) -> InterpreterResult { - env = interpret_group(&i.cond.borrow(), continuous_assignments, env)?; + if let Some(comb) = &i.cond { + env = + interpret_comb_group(&comb.borrow(), continuous_assignments, env)?; + } + let cond_flag = env.get_from_port(&i.port.borrow()).as_u64(); - env = finish_group_interpretation( - &i.cond.borrow(), - continuous_assignments, - env, - )?; + if let Some(comb) = &i.cond { + env = finish_comb_group_interpretation( + &comb.borrow(), + continuous_assignments, + env, + )?; + } let target = if cond_flag == 0 { &i.fbranch @@ -126,14 +133,23 @@ fn eval_while( comp: &ir::Component, ) -> InterpreterResult { loop { - env = interpret_group(&w.cond.borrow(), continuous_assignments, env)?; + if let Some(comb) = &w.cond { + env = interpret_comb_group( + &comb.borrow(), + continuous_assignments, + env, + )?; + } let cond_val = env.get_from_port(&w.port.borrow()).as_u64(); - env = finish_group_interpretation( - &w.cond.borrow(), - continuous_assignments, - env, - )?; + + if let Some(comb) = &w.cond { + env = finish_comb_group_interpretation( + &comb.borrow(), + continuous_assignments, + env, + )?; + } if cond_val == 0 { break; diff --git a/interp/src/interpreter/interpret_group.rs b/interp/src/interpreter/interpret_group.rs index 445993e177..dbd69c4b89 100644 --- a/interp/src/interpreter/interpret_group.rs +++ b/interp/src/interpreter/interpret_group.rs @@ -58,7 +58,7 @@ pub fn interp_cont( let mut assign_interp = AssignmentInterpreter::new( env, - done_prt_ref, + Some(done_prt_ref), (std::iter::empty(), continuous_assignments.iter()), ); assign_interp.run()?; @@ -73,7 +73,7 @@ pub fn interp_cont( // required because of lifetime shennanigans let final_env = finish_interpretation( res, - &done_port.borrow() as &ir::Port as ConstPort, + Some(&done_port.borrow() as &ir::Port as ConstPort), continuous_assignments.iter(), ); final_env @@ -91,7 +91,23 @@ pub fn interpret_group( let interp = AssignmentInterpreter::new( env, - grp_done_ref, + Some(grp_done_ref), + (group.assignments.iter(), continuous_assignments.iter()), + ); + + interp.run_and_deconstruct() +} + +/// Evaluates a group, given an environment. +pub fn interpret_comb_group( + group: &ir::CombGroup, + // TODO (griffin): Use these during interpretation + continuous_assignments: &[ir::Assignment], + env: InterpreterState, +) -> InterpreterResult { + let interp = AssignmentInterpreter::new( + env, + None, (group.assignments.iter(), continuous_assignments.iter()), ); @@ -108,7 +124,22 @@ pub fn finish_group_interpretation( finish_interpretation( env, - grp_done_ref, + Some(grp_done_ref), + group + .assignments + .iter() + .chain(continuous_assignments.iter()), + ) +} + +pub fn finish_comb_group_interpretation( + group: &ir::CombGroup, + continuous_assignments: &[ir::Assignment], + env: InterpreterState, +) -> InterpreterResult { + finish_interpretation::<_, ConstPort>( + env, + None, group .assignments .iter() @@ -198,7 +229,7 @@ pub(crate) fn finish_interpretation< P: AsRaw, >( mut env: InterpreterState, - done_signal: P, + done_signal: Option

, assigns: I, ) -> InterpreterResult { // replace port values for all the assignments @@ -213,7 +244,10 @@ pub(crate) fn finish_interpretation< let cells = get_dest_cells(assigns.iter().copied()); - env.insert(done_signal.as_raw(), Value::bit_low()); + if let Some(done_signal) = done_signal { + env.insert(done_signal.as_raw(), Value::bit_low()); + } + eval_prims(&mut env, cells.iter(), true); Ok(env) diff --git a/interp/src/interpreter/steppers/control_interpreter.rs b/interp/src/interpreter/steppers/control_interpreter.rs index c28b52a40e..5a84f54a50 100644 --- a/interp/src/interpreter/steppers/control_interpreter.rs +++ b/interp/src/interpreter/steppers/control_interpreter.rs @@ -5,7 +5,7 @@ use crate::utils::AsRaw; use crate::{ environment::InterpreterState, errors::InterpreterResult, - interpreter::utils::{is_signal_high, ConstPort, ReferenceHolder}, + interpreter::utils::{is_signal_high, ConstPort}, values::Value, }; use calyx::ir::{self, Assignment, Component, Control}; @@ -76,11 +76,60 @@ impl Interpreter for EmptyInterpreter { } } -type EnableHolder<'a> = ReferenceHolder<'a, ir::Group>; +pub enum EnableHolder<'a> { + RefGroup(Ref<'a, ir::Group>), + RefCombGroup(Ref<'a, ir::CombGroup>), + BorrowGrp(&'a ir::Group), + BorrowCombGroup(&'a ir::CombGroup), +} + +impl<'a> From<&'a ir::Group> for EnableHolder<'a> { + fn from(grp: &'a ir::Group) -> Self { + Self::BorrowGrp(grp) + } +} -impl<'a> From<&'a ir::Enable> for ReferenceHolder<'a, ir::Group> { - fn from(e: &'a ir::Enable) -> Self { - e.group.borrow().into() +impl<'a> From<&'a ir::CombGroup> for EnableHolder<'a> { + fn from(comb_grp: &'a ir::CombGroup) -> Self { + Self::BorrowCombGroup(comb_grp) + } +} + +impl<'a> From> for EnableHolder<'a> { + fn from(grp: Ref<'a, ir::Group>) -> Self { + Self::RefGroup(grp) + } +} + +impl<'a> From> for EnableHolder<'a> { + fn from(comb_grp: Ref<'a, ir::CombGroup>) -> Self { + Self::RefCombGroup(comb_grp) + } +} + +impl<'a> From<&'a ir::Enable> for EnableHolder<'a> { + fn from(en: &'a ir::Enable) -> Self { + Self::RefGroup(en.group.borrow()) + } +} + +impl<'a> EnableHolder<'a> { + fn assignments(&self) -> &[ir::Assignment] { + match self { + EnableHolder::RefGroup(x) => &x.assignments, + EnableHolder::RefCombGroup(x) => &x.assignments, + EnableHolder::BorrowGrp(x) => &x.assignments, + EnableHolder::BorrowCombGroup(x) => &x.assignments, + } + } + + fn done_port(&self) -> Option { + match self { + EnableHolder::RefGroup(x) => Some(get_done_port(x).as_raw()), + EnableHolder::BorrowGrp(x) => Some(get_done_port(x).as_raw()), + EnableHolder::BorrowCombGroup(_) + | EnableHolder::RefCombGroup(_) => None, + } } } @@ -102,15 +151,11 @@ impl<'a> EnableInterpreter<'a> { { let enable: EnableHolder = enable.into(); let assigns = ( - enable.assignments.iter().cloned().collect_vec(), + enable.assignments().iter().cloned().collect_vec(), continuous.iter().cloned().collect_vec(), ); - let done = get_done_port(&enable); - let interp = AssignmentInterpreter::new_owned( - env, - &done.borrow() as &ir::Port as *const ir::Port, - assigns, - ); + let done = enable.done_port(); + let interp = AssignmentInterpreter::new_owned(env, done, assigns); Self { enable, group_name, @@ -121,7 +166,7 @@ impl<'a> EnableInterpreter<'a> { impl<'a> EnableInterpreter<'a> { fn reset(self) -> InterpreterState { - self.interp.reset(self.enable.assignments.iter()) + self.interp.reset(self.enable.assignments().iter()) } fn get>(&self, port: P) -> &Value { self.interp.get(port) @@ -299,7 +344,7 @@ impl<'a> Interpreter for ParInterpreter<'a> { } } pub struct IfInterpreter<'a> { - port: ConstPort, + cond_port: ConstPort, cond: Option>, tbranch: &'a Control, fbranch: &'a Control, @@ -313,19 +358,36 @@ impl<'a> IfInterpreter<'a> { env: InterpreterState, continuous_assigns: &'a [Assignment], ) -> Self { - let port: ConstPort = ctrl_if.port.as_ptr(); - let cond = EnableInterpreter::new( - ctrl_if.cond.borrow(), - Some(ctrl_if.cond.borrow().name().clone()), - env, - continuous_assigns, - ); + let cond_port: ConstPort = ctrl_if.port.as_ptr(); + + let (cond, branch_interp) = if let Some(cond) = &ctrl_if.cond { + ( + Some(EnableInterpreter::new( + cond.borrow(), + Some(cond.borrow().name().clone()), + env, + continuous_assigns, + )), + None, + ) + } else { + let grp = if is_signal_high(env.get_from_port(cond_port)) { + &ctrl_if.tbranch + } else { + &ctrl_if.fbranch + }; + ( + None, + Some(ControlInterpreter::new(grp, env, continuous_assigns)), + ) + }; + Self { - port, - cond: Some(cond), + cond_port, + cond, tbranch: &ctrl_if.tbranch, fbranch: &ctrl_if.fbranch, - branch_interp: None, + branch_interp, continuous_assignments: continuous_assigns, } } @@ -338,7 +400,7 @@ impl<'a> Interpreter for IfInterpreter<'a> { let i = self.cond.take().unwrap(); let branch; #[allow(clippy::branches_sharing_code)] - if is_signal_high(i.get(self.port)) { + if is_signal_high(i.get(self.cond_port)) { let env = i.deconstruct(); branch = ControlInterpreter::new( self.tbranch, @@ -396,11 +458,12 @@ impl<'a> Interpreter for IfInterpreter<'a> { } pub struct WhileInterpreter<'a> { port: ConstPort, - cond: Ref<'a, ir::Group>, + cond: Option>, body: &'a Control, continuous_assignments: &'a [ir::Assignment], cond_interp: Option>, body_interp: Option>, + terminal_env: Option, } impl<'a> WhileInterpreter<'a> { @@ -410,20 +473,41 @@ impl<'a> WhileInterpreter<'a> { continuous_assignments: &'a [Assignment], ) -> Self { let port: ConstPort = ctrl_while.port.as_ptr(); - let cond = ctrl_while.cond.borrow(); - let cond_interp = EnableInterpreter::new( - Ref::clone(&cond), - Some(cond.name().clone()), - env, - continuous_assignments, - ); + let cond_interp; + let body_interp; + let terminal_env; + + if let Some(cond) = &ctrl_while.cond { + cond_interp = Some(EnableInterpreter::new( + cond.borrow(), + Some(cond.borrow().name().clone()), + env, + continuous_assignments, + )); + terminal_env = None; + body_interp = None; + } else if is_signal_high(env.get_from_port(port)) { + body_interp = Some(ControlInterpreter::new( + &ctrl_while.body, + env, + continuous_assignments, + )); + terminal_env = None; + cond_interp = None; + } else { + terminal_env = Some(env); + body_interp = None; + cond_interp = None; + } + Self { port, - cond, + cond: ctrl_while.cond.as_ref().map(|x| x.borrow()), body: &ctrl_while.body, continuous_assignments, - cond_interp: Some(cond_interp), - body_interp: None, + cond_interp, + body_interp, + terminal_env, } } } @@ -441,7 +525,7 @@ impl<'a> Interpreter for WhileInterpreter<'a> { ); self.body_interp = Some(body_interp) } else { - self.cond_interp = Some(ci) + self.terminal_env = Some(ci.deconstruct()) } } else { ci.step()? @@ -451,14 +535,27 @@ impl<'a> Interpreter for WhileInterpreter<'a> { bi.step()? } else { let bi = self.body_interp.take().unwrap(); - let cond_interp = EnableInterpreter::new( - Ref::clone(&self.cond), - Some(self.cond.name().clone()), - bi.deconstruct(), - self.continuous_assignments, - ); - self.cond_interp = Some(cond_interp) + let env = bi.deconstruct(); + + if let Some(cond) = &self.cond { + let cond_interp = EnableInterpreter::new( + Ref::clone(cond), + Some(cond.name().clone()), + env, + self.continuous_assignments, + ); + self.cond_interp = Some(cond_interp) + } else if is_signal_high(env.get_from_port(self.port)) { + self.body_interp = Some(ControlInterpreter::new( + self.body, + env, + self.continuous_assignments, + )); + } else { + self.terminal_env = Some(env); + } } + } else if self.terminal_env.is_some() { } else { panic!("While Interpreter is in an invalid state") } @@ -466,16 +563,11 @@ impl<'a> Interpreter for WhileInterpreter<'a> { } fn deconstruct(self) -> InterpreterState { - self.cond_interp.unwrap().deconstruct() + self.terminal_env.unwrap() } fn is_done(&self) -> bool { - self.body_interp.is_none() - && self.cond_interp.is_some() - && self.cond_interp.as_ref().unwrap().is_done() - && !is_signal_high( - self.cond_interp.as_ref().unwrap().get(self.port), - ) + self.terminal_env.is_some() } fn get_env(&self) -> Vec<&InterpreterState> { @@ -483,6 +575,8 @@ impl<'a> Interpreter for WhileInterpreter<'a> { cond.get_env() } else if let Some(body) = &self.body_interp { body.get_env() + } else if let Some(env) = &self.terminal_env { + vec![env] } else { unreachable!("Invalid internal state for WhileInterpreter") } @@ -652,7 +746,7 @@ impl<'a> StructuralInterpreter<'a> { let interp = AssignmentInterpreter::new( env, - done_port, + Some(done_port), (std::iter::empty(), continuous_assignments.iter()), ); @@ -673,8 +767,12 @@ impl<'a> Interpreter for StructuralInterpreter<'a> { fn deconstruct(self) -> InterpreterState { let mut final_env = self.interp.deconstruct(); final_env.insert(self.go_port, Value::bit_low()); - finish_interpretation(final_env, self.done_port, self.continuous.iter()) - .unwrap() + finish_interpretation( + final_env, + Some(self.done_port), + self.continuous.iter(), + ) + .unwrap() } fn run(&mut self) -> InterpreterResult<()> { diff --git a/interp/src/interpreter/steppers/group_interpreter.rs b/interp/src/interpreter/steppers/group_interpreter.rs index 7f25e968c7..6530090370 100644 --- a/interp/src/interpreter/steppers/group_interpreter.rs +++ b/interp/src/interpreter/steppers/group_interpreter.rs @@ -66,7 +66,7 @@ where /// group of assignments pub struct AssignmentInterpreter<'a> { state: InterpreterState, - done_port: ConstPort, + done_port: Option, assigns: AssignmentOwner<'a>, cells: Vec>, val_changed: Option, @@ -77,7 +77,7 @@ impl<'a> AssignmentInterpreter<'a> { /// assignments from an outside context pub fn new( state: InterpreterState, - done_signal: ConstPort, + done_signal: Option, assigns: (I1, I2), ) -> Self where @@ -101,7 +101,7 @@ impl<'a> AssignmentInterpreter<'a> { /// interpretes pub fn new_owned( state: InterpreterState, - done_signal: ConstPort, + done_signal: Option, vecs: (Vec, Vec), ) -> Self { let done_port = done_signal; @@ -268,7 +268,10 @@ impl<'a> AssignmentInterpreter<'a> { #[inline] fn is_done(&self) -> bool { - utils::is_signal_high(self.state.get_from_port(self.done_port)) + self.done_port.is_none() + || utils::is_signal_high( + self.state.get_from_port(self.done_port.unwrap()), + ) } pub fn deconstruct(self) -> InterpreterState { diff --git a/interp/src/primitives/stateful.rs b/interp/src/primitives/stateful.rs index 42d98fba83..8b13404c5e 100644 --- a/interp/src/primitives/stateful.rs +++ b/interp/src/primitives/stateful.rs @@ -18,15 +18,14 @@ where }) } -///Pipelined Multiplication (3 cycles) -///Still bounded by u64. -///How to use: -///[execute] with the desired bindings. To capture these bindings -///into the internal (out) queue, [do_tick()]. -///The product associated with a given input will -///be output on the third [do_tick()]. -///Note: Calling [execute] multiple times before [do_tick()] has no effect; only -///the last set of inputs prior to the [do_tick()] will be saved. +/// Pipelined Multiplication (3 cycles) +/// Still bounded by u64. +/// How to use: +/// [Primitive::execute] with the desired bindings. +/// To capture these bindings into the internal (out) queue, [Primitive::do_tick]. +/// The product associated with a given input will be output on the third [do_tick()]. +/// Note: Calling [Primitive::execute] multiple times before [Primitive::do_tick] has no effect; only the last +/// set of inputs prior to the [Primitve::do_tick] will be saved. #[derive(Default)] pub struct StdMultPipe { pub width: u64, diff --git a/interp/tests/complex/unsigned-dot-product.futil b/interp/tests/complex/unsigned-dot-product.futil index 0de0f02358..be581681ad 100644 --- a/interp/tests/complex/unsigned-dot-product.futil +++ b/interp/tests/complex/unsigned-dot-product.futil @@ -16,8 +16,7 @@ component main() -> () { @external mult = std_mult_pipe(32); } wires { - group is_less_than<"static"=0> { - is_less_than[done] = 1'd1; + comb group is_less_than<"static"=0> { lt0.left = counter.out; lt0.right = 3'd4; } // Control segment for `counter` < `4`. diff --git a/interp/tests/control/if.futil b/interp/tests/control/if.futil index d5548255d3..69ff5ed80b 100644 --- a/interp/tests/control/if.futil +++ b/interp/tests/control/if.futil @@ -7,12 +7,11 @@ component main() -> () { } wires { - group cond<"static"=0> { + comb group cond<"static"=0> { //b/c lt is used in this distinctly not-last group, //by end of execution all its ports are 0/X/not asserted. lt.left = 32'd9; lt.right = 32'd16; - cond[done] = 1'd1; } group true<"static"=1> { diff --git a/interp/tests/control/if_reg.futil b/interp/tests/control/if_reg.futil index 7a536e67dc..69fde2ae28 100644 --- a/interp/tests/control/if_reg.futil +++ b/interp/tests/control/if_reg.futil @@ -36,10 +36,14 @@ component main() -> () { //b/c this isn't just 1 group prog, //reg1 should have [done] low //at the end of execution (tho prog. ends when reg1.done is high) - if lt0.out with cond { + seq { + cond; + if reg0.out { true; } else { false; } + } + } } diff --git a/interp/tests/control/iteration/iter_mult.futil b/interp/tests/control/iteration/iter_mult.futil index 1753ec79b1..5627769dd0 100644 --- a/interp/tests/control/iteration/iter_mult.futil +++ b/interp/tests/control/iteration/iter_mult.futil @@ -131,11 +131,10 @@ component main() -> () { shift_c[done] = c.done; } - group cond_while { + comb group cond_while { //lt compares i and 4 lt.left = i.out; lt.right = 3'd4; - cond_while[done] = 1'b1; } group incr_while { @@ -147,10 +146,9 @@ component main() -> () { incr_while[done] = i.done; } - group cond_if { + comb group cond_if { //get LSB of Q slicer.in = q.out; - cond_if[done] = 1'b1; } } diff --git a/interp/tests/control/iteration/while.futil b/interp/tests/control/iteration/while.futil index f82d2012e5..4e2b6b1ff6 100644 --- a/interp/tests/control/iteration/while.futil +++ b/interp/tests/control/iteration/while.futil @@ -8,11 +8,10 @@ component main() -> () { } wires { - group cond<"static"=0> { //how can something take 0 cycles? + comb group cond<"static"=0> { //how can something take 0 cycles? i.addr0 = 1'd0; lt.left = i.read_data; lt.right = 32'd8; - cond[done] = 1'b1; } group incr<"static"=1> { diff --git a/interp/tests/control/static/while_static.futil b/interp/tests/control/static/while_static.futil index 00c32f3848..5dae8dfc4e 100644 --- a/interp/tests/control/static/while_static.futil +++ b/interp/tests/control/static/while_static.futil @@ -6,8 +6,7 @@ component main() -> () { } wires { - group cond_while { - cond_while[done] = 1'b1; + comb group cond_while { } } diff --git a/interp/tests/errors/multiple_drivers_comb.futil b/interp/tests/errors/multiple_drivers_comb.futil index 5f88b21e3a..24edc54fd6 100644 --- a/interp/tests/errors/multiple_drivers_comb.futil +++ b/interp/tests/errors/multiple_drivers_comb.futil @@ -8,11 +8,10 @@ component main() -> () { } wires { - group op { + comb group op { add0.left = const0.out; add0.left = const1.out; add0.right = const1.out; - op[done] = 1'b1; } } diff --git a/primitives/binary_operators.futil b/primitives/binary_operators.futil index bf4818077c..fc0290c4e5 100644 --- a/primitives/binary_operators.futil +++ b/primitives/binary_operators.futil @@ -1,10 +1,10 @@ extern "binary_operators.sv" { /// =================== Unsigned, Fixed Point ========================= - primitive std_fp_add<"share"=1>[ + comb primitive std_fp_add<"share"=1>[ WIDTH, INT_WIDTH, FRAC_WIDTH ](left: WIDTH, right: WIDTH) ->(out: WIDTH); - primitive std_fp_sub<"share"=1>[ + comb primitive std_fp_sub<"share"=1>[ WIDTH, INT_WIDTH, FRAC_WIDTH ](left: WIDTH, right: WIDTH)->(out: WIDTH); @@ -33,11 +33,11 @@ extern "binary_operators.sv" { @done done: 1 ); - primitive std_fp_gt<"share"=1>[ + comb primitive std_fp_gt<"share"=1>[ WIDTH, INT_WIDTH, FRAC_WIDTH ](left: WIDTH, right: WIDTH) -> (out: 1); - primitive std_fp_add_dwidth<"share"=1>[ + comb primitive std_fp_add_dwidth<"share"=1>[ WIDTH1, WIDTH2, INT_WIDTH1, @@ -48,11 +48,11 @@ extern "binary_operators.sv" { ](left: WIDTH1, right: WIDTH2) -> (out: OUT_WIDTH); /// =================== Signed, Fixed Point ========================= - primitive std_fp_sadd<"share"=1>[ + comb primitive std_fp_sadd<"share"=1>[ WIDTH, INT_WIDTH, FRAC_WIDTH ](left: WIDTH, right: WIDTH) -> (out: WIDTH); - primitive std_fp_ssub<"share"=1>[ + comb primitive std_fp_ssub<"share"=1>[ WIDTH, INT_WIDTH, FRAC_WIDTH ](left: WIDTH, right: WIDTH) -> (out: WIDTH); @@ -81,15 +81,15 @@ extern "binary_operators.sv" { @done done: 1 ); - primitive std_fp_sgt<"share"=1>[ + comb primitive std_fp_sgt<"share"=1>[ WIDTH, INT_WIDTH, FRAC_WIDTH ](left: WIDTH, right: WIDTH) -> (out: 1); - primitive std_fp_slt<"share"=1>[ + comb primitive std_fp_slt<"share"=1>[ WIDTH, INT_WIDTH, FRAC_WIDTH ](left: WIDTH, right: WIDTH) -> (out: 1); - primitive std_fp_sadd_dwidth<"share"=1>[ + comb primitive std_fp_sadd_dwidth<"share"=1>[ WIDTH1, WIDTH2 , INT_WIDTH1, @@ -125,8 +125,8 @@ extern "binary_operators.sv" { ); /// =================== Signed, Bitnum ========================= - primitive std_sadd<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); - primitive std_ssub<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); + comb primitive std_sadd<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); + comb primitive std_ssub<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); primitive std_smult_pipe[WIDTH]( @write_together(1) left: WIDTH, @@ -148,13 +148,13 @@ extern "binary_operators.sv" { @done done: 1 ); - primitive std_sgt<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: 1); - primitive std_slt<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: 1); - primitive std_seq<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: 1); - primitive std_sneq<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: 1); - primitive std_sge<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: 1); - primitive std_sle<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: 1); - primitive std_slsh<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); - primitive std_srsh<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); + comb primitive std_sgt<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: 1); + comb primitive std_slt<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: 1); + comb primitive std_seq<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: 1); + comb primitive std_sneq<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: 1); + comb primitive std_sge<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: 1); + comb primitive std_sle<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: 1); + comb primitive std_slsh<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); + comb primitive std_srsh<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); } diff --git a/primitives/core.futil b/primitives/core.futil index af79efd097..65f1318c07 100644 --- a/primitives/core.futil +++ b/primitives/core.futil @@ -1,27 +1,27 @@ extern "core.sv" { /// Primitives - primitive std_const<"share"=1>[WIDTH, VALUE]() -> (out: WIDTH); - primitive std_slice<"share"=1>[IN_WIDTH, OUT_WIDTH](in: IN_WIDTH) -> (out: OUT_WIDTH); - primitive std_pad<"share"=1>[IN_WIDTH, OUT_WIDTH](in: IN_WIDTH) -> (out: OUT_WIDTH); + comb primitive std_const<"share"=1>[WIDTH, VALUE]() -> (out: WIDTH); + comb primitive std_slice<"share"=1>[IN_WIDTH, OUT_WIDTH](in: IN_WIDTH) -> (out: OUT_WIDTH); + comb primitive std_pad<"share"=1>[IN_WIDTH, OUT_WIDTH](in: IN_WIDTH) -> (out: OUT_WIDTH); /// Logical operators - primitive std_not<"share"=1>[WIDTH](in: WIDTH) -> (out: WIDTH); - primitive std_and<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); - primitive std_or<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); - primitive std_xor<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); + comb primitive std_not<"share"=1>[WIDTH](in: WIDTH) -> (out: WIDTH); + comb primitive std_and<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); + comb primitive std_or<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); + comb primitive std_xor<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); /// Numerical Operators - primitive std_add<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); - primitive std_sub<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); - primitive std_gt<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: 1); - primitive std_lt<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: 1); - primitive std_eq<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: 1); - primitive std_neq<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: 1); - primitive std_ge<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: 1); - primitive std_le<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: 1); - primitive std_lsh<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); - primitive std_rsh<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); - primitive std_mux<"share"=1>[WIDTH](cond: 1, tru: WIDTH, fal: WIDTH) -> (out: WIDTH); + comb primitive std_add<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); + comb primitive std_sub<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); + comb primitive std_gt<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: 1); + comb primitive std_lt<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: 1); + comb primitive std_eq<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: 1); + comb primitive std_neq<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: 1); + comb primitive std_ge<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: 1); + comb primitive std_le<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: 1); + comb primitive std_lsh<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); + comb primitive std_rsh<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); + comb primitive std_mux<"share"=1>[WIDTH](cond: 1, tru: WIDTH, fal: WIDTH) -> (out: WIDTH); /// Memories primitive std_reg<"static"=1>[WIDTH]( diff --git a/primitives/math.futil b/primitives/math.futil index 2cb46210c8..915d524788 100644 --- a/primitives/math.futil +++ b/primitives/math.futil @@ -57,10 +57,9 @@ component pow(base: 32, exp: 32) -> (out: 32) { count.write_en = 1'd1; incr_count[done] = count.done; } - group cond<"static"=0> { + comb group cond { lt.right = exp; lt.left = count.out; - cond[done] = 1'd1; } out = t.out; } diff --git a/primitives/tcam.futil b/primitives/tcam.futil index 5ac07ac740..3a8c40c972 100644 --- a/primitives/tcam.futil +++ b/primitives/tcam.futil @@ -73,7 +73,7 @@ component comparator_element(lenA: 5, lenB: 5, addrA: 5, addrB: 5, mlA: 1, mlB: ml = std_reg(1); } wires { - group select<"static"=0> { + comb group select { gt0.left = lenA; gt0.right = lenB; not0.in = mlB; @@ -81,7 +81,6 @@ component comparator_element(lenA: 5, lenB: 5, addrA: 5, addrB: 5, mlA: 1, mlB: or0.right = gt0.out; and0.left = mlA; and0.right = or0.out; - select[done] = 1'd1; } group A<"static"=1> { len.write_en = 1'd1; @@ -312,23 +311,46 @@ component TCAM_IPv4(write_en: 1, search_en: 1, in: 32, prefix_len: 6, write_inde ce31 = comparator_element(); ce40 = comparator_element(); + + comb_reg0 = std_reg(1); + comb_reg1 = std_reg(1); + comb_reg2 = std_reg(1); + comb_reg3 = std_reg(1); + comb_reg4 = std_reg(1); + comb_reg5 = std_reg(1); + comb_reg6 = std_reg(1); + comb_reg7 = std_reg(1); + comb_reg8 = std_reg(1); + comb_reg9 = std_reg(1); + comb_reg10 = std_reg(1); + comb_reg11 = std_reg(1); + comb_reg12 = std_reg(1); + comb_reg13 = std_reg(1); + comb_reg14 = std_reg(1); + comb_reg15 = std_reg(1); + comb_reg16 = std_reg(1); + comb_reg17 = std_reg(1); + comb_reg18 = std_reg(1); + comb_reg19 = std_reg(1); + comb_reg20 = std_reg(1); + comb_reg21 = std_reg(1); + comb_reg22 = std_reg(1); + comb_reg23 = std_reg(1); + comb_reg24 = std_reg(1); + comb_reg25 = std_reg(1); + comb_reg26 = std_reg(1); + comb_reg27 = std_reg(1); + comb_reg28 = std_reg(1); + comb_reg29 = std_reg(1); + comb_reg30 = std_reg(1); + comb_reg31 = std_reg(1); + out = std_reg(5); } wires { - group is_write_enabled<"static"=0> { - w_eq.left = write_en; - w_eq.right = 1'd1; - is_write_enabled[done] = 1'd1; - } - group is_length_zero<"static"=0> { + comb group is_length_zero { z_eq.left = 6'd0; z_eq.right = prefix_len; - is_length_zero[done] = 1'd1; - } - group is_search_enabled<"static"=0> { - s_eq.left = search_en; - s_eq.right = 1'd1; - is_search_enabled[done] = 1'd1; } group write_zero<"static"=1> { zero_index.write_en = 1'd1; @@ -655,7 +677,7 @@ component TCAM_IPv4(write_en: 1, search_en: 1, in: 32, prefix_len: 6, write_inde p31.in = in; write31[done] = p31.done & l31.done ? 1'd1; } - group find_write_index<"static"=0> { + group find_write_index { is_index0.left = 5'd0; is_index1.left = 5'd1; is_index2.left = 5'd2; @@ -722,12 +744,71 @@ component TCAM_IPv4(write_en: 1, search_en: 1, in: 32, prefix_len: 6, write_inde is_index29.right = write_index; is_index30.right = write_index; is_index31.right = write_index; - find_write_index[done] = 1'd1; - } - group validity<"static"=0> { - is_invalid.left = ce40.mlX; - is_invalid.right = 1'd0; - validity[done] = 1'd1; + comb_reg0.in = is_index0.out; + comb_reg0.write_en = 1'd1; + comb_reg1.in = is_index1.out; + comb_reg1.write_en = 1'd1; + comb_reg2.in = is_index2.out; + comb_reg2.write_en = 1'd1; + comb_reg3.in = is_index3.out; + comb_reg3.write_en = 1'd1; + comb_reg4.in = is_index4.out; + comb_reg4.write_en = 1'd1; + comb_reg5.in = is_index5.out; + comb_reg5.write_en = 1'd1; + comb_reg6.in = is_index6.out; + comb_reg6.write_en = 1'd1; + comb_reg7.in = is_index7.out; + comb_reg7.write_en = 1'd1; + comb_reg8.in = is_index8.out; + comb_reg8.write_en = 1'd1; + comb_reg9.in = is_index9.out; + comb_reg9.write_en = 1'd1; + comb_reg10.in = is_index10.out; + comb_reg10.write_en = 1'd1; + comb_reg11.in = is_index11.out; + comb_reg11.write_en = 1'd1; + comb_reg12.in = is_index12.out; + comb_reg12.write_en = 1'd1; + comb_reg13.in = is_index13.out; + comb_reg13.write_en = 1'd1; + comb_reg14.in = is_index14.out; + comb_reg14.write_en = 1'd1; + comb_reg15.in = is_index15.out; + comb_reg15.write_en = 1'd1; + comb_reg16.in = is_index16.out; + comb_reg16.write_en = 1'd1; + comb_reg17.in = is_index17.out; + comb_reg17.write_en = 1'd1; + comb_reg18.in = is_index18.out; + comb_reg18.write_en = 1'd1; + comb_reg19.in = is_index19.out; + comb_reg19.write_en = 1'd1; + comb_reg20.in = is_index20.out; + comb_reg20.write_en = 1'd1; + comb_reg21.in = is_index21.out; + comb_reg21.write_en = 1'd1; + comb_reg22.in = is_index22.out; + comb_reg22.write_en = 1'd1; + comb_reg23.in = is_index23.out; + comb_reg23.write_en = 1'd1; + comb_reg24.in = is_index24.out; + comb_reg24.write_en = 1'd1; + comb_reg25.in = is_index25.out; + comb_reg25.write_en = 1'd1; + comb_reg26.in = is_index26.out; + comb_reg26.write_en = 1'd1; + comb_reg27.in = is_index27.out; + comb_reg27.write_en = 1'd1; + comb_reg28.in = is_index28.out; + comb_reg28.write_en = 1'd1; + comb_reg29.in = is_index29.out; + comb_reg29.write_en = 1'd1; + comb_reg30.in = is_index30.out; + comb_reg30.write_en = 1'd1; + comb_reg31.in = is_index31.out; + comb_reg31.write_en = 1'd1; + find_write_index[done] = comb_reg0.done; } group default_to_zero_length_index<"static"=1> { out.write_en = 1'd1; @@ -745,47 +826,50 @@ component TCAM_IPv4(write_en: 1, search_en: 1, in: 32, prefix_len: 6, write_inde control { par { - if w_eq.out with is_write_enabled { + if write_en { if z_eq.out with is_length_zero { write_zero; } else { - par { - if is_index0.out with find_write_index { write0; } - if is_index1.out with find_write_index { write1; } - if is_index2.out with find_write_index { write2; } - if is_index3.out with find_write_index { write3; } - if is_index4.out with find_write_index { write4; } - if is_index5.out with find_write_index { write5; } - if is_index6.out with find_write_index { write6; } - if is_index7.out with find_write_index { write7; } - if is_index8.out with find_write_index { write8; } - if is_index9.out with find_write_index { write9; } - if is_index10.out with find_write_index { write10; } - if is_index11.out with find_write_index { write11; } - if is_index12.out with find_write_index { write12; } - if is_index13.out with find_write_index { write13; } - if is_index14.out with find_write_index { write14; } - if is_index15.out with find_write_index { write15; } - if is_index16.out with find_write_index { write16; } - if is_index17.out with find_write_index { write17; } - if is_index18.out with find_write_index { write18; } - if is_index19.out with find_write_index { write19; } - if is_index20.out with find_write_index { write20; } - if is_index21.out with find_write_index { write21; } - if is_index22.out with find_write_index { write22; } - if is_index23.out with find_write_index { write23; } - if is_index24.out with find_write_index { write24; } - if is_index25.out with find_write_index { write25; } - if is_index26.out with find_write_index { write26; } - if is_index27.out with find_write_index { write27; } - if is_index28.out with find_write_index { write28; } - if is_index29.out with find_write_index { write29; } - if is_index30.out with find_write_index { write30; } - if is_index31.out with find_write_index { write31; } + seq { + find_write_index; + par { + if comb_reg0.out { write0; } + if comb_reg1.out { write1; } + if comb_reg2.out { write2; } + if comb_reg3.out { write3; } + if comb_reg4.out { write4; } + if comb_reg5.out { write5; } + if comb_reg6.out { write6; } + if comb_reg7.out { write7; } + if comb_reg8.out { write8; } + if comb_reg9.out { write9; } + if comb_reg10.out { write10; } + if comb_reg11.out { write11; } + if comb_reg12.out { write12; } + if comb_reg13.out { write13; } + if comb_reg14.out { write14; } + if comb_reg15.out { write15; } + if comb_reg16.out { write16; } + if comb_reg17.out { write17; } + if comb_reg18.out { write18; } + if comb_reg19.out { write19; } + if comb_reg20.out { write20; } + if comb_reg21.out { write21; } + if comb_reg22.out { write22; } + if comb_reg23.out { write23; } + if comb_reg24.out { write24; } + if comb_reg25.out { write25; } + if comb_reg26.out { write26; } + if comb_reg27.out { write27; } + if comb_reg28.out { write28; } + if comb_reg29.out { write29; } + if comb_reg30.out { write30; } + if comb_reg31.out { write31; } + } } } } - if s_eq.out with is_search_enabled { + if search_en { seq { par { invoke me0(in=in, prefix=p0.out, length=l0.out)(); @@ -860,7 +944,8 @@ component TCAM_IPv4(write_en: 1, search_en: 1, in: 32, prefix_len: 6, write_inde invoke ce31(lenA=ce22.lenX, lenB=ce23.lenX, addrA=ce22.addrX, addrB=ce23.addrX, mlA=ce22.mlX, mlB=ce23.mlX)(); } invoke ce40(lenA=ce30.lenX, lenB=ce31.lenX, addrA=ce30.addrX, addrB=ce31.addrX, mlA=ce30.mlX, mlB=ce31.mlX)(); - if is_invalid.out with validity { default_to_zero_length_index; } else { save_index; } + // If the final comparator has a valid value then save it. + if ce40.mlX { save_index; } else { default_to_zero_length_index; } } } } diff --git a/primitives/unsynthesizable.futil b/primitives/unsynthesizable.futil index e3d32fad66..2cc79a0d98 100644 --- a/primitives/unsynthesizable.futil +++ b/primitives/unsynthesizable.futil @@ -1,12 +1,12 @@ extern "unsynthesizable.sv" { // Designs that use these primitives cannot be synthesized. - primitive std_unsyn_mult<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); - primitive std_unsyn_div<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); - primitive std_unsyn_mod<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); + comb primitive std_unsyn_mult<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); + comb primitive std_unsyn_div<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); + comb primitive std_unsyn_mod<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); - primitive std_unsyn_smult<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); - primitive std_unsyn_sdiv<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); - primitive std_unsyn_smod<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); + comb primitive std_unsyn_smult<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); + comb primitive std_unsyn_sdiv<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); + comb primitive std_unsyn_smod<"share"=1>[WIDTH](left: WIDTH, right: WIDTH) -> (out: WIDTH); } diff --git a/runt.toml b/runt.toml index ee4ea54fd8..e562549bc3 100644 --- a/runt.toml +++ b/runt.toml @@ -5,7 +5,6 @@ ver = "0.3.2" [[tests]] name = "[core] passes" paths = [ - "tests/passes/*.futil", "tests/passes/**/*.futil", ] # gets the pass flags a comment on the first line of the test file @@ -28,6 +27,7 @@ cmd = """ name = "[core] errors" paths = [ "tests/errors/*.futil", + "tests/errors/papercut/*.futil", "tests/errors/parser/*.futil" ] cmd = """ @@ -57,7 +57,6 @@ paths = [ ] cmd = """ fud exec -s verilog.data {}.data \ - -s futil.flags '-d static-timing' \ -s futil.exec './target/debug/futil' \ -s verilog.cycle_limit 500 \ {} --to dat \ @@ -87,18 +86,19 @@ fud exec -s verilog.data {}.data \ {} --to dat -q | jq .memories """ -## Same as correctness dynamic but runs with static-timing. -[[tests]] -name = "correctness static timing" -paths = [ "tests/correctness/*.futil" ] -cmd = """ -fud exec -s verilog.cycle_limit 500 \ - -s verilog.data {}.data \ - -s futil.exec './target/debug/futil' \ - {} --to dat \ - -q \ - | jq .memories -""" +## XXX: Same as correctness dynamic but runs with static-timing. +## Currently disabled. +# [[tests]] +# name = "correctness static timing" +# paths = [ "tests/correctness/*.futil" ] +# cmd = """ +# fud exec -s verilog.cycle_limit 500 \ + # -s verilog.data {}.data \ + # -s futil.exec './target/debug/futil' \ + # {} --to dat \ + # -q \ + # | jq .memories +# """ [[tests]] name = "[frontend] systolic array correctness" @@ -203,21 +203,27 @@ python3 {} name = "language tutorial" paths = ["examples/tutorial/*.futil"] cmd = """ -fud e {} --to dat -s verilog.data examples/tutorial/data.json -q +fud e {} -s verilog.cycle_limit 500 \ + -s verilog.data examples/tutorial/data.json \ + --to dat -q """ [[tests]] name = "memory by reference tutorial" paths = ["examples/futil/memory-by-reference/*.futil"] cmd = """ -fud e {} --to dat -s verilog.data {}.data -q +fud e {} -s verilog.cycle_limit 500 \ + -s verilog.data {}.data \ + --to dat -q """ [[tests]] name = "dahlia examples" paths = ["examples/dahlia/*.fuse"] cmd = """ -fud e {} --to dat -s verilog.data {}.data -q +fud e {} -s verilog.cycle_limit 500 \ + -s verilog.data {}.data \ + --to dat -q """ [[tests]] diff --git a/src/backend/circt.rs b/src/backend/circt.rs index 772cd1f93d..cfcba1f490 100644 --- a/src/backend/circt.rs +++ b/src/backend/circt.rs @@ -134,6 +134,10 @@ impl CirctBackend { Self::write_group(&group.borrow(), 4, f)?; writeln!(f)?; } + for comb_group in comp.comb_groups.iter() { + Self::write_comb_group(&comb_group.borrow(), 4, f)?; + writeln!(f)?; + } // Write the continuous assignments for assign in &comp.continuous_assignments { Self::write_assignment(assign, 4, f)?; @@ -162,6 +166,7 @@ impl CirctBackend { ir::CellType::Primitive { name, param_binding, + .. } => { let bind: HashMap<&str, u64> = param_binding .iter() @@ -301,6 +306,23 @@ impl CirctBackend { write!(f, "{}}}", " ".repeat(indent_level)) } + /// Format and write combinational groups + pub fn write_comb_group( + group: &ir::CombGroup, + indent_level: usize, + f: &mut F, + ) -> io::Result<()> { + write!(f, "{}", " ".repeat(indent_level))?; + write!(f, "calyx.group @{}", group.name().id)?; + writeln!(f, " {{")?; + + for assign in &group.assignments { + Self::write_assignment(assign, indent_level + 2, f)?; + writeln!(f)?; + } + write!(f, "{}}}", " ".repeat(indent_level)) + } + /// Format and write a control program pub fn write_control( control: &ir::Control, @@ -336,11 +358,15 @@ impl CirctBackend { fbranch, .. }) => { + assert!( + cond.is_some(), + "`if` without `with` not support in CIRCT backend" + ); writeln!( f, "calyx.if {} with @{} {{", Self::get_port_access(&port.borrow()), - cond.borrow().name().id + cond.as_ref().unwrap().borrow().name().id )?; Self::write_control(tbranch, indent_level + 2, f)?; write!(f, "{}}}", " ".repeat(indent_level))?; @@ -355,11 +381,15 @@ impl CirctBackend { ir::Control::While(ir::While { port, cond, body, .. }) => { + assert!( + cond.is_some(), + "`while` without `with` not support in CIRCT backend" + ); writeln!( f, "calyx.while {} with @{} {{", Self::get_port_access(&port.borrow()), - cond.borrow().name().id + cond.as_ref().unwrap().borrow().name().id )?; Self::write_control(body, indent_level + 2, f)?; writeln!(f, "{}}}", " ".repeat(indent_level)) diff --git a/src/cmdline.rs b/src/cmdline.rs index b66e7fc62b..69b283f64b 100644 --- a/src/cmdline.rs +++ b/src/cmdline.rs @@ -49,6 +49,10 @@ pub struct Opts { #[argh(option, short = 'd', long = "disable-pass")] pub disable_pass: Vec, + /// extra options passed to the context + #[argh(option, short = 'x', long = "extra-opt")] + pub extra_opts: Vec, + /// list all avaliable pass options #[argh(switch, long = "list-passes")] pub list_passes: bool, diff --git a/src/main.rs b/src/main.rs index 741246b57a..082dff0763 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,7 @@ fn main() -> CalyxResult<()> { let pm = PassManager::default_passes()?; // parse the command line arguments into Opts struct - let opts = Opts::get_opts(); + let mut opts = Opts::get_opts(); // list all the avaliable pass options when flag --list-passes is enabled if opts.list_passes { @@ -20,15 +20,16 @@ fn main() -> CalyxResult<()> { let namespace = frontend::NamespaceDef::new(&opts.file, &opts.lib_path)?; // Build the IR representation - let mut rep = ir::from_ast::ast_to_ir( + let mut ctx = ir::from_ast::ast_to_ir( namespace, opts.enable_synthesis, !opts.disable_verify, )?; + ctx.extra_opts = opts.extra_opts.drain(..).collect(); // Run all passes specified by the command line - pm.execute_plan(&mut rep, &opts.pass, &opts.disable_pass)?; + pm.execute_plan(&mut ctx, &opts.pass, &opts.disable_pass)?; - opts.run_backend(&rep)?; + opts.run_backend(&ctx)?; Ok(()) } diff --git a/tests/backend/circt/no-guards.expect b/tests/backend/circt/no-guards.expect index 9940b25c7c..0a054455e0 100644 --- a/tests/backend/circt/no-guards.expect +++ b/tests/backend/circt/no-guards.expect @@ -34,17 +34,21 @@ calyx.component @main(%go: i1, %clk: i1, %reset: i1, %go0: i1, %clk0: i1, %reset calyx.assign %r.write_en = %_1_1.out : i1 calyx.group_done %r.done : i1 } + calyx.group @CombGroup { + calyx.assign %add.left = %r.out : i8 + calyx.assign %add.right = %_1_8.out : i8 + } calyx.assign %c0.go = %_0_1.out : i1 } calyx.control { calyx.seq { - calyx.enable @Group1 - calyx.while %c1.out with @Group2 { + calyx.enable @Group2 + calyx.while %c1.out with @CombGroup { calyx.seq { calyx.enable @Group1 calyx.enable @Group1 - calyx.if %c1.out with @Group2 { + calyx.if %c1.out with @CombGroup { calyx.enable @Group2 } } diff --git a/tests/backend/circt/no-guards.futil b/tests/backend/circt/no-guards.futil index b00e2a7132..7579c5e547 100644 --- a/tests/backend/circt/no-guards.futil +++ b/tests/backend/circt/no-guards.futil @@ -39,16 +39,20 @@ component main(go: 1, clk: 1, reset: 1) -> (done: 1) { r.write_en = 1'd1; Group2[done] = r.done; } + comb group CombGroup { + add.left = r.out; + add.right = 8'd1; + } c0.go = 1'd0; } control { seq { - Group1; - while c1.out with Group2 { + Group2; + while c1.out with CombGroup { seq { Group1; Group1; - if c1.out with Group2 { + if c1.out with CombGroup { Group2; } } diff --git a/tests/backend/circt/with-guards.expect b/tests/backend/circt/with-guards.expect index 5d25c8dabb..54c9545822 100644 --- a/tests/backend/circt/with-guards.expect +++ b/tests/backend/circt/with-guards.expect @@ -22,8 +22,10 @@ calyx.component @main(%go: i1, %clk: i1, %reset: i1) -> (%done: i1) { %m1.addr0, %m1.addr1, %m1.write_data, %m1.write_en, %m1.clk, %m1.read_data, %m1.done = calyx.memory "m1"<[64, 64] x 8> [6, 6] : i6, i6, i8, i1, i1, i8, i1 %a0.left, %a0.right, %a0.out = calyx.std_add "a0" : i32, i32, i32 %s0.in, %s0.out = calyx.std_slice "s0" : i32, i8 + %add.left, %add.right, %add.out = calyx.std_add "add" : i8, i8, i8 %_0_1.out = hw.constant 0 : i1 %_1_1.out = hw.constant 1 : i1 + %_1_8.out = hw.constant 1 : i8 %or0.left, %or0.right, %or0.out = calyx.std_or "or0" : i1, i1, i1 %or1.left, %or1.right, %or1.out = calyx.std_or "or1" : i1, i1, i1 %and0.left, %and0.right, %and0.out = calyx.std_and "and0" : i1, i1, i1 @@ -45,17 +47,21 @@ calyx.component @main(%go: i1, %clk: i1, %reset: i1) -> (%done: i1) { calyx.assign %and0.right = %or1.out : i1 calyx.group_done %c1.done, %and0.out ? : i1 } + calyx.group @CombGroup { + calyx.assign %add.left = %r.out : i8 + calyx.assign %add.right = %_1_8.out : i8 + } calyx.assign %c0.go = %c1.out, %_1_1.out ? : i1 } calyx.control { calyx.seq { calyx.enable @Group1 - calyx.while %c1.in with @Group2 { + calyx.while %c1.in with @CombGroup { calyx.seq { calyx.enable @Group1 calyx.enable @Group1 - calyx.if %c1.in with @Group2 { + calyx.if %c1.in with @CombGroup { calyx.enable @Group2 } } diff --git a/tests/backend/circt/with-guards.futil b/tests/backend/circt/with-guards.futil index 09ba058bdf..761a4604bd 100644 --- a/tests/backend/circt/with-guards.futil +++ b/tests/backend/circt/with-guards.futil @@ -25,6 +25,7 @@ component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { m1 = std_mem_d2(8, 64, 64, 6, 6); a0 = std_add(32); s0 = std_slice(32, 8); + add = std_add(8); } wires { group Group1 { @@ -38,16 +39,20 @@ component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { c1.in = (c1.out | c0.done) ? c1.out; Group2[done] = (c1.out & 1'd1 & (c1.out | c0.done)) ? c1.done; } + comb group CombGroup { + add.left = r.out; + add.right = 8'd1; + } c0.go = c1.out; } control { seq { Group1; - while c1.in with Group2 { + while c1.in with CombGroup { seq { Group1; Group1; - if c1.in with Group2 { + if c1.in with CombGroup { Group2; } } diff --git a/tests/correctness/exp/degree-4-signed.expect b/tests/correctness/exp/degree-4-signed.expect index 356232b8f5..cad81378ce 100644 --- a/tests/correctness/exp/degree-4-signed.expect +++ b/tests/correctness/exp/degree-4-signed.expect @@ -1,5 +1,5 @@ { - "cycles": 88, + "cycles": 60, "memories": { "ret": [ "2.7182769775390625" diff --git a/tests/correctness/exp/degree-4-signed.txt.futil b/tests/correctness/exp/degree-4-signed.txt.futil deleted file mode 100644 index 27748a283c..0000000000 --- a/tests/correctness/exp/degree-4-signed.txt.futil +++ /dev/null @@ -1,274 +0,0 @@ -import "primitives/std.lib"; -component exp(x: 32) -> (out: 32) { - cells { - exponent_value = std_reg(32); - int_x = std_reg(32); - frac_x = std_reg(32); - m = std_reg(32); - and0 = std_and(32); - and1 = std_and(32); - rsh = std_rsh(32); - lt = std_slt(32); - c2 = std_const(32, 2); - c3 = std_const(32, 3); - c4 = std_const(32, 4); - one = std_const(32, 65536); - e = std_const(32, 178145); - negative_one = std_const(32, 4294901760); - product2 = std_reg(32); - product3 = std_reg(32); - product4 = std_reg(32); - p2 = std_reg(32); - p3 = std_reg(32); - p4 = std_reg(32); - sum1 = std_reg(32); - sum2 = std_reg(32); - add1 = std_fp_sadd(32, 16, 16); - add2 = std_fp_sadd(32, 16, 16); - mult_pipe1 = std_fp_smult_pipe(32, 16, 16); - mult_pipe2 = std_fp_smult_pipe(32, 16, 16); - mult_pipe3 = std_fp_smult_pipe(32, 16, 16); - mult_pipe4 = std_fp_smult_pipe(32, 16, 16); - div_pipe = std_fp_sdiv_pipe(32, 16, 16); - reciprocal_factorial2 = std_const(32, 32768); - reciprocal_factorial3 = std_const(32, 10923); - reciprocal_factorial4 = std_const(32, 2731); - pow1 = fp_pow(); - pow2 = fp_pow(); - pow3 = fp_pow(); - pow4 = fp_pow(); - } - wires { - group init<"static"=1> { - exponent_value.write_en = 1'd1; - exponent_value.in = x; - init[done] = exponent_value.done; - } - group split_bits { - and0.left = exponent_value.out; - and0.right = 32'd4294901760; - rsh.left = and0.out; - rsh.right = 32'd16; - and1.left = exponent_value.out; - and1.right = 32'd65535; - int_x.write_en = 1'd1; - frac_x.write_en = 1'd1; - int_x.in = rsh.out; - frac_x.in = and1.out; - split_bits[done] = int_x.done & frac_x.done ? 1'd1; - } - group negate { - mult_pipe1.left = exponent_value.out; - mult_pipe1.right = negative_one.out; - mult_pipe1.go = !mult_pipe1.done ? 1'd1; - exponent_value.write_en = mult_pipe1.done; - exponent_value.in = mult_pipe1.out; - negate[done] = exponent_value.done; - } - group is_negative<"static"=0> { - lt.left = x; - lt.right = 32'd0; - is_negative[done] = 1'd1; - } - group reciprocal { - div_pipe.left = one.out; - div_pipe.right = m.out; - div_pipe.go = !div_pipe.done ? 1'd1; - m.write_en = div_pipe.done; - m.in = div_pipe.out_quotient; - reciprocal[done] = m.done; - } - group consume_pow2<"static"=1> { - p2.write_en = 1'd1; - p2.in = pow2.out; - consume_pow2[done] = p2.done ? 1'd1; - } - group consume_pow3<"static"=1> { - p3.write_en = 1'd1; - p3.in = pow3.out; - consume_pow3[done] = p3.done ? 1'd1; - } - group consume_pow4<"static"=1> { - p4.write_en = 1'd1; - p4.in = pow4.out; - consume_pow4[done] = p4.done ? 1'd1; - } - group mult_by_reciprocal_factorial2 { - mult_pipe2.left = p2.out; - mult_pipe2.right = reciprocal_factorial2.out; - mult_pipe2.go = !mult_pipe2.done ? 1'd1; - product2.write_en = mult_pipe2.done; - product2.in = mult_pipe2.out; - mult_by_reciprocal_factorial2[done] = product2.done; - } - group mult_by_reciprocal_factorial3 { - mult_pipe3.left = p3.out; - mult_pipe3.right = reciprocal_factorial3.out; - mult_pipe3.go = !mult_pipe3.done ? 1'd1; - product3.write_en = mult_pipe3.done; - product3.in = mult_pipe3.out; - mult_by_reciprocal_factorial3[done] = product3.done; - } - group mult_by_reciprocal_factorial4 { - mult_pipe4.left = p4.out; - mult_pipe4.right = reciprocal_factorial4.out; - mult_pipe4.go = !mult_pipe4.done ? 1'd1; - product4.write_en = mult_pipe4.done; - product4.in = mult_pipe4.out; - mult_by_reciprocal_factorial4[done] = product4.done; - } - group sum_round1_1<"static"=1> { - add1.left = frac_x.out; - add1.right = product2.out; - sum1.write_en = 1'd1; - sum1.in = add1.out; - sum_round1_1[done] = sum1.done; - } - group sum_round1_2<"static"=1> { - add2.left = product3.out; - add2.right = product4.out; - sum2.write_en = 1'd1; - sum2.in = add2.out; - sum_round1_2[done] = sum2.done; - } - group sum_round2_1<"static"=1> { - add1.left = sum1.out; - add1.right = sum2.out; - sum1.write_en = 1'd1; - sum1.in = add1.out; - sum_round2_1[done] = sum1.done; - } - group add_degree_zero<"static"=1> { - add1.left = sum1.out; - add1.right = one.out; - sum1.write_en = 1'd1; - sum1.in = add1.out; - add_degree_zero[done] = sum1.done; - } - group final_multiply { - mult_pipe1.left = pow1.out; - mult_pipe1.right = sum1.out; - mult_pipe1.go = !mult_pipe1.done ? 1'd1; - m.write_en = mult_pipe1.done; - m.in = mult_pipe1.out; - final_multiply[done] = m.done; - } - out = m.out; - } - control { - seq { - init; - if lt.out with is_negative { - negate; - } - split_bits; - par { - invoke pow1(base=e.out, integer_exp=int_x.out)(); - invoke pow2(base=frac_x.out, integer_exp=c2.out)(); - invoke pow3(base=frac_x.out, integer_exp=c3.out)(); - invoke pow4(base=frac_x.out, integer_exp=c4.out)(); - } - par { - consume_pow2; - consume_pow3; - consume_pow4; - } - par { - mult_by_reciprocal_factorial2; - mult_by_reciprocal_factorial3; - mult_by_reciprocal_factorial4; - } - par { - sum_round1_1; - sum_round1_2; - } - par { - sum_round2_1; - } - add_degree_zero; - final_multiply; - if lt.out with is_negative { - reciprocal; - } - } - } -} -component fp_pow(base: 32, integer_exp: 32) -> (out: 32) { - cells { - pow = std_reg(32); - count = std_reg(32); - mul = std_fp_smult_pipe(32, 16, 16); - lt = std_slt(32); - incr = std_sadd(32); - } - wires { - group init { - pow.in = 32'd65536; - pow.write_en = 1'd1; - count.in = 32'd0; - count.write_en = 1'd1; - init[done] = pow.done & count.done ? 1'd1; - } - group execute_mul { - mul.left = base; - mul.right = pow.out; - mul.go = !mul.done ? 1'd1; - pow.write_en = mul.done; - pow.in = mul.out; - execute_mul[done] = pow.done; - } - group incr_count { - incr.left = 32'd1; - incr.right = count.out; - count.in = incr.out; - count.write_en = 1'd1; - incr_count[done] = count.done; - } - group cond { - lt.left = count.out; - lt.right = integer_exp; - cond[done] = 1'd1; - } - out = pow.out; - } - control { - seq { - init; - while lt.out with cond { - par { - execute_mul; - incr_count; - } - } - } - } -} -component main() -> () { - cells { - t = std_reg(32); - @external(1) x = std_mem_d1(32, 1, 1); - @external(1) ret = std_mem_d1(32, 1, 1); - e = exp(); - } - wires { - group init { - x.addr0 = 1'd0; - t.in = x.read_data; - t.write_en = 1'd1; - init[done] = t.done; - } - group write_to_memory { - ret.addr0 = 1'd0; - ret.write_en = 1'd1; - ret.write_data = e.out; - write_to_memory[done] = ret.done; - } - } - control { - seq { - init; - invoke e(x=t.out)(); - write_to_memory; - } - } -} diff --git a/tests/correctness/exp/degree-4-unsigned.expect b/tests/correctness/exp/degree-4-unsigned.expect index 8f6c7f474e..b09674cdcc 100644 --- a/tests/correctness/exp/degree-4-unsigned.expect +++ b/tests/correctness/exp/degree-4-unsigned.expect @@ -1,5 +1,5 @@ { - "cycles": 78, + "cycles": 56, "memories": { "ret": [ "2.7182769775390625" diff --git a/tests/correctness/exp/degree-4-unsigned.txt.futil b/tests/correctness/exp/degree-4-unsigned.txt.futil deleted file mode 100644 index 92147e8c38..0000000000 --- a/tests/correctness/exp/degree-4-unsigned.txt.futil +++ /dev/null @@ -1,244 +0,0 @@ -import "primitives/std.lib"; -component exp(x: 32) -> (out: 32) { - cells { - exponent_value = std_reg(32); - int_x = std_reg(32); - frac_x = std_reg(32); - m = std_reg(32); - and0 = std_and(32); - and1 = std_and(32); - rsh = std_rsh(32); - c2 = std_const(32, 2); - c3 = std_const(32, 3); - c4 = std_const(32, 4); - one = std_const(32, 65536); - e = std_const(32, 178145); - product2 = std_reg(32); - product3 = std_reg(32); - product4 = std_reg(32); - p2 = std_reg(32); - p3 = std_reg(32); - p4 = std_reg(32); - sum1 = std_reg(32); - sum2 = std_reg(32); - add1 = std_fp_add(32, 16, 16); - add2 = std_fp_add(32, 16, 16); - mult_pipe1 = std_fp_mult_pipe(32, 16, 16); - mult_pipe2 = std_fp_mult_pipe(32, 16, 16); - mult_pipe3 = std_fp_mult_pipe(32, 16, 16); - mult_pipe4 = std_fp_mult_pipe(32, 16, 16); - reciprocal_factorial2 = std_const(32, 32768); - reciprocal_factorial3 = std_const(32, 10923); - reciprocal_factorial4 = std_const(32, 2731); - pow1 = fp_pow(); - pow2 = fp_pow(); - pow3 = fp_pow(); - pow4 = fp_pow(); - } - wires { - group init<"static"=1> { - exponent_value.write_en = 1'd1; - exponent_value.in = x; - init[done] = exponent_value.done; - } - group split_bits { - and0.left = exponent_value.out; - and0.right = 32'd4294901760; - rsh.left = and0.out; - rsh.right = 32'd16; - and1.left = exponent_value.out; - and1.right = 32'd65535; - int_x.write_en = 1'd1; - frac_x.write_en = 1'd1; - int_x.in = rsh.out; - frac_x.in = and1.out; - split_bits[done] = int_x.done & frac_x.done ? 1'd1; - } - group consume_pow2<"static"=1> { - p2.write_en = 1'd1; - p2.in = pow2.out; - consume_pow2[done] = p2.done ? 1'd1; - } - group consume_pow3<"static"=1> { - p3.write_en = 1'd1; - p3.in = pow3.out; - consume_pow3[done] = p3.done ? 1'd1; - } - group consume_pow4<"static"=1> { - p4.write_en = 1'd1; - p4.in = pow4.out; - consume_pow4[done] = p4.done ? 1'd1; - } - group mult_by_reciprocal_factorial2 { - mult_pipe2.left = p2.out; - mult_pipe2.right = reciprocal_factorial2.out; - mult_pipe2.go = !mult_pipe2.done ? 1'd1; - product2.write_en = mult_pipe2.done; - product2.in = mult_pipe2.out; - mult_by_reciprocal_factorial2[done] = product2.done; - } - group mult_by_reciprocal_factorial3 { - mult_pipe3.left = p3.out; - mult_pipe3.right = reciprocal_factorial3.out; - mult_pipe3.go = !mult_pipe3.done ? 1'd1; - product3.write_en = mult_pipe3.done; - product3.in = mult_pipe3.out; - mult_by_reciprocal_factorial3[done] = product3.done; - } - group mult_by_reciprocal_factorial4 { - mult_pipe4.left = p4.out; - mult_pipe4.right = reciprocal_factorial4.out; - mult_pipe4.go = !mult_pipe4.done ? 1'd1; - product4.write_en = mult_pipe4.done; - product4.in = mult_pipe4.out; - mult_by_reciprocal_factorial4[done] = product4.done; - } - group sum_round1_1<"static"=1> { - add1.left = frac_x.out; - add1.right = product2.out; - sum1.write_en = 1'd1; - sum1.in = add1.out; - sum_round1_1[done] = sum1.done; - } - group sum_round1_2<"static"=1> { - add2.left = product3.out; - add2.right = product4.out; - sum2.write_en = 1'd1; - sum2.in = add2.out; - sum_round1_2[done] = sum2.done; - } - group sum_round2_1<"static"=1> { - add1.left = sum1.out; - add1.right = sum2.out; - sum1.write_en = 1'd1; - sum1.in = add1.out; - sum_round2_1[done] = sum1.done; - } - group add_degree_zero<"static"=1> { - add1.left = sum1.out; - add1.right = one.out; - sum1.write_en = 1'd1; - sum1.in = add1.out; - add_degree_zero[done] = sum1.done; - } - group final_multiply { - mult_pipe1.left = pow1.out; - mult_pipe1.right = sum1.out; - mult_pipe1.go = !mult_pipe1.done ? 1'd1; - m.write_en = mult_pipe1.done; - m.in = mult_pipe1.out; - final_multiply[done] = m.done; - } - out = m.out; - } - control { - seq { - init; - split_bits; - par { - invoke pow1(base=e.out, integer_exp=int_x.out)(); - invoke pow2(base=frac_x.out, integer_exp=c2.out)(); - invoke pow3(base=frac_x.out, integer_exp=c3.out)(); - invoke pow4(base=frac_x.out, integer_exp=c4.out)(); - } - par { - consume_pow2; - consume_pow3; - consume_pow4; - } - par { - mult_by_reciprocal_factorial2; - mult_by_reciprocal_factorial3; - mult_by_reciprocal_factorial4; - } - par { - sum_round1_1; - sum_round1_2; - } - par { - sum_round2_1; - } - add_degree_zero; - final_multiply; - } - } -} -component fp_pow(base: 32, integer_exp: 32) -> (out: 32) { - cells { - pow = std_reg(32); - count = std_reg(32); - mul = std_fp_mult_pipe(32, 16, 16); - lt = std_lt(32); - incr = std_add(32); - } - wires { - group init { - pow.in = 32'd65536; - pow.write_en = 1'd1; - count.in = 32'd0; - count.write_en = 1'd1; - init[done] = pow.done & count.done ? 1'd1; - } - group execute_mul { - mul.left = base; - mul.right = pow.out; - mul.go = !mul.done ? 1'd1; - pow.write_en = mul.done; - pow.in = mul.out; - execute_mul[done] = pow.done; - } - group incr_count { - incr.left = 32'd1; - incr.right = count.out; - count.in = incr.out; - count.write_en = 1'd1; - incr_count[done] = count.done; - } - group cond { - lt.left = count.out; - lt.right = integer_exp; - cond[done] = 1'd1; - } - out = pow.out; - } - control { - seq { - init; - while lt.out with cond { - par { - execute_mul; - incr_count; - } - } - } - } -} -component main() -> () { - cells { - t = std_reg(32); - @external(1) x = std_mem_d1(32, 1, 1); - @external(1) ret = std_mem_d1(32, 1, 1); - e = exp(); - } - wires { - group init { - x.addr0 = 1'd0; - t.in = x.read_data; - t.write_en = 1'd1; - init[done] = t.done; - } - group write_to_memory { - ret.addr0 = 1'd0; - ret.write_en = 1'd1; - ret.write_data = e.out; - write_to_memory[done] = ret.done; - } - } - control { - seq { - init; - invoke e(x=t.out)(); - write_to_memory; - } - } -} diff --git a/tests/correctness/exp/degree-8-signed.expect b/tests/correctness/exp/degree-8-signed.expect index ef9a2abbb2..344e8943f6 100644 --- a/tests/correctness/exp/degree-8-signed.expect +++ b/tests/correctness/exp/degree-8-signed.expect @@ -1,5 +1,5 @@ { - "cycles": 187, + "cycles": 144, "memories": { "ret": [ "0.0001068115234375" diff --git a/tests/correctness/exp/degree-8-signed.txt.futil b/tests/correctness/exp/degree-8-signed.txt.futil deleted file mode 100644 index 9fc485d2e2..0000000000 --- a/tests/correctness/exp/degree-8-signed.txt.futil +++ /dev/null @@ -1,400 +0,0 @@ -import "primitives/std.lib"; -component exp(x: 32) -> (out: 32) { - cells { - exponent_value = std_reg(32); - int_x = std_reg(32); - frac_x = std_reg(32); - m = std_reg(32); - and0 = std_and(32); - and1 = std_and(32); - rsh = std_rsh(32); - lt = std_slt(32); - c2 = std_const(32, 2); - c3 = std_const(32, 3); - c4 = std_const(32, 4); - c5 = std_const(32, 5); - c6 = std_const(32, 6); - c7 = std_const(32, 7); - c8 = std_const(32, 8); - one = std_const(32, 65536); - e = std_const(32, 178145); - negative_one = std_const(32, 4294901760); - product2 = std_reg(32); - product3 = std_reg(32); - product4 = std_reg(32); - product5 = std_reg(32); - product6 = std_reg(32); - product7 = std_reg(32); - product8 = std_reg(32); - p2 = std_reg(32); - p3 = std_reg(32); - p4 = std_reg(32); - p5 = std_reg(32); - p6 = std_reg(32); - p7 = std_reg(32); - p8 = std_reg(32); - sum1 = std_reg(32); - sum2 = std_reg(32); - sum3 = std_reg(32); - sum4 = std_reg(32); - add1 = std_fp_sadd(32, 16, 16); - add2 = std_fp_sadd(32, 16, 16); - add3 = std_fp_sadd(32, 16, 16); - add4 = std_fp_sadd(32, 16, 16); - mult_pipe1 = std_fp_smult_pipe(32, 16, 16); - mult_pipe2 = std_fp_smult_pipe(32, 16, 16); - mult_pipe3 = std_fp_smult_pipe(32, 16, 16); - mult_pipe4 = std_fp_smult_pipe(32, 16, 16); - mult_pipe5 = std_fp_smult_pipe(32, 16, 16); - mult_pipe6 = std_fp_smult_pipe(32, 16, 16); - mult_pipe7 = std_fp_smult_pipe(32, 16, 16); - mult_pipe8 = std_fp_smult_pipe(32, 16, 16); - div_pipe = std_fp_sdiv_pipe(32, 16, 16); - reciprocal_factorial2 = std_const(32, 32768); - reciprocal_factorial3 = std_const(32, 10923); - reciprocal_factorial4 = std_const(32, 2731); - reciprocal_factorial5 = std_const(32, 546); - reciprocal_factorial6 = std_const(32, 91); - reciprocal_factorial7 = std_const(32, 13); - reciprocal_factorial8 = std_const(32, 2); - pow1 = fp_pow(); - pow2 = fp_pow(); - pow3 = fp_pow(); - pow4 = fp_pow(); - pow5 = fp_pow(); - pow6 = fp_pow(); - pow7 = fp_pow(); - pow8 = fp_pow(); - } - wires { - group init<"static"=1> { - exponent_value.write_en = 1'd1; - exponent_value.in = x; - init[done] = exponent_value.done; - } - group split_bits { - and0.left = exponent_value.out; - and0.right = 32'd4294901760; - rsh.left = and0.out; - rsh.right = 32'd16; - and1.left = exponent_value.out; - and1.right = 32'd65535; - int_x.write_en = 1'd1; - frac_x.write_en = 1'd1; - int_x.in = rsh.out; - frac_x.in = and1.out; - split_bits[done] = int_x.done & frac_x.done ? 1'd1; - } - group negate { - mult_pipe1.left = exponent_value.out; - mult_pipe1.right = negative_one.out; - mult_pipe1.go = !mult_pipe1.done ? 1'd1; - exponent_value.write_en = mult_pipe1.done; - exponent_value.in = mult_pipe1.out; - negate[done] = exponent_value.done; - } - group is_negative<"static"=0> { - lt.left = x; - lt.right = 32'd0; - is_negative[done] = 1'd1; - } - group reciprocal { - div_pipe.left = one.out; - div_pipe.right = m.out; - div_pipe.go = !div_pipe.done ? 1'd1; - m.write_en = div_pipe.done; - m.in = div_pipe.out_quotient; - reciprocal[done] = m.done; - } - group consume_pow2<"static"=1> { - p2.write_en = 1'd1; - p2.in = pow2.out; - consume_pow2[done] = p2.done ? 1'd1; - } - group consume_pow3<"static"=1> { - p3.write_en = 1'd1; - p3.in = pow3.out; - consume_pow3[done] = p3.done ? 1'd1; - } - group consume_pow4<"static"=1> { - p4.write_en = 1'd1; - p4.in = pow4.out; - consume_pow4[done] = p4.done ? 1'd1; - } - group consume_pow5<"static"=1> { - p5.write_en = 1'd1; - p5.in = pow5.out; - consume_pow5[done] = p5.done ? 1'd1; - } - group consume_pow6<"static"=1> { - p6.write_en = 1'd1; - p6.in = pow6.out; - consume_pow6[done] = p6.done ? 1'd1; - } - group consume_pow7<"static"=1> { - p7.write_en = 1'd1; - p7.in = pow7.out; - consume_pow7[done] = p7.done ? 1'd1; - } - group consume_pow8<"static"=1> { - p8.write_en = 1'd1; - p8.in = pow8.out; - consume_pow8[done] = p8.done ? 1'd1; - } - group mult_by_reciprocal_factorial2 { - mult_pipe2.left = p2.out; - mult_pipe2.right = reciprocal_factorial2.out; - mult_pipe2.go = !mult_pipe2.done ? 1'd1; - product2.write_en = mult_pipe2.done; - product2.in = mult_pipe2.out; - mult_by_reciprocal_factorial2[done] = product2.done; - } - group mult_by_reciprocal_factorial3 { - mult_pipe3.left = p3.out; - mult_pipe3.right = reciprocal_factorial3.out; - mult_pipe3.go = !mult_pipe3.done ? 1'd1; - product3.write_en = mult_pipe3.done; - product3.in = mult_pipe3.out; - mult_by_reciprocal_factorial3[done] = product3.done; - } - group mult_by_reciprocal_factorial4 { - mult_pipe4.left = p4.out; - mult_pipe4.right = reciprocal_factorial4.out; - mult_pipe4.go = !mult_pipe4.done ? 1'd1; - product4.write_en = mult_pipe4.done; - product4.in = mult_pipe4.out; - mult_by_reciprocal_factorial4[done] = product4.done; - } - group mult_by_reciprocal_factorial5 { - mult_pipe5.left = p5.out; - mult_pipe5.right = reciprocal_factorial5.out; - mult_pipe5.go = !mult_pipe5.done ? 1'd1; - product5.write_en = mult_pipe5.done; - product5.in = mult_pipe5.out; - mult_by_reciprocal_factorial5[done] = product5.done; - } - group mult_by_reciprocal_factorial6 { - mult_pipe6.left = p6.out; - mult_pipe6.right = reciprocal_factorial6.out; - mult_pipe6.go = !mult_pipe6.done ? 1'd1; - product6.write_en = mult_pipe6.done; - product6.in = mult_pipe6.out; - mult_by_reciprocal_factorial6[done] = product6.done; - } - group mult_by_reciprocal_factorial7 { - mult_pipe7.left = p7.out; - mult_pipe7.right = reciprocal_factorial7.out; - mult_pipe7.go = !mult_pipe7.done ? 1'd1; - product7.write_en = mult_pipe7.done; - product7.in = mult_pipe7.out; - mult_by_reciprocal_factorial7[done] = product7.done; - } - group mult_by_reciprocal_factorial8 { - mult_pipe8.left = p8.out; - mult_pipe8.right = reciprocal_factorial8.out; - mult_pipe8.go = !mult_pipe8.done ? 1'd1; - product8.write_en = mult_pipe8.done; - product8.in = mult_pipe8.out; - mult_by_reciprocal_factorial8[done] = product8.done; - } - group sum_round1_1<"static"=1> { - add1.left = frac_x.out; - add1.right = product2.out; - sum1.write_en = 1'd1; - sum1.in = add1.out; - sum_round1_1[done] = sum1.done; - } - group sum_round1_2<"static"=1> { - add2.left = product3.out; - add2.right = product4.out; - sum2.write_en = 1'd1; - sum2.in = add2.out; - sum_round1_2[done] = sum2.done; - } - group sum_round1_3<"static"=1> { - add3.left = product5.out; - add3.right = product6.out; - sum3.write_en = 1'd1; - sum3.in = add3.out; - sum_round1_3[done] = sum3.done; - } - group sum_round1_4<"static"=1> { - add4.left = product7.out; - add4.right = product8.out; - sum4.write_en = 1'd1; - sum4.in = add4.out; - sum_round1_4[done] = sum4.done; - } - group sum_round2_1<"static"=1> { - add1.left = sum1.out; - add1.right = sum2.out; - sum1.write_en = 1'd1; - sum1.in = add1.out; - sum_round2_1[done] = sum1.done; - } - group sum_round2_2<"static"=1> { - add2.left = sum3.out; - add2.right = sum4.out; - sum2.write_en = 1'd1; - sum2.in = add2.out; - sum_round2_2[done] = sum2.done; - } - group sum_round3_1<"static"=1> { - add1.left = sum1.out; - add1.right = sum2.out; - sum1.write_en = 1'd1; - sum1.in = add1.out; - sum_round3_1[done] = sum1.done; - } - group add_degree_zero<"static"=1> { - add1.left = sum1.out; - add1.right = one.out; - sum1.write_en = 1'd1; - sum1.in = add1.out; - add_degree_zero[done] = sum1.done; - } - group final_multiply { - mult_pipe1.left = pow1.out; - mult_pipe1.right = sum1.out; - mult_pipe1.go = !mult_pipe1.done ? 1'd1; - m.write_en = mult_pipe1.done; - m.in = mult_pipe1.out; - final_multiply[done] = m.done; - } - out = m.out; - } - control { - seq { - init; - if lt.out with is_negative { - negate; - } - split_bits; - par { - invoke pow1(base=e.out, integer_exp=int_x.out)(); - invoke pow2(base=frac_x.out, integer_exp=c2.out)(); - invoke pow3(base=frac_x.out, integer_exp=c3.out)(); - invoke pow4(base=frac_x.out, integer_exp=c4.out)(); - invoke pow5(base=frac_x.out, integer_exp=c5.out)(); - invoke pow6(base=frac_x.out, integer_exp=c6.out)(); - invoke pow7(base=frac_x.out, integer_exp=c7.out)(); - invoke pow8(base=frac_x.out, integer_exp=c8.out)(); - } - par { - consume_pow2; - consume_pow3; - consume_pow4; - consume_pow5; - consume_pow6; - consume_pow7; - consume_pow8; - } - par { - mult_by_reciprocal_factorial2; - mult_by_reciprocal_factorial3; - mult_by_reciprocal_factorial4; - mult_by_reciprocal_factorial5; - mult_by_reciprocal_factorial6; - mult_by_reciprocal_factorial7; - mult_by_reciprocal_factorial8; - } - par { - sum_round1_1; - sum_round1_2; - sum_round1_3; - sum_round1_4; - } - par { - sum_round2_1; - sum_round2_2; - } - par { - sum_round3_1; - } - add_degree_zero; - final_multiply; - if lt.out with is_negative { - reciprocal; - } - } - } -} -component fp_pow(base: 32, integer_exp: 32) -> (out: 32) { - cells { - pow = std_reg(32); - count = std_reg(32); - mul = std_fp_smult_pipe(32, 16, 16); - lt = std_slt(32); - incr = std_sadd(32); - } - wires { - group init { - pow.in = 32'd65536; - pow.write_en = 1'd1; - count.in = 32'd0; - count.write_en = 1'd1; - init[done] = pow.done & count.done ? 1'd1; - } - group execute_mul { - mul.left = base; - mul.right = pow.out; - mul.go = !mul.done ? 1'd1; - pow.write_en = mul.done; - pow.in = mul.out; - execute_mul[done] = pow.done; - } - group incr_count { - incr.left = 32'd1; - incr.right = count.out; - count.in = incr.out; - count.write_en = 1'd1; - incr_count[done] = count.done; - } - group cond { - lt.left = count.out; - lt.right = integer_exp; - cond[done] = 1'd1; - } - out = pow.out; - } - control { - seq { - init; - while lt.out with cond { - par { - execute_mul; - incr_count; - } - } - } - } -} -component main() -> () { - cells { - t = std_reg(32); - @external(1) x = std_mem_d1(32, 1, 1); - @external(1) ret = std_mem_d1(32, 1, 1); - e = exp(); - } - wires { - group init { - x.addr0 = 1'd0; - t.in = x.read_data; - t.write_en = 1'd1; - init[done] = t.done; - } - group write_to_memory { - ret.addr0 = 1'd0; - ret.write_en = 1'd1; - ret.write_data = e.out; - write_to_memory[done] = ret.done; - } - } - control { - seq { - init; - invoke e(x=t.out)(); - write_to_memory; - } - } -} diff --git a/tests/correctness/exp/degree-8-unsigned.expect b/tests/correctness/exp/degree-8-unsigned.expect index 5035bd8fa9..47164cff1b 100644 --- a/tests/correctness/exp/degree-8-unsigned.expect +++ b/tests/correctness/exp/degree-8-unsigned.expect @@ -1,5 +1,5 @@ { - "cycles": 125, + "cycles": 88, "memories": { "ret": [ "9181.710357666015625" diff --git a/tests/correctness/exp/degree-8-unsigned.txt.futil b/tests/correctness/exp/degree-8-unsigned.txt.futil deleted file mode 100644 index 128b1a5fa0..0000000000 --- a/tests/correctness/exp/degree-8-unsigned.txt.futil +++ /dev/null @@ -1,370 +0,0 @@ -import "primitives/std.lib"; -component exp(x: 32) -> (out: 32) { - cells { - exponent_value = std_reg(32); - int_x = std_reg(32); - frac_x = std_reg(32); - m = std_reg(32); - and0 = std_and(32); - and1 = std_and(32); - rsh = std_rsh(32); - c2 = std_const(32, 2); - c3 = std_const(32, 3); - c4 = std_const(32, 4); - c5 = std_const(32, 5); - c6 = std_const(32, 6); - c7 = std_const(32, 7); - c8 = std_const(32, 8); - one = std_const(32, 65536); - e = std_const(32, 178145); - product2 = std_reg(32); - product3 = std_reg(32); - product4 = std_reg(32); - product5 = std_reg(32); - product6 = std_reg(32); - product7 = std_reg(32); - product8 = std_reg(32); - p2 = std_reg(32); - p3 = std_reg(32); - p4 = std_reg(32); - p5 = std_reg(32); - p6 = std_reg(32); - p7 = std_reg(32); - p8 = std_reg(32); - sum1 = std_reg(32); - sum2 = std_reg(32); - sum3 = std_reg(32); - sum4 = std_reg(32); - add1 = std_fp_add(32, 16, 16); - add2 = std_fp_add(32, 16, 16); - add3 = std_fp_add(32, 16, 16); - add4 = std_fp_add(32, 16, 16); - mult_pipe1 = std_fp_mult_pipe(32, 16, 16); - mult_pipe2 = std_fp_mult_pipe(32, 16, 16); - mult_pipe3 = std_fp_mult_pipe(32, 16, 16); - mult_pipe4 = std_fp_mult_pipe(32, 16, 16); - mult_pipe5 = std_fp_mult_pipe(32, 16, 16); - mult_pipe6 = std_fp_mult_pipe(32, 16, 16); - mult_pipe7 = std_fp_mult_pipe(32, 16, 16); - mult_pipe8 = std_fp_mult_pipe(32, 16, 16); - reciprocal_factorial2 = std_const(32, 32768); - reciprocal_factorial3 = std_const(32, 10923); - reciprocal_factorial4 = std_const(32, 2731); - reciprocal_factorial5 = std_const(32, 546); - reciprocal_factorial6 = std_const(32, 91); - reciprocal_factorial7 = std_const(32, 13); - reciprocal_factorial8 = std_const(32, 2); - pow1 = fp_pow(); - pow2 = fp_pow(); - pow3 = fp_pow(); - pow4 = fp_pow(); - pow5 = fp_pow(); - pow6 = fp_pow(); - pow7 = fp_pow(); - pow8 = fp_pow(); - } - wires { - group init<"static"=1> { - exponent_value.write_en = 1'd1; - exponent_value.in = x; - init[done] = exponent_value.done; - } - group split_bits { - and0.left = exponent_value.out; - and0.right = 32'd4294901760; - rsh.left = and0.out; - rsh.right = 32'd16; - and1.left = exponent_value.out; - and1.right = 32'd65535; - int_x.write_en = 1'd1; - frac_x.write_en = 1'd1; - int_x.in = rsh.out; - frac_x.in = and1.out; - split_bits[done] = int_x.done & frac_x.done ? 1'd1; - } - group consume_pow2<"static"=1> { - p2.write_en = 1'd1; - p2.in = pow2.out; - consume_pow2[done] = p2.done ? 1'd1; - } - group consume_pow3<"static"=1> { - p3.write_en = 1'd1; - p3.in = pow3.out; - consume_pow3[done] = p3.done ? 1'd1; - } - group consume_pow4<"static"=1> { - p4.write_en = 1'd1; - p4.in = pow4.out; - consume_pow4[done] = p4.done ? 1'd1; - } - group consume_pow5<"static"=1> { - p5.write_en = 1'd1; - p5.in = pow5.out; - consume_pow5[done] = p5.done ? 1'd1; - } - group consume_pow6<"static"=1> { - p6.write_en = 1'd1; - p6.in = pow6.out; - consume_pow6[done] = p6.done ? 1'd1; - } - group consume_pow7<"static"=1> { - p7.write_en = 1'd1; - p7.in = pow7.out; - consume_pow7[done] = p7.done ? 1'd1; - } - group consume_pow8<"static"=1> { - p8.write_en = 1'd1; - p8.in = pow8.out; - consume_pow8[done] = p8.done ? 1'd1; - } - group mult_by_reciprocal_factorial2 { - mult_pipe2.left = p2.out; - mult_pipe2.right = reciprocal_factorial2.out; - mult_pipe2.go = !mult_pipe2.done ? 1'd1; - product2.write_en = mult_pipe2.done; - product2.in = mult_pipe2.out; - mult_by_reciprocal_factorial2[done] = product2.done; - } - group mult_by_reciprocal_factorial3 { - mult_pipe3.left = p3.out; - mult_pipe3.right = reciprocal_factorial3.out; - mult_pipe3.go = !mult_pipe3.done ? 1'd1; - product3.write_en = mult_pipe3.done; - product3.in = mult_pipe3.out; - mult_by_reciprocal_factorial3[done] = product3.done; - } - group mult_by_reciprocal_factorial4 { - mult_pipe4.left = p4.out; - mult_pipe4.right = reciprocal_factorial4.out; - mult_pipe4.go = !mult_pipe4.done ? 1'd1; - product4.write_en = mult_pipe4.done; - product4.in = mult_pipe4.out; - mult_by_reciprocal_factorial4[done] = product4.done; - } - group mult_by_reciprocal_factorial5 { - mult_pipe5.left = p5.out; - mult_pipe5.right = reciprocal_factorial5.out; - mult_pipe5.go = !mult_pipe5.done ? 1'd1; - product5.write_en = mult_pipe5.done; - product5.in = mult_pipe5.out; - mult_by_reciprocal_factorial5[done] = product5.done; - } - group mult_by_reciprocal_factorial6 { - mult_pipe6.left = p6.out; - mult_pipe6.right = reciprocal_factorial6.out; - mult_pipe6.go = !mult_pipe6.done ? 1'd1; - product6.write_en = mult_pipe6.done; - product6.in = mult_pipe6.out; - mult_by_reciprocal_factorial6[done] = product6.done; - } - group mult_by_reciprocal_factorial7 { - mult_pipe7.left = p7.out; - mult_pipe7.right = reciprocal_factorial7.out; - mult_pipe7.go = !mult_pipe7.done ? 1'd1; - product7.write_en = mult_pipe7.done; - product7.in = mult_pipe7.out; - mult_by_reciprocal_factorial7[done] = product7.done; - } - group mult_by_reciprocal_factorial8 { - mult_pipe8.left = p8.out; - mult_pipe8.right = reciprocal_factorial8.out; - mult_pipe8.go = !mult_pipe8.done ? 1'd1; - product8.write_en = mult_pipe8.done; - product8.in = mult_pipe8.out; - mult_by_reciprocal_factorial8[done] = product8.done; - } - group sum_round1_1<"static"=1> { - add1.left = frac_x.out; - add1.right = product2.out; - sum1.write_en = 1'd1; - sum1.in = add1.out; - sum_round1_1[done] = sum1.done; - } - group sum_round1_2<"static"=1> { - add2.left = product3.out; - add2.right = product4.out; - sum2.write_en = 1'd1; - sum2.in = add2.out; - sum_round1_2[done] = sum2.done; - } - group sum_round1_3<"static"=1> { - add3.left = product5.out; - add3.right = product6.out; - sum3.write_en = 1'd1; - sum3.in = add3.out; - sum_round1_3[done] = sum3.done; - } - group sum_round1_4<"static"=1> { - add4.left = product7.out; - add4.right = product8.out; - sum4.write_en = 1'd1; - sum4.in = add4.out; - sum_round1_4[done] = sum4.done; - } - group sum_round2_1<"static"=1> { - add1.left = sum1.out; - add1.right = sum2.out; - sum1.write_en = 1'd1; - sum1.in = add1.out; - sum_round2_1[done] = sum1.done; - } - group sum_round2_2<"static"=1> { - add2.left = sum3.out; - add2.right = sum4.out; - sum2.write_en = 1'd1; - sum2.in = add2.out; - sum_round2_2[done] = sum2.done; - } - group sum_round3_1<"static"=1> { - add1.left = sum1.out; - add1.right = sum2.out; - sum1.write_en = 1'd1; - sum1.in = add1.out; - sum_round3_1[done] = sum1.done; - } - group add_degree_zero<"static"=1> { - add1.left = sum1.out; - add1.right = one.out; - sum1.write_en = 1'd1; - sum1.in = add1.out; - add_degree_zero[done] = sum1.done; - } - group final_multiply { - mult_pipe1.left = pow1.out; - mult_pipe1.right = sum1.out; - mult_pipe1.go = !mult_pipe1.done ? 1'd1; - m.write_en = mult_pipe1.done; - m.in = mult_pipe1.out; - final_multiply[done] = m.done; - } - out = m.out; - } - control { - seq { - init; - split_bits; - par { - invoke pow1(base=e.out, integer_exp=int_x.out)(); - invoke pow2(base=frac_x.out, integer_exp=c2.out)(); - invoke pow3(base=frac_x.out, integer_exp=c3.out)(); - invoke pow4(base=frac_x.out, integer_exp=c4.out)(); - invoke pow5(base=frac_x.out, integer_exp=c5.out)(); - invoke pow6(base=frac_x.out, integer_exp=c6.out)(); - invoke pow7(base=frac_x.out, integer_exp=c7.out)(); - invoke pow8(base=frac_x.out, integer_exp=c8.out)(); - } - par { - consume_pow2; - consume_pow3; - consume_pow4; - consume_pow5; - consume_pow6; - consume_pow7; - consume_pow8; - } - par { - mult_by_reciprocal_factorial2; - mult_by_reciprocal_factorial3; - mult_by_reciprocal_factorial4; - mult_by_reciprocal_factorial5; - mult_by_reciprocal_factorial6; - mult_by_reciprocal_factorial7; - mult_by_reciprocal_factorial8; - } - par { - sum_round1_1; - sum_round1_2; - sum_round1_3; - sum_round1_4; - } - par { - sum_round2_1; - sum_round2_2; - } - par { - sum_round3_1; - } - add_degree_zero; - final_multiply; - } - } -} -component fp_pow(base: 32, integer_exp: 32) -> (out: 32) { - cells { - pow = std_reg(32); - count = std_reg(32); - mul = std_fp_mult_pipe(32, 16, 16); - lt = std_lt(32); - incr = std_add(32); - } - wires { - group init { - pow.in = 32'd65536; - pow.write_en = 1'd1; - count.in = 32'd0; - count.write_en = 1'd1; - init[done] = pow.done & count.done ? 1'd1; - } - group execute_mul { - mul.left = base; - mul.right = pow.out; - mul.go = !mul.done ? 1'd1; - pow.write_en = mul.done; - pow.in = mul.out; - execute_mul[done] = pow.done; - } - group incr_count { - incr.left = 32'd1; - incr.right = count.out; - count.in = incr.out; - count.write_en = 1'd1; - incr_count[done] = count.done; - } - group cond { - lt.left = count.out; - lt.right = integer_exp; - cond[done] = 1'd1; - } - out = pow.out; - } - control { - seq { - init; - while lt.out with cond { - par { - execute_mul; - incr_count; - } - } - } - } -} -component main() -> () { - cells { - t = std_reg(32); - @external(1) x = std_mem_d1(32, 1, 1); - @external(1) ret = std_mem_d1(32, 1, 1); - e = exp(); - } - wires { - group init { - x.addr0 = 1'd0; - t.in = x.read_data; - t.write_en = 1'd1; - init[done] = t.done; - } - group write_to_memory { - ret.addr0 = 1'd0; - ret.write_en = 1'd1; - ret.write_data = e.out; - write_to_memory[done] = ret.done; - } - } - control { - seq { - init; - invoke e(x=t.out)(); - write_to_memory; - } - } -} diff --git a/tests/correctness/if-static-different-latencies.futil b/tests/correctness/if-static-different-latencies.futil index 67ca831e0b..a47141b3fe 100644 --- a/tests/correctness/if-static-different-latencies.futil +++ b/tests/correctness/if-static-different-latencies.futil @@ -16,10 +16,9 @@ component main() -> () { add = std_add(32); } wires { - group cond { + comb group cond { eq.left = 1'd0; eq.right = 1'd1; - cond[done] = 1'd1; } group true { diff --git a/tests/correctness/if.futil b/tests/correctness/if.futil index 6b73ebade6..7bf5565bf1 100644 --- a/tests/correctness/if.futil +++ b/tests/correctness/if.futil @@ -7,10 +7,9 @@ component main() -> () { } wires { - group cond<"static"=0> { + comb group cond { lt.left = 32'd5; lt.right = 32'd9; - cond[done] = 1'd1; } group true<"static"=1> { diff --git a/tests/correctness/invoke-memory.futil b/tests/correctness/invoke-memory.futil index 7cf380735d..23ce8666f9 100644 --- a/tests/correctness/invoke-memory.futil +++ b/tests/correctness/invoke-memory.futil @@ -7,10 +7,9 @@ component copy(dest_done: 1, src_read_data: 32, length: 3) -> add = std_add(3); } wires { - group cond<"static"=0> { + comb group cond { lt.left = N.out; lt.right = length; - cond[done] = 1'd1; } group upd_index<"static"=1> { add.left = N.out; diff --git a/tests/correctness/invoke.futil b/tests/correctness/invoke.futil index b3bbe90133..01022bae01 100644 --- a/tests/correctness/invoke.futil +++ b/tests/correctness/invoke.futil @@ -33,10 +33,9 @@ component exponent(base: 32, exp: 4) -> (out: 32) { count.write_en = 1'd1; incr_count[done] = count.done; } - group cond<"static"=0> { + comb group cond { lt.right = exp; lt.left = count.out; - cond[done] = 1'd1; } out = pow.out; @@ -66,8 +65,7 @@ component main() -> () { tmp_0 = std_reg(32); } wires { - group cond0 { - cond0[done] = 1'd1; + comb group cond0 { le0.left = i0.out; le0.right = const1.out; } diff --git a/tests/correctness/ntt-pipeline/ntt-16-reduced-4.expect b/tests/correctness/ntt-pipeline/ntt-16-reduced-4.expect index 5eac7dfc52..e0dc74d1e6 100644 --- a/tests/correctness/ntt-pipeline/ntt-16-reduced-4.expect +++ b/tests/correctness/ntt-pipeline/ntt-16-reduced-4.expect @@ -1,5 +1,5 @@ { - "cycles": 735, + "cycles": 680, "memories": { "a": [ 7371, diff --git a/tests/correctness/ntt-pipeline/ntt-16-reduced-4.txt.futil b/tests/correctness/ntt-pipeline/ntt-16-reduced-4.txt.futil deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/correctness/ntt-pipeline/ntt-16.expect b/tests/correctness/ntt-pipeline/ntt-16.expect index 549de0ed09..c2973c3d39 100644 --- a/tests/correctness/ntt-pipeline/ntt-16.expect +++ b/tests/correctness/ntt-pipeline/ntt-16.expect @@ -1,5 +1,5 @@ { - "cycles": 249, + "cycles": 210, "memories": { "a": [ 7371, diff --git a/tests/correctness/ntt-pipeline/ntt-16.txt.futil b/tests/correctness/ntt-pipeline/ntt-16.txt.futil deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/correctness/ntt-pipeline/ntt-8.expect b/tests/correctness/ntt-pipeline/ntt-8.expect index 096985ca4d..e7db564f35 100644 --- a/tests/correctness/ntt-pipeline/ntt-8.expect +++ b/tests/correctness/ntt-pipeline/ntt-8.expect @@ -1,5 +1,5 @@ { - "cycles": 172, + "cycles": 151, "memories": { "a": [ 5390, diff --git a/tests/correctness/ntt-pipeline/ntt-8.txt.futil b/tests/correctness/ntt-pipeline/ntt-8.txt.futil deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/correctness/pipelined-mac.futil b/tests/correctness/pipelined-mac.futil index 1dadb7915b..d18cdb6ce3 100644 --- a/tests/correctness/pipelined-mac.futil +++ b/tests/correctness/pipelined-mac.futil @@ -60,10 +60,6 @@ component pipelined_mac( unset_out_valid[done] = out_valid.done; } - group no_op { - no_op[done] = 1'd1; - } - output_valid = out_valid.out; out = pipe2.out; } @@ -71,17 +67,17 @@ component pipelined_mac( seq { // Execute all stages in parallel par { - if data_valid with no_op { stage1; } - if stage2_valid.out with no_op { stage2; } + if data_valid { stage1; } + if stage2_valid.out { stage2; } } // Configure valid signals for next invoke par { - if data_valid with no_op { + if data_valid { set_stage2_valid; } else { unset_stage2_valid; } - if stage2_valid.out with no_op { + if stage2_valid.out { set_out_valid; } else { unset_out_valid; @@ -143,10 +139,9 @@ component main() -> () { out.write_data = mac.out; save_out[done] = out.done; } - group in_range { + comb group in_range { lt0.left = idx0.out; lt0.right = 4'd10; - in_range[done] = 1'd1; } } control { diff --git a/tests/correctness/systolic/array-1.expect b/tests/correctness/systolic/array-1.expect index 014866d39a..a16f11720c 100644 --- a/tests/correctness/systolic/array-1.expect +++ b/tests/correctness/systolic/array-1.expect @@ -1,5 +1,5 @@ { - "cycles": 24, + "cycles": 35, "out_00": 760, "pe_00": [ 0, diff --git a/tests/correctness/systolic/array-2.expect b/tests/correctness/systolic/array-2.expect index 6823531188..7201d01fd9 100644 --- a/tests/correctness/systolic/array-2.expect +++ b/tests/correctness/systolic/array-2.expect @@ -1,5 +1,5 @@ { - "cycles": 39, + "cycles": 56, "out_00": 760, "out_01": 400, "out_10": 1120, diff --git a/tests/correctness/systolic/array-3.expect b/tests/correctness/systolic/array-3.expect index 1414cc7f98..9e844ea4e3 100644 --- a/tests/correctness/systolic/array-3.expect +++ b/tests/correctness/systolic/array-3.expect @@ -1,5 +1,5 @@ { - "cycles": 56, + "cycles": 79, "out_00": 760, "out_01": 400, "out_02": 1120, diff --git a/tests/correctness/unsigned-dot-product.futil b/tests/correctness/unsigned-dot-product.futil index c26272319b..a16ca5b293 100644 --- a/tests/correctness/unsigned-dot-product.futil +++ b/tests/correctness/unsigned-dot-product.futil @@ -17,8 +17,7 @@ component main() -> () { mult = std_mult_pipe(32); } wires { - group is_less_than<"static"=0> { - is_less_than[done] = 1'd1; + comb group is_less_than<"static"=0> { lt0.left = counter.out; lt0.right = const1.out; } // Control segment for `counter` < `4`. diff --git a/tests/correctness/while.futil b/tests/correctness/while.futil index d496c73d6f..392c217a5d 100644 --- a/tests/correctness/while.futil +++ b/tests/correctness/while.futil @@ -8,28 +8,30 @@ component main() -> () { } wires { - group cond<"static"=0> { + comb group cond<"static"=0> { i.addr0 = 1'd0; lt.left = i.read_data; lt.right = 32'd8; - cond[done] = 1'b1; } group incr<"static"=1> { + add.right = i.read_data; + add.left = 32'd1; + i.write_data = add.out; i.addr0 = 1'd0; i.write_en = 1'b1; - add.right = i.read_data; - add.left = 32'd1; - incr[done] = i.done; } } control { while lt.out with cond { - incr; + seq { + incr; + incr; + } } -} + } } diff --git a/tests/errors/comb-group-in-control.expect b/tests/errors/comb-group-in-control.expect index d73e4d4f53..f8de0fe0eb 100644 --- a/tests/errors/comb-group-in-control.expect +++ b/tests/errors/comb-group-in-control.expect @@ -3,4 +3,4 @@ ---STDERR--- Error: Malformed Structure: 7 | group a { - | ^ Group with constant done condition not allowed inside normal control operators + | ^ Group with constant done condition are invalid. Use `comb group` instead to define a combinational group. diff --git a/tests/errors/mismatch-widths.expect b/tests/errors/mismatch-widths.expect index 8a1264fa7c..e26c56d67c 100644 --- a/tests/errors/mismatch-widths.expect +++ b/tests/errors/mismatch-widths.expect @@ -1,5 +1,5 @@ ---CODE--- 101 ---STDERR--- -thread 'main' panicked at 'Invalid assignment. `x.out' and `add.left' have different widths', calyx/src/ir/builder.rs:182:9 +thread 'main' panicked at 'Invalid assignment. `x.out' and `add.left' have different widths', calyx/src/ir/builder.rs:203:9 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace diff --git a/tests/errors/papercut-cell-and-group-conflict.expect b/tests/errors/papercut/cell-and-group-conflict.expect similarity index 100% rename from tests/errors/papercut-cell-and-group-conflict.expect rename to tests/errors/papercut/cell-and-group-conflict.expect diff --git a/tests/errors/papercut-cell-and-group-conflict.futil b/tests/errors/papercut/cell-and-group-conflict.futil similarity index 100% rename from tests/errors/papercut-cell-and-group-conflict.futil rename to tests/errors/papercut/cell-and-group-conflict.futil diff --git a/tests/errors/papercut-cell-as-group.expect b/tests/errors/papercut/cell-as-group.expect similarity index 100% rename from tests/errors/papercut-cell-as-group.expect rename to tests/errors/papercut/cell-as-group.expect diff --git a/tests/errors/papercut-cell-as-group.futil b/tests/errors/papercut/cell-as-group.futil similarity index 100% rename from tests/errors/papercut-cell-as-group.futil rename to tests/errors/papercut/cell-as-group.futil diff --git a/tests/errors/papercut/comb-port-in-condition.expect b/tests/errors/papercut/comb-port-in-condition.expect new file mode 100644 index 0000000000..125c1a7f09 --- /dev/null +++ b/tests/errors/papercut/comb-port-in-condition.expect @@ -0,0 +1,6 @@ +---CODE--- +1 +---STDERR--- +Error: +4 | le = std_le(32); + | ^^ [Papercut] Port `le.out` is an output port on combinational primitive `std_le` and will always output 0. Add a `with` statement to the `if` statement to ensure it has a valid value during execution. diff --git a/tests/errors/papercut/comb-port-in-condition.futil b/tests/errors/papercut/comb-port-in-condition.futil new file mode 100644 index 0000000000..23fdec6878 --- /dev/null +++ b/tests/errors/papercut/comb-port-in-condition.futil @@ -0,0 +1,10 @@ +import "primitives/core.futil"; +component main() -> () { + cells { + le = std_le(32); + } + wires { } + control { + if le.out { seq {} } + } +} diff --git a/tests/errors/papercut-no-control-no-done.expect b/tests/errors/papercut/no-control-no-done.expect similarity index 100% rename from tests/errors/papercut-no-control-no-done.expect rename to tests/errors/papercut/no-control-no-done.expect diff --git a/tests/errors/papercut-no-control-no-done.futil b/tests/errors/papercut/no-control-no-done.futil similarity index 100% rename from tests/errors/papercut-no-control-no-done.futil rename to tests/errors/papercut/no-control-no-done.futil diff --git a/tests/errors/papercut-no-done.expect b/tests/errors/papercut/no-done.expect similarity index 100% rename from tests/errors/papercut-no-done.expect rename to tests/errors/papercut/no-done.expect diff --git a/tests/errors/papercut-no-done.futil b/tests/errors/papercut/no-done.futil similarity index 100% rename from tests/errors/papercut-no-done.futil rename to tests/errors/papercut/no-done.futil diff --git a/tests/errors/papercut-read-missing-write.expect b/tests/errors/papercut/read-missing-write.expect similarity index 100% rename from tests/errors/papercut-read-missing-write.expect rename to tests/errors/papercut/read-missing-write.expect diff --git a/tests/errors/papercut-read-missing-write.futil b/tests/errors/papercut/read-missing-write.futil similarity index 100% rename from tests/errors/papercut-read-missing-write.futil rename to tests/errors/papercut/read-missing-write.futil diff --git a/tests/errors/reserved-cell-name.expect b/tests/errors/reserved-cell-name.expect deleted file mode 100644 index 60cb7d0ef0..0000000000 --- a/tests/errors/reserved-cell-name.expect +++ /dev/null @@ -1,6 +0,0 @@ ----CODE--- -1 ----STDERR--- -Error: -4 | reg = std_reg(32); - | ^^^ Use of reserved keyword: reg diff --git a/tests/errors/reserved-cell-name.futil b/tests/errors/reserved-cell-name.futil deleted file mode 100644 index 27b501b9dc..0000000000 --- a/tests/errors/reserved-cell-name.futil +++ /dev/null @@ -1,10 +0,0 @@ -import "primitives/std.lib"; -component main() -> () { - cells { - reg = std_reg(32); - } - wires { - } - control { - } -} diff --git a/tests/frontend/dahlia/combine.expect b/tests/frontend/dahlia/combine.expect index ad2ab9067a..48ded1ebee 100644 --- a/tests/frontend/dahlia/combine.expect +++ b/tests/frontend/dahlia/combine.expect @@ -13,8 +13,7 @@ component main() -> () { res_0 = std_reg(32); } wires { - group cond0<"static"=0> { - cond0[done] = 1'd1; + comb group cond0 { le0.left = i0.out; le0.right = const1.out; } diff --git a/tests/frontend/dahlia/for.expect b/tests/frontend/dahlia/for.expect index e13cbf42fe..6eeb36dd4c 100644 --- a/tests/frontend/dahlia/for.expect +++ b/tests/frontend/dahlia/for.expect @@ -9,8 +9,7 @@ component main() -> () { le0 = std_le(32); } wires { - group cond0<"static"=0> { - cond0[done] = 1'd1; + comb group cond0 { le0.left = i0.out; le0.right = const1.out; } diff --git a/tests/frontend/dahlia/if.expect b/tests/frontend/dahlia/if.expect index 206acffba3..dcbef038f2 100644 --- a/tests/frontend/dahlia/if.expect +++ b/tests/frontend/dahlia/if.expect @@ -10,8 +10,7 @@ component main() -> () { y_0 = std_reg(32); } wires { - group cond0<"static"=0> { - cond0[done] = 1'd1; + comb group cond0 { lt0.left = const0.out; lt0.right = const1.out; } diff --git a/tests/frontend/dahlia/invoke-memory.expect b/tests/frontend/dahlia/invoke-memory.expect index 6b2ef0829b..911bd79ee6 100644 --- a/tests/frontend/dahlia/invoke-memory.expect +++ b/tests/frontend/dahlia/invoke-memory.expect @@ -7,8 +7,7 @@ component mem_copy(dest0_read_data: 32, dest0_done: 1, src0_read_data: 32, src0_ src_read0_0 = std_reg(32); } wires { - group cond0<"static"=0> { - cond0[done] = 1'd1; + comb group cond0 { lt0.left = i_0.out; lt0.right = length; } diff --git a/tests/frontend/dahlia/matadd-fixed-point.expect b/tests/frontend/dahlia/matadd-fixed-point.expect index 9311cbd315..e428b73c26 100644 --- a/tests/frontend/dahlia/matadd-fixed-point.expect +++ b/tests/frontend/dahlia/matadd-fixed-point.expect @@ -21,13 +21,11 @@ component main() -> () { @external(1) result0_00_0 = std_mem_d2(16,2,2,2,2); } wires { - group cond0<"static"=0> { - cond0[done] = 1'd1; + comb group cond0 { le0.left = i0.out; le0.right = const1.out; } - group cond1<"static"=0> { - cond1[done] = 1'd1; + comb group cond1 { le1.left = j0.out; le1.right = const3.out; } diff --git a/tests/frontend/dahlia/signed_dotproduct.expect b/tests/frontend/dahlia/signed_dotproduct.expect index 50e753c41b..f98641e74b 100644 --- a/tests/frontend/dahlia/signed_dotproduct.expect +++ b/tests/frontend/dahlia/signed_dotproduct.expect @@ -22,8 +22,7 @@ component main() -> () { v_0 = std_reg(32); } wires { - group cond0<"static"=0> { - cond0[done] = 1'd1; + comb group cond0 { le0.left = i0.out; le0.right = const2.out; } diff --git a/tests/frontend/dahlia/unroll.expect b/tests/frontend/dahlia/unroll.expect index b467f517e2..7b0405d58c 100644 --- a/tests/frontend/dahlia/unroll.expect +++ b/tests/frontend/dahlia/unroll.expect @@ -26,8 +26,7 @@ component main() -> () { slice3 = std_slice(4,2); } wires { - group cond0<"static"=0> { - cond0[done] = 1'd1; + comb group cond0 { le0.left = i0.out; le0.right = const1.out; } diff --git a/tests/frontend/exp/degree-2-unsigned.expect b/tests/frontend/exp/degree-2-unsigned.expect index 8f54b34e2f..360dd67764 100644 --- a/tests/frontend/exp/degree-2-unsigned.expect +++ b/tests/frontend/exp/degree-2-unsigned.expect @@ -130,10 +130,9 @@ component fp_pow(base: 32, integer_exp: 32) -> (out: 32) { count.write_en = 1'd1; incr_count[done] = count.done; } - group cond { + comb group cond { lt.left = count.out; lt.right = integer_exp; - cond[done] = 1'd1; } out = pow.out; } diff --git a/tests/frontend/exp/degree-4-signed.expect b/tests/frontend/exp/degree-4-signed.expect index 90812614e4..c6fc641fca 100644 --- a/tests/frontend/exp/degree-4-signed.expect +++ b/tests/frontend/exp/degree-4-signed.expect @@ -65,10 +65,9 @@ component exp(x: 16) -> (out: 16) { exponent_value.in = mult_pipe1.out; negate[done] = exponent_value.done; } - group is_negative<"static"=0> { + comb group is_negative { lt.left = x; lt.right = 16'd0; - is_negative[done] = 1'd1; } group reciprocal { div_pipe.left = one.out; @@ -224,10 +223,9 @@ component fp_pow(base: 16, integer_exp: 16) -> (out: 16) { count.write_en = 1'd1; incr_count[done] = count.done; } - group cond { + comb group cond { lt.left = count.out; lt.right = integer_exp; - cond[done] = 1'd1; } out = pow.out; } diff --git a/tests/frontend/exp/degree-4-unsigned.expect b/tests/frontend/exp/degree-4-unsigned.expect index 9538eaf2fe..123e43338a 100644 --- a/tests/frontend/exp/degree-4-unsigned.expect +++ b/tests/frontend/exp/degree-4-unsigned.expect @@ -194,10 +194,9 @@ component fp_pow(base: 16, integer_exp: 16) -> (out: 16) { count.write_en = 1'd1; incr_count[done] = count.done; } - group cond { + comb group cond { lt.left = count.out; lt.right = integer_exp; - cond[done] = 1'd1; } out = pow.out; } diff --git a/tests/frontend/relay/batch_flatten-same-dimensions.expect b/tests/frontend/relay/batch_flatten-same-dimensions.expect index 287e607036..68db933dad 100644 --- a/tests/frontend/relay/batch_flatten-same-dimensions.expect +++ b/tests/frontend/relay/batch_flatten-same-dimensions.expect @@ -36,13 +36,11 @@ component batch_flatten_2x4096(x0_0_read_data: 32, x0_0_done: 1, x10_0_read_data x_read0_0 = std_reg(32); } wires { - group cond0<"static"=0> { - cond0[done] = 1'd1; + comb group cond0 { le0.left = __i0.out; le0.right = const2.out; } - group cond1<"static"=0> { - cond1[done] = 1'd1; + comb group cond1 { le1.left = __j0.out; le1.right = const4.out; } diff --git a/tests/frontend/relay/batch_flatten.expect b/tests/frontend/relay/batch_flatten.expect index 27c3f8401a..487dcf8f5d 100644 --- a/tests/frontend/relay/batch_flatten.expect +++ b/tests/frontend/relay/batch_flatten.expect @@ -42,18 +42,15 @@ component batch_flatten_1x4(x0_0_0_read_data: 32, x0_0_0_done: 1, x10_0_read_dat x_read0_0 = std_reg(32); } wires { - group cond0<"static"=0> { - cond0[done] = 1'd1; + comb group cond0 { le0.left = __i0.out; le0.right = const2.out; } - group cond1<"static"=0> { - cond1[done] = 1'd1; + comb group cond1 { le1.left = __j0.out; le1.right = const4.out; } - group cond2<"static"=0> { - cond2[done] = 1'd1; + comb group cond2 { le2.left = __k0.out; le2.right = const6.out; } diff --git a/tests/frontend/relay/batch_matmul.expect b/tests/frontend/relay/batch_matmul.expect index 19ac9a61f6..7d62a21d54 100644 --- a/tests/frontend/relay/batch_matmul.expect +++ b/tests/frontend/relay/batch_matmul.expect @@ -71,38 +71,31 @@ component batch_matmul_4x7x7(a0_0_0_read_data: 32, a0_0_0_done: 1, b0_0_0_read_d red_read00 = std_reg(32); } wires { - group cond0<"static"=0> { - cond0[done] = 1'd1; + comb group cond0 { le0.left = __batch0.out; le0.right = const1.out; } - group cond1<"static"=0> { - cond1[done] = 1'd1; + comb group cond1 { le1.left = __i0.out; le1.right = const3.out; } - group cond2<"static"=0> { - cond2[done] = 1'd1; + comb group cond2 { le2.left = __j0.out; le2.right = const5.out; } - group cond3<"static"=0> { - cond3[done] = 1'd1; + comb group cond3 { le3.left = __batch1.out; le3.right = const10.out; } - group cond4<"static"=0> { - cond4[done] = 1'd1; + comb group cond4 { le4.left = __i1.out; le4.right = const12.out; } - group cond5<"static"=0> { - cond5[done] = 1'd1; + comb group cond5 { le5.left = __j1.out; le5.right = const14.out; } - group cond6<"static"=0> { - cond6[done] = 1'd1; + comb group cond6 { le6.left = __k0.out; le6.right = const16.out; } diff --git a/tests/frontend/relay/bias_add.expect b/tests/frontend/relay/bias_add.expect index 17701a735e..2e8439d902 100644 --- a/tests/frontend/relay/bias_add.expect +++ b/tests/frontend/relay/bias_add.expect @@ -47,23 +47,19 @@ component bias_add_1x64x512x256(x0_0_0_0_read_data: 32, x0_0_0_0_done: 1, bias0_ x_read0_0 = std_reg(32); } wires { - group cond0<"static"=0> { - cond0[done] = 1'd1; + comb group cond0 { le0.left = __i0.out; le0.right = const1.out; } - group cond1<"static"=0> { - cond1[done] = 1'd1; + comb group cond1 { le1.left = __j0.out; le1.right = const3.out; } - group cond2<"static"=0> { - cond2[done] = 1'd1; + comb group cond2 { le2.left = __k0.out; le2.right = const5.out; } - group cond3<"static"=0> { - cond3[done] = 1'd1; + comb group cond3 { le3.left = __l0.out; le3.right = const7.out; } diff --git a/tests/frontend/relay/broadcast.expect b/tests/frontend/relay/broadcast.expect index 955ba10b9c..a516aba980 100644 --- a/tests/frontend/relay/broadcast.expect +++ b/tests/frontend/relay/broadcast.expect @@ -36,13 +36,11 @@ component add_2x4(x0_0_read_data: 32, x0_0_done: 1, y0_0_read_data: 32, y0_0_don y_read0_0 = std_reg(32); } wires { - group cond0<"static"=0> { - cond0[done] = 1'd1; + comb group cond0 { le0.left = __i0.out; le0.right = const1.out; } - group cond1<"static"=0> { - cond1[done] = 1'd1; + comb group cond1 { le1.left = __j0.out; le1.right = const3.out; } diff --git a/tests/frontend/relay/constant-multiply.expect b/tests/frontend/relay/constant-multiply.expect index 6a0a86d6d0..034cd7af3d 100644 --- a/tests/frontend/relay/constant-multiply.expect +++ b/tests/frontend/relay/constant-multiply.expect @@ -29,8 +29,7 @@ component multiply_1(x0_read_data: 32, x0_done: 1, x1: 32, x20_read_data: 32, x2 x_read0_0 = std_reg(32); } wires { - group cond0<"static"=0> { - cond0[done] = 1'd1; + comb group cond0 { le0.left = __i0.out; le0.right = const1.out; } diff --git a/tests/frontend/relay/conv2d.expect b/tests/frontend/relay/conv2d.expect index 62cfa85181..f0f997b452 100644 --- a/tests/frontend/relay/conv2d.expect +++ b/tests/frontend/relay/conv2d.expect @@ -91,38 +91,31 @@ component conv2d_5x512x14x14(data0_0_0_0_read_data: 32, data0_0_0_0_done: 1, wei weight_read0_0 = std_reg(32); } wires { - group cond0<"static"=0> { - cond0[done] = 1'd1; + comb group cond0 { le0.left = __b0.out; le0.right = const1.out; } - group cond1<"static"=0> { - cond1[done] = 1'd1; + comb group cond1 { le1.left = __c0.out; le1.right = const3.out; } - group cond2<"static"=0> { - cond2[done] = 1'd1; + comb group cond2 { le2.left = __y0.out; le2.right = const5.out; } - group cond3<"static"=0> { - cond3[done] = 1'd1; + comb group cond3 { le3.left = __x0.out; le3.right = const7.out; } - group cond4<"static"=0> { - cond4[done] = 1'd1; + comb group cond4 { le4.left = __k0.out; le4.right = const10.out; } - group cond5<"static"=0> { - cond5[done] = 1'd1; + comb group cond5 { le5.left = __dy0.out; le5.right = const12.out; } - group cond6<"static"=0> { - cond6[done] = 1'd1; + comb group cond6 { le6.left = __dx0.out; le6.right = const14.out; } diff --git a/tests/frontend/relay/dense.expect b/tests/frontend/relay/dense.expect index 098c8f5554..6ee5c44b5e 100644 --- a/tests/frontend/relay/dense.expect +++ b/tests/frontend/relay/dense.expect @@ -59,28 +59,23 @@ component dense_1x10(x0_0_read_data: 32, x0_0_done: 1, y0_0_read_data: 32, y0_0_ y_read0_0 = std_reg(32); } wires { - group cond0<"static"=0> { - cond0[done] = 1'd1; + comb group cond0 { le0.left = __i0.out; le0.right = const1.out; } - group cond1<"static"=0> { - cond1[done] = 1'd1; + comb group cond1 { le1.left = __j0.out; le1.right = const3.out; } - group cond2<"static"=0> { - cond2[done] = 1'd1; + comb group cond2 { le2.left = __i1.out; le2.right = const7.out; } - group cond3<"static"=0> { - cond3[done] = 1'd1; + comb group cond3 { le3.left = __j1.out; le3.right = const9.out; } - group cond4<"static"=0> { - cond4[done] = 1'd1; + comb group cond4 { le4.left = __k0.out; le4.right = const11.out; } diff --git a/tests/frontend/relay/duplicate-relay-call.expect b/tests/frontend/relay/duplicate-relay-call.expect index 13838c7205..ad9c8c27cd 100644 --- a/tests/frontend/relay/duplicate-relay-call.expect +++ b/tests/frontend/relay/duplicate-relay-call.expect @@ -37,13 +37,11 @@ component negative_2x2_1(x10_0_read_data: 32, x10_0_done: 1, x20_0_read_data: 32 x1_read0_0 = std_reg(32); } wires { - group cond0<"static"=0> { - cond0[done] = 1'd1; + comb group cond0 { le0.left = __i0.out; le0.right = const1.out; } - group cond1<"static"=0> { - cond1[done] = 1'd1; + comb group cond1 { le1.left = __j0.out; le1.right = const3.out; } @@ -126,13 +124,11 @@ component negative_2x2(x0_0_read_data: 32, x0_0_done: 1, x10_0_read_data: 32, x1 x_read0_0 = std_reg(32); } wires { - group cond2<"static"=0> { - cond2[done] = 1'd1; + comb group cond2 { le2.left = __i1.out; le2.right = const8.out; } - group cond3<"static"=0> { - cond3[done] = 1'd1; + comb group cond3 { le3.left = __j1.out; le3.right = const10.out; } diff --git a/tests/frontend/relay/max_pool2d.expect b/tests/frontend/relay/max_pool2d.expect index 3afd65778d..a101a6c2a5 100644 --- a/tests/frontend/relay/max_pool2d.expect +++ b/tests/frontend/relay/max_pool2d.expect @@ -82,38 +82,31 @@ component max_pool2d_2x2x2x2(data0_0_0_0_read_data: 32, data0_0_0_0_done: 1, res slice9 = std_slice(32,2); } wires { - group cond0<"static"=0> { - cond0[done] = 1'd1; + comb group cond0 { le0.left = __b0.out; le0.right = const1.out; } - group cond1<"static"=0> { - cond1[done] = 1'd1; + comb group cond1 { le1.left = __c0.out; le1.right = const3.out; } - group cond2<"static"=0> { - cond2[done] = 1'd1; + comb group cond2 { le2.left = __y0.out; le2.right = const5.out; } - group cond3<"static"=0> { - cond3[done] = 1'd1; + comb group cond3 { le3.left = __x0.out; le3.right = const7.out; } - group cond4<"static"=0> { - cond4[done] = 1'd1; + comb group cond4 { le4.left = __m0.out; le4.right = const11.out; } - group cond5<"static"=0> { - cond5[done] = 1'd1; + comb group cond5 { le5.left = __n0.out; le5.right = const13.out; } - group cond6<"static"=0> { - cond6[done] = 1'd1; + comb group cond6 { gt0.left = __current_0.out; gt0.right = __max_0.out; } diff --git a/tests/frontend/relay/negative.expect b/tests/frontend/relay/negative.expect index 7db547beef..0409f66ce5 100644 --- a/tests/frontend/relay/negative.expect +++ b/tests/frontend/relay/negative.expect @@ -28,8 +28,7 @@ component negative_4(x0_read_data: 32, x0_done: 1, x10_read_data: 32, x10_done: x_read0_0 = std_reg(32); } wires { - group cond0<"static"=0> { - cond0[done] = 1'd1; + comb group cond0 { le0.left = __i0.out; le0.right = const1.out; } diff --git a/tests/frontend/relay/relu.expect b/tests/frontend/relay/relu.expect index 0a48081edb..27d174d776 100644 --- a/tests/frontend/relay/relu.expect +++ b/tests/frontend/relay/relu.expect @@ -48,28 +48,23 @@ component relu_2x4x8x32(x0_0_0_0_read_data: 32, x0_0_0_0_done: 1, x10_0_0_0_read x_read1_0 = std_reg(32); } wires { - group cond0<"static"=0> { - cond0[done] = 1'd1; + comb group cond0 { le0.left = __i0.out; le0.right = const1.out; } - group cond1<"static"=0> { - cond1[done] = 1'd1; + comb group cond1 { le1.left = __j0.out; le1.right = const3.out; } - group cond2<"static"=0> { - cond2[done] = 1'd1; + comb group cond2 { le2.left = __k0.out; le2.right = const5.out; } - group cond3<"static"=0> { - cond3[done] = 1'd1; + comb group cond3 { le3.left = __l0.out; le3.right = const7.out; } - group cond4<"static"=0> { - cond4[done] = 1'd1; + comb group cond4 { gt0.left = x_read0_0.out; gt0.right = fp_const0.out; } diff --git a/tests/frontend/relay/reshape.expect b/tests/frontend/relay/reshape.expect index 15bad9b473..4f068f1a3b 100644 --- a/tests/frontend/relay/reshape.expect +++ b/tests/frontend/relay/reshape.expect @@ -49,23 +49,19 @@ component reshape_1x8(x0_0_0_0_read_data: 32, x0_0_0_0_done: 1, x10_0_read_data: x_read0_0 = std_reg(32); } wires { - group cond0<"static"=0> { - cond0[done] = 1'd1; + comb group cond0 { le0.left = __i0.out; le0.right = const2.out; } - group cond1<"static"=0> { - cond1[done] = 1'd1; + comb group cond1 { le1.left = __j0.out; le1.right = const4.out; } - group cond2<"static"=0> { - cond2[done] = 1'd1; + comb group cond2 { le2.left = __k0.out; le2.right = const6.out; } - group cond3<"static"=0> { - cond3[done] = 1'd1; + comb group cond3 { le3.left = __l0.out; le3.right = const8.out; } diff --git a/tests/frontend/relay/softmax.expect b/tests/frontend/relay/softmax.expect index 746e21cbd8..dccaa0d9e5 100644 --- a/tests/frontend/relay/softmax.expect +++ b/tests/frontend/relay/softmax.expect @@ -70,33 +70,27 @@ component softmax_1x10(x0_0_read_data: 32, x0_0_done: 1, x10_0_read_data: 32, x1 x_read3_0 = std_reg(32); } wires { - group cond0<"static"=0> { - cond0[done] = 1'd1; + comb group cond0 { le0.left = __i0.out; le0.right = const3.out; } - group cond1<"static"=0> { - cond1[done] = 1'd1; + comb group cond1 { le1.left = __j0.out; le1.right = const5.out; } - group cond2<"static"=0> { - cond2[done] = 1'd1; + comb group cond2 { gt0.left = x_read0_0.out; gt0.right = __max_0.out; } - group cond3<"static"=0> { - cond3[done] = 1'd1; + comb group cond3 { le2.left = __i1.out; le2.right = const9.out; } - group cond4<"static"=0> { - cond4[done] = 1'd1; + comb group cond4 { le3.left = __j1.out; le3.right = const11.out; } - group cond5<"static"=0> { - cond5[done] = 1'd1; + comb group cond5 { le4.left = __k0.out; le4.right = const14.out; } @@ -405,10 +399,9 @@ component exp(x: 32) -> (out: 32) { exponent_value.in = mult_pipe1.out; negate[done] = exponent_value.done; } - group is_negative<"static"=0> { + comb group is_negative { lt.left = x; lt.right = 32'd0; - is_negative[done] = 1'd1; } group reciprocal { div_pipe.left = one.out; @@ -662,10 +655,9 @@ component fp_pow(base: 32, integer_exp: 32) -> (out: 32) { count.write_en = 1'd1; incr_count[done] = count.done; } - group cond { + comb group cond { lt.left = count.out; lt.right = integer_exp; - cond[done] = 1'd1; } out = pow.out; } diff --git a/tests/frontend/relay/sqrt.expect b/tests/frontend/relay/sqrt.expect index 8fcf605bf0..ecb613a968 100644 --- a/tests/frontend/relay/sqrt.expect +++ b/tests/frontend/relay/sqrt.expect @@ -34,13 +34,11 @@ component sqrt_1x4(x0_0_read_data: 32, x0_0_done: 1, x10_0_read_data: 32, x10_0_ x_read0_0 = std_reg(32); } wires { - group cond0<"static"=0> { - cond0[done] = 1'd1; + comb group cond0 { le0.left = __i0.out; le0.right = const1.out; } - group cond1<"static"=0> { - cond1[done] = 1'd1; + comb group cond1 { le1.left = __j0.out; le1.right = const3.out; } diff --git a/tests/frontend/relay/tensor_add.expect b/tests/frontend/relay/tensor_add.expect index 8a8e7bdeeb..f3bcb122b1 100644 --- a/tests/frontend/relay/tensor_add.expect +++ b/tests/frontend/relay/tensor_add.expect @@ -35,13 +35,11 @@ component add_2x4(x0_0_read_data: 32, x0_0_done: 1, y0_0_read_data: 32, y0_0_don y_read0_0 = std_reg(32); } wires { - group cond0<"static"=0> { - cond0[done] = 1'd1; + comb group cond0 { le0.left = __i0.out; le0.right = const1.out; } - group cond1<"static"=0> { - cond1[done] = 1'd1; + comb group cond1 { le1.left = __j0.out; le1.right = const3.out; } diff --git a/tests/parsing/attributes.expect b/tests/parsing/attributes.expect index 07f06d2446..e3100ab8f7 100644 --- a/tests/parsing/attributes.expect +++ b/tests/parsing/attributes.expect @@ -6,14 +6,16 @@ component main<"static"=1>(@stable(32) @go_port in: 32, go: 1, clk: 1, @go go0: @external(32) le = std_le(32); } wires { - group cond<"stable"=1> { - cond[done] = 1'd1; + group upd<"stable"=1> { + upd[done] = r.done; + } + comb group cond<"stable"=0> { } } control { @bound(32) while le.out with cond { - cond; + upd; } } } diff --git a/tests/passes/compile-control/compile-if-static.expect b/tests/passes/compile-control/compile-if-static.expect deleted file mode 100644 index e9ecb780f6..0000000000 --- a/tests/passes/compile-control/compile-if-static.expect +++ /dev/null @@ -1,49 +0,0 @@ -import "primitives/std.lib"; -component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { - cells { - t = std_reg(1); - f = std_reg(1); - lt = std_lt(1); - @generated comb_reg = std_reg(1); - @generated fsm = std_reg(2); - @generated cond_stored = std_reg(1); - @generated incr = std_add(2); - } - wires { - group true<"static"=1> { - t.in = 1'd1; - t.write_en = 1'd1; - true[done] = t.done; - } - group false<"static"=1> { - f.in = 1'd1; - f.write_en = 1'd1; - false[done] = f.done; - } - group cond<"static"=1> { - lt.left = 1'd1; - lt.right = 1'd0; - cond[done] = comb_reg.done ? 1'd1; - comb_reg.in = lt.out; - comb_reg.write_en = 1'd1; - } - group static_if<"static"=3> { - incr.left = fsm.out; - incr.right = 2'd1; - fsm.in = fsm.out != 2'd3 ? incr.out; - fsm.write_en = fsm.out != 2'd3 ? 1'd1; - cond[go] = fsm.out < 2'd1 ? 1'd1; - cond_stored.write_en = fsm.out == 2'd1 ? 1'd1; - true[go] = fsm.out > 2'd1 & fsm.out < 2'd3 & cond_stored.out ? 1'd1; - false[go] = fsm.out > 2'd1 & fsm.out < 2'd3 & !cond_stored.out ? 1'd1; - static_if[done] = fsm.out == 2'd3 ? 1'd1; - cond_stored.in = fsm.out == 2'd1 ? comb_reg.out; - } - fsm.in = fsm.out == 2'd3 ? 2'd0; - fsm.write_en = fsm.out == 2'd3 ? 1'd1; - } - - control { - static_if; - } -} diff --git a/tests/passes/compile-control/compile-if.expect b/tests/passes/compile-control/compile-if.expect deleted file mode 100644 index ef7f1b721e..0000000000 --- a/tests/passes/compile-control/compile-if.expect +++ /dev/null @@ -1,50 +0,0 @@ -import "primitives/std.lib"; -component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { - cells { - t = std_reg(1); - f = std_reg(1); - lt = std_lt(1); - @generated cond_computed = std_reg(1); - @generated cond_stored = std_reg(1); - @generated done_reg = std_reg(1); - } - wires { - group true { - t.in = 1'd1; - t.write_en = 1'd1; - true[done] = t.done; - } - group false { - f.in = 1'd1; - f.write_en = 1'd1; - false[done] = f.done; - } - group cond { - lt.left = 1'd1; - lt.right = 1'd0; - cond[done] = 1'd1; - } - group if { - cond_stored.in = cond[go] & cond[done] ? lt.out; - cond_stored.write_en = cond[go] & cond[done] ? lt.out; - cond[go] = !cond_computed.out ? 1'd1; - cond_computed.in = cond[go] & cond[done] ? 1'd1; - cond_computed.write_en = cond[go] & cond[done] ? 1'd1; - true[go] = !true[done] & cond_computed.out & cond_stored.out ? 1'd1; - false[go] = !false[done] & cond_computed.out & !cond_stored.out ? 1'd1; - done_reg.in = cond_computed.out & cond_stored.out & true[done] | cond_computed.out & !cond_stored.out & false[done] ? 1'd1; - done_reg.write_en = cond_computed.out & cond_stored.out & true[done] | cond_computed.out & !cond_stored.out & false[done] ? 1'd1; - if[done] = done_reg.out ? 1'd1; - } - done_reg.in = done_reg.out ? 1'd0; - done_reg.write_en = done_reg.out ? 1'd1; - cond_computed.in = done_reg.out ? 1'd0; - cond_computed.write_en = done_reg.out ? 1'd1; - cond_stored.in = done_reg.out ? 1'd0; - cond_stored.write_en = done_reg.out ? 1'd1; - } - - control { - if; - } -} diff --git a/tests/passes/compile-control/compile-par-static.expect b/tests/passes/compile-control/compile-par-static.expect deleted file mode 100644 index fd89014174..0000000000 --- a/tests/passes/compile-control/compile-par-static.expect +++ /dev/null @@ -1,55 +0,0 @@ -import "primitives/std.lib"; -component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { - cells { - a = std_reg(2); - b = std_reg(2); - c = std_reg(2); - @generated par_reset = std_reg(1); - @generated par_done_reg = std_reg(1); - @generated par_done_reg0 = std_reg(1); - @generated par_done_reg1 = std_reg(1); - } - wires { - group A<"static"=1> { - a.in = 2'd0; - a.write_en = 1'd1; - A[done] = a.done; - } - group B<"static"=1> { - b.in = 2'd1; - b.write_en = 1'd1; - B[done] = b.done; - } - group C<"static"=1> { - c.in = 2'd2; - c.write_en = 1'd1; - C[done] = c.done; - } - group par { - A[go] = !(par_done_reg.out | A[done]) ? 1'd1; - par_done_reg.in = A[done] ? 1'd1; - par_done_reg.write_en = A[done] ? 1'd1; - B[go] = !(par_done_reg0.out | B[done]) ? 1'd1; - par_done_reg0.in = B[done] ? 1'd1; - par_done_reg0.write_en = B[done] ? 1'd1; - C[go] = !(par_done_reg1.out | C[done]) ? 1'd1; - par_done_reg1.in = C[done] ? 1'd1; - par_done_reg1.write_en = C[done] ? 1'd1; - par_reset.in = par_done_reg.out & par_done_reg0.out & par_done_reg1.out ? 1'd1; - par_reset.write_en = par_done_reg.out & par_done_reg0.out & par_done_reg1.out ? 1'd1; - par[done] = par_reset.out ? 1'd1; - } - par_reset.in = par_reset.out ? 1'd0; - par_reset.write_en = par_reset.out ? 1'd1; - par_done_reg.in = par_reset.out ? 1'd0; - par_done_reg.write_en = par_reset.out ? 1'd1; - par_done_reg0.in = par_reset.out ? 1'd0; - par_done_reg0.write_en = par_reset.out ? 1'd1; - par_done_reg1.in = par_reset.out ? 1'd0; - par_done_reg1.write_en = par_reset.out ? 1'd1; - } - - control { - par; - } -} diff --git a/tests/passes/compile-control/compile-par.expect b/tests/passes/compile-control/compile-par.expect deleted file mode 100644 index 52ad07125f..0000000000 --- a/tests/passes/compile-control/compile-par.expect +++ /dev/null @@ -1,55 +0,0 @@ -import "primitives/std.lib"; -component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { - cells { - a = std_reg(2); - b = std_reg(2); - c = std_reg(2); - @generated par_reset = std_reg(1); - @generated par_done_reg = std_reg(1); - @generated par_done_reg0 = std_reg(1); - @generated par_done_reg1 = std_reg(1); - } - wires { - group A { - a.in = 2'd0; - a.write_en = 1'd1; - A[done] = a.done; - } - group B { - b.in = 2'd1; - b.write_en = 1'd1; - B[done] = b.done; - } - group C { - c.in = 2'd2; - c.write_en = 1'd1; - C[done] = c.done; - } - group par { - A[go] = !(par_done_reg.out | A[done]) ? 1'd1; - par_done_reg.in = A[done] ? 1'd1; - par_done_reg.write_en = A[done] ? 1'd1; - B[go] = !(par_done_reg0.out | B[done]) ? 1'd1; - par_done_reg0.in = B[done] ? 1'd1; - par_done_reg0.write_en = B[done] ? 1'd1; - C[go] = !(par_done_reg1.out | C[done]) ? 1'd1; - par_done_reg1.in = C[done] ? 1'd1; - par_done_reg1.write_en = C[done] ? 1'd1; - par_reset.in = par_done_reg.out & par_done_reg0.out & par_done_reg1.out ? 1'd1; - par_reset.write_en = par_done_reg.out & par_done_reg0.out & par_done_reg1.out ? 1'd1; - par[done] = par_reset.out ? 1'd1; - } - par_reset.in = par_reset.out ? 1'd0; - par_reset.write_en = par_reset.out ? 1'd1; - par_done_reg.in = par_reset.out ? 1'd0; - par_done_reg.write_en = par_reset.out ? 1'd1; - par_done_reg0.in = par_reset.out ? 1'd0; - par_done_reg0.write_en = par_reset.out ? 1'd1; - par_done_reg1.in = par_reset.out ? 1'd0; - par_done_reg1.write_en = par_reset.out ? 1'd1; - } - - control { - par; - } -} diff --git a/tests/passes/compile-control/compile-seq-static.expect b/tests/passes/compile-control/compile-seq-static.expect deleted file mode 100644 index 053050da82..0000000000 --- a/tests/passes/compile-control/compile-seq-static.expect +++ /dev/null @@ -1,44 +0,0 @@ -import "primitives/std.lib"; -component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { - cells { - a = std_reg(2); - b = std_reg(2); - c = std_reg(2); - @generated fsm = std_reg(2); - } - wires { - group A<"static"=1> { - a.in = 2'd0; - a.write_en = 1'd1; - A[done] = a.done; - } - group B<"static"=1> { - b.in = 2'd1; - b.write_en = 1'd1; - B[done] = b.done; - } - group C<"static"=1> { - c.in = 2'd2; - c.write_en = 1'd1; - C[done] = c.done; - } - group seq { - A[go] = fsm.out == 2'd0 & !A[done] ? 1'd1; - fsm.in = fsm.out == 2'd0 & A[done] ? 2'd1; - fsm.write_en = fsm.out == 2'd0 & A[done] ? 1'd1; - B[go] = fsm.out == 2'd1 & !B[done] ? 1'd1; - fsm.in = fsm.out == 2'd1 & B[done] ? 2'd2; - fsm.write_en = fsm.out == 2'd1 & B[done] ? 1'd1; - C[go] = fsm.out == 2'd2 & !C[done] ? 1'd1; - fsm.in = fsm.out == 2'd2 & C[done] ? 2'd3; - fsm.write_en = fsm.out == 2'd2 & C[done] ? 1'd1; - seq[done] = fsm.out == 2'd3 ? 1'd1; - } - fsm.in = fsm.out == 2'd3 ? 2'd0; - fsm.write_en = fsm.out == 2'd3 ? 1'd1; - } - - control { - seq; - } -} diff --git a/tests/passes/compile-control/compile-seq.expect b/tests/passes/compile-control/compile-seq.expect deleted file mode 100644 index ccd77db5ba..0000000000 --- a/tests/passes/compile-control/compile-seq.expect +++ /dev/null @@ -1,44 +0,0 @@ -import "primitives/std.lib"; -component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { - cells { - a = std_reg(2); - b = std_reg(2); - c = std_reg(2); - @generated fsm = std_reg(2); - } - wires { - group A { - a.in = 2'd0; - a.write_en = 1'd1; - A[done] = a.done; - } - group B { - b.in = 2'd1; - b.write_en = 1'd1; - B[done] = b.done; - } - group C { - c.in = 2'd2; - c.write_en = 1'd1; - C[done] = c.done; - } - group seq { - A[go] = fsm.out == 2'd0 & !A[done] ? 1'd1; - fsm.in = fsm.out == 2'd0 & A[done] ? 2'd1; - fsm.write_en = fsm.out == 2'd0 & A[done] ? 1'd1; - B[go] = fsm.out == 2'd1 & !B[done] ? 1'd1; - fsm.in = fsm.out == 2'd1 & B[done] ? 2'd2; - fsm.write_en = fsm.out == 2'd1 & B[done] ? 1'd1; - C[go] = fsm.out == 2'd2 & !C[done] ? 1'd1; - fsm.in = fsm.out == 2'd2 & C[done] ? 2'd3; - fsm.write_en = fsm.out == 2'd2 & C[done] ? 1'd1; - seq[done] = fsm.out == 2'd3 ? 1'd1; - } - fsm.in = fsm.out == 2'd3 ? 2'd0; - fsm.write_en = fsm.out == 2'd3 ? 1'd1; - } - - control { - seq; - } -} diff --git a/tests/passes/compile-control/compile-while-static.expect b/tests/passes/compile-control/compile-while-static.expect deleted file mode 100644 index c03ae25311..0000000000 --- a/tests/passes/compile-control/compile-while-static.expect +++ /dev/null @@ -1,47 +0,0 @@ -import "primitives/std.lib"; -component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { - cells { - add = std_add(32); - add_r = std_reg(32); - lt = std_lt(32); - lt_r = std_reg(1); - @generated fsm = std_reg(2); - @generated cond_stored = std_reg(1); - @generated incr = std_add(2); - } - wires { - group do_add<"static"=1> { - add.right = 32'd4; - add.left = 32'd4; - add_r.in = add.out; - add_r.write_en = 1'd1; - do_add[done] = add_r.done; - } - group cond<"static"=1> { - lt.right = 32'd5; - lt.left = 32'd1; - lt_r.in = lt.out; - lt_r.write_en = 1'd1; - cond[done] = lt_r.out; - } - group static_while { - incr.left = fsm.out; - incr.right = 2'd1; - fsm.in = fsm.out < 2'd2 | fsm.out >= 2'd2 & fsm.out < 2'd3 & cond_stored.out ? incr.out; - fsm.write_en = fsm.out < 2'd2 | fsm.out >= 2'd2 & fsm.out < 2'd3 & cond_stored.out ? 1'd1; - cond[go] = fsm.out < 2'd1 ? 1'd1; - cond_stored.write_en = fsm.out == 2'd1 ? 1'd1; - do_add[go] = cond_stored.out & fsm.out >= 2'd2 & fsm.out < 2'd3 ? 1'd1; - fsm.in = fsm.out == 2'd3 ? 2'd0; - fsm.write_en = fsm.out == 2'd3 ? 1'd1; - static_while[done] = fsm.out == 2'd2 & !cond_stored.out ? 1'd1; - cond_stored.in = fsm.out == 2'd1 ? lt_r.out; - } - fsm.in = fsm.out == 2'd2 & !cond_stored.out ? 2'd0; - fsm.write_en = fsm.out == 2'd2 & !cond_stored.out ? 1'd1; - } - - control { - static_while; - } -} diff --git a/tests/passes/compile-control/compile-while.expect b/tests/passes/compile-control/compile-while.expect deleted file mode 100644 index fd00cb5963..0000000000 --- a/tests/passes/compile-control/compile-while.expect +++ /dev/null @@ -1,43 +0,0 @@ -import "primitives/std.lib"; -component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { - cells { - add = std_add(32); - lt = std_lt(32); - @generated cond_computed = std_reg(1); - @generated cond_stored = std_reg(1); - @generated done_reg = std_reg(1); - } - wires { - group do_add { - add.right = 32'd4; - add.left = 32'd4; - do_add[done] = 1'd1; - } - group cond { - lt.right = 32'd5; - lt.left = 32'd1; - cond[done] = 1'd1; - } - group while { - cond_stored.in = cond[go] & cond[done] ? lt.out; - cond[go] = !cond_computed.out ? 1'd1; - cond_computed.in = cond[go] & cond[done] ? 1'd1; - cond_computed.write_en = cond[go] & cond[done] ? 1'd1; - cond_stored.write_en = cond[go] & cond[done] ? 1'd1; - do_add[go] = cond_stored.out & cond_computed.out & !do_add[done] ? 1'd1; - cond_computed.in = cond_stored.out & cond_computed.out & do_add[done] ? 1'd0; - cond_computed.write_en = cond_stored.out & cond_computed.out & do_add[done] ? 1'd1; - done_reg.in = cond_computed.out & !cond_stored.out ? 1'd1; - done_reg.write_en = cond_computed.out & !cond_stored.out ? 1'd1; - while[done] = done_reg.out ? 1'd1; - cond_computed.in = cond_computed.out & !cond_stored.out ? 1'd0; - cond_computed.write_en = cond_computed.out & !cond_stored.out ? 1'd1; - } - done_reg.in = done_reg.out ? 1'd0; - done_reg.write_en = done_reg.out ? 1'd1; - } - - control { - while; - } -} diff --git a/tests/passes/compile-empty.expect b/tests/passes/compile-empty.expect index 255adb1e09..50a12bc2be 100644 --- a/tests/passes/compile-empty.expect +++ b/tests/passes/compile-empty.expect @@ -15,10 +15,12 @@ component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { empty_reg.in = 1'd1; _empty[done] = empty_reg.done; } + comb group cond { + } } control { - if r.out with do_incr { + if r.out with cond { do_incr; } else { _empty; diff --git a/tests/passes/compile-empty.futil b/tests/passes/compile-empty.futil index 458d399a9d..7867f05656 100644 --- a/tests/passes/compile-empty.futil +++ b/tests/passes/compile-empty.futil @@ -6,6 +6,7 @@ component main() -> () { r = std_reg(1); } wires { + comb group cond {} group do_incr { r.in = 1'd1; r.write_en = 1'd1; @@ -13,7 +14,7 @@ component main() -> () { } } control { - if r.out with do_incr { + if r.out with cond { do_incr; } } diff --git a/tests/passes/dead-group-removal/group.expect b/tests/passes/dead-group-removal/group.expect new file mode 100644 index 0000000000..c1d7548067 --- /dev/null +++ b/tests/passes/dead-group-removal/group.expect @@ -0,0 +1,32 @@ +import "primitives/core.futil"; +component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { + cells { + r = std_reg(32); + lt = std_lt(32); + } + wires { + group one { + r.in = 32'd1; + r.write_en = 1'd1; + one[done] = r.done; + } + group two { + r.in = 32'd1; + r.write_en = 1'd1; + two[done] = r.done; + } + comb group cond { + lt.left = 32'd0; + lt.right = r.out; + } + } + + control { + seq { + one; + if r.out with cond { + two; + } + } + } +} diff --git a/tests/passes/dead-group-removal/group.futil b/tests/passes/dead-group-removal/group.futil new file mode 100644 index 0000000000..56cb2dd44b --- /dev/null +++ b/tests/passes/dead-group-removal/group.futil @@ -0,0 +1,46 @@ +// -p dead-group-removal +import "primitives/core.futil"; +component main() -> () { + cells { + r = std_reg(32); + lt = std_lt(32); + } + wires { + comb group dead_comb { + lt.left = 32'd0; + lt.right = r.out; + } + comb group cond { + lt.left = 32'd0; + lt.right = r.out; + } + group one { + r.in = 32'd1; + r.write_en = 1'd1; + one[done] = r.done; + } + group two { + r.in = 32'd1; + r.write_en = 1'd1; + two[done] = r.done; + } + group dead_one { + r.in = 32'd1; + r.write_en = 1'd1; + dead_one[done] = r.done; + } + group dead_two { + r.in = 32'd1; + r.write_en = 1'd1; + dead_two[done] = r.done; + } + } + control { + seq { + one; + if r.out with cond { + two; + } + } + } +} diff --git a/tests/passes/infer-static/bounded-loop.expect b/tests/passes/infer-static/bounded-loop.expect index ac025c8291..835a8a9091 100644 --- a/tests/passes/infer-static/bounded-loop.expect +++ b/tests/passes/infer-static/bounded-loop.expect @@ -7,11 +7,6 @@ component main<"static"=10>(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done d add = std_add(32); } wires { - group cond<"static"=0> { - lt.left = i.read_data; - lt.right = 32'd5; - cond[done] = 1'd1; - } group incr_i<"static"=1> { i.write_data = add.out; i.addr0 = 1'd0; @@ -28,6 +23,10 @@ component main<"static"=10>(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done d add.left = 32'd1; incr_j[done] = j.done; } + comb group cond { + lt.left = i.read_data; + lt.right = 32'd5; + } } control { diff --git a/tests/passes/infer-static/bounded-loop.futil b/tests/passes/infer-static/bounded-loop.futil index a66d1577b7..bf5a1105e4 100644 --- a/tests/passes/infer-static/bounded-loop.futil +++ b/tests/passes/infer-static/bounded-loop.futil @@ -11,10 +11,9 @@ component main() -> () { } wires { - group cond<"static"=0> { + comb group cond { lt.left = i.read_data; lt.right = 32'd5; - cond[done] = 1'b1; } group incr_i<"static"=1> { diff --git a/tests/passes/minimize-regs/condition-register.expect b/tests/passes/minimize-regs/condition-register.expect index f38d7d3385..a40f0cc5cb 100644 --- a/tests/passes/minimize-regs/condition-register.expect +++ b/tests/passes/minimize-regs/condition-register.expect @@ -2,6 +2,7 @@ import "primitives/std.lib"; component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { cells { x = std_reg(1); + y = std_reg(1); } wires { group wr_x { @@ -9,42 +10,22 @@ component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { x.write_en = 1'd1; wr_x[done] = x.done; } - group cond { - x.in = 1'd1; - x.write_en = 1'd1; - cond[done] = x.done; - } - group some_math { - x.in = 1'd0; - x.write_en = 1'd1; - some_math[done] = x.done; - } - group wr_z { - x.in = 1'd1; - x.write_en = 1'd1; - wr_z[done] = x.done; - } - group cond1 { - x.in = 1'd1; - x.write_en = 1'd1; - cond1[done] = x.done; + group wr_y { + y.in = 1'd1; + y.write_en = 1'd1; + wr_y[done] = y.done; } - group some_math1 { - x.in = 1'd0; - x.write_en = 1'd1; - some_math[done] = x.done; + group rd_y { + rd_y[done] = y.out; } } control { seq { wr_x; - if x.out with cond { - some_math; - } - wr_z; - while x.out with cond1 { - some_math1; + wr_y; + if x.out { + rd_y; } } } diff --git a/tests/passes/minimize-regs/condition-register.futil b/tests/passes/minimize-regs/condition-register.futil index 0b41781573..18edc2da1d 100644 --- a/tests/passes/minimize-regs/condition-register.futil +++ b/tests/passes/minimize-regs/condition-register.futil @@ -1,10 +1,9 @@ -// -p minimize-regs -p dead-cell-removal +// -p well-formed -p remove-comb-groups -p minimize-regs -p dead-cell-removal import "primitives/std.lib"; component main() -> () { cells { x = std_reg(1); y = std_reg(1); - z = std_reg(1); } wires { @@ -14,46 +13,24 @@ component main() -> () { wr_x[done] = x.done; } - group cond { + group wr_y { y.in = 1'd1; y.write_en = 1'd1; - cond[done] = y.done; + wr_y[done] = y.done; } - group some_math { - y.in = 1'd0; - y.write_en = 1'd1; - some_math[done] = y.done; - } - - group wr_z { - z.in = 1'd1; - z.write_en = 1'd1; - wr_z[done] = z.done; + group rd_y { + rd_y[done] = y.out; } - group cond1 { - z.in = 1'd1; - z.write_en = 1'd1; - cond1[done] = z.done; - } - - group some_math1 { - z.in = 1'd0; - z.write_en = 1'd1; - some_math[done] = z.done; - } } control { seq { - wr_x; - if y.out with cond { - some_math; - } - wr_z; - while z.out with cond1 { - some_math1; + wr_x; // writes to x + wr_y; // writes to y + if x.out { // reads x + rd_y; // reads y } } } diff --git a/tests/passes/minimize-regs/continuous-assignment.futil b/tests/passes/minimize-regs/continuous-assignment.futil index 4824b5ff6a..a7beb16576 100644 --- a/tests/passes/minimize-regs/continuous-assignment.futil +++ b/tests/passes/minimize-regs/continuous-assignment.futil @@ -1,4 +1,4 @@ -// -p minimize-regs -p dead-cell-removal +// -p well-formed -p remove-comb-groups -p minimize-regs -p dead-cell-removal import "primitives/std.lib"; component main() -> (x_out: 32) { cells { diff --git a/tests/passes/minimize-regs/escape-boundary.futil b/tests/passes/minimize-regs/escape-boundary.futil index 1a6e5b0a24..da0fa1cdb3 100644 --- a/tests/passes/minimize-regs/escape-boundary.futil +++ b/tests/passes/minimize-regs/escape-boundary.futil @@ -1,4 +1,4 @@ -// -p minimize-regs -p dead-cell-removal +// -p well-formed -p remove-comb-groups -p minimize-regs -p dead-cell-removal import "primitives/std.lib"; component main() -> (x_out: 32) { cells { diff --git a/tests/passes/minimize-regs/invoke.expect b/tests/passes/minimize-regs/invoke.expect index 381a877ff9..865d93e684 100644 --- a/tests/passes/minimize-regs/invoke.expect +++ b/tests/passes/minimize-regs/invoke.expect @@ -35,7 +35,7 @@ component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { group rd_x { add_x.left = x.out; add_x.right = x.out; - rd_x[done] = 1'd1; + rd_x[done] = x.done; } group wr_y { x.in = 32'd10; diff --git a/tests/passes/minimize-regs/invoke.futil b/tests/passes/minimize-regs/invoke.futil index db41a5c5ed..dead6d9d82 100644 --- a/tests/passes/minimize-regs/invoke.futil +++ b/tests/passes/minimize-regs/invoke.futil @@ -1,4 +1,4 @@ -// -p minimize-regs -p dead-cell-removal +// -p well-formed -p remove-comb-groups -p minimize-regs -p dead-cell-removal import "primitives/std.lib"; component add(left: 32, right: 32) -> (out: 32) { @@ -39,7 +39,7 @@ component main() -> () { group rd_x { add_x.left = x.out; add_x.right = x.out; - rd_x[done] = 1'd1; + rd_x[done] = x.done; // XXX: This is wrong functionally } group wr_y { y.in = 32'd10; diff --git a/tests/passes/minimize-regs/live-register-analysis.expect b/tests/passes/minimize-regs/live-register-analysis.expect index 6478142e4a..77a565e322 100644 --- a/tests/passes/minimize-regs/live-register-analysis.expect +++ b/tests/passes/minimize-regs/live-register-analysis.expect @@ -11,18 +11,9 @@ component kernel(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { i0 = std_reg(6); le0 = std_le(6); le1 = std_le(6); + @generated comb_reg = std_reg(1); } wires { - group cond0<"static"=0> { - cond0[done] = 1'd1; - le0.left = i0.out; - le0.right = 6'd31; - } - group cond1<"static"=0> { - cond1[done] = 1'd1; - le1.left = i0.out; - le1.right = 6'd31; - } group let0<"static"=1> { i0.in = 6'd0; i0.write_en = 1'd1; @@ -83,28 +74,54 @@ component kernel(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { i0.in = add1.out; upd7[done] = i0.done; } + group cond00<"static"=1> { + le0.left = i0.out; + le0.right = 6'd31; + comb_reg.in = le0.out; + comb_reg.write_en = 1'd1; + cond00[done] = comb_reg.done ? 1'd1; + } + group cond10<"static"=1> { + le1.left = i0.out; + le1.right = 6'd31; + comb_reg.in = le1.out; + comb_reg.write_en = 1'd1; + cond10[done] = comb_reg.done ? 1'd1; + } } control { seq { let0; - while le0.out with cond0 { - seq { - upd0; - par { - upd1; - upd2; + seq { + cond00; + while comb_reg.out { + seq { + seq { + upd0; + par { + upd1; + upd2; + } + upd3; + upd4; + } + cond00; } - upd3; - upd4; } } let1; - while le1.out with cond1 { - seq { - upd5; - upd6; - upd7; + seq { + cond10; + while comb_reg.out { + seq { + seq { + upd5; + upd6; + upd7; + } + cond10; + } } } } diff --git a/tests/passes/minimize-regs/live-register-analysis.futil b/tests/passes/minimize-regs/live-register-analysis.futil index 1cf30966fc..d1187372f8 100644 --- a/tests/passes/minimize-regs/live-register-analysis.futil +++ b/tests/passes/minimize-regs/live-register-analysis.futil @@ -1,4 +1,4 @@ -// -p minimize-regs -p dead-cell-removal +// -p well-formed -p remove-comb-groups -p minimize-regs -p dead-cell-removal import "primitives/std.lib"; component kernel() -> () { cells { @@ -16,13 +16,11 @@ component kernel() -> () { le1 = std_le(6); } wires { - group cond0<"static"=0> { - cond0[done] = 1'd1; + comb group cond0 { le0.left = i0.out; le0.right = 6'd31; } - group cond1<"static"=0> { - cond1[done] = 1'd1; + comb group cond1 { le1.left = i1.out; le1.right = 6'd31; } diff --git a/tests/passes/minimize-regs/nested-par.expect b/tests/passes/minimize-regs/nested-par.expect index cea1707fa7..5068bf7a6a 100644 --- a/tests/passes/minimize-regs/nested-par.expect +++ b/tests/passes/minimize-regs/nested-par.expect @@ -24,10 +24,10 @@ component kernel(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { b0.write_en = 1'd1; wr_b0[done] = b0.done; } - group rd_x0<"static"=0> { + group rd_x0 { read_x0.right = before0.out; read_x0.left = before0.out; - rd_x0[done] = 1'd1; + rd_x0[done] = before0.done; } group wr_before1<"static"=1> { before1.in = 4'd1; @@ -44,10 +44,10 @@ component kernel(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { b1.write_en = 1'd1; wr_b1[done] = b1.done; } - group rd_x1<"static"=0> { + group rd_x1 { read_x1.right = before1.out; read_x1.left = before1.out; - rd_x1[done] = 1'd1; + rd_x1[done] = before1.done; } } diff --git a/tests/passes/minimize-regs/nested-par.futil b/tests/passes/minimize-regs/nested-par.futil index d3172a83dd..682d9f003f 100644 --- a/tests/passes/minimize-regs/nested-par.futil +++ b/tests/passes/minimize-regs/nested-par.futil @@ -1,4 +1,4 @@ -// -p minimize-regs -p dead-cell-removal +// -p well-formed -p remove-comb-groups -p minimize-regs -p dead-cell-removal import "primitives/std.lib"; component kernel() -> () { cells { @@ -28,10 +28,10 @@ component kernel() -> () { b0.write_en = 1'd1; wr_b0[done] = b0.done; } - group rd_x0<"static"=0> { + group rd_x0 { read_x0.right = x0.out; read_x0.left = x0.out; - rd_x0[done] = 1'd1; + rd_x0[done] = x0.done; // XXX: This is functionally wrong } group wr_before1<"static"=1> { @@ -49,10 +49,10 @@ component kernel() -> () { b1.write_en = 1'd1; wr_b1[done] = b1.done; } - group rd_x1<"static"=0> { + group rd_x1 { read_x1.right = x1.out; read_x1.left = x1.out; - rd_x1[done] = 1'd1; + rd_x1[done] = x1.done; // XXX: This is functionally wrong } } control { diff --git a/tests/passes/minimize-regs/par-while-liveness.expect b/tests/passes/minimize-regs/par-while-liveness.expect index c5e6ad8373..7d73ca6195 100644 --- a/tests/passes/minimize-regs/par-while-liveness.expect +++ b/tests/passes/minimize-regs/par-while-liveness.expect @@ -26,23 +26,10 @@ component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { le0 = std_le(4); le1 = std_le(4); le2 = std_le(4); + @generated comb_reg = std_reg(1); + @generated comb_reg0 = std_reg(1); } wires { - group cond0<"static"=0> { - cond0[done] = 1'd1; - le0.left = i0.out; - le0.right = const1.out; - } - group cond1<"static"=0> { - cond1[done] = 1'd1; - le1.left = i1.out; - le1.right = const4.out; - } - group cond2<"static"=0> { - cond2[done] = 1'd1; - le2.left = i0.out; - le2.right = const7.out; - } group let0<"static"=1> { i0.in = const0.out; i0.write_en = 1'd1; @@ -115,6 +102,27 @@ component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { i0.in = add2.out; upd8[done] = i0.done ? 1'd1; } + group cond00<"static"=1> { + le0.left = i0.out; + le0.right = const1.out; + comb_reg.in = le0.out; + comb_reg.write_en = 1'd1; + cond00[done] = comb_reg.done ? 1'd1; + } + group cond10<"static"=1> { + le1.left = i1.out; + le1.right = const4.out; + comb_reg0.in = le1.out; + comb_reg0.write_en = 1'd1; + cond10[done] = comb_reg0.done ? 1'd1; + } + group cond20<"static"=1> { + le2.left = i0.out; + le2.right = const7.out; + comb_reg.in = le2.out; + comb_reg.write_en = 1'd1; + cond20[done] = comb_reg.done ? 1'd1; + } } control { @@ -122,31 +130,49 @@ component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { par { seq { let0; - while le0.out with cond0 { - seq { - upd0; - upd1; - upd2; + seq { + cond00; + while comb_reg.out { + seq { + seq { + upd0; + upd1; + upd2; + } + cond00; + } } } } seq { let1; - while le1.out with cond1 { - seq { - upd3; - upd4; - upd5; + seq { + cond10; + while comb_reg0.out { + seq { + seq { + upd3; + upd4; + upd5; + } + cond10; + } } } } } let2; - while le2.out with cond2 { - seq { - upd6; - upd7; - upd8; + seq { + cond20; + while comb_reg.out { + seq { + seq { + upd6; + upd7; + upd8; + } + cond20; + } } } } diff --git a/tests/passes/minimize-regs/par-while-liveness.futil b/tests/passes/minimize-regs/par-while-liveness.futil index 8e533c6182..82194e3ece 100644 --- a/tests/passes/minimize-regs/par-while-liveness.futil +++ b/tests/passes/minimize-regs/par-while-liveness.futil @@ -1,4 +1,4 @@ -// -p minimize-regs -p dead-cell-removal +// -p well-formed -p remove-comb-groups -p minimize-regs -p dead-cell-removal import "primitives/std.lib"; component main() -> () { cells { @@ -31,18 +31,15 @@ component main() -> () { le2 = std_le(4); } wires { - group cond0<"static"=0> { - cond0[done] = 1'd1; + comb group cond0 { le0.left = i0.out; le0.right = const1.out; } - group cond1<"static"=0> { - cond1[done] = 1'd1; + comb group cond1 { le1.left = i1.out; le1.right = const4.out; } - group cond2<"static"=0> { - cond2[done] = 1'd1; + comb group cond2 { le2.left = i2.out; le2.right = const7.out; } diff --git a/tests/passes/minimize-regs/par-write.futil b/tests/passes/minimize-regs/par-write.futil index 735a0e1357..69ab94eff6 100644 --- a/tests/passes/minimize-regs/par-write.futil +++ b/tests/passes/minimize-regs/par-write.futil @@ -1,4 +1,4 @@ -// -p minimize-regs -p dead-cell-removal +// -p well-formed -p remove-comb-groups -p minimize-regs -p dead-cell-removal import "primitives/std.lib"; component main() -> () { diff --git a/tests/passes/minimize-regs/simple-liveness.expect b/tests/passes/minimize-regs/simple-liveness.expect index 43e77b4b2a..918dc1b927 100644 --- a/tests/passes/minimize-regs/simple-liveness.expect +++ b/tests/passes/minimize-regs/simple-liveness.expect @@ -24,7 +24,7 @@ component kernel(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { group rd_x<"static"=1> { read_x.right = before.out; read_x.left = before.out; - rd_x[done] = 1'd1; + rd_x[done] = before.done; } } diff --git a/tests/passes/minimize-regs/simple-liveness.futil b/tests/passes/minimize-regs/simple-liveness.futil index 92ce71eafd..21089f95c4 100644 --- a/tests/passes/minimize-regs/simple-liveness.futil +++ b/tests/passes/minimize-regs/simple-liveness.futil @@ -1,4 +1,4 @@ -// -p minimize-regs -p dead-cell-removal +// -p well-formed -p remove-comb-groups -p minimize-regs -p dead-cell-removal import "primitives/std.lib"; component kernel() -> () { cells { @@ -26,7 +26,7 @@ component kernel() -> () { group rd_x<"static"=1> { read_x.right = x.out; read_x.left = x.out; - rd_x[done] = 1'b1; + rd_x[done] = x.done; // XXX: This is functionally incorrect } } control { diff --git a/tests/passes/minimize-regs/thread-local.expect b/tests/passes/minimize-regs/thread-local.expect index 8f2aaad3d3..0ecb838190 100644 --- a/tests/passes/minimize-regs/thread-local.expect +++ b/tests/passes/minimize-regs/thread-local.expect @@ -15,7 +15,7 @@ component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { group rd_x { add_x.left = x.out; add_x.right = x.out; - rd_x[done] = 1'd1; + rd_x[done] = x.done; } group wr_y { y.in = 32'd4; @@ -25,7 +25,7 @@ component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { group rd_y { add_y.left = y.out; add_y.right = y.out; - rd_y[done] = 1'd1; + rd_y[done] = y.done; } } diff --git a/tests/passes/minimize-regs/thread-local.futil b/tests/passes/minimize-regs/thread-local.futil index 60a0f7b282..fffb7e846a 100644 --- a/tests/passes/minimize-regs/thread-local.futil +++ b/tests/passes/minimize-regs/thread-local.futil @@ -1,4 +1,4 @@ -// -p minimize-regs -p dead-cell-removal +// -p well-formed -p remove-comb-groups -p minimize-regs -p dead-cell-removal import "primitives/std.lib"; component main() -> () { @@ -19,7 +19,7 @@ component main() -> () { group rd_x { add_x.left = x.out; add_x.right = x.out; - rd_x[done] = 1'b1; + rd_x[done] = x.done; // XXX: This is functionally wrong } group wr_y { @@ -31,7 +31,7 @@ component main() -> () { group rd_y { add_y.left = y.out; add_y.right = y.out; - rd_y[done] = 1'b1; + rd_y[done] = y.done; // XXX: This is functionally wrong } } diff --git a/tests/passes/regressions/group-multi-drive.expect b/tests/passes/regressions/group-multi-drive.expect index 42572f8f20..644d5306ef 100644 --- a/tests/passes/regressions/group-multi-drive.expect +++ b/tests/passes/regressions/group-multi-drive.expect @@ -7,8 +7,8 @@ component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { } wires { done = fsm.out == 2'd2 ? 1'd1; - add.left = !r.done & go & (fsm.out == 2'd1 | fsm.out == 2'd0) ? 32'd1; - add.right = !r.done & go & (fsm.out == 2'd1 | fsm.out == 2'd0) ? r.out; + add.left = go & (!r.done & fsm.out == 2'd1 | fsm.out == 2'd0) ? 32'd1; + add.right = go & (!r.done & fsm.out == 2'd1 | fsm.out == 2'd0) ? r.out; fsm.clk = clk; fsm.in = fsm.out == 2'd2 ? 2'd0; fsm.in = fsm.out == 2'd0 & r.done & go ? 2'd1; @@ -16,8 +16,8 @@ component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { fsm.reset = reset; fsm.write_en = fsm.out == 2'd2 | r.done & go & fsm.out == 2'd1 | fsm.out == 2'd0 & r.done & go ? 1'd1; r.clk = clk; - r.in = !r.done & go & (fsm.out == 2'd1 | fsm.out == 2'd0) ? add.out; - r.write_en = !r.done & go & (fsm.out == 2'd1 | fsm.out == 2'd0) ? 1'd1; + r.in = go & (!r.done & fsm.out == 2'd1 | fsm.out == 2'd0) ? add.out; + r.write_en = go & (!r.done & fsm.out == 2'd1 | fsm.out == 2'd0) ? 1'd1; } control {} diff --git a/tests/passes/regressions/group-multi-drive.futil b/tests/passes/regressions/group-multi-drive.futil index 372c214fab..f0f1554881 100644 --- a/tests/passes/regressions/group-multi-drive.futil +++ b/tests/passes/regressions/group-multi-drive.futil @@ -1,4 +1,4 @@ -// -p all -d infer-static-timing -d static-timing -d well-formed -d papercut -d resource-sharing -p simplify-guards +// -p all -d infer-static-timing -d well-formed -d papercut -d resource-sharing -p simplify-guards import "primitives/std.lib"; component main() -> () { diff --git a/tests/passes/remove-comb-groups/comb-with-static.futil b/tests/passes/remove-comb-groups/comb-with-static.futil index db65adde1d..948b2e65eb 100644 --- a/tests/passes/remove-comb-groups/comb-with-static.futil +++ b/tests/passes/remove-comb-groups/comb-with-static.futil @@ -6,10 +6,9 @@ component main<"static"=1>(in: 32) -> () { r = std_reg(32); } wires { - group find_index { + comb group find_index { e0.left = in; e0.right = 32'd1; - find_index[done] = 1'd1; } group write { diff --git a/tests/passes/remove-comb-groups/multi-use.expect b/tests/passes/remove-comb-groups/multi-use.expect index 31840aabd9..4423f630e2 100644 --- a/tests/passes/remove-comb-groups/multi-use.expect +++ b/tests/passes/remove-comb-groups/multi-use.expect @@ -12,7 +12,12 @@ component main(in: 32, @go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: @generated comb_reg2 = std_reg(1); } wires { - group find_index<"static"=1> { + group write { + r.write_en = 1'd1; + r.in = 32'd1; + write[done] = r.done; + } + group find_index0<"static"=1> { e0.left = in; e0.right = 32'd1; e1.left = in; @@ -21,7 +26,6 @@ component main(in: 32, @go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: e2.right = 32'd2; e3.left = in; e3.right = 32'd3; - find_index[done] = comb_reg.done & comb_reg0.done & comb_reg1.done & comb_reg2.done ? 1'd1; comb_reg.in = e0.out; comb_reg.write_en = 1'd1; comb_reg0.in = e1.out; @@ -30,27 +34,44 @@ component main(in: 32, @go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: comb_reg1.write_en = 1'd1; comb_reg2.in = e3.out; comb_reg2.write_en = 1'd1; - } - group write { - r.write_en = 1'd1; - r.in = 32'd1; - write[done] = r.done; + find_index0[done] = comb_reg.done & comb_reg0.done & comb_reg1.done & comb_reg2.done ? 1'd1; } } control { par { - if comb_reg.out with find_index { - write; + seq { + find_index0; + if comb_reg.out { + write; + } + } + seq { + find_index0; + if comb_reg0.out { + write; + } } - if comb_reg0.out with find_index { - write; + seq { + find_index0; + if comb_reg1.out { + write; + } } - if comb_reg1.out with find_index { - write; + seq { + find_index0; + if comb_reg2.out { + write; + } } - if comb_reg2.out with find_index { - write; + seq { + find_index0; + while comb_reg.out { + seq { + write; + find_index0; + } + } } } } diff --git a/tests/passes/remove-comb-groups/multi-use.futil b/tests/passes/remove-comb-groups/multi-use.futil index 347b549a4b..82995b7a67 100644 --- a/tests/passes/remove-comb-groups/multi-use.futil +++ b/tests/passes/remove-comb-groups/multi-use.futil @@ -9,7 +9,7 @@ component main(in: 32) -> () { r = std_reg(32); } wires { - group find_index { + comb group find_index { e0.left = in; e0.right = 32'd1; @@ -21,8 +21,6 @@ component main(in: 32) -> () { e3.left = in; e3.right = 32'd3; - - find_index[done] = 1'd1; } group write { @@ -37,6 +35,7 @@ component main(in: 32) -> () { if e1.out with find_index { write; } if e2.out with find_index { write; } if e3.out with find_index { write; } + while e0.out with find_index { write; } } } } diff --git a/tests/passes/resource-sharing/cond-port.expect b/tests/passes/resource-sharing/cond-port.expect index 3e0d5993c3..91dea43fcc 100644 --- a/tests/passes/resource-sharing/cond-port.expect +++ b/tests/passes/resource-sharing/cond-port.expect @@ -5,13 +5,9 @@ component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { gt1 = std_gt(32); x_0 = std_reg(32); y_0 = std_reg(1); + @generated comb_reg = std_reg(1); } wires { - group cond0 { - cond0[done] = 1'd1; - gt0.left = x_0.out; - gt0.right = 32'd2; - } group let0 { x_0.in = 32'd1; x_0.write_en = 1'd1; @@ -29,14 +25,24 @@ component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { x_0.in = 32'd10; upd0[done] = x_0.done ? 1'd1; } + group cond00<"static"=1> { + gt0.left = x_0.out; + gt0.right = 32'd2; + comb_reg.in = gt0.out; + comb_reg.write_en = 1'd1; + cond00[done] = comb_reg.done ? 1'd1; + } } control { seq { let0; let1; - if gt0.out with cond0 { - upd0; + seq { + cond00; + if comb_reg.out { + upd0; + } } } } diff --git a/tests/passes/resource-sharing/cond-port.futil b/tests/passes/resource-sharing/cond-port.futil index 68ae2764a4..397c52ec1e 100644 --- a/tests/passes/resource-sharing/cond-port.futil +++ b/tests/passes/resource-sharing/cond-port.futil @@ -1,4 +1,4 @@ -// -p resource-sharing +// -p remove-comb-groups -p resource-sharing import "primitives/std.lib"; component main() -> () { cells { @@ -8,8 +8,7 @@ component main() -> () { y_0 = std_reg(1); } wires { - group cond0 { - cond0[done] = 1'd1; + comb group cond0 { gt1.left = x_0.out; gt1.right = 32'd2; } diff --git a/tests/passes/compile-control/compile-if-static.futil b/tests/passes/static-timing/compile-if-static.futil similarity index 100% rename from tests/passes/compile-control/compile-if-static.futil rename to tests/passes/static-timing/compile-if-static.futil diff --git a/tests/passes/compile-control/compile-par-static.futil b/tests/passes/static-timing/compile-par-static.futil similarity index 100% rename from tests/passes/compile-control/compile-par-static.futil rename to tests/passes/static-timing/compile-par-static.futil diff --git a/tests/passes/compile-control/compile-seq-static.futil b/tests/passes/static-timing/compile-seq-static.futil similarity index 100% rename from tests/passes/compile-control/compile-seq-static.futil rename to tests/passes/static-timing/compile-seq-static.futil diff --git a/tests/passes/compile-control/compile-while-static.futil b/tests/passes/static-timing/compile-while-static.futil similarity index 100% rename from tests/passes/compile-control/compile-while-static.futil rename to tests/passes/static-timing/compile-while-static.futil diff --git a/tests/passes/tdcc/branch-at-start.expect b/tests/passes/tdcc/branch-at-start.expect new file mode 100644 index 0000000000..edbf0f76a4 --- /dev/null +++ b/tests/passes/tdcc/branch-at-start.expect @@ -0,0 +1,24 @@ +======== main:tdcc ========= +0: + one[go] = is_valid & is_even.out ? 1'd1; + two[go] = is_valid & !is_even.out ? 1'd1; +1: + one[go] = !one[done] ? 1'd1; + one[go] = one[done] & is_valid & is_even.out ? 1'd1; + two[go] = one[done] & is_valid & !is_even.out ? 1'd1; +2: + one[go] = two[done] & is_valid & is_even.out ? 1'd1; + two[go] = !two[done] ? 1'd1; + two[go] = two[done] & is_valid & !is_even.out ? 1'd1; +3: + +transitions: + (0, 1): is_valid & is_even.out + (0, 2): is_valid & !is_even.out + (0, 3): !is_valid + (1, 1): one[done] & is_valid & is_even.out + (1, 2): one[done] & is_valid & !is_even.out + (1, 3): one[done] & !is_valid + (2, 1): two[done] & is_valid & is_even.out + (2, 2): two[done] & is_valid & !is_even.out + (2, 3): two[done] & !is_valid diff --git a/tests/passes/tdcc/branch-at-start.futil b/tests/passes/tdcc/branch-at-start.futil new file mode 100644 index 0000000000..78714f0b4c --- /dev/null +++ b/tests/passes/tdcc/branch-at-start.futil @@ -0,0 +1,30 @@ +// -x tdcc:dump-fsm -d post-opt -d lower -b none +import "primitives/core.futil"; +component main(is_valid: 1) -> () { + cells { + is_even = std_reg(1); + is_not_zero = std_reg(1); + r = std_reg(32); + } + wires { + group one { + is_not_zero.in = 1'd0; + is_not_zero.write_en = 1'd0; + one[done] = is_not_zero.done; + } + group two { + r.in = 32'd0; + r.write_en = 1'd0; + two[done] = r.done; + } + } + control { + while is_valid { + if is_even.out { + one; + } else { + two; + } + } + } +} diff --git a/tests/passes/tdcc/compile-if.expect b/tests/passes/tdcc/compile-if.expect new file mode 100644 index 0000000000..c6dbc150ee --- /dev/null +++ b/tests/passes/tdcc/compile-if.expect @@ -0,0 +1,16 @@ +======== main:tdcc ========= +0: + cond0[go] = !cond0[done] ? 1'd1; + true[go] = cond0[done] & t.out ? 1'd1; + false[go] = cond0[done] & !t.out ? 1'd1; +1: + true[go] = !true[done] ? 1'd1; +2: + false[go] = !false[done] ? 1'd1; +3: + +transitions: + (0, 1): cond0[done] & t.out + (0, 2): cond0[done] & !t.out + (1, 3): true[done] + (2, 3): false[done] diff --git a/tests/passes/compile-control/compile-if.futil b/tests/passes/tdcc/compile-if.futil similarity index 87% rename from tests/passes/compile-control/compile-if.futil rename to tests/passes/tdcc/compile-if.futil index 400976b400..339fe72b6a 100644 --- a/tests/passes/compile-control/compile-if.futil +++ b/tests/passes/tdcc/compile-if.futil @@ -1,4 +1,4 @@ -// -p compile-control +// -x tdcc:dump-fsm -d post-opt -d lower -b none import "primitives/std.lib"; @@ -22,10 +22,9 @@ component main() -> () { false[done] = f.done; } - group cond { + comb group cond { lt.left = 1'b1; lt.right = 1'b0; - cond[done] = 1'b1; } } diff --git a/tests/passes/tdcc/compile-par.expect b/tests/passes/tdcc/compile-par.expect new file mode 100644 index 0000000000..6415054391 --- /dev/null +++ b/tests/passes/tdcc/compile-par.expect @@ -0,0 +1,15 @@ +======== main:tdcc ========= +0: + A[go] = !A[done] ? 1'd1; + par[go] = A[done] ? 1'd1; +1: + par[go] = !par[done] ? 1'd1; + B[go] = par[done] ? 1'd1; +2: + B[go] = !B[done] ? 1'd1; +3: + +transitions: + (0, 1): A[done] + (1, 2): par[done] + (2, 3): B[done] diff --git a/tests/passes/compile-control/compile-par.futil b/tests/passes/tdcc/compile-par.futil similarity index 80% rename from tests/passes/compile-control/compile-par.futil rename to tests/passes/tdcc/compile-par.futil index 986003aad9..1c0a3c3c1b 100644 --- a/tests/passes/compile-control/compile-par.futil +++ b/tests/passes/tdcc/compile-par.futil @@ -1,4 +1,4 @@ -// -p compile-control +// -x tdcc:dump-fsm -d post-opt -d lower -b none import "primitives/std.lib"; @@ -30,6 +30,10 @@ component main() -> () { } control { - par { A; B; C; } + seq { + A; + par { A; B; C; } + B; + } } } diff --git a/tests/passes/tdcc/compile-seq.expect b/tests/passes/tdcc/compile-seq.expect new file mode 100644 index 0000000000..a4cda039ac --- /dev/null +++ b/tests/passes/tdcc/compile-seq.expect @@ -0,0 +1,15 @@ +======== main:tdcc ========= +0: + A[go] = !A[done] ? 1'd1; + B[go] = A[done] ? 1'd1; +1: + B[go] = !B[done] ? 1'd1; + C[go] = B[done] ? 1'd1; +2: + C[go] = !C[done] ? 1'd1; +3: + +transitions: + (0, 1): A[done] + (1, 2): B[done] + (2, 3): C[done] diff --git a/tests/passes/compile-control/compile-seq.futil b/tests/passes/tdcc/compile-seq.futil similarity index 90% rename from tests/passes/compile-control/compile-seq.futil rename to tests/passes/tdcc/compile-seq.futil index 224cd543d9..cdf968f07f 100644 --- a/tests/passes/compile-control/compile-seq.futil +++ b/tests/passes/tdcc/compile-seq.futil @@ -1,4 +1,4 @@ -// -p compile-control +// -x tdcc:dump-fsm -d post-opt -d lower -b none import "primitives/std.lib"; diff --git a/tests/passes/tdcc/compile-while.expect b/tests/passes/tdcc/compile-while.expect new file mode 100644 index 0000000000..974471ed13 --- /dev/null +++ b/tests/passes/tdcc/compile-while.expect @@ -0,0 +1,18 @@ +======== main:tdcc ========= +0: + cond0[go] = !cond0[done] ? 1'd1; + do_add[go] = cond0[done] & comb_reg.out ? 1'd1; +1: + do_add[go] = !do_add[done] ? 1'd1; + cond0[go] = do_add[done] ? 1'd1; +2: + do_add[go] = cond0[done] & comb_reg.out ? 1'd1; + cond0[go] = !cond0[done] ? 1'd1; +3: + +transitions: + (0, 1): cond0[done] & comb_reg.out + (0, 3): cond0[done] & !comb_reg.out + (1, 2): do_add[done] + (2, 1): cond0[done] & comb_reg.out + (2, 3): cond0[done] & !comb_reg.out diff --git a/tests/passes/compile-control/compile-while.futil b/tests/passes/tdcc/compile-while.futil similarity index 65% rename from tests/passes/compile-control/compile-while.futil rename to tests/passes/tdcc/compile-while.futil index 9513ef905c..f7917c8bc2 100644 --- a/tests/passes/compile-control/compile-while.futil +++ b/tests/passes/tdcc/compile-while.futil @@ -1,4 +1,4 @@ -// -p compile-control +// -x tdcc:dump-fsm -d post-opt -d lower -b none import "primitives/std.lib"; @@ -6,19 +6,21 @@ component main() -> () { cells { add = std_add(32); lt = std_lt(32); + r = std_reg(32); } wires { group do_add { add.right = 32'd4; add.left = 32'd4; - do_add[done] = 1'b1; + r.in = add.out; + r.write_en = 1'd1; + do_add[done] = r.done; } - group cond { + comb group cond { lt.right = 32'd5; lt.left = 32'd1; - cond[done] = 1'b1; } } diff --git a/tests/passes/tdcc/seq-with-same-done.expect b/tests/passes/tdcc/seq-with-same-done.expect new file mode 100644 index 0000000000..bafd386558 --- /dev/null +++ b/tests/passes/tdcc/seq-with-same-done.expect @@ -0,0 +1,11 @@ +======== main:tdcc ========= +0: + one[go] = !one[done] ? 1'd1; + two[go] = one[done] ? 1'd1; +1: + two[go] = !two[done] ? 1'd1; +2: + +transitions: + (0, 1): one[done] + (1, 2): two[done] diff --git a/tests/passes/tdcc/seq-with-same-done.futil b/tests/passes/tdcc/seq-with-same-done.futil new file mode 100644 index 0000000000..6c3d1b8fa1 --- /dev/null +++ b/tests/passes/tdcc/seq-with-same-done.futil @@ -0,0 +1,27 @@ +// -x tdcc:dump-fsm -d post-opt -d lower -b none + +import "primitives/std.lib"; +component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { + cells { + r = std_reg(1); + } + wires { + group one { + r.in = 1'd0; + r.write_en = 1'd1; + one[done] = r.done ? 1'd1; + } + group two { + r.in = 1'd1; + r.write_en = 1'd1; + two[done] = r.done ? 1'd1; + } + } + + control { + seq { + one; + two; + } + } +} diff --git a/tests/passes/tdcc/while-if.expect b/tests/passes/tdcc/while-if.expect new file mode 100644 index 0000000000..657f3b5206 --- /dev/null +++ b/tests/passes/tdcc/while-if.expect @@ -0,0 +1,31 @@ +======== main:tdcc ========= +0: + one[go] = !one[done] ? 1'd1; + two[go] = one[done] & is_not_zero.out & is_even.out ? 1'd1; + three[go] = one[done] & is_not_zero.out & !is_even.out ? 1'd1; + four[go] = one[done] & !is_not_zero.out ? 1'd1; +1: + two[go] = !two[done] ? 1'd1; + two[go] = two[done] & is_not_zero.out & is_even.out ? 1'd1; + three[go] = two[done] & is_not_zero.out & !is_even.out ? 1'd1; + four[go] = two[done] & !is_not_zero.out ? 1'd1; +2: + two[go] = three[done] & is_not_zero.out & is_even.out ? 1'd1; + three[go] = !three[done] ? 1'd1; + three[go] = three[done] & is_not_zero.out & !is_even.out ? 1'd1; + four[go] = three[done] & !is_not_zero.out ? 1'd1; +3: + four[go] = !four[done] ? 1'd1; +4: + +transitions: + (0, 1): one[done] & is_not_zero.out & is_even.out + (0, 2): one[done] & is_not_zero.out & !is_even.out + (0, 3): one[done] & !is_not_zero.out + (1, 1): two[done] & is_not_zero.out & is_even.out + (1, 2): two[done] & is_not_zero.out & !is_even.out + (1, 3): two[done] & !is_not_zero.out + (2, 1): three[done] & is_not_zero.out & is_even.out + (2, 2): three[done] & is_not_zero.out & !is_even.out + (2, 3): three[done] & !is_not_zero.out + (3, 4): four[done] diff --git a/tests/passes/tdcc/while-if.futil b/tests/passes/tdcc/while-if.futil new file mode 100644 index 0000000000..c6c369e48b --- /dev/null +++ b/tests/passes/tdcc/while-if.futil @@ -0,0 +1,44 @@ +// -x tdcc:dump-fsm -d post-opt -d lower -b none +import "primitives/core.futil"; +component main() -> () { + cells { + is_even = std_reg(1); + is_not_zero = std_reg(1); + r = std_reg(32); + } + wires { + group one { + is_not_zero.in = 1'd0; + is_not_zero.write_en = 1'd0; + one[done] = is_not_zero.done; + } + group two { + r.in = 32'd0; + r.write_en = 1'd0; + two[done] = r.done; + } + group three { + r.in = 32'd0; + r.write_en = 1'd0; + three[done] = r.done; + } + group four { + is_even.in = 1'd0; + is_even.write_en = 1'd0; + four[done] = is_even.done; + } + } + control { + seq { + one; + while is_not_zero.out { + if is_even.out { + two; + } else { + three; + } + } + four; + } + } +} diff --git a/tests/passes/unsharing/continuous.expect b/tests/passes/unsharing/continuous.expect index 8bf36f33d2..b40d3f1e8c 100644 --- a/tests/passes/unsharing/continuous.expect +++ b/tests/passes/unsharing/continuous.expect @@ -68,7 +68,8 @@ component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { seq { set_flag; zero; - if flag.out with cond { + cond; + if flag.out { seq { one; } diff --git a/tests/passes/unsharing/continuous.futil b/tests/passes/unsharing/continuous.futil index fcd54131e8..5fbde95c29 100644 --- a/tests/passes/unsharing/continuous.futil +++ b/tests/passes/unsharing/continuous.futil @@ -2,89 +2,90 @@ import "primitives/core.futil"; component main() -> () { - cells { - r = std_reg(32); - x = std_reg(32); - y = std_reg(32); - add2 = std_add(32); - flag = std_reg(1); - other = std_reg(32); - } - - wires { - group zero { - r.in = 32'd0; - r.write_en = 1'd1; - x.write_en = 1'd1; - x.in = 32'd0; - y.write_en = 1'd1; - y.in = 32'd0; - zero[done] = r.done; - } + cells { + r = std_reg(32); + x = std_reg(32); + y = std_reg(32); + add2 = std_add(32); + flag = std_reg(1); + other = std_reg(32); + } - group one { - r.in = 32'd1; - r.write_en = 1'd1; - x.write_en = 1'd1; - x.in = 32'd1; - y.write_en = 1'd1; - y.in = 32'd1; - one[done] = r.done; - } + wires { + group zero { + r.in = 32'd0; + r.write_en = 1'd1; + x.write_en = 1'd1; + x.in = 32'd0; + y.write_en = 1'd1; + y.in = 32'd0; + zero[done] = r.done; + } - group cond { - flag.in = flag.out ? 1'd0; - flag.in = !flag.out ? 1'd1; - flag.write_en = 1'd1; - cond[done] = flag.done; - } + group one { + r.in = 32'd1; + r.write_en = 1'd1; + x.write_en = 1'd1; + x.in = 32'd1; + y.write_en = 1'd1; + y.in = 32'd1; + one[done] = r.done; + } - group final { - add2.left = 32'd154; - add2.right = r.out; - other.in = add2.out; - other.write_en = 1'd1; - final[done] = other.done; - } + group cond { + flag.in = flag.out ? 1'd0; + flag.in = !flag.out ? 1'd1; + flag.write_en = 1'd1; + cond[done] = flag.done; + } - group alt { - r.in = 32'd99; - r.write_en = 1'd1; - alt[done] = r.done; - } + group final { + add2.left = 32'd154; + add2.right = r.out; + other.in = add2.out; + other.write_en = 1'd1; + final[done] = other.done; + } - group five { - r.in = 32'd5; - r.write_en = 1'd1; - y.write_en = 1'd1; - y.in = 32'd5; - x.write_en = 1'd1; - x.in = 32'd5; - five[done] = r.done; - } + group alt { + r.in = 32'd99; + r.write_en = 1'd1; + alt[done] = r.done; + } - group set_flag { - flag.in = 1'd1; - flag.write_en = 1'd1; - set_flag[done] = flag.done; - } + group five { + r.in = 32'd5; + r.write_en = 1'd1; + y.write_en = 1'd1; + y.in = 32'd5; + x.write_en = 1'd1; + x.in = 32'd5; + five[done] = r.done; + } - r.in = 1'd0 ? 32'd1; + group set_flag { + flag.in = 1'd1; + flag.write_en = 1'd1; + set_flag[done] = flag.done; } - control { - seq{ - set_flag; - zero; - if flag.out with cond { - seq {one;} - } else { - seq {five;} - } + r.in = 1'd0 ? 32'd1; + } + + control { + seq{ + set_flag; + zero; + cond; + if flag.out { + seq {one;} + } else { + seq {five;} + } - final; - alt; + final; + alt; - } } + } } diff --git a/tests/passes/unsharing/unsharing.expect b/tests/passes/unsharing/unsharing.expect index 1a0b4b630f..84b95cc1fb 100644 --- a/tests/passes/unsharing/unsharing.expect +++ b/tests/passes/unsharing/unsharing.expect @@ -63,7 +63,8 @@ component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { seq { set_flag; zero; - if flag.out with cond { + cond; + if flag.out { seq { one; } diff --git a/tests/passes/unsharing/unsharing.futil b/tests/passes/unsharing/unsharing.futil index fda020399d..411e4b9b9a 100644 --- a/tests/passes/unsharing/unsharing.futil +++ b/tests/passes/unsharing/unsharing.futil @@ -2,83 +2,84 @@ import "primitives/core.futil"; component main() -> () { - cells { - r = std_reg(32); - x = std_reg(32); - y = std_reg(32); - add2 = std_add(32); - flag = std_reg(1); - other = std_reg(32); - } + cells { + r = std_reg(32); + x = std_reg(32); + y = std_reg(32); + add2 = std_add(32); + flag = std_reg(1); + other = std_reg(32); + } - wires { - group zero { - r.in = 32'd0; - r.write_en = 1'd1; - x.write_en = 1'd1; - x.in = 32'd0; - y.write_en = 1'd1; - y.in = 32'd0; - zero[done] = r.done; - } + wires { + group zero { + r.in = 32'd0; + r.write_en = 1'd1; + x.write_en = 1'd1; + x.in = 32'd0; + y.write_en = 1'd1; + y.in = 32'd0; + zero[done] = r.done; + } - group one { - r.in = 32'd1; - r.write_en = 1'd1; - x.write_en = 1'd1; - x.in = 32'd1; - one[done] = r.done; - } + group one { + r.in = 32'd1; + r.write_en = 1'd1; + x.write_en = 1'd1; + x.in = 32'd1; + one[done] = r.done; + } - group cond { - flag.in = flag.out ? 1'd0; - flag.in = !flag.out ? 1'd1; - flag.write_en = 1'd1; - cond[done] = flag.done; - } + group cond { + flag.in = flag.out ? 1'd0; + flag.in = !flag.out ? 1'd1; + flag.write_en = 1'd1; + cond[done] = flag.done; + } - group final { - add2.left = 32'd154; - add2.right = r.out; - other.in = add2.out; - other.write_en = 1'd1; - final[done] = other.done; - } + group final { + add2.left = 32'd154; + add2.right = r.out; + other.in = add2.out; + other.write_en = 1'd1; + final[done] = other.done; + } - group alt { - r.in = 32'd99; - r.write_en = 1'd1; - alt[done] = r.done; - } + group alt { + r.in = 32'd99; + r.write_en = 1'd1; + alt[done] = r.done; + } - group five { - r.in = 32'd5; - r.write_en = 1'd1; - y.write_en = 1'd1; - y.in = 32'd5; - five[done] = r.done; - } + group five { + r.in = 32'd5; + r.write_en = 1'd1; + y.write_en = 1'd1; + y.in = 32'd5; + five[done] = r.done; + } - group set_flag { - flag.in = 1'd1; - flag.write_en = 1'd1; - set_flag[done] = flag.done; - } + group set_flag { + flag.in = 1'd1; + flag.write_en = 1'd1; + set_flag[done] = flag.done; } + } - control { - seq{ - set_flag; - zero; - if flag.out with cond { - seq {one;} - } else { - seq {five;} - } + control { + seq{ + set_flag; + zero; + cond; + if flag.out { + seq {one;} + } else { + seq {five;} + } - final; - alt; + final; + alt; - } } + } } diff --git a/tests/passes/unsharing/while.expect b/tests/passes/unsharing/while.expect index c0b99a3bfd..baa8706f54 100644 --- a/tests/passes/unsharing/while.expect +++ b/tests/passes/unsharing/while.expect @@ -53,7 +53,8 @@ component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { seq { set_flag; zero; - while flag.out with cond { + cond; + while flag.out { seq { one; } diff --git a/tests/passes/unsharing/while.futil b/tests/passes/unsharing/while.futil index 2503eff50c..e864f205af 100644 --- a/tests/passes/unsharing/while.futil +++ b/tests/passes/unsharing/while.futil @@ -2,71 +2,72 @@ import "primitives/core.futil"; component main() -> () { - cells { - r = std_reg(32); - add2 = std_add(32); - flag = std_reg(1); - other = std_reg(32); - } + cells { + r = std_reg(32); + add2 = std_add(32); + flag = std_reg(1); + other = std_reg(32); + } - wires { - group zero { - r.in = 32'd0; - r.write_en = 1'd1; - zero[done] = r.done; - } + wires { + group zero { + r.in = 32'd0; + r.write_en = 1'd1; + zero[done] = r.done; + } - group one { - r.in = 32'd1; - r.write_en = 1'd1; - one[done] = r.done; - } + group one { + r.in = 32'd1; + r.write_en = 1'd1; + one[done] = r.done; + } - group cond { - flag.in = flag.out ? 1'd0; - flag.in = !flag.out ? 1'd1; - flag.write_en = 1'd1; - cond[done] = flag.done; - } + group cond { + flag.in = flag.out ? 1'd0; + flag.in = !flag.out ? 1'd1; + flag.write_en = 1'd1; + cond[done] = flag.done; + } - group final { - add2.left = 32'd154; - add2.right = r.out; - other.in = add2.out; - other.write_en = 1'd1; - final[done] = other.done; - } + group final { + add2.left = 32'd154; + add2.right = r.out; + other.in = add2.out; + other.write_en = 1'd1; + final[done] = other.done; + } - group alt { - r.in = 32'd99; - r.write_en = 1'd1; - alt[done] = r.done; - } + group alt { + r.in = 32'd99; + r.write_en = 1'd1; + alt[done] = r.done; + } - group five { - r.in = 32'd5; - r.write_en = 1'd1; - five[done] = r.done; - } + group five { + r.in = 32'd5; + r.write_en = 1'd1; + five[done] = r.done; + } - group set_flag { - flag.in = 1'd1; - flag.write_en = 1'd1; - set_flag[done] = flag.done; - } + group set_flag { + flag.in = 1'd1; + flag.write_en = 1'd1; + set_flag[done] = flag.done; } + } - control { - seq{ - set_flag; - zero; - while flag.out with cond { - seq {one;} - } - final; - five; - alt; + control { + seq{ + set_flag; + zero; + cond; + while flag.out { + seq {one;} + } + final; + five; + alt; - } } + } } diff --git a/tools/flag-compare.sh b/tools/flag-compare.sh index 3daee0a891..ee0abdc90b 100755 --- a/tools/flag-compare.sh +++ b/tools/flag-compare.sh @@ -3,10 +3,10 @@ # Usage: # ./flag-compare.sh -set -euf -o pipefail +set -uf -o pipefail -flag1='-p all -d static-timing' -flag2='-p all' +flag1='-p dead-group-removal -p all' +flag2='-p dead-group-removal -p all' file=$1 data=$2 diff --git a/tools/vim/futil/syntax/futil.vim b/tools/vim/futil/syntax/futil.vim index 4151e03dcc..2a65c1929a 100644 --- a/tools/vim/futil/syntax/futil.vim +++ b/tools/vim/futil/syntax/futil.vim @@ -3,7 +3,7 @@ if exists("b:current_syntax") endif " Numbers -syn match futilConstant "\v<[0-9]+('d[0-9]+)?>" +syn match futilConstant "\v<[0-9]+('(d|b|x|o)[0-9]+)?>" hi link futilConstant Constant " String literals for attributes @@ -44,6 +44,9 @@ hi link futilPortParam Type syn match futilArrow '->' nextgroup=futilPorts skipwhite skipnl hi link futilArrow futilOperator +" Modifiers for components, groups, primitives +syn keyword futilModifier comb +hi link futilModifier Operator " Highlight holes syn keyword futilHole go done