//////////////////////////////////////////////////////////////////////////////// // // Filename: axildouble.v // {{{ // Project: WB2AXIPSP: bus bridges and other odds and ends // // Purpose: Create a special slave which can be used to reduce crossbar // logic for multiple simplified slaves. This is a companion // core to the similar axilsingle core, but allowing the slave to // decode the clock between multiple possible addresses. // // To use this, the slave must follow specific (simplified AXI) rules: // // Write interface // 1. The slave must guarantee that AWREADY == WREADY = 1 // (This core doesn't have AWREADY or WREADY inputs) // 2. The slave must also guarantee that BVALID == $past(AWVALID) // (This core internally generates BVALID) // 3. The controller will guarantee that AWVALID == WVALID // (You can connect AWVALID to WVALID when connecting to your core) // 4. The controller will also guarantee that BREADY = 1 // (This core doesn't have a BVALID input) // // Read interface // 1. The slave must guarantee that ARREADY == 1 // (This core doesn't have an ARREADY input) // 2. The slave must also guarantee that RVALID == $past(ARVALID) // (This core doesn't have an RVALID input, trusting the slave // instead) // 3. The controller will guarantee that RREADY = 1 // (This core doesn't have an RREADY output) // // // Why? This simplifies slave logic. Slaves may interact with the bus // using only the logic below: // // always @(posedge S_AXI_ACLK) // if (AWVALID) case(AWADDR) // R1: slvreg_1 <= WDATA; // R2: slvreg_2 <= WDATA; // R3: slvreg_3 <= WDATA; // R4: slvreg_4 <= WDATA; // endcase // // always @(*) // BRESP = 2'b00; // // always @(posedge S_AXI_ACLK) // if (ARVALID) // case(ARADDR) // R1: RDATA <= slvreg_1; // R2: RDATA <= slvreg_2; // R3: RDATA <= slvreg_3; // R4: RDATA <= slvreg_4; // endcase // // always @(*) // RRESP = 2'b00; // // This core will then keep track of the more complex bus logic, // simplifying both slaves and connection logic. Slaves with the more // complicated (and proper/accurate) logic, that follow the rules above, // should have no problems with this additional logic. // // Performance: // // This core can sustain one read/write per clock as long as the upstream // AXI master keeps S_AXI_[BR]READY high. If S_AXI_[BR]READY ever drops, // there's some flexibility provided by the return FIFO, so the master // might not notice a drop in throughput until the FIFO fills. // // The more practical performance measure is the latency of this core. // That I've measured at four clocks. // // Creator: Dan Gisselquist, Ph.D. // Gisselquist Technology, LLC // //////////////////////////////////////////////////////////////////////////////// // }}} // Copyright (C) 2019-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 // `ifdef VERILATOR // `define FORMAL // `endif // }}} module axildouble #( // {{{ parameter integer C_AXI_DATA_WIDTH = 32, parameter integer C_AXI_ADDR_WIDTH = 32, // // NS is the number of slave interfaces parameter NS = 8, // // parameter [NS*C_AXI_ADDR_WIDTH-1:0] SLAVE_ADDR = { { 3'b111, {(C_AXI_ADDR_WIDTH-3){1'b0}} }, { 3'b110, {(C_AXI_ADDR_WIDTH-3){1'b0}} }, { 3'b101, {(C_AXI_ADDR_WIDTH-3){1'b0}} }, { 3'b100, {(C_AXI_ADDR_WIDTH-3){1'b0}} }, { 3'b011, {(C_AXI_ADDR_WIDTH-3){1'b0}} }, { 3'b010, {(C_AXI_ADDR_WIDTH-3){1'b0}} }, { 4'b0001,{(C_AXI_ADDR_WIDTH-4){1'b0}} }, { 4'b0000,{(C_AXI_ADDR_WIDTH-4){1'b0}} } }, // // parameter [NS*C_AXI_ADDR_WIDTH-1:0] SLAVE_MASK = (NS <= 1) ? 0 : { {(NS-2){ 3'b111,{(C_AXI_ADDR_WIDTH-3){1'b0}} }}, {(2){ 4'b1111,{(C_AXI_ADDR_WIDTH-4){1'b0}} }} }, // // // LGFLEN specifies the log (based two) of the number of // transactions that may need to be held outstanding internally. // If you really want high throughput, and if you expect any // back pressure at all, then increase LGFLEN. Otherwise the // default value of 3 (FIFO size = 8) should be sufficient // to maintain full loading parameter LGFLEN=3, // // If set, OPT_LOWPOWER will set all unused registers, both // internal and external, to zero anytime their corresponding // *VALID bit is clear parameter [0:0] OPT_LOWPOWER = 0 // }}} ) ( // {{{ input wire S_AXI_ACLK, input wire S_AXI_ARESETN, // input wire S_AXI_AWVALID, output wire S_AXI_AWREADY, input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_AWADDR, input wire [3-1: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 [2-1:0] S_AXI_BRESP, output wire S_AXI_BVALID, input wire S_AXI_BREADY, // input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_ARADDR, input wire [3-1:0] S_AXI_ARPROT, input wire S_AXI_ARVALID, output wire S_AXI_ARREADY, // output wire [C_AXI_DATA_WIDTH-1:0] S_AXI_RDATA, output wire [2-1:0] S_AXI_RRESP, output wire S_AXI_RVALID, input wire S_AXI_RREADY, // // // output wire [NS-1:0] M_AXI_AWVALID, output wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_AWADDR, output wire [3-1:0] M_AXI_AWPROT, // output wire [C_AXI_DATA_WIDTH-1:0] M_AXI_WDATA, output wire [C_AXI_DATA_WIDTH/8-1:0] M_AXI_WSTRB, // input wire [NS*2-1:0] M_AXI_BRESP, // output wire [NS-1:0] M_AXI_ARVALID, output wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_ARADDR, output wire [3-1:0] M_AXI_ARPROT, // input wire [NS*C_AXI_DATA_WIDTH-1:0] M_AXI_RDATA, input wire [NS*2-1:0] M_AXI_RRESP // }}} ); // // AW, and DW, are short-hand abbreviations used locally. localparam AW = C_AXI_ADDR_WIDTH; localparam DW = C_AXI_DATA_WIDTH; localparam LGNS = $clog2(NS); // localparam INTERCONNECT_ERROR = 2'b11; //////////////////////////////////////////////////////////////////////// // // Write logic: // //////////////////////////////////////////////////////////////////////// // // wire awskid_valid, bffull, bempty, write_awskidready, dcd_awvalid; reg write_bvalid, write_response; reg bfull, write_wready, write_no_index; wire [NS:0] wdecode; wire [AW-1:0] awskid_addr; wire [AW-1:0] m_awaddr; reg [LGNS-1:0] write_windex, write_bindex; wire [3-1:0] awskid_prot, m_axi_awprot; wire [LGFLEN:0] bfill; reg [LGFLEN:0] write_count; reg [1:0] write_resp; integer k; skidbuffer #(.OPT_LOWPOWER(OPT_LOWPOWER), .OPT_OUTREG(0), .DW(AW+3)) awskid( .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN), .i_valid(S_AXI_AWVALID), .o_ready(S_AXI_AWREADY), .i_data({ S_AXI_AWPROT, S_AXI_AWADDR }), .o_valid(awskid_valid), .i_ready(write_awskidready), .o_data({ awskid_prot, awskid_addr })); wire awskd_stall; addrdecode #(.AW(AW), .DW(3), .NS(NS), .SLAVE_ADDR(SLAVE_ADDR), .SLAVE_MASK(SLAVE_MASK), .OPT_REGISTERED(1'b1)) wraddr(.i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN), .i_valid(awskid_valid && write_awskidready), .o_stall(awskd_stall), .i_addr(awskid_addr), .i_data(awskid_prot), .o_valid(dcd_awvalid), .i_stall(!S_AXI_WVALID), .o_decode(wdecode), .o_addr(m_awaddr), .o_data(m_axi_awprot)); always @(*) write_wready = dcd_awvalid; assign S_AXI_WREADY = write_wready; assign M_AXI_AWVALID = (S_AXI_WVALID) ? wdecode[NS-1:0] : 0; assign M_AXI_AWADDR = m_awaddr; assign M_AXI_AWPROT = m_axi_awprot; assign M_AXI_WDATA = S_AXI_WDATA; assign M_AXI_WSTRB = S_AXI_WSTRB; assign write_awskidready = (S_AXI_WVALID || !S_AXI_WREADY) && !bfull; always @(*) begin write_windex = 0; for(k=0; k 4); always @(*) cover(cvr_arvalids > 4); always @(*) cover(cvr_reads > 4); always @(*) cover(cvr_writes > 4); always @(*) cover((cvr_writes > 4) && (cvr_reads > 4)); `endif endmodule // `ifndef YOSYS // `default_nettype wire // `endif