From 7c3adb6c2be95946ab8d16bff5b1a14a5aad4a7b Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Wed, 22 Oct 2014 10:47:03 -0700 Subject: [PATCH] Add AXI stream frame joiner, generator, and testbench --- rtl/axis_frame_join.py | 403 +++++++++++++++++++++++++ rtl/axis_frame_join_4.v | 365 +++++++++++++++++++++++ tb/test_axis_frame_join_4.py | 555 +++++++++++++++++++++++++++++++++++ tb/test_axis_frame_join_4.v | 143 +++++++++ 4 files changed, 1466 insertions(+) create mode 100755 rtl/axis_frame_join.py create mode 100644 rtl/axis_frame_join_4.v create mode 100755 tb/test_axis_frame_join_4.py create mode 100644 tb/test_axis_frame_join_4.v diff --git a/rtl/axis_frame_join.py b/rtl/axis_frame_join.py new file mode 100755 index 000000000..50c44de2c --- /dev/null +++ b/rtl/axis_frame_join.py @@ -0,0 +1,403 @@ +#!/usr/bin/env python +"""axis_frame_join + +Generates an AXI Stream frame join module with a specific number of input ports + +Usage: axis_frame_join [OPTION]... + -?, --help display this help and exit + -p, --ports specify number of ports + -n, --name specify module name + -o, --output specify output file name +""" + +import io +import sys +import getopt +from math import * +from jinja2 import Template + +class Usage(Exception): + def __init__(self, msg): + self.msg = msg + +def main(argv=None): + if argv is None: + argv = sys.argv + try: + try: + opts, args = getopt.getopt(argv[1:], "?n:p:o:", ["help", "name=", "ports=", "output="]) + except getopt.error as msg: + raise Usage(msg) + # more code, unchanged + except Usage as err: + print(err.msg, file=sys.stderr) + print("for help use --help", file=sys.stderr) + return 2 + + ports = 4 + name = None + out_name = None + + # process options + for o, a in opts: + if o in ('-?', '--help'): + print(__doc__) + sys.exit(0) + if o in ('-p', '--ports'): + ports = int(a) + if o in ('-n', '--name'): + name = a + if o in ('-o', '--outputs'): + out_name = a + + if name is None: + name = "axis_frame_join_{0}".format(ports) + + if out_name is None: + out_name = name + ".v" + + print("Opening file '%s'..." % out_name) + + try: + out_file = open(out_name, 'w') + except Exception as ex: + print("Error opening \"%s\": %s" %(out_name, ex.strerror), file=sys.stderr) + exit(1) + + print("Generating {0} port AXI Stream frame joiner {1}...".format(ports, name)) + + select_width = ceil(log2(ports)) + + t = Template(u"""/* + +Copyright (c) 2014 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream {{n}} port frame joiner + */ +module {{name}} # +( + parameter ENABLE_TAG = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI inputs + */ +{%- for p in ports %} + input wire [7:0] input_{{p}}_axis_tdata, + input wire input_{{p}}_axis_tvalid, + output wire input_{{p}}_axis_tready, + input wire input_{{p}}_axis_tlast, + input wire input_{{p}}_axis_tuser, +{% endfor %} + /* + * AXI output + */ + output wire [7:0] output_axis_tdata, + output wire output_axis_tvalid, + input wire output_axis_tready, + output wire output_axis_tlast, + output wire output_axis_tuser, + + /* + * Configuration + */ + input wire [15:0] tag, + + /* + * Status signals + */ + output wire busy +); + +// state register +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_WRITE_TAG = 2'd1, + STATE_TRANSFER = 2'd2; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +reg [2:0] frame_ptr_reg = 0, frame_ptr_next; +reg [{{w-1}}:0] port_sel_reg = 0, port_sel_next; + +reg busy_reg = 0, busy_next; + +reg [7:0] input_tdata; +reg input_tvalid; +reg input_tlast; +reg input_tuser; + +reg output_tuser_reg = 0, output_tuser_next; + +// internal datapath +reg [7:0] output_axis_tdata_int; +reg output_axis_tvalid_int; +reg output_axis_tready_int = 0; +reg output_axis_tlast_int; +reg output_axis_tuser_int; +wire output_axis_tready_int_early = output_axis_tready; +{% for p in ports %} +reg input_{{p}}_axis_tready_reg = 0, input_{{p}}_axis_tready_next; +{%- endfor %} +{% for p in ports %} +assign input_{{p}}_axis_tready = input_{{p}}_axis_tready_reg; +{%- endfor %} + +assign busy = busy_reg; + +always @* begin + state_next = 2'bz; + + frame_ptr_next = frame_ptr_reg; + port_sel_next = port_sel_reg; +{% for p in ports %} + input_{{p}}_axis_tready_next = 0; +{%- endfor %} + + output_axis_tdata_int = 0; + output_axis_tvalid_int = 0; + output_axis_tlast_int = 0; + output_axis_tuser_int = 0; + + output_tuser_next = output_tuser_reg; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + frame_ptr_next = 0; + port_sel_next = 0; + output_tuser_next = 0; + + if (ENABLE_TAG) begin + // next cycle if started will send tag, so do not enable input + input_0_axis_tready_next = 0; + end else begin + // next cycle if started will send data, so enable input + input_0_axis_tready_next = output_axis_tready_int_early; + end + + if (input_0_axis_tvalid) begin + // input 0 valid; start transferring data + if (ENABLE_TAG) begin + // tag enabled, so transmit it + if (output_axis_tready_int) begin + // output is ready, so short-circuit first tag byte + frame_ptr_next = 1; + output_axis_tdata_int = tag[15:8]; + output_axis_tvalid_int = 1; + end + + state_next = STATE_WRITE_TAG; + end else begin + // tag disabled, so transmit data + if (output_axis_tready_int) begin + // output is ready, so short-circuit first data byte + output_axis_tdata_int = input_0_axis_tdata; + output_axis_tvalid_int = 1; + end + state_next = STATE_TRANSFER; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_WRITE_TAG: begin + // write tag data + if (output_axis_tready_int) begin + // output ready, so send tag byte + state_next = STATE_WRITE_TAG; + frame_ptr_next = frame_ptr_reg + 1; + output_axis_tvalid_int = 1; + case (frame_ptr_reg) + 2'd0: output_axis_tdata_int = tag[15:8]; + 2'd1: begin + // last tag byte - get ready to send data, enable input if ready + output_axis_tdata_int = tag[7:0]; + input_0_axis_tready_next = output_axis_tready_int_early; + state_next = STATE_TRANSFER; + end + endcase + end else begin + state_next = STATE_WRITE_TAG; + end + end + STATE_TRANSFER: begin + // transfer input data + + // grab correct input lines, set ready line correctly + case (port_sel_reg) +{%- for p in ports %} + {{w}}'d{{p}}: begin + input_tdata = input_{{p}}_axis_tdata; + input_tvalid = input_{{p}}_axis_tvalid; + input_tlast = input_{{p}}_axis_tlast; + input_tuser = input_{{p}}_axis_tuser; + input_{{p}}_axis_tready_next = output_axis_tready_int_early; + end +{%- endfor %} + endcase + + if (input_tvalid & output_axis_tready_int) begin + // output ready, transfer byte + state_next = STATE_TRANSFER; + output_axis_tdata_int = input_tdata; + output_axis_tvalid_int = input_tvalid; + + if (input_tlast) begin + // last flag received, switch to next port + port_sel_next = port_sel_reg + 1; + // save tuser - assert tuser out if ANY tuser asserts received + output_tuser_next = output_tuser_next | input_tuser; + // disable input +{%- for p in ports %} + input_{{p}}_axis_tready_next = 0; +{%- endfor %} + + if (port_sel_reg == {{n-1}}) begin + // last port - send tlast and tuser and revert to idle + output_axis_tlast_int = 1; + output_axis_tuser_int = output_tuser_next; + state_next = STATE_IDLE; + end else begin + // otherwise, disable enable next port + case (port_sel_next) +{%- for p in ports %} + {{w}}'d{{p}}: input_{{p}}_axis_tready_next = output_axis_tready_int_early; +{%- endfor %} + endcase + end + end + end else begin + state_next = STATE_TRANSFER; + end + end + endcase +end + +always @(posedge clk or posedge rst) begin + if (rst) begin + state_reg <= STATE_IDLE; + frame_ptr_reg <= 0; + port_sel_reg <= 0; +{%- for p in ports %} + input_{{p}}_axis_tready_reg <= 0; +{%- endfor %} + output_tuser_reg <= 0; + busy_reg <= 0; + end else begin + state_reg <= state_next; + + frame_ptr_reg <= frame_ptr_next; + + port_sel_reg <= port_sel_next; +{% for p in ports %} + input_{{p}}_axis_tready_reg <= input_{{p}}_axis_tready_next; +{%- endfor %} + + output_tuser_reg <= output_tuser_next; + + busy_reg <= state_next != STATE_IDLE; + end +end + +// output datapath logic +reg [7:0] output_axis_tdata_reg = 0; +reg output_axis_tvalid_reg = 0; +reg output_axis_tlast_reg = 0; +reg output_axis_tuser_reg = 0; + +reg [7:0] temp_axis_tdata_reg = 0; +reg temp_axis_tvalid_reg = 0; +reg temp_axis_tlast_reg = 0; +reg temp_axis_tuser_reg = 0; + +assign output_axis_tdata = output_axis_tdata_reg; +assign output_axis_tvalid = output_axis_tvalid_reg; +assign output_axis_tlast = output_axis_tlast_reg; +assign output_axis_tuser = output_axis_tuser_reg; + +always @(posedge clk or posedge rst) begin + if (rst) begin + output_axis_tdata_reg <= 0; + output_axis_tvalid_reg <= 0; + output_axis_tlast_reg <= 0; + output_axis_tuser_reg <= 0; + output_axis_tready_int <= 0; + temp_axis_tdata_reg <= 0; + temp_axis_tvalid_reg <= 0; + temp_axis_tlast_reg <= 0; + temp_axis_tuser_reg <= 0; + end else begin + // transfer sink ready state to source + // also enable ready input next cycle if output is currently not valid and will not become valid next cycle + output_axis_tready_int <= output_axis_tready | (~output_axis_tvalid_reg & ~output_axis_tvalid_int); + + if (output_axis_tready_int) begin + // input is ready + if (output_axis_tready | ~output_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + output_axis_tdata_reg <= output_axis_tdata_int; + output_axis_tvalid_reg <= output_axis_tvalid_int; + output_axis_tlast_reg <= output_axis_tlast_int; + output_axis_tuser_reg <= output_axis_tuser_int; + end else begin + // output is not ready, store input in temp + temp_axis_tdata_reg <= output_axis_tdata_int; + temp_axis_tvalid_reg <= output_axis_tvalid_int; + temp_axis_tlast_reg <= output_axis_tlast_int; + temp_axis_tuser_reg <= output_axis_tuser_int; + end + end else if (output_axis_tready) begin + // input is not ready, but output is ready + output_axis_tdata_reg <= temp_axis_tdata_reg; + output_axis_tvalid_reg <= temp_axis_tvalid_reg; + output_axis_tlast_reg <= temp_axis_tlast_reg; + output_axis_tuser_reg <= temp_axis_tuser_reg; + end + end +end + +endmodule + +""") + + out_file.write(t.render( + n=ports, + w=select_width, + name=name, + ports=range(ports) + )) + + print("Done") + +if __name__ == "__main__": + sys.exit(main()) + diff --git a/rtl/axis_frame_join_4.v b/rtl/axis_frame_join_4.v new file mode 100644 index 000000000..e3cc89e49 --- /dev/null +++ b/rtl/axis_frame_join_4.v @@ -0,0 +1,365 @@ +/* + +Copyright (c) 2014 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream 4 port frame joiner + */ +module axis_frame_join_4 # +( + parameter ENABLE_TAG = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI inputs + */ + input wire [7:0] input_0_axis_tdata, + input wire input_0_axis_tvalid, + output wire input_0_axis_tready, + input wire input_0_axis_tlast, + input wire input_0_axis_tuser, + + input wire [7:0] input_1_axis_tdata, + input wire input_1_axis_tvalid, + output wire input_1_axis_tready, + input wire input_1_axis_tlast, + input wire input_1_axis_tuser, + + input wire [7:0] input_2_axis_tdata, + input wire input_2_axis_tvalid, + output wire input_2_axis_tready, + input wire input_2_axis_tlast, + input wire input_2_axis_tuser, + + input wire [7:0] input_3_axis_tdata, + input wire input_3_axis_tvalid, + output wire input_3_axis_tready, + input wire input_3_axis_tlast, + input wire input_3_axis_tuser, + + /* + * AXI output + */ + output wire [7:0] output_axis_tdata, + output wire output_axis_tvalid, + input wire output_axis_tready, + output wire output_axis_tlast, + output wire output_axis_tuser, + + /* + * Configuration + */ + input wire [15:0] tag, + + /* + * Status signals + */ + output wire busy +); + +// state register +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_WRITE_TAG = 2'd1, + STATE_TRANSFER = 2'd2; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +reg [2:0] frame_ptr_reg = 0, frame_ptr_next; +reg [1:0] port_sel_reg = 0, port_sel_next; + +reg busy_reg = 0, busy_next; + +reg [7:0] input_tdata; +reg input_tvalid; +reg input_tlast; +reg input_tuser; + +reg output_tuser_reg = 0, output_tuser_next; + +// internal datapath +reg [7:0] output_axis_tdata_int; +reg output_axis_tvalid_int; +reg output_axis_tready_int = 0; +reg output_axis_tlast_int; +reg output_axis_tuser_int; +wire output_axis_tready_int_early = output_axis_tready; + +reg input_0_axis_tready_reg = 0, input_0_axis_tready_next; +reg input_1_axis_tready_reg = 0, input_1_axis_tready_next; +reg input_2_axis_tready_reg = 0, input_2_axis_tready_next; +reg input_3_axis_tready_reg = 0, input_3_axis_tready_next; + +assign input_0_axis_tready = input_0_axis_tready_reg; +assign input_1_axis_tready = input_1_axis_tready_reg; +assign input_2_axis_tready = input_2_axis_tready_reg; +assign input_3_axis_tready = input_3_axis_tready_reg; + +assign busy = busy_reg; + +always @* begin + state_next = 2'bz; + + frame_ptr_next = frame_ptr_reg; + port_sel_next = port_sel_reg; + + input_0_axis_tready_next = 0; + input_1_axis_tready_next = 0; + input_2_axis_tready_next = 0; + input_3_axis_tready_next = 0; + + output_axis_tdata_int = 0; + output_axis_tvalid_int = 0; + output_axis_tlast_int = 0; + output_axis_tuser_int = 0; + + output_tuser_next = output_tuser_reg; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + frame_ptr_next = 0; + port_sel_next = 0; + output_tuser_next = 0; + + if (ENABLE_TAG) begin + // next cycle if started will send tag, so do not enable input + input_0_axis_tready_next = 0; + end else begin + // next cycle if started will send data, so enable input + input_0_axis_tready_next = output_axis_tready_int_early; + end + + if (input_0_axis_tvalid) begin + // input 0 valid; start transferring data + if (ENABLE_TAG) begin + // tag enabled, so transmit it + if (output_axis_tready_int) begin + // output is ready, so short-circuit first tag byte + frame_ptr_next = 1; + output_axis_tdata_int = tag[15:8]; + output_axis_tvalid_int = 1; + end + + state_next = STATE_WRITE_TAG; + end else begin + // tag disabled, so transmit data + if (output_axis_tready_int) begin + // output is ready, so short-circuit first data byte + output_axis_tdata_int = input_0_axis_tdata; + output_axis_tvalid_int = 1; + end + state_next = STATE_TRANSFER; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_WRITE_TAG: begin + // write tag data + if (output_axis_tready_int) begin + // output ready, so send tag byte + state_next = STATE_WRITE_TAG; + frame_ptr_next = frame_ptr_reg + 1; + output_axis_tvalid_int = 1; + case (frame_ptr_reg) + 2'd0: output_axis_tdata_int = tag[15:8]; + 2'd1: begin + // last tag byte - get ready to send data, enable input if ready + output_axis_tdata_int = tag[7:0]; + input_0_axis_tready_next = output_axis_tready_int_early; + state_next = STATE_TRANSFER; + end + endcase + end else begin + state_next = STATE_WRITE_TAG; + end + end + STATE_TRANSFER: begin + // transfer input data + + // grab correct input lines, set ready line correctly + case (port_sel_reg) + 2'd0: begin + input_tdata = input_0_axis_tdata; + input_tvalid = input_0_axis_tvalid; + input_tlast = input_0_axis_tlast; + input_tuser = input_0_axis_tuser; + input_0_axis_tready_next = output_axis_tready_int_early; + end + 2'd1: begin + input_tdata = input_1_axis_tdata; + input_tvalid = input_1_axis_tvalid; + input_tlast = input_1_axis_tlast; + input_tuser = input_1_axis_tuser; + input_1_axis_tready_next = output_axis_tready_int_early; + end + 2'd2: begin + input_tdata = input_2_axis_tdata; + input_tvalid = input_2_axis_tvalid; + input_tlast = input_2_axis_tlast; + input_tuser = input_2_axis_tuser; + input_2_axis_tready_next = output_axis_tready_int_early; + end + 2'd3: begin + input_tdata = input_3_axis_tdata; + input_tvalid = input_3_axis_tvalid; + input_tlast = input_3_axis_tlast; + input_tuser = input_3_axis_tuser; + input_3_axis_tready_next = output_axis_tready_int_early; + end + endcase + + if (input_tvalid & output_axis_tready_int) begin + // output ready, transfer byte + state_next = STATE_TRANSFER; + output_axis_tdata_int = input_tdata; + output_axis_tvalid_int = input_tvalid; + + if (input_tlast) begin + // last flag received, switch to next port + port_sel_next = port_sel_reg + 1; + // save tuser - assert tuser out if ANY tuser asserts received + output_tuser_next = output_tuser_next | input_tuser; + // disable input + input_0_axis_tready_next = 0; + input_1_axis_tready_next = 0; + input_2_axis_tready_next = 0; + input_3_axis_tready_next = 0; + + if (port_sel_reg == 3) begin + // last port - send tlast and tuser and revert to idle + output_axis_tlast_int = 1; + output_axis_tuser_int = output_tuser_next; + state_next = STATE_IDLE; + end else begin + // otherwise, disable enable next port + case (port_sel_next) + 2'd0: input_0_axis_tready_next = output_axis_tready_int_early; + 2'd1: input_1_axis_tready_next = output_axis_tready_int_early; + 2'd2: input_2_axis_tready_next = output_axis_tready_int_early; + 2'd3: input_3_axis_tready_next = output_axis_tready_int_early; + endcase + end + end + end else begin + state_next = STATE_TRANSFER; + end + end + endcase +end + +always @(posedge clk or posedge rst) begin + if (rst) begin + state_reg <= STATE_IDLE; + frame_ptr_reg <= 0; + port_sel_reg <= 0; + input_0_axis_tready_reg <= 0; + input_1_axis_tready_reg <= 0; + input_2_axis_tready_reg <= 0; + input_3_axis_tready_reg <= 0; + output_tuser_reg <= 0; + busy_reg <= 0; + end else begin + state_reg <= state_next; + + frame_ptr_reg <= frame_ptr_next; + + port_sel_reg <= port_sel_next; + + input_0_axis_tready_reg <= input_0_axis_tready_next; + input_1_axis_tready_reg <= input_1_axis_tready_next; + input_2_axis_tready_reg <= input_2_axis_tready_next; + input_3_axis_tready_reg <= input_3_axis_tready_next; + + output_tuser_reg <= output_tuser_next; + + busy_reg <= state_next != STATE_IDLE; + end +end + +// output datapath logic +reg [7:0] output_axis_tdata_reg = 0; +reg output_axis_tvalid_reg = 0; +reg output_axis_tlast_reg = 0; +reg output_axis_tuser_reg = 0; + +reg [7:0] temp_axis_tdata_reg = 0; +reg temp_axis_tvalid_reg = 0; +reg temp_axis_tlast_reg = 0; +reg temp_axis_tuser_reg = 0; + +assign output_axis_tdata = output_axis_tdata_reg; +assign output_axis_tvalid = output_axis_tvalid_reg; +assign output_axis_tlast = output_axis_tlast_reg; +assign output_axis_tuser = output_axis_tuser_reg; + +always @(posedge clk or posedge rst) begin + if (rst) begin + output_axis_tdata_reg <= 0; + output_axis_tvalid_reg <= 0; + output_axis_tlast_reg <= 0; + output_axis_tuser_reg <= 0; + output_axis_tready_int <= 0; + temp_axis_tdata_reg <= 0; + temp_axis_tvalid_reg <= 0; + temp_axis_tlast_reg <= 0; + temp_axis_tuser_reg <= 0; + end else begin + // transfer sink ready state to source + // also enable ready input next cycle if output is currently not valid and will not become valid next cycle + output_axis_tready_int <= output_axis_tready | (~output_axis_tvalid_reg & ~output_axis_tvalid_int); + + if (output_axis_tready_int) begin + // input is ready + if (output_axis_tready | ~output_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + output_axis_tdata_reg <= output_axis_tdata_int; + output_axis_tvalid_reg <= output_axis_tvalid_int; + output_axis_tlast_reg <= output_axis_tlast_int; + output_axis_tuser_reg <= output_axis_tuser_int; + end else begin + // output is not ready, store input in temp + temp_axis_tdata_reg <= output_axis_tdata_int; + temp_axis_tvalid_reg <= output_axis_tvalid_int; + temp_axis_tlast_reg <= output_axis_tlast_int; + temp_axis_tuser_reg <= output_axis_tuser_int; + end + end else if (output_axis_tready) begin + // input is not ready, but output is ready + output_axis_tdata_reg <= temp_axis_tdata_reg; + output_axis_tvalid_reg <= temp_axis_tvalid_reg; + output_axis_tlast_reg <= temp_axis_tlast_reg; + output_axis_tuser_reg <= temp_axis_tuser_reg; + end + end +end + +endmodule diff --git a/tb/test_axis_frame_join_4.py b/tb/test_axis_frame_join_4.py new file mode 100755 index 000000000..bdd5ba98e --- /dev/null +++ b/tb/test_axis_frame_join_4.py @@ -0,0 +1,555 @@ +#!/usr/bin/env python2 +""" + +Copyright (c) 2014 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from myhdl import * +import os +from Queue import Queue +import struct + +import axis_ep + +module = 'axis_frame_join_4' + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("test_%s.v" % module) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o test_%s.vvp %s" % (module, src) + +def dut_axis_frame_join_4(clk, + rst, + current_test, + + input_0_axis_tdata, + input_0_axis_tvalid, + input_0_axis_tready, + input_0_axis_tlast, + input_0_axis_tuser, + + input_1_axis_tdata, + input_1_axis_tvalid, + input_1_axis_tready, + input_1_axis_tlast, + input_1_axis_tuser, + + input_2_axis_tdata, + input_2_axis_tvalid, + input_2_axis_tready, + input_2_axis_tlast, + input_2_axis_tuser, + + input_3_axis_tdata, + input_3_axis_tvalid, + input_3_axis_tready, + input_3_axis_tlast, + input_3_axis_tuser, + + output_axis_tdata, + output_axis_tvalid, + output_axis_tready, + output_axis_tlast, + output_axis_tuser, + + tag, + busy): + + if os.system(build_cmd): + raise Exception("Error running build command") + return Cosimulation("vvp -m myhdl test_%s.vvp -lxt2" % module, + clk=clk, + rst=rst, + current_test=current_test, + + input_0_axis_tdata=input_0_axis_tdata, + input_0_axis_tvalid=input_0_axis_tvalid, + input_0_axis_tready=input_0_axis_tready, + input_0_axis_tlast=input_0_axis_tlast, + input_0_axis_tuser=input_0_axis_tuser, + + input_1_axis_tdata=input_1_axis_tdata, + input_1_axis_tvalid=input_1_axis_tvalid, + input_1_axis_tready=input_1_axis_tready, + input_1_axis_tlast=input_1_axis_tlast, + input_1_axis_tuser=input_1_axis_tuser, + + input_2_axis_tdata=input_2_axis_tdata, + input_2_axis_tvalid=input_2_axis_tvalid, + input_2_axis_tready=input_2_axis_tready, + input_2_axis_tlast=input_2_axis_tlast, + input_2_axis_tuser=input_2_axis_tuser, + + input_3_axis_tdata=input_3_axis_tdata, + input_3_axis_tvalid=input_3_axis_tvalid, + input_3_axis_tready=input_3_axis_tready, + input_3_axis_tlast=input_3_axis_tlast, + input_3_axis_tuser=input_3_axis_tuser, + + output_axis_tdata=output_axis_tdata, + output_axis_tvalid=output_axis_tvalid, + output_axis_tready=output_axis_tready, + output_axis_tlast=output_axis_tlast, + output_axis_tuser=output_axis_tuser, + + tag=tag, + busy=busy) + +def bench(): + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + input_0_axis_tdata = Signal(intbv(0)[8:]) + input_0_axis_tvalid = Signal(bool(0)) + input_0_axis_tlast = Signal(bool(0)) + input_0_axis_tuser = Signal(bool(0)) + input_1_axis_tdata = Signal(intbv(0)[8:]) + input_1_axis_tvalid = Signal(bool(0)) + input_1_axis_tlast = Signal(bool(0)) + input_1_axis_tuser = Signal(bool(0)) + input_2_axis_tdata = Signal(intbv(0)[8:]) + input_2_axis_tvalid = Signal(bool(0)) + input_2_axis_tlast = Signal(bool(0)) + input_2_axis_tuser = Signal(bool(0)) + input_3_axis_tdata = Signal(intbv(0)[8:]) + input_3_axis_tvalid = Signal(bool(0)) + input_3_axis_tlast = Signal(bool(0)) + input_3_axis_tuser = Signal(bool(0)) + output_axis_tready = Signal(bool(0)) + tag = Signal(intbv(0)[15:]) + + # Outputs + input_0_axis_tready = Signal(bool(0)) + input_1_axis_tready = Signal(bool(0)) + input_2_axis_tready = Signal(bool(0)) + input_3_axis_tready = Signal(bool(0)) + output_axis_tdata = Signal(intbv(0)[8:]) + output_axis_tvalid = Signal(bool(0)) + output_axis_tlast = Signal(bool(0)) + output_axis_tuser = Signal(bool(0)) + busy = Signal(bool(0)) + + # sources and sinks + source_0_queue = Queue() + source_0_pause = Signal(bool(0)) + source_1_queue = Queue() + source_1_pause = Signal(bool(0)) + source_2_queue = Queue() + source_2_pause = Signal(bool(0)) + source_3_queue = Queue() + source_3_pause = Signal(bool(0)) + sink_queue = Queue() + sink_pause = Signal(bool(0)) + + source_0 = axis_ep.AXIStreamSource(clk, + rst, + tdata=input_0_axis_tdata, + tvalid=input_0_axis_tvalid, + tready=input_0_axis_tready, + tlast=input_0_axis_tlast, + tuser=input_0_axis_tuser, + fifo=source_0_queue, + pause=source_0_pause, + name='source_0') + + source_1 = axis_ep.AXIStreamSource(clk, + rst, + tdata=input_1_axis_tdata, + tvalid=input_1_axis_tvalid, + tready=input_1_axis_tready, + tlast=input_1_axis_tlast, + tuser=input_1_axis_tuser, + fifo=source_1_queue, + pause=source_1_pause, + name='source_1') + + source_2 = axis_ep.AXIStreamSource(clk, + rst, + tdata=input_2_axis_tdata, + tvalid=input_2_axis_tvalid, + tready=input_2_axis_tready, + tlast=input_2_axis_tlast, + tuser=input_2_axis_tuser, + fifo=source_2_queue, + pause=source_2_pause, + name='source_2') + + source_3 = axis_ep.AXIStreamSource(clk, + rst, + tdata=input_3_axis_tdata, + tvalid=input_3_axis_tvalid, + tready=input_3_axis_tready, + tlast=input_3_axis_tlast, + tuser=input_3_axis_tuser, + fifo=source_3_queue, + pause=source_3_pause, + name='source_3') + + sink = axis_ep.AXIStreamSink(clk, + rst, + tdata=output_axis_tdata, + tvalid=output_axis_tvalid, + tready=output_axis_tready, + tlast=output_axis_tlast, + tuser=output_axis_tuser, + fifo=sink_queue, + pause=sink_pause, + name='sink') + + # DUT + dut = dut_axis_frame_join_4(clk, + rst, + current_test, + + input_0_axis_tdata, + input_0_axis_tvalid, + input_0_axis_tready, + input_0_axis_tlast, + input_0_axis_tuser, + + input_1_axis_tdata, + input_1_axis_tvalid, + input_1_axis_tready, + input_1_axis_tlast, + input_1_axis_tuser, + + input_2_axis_tdata, + input_2_axis_tvalid, + input_2_axis_tready, + input_2_axis_tlast, + input_2_axis_tuser, + + input_3_axis_tdata, + input_3_axis_tvalid, + input_3_axis_tready, + input_3_axis_tlast, + input_3_axis_tuser, + + output_axis_tdata, + output_axis_tvalid, + output_axis_tready, + output_axis_tlast, + output_axis_tuser, + + tag, + busy) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + yield clk.posedge + tag.next = 1 + + yield clk.posedge + print("test 1: test packet") + current_test.next = 1 + + test_frame_0 = axis_ep.AXIStreamFrame(b'\x00\xAA\xBB\xCC\xDD\x00') + test_frame_1 = axis_ep.AXIStreamFrame(b'\x01\xAA\xBB\xCC\xDD\x01') + test_frame_2 = axis_ep.AXIStreamFrame(b'\x02\xAA\xBB\xCC\xDD\x02') + test_frame_3 = axis_ep.AXIStreamFrame(b'\x03\xAA\xBB\xCC\xDD\x03') + source_0_queue.put(test_frame_0) + source_1_queue.put(test_frame_1) + source_2_queue.put(test_frame_2) + source_3_queue.put(test_frame_3) + yield clk.posedge + + yield output_axis_tlast.posedge + yield clk.posedge + yield clk.posedge + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame.data == struct.pack('>H', tag) + test_frame_0.data + test_frame_1.data + test_frame_2.data + test_frame_3.data + + yield delay(100) + + yield clk.posedge + print("test 2: longer packet") + current_test.next = 2 + + test_frame_0 = axis_ep.AXIStreamFrame(b'\x00' + bytearray(range(256)) + b'\x00') + test_frame_1 = axis_ep.AXIStreamFrame(b'\x01\xAA\xBB\xCC\xDD\x01') + test_frame_2 = axis_ep.AXIStreamFrame(b'\x02\xAA\xBB\xCC\xDD\x02') + test_frame_3 = axis_ep.AXIStreamFrame(b'\x03\xAA\xBB\xCC\xDD\x03') + source_0_queue.put(test_frame_0) + source_1_queue.put(test_frame_1) + source_2_queue.put(test_frame_2) + source_3_queue.put(test_frame_3) + yield clk.posedge + + yield output_axis_tlast.posedge + yield clk.posedge + yield clk.posedge + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame.data == struct.pack('>H', tag) + test_frame_0.data + test_frame_1.data + test_frame_2.data + test_frame_3.data + + yield delay(100) + + yield clk.posedge + print("test 3: test packet with pauses") + current_test.next = 3 + + test_frame_0 = axis_ep.AXIStreamFrame(b'\x00\xAA\xBB\xCC\xDD\x00') + test_frame_1 = axis_ep.AXIStreamFrame(b'\x01\xAA\xBB\xCC\xDD\x01') + test_frame_2 = axis_ep.AXIStreamFrame(b'\x02\xAA\xBB\xCC\xDD\x02') + test_frame_3 = axis_ep.AXIStreamFrame(b'\x03\xAA\xBB\xCC\xDD\x03') + source_0_queue.put(test_frame_0) + source_1_queue.put(test_frame_1) + source_2_queue.put(test_frame_2) + source_3_queue.put(test_frame_3) + yield clk.posedge + + yield delay(64) + yield clk.posedge + source_1_pause.next = True + yield delay(32) + yield clk.posedge + source_1_pause.next = False + + yield delay(64) + yield clk.posedge + sink_pause.next = True + yield delay(32) + yield clk.posedge + sink_pause.next = False + + yield output_axis_tlast.posedge + yield clk.posedge + yield clk.posedge + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame.data == struct.pack('>H', tag) + test_frame_0.data + test_frame_1.data + test_frame_2.data + test_frame_3.data + + yield delay(100) + + yield clk.posedge + print("test 4: back-to-back packets") + current_test.next = 4 + + test_frame_0a = axis_ep.AXIStreamFrame(b'\x00\xAA\xBB\xCC\xDD\x00') + test_frame_0b = axis_ep.AXIStreamFrame(b'\x00\xAA\xBB\xCC\xDD\x00') + test_frame_1a = axis_ep.AXIStreamFrame(b'\x01\xAA\xBB\xCC\xDD\x01') + test_frame_1b = axis_ep.AXIStreamFrame(b'\x01\xAA\xBB\xCC\xDD\x01') + test_frame_2a = axis_ep.AXIStreamFrame(b'\x02\xAA\xBB\xCC\xDD\x02') + test_frame_2b = axis_ep.AXIStreamFrame(b'\x02\xAA\xBB\xCC\xDD\x02') + test_frame_3a = axis_ep.AXIStreamFrame(b'\x03\xAA\xBB\xCC\xDD\x03') + test_frame_3b = axis_ep.AXIStreamFrame(b'\x03\xAA\xBB\xCC\xDD\x03') + source_0_queue.put(test_frame_0a) + source_0_queue.put(test_frame_0b) + source_1_queue.put(test_frame_1a) + source_1_queue.put(test_frame_1b) + source_2_queue.put(test_frame_2a) + source_2_queue.put(test_frame_2b) + source_3_queue.put(test_frame_3a) + source_3_queue.put(test_frame_3b) + yield clk.posedge + + yield output_axis_tlast.posedge + yield clk.posedge + yield output_axis_tlast.posedge + yield clk.posedge + yield clk.posedge + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame.data == struct.pack('>H', tag) + test_frame_0a.data + test_frame_1a.data + test_frame_2a.data + test_frame_3a.data + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame.data == struct.pack('>H', tag) + test_frame_0b.data + test_frame_1b.data + test_frame_2b.data + test_frame_3b.data + + yield delay(100) + + yield clk.posedge + print("test 5: alternate pause source") + current_test.next = 5 + + test_frame_0a = axis_ep.AXIStreamFrame(b'\x00\xAA\xBB\xCC\xDD\x00') + test_frame_0b = axis_ep.AXIStreamFrame(b'\x00\xAA\xBB\xCC\xDD\x00') + test_frame_1a = axis_ep.AXIStreamFrame(b'\x01\xAA\xBB\xCC\xDD\x01') + test_frame_1b = axis_ep.AXIStreamFrame(b'\x01\xAA\xBB\xCC\xDD\x01') + test_frame_2a = axis_ep.AXIStreamFrame(b'\x02\xAA\xBB\xCC\xDD\x02') + test_frame_2b = axis_ep.AXIStreamFrame(b'\x02\xAA\xBB\xCC\xDD\x02') + test_frame_3a = axis_ep.AXIStreamFrame(b'\x03\xAA\xBB\xCC\xDD\x03') + test_frame_3b = axis_ep.AXIStreamFrame(b'\x03\xAA\xBB\xCC\xDD\x03') + source_0_queue.put(test_frame_0a) + source_0_queue.put(test_frame_0b) + source_1_queue.put(test_frame_1a) + source_1_queue.put(test_frame_1b) + source_2_queue.put(test_frame_2a) + source_2_queue.put(test_frame_2b) + source_3_queue.put(test_frame_3a) + source_3_queue.put(test_frame_3b) + yield clk.posedge + + while input_3_axis_tvalid or output_axis_tvalid: + source_0_pause.next = True + source_1_pause.next = True + source_2_pause.next = True + source_3_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + source_0_pause.next = False + source_1_pause.next = False + source_2_pause.next = False + source_3_pause.next = False + yield clk.posedge + + yield clk.posedge + yield clk.posedge + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame.data == struct.pack('>H', tag) + test_frame_0a.data + test_frame_1a.data + test_frame_2a.data + test_frame_3a.data + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame.data == struct.pack('>H', tag) + test_frame_0b.data + test_frame_1b.data + test_frame_2b.data + test_frame_3b.data + + yield delay(100) + + yield clk.posedge + print("test 6: alternate pause sink") + current_test.next = 6 + + test_frame_0a = axis_ep.AXIStreamFrame(b'\x00\xAA\xBB\xCC\xDD\x00') + test_frame_0b = axis_ep.AXIStreamFrame(b'\x00\xAA\xBB\xCC\xDD\x00') + test_frame_1a = axis_ep.AXIStreamFrame(b'\x01\xAA\xBB\xCC\xDD\x01') + test_frame_1b = axis_ep.AXIStreamFrame(b'\x01\xAA\xBB\xCC\xDD\x01') + test_frame_2a = axis_ep.AXIStreamFrame(b'\x02\xAA\xBB\xCC\xDD\x02') + test_frame_2b = axis_ep.AXIStreamFrame(b'\x02\xAA\xBB\xCC\xDD\x02') + test_frame_3a = axis_ep.AXIStreamFrame(b'\x03\xAA\xBB\xCC\xDD\x03') + test_frame_3b = axis_ep.AXIStreamFrame(b'\x03\xAA\xBB\xCC\xDD\x03') + source_0_queue.put(test_frame_0a) + source_0_queue.put(test_frame_0b) + source_1_queue.put(test_frame_1a) + source_1_queue.put(test_frame_1b) + source_2_queue.put(test_frame_2a) + source_2_queue.put(test_frame_2b) + source_3_queue.put(test_frame_3a) + source_3_queue.put(test_frame_3b) + yield clk.posedge + + while input_3_axis_tvalid or output_axis_tvalid: + sink_pause.next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + sink_pause.next = False + yield clk.posedge + + yield clk.posedge + yield clk.posedge + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame.data == struct.pack('>H', tag) + test_frame_0a.data + test_frame_1a.data + test_frame_2a.data + test_frame_3a.data + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame.data == struct.pack('>H', tag) + test_frame_0b.data + test_frame_1b.data + test_frame_2b.data + test_frame_3b.data + + yield delay(100) + + yield clk.posedge + print("test 7: tuser assert") + current_test.next = 7 + + test_frame_0 = axis_ep.AXIStreamFrame(b'\x00\xAA\xBB\xCC\xDD\x00') + test_frame_1 = axis_ep.AXIStreamFrame(b'\x01\xAA\xBB\xCC\xDD\x01') + test_frame_2 = axis_ep.AXIStreamFrame(b'\x02\xAA\xBB\xCC\xDD\x02') + test_frame_3 = axis_ep.AXIStreamFrame(b'\x03\xAA\xBB\xCC\xDD\x03') + test_frame_0.user = 1 + source_0_queue.put(test_frame_0) + source_1_queue.put(test_frame_1) + source_2_queue.put(test_frame_2) + source_3_queue.put(test_frame_3) + yield clk.posedge + + yield output_axis_tlast.posedge + yield clk.posedge + yield clk.posedge + + rx_frame = None + if not sink_queue.empty(): + rx_frame = sink_queue.get() + + assert rx_frame.data == struct.pack('>H', tag) + test_frame_0.data + test_frame_1.data + test_frame_2.data + test_frame_3.data + assert rx_frame.user[-1] + + yield delay(100) + + raise StopSimulation + + return dut, source_0, source_1, source_2, source_3, sink, clkgen, check + +def test_bench(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() + diff --git a/tb/test_axis_frame_join_4.v b/tb/test_axis_frame_join_4.v new file mode 100644 index 000000000..21e005b32 --- /dev/null +++ b/tb/test_axis_frame_join_4.v @@ -0,0 +1,143 @@ +/* + +Copyright (c) 2014 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1 ns / 1 ps + +module test_axis_frame_join_4; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [7:0] input_0_axis_tdata = 8'd0; +reg input_0_axis_tvalid = 1'b0; +reg input_0_axis_tlast = 1'b0; +reg input_0_axis_tuser = 1'b0; +reg [7:0] input_1_axis_tdata = 8'd0; +reg input_1_axis_tvalid = 1'b0; +reg input_1_axis_tlast = 1'b0; +reg input_1_axis_tuser = 1'b0; +reg [7:0] input_2_axis_tdata = 8'd0; +reg input_2_axis_tvalid = 1'b0; +reg input_2_axis_tlast = 1'b0; +reg input_2_axis_tuser = 1'b0; +reg [7:0] input_3_axis_tdata = 8'd0; +reg input_3_axis_tvalid = 1'b0; +reg input_3_axis_tlast = 1'b0; +reg input_3_axis_tuser = 1'b0; +reg output_axis_tready = 1'b0; +reg [15:0] tag = 0; + +// Outputs +wire input_0_axis_tready; +wire input_1_axis_tready; +wire input_2_axis_tready; +wire input_3_axis_tready; +wire [7:0] output_axis_tdata; +wire output_axis_tvalid; +wire output_axis_tlast; +wire output_axis_tuser; +wire busy; + +initial begin + // myhdl integration + $from_myhdl(clk, + rst, + current_test, + input_0_axis_tdata, + input_0_axis_tvalid, + input_0_axis_tlast, + input_0_axis_tuser, + input_1_axis_tdata, + input_1_axis_tvalid, + input_1_axis_tlast, + input_1_axis_tuser, + input_2_axis_tdata, + input_2_axis_tvalid, + input_2_axis_tlast, + input_2_axis_tuser, + input_3_axis_tdata, + input_3_axis_tvalid, + input_3_axis_tlast, + input_3_axis_tuser, + output_axis_tready, + tag); + $to_myhdl(input_0_axis_tready, + input_1_axis_tready, + input_2_axis_tready, + input_3_axis_tready, + output_axis_tdata, + output_axis_tvalid, + output_axis_tlast, + output_axis_tuser, + busy); + + // dump file + $dumpfile("test_axis_frame_join_4.lxt"); + $dumpvars(0, test_axis_frame_join_4); +end + +axis_frame_join_4 #( + .ENABLE_TAG(1) +) +UUT ( + .clk(clk), + .rst(rst), + // axi input + .input_0_axis_tdata(input_0_axis_tdata), + .input_0_axis_tvalid(input_0_axis_tvalid), + .input_0_axis_tready(input_0_axis_tready), + .input_0_axis_tlast(input_0_axis_tlast), + .input_0_axis_tuser(input_0_axis_tuser), + .input_1_axis_tdata(input_1_axis_tdata), + .input_1_axis_tvalid(input_1_axis_tvalid), + .input_1_axis_tready(input_1_axis_tready), + .input_1_axis_tlast(input_1_axis_tlast), + .input_1_axis_tuser(input_1_axis_tuser), + .input_2_axis_tdata(input_2_axis_tdata), + .input_2_axis_tvalid(input_2_axis_tvalid), + .input_2_axis_tready(input_2_axis_tready), + .input_2_axis_tlast(input_2_axis_tlast), + .input_2_axis_tuser(input_2_axis_tuser), + .input_3_axis_tdata(input_3_axis_tdata), + .input_3_axis_tvalid(input_3_axis_tvalid), + .input_3_axis_tready(input_3_axis_tready), + .input_3_axis_tlast(input_3_axis_tlast), + .input_3_axis_tuser(input_3_axis_tuser), + // axi output + .output_axis_tdata(output_axis_tdata), + .output_axis_tvalid(output_axis_tvalid), + .output_axis_tready(output_axis_tready), + .output_axis_tlast(output_axis_tlast), + .output_axis_tuser(output_axis_tuser), + // config + .tag(tag), + // status + .busy(busy) +); + +endmodule