Skip to content

Commit

Permalink
merge of axi generator renaming
Browse files Browse the repository at this point in the history
  • Loading branch information
nathanielnrn committed Dec 31, 2024
1 parent 0a02e0b commit 4a21275
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 49 deletions.
4 changes: 3 additions & 1 deletion fud2/scripts/axi.rhai
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ fn wrapper_setup(e) {
let generator_path = if dynamic == "true" {
"$calyx-base/yxi/axi-calyx/dynamic_axi_generator.py"
} else {
"$calyx-base/yxi/axi-calyx/axi-generator.py"
"$calyx-base/yxi/axi-calyx/axi_generator.py"
};
e.config_var_or("axi-generator", "axi.generator", generator_path);
e.config_var_or("python", "python", "python3");
Expand All @@ -37,6 +37,8 @@ fn wrapper_setup(e) {
// Define a simple `combine` rule that just concatenates any numer of files.
e.rule("combine", "cat $in > $out");

// Removes imports and `external` primitive blocks added by passes by removing
// everything up until the first line containing `component main`
e.rule(
"remove-imports",
"sed '/^import/d' $in > $out",
Expand Down
2 changes: 1 addition & 1 deletion fud2/tests/snapshots/tests__test@plan_axi-wrapped.snap
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ yxi = $calyx-base/target/debug/yxi
rule yxi
command = $yxi -l $calyx-base $in > $out

axi-generator = $calyx-base/yxi/axi-calyx/axi-generator.py
axi-generator = $calyx-base/yxi/axi-calyx/axi_generator.py
python = python3
rule gen-axi
command = $python $axi-generator $in > $out
Expand Down
8 changes: 4 additions & 4 deletions runt.toml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ cmd = """

[[tests]]
name = "fud2 yxi invocation"
paths = ["yxi/tests/ref-mems-vec-add.futil"]
paths = ["yxi/tests/yxi-tool/ref-mems-vec-add.futil"]
cmd = """
fud2 {} --from calyx --to yxi
"""
Expand Down Expand Up @@ -604,14 +604,14 @@ fud e {} -s verilog.cycle_limit 500 \
name = "calyx-py read-write-compute AXI wrapper generation"
paths = ["yxi/tests/axi/read-compute-write/*.yxi"]
cmd = """
python3 yxi/axi-calyx/axi-generator.py {}
python3 yxi/axi-calyx/axi_generator.py {}
"""

[[tests]]
name = "calyx-py dynamic AXI wrapper generation"
paths = ["yxi/tests/axi/dynamic/*.yxi"]
cmd = """
python3 yxi/axi-calyx/dynamic-axi-generator.py {}
python3 yxi/axi-calyx/dynamic_axi_generator.py {}
"""

#Ignore fud2 stderr for now due to ninja nondeterminism
Expand Down Expand Up @@ -681,4 +681,4 @@ make --silent -B TEST_PATH={} COCOTB_LOG_LEVEL=CRITICAL |\
make clean && \
rm -f results.xml
"""
"""
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# The *original* axi generator which implement a read-compute-write sequence
# to get data in and out of the computational kernel.
# A `dynamic_axi_generator` also exists

from calyx.builder import (
Builder,
add_comp_ports,
Expand Down
98 changes: 55 additions & 43 deletions yxi/axi-calyx/dynamic_axi_generator.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
from calyx.builder import (
Builder,
add_comp_ports,
invoke,
par,
while_,
if_
)
# Implements an AXI controller which dynamically reads and writes data
# in and out of the computational kernel as needed. Compare with the
# read-compute-write implementation in the original `axi_generator`.

from calyx.builder import Builder, add_comp_ports, invoke, par, while_, if_
from axi_controller_generator import add_control_subordinate
from typing import Literal
from math import log2, ceil
Expand All @@ -21,11 +18,12 @@
width_key = "data_width"
size_key = "total_size"
name_key = "name"
#This returns an array based on dimensions of memory
# This returns an array based on dimensions of memory
address_width_key = "idx_sizes"
type_key = "memory_type"

#TODO (nathanielnrn): Should we make these comb groups?

# TODO (nathanielnrn): Should we make these comb groups?
def add_address_translator(prog, mem):
address_width = mem[address_width_key][0]
data_width = mem[width_key]
Expand All @@ -36,13 +34,14 @@ def add_address_translator(prog, mem):
translator_output = [("axi_address", 64)]
add_comp_ports(address_translator, translator_inputs, translator_output)

#Cells
#XRT expects 64 bit address.
address_mult = address_translator.const_mult(64, width_in_bytes(data_width), f"mul_{name}")
# Cells
# XRT expects 64 bit address.
address_mult = address_translator.const_mult(
64, width_in_bytes(data_width), f"mul_{name}"
)
pad_input_addr = address_translator.pad(address_width, 64, f"pad_input_addr")


#Assignment
# Assignment
with address_translator.continuous:
pad_input_addr.in_ = address_translator.this()["calyx_mem_addr"]
address_mult.in_ = pad_input_addr.out
Expand Down Expand Up @@ -113,12 +112,14 @@ def _add_m_to_s_address_channel(prog, mem, prefix: Literal["AW", "AR"]):
xhandshake_occurred.write_en = (~xhandshake_occurred.out) @ 1

# Drive output signals for transfer
m_to_s_address_channel.this()[f"{x}ADDR"] = m_to_s_address_channel.this()["axi_address"]
m_to_s_address_channel.this()[f"{x}ADDR"] = m_to_s_address_channel.this()[
"axi_address"
]
# This is taken from mem size, we assume the databus width is the size
# of our memory cell and that width is a power of 2
# TODO(nathanielnrn): convert to binary instead of decimal
m_to_s_address_channel.this()[f"{x}SIZE"] = width_xsize(mem[width_key])
#Dynamic accesses only need asingle transfer per transcation
# Dynamic accesses only need asingle transfer per transcation
m_to_s_address_channel.this()[f"{x}LEN"] = 0
m_to_s_address_channel.this()[f"{x}BURST"] = 1 # Must be INCR for XRT
# Required by spec, we hardcode to privileged, non-secure, data access
Expand All @@ -130,12 +131,10 @@ def _add_m_to_s_address_channel(prog, mem, prefix: Literal["AW", "AR"]):
bt_reg.write_en = 1
do_x_transfer.done = bt_reg.out


# ARLEN must be between 0-255, make sure to subtract 1 from yxi
# size when assigning to ARLEN
# assert mem[size_key] < 256, "Memory size must be less than 256"


m_to_s_address_channel.control += [
par(
invoke(bt_reg, in_in=0),
Expand Down Expand Up @@ -227,7 +226,7 @@ def add_read_channel(prog, mem):
# Control
invoke_n_RLAST = invoke(n_RLAST, in_in=1)
# invoke_bt_reg = invoke(bt_reg, in_in=0)

# Could arguably get rid of this while loop for the dynamic verison, but this
# matches nicely with non dynamic version and conforms to spec,
# and will be easier to extend to variable length dynamic transfers in the future
Expand All @@ -245,11 +244,7 @@ def add_write_channel(prog, mem):
name = mem[name_key]
# Inputs/Outputs
write_channel = prog.component(f"m_write_channel_{name}")
channel_inputs = [
("ARESETn", 1),
("WREADY", 1),
("write_data", data_width)
]
channel_inputs = [("ARESETn", 1), ("WREADY", 1), ("write_data", data_width)]
# TODO(nathanielnrn): We currently assume WDATA is the same width as the
# memory. This limits throughput many AXI data busses are much wider
# i.e., 512 bits.
Expand Down Expand Up @@ -294,7 +289,7 @@ def add_write_channel(prog, mem):
write_channel.this()["WLAST"] = 1

# done after handshake
#TODO(nathanielnrn): Perhaps we can combine between handshake_occurred and bt_reg
# TODO(nathanielnrn): Perhaps we can combine between handshake_occurred and bt_reg
bt_reg.in_ = (wvalid.out & WREADY) @ 1
bt_reg.in_ = ~(wvalid.out & WREADY) @ 0
bt_reg.write_en = 1
Expand Down Expand Up @@ -350,6 +345,7 @@ def add_bresp_channel(prog, mem):
# Control
bresp_channel.control += [invoke(bt_reg, in_in=0), block_transfer]


def add_read_controller(prog, mem):
add_arread_channel(prog, mem)
add_read_channel(prog, mem)
Expand Down Expand Up @@ -377,18 +373,21 @@ def add_read_controller(prog, mem):
(f"ARBURST", 2),
(f"ARPROT", 3),
(f"RREADY", 1),
#sent out to axi_dyn_mem
# sent out to axi_dyn_mem
(f"read_data", data_width),
]

add_comp_ports(read_controller, read_controller_inputs, read_controller_outputs)

#Cells
simple_ar_channel = read_controller.cell(f"ar_channel_{name}", prog.get_component(f"m_ar_channel_{name}"))
simple_read_channel = read_controller.cell(f"read_channel_{name}", prog.get_component(f"m_read_channel_{name}"))
# Cells
simple_ar_channel = read_controller.cell(
f"ar_channel_{name}", prog.get_component(f"m_ar_channel_{name}")
)
simple_read_channel = read_controller.cell(
f"read_channel_{name}", prog.get_component(f"m_read_channel_{name}")
)
# No groups necesarry


# Control
# Invokes

Expand Down Expand Up @@ -422,6 +421,7 @@ def add_read_controller(prog, mem):
simple_read_invoke,
]


def add_write_controller(prog, mem):
add_awwrite_channel(prog, mem)
add_write_channel(prog, mem)
Expand Down Expand Up @@ -455,13 +455,18 @@ def add_write_controller(prog, mem):

add_comp_ports(write_controller, write_controller_inputs, write_controller_outputs)

#Cells
simple_aw_channel = write_controller.cell(f"aw_channel_{name}", prog.get_component(f"m_aw_channel_{name}"))
simple_write_channel = write_controller.cell(f"write_channel_{name}", prog.get_component(f"m_write_channel_{name}"))
simple_bresp_channel = write_controller.cell(f"bresp_channel_{name}", prog.get_component(f"m_bresp_channel_{name}"))
# Cells
simple_aw_channel = write_controller.cell(
f"aw_channel_{name}", prog.get_component(f"m_aw_channel_{name}")
)
simple_write_channel = write_controller.cell(
f"write_channel_{name}", prog.get_component(f"m_write_channel_{name}")
)
simple_bresp_channel = write_controller.cell(
f"bresp_channel_{name}", prog.get_component(f"m_bresp_channel_{name}")
)
# No groups necesarry


# Control
# Invokes
simple_aw_invoke = invoke(
Expand Down Expand Up @@ -498,6 +503,7 @@ def add_write_controller(prog, mem):
simple_bresp_invoke,
]


def add_axi_dyn_mem(prog, mem):
address_width = mem[address_width_key][0]
data_width = mem[width_key]
Expand All @@ -506,7 +512,7 @@ def add_axi_dyn_mem(prog, mem):
prog.import_("primitives/memories/dyn.futil")
axi_dyn_mem = prog.component(f"axi_dyn_mem_{name}")
# Inputs/Outputs
dyn_mem_inputs =[
dyn_mem_inputs = [
("addr0", address_width, [("write_together", 1), "data"]),
("content_en", 1, [("write_together", 1), ("go", 1)]),
("write_en", 1, [("write_together", 2)]),
Expand Down Expand Up @@ -606,13 +612,12 @@ def add_axi_dyn_mem(prog, mem):
out_WDATA=this_component["WDATA"],
out_BREADY=this_component["BREADY"],
)

axi_dyn_mem.control += [
latch_write_en,
if_(write_en_reg.out, write_controller_invoke, read_controller_invoke)
]



# NOTE: Unlike the channel functions, this can expect multiple mems
def add_wrapper_comp(prog, mems):
Expand Down Expand Up @@ -746,7 +751,10 @@ def add_wrapper_comp(prog, mems):

# TODO: Don't think these need to be marked external, but we
# we need to raise them at some point form original calyx program
axi_mem = wrapper_comp.cell(f"axi_dyn_mem_{mem_name}", prog.get_component(f"axi_dyn_mem_{mem_name}"))
axi_mem = wrapper_comp.cell(
f"axi_dyn_mem_{mem_name}", prog.get_component(f"axi_dyn_mem_{mem_name}")
)

# Wires

with wrapper_comp.continuous:
Expand All @@ -759,7 +767,9 @@ def add_wrapper_comp(prog, mems):
# Connect wrapper ports with axi_dyn_mem ports

# Read controller portion inputs
axi_mem["ARESETn"] = wrapper_comp.this()[f"{mem_name}_ARESETn"] #note that both styles work
axi_mem["ARESETn"] = wrapper_comp.this()[
f"{mem_name}_ARESETn"
] # note that both styles work
# wrapper_comp.this()[f"{mem_name}_ARESETn"] = axi_mem["ARESETn"] #note that both styles work
axi_mem.ARREADY = wrapper_comp.this()[f"{mem_name}_ARREADY"]
axi_mem.RVALID = wrapper_comp.this()[f"{mem_name}_RVALID"]
Expand Down Expand Up @@ -876,7 +886,9 @@ def check_mems_wellformed(mems):
mem[width_key]
).is_integer(), "Width must be a power of 2 to be correctly described by xSIZE"
assert mem[size_key] > 0, "Memory size must be greater than 0"
assert mem[type_key] == "Dynamic", "Only dynamic memories are currently supported for dynamic axi"
assert (
mem[type_key] == "Dynamic"
), "Only dynamic memories are currently supported for dynamic axi"


if __name__ == "__main__":
Expand All @@ -894,4 +906,4 @@ def check_mems_wellformed(mems):
yxifile = open(yxi_filename)
yxi = json.load(yxifile)
mems = yxi["memories"]
build().emit()
build().emit()

0 comments on commit 4a21275

Please sign in to comment.