//////////////////////////////////////////////////////////////////////////////// // // Filename: axisswitch.v // {{{ // Project: WB2AXIPSP: bus bridges and other odds and ends // // Purpose: Switch from among several AXI streams based upon an AXI-lite // controlled index. All streams must have the same width. // The switch will use TLAST to guarantee that it will not change // mid-packet. If TLAST is unused for a particular input, simply set it // to 1'b1. // // Creator: Dan Gisselquist, Ph.D. // Gisselquist Technology, LLC // //////////////////////////////////////////////////////////////////////////////// // }}} // Copyright (C) 2020-2024, Gisselquist Technology, LLC // {{{ // // This file is part of the WB2AXIP project. // // The WB2AXIP project contains free software and gateware, licensed under the // Apache License, Version 2.0 (the "License"). You may not use this project, // or this file, except in compliance with the License. You may obtain a copy // of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. // //////////////////////////////////////////////////////////////////////////////// // `default_nettype none // }}} module axisswitch #( // {{{ // // Size of the AXI-lite bus. These are fixed, since 1) AXI-lite // is fixed at a width of 32-bits by Xilinx def'n, and 2) since // we only ever have a single configuration words. parameter C_AXI_ADDR_WIDTH = 2, localparam C_AXI_DATA_WIDTH = 32, // parameter NUM_STREAMS = 4, parameter C_AXIS_DATA_WIDTH = 32, parameter [0:0] OPT_LOWPOWER = 0 // }}} ) ( // {{{ input wire S_AXI_ACLK, input wire S_AXI_ARESETN, // AXI-Lite control // {{{ input wire S_AXI_AWVALID, output wire S_AXI_AWREADY, input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_AWADDR, input wire [2:0] S_AXI_AWPROT, // input wire S_AXI_WVALID, output wire S_AXI_WREADY, input wire [C_AXI_DATA_WIDTH-1:0] S_AXI_WDATA, input wire [C_AXI_DATA_WIDTH/8-1:0] S_AXI_WSTRB, // output wire S_AXI_BVALID, input wire S_AXI_BREADY, output wire [1:0] S_AXI_BRESP, // input wire S_AXI_ARVALID, output wire S_AXI_ARREADY, input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_ARADDR, input wire [2:0] S_AXI_ARPROT, // output wire S_AXI_RVALID, input wire S_AXI_RREADY, output wire [C_AXI_DATA_WIDTH-1:0] S_AXI_RDATA, output wire [1:0] S_AXI_RRESP, // }}} // AXI stream inputs to be switched // {{{ input wire [NUM_STREAMS-1:0] S_AXIS_TVALID, output wire [NUM_STREAMS-1:0] S_AXIS_TREADY, input wire [NUM_STREAMS*C_AXIS_DATA_WIDTH-1:0] S_AXIS_TDATA, input wire [NUM_STREAMS-1:0] S_AXIS_TLAST, // }}} // AXI stream output result // {{{ output reg M_AXIS_TVALID, input wire M_AXIS_TREADY, output reg [C_AXIS_DATA_WIDTH-1:0] M_AXIS_TDATA, output reg M_AXIS_TLAST // }}} // }}} ); //////////////////////////////////////////////////////////////////////// // // Register/wire signal declarations // {{{ //////////////////////////////////////////////////////////////////////// // // localparam LGNS = $clog2(NUM_STREAMS); localparam ADDRLSB = $clog2(C_AXI_DATA_WIDTH)-3; wire i_reset = !S_AXI_ARESETN; wire axil_write_ready; wire [0:0] awskd_addr; // UNUSED // wire [C_AXI_DATA_WIDTH-1:0] wskd_data; wire [C_AXI_DATA_WIDTH/8-1:0] wskd_strb; reg axil_bvalid; // wire axil_read_ready; wire [0:0] arskd_addr; // UNUSED reg [C_AXI_DATA_WIDTH-1:0] axil_read_data; reg axil_read_valid; reg [LGNS-1:0] r_index; wire [31:0] wskd_index; genvar gk; reg [NUM_STREAMS-1:0] skd_switch_ready; reg [LGNS-1:0] switch_index; wire [C_AXIS_DATA_WIDTH-1:0] skd_data [0:NUM_STREAMS-1]; wire [NUM_STREAMS-1:0] skd_valid, skd_last; reg mid_packet, r_mid_packet; // }}} //////////////////////////////////////////////////////////////////////// // // AXI-lite signaling // {{{ //////////////////////////////////////////////////////////////////////// // // // // Write signaling // // {{{ wire awskd_valid, wskd_valid; skidbuffer #(.OPT_OUTREG(0), .OPT_LOWPOWER(OPT_LOWPOWER), .DW(1)) axilawskid(// .i_clk(S_AXI_ACLK), .i_reset(i_reset), .i_valid(S_AXI_AWVALID), .o_ready(S_AXI_AWREADY), .i_data(1'b0), .o_valid(awskd_valid), .i_ready(axil_write_ready), .o_data(awskd_addr)); skidbuffer #(.OPT_OUTREG(0), .OPT_LOWPOWER(OPT_LOWPOWER), .DW(C_AXI_DATA_WIDTH+C_AXI_DATA_WIDTH/8)) axilwskid(// .i_clk(S_AXI_ACLK), .i_reset(i_reset), .i_valid(S_AXI_WVALID), .o_ready(S_AXI_WREADY), .i_data({ S_AXI_WDATA, S_AXI_WSTRB }), .o_valid(wskd_valid), .i_ready(axil_write_ready), .o_data({ wskd_data, wskd_strb })); assign axil_write_ready = awskd_valid && wskd_valid && (!S_AXI_BVALID || S_AXI_BREADY); initial axil_bvalid = 0; always @(posedge S_AXI_ACLK) if (i_reset) axil_bvalid <= 0; else if (axil_write_ready) axil_bvalid <= 1; else if (S_AXI_BREADY) axil_bvalid <= 0; assign S_AXI_BVALID = axil_bvalid; assign S_AXI_BRESP = 2'b00; // }}} // // Read signaling // // {{{ wire arskd_valid; skidbuffer #(.OPT_OUTREG(0), .OPT_LOWPOWER(OPT_LOWPOWER), .DW(1)) axilarskid(// .i_clk(S_AXI_ACLK), .i_reset(i_reset), .i_valid(S_AXI_ARVALID), .o_ready(S_AXI_ARREADY), .i_data(1'b0), .o_valid(arskd_valid), .i_ready(axil_read_ready), .o_data(arskd_addr)); assign axil_read_ready = arskd_valid && (!axil_read_valid || S_AXI_RREADY); initial axil_read_valid = 1'b0; always @(posedge S_AXI_ACLK) if (i_reset) axil_read_valid <= 1'b0; else if (axil_read_ready) axil_read_valid <= 1'b1; else if (S_AXI_RREADY) axil_read_valid <= 1'b0; assign S_AXI_RVALID = axil_read_valid; assign S_AXI_RDATA = axil_read_data; assign S_AXI_RRESP = 2'b00; // }}} // }}} //////////////////////////////////////////////////////////////////////// // // AXI-lite register logic // {{{ //////////////////////////////////////////////////////////////////////// // // // apply_wstrb(old_data, new_data, write_strobes) // r_index, (wskd_index) // {{{ assign wskd_index = apply_wstrb( { {(C_AXI_DATA_WIDTH-LGNS){1'b0}}, r_index }, wskd_data, wskd_strb); // r_index initial r_index = 0; always @(posedge S_AXI_ACLK) if (i_reset) r_index <= 0; else if (axil_write_ready) r_index <= wskd_index[LGNS-1:0]; // }}} // axil_read_data // {{{ initial axil_read_data = 0; always @(posedge S_AXI_ACLK) if (OPT_LOWPOWER && !S_AXI_ARESETN) axil_read_data <= 0; else if (!S_AXI_RVALID || S_AXI_RREADY) begin axil_read_data <= 0; axil_read_data[LGNS-1:0] <= r_index; if (OPT_LOWPOWER && !axil_read_ready) axil_read_data <= 0; end // }}} // function apply_wstrb // {{{ function [C_AXI_DATA_WIDTH-1:0] apply_wstrb; input [C_AXI_DATA_WIDTH-1:0] prior_data; input [C_AXI_DATA_WIDTH-1:0] new_data; input [C_AXI_DATA_WIDTH/8-1:0] wstrb; integer k; for(k=0; k