From d3ac4c6a21f51dab972a1b045945c9d20f8a402e Mon Sep 17 00:00:00 2001 From: Nathaniel Navarro Date: Sun, 28 Jan 2024 21:42:09 -0500 Subject: [PATCH 01/23] WIP axi-gen main component Done with input/output, cells, wires, and groups (there are none) TODO: Control sequence --- yxi/axi-calyx/axi-generator.py | 150 +++++++++++++++++++++++++++++---- 1 file changed, 135 insertions(+), 15 deletions(-) diff --git a/yxi/axi-calyx/axi-generator.py b/yxi/axi-calyx/axi-generator.py index 21e61eade1..2e1cbc2729 100644 --- a/yxi/axi-calyx/axi-generator.py +++ b/yxi/axi-calyx/axi-generator.py @@ -44,15 +44,15 @@ def add_arread_channel(prog, mem): 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) + aw_channel = _add_m_to_s_address_channel(prog, mem, "AW") + max_transfers = aw_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"): + with aw_channel.get_group("do_aw_transfer"): max_transfers.in_ = mem["size"] - 1 max_transfers.write_en = 1 @@ -117,7 +117,7 @@ def _add_m_to_s_address_channel(prog, mem, prefix: Literal["AW", "AR"]): xvalid.write_en = 1 xhandshake_occurred.in_ = (xvalid.out & xREADY) @ 1 - xhandshake_occurred.write_en = (~xhandshake_occurred.out) @ 1 + xhandshake_occurred.write_en = (~xhandshake_occurred.out) @ 1 # Drive output signals for transfer m_to_s_address_channel.this()[f"{x}ADDR"] = curr_addr_axi.out @@ -203,7 +203,9 @@ def add_read_channel(prog, mem): # according to zipcpu, rready should be registered rready = read_channel.reg("rready", 1) - curr_addr_internal_mem = read_channel.reg("curr_addr_internal_mem", clog2(mem["size"]), is_ref=True) + curr_addr_internal_mem = read_channel.reg( + "curr_addr_internal_mem", clog2(mem["size"]), is_ref=True + ) curr_addr_axi = read_channel.reg("curr_addr_axi", 64, is_ref=True) # Registed because RLAST is high with laster transfer, not after # before this we were terminating immediately with @@ -320,7 +322,9 @@ def add_write_channel(prog, mem): wvalid = write_channel.reg("wvalid", 1) w_handshake_occurred = write_channel.reg("w_handshake_occurred", 1) # internal calyx memory indexing - curr_addr_internal_mem = write_channel.reg("curr_addr_internal_mem", clog2(mem["size"]), is_ref=True) + curr_addr_internal_mem = write_channel.reg( + "curr_addr_internal_mem", clog2(mem["size"]), is_ref=True + ) # host indexing, must be 64 bits curr_addr_axi = write_channel.reg("curr_addr_axi", 64, is_ref=True) @@ -357,12 +361,8 @@ def add_write_channel(prog, mem): mem_ref.read_en = 1 write_channel.this()["WDATA"] = mem_ref.read_data - write_channel.this()["WLAST"] = ( - max_trnsfrs.out == curr_trsnfr_count.out - ) @ 1 - write_channel.this()["WLAST"] = ( - max_trnsfrs.out != curr_trsnfr_count.out - ) @ 0 + write_channel.this()["WLAST"] = (max_trnsfrs.out == curr_trsnfr_count.out) @ 1 + write_channel.this()["WLAST"] = (max_trnsfrs.out != curr_trsnfr_count.out) @ 0 # set high when WLAST is high and a handshake occurs n_finished_last_trnsfr.in_ = ( @@ -393,7 +393,12 @@ def add_write_channel(prog, mem): while_n_finished_last_trnsfr_body = [ invoke(bt_reg, in_in=0), service_write_transfer, - par(curr_addr_internal_mem_incr, curr_trsnfr_count_incr, curr_addr_axi_incr, invoke(w_handshake_occurred, in_in=0)), + par( + curr_addr_internal_mem_incr, + curr_trsnfr_count_incr, + curr_addr_axi_incr, + invoke(w_handshake_occurred, in_in=0), + ), ] while_n_finished_last_trnsfr = while_( n_finished_last_trnsfr.out, while_n_finished_last_trnsfr_body @@ -442,6 +447,115 @@ def add_bresp_channel(prog, mem): bresp_channel.control += [invoke(bt_reg, in_in=0), block_transfer] +# NOTE: Unlike the channel functions, this can expect multiple mems +def add_main_comp(prog, mems): + main_comp = prog.component("main") + # Get handles to be used later + read_channel = prog.get_component("m_read_channel") + write_channel = prog.get_component("m_write_channel") + ar_channel = prog.get_component("m_ar_channel") + aw_channel = prog.get_component("m_aw_channel") + bresp_channel = prog.get_component("m_bresp_channel") + + # Inputs/Outputs + main_inputs = [] + main_outputs = [] + for mem in mems: + mem_name = mem["name"] + main_inputs.append( + [ + (f"{mem_name}_ARESETn", 1), + (f"{mem_name}_ARREADY", 1), + (f"{mem_name}_RVALID", 1), + (f"{mem_name}_RLAST", 1), + (f"{mem_name}_RDATA", mem["width"]), + (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 + (f"{mem_name}_BRESP", 2), + # Only needed for coctb compatability, tied low + (f"{mem_name}_RID", 1), + ] + ) + main_outputs.append( + [ + (f"{mem_name}_ARVALID", 1), + (f"{mem_name}_ARADDR", 64), + (f"{mem_name}_ARSIZE", 3), + (f"{mem_name}_ARLEN", 8), + (f"{mem_name}_ARBURST", 2), + (f"{mem_name}_RREADY", 1), + (f"{mem_name}_AWVALID", 1), + (f"{mem_name}_AWADDR", 64), + (f"{mem_name}_AWSIZE", 3), + (f"{mem_name}_AWLEN", 8), + (f"{mem_name}_AWBURST", 2), + (f"{mem_name}_AWPROT", 3), + (f"{mem_name}_WVALID", 1), + (f"{mem_name}_WLAST", 1), + (f"{mem_name}_WDATA", mem["width"]), + (f"{mem_name}_BREADY", 1), + # ID signals are needed for coco compatability, tied low + (f"{mem_name}_ARID", 1), + (f"{mem_name}_AWID", 1), + (f"{mem_name}_WID", 1), + (f"{mem_name}_BID", 1), + ] + ) + + add_comp_params(main_comp, main_inputs, main_outputs) + + # Cells + # Read stuff + curr_addr_internal_mem = main_comp.reg( + f"curr_addr_internal_mem_{mem_name}", clog2(mem["size"]) + ) + curr_addr_axi = main_comp.reg(f"curr_addr_axi_{mem_name}", 64) + + main_comp.cell(f"ar_channel_{mem_name}", ar_channel) + main_comp.cell(f"read_channel_{mem_name}", read_channel) + + # TODO: Don't think these need to be marked external, but we + # we need to raise them at some point form original calyx program + internal_mem = main_comp.seq_mem_d1( + name=f"internal_mem_{mem_name}", + bitwidth=mem["width"], + len=mem["size"], + idx_size=clog2(mem["size"]), + ) + + # TODO!!!: Add vec_add / computation kernel here + + # Write stuff + max_transfers = main_comp.reg(f"max_transfers_{mem_name}", 8) + main_comp.cell(f"aw_channel_{mem_name}", aw_channel) + main_comp.cell(f"write_channel_{mem_name}", write_channel) + main_comp.cel(f"bresp_channel_{mem_name}", bresp_channel) + + # Wires + + # Tie IDs low, needed for cocotb compatability. Not used anywhere + ARID = main_comp.this()[f"{mem_name}_ARID"] + AWID = main_comp.this()[f"{mem_name}_AWID"] + WID = main_comp.this()[f"{mem_name}_WID"] + BID = main_comp.this()[f"{mem_name}_BID"] + + ARID = 0 + AWID = 0 + WID = 0 + BID = 0 + + # No groups needed! + + # Control + init_par = par() + # TODO!!!: Move par of seqs outside of for loop + # look at how to append to par blocks + + # Helper functions def width_in_bytes(width: int): assert width % 8 == 0, "Width must be a multiple of 8." @@ -471,12 +585,18 @@ def build(): add_bresp_channel(prog, mems[0]) return prog.program + def check_mems_welformed(mems): """Checks if memories from yxi are well formed. Returns true if they are, false otherwise.""" for mem in mems: - assert mem["width"] % 8 == 0, "Width must be a multiple of 8 to alow byte addressing to host" - assert log2(mem["width"]).is_integer(), "Width must be a power of 2 to be correctly described by xSIZE" + assert ( + mem["width"] % 8 == 0 + ), "Width must be a multiple of 8 to alow byte addressing to host" + assert log2( + mem["width"] + ).is_integer(), "Width must be a power of 2 to be correctly described by xSIZE" assert mem["size"] > 0, "Memory size must be greater than 0" + if __name__ == "__main__": build().emit() From 7498f389568a10baef370c353b0849ca5a0340ff Mon Sep 17 00:00:00 2001 From: Nathaniel Navarro Date: Fri, 2 Feb 2024 12:46:43 -0500 Subject: [PATCH 02/23] Add clearer exceptions in invokes --- calyx-py/calyx/py_ast.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/calyx-py/calyx/py_ast.py b/calyx-py/calyx/py_ast.py index 5b91d42c50..ca13c90dce 100644 --- a/calyx-py/calyx/py_ast.py +++ b/calyx-py/calyx/py_ast.py @@ -371,7 +371,11 @@ def doc(self) -> str: # Add ref cells if present if len(self.ref_cells) > 0: - rcs = ", ".join([f"{n}={arg.doc()}" for (n, arg) in self.ref_cells]) + rcs = "" + try: + rcs = ", ".join([f"{n}={arg.doc()}" for (n, arg) in self.ref_cells]) + except AttributeError as e: + raise AttributeError(f"Ensure you are passing in a cell to any `ref_` arguments (as opposed to ,say, ports).") inv += f"[{rcs}]" # Inputs and outputs From d12071fdb18fd994122f14eea907fcbcdb3b867f Mon Sep 17 00:00:00 2001 From: Nathaniel Navarro Date: Fri, 2 Feb 2024 15:01:25 -0500 Subject: [PATCH 03/23] get invoke of channels working --- yxi/axi-calyx/axi-generator.py | 61 +++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/yxi/axi-calyx/axi-generator.py b/yxi/axi-calyx/axi-generator.py index 2e1cbc2729..40f4fabcc4 100644 --- a/yxi/axi-calyx/axi-generator.py +++ b/yxi/axi-calyx/axi-generator.py @@ -19,17 +19,17 @@ { "name": "A0", "width": 32, - "size": 8 + "size": 16 }, { "name": "B0", "width": 32, - "size": 8 + "size": 16 }, { - "name": "v0", + "name": "Sum0", "width": 32, - "size": 1 + "size": 16 } ] } @@ -458,12 +458,10 @@ def add_main_comp(prog, mems): bresp_channel = prog.get_component("m_bresp_channel") # Inputs/Outputs - main_inputs = [] - main_outputs = [] + main_control = [] for mem in mems: mem_name = mem["name"] - main_inputs.append( - [ + main_inputs = [ (f"{mem_name}_ARESETn", 1), (f"{mem_name}_ARREADY", 1), (f"{mem_name}_RVALID", 1), @@ -479,9 +477,8 @@ def add_main_comp(prog, mems): # Only needed for coctb compatability, tied low (f"{mem_name}_RID", 1), ] - ) - main_outputs.append( - [ + + main_outputs = [ (f"{mem_name}_ARVALID", 1), (f"{mem_name}_ARADDR", 64), (f"{mem_name}_ARSIZE", 3), @@ -504,7 +501,7 @@ def add_main_comp(prog, mems): (f"{mem_name}_WID", 1), (f"{mem_name}_BID", 1), ] - ) + add_comp_params(main_comp, main_inputs, main_outputs) @@ -515,7 +512,7 @@ def add_main_comp(prog, mems): ) curr_addr_axi = main_comp.reg(f"curr_addr_axi_{mem_name}", 64) - main_comp.cell(f"ar_channel_{mem_name}", ar_channel) + ar_channel_cell = main_comp.cell(f"ar_channel_{mem_name}", ar_channel) main_comp.cell(f"read_channel_{mem_name}", read_channel) # TODO: Don't think these need to be marked external, but we @@ -533,7 +530,7 @@ def add_main_comp(prog, mems): max_transfers = main_comp.reg(f"max_transfers_{mem_name}", 8) main_comp.cell(f"aw_channel_{mem_name}", aw_channel) main_comp.cell(f"write_channel_{mem_name}", write_channel) - main_comp.cel(f"bresp_channel_{mem_name}", bresp_channel) + main_comp.cell(f"bresp_channel_{mem_name}", bresp_channel) # Wires @@ -550,8 +547,41 @@ def add_main_comp(prog, mems): # No groups needed! + # set up internal control blocks + #TODO: turn these into parts of a par block + this_component = main_comp.this() + ar_channel_invoke = invoke( + # main_comp.get_cell(f"ar_channel_{mem_name}"), + main_comp.get_cell(f"ar_channel_{mem_name}"), + ref_curr_addr_axi=curr_addr_axi, + in_ARESETn=this_component[f"{mem_name}_ARESETn"], + in_ARREADY=this_component[f"{mem_name}_ARREADY"], + out_AVALID=this_component[f"{mem_name}_ARVALID"], + out_ARADDR=this_component[f"{mem_name}_ARADDR"], + out_ARSIZE=this_component[f"{mem_name}_ARSIZE"], + out_ARLEN=this_component[f"{mem_name}_ARLEN"], + out_ARBURST=this_component[f"{mem_name}_ARBURST"], + ) + read_channel_invoke = invoke( + main_comp.get_cell(f"read_channel_{mem_name}"), + ref_mem_ref = internal_mem, + ref_curr_addr_internal_mem = curr_addr_internal_mem, + ref_curr_addr_axi = curr_addr_axi, + in_ARESETn = this_component[f"{mem_name}_ARESETn"], + in_RVALID = this_component[f"{mem_name}_RVALID"], + in_RLAST = this_component[f"{mem_name}_RLAST"], + in_RDATA = this_component[f"{mem_name}_RDATA"], + #TODO: Do we need this? Don't think this goes anywhere + in_RRESP = this_component[f"{mem_name}_RRESP"], + out_RREADY = this_component[f"{mem_name}_RREADY"] + ) + #TODO: We want to have a par block of 3 sequences. + #The below creates a seq of 3 par blocks of sequences + par_block = par([ar_channel_invoke, read_channel_invoke]) + main_comp.control += [par_block] + # Control - init_par = par() + # init_par = par() # TODO!!!: Move par of seqs outside of for loop # look at how to append to par blocks @@ -583,6 +613,7 @@ def build(): add_read_channel(prog, mems[0]) add_write_channel(prog, mems[0]) add_bresp_channel(prog, mems[0]) + add_main_comp(prog, mems) return prog.program From e8cb4ba5f6c71ffe9d9100a28752553ad77122f3 Mon Sep 17 00:00:00 2001 From: Nathaniel Navarro Date: Sun, 4 Feb 2024 22:47:10 -0500 Subject: [PATCH 04/23] Add `*=` for par block control composition --- calyx-py/calyx/builder.py | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/calyx-py/calyx/builder.py b/calyx-py/calyx/builder.py index 2b444a499b..f752a2f8ab 100644 --- a/calyx-py/calyx/builder.py +++ b/calyx-py/calyx/builder.py @@ -1,7 +1,7 @@ from __future__ import annotations import threading -from typing import Dict, Union, Optional, List +from typing import Dict, Union, Optional, List, Type from dataclasses import dataclass from . import py_ast as ast @@ -897,22 +897,38 @@ def __init__(self, stmt=None): def __add__(self, other): """Build sequential composition.""" other_stmt = as_control(other) + return self._flatten_comp(self.stmt, other_stmt, ast.SeqComp) + def __mul__(self, other): + """Build parallel composition.""" + other_stmt = as_control(other) + return self._flatten_comp(self.stmt, other_stmt, ast.ParComp) + + def _flatten_comp( + self, + self_stmt: Union[ast.SeqComp, ast.ParComp], + other_stmt: Union[ast.SeqComp, ast.ParComp], + return_type: Type[Union[ast.SeqComp, ast.ParComp]], + ): + """Keeps `seq`s and `par`s flat if types match. + So something like `seq{seq{A,B}}` remains `seq{A;B}`. + `return_type` should be the class name we are interested in flattening. + """ + # One side empty if isinstance(self.stmt, ast.Empty): - # Special cases for when one side is empty. return ControlBuilder(other_stmt) elif isinstance(other_stmt, ast.Empty): return self - elif isinstance(self.stmt, ast.SeqComp) and isinstance(other_stmt, ast.SeqComp): - # Special cases for when we already have at least one seq. - return ControlBuilder(ast.SeqComp(self.stmt.stmts + other_stmt.stmts)) - elif isinstance(self.stmt, ast.SeqComp): - return ControlBuilder(ast.SeqComp(self.stmt.stmts + [other_stmt])) - elif isinstance(other_stmt, ast.SeqComp): - return ControlBuilder(ast.SeqComp([self.stmt] + other_stmt.stmts)) + # At least one side is of type `return_type` + elif isinstance(self.stmt, return_type) and isinstance(other_stmt, return_type): + return ControlBuilder(return_type(self.stmt.stmts + other_stmt.stmts)) + elif isinstance(self.stmt, return_type): + return ControlBuilder(return_type(self.stmt.stmts + [other_stmt])) + elif isinstance(other_stmt, return_type): + return ControlBuilder(return_type([self.stmt] + other_stmt.stmts)) + # General case else: - # General case. - return ControlBuilder(ast.SeqComp([self.stmt, as_control(other)])) + return ControlBuilder(return_type([self.stmt, other_stmt])) class ExprBuilder: From 7262a5459b6e58711baf607d8e979951273d137f Mon Sep 17 00:00:00 2001 From: Nathaniel Navarro Date: Mon, 5 Feb 2024 16:58:38 -0500 Subject: [PATCH 05/23] Add small runt test for `+=` and `*=` --- calyx-py/test/par.expect | 38 ++++++++++++++++++++++++++++++++++++++ calyx-py/test/par.py | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 calyx-py/test/par.expect create mode 100644 calyx-py/test/par.py diff --git a/calyx-py/test/par.expect b/calyx-py/test/par.expect new file mode 100644 index 0000000000..11f7a508f4 --- /dev/null +++ b/calyx-py/test/par.expect @@ -0,0 +1,38 @@ +import "primitives/core.futil"; +component my_comp() -> () { + cells { + + } + wires { + group my_group { + + } + group my_group2 { + + } + group my_group3 { + + } + } + control { + par { + seq { + par { + my_group; + my_group2; + my_group3; + my_group2; + my_group3; + } + my_group; + my_group2; + my_group; + my_group3; + } + seq { + my_group2; + my_group3; + } + } + } +} diff --git a/calyx-py/test/par.py b/calyx-py/test/par.py new file mode 100644 index 0000000000..58db1e0f9e --- /dev/null +++ b/calyx-py/test/par.py @@ -0,0 +1,37 @@ +# This is meant to test the `__add__` and `__mul__` functionality of +# the builder's ControlBuilder. In particular we look at `+=` and `*=` +from calyx.builder import ( + Builder, + par, +) + + +def add_par_thing(prog): + my_comp = prog.component("my_comp") + + my_group = my_comp.group("my_group") + my_group2 = my_comp.group("my_group2") + my_group3 = my_comp.group("my_group3") + + my_par = par(my_group2, my_group3) + + my_comp.control *= my_group + # Make sure that an ast.ParComp and CompBuilder par get flattened. + my_comp.control *= my_par + my_comp.control *= par(my_group2, my_group3) + # Turn into seq of par group then [my_group, my_group2] + my_comp.control += [my_group, my_group2] + my_comp.control += my_group + my_comp.control += [my_group3] + # Check going from seq to par block + my_comp.control *= [my_group2, my_group3] + + +def build(): + prog = Builder() + add_par_thing(prog) + return prog.program + + +if __name__ == "__main__": + build().emit() From f558c283b564b26e04bbad3ed101dee13ed9e6fa Mon Sep 17 00:00:00 2001 From: Nathaniel Navarro Date: Mon, 5 Feb 2024 17:19:55 -0500 Subject: [PATCH 06/23] add some documentation about *= --- docs/builder/ref.md | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/docs/builder/ref.md b/docs/builder/ref.md index 423da6db87..6cae987f53 100644 --- a/docs/builder/ref.md +++ b/docs/builder/ref.md @@ -257,11 +257,14 @@ my_component.control += my_component.get_group("my_group") ### `seq` -Control statements are [sequenced][seq] in the order that they appear in a component's control program, represented by a Python list. Let's say we want to sequence the control statements `A`, `B`, and `C`. +Control statements are [sequenced][seq] in the order that they appear in a component's +control program, represented by a Python list. +Let's say we want to sequence the control statements `A`, `B`, and `C`. ```python my_component.control += [A, B, C] ``` +It's worth nothing that sequential usage of `+=` will not nest `seq` blocks unless necessary. ### `par` @@ -271,6 +274,30 @@ For [parallel compositions][par] of control programs, use the `par()` function. my_component.control += [par(A, B), C] ``` +Alternatively, we can construct `par` blocks by using `*=`. To compose the same control program as above, with `A` and `B` in parallel and sequencing this composition before `C` we could write: + +```python +my_component.control *= par(A,B) +my_component.control += C +``` + +### Chaining `seq` and `par` + +It is possible to chain together sequential usage of `+=` and `*=` to construct a control program ``sequentially''. +In this way, the control program's construction in the builder will look similar to the control program in the output Calyx. For example, +the previous python example in the `par` section will produce a par block of `A` and `B`, which is sequenced before `C`: + +``` +seq{ + par{ + A; + B; + } + C; +} + +``` + ### `if` See the language reference for [`if`][if]. From b507509447c15d919d72f95ac22ce44216454d55 Mon Sep 17 00:00:00 2001 From: Nathaniel Navarro Date: Thu, 8 Feb 2024 20:36:25 -0500 Subject: [PATCH 07/23] better exceptions py builder --- calyx-py/calyx/py_ast.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/calyx-py/calyx/py_ast.py b/calyx-py/calyx/py_ast.py index ca13c90dce..d96bf5b7b3 100644 --- a/calyx-py/calyx/py_ast.py +++ b/calyx-py/calyx/py_ast.py @@ -375,7 +375,7 @@ def doc(self) -> str: try: rcs = ", ".join([f"{n}={arg.doc()}" for (n, arg) in self.ref_cells]) except AttributeError as e: - raise AttributeError(f"Ensure you are passing in a cell to any `ref_` arguments (as opposed to ,say, ports).") + raise AttributeError(f"`ref_` arguments must be a cell, but a {type(arg)} was passed in.") inv += f"[{rcs}]" # Inputs and outputs From c626bc0f40c9c691823a8cd9bedbefbc40da318f Mon Sep 17 00:00:00 2001 From: Nathaniel Navarro Date: Mon, 12 Feb 2024 00:04:34 -0500 Subject: [PATCH 08/23] AXI generator that creates a valid calyx program TBD If the spec is implemented properly (probably not) TODO: 1. Get AXI working with cocotb tests 2. Hook up runt tests 3. Refactor/minimize generator code 4... Fud2 integration? --- yxi/axi-calyx/axi-generator.py | 103 +++++++++++++++++++++++++-------- 1 file changed, 80 insertions(+), 23 deletions(-) diff --git a/yxi/axi-calyx/axi-generator.py b/yxi/axi-calyx/axi-generator.py index 40f4fabcc4..0ed79dcb7f 100644 --- a/yxi/axi-calyx/axi-generator.py +++ b/yxi/axi-calyx/axi-generator.py @@ -268,7 +268,7 @@ def add_read_channel(prog, mem): mem_ref.addr0 = curr_addr_internal_mem.out mem_ref.write_data = read_data_reg.out mem_ref.write_en = 1 - service_read_transfer.done = mem_ref.done + service_read_transfer.done = mem_ref.write_done # creates group that increments curr_addr_internal_mem by 1. Creates adder and wires up correctly curr_addr_internal_mem_incr = read_channel.incr(curr_addr_internal_mem, 1) @@ -330,7 +330,7 @@ def add_write_channel(prog, mem): curr_trsnfr_count = write_channel.reg("curr_trsnfr_count", 8) # Number of transfers we want to do in current txn - max_trnsfrs = write_channel.reg("max_trnsfrs", 8, is_ref=True) + max_transfers = write_channel.reg("max_transfers", 8, is_ref=True) # Register because w last is high with last transfer. Before this # We were terminating immediately with last transfer and not servicing it. @@ -361,15 +361,15 @@ def add_write_channel(prog, mem): mem_ref.read_en = 1 write_channel.this()["WDATA"] = mem_ref.read_data - write_channel.this()["WLAST"] = (max_trnsfrs.out == curr_trsnfr_count.out) @ 1 - write_channel.this()["WLAST"] = (max_trnsfrs.out != curr_trsnfr_count.out) @ 0 + write_channel.this()["WLAST"] = (max_transfers.out == curr_trsnfr_count.out) @ 1 + write_channel.this()["WLAST"] = (max_transfers.out != curr_trsnfr_count.out) @ 0 # set high when WLAST is high and a handshake occurs n_finished_last_trnsfr.in_ = ( - (max_trnsfrs.out == curr_trsnfr_count.out) & (wvalid.out & WREADY) + (max_transfers.out == curr_trsnfr_count.out) & (wvalid.out & WREADY) ) @ 0 n_finished_last_trnsfr.write_en = ( - (max_trnsfrs.out == curr_trsnfr_count.out) & (wvalid.out & WREADY) + (max_transfers.out == curr_trsnfr_count.out) & (wvalid.out & WREADY) ) @ 1 # done after handshake @@ -457,10 +457,13 @@ def add_main_comp(prog, mems): aw_channel = prog.get_component("m_aw_channel") bresp_channel = prog.get_component("m_bresp_channel") - # Inputs/Outputs - main_control = [] + curr_addr_axi_par = [] + curr_addr_internal_par = [] + reads_par = [] + writes_par = [] for mem in mems: mem_name = mem["name"] + # Inputs/Outputs main_inputs = [ (f"{mem_name}_ARESETn", 1), (f"{mem_name}_ARREADY", 1), @@ -550,18 +553,20 @@ def add_main_comp(prog, mems): # set up internal control blocks #TODO: turn these into parts of a par block this_component = main_comp.this() + ar_channel_invoke = invoke( - # main_comp.get_cell(f"ar_channel_{mem_name}"), - main_comp.get_cell(f"ar_channel_{mem_name}"), - ref_curr_addr_axi=curr_addr_axi, - in_ARESETn=this_component[f"{mem_name}_ARESETn"], - in_ARREADY=this_component[f"{mem_name}_ARREADY"], - out_AVALID=this_component[f"{mem_name}_ARVALID"], - out_ARADDR=this_component[f"{mem_name}_ARADDR"], - out_ARSIZE=this_component[f"{mem_name}_ARSIZE"], - out_ARLEN=this_component[f"{mem_name}_ARLEN"], - out_ARBURST=this_component[f"{mem_name}_ARBURST"], - ) + # main_comp.get_cell(f"ar_channel_{mem_name}"), + main_comp.get_cell(f"ar_channel_{mem_name}"), + ref_curr_addr_axi=curr_addr_axi, + in_ARESETn=this_component[f"{mem_name}_ARESETn"], + in_ARREADY=this_component[f"{mem_name}_ARREADY"], + out_ARVALID=this_component[f"{mem_name}_ARVALID"], + out_ARADDR=this_component[f"{mem_name}_ARADDR"], + out_ARSIZE=this_component[f"{mem_name}_ARSIZE"], + out_ARLEN=this_component[f"{mem_name}_ARLEN"], + out_ARBURST=this_component[f"{mem_name}_ARBURST"] + ) + read_channel_invoke = invoke( main_comp.get_cell(f"read_channel_{mem_name}"), ref_mem_ref = internal_mem, @@ -575,11 +580,63 @@ def add_main_comp(prog, mems): in_RRESP = this_component[f"{mem_name}_RRESP"], out_RREADY = this_component[f"{mem_name}_RREADY"] ) - #TODO: We want to have a par block of 3 sequences. - #The below creates a seq of 3 par blocks of sequences - par_block = par([ar_channel_invoke, read_channel_invoke]) - main_comp.control += [par_block] + aw_channel_invoke = invoke( + main_comp.get_cell(f"aw_channel_{mem_name}"), + ref_curr_addr_axi = curr_addr_axi, + ref_max_transfers = max_transfers, + in_ARESETn = this_component[f"{mem_name}_ARESETn"], + in_AWREADY = this_component[f"{mem_name}_AWREADY"], + out_AWVALID = this_component[f"{mem_name}_AWVALID"], + out_AWADDR = this_component[f"{mem_name}_AWADDR"], + out_AWSIZE = this_component[f"{mem_name}_AWSIZE"], + out_AWLEN = this_component[f"{mem_name}_AWLEN"], + out_AWBURST = this_component[f"{mem_name}_AWBURST"], + out_AWPROT = this_component[f"{mem_name}_AWPROT"] + ) + + write_channel_invoke = invoke( + main_comp.get_cell(f"write_channel_{mem_name}"), + ref_mem_ref = internal_mem, + ref_curr_addr_internal_mem = curr_addr_internal_mem, + ref_curr_addr_axi = curr_addr_axi, + ref_max_transfers = max_transfers, + in_ARESETn = this_component[f"{mem_name}_ARESETn"], + in_WREADY = this_component[f"{mem_name}_WREADY"], + out_WVALID = this_component[f"{mem_name}_WVALID"], + out_WLAST = this_component[f"{mem_name}_WLAST"], + out_WDATA = this_component[f"{mem_name}_WDATA"], + ) + + bresp_channel_invoke = invoke( + main_comp.get_cell(f"bresp_channel_{mem_name}"), + in_BVALID = this_component[f"{mem_name}_BVALID"], + out_BREADY = this_component[f"{mem_name}_BREADY"] + ) + + curr_addr_axi_invoke = invoke( + curr_addr_axi, in_in=0x1000 + ) + curr_addr_internal_invoke = invoke( + curr_addr_internal_mem, in_in=0x0000 + ) + + curr_addr_axi_par.append(curr_addr_axi_invoke) + curr_addr_internal_par.append(curr_addr_internal_invoke) + reads_par.append([ar_channel_invoke, read_channel_invoke]) + writes_par.append([aw_channel_invoke, write_channel_invoke, bresp_channel_invoke]) + + #Compiler should reschedule these 2 seqs to be in parallel right? + main_comp.control += par(*curr_addr_axi_par) + main_comp.control += par(*curr_addr_internal_par) + + main_comp.control += par(*reads_par) + # main_comp.control += TODO: vec_add_cell_invoke + # Reset axi adress to 0 + main_comp.control += par(*curr_addr_axi_par) + main_comp.control += par(*writes_par) + + # Control # init_par = par() # TODO!!!: Move par of seqs outside of for loop From eb3e4518f9208c27c727abe097bbeef0d1d6921e Mon Sep 17 00:00:00 2001 From: Nathaniel Navarro Date: Mon, 12 Feb 2024 00:14:54 -0500 Subject: [PATCH 09/23] Revert "Merge branch 'builder-qol' into py-axi-gen-main" This reverts commit a9628ce8cc8eafa854d245282667af7e49210ba4, reversing changes made to 7a28be662644c2a772f6274ee7a3ac11ee5fed63. --- calyx-py/calyx/builder.py | 38 +++++++++++--------------------------- calyx-py/calyx/py_ast.py | 6 +----- calyx-py/test/par.expect | 38 -------------------------------------- calyx-py/test/par.py | 37 ------------------------------------- docs/builder/ref.md | 29 +---------------------------- 5 files changed, 13 insertions(+), 135 deletions(-) delete mode 100644 calyx-py/test/par.expect delete mode 100644 calyx-py/test/par.py diff --git a/calyx-py/calyx/builder.py b/calyx-py/calyx/builder.py index 65f1a3617c..bbfea8ac7a 100644 --- a/calyx-py/calyx/builder.py +++ b/calyx-py/calyx/builder.py @@ -1,7 +1,7 @@ from __future__ import annotations import threading -from typing import Dict, Union, Optional, List, Type +from typing import Dict, Union, Optional, List from dataclasses import dataclass from . import py_ast as ast @@ -898,38 +898,22 @@ def __init__(self, stmt=None): def __add__(self, other): """Build sequential composition.""" other_stmt = as_control(other) - return self._flatten_comp(self.stmt, other_stmt, ast.SeqComp) - def __mul__(self, other): - """Build parallel composition.""" - other_stmt = as_control(other) - return self._flatten_comp(self.stmt, other_stmt, ast.ParComp) - - def _flatten_comp( - self, - self_stmt: Union[ast.SeqComp, ast.ParComp], - other_stmt: Union[ast.SeqComp, ast.ParComp], - return_type: Type[Union[ast.SeqComp, ast.ParComp]], - ): - """Keeps `seq`s and `par`s flat if types match. - So something like `seq{seq{A,B}}` remains `seq{A;B}`. - `return_type` should be the class name we are interested in flattening. - """ - # One side empty if isinstance(self.stmt, ast.Empty): + # Special cases for when one side is empty. return ControlBuilder(other_stmt) elif isinstance(other_stmt, ast.Empty): return self - # At least one side is of type `return_type` - elif isinstance(self.stmt, return_type) and isinstance(other_stmt, return_type): - return ControlBuilder(return_type(self.stmt.stmts + other_stmt.stmts)) - elif isinstance(self.stmt, return_type): - return ControlBuilder(return_type(self.stmt.stmts + [other_stmt])) - elif isinstance(other_stmt, return_type): - return ControlBuilder(return_type([self.stmt] + other_stmt.stmts)) - # General case + elif isinstance(self.stmt, ast.SeqComp) and isinstance(other_stmt, ast.SeqComp): + # Special cases for when we already have at least one seq. + return ControlBuilder(ast.SeqComp(self.stmt.stmts + other_stmt.stmts)) + elif isinstance(self.stmt, ast.SeqComp): + return ControlBuilder(ast.SeqComp(self.stmt.stmts + [other_stmt])) + elif isinstance(other_stmt, ast.SeqComp): + return ControlBuilder(ast.SeqComp([self.stmt] + other_stmt.stmts)) else: - return ControlBuilder(return_type([self.stmt, other_stmt])) + # General case. + return ControlBuilder(ast.SeqComp([self.stmt, as_control(other)])) class ExprBuilder: diff --git a/calyx-py/calyx/py_ast.py b/calyx-py/calyx/py_ast.py index 2d932349ae..e22fdf894d 100644 --- a/calyx-py/calyx/py_ast.py +++ b/calyx-py/calyx/py_ast.py @@ -371,11 +371,7 @@ def doc(self) -> str: # Add ref cells if present if len(self.ref_cells) > 0: - rcs = "" - try: - rcs = ", ".join([f"{n}={arg.doc()}" for (n, arg) in self.ref_cells]) - except AttributeError as e: - raise AttributeError(f"`ref_` arguments must be a cell, but a {type(arg)} was passed in.") + rcs = ", ".join([f"{n}={arg.doc()}" for (n, arg) in self.ref_cells]) inv += f"[{rcs}]" # Inputs and outputs diff --git a/calyx-py/test/par.expect b/calyx-py/test/par.expect deleted file mode 100644 index 11f7a508f4..0000000000 --- a/calyx-py/test/par.expect +++ /dev/null @@ -1,38 +0,0 @@ -import "primitives/core.futil"; -component my_comp() -> () { - cells { - - } - wires { - group my_group { - - } - group my_group2 { - - } - group my_group3 { - - } - } - control { - par { - seq { - par { - my_group; - my_group2; - my_group3; - my_group2; - my_group3; - } - my_group; - my_group2; - my_group; - my_group3; - } - seq { - my_group2; - my_group3; - } - } - } -} diff --git a/calyx-py/test/par.py b/calyx-py/test/par.py deleted file mode 100644 index 58db1e0f9e..0000000000 --- a/calyx-py/test/par.py +++ /dev/null @@ -1,37 +0,0 @@ -# This is meant to test the `__add__` and `__mul__` functionality of -# the builder's ControlBuilder. In particular we look at `+=` and `*=` -from calyx.builder import ( - Builder, - par, -) - - -def add_par_thing(prog): - my_comp = prog.component("my_comp") - - my_group = my_comp.group("my_group") - my_group2 = my_comp.group("my_group2") - my_group3 = my_comp.group("my_group3") - - my_par = par(my_group2, my_group3) - - my_comp.control *= my_group - # Make sure that an ast.ParComp and CompBuilder par get flattened. - my_comp.control *= my_par - my_comp.control *= par(my_group2, my_group3) - # Turn into seq of par group then [my_group, my_group2] - my_comp.control += [my_group, my_group2] - my_comp.control += my_group - my_comp.control += [my_group3] - # Check going from seq to par block - my_comp.control *= [my_group2, my_group3] - - -def build(): - prog = Builder() - add_par_thing(prog) - return prog.program - - -if __name__ == "__main__": - build().emit() diff --git a/docs/builder/ref.md b/docs/builder/ref.md index 6cae987f53..423da6db87 100644 --- a/docs/builder/ref.md +++ b/docs/builder/ref.md @@ -257,14 +257,11 @@ my_component.control += my_component.get_group("my_group") ### `seq` -Control statements are [sequenced][seq] in the order that they appear in a component's -control program, represented by a Python list. -Let's say we want to sequence the control statements `A`, `B`, and `C`. +Control statements are [sequenced][seq] in the order that they appear in a component's control program, represented by a Python list. Let's say we want to sequence the control statements `A`, `B`, and `C`. ```python my_component.control += [A, B, C] ``` -It's worth nothing that sequential usage of `+=` will not nest `seq` blocks unless necessary. ### `par` @@ -274,30 +271,6 @@ For [parallel compositions][par] of control programs, use the `par()` function. my_component.control += [par(A, B), C] ``` -Alternatively, we can construct `par` blocks by using `*=`. To compose the same control program as above, with `A` and `B` in parallel and sequencing this composition before `C` we could write: - -```python -my_component.control *= par(A,B) -my_component.control += C -``` - -### Chaining `seq` and `par` - -It is possible to chain together sequential usage of `+=` and `*=` to construct a control program ``sequentially''. -In this way, the control program's construction in the builder will look similar to the control program in the output Calyx. For example, -the previous python example in the `par` section will produce a par block of `A` and `B`, which is sequenced before `C`: - -``` -seq{ - par{ - A; - B; - } - C; -} - -``` - ### `if` See the language reference for [`if`][if]. From ebec96c576344b2322fcbab5659c904cca5c71ee Mon Sep 17 00:00:00 2001 From: Nathaniel Navarro Date: Tue, 13 Feb 2024 23:20:58 -0500 Subject: [PATCH 10/23] Add invocation and instantiaion of main_compute --- yxi/axi-calyx/axi-generator.py | 81 ++++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 34 deletions(-) diff --git a/yxi/axi-calyx/axi-generator.py b/yxi/axi-calyx/axi-generator.py index 0ed79dcb7f..49785250cf 100644 --- a/yxi/axi-calyx/axi-generator.py +++ b/yxi/axi-calyx/axi-generator.py @@ -449,7 +449,7 @@ def add_bresp_channel(prog, mem): # NOTE: Unlike the channel functions, this can expect multiple mems def add_main_comp(prog, mems): - main_comp = prog.component("main") + wrapper_comp = prog.component("wrapper") # Get handles to be used later read_channel = prog.get_component("m_read_channel") write_channel = prog.get_component("m_write_channel") @@ -461,10 +461,15 @@ def add_main_comp(prog, mems): curr_addr_internal_par = [] reads_par = [] writes_par = [] + ref_mem_kwargs = {} + + #Create single main cell + main_compute = wrapper_comp.comp_instance("main_compute", "main", check_undeclared=False) + for mem in mems: mem_name = mem["name"] # Inputs/Outputs - main_inputs = [ + wrapper_inputs = [ (f"{mem_name}_ARESETn", 1), (f"{mem_name}_ARREADY", 1), (f"{mem_name}_RVALID", 1), @@ -481,7 +486,7 @@ def add_main_comp(prog, mems): (f"{mem_name}_RID", 1), ] - main_outputs = [ + wrapper_outputs = [ (f"{mem_name}_ARVALID", 1), (f"{mem_name}_ARADDR", 64), (f"{mem_name}_ARSIZE", 3), @@ -506,42 +511,41 @@ def add_main_comp(prog, mems): ] - add_comp_params(main_comp, main_inputs, main_outputs) + add_comp_params(wrapper_comp, wrapper_inputs, wrapper_outputs) # Cells # Read stuff - curr_addr_internal_mem = main_comp.reg( + curr_addr_internal_mem = wrapper_comp.reg( f"curr_addr_internal_mem_{mem_name}", clog2(mem["size"]) ) - curr_addr_axi = main_comp.reg(f"curr_addr_axi_{mem_name}", 64) + curr_addr_axi = wrapper_comp.reg(f"curr_addr_axi_{mem_name}", 64) - ar_channel_cell = main_comp.cell(f"ar_channel_{mem_name}", ar_channel) - main_comp.cell(f"read_channel_{mem_name}", read_channel) + wrapper_comp.cell(f"ar_channel_{mem_name}", ar_channel) + wrapper_comp.cell(f"read_channel_{mem_name}", read_channel) # TODO: Don't think these need to be marked external, but we # we need to raise them at some point form original calyx program - internal_mem = main_comp.seq_mem_d1( + internal_mem = wrapper_comp.seq_mem_d1( name=f"internal_mem_{mem_name}", bitwidth=mem["width"], len=mem["size"], idx_size=clog2(mem["size"]), ) - # TODO!!!: Add vec_add / computation kernel here # Write stuff - max_transfers = main_comp.reg(f"max_transfers_{mem_name}", 8) - main_comp.cell(f"aw_channel_{mem_name}", aw_channel) - main_comp.cell(f"write_channel_{mem_name}", write_channel) - main_comp.cell(f"bresp_channel_{mem_name}", bresp_channel) + max_transfers = wrapper_comp.reg(f"max_transfers_{mem_name}", 8) + wrapper_comp.cell(f"aw_channel_{mem_name}", aw_channel) + wrapper_comp.cell(f"write_channel_{mem_name}", write_channel) + wrapper_comp.cell(f"bresp_channel_{mem_name}", bresp_channel) # Wires # Tie IDs low, needed for cocotb compatability. Not used anywhere - ARID = main_comp.this()[f"{mem_name}_ARID"] - AWID = main_comp.this()[f"{mem_name}_AWID"] - WID = main_comp.this()[f"{mem_name}_WID"] - BID = main_comp.this()[f"{mem_name}_BID"] + ARID = wrapper_comp.this()[f"{mem_name}_ARID"] + AWID = wrapper_comp.this()[f"{mem_name}_AWID"] + WID = wrapper_comp.this()[f"{mem_name}_WID"] + BID = wrapper_comp.this()[f"{mem_name}_BID"] ARID = 0 AWID = 0 @@ -552,11 +556,11 @@ def add_main_comp(prog, mems): # set up internal control blocks #TODO: turn these into parts of a par block - this_component = main_comp.this() + this_component = wrapper_comp.this() ar_channel_invoke = invoke( # main_comp.get_cell(f"ar_channel_{mem_name}"), - main_comp.get_cell(f"ar_channel_{mem_name}"), + wrapper_comp.get_cell(f"ar_channel_{mem_name}"), ref_curr_addr_axi=curr_addr_axi, in_ARESETn=this_component[f"{mem_name}_ARESETn"], in_ARREADY=this_component[f"{mem_name}_ARREADY"], @@ -568,7 +572,7 @@ def add_main_comp(prog, mems): ) read_channel_invoke = invoke( - main_comp.get_cell(f"read_channel_{mem_name}"), + wrapper_comp.get_cell(f"read_channel_{mem_name}"), ref_mem_ref = internal_mem, ref_curr_addr_internal_mem = curr_addr_internal_mem, ref_curr_addr_axi = curr_addr_axi, @@ -582,7 +586,7 @@ def add_main_comp(prog, mems): ) aw_channel_invoke = invoke( - main_comp.get_cell(f"aw_channel_{mem_name}"), + wrapper_comp.get_cell(f"aw_channel_{mem_name}"), ref_curr_addr_axi = curr_addr_axi, ref_max_transfers = max_transfers, in_ARESETn = this_component[f"{mem_name}_ARESETn"], @@ -596,7 +600,7 @@ def add_main_comp(prog, mems): ) write_channel_invoke = invoke( - main_comp.get_cell(f"write_channel_{mem_name}"), + wrapper_comp.get_cell(f"write_channel_{mem_name}"), ref_mem_ref = internal_mem, ref_curr_addr_internal_mem = curr_addr_internal_mem, ref_curr_addr_axi = curr_addr_axi, @@ -609,7 +613,7 @@ def add_main_comp(prog, mems): ) bresp_channel_invoke = invoke( - main_comp.get_cell(f"bresp_channel_{mem_name}"), + wrapper_comp.get_cell(f"bresp_channel_{mem_name}"), in_BVALID = this_component[f"{mem_name}_BVALID"], out_BREADY = this_component[f"{mem_name}_BREADY"] ) @@ -625,22 +629,31 @@ def add_main_comp(prog, mems): curr_addr_internal_par.append(curr_addr_internal_invoke) reads_par.append([ar_channel_invoke, read_channel_invoke]) writes_par.append([aw_channel_invoke, write_channel_invoke, bresp_channel_invoke]) + #Creates ` = internal_mem_` as refs in invocation of `main_compute` + ref_mem_kwargs[f"ref_{mem_name}"] = internal_mem + + + #Compute invoke + #Assumes refs should be of form ` = internal_mem_` + main_compute_invoke = invoke( + wrapper_comp.get_cell("main_compute"), + **ref_mem_kwargs + ) + #Compiler should reschedule these 2 seqs to be in parallel right? - main_comp.control += par(*curr_addr_axi_par) - main_comp.control += par(*curr_addr_internal_par) + wrapper_comp.control += par(*curr_addr_axi_par) + wrapper_comp.control += par(*curr_addr_internal_par) - main_comp.control += par(*reads_par) - # main_comp.control += TODO: vec_add_cell_invoke + wrapper_comp.control += par(*reads_par) + wrapper_comp.control += main_compute_invoke # Reset axi adress to 0 - main_comp.control += par(*curr_addr_axi_par) - main_comp.control += par(*writes_par) + wrapper_comp.control += par(*curr_addr_axi_par) + wrapper_comp.control += par(*writes_par) - # Control - # init_par = par() - # TODO!!!: Move par of seqs outside of for loop - # look at how to append to par blocks + + # Helper functions From 75dafbb8230795b86a06c30c0ed958f0d1096882 Mon Sep 17 00:00:00 2001 From: Nathaniel Navarro Date: Sat, 17 Feb 2024 15:52:30 -0500 Subject: [PATCH 11/23] Attempt at generalizing attributes --- calyx-py/calyx/builder.py | 6 ++++++ calyx-py/calyx/py_ast.py | 27 ++++++++++++++++++++++----- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/calyx-py/calyx/builder.py b/calyx-py/calyx/builder.py index bbfea8ac7a..159e3ce686 100644 --- a/calyx-py/calyx/builder.py +++ b/calyx-py/calyx/builder.py @@ -76,6 +76,7 @@ def __init__( self.prog = prog self.component: ast.Component = ast.Component( name, + attributes = [], inputs=[], outputs=[], structs=cells, @@ -112,6 +113,11 @@ def output(self, name: str, size: int) -> ExprBuilder: self.component.outputs.append(ast.PortDef(ast.CompVar(name), size)) return self.this()[name] + def attribute(self, name: str, value: int) -> None: + """Declare an attribute on the component. + """ + self.component.attributes.append(ast.CompAttribute(name, value)) + def this(self) -> ThisBuilder: """Get a handle to the component's `this` cell. diff --git a/calyx-py/calyx/py_ast.py b/calyx-py/calyx/py_ast.py index 4b0269840a..bd8f24d4be 100644 --- a/calyx-py/calyx/py_ast.py +++ b/calyx-py/calyx/py_ast.py @@ -45,6 +45,7 @@ def doc(self) -> str: @dataclass class Component: name: str + attributes : list[Attribute] inputs: list[PortDef] outputs: list[PortDef] wires: list[Structure] @@ -55,15 +56,17 @@ class Component: def __init__( self, name: str, + attributes: list[CompAttribute], inputs: list[PortDef], outputs: list[PortDef], structs: list[Structure], controls: Control, latency: Optional[int] = None, ): + self.name = name + self.attributes = attributes self.inputs = inputs self.outputs = outputs - self.name = name self.controls = controls self.latency = latency @@ -85,16 +88,30 @@ def get_cell(self, name: str) -> Cell: def doc(self) -> str: ins = ", ".join([s.doc() for s in self.inputs]) outs = ", ".join([s.doc() for s in self.outputs]) - latency_annotation = ( - f"static<{self.latency}> " if self.latency is not None else "" - ) - signature = f"{latency_annotation}component {self.name}({ins}) -> ({outs})" + if(self.latency): + self.attributes.append(CompAttribute("static",self.latency)) + attribute_annotation = ", ".join(f"<{a.doc}>" for a in self.attributes) + signature = f"{attribute_annotation}component {self.name}({ins}) -> ({outs})" cells = block("cells", [c.doc() for c in self.cells]) wires = block("wires", [w.doc() for w in self.wires]) controls = block("control", [self.controls.doc()]) return block(signature, [cells, wires, controls]) +#Attribute +@dataclass +class Attribute(Emittable): + pass + +@dataclass +class CompAttribute(Attribute): + name: str + value: int + + def doc(self) -> str: + return f"\"{self.name}\"={self.value}" + + # Ports @dataclass class Port(Emittable): From e043f594463843ae91514df532a3fc1cb6fcccac Mon Sep 17 00:00:00 2001 From: Nathaniel Navarro Date: Sat, 17 Feb 2024 17:28:25 -0500 Subject: [PATCH 12/23] Add component attribute support to builder --- calyx-py/calyx/py_ast.py | 9 +++++---- calyx-py/test/example.py | 7 ++++++- calyx-py/test/if.py | 7 ++++++- docs/builder/ref.md | 15 +++++++++++++++ 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/calyx-py/calyx/py_ast.py b/calyx-py/calyx/py_ast.py index bd8f24d4be..a6d081242f 100644 --- a/calyx-py/calyx/py_ast.py +++ b/calyx-py/calyx/py_ast.py @@ -88,10 +88,11 @@ def get_cell(self, name: str) -> Cell: def doc(self) -> str: ins = ", ".join([s.doc() for s in self.inputs]) outs = ", ".join([s.doc() for s in self.outputs]) - if(self.latency): - self.attributes.append(CompAttribute("static",self.latency)) - attribute_annotation = ", ".join(f"<{a.doc}>" for a in self.attributes) - signature = f"{attribute_annotation}component {self.name}({ins}) -> ({outs})" + latency_annotation = ( + f"static<{self.latency}>" if self.latency is not None else "" + ) + attribute_annotation = f"<{', '.join([f'{a.doc()}' for a in self.attributes])}>" if self.attributes else "" + signature = f"{latency_annotation}component {self.name}{attribute_annotation}({ins}) -> ({outs})" cells = block("cells", [c.doc() for c in self.cells]) wires = block("wires", [w.doc() for w in self.wires]) controls = block("control", [self.controls.doc()]) diff --git a/calyx-py/test/example.py b/calyx-py/test/example.py index 83ad49ac19..f3986a0b98 100644 --- a/calyx-py/test/example.py +++ b/calyx-py/test/example.py @@ -63,7 +63,12 @@ # Create the component. main_component = Component( - name="main", inputs=[], outputs=[], structs=cells + wires, controls=controls + name="main", + attributes=[], + inputs=[], + outputs=[], + structs=cells + wires, + controls=controls, ) # Create the Calyx program. diff --git a/calyx-py/test/if.py b/calyx-py/test/if.py index d7f60600b6..6acf102cc6 100644 --- a/calyx-py/test/if.py +++ b/calyx-py/test/if.py @@ -42,7 +42,12 @@ controls = If(CompPort(lt, "out"), cond, Enable(true), Enable(false)) main_component = Component( - name="main", inputs=[], outputs=[], structs=cells + wires, controls=controls + name="main", + attributes=[], + inputs=[], + outputs=[], + structs=cells + wires, + controls=controls, ) # Create the Calyx program. diff --git a/docs/builder/ref.md b/docs/builder/ref.md index 423da6db87..787787dd74 100644 --- a/docs/builder/ref.md +++ b/docs/builder/ref.md @@ -93,6 +93,21 @@ def add_my_component(prog): Note that it's possible to [create a handle][hndl] to input and output ports. +### Defining Component Attributes + +Components can be given attributes. Similar to ports, just specify the name of the attribute and its value. +Note that `attribute(name, value)` does not create a handle to the attribute. + +```python +my_component.attribute("my_attribute", 1) +``` + +Will create a component that looks like: + +``` +component my_component<"my_attribute"=1>(...) -> (...) { +``` + ### Multi-Component Designs Calyx supports [multi-component designs][multi]. The [top-level example][top] demonstrates how to construct multi-component designs using the library. From 3e0a741b4ed9041e9c0d4ece4b39fdbfd1bb0050 Mon Sep 17 00:00:00 2001 From: Nathaniel Navarro Date: Sat, 17 Feb 2024 17:30:31 -0500 Subject: [PATCH 13/23] update invoke.py in test/ --- calyx-py/test/invoke.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/calyx-py/test/invoke.py b/calyx-py/test/invoke.py index fb46c34aba..a941abf735 100644 --- a/calyx-py/test/invoke.py +++ b/calyx-py/test/invoke.py @@ -37,6 +37,7 @@ foo_component = Component( name="foo", + attributes=[], inputs=[PortDef(CompVar("a"), 32)], outputs=[PortDef(CompVar("out"), 32)], structs=foo_cells + foo_wires, @@ -83,6 +84,7 @@ main_component = Component( name="main", + attributes=[], inputs=[], outputs=[], structs=cells + wires, From 0aadcfaef5e5e121a03e87c1acd4fd6406a9f42d Mon Sep 17 00:00:00 2001 From: Nathaniel Navarro Date: Sat, 17 Feb 2024 17:40:51 -0500 Subject: [PATCH 14/23] typo fix in docs --- docs/builder/ref.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/builder/ref.md b/docs/builder/ref.md index 787787dd74..b60c0c813a 100644 --- a/docs/builder/ref.md +++ b/docs/builder/ref.md @@ -96,7 +96,7 @@ Note that it's possible to [create a handle][hndl] to input and output ports. ### Defining Component Attributes Components can be given attributes. Similar to ports, just specify the name of the attribute and its value. -Note that `attribute(name, value)` does not create a handle to the attribute. +Note that `attribute(name, value)` does not return a handle to the attribute. ```python my_component.attribute("my_attribute", 1) From 5431d975038add82e8158c24b015ebfbdf830738 Mon Sep 17 00:00:00 2001 From: Nathaniel Navarro Date: Sat, 17 Feb 2024 17:44:04 -0500 Subject: [PATCH 15/23] Add toplevel attribute to `wrapper` --- yxi/axi-calyx/axi-generator.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/yxi/axi-calyx/axi-generator.py b/yxi/axi-calyx/axi-generator.py index 49785250cf..7e4efac097 100644 --- a/yxi/axi-calyx/axi-generator.py +++ b/yxi/axi-calyx/axi-generator.py @@ -450,6 +450,7 @@ def add_bresp_channel(prog, mem): # NOTE: Unlike the channel functions, this can expect multiple mems def add_main_comp(prog, mems): wrapper_comp = prog.component("wrapper") + wrapper_comp.attribute("toplevel",1) # Get handles to be used later read_channel = prog.get_component("m_read_channel") write_channel = prog.get_component("m_write_channel") @@ -465,6 +466,7 @@ def add_main_comp(prog, mems): #Create single main cell main_compute = wrapper_comp.comp_instance("main_compute", "main", check_undeclared=False) + for mem in mems: mem_name = mem["name"] From fcfa0c8866a39c348af64c37ea692fe58fd85216 Mon Sep 17 00:00:00 2001 From: Nathaniel Navarro Date: Sat, 17 Feb 2024 18:17:04 -0500 Subject: [PATCH 16/23] Make fake yxi align with vec-add memory widths --- yxi/axi-calyx/axi-generator.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yxi/axi-calyx/axi-generator.py b/yxi/axi-calyx/axi-generator.py index 7e4efac097..e73def9023 100644 --- a/yxi/axi-calyx/axi-generator.py +++ b/yxi/axi-calyx/axi-generator.py @@ -19,17 +19,17 @@ { "name": "A0", "width": 32, - "size": 16 + "size": 8 }, { "name": "B0", "width": 32, - "size": 16 + "size": 8 }, { "name": "Sum0", "width": 32, - "size": 16 + "size": 8 } ] } From 9f624ce281f200e6cf6fa5e569625c395eed0701 Mon Sep 17 00:00:00 2001 From: Nathaniel Navarro Date: Sat, 17 Feb 2024 18:23:19 -0500 Subject: [PATCH 17/23] Cleanup and add dynamic-wrapped-vec-add --- yxi/axi-calyx/axi-reads-calyx.futil | 374 ------------- yxi/axi-calyx/axi-writes-calyx.futil | 522 ------------------ yxi/axi-calyx/cocotb/Makefile | 2 +- yxi/axi-calyx/cocotb/sim.sh | 6 +- .../generated-axi-with-vec-add.futil | 457 +++++++++++++++ 5 files changed, 461 insertions(+), 900 deletions(-) delete mode 100644 yxi/axi-calyx/axi-reads-calyx.futil delete mode 100644 yxi/axi-calyx/axi-writes-calyx.futil create mode 100644 yxi/axi-calyx/generated-axi-with-vec-add.futil diff --git a/yxi/axi-calyx/axi-reads-calyx.futil b/yxi/axi-calyx/axi-reads-calyx.futil deleted file mode 100644 index b566cbe5c7..0000000000 --- a/yxi/axi-calyx/axi-reads-calyx.futil +++ /dev/null @@ -1,374 +0,0 @@ -// ### -// This file contains the components needed to perform read transacitons via AXI. -// Current goal is to create a cocotb testbench that tests correctness of this. -// See https://github.com/cucapra/calyx/issues/1733 for more information. -// -// This wrapper assumes it is part of a dot product computation with vectors of -// length 16 -// It assumes a bus data width of 32 -// This is largely a work in progress and as of Nov 20 2023 is not intended to -// actually be used for anything -// ### - -import "primitives/core.futil"; -import "primitives/memories/comb.futil"; -import "primitives/compile.futil"; -import "primitives/math.futil"; -import "primitives/memories/seq.futil"; - - -//this goes m->s unlike read channel -component m_arread_channel( - ARESET: 1, - ARREADY: 1 -) -> ( - ARVALID: 1, - // This needs to be 64, see link below `m_axi` section. - ARADDR: 64, - // 2^ARSIZE is bytes used in transfer. For memory-mapped AXI (which is what we - // are doing I believe), should match width of data bus (to shell?, so 32 wide? This - // is 3'b010) - // see https://docs.xilinx.com/r/en-US/ug1393-vitis-application-acceleration/Kernel-Interface-Requirements - // for restrictions - ARSIZE: 3, - // in AXI4 this is 8 bits, 1-256 transfers in requested transaction. - ARLEN : 8, - // 00 for fixed, 01 for incrementing, 2 for wrap, - // needs to be incr for RTL kernels (can't use wrapped of fixed - ARBURST : 2, - // required by spec. We hardwire this to priviliged access, non secure, data access. - ARPROT : 3) { - cells{ - is_arvalid = std_reg(1); - - // gets set high with ARVALID and remains high - arvalid_was_high = std_reg(1); - // TODO(nathanielnrn): should arguably eventually live in `s_axi_control` - // but for now will live here. - ref base_addr = std_reg(64); - - // number of trasfers in a transaction. This is sent to subordinate - txn_len = std_reg(8); - - // number of txns we want to occur before entire m_arread_channel is done - // this is internal to the channel (unlike txn_len) - txn_n = std_const(32,1); - txn_count = std_reg(32); - perform_reads = std_neq(32); - txn_adder = std_add(32); - - //"block_transfer" register. need to put into a reg to avoid combinational loops - bt_reg = std_reg(1); - - - } - - wires{ - - ARVALID = is_arvalid.out; - - group deassert_val { - is_arvalid.in = 1'b0; - is_arvalid.write_en = 1'b1; - deassert_val[done] = is_arvalid.done; - } - - group reset_bt { - bt_reg.in = 1'b0; - bt_reg.write_en = 1'b1; - reset_bt[done] = bt_reg.done; - } - - // this asserts valid and defines all inputs correctly - // because valid should not be deasserted until handshake occurs - // this all needs to be one group - // this contains blocking logic previously in its own group - group do_ar_transfer { - //assert ARVALID - is_arvalid.in = !(is_arvalid.out & ARREADY) & !arvalid_was_high.out ? 1'b1; - - // TODO(nathanielnrn): in theory should be able to get rid of arvalid_was_high - // but for now we will be explicit and reduce this in generation maybe. Not sure - // it even matters. - // This makes ARVALID go low after a single cycle. Without it it stays high for 2. - is_arvalid.in = is_arvalid.out & ARREADY & arvalid_was_high.out ? 1'b0; - is_arvalid.write_en = 1'b1; - - arvalid_was_high.in = !(is_arvalid.out & ARREADY) & !arvalid_was_high.out ? 1'b1; - arvalid_was_high.write_en = !(is_arvalid.out & ARREADY) & !arvalid_was_high.out ? 1'b1; - - - // drive output signals for transfer - ARADDR = base_addr.out; - // see link above, needs to match data width to host. - // In this case 2^2 = 4 bytes = 32 bits = width of our data_bus. - ARSIZE = 3'b010; - // For now this can be taken from .yxi, as size of mem, because we are assuming - // data_bus width that matches size of memory cells - // If we want to use bigger mems need to be able to update base addr - ARLEN = txn_len.out; - ARBURST = 2'b01; //incr - // privileged, non-secure, instruction access - ARPROT = 3'b110; - - - //done when one cycle after handshake (handshake happens for a single cycle) - bt_reg.in = ARREADY & is_arvalid.out ? 1'b1; - bt_reg.in = !(ARREADY & is_arvalid.out) ? 1'b0; - bt_reg.write_en = 1'b1; - do_ar_transfer[done] = bt_reg.out; - } - - - //txn bookkeeping. - //We are done performing reads when txn_count == txn_n - group txn_count_init { - txn_count.in = 32'b0; - txn_count.write_en = 1'b1; - txn_count_init[done] = txn_count.done; - - } - - group txn_len_init { - //TODO(nathanielnrn): 15 is good for word wide data bus. We'd - //expect 16 transfers. Number of transfers that occur is ARLEN + 1 - txn_len.in = 8'd15; - txn_len.write_en = 1'b1; - txn_len_init[done] = txn_len.done; - } - - group txn_incr { - txn_adder.left = txn_count.out; - txn_adder.right = 32'b1; - txn_count.in = txn_adder.out; - txn_count.write_en = 1'b1; - txn_incr[done] = txn_count.done; - - } - - comb group check_reads_done { - perform_reads.left = txn_count.out; - perform_reads.right = txn_n.out; - } - } - - control{ - //XXX(nathanielnrn): What is best way to offer more flexiblity beyond just a counter? - seq{ - txn_count_init; - txn_len_init; - while perform_reads.out with check_reads_done{ - seq{ - reset_bt; - do_ar_transfer; - deassert_val; - txn_incr; - } - } - } - } -} - - - - -component m_read_channel( - ARESET : 1, - RVALID : 1, - RLAST : 1, - RDATA : 32, - RRESP : 2, // Note: This is generated in subordinate! had this backwards in earlier version -) -> ( - // NOTE: In general, according to ZipCPU we want xREADY signals to be registered - // because (IIRC) it helps avoid combinational loops between READY and VALID. - RREADY : 1, -) { - cells { - // 16 is due to dot-product vector length assumption - // For this manual implementation we are just writing into this data based - // on the data we read from cocotb - ref data_received = seq_mem_d1(32, 16, 64); - is_rdy = std_reg(1); - ref curr_addr = std_reg(64); - - // registered because RLAST is high with last transfer, not after - // before this was registered we were terminating immediately with - // last transfer and not servicing it - n_RLAST = std_reg(1); - - // TODO: get this width from yxi - read_data_reg = std_reg(32); - - //address of seq_d1_mem we are writing to - curr_addr_adder = std_add(64); - - // block_transfer reg to avoid combinational loops - bt_reg = std_reg(1); - - } - wires{ - - RREADY = is_rdy.out; - data_received.read_en = 1'b0; - - group init_n_RLAST { - n_RLAST.in = 1'b1; - n_RLAST.write_en = 1'b1; - init_n_RLAST[done] = n_RLAST.done; - } - - // Used to block any servicing until handshake occurs. - group reset_bt { - bt_reg.in = 1'b0; - bt_reg.write_en = 1'b1; - reset_bt[done] = bt_reg.done; - } - - // NOTE: xVALID signals must be high until xREADY is high as well, so this works - // because if xREADY is high (is_rdy.out) then RVALID being high makes 1 flip - // and group will be done by bt_reg.out - group block_transfer { - // set RREADY high - // TODO (nathanielnrn): technically we can make RREADY depend on on RVALID (but not vice versa). - // Could we simplify this we just making things ready when we are in - // block_transfer && RVALID? - - //NOTE: is_rdy.in = 1'b1; does not work, it leaves RREADY high for 2 cycles - // this both asserts and deasserts one cycle later - // TODO(nathanielnrn): Spec recommends defaulting xREADY high as it - // can get rid of extra cycle. Maybe doing so here would be useful? - // as opposed to waiting for RVALID - is_rdy.in = !(RVALID & is_rdy.out) ? 1'b1; - is_rdy.in = RVALID & is_rdy.out ? 1'b0; - is_rdy.write_en = 1'b1; - - - //store the data we want to write - read_data_reg.in = RDATA; - read_data_reg.write_en = is_rdy.out; - - //update n_RLAST reg - n_RLAST.in = RLAST ? 1'b0; - n_RLAST.in = !RLAST ? 1'b1; - n_RLAST.write_en = 1'b1; - - - // we are done after handshake - bt_reg.in = is_rdy.out & RVALID ? 1'b1; - bt_reg.in = !(is_rdy.out & RVALID) ? 1'b0; - bt_reg.write_en = 1'b1; - block_transfer[done] = bt_reg.out; - } - - group receive_r_transfer{ - // keep RREADY low; - is_rdy.in = 1'b0; - is_rdy.write_en = 1'b1; - - //write the data we received during transfer to seq_d1_mem - data_received.addr0 = curr_addr.out; - data_received.write_en = 1'b1; - data_received.write_data = read_data_reg.out; - receive_r_transfer[done] = data_received.write_done; - - } - - group incr_curr_addr{ - curr_addr_adder.left = 64'd1 ; - curr_addr_adder.right = curr_addr.out; - curr_addr.in = curr_addr_adder.out; - curr_addr.write_en = 1'b1; - incr_curr_addr[done] = curr_addr.done; - } - } - control{ - init_n_RLAST; - while n_RLAST.out{ - seq{ - reset_bt; - block_transfer; - receive_r_transfer; - incr_curr_addr; - } - } - } -} - -//TODO(nathanielnrn): this is axi_wrapper, prefer to use @toplevel attribute but its not working -// See individual channel components for explanations of signals -component main( - m_ARESET : 1, - m_ARREADY : 1, - - m_RVALID : 1, - m_RLAST : 1, - m_RDATA : 32, - m_RRESP : 2, - //NOTE: Only used for cocotb compatability, doesn't do anything within the wrapper itself currently. - m_RID : 1, -) -> ( - m_ARVALID : 1, - m_ARADDR: 64, - m_ARSIZE: 3, - m_ARLEN : 8, - m_ARBURST : 2, - - m_RREADY : 1, - //NOTE: Only used for cocotb compatability, doesn't do anything within the wrapper itself currently. - m_ARID : 1 -) { - cells{ - vec1_data = seq_mem_d1(32,16,64); - output_data = seq_mem_d1(32,1,0); - - curr_addr = std_reg(64); - base_addr = std_reg(64); - - read_channel = m_read_channel(); - arread_channel = m_arread_channel(); - - } - - wires{ - - m_ARID = 1'b0; - - group set_curr_to_base_addr{ - curr_addr.in = base_addr.out; - curr_addr.write_en = 1'b1; - set_curr_to_base_addr[done] = curr_addr.done; - } - } - control{ - seq{ - invoke arread_channel[base_addr = base_addr] - ( - ARESET = m_ARESET, - ARREADY = m_ARREADY - ) - ( - ARVALID = m_ARVALID, - ARADDR = m_ARADDR, - ARSIZE = m_ARSIZE, - ARLEN = m_ARLEN, - ARBURST = m_ARBURST - ); - - set_curr_to_base_addr; - - invoke read_channel[data_received = vec1_data, curr_addr = curr_addr] - ( - ARESET = m_ARESET, - RVALID = m_RVALID, - RLAST = m_RLAST, - RDATA = m_RDATA, - RRESP = m_RRESP - ) - ( - RREADY = m_RREADY - ); - } - } - - -} diff --git a/yxi/axi-calyx/axi-writes-calyx.futil b/yxi/axi-calyx/axi-writes-calyx.futil deleted file mode 100644 index 402d9f8a48..0000000000 --- a/yxi/axi-calyx/axi-writes-calyx.futil +++ /dev/null @@ -1,522 +0,0 @@ -// ### -// This file contains the components needed to perform write transacitons via AXI. -// Current goal is to create a cocotb testbench that tests correctness of this. -// See https://github.com/cucapra/calyx/issues/1733 for more information. -// -// This wrapper assumes it is part of a dot product computation with vectors of -// length 16 -// It assumes a bus data width of 32 -// This is largely a work in progress and as of Nov 20 2023 is not intended to -// actually be used for anything -// ### - -import "primitives/core.futil"; -import "primitives/memories/comb.futil"; -import "primitives/compile.futil"; -import "primitives/math.futil"; -import "primitives/memories/seq.futil"; - - -//this goes m->s -component m_awwrite_channel( - ARESET: 1, - AWREADY: 1 -) -> ( - AWVALID: 1, - // This needs to be 64, see link below `m_axi` section. - AWADDR: 64, - // 2^AWSIZE is bytes used in transfer. For memory-mapped AXI (which is what we - // are doing I believe), should match width of data bus (to shell?, so 32 wide? This - // is 3'b010) - // see https://docs.xilinx.com/r/en-US/ug1393-vitis-application-acceleration/Kernel-Interface-Requirements - // for restrictions - AWSIZE: 3, - // in AXI4 this is 8 bits, 1-256 transfers in requested transaction. - AWLEN : 8, - // 00 for fixed, 01 for incrementing, 2 for wrap, - // needs to be incr for RTL kernels (can't use wrapped or fixed) - AWBURST : 2, - // required according to spec. We hardcode this - AWPROT : 3) { - cells{ - is_awvalid = std_reg(1); - - // gets set high with AWVALID and remains high - awvalid_was_high = std_reg(1); - // TODO(nathanielnrn): should arguably eventually live in `s_axi_control` - // but for now will live here. - ref base_addr = std_reg(64); - - //we write to this here and read from it in m_write_channel - ref max_trnsfrs = std_reg(8); - - // number of trasfers in a transaction. This is sent to subordinate - txn_len = std_reg(8); - - // number of txns we want to occur before entire m_awwrite_channel is done - // this is internal to the channel (unlike txn_len) - txn_n = std_const(32,1); - txn_count = std_reg(32); - perform_write_txns = std_neq(32); - txn_adder = std_add(32); - - //"block_transfer" register. need to put into a reg to avoid combinational loops - bt_reg = std_reg(1); - - - } - - wires{ - - AWVALID = is_awvalid.out; - - group deassert_val { - is_awvalid.in = 1'b0; - is_awvalid.write_en = 1'b1; - deassert_val[done] = is_awvalid.done; - } - - group reset_bt { - bt_reg.in = 1'b0; - bt_reg.write_en = 1'b1; - reset_bt[done] = bt_reg.done; - } - - // this asserts valid and defines all inputs correctly - // because valid should not be deasserted until handshake occurs - // this all needs to be one group - // this contains blocking logic previously in its own group - group do_aw_transfer { - //assert AWVALID - is_awvalid.in = !(is_awvalid.out & AWREADY) & !awvalid_was_high.out ? 1'b1; - - // TODO(nathanielnrn): in theory should be able to get rid of awvalid_was_high - // but for now we will be explicit and reduce this in generation maybe. Not sure - // it even matters. - // This makes AWVALID go low after a single cycle. Without it it stays high for 2. - is_awvalid.in = is_awvalid.out & AWREADY & awvalid_was_high.out ? 1'b0; - is_awvalid.write_en = 1'b1; - - awvalid_was_high.in = !(is_awvalid.out & AWREADY) & !awvalid_was_high.out ? 1'b1; - awvalid_was_high.write_en = !(is_awvalid.out & AWREADY) & !awvalid_was_high.out ? 1'b1; - - - // drive output signals for transfer - AWADDR = base_addr.out; - // see link above, needs to match data width to host. - // In this case 2^2 = 4 bytes = 32 bits = width of our data_bus. - AWSIZE = 3'b010; - // For now this can be taken from .yxi, as size of mem, because we are assuming - // data_bus width that matches size of memory cells - // If we want to use bigger mems need to be able to update base addr - AWLEN = txn_len.out; - AWBURST = 2'b01; //incr - // 3'b110 is [privileged access] [Non-secure access] [Data access]] - AWPROT = 3'b110; - - - // TODO(nathanielnrn): This is used to tell write_channel how many transfers to do - // we eventually want this to correspond to AWLEN - // (need a case statement or mux or something) - // for now hardcoding to 15 for 16 transfers - max_trnsfrs.in = 8'd15; - max_trnsfrs.write_en = 1'b1; - - //done when one cycle after handshake (handshake happens for a single cycle) - bt_reg.in = AWREADY & is_awvalid.out ? 1'b1; - bt_reg.in = !(AWREADY & is_awvalid.out) ? 1'b0; - bt_reg.write_en = 1'b1; - do_aw_transfer[done] = bt_reg.out; - } - - - //txn bookkeeping. - //We are done performing reads when txn_count == txn_n - group txn_count_init { - txn_count.in = 32'b0; - txn_count.write_en = 1'b1; - txn_count_init[done] = txn_count.done; - - } - - group txn_len_init { - //TODO(nathanielnrn): 15 is good for word wide data bus. We'd - //expect 16 transfers. Number of transfers that occur is AWLEN + 1 - txn_len.in = 8'd15; - txn_len.write_en = 1'b1; - txn_len_init[done] = txn_len.done; - } - - group txn_incr { - txn_adder.left = txn_count.out; - txn_adder.right = 32'b1; - txn_count.in = txn_adder.out; - txn_count.write_en = 1'b1; - txn_incr[done] = txn_count.done; - - } - - comb group check_writes_done { - perform_write_txns.left = txn_count.out; - perform_write_txns.right = txn_n.out; - } - } - - control{ - //XXX(nathanielnrn): What is best way to offer more flexiblity beyond just a counter? - seq{ - txn_count_init; - txn_len_init; - while perform_write_txns.out with check_writes_done{ - seq{ - reset_bt; - do_aw_transfer; - deassert_val; - txn_incr; - } - } - } - } -} - - - - -component m_write_channel( - ARESET : 1, - WREADY : 1, -) -> ( - WVALID : 1, - WLAST : 1, - WDATA : 32, -) { - cells { - // 16 is due to dot-product vector length assumption - // For this manual implementation we are just writing into this data based - // on the data we read from cocotb - ref internal_mem = seq_mem_d1(32, 16, 64); - wvalid = std_reg(1); - wvalid_was_high = std_reg(1); - // used internally to access our seq_mem_d1 - ref curr_addr = std_reg(64); - - //this increments - curr_trnsfr_count = std_reg(8); //between 0 and 255, add +1 for transfer count - //this is number of transfer we want to do - ref max_trnsfrs = std_reg(8); - - // registered because wlast is high with last transfer, not after - // before this was registered we were terminating immediately with - // last transfer and not servicing it. This is for while loop in control group - n_finished_last_trnsfr = std_reg(1); - - // TODO: get this width from yxi - //read_data_reg = std_reg(32); - - //used for address of seq_d1_mem we are reading from - curr_addr_adder = std_add(64); - curr_trnsfr_count_adder = std_add(8); - - - // block_transfer reg to avoid combinational loops - bt_reg = std_reg(1); - - - - //write init stuff TODO: delete this - n_init_done = std_reg(1); - curr_addr_slice = std_slice(64,32); - - } - wires{ - - WVALID = wvalid.out; - //internal_mem.write_en = 1'b0; - - - - //stuff for initialization - group reset_curr_addr{ - curr_addr.in = 64'b0; - curr_addr.write_en = 1'b1; - reset_curr_addr[done] = curr_addr.done; - } - - group write_to_internal{ - internal_mem.read_en = 1'b0; - internal_mem.write_en = 1'b1; - internal_mem.addr0 = curr_addr.out; - - curr_addr_slice.in = curr_addr.out; - internal_mem.write_data = curr_addr_slice.out; - write_to_internal[done] = internal_mem.write_done; - } - - group check_if_writes_done{ - n_init_done.in = curr_addr.out == 64'd16 ? 1'b0; - n_init_done.in = !(curr_addr.out == 64'd16) ? 1'b1; - n_init_done.write_en = 1'b1; - check_if_writes_done[done] = n_init_done.done; - } - - group init_done{ - n_init_done.in = 1'b1; - n_init_done.write_en = 1'b1; - init_done[done] = n_init_done.done; - } - //end stuff for initialization - - - group init_n_finished_last_trnsfr { - n_finished_last_trnsfr.in = 1'b1; - n_finished_last_trnsfr.write_en = 1'b1; - init_n_finished_last_trnsfr[done] = n_finished_last_trnsfr.done; - } - - //Used to block any servicing until handshake occurs. - group reset_bt { - bt_reg.in = 1'b0; - bt_reg.write_en = 1'b1; - reset_bt[done] = bt_reg.done; - } - - //NOTE: xVALID signals must be high until xREADY is high as well, so this works - //because if xREADY is high (is_rdy.out) then RVALID being high makes 1 flip - //and group will be done by bt_reg.out - group do_write_transfer { - //set RREADY high - //TODO (nathanielnrn): technically we can make RREADY depend on on RVALID (but not vice versa). - //Could we simplify this we just making things ready when we are in - //block_transfer && RVALID? - - //NOTE: is_rdy.in = 1'b1; does not work, it leaves RREADY high for 2 cycles - // this both asserts and deasserts one cycle later - wvalid.in = !(wvalid.out & WREADY & wvalid_was_high.out) ? 1'b1; - // TODO(nathanielnrn): Can prob get rid of wvalid_was_high - wvalid.in = wvalid.out & WREADY & wvalid_was_high.out ? 1'b0; - wvalid.write_en = 1'b1; - - //set to 1 after valid has been high even once - wvalid_was_high.in = 1'b1; - wvalid_was_high.write_en = !(wvalid.out & WREADY) & !wvalid_was_high.out ? 1'b1; - - - // set data output based on curr_addr register - internal_mem.addr0 = curr_addr.out; - internal_mem.read_en = 1'b1; - WDATA = internal_mem.read_data; - - //set wlast - WLAST = max_trnsfrs.out == curr_trnsfr_count.out ? 1'b1; - WLAST = max_trnsfrs.out == curr_trnsfr_count.out ? 1'b1; - - //set high only when WLAST is high and a handshake occurs. - n_finished_last_trnsfr.in = (max_trnsfrs.out == curr_trnsfr_count.out) & wvalid.out & WREADY ? 1'b0; - n_finished_last_trnsfr.write_en = (max_trnsfrs.out == curr_trnsfr_count.out) & wvalid.out & WREADY ? 1'b1; - - - - // we are done after handshake - bt_reg.in = wvalid.out & WREADY ? 1'b1; - bt_reg.in = !(wvalid.out & WREADY) ? 1'b0; - bt_reg.write_en = 1'b1; - do_write_transfer[done] = bt_reg.out; - } - - group incr_curr_addr{ - curr_addr_adder.left = 64'd1 ; - curr_addr_adder.right = curr_addr.out; - curr_addr.in = curr_addr_adder.out; - curr_addr.write_en = 1'b1; - incr_curr_addr[done] = curr_addr.done; - } - - group incr_curr_trnsfr_count { - curr_trnsfr_count_adder.left = 8'd1; - curr_trnsfr_count_adder.right = curr_trnsfr_count.out; - curr_trnsfr_count.in = curr_trnsfr_count_adder.out; - curr_trnsfr_count.write_en = 1'b1; - incr_curr_trnsfr_count[done] = curr_trnsfr_count.done; - } - - } - control{ - seq{ - - //done for writing to internal mem - init_done; - reset_curr_addr; - while n_init_done.out { - seq{ - write_to_internal; - incr_curr_addr; - check_if_writes_done; - } - } - //end writing to internal mem - reset_curr_addr; - init_n_finished_last_trnsfr; - while n_finished_last_trnsfr.out{ - seq{ - reset_bt; - do_write_transfer; - par{ - incr_curr_addr; - incr_curr_trnsfr_count; - } - } - } - } - } -} - - -//We assume that all responses are OKAY because we dont have any error handling. -//So basically this just sets BREADY high then lowers it -component m_bresp_channel( - ARESET : 1, - BVALID : 1, - // We assume all writes are valid. - //BRESP : 2, -) -> ( - // NOTE: In general, according to ZipCPU we want xREADY signals to be registered - // because (IIRC) it helps avoid combinational loops between READY and VALID. - BREADY : 1, -) { - cells{ - bready = std_reg(1); - bt_reg = std_reg(1); - - } - wires{ - BREADY = bready.out; - group reset_bt_reg{ - bt_reg.in = 1'b0; - bt_reg.write_en = 1'b1; - reset_bt_reg[done] = bt_reg.done; - } - - // TODO(nathanielnrn): This is probably very unoptimal and takes multiple - // cycles to simply do a handshake. Can probably be much better - group block_transfer{ - bready.in = !(BVALID & bready.out) ? 1'b1; - bready.in = BVALID & bready.out ? 1'b0; - bready.write_en = 1'b1; - - bt_reg.in = bready.out & BVALID ? 1'b1; - bt_reg.in = !(bready.out & BVALID) ? 1'b0; - bt_reg.write_en = 1'b1; - block_transfer[done] = bt_reg.out; - } - - } - control{ - seq{ - reset_bt_reg; - block_transfer; - } - } -} - - - -//TODO(nathanielnrn): this is axi_wrapper, prefer to use @toplevel attribute but its not working -// See individual channel components for explanations of signals -component main( - m_ARESET : 1, - m_AWREADY : 1, - - m_WRESP : 2, - m_WREADY : 1, - - m_BVALID : 1, - // Used only for waveform tracing. Not sent anywhere - // Note this AXI4 has this at 2 bits, while latest has it at 3. - m_BRESP : 2, -) -> ( - - m_AWVALID : 1, - m_AWADDR: 64, - m_AWSIZE: 3, - m_AWLEN : 8, - m_AWBURST : 2, - m_AWPROT : 3, - - m_WVALID : 1, - m_WLAST : 1, - m_WDATA : 32, - - m_BREADY : 1, - //NOTE: Only used for cocotb compatability, doesn't do anything within the wrapper itself currently. - m_AWID : 1, - m_WID : 1, - m_BID : 1, -) { - cells{ - vec1_data = seq_mem_d1(32,16,64); - output_data = seq_mem_d1(32,1,0); - - curr_addr = std_reg(64); - base_addr = std_reg(64); - - max_trnsfrs = std_reg(8); - - awwrite_channel = m_awwrite_channel(); - write_channel = m_write_channel(); - bresp_channel = m_bresp_channel(); - - } - - wires{ - - m_AWID = 1'b0; - m_WID = 1'b0; - m_BID = 1'b0; - - group set_curr_to_base_addr{ - curr_addr.in = base_addr.out; - curr_addr.write_en = 1'b1; - set_curr_to_base_addr[done] = curr_addr.done; - } - } - control{ - seq{ - invoke awwrite_channel[base_addr = base_addr, max_trnsfrs = max_trnsfrs] - ( - ARESET = m_ARESET, - AWREADY = m_AWREADY - ) - ( - AWVALID = m_AWVALID, - AWADDR = m_AWADDR, - AWSIZE = m_AWSIZE, - AWLEN = m_AWLEN, - AWBURST = m_AWBURST, - AWPROT = m_AWPROT - ); - - set_curr_to_base_addr; - - invoke write_channel[internal_mem = vec1_data, curr_addr = curr_addr, max_trnsfrs = max_trnsfrs] - ( - ARESET = m_ARESET, - WREADY = m_WREADY - ) - ( - WVALID = m_WVALID, - WLAST = m_WLAST, - WDATA = m_WDATA - ); - - invoke bresp_channel - ( - BVALID = m_BVALID - ) - ( - BREADY = m_BREADY - ); - } - } - - -} diff --git a/yxi/axi-calyx/cocotb/Makefile b/yxi/axi-calyx/cocotb/Makefile index dc468c6d5b..17eb646680 100644 --- a/yxi/axi-calyx/cocotb/Makefile +++ b/yxi/axi-calyx/cocotb/Makefile @@ -7,7 +7,7 @@ TOPLEVEL_LANG ?= verilog #Needed to extract desired test from runt invocation -VERILOG_SOURCES += $(PWD)/../outputs/axi-combined.v +VERILOG_SOURCES += $(PWD)/../outputs/generated-axi-with-vec-add.v #Defines build directory, if left to default only a single computation is run SIM_BUILD=sim_build/axi-combined diff --git a/yxi/axi-calyx/cocotb/sim.sh b/yxi/axi-calyx/cocotb/sim.sh index 1e991788f6..be403506ae 100644 --- a/yxi/axi-calyx/cocotb/sim.sh +++ b/yxi/axi-calyx/cocotb/sim.sh @@ -2,7 +2,7 @@ # Intended to convert from calyx to synthesizable verilog, enable waveform tracing and run tests defined in Makefile #expects an outputs/ dir one level up from here -fud e ../axi-combined-calyx.futil --from calyx --to synth-verilog -o ../outputs/axi-combined.v \ - && ../vcdump.py ../outputs/axi-combined.v \ +fud e ../generated-axi-with-vec-add.futil --from calyx --to synth-verilog -o ../outputs/generated-axi-with-vec-add.v \ + && ../vcdump.py ../outputs/generated-axi-with-vec-add.v \ && make WAVES=1 \ - && mv out.vcd axi-combined.fst + && mv out.vcd generated-axi-with-vec-add.fst diff --git a/yxi/axi-calyx/generated-axi-with-vec-add.futil b/yxi/axi-calyx/generated-axi-with-vec-add.futil new file mode 100644 index 0000000000..6d9bb10a95 --- /dev/null +++ b/yxi/axi-calyx/generated-axi-with-vec-add.futil @@ -0,0 +1,457 @@ +import "primitives/core.futil"; +import "primitives/binary_operators.futil"; +import "primitives/memories/seq.futil"; +component m_ar_channel(ARESETn: 1, ARREADY: 1) -> (ARVALID: 1, ARADDR: 64, ARSIZE: 3, ARLEN: 8, ARBURST: 2, ARPROT: 3) { + cells { + arvalid = std_reg(1); + ar_handshake_occurred = std_reg(1); + ref curr_addr_axi = std_reg(64); + arlen = std_reg(8); + txn_n = std_const(32, 1); + txn_count = std_reg(32); + txn_adder = std_add(32); + bt_reg = std_reg(1); + perform_reads = std_neq(32); + } + wires { + ARVALID = arvalid.out; + group do_ar_transfer { + arvalid.in = (!(arvalid.out & ARREADY) & !ar_handshake_occurred.out) ? 1'd1; + arvalid.in = ((arvalid.out & ARREADY) | ar_handshake_occurred.out) ? 1'd0; + arvalid.write_en = 1'd1; + ar_handshake_occurred.in = (arvalid.out & ARREADY) ? 1'd1; + ar_handshake_occurred.write_en = !ar_handshake_occurred.out ? 1'd1; + ARADDR = curr_addr_axi.out; + ARSIZE = 3'd2; + ARLEN = arlen.out; + ARBURST = 2'd1; + ARPROT = 3'd6; + bt_reg.in = (ARREADY & arvalid.out) ? 1'd1; + bt_reg.in = !(ARREADY & arvalid.out) ? 1'd0; + bt_reg.write_en = 1'd1; + do_ar_transfer[done] = bt_reg.out; + } + group incr_txn_count { + txn_adder.left = txn_count.out; + txn_adder.right = 32'd1; + txn_count.in = txn_adder.out; + txn_count.write_en = 1'd1; + incr_txn_count[done] = txn_count.done; + } + comb group perform_reads_group { + perform_reads.left = txn_count.out; + perform_reads.right = txn_n.out; + } + } + control { + seq { + invoke txn_count(in=32'd0)(); + invoke arlen(in=8'd7)(); + while perform_reads.out with perform_reads_group { + seq { + par { + invoke bt_reg(in=1'd0)(); + invoke ar_handshake_occurred(in=1'd0)(); + } + do_ar_transfer; + invoke arvalid(in=1'd0)(); + incr_txn_count; + } + } + } + } +} +component m_aw_channel(ARESETn: 1, AWREADY: 1) -> (AWVALID: 1, AWADDR: 64, AWSIZE: 3, AWLEN: 8, AWBURST: 2, AWPROT: 3) { + cells { + awvalid = std_reg(1); + aw_handshake_occurred = std_reg(1); + ref curr_addr_axi = std_reg(64); + awlen = std_reg(8); + txn_n = std_const(32, 1); + txn_count = std_reg(32); + txn_adder = std_add(32); + bt_reg = std_reg(1); + perform_writes = std_neq(32); + ref max_transfers = std_reg(8); + } + wires { + AWVALID = awvalid.out; + group do_aw_transfer { + awvalid.in = (!(awvalid.out & AWREADY) & !aw_handshake_occurred.out) ? 1'd1; + awvalid.in = ((awvalid.out & AWREADY) | aw_handshake_occurred.out) ? 1'd0; + awvalid.write_en = 1'd1; + aw_handshake_occurred.in = (awvalid.out & AWREADY) ? 1'd1; + aw_handshake_occurred.write_en = !aw_handshake_occurred.out ? 1'd1; + AWADDR = curr_addr_axi.out; + AWSIZE = 3'd2; + AWLEN = awlen.out; + AWBURST = 2'd1; + AWPROT = 3'd6; + bt_reg.in = (AWREADY & awvalid.out) ? 1'd1; + bt_reg.in = !(AWREADY & awvalid.out) ? 1'd0; + bt_reg.write_en = 1'd1; + do_aw_transfer[done] = bt_reg.out; + max_transfers.in = 8'd7; + max_transfers.write_en = 1'd1; + } + group incr_txn_count { + txn_adder.left = txn_count.out; + txn_adder.right = 32'd1; + txn_count.in = txn_adder.out; + txn_count.write_en = 1'd1; + incr_txn_count[done] = txn_count.done; + } + comb group perform_writes_group { + perform_writes.left = txn_count.out; + perform_writes.right = txn_n.out; + } + } + control { + seq { + invoke txn_count(in=32'd0)(); + invoke awlen(in=8'd7)(); + while perform_writes.out with perform_writes_group { + seq { + par { + invoke bt_reg(in=1'd0)(); + invoke aw_handshake_occurred(in=1'd0)(); + } + do_aw_transfer; + invoke awvalid(in=1'd0)(); + incr_txn_count; + } + } + } + } +} +component m_read_channel(ARESETn: 1, RVALID: 1, RLAST: 1, RDATA: 32, RRESP: 2) -> (RREADY: 1) { + cells { + ref mem_ref = seq_mem_d1(32, 8, 3); + rready = std_reg(1); + ref curr_addr_internal_mem = std_reg(3); + ref curr_addr_axi = std_reg(64); + n_RLAST = std_reg(1); + read_data_reg = std_reg(32); + bt_reg = std_reg(1); + curr_addr_internal_mem_incr = std_add(3); + curr_addr_axi_incr = std_add(64); + } + wires { + RREADY = rready.out; + mem_ref.read_en = 1'd0; + group block_transfer { + rready.in = !(rready.out & RVALID) ? 1'd1; + rready.in = (rready.out & RVALID) ? 1'd0; + rready.write_en = 1'd1; + read_data_reg.in = RDATA; + read_data_reg.write_en = (rready.out & RVALID) ? 1'd1; + read_data_reg.write_en = !(rready.out & RVALID) ? 1'd0; + n_RLAST.in = RLAST ? 1'd0; + n_RLAST.in = !RLAST ? 1'd1; + n_RLAST.write_en = 1'd1; + bt_reg.in = (rready.out & RVALID) ? 1'd1; + bt_reg.in = !(rready.out & RVALID) ? 1'd0; + bt_reg.write_en = 1'd1; + block_transfer[done] = bt_reg.out; + } + group service_read_transfer { + rready.in = 1'd0; + rready.write_en = 1'd1; + mem_ref.addr0 = curr_addr_internal_mem.out; + mem_ref.write_data = read_data_reg.out; + mem_ref.write_en = 1'd1; + service_read_transfer[done] = mem_ref.write_done; + } + group curr_addr_internal_mem_incr_group { + curr_addr_internal_mem_incr.left = curr_addr_internal_mem.out; + curr_addr_internal_mem_incr.right = 3'd1; + curr_addr_internal_mem.write_en = 1'd1; + curr_addr_internal_mem.in = curr_addr_internal_mem_incr.out; + curr_addr_internal_mem_incr_group[done] = curr_addr_internal_mem.done; + } + group curr_addr_axi_incr_group { + curr_addr_axi_incr.left = curr_addr_axi.out; + curr_addr_axi_incr.right = 64'd4; + curr_addr_axi.write_en = 1'd1; + curr_addr_axi.in = curr_addr_axi_incr.out; + curr_addr_axi_incr_group[done] = curr_addr_axi.done; + } + } + control { + seq { + invoke n_RLAST(in=1'd1)(); + while n_RLAST.out { + seq { + invoke bt_reg(in=1'd0)(); + block_transfer; + service_read_transfer; + par { + curr_addr_internal_mem_incr_group; + curr_addr_axi_incr_group; + } + } + } + } + } +} +component m_write_channel(ARESETn: 1, WREADY: 1) -> (WVALID: 1, WLAST: 1, WDATA: 32) { + cells { + ref mem_ref = seq_mem_d1(32, 8, 3); + wvalid = std_reg(1); + w_handshake_occurred = std_reg(1); + ref curr_addr_internal_mem = std_reg(3); + ref curr_addr_axi = std_reg(64); + curr_trsnfr_count = std_reg(8); + ref max_transfers = std_reg(8); + n_finished_last_trnsfr = std_reg(1); + bt_reg = std_reg(1); + curr_addr_internal_mem_incr = std_add(3); + curr_addr_axi_incr = std_add(64); + curr_trsnfr_count_incr = std_add(8); + } + wires { + WVALID = wvalid.out; + group service_write_transfer { + wvalid.in = (!(wvalid.out & WREADY) & !w_handshake_occurred.out) ? 1'd1; + wvalid.in = ((wvalid.out & WREADY) | w_handshake_occurred.out) ? 1'd0; + wvalid.write_en = 1'd1; + w_handshake_occurred.in = (wvalid.out & WREADY) ? 1'd1; + w_handshake_occurred.write_en = !w_handshake_occurred.out ? 1'd1; + mem_ref.addr0 = curr_addr_internal_mem.out; + mem_ref.read_en = 1'd1; + WDATA = mem_ref.read_data; + WLAST = (max_transfers.out == curr_trsnfr_count.out) ? 1'd1; + WLAST = (max_transfers.out != curr_trsnfr_count.out) ? 1'd0; + n_finished_last_trnsfr.in = ((max_transfers.out == curr_trsnfr_count.out) & (wvalid.out & WREADY)) ? 1'd0; + n_finished_last_trnsfr.write_en = ((max_transfers.out == curr_trsnfr_count.out) & (wvalid.out & WREADY)) ? 1'd1; + bt_reg.in = (wvalid.out & WREADY) ? 1'd1; + bt_reg.in = !(wvalid.out & WREADY) ? 1'd0; + bt_reg.write_en = 1'd1; + service_write_transfer[done] = bt_reg.out; + } + group curr_addr_internal_mem_incr_group { + curr_addr_internal_mem_incr.left = curr_addr_internal_mem.out; + curr_addr_internal_mem_incr.right = 3'd1; + curr_addr_internal_mem.write_en = 1'd1; + curr_addr_internal_mem.in = curr_addr_internal_mem_incr.out; + curr_addr_internal_mem_incr_group[done] = curr_addr_internal_mem.done; + } + group curr_addr_axi_incr_group { + curr_addr_axi_incr.left = curr_addr_axi.out; + curr_addr_axi_incr.right = 64'd4; + curr_addr_axi.write_en = 1'd1; + curr_addr_axi.in = curr_addr_axi_incr.out; + curr_addr_axi_incr_group[done] = curr_addr_axi.done; + } + group curr_trsnfr_count_incr_group { + curr_trsnfr_count_incr.left = curr_trsnfr_count.out; + curr_trsnfr_count_incr.right = 8'd1; + curr_trsnfr_count.write_en = 1'd1; + curr_trsnfr_count.in = curr_trsnfr_count_incr.out; + curr_trsnfr_count_incr_group[done] = curr_trsnfr_count.done; + } + } + control { + seq { + invoke curr_addr_internal_mem(in=3'd0)(); + invoke n_finished_last_trnsfr(in=1'd1)(); + while n_finished_last_trnsfr.out { + seq { + invoke bt_reg(in=1'd0)(); + service_write_transfer; + par { + curr_addr_internal_mem_incr_group; + curr_trsnfr_count_incr_group; + curr_addr_axi_incr_group; + invoke w_handshake_occurred(in=1'd0)(); + } + } + } + } + } +} +component m_bresp_channel(ARESETn: 1, BVALID: 1) -> (BREADY: 1) { + cells { + bready = std_reg(1); + bt_reg = std_reg(1); + } + wires { + BREADY = bready.out; + group block_transfer { + bready.in = !(bready.out & BVALID) ? 1'd1; + bready.in = (bready.out & BVALID) ? 1'd0; + bready.write_en = 1'd1; + bt_reg.in = (bready.out & BVALID) ? 1'd1; + bt_reg.in = !(bready.out & BVALID) ? 1'd0; + bt_reg.write_en = 1'd1; + block_transfer[done] = bt_reg.out; + } + } + control { + seq { + invoke bt_reg(in=1'd0)(); + block_transfer; + } + } +} +component wrapper<"toplevel"=1>(A0_ARESETn: 1, A0_ARREADY: 1, A0_RVALID: 1, A0_RLAST: 1, A0_RDATA: 32, A0_RRESP: 2, A0_AWREADY: 1, A0_WRESP: 2, A0_WREADY: 1, A0_BVALID: 1, A0_BRESP: 2, A0_RID: 1, B0_ARESETn: 1, B0_ARREADY: 1, B0_RVALID: 1, B0_RLAST: 1, B0_RDATA: 32, B0_RRESP: 2, B0_AWREADY: 1, B0_WRESP: 2, B0_WREADY: 1, B0_BVALID: 1, B0_BRESP: 2, B0_RID: 1, Sum0_ARESETn: 1, Sum0_ARREADY: 1, Sum0_RVALID: 1, Sum0_RLAST: 1, Sum0_RDATA: 32, Sum0_RRESP: 2, Sum0_AWREADY: 1, Sum0_WRESP: 2, Sum0_WREADY: 1, Sum0_BVALID: 1, Sum0_BRESP: 2, Sum0_RID: 1) -> (A0_ARVALID: 1, A0_ARADDR: 64, A0_ARSIZE: 3, A0_ARLEN: 8, A0_ARBURST: 2, A0_RREADY: 1, A0_AWVALID: 1, A0_AWADDR: 64, A0_AWSIZE: 3, A0_AWLEN: 8, A0_AWBURST: 2, A0_AWPROT: 3, A0_WVALID: 1, A0_WLAST: 1, A0_WDATA: 32, A0_BREADY: 1, A0_ARID: 1, A0_AWID: 1, A0_WID: 1, A0_BID: 1, B0_ARVALID: 1, B0_ARADDR: 64, B0_ARSIZE: 3, B0_ARLEN: 8, B0_ARBURST: 2, B0_RREADY: 1, B0_AWVALID: 1, B0_AWADDR: 64, B0_AWSIZE: 3, B0_AWLEN: 8, B0_AWBURST: 2, B0_AWPROT: 3, B0_WVALID: 1, B0_WLAST: 1, B0_WDATA: 32, B0_BREADY: 1, B0_ARID: 1, B0_AWID: 1, B0_WID: 1, B0_BID: 1, Sum0_ARVALID: 1, Sum0_ARADDR: 64, Sum0_ARSIZE: 3, Sum0_ARLEN: 8, Sum0_ARBURST: 2, Sum0_RREADY: 1, Sum0_AWVALID: 1, Sum0_AWADDR: 64, Sum0_AWSIZE: 3, Sum0_AWLEN: 8, Sum0_AWBURST: 2, Sum0_AWPROT: 3, Sum0_WVALID: 1, Sum0_WLAST: 1, Sum0_WDATA: 32, Sum0_BREADY: 1, Sum0_ARID: 1, Sum0_AWID: 1, Sum0_WID: 1, Sum0_BID: 1) { + cells { + main_compute = main(); + curr_addr_internal_mem_A0 = std_reg(3); + curr_addr_axi_A0 = std_reg(64); + ar_channel_A0 = m_ar_channel(); + read_channel_A0 = m_read_channel(); + internal_mem_A0 = seq_mem_d1(32, 8, 3); + max_transfers_A0 = std_reg(8); + aw_channel_A0 = m_aw_channel(); + write_channel_A0 = m_write_channel(); + bresp_channel_A0 = m_bresp_channel(); + curr_addr_internal_mem_B0 = std_reg(3); + curr_addr_axi_B0 = std_reg(64); + ar_channel_B0 = m_ar_channel(); + read_channel_B0 = m_read_channel(); + internal_mem_B0 = seq_mem_d1(32, 8, 3); + max_transfers_B0 = std_reg(8); + aw_channel_B0 = m_aw_channel(); + write_channel_B0 = m_write_channel(); + bresp_channel_B0 = m_bresp_channel(); + curr_addr_internal_mem_Sum0 = std_reg(3); + curr_addr_axi_Sum0 = std_reg(64); + ar_channel_Sum0 = m_ar_channel(); + read_channel_Sum0 = m_read_channel(); + internal_mem_Sum0 = seq_mem_d1(32, 8, 3); + max_transfers_Sum0 = std_reg(8); + aw_channel_Sum0 = m_aw_channel(); + write_channel_Sum0 = m_write_channel(); + bresp_channel_Sum0 = m_bresp_channel(); + } + wires { + + } + control { + seq { + par { + invoke curr_addr_axi_A0(in=64'd4096)(); + invoke curr_addr_axi_B0(in=64'd4096)(); + invoke curr_addr_axi_Sum0(in=64'd4096)(); + } + par { + invoke curr_addr_internal_mem_A0(in=3'd0)(); + invoke curr_addr_internal_mem_B0(in=3'd0)(); + invoke curr_addr_internal_mem_Sum0(in=3'd0)(); + } + par { + seq { + invoke ar_channel_A0[curr_addr_axi=curr_addr_axi_A0](ARESETn=A0_ARESETn, ARREADY=A0_ARREADY)(ARVALID=A0_ARVALID, ARADDR=A0_ARADDR, ARSIZE=A0_ARSIZE, ARLEN=A0_ARLEN, ARBURST=A0_ARBURST); + invoke read_channel_A0[mem_ref=internal_mem_A0, curr_addr_internal_mem=curr_addr_internal_mem_A0, curr_addr_axi=curr_addr_axi_A0](ARESETn=A0_ARESETn, RVALID=A0_RVALID, RLAST=A0_RLAST, RDATA=A0_RDATA, RRESP=A0_RRESP)(RREADY=A0_RREADY); + } + seq { + invoke ar_channel_B0[curr_addr_axi=curr_addr_axi_B0](ARESETn=B0_ARESETn, ARREADY=B0_ARREADY)(ARVALID=B0_ARVALID, ARADDR=B0_ARADDR, ARSIZE=B0_ARSIZE, ARLEN=B0_ARLEN, ARBURST=B0_ARBURST); + invoke read_channel_B0[mem_ref=internal_mem_B0, curr_addr_internal_mem=curr_addr_internal_mem_B0, curr_addr_axi=curr_addr_axi_B0](ARESETn=B0_ARESETn, RVALID=B0_RVALID, RLAST=B0_RLAST, RDATA=B0_RDATA, RRESP=B0_RRESP)(RREADY=B0_RREADY); + } + seq { + invoke ar_channel_Sum0[curr_addr_axi=curr_addr_axi_Sum0](ARESETn=Sum0_ARESETn, ARREADY=Sum0_ARREADY)(ARVALID=Sum0_ARVALID, ARADDR=Sum0_ARADDR, ARSIZE=Sum0_ARSIZE, ARLEN=Sum0_ARLEN, ARBURST=Sum0_ARBURST); + invoke read_channel_Sum0[mem_ref=internal_mem_Sum0, curr_addr_internal_mem=curr_addr_internal_mem_Sum0, curr_addr_axi=curr_addr_axi_Sum0](ARESETn=Sum0_ARESETn, RVALID=Sum0_RVALID, RLAST=Sum0_RLAST, RDATA=Sum0_RDATA, RRESP=Sum0_RRESP)(RREADY=Sum0_RREADY); + } + } + invoke main_compute[A0=internal_mem_A0, B0=internal_mem_B0, Sum0=internal_mem_Sum0]()(); + par { + invoke curr_addr_axi_A0(in=64'd4096)(); + invoke curr_addr_axi_B0(in=64'd4096)(); + invoke curr_addr_axi_Sum0(in=64'd4096)(); + } + par { + seq { + invoke aw_channel_A0[curr_addr_axi=curr_addr_axi_A0, max_transfers=max_transfers_A0](ARESETn=A0_ARESETn, AWREADY=A0_AWREADY)(AWVALID=A0_AWVALID, AWADDR=A0_AWADDR, AWSIZE=A0_AWSIZE, AWLEN=A0_AWLEN, AWBURST=A0_AWBURST, AWPROT=A0_AWPROT); + invoke write_channel_A0[mem_ref=internal_mem_A0, curr_addr_internal_mem=curr_addr_internal_mem_A0, curr_addr_axi=curr_addr_axi_A0, max_transfers=max_transfers_A0](ARESETn=A0_ARESETn, WREADY=A0_WREADY)(WVALID=A0_WVALID, WLAST=A0_WLAST, WDATA=A0_WDATA); + invoke bresp_channel_A0(BVALID=A0_BVALID)(BREADY=A0_BREADY); + } + seq { + invoke aw_channel_B0[curr_addr_axi=curr_addr_axi_B0, max_transfers=max_transfers_B0](ARESETn=B0_ARESETn, AWREADY=B0_AWREADY)(AWVALID=B0_AWVALID, AWADDR=B0_AWADDR, AWSIZE=B0_AWSIZE, AWLEN=B0_AWLEN, AWBURST=B0_AWBURST, AWPROT=B0_AWPROT); + invoke write_channel_B0[mem_ref=internal_mem_B0, curr_addr_internal_mem=curr_addr_internal_mem_B0, curr_addr_axi=curr_addr_axi_B0, max_transfers=max_transfers_B0](ARESETn=B0_ARESETn, WREADY=B0_WREADY)(WVALID=B0_WVALID, WLAST=B0_WLAST, WDATA=B0_WDATA); + invoke bresp_channel_B0(BVALID=B0_BVALID)(BREADY=B0_BREADY); + } + seq { + invoke aw_channel_Sum0[curr_addr_axi=curr_addr_axi_Sum0, max_transfers=max_transfers_Sum0](ARESETn=Sum0_ARESETn, AWREADY=Sum0_AWREADY)(AWVALID=Sum0_AWVALID, AWADDR=Sum0_AWADDR, AWSIZE=Sum0_AWSIZE, AWLEN=Sum0_AWLEN, AWBURST=Sum0_AWBURST, AWPROT=Sum0_AWPROT); + invoke write_channel_Sum0[mem_ref=internal_mem_Sum0, curr_addr_internal_mem=curr_addr_internal_mem_Sum0, curr_addr_axi=curr_addr_axi_Sum0, max_transfers=max_transfers_Sum0](ARESETn=Sum0_ARESETn, WREADY=Sum0_WREADY)(WVALID=Sum0_WVALID, WLAST=Sum0_WLAST, WDATA=Sum0_WDATA); + invoke bresp_channel_Sum0(BVALID=Sum0_BVALID)(BREADY=Sum0_BREADY); + } + } + } + } +} + +component main() -> () { + cells { + //Modified to 64 width address because XRT expects 64 bit memory addresses + ref A0 = seq_mem_d1(32,8,3); + A_read0_0 = std_reg(32); + ref B0 = seq_mem_d1(32,8,3); + B_read0_0 = std_reg(32); + ref Sum0 = seq_mem_d1(32,8,3); + add0 = std_add(32); + add1 = std_add(3); + const0 = std_const(3,0); + const1 = std_const(3,7); + const2 = std_const(3,1); + i0 = std_reg(3); + le0 = std_le(3); + } + wires { + comb group cond0 { + le0.left = i0.out; + le0.right = const1.out; + } + group let0<"static"=1> { + i0.in = const0.out; + i0.write_en = 1'd1; + let0[done] = i0.done; + } + //modified upd0 and upd1 to use seq_mem correctly + group upd0<"static"=2> { + A_read0_0.write_en = A0.read_done; + A0.addr0 = i0.out; + A0.read_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<"static"=2> { + B_read0_0.write_en = B0.read_done; + B0.addr0 = i0.out; + B0.read_en = 1'b1; + B_read0_0.in = 1'd1 ? B0.read_data; + upd1[done] = B_read0_0.done ? 1'd1; + } + group upd2<"static"=1> { + Sum0.addr0 = i0.out; + Sum0.write_en = 1'd1; + add0.left = A_read0_0.out; + add0.right = B_read0_0.out; + Sum0.write_data = 1'd1 ? add0.out; + upd2[done] = Sum0.write_done ? 1'd1; + } + group upd3<"static"=1> { + 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; + } + } + } + } +} From 806b985322e89c9f9dc1ebcd4f1ca57c8870f4d7 Mon Sep 17 00:00:00 2001 From: Nathaniel Navarro Date: Mon, 19 Feb 2024 16:28:20 -0500 Subject: [PATCH 18/23] Get generated wrapper to run properly --- yxi/axi-calyx/axi-generator.py | 14 +++++--------- yxi/axi-calyx/cocotb/Makefile | 2 +- yxi/axi-calyx/cocotb/axi-combined-tests.py | 6 +++--- yxi/axi-calyx/generated-axi-with-vec-add.futil | 14 ++++++++++++-- yxi/axi-calyx/vcdump.py | 2 +- 5 files changed, 22 insertions(+), 16 deletions(-) diff --git a/yxi/axi-calyx/axi-generator.py b/yxi/axi-calyx/axi-generator.py index e73def9023..63e7adbef7 100644 --- a/yxi/axi-calyx/axi-generator.py +++ b/yxi/axi-calyx/axi-generator.py @@ -544,15 +544,11 @@ def add_main_comp(prog, mems): # Wires # Tie IDs low, needed for cocotb compatability. Not used anywhere - ARID = wrapper_comp.this()[f"{mem_name}_ARID"] - AWID = wrapper_comp.this()[f"{mem_name}_AWID"] - WID = wrapper_comp.this()[f"{mem_name}_WID"] - BID = wrapper_comp.this()[f"{mem_name}_BID"] - - ARID = 0 - AWID = 0 - WID = 0 - BID = 0 + with wrapper_comp.continuous: + wrapper_comp.this()[f"{mem_name}_ARID"] = 0 + wrapper_comp.this()[f"{mem_name}_AWID"] = 0 + wrapper_comp.this()[f"{mem_name}_WID"] = 0 + wrapper_comp.this()[f"{mem_name}_BID"] = 0 # No groups needed! diff --git a/yxi/axi-calyx/cocotb/Makefile b/yxi/axi-calyx/cocotb/Makefile index 17eb646680..9d376e0712 100644 --- a/yxi/axi-calyx/cocotb/Makefile +++ b/yxi/axi-calyx/cocotb/Makefile @@ -13,7 +13,7 @@ VERILOG_SOURCES += $(PWD)/../outputs/generated-axi-with-vec-add.v SIM_BUILD=sim_build/axi-combined # TOPLEVEL is the name of the toplevel module in your Verilog or VHDL file -TOPLEVEL = main +TOPLEVEL = wrapper # MODULE is the basename of the Python test file MODULE = axi-combined-tests diff --git a/yxi/axi-calyx/cocotb/axi-combined-tests.py b/yxi/axi-calyx/cocotb/axi-combined-tests.py index f99782174e..6fda86f25d 100644 --- a/yxi/axi-calyx/cocotb/axi-combined-tests.py +++ b/yxi/axi-calyx/cocotb/axi-combined-tests.py @@ -117,7 +117,7 @@ async def run_module( A0 = AxiRam( # NOTE: prefix should not contain the final "_" - AxiBus.from_prefix(module, "m0"), + AxiBus.from_prefix(module, "A0"), module.clk, module.reset, # size in bytes @@ -127,7 +127,7 @@ async def run_module( B0 = AxiRam( # NOTE: prefix should not contain the final "_" - AxiBus.from_prefix(module, "m1"), + AxiBus.from_prefix(module, "B0"), module.clk, module.reset, # size in bytes @@ -137,7 +137,7 @@ async def run_module( Sum0 = AxiRam( # NOTE: prefix should not contain the final "_" - AxiBus.from_prefix(module, "m2"), + AxiBus.from_prefix(module, "Sum0"), module.clk, module.reset, # size in bytes diff --git a/yxi/axi-calyx/generated-axi-with-vec-add.futil b/yxi/axi-calyx/generated-axi-with-vec-add.futil index 6d9bb10a95..ab3c496b40 100644 --- a/yxi/axi-calyx/generated-axi-with-vec-add.futil +++ b/yxi/axi-calyx/generated-axi-with-vec-add.futil @@ -326,7 +326,18 @@ component wrapper<"toplevel"=1>(A0_ARESETn: 1, A0_ARREADY: 1, A0_RVALID: 1, A0_R bresp_channel_Sum0 = m_bresp_channel(); } wires { - + A0_ARID = 1'd0; + A0_AWID = 1'd0; + A0_WID = 1'd0; + A0_BID = 1'd0; + B0_ARID = 1'd0; + B0_AWID = 1'd0; + B0_WID = 1'd0; + B0_BID = 1'd0; + Sum0_ARID = 1'd0; + Sum0_AWID = 1'd0; + Sum0_WID = 1'd0; + Sum0_BID = 1'd0; } control { seq { @@ -380,7 +391,6 @@ component wrapper<"toplevel"=1>(A0_ARESETn: 1, A0_ARREADY: 1, A0_RVALID: 1, A0_R } } } - component main() -> () { cells { //Modified to 64 width address because XRT expects 64 bit memory addresses diff --git a/yxi/axi-calyx/vcdump.py b/yxi/axi-calyx/vcdump.py index 09772d5b27..ca27611662 100755 --- a/yxi/axi-calyx/vcdump.py +++ b/yxi/axi-calyx/vcdump.py @@ -31,7 +31,7 @@ def replace_line(file_path, old_line, new_line): `ifdef COCOTB_SIM initial begin $dumpfile ("out.vcd"); - $dumpvars (0, main); + $dumpvars (0, wrapper); #1; end `endif From f089dddcd58fdbe1ffcaffaa72609c8c03b5181b Mon Sep 17 00:00:00 2001 From: Nathaniel Navarro Date: Fri, 23 Feb 2024 21:48:22 -0500 Subject: [PATCH 19/23] Add working generated wrapper + vec_add Issue before was the vec_add was wrong, we need 4 bits instead of 3, even though we're counting 0-7 because of the presence of a <= check (so we need to get to 8 for our while loop to terminate) --- yxi/axi-calyx/cocotb/axi-combined-tests.py | 18 +-- yxi/axi-calyx/fixed-vec-add.futil | 77 +++++++++++ .../generated-axi-with-vec-add.futil | 129 +++++++++--------- 3 files changed, 152 insertions(+), 72 deletions(-) create mode 100644 yxi/axi-calyx/fixed-vec-add.futil diff --git a/yxi/axi-calyx/cocotb/axi-combined-tests.py b/yxi/axi-calyx/cocotb/axi-combined-tests.py index 6fda86f25d..978d6e44e0 100644 --- a/yxi/axi-calyx/cocotb/axi-combined-tests.py +++ b/yxi/axi-calyx/cocotb/axi-combined-tests.py @@ -20,9 +20,9 @@ async def read_channels_happy_path(main): B0_in = [126, 62, 30, 14, 6, 2, 0, 1] expected_sum = [A0_in[i] + B0_in[i] for i in range(len(B0_in))] await run_module(main, A0_in, B0_in, expected_sum) # checks cocotb axi rams - await assert_mem_content(main.A0, A0_in) # checks in verilog module, as opposed to axiram - await assert_mem_content(main.B0, B0_in) - await assert_mem_content(main.Sum0, expected_sum) + await assert_mem_content(main.internal_mem_A0, A0_in) # checks in verilog module, as opposed to axiram + await assert_mem_content(main.internal_mem_B0, B0_in) + await assert_mem_content(main.internal_mem_Sum0, expected_sum) # Adding extra data to backing mmap does not ruin reading of 8 elements and writing them correctly. @@ -43,9 +43,9 @@ async def read_channels_extra_mmap_data(main): expected_sum = [A0_in[i] + B0_in[i] for i in range(len(B0_in))] await run_module(main, A0_in, B0_in, expected_sum) - await assert_mem_content(main.Sum0, expected_sum[0:8]) - await assert_mem_content(main.A0, A0_in[0:8]) - await assert_mem_content(main.B0, B0_in[0:8]) + await assert_mem_content(main.internal_mem_Sum0, expected_sum[0:8]) + await assert_mem_content(main.internal_mem_A0, A0_in[0:8]) + await assert_mem_content(main.internal_mem_B0, B0_in[0:8]) ################## @@ -176,9 +176,9 @@ async def run_module( await assert_axi_ram_content(Sum0, Sum0_expected[0:8], base_address) if debug: - print(f"A0 is: {module.A0.mem.value}") - print(f"B0 is: {module.B0.mem.value}") - print(f"Sum0 is: {module.Sum0.mem.value}") + print(f"A0 is: {module.internal_mem_A0.mem.value}") + print(f"B0 is: {module.internal_mem_B0.mem.value}") + print(f"Sum0 is: {module.internal_mem_Sum0.mem.value}") # TODO(nathanielnrn): Decide between these and xilinx cocotb tests, refactor out diff --git a/yxi/axi-calyx/fixed-vec-add.futil b/yxi/axi-calyx/fixed-vec-add.futil new file mode 100644 index 0000000000..fae769cff9 --- /dev/null +++ b/yxi/axi-calyx/fixed-vec-add.futil @@ -0,0 +1,77 @@ +component main() -> () { + cells { + //Modified to 64 width address because XRT expects 64 bit memory addresses + ref A0 = seq_mem_d1(32,8,3); + A_read0_0 = std_reg(32); + ref B0 = seq_mem_d1(32,8,3); + B_read0_0 = std_reg(32); + ref Sum0 = seq_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 { + + bit_slice.in = i0.out; + comb group cond0 { + le0.left = i0.out; + le0.right = const1.out; + } + group let0<"static"=1> { + i0.in = const0.out; + i0.write_en = 1'd1; + let0[done] = i0.done; + } + //modified upd0 and upd1 to use seq_mem correctly + group upd0<"static"=2> { + A_read0_0.write_en = A0.read_done; + A0.addr0 = bit_slice.out; + A0.read_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<"static"=2> { + B_read0_0.write_en = B0.read_done; + B0.addr0 = bit_slice.out; + B0.read_en = 1'b1; + B_read0_0.in = 1'd1 ? B0.read_data; + upd1[done] = B_read0_0.done ? 1'd1; + } + group upd2<"static"=1> { + Sum0.addr0 = bit_slice.out; + Sum0.write_en = 1'd1; + add0.left = B_read0_0.out; + add0.right = A_read0_0.out; + Sum0.write_data = 1'd1 ? add0.out; + upd2[done] = Sum0.write_done ? 1'd1; + } + group upd3<"static"=1> { + 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; + } + } + } + } +} diff --git a/yxi/axi-calyx/generated-axi-with-vec-add.futil b/yxi/axi-calyx/generated-axi-with-vec-add.futil index ab3c496b40..d071e1fd85 100644 --- a/yxi/axi-calyx/generated-axi-with-vec-add.futil +++ b/yxi/axi-calyx/generated-axi-with-vec-add.futil @@ -392,75 +392,78 @@ component wrapper<"toplevel"=1>(A0_ARESETn: 1, A0_ARREADY: 1, A0_RVALID: 1, A0_R } } component main() -> () { - cells { - //Modified to 64 width address because XRT expects 64 bit memory addresses - ref A0 = seq_mem_d1(32,8,3); - A_read0_0 = std_reg(32); - ref B0 = seq_mem_d1(32,8,3); - B_read0_0 = std_reg(32); - ref Sum0 = seq_mem_d1(32,8,3); - add0 = std_add(32); - add1 = std_add(3); - const0 = std_const(3,0); - const1 = std_const(3,7); - const2 = std_const(3,1); - i0 = std_reg(3); - le0 = std_le(3); - } - wires { - comb group cond0 { - le0.left = i0.out; - le0.right = const1.out; - } - group let0<"static"=1> { - i0.in = const0.out; - i0.write_en = 1'd1; - let0[done] = i0.done; - } - //modified upd0 and upd1 to use seq_mem correctly - group upd0<"static"=2> { - A_read0_0.write_en = A0.read_done; - A0.addr0 = i0.out; - A0.read_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<"static"=2> { + cells { + //Modified to 64 width address because XRT expects 64 bit memory addresses + ref A0 = seq_mem_d1(32,8,3); + A_read0_0 = std_reg(32); + ref B0 = seq_mem_d1(32,8,3); + B_read0_0 = std_reg(32); + ref Sum0 = seq_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 { + + bit_slice.in = i0.out; + comb group cond0 { + le0.left = i0.out; + le0.right = const1.out; + } + group let0<"static"=1> { + i0.in = const0.out; + i0.write_en = 1'd1; + let0[done] = i0.done; + } + //modified upd0 and upd1 to use seq_mem correctly + group upd0<"static"=2> { + A_read0_0.write_en = A0.read_done; + A0.addr0 = bit_slice.out; + A0.read_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<"static"=2> { B_read0_0.write_en = B0.read_done; - B0.addr0 = i0.out; + B0.addr0 = bit_slice.out; B0.read_en = 1'b1; B_read0_0.in = 1'd1 ? B0.read_data; upd1[done] = B_read0_0.done ? 1'd1; } group upd2<"static"=1> { - Sum0.addr0 = i0.out; - Sum0.write_en = 1'd1; - add0.left = A_read0_0.out; - add0.right = B_read0_0.out; - Sum0.write_data = 1'd1 ? add0.out; - upd2[done] = Sum0.write_done ? 1'd1; - } - group upd3<"static"=1> { - 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; - } + Sum0.addr0 = bit_slice.out; + Sum0.write_en = 1'd1; + add0.left = B_read0_0.out; + add0.right = A_read0_0.out; + Sum0.write_data = 1'd1 ? add0.out; + upd2[done] = Sum0.write_done ? 1'd1; + } + group upd3<"static"=1> { + 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; + } } } } From 50516ec713611ca2f8b01e4dbf67a0494d5bda43 Mon Sep 17 00:00:00 2001 From: Nathaniel Navarro Date: Fri, 23 Feb 2024 22:44:12 -0500 Subject: [PATCH 20/23] remove redundant attributes argument in py_ast --- calyx-py/calyx/py_ast.py | 1 - 1 file changed, 1 deletion(-) diff --git a/calyx-py/calyx/py_ast.py b/calyx-py/calyx/py_ast.py index aff2a1ad6e..07609fdebc 100644 --- a/calyx-py/calyx/py_ast.py +++ b/calyx-py/calyx/py_ast.py @@ -56,7 +56,6 @@ class Component: def __init__( self, name: str, - attributes: list[CompAttribute], inputs: list[PortDef], outputs: list[PortDef], structs: list[Structure], From 5419b3d7331daf6e551ca7883efeba1180624207 Mon Sep 17 00:00:00 2001 From: Nathaniel Navarro Date: Sat, 24 Feb 2024 22:06:29 -0500 Subject: [PATCH 21/23] Add space to py_ast.py to pass runt tests --- calyx-py/calyx/py_ast.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/calyx-py/calyx/py_ast.py b/calyx-py/calyx/py_ast.py index 07609fdebc..0dbc212937 100644 --- a/calyx-py/calyx/py_ast.py +++ b/calyx-py/calyx/py_ast.py @@ -89,7 +89,7 @@ def doc(self) -> str: ins = ", ".join([s.doc() for s in self.inputs]) outs = ", ".join([s.doc() for s in self.outputs]) latency_annotation = ( - f"static<{self.latency}>" if self.latency is not None else "" + f"static<{self.latency}> " if self.latency is not None else "" ) attribute_annotation = f"<{', '.join([f'{a.doc()}' for a in self.attributes])}>" if self.attributes else "" signature = f"{latency_annotation}component {self.name}{attribute_annotation}({ins}) -> ({outs})" From 10b4ef2aa5a2e2bf0bdd2f57f66808d854ceb561 Mon Sep 17 00:00:00 2001 From: Nathaniel Navarro Date: Sun, 25 Feb 2024 18:32:05 -0500 Subject: [PATCH 22/23] rename read_en to content_en --- yxi/axi-calyx/axi-generator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yxi/axi-calyx/axi-generator.py b/yxi/axi-calyx/axi-generator.py index 63e7adbef7..22702345f1 100644 --- a/yxi/axi-calyx/axi-generator.py +++ b/yxi/axi-calyx/axi-generator.py @@ -220,7 +220,7 @@ def add_read_channel(prog, mem): 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 + mem_ref.content_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) @@ -358,7 +358,7 @@ def add_write_channel(prog, mem): # Set data output based on intermal memory output mem_ref.addr0 = curr_addr_internal_mem.out - mem_ref.read_en = 1 + mem_ref.content_en = 1 write_channel.this()["WDATA"] = mem_ref.read_data write_channel.this()["WLAST"] = (max_transfers.out == curr_trsnfr_count.out) @ 1 From 883cac8695f6a5cb747c7c64a02ada34349841bd Mon Sep 17 00:00:00 2001 From: Nathaniel Navarro Date: Mon, 26 Feb 2024 19:15:58 -0500 Subject: [PATCH 23/23] add runt test on generation of calyx-py axi wrapper --- runt.toml | 7 + yxi/axi-calyx/axi-generator.expect | 393 +++++++++++++++++++++++++++++ 2 files changed, 400 insertions(+) create mode 100644 yxi/axi-calyx/axi-generator.expect diff --git a/runt.toml b/runt.toml index 3d2fbc5666..96bebca7e1 100644 --- a/runt.toml +++ b/runt.toml @@ -481,6 +481,13 @@ fud e {} -s verilog.cycle_limit 500 \ --to dat -q """ +[[tests]] +name = "calyx-py AXI wrapper generation" +paths = ["yxi/axi-calyx/axi-generator.py"] +cmd = """ +python3 {} +""" + ##### Xilinx Tests ###### [[tests]] name = "AXI wrapper generation" diff --git a/yxi/axi-calyx/axi-generator.expect b/yxi/axi-calyx/axi-generator.expect new file mode 100644 index 0000000000..ab5b60ba87 --- /dev/null +++ b/yxi/axi-calyx/axi-generator.expect @@ -0,0 +1,393 @@ +import "primitives/core.futil"; +import "primitives/binary_operators.futil"; +import "primitives/memories/seq.futil"; +component m_ar_channel(ARESETn: 1, ARREADY: 1) -> (ARVALID: 1, ARADDR: 64, ARSIZE: 3, ARLEN: 8, ARBURST: 2, ARPROT: 3) { + cells { + arvalid = std_reg(1); + ar_handshake_occurred = std_reg(1); + ref curr_addr_axi = std_reg(64); + arlen = std_reg(8); + txn_n = std_const(32, 1); + txn_count = std_reg(32); + txn_adder = std_add(32); + bt_reg = std_reg(1); + perform_reads = std_neq(32); + } + wires { + ARVALID = arvalid.out; + group do_ar_transfer { + arvalid.in = (!(arvalid.out & ARREADY) & !ar_handshake_occurred.out) ? 1'd1; + arvalid.in = ((arvalid.out & ARREADY) | ar_handshake_occurred.out) ? 1'd0; + arvalid.write_en = 1'd1; + ar_handshake_occurred.in = (arvalid.out & ARREADY) ? 1'd1; + ar_handshake_occurred.write_en = !ar_handshake_occurred.out ? 1'd1; + ARADDR = curr_addr_axi.out; + ARSIZE = 3'd2; + ARLEN = arlen.out; + ARBURST = 2'd1; + ARPROT = 3'd6; + bt_reg.in = (ARREADY & arvalid.out) ? 1'd1; + bt_reg.in = !(ARREADY & arvalid.out) ? 1'd0; + bt_reg.write_en = 1'd1; + do_ar_transfer[done] = bt_reg.out; + } + group incr_txn_count { + txn_adder.left = txn_count.out; + txn_adder.right = 32'd1; + txn_count.in = txn_adder.out; + txn_count.write_en = 1'd1; + incr_txn_count[done] = txn_count.done; + } + comb group perform_reads_group { + perform_reads.left = txn_count.out; + perform_reads.right = txn_n.out; + } + } + control { + seq { + invoke txn_count(in=32'd0)(); + invoke arlen(in=8'd7)(); + while perform_reads.out with perform_reads_group { + seq { + par { + invoke bt_reg(in=1'd0)(); + invoke ar_handshake_occurred(in=1'd0)(); + } + do_ar_transfer; + invoke arvalid(in=1'd0)(); + incr_txn_count; + } + } + } + } +} +component m_aw_channel(ARESETn: 1, AWREADY: 1) -> (AWVALID: 1, AWADDR: 64, AWSIZE: 3, AWLEN: 8, AWBURST: 2, AWPROT: 3) { + cells { + awvalid = std_reg(1); + aw_handshake_occurred = std_reg(1); + ref curr_addr_axi = std_reg(64); + awlen = std_reg(8); + txn_n = std_const(32, 1); + txn_count = std_reg(32); + txn_adder = std_add(32); + bt_reg = std_reg(1); + perform_writes = std_neq(32); + ref max_transfers = std_reg(8); + } + wires { + AWVALID = awvalid.out; + group do_aw_transfer { + awvalid.in = (!(awvalid.out & AWREADY) & !aw_handshake_occurred.out) ? 1'd1; + awvalid.in = ((awvalid.out & AWREADY) | aw_handshake_occurred.out) ? 1'd0; + awvalid.write_en = 1'd1; + aw_handshake_occurred.in = (awvalid.out & AWREADY) ? 1'd1; + aw_handshake_occurred.write_en = !aw_handshake_occurred.out ? 1'd1; + AWADDR = curr_addr_axi.out; + AWSIZE = 3'd2; + AWLEN = awlen.out; + AWBURST = 2'd1; + AWPROT = 3'd6; + bt_reg.in = (AWREADY & awvalid.out) ? 1'd1; + bt_reg.in = !(AWREADY & awvalid.out) ? 1'd0; + bt_reg.write_en = 1'd1; + do_aw_transfer[done] = bt_reg.out; + max_transfers.in = 8'd7; + max_transfers.write_en = 1'd1; + } + group incr_txn_count { + txn_adder.left = txn_count.out; + txn_adder.right = 32'd1; + txn_count.in = txn_adder.out; + txn_count.write_en = 1'd1; + incr_txn_count[done] = txn_count.done; + } + comb group perform_writes_group { + perform_writes.left = txn_count.out; + perform_writes.right = txn_n.out; + } + } + control { + seq { + invoke txn_count(in=32'd0)(); + invoke awlen(in=8'd7)(); + while perform_writes.out with perform_writes_group { + seq { + par { + invoke bt_reg(in=1'd0)(); + invoke aw_handshake_occurred(in=1'd0)(); + } + do_aw_transfer; + invoke awvalid(in=1'd0)(); + incr_txn_count; + } + } + } + } +} +component m_read_channel(ARESETn: 1, RVALID: 1, RLAST: 1, RDATA: 32, RRESP: 2) -> (RREADY: 1) { + cells { + ref mem_ref = seq_mem_d1(32, 8, 3); + rready = std_reg(1); + ref curr_addr_internal_mem = std_reg(3); + ref curr_addr_axi = std_reg(64); + n_RLAST = std_reg(1); + read_data_reg = std_reg(32); + bt_reg = std_reg(1); + curr_addr_internal_mem_incr = std_add(3); + curr_addr_axi_incr = std_add(64); + } + wires { + RREADY = rready.out; + mem_ref.content_en = 1'd0; + group block_transfer { + rready.in = !(rready.out & RVALID) ? 1'd1; + rready.in = (rready.out & RVALID) ? 1'd0; + rready.write_en = 1'd1; + read_data_reg.in = RDATA; + read_data_reg.write_en = (rready.out & RVALID) ? 1'd1; + read_data_reg.write_en = !(rready.out & RVALID) ? 1'd0; + n_RLAST.in = RLAST ? 1'd0; + n_RLAST.in = !RLAST ? 1'd1; + n_RLAST.write_en = 1'd1; + bt_reg.in = (rready.out & RVALID) ? 1'd1; + bt_reg.in = !(rready.out & RVALID) ? 1'd0; + bt_reg.write_en = 1'd1; + block_transfer[done] = bt_reg.out; + } + group service_read_transfer { + rready.in = 1'd0; + rready.write_en = 1'd1; + mem_ref.addr0 = curr_addr_internal_mem.out; + mem_ref.write_data = read_data_reg.out; + mem_ref.write_en = 1'd1; + service_read_transfer[done] = mem_ref.write_done; + } + group curr_addr_internal_mem_incr_group { + curr_addr_internal_mem_incr.left = curr_addr_internal_mem.out; + curr_addr_internal_mem_incr.right = 3'd1; + curr_addr_internal_mem.write_en = 1'd1; + curr_addr_internal_mem.in = curr_addr_internal_mem_incr.out; + curr_addr_internal_mem_incr_group[done] = curr_addr_internal_mem.done; + } + group curr_addr_axi_incr_group { + curr_addr_axi_incr.left = curr_addr_axi.out; + curr_addr_axi_incr.right = 64'd4; + curr_addr_axi.write_en = 1'd1; + curr_addr_axi.in = curr_addr_axi_incr.out; + curr_addr_axi_incr_group[done] = curr_addr_axi.done; + } + } + control { + seq { + invoke n_RLAST(in=1'd1)(); + while n_RLAST.out { + seq { + invoke bt_reg(in=1'd0)(); + block_transfer; + service_read_transfer; + par { + curr_addr_internal_mem_incr_group; + curr_addr_axi_incr_group; + } + } + } + } + } +} +component m_write_channel(ARESETn: 1, WREADY: 1) -> (WVALID: 1, WLAST: 1, WDATA: 32) { + cells { + ref mem_ref = seq_mem_d1(32, 8, 3); + wvalid = std_reg(1); + w_handshake_occurred = std_reg(1); + ref curr_addr_internal_mem = std_reg(3); + ref curr_addr_axi = std_reg(64); + curr_trsnfr_count = std_reg(8); + ref max_transfers = std_reg(8); + n_finished_last_trnsfr = std_reg(1); + bt_reg = std_reg(1); + curr_addr_internal_mem_incr = std_add(3); + curr_addr_axi_incr = std_add(64); + curr_trsnfr_count_incr = std_add(8); + } + wires { + WVALID = wvalid.out; + group service_write_transfer { + wvalid.in = (!(wvalid.out & WREADY) & !w_handshake_occurred.out) ? 1'd1; + wvalid.in = ((wvalid.out & WREADY) | w_handshake_occurred.out) ? 1'd0; + wvalid.write_en = 1'd1; + w_handshake_occurred.in = (wvalid.out & WREADY) ? 1'd1; + w_handshake_occurred.write_en = !w_handshake_occurred.out ? 1'd1; + mem_ref.addr0 = curr_addr_internal_mem.out; + mem_ref.content_en = 1'd1; + WDATA = mem_ref.read_data; + WLAST = (max_transfers.out == curr_trsnfr_count.out) ? 1'd1; + WLAST = (max_transfers.out != curr_trsnfr_count.out) ? 1'd0; + n_finished_last_trnsfr.in = ((max_transfers.out == curr_trsnfr_count.out) & (wvalid.out & WREADY)) ? 1'd0; + n_finished_last_trnsfr.write_en = ((max_transfers.out == curr_trsnfr_count.out) & (wvalid.out & WREADY)) ? 1'd1; + bt_reg.in = (wvalid.out & WREADY) ? 1'd1; + bt_reg.in = !(wvalid.out & WREADY) ? 1'd0; + bt_reg.write_en = 1'd1; + service_write_transfer[done] = bt_reg.out; + } + group curr_addr_internal_mem_incr_group { + curr_addr_internal_mem_incr.left = curr_addr_internal_mem.out; + curr_addr_internal_mem_incr.right = 3'd1; + curr_addr_internal_mem.write_en = 1'd1; + curr_addr_internal_mem.in = curr_addr_internal_mem_incr.out; + curr_addr_internal_mem_incr_group[done] = curr_addr_internal_mem.done; + } + group curr_addr_axi_incr_group { + curr_addr_axi_incr.left = curr_addr_axi.out; + curr_addr_axi_incr.right = 64'd4; + curr_addr_axi.write_en = 1'd1; + curr_addr_axi.in = curr_addr_axi_incr.out; + curr_addr_axi_incr_group[done] = curr_addr_axi.done; + } + group curr_trsnfr_count_incr_group { + curr_trsnfr_count_incr.left = curr_trsnfr_count.out; + curr_trsnfr_count_incr.right = 8'd1; + curr_trsnfr_count.write_en = 1'd1; + curr_trsnfr_count.in = curr_trsnfr_count_incr.out; + curr_trsnfr_count_incr_group[done] = curr_trsnfr_count.done; + } + } + control { + seq { + invoke curr_addr_internal_mem(in=3'd0)(); + invoke n_finished_last_trnsfr(in=1'd1)(); + while n_finished_last_trnsfr.out { + seq { + invoke bt_reg(in=1'd0)(); + service_write_transfer; + par { + curr_addr_internal_mem_incr_group; + curr_trsnfr_count_incr_group; + curr_addr_axi_incr_group; + invoke w_handshake_occurred(in=1'd0)(); + } + } + } + } + } +} +component m_bresp_channel(ARESETn: 1, BVALID: 1) -> (BREADY: 1) { + cells { + bready = std_reg(1); + bt_reg = std_reg(1); + } + wires { + BREADY = bready.out; + group block_transfer { + bready.in = !(bready.out & BVALID) ? 1'd1; + bready.in = (bready.out & BVALID) ? 1'd0; + bready.write_en = 1'd1; + bt_reg.in = (bready.out & BVALID) ? 1'd1; + bt_reg.in = !(bready.out & BVALID) ? 1'd0; + bt_reg.write_en = 1'd1; + block_transfer[done] = bt_reg.out; + } + } + control { + seq { + invoke bt_reg(in=1'd0)(); + block_transfer; + } + } +} +component wrapper<"toplevel"=1>(A0_ARESETn: 1, A0_ARREADY: 1, A0_RVALID: 1, A0_RLAST: 1, A0_RDATA: 32, A0_RRESP: 2, A0_AWREADY: 1, A0_WRESP: 2, A0_WREADY: 1, A0_BVALID: 1, A0_BRESP: 2, A0_RID: 1, B0_ARESETn: 1, B0_ARREADY: 1, B0_RVALID: 1, B0_RLAST: 1, B0_RDATA: 32, B0_RRESP: 2, B0_AWREADY: 1, B0_WRESP: 2, B0_WREADY: 1, B0_BVALID: 1, B0_BRESP: 2, B0_RID: 1, Sum0_ARESETn: 1, Sum0_ARREADY: 1, Sum0_RVALID: 1, Sum0_RLAST: 1, Sum0_RDATA: 32, Sum0_RRESP: 2, Sum0_AWREADY: 1, Sum0_WRESP: 2, Sum0_WREADY: 1, Sum0_BVALID: 1, Sum0_BRESP: 2, Sum0_RID: 1) -> (A0_ARVALID: 1, A0_ARADDR: 64, A0_ARSIZE: 3, A0_ARLEN: 8, A0_ARBURST: 2, A0_RREADY: 1, A0_AWVALID: 1, A0_AWADDR: 64, A0_AWSIZE: 3, A0_AWLEN: 8, A0_AWBURST: 2, A0_AWPROT: 3, A0_WVALID: 1, A0_WLAST: 1, A0_WDATA: 32, A0_BREADY: 1, A0_ARID: 1, A0_AWID: 1, A0_WID: 1, A0_BID: 1, B0_ARVALID: 1, B0_ARADDR: 64, B0_ARSIZE: 3, B0_ARLEN: 8, B0_ARBURST: 2, B0_RREADY: 1, B0_AWVALID: 1, B0_AWADDR: 64, B0_AWSIZE: 3, B0_AWLEN: 8, B0_AWBURST: 2, B0_AWPROT: 3, B0_WVALID: 1, B0_WLAST: 1, B0_WDATA: 32, B0_BREADY: 1, B0_ARID: 1, B0_AWID: 1, B0_WID: 1, B0_BID: 1, Sum0_ARVALID: 1, Sum0_ARADDR: 64, Sum0_ARSIZE: 3, Sum0_ARLEN: 8, Sum0_ARBURST: 2, Sum0_RREADY: 1, Sum0_AWVALID: 1, Sum0_AWADDR: 64, Sum0_AWSIZE: 3, Sum0_AWLEN: 8, Sum0_AWBURST: 2, Sum0_AWPROT: 3, Sum0_WVALID: 1, Sum0_WLAST: 1, Sum0_WDATA: 32, Sum0_BREADY: 1, Sum0_ARID: 1, Sum0_AWID: 1, Sum0_WID: 1, Sum0_BID: 1) { + cells { + main_compute = main(); + curr_addr_internal_mem_A0 = std_reg(3); + curr_addr_axi_A0 = std_reg(64); + ar_channel_A0 = m_ar_channel(); + read_channel_A0 = m_read_channel(); + internal_mem_A0 = seq_mem_d1(32, 8, 3); + max_transfers_A0 = std_reg(8); + aw_channel_A0 = m_aw_channel(); + write_channel_A0 = m_write_channel(); + bresp_channel_A0 = m_bresp_channel(); + curr_addr_internal_mem_B0 = std_reg(3); + curr_addr_axi_B0 = std_reg(64); + ar_channel_B0 = m_ar_channel(); + read_channel_B0 = m_read_channel(); + internal_mem_B0 = seq_mem_d1(32, 8, 3); + max_transfers_B0 = std_reg(8); + aw_channel_B0 = m_aw_channel(); + write_channel_B0 = m_write_channel(); + bresp_channel_B0 = m_bresp_channel(); + curr_addr_internal_mem_Sum0 = std_reg(3); + curr_addr_axi_Sum0 = std_reg(64); + ar_channel_Sum0 = m_ar_channel(); + read_channel_Sum0 = m_read_channel(); + internal_mem_Sum0 = seq_mem_d1(32, 8, 3); + max_transfers_Sum0 = std_reg(8); + aw_channel_Sum0 = m_aw_channel(); + write_channel_Sum0 = m_write_channel(); + bresp_channel_Sum0 = m_bresp_channel(); + } + wires { + A0_ARID = 1'd0; + A0_AWID = 1'd0; + A0_WID = 1'd0; + A0_BID = 1'd0; + B0_ARID = 1'd0; + B0_AWID = 1'd0; + B0_WID = 1'd0; + B0_BID = 1'd0; + Sum0_ARID = 1'd0; + Sum0_AWID = 1'd0; + Sum0_WID = 1'd0; + Sum0_BID = 1'd0; + } + control { + seq { + par { + invoke curr_addr_axi_A0(in=64'd4096)(); + invoke curr_addr_axi_B0(in=64'd4096)(); + invoke curr_addr_axi_Sum0(in=64'd4096)(); + } + par { + invoke curr_addr_internal_mem_A0(in=3'd0)(); + invoke curr_addr_internal_mem_B0(in=3'd0)(); + invoke curr_addr_internal_mem_Sum0(in=3'd0)(); + } + par { + seq { + invoke ar_channel_A0[curr_addr_axi=curr_addr_axi_A0](ARESETn=A0_ARESETn, ARREADY=A0_ARREADY)(ARVALID=A0_ARVALID, ARADDR=A0_ARADDR, ARSIZE=A0_ARSIZE, ARLEN=A0_ARLEN, ARBURST=A0_ARBURST); + invoke read_channel_A0[mem_ref=internal_mem_A0, curr_addr_internal_mem=curr_addr_internal_mem_A0, curr_addr_axi=curr_addr_axi_A0](ARESETn=A0_ARESETn, RVALID=A0_RVALID, RLAST=A0_RLAST, RDATA=A0_RDATA, RRESP=A0_RRESP)(RREADY=A0_RREADY); + } + seq { + invoke ar_channel_B0[curr_addr_axi=curr_addr_axi_B0](ARESETn=B0_ARESETn, ARREADY=B0_ARREADY)(ARVALID=B0_ARVALID, ARADDR=B0_ARADDR, ARSIZE=B0_ARSIZE, ARLEN=B0_ARLEN, ARBURST=B0_ARBURST); + invoke read_channel_B0[mem_ref=internal_mem_B0, curr_addr_internal_mem=curr_addr_internal_mem_B0, curr_addr_axi=curr_addr_axi_B0](ARESETn=B0_ARESETn, RVALID=B0_RVALID, RLAST=B0_RLAST, RDATA=B0_RDATA, RRESP=B0_RRESP)(RREADY=B0_RREADY); + } + seq { + invoke ar_channel_Sum0[curr_addr_axi=curr_addr_axi_Sum0](ARESETn=Sum0_ARESETn, ARREADY=Sum0_ARREADY)(ARVALID=Sum0_ARVALID, ARADDR=Sum0_ARADDR, ARSIZE=Sum0_ARSIZE, ARLEN=Sum0_ARLEN, ARBURST=Sum0_ARBURST); + invoke read_channel_Sum0[mem_ref=internal_mem_Sum0, curr_addr_internal_mem=curr_addr_internal_mem_Sum0, curr_addr_axi=curr_addr_axi_Sum0](ARESETn=Sum0_ARESETn, RVALID=Sum0_RVALID, RLAST=Sum0_RLAST, RDATA=Sum0_RDATA, RRESP=Sum0_RRESP)(RREADY=Sum0_RREADY); + } + } + invoke main_compute[A0=internal_mem_A0, B0=internal_mem_B0, Sum0=internal_mem_Sum0]()(); + par { + invoke curr_addr_axi_A0(in=64'd4096)(); + invoke curr_addr_axi_B0(in=64'd4096)(); + invoke curr_addr_axi_Sum0(in=64'd4096)(); + } + par { + seq { + invoke aw_channel_A0[curr_addr_axi=curr_addr_axi_A0, max_transfers=max_transfers_A0](ARESETn=A0_ARESETn, AWREADY=A0_AWREADY)(AWVALID=A0_AWVALID, AWADDR=A0_AWADDR, AWSIZE=A0_AWSIZE, AWLEN=A0_AWLEN, AWBURST=A0_AWBURST, AWPROT=A0_AWPROT); + invoke write_channel_A0[mem_ref=internal_mem_A0, curr_addr_internal_mem=curr_addr_internal_mem_A0, curr_addr_axi=curr_addr_axi_A0, max_transfers=max_transfers_A0](ARESETn=A0_ARESETn, WREADY=A0_WREADY)(WVALID=A0_WVALID, WLAST=A0_WLAST, WDATA=A0_WDATA); + invoke bresp_channel_A0(BVALID=A0_BVALID)(BREADY=A0_BREADY); + } + seq { + invoke aw_channel_B0[curr_addr_axi=curr_addr_axi_B0, max_transfers=max_transfers_B0](ARESETn=B0_ARESETn, AWREADY=B0_AWREADY)(AWVALID=B0_AWVALID, AWADDR=B0_AWADDR, AWSIZE=B0_AWSIZE, AWLEN=B0_AWLEN, AWBURST=B0_AWBURST, AWPROT=B0_AWPROT); + invoke write_channel_B0[mem_ref=internal_mem_B0, curr_addr_internal_mem=curr_addr_internal_mem_B0, curr_addr_axi=curr_addr_axi_B0, max_transfers=max_transfers_B0](ARESETn=B0_ARESETn, WREADY=B0_WREADY)(WVALID=B0_WVALID, WLAST=B0_WLAST, WDATA=B0_WDATA); + invoke bresp_channel_B0(BVALID=B0_BVALID)(BREADY=B0_BREADY); + } + seq { + invoke aw_channel_Sum0[curr_addr_axi=curr_addr_axi_Sum0, max_transfers=max_transfers_Sum0](ARESETn=Sum0_ARESETn, AWREADY=Sum0_AWREADY)(AWVALID=Sum0_AWVALID, AWADDR=Sum0_AWADDR, AWSIZE=Sum0_AWSIZE, AWLEN=Sum0_AWLEN, AWBURST=Sum0_AWBURST, AWPROT=Sum0_AWPROT); + invoke write_channel_Sum0[mem_ref=internal_mem_Sum0, curr_addr_internal_mem=curr_addr_internal_mem_Sum0, curr_addr_axi=curr_addr_axi_Sum0, max_transfers=max_transfers_Sum0](ARESETn=Sum0_ARESETn, WREADY=Sum0_WREADY)(WVALID=Sum0_WVALID, WLAST=Sum0_WLAST, WDATA=Sum0_WDATA); + invoke bresp_channel_Sum0(BVALID=Sum0_BVALID)(BREADY=Sum0_BREADY); + } + } + } + } +}