diff --git a/rtl/axis_ram_switch.v b/rtl/axis_ram_switch.v new file mode 100644 index 00000000..bfd00030 --- /dev/null +++ b/rtl/axis_ram_switch.v @@ -0,0 +1,1088 @@ +/* + +Copyright (c) 2020 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 RAM switch + */ +module axis_ram_switch # +( + // FIFO depth in words (each virtual FIFO) + // KEEP_WIDTH words per cycle if KEEP_ENABLE set + // Rounded up to nearest power of 2 cycles + parameter FIFO_DEPTH = 4096, + // Command FIFO depth (each virtual FIFO) + // Rounded up to nearest power of 2 + parameter CMD_FIFO_DEPTH = 32, + // Speedup factor (internal data width scaling factor) + // Speedup of 0 scales internal width to provide maximum bandwidth + parameter SPEEDUP = 0, + // Number of AXI stream inputs + parameter S_COUNT = 4, + // Number of AXI stream outputs + parameter M_COUNT = 4, + // Width of input AXI stream interfaces in bits + parameter S_DATA_WIDTH = 8, + // Propagate tkeep signal + parameter S_KEEP_ENABLE = (S_DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter S_KEEP_WIDTH = (S_DATA_WIDTH/8), + // Width of output AXI stream interfaces in bits + parameter M_DATA_WIDTH = 8, + // Propagate tkeep signal + parameter M_KEEP_ENABLE = (M_DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter M_KEEP_WIDTH = (M_DATA_WIDTH/8), + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // tdest signal width + // must be wide enough to uniquely address outputs + parameter DEST_WIDTH = $clog2(M_COUNT), + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1, + // tuser value for bad frame marker + parameter USER_BAD_FRAME_VALUE = 1'b1, + // tuser mask for bad frame marker + parameter USER_BAD_FRAME_MASK = 1'b1, + // Drop frames marked bad + parameter DROP_BAD_FRAME = 0, + // Drop incoming frames when full + // When set, s_axis_tready is always asserted + parameter DROP_WHEN_FULL = 0, + // Output interface routing base tdest selection + // Concatenate M_COUNT DEST_WIDTH sized constants + // Port selected if M_BASE <= tdest <= M_TOP + // set to zero for default routing with tdest MSBs as port index + parameter M_BASE = 0, + // Output interface routing top tdest selection + // Concatenate M_COUNT DEST_WIDTH sized constants + // Port selected if M_BASE <= tdest <= M_TOP + // set to zero to inherit from M_BASE + parameter M_TOP = 0, + // Interface connection control + // M_COUNT concatenated fields of S_COUNT bits + parameter M_CONNECT = {M_COUNT{{S_COUNT{1'b1}}}}, + // arbitration type: "PRIORITY" or "ROUND_ROBIN" + parameter ARB_TYPE = "ROUND_ROBIN", + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "HIGH" +) +( + input wire clk, + input wire rst, + + /* + * AXI Stream inputs + */ + input wire [S_COUNT*S_DATA_WIDTH-1:0] s_axis_tdata, + input wire [S_COUNT*S_KEEP_WIDTH-1:0] s_axis_tkeep, + input wire [S_COUNT-1:0] s_axis_tvalid, + output wire [S_COUNT-1:0] s_axis_tready, + input wire [S_COUNT-1:0] s_axis_tlast, + input wire [S_COUNT*ID_WIDTH-1:0] s_axis_tid, + input wire [S_COUNT*DEST_WIDTH-1:0] s_axis_tdest, + input wire [S_COUNT*USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI Stream outputs + */ + output wire [M_COUNT*M_DATA_WIDTH-1:0] m_axis_tdata, + output wire [M_COUNT*M_KEEP_WIDTH-1:0] m_axis_tkeep, + output wire [M_COUNT-1:0] m_axis_tvalid, + input wire [M_COUNT-1:0] m_axis_tready, + output wire [M_COUNT-1:0] m_axis_tlast, + output wire [M_COUNT*ID_WIDTH-1:0] m_axis_tid, + output wire [M_COUNT*DEST_WIDTH-1:0] m_axis_tdest, + output wire [M_COUNT*USER_WIDTH-1:0] m_axis_tuser, + + /* + * Status + */ + output wire [S_COUNT-1:0] status_overflow, + output wire [S_COUNT-1:0] status_bad_frame, + output wire [S_COUNT-1:0] status_good_frame +); + +parameter CL_S_COUNT = $clog2(S_COUNT); +parameter CL_M_COUNT = $clog2(M_COUNT); + +// force keep width to 1 when disabled +parameter S_KEEP_WIDTH_INT = S_KEEP_ENABLE ? S_KEEP_WIDTH : 1; +parameter M_KEEP_WIDTH_INT = M_KEEP_ENABLE ? M_KEEP_WIDTH : 1; + +// bus word sizes (must be identical) +parameter S_DATA_WORD_SIZE = S_DATA_WIDTH / S_KEEP_WIDTH_INT; +parameter M_DATA_WORD_SIZE = M_DATA_WIDTH / M_KEEP_WIDTH_INT; +// total data and keep widths +parameter MIN_DATA_WIDTH = (M_KEEP_WIDTH_INT > S_KEEP_WIDTH_INT ? M_DATA_WIDTH : S_DATA_WIDTH); +parameter MIN_KEEP_WIDTH = (M_KEEP_WIDTH_INT > S_KEEP_WIDTH_INT ? M_KEEP_WIDTH_INT : S_KEEP_WIDTH_INT); +// speedup factor +parameter M_TOTAL_DATA_WIDTH = M_DATA_WIDTH*M_COUNT; +parameter S_TOTAL_DATA_WIDTH = S_DATA_WIDTH*S_COUNT; +parameter SPEEDUP_INT = SPEEDUP > 0 ? SPEEDUP : (M_TOTAL_DATA_WIDTH > S_TOTAL_DATA_WIDTH ? (M_TOTAL_DATA_WIDTH / MIN_DATA_WIDTH) : (S_TOTAL_DATA_WIDTH / MIN_DATA_WIDTH)); +parameter DATA_WIDTH = MIN_DATA_WIDTH*SPEEDUP_INT; +parameter KEEP_WIDTH = MIN_KEEP_WIDTH*SPEEDUP_INT; + +parameter ADDR_WIDTH = $clog2(FIFO_DEPTH/KEEP_WIDTH); +parameter RAM_ADDR_WIDTH = $clog2(S_COUNT*FIFO_DEPTH/KEEP_WIDTH); + +parameter CMD_ADDR_WIDTH = $clog2(CMD_FIFO_DEPTH); + +integer i, j; + +// check configuration +initial begin + if (S_DATA_WORD_SIZE * S_KEEP_WIDTH_INT != S_DATA_WIDTH) begin + $error("Error: input data width not evenly divisble (instance %m)"); + $finish; + end + + if (M_DATA_WORD_SIZE * M_KEEP_WIDTH_INT != M_DATA_WIDTH) begin + $error("Error: output data width not evenly divisble (instance %m)"); + $finish; + end + + if (S_DATA_WORD_SIZE != M_DATA_WORD_SIZE) begin + $error("Error: word size mismatch (instance %m)"); + $finish; + end + + if (DEST_WIDTH < CL_M_COUNT) begin + $error("Error: DEST_WIDTH too small for port count (instance %m)"); + $finish; + end + + if (M_BASE == 0) begin + // M_BASE is zero, route with tdest as port index + end else if (M_TOP == 0) begin + // M_TOP is zero, assume equal to M_BASE + for (i = 0; i < M_COUNT; i = i + 1) begin + for (j = i+1; j < M_COUNT; j = j + 1) begin + if (M_BASE[i*DEST_WIDTH +: DEST_WIDTH] == M_BASE[j*DEST_WIDTH +: DEST_WIDTH]) begin + $display("%d: %08x", i, M_BASE[i*DEST_WIDTH +: DEST_WIDTH]); + $display("%d: %08x", j, M_BASE[j*DEST_WIDTH +: DEST_WIDTH]); + $error("Error: ranges overlap (instance %m)"); + $finish; + end + end + end + end else begin + for (i = 0; i < M_COUNT; i = i + 1) begin + if (M_BASE[i*DEST_WIDTH +: DEST_WIDTH] > M_TOP[i*DEST_WIDTH +: DEST_WIDTH]) begin + $error("Error: invalid range (instance %m)"); + $finish; + end + end + + for (i = 0; i < M_COUNT; i = i + 1) begin + for (j = i+1; j < M_COUNT; j = j + 1) begin + if (M_BASE[i*DEST_WIDTH +: DEST_WIDTH] <= M_TOP[j*DEST_WIDTH +: DEST_WIDTH] && M_BASE[j*DEST_WIDTH +: DEST_WIDTH] <= M_TOP[i*DEST_WIDTH +: DEST_WIDTH]) begin + $display("%d: %08x-%08x", i, M_BASE[i*DEST_WIDTH +: DEST_WIDTH], M_TOP[i*DEST_WIDTH +: DEST_WIDTH]); + $display("%d: %08x-%08x", j, M_BASE[j*DEST_WIDTH +: DEST_WIDTH], M_TOP[j*DEST_WIDTH +: DEST_WIDTH]); + $error("Error: ranges overlap (instance %m)"); + $finish; + end + end + end + end +end + +// Shared RAM +reg [DATA_WIDTH-1:0] mem[(2**RAM_ADDR_WIDTH)-1:0]; +reg [DATA_WIDTH-1:0] mem_read_data_reg; +reg [M_COUNT-1:0] mem_read_data_valid_reg; + +wire [S_COUNT*DATA_WIDTH-1:0] port_ram_wr_data; +wire [S_COUNT*RAM_ADDR_WIDTH-1:0] port_ram_wr_addr; +wire [S_COUNT-1:0] port_ram_wr_en; +wire [S_COUNT-1:0] port_ram_wr_ack; + +wire [M_COUNT*RAM_ADDR_WIDTH-1:0] port_ram_rd_addr; +wire [M_COUNT-1:0] port_ram_rd_en; +wire [M_COUNT-1:0] port_ram_rd_ack; +wire [M_COUNT*DATA_WIDTH-1:0] port_ram_rd_data; +wire [M_COUNT-1:0] port_ram_rd_data_valid; + +assign port_ram_rd_data = {M_COUNT{mem_read_data_reg}}; +assign port_ram_rd_data_valid = mem_read_data_valid_reg; + +wire [CL_S_COUNT-1:0] ram_wr_sel; +wire ram_wr_en; + +wire [CL_M_COUNT-1:0] ram_rd_sel; +wire ram_rd_en; + +generate + +if (S_COUNT > 1) begin + + arbiter #( + .PORTS(S_COUNT), + .TYPE("ROUND_ROBIN"), + .BLOCK("NONE"), + .LSB_PRIORITY("HIGH") + ) + ram_write_arb_inst ( + .clk(clk), + .rst(rst), + .request(port_ram_wr_en & ~port_ram_wr_ack), + .acknowledge({S_COUNT{1'b0}}), + .grant(port_ram_wr_ack), + .grant_valid(ram_wr_en), + .grant_encoded(ram_wr_sel) + ); + +end else begin + + assign ram_wr_en = port_ram_wr_en; + assign port_ram_wr_ack = port_ram_wr_en; + assign ram_wr_sel = 0; + +end + +endgenerate + +always @(posedge clk) begin + if (ram_wr_en) begin + mem[port_ram_wr_addr[ram_wr_sel*RAM_ADDR_WIDTH +: RAM_ADDR_WIDTH]] <= port_ram_wr_data[ram_wr_sel*DATA_WIDTH +: DATA_WIDTH]; + end +end + +generate + +if (M_COUNT > 1) begin + + arbiter #( + .PORTS(M_COUNT), + .TYPE("ROUND_ROBIN"), + .BLOCK("NONE"), + .LSB_PRIORITY("HIGH") + ) + ram_read_arb_inst ( + .clk(clk), + .rst(rst), + .request(port_ram_rd_en & ~port_ram_rd_ack), + .acknowledge({M_COUNT{1'b0}}), + .grant(port_ram_rd_ack), + .grant_valid(ram_rd_en), + .grant_encoded(ram_rd_sel) + ); + +end else begin + + assign ram_rd_en = port_ram_rd_en; + assign port_ram_rd_ack = port_ram_rd_en; + assign ram_rd_sel = 0; + +end + +endgenerate + +always @(posedge clk) begin + mem_read_data_valid_reg <= 0; + + if (ram_rd_en) begin + mem_read_data_reg <= mem[port_ram_rd_addr[ram_rd_sel*RAM_ADDR_WIDTH +: RAM_ADDR_WIDTH]]; + mem_read_data_valid_reg <= 1 << ram_rd_sel; + end + + if (rst) begin + mem_read_data_valid_reg <= 0; + end +end + +// Interconnect +wire [S_COUNT*RAM_ADDR_WIDTH-1:0] int_cmd_addr; +wire [S_COUNT*ADDR_WIDTH-1:0] int_cmd_len; +wire [S_COUNT*CMD_ADDR_WIDTH-1:0] int_cmd_id; +wire [S_COUNT*KEEP_WIDTH-1:0] int_cmd_tkeep; +wire [S_COUNT*ID_WIDTH-1:0] int_cmd_tid; +wire [S_COUNT*DEST_WIDTH-1:0] int_cmd_tdest; +wire [S_COUNT*USER_WIDTH-1:0] int_cmd_tuser; + +wire [S_COUNT*M_COUNT-1:0] int_cmd_valid; +wire [M_COUNT*S_COUNT-1:0] int_cmd_ready; + +wire [M_COUNT*CMD_ADDR_WIDTH-1:0] int_cmd_status_id; + +wire [M_COUNT*S_COUNT-1:0] int_cmd_status_valid; +wire [S_COUNT*M_COUNT-1:0] int_cmd_status_ready; + +generate + + genvar m, n; + + for (m = 0; m < S_COUNT; m = m + 1) begin : s_ifaces + + wire [DATA_WIDTH-1:0] port_axis_tdata; + wire [KEEP_WIDTH-1:0] port_axis_tkeep; + wire port_axis_tvalid; + wire port_axis_tready; + wire port_axis_tlast; + wire [ID_WIDTH-1:0] port_axis_tid; + wire [DEST_WIDTH-1:0] port_axis_tdest; + wire [USER_WIDTH-1:0] port_axis_tuser; + + axis_adapter #( + .S_DATA_WIDTH(S_DATA_WIDTH), + .S_KEEP_ENABLE(S_KEEP_ENABLE), + .S_KEEP_WIDTH(S_KEEP_WIDTH), + .M_DATA_WIDTH(DATA_WIDTH), + .M_KEEP_ENABLE(1), + .M_KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(1), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) + ) + adapter_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata[S_DATA_WIDTH*m +: S_DATA_WIDTH]), + .s_axis_tkeep(s_axis_tkeep[S_KEEP_WIDTH*m +: S_KEEP_WIDTH]), + .s_axis_tvalid(s_axis_tvalid[m]), + .s_axis_tready(s_axis_tready[m]), + .s_axis_tlast(s_axis_tlast[m]), + .s_axis_tid(s_axis_tid[ID_WIDTH*m +: ID_WIDTH]), + .s_axis_tdest(s_axis_tdest[DEST_WIDTH*m +: DEST_WIDTH]), + .s_axis_tuser(s_axis_tuser[USER_WIDTH*m +: USER_WIDTH]), + // AXI output + .m_axis_tdata(port_axis_tdata), + .m_axis_tkeep(port_axis_tkeep), + .m_axis_tvalid(port_axis_tvalid), + .m_axis_tready(port_axis_tready), + .m_axis_tlast(port_axis_tlast), + .m_axis_tid(port_axis_tid), + .m_axis_tdest(port_axis_tdest), + .m_axis_tuser(port_axis_tuser) + ); + + // decoding + reg [CL_M_COUNT-1:0] select_reg = 0, select_next; + reg drop_reg = 1'b0, drop_next; + reg select_valid_reg = 1'b0, select_valid_next; + + integer k; + + always @* begin + select_next = select_reg; + drop_next = drop_reg && !(port_axis_tvalid && port_axis_tready && port_axis_tlast); + select_valid_next = select_valid_reg && !(port_axis_tvalid && port_axis_tready && port_axis_tlast); + + if (port_axis_tvalid && !select_valid_reg && !drop_reg) begin + select_next = 1'b0; + select_valid_next = 1'b0; + drop_next = 1'b1; + for (k = 0; k < M_COUNT; k = k + 1) begin + if (M_BASE == 0) begin + // M_BASE is zero, route with $clog2(M_COUNT) MSBs of tdest as port index + if (port_axis_tdest[DEST_WIDTH-CL_M_COUNT +: CL_M_COUNT] == k && (M_CONNECT & (1 << (m+k*S_COUNT)))) begin + select_next = k; + select_valid_next = 1'b1; + drop_next = 1'b0; + end + end else if (M_TOP == 0) begin + // M_TOP is zero, assume equal to M_BASE + if (port_axis_tdest == M_BASE[k*DEST_WIDTH +: DEST_WIDTH] && (M_CONNECT & (1 << (m+k*S_COUNT)))) begin + select_next = k; + select_valid_next = 1'b1; + drop_next = 1'b0; + end + end else begin + if (port_axis_tdest >= M_BASE[k*DEST_WIDTH +: DEST_WIDTH] && port_axis_tdest <= M_TOP[k*DEST_WIDTH +: DEST_WIDTH] && (M_CONNECT & (1 << (m+k*S_COUNT)))) begin + select_next = k; + select_valid_next = 1'b1; + drop_next = 1'b0; + end + end + end + end + end + + always @(posedge clk) begin + select_reg <= select_next; + drop_reg <= drop_next; + select_valid_reg <= select_valid_next; + + if (rst) begin + select_valid_reg <= 1'b0; + end + end + + // status arbitration + wire [M_COUNT-1:0] request; + wire [M_COUNT-1:0] acknowledge; + wire [M_COUNT-1:0] grant; + wire grant_valid; + wire [CL_M_COUNT-1:0] grant_encoded; + + arbiter #( + .PORTS(M_COUNT), + .TYPE(ARB_TYPE), + .BLOCK("ACKNOWLEDGE"), + .LSB_PRIORITY(LSB_PRIORITY) + ) + cmd_status_arb_inst ( + .clk(clk), + .rst(rst), + .request(request), + .acknowledge(acknowledge), + .grant(grant), + .grant_valid(grant_valid), + .grant_encoded(grant_encoded) + ); + + // mux + wire [CMD_ADDR_WIDTH-1:0] cmd_status_id_mux = int_cmd_status_id[grant_encoded*CMD_ADDR_WIDTH +: CMD_ADDR_WIDTH]; + wire cmd_status_valid_mux = int_cmd_status_valid[grant_encoded*S_COUNT+m] && grant_valid; + wire cmd_status_ready_mux; + + assign int_cmd_status_ready[m*M_COUNT +: M_COUNT] = (grant_valid && cmd_status_ready_mux) << grant_encoded; + + for (n = 0; n < M_COUNT; n = n + 1) begin + assign request[n] = int_cmd_status_valid[m+n*S_COUNT] && !grant[n]; + assign acknowledge[n] = grant[n] && int_cmd_status_valid[m+n*S_COUNT] && cmd_status_ready_mux; + end + + reg [ADDR_WIDTH:0] wr_ptr_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_next; + reg [ADDR_WIDTH:0] wr_ptr_cur_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_cur_next; + reg [ADDR_WIDTH:0] rd_ptr_reg = {ADDR_WIDTH+1{1'b0}}, rd_ptr_next; + + reg [ADDR_WIDTH-1:0] len_reg = {ADDR_WIDTH{1'b0}}, len_next; + + // full when first MSB different but rest same + wire full = wr_ptr_cur_reg == (rd_ptr_reg ^ {1'b1, {ADDR_WIDTH{1'b0}}}); + // empty when pointers match exactly + wire empty = wr_ptr_reg == rd_ptr_reg; + // overflow within packet + wire full_wr = wr_ptr_cur_reg == (wr_ptr_reg ^ {1'b1, {ADDR_WIDTH{1'b0}}}); + + reg drop_frame_reg = 1'b0, drop_frame_next; + reg overflow_reg = 1'b0, overflow_next; + reg bad_frame_reg = 1'b0, bad_frame_next; + reg good_frame_reg = 1'b0, good_frame_next; + + reg [DATA_WIDTH-1:0] ram_wr_data_reg = {DATA_WIDTH{1'b0}}, ram_wr_data_next; + reg [ADDR_WIDTH-1:0] ram_wr_addr_reg = {ADDR_WIDTH{1'b0}}, ram_wr_addr_next; + reg ram_wr_en_reg = 1'b0, ram_wr_en_next; + wire ram_wr_ack; + + reg [2**CMD_ADDR_WIDTH-1:0] cmd_table_active = 0; + reg [2**CMD_ADDR_WIDTH-1:0] cmd_table_commit = 0; + reg [RAM_ADDR_WIDTH+1-1:0] cmd_table_addr_start[2**CMD_ADDR_WIDTH-1:0]; + reg [RAM_ADDR_WIDTH+1-1:0] cmd_table_addr_end[2**CMD_ADDR_WIDTH-1:0]; + reg [ADDR_WIDTH-1:0] cmd_table_len[2**CMD_ADDR_WIDTH-1:0]; + reg [CL_M_COUNT-1:0] cmd_table_select[2**CMD_ADDR_WIDTH-1:0]; + reg [KEEP_WIDTH-1:0] cmd_table_tkeep[2**CMD_ADDR_WIDTH-1:0]; + reg [ID_WIDTH-1:0] cmd_table_tid[2**CMD_ADDR_WIDTH-1:0]; + reg [DEST_WIDTH-1:0] cmd_table_tdest[2**CMD_ADDR_WIDTH-1:0]; + reg [USER_WIDTH-1:0] cmd_table_tuser[2**CMD_ADDR_WIDTH-1:0]; + + reg [CMD_ADDR_WIDTH+1-1:0] cmd_table_start_ptr_reg = 0; + reg [RAM_ADDR_WIDTH+1-1:0] cmd_table_start_addr_start; + reg [RAM_ADDR_WIDTH+1-1:0] cmd_table_start_addr_end; + reg [ADDR_WIDTH-1:0] cmd_table_start_len; + reg [CL_M_COUNT-1:0] cmd_table_start_select; + reg [KEEP_WIDTH-1:0] cmd_table_start_tkeep; + reg [ID_WIDTH-1:0] cmd_table_start_tid; + reg [DEST_WIDTH-1:0] cmd_table_start_tdest; + reg [USER_WIDTH-1:0] cmd_table_start_tuser; + reg cmd_table_start_en; + reg [CMD_ADDR_WIDTH+1-1:0] cmd_table_read_ptr_reg = 0; + reg cmd_table_read_en; + reg [CMD_ADDR_WIDTH-1:0] cmd_table_commit_ptr; + reg cmd_table_commit_en; + reg [CMD_ADDR_WIDTH+1-1:0] cmd_table_finish_ptr_reg = 0; + reg cmd_table_finish_en; + + reg [RAM_ADDR_WIDTH-1:0] cmd_addr_reg = {RAM_ADDR_WIDTH{1'b0}}, cmd_addr_next; + reg [ADDR_WIDTH-1:0] cmd_len_reg = {ADDR_WIDTH{1'b0}}, cmd_len_next; + reg [CMD_ADDR_WIDTH-1:0] cmd_id_reg = {CMD_ADDR_WIDTH{1'b0}}, cmd_id_next; + reg [KEEP_WIDTH-1:0] cmd_tkeep_reg = {KEEP_WIDTH{1'b0}}, cmd_tkeep_next; + reg [ID_WIDTH-1:0] cmd_tid_reg = {ID_WIDTH{1'b0}}, cmd_tid_next; + reg [DEST_WIDTH-1:0] cmd_tdest_reg = {DEST_WIDTH{1'b0}}, cmd_tdest_next; + reg [USER_WIDTH-1:0] cmd_tuser_reg = {USER_WIDTH{1'b0}}, cmd_tuser_next; + reg [M_COUNT-1:0] cmd_valid_reg = 0, cmd_valid_next; + + reg cmd_status_ready_reg = 1'b0, cmd_status_ready_next; + + wire [M_COUNT-1:0] port_cmd_ready; + for (n = 0; n < M_COUNT; n = n + 1) begin + assign port_cmd_ready[n] = int_cmd_ready[m+n*S_COUNT]; + end + + assign port_axis_tready = (select_valid_reg && (!ram_wr_en_reg || ram_wr_ack) && (!full || full_wr || DROP_WHEN_FULL) && ($unsigned(cmd_table_start_ptr_reg - cmd_table_finish_ptr_reg) < 2**CMD_ADDR_WIDTH)) || drop_reg; + + assign port_ram_wr_data[m*DATA_WIDTH +: DATA_WIDTH] = ram_wr_data_reg; + assign port_ram_wr_addr[m*RAM_ADDR_WIDTH +: RAM_ADDR_WIDTH] = ram_wr_addr_reg[ADDR_WIDTH-1:0] | (m << ADDR_WIDTH); + assign port_ram_wr_en[m] = ram_wr_en_reg; + assign ram_wr_ack = port_ram_wr_ack[m]; + + assign int_cmd_addr[m*RAM_ADDR_WIDTH +: RAM_ADDR_WIDTH] = cmd_addr_reg[ADDR_WIDTH-1:0] | (m << ADDR_WIDTH); + assign int_cmd_len[m*ADDR_WIDTH +: ADDR_WIDTH] = cmd_len_reg; + assign int_cmd_id[m*CMD_ADDR_WIDTH +: CMD_ADDR_WIDTH] = cmd_id_reg; + assign int_cmd_tkeep[m*KEEP_WIDTH +: KEEP_WIDTH] = cmd_tkeep_reg; + assign int_cmd_tid[m*ID_WIDTH +: ID_WIDTH] = cmd_tid_reg; + assign int_cmd_tdest[m*DEST_WIDTH +: DEST_WIDTH] = cmd_tdest_reg; + assign int_cmd_tuser[m*USER_WIDTH +: USER_WIDTH] = cmd_tuser_reg; + assign int_cmd_valid[m*M_COUNT +: M_COUNT] = cmd_valid_reg; + + assign cmd_status_ready_mux = cmd_status_ready_reg; + + assign status_overflow[m] = overflow_reg; + assign status_bad_frame[m] = bad_frame_reg; + assign status_good_frame[m] = good_frame_reg; + + always @* begin + wr_ptr_next = wr_ptr_reg; + wr_ptr_cur_next = wr_ptr_cur_reg; + rd_ptr_next = rd_ptr_reg; + + len_next = len_reg; + + drop_frame_next = drop_frame_reg; + overflow_next = 1'b0; + bad_frame_next = 1'b0; + good_frame_next = 1'b0; + + ram_wr_data_next = ram_wr_data_reg; + ram_wr_addr_next = ram_wr_addr_reg; + ram_wr_en_next = ram_wr_en_reg && !ram_wr_ack; + + cmd_table_start_addr_start = wr_ptr_reg; + cmd_table_start_addr_end = wr_ptr_cur_reg + 1; + cmd_table_start_len = len_reg; + cmd_table_start_select = select_reg; + cmd_table_start_tkeep = S_KEEP_ENABLE ? port_axis_tkeep : 1'b1; + cmd_table_start_tid = port_axis_tid; + cmd_table_start_tdest = port_axis_tdest; + cmd_table_start_tuser = port_axis_tuser; + cmd_table_start_en = 1'b0; + + cmd_table_read_en = 1'b0; + + cmd_table_commit_ptr = 0; + cmd_table_commit_en = 1'b0; + + cmd_table_finish_en = 1'b0; + + cmd_addr_next = cmd_addr_reg; + cmd_len_next = cmd_len_reg; + cmd_id_next = cmd_id_reg; + cmd_tkeep_next = cmd_tkeep_reg; + cmd_tid_next = cmd_tid_reg; + cmd_tdest_next = cmd_tdest_reg; + cmd_tuser_next = cmd_tuser_reg; + cmd_valid_next = cmd_valid_reg; + + cmd_status_ready_next = 1'b0; + + // issue memory writes and commands + if (port_axis_tready && port_axis_tvalid && select_valid_reg && !drop_reg) begin + if (full || full_wr || drop_frame_reg) begin + // full, packet overflow, or currently dropping frame + // drop frame + drop_frame_next = 1'b1; + if (port_axis_tlast) begin + // end of frame, reset write pointer + wr_ptr_cur_next = wr_ptr_reg; + drop_frame_next = 1'b0; + overflow_next = 1'b1; + end + end else begin + wr_ptr_cur_next = wr_ptr_cur_reg + 1; + len_next = len_reg + 1; + + // issue write operation + ram_wr_data_next = port_axis_tdata; + ram_wr_addr_next = wr_ptr_cur_reg; + ram_wr_en_next = 1'b1; + + if (port_axis_tlast) begin + // end of frame + len_next = 0; + if (DROP_BAD_FRAME && USER_BAD_FRAME_MASK & ~(port_axis_tuser ^ USER_BAD_FRAME_VALUE)) begin + // bad packet, reset write pointer + wr_ptr_cur_next = wr_ptr_reg; + bad_frame_next = 1'b1; + end else begin + // good packet, update write pointer + wr_ptr_next = wr_ptr_cur_reg + 1; + good_frame_next = 1'b1; + + cmd_table_start_addr_start = wr_ptr_reg; + cmd_table_start_addr_end = wr_ptr_cur_reg + 1; + cmd_table_start_len = len_reg; + cmd_table_start_select = select_reg; + cmd_table_start_tkeep = S_KEEP_ENABLE ? port_axis_tkeep : 1'b1; + cmd_table_start_tid = port_axis_tid; + cmd_table_start_tdest = port_axis_tdest; + cmd_table_start_tuser = port_axis_tuser; + cmd_table_start_en = 1'b1; + end + end + end + end + + // read + cmd_valid_next = cmd_valid_reg & ~port_cmd_ready; + if (!cmd_valid_reg && cmd_table_active[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]] && cmd_table_read_ptr_reg != cmd_table_start_ptr_reg) begin + cmd_table_read_en = 1'b1; + cmd_addr_next = cmd_table_addr_start[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]]; + cmd_len_next = cmd_table_len[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]]; + cmd_id_next = cmd_table_read_ptr_reg; + cmd_tkeep_next = cmd_table_tkeep[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]]; + cmd_tid_next = cmd_table_tid[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]]; + cmd_tdest_next = cmd_table_tdest[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]]; + cmd_tuser_next = cmd_table_tuser[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]]; + cmd_valid_next = 1 << cmd_table_select[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]]; + end + + // commit + if (cmd_status_valid_mux) begin + cmd_status_ready_next = 1'b1; + cmd_table_commit_ptr = cmd_status_id_mux; + cmd_table_commit_en = 1'b1; + end + + // clean-up + if (cmd_table_active[cmd_table_finish_ptr_reg[CMD_ADDR_WIDTH-1:0]] && cmd_table_commit[cmd_table_finish_ptr_reg[CMD_ADDR_WIDTH-1:0]] && cmd_table_finish_ptr_reg != cmd_table_start_ptr_reg) begin + // update read pointer + rd_ptr_next = cmd_table_addr_end[cmd_table_finish_ptr_reg[CMD_ADDR_WIDTH-1:0]]; + cmd_table_finish_en = 1'b1; + end + end + + always @(posedge clk) begin + wr_ptr_reg <= wr_ptr_next; + wr_ptr_cur_reg <= wr_ptr_cur_next; + rd_ptr_reg <= rd_ptr_next; + + len_reg <= len_next; + + drop_frame_reg <= drop_frame_next; + overflow_reg <= overflow_next; + bad_frame_reg <= bad_frame_next; + good_frame_reg <= good_frame_next; + + ram_wr_data_reg <= ram_wr_data_next; + ram_wr_addr_reg <= ram_wr_addr_next; + ram_wr_en_reg <= ram_wr_en_next; + + cmd_addr_reg <= cmd_addr_next; + cmd_len_reg <= cmd_len_next; + cmd_id_reg <= cmd_id_next; + cmd_tkeep_reg <= cmd_tkeep_next; + cmd_tid_reg <= cmd_tid_next; + cmd_tdest_reg <= cmd_tdest_next; + cmd_tuser_reg <= cmd_tuser_next; + cmd_valid_reg <= cmd_valid_next; + + cmd_status_ready_reg <= cmd_status_ready_next; + + if (cmd_table_start_en) begin + cmd_table_start_ptr_reg <= cmd_table_start_ptr_reg + 1; + cmd_table_active[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= 1'b1; + cmd_table_commit[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= 1'b0; + cmd_table_addr_start[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= cmd_table_start_addr_start; + cmd_table_addr_end[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= cmd_table_start_addr_end; + cmd_table_len[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= cmd_table_start_len; + cmd_table_select[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= cmd_table_start_select; + cmd_table_tkeep[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= cmd_table_start_tkeep; + cmd_table_tid[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= cmd_table_start_tid; + cmd_table_tdest[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= cmd_table_start_tdest; + cmd_table_tuser[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= cmd_table_start_tuser; + end + + if (cmd_table_read_en) begin + cmd_table_read_ptr_reg <= cmd_table_read_ptr_reg + 1; + end + + if (cmd_table_commit_en) begin + cmd_table_commit[cmd_table_commit_ptr] <= 1'b1; + end + + if (cmd_table_finish_en) begin + cmd_table_finish_ptr_reg <= cmd_table_finish_ptr_reg + 1; + cmd_table_active[cmd_table_finish_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= 1'b1; + end + + if (rst) begin + wr_ptr_reg <= {ADDR_WIDTH+1{1'b0}}; + wr_ptr_cur_reg <= {ADDR_WIDTH+1{1'b0}}; + rd_ptr_reg <= {ADDR_WIDTH+1{1'b0}}; + len_reg <= {ADDR_WIDTH{1'b0}}; + drop_frame_reg <= 1'b0; + overflow_reg <= 1'b0; + bad_frame_reg <= 1'b0; + good_frame_reg <= 1'b0; + ram_wr_en_reg <= 1'b0; + cmd_valid_reg <= 1'b0; + cmd_status_ready_reg <= 1'b0; + cmd_table_start_ptr_reg <= 0; + cmd_table_read_ptr_reg <= 0; + cmd_table_finish_ptr_reg <= 0; + end + end + end // s_ifaces + + for (n = 0; n < M_COUNT; n = n + 1) begin : m_ifaces + + // command arbitration + wire [S_COUNT-1:0] request; + wire [S_COUNT-1:0] acknowledge; + wire [S_COUNT-1:0] grant; + wire grant_valid; + wire [CL_S_COUNT-1:0] grant_encoded; + + arbiter #( + .PORTS(S_COUNT), + .TYPE(ARB_TYPE), + .BLOCK("ACKNOWLEDGE"), + .LSB_PRIORITY(LSB_PRIORITY) + ) + cmd_arb_inst ( + .clk(clk), + .rst(rst), + .request(request), + .acknowledge(acknowledge), + .grant(grant), + .grant_valid(grant_valid), + .grant_encoded(grant_encoded) + ); + + // mux + wire [RAM_ADDR_WIDTH-1:0] cmd_addr_mux = int_cmd_addr[grant_encoded*RAM_ADDR_WIDTH +: RAM_ADDR_WIDTH]; + wire [ADDR_WIDTH-1:0] cmd_len_mux = int_cmd_len[grant_encoded*ADDR_WIDTH +: ADDR_WIDTH]; + wire [CMD_ADDR_WIDTH-1:0] cmd_id_mux = int_cmd_id[grant_encoded*CMD_ADDR_WIDTH +: CMD_ADDR_WIDTH]; + wire [KEEP_WIDTH-1:0] cmd_tkeep_mux = int_cmd_tkeep[grant_encoded*KEEP_WIDTH +: KEEP_WIDTH]; + wire [ID_WIDTH-1:0] cmd_tid_mux = int_cmd_tid[grant_encoded*ID_WIDTH +: ID_WIDTH]; + wire [DEST_WIDTH-1:0] cmd_tdest_mux = int_cmd_tdest[grant_encoded*DEST_WIDTH +: DEST_WIDTH]; + wire [USER_WIDTH-1:0] cmd_tuser_mux = int_cmd_tuser[grant_encoded*USER_WIDTH +: USER_WIDTH]; + wire cmd_valid_mux = int_cmd_valid[grant_encoded*M_COUNT+n] && grant_valid; + wire cmd_ready_mux; + + assign int_cmd_ready[n*S_COUNT +: S_COUNT] = (grant_valid && cmd_ready_mux) << grant_encoded; + + for (m = 0; m < S_COUNT; m = m + 1) begin + assign request[m] = int_cmd_valid[m*M_COUNT+n] && !grant[m]; + assign acknowledge[m] = grant[m] && int_cmd_valid[m*M_COUNT+n] && cmd_ready_mux; + end + + reg [RAM_ADDR_WIDTH-1:0] rd_ptr_reg = {RAM_ADDR_WIDTH-1{1'b0}}, rd_ptr_next; + + reg [ADDR_WIDTH-1:0] len_reg = {ADDR_WIDTH{1'b0}}, len_next; + + reg [CL_S_COUNT-1:0] src_reg = 0, src_next; + reg [CMD_ADDR_WIDTH-1:0] id_reg = 0, id_next; + + reg [KEEP_WIDTH-1:0] last_cycle_tkeep_reg = {KEEP_WIDTH{1'b0}}, last_cycle_tkeep_next; + reg [ID_WIDTH-1:0] tid_reg = {ID_WIDTH{1'b0}}, tid_next; + reg [DEST_WIDTH-1:0] tdest_reg = {DEST_WIDTH{1'b0}}, tdest_next; + reg [USER_WIDTH-1:0] tuser_reg = {USER_WIDTH{1'b0}}, tuser_next; + + reg [DATA_WIDTH-1:0] out_axis_tdata_reg = {DATA_WIDTH{1'b0}}, out_axis_tdata_next; + reg [KEEP_WIDTH-1:0] out_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}, out_axis_tkeep_next; + reg out_axis_tvalid_reg = 1'b0, out_axis_tvalid_next; + wire out_axis_tready; + reg out_axis_tlast_reg = 1'b0, out_axis_tlast_next; + reg [ID_WIDTH-1:0] out_axis_tid_reg = {ID_WIDTH{1'b0}}, out_axis_tid_next; + reg [DEST_WIDTH-1:0] out_axis_tdest_reg = {DEST_WIDTH{1'b0}}, out_axis_tdest_next; + reg [USER_WIDTH-1:0] out_axis_tuser_reg = {USER_WIDTH{1'b0}}, out_axis_tuser_next; + + reg [RAM_ADDR_WIDTH-1:0] ram_rd_addr_reg = {RAM_ADDR_WIDTH{1'b0}}, ram_rd_addr_next; + reg ram_rd_en_reg = 1'b0, ram_rd_en_next; + wire ram_rd_ack; + wire [DATA_WIDTH-1:0] ram_rd_data; + wire ram_rd_data_valid; + + reg cmd_ready_reg = 1'b0, cmd_ready_next; + + reg [CMD_ADDR_WIDTH-1:0] cmd_status_id_reg = {CMD_ADDR_WIDTH{1'b0}}, cmd_status_id_next; + reg [S_COUNT-1:0] cmd_status_valid_reg = 0, cmd_status_valid_next; + + wire [S_COUNT-1:0] port_cmd_status_ready; + for (m = 0; m < S_COUNT; m = m + 1) begin + assign port_cmd_status_ready[m] = int_cmd_status_ready[m*M_COUNT+n]; + end + + reg [DATA_WIDTH-1:0] out_fifo_tdata[31:0]; + reg [KEEP_WIDTH-1:0] out_fifo_tkeep[31:0]; + reg out_fifo_tlast[31:0]; + reg [ID_WIDTH-1:0] out_fifo_tid[31:0]; + reg [DEST_WIDTH-1:0] out_fifo_tdest[31:0]; + reg [USER_WIDTH-1:0] out_fifo_tuser[31:0]; + + reg [5:0] out_fifo_data_wr_ptr_reg = 0; + reg [DATA_WIDTH-1:0] out_fifo_data_wr_tdata; + reg out_fifo_data_wr_en; + reg [5:0] out_fifo_ctrl_wr_ptr_reg = 0; + reg [KEEP_WIDTH-1:0] out_fifo_ctrl_wr_tkeep; + reg out_fifo_ctrl_wr_tlast; + reg [ID_WIDTH-1:0] out_fifo_ctrl_wr_tid; + reg [DEST_WIDTH-1:0] out_fifo_ctrl_wr_tdest; + reg [USER_WIDTH-1:0] out_fifo_ctrl_wr_tuser; + reg out_fifo_ctrl_wr_en; + reg [5:0] out_fifo_rd_ptr_reg = 0; + reg out_fifo_rd_en; + + assign port_ram_rd_addr[n*RAM_ADDR_WIDTH +: RAM_ADDR_WIDTH] = ram_rd_addr_reg; + assign port_ram_rd_en[n] = ram_rd_en_reg; + assign ram_rd_ack = port_ram_rd_ack[n]; + assign ram_rd_data = port_ram_rd_data[n*DATA_WIDTH +: DATA_WIDTH]; + assign ram_rd_data_valid = port_ram_rd_data_valid[n]; + + assign cmd_ready_mux = cmd_ready_reg; + + assign int_cmd_status_id[n*CMD_ADDR_WIDTH +: CMD_ADDR_WIDTH] = cmd_status_id_reg; + assign int_cmd_status_valid[n*S_COUNT +: S_COUNT] = cmd_status_valid_reg; + + always @* begin + rd_ptr_next = rd_ptr_reg; + + len_next = len_reg; + + src_next = src_reg; + id_next = id_reg; + + last_cycle_tkeep_next = last_cycle_tkeep_reg; + tid_next = tid_reg; + tdest_next = tdest_reg; + tuser_next = tuser_reg; + + out_axis_tdata_next = out_axis_tdata_reg; + out_axis_tkeep_next = out_axis_tkeep_reg; + out_axis_tvalid_next = out_axis_tvalid_reg && !out_axis_tready; + out_axis_tlast_next = out_axis_tlast_reg; + out_axis_tid_next = out_axis_tid_reg; + out_axis_tdest_next = out_axis_tdest_reg; + out_axis_tuser_next = out_axis_tuser_reg; + + ram_rd_addr_next = ram_rd_addr_reg; + ram_rd_en_next = ram_rd_en_reg && !ram_rd_ack; + + cmd_ready_next = 1'b0; + + cmd_status_id_next = cmd_status_id_reg; + cmd_status_valid_next = cmd_status_valid_reg & ~port_cmd_status_ready; + + out_fifo_data_wr_tdata = ram_rd_data; + out_fifo_data_wr_en = 1'b0; + out_fifo_ctrl_wr_tkeep = len_next == 0 ? last_cycle_tkeep_next : {KEEP_WIDTH{1'b1}}; + out_fifo_ctrl_wr_tlast = len_next == 0; + out_fifo_ctrl_wr_tid = tid_reg; + out_fifo_ctrl_wr_tdest = tdest_reg; + out_fifo_ctrl_wr_tuser = tuser_reg; + out_fifo_ctrl_wr_en = 1'b0; + out_fifo_rd_en = 1'b0; + + // receive commands and issue memory reads + if ((!ram_rd_en_reg || ram_rd_ack) && ($unsigned(out_fifo_ctrl_wr_ptr_reg - out_fifo_rd_ptr_reg) < 16) && ($unsigned(out_fifo_data_wr_ptr_reg - out_fifo_rd_ptr_reg) < 16)) begin + if (len_reg != 0) begin + // more data to read + + // update counters + rd_ptr_next[ADDR_WIDTH-1:0] = rd_ptr_reg[ADDR_WIDTH-1:0] + 1; + len_next = len_reg - 1; + + // issue memory read + ram_rd_addr_next = rd_ptr_reg; + ram_rd_en_next = 1'b1; + + // write output control FIFO + out_fifo_ctrl_wr_tkeep = len_next == 0 ? last_cycle_tkeep_next : {KEEP_WIDTH{1'b1}}; + out_fifo_ctrl_wr_tlast = len_next == 0; + out_fifo_ctrl_wr_tid = tid_reg; + out_fifo_ctrl_wr_tdest = tdest_reg; + out_fifo_ctrl_wr_tuser = tuser_reg; + out_fifo_ctrl_wr_en = 1'b1; + + if (len_next == 0) begin + // indicate operation complete + cmd_status_id_next = id_reg; + cmd_status_valid_next = 1 << src_reg; + end + end else if (cmd_valid_mux && !cmd_ready_reg) begin + // fetch new command + cmd_ready_next = 1'b1; + rd_ptr_next = cmd_addr_mux; + rd_ptr_next[ADDR_WIDTH-1:0] = cmd_addr_mux[ADDR_WIDTH-1:0] + 1; + len_next = cmd_len_mux; + last_cycle_tkeep_next = cmd_tkeep_mux; + id_next = cmd_id_mux; + tid_next = cmd_tid_mux; + tdest_next = cmd_tdest_mux; + tuser_next = cmd_tuser_mux; + + src_next = grant_encoded; + + // issue memory read + ram_rd_addr_next = cmd_addr_mux; + ram_rd_en_next = 1'b1; + + // write output control FIFO + out_fifo_ctrl_wr_tkeep = len_next == 0 ? last_cycle_tkeep_next : {KEEP_WIDTH{1'b1}}; + out_fifo_ctrl_wr_tlast = len_next == 0; + out_fifo_ctrl_wr_tid = cmd_tid_mux; + out_fifo_ctrl_wr_tdest = cmd_tdest_mux; + out_fifo_ctrl_wr_tuser = cmd_tuser_mux; + out_fifo_ctrl_wr_en = 1'b1; + + if (len_next == 0) begin + // indicate operation complete + cmd_status_id_next = cmd_id_mux; + cmd_status_valid_next = 1 << grant_encoded; + end + end + end + + // write RAM read data to output data FIFO + if (ram_rd_data_valid) begin + out_fifo_data_wr_tdata = ram_rd_data; + out_fifo_data_wr_en = 1'b1; + end + + // generate output AXI stream data from control and data FIFOs + if ((out_axis_tready || !out_axis_tvalid_reg) && (out_fifo_rd_ptr_reg != out_fifo_ctrl_wr_ptr_reg) && (out_fifo_rd_ptr_reg != out_fifo_data_wr_ptr_reg)) begin + out_fifo_rd_en = 1'b1; + out_axis_tdata_next = out_fifo_tdata[out_fifo_rd_ptr_reg[4:0]]; + out_axis_tkeep_next = out_fifo_tkeep[out_fifo_rd_ptr_reg[4:0]]; + out_axis_tvalid_next = 1'b1; + out_axis_tlast_next = out_fifo_tlast[out_fifo_rd_ptr_reg[4:0]]; + out_axis_tid_next = out_fifo_tid[out_fifo_rd_ptr_reg[4:0]]; + out_axis_tdest_next = out_fifo_tdest[out_fifo_rd_ptr_reg[4:0]]; + out_axis_tuser_next = out_fifo_tuser[out_fifo_rd_ptr_reg[4:0]]; + end + end + + always @(posedge clk) begin + rd_ptr_reg <= rd_ptr_next; + + len_reg <= len_next; + + src_reg <= src_next; + id_reg <= id_next; + + last_cycle_tkeep_reg <= last_cycle_tkeep_next; + tid_reg <= tid_next; + tdest_reg <= tdest_next; + tuser_reg <= tuser_next; + + out_axis_tdata_reg <= out_axis_tdata_next; + out_axis_tkeep_reg <= out_axis_tkeep_next; + out_axis_tvalid_reg <= out_axis_tvalid_next; + out_axis_tlast_reg <= out_axis_tlast_next; + out_axis_tid_reg <= out_axis_tid_next; + out_axis_tdest_reg <= out_axis_tdest_next; + out_axis_tuser_reg <= out_axis_tuser_next; + + ram_rd_addr_reg <= ram_rd_addr_next; + ram_rd_en_reg <= ram_rd_en_next; + + cmd_ready_reg <= cmd_ready_next; + + cmd_status_id_reg <= cmd_status_id_next; + cmd_status_valid_reg <= cmd_status_valid_next; + + if (out_fifo_data_wr_en) begin + out_fifo_data_wr_ptr_reg <= out_fifo_data_wr_ptr_reg + 1; + out_fifo_tdata[out_fifo_data_wr_ptr_reg[4:0]] <= out_fifo_data_wr_tdata; + end + + if (out_fifo_ctrl_wr_en) begin + out_fifo_ctrl_wr_ptr_reg <= out_fifo_ctrl_wr_ptr_reg + 1; + out_fifo_tkeep[out_fifo_ctrl_wr_ptr_reg[4:0]] <= out_fifo_ctrl_wr_tkeep; + out_fifo_tlast[out_fifo_ctrl_wr_ptr_reg[4:0]] <= out_fifo_ctrl_wr_tlast; + out_fifo_tid[out_fifo_ctrl_wr_ptr_reg[4:0]] <= out_fifo_ctrl_wr_tid; + out_fifo_tdest[out_fifo_ctrl_wr_ptr_reg[4:0]] <= out_fifo_ctrl_wr_tdest; + out_fifo_tuser[out_fifo_ctrl_wr_ptr_reg[4:0]] <= out_fifo_ctrl_wr_tuser; + end + + if (out_fifo_rd_en) begin + out_fifo_rd_ptr_reg <= out_fifo_rd_ptr_reg + 1; + end + + if (rst) begin + len_reg <= 0; + out_axis_tvalid_reg <= 1'b0; + ram_rd_en_reg <= 1'b0; + cmd_ready_reg <= 1'b0; + cmd_status_valid_reg <= 0; + out_fifo_data_wr_ptr_reg <= 0; + out_fifo_ctrl_wr_ptr_reg <= 0; + out_fifo_rd_ptr_reg <= 0; + end + end + + axis_adapter #( + .S_DATA_WIDTH(DATA_WIDTH), + .S_KEEP_ENABLE(1), + .S_KEEP_WIDTH(KEEP_WIDTH), + .M_DATA_WIDTH(M_DATA_WIDTH), + .M_KEEP_ENABLE(M_KEEP_ENABLE), + .M_KEEP_WIDTH(M_KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(1), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) + ) + adapter_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(out_axis_tdata_reg), + .s_axis_tkeep(out_axis_tkeep_reg), + .s_axis_tvalid(out_axis_tvalid_reg), + .s_axis_tready(out_axis_tready), + .s_axis_tlast(out_axis_tlast_reg), + .s_axis_tid(out_axis_tid_reg), + .s_axis_tdest(out_axis_tdest_reg), + .s_axis_tuser(out_axis_tuser_reg), + // AXI output + .m_axis_tdata(m_axis_tdata[M_DATA_WIDTH*n +: M_DATA_WIDTH]), + .m_axis_tkeep(m_axis_tkeep[M_KEEP_WIDTH*n +: M_KEEP_WIDTH]), + .m_axis_tvalid(m_axis_tvalid[n]), + .m_axis_tready(m_axis_tready[n]), + .m_axis_tlast(m_axis_tlast[n]), + .m_axis_tid(m_axis_tid[ID_WIDTH*n +: ID_WIDTH]), + .m_axis_tdest(m_axis_tdest[DEST_WIDTH*n +: DEST_WIDTH]), + .m_axis_tuser(m_axis_tuser[USER_WIDTH*n +: USER_WIDTH]) + ); + end // m_ifaces + +endgenerate + +endmodule diff --git a/tb/test_axis_ram_switch_1x4_256_64.py b/tb/test_axis_ram_switch_1x4_256_64.py new file mode 100755 index 00000000..66ae46d2 --- /dev/null +++ b/tb/test_axis_ram_switch_1x4_256_64.py @@ -0,0 +1,776 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2020 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 + +import axis_ep + +module = 'axis_ram_switch' +testbench = 'test_%s_1x4_256_64' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +# srcs.append("../rtl/axis_ram_switch_input.v") +# srcs.append("../rtl/axis_ram_switch_output.v") +srcs.append("../rtl/axis_adapter.v") +srcs.append("../rtl/arbiter.v") +srcs.append("../rtl/priority_encoder.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + FIFO_DEPTH = 512 + SPEEDUP = 0 + S_COUNT = 1 + M_COUNT = 4 + S_DATA_WIDTH = 256 + S_KEEP_ENABLE = (S_DATA_WIDTH>8) + S_KEEP_WIDTH = (S_DATA_WIDTH/8) + M_DATA_WIDTH = 64 + M_KEEP_ENABLE = (M_DATA_WIDTH>8) + M_KEEP_WIDTH = (M_DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_WIDTH = (M_COUNT+1-1).bit_length() + USER_ENABLE = 1 + USER_WIDTH = 1 + USER_BAD_FRAME_VALUE = 1 + USER_BAD_FRAME_MASK = 1 + DROP_BAD_FRAME = 1 + DROP_WHEN_FULL = 0 + M_BASE = [0, 1, 2, 3] + M_TOP = [0, 1, 2, 3] + M_CONNECT = [0b1111]*M_COUNT + ARB_TYPE = "ROUND_ROBIN" + LSB_PRIORITY = "HIGH" + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata_list = [Signal(intbv(0)[S_DATA_WIDTH:]) for i in range(S_COUNT)] + s_axis_tkeep_list = [Signal(intbv(1)[S_KEEP_WIDTH:]) for i in range(S_COUNT)] + s_axis_tvalid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_axis_tlast_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_axis_tid_list = [Signal(intbv(0)[ID_WIDTH:]) for i in range(S_COUNT)] + s_axis_tdest_list = [Signal(intbv(0)[DEST_WIDTH:]) for i in range(S_COUNT)] + s_axis_tuser_list = [Signal(intbv(0)[USER_WIDTH:]) for i in range(S_COUNT)] + + # s_axis_tdata = ConcatSignal(*reversed(s_axis_tdata_list)) + # s_axis_tkeep = ConcatSignal(*reversed(s_axis_tkeep_list)) + # s_axis_tvalid = ConcatSignal(*reversed(s_axis_tvalid_list)) + # s_axis_tlast = ConcatSignal(*reversed(s_axis_tlast_list)) + # s_axis_tid = ConcatSignal(*reversed(s_axis_tid_list)) + # s_axis_tdest = ConcatSignal(*reversed(s_axis_tdest_list)) + # s_axis_tuser = ConcatSignal(*reversed(s_axis_tuser_list)) + + if S_COUNT == 1: + s_axis_tdata = s_axis_tdata_list[0] + s_axis_tkeep = s_axis_tkeep_list[0] + s_axis_tvalid = s_axis_tvalid_list[0] + s_axis_tlast = s_axis_tlast_list[0] + s_axis_tid = s_axis_tid_list[0] + s_axis_tdest = s_axis_tdest_list[0] + s_axis_tuser = s_axis_tuser_list[0] + else: + s_axis_tdata = ConcatSignal(*reversed(s_axis_tdata_list)) + s_axis_tkeep = ConcatSignal(*reversed(s_axis_tkeep_list)) + s_axis_tvalid = ConcatSignal(*reversed(s_axis_tvalid_list)) + s_axis_tlast = ConcatSignal(*reversed(s_axis_tlast_list)) + s_axis_tid = ConcatSignal(*reversed(s_axis_tid_list)) + s_axis_tdest = ConcatSignal(*reversed(s_axis_tdest_list)) + s_axis_tuser = ConcatSignal(*reversed(s_axis_tuser_list)) + + m_axis_tready_list = [Signal(bool(0)) for i in range(M_COUNT)] + + # m_axis_tready = ConcatSignal(*reversed(m_axis_tready_list)) + + if M_COUNT == 1: + m_axis_tready = m_axis_tready_list[0] + else: + m_axis_tready = ConcatSignal(*reversed(m_axis_tready_list)) + + # Outputs + s_axis_tready = Signal(intbv(0)[S_COUNT:]) + + s_axis_tready_list = [s_axis_tready(i) for i in range(S_COUNT)] + + m_axis_tdata = Signal(intbv(0)[M_COUNT*M_DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(0xf)[M_COUNT*M_KEEP_WIDTH:]) + m_axis_tvalid = Signal(intbv(0)[M_COUNT:]) + m_axis_tlast = Signal(intbv(0)[M_COUNT:]) + m_axis_tid = Signal(intbv(0)[M_COUNT*ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[M_COUNT*DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[M_COUNT*USER_WIDTH:]) + + m_axis_tdata_list = [m_axis_tdata((i+1)*M_DATA_WIDTH, i*M_DATA_WIDTH) for i in range(M_COUNT)] + m_axis_tkeep_list = [m_axis_tkeep((i+1)*M_KEEP_WIDTH, i*M_KEEP_WIDTH) for i in range(M_COUNT)] + m_axis_tvalid_list = [m_axis_tvalid(i) for i in range(M_COUNT)] + m_axis_tlast_list = [m_axis_tlast(i) for i in range(M_COUNT)] + m_axis_tid_list = [m_axis_tid((i+1)*ID_WIDTH, i*ID_WIDTH) for i in range(M_COUNT)] + m_axis_tdest_list = [m_axis_tdest((i+1)*DEST_WIDTH, i*DEST_WIDTH) for i in range(M_COUNT)] + m_axis_tuser_list = [m_axis_tuser((i+1)*USER_WIDTH, i*USER_WIDTH) for i in range(M_COUNT)] + + status_overflow = Signal(intbv(0)[S_COUNT:]) + status_bad_frame = Signal(intbv(0)[S_COUNT:]) + status_good_frame = Signal(intbv(0)[S_COUNT:]) + + # sources and sinks + source_pause_list = [] + source_list = [] + source_logic_list = [] + sink_pause_list = [] + sink_list = [] + sink_logic_list = [] + + for k in range(S_COUNT): + s = axis_ep.AXIStreamSource() + p = Signal(bool(0)) + + source_list.append(s) + source_pause_list.append(p) + + source_logic_list.append(s.create_logic( + clk, + rst, + tdata=s_axis_tdata_list[k], + tkeep=s_axis_tkeep_list[k], + tvalid=s_axis_tvalid_list[k], + tready=s_axis_tready_list[k], + tlast=s_axis_tlast_list[k], + tid=s_axis_tid_list[k], + tdest=s_axis_tdest_list[k], + tuser=s_axis_tuser_list[k], + pause=p, + name='source_%d' % k + )) + + for k in range(M_COUNT): + s = axis_ep.AXIStreamSink() + p = Signal(bool(0)) + + sink_list.append(s) + sink_pause_list.append(p) + + sink_logic_list.append(s.create_logic( + clk, + rst, + tdata=m_axis_tdata_list[k], + tkeep=m_axis_tkeep_list[k], + tvalid=m_axis_tvalid_list[k], + tready=m_axis_tready_list[k], + tlast=m_axis_tlast_list[k], + tid=m_axis_tid_list[k], + tdest=m_axis_tdest_list[k], + tuser=m_axis_tuser_list[k], + pause=p, + name='sink_%d' % k + )) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser, + + status_overflow=status_overflow, + status_bad_frame=status_bad_frame, + status_good_frame=status_good_frame + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + status_overflow_latch = Signal(intbv(0)[S_COUNT:]) + status_bad_frame_latch = Signal(intbv(0)[S_COUNT:]) + status_good_frame_latch = Signal(intbv(0)[S_COUNT:]) + + @always(clk.posedge) + def monitor(): + if status_overflow: + status_overflow_latch.next = status_overflow_latch | status_overflow + if status_bad_frame: + status_bad_frame_latch.next = status_bad_frame_latch | status_bad_frame + if status_good_frame: + status_good_frame_latch.next = status_good_frame_latch | status_good_frame + + def wait_normal(): + while s_axis_tvalid: + yield clk.posedge + + def wait_pause_source(): + while s_axis_tvalid: + yield clk.posedge + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = False + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = True + yield clk.posedge + + for k in range(S_COUNT): + source_pause_list[k].next = False + + def wait_pause_sink(): + while s_axis_tvalid: + for k in range(M_COUNT): + sink_pause_list[k].next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + for k in range(M_COUNT): + sink_pause_list[k].next = False + yield clk.posedge + + @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 + + # testbench stimulus + + yield clk.posedge + print("test 1: 0000 -> 0123") + current_test.next = 1 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x02\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x02\x00\x01\xFF'+bytearray(range(256)), id=0, dest=1) + test_frame2 = axis_ep.AXIStreamFrame(b'\x02\x00\x02\xFF'+bytearray(range(256)), id=0, dest=2) + test_frame3 = axis_ep.AXIStreamFrame(b'\x02\x00\x03\xFF'+bytearray(range(256)), id=0, dest=3) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + source_list[0].send(test_frame0) + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + source_list[0].send(test_frame3) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[1].wait() + rx_frame1 = sink_list[1].recv() + + assert rx_frame1 == test_frame1 + + yield sink_list[2].wait() + rx_frame2 = sink_list[2].recv() + + assert rx_frame2 == test_frame2 + + yield sink_list[3].wait() + rx_frame3 = sink_list[3].recv() + + assert rx_frame3 == test_frame3 + + assert sink_list[0].empty() + assert sink_list[1].empty() + assert sink_list[2].empty() + assert sink_list[3].empty() + + assert status_overflow_latch == 0x0 + assert status_bad_frame_latch == 0x0 + assert status_good_frame_latch == 0x1 + + yield delay(100) + + yield clk.posedge + print("test 2: 0000 -> 0000") + current_test.next = 2 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x02\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x02\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + test_frame2 = axis_ep.AXIStreamFrame(b'\x02\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + test_frame3 = axis_ep.AXIStreamFrame(b'\x02\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + source_list[0].send(test_frame0) + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + source_list[0].send(test_frame3) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[0].wait() + rx_frame1 = sink_list[0].recv() + + assert rx_frame1 == test_frame1 + + yield sink_list[0].wait() + rx_frame2 = sink_list[0].recv() + + assert rx_frame2 == test_frame2 + + yield sink_list[0].wait() + rx_frame3 = sink_list[0].recv() + + assert rx_frame3 == test_frame3 + + assert sink_list[0].empty() + assert sink_list[1].empty() + assert sink_list[2].empty() + assert sink_list[3].empty() + + assert status_overflow_latch == 0x0 + assert status_bad_frame_latch == 0x0 + assert status_good_frame_latch == 0x1 + + yield delay(100) + + yield clk.posedge + print("test 3: bad decoding") + current_test.next = 3 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x03\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x03\x00\x01\xFF'+bytearray(range(256)), id=0, dest=1) + test_frame2 = axis_ep.AXIStreamFrame(b'\x03\x00\x04\xFF'+bytearray(range(256)), id=0, dest=4) + test_frame3 = axis_ep.AXIStreamFrame(b'\x03\x00\x05\xFF'+bytearray(range(256)), id=0, dest=5) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + source_list[0].send(test_frame0) + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + source_list[0].send(test_frame3) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[1].wait() + rx_frame1 = sink_list[1].recv() + + assert rx_frame1 == test_frame1 + + assert sink_list[0].empty() + assert sink_list[1].empty() + assert sink_list[2].empty() + assert sink_list[3].empty() + + assert status_overflow_latch == 0x0 + assert status_bad_frame_latch == 0x0 + assert status_good_frame_latch == 0x1 + + yield delay(100) + + yield clk.posedge + print("test 4: tuser assert") + current_test.next = 4 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x04\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x04\x00\x01\xFF'+bytearray(range(256)), id=0, dest=1, last_cycle_user=1) + test_frame2 = axis_ep.AXIStreamFrame(b'\x04\x00\x02\xFF'+bytearray(range(256)), id=0, dest=2) + test_frame3 = axis_ep.AXIStreamFrame(b'\x04\x00\x03\xFF'+bytearray(range(256)), id=0, dest=3) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + source_list[0].send(test_frame0) + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + source_list[0].send(test_frame3) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[2].wait() + rx_frame2 = sink_list[2].recv() + + assert rx_frame2 == test_frame2 + + yield sink_list[3].wait() + rx_frame3 = sink_list[3].recv() + + assert rx_frame3 == test_frame3 + + assert sink_list[0].empty() + assert sink_list[1].empty() + assert sink_list[2].empty() + assert sink_list[3].empty() + + assert status_overflow_latch == 0x0 + assert status_bad_frame_latch == 0x1 + assert status_good_frame_latch == 0x1 + + yield delay(100) + + yield clk.posedge + print("test 5: single packet overflow") + current_test.next = 5 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x05\x00\x00\xFF'+bytearray(range(256))*3, id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x05\x01\x01\xFF'+bytearray(range(256))*3, id=0, dest=1) + test_frame2 = axis_ep.AXIStreamFrame(b'\x05\x02\x02\xFF'+bytearray(range(256))*3, id=0, dest=2) + test_frame3 = axis_ep.AXIStreamFrame(b'\x05\x03\x03\xFF'+bytearray(range(256))*3, id=0, dest=3) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + source_list[0].send(test_frame0) + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + source_list[0].send(test_frame3) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield delay(100) + + assert sink_list[0].empty() + assert sink_list[1].empty() + assert sink_list[2].empty() + assert sink_list[3].empty() + + assert status_overflow_latch == 0x1 + assert status_bad_frame_latch == 0x0 + assert status_good_frame_latch == 0x0 + + yield delay(100) + + yield clk.posedge + print("test 6: initial sink pause") + current_test.next = 6 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x06\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + source_list[0].send(test_frame0) + + for k in range(M_COUNT): + sink_pause_list[k].next = True + + yield clk.posedge + yield clk.posedge + while (s_axis_tvalid): + yield clk.posedge + for k in range(20): + yield clk.posedge + + for k in range(M_COUNT): + sink_pause_list[k].next = False + + yield wait_normal() + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + assert sink_list[0].empty() + assert sink_list[1].empty() + assert sink_list[2].empty() + assert sink_list[3].empty() + + assert status_overflow_latch == 0x0 + assert status_bad_frame_latch == 0x0 + assert status_good_frame_latch == 0x1 + + yield delay(100) + + yield clk.posedge + print("test 7: initial sink pause, reset") + current_test.next = 7 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x07\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + source_list[0].send(test_frame0) + + for k in range(M_COUNT): + sink_pause_list[k].next = True + + yield clk.posedge + yield clk.posedge + while (s_axis_tvalid): + yield clk.posedge + for k in range(20): + yield clk.posedge + + rst.next = 1 + yield clk.posedge + rst.next = 0 + + for k in range(M_COUNT): + sink_pause_list[k].next = False + + yield delay(500) + + assert sink_list[0].empty() + assert sink_list[1].empty() + assert sink_list[2].empty() + assert sink_list[3].empty() + + assert status_overflow_latch == 0x0 + assert status_bad_frame_latch == 0x0 + assert status_good_frame_latch == 0x1 + + yield delay(100) + + yield clk.posedge + print("test 8: backpressure test") + current_test.next = 8 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x08\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x08\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + test_frame2 = axis_ep.AXIStreamFrame(b'\x08\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + test_frame3 = axis_ep.AXIStreamFrame(b'\x08\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + source_list[0].send(test_frame0) + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + source_list[0].send(test_frame3) + + for k in range(M_COUNT): + sink_pause_list[k].next = True + + for k in range(100): + yield clk.posedge + + for k in range(M_COUNT): + sink_pause_list[k].next = False + + yield wait() + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[0].wait() + rx_frame1 = sink_list[0].recv() + + assert rx_frame1 == test_frame1 + + yield sink_list[0].wait() + rx_frame2 = sink_list[0].recv() + + assert rx_frame2 == test_frame2 + + yield sink_list[0].wait() + rx_frame3 = sink_list[0].recv() + + assert rx_frame3 == test_frame3 + + assert sink_list[0].empty() + assert sink_list[1].empty() + assert sink_list[2].empty() + assert sink_list[3].empty() + + assert status_overflow_latch == 0x0 + assert status_bad_frame_latch == 0x0 + assert status_good_frame_latch == 0x1 + + yield delay(100) + + yield clk.posedge + print("test 9: many small packets, one to one") + current_test.next = 9 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x09\x00\x00\xFF'+bytearray(range(4)), id=0, dest=0) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + for k in range(64): + source_list[0].send(test_frame0) + yield clk.posedge + yield clk.posedge + + yield wait() + + for k in range(64): + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + assert sink_list[0].empty() + assert sink_list[1].empty() + assert sink_list[2].empty() + assert sink_list[3].empty() + + assert status_overflow_latch == 0x0 + assert status_bad_frame_latch == 0x0 + assert status_good_frame_latch == 0x1 + + yield delay(100) + + yield clk.posedge + print("test 10: many small packets, one to many") + current_test.next = 10 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x0A\x00\x00\xFF'+bytearray(range(4)), id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x0A\x00\x01\xFF'+bytearray(range(4)), id=0, dest=1) + test_frame2 = axis_ep.AXIStreamFrame(b'\x0A\x00\x02\xFF'+bytearray(range(4)), id=0, dest=2) + test_frame3 = axis_ep.AXIStreamFrame(b'\x0A\x00\x03\xFF'+bytearray(range(4)), id=0, dest=3) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + for k in range(64): + source_list[0].send(test_frame0) + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + source_list[0].send(test_frame3) + yield clk.posedge + yield clk.posedge + + yield wait() + + for k in range(64): + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[1].wait() + rx_frame1 = sink_list[1].recv() + + assert rx_frame1 == test_frame1 + + yield sink_list[2].wait() + rx_frame2 = sink_list[2].recv() + + assert rx_frame2 == test_frame2 + + yield sink_list[3].wait() + rx_frame3 = sink_list[3].recv() + + assert rx_frame3 == test_frame3 + + assert sink_list[0].empty() + assert sink_list[1].empty() + assert sink_list[2].empty() + assert sink_list[3].empty() + + assert status_overflow_latch == 0x0 + assert status_bad_frame_latch == 0x0 + assert status_good_frame_latch == 0x1 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/tb/test_axis_ram_switch_1x4_256_64.v b/tb/test_axis_ram_switch_1x4_256_64.v new file mode 100644 index 00000000..e5b20ff7 --- /dev/null +++ b/tb/test_axis_ram_switch_1x4_256_64.v @@ -0,0 +1,174 @@ +/* + +Copyright (c) 2020 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 + +/* + * Testbench for axis_ram_switch + */ +module test_axis_ram_switch_1x4_256_64; + +// Parameters +parameter FIFO_DEPTH = 512; +parameter SPEEDUP = 0; +parameter S_COUNT = 1; +parameter M_COUNT = 4; +parameter S_DATA_WIDTH = 256; +parameter S_KEEP_ENABLE = (S_DATA_WIDTH>8); +parameter S_KEEP_WIDTH = (S_DATA_WIDTH/8); +parameter M_DATA_WIDTH = 64; +parameter M_KEEP_ENABLE = (M_DATA_WIDTH>8); +parameter M_KEEP_WIDTH = (M_DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_WIDTH = $clog2(M_COUNT+1); +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; +parameter USER_BAD_FRAME_VALUE = 1'b1; +parameter USER_BAD_FRAME_MASK = 1'b1; +parameter DROP_BAD_FRAME = 1; +parameter DROP_WHEN_FULL = 0; +parameter M_BASE = {3'd3, 3'd2, 3'd1, 3'd0}; +parameter M_TOP = {3'd3, 3'd2, 3'd1, 3'd0}; +parameter M_CONNECT = {M_COUNT{{S_COUNT{1'b1}}}}; +parameter ARB_TYPE = "ROUND_ROBIN"; +parameter LSB_PRIORITY = "HIGH"; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [S_COUNT*S_DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [S_COUNT*S_KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg [S_COUNT-1:0] s_axis_tvalid = 0; +reg [S_COUNT-1:0] s_axis_tlast = 0; +reg [S_COUNT*ID_WIDTH-1:0] s_axis_tid = 0; +reg [S_COUNT*DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [S_COUNT*USER_WIDTH-1:0] s_axis_tuser = 0; +reg [M_COUNT-1:0] m_axis_tready = 0; + +// Outputs +wire [S_COUNT-1:0] s_axis_tready; +wire [M_COUNT*M_DATA_WIDTH-1:0] m_axis_tdata; +wire [M_COUNT*M_KEEP_WIDTH-1:0] m_axis_tkeep; +wire [M_COUNT-1:0] m_axis_tvalid; +wire [M_COUNT-1:0] m_axis_tlast; +wire [M_COUNT*ID_WIDTH-1:0] m_axis_tid; +wire [M_COUNT*DEST_WIDTH-1:0] m_axis_tdest; +wire [M_COUNT*USER_WIDTH-1:0] m_axis_tuser; +wire [S_COUNT-1:0] status_overflow; +wire [S_COUNT-1:0] status_bad_frame; +wire [S_COUNT-1:0] status_good_frame; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser, + status_overflow, + status_bad_frame, + status_good_frame + ); + + // dump file + $dumpfile("test_axis_ram_switch_1x4_256_64.lxt"); + $dumpvars(0, test_axis_ram_switch_1x4_256_64); +end + +axis_ram_switch #( + .FIFO_DEPTH(FIFO_DEPTH), + .SPEEDUP(SPEEDUP), + .S_COUNT(S_COUNT), + .M_COUNT(M_COUNT), + .S_DATA_WIDTH(S_DATA_WIDTH), + .S_KEEP_ENABLE(S_KEEP_ENABLE), + .S_KEEP_WIDTH(S_KEEP_WIDTH), + .M_DATA_WIDTH(M_DATA_WIDTH), + .M_KEEP_ENABLE(M_KEEP_ENABLE), + .M_KEEP_WIDTH(M_KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .USER_BAD_FRAME_VALUE(USER_BAD_FRAME_VALUE), + .USER_BAD_FRAME_MASK(USER_BAD_FRAME_MASK), + .DROP_BAD_FRAME(DROP_BAD_FRAME), + .DROP_WHEN_FULL(DROP_WHEN_FULL), + .M_BASE(M_BASE), + .M_TOP(M_TOP), + .M_CONNECT(M_CONNECT), + .ARB_TYPE(ARB_TYPE), + .LSB_PRIORITY(LSB_PRIORITY) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI inputs + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser), + // Status + .status_overflow(status_overflow), + .status_bad_frame(status_bad_frame), + .status_good_frame(status_good_frame) +); + +endmodule diff --git a/tb/test_axis_ram_switch_4x1_64_256.py b/tb/test_axis_ram_switch_4x1_64_256.py new file mode 100755 index 00000000..45b933b0 --- /dev/null +++ b/tb/test_axis_ram_switch_4x1_64_256.py @@ -0,0 +1,749 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2020 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 + +import axis_ep + +module = 'axis_ram_switch' +testbench = 'test_%s_4x1_64_256' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +# srcs.append("../rtl/axis_ram_switch_input.v") +# srcs.append("../rtl/axis_ram_switch_output.v") +srcs.append("../rtl/axis_adapter.v") +srcs.append("../rtl/arbiter.v") +srcs.append("../rtl/priority_encoder.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + FIFO_DEPTH = 512 + SPEEDUP = 0 + S_COUNT = 4 + M_COUNT = 1 + S_DATA_WIDTH = 64 + S_KEEP_ENABLE = (S_DATA_WIDTH>8) + S_KEEP_WIDTH = (S_DATA_WIDTH/8) + M_DATA_WIDTH = 256 + M_KEEP_ENABLE = (M_DATA_WIDTH>8) + M_KEEP_WIDTH = (M_DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_WIDTH = (M_COUNT+1-1).bit_length() + USER_ENABLE = 1 + USER_WIDTH = 1 + USER_BAD_FRAME_VALUE = 1 + USER_BAD_FRAME_MASK = 1 + DROP_BAD_FRAME = 1 + DROP_WHEN_FULL = 0 + M_BASE = [0, 1, 2, 3] + M_TOP = [0, 1, 2, 3] + M_CONNECT = [0b1111]*M_COUNT + ARB_TYPE = "ROUND_ROBIN" + LSB_PRIORITY = "HIGH" + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata_list = [Signal(intbv(0)[S_DATA_WIDTH:]) for i in range(S_COUNT)] + s_axis_tkeep_list = [Signal(intbv(1)[S_KEEP_WIDTH:]) for i in range(S_COUNT)] + s_axis_tvalid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_axis_tlast_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_axis_tid_list = [Signal(intbv(0)[ID_WIDTH:]) for i in range(S_COUNT)] + s_axis_tdest_list = [Signal(intbv(0)[DEST_WIDTH:]) for i in range(S_COUNT)] + s_axis_tuser_list = [Signal(intbv(0)[USER_WIDTH:]) for i in range(S_COUNT)] + + # s_axis_tdata = ConcatSignal(*reversed(s_axis_tdata_list)) + # s_axis_tkeep = ConcatSignal(*reversed(s_axis_tkeep_list)) + # s_axis_tvalid = ConcatSignal(*reversed(s_axis_tvalid_list)) + # s_axis_tlast = ConcatSignal(*reversed(s_axis_tlast_list)) + # s_axis_tid = ConcatSignal(*reversed(s_axis_tid_list)) + # s_axis_tdest = ConcatSignal(*reversed(s_axis_tdest_list)) + # s_axis_tuser = ConcatSignal(*reversed(s_axis_tuser_list)) + + if S_COUNT == 1: + s_axis_tdata = s_axis_tdata_list[0] + s_axis_tkeep = s_axis_tkeep_list[0] + s_axis_tvalid = s_axis_tvalid_list[0] + s_axis_tlast = s_axis_tlast_list[0] + s_axis_tid = s_axis_tid_list[0] + s_axis_tdest = s_axis_tdest_list[0] + s_axis_tuser = s_axis_tuser_list[0] + else: + s_axis_tdata = ConcatSignal(*reversed(s_axis_tdata_list)) + s_axis_tkeep = ConcatSignal(*reversed(s_axis_tkeep_list)) + s_axis_tvalid = ConcatSignal(*reversed(s_axis_tvalid_list)) + s_axis_tlast = ConcatSignal(*reversed(s_axis_tlast_list)) + s_axis_tid = ConcatSignal(*reversed(s_axis_tid_list)) + s_axis_tdest = ConcatSignal(*reversed(s_axis_tdest_list)) + s_axis_tuser = ConcatSignal(*reversed(s_axis_tuser_list)) + + m_axis_tready_list = [Signal(bool(0)) for i in range(M_COUNT)] + + # m_axis_tready = ConcatSignal(*reversed(m_axis_tready_list)) + + if M_COUNT == 1: + m_axis_tready = m_axis_tready_list[0] + else: + m_axis_tready = ConcatSignal(*reversed(m_axis_tready_list)) + + # Outputs + s_axis_tready = Signal(intbv(0)[S_COUNT:]) + + s_axis_tready_list = [s_axis_tready(i) for i in range(S_COUNT)] + + m_axis_tdata = Signal(intbv(0)[M_COUNT*M_DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(0xf)[M_COUNT*M_KEEP_WIDTH:]) + m_axis_tvalid = Signal(intbv(0)[M_COUNT:]) + m_axis_tlast = Signal(intbv(0)[M_COUNT:]) + m_axis_tid = Signal(intbv(0)[M_COUNT*ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[M_COUNT*DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[M_COUNT*USER_WIDTH:]) + + m_axis_tdata_list = [m_axis_tdata((i+1)*M_DATA_WIDTH, i*M_DATA_WIDTH) for i in range(M_COUNT)] + m_axis_tkeep_list = [m_axis_tkeep((i+1)*M_KEEP_WIDTH, i*M_KEEP_WIDTH) for i in range(M_COUNT)] + m_axis_tvalid_list = [m_axis_tvalid(i) for i in range(M_COUNT)] + m_axis_tlast_list = [m_axis_tlast(i) for i in range(M_COUNT)] + m_axis_tid_list = [m_axis_tid((i+1)*ID_WIDTH, i*ID_WIDTH) for i in range(M_COUNT)] + m_axis_tdest_list = [m_axis_tdest((i+1)*DEST_WIDTH, i*DEST_WIDTH) for i in range(M_COUNT)] + m_axis_tuser_list = [m_axis_tuser((i+1)*USER_WIDTH, i*USER_WIDTH) for i in range(M_COUNT)] + + status_overflow = Signal(intbv(0)[S_COUNT:]) + status_bad_frame = Signal(intbv(0)[S_COUNT:]) + status_good_frame = Signal(intbv(0)[S_COUNT:]) + + # sources and sinks + source_pause_list = [] + source_list = [] + source_logic_list = [] + sink_pause_list = [] + sink_list = [] + sink_logic_list = [] + + for k in range(S_COUNT): + s = axis_ep.AXIStreamSource() + p = Signal(bool(0)) + + source_list.append(s) + source_pause_list.append(p) + + source_logic_list.append(s.create_logic( + clk, + rst, + tdata=s_axis_tdata_list[k], + tkeep=s_axis_tkeep_list[k], + tvalid=s_axis_tvalid_list[k], + tready=s_axis_tready_list[k], + tlast=s_axis_tlast_list[k], + tid=s_axis_tid_list[k], + tdest=s_axis_tdest_list[k], + tuser=s_axis_tuser_list[k], + pause=p, + name='source_%d' % k + )) + + for k in range(M_COUNT): + s = axis_ep.AXIStreamSink() + p = Signal(bool(0)) + + sink_list.append(s) + sink_pause_list.append(p) + + sink_logic_list.append(s.create_logic( + clk, + rst, + tdata=m_axis_tdata_list[k], + tkeep=m_axis_tkeep_list[k], + tvalid=m_axis_tvalid_list[k], + tready=m_axis_tready_list[k], + tlast=m_axis_tlast_list[k], + tid=m_axis_tid_list[k], + tdest=m_axis_tdest_list[k], + tuser=m_axis_tuser_list[k], + pause=p, + name='sink_%d' % k + )) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser, + + status_overflow=status_overflow, + status_bad_frame=status_bad_frame, + status_good_frame=status_good_frame + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + status_overflow_latch = Signal(intbv(0)[S_COUNT:]) + status_bad_frame_latch = Signal(intbv(0)[S_COUNT:]) + status_good_frame_latch = Signal(intbv(0)[S_COUNT:]) + + @always(clk.posedge) + def monitor(): + if status_overflow: + status_overflow_latch.next = status_overflow_latch | status_overflow + if status_bad_frame: + status_bad_frame_latch.next = status_bad_frame_latch | status_bad_frame + if status_good_frame: + status_good_frame_latch.next = status_good_frame_latch | status_good_frame + + def wait_normal(): + while s_axis_tvalid: + yield clk.posedge + + def wait_pause_source(): + while s_axis_tvalid: + yield clk.posedge + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = False + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = True + yield clk.posedge + + for k in range(S_COUNT): + source_pause_list[k].next = False + + def wait_pause_sink(): + while s_axis_tvalid: + for k in range(M_COUNT): + sink_pause_list[k].next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + for k in range(M_COUNT): + sink_pause_list[k].next = False + yield clk.posedge + + @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 + + # testbench stimulus + + yield clk.posedge + print("test 1: 0123 -> 0000") + current_test.next = 1 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x01\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x01\x01\x00\xFF'+bytearray(range(256)), id=1, dest=0) + test_frame2 = axis_ep.AXIStreamFrame(b'\x01\x02\x00\xFF'+bytearray(range(256)), id=2, dest=0) + test_frame3 = axis_ep.AXIStreamFrame(b'\x01\x03\x00\xFF'+bytearray(range(256)), id=3, dest=0) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + source_list[0].send(test_frame0) + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[3].send(test_frame3) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[0].wait() + rx_frame1 = sink_list[0].recv() + + assert rx_frame1 == test_frame1 + + yield sink_list[0].wait() + rx_frame2 = sink_list[0].recv() + + assert rx_frame2 == test_frame2 + + yield sink_list[0].wait() + rx_frame3 = sink_list[0].recv() + + assert rx_frame3 == test_frame3 + + assert sink_list[0].empty() + + assert status_overflow_latch == 0x0 + assert status_bad_frame_latch == 0x0 + assert status_good_frame_latch == 0xf + + yield delay(100) + + yield clk.posedge + print("test 2: 0000 -> 0000") + current_test.next = 2 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x02\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x02\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + test_frame2 = axis_ep.AXIStreamFrame(b'\x02\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + test_frame3 = axis_ep.AXIStreamFrame(b'\x02\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + source_list[0].send(test_frame0) + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + source_list[0].send(test_frame3) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[0].wait() + rx_frame1 = sink_list[0].recv() + + assert rx_frame1 == test_frame1 + + yield sink_list[0].wait() + rx_frame2 = sink_list[0].recv() + + assert rx_frame2 == test_frame2 + + yield sink_list[0].wait() + rx_frame3 = sink_list[0].recv() + + assert rx_frame3 == test_frame3 + + assert sink_list[0].empty() + + assert status_overflow_latch == 0x0 + assert status_bad_frame_latch == 0x0 + assert status_good_frame_latch == 0x1 + + yield delay(100) + + yield clk.posedge + print("test 3: bad decoding") + current_test.next = 3 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x03\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x03\x01\x01\xFF'+bytearray(range(256)), id=1, dest=1) + test_frame2 = axis_ep.AXIStreamFrame(b'\x03\x02\x00\xFF'+bytearray(range(256)), id=2, dest=0) + test_frame3 = axis_ep.AXIStreamFrame(b'\x03\x03\x01\xFF'+bytearray(range(256)), id=3, dest=1) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + source_list[0].send(test_frame0) + yield clk.posedge + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[3].send(test_frame3) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[0].wait() + rx_frame2 = sink_list[0].recv() + + assert rx_frame2 == test_frame2 + + assert sink_list[0].empty() + + assert status_overflow_latch == 0x0 + assert status_bad_frame_latch == 0x0 + assert status_good_frame_latch == 0x5 + + yield delay(100) + + yield clk.posedge + print("test 4: tuser assert") + current_test.next = 4 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x04\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x04\x00\x01\xFF'+bytearray(range(256)), id=0, dest=0, last_cycle_user=1) + test_frame2 = axis_ep.AXIStreamFrame(b'\x04\x00\x02\xFF'+bytearray(range(256)), id=0, dest=0) + test_frame3 = axis_ep.AXIStreamFrame(b'\x04\x00\x03\xFF'+bytearray(range(256)), id=0, dest=0) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + source_list[0].send(test_frame0) + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + source_list[0].send(test_frame3) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[0].wait() + rx_frame2 = sink_list[0].recv() + + assert rx_frame2 == test_frame2 + + yield sink_list[0].wait() + rx_frame3 = sink_list[0].recv() + + assert rx_frame3 == test_frame3 + + assert sink_list[0].empty() + + assert status_overflow_latch == 0x0 + assert status_bad_frame_latch == 0x1 + assert status_good_frame_latch == 0x1 + + yield delay(100) + + yield clk.posedge + print("test 5: single packet overflow") + current_test.next = 5 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x05\x00\x00\xFF'+bytearray(range(256))*3, id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x05\x01\x01\xFF'+bytearray(range(256))*3, id=1, dest=0) + test_frame2 = axis_ep.AXIStreamFrame(b'\x05\x02\x02\xFF'+bytearray(range(256))*3, id=2, dest=0) + test_frame3 = axis_ep.AXIStreamFrame(b'\x05\x03\x03\xFF'+bytearray(range(256))*3, id=3, dest=0) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + source_list[0].send(test_frame0) + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[3].send(test_frame3) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield delay(100) + + assert sink_list[0].empty() + + assert status_overflow_latch == 0xf + assert status_bad_frame_latch == 0x0 + assert status_good_frame_latch == 0x0 + + yield delay(100) + + yield clk.posedge + print("test 6: initial sink pause") + current_test.next = 6 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x06\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + source_list[0].send(test_frame0) + + for k in range(M_COUNT): + sink_pause_list[k].next = True + + yield clk.posedge + yield clk.posedge + while (s_axis_tvalid): + yield clk.posedge + for k in range(20): + yield clk.posedge + + for k in range(M_COUNT): + sink_pause_list[k].next = False + + yield wait_normal() + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + assert sink_list[0].empty() + + assert status_overflow_latch == 0x0 + assert status_bad_frame_latch == 0x0 + assert status_good_frame_latch == 0x1 + + yield delay(100) + + yield clk.posedge + print("test 7: initial sink pause, reset") + current_test.next = 7 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x07\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + source_list[0].send(test_frame0) + + for k in range(M_COUNT): + sink_pause_list[k].next = True + + yield clk.posedge + yield clk.posedge + while (s_axis_tvalid): + yield clk.posedge + for k in range(20): + yield clk.posedge + + rst.next = 1 + yield clk.posedge + rst.next = 0 + + for k in range(M_COUNT): + sink_pause_list[k].next = False + + yield delay(500) + + assert sink_list[0].empty() + + assert status_overflow_latch == 0x0 + assert status_bad_frame_latch == 0x0 + assert status_good_frame_latch == 0x1 + + yield delay(100) + + yield clk.posedge + print("test 8: backpressure test") + current_test.next = 8 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x08\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x08\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + test_frame2 = axis_ep.AXIStreamFrame(b'\x08\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + test_frame3 = axis_ep.AXIStreamFrame(b'\x08\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + source_list[0].send(test_frame0) + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + source_list[0].send(test_frame3) + + for k in range(M_COUNT): + sink_pause_list[k].next = True + + for k in range(100): + yield clk.posedge + + for k in range(M_COUNT): + sink_pause_list[k].next = False + + yield wait() + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[0].wait() + rx_frame1 = sink_list[0].recv() + + assert rx_frame1 == test_frame1 + + yield sink_list[0].wait() + rx_frame2 = sink_list[0].recv() + + assert rx_frame2 == test_frame2 + + yield sink_list[0].wait() + rx_frame3 = sink_list[0].recv() + + assert rx_frame3 == test_frame3 + + assert sink_list[0].empty() + + assert status_overflow_latch == 0x0 + assert status_bad_frame_latch == 0x0 + assert status_good_frame_latch == 0x1 + + yield delay(100) + + yield clk.posedge + print("test 9: many small packets, one to one") + current_test.next = 9 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x09\x00\x00\xFF'+bytearray(range(4)), id=0, dest=0) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + for k in range(64): + source_list[0].send(test_frame0) + yield clk.posedge + yield clk.posedge + + yield wait() + + for k in range(64): + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + assert sink_list[0].empty() + + assert status_overflow_latch == 0x0 + assert status_bad_frame_latch == 0x0 + assert status_good_frame_latch == 0x1 + + yield delay(100) + + yield clk.posedge + print("test 10: many small packets, many to one") + current_test.next = 10 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x0A\x00\x00\xFF'+bytearray(range(4)), id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x0A\x01\x00\xFF'+bytearray(range(4)), id=1, dest=0) + test_frame2 = axis_ep.AXIStreamFrame(b'\x0A\x02\x00\xFF'+bytearray(range(4)), id=2, dest=0) + test_frame3 = axis_ep.AXIStreamFrame(b'\x0A\x03\x00\xFF'+bytearray(range(4)), id=3, dest=0) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + for k in range(64): + source_list[0].send(test_frame0) + yield clk.posedge + yield clk.posedge + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[3].send(test_frame3) + yield clk.posedge + yield clk.posedge + + yield wait() + + for k in range(64): + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[0].wait() + rx_frame1 = sink_list[0].recv() + + assert rx_frame1 == test_frame1 + + yield sink_list[0].wait() + rx_frame2 = sink_list[0].recv() + + assert rx_frame2 == test_frame2 + + yield sink_list[0].wait() + rx_frame3 = sink_list[0].recv() + + assert rx_frame3 == test_frame3 + + assert sink_list[0].empty() + + assert status_overflow_latch == 0x0 + assert status_bad_frame_latch == 0x0 + assert status_good_frame_latch == 0xf + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/tb/test_axis_ram_switch_4x1_64_256.v b/tb/test_axis_ram_switch_4x1_64_256.v new file mode 100644 index 00000000..44a760b2 --- /dev/null +++ b/tb/test_axis_ram_switch_4x1_64_256.v @@ -0,0 +1,174 @@ +/* + +Copyright (c) 2020 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 + +/* + * Testbench for axis_ram_switch + */ +module test_axis_ram_switch_4x1_64_256; + +// Parameters +parameter FIFO_DEPTH = 512; +parameter SPEEDUP = 0; +parameter S_COUNT = 4; +parameter M_COUNT = 1; +parameter S_DATA_WIDTH = 64; +parameter S_KEEP_ENABLE = (S_DATA_WIDTH>8); +parameter S_KEEP_WIDTH = (S_DATA_WIDTH/8); +parameter M_DATA_WIDTH = 256; +parameter M_KEEP_ENABLE = (M_DATA_WIDTH>8); +parameter M_KEEP_WIDTH = (M_DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_WIDTH = $clog2(M_COUNT+1); +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; +parameter USER_BAD_FRAME_VALUE = 1'b1; +parameter USER_BAD_FRAME_MASK = 1'b1; +parameter DROP_BAD_FRAME = 1; +parameter DROP_WHEN_FULL = 0; +parameter M_BASE = {3'd3, 3'd2, 3'd1, 3'd0}; +parameter M_TOP = {3'd3, 3'd2, 3'd1, 3'd0}; +parameter M_CONNECT = {M_COUNT{{S_COUNT{1'b1}}}}; +parameter ARB_TYPE = "ROUND_ROBIN"; +parameter LSB_PRIORITY = "HIGH"; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [S_COUNT*S_DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [S_COUNT*S_KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg [S_COUNT-1:0] s_axis_tvalid = 0; +reg [S_COUNT-1:0] s_axis_tlast = 0; +reg [S_COUNT*ID_WIDTH-1:0] s_axis_tid = 0; +reg [S_COUNT*DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [S_COUNT*USER_WIDTH-1:0] s_axis_tuser = 0; +reg [M_COUNT-1:0] m_axis_tready = 0; + +// Outputs +wire [S_COUNT-1:0] s_axis_tready; +wire [M_COUNT*M_DATA_WIDTH-1:0] m_axis_tdata; +wire [M_COUNT*M_KEEP_WIDTH-1:0] m_axis_tkeep; +wire [M_COUNT-1:0] m_axis_tvalid; +wire [M_COUNT-1:0] m_axis_tlast; +wire [M_COUNT*ID_WIDTH-1:0] m_axis_tid; +wire [M_COUNT*DEST_WIDTH-1:0] m_axis_tdest; +wire [M_COUNT*USER_WIDTH-1:0] m_axis_tuser; +wire [S_COUNT-1:0] status_overflow; +wire [S_COUNT-1:0] status_bad_frame; +wire [S_COUNT-1:0] status_good_frame; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser, + status_overflow, + status_bad_frame, + status_good_frame + ); + + // dump file + $dumpfile("test_axis_ram_switch_4x1_64_256.lxt"); + $dumpvars(0, test_axis_ram_switch_4x1_64_256); +end + +axis_ram_switch #( + .FIFO_DEPTH(FIFO_DEPTH), + .SPEEDUP(SPEEDUP), + .S_COUNT(S_COUNT), + .M_COUNT(M_COUNT), + .S_DATA_WIDTH(S_DATA_WIDTH), + .S_KEEP_ENABLE(S_KEEP_ENABLE), + .S_KEEP_WIDTH(S_KEEP_WIDTH), + .M_DATA_WIDTH(M_DATA_WIDTH), + .M_KEEP_ENABLE(M_KEEP_ENABLE), + .M_KEEP_WIDTH(M_KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .USER_BAD_FRAME_VALUE(USER_BAD_FRAME_VALUE), + .USER_BAD_FRAME_MASK(USER_BAD_FRAME_MASK), + .DROP_BAD_FRAME(DROP_BAD_FRAME), + .DROP_WHEN_FULL(DROP_WHEN_FULL), + .M_BASE(M_BASE), + .M_TOP(M_TOP), + .M_CONNECT(M_CONNECT), + .ARB_TYPE(ARB_TYPE), + .LSB_PRIORITY(LSB_PRIORITY) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI inputs + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser), + // Status + .status_overflow(status_overflow), + .status_bad_frame(status_bad_frame), + .status_good_frame(status_good_frame) +); + +endmodule diff --git a/tb/test_axis_ram_switch_4x4_64_64.py b/tb/test_axis_ram_switch_4x4_64_64.py new file mode 100755 index 00000000..5628f39a --- /dev/null +++ b/tb/test_axis_ram_switch_4x4_64_64.py @@ -0,0 +1,1020 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2020 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 + +import axis_ep + +module = 'axis_ram_switch' +testbench = 'test_%s_4x4_64_64' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/axis_adapter.v") +srcs.append("../rtl/arbiter.v") +srcs.append("../rtl/priority_encoder.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + FIFO_DEPTH = 512 + SPEEDUP = 0 + S_COUNT = 4 + M_COUNT = 4 + S_DATA_WIDTH = 64 + S_KEEP_ENABLE = (S_DATA_WIDTH>8) + S_KEEP_WIDTH = (S_DATA_WIDTH/8) + M_DATA_WIDTH = 64 + M_KEEP_ENABLE = (M_DATA_WIDTH>8) + M_KEEP_WIDTH = (M_DATA_WIDTH/8) + ID_ENABLE = 1 + ID_WIDTH = 8 + DEST_WIDTH = (M_COUNT+1-1).bit_length() + USER_ENABLE = 1 + USER_WIDTH = 1 + USER_BAD_FRAME_VALUE = 1 + USER_BAD_FRAME_MASK = 1 + DROP_BAD_FRAME = 1 + DROP_WHEN_FULL = 0 + M_BASE = [0, 1, 2, 3] + M_TOP = [0, 1, 2, 3] + M_CONNECT = [0b1111]*M_COUNT + ARB_TYPE = "ROUND_ROBIN" + LSB_PRIORITY = "HIGH" + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata_list = [Signal(intbv(0)[S_DATA_WIDTH:]) for i in range(S_COUNT)] + s_axis_tkeep_list = [Signal(intbv(1)[S_KEEP_WIDTH:]) for i in range(S_COUNT)] + s_axis_tvalid_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_axis_tlast_list = [Signal(bool(0)) for i in range(S_COUNT)] + s_axis_tid_list = [Signal(intbv(0)[ID_WIDTH:]) for i in range(S_COUNT)] + s_axis_tdest_list = [Signal(intbv(0)[DEST_WIDTH:]) for i in range(S_COUNT)] + s_axis_tuser_list = [Signal(intbv(0)[USER_WIDTH:]) for i in range(S_COUNT)] + + s_axis_tdata = ConcatSignal(*reversed(s_axis_tdata_list)) + s_axis_tkeep = ConcatSignal(*reversed(s_axis_tkeep_list)) + s_axis_tvalid = ConcatSignal(*reversed(s_axis_tvalid_list)) + s_axis_tlast = ConcatSignal(*reversed(s_axis_tlast_list)) + s_axis_tid = ConcatSignal(*reversed(s_axis_tid_list)) + s_axis_tdest = ConcatSignal(*reversed(s_axis_tdest_list)) + s_axis_tuser = ConcatSignal(*reversed(s_axis_tuser_list)) + + m_axis_tready_list = [Signal(bool(0)) for i in range(M_COUNT)] + + m_axis_tready = ConcatSignal(*reversed(m_axis_tready_list)) + + # Outputs + s_axis_tready = Signal(intbv(0)[S_COUNT:]) + + s_axis_tready_list = [s_axis_tready(i) for i in range(S_COUNT)] + + m_axis_tdata = Signal(intbv(0)[M_COUNT*M_DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(0xf)[M_COUNT*M_KEEP_WIDTH:]) + m_axis_tvalid = Signal(intbv(0)[M_COUNT:]) + m_axis_tlast = Signal(intbv(0)[M_COUNT:]) + m_axis_tid = Signal(intbv(0)[M_COUNT*ID_WIDTH:]) + m_axis_tdest = Signal(intbv(0)[M_COUNT*DEST_WIDTH:]) + m_axis_tuser = Signal(intbv(0)[M_COUNT*USER_WIDTH:]) + + m_axis_tdata_list = [m_axis_tdata((i+1)*M_DATA_WIDTH, i*M_DATA_WIDTH) for i in range(M_COUNT)] + m_axis_tkeep_list = [m_axis_tkeep((i+1)*M_KEEP_WIDTH, i*M_KEEP_WIDTH) for i in range(M_COUNT)] + m_axis_tvalid_list = [m_axis_tvalid(i) for i in range(M_COUNT)] + m_axis_tlast_list = [m_axis_tlast(i) for i in range(M_COUNT)] + m_axis_tid_list = [m_axis_tid((i+1)*ID_WIDTH, i*ID_WIDTH) for i in range(M_COUNT)] + m_axis_tdest_list = [m_axis_tdest((i+1)*DEST_WIDTH, i*DEST_WIDTH) for i in range(M_COUNT)] + m_axis_tuser_list = [m_axis_tuser((i+1)*USER_WIDTH, i*USER_WIDTH) for i in range(M_COUNT)] + + status_overflow = Signal(intbv(0)[S_COUNT:]) + status_bad_frame = Signal(intbv(0)[S_COUNT:]) + status_good_frame = Signal(intbv(0)[S_COUNT:]) + + # sources and sinks + source_pause_list = [] + source_list = [] + source_logic_list = [] + sink_pause_list = [] + sink_list = [] + sink_logic_list = [] + + for k in range(S_COUNT): + s = axis_ep.AXIStreamSource() + p = Signal(bool(0)) + + source_list.append(s) + source_pause_list.append(p) + + source_logic_list.append(s.create_logic( + clk, + rst, + tdata=s_axis_tdata_list[k], + tkeep=s_axis_tkeep_list[k], + tvalid=s_axis_tvalid_list[k], + tready=s_axis_tready_list[k], + tlast=s_axis_tlast_list[k], + tid=s_axis_tid_list[k], + tdest=s_axis_tdest_list[k], + tuser=s_axis_tuser_list[k], + pause=p, + name='source_%d' % k + )) + + for k in range(M_COUNT): + s = axis_ep.AXIStreamSink() + p = Signal(bool(0)) + + sink_list.append(s) + sink_pause_list.append(p) + + sink_logic_list.append(s.create_logic( + clk, + rst, + tdata=m_axis_tdata_list[k], + tkeep=m_axis_tkeep_list[k], + tvalid=m_axis_tvalid_list[k], + tready=m_axis_tready_list[k], + tlast=m_axis_tlast_list[k], + tid=m_axis_tid_list[k], + tdest=m_axis_tdest_list[k], + tuser=m_axis_tuser_list[k], + pause=p, + name='sink_%d' % k + )) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tid=s_axis_tid, + s_axis_tdest=s_axis_tdest, + s_axis_tuser=s_axis_tuser, + + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tready=m_axis_tready, + m_axis_tlast=m_axis_tlast, + m_axis_tid=m_axis_tid, + m_axis_tdest=m_axis_tdest, + m_axis_tuser=m_axis_tuser, + + status_overflow=status_overflow, + status_bad_frame=status_bad_frame, + status_good_frame=status_good_frame + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + status_overflow_latch = Signal(intbv(0)[S_COUNT:]) + status_bad_frame_latch = Signal(intbv(0)[S_COUNT:]) + status_good_frame_latch = Signal(intbv(0)[S_COUNT:]) + + @always(clk.posedge) + def monitor(): + if status_overflow: + status_overflow_latch.next = status_overflow_latch | status_overflow + if status_bad_frame: + status_bad_frame_latch.next = status_bad_frame_latch | status_bad_frame + if status_good_frame: + status_good_frame_latch.next = status_good_frame_latch | status_good_frame + + def wait_normal(): + while s_axis_tvalid: + yield clk.posedge + + def wait_pause_source(): + while s_axis_tvalid: + yield clk.posedge + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = False + yield clk.posedge + for k in range(S_COUNT): + source_pause_list[k].next = True + yield clk.posedge + + for k in range(S_COUNT): + source_pause_list[k].next = False + + def wait_pause_sink(): + while s_axis_tvalid: + for k in range(M_COUNT): + sink_pause_list[k].next = True + yield clk.posedge + yield clk.posedge + yield clk.posedge + for k in range(M_COUNT): + sink_pause_list[k].next = False + yield clk.posedge + + @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 + + # testbench stimulus + + yield clk.posedge + print("test 1: 0123 -> 0123") + current_test.next = 1 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x01\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x01\x01\x01\xFF'+bytearray(range(256)), id=1, dest=1) + test_frame2 = axis_ep.AXIStreamFrame(b'\x01\x02\x02\xFF'+bytearray(range(256)), id=2, dest=2) + test_frame3 = axis_ep.AXIStreamFrame(b'\x01\x03\x03\xFF'+bytearray(range(256)), id=3, dest=3) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + source_list[0].send(test_frame0) + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[3].send(test_frame3) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[1].wait() + rx_frame1 = sink_list[1].recv() + + assert rx_frame1 == test_frame1 + + yield sink_list[2].wait() + rx_frame2 = sink_list[2].recv() + + assert rx_frame2 == test_frame2 + + yield sink_list[3].wait() + rx_frame3 = sink_list[3].recv() + + assert rx_frame3 == test_frame3 + + assert sink_list[0].empty() + assert sink_list[1].empty() + assert sink_list[2].empty() + assert sink_list[3].empty() + + assert status_overflow_latch == 0x0 + assert status_bad_frame_latch == 0x0 + assert status_good_frame_latch == 0xf + + yield delay(100) + + yield clk.posedge + print("test 2: 0123 -> 3210") + current_test.next = 2 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x02\x00\x03\xFF'+bytearray(range(256)), id=0, dest=3) + test_frame1 = axis_ep.AXIStreamFrame(b'\x02\x01\x02\xFF'+bytearray(range(256)), id=1, dest=2) + test_frame2 = axis_ep.AXIStreamFrame(b'\x02\x02\x01\xFF'+bytearray(range(256)), id=2, dest=1) + test_frame3 = axis_ep.AXIStreamFrame(b'\x02\x03\x00\xFF'+bytearray(range(256)), id=3, dest=0) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + source_list[0].send(test_frame0) + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[3].send(test_frame3) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame3 + + yield sink_list[1].wait() + rx_frame1 = sink_list[1].recv() + + assert rx_frame1 == test_frame2 + + yield sink_list[2].wait() + rx_frame2 = sink_list[2].recv() + + assert rx_frame2 == test_frame1 + + yield sink_list[3].wait() + rx_frame3 = sink_list[3].recv() + + assert rx_frame3 == test_frame0 + + assert sink_list[0].empty() + assert sink_list[1].empty() + assert sink_list[2].empty() + assert sink_list[3].empty() + + assert status_overflow_latch == 0x0 + assert status_bad_frame_latch == 0x0 + assert status_good_frame_latch == 0xf + + yield delay(100) + + yield clk.posedge + print("test 3: 0000 -> 0123") + current_test.next = 3 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x03\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x03\x00\x01\xFF'+bytearray(range(256)), id=0, dest=1) + test_frame2 = axis_ep.AXIStreamFrame(b'\x03\x00\x02\xFF'+bytearray(range(256)), id=0, dest=2) + test_frame3 = axis_ep.AXIStreamFrame(b'\x03\x00\x03\xFF'+bytearray(range(256)), id=0, dest=3) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + source_list[0].send(test_frame0) + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + source_list[0].send(test_frame3) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[1].wait() + rx_frame1 = sink_list[1].recv() + + assert rx_frame1 == test_frame1 + + yield sink_list[2].wait() + rx_frame2 = sink_list[2].recv() + + assert rx_frame2 == test_frame2 + + yield sink_list[3].wait() + rx_frame3 = sink_list[3].recv() + + assert rx_frame3 == test_frame3 + + assert sink_list[0].empty() + assert sink_list[1].empty() + assert sink_list[2].empty() + assert sink_list[3].empty() + + assert status_overflow_latch == 0x0 + assert status_bad_frame_latch == 0x0 + assert status_good_frame_latch == 0x1 + + yield delay(100) + + yield clk.posedge + print("test 4: 0123 -> 0000") + current_test.next = 4 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x04\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x04\x01\x00\xFF'+bytearray(range(256)), id=1, dest=0) + test_frame2 = axis_ep.AXIStreamFrame(b'\x04\x02\x00\xFF'+bytearray(range(256)), id=2, dest=0) + test_frame3 = axis_ep.AXIStreamFrame(b'\x04\x03\x00\xFF'+bytearray(range(256)), id=3, dest=0) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + source_list[0].send(test_frame0) + yield clk.posedge + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[3].send(test_frame3) + yield clk.posedge + + yield wait() + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[0].wait() + rx_frame1 = sink_list[0].recv() + + assert rx_frame1 == test_frame1 + + yield sink_list[0].wait() + rx_frame2 = sink_list[0].recv() + + assert rx_frame2 == test_frame2 + + yield sink_list[0].wait() + rx_frame3 = sink_list[0].recv() + + assert rx_frame3 == test_frame3 + + assert sink_list[0].empty() + assert sink_list[1].empty() + assert sink_list[2].empty() + assert sink_list[3].empty() + + assert status_overflow_latch == 0x0 + assert status_bad_frame_latch == 0x0 + assert status_good_frame_latch == 0xf + + yield delay(100) + + yield clk.posedge + print("test 5: 0000 -> 0000") + current_test.next = 5 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x05\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x05\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + test_frame2 = axis_ep.AXIStreamFrame(b'\x05\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + test_frame3 = axis_ep.AXIStreamFrame(b'\x05\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + source_list[0].send(test_frame0) + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + source_list[0].send(test_frame3) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[0].wait() + rx_frame1 = sink_list[0].recv() + + assert rx_frame1 == test_frame1 + + yield sink_list[0].wait() + rx_frame2 = sink_list[0].recv() + + assert rx_frame2 == test_frame2 + + yield sink_list[0].wait() + rx_frame3 = sink_list[0].recv() + + assert rx_frame3 == test_frame3 + + assert sink_list[0].empty() + assert sink_list[1].empty() + assert sink_list[2].empty() + assert sink_list[3].empty() + + assert status_overflow_latch == 0x0 + assert status_bad_frame_latch == 0x0 + assert status_good_frame_latch == 0x1 + + yield delay(100) + + yield clk.posedge + print("test 6: bad decoding") + current_test.next = 6 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x06\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x06\x01\x01\xFF'+bytearray(range(256)), id=1, dest=1) + test_frame2 = axis_ep.AXIStreamFrame(b'\x06\x02\x04\xFF'+bytearray(range(256)), id=2, dest=4) + test_frame3 = axis_ep.AXIStreamFrame(b'\x06\x03\x05\xFF'+bytearray(range(256)), id=3, dest=5) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + source_list[0].send(test_frame0) + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[3].send(test_frame3) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[1].wait() + rx_frame1 = sink_list[1].recv() + + assert rx_frame1 == test_frame1 + + assert sink_list[0].empty() + assert sink_list[1].empty() + assert sink_list[2].empty() + assert sink_list[3].empty() + + assert status_overflow_latch == 0x0 + assert status_bad_frame_latch == 0x0 + assert status_good_frame_latch == 0x3 + + yield delay(100) + + yield clk.posedge + print("test 7: tuser assert") + current_test.next = 7 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x07\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x07\x00\x01\xFF'+bytearray(range(256)), id=0, dest=1, last_cycle_user=1) + test_frame2 = axis_ep.AXIStreamFrame(b'\x07\x00\x02\xFF'+bytearray(range(256)), id=0, dest=2) + test_frame3 = axis_ep.AXIStreamFrame(b'\x07\x00\x03\xFF'+bytearray(range(256)), id=0, dest=3) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + source_list[0].send(test_frame0) + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + source_list[0].send(test_frame3) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[2].wait() + rx_frame2 = sink_list[2].recv() + + assert rx_frame2 == test_frame2 + + yield sink_list[3].wait() + rx_frame3 = sink_list[3].recv() + + assert rx_frame3 == test_frame3 + + assert sink_list[0].empty() + assert sink_list[1].empty() + assert sink_list[2].empty() + assert sink_list[3].empty() + + assert status_overflow_latch == 0x0 + assert status_bad_frame_latch == 0x1 + assert status_good_frame_latch == 0x1 + + yield delay(100) + + yield clk.posedge + print("test 8: single packet overflow") + current_test.next = 8 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x08\x00\x00\xFF'+bytearray(range(256))*3, id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x08\x01\x01\xFF'+bytearray(range(256))*3, id=1, dest=1) + test_frame2 = axis_ep.AXIStreamFrame(b'\x08\x02\x02\xFF'+bytearray(range(256))*3, id=2, dest=2) + test_frame3 = axis_ep.AXIStreamFrame(b'\x08\x03\x03\xFF'+bytearray(range(256))*3, id=3, dest=3) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + source_list[0].send(test_frame0) + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[3].send(test_frame3) + yield clk.posedge + yield clk.posedge + + yield wait() + + yield delay(100) + + assert sink_list[0].empty() + assert sink_list[1].empty() + assert sink_list[2].empty() + assert sink_list[3].empty() + + assert status_overflow_latch == 0xf + assert status_bad_frame_latch == 0x0 + assert status_good_frame_latch == 0x0 + + yield delay(100) + + yield clk.posedge + print("test 9: initial sink pause") + current_test.next = 9 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x09\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x09\x01\x01\xFF'+bytearray(range(256)), id=1, dest=1) + test_frame2 = axis_ep.AXIStreamFrame(b'\x09\x02\x02\xFF'+bytearray(range(256)), id=2, dest=2) + test_frame3 = axis_ep.AXIStreamFrame(b'\x09\x03\x03\xFF'+bytearray(range(256)), id=3, dest=3) + + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + source_list[0].send(test_frame0) + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[3].send(test_frame3) + + for k in range(M_COUNT): + sink_pause_list[k].next = True + + yield clk.posedge + yield clk.posedge + while (s_axis_tvalid): + yield clk.posedge + for k in range(20): + yield clk.posedge + + for k in range(M_COUNT): + sink_pause_list[k].next = False + + yield wait_normal() + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[1].wait() + rx_frame1 = sink_list[1].recv() + + assert rx_frame1 == test_frame1 + + yield sink_list[2].wait() + rx_frame2 = sink_list[2].recv() + + assert rx_frame2 == test_frame2 + + yield sink_list[3].wait() + rx_frame3 = sink_list[3].recv() + + assert rx_frame3 == test_frame3 + + assert sink_list[0].empty() + assert sink_list[1].empty() + assert sink_list[2].empty() + assert sink_list[3].empty() + + assert status_overflow_latch == 0x0 + assert status_bad_frame_latch == 0x0 + assert status_good_frame_latch == 0xf + + yield delay(100) + + yield clk.posedge + print("test 10: initial sink pause, reset") + current_test.next = 10 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x0A\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x0A\x01\x01\xFF'+bytearray(range(256)), id=1, dest=1) + test_frame2 = axis_ep.AXIStreamFrame(b'\x0A\x02\x02\xFF'+bytearray(range(256)), id=2, dest=2) + test_frame3 = axis_ep.AXIStreamFrame(b'\x0A\x03\x03\xFF'+bytearray(range(256)), id=3, dest=3) + + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + source_list[0].send(test_frame0) + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[3].send(test_frame3) + + for k in range(M_COUNT): + sink_pause_list[k].next = True + + yield clk.posedge + yield clk.posedge + while (s_axis_tvalid): + yield clk.posedge + for k in range(20): + yield clk.posedge + + rst.next = 1 + yield clk.posedge + rst.next = 0 + + for k in range(M_COUNT): + sink_pause_list[k].next = False + + yield delay(500) + + assert sink_list[0].empty() + assert sink_list[1].empty() + assert sink_list[2].empty() + assert sink_list[3].empty() + + assert status_overflow_latch == 0x0 + assert status_bad_frame_latch == 0x0 + assert status_good_frame_latch == 0xf + + yield delay(100) + + yield clk.posedge + print("test 11: backpressure test") + current_test.next = 11 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x0B\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x0B\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + test_frame2 = axis_ep.AXIStreamFrame(b'\x0B\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + test_frame3 = axis_ep.AXIStreamFrame(b'\x0B\x00\x00\xFF'+bytearray(range(256)), id=0, dest=0) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + source_list[0].send(test_frame0) + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + source_list[0].send(test_frame3) + + for k in range(M_COUNT): + sink_pause_list[k].next = True + + for k in range(100): + yield clk.posedge + + for k in range(M_COUNT): + sink_pause_list[k].next = False + + yield wait() + + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[0].wait() + rx_frame1 = sink_list[0].recv() + + assert rx_frame1 == test_frame1 + + yield sink_list[0].wait() + rx_frame2 = sink_list[0].recv() + + assert rx_frame2 == test_frame2 + + yield sink_list[0].wait() + rx_frame3 = sink_list[0].recv() + + assert rx_frame3 == test_frame3 + + assert sink_list[0].empty() + assert sink_list[1].empty() + assert sink_list[2].empty() + assert sink_list[3].empty() + + assert status_overflow_latch == 0x0 + assert status_bad_frame_latch == 0x0 + assert status_good_frame_latch == 0x1 + + yield delay(100) + + yield clk.posedge + print("test 12: many small packets, one to one parallel") + current_test.next = 12 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x0C\x00\x00\xFF'+bytearray(range(4)), id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x0C\x01\x01\xFF'+bytearray(range(4)), id=1, dest=1) + test_frame2 = axis_ep.AXIStreamFrame(b'\x0C\x02\x02\xFF'+bytearray(range(4)), id=2, dest=2) + test_frame3 = axis_ep.AXIStreamFrame(b'\x0C\x03\x03\xFF'+bytearray(range(4)), id=3, dest=3) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + for k in range(64): + source_list[0].send(test_frame0) + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[3].send(test_frame3) + yield clk.posedge + yield clk.posedge + + yield wait() + + for k in range(64): + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[1].wait() + rx_frame1 = sink_list[1].recv() + + assert rx_frame1 == test_frame1 + + yield sink_list[2].wait() + rx_frame2 = sink_list[2].recv() + + assert rx_frame2 == test_frame2 + + yield sink_list[3].wait() + rx_frame3 = sink_list[3].recv() + + assert rx_frame3 == test_frame3 + + assert sink_list[0].empty() + assert sink_list[1].empty() + assert sink_list[2].empty() + assert sink_list[3].empty() + + assert status_overflow_latch == 0x0 + assert status_bad_frame_latch == 0x0 + assert status_good_frame_latch == 0xf + + yield delay(100) + + yield clk.posedge + print("test 13: many small packets, many to one") + current_test.next = 13 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x0D\x00\x00\xFF'+bytearray(range(4)), id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x0D\x01\x00\xFF'+bytearray(range(4)), id=1, dest=0) + test_frame2 = axis_ep.AXIStreamFrame(b'\x0D\x02\x00\xFF'+bytearray(range(4)), id=2, dest=0) + test_frame3 = axis_ep.AXIStreamFrame(b'\x0D\x03\x00\xFF'+bytearray(range(4)), id=3, dest=0) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + for k in range(64): + source_list[0].send(test_frame0) + yield clk.posedge + yield clk.posedge + source_list[1].send(test_frame1) + source_list[2].send(test_frame2) + source_list[3].send(test_frame3) + yield clk.posedge + yield clk.posedge + + yield wait() + + for k in range(64): + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[0].wait() + rx_frame1 = sink_list[0].recv() + + assert rx_frame1 == test_frame1 + + yield sink_list[0].wait() + rx_frame2 = sink_list[0].recv() + + assert rx_frame2 == test_frame2 + + yield sink_list[0].wait() + rx_frame3 = sink_list[0].recv() + + assert rx_frame3 == test_frame3 + + assert sink_list[0].empty() + assert sink_list[1].empty() + assert sink_list[2].empty() + assert sink_list[3].empty() + + assert status_overflow_latch == 0x0 + assert status_bad_frame_latch == 0x0 + assert status_good_frame_latch == 0xf + + yield delay(100) + + yield clk.posedge + print("test 14: many small packets, one to many") + current_test.next = 14 + + test_frame0 = axis_ep.AXIStreamFrame(b'\x0E\x00\x00\xFF'+bytearray(range(4)), id=0, dest=0) + test_frame1 = axis_ep.AXIStreamFrame(b'\x0E\x00\x01\xFF'+bytearray(range(4)), id=0, dest=1) + test_frame2 = axis_ep.AXIStreamFrame(b'\x0E\x00\x02\xFF'+bytearray(range(4)), id=0, dest=2) + test_frame3 = axis_ep.AXIStreamFrame(b'\x0E\x00\x03\xFF'+bytearray(range(4)), id=0, dest=3) + + for wait in wait_normal, wait_pause_source, wait_pause_sink: + status_overflow_latch.next = 0 + status_bad_frame_latch.next = 0 + status_good_frame_latch.next = 0 + + for k in range(64): + source_list[0].send(test_frame0) + source_list[0].send(test_frame1) + source_list[0].send(test_frame2) + source_list[0].send(test_frame3) + yield clk.posedge + yield clk.posedge + + yield wait() + + for k in range(64): + yield sink_list[0].wait() + rx_frame0 = sink_list[0].recv() + + assert rx_frame0 == test_frame0 + + yield sink_list[1].wait() + rx_frame1 = sink_list[1].recv() + + assert rx_frame1 == test_frame1 + + yield sink_list[2].wait() + rx_frame2 = sink_list[2].recv() + + assert rx_frame2 == test_frame2 + + yield sink_list[3].wait() + rx_frame3 = sink_list[3].recv() + + assert rx_frame3 == test_frame3 + + assert sink_list[0].empty() + assert sink_list[1].empty() + assert sink_list[2].empty() + assert sink_list[3].empty() + + assert status_overflow_latch == 0x0 + assert status_bad_frame_latch == 0x0 + assert status_good_frame_latch == 0x1 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/tb/test_axis_ram_switch_4x4_64_64.v b/tb/test_axis_ram_switch_4x4_64_64.v new file mode 100644 index 00000000..1882ee25 --- /dev/null +++ b/tb/test_axis_ram_switch_4x4_64_64.v @@ -0,0 +1,174 @@ +/* + +Copyright (c) 2020 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 + +/* + * Testbench for axis_ram_switch + */ +module test_axis_ram_switch_4x4_64_64; + +// Parameters +parameter FIFO_DEPTH = 512; +parameter SPEEDUP = 0; +parameter S_COUNT = 4; +parameter M_COUNT = 4; +parameter S_DATA_WIDTH = 64; +parameter S_KEEP_ENABLE = (S_DATA_WIDTH>8); +parameter S_KEEP_WIDTH = (S_DATA_WIDTH/8); +parameter M_DATA_WIDTH = 64; +parameter M_KEEP_ENABLE = (M_DATA_WIDTH>8); +parameter M_KEEP_WIDTH = (M_DATA_WIDTH/8); +parameter ID_ENABLE = 1; +parameter ID_WIDTH = 8; +parameter DEST_WIDTH = $clog2(M_COUNT+1); +parameter USER_ENABLE = 1; +parameter USER_WIDTH = 1; +parameter USER_BAD_FRAME_VALUE = 1'b1; +parameter USER_BAD_FRAME_MASK = 1'b1; +parameter DROP_BAD_FRAME = 1; +parameter DROP_WHEN_FULL = 0; +parameter M_BASE = {3'd3, 3'd2, 3'd1, 3'd0}; +parameter M_TOP = {3'd3, 3'd2, 3'd1, 3'd0}; +parameter M_CONNECT = {M_COUNT{{S_COUNT{1'b1}}}}; +parameter ARB_TYPE = "ROUND_ROBIN"; +parameter LSB_PRIORITY = "HIGH"; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [S_COUNT*S_DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [S_COUNT*S_KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg [S_COUNT-1:0] s_axis_tvalid = 0; +reg [S_COUNT-1:0] s_axis_tlast = 0; +reg [S_COUNT*ID_WIDTH-1:0] s_axis_tid = 0; +reg [S_COUNT*DEST_WIDTH-1:0] s_axis_tdest = 0; +reg [S_COUNT*USER_WIDTH-1:0] s_axis_tuser = 0; +reg [M_COUNT-1:0] m_axis_tready = 0; + +// Outputs +wire [S_COUNT-1:0] s_axis_tready; +wire [M_COUNT*M_DATA_WIDTH-1:0] m_axis_tdata; +wire [M_COUNT*M_KEEP_WIDTH-1:0] m_axis_tkeep; +wire [M_COUNT-1:0] m_axis_tvalid; +wire [M_COUNT-1:0] m_axis_tlast; +wire [M_COUNT*ID_WIDTH-1:0] m_axis_tid; +wire [M_COUNT*DEST_WIDTH-1:0] m_axis_tdest; +wire [M_COUNT*USER_WIDTH-1:0] m_axis_tuser; +wire [S_COUNT-1:0] status_overflow; +wire [S_COUNT-1:0] status_bad_frame; +wire [S_COUNT-1:0] status_good_frame; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tid, + s_axis_tdest, + s_axis_tuser, + m_axis_tready + ); + $to_myhdl( + s_axis_tready, + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tid, + m_axis_tdest, + m_axis_tuser, + status_overflow, + status_bad_frame, + status_good_frame + ); + + // dump file + $dumpfile("test_axis_ram_switch_4x4_64_64.lxt"); + $dumpvars(0, test_axis_ram_switch_4x4_64_64); +end + +axis_ram_switch #( + .FIFO_DEPTH(FIFO_DEPTH), + .SPEEDUP(SPEEDUP), + .S_COUNT(S_COUNT), + .M_COUNT(M_COUNT), + .S_DATA_WIDTH(S_DATA_WIDTH), + .S_KEEP_ENABLE(S_KEEP_ENABLE), + .S_KEEP_WIDTH(S_KEEP_WIDTH), + .M_DATA_WIDTH(M_DATA_WIDTH), + .M_KEEP_ENABLE(M_KEEP_ENABLE), + .M_KEEP_WIDTH(M_KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .USER_BAD_FRAME_VALUE(USER_BAD_FRAME_VALUE), + .USER_BAD_FRAME_MASK(USER_BAD_FRAME_MASK), + .DROP_BAD_FRAME(DROP_BAD_FRAME), + .DROP_WHEN_FULL(DROP_WHEN_FULL), + .M_BASE(M_BASE), + .M_TOP(M_TOP), + .M_CONNECT(M_CONNECT), + .ARB_TYPE(ARB_TYPE), + .LSB_PRIORITY(LSB_PRIORITY) +) +UUT ( + .clk(clk), + .rst(rst), + // AXI inputs + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser), + // Status + .status_overflow(status_overflow), + .status_bad_frame(status_bad_frame), + .status_good_frame(status_good_frame) +); + +endmodule