Skip to content

Commit

Permalink
Implement combinational groups and primitives (#635)
Browse files Browse the repository at this point in the history
* simplify parsing names with attributes attached

* initial implementation of comb groups

* top-down-cc requires cond ports to be @stable

* remove-comb-groups updates control now

* make `with` option in `if` and `while`

* fix top down compilation and disable static-timing pass

* compiler workflow should not build interpreter

* fix some doc comments

* add comb primitive syntax

* make combinational primitives with `comb`

* Implement `Schedule::realize_schedule`

* some cleanup and register top-down-st

* remove with_guard from calculate_states_recur

* fix bug in back edge generation of top-down-cc while FSM

* Fix bug in tdcc FSM generation, implement dead-group-removal pass, and fix misc bugs in passes

* add CombGroup to calyx-py and fix frontends to generate comb groups

* remove generated calyx files for exp frontend

* flag-compare runs dead-group-removal before runnning the compiler

* doc comment for dead-cell-removal

* extensive comments for tdcc

* disable broken passes and disable static-timing testing

* disable compile-control tests

* update some tests

* fix bug introduced in tdcc

* mechanism to pass cli option to passes

* tests for tdcc dump FSMs

* fix more tests

* update more tests

* update correctness tests

* systolic tests update

* another tdcc FSM gen test

* tdcc: handle branch at start

* tdcc: visualize final state

* dead-group-removal: tests

* papercut: check if cond port is combinational

* fix tcam implementation

* fix mrxl

* update test for live_range_analysis

* emit comb groups in circt backend

* update test

* build dahlia with getHeaders

* fmt

* update interpreter to work (maybe) with comb groups

* fix while bug

* update tests and add timeout

* make tdcc happy

* clean up warnings

* clippy

* reserved cells test is not longer relevant

* fmt

Co-authored-by: Griffin Berlstein <griffin@berlste.in>
  • Loading branch information
rachitnigam and EclecticGriffin authored Sep 7, 2021
1 parent 53c5017 commit 533d611
Show file tree
Hide file tree
Showing 208 changed files with 2,811 additions and 3,417 deletions.
5 changes: 2 additions & 3 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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: |
Expand Down Expand Up @@ -228,7 +228,6 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: build
args: --all

- name: Test
run: |
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,5 @@ __pycache__
!.vscode/settings.json
!.vscode/launch.json
!.vscode/tasks.json

./tests/correctness/exp/*.futil
103 changes: 68 additions & 35 deletions calyx-py/calyx/gen_exp.py
Original file line number Diff line number Diff line change
@@ -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.
"""
Expand Down Expand Up @@ -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"))),
Expand All @@ -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]:
Expand All @@ -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))
Expand Down Expand Up @@ -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"),
Expand Down Expand Up @@ -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
),
Expand Down Expand Up @@ -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
Expand All @@ -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(
Expand Down Expand Up @@ -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,
)
Expand All @@ -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=[
Expand Down Expand Up @@ -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")),
]
Expand Down Expand Up @@ -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")),
],
)
]
Expand All @@ -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.
Expand Down Expand Up @@ -547,18 +575,21 @@ 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)]
)
]

divide_and_conquer = []
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

Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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),
Expand Down
16 changes: 14 additions & 2 deletions calyx-py/calyx/py_ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -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};"
Expand All @@ -180,14 +180,26 @@ 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}",
[c.doc() for c in self.connections],
)


@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
Expand Down
27 changes: 19 additions & 8 deletions calyx/src/analysis/control_ports.rs
Original file line number Diff line number Diff line change
@@ -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<ir::Id, Vec<RRC<ir::Port>>>,
}
Expand Down Expand Up @@ -36,21 +39,25 @@ 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);
}
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, .. })
Expand All @@ -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 }
}
}
Loading

0 comments on commit 533d611

Please sign in to comment.