Skip to content

Commit

Permalink
PIFO Trees: Telemetry (#1736)
Browse files Browse the repository at this point in the history
  • Loading branch information
anshumanmohan authored Nov 16, 2023
1 parent bf07990 commit 4fe0a9f
Show file tree
Hide file tree
Showing 7 changed files with 943 additions and 1,212 deletions.
2 changes: 2 additions & 0 deletions calyx-py/calyx/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -422,9 +422,11 @@ def unary_use(self, input, cell, groupname=None):
"""Accepts a cell that performs some computation on value `input`.
Creates a combinational group that wires up the cell with this port.
Returns the cell and the combintational group.
comb group `groupname` {
`cell.name`.in = `input`;
}
Returns handles to the cell and the combinational group.
"""
groupname = groupname or f"{cell.name}_group"
Expand Down
125 changes: 119 additions & 6 deletions calyx-py/calyx/queue_call.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
def insert_main(prog, queue):
"""Inserts the component `main` into the program.
This will be used to `invoke` the component `queue` and feed it a list of commands.
This component will directly interface with external memories and will
finally populate an external memory with the answers.
"""
main: cb.ComponentBuilder = prog.component("main")

Expand All @@ -20,7 +22,7 @@ def insert_main(prog, queue):
# the value at `i` is pushed if the command at `i` is `2`.
# - output: a list of answers, reflecting any pops or peeks from the queue.
#
# The user-facing interface of the `queue` component is:
# The user-facing interface of the `queue` component is assumed to be:
# - input `cmd`
# where each command is a 2-bit unsigned integer, with the following format:
# `0`: pop
Expand All @@ -35,12 +37,9 @@ def insert_main(prog, queue):
values = main.seq_mem_d1("values", 32, queue_util.MAX_CMDS, 32, is_external=True)
ans_mem = main.seq_mem_d1("ans_mem", 32, queue_util.MAX_CMDS, 32, is_external=True)

# The two components we'll use:
# We'll invoke the queue component, which takes two inputs by reference
# and one input directly.
queue = main.cell("myqueue", queue)

# We will use the `invoke` method to call the `queue` component.
# The queue component takes two inputs by reference and one input directly.
# The two `ref` inputs:
err = main.reg("err", 1) # A flag to indicate an error
ans = main.reg("ans", 32) # A memory to hold the answer of a pop or peek

Expand Down Expand Up @@ -103,3 +102,117 @@ def insert_main(prog, queue):
],
),
]

return main


def insert_runner(prog, queue, name, stats_component):
"""Inserts the component `name` into the program.
This will be used to `invoke` the component `queue` and feed it one command.
This component is designed to be invoked by some other component, and does not
directly interface with external memories.
"""
assert (
name != "main"
), "This method is not designed for the creation of `main`-style components."

runner: cb.ComponentBuilder = prog.component(name)

# We take a stats component by reference,
# but all we'll really do with it is pass it to the queue component.
stats = runner.cell("stats_runner", stats_component, is_ref=True)

# We'll invoke the queue component.
queue = runner.cell("myqueue", queue)

# The user-facing interface of this component is captured by a number
# of items that are passed to this component by reference.
#
# - 1: `commands`, a list of commands.
# Where each command is a 2-bit unsigned integer with the following format:
# `0`: pop
# `1`: peek
# `2`: push
# - 2: `values`, a list of values.
# Where each value is a 32-bit unsigned integer.
# The value at `i` is pushed if the command at `i` is `2`.
# - 3: `has_ans`, a 1-bit unsigned integer.
# We raise/lower this to indicate whether the queue had a reply to the command.
# - 4: `component_ans`, a 32-bit unsigned integer.
# We put in this register the answer to the command, if any.
# - 5: `component_err`, a 1-bit unsigned integer.
# We raise/lower it to indicates whether an error occurred
# and the queue should no longer be invoked.
#
# The user-facing interface of the `queue` component is assumed to be:
# - input `cmd`
# where each command is a 2-bit unsigned integer, with the following format:
# `0`: pop
# `1`: peek
# `2`: push
# - input `value`
# which is a 32-bit unsigned integer. If `cmd` is `2`, push this value.
# - ref register `ans`, into which the result of a pop or peek is written.
# - ref register `err`, which is raised if an error occurs.

# Our memories and registers, all of which are passed to us by reference.
commands = runner.seq_mem_d1("commands", 2, queue_util.MAX_CMDS, 32, is_ref=True)
values = runner.seq_mem_d1("values", 32, queue_util.MAX_CMDS, 32, is_ref=True)
has_ans = runner.reg("has_ans", 1, is_ref=True)
ans = runner.reg("component_ans", 32, is_ref=True)
err = runner.reg("component_err", 1, is_ref=True)

i = runner.reg("i", 32) # The index of the command we're currently processing
cmd = runner.reg("command", 2) # The command we're currently processing
value = runner.reg("value", 32) # The value we're currently processing

incr_i = runner.incr(i) # i++
cmd_le_1 = runner.le_use(cmd.out, 1) # cmd <= 1, meaning cmd is pop or peek

# Wiring to perform `cmd := commands[i]` and `value := values[i]`.
read_cmd = runner.mem_read_seq_d1(commands, i.out, "read_cmd_phase1")
write_cmd_to_reg = runner.mem_write_seq_d1_to_reg(commands, cmd, "write_cmd_phase2")
read_value = runner.mem_read_seq_d1(values, i.out, "read_value")
write_value_to_reg = runner.mem_write_seq_d1_to_reg(
values, value, "write_value_to_reg"
)

# Wiring to raise/lower flags and compute a negation.
raise_has_ans = runner.reg_store(has_ans, 1, "raise_has_ans")
lower_has_ans = runner.reg_store(has_ans, 0, "lower_has_ans")
not_err = runner.not_use(err.out)

# Wiring that raises `err` iff `i = MAX_CMDS`.
check_if_out_of_cmds, _ = runner.eq_store_in_reg(
i.out, cb.const(32, queue_util.MAX_CMDS), "i_eq_MAX_CMDS", 32, err
)

runner.control += [
read_cmd,
write_cmd_to_reg, # `cmd := commands[i]`
read_value,
write_value_to_reg, # `value := values[i]`
cb.invoke( # Invoke the queue.
queue,
in_cmd=cmd.out,
in_value=value.out,
ref_ans=ans,
ref_err=err,
ref_stats=stats,
),
# We're back from the invoke, and it's time for some post-mortem analysis.
cb.if_with(
not_err, # If there was no error
[
cb.if_with(
cmd_le_1, # If the command was a pop or peek
[raise_has_ans], # then raise the `has_ans` flag
[lower_has_ans], # else lower the `has_ans` flag
),
],
),
incr_i, # Increment the command index
check_if_out_of_cmds, # If we're out of commands, raise `err`
]

return runner
Loading

0 comments on commit 4fe0a9f

Please sign in to comment.