Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add calyx-py AXI generator read channel #1856

Merged
merged 107 commits into from
Jan 23, 2024
Merged
Changes from 106 commits
Commits
Show all changes
107 commits
Select commit Hold shift + click to select a range
e7b39e7
init commit of hardcoded axi wrapper for a 'main' kernel
nathanielnrn Oct 23, 2023
fd3a0e5
add axi-reads-calix
nathanielnrn Nov 20, 2023
cb38574
hook up inputs to channels in the wrapper. tbd if this works
nathanielnrn Nov 23, 2023
203387f
Working calyx verison of AR and R
nathanielnrn Nov 27, 2023
534ecc0
Track output of compiled calyx read channel
nathanielnrn Nov 27, 2023
46f7d7a
update gitignore to get rid of sim_build and other cocotb artifacts
nathanielnrn Dec 3, 2023
cabba39
Working make files for running cocotb tests
nathanielnrn Dec 3, 2023
9fbe03e
Add xID signals for cocotb compatability
nathanielnrn Dec 3, 2023
74d9d8f
Fix prefix issue on cocotb axi test bench
nathanielnrn Dec 3, 2023
e5b84be
commit to repro 'make WAVES=1' cocotb error from axi-reads-calyx.futil
nathanielnrn Dec 6, 2023
6729dac
axi-reads patch
nathanielnrn Dec 13, 2023
cdef8b6
sync debug
rachitnigam Dec 13, 2023
a1d8e5a
Add txn_len initialization to 16 in calyx program
nathanielnrn Dec 18, 2023
51f91d3
AXI Read fixed to get to read channel start
nathanielnrn Dec 19, 2023
4d80ca7
Add integer byte conversion for tests on Calyx AXI testharness
nathanielnrn Dec 19, 2023
7725cc3
WIP get reads to work. Add incr_curr_addr group
nathanielnrn Dec 19, 2023
11c3bc3
remove .fst from tracking
nathanielnrn Dec 20, 2023
736264b
Add more data to testbench to make waveform viewing easier
nathanielnrn Dec 20, 2023
2c7c241
Reads seem to be terminating correctly at RLAST
nathanielnrn Dec 20, 2023
21d13f9
AR transfers seem to work, valid is high for 1 cycle
nathanielnrn Dec 20, 2023
7312254
Unreduced axi-reads-calyx.futil
nathanielnrn Dec 21, 2023
e2e848f
Cocotb testbench now passes
nathanielnrn Dec 21, 2023
4fed24a
Formatted and passing axi-read-tests
nathanielnrn Dec 21, 2023
6710f3f
Reduce and comment axi-reads-calyx.futil
nathanielnrn Dec 21, 2023
f30b274
remove axi-reads.v from being tracked
nathanielnrn Dec 21, 2023
6a8d6a6
add a todo
nathanielnrn Dec 21, 2023
d084d3a
add required ARPROT signal. This is hardcoded to be priviliged
nathanielnrn Dec 21, 2023
06db156
rename directories to yxi/axi-calyx
nathanielnrn Dec 21, 2023
2cf9ed5
initial commit of axi-writes-calyx, a copy of axi-reads-calyx
nathanielnrn Dec 21, 2023
8cc10a0
WIP axi writes
nathanielnrn Dec 21, 2023
8b99289
rename directories
nathanielnrn Dec 21, 2023
6328e0c
WIP imlpementing writes
nathanielnrn Dec 21, 2023
b3567d1
add testing for writes, note makefile is overwritten so now tests wri…
nathanielnrn Dec 21, 2023
fe8f284
passing axi writes and testing
nathanielnrn Dec 23, 2023
a13cd60
init commit of hardcoded axi wrapper for a 'main' kernel
nathanielnrn Oct 23, 2023
505ae00
add axi-reads-calix
nathanielnrn Nov 20, 2023
3f6f129
hook up inputs to channels in the wrapper. tbd if this works
nathanielnrn Nov 23, 2023
ba5e1a4
Working calyx verison of AR and R
nathanielnrn Nov 27, 2023
d93d852
Track output of compiled calyx read channel
nathanielnrn Nov 27, 2023
6fbc3ca
Working make files for running cocotb tests
nathanielnrn Dec 3, 2023
a6426a6
Add xID signals for cocotb compatability
nathanielnrn Dec 3, 2023
d2db261
Fix prefix issue on cocotb axi test bench
nathanielnrn Dec 3, 2023
3aa9722
commit to repro 'make WAVES=1' cocotb error from axi-reads-calyx.futil
nathanielnrn Dec 6, 2023
d813517
axi-reads patch
nathanielnrn Dec 13, 2023
58a9de6
sync debug
rachitnigam Dec 13, 2023
cfd40d6
Add txn_len initialization to 16 in calyx program
nathanielnrn Dec 18, 2023
b26f0ea
AXI Read fixed to get to read channel start
nathanielnrn Dec 19, 2023
96b9975
Add integer byte conversion for tests on Calyx AXI testharness
nathanielnrn Dec 19, 2023
03e93a8
WIP get reads to work. Add incr_curr_addr group
nathanielnrn Dec 19, 2023
4a1291f
remove .fst from tracking
nathanielnrn Dec 20, 2023
3abee21
Add more data to testbench to make waveform viewing easier
nathanielnrn Dec 20, 2023
94efbc9
Reads seem to be terminating correctly at RLAST
nathanielnrn Dec 20, 2023
7179097
AR transfers seem to work, valid is high for 1 cycle
nathanielnrn Dec 20, 2023
2f841e8
Unreduced axi-reads-calyx.futil
nathanielnrn Dec 21, 2023
618f9b6
Cocotb testbench now passes
nathanielnrn Dec 21, 2023
7866017
Formatted and passing axi-read-tests
nathanielnrn Dec 21, 2023
c926cf8
Reduce and comment axi-reads-calyx.futil
nathanielnrn Dec 21, 2023
4120ee3
remove axi-reads.v from being tracked
nathanielnrn Dec 21, 2023
8c29f01
add a todo
nathanielnrn Dec 21, 2023
14066a1
add required ARPROT signal. This is hardcoded to be priviliged
nathanielnrn Dec 21, 2023
ecb5626
rename directories to yxi/axi-calyx
nathanielnrn Dec 21, 2023
d40a066
initial commit of axi-writes-calyx, a copy of axi-reads-calyx
nathanielnrn Dec 21, 2023
297e60e
WIP axi writes
nathanielnrn Dec 21, 2023
e6cc577
rename directories
nathanielnrn Dec 21, 2023
681c316
WIP imlpementing writes
nathanielnrn Dec 21, 2023
22926ab
add testing for writes, note makefile is overwritten so now tests wri…
nathanielnrn Dec 21, 2023
04200f5
passing axi writes and testing
nathanielnrn Dec 23, 2023
e24d114
Work on full AXI wrapper, reads and compute works
nathanielnrn Jan 8, 2024
b212c56
cleaned up combined futil and tests
nathanielnrn Jan 10, 2024
6fd6697
delete axi-reads* which is subsumed by axi-combined
nathanielnrn Jan 10, 2024
9d4fc22
add axi-combined-tests.py
nathanielnrn Jan 10, 2024
e9e6317
remove axi-writes as it is subsumed by axi-combined
nathanielnrn Jan 10, 2024
5c51038
formatting
nathanielnrn Jan 10, 2024
b6437d1
Merge branch 'axi-writes' of github.com:calyxir/calyx into axi-writes
nathanielnrn Jan 10, 2024
4fbdcc4
Update yxi/axi-calyx/axi-combined-calyx.futil
nathanielnrn Jan 11, 2024
624e0d5
formatting
nathanielnrn Jan 10, 2024
923fc5e
Merge branch 'axi-writes' of github.com:calyxir/calyx into axi-writes
nathanielnrn Jan 11, 2024
c7e3839
Merge branch 'axi-writes' of github.com:calyxir/calyx into axi-writes
nathanielnrn Jan 11, 2024
9fd9cd3
add sim.sh which goes from calyx to running tests
nathanielnrn Jan 11, 2024
53c53d4
simplify valid.in signals
nathanielnrn Jan 11, 2024
31b6986
WIP: replace groups with reg invokes
nathanielnrn Jan 11, 2024
eb9a817
Merge branch 'axi-writes' of github.com:calyxir/calyx into axi-writes
nathanielnrn Jan 11, 2024
02631c0
add python file that enables waveform (vcd/fst) generation
nathanielnrn Jan 12, 2024
c7869e9
formatting
nathanielnrn Jan 12, 2024
880b1a8
simplify valid.in signals
nathanielnrn Jan 11, 2024
ab2a31a
WIP: replace groups with reg invokes
nathanielnrn Jan 11, 2024
4c1d9fb
Replaces register-init groups with invokes
nathanielnrn Jan 12, 2024
ff5c2c9
Merge branch 'main' into axi-wrapper-opts
nathanielnrn Jan 16, 2024
3e7fe70
Merge branch 'axi-wrapper-opts' of github.com:calyxir/calyx into axi-…
nathanielnrn Jan 16, 2024
80c509a
Formatting of invokes
nathanielnrn Jan 17, 2024
01b7bd0
Merge branch 'main' into axi-wrapper-opts
nathanielnrn Jan 18, 2024
60462e6
Replace reg groups with invokes in main
nathanielnrn Jan 18, 2024
141c203
Modify tests to account for base address != 0
nathanielnrn Jan 18, 2024
a8ec4b6
Separate base-address calyx-mem-address dependency
nathanielnrn Jan 18, 2024
b5d1053
Merge branch 'main' into address-fixes-axi
nathanielnrn Jan 18, 2024
391c03d
move incrs into par block
nathanielnrn Jan 18, 2024
e463d47
iitial axi-generator commit
nathanielnrn Jan 11, 2024
6b7073b
WIP get arread-channel working
nathanielnrn Jan 11, 2024
9c911d8
Finished ARREAD channel.
nathanielnrn Jan 16, 2024
4e6522c
Create m_to_s_address_channel for {AR,AW} channels
nathanielnrn Jan 17, 2024
5cc562d
WIP: Add read channel
nathanielnrn Jan 17, 2024
516a244
Finished read_channel. Still need to fix #1850
nathanielnrn Jan 17, 2024
401aad0
Finished read channels
nathanielnrn Jan 18, 2024
b278242
Remove read channel to break up into multiple PRs
nathanielnrn Jan 19, 2024
5be47b8
Merge branch 'main' into py-axi-generator-address-channels
nathanielnrn Jan 21, 2024
f1a054f
Add read channel back
nathanielnrn Jan 19, 2024
87012db
Merge branch 'main' into py-axi-generator-read-channel
nathanielnrn Jan 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
327 changes: 327 additions & 0 deletions yxi/axi-calyx/axi-generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,327 @@
from calyx.builder import (
Builder,
add_comp_params,
invoke,
while_with,
par,
while_,
)
from typing import Literal
from math import log2, ceil
import json

# In general, ports to the wrapper are uppercase, internal registers are lower case.

yxi_input = """
{
"toplevel": "main",
"memories": [
{
"name": "A0",
"width": 32,
"size": 8
},
{
"name": "B0",
"width": 32,
"size": 8
},
{
"name": "v0",
"width": 32,
"size": 1
}
]
}
"""

yxi = json.loads(yxi_input)
mems = yxi["memories"]


def add_arread_channel(prog, mem):
_add_m_to_s_address_channel(prog, mem, "AR")


def add_awwrite_channel(prog, mem):
awwrite_channel = _add_m_to_s_address_channel(prog, mem, "AW")
max_transfers = awwrite_channel.reg("max_transfers", 8, is_ref=True)

# TODO(nathanielnrn): We eventually want to move beyond
# the implicit 1 transaction that is the size of the memory
# How should we store this?
# Recall this goes to write channel as number of transfers it expectes to do before
# setting WLAST high
with awwrite_channel.get_group("do_aw_transfer"):
max_transfers.in_ = mem["size"] - 1
max_transfers.write_en = 1


def _add_m_to_s_address_channel(prog, mem, prefix: Literal["AW", "AR"]):
"""Adds a manager to subordinate
channel to the program. Uses `prefix` to name the channels
appropriately. Expected to be either "AW" or "AR."
Contains all of the channels shared between AW and AR channels.

Returns a component builder in case there are additional
cells/wires/groups that need to be added to the component.
"""

assert prefix in ["AW", "AR"], "Prefix must be either AW or AR."

# Following Arm's notation of denoting `xVALID` and `xREADY` signals
# `x` stands for the prefix of the channel, i.e. `AW` or `AR`
lc_x = prefix.lower()
x = prefix
# Inputs/outputs
m_to_s_address_channel = prog.component(f"m_{lc_x}_channel")
channel_inputs = [("ARESETn", 1), (f"{x}READY", 1)]
channel_outputs = [
(f"{x}VALID", 1),
(f"{x}ADDR", 64),
(f"{x}SIZE", 3), # bytes used in transfer
(f"{x}LEN", 8), # number of transfers in transaction
(f"{x}BURST", 2), # for XRT should be tied to 2'b01 for WRAP burst
(f"{x}PROT", 3), # tied to be priviliged, nonsecure, data access request
]
add_comp_params(m_to_s_address_channel, channel_inputs, channel_outputs)

# Cells
xvalid = m_to_s_address_channel.reg(f"{lc_x}valid", 1)
xvalid_was_high = m_to_s_address_channel.reg(f"{lc_x}valid_was_high", 1)
base_addr = m_to_s_address_channel.reg("base_addr", 64, is_ref=True)
xlen = m_to_s_address_channel.reg(f"{lc_x}len", 8)

# Number of txns we want to occur before m_arread_channel is done
# TODO: parameterize
txn_n = m_to_s_address_channel.const("txn_n", 32, 1)
txn_count = m_to_s_address_channel.reg("txn_count", 32)
txn_adder = m_to_s_address_channel.add(32, "txn_adder")

# Need to put block_transfer register here to avoid combinational loops
bt_reg = m_to_s_address_channel.reg("bt_reg", 1)

# Wires
with m_to_s_address_channel.continuous:
m_to_s_address_channel.this()[f"{x}VALID"] = xvalid.out

# Groups
# Responsible for asserting ARVALID, and deasserting it a cycle after the handshake.
# This is necesarry because of the way transitions between groups work.
# See #1828 https://github.com/calyxir/calyx/issues/1828
with m_to_s_address_channel.group(f"do_{lc_x}_transfer") as do_x_transfer:
xREADY = m_to_s_address_channel.this()[f"{x}READY"]
# TODO: Can we simplify this?
# See comments #1846 https://github.com/calyxir/calyx/pull/1846
# Assert arvalid if it was not previously high
xvalid.in_ = ~xvalid_was_high.out @ 1
# Deassert in the next cycle once it is high
xvalid.in_ = (xvalid.out & xREADY & xvalid_was_high.out) @ 0
xvalid.write_en = 1

xvalid_was_high.in_ = (~(xvalid.out & xREADY) & ~xvalid_was_high.out) @ 1
xvalid_was_high.write_en = (~(xvalid.out & xREADY) & ~xvalid_was_high.out) @ 1

# Drive output signals for transfer
m_to_s_address_channel.this()[f"{x}ADDR"] = base_addr.out
# 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"])
# TODO(nathanielnrn): Figure our how to set arlen. For now set to size of mem.
m_to_s_address_channel.this()[f"{x}LEN"] = xlen.out
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
m_to_s_address_channel.this()[f"{x}PROT"] = 0b110

# control block_transfer reg to go low after one cycle
bt_reg.in_ = (xREADY & xvalid.out) @ 1
bt_reg.in_ = ~(xREADY & xvalid.out) @ 0
bt_reg.write_en = 1
do_x_transfer.done = bt_reg.out

with m_to_s_address_channel.group("incr_txn_count") as incr_txn_count:
txn_adder.left = txn_count.out
txn_adder.right = 1
txn_count.in_ = txn_adder.out
txn_count.write_en = 1
incr_txn_count.done = txn_count.done

# Control
# check if txn_count == txn_n
cellname = "perform_reads" if prefix == "AR" else "perform_writes"
check_transactions_done = m_to_s_address_channel.neq_use(
txn_count.out, txn_n.out, signed=False, cellname=cellname, width=32
)
# with arread_channel.comb_group("check_reads_done") as check_reads_done:
# perform_reads.left = txn_count.out
# perform_reads.right = txn_n.out

invoke_txn_count = invoke(txn_count, in_in=0)
# ARLEN must be between 0-255, make sure to subtract 1 from yxi
# size when assigning to ARLEN
assert mem["size"] < 256, "Memory size must be less than 256"
invoke_xlen = invoke(xlen, in_in=mem["size"] - 1)

while_body = [
par(
invoke(bt_reg, in_in=0),
invoke(xvalid_was_high, in_in=0),
),
do_x_transfer,
invoke(xvalid, in_in=0),
incr_txn_count,
]

while_loop = while_with(check_transactions_done, while_body)
m_to_s_address_channel.control += [invoke_txn_count, invoke_xlen, while_loop]
return m_to_s_address_channel


def add_read_channel(prog, mem):
# Inputs/Outputs
read_channel = prog.component("m_read_channel")
# TODO(nathanielnrn): We currently assume RDATA is the same width as the
# memory. This limits throughput many AXI data busses are much wider
# i.e., 512 bits.
channel_inputs = [
("ARESETn", 1),
("RVALID", 1),
("RLAST", 1),
("RDATA", mem["width"]),
("RRESP", 2),
]
channel_outputs = [("RREADY", 1)]
add_comp_params(read_channel, channel_inputs, channel_outputs)

# Cells

# We assume idx_size is exactly clog2(len). See comment in #1751
# https://github.com/calyxir/calyx/issues/1751#issuecomment-1778360566
mem_ref = read_channel.seq_mem_d1(
name="mem_ref",
bitwidth=mem["width"],
len=mem["size"],
idx_size=clog2(mem["size"]),
is_external=False,
is_ref=True,
)

# according to zipcpu, rready should be registered
rready = read_channel.reg("rready", 1)
curr_addr = read_channel.reg("curr_addr", clog2(mem["size"]), is_ref=True)
base_addr = read_channel.reg("base_addr", 64, is_ref=True)
# Registed because RLAST is high with laster transfer, not after
# before this we were terminating immediately with
# last transfer and not servicing it
n_RLAST = read_channel.reg("n_RLAST", 1)
# Stores data we want to write to our memory at end of block_transfer group
read_data_reg = read_channel.reg("read_data_reg", mem["width"])

bt_reg = read_channel.reg("bt_reg", 1)

# Groups
with read_channel.continuous:
read_channel.this()["RREADY"] = rready.out
# Tie this low as we are only ever writing to seq_mem
mem_ref.read_en = 0

# Wait for handshake. Ensure that when this is done we are ready to write
# (i.e., read_data_reg.write_en = is_rdy.out)
# xVALID signals must be high until xREADY is high too, this works because
# if xREADY is high, then xVALID being high makes 1 flip and group
# is done by bt_reg.out
with read_channel.group("block_transfer") as block_transfer:
RVALID = read_channel.this()["RVALID"]
RDATA = read_channel.this()["RDATA"]
RLAST = read_channel.this()["RLAST"]
# TODO(nathanielnrn): We are allowed to have RREADY depend on RVALID.
# Can we simplify to just RVALID?

# rready.in = 1 does not work because it leaves RREADY high for 2 cycles.
# The way it is below leaves it high for only 1 cycle. See #1828
# https://github.com/calyxir/calyx/issues/1828

# TODO(nathanielnrn): Spec recommends defaulting xREADY high to get rid
# of extra cycles. Can we do this as opposed to waiting for RVALID?
rready.in_ = ~(rready.out & RVALID) @ 1
rready.in_ = (rready.out & RVALID) @ 0
rready.write_en = 1

# Store data we want to write
read_data_reg.in_ = RDATA
read_data_reg.write_en = (rready.out & RVALID) @ 1
read_data_reg.write_en = ~(rready.out & RVALID) @ 0

n_RLAST.in_ = RLAST @ 0
n_RLAST.in_ = ~RLAST @ 1
n_RLAST.write_en = 1

# We are done after handshake
bt_reg.in_ = (rready.out & RVALID) @ 1
bt_reg.in_ = ~(rready.out & RVALID) @ 0
bt_reg.write_en = 1
block_transfer.done = bt_reg.out

with read_channel.group("service_read_transfer") as service_read_transfer:
# not ready till done servicing
rready.in_ = 0
rready.write_en = 1

# write data we received to mem_ref
mem_ref.addr0 = curr_addr.out
mem_ref.write_data = read_data_reg.out
mem_ref.write_en = 1
service_read_transfer.done = mem_ref.done

# creates group that increments curr_addr by 1. Creates adder and wires up correctly
curr_addr_incr = read_channel.incr(curr_addr, 1)
# TODO(nathanielnrn): Currently we assume that width is a power of 2.
# In the future we should allow for non-power of 2 widths, will need some
# splicing for this.
# See https://cucapra.slack.com/archives/C05TRBNKY93/p1705587169286609?thread_ts=1705524171.974079&cid=C05TRBNKY93 # noqa: E501
Comment on lines +279 to +282
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All makes sense. For starters, one thing we could do here is just assert right at the beginning (i.e., in the build function) that the relevant parameters in the YXI document are all powers of two.

base_addr_incr = read_channel.incr(base_addr, ceil(mem["width"] / 8))

# Control
invoke_n_RLAST = invoke(n_RLAST, in_in=1)
invoke_bt_reg = invoke(bt_reg, in_in=0)
while_body = [
invoke_bt_reg,
block_transfer,
service_read_transfer,
par(curr_addr_incr, base_addr_incr),
]
while_n_RLAST = while_(n_RLAST.out, while_body)

read_channel.control += [invoke_n_RLAST, while_n_RLAST]


# Helper functions
def width_in_bytes(width: int):
assert width % 8 == 0, "Width must be a multiple of 8."
return width // 8


def width_xsize(width: int):
log = log2(width_in_bytes(width))
assert log.is_integer(), "Width must be a power of 2."
return int(log)


def clog2(x):
"""Ceiling log2"""
if x <= 0:
raise ValueError("x must be positive")
return (x - 1).bit_length()


def build():
prog = Builder()
add_arread_channel(prog, mems[0])
add_awwrite_channel(prog, mems[0])
add_read_channel(prog, mems[0])
return prog.program


if __name__ == "__main__":
build().emit()
Loading