Skip to content

Commit

Permalink
[fud2] Add dynamic generation option to axi-wrapped state (#2136)
Browse files Browse the repository at this point in the history
* add method to get a confg key witha constrained set of values

* Add dynamic generation for `axi-wrapped` state

Use `--set dynamic=true` targeting 1--to axi-wrapped`

* add method to get a confg key witha constrained set of values

* Make InvalidValue a c-like struct and fix err msg

* formatting

* change debug representation to a join

* formatting

* trigger ci

* Introduce dynamic AXI runt tests (#2139)

* wip move axi tests around

* remove hardcoded dyn-mem-vec-add from dynamic axi generator

* add raw dynamic axi generator tests

* add dyn_mem import into dynamic axi generator

* more dynamic axi runt tests

* fud2 cocotb tests for dynamic axi

* remove hardcoded compute

* remove stderr ipe to dev/null in fud2 cocotb axi tests

* return 2> dev/null in runt cocotb axi tests

* more runt fixes

* more runt fixes, cocotb dynamic test still fails on CI

* upgrade pip to get latest stable version of cocotb

Changes dockerfile

* hook up xPROT signals correctly

* add ARPROT in places as well

* update runt tests

* fix xPROT connections, revert cocotb in dockerfile to 1.6.2
  • Loading branch information
nathanielnrn authored Jun 15, 2024
1 parent 941b4b5 commit a3acbe3
Show file tree
Hide file tree
Showing 20 changed files with 24,718 additions and 117 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ RUN echo "deb https://repo.scala-sbt.org/scalasbt/debian all main" | tee /etc/ap
RUN python3 -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"

# Install python dependencies
# Install python dependencies cocotb==1.6.2 seems to be for Xilinx cocotb tests
RUN python3 -m pip install numpy flit prettytable wheel hypothesis pytest simplejson cocotb==1.6.2
# Current cocotb-bus has a bug that is fixed in more up to date repo
RUN python3 -m pip install git+https://github.com/cocotb/cocotb-bus.git cocotbext-axi
Expand Down
17 changes: 12 additions & 5 deletions fud2/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::str::FromStr;

use fud_core::{
exec::{SetupRef, StateRef},
run::{EmitResult, StreamEmitter},
Expand Down Expand Up @@ -760,11 +762,16 @@ pub fn build_driver(bld: &mut DriverBuilder) {
let wrapper_setup = bld.setup("YXI and AXI generation", |e| {
// Define a `gen-axi` rule that invokes our Python code generator program.
// For now point to standalone axi-generator.py. Can maybe turn this into a rsrc file?
e.config_var_or(
"axi-generator",
"axi.generator",
"$calyx-base/yxi/axi-calyx/axi-generator.py",
)?;
let dynamic =
e.config_constrained_or("dynamic", vec!["true", "false"], "false")?;
let generator_path = if FromStr::from_str(&dynamic)
.expect("The dynamic flag should be either 'true' or 'false'.")
{
"$calyx-base/yxi/axi-calyx/dynamic-axi-generator.py"
} else {
"$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")?;

e.rule("gen-axi", "$python $axi-generator $in > $out")?;
Expand Down
27 changes: 18 additions & 9 deletions runt.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ cmd = """
"""

[[tests]]
name = "fud2 yxi"
name = "fud2 yxi invocation"
paths = ["yxi/tests/ref-mems-vec-add.futil"]
cmd = """
fud2 {} --from calyx --to yxi
Expand Down Expand Up @@ -552,34 +552,43 @@ fud e {} -s verilog.cycle_limit 500 \
-s verilog.data {}.data \
--to dat -q
"""

##### Calyx AXI tests #####
[[tests]]
name = "calyx-py AXI wrapper generation"
paths = ["yxi/tests/axi/*.yxi"]
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 {}
"""

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

#Ignore fud2 stderr for now due to ninja nondeterminism
[[tests]]
name = "fud2 AXI wrapper invocation"
paths = ["yxi/tests/axi/seq-mem-vec-add.futil"]
paths = ["yxi/tests/axi/*/*-mem-vec-add.futil"]
cmd = """
fud2 {} --to calyx --through axi-wrapped 2> /dev/null
dynamic=$(head -n 2 {} | tail -n 1 | cut -c 4-)
fud2 {} --to calyx --through axi-wrapped --set $dynamic 2> /dev/null
"""

[[tests]]
name = "fud2 AXI-wrapped to verilog"
paths = ["yxi/tests/axi/seq-mem-vec-add-axi-wrapped.futil"]
paths = ["yxi/tests/axi/*/*-mem-vec-add-axi-wrapped.futil"]
cmd = """
fud2 {} --from calyx --to verilog-noverify 2> /dev/null
"""

[[tests]]
name = "fud2 cocotb execution"
paths = ["yxi/tests/axi/seq-mem-vec-add-verilog.v"]
name = "fud2 cocotb AXI-wrapped xecution"
paths = ["yxi/tests/axi/*/*-mem-vec-add-verilog.v"]
cmd = """
fud2 {} --from verilog-noverify --to cocotb-axi --set sim.data=yxi/tests/axi/vectorized-add.data 2> /dev/null
fud2 {} --from verilog-noverify --to cocotb-axi --set sim.data=yxi/tests/axi/vectorized-add.data 2> /dev/null
"""

##### Xilinx Tests ######
Expand Down
114 changes: 12 additions & 102 deletions yxi/axi-calyx/dynamic-axi-generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
Builder,
add_comp_ports,
invoke,
while_with,
par,
while_,
if_
Expand All @@ -12,95 +11,6 @@
import json
import sys


#fixed vec_add main
vec_add_main = """
component main() -> () {
cells {
//Modified to 64 width address because XRT expects 64 bit memory addresses
ref A0 = dyn_mem_d1(32,8,3);
A_read0_0 = std_reg(32);
ref B0 = dyn_mem_d1(32,8,3);
B_read0_0 = std_reg(32);
ref Sum0 = dyn_mem_d1(32,8,3);
add0 = std_add(32);
add1 = std_add(4);
const0 = std_const(4,0);
const1 = std_const(4,7);
const2 = std_const(4,1);
i0 = std_reg(4);
le0 = std_le(4);
bit_slice = std_bit_slice(4,0,2,3);
}
wires {
A0.write_en = 1'b0;
B0.write_en = 1'b0;
bit_slice.in = i0.out;
comb group cond0 {
le0.left = i0.out;
le0.right = const1.out;
}
group let0 {
i0.in = const0.out;
i0.write_en = 1'd1;
let0[done] = i0.done;
}
//modified upd0 and upd1 to use seq_mem correctly
group upd0 {
A_read0_0.write_en = A0.done;
A0.addr0 = bit_slice.out;
A0.content_en = 1'b1;
A_read0_0.in = 1'd1 ? A0.read_data;
upd0[done] = A_read0_0.done ? 1'd1;
}
//see comment for upd0
group upd1 {
B_read0_0.write_en = B0.done;
B0.addr0 = bit_slice.out;
B0.content_en = 1'b1;
B_read0_0.in = 1'd1 ? B0.read_data;
upd1[done] = B_read0_0.done ? 1'd1;
}
group upd2 {
Sum0.addr0 = bit_slice.out;
Sum0.content_en = 1'd1;
Sum0.write_en = 1'd1;
//Are these A0 and B0 assignments neeeded?
//B0.content_en = 1'b1;
//B0.addr0 = bit_slice.out;
//A0.addr0 = bit_slice.out;
//A0.content_en = 1'b1;
add0.left = B_read0_0.out;
add0.right = A_read0_0.out;
Sum0.write_data = 1'd1 ? add0.out;
upd2[done] = Sum0.done ? 1'd1;
}
group upd3 {
i0.write_en = 1'd1;
add1.left = i0.out;
add1.right = const2.out;
i0.in = 1'd1 ? add1.out;
upd3[done] = i0.done ? 1'd1;
}
}
control {
seq {
let0;
while le0.out with cond0 {
seq {
par {
upd0;
upd1;
}
upd2;
upd3;
}
}
}
}
}
"""
# In general, ports to the wrapper are uppercase, internal registers are lower case.

# Since yxi is still young, keys and formatting change often.
Expand Down Expand Up @@ -459,6 +369,7 @@ def add_read_controller(prog, mem):
(f"ARSIZE", 3),
(f"ARLEN", 8),
(f"ARBURST", 2),
(f"ARPROT", 3),
(f"RREADY", 1),
#sent out to axi_dyn_mem
(f"read_data", data_width),
Expand Down Expand Up @@ -488,6 +399,7 @@ def add_read_controller(prog, mem):
out_ARSIZE=read_controller.this()["ARSIZE"],
out_ARLEN=read_controller.this()["ARLEN"],
out_ARBURST=read_controller.this()["ARBURST"],
out_ARPROT=read_controller.this()["ARPROT"],
)
simple_read_invoke = invoke(
simple_read_channel,
Expand Down Expand Up @@ -525,6 +437,7 @@ def add_write_controller(prog, mem):
(f"AWSIZE", 3),
(f"AWLEN", 8),
(f"AWBURST", 2),
(f"AWPROT", 3),
(f"WVALID", 1),
(f"WLAST", 1),
(f"WDATA", data_width),
Expand Down Expand Up @@ -552,6 +465,7 @@ def add_write_controller(prog, mem):
out_AWSIZE=write_controller.this()["AWSIZE"],
out_AWLEN=write_controller.this()["AWLEN"],
out_AWBURST=write_controller.this()["AWBURST"],
out_AWPROT=write_controller.this()["AWPROT"],
)
simple_write_invoke = invoke(
simple_write_channel,
Expand Down Expand Up @@ -580,9 +494,10 @@ def add_axi_dyn_mem(prog, mem):
data_width = mem[width_key]
name = mem[name_key]

prog.import_("primitives/memories/dyn.futil")
axi_dyn_mem = prog.component(f"axi_dyn_mem_{name}")
# Inputs/Outputs
seq_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 All @@ -594,19 +509,19 @@ def add_axi_dyn_mem(prog, mem):
(f"RDATA", mem[width_key]),
(f"RRESP", 2),
(f"AWREADY", 1),
(f"WRESP", 2),
(f"WREADY", 1),
(f"BVALID", 1),
# Only used for waveform tracing, not sent anywhere
(f"BRESP", 2),
]
seq_mem_outputs = [
dyn_mem_outputs = [
("read_data", data_width, ["stable"]),
(f"ARVALID", 1),
(f"ARADDR", 64),
(f"ARSIZE", 3),
(f"ARLEN", 8),
(f"ARBURST", 2),
(f"ARPROT", 3),
(f"RREADY", 1),
(f"AWVALID", 1),
(f"AWADDR", 64),
Expand All @@ -619,7 +534,7 @@ def add_axi_dyn_mem(prog, mem):
(f"WDATA", mem[width_key]),
(f"BREADY", 1),
]
add_comp_ports(axi_dyn_mem, seq_mem_inputs, seq_mem_outputs)
add_comp_ports(axi_dyn_mem, dyn_mem_inputs, dyn_mem_outputs)

# Cells
address_translator = axi_dyn_mem.cell(f"address_translator_{name}", prog.get_component(f"address_translator_{name}"))
Expand Down Expand Up @@ -648,6 +563,7 @@ def add_axi_dyn_mem(prog, mem):
out_ARSIZE=this_component[f"ARSIZE"],
out_ARLEN=this_component[f"ARLEN"],
out_ARBURST=this_component[f"ARBURST"],
out_ARPROT=this_component[f"ARPROT"],
out_RREADY=this_component[f"RREADY"],
out_read_data=this_component[f"read_data"],
)
Expand All @@ -665,6 +581,7 @@ def add_axi_dyn_mem(prog, mem):
out_AWSIZE=this_component["AWSIZE"],
out_AWLEN=this_component["AWLEN"],
out_AWBURST=this_component["AWBURST"],
out_AWPROT=this_component[f"AWPROT"],
out_WVALID=this_component["WVALID"],
out_WLAST=this_component["WLAST"],
out_WDATA=this_component["WDATA"],
Expand All @@ -688,10 +605,6 @@ def add_main_comp(prog, mems):
# aw_channel = prog.get_component("m_aw_channel")
# bresp_channel = prog.get_component("m_bresp_channel")

curr_addr_axi_par = []
curr_addr_internal_par = []
reads_par = []
writes_par = []
ref_mem_kwargs = {}

# Create single main cell
Expand All @@ -710,7 +623,6 @@ def add_main_comp(prog, mems):
(f"{mem_name}_RDATA", mem[width_key]),
(f"{mem_name}_RRESP", 2),
(f"{mem_name}_AWREADY", 1),
(f"{mem_name}_WRESP", 2),
(f"{mem_name}_WREADY", 1),
(f"{mem_name}_BVALID", 1),
# Only used for waveform tracing, not sent anywhere
Expand Down Expand Up @@ -877,6 +789,4 @@ def check_mems_welformed(mems):
yxifile = open(yxi_filename)
yxi = json.load(yxifile)
mems = yxi["memories"]
build().emit()

print(vec_add_main)
build().emit()
Loading

0 comments on commit a3acbe3

Please sign in to comment.