Skip to content

Commit 6298696

Browse files
committed
Fix read-compute-write tests
This gives us a single runt test that goes from a calyx vector add to its expected output after being simulated with cocotb. Can replace a lot of what was removed in #2515
1 parent bda80c0 commit 6298696

File tree

9 files changed

+312
-101
lines changed

9 files changed

+312
-101
lines changed

runt.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,3 +671,18 @@ make --silent -B TEST_PATH={} COCOTB_LOG_LEVEL=CRITICAL |\
671671
rm -f results.xml
672672
673673
"""
674+
675+
676+
# Assumes same basename for .futil and .yxi
677+
# TODO(nnavarro): Change paths to `yxi/tests/axi/*/*-vec-add.futil` when calyx-AXI wrapper is fixed
678+
[[tests]]
679+
name = "Cocotb axi wrapper correctness test"
680+
paths = ["yxi/tests/axi/read-compute-write/*-vec-add.futil"]
681+
cmd = """
682+
dynamic=$(head -n 2 {} | tail -n 1 | cut -c 4-) && \
683+
dirname=$(dirname {}) && \
684+
filename=$(basename {} .futil) && \
685+
fud2 {} --to calyx --through axi-wrapped --set ${dynamic} -o axi-wrapped.futil 2> /dev/null && \
686+
fud2 axi-wrapped.futil --from calyx --to verilog-noverify -o axi-wrapped.v 2> /dev/null && \
687+
fud2 axi-wrapped.v --from verilog-noverify --to cocotb-axi --set sim.data=${dirname}/../vectorized-add.data 2> /dev/null
688+
"""

yxi/axi-calyx/axi_controller_generator.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -185,9 +185,9 @@ def add_bresp_channel(prog):
185185

186186
with bresp_channel.continuous:
187187
bresp_channel.this()["BVALID"] = bvalid.out
188-
bresp_channel.this()["BRESP"] = (
189-
0b00 # Assume OKAY. Could make this dynamic in the future by passing in a ref cell.
190-
)
188+
bresp_channel.this()[
189+
"BRESP"
190+
] = 0b00 # Assume OKAY. Could make this dynamic in the future by passing in a ref cell.
191191

192192
with bresp_channel.group("block_transfer") as block_transfer:
193193
BREADY = bresp_channel.this()["BREADY"]
@@ -593,16 +593,16 @@ def build():
593593
def check_mems_welformed(mems):
594594
"""Checks if memories from yxi are well formed. Returns true if they are, false otherwise."""
595595
for mem in mems:
596-
assert mem[width_key] % 8 == 0, (
597-
"Width must be a multiple of 8 to alow byte addressing to host"
598-
)
599-
assert log2(mem[width_key]).is_integer(), (
600-
"Width must be a power of 2 to be correctly described by xSIZE"
601-
)
596+
assert (
597+
mem[width_key] % 8 == 0
598+
), "Width must be a multiple of 8 to alow byte addressing to host"
599+
assert log2(
600+
mem[width_key]
601+
).is_integer(), "Width must be a power of 2 to be correctly described by xSIZE"
602602
assert mem[size_key] > 0, "Memory size must be greater than 0"
603-
assert mem[type_key] == "Dynamic", (
604-
"Only dynamic memories are currently supported for dynamic axi"
605-
)
603+
assert (
604+
mem[type_key] == "Dynamic"
605+
), "Only dynamic memories are currently supported for dynamic axi"
606606

607607

608608
if __name__ == "__main__":

yxi/axi-calyx/axi_generator.py

Lines changed: 77 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ def add_bresp_channel(prog, mem):
446446

447447
# NOTE: Unlike the channel functions, this can expect multiple mems
448448
def add_main_comp(prog, mems):
449-
wrapper_comp = prog.component("wrapper")
449+
wrapper_comp = prog.component("Toplevel")
450450
wrapper_comp.attribute("toplevel", 1)
451451
# Get handles to be used later
452452
read_channel = prog.get_component("m_read_channel")
@@ -468,52 +468,54 @@ def add_main_comp(prog, mems):
468468
# Naming the clock signal `ap_clk` ensures Xilinx tool compatability
469469
wrapper_comp.input("ap_clk", 1, ["clk"])
470470

471-
# TODO: To get these ports to interface with XRT and our current xml_generator.py we need to add a
472-
# `prefixed_mem_name = f"m_axi_{mem[name_key]}"`
473-
# and replace most (but not all) usages of {mem_name} with `prefixed_mem_name`.
474-
475471
for mem in mems:
472+
476473
mem_name = mem[name_key]
474+
# These input/output names in the toplevel (i.e. 'm_axi_A0_ARREADY') need to match
475+
# the ports(not!! args) kernel.xml file generated by `xml_generator.py`.
476+
# We add the prefix `m_axi_` to maintain compatibility with the old verilog wrapper.
477+
# Once we deprecate the old wrapper we can probably remove this prefix here and modify `xml_generator.py`
478+
prefixed_mem_name = f"m_axi_{mem[name_key]}"
477479
# Inputs/Outputs
478480
wrapper_inputs = [
479-
(f"{mem_name}_ARESETn", 1),
480-
(f"{mem_name}_ARREADY", 1),
481-
(f"{mem_name}_RVALID", 1),
482-
(f"{mem_name}_RLAST", 1),
483-
(f"{mem_name}_RDATA", mem[width_key]),
484-
(f"{mem_name}_RRESP", 2),
485-
(f"{mem_name}_AWREADY", 1),
486-
(f"{mem_name}_WRESP", 2),
487-
(f"{mem_name}_WREADY", 1),
488-
(f"{mem_name}_BVALID", 1),
481+
(f"{prefixed_mem_name}_ARESETn", 1),
482+
(f"{prefixed_mem_name}_ARREADY", 1),
483+
(f"{prefixed_mem_name}_RVALID", 1),
484+
(f"{prefixed_mem_name}_RLAST", 1),
485+
(f"{prefixed_mem_name}_RDATA", mem[width_key]),
486+
(f"{prefixed_mem_name}_RRESP", 2),
487+
(f"{prefixed_mem_name}_AWREADY", 1),
488+
(f"{prefixed_mem_name}_WRESP", 2),
489+
(f"{prefixed_mem_name}_WREADY", 1),
490+
(f"{prefixed_mem_name}_BVALID", 1),
489491
# Only used for waveform tracing, not sent anywhere
490-
(f"{mem_name}_BRESP", 2),
492+
(f"{prefixed_mem_name}_BRESP", 2),
491493
# Only needed for coctb compatability, tied low
492-
(f"{mem_name}_RID", 1),
494+
(f"{prefixed_mem_name}_RID", 1),
493495
]
494496

495497
wrapper_outputs = [
496-
(f"{mem_name}_ARVALID", 1),
497-
(f"{mem_name}_ARADDR", 64),
498-
(f"{mem_name}_ARSIZE", 3),
499-
(f"{mem_name}_ARLEN", 8),
500-
(f"{mem_name}_ARBURST", 2),
501-
(f"{mem_name}_RREADY", 1),
502-
(f"{mem_name}_AWVALID", 1),
503-
(f"{mem_name}_AWADDR", 64),
504-
(f"{mem_name}_AWSIZE", 3),
505-
(f"{mem_name}_AWLEN", 8),
506-
(f"{mem_name}_AWBURST", 2),
507-
(f"{mem_name}_AWPROT", 3),
508-
(f"{mem_name}_WVALID", 1),
509-
(f"{mem_name}_WLAST", 1),
510-
(f"{mem_name}_WDATA", mem[width_key]),
511-
(f"{mem_name}_BREADY", 1),
498+
(f"{prefixed_mem_name}_ARVALID", 1),
499+
(f"{prefixed_mem_name}_ARADDR", 64),
500+
(f"{prefixed_mem_name}_ARSIZE", 3),
501+
(f"{prefixed_mem_name}_ARLEN", 8),
502+
(f"{prefixed_mem_name}_ARBURST", 2),
503+
(f"{prefixed_mem_name}_RREADY", 1),
504+
(f"{prefixed_mem_name}_AWVALID", 1),
505+
(f"{prefixed_mem_name}_AWADDR", 64),
506+
(f"{prefixed_mem_name}_AWSIZE", 3),
507+
(f"{prefixed_mem_name}_AWLEN", 8),
508+
(f"{prefixed_mem_name}_AWBURST", 2),
509+
(f"{prefixed_mem_name}_AWPROT", 3),
510+
(f"{prefixed_mem_name}_WVALID", 1),
511+
(f"{prefixed_mem_name}_WLAST", 1),
512+
(f"{prefixed_mem_name}_WDATA", mem[width_key]),
513+
(f"{prefixed_mem_name}_BREADY", 1),
512514
# ID signals are needed for coco compatability, tied low
513-
(f"{mem_name}_ARID", 1),
514-
(f"{mem_name}_AWID", 1),
515-
(f"{mem_name}_WID", 1),
516-
(f"{mem_name}_BID", 1),
515+
(f"{prefixed_mem_name}_ARID", 1),
516+
(f"{prefixed_mem_name}_AWID", 1),
517+
(f"{prefixed_mem_name}_WID", 1),
518+
(f"{prefixed_mem_name}_BID", 1),
517519
]
518520

519521
add_comp_ports(wrapper_comp, wrapper_inputs, wrapper_outputs)
@@ -547,10 +549,10 @@ def add_main_comp(prog, mems):
547549

548550
# Tie IDs low, needed for cocotb compatability. Not used anywhere
549551
with wrapper_comp.continuous:
550-
wrapper_comp.this()[f"{mem_name}_ARID"] = 0
551-
wrapper_comp.this()[f"{mem_name}_AWID"] = 0
552-
wrapper_comp.this()[f"{mem_name}_WID"] = 0
553-
wrapper_comp.this()[f"{mem_name}_BID"] = 0
552+
wrapper_comp.this()[f"{prefixed_mem_name}_ARID"] = 0
553+
wrapper_comp.this()[f"{prefixed_mem_name}_AWID"] = 0
554+
wrapper_comp.this()[f"{prefixed_mem_name}_WID"] = 0
555+
wrapper_comp.this()[f"{prefixed_mem_name}_BID"] = 0
554556

555557
# No groups needed!
556558

@@ -562,41 +564,41 @@ def add_main_comp(prog, mems):
562564
# main_comp.get_cell(f"ar_channel_{mem_name}"),
563565
wrapper_comp.get_cell(f"ar_channel_{mem_name}"),
564566
ref_curr_addr_axi=curr_addr_axi,
565-
in_ARESETn=this_component[f"{mem_name}_ARESETn"],
566-
in_ARREADY=this_component[f"{mem_name}_ARREADY"],
567-
out_ARVALID=this_component[f"{mem_name}_ARVALID"],
568-
out_ARADDR=this_component[f"{mem_name}_ARADDR"],
569-
out_ARSIZE=this_component[f"{mem_name}_ARSIZE"],
570-
out_ARLEN=this_component[f"{mem_name}_ARLEN"],
571-
out_ARBURST=this_component[f"{mem_name}_ARBURST"],
567+
in_ARESETn=this_component[f"{prefixed_mem_name}_ARESETn"],
568+
in_ARREADY=this_component[f"{prefixed_mem_name}_ARREADY"],
569+
out_ARVALID=this_component[f"{prefixed_mem_name}_ARVALID"],
570+
out_ARADDR=this_component[f"{prefixed_mem_name}_ARADDR"],
571+
out_ARSIZE=this_component[f"{prefixed_mem_name}_ARSIZE"],
572+
out_ARLEN=this_component[f"{prefixed_mem_name}_ARLEN"],
573+
out_ARBURST=this_component[f"{prefixed_mem_name}_ARBURST"],
572574
)
573575

574576
read_channel_invoke = invoke(
575577
wrapper_comp.get_cell(f"read_channel_{mem_name}"),
576578
ref_mem_ref=internal_mem,
577579
ref_curr_addr_internal_mem=curr_addr_internal_mem,
578580
ref_curr_addr_axi=curr_addr_axi,
579-
in_ARESETn=this_component[f"{mem_name}_ARESETn"],
580-
in_RVALID=this_component[f"{mem_name}_RVALID"],
581-
in_RLAST=this_component[f"{mem_name}_RLAST"],
582-
in_RDATA=this_component[f"{mem_name}_RDATA"],
581+
in_ARESETn=this_component[f"{prefixed_mem_name}_ARESETn"],
582+
in_RVALID=this_component[f"{prefixed_mem_name}_RVALID"],
583+
in_RLAST=this_component[f"{prefixed_mem_name}_RLAST"],
584+
in_RDATA=this_component[f"{prefixed_mem_name}_RDATA"],
583585
# TODO: Do we need this? Don't think this goes anywhere
584-
in_RRESP=this_component[f"{mem_name}_RRESP"],
585-
out_RREADY=this_component[f"{mem_name}_RREADY"],
586+
in_RRESP=this_component[f"{prefixed_mem_name}_RRESP"],
587+
out_RREADY=this_component[f"{prefixed_mem_name}_RREADY"],
586588
)
587589

588590
aw_channel_invoke = invoke(
589591
wrapper_comp.get_cell(f"aw_channel_{mem_name}"),
590592
ref_curr_addr_axi=curr_addr_axi,
591593
ref_max_transfers=max_transfers,
592-
in_ARESETn=this_component[f"{mem_name}_ARESETn"],
593-
in_AWREADY=this_component[f"{mem_name}_AWREADY"],
594-
out_AWVALID=this_component[f"{mem_name}_AWVALID"],
595-
out_AWADDR=this_component[f"{mem_name}_AWADDR"],
596-
out_AWSIZE=this_component[f"{mem_name}_AWSIZE"],
597-
out_AWLEN=this_component[f"{mem_name}_AWLEN"],
598-
out_AWBURST=this_component[f"{mem_name}_AWBURST"],
599-
out_AWPROT=this_component[f"{mem_name}_AWPROT"],
594+
in_ARESETn=this_component[f"{prefixed_mem_name}_ARESETn"],
595+
in_AWREADY=this_component[f"{prefixed_mem_name}_AWREADY"],
596+
out_AWVALID=this_component[f"{prefixed_mem_name}_AWVALID"],
597+
out_AWADDR=this_component[f"{prefixed_mem_name}_AWADDR"],
598+
out_AWSIZE=this_component[f"{prefixed_mem_name}_AWSIZE"],
599+
out_AWLEN=this_component[f"{prefixed_mem_name}_AWLEN"],
600+
out_AWBURST=this_component[f"{prefixed_mem_name}_AWBURST"],
601+
out_AWPROT=this_component[f"{prefixed_mem_name}_AWPROT"],
600602
)
601603

602604
write_channel_invoke = invoke(
@@ -605,17 +607,17 @@ def add_main_comp(prog, mems):
605607
ref_curr_addr_internal_mem=curr_addr_internal_mem,
606608
ref_curr_addr_axi=curr_addr_axi,
607609
ref_max_transfers=max_transfers,
608-
in_ARESETn=this_component[f"{mem_name}_ARESETn"],
609-
in_WREADY=this_component[f"{mem_name}_WREADY"],
610-
out_WVALID=this_component[f"{mem_name}_WVALID"],
611-
out_WLAST=this_component[f"{mem_name}_WLAST"],
612-
out_WDATA=this_component[f"{mem_name}_WDATA"],
610+
in_ARESETn=this_component[f"{prefixed_mem_name}_ARESETn"],
611+
in_WREADY=this_component[f"{prefixed_mem_name}_WREADY"],
612+
out_WVALID=this_component[f"{prefixed_mem_name}_WVALID"],
613+
out_WLAST=this_component[f"{prefixed_mem_name}_WLAST"],
614+
out_WDATA=this_component[f"{prefixed_mem_name}_WDATA"],
613615
)
614616

615617
bresp_channel_invoke = invoke(
616618
wrapper_comp.get_cell(f"bresp_channel_{mem_name}"),
617-
in_BVALID=this_component[f"{mem_name}_BVALID"],
618-
out_BREADY=this_component[f"{mem_name}_BREADY"],
619+
in_BVALID=this_component[f"{prefixed_mem_name}_BVALID"],
620+
out_BREADY=this_component[f"{prefixed_mem_name}_BREADY"],
619621
)
620622

621623
curr_addr_axi_invoke = invoke(curr_addr_axi, in_in=0x1000)
@@ -690,12 +692,12 @@ def build():
690692
def check_mems_welformed(mems):
691693
"""Checks if memories from yxi are well formed. Returns true if they are, false otherwise."""
692694
for mem in mems:
693-
assert mem[width_key] % 8 == 0, (
694-
"Width must be a multiple of 8 to alow byte addressing to host"
695-
)
696-
assert log2(mem[width_key]).is_integer(), (
697-
"Width must be a power of 2 to be correctly described by xSIZE"
698-
)
695+
assert (
696+
mem[width_key] % 8 == 0
697+
), "Width must be a multiple of 8 to alow byte addressing to host"
698+
assert log2(
699+
mem[width_key]
700+
).is_integer(), "Width must be a power of 2 to be correctly described by xSIZE"
699701
assert mem[size_key] > 0, "Memory size must be greater than 0"
700702

701703

yxi/axi-calyx/cocotb/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ VERILOG_SOURCES += $(VERILOG_SOURCE)
1313
SIM_BUILD=sim_build/
1414

1515
# TOPLEVEL is the name of the toplevel module in your Verilog or VHDL file
16-
TOPLEVEL = wrapper
16+
TOPLEVEL = Toplevel
1717

1818
# MODULE is the basename of the Python test file
1919
MODULE = run_axi_test

yxi/axi-calyx/cocotb/run_axi_test.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ async def run(toplevel):
1414
from axi_test import run_kernel_test
1515

1616
data_path = os.environ.get("DATA_PATH")
17-
if not os.path.isabs(data_path):
17+
if data_path and not os.path.isabs(data_path):
1818
data_path = os.getcwd() + "/" + data_path
19-
assert data_path is not None and os.path.isfile(data_path), (
20-
"DATA_PATH must be set and must be a valid file."
21-
)
19+
assert data_path is not None and os.path.isfile(
20+
data_path
21+
), f"DATA_PATH must be set and must be a valid file. Got: {data_path}"
2222

2323
await run_kernel_test(toplevel, data_path)

yxi/axi-calyx/dynamic_axi_generator.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -903,16 +903,16 @@ def build():
903903
def check_mems_wellformed(mems):
904904
"""Checks if memories from yxi are well formed. Returns true if they are, false otherwise."""
905905
for mem in mems:
906-
assert mem[width_key] % 8 == 0, (
907-
"Width must be a multiple of 8 to alow byte addressing to host"
908-
)
909-
assert log2(mem[width_key]).is_integer(), (
910-
"Width must be a power of 2 to be correctly described by xSIZE"
911-
)
906+
assert (
907+
mem[width_key] % 8 == 0
908+
), "Width must be a multiple of 8 to alow byte addressing to host"
909+
assert log2(
910+
mem[width_key]
911+
).is_integer(), "Width must be a power of 2 to be correctly described by xSIZE"
912912
assert mem[size_key] > 0, "Memory size must be greater than 0"
913-
assert mem[type_key] == "Dynamic", (
914-
"Only dynamic memories are currently supported for dynamic axi"
915-
)
913+
assert (
914+
mem[type_key] == "Dynamic"
915+
), "Only dynamic memories are currently supported for dynamic axi"
916916

917917

918918
if __name__ == "__main__":
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
{
2+
"A0": {
3+
"data": [
4+
1,
5+
3,
6+
7,
7+
15,
8+
31,
9+
63,
10+
127,
11+
255
12+
],
13+
"format": {
14+
"numeric_type": "bitnum",
15+
"is_signed": false,
16+
"width": 32
17+
}
18+
},
19+
"B0": {
20+
"data": [
21+
1,
22+
1,
23+
1,
24+
1,
25+
1,
26+
1,
27+
1,
28+
1
29+
],
30+
"format": {
31+
"numeric_type": "bitnum",
32+
"is_signed": false,
33+
"width": 32
34+
}
35+
},
36+
"Sum0": {
37+
"data": [
38+
2,
39+
4,
40+
8,
41+
16,
42+
32,
43+
64,
44+
128,
45+
256
46+
],
47+
"format": {
48+
"numeric_type": "bitnum",
49+
"is_signed": false,
50+
"width": 32
51+
}
52+
}
53+
}

0 commit comments

Comments
 (0)