Skip to content

Commit

Permalink
Piezo, PIFOTree: static option! (#1783)
Browse files Browse the repository at this point in the history
  • Loading branch information
anshumanmohan committed Nov 20, 2023
1 parent ac322ed commit 666d06e
Show file tree
Hide file tree
Showing 9 changed files with 762 additions and 26 deletions.
59 changes: 56 additions & 3 deletions calyx-py/calyx/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -523,17 +523,23 @@ def bitwise_flip_reg(self, reg, cellname=None):
not_group.done = reg.done
return not_group

def incr(self, reg, val=1, signed=False, cellname=None):
def incr(self, reg, val=1, signed=False, cellname=None, static=False):
"""Inserts wiring into `self` to perform `reg := reg + val`."""
cellname = cellname or f"{reg.name}_incr"
width = reg.infer_width_reg()
add_cell = self.add(width, cellname, signed)
with self.group(f"{cellname}_group") as incr_group:
group = (
self.static_group(f"{cellname}_group", 1)
if static
else self.group(f"{cellname}_group")
)
with group as incr_group:
add_cell.left = reg.out
add_cell.right = const(width, val)
reg.write_en = 1
reg.in_ = add_cell.out
incr_group.done = reg.done
if not static:
incr_group.done = reg.done
return incr_group

def decr(self, reg, val=1, signed=False, cellname=None):
Expand Down Expand Up @@ -847,6 +853,32 @@ def invoke(cell: CellBuilder, **kwargs) -> ast.Invoke:
)


def static_invoke(cell: CellBuilder, **kwargs) -> ast.Invoke:
"""Build a `static invoke` control statement.
The keyword arguments should have the form `in_*`, `out_*`, or `ref_*`, where
`*` is the name of an input port, output port, or ref cell on the invoked cell.
"""
return ast.StaticInvoke(
cell._cell.id,
[
(k[3:], ExprBuilder.unwrap(v))
for (k, v) in kwargs.items()
if k.startswith("in_")
],
[
(k[4:], ExprBuilder.unwrap(v))
for (k, v) in kwargs.items()
if k.startswith("out_")
],
[
(k[4:], CellBuilder.unwrap_id(v))
for (k, v) in kwargs.items()
if k.startswith("ref_")
],
)


class ControlBuilder:
"""Wraps control statements for convenient construction."""

Expand Down Expand Up @@ -1256,6 +1288,15 @@ def par(*args) -> ast.ParComp:
return ast.ParComp([as_control(x) for x in args])


def static_par(*args) -> ast.StaticParComp:
"""Build a static parallel composition of control expressions.
Each argument will become its own parallel arm in the resulting composition.
So `par([a,b])` becomes `par {seq {a; b;}}` while `par(a, b)` becomes `par {a; b;}`.
"""
return ast.StaticParComp([as_control(x) for x in args])


def seq(*args) -> ast.SeqComp:
"""Build a sequential composition of control expressions.
Expand All @@ -1268,6 +1309,18 @@ def seq(*args) -> ast.SeqComp:
return ast.SeqComp([as_control(x) for x in args])


def static_seq(*args) -> ast.StaticSeqComp:
"""Build a static sequential composition of control expressions.
Prefer use of python list syntax over this function. Use only when not directly
modifying the control program with the `+=` operator.
Each argument will become its own sequential arm in the resulting composition.
So `seq([a,b], c)` becomes `seq { seq {a; b;} c }` while `seq(a, b, c)` becomes `seq
{a; b; c;}`.
"""
return ast.StaticSeqComp([as_control(x) for x in args])


def add_comp_params(comp: ComponentBuilder, input_ports: List, output_ports: List):
"""
Adds `input_ports`/`output_ports` as inputs/outputs to comp.
Expand Down
8 changes: 6 additions & 2 deletions calyx-py/test/correctness/pifo.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def invoke_subqueue(queue_cell, cmd, value, ans, err) -> cb.invoke:
)


def insert_pifo(prog, name, queue_l, queue_r, boundary, stats=None):
def insert_pifo(prog, name, queue_l, queue_r, boundary, stats=None, static=False):
"""Inserts the component `pifo` into the program.
The PIFO achieves a 50/50 split between two "flows" or "kinds".
Expand Down Expand Up @@ -301,7 +301,11 @@ def insert_pifo(prog, name, queue_l, queue_r, boundary, stats=None):
# Increment the length and also
# tell the stats component what flow we pushed.
len_incr,
cb.invoke(stats, in_flow=flow.out),
(
cb.static_invoke(stats, in_flow=flow.out)
if static
else cb.invoke(stats, in_flow=flow.out)
),
],
),
],
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from calyx import queue_util


def insert_stats(prog, name):
def insert_stats(prog, name, static=False):
"""Inserts a stats component called `name` into the program `prog`.
It maintains:
Expand All @@ -20,9 +20,13 @@ def insert_stats(prog, name):
When invoked, the component reads the flow index and increments
`count_0_sto` or `count_1_sto` as appropriate.
If `static` is False, this is a dynamic component.
Otherwise, it is a static component with delay 1.
"""

stats: cb.ComponentBuilder = prog.component(name)
stats: cb.ComponentBuilder = prog.component(name, latency=1 if static else None)

flow = stats.input("flow", 1)
stats.output("count_0", 32)
stats.output("count_1", 32)
Expand All @@ -32,24 +36,36 @@ def insert_stats(prog, name):
count_1_sto = stats.reg("count_1_sto", 32)

# Wiring to increment the appropriate register.
count_0_incr = stats.incr(count_0_sto)
count_1_incr = stats.incr(count_1_sto)
count_0_incr = stats.incr(count_0_sto, static=static)
count_1_incr = stats.incr(count_1_sto, static=static)

# Equality checks.
flow_eq_0 = stats.eq_use(flow, 0)
flow_eq_1 = stats.eq_use(flow, 1)
# The rest of the logic varies depending on whether the component is static.

with stats.continuous:
stats.this().count_0 = count_0_sto.out
stats.this().count_1 = count_1_sto.out
# If not static, we can use comb groups.
if not static:
flow_eq_0 = stats.eq_use(flow, 0)

# The main logic.
stats.control += [
cb.par(
cb.if_with(flow_eq_0, [count_0_incr]),
cb.if_with(flow_eq_1, [count_1_incr]),
),
]
with stats.continuous:
stats.this().count_0 = count_0_sto.out
stats.this().count_1 = count_1_sto.out

stats.control += cb.par(
cb.if_with(flow_eq_0, count_0_incr, count_1_incr),
)

# If static, we need to use continuous assignments and not comb groups.
else:
eq_cell = stats.eq(1, "eq_cell")

with stats.continuous:
stats.this().count_0 = count_0_sto.out
stats.this().count_1 = count_1_sto.out
eq_cell.left = flow
eq_cell.right = 0

stats.control += cb.static_par(
cb.static_if(eq_cell.out, count_0_incr, count_1_incr),
)

return stats

Expand Down Expand Up @@ -139,16 +155,18 @@ def insert_main(prog, dataplane, controller, stats_component):
]


def build():
"""Top-level function to build the program."""
def build(static=False):
"""Top-level function to build the program.
The `static` flag determines whether the program is static or dynamic.
"""
prog = cb.Builder()
stats_component = insert_stats(prog, "stats")
stats_component = insert_stats(prog, "stats", static)
fifo_purple = fifo.insert_fifo(prog, "fifo_purple")
fifo_tangerine = fifo.insert_fifo(prog, "fifo_tangerine")
pifo_red = pifo.insert_pifo(prog, "pifo_red", fifo_purple, fifo_tangerine, 100)
fifo_blue = fifo.insert_fifo(prog, "fifo_blue")
pifo_root = pifo.insert_pifo(
prog, "pifo_root", pifo_red, fifo_blue, 200, stats_component
prog, "pifo_root", pifo_red, fifo_blue, 200, stats_component, static
)
# The root PIFO will take a stats component by reference.
dataplane = queue_call.insert_runner(prog, pifo_root, "dataplane", stats_component)
Expand Down
Loading

0 comments on commit 666d06e

Please sign in to comment.