-
Notifications
You must be signed in to change notification settings - Fork 50
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
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 fd3a0e5
add axi-reads-calix
nathanielnrn cb38574
hook up inputs to channels in the wrapper. tbd if this works
nathanielnrn 203387f
Working calyx verison of AR and R
nathanielnrn 534ecc0
Track output of compiled calyx read channel
nathanielnrn 46f7d7a
update gitignore to get rid of sim_build and other cocotb artifacts
nathanielnrn cabba39
Working make files for running cocotb tests
nathanielnrn 9fbe03e
Add xID signals for cocotb compatability
nathanielnrn 74d9d8f
Fix prefix issue on cocotb axi test bench
nathanielnrn e5b84be
commit to repro 'make WAVES=1' cocotb error from axi-reads-calyx.futil
nathanielnrn 6729dac
axi-reads patch
nathanielnrn cdef8b6
sync debug
rachitnigam a1d8e5a
Add txn_len initialization to 16 in calyx program
nathanielnrn 51f91d3
AXI Read fixed to get to read channel start
nathanielnrn 4d80ca7
Add integer byte conversion for tests on Calyx AXI testharness
nathanielnrn 7725cc3
WIP get reads to work. Add incr_curr_addr group
nathanielnrn 11c3bc3
remove .fst from tracking
nathanielnrn 736264b
Add more data to testbench to make waveform viewing easier
nathanielnrn 2c7c241
Reads seem to be terminating correctly at RLAST
nathanielnrn 21d13f9
AR transfers seem to work, valid is high for 1 cycle
nathanielnrn 7312254
Unreduced axi-reads-calyx.futil
nathanielnrn e2e848f
Cocotb testbench now passes
nathanielnrn 4fed24a
Formatted and passing axi-read-tests
nathanielnrn 6710f3f
Reduce and comment axi-reads-calyx.futil
nathanielnrn f30b274
remove axi-reads.v from being tracked
nathanielnrn 6a8d6a6
add a todo
nathanielnrn d084d3a
add required ARPROT signal. This is hardcoded to be priviliged
nathanielnrn 06db156
rename directories to yxi/axi-calyx
nathanielnrn 2cf9ed5
initial commit of axi-writes-calyx, a copy of axi-reads-calyx
nathanielnrn 8cc10a0
WIP axi writes
nathanielnrn 8b99289
rename directories
nathanielnrn 6328e0c
WIP imlpementing writes
nathanielnrn b3567d1
add testing for writes, note makefile is overwritten so now tests wri…
nathanielnrn fe8f284
passing axi writes and testing
nathanielnrn a13cd60
init commit of hardcoded axi wrapper for a 'main' kernel
nathanielnrn 505ae00
add axi-reads-calix
nathanielnrn 3f6f129
hook up inputs to channels in the wrapper. tbd if this works
nathanielnrn ba5e1a4
Working calyx verison of AR and R
nathanielnrn d93d852
Track output of compiled calyx read channel
nathanielnrn 6fbc3ca
Working make files for running cocotb tests
nathanielnrn a6426a6
Add xID signals for cocotb compatability
nathanielnrn d2db261
Fix prefix issue on cocotb axi test bench
nathanielnrn 3aa9722
commit to repro 'make WAVES=1' cocotb error from axi-reads-calyx.futil
nathanielnrn d813517
axi-reads patch
nathanielnrn 58a9de6
sync debug
rachitnigam cfd40d6
Add txn_len initialization to 16 in calyx program
nathanielnrn b26f0ea
AXI Read fixed to get to read channel start
nathanielnrn 96b9975
Add integer byte conversion for tests on Calyx AXI testharness
nathanielnrn 03e93a8
WIP get reads to work. Add incr_curr_addr group
nathanielnrn 4a1291f
remove .fst from tracking
nathanielnrn 3abee21
Add more data to testbench to make waveform viewing easier
nathanielnrn 94efbc9
Reads seem to be terminating correctly at RLAST
nathanielnrn 7179097
AR transfers seem to work, valid is high for 1 cycle
nathanielnrn 2f841e8
Unreduced axi-reads-calyx.futil
nathanielnrn 618f9b6
Cocotb testbench now passes
nathanielnrn 7866017
Formatted and passing axi-read-tests
nathanielnrn c926cf8
Reduce and comment axi-reads-calyx.futil
nathanielnrn 4120ee3
remove axi-reads.v from being tracked
nathanielnrn 8c29f01
add a todo
nathanielnrn 14066a1
add required ARPROT signal. This is hardcoded to be priviliged
nathanielnrn ecb5626
rename directories to yxi/axi-calyx
nathanielnrn d40a066
initial commit of axi-writes-calyx, a copy of axi-reads-calyx
nathanielnrn 297e60e
WIP axi writes
nathanielnrn e6cc577
rename directories
nathanielnrn 681c316
WIP imlpementing writes
nathanielnrn 22926ab
add testing for writes, note makefile is overwritten so now tests wri…
nathanielnrn 04200f5
passing axi writes and testing
nathanielnrn e24d114
Work on full AXI wrapper, reads and compute works
nathanielnrn b212c56
cleaned up combined futil and tests
nathanielnrn 6fd6697
delete axi-reads* which is subsumed by axi-combined
nathanielnrn 9d4fc22
add axi-combined-tests.py
nathanielnrn e9e6317
remove axi-writes as it is subsumed by axi-combined
nathanielnrn 5c51038
formatting
nathanielnrn b6437d1
Merge branch 'axi-writes' of github.com:calyxir/calyx into axi-writes
nathanielnrn 4fbdcc4
Update yxi/axi-calyx/axi-combined-calyx.futil
nathanielnrn 624e0d5
formatting
nathanielnrn 923fc5e
Merge branch 'axi-writes' of github.com:calyxir/calyx into axi-writes
nathanielnrn c7e3839
Merge branch 'axi-writes' of github.com:calyxir/calyx into axi-writes
nathanielnrn 9fd9cd3
add sim.sh which goes from calyx to running tests
nathanielnrn 53c53d4
simplify valid.in signals
nathanielnrn 31b6986
WIP: replace groups with reg invokes
nathanielnrn eb9a817
Merge branch 'axi-writes' of github.com:calyxir/calyx into axi-writes
nathanielnrn 02631c0
add python file that enables waveform (vcd/fst) generation
nathanielnrn c7869e9
formatting
nathanielnrn 880b1a8
simplify valid.in signals
nathanielnrn ab2a31a
WIP: replace groups with reg invokes
nathanielnrn 4c1d9fb
Replaces register-init groups with invokes
nathanielnrn ff5c2c9
Merge branch 'main' into axi-wrapper-opts
nathanielnrn 3e7fe70
Merge branch 'axi-wrapper-opts' of github.com:calyxir/calyx into axi-…
nathanielnrn 80c509a
Formatting of invokes
nathanielnrn 01b7bd0
Merge branch 'main' into axi-wrapper-opts
nathanielnrn 60462e6
Replace reg groups with invokes in main
nathanielnrn 141c203
Modify tests to account for base address != 0
nathanielnrn a8ec4b6
Separate base-address calyx-mem-address dependency
nathanielnrn b5d1053
Merge branch 'main' into address-fixes-axi
nathanielnrn 391c03d
move incrs into par block
nathanielnrn e463d47
iitial axi-generator commit
nathanielnrn 6b7073b
WIP get arread-channel working
nathanielnrn 9c911d8
Finished ARREAD channel.
nathanielnrn 4e6522c
Create m_to_s_address_channel for {AR,AW} channels
nathanielnrn 5cc562d
WIP: Add read channel
nathanielnrn 516a244
Finished read_channel. Still need to fix #1850
nathanielnrn 401aad0
Finished read channels
nathanielnrn b278242
Remove read channel to break up into multiple PRs
nathanielnrn 5be47b8
Merge branch 'main' into py-axi-generator-address-channels
nathanielnrn f1a054f
Add read channel back
nathanielnrn 87012db
Merge branch 'main' into py-axi-generator-read-channel
nathanielnrn File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
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() |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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 thebuild
function) that the relevant parameters in the YXI document are all powers of two.