| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // Filename: axixbar.v |
| // |
| // Project: WB2AXIPSP: bus bridges and other odds and ends |
| // |
| // Purpose: Create a full crossbar between NM AXI sources (masters), and NS |
| // AXI slaves. Every master can talk to any slave, provided it |
| // isn't already busy. |
| // {{{ |
| // Performance: This core has been designed with the goal of being able to push |
| // one transaction through the interconnect, from any master to |
| // any slave, per clock cycle. This may perhaps be its most unique |
| // feature. While throughput is good, latency is something else. |
| // |
| // The arbiter requires a clock to switch, then another clock to send data |
| // downstream. This creates a minimum two clock latency up front. The |
| // return path suffers another clock of latency as well, placing the |
| // minimum latency at four clocks. The minimum write latency is at |
| // least one clock longer, since the write data must wait for the write |
| // address before proceeeding. |
| // |
| // Note that this arbiter only forwards AxID fields. It does not use |
| // them in arbitration. As a result, only one master may ever make |
| // requests of any given slave at a time. All responses from a slave |
| // will be returned to that known master. This is a known limitation in |
| // this implementation which will be fixed (in time) with funding and |
| // interest. Until that time, in order for a second master to access |
| // a given slave, the first master must receive all of its acknowledgments. |
| // |
| // Usage: To use, you must first set NM and NS to the number of masters |
| // and the number of slaves you wish to connect to. You then need to |
| // adjust the addresses of the slaves, found SLAVE_ADDR array. Those |
| // bits that are relevant in SLAVE_ADDR to then also be set in SLAVE_MASK. |
| // Adjusting the data and address widths go without saying. |
| // |
| // Lower numbered masters are given priority in any "fight". |
| // |
| // Channel grants are given on the condition that 1) they are requested, |
| // 2) no other channel has a grant, 3) all of the responses have been |
| // received from the current channel, and 4) the internal counters are |
| // not overflowing. |
| // |
| // The core limits the number of outstanding transactions on any channel to |
| // 1<<LGMAXBURST-1. |
| // |
| // Channel grants are lost 1) after OPT_LINGER clocks of being idle, or |
| // 2) when another master requests an idle (but still lingering) channel |
| // assignment, or 3) once all the responses have been returned to the |
| // current channel, and the current master is requesting another channel. |
| // |
| // A special slave is allocated for the case of no valid address. |
| // |
| // Since the write channel has no address information, the write data |
| // channel always be delayed by at least one clock from the write address |
| // channel. |
| // |
| // If OPT_LOWPOWER is set, then unused values will be set to zero. |
| // This can also be used to help identify relevant values within any |
| // trace. |
| // |
| // |
| // Creator: Dan Gisselquist, Ph.D. |
| // Gisselquist Technology, LLC |
| // }}} |
| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // Copyright (C) 2019-2020, 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 axixbar #( |
| // {{{ |
| parameter integer C_AXI_DATA_WIDTH = 32, |
| parameter integer C_AXI_ADDR_WIDTH = 32, |
| parameter integer C_AXI_ID_WIDTH = 2, |
| // |
| // NM is the number of masters driving incoming slave channels |
| parameter NM = 4, |
| // |
| // NS is the number of slaves connected to the crossbar |
| parameter NS = 8, |
| // |
| // IW, AW, and DW, are short-hand abbreviations used locally. |
| localparam IW = C_AXI_ID_WIDTH, |
| localparam AW = C_AXI_ADDR_WIDTH, |
| localparam DW = C_AXI_DATA_WIDTH, |
| // |
| // SLAVE_ADDR is an array of addresses, describing each of |
| // {{{ |
| // the slave channels. It works tightly with SLAVE_MASK, |
| // so that when (ADDR & MASK == ADDR), the channel in question |
| // has been requested. |
| // |
| // It is an internal in the setup of this core to doubly map |
| // an address, such that (addr & SLAVE_MASK[k])==SLAVE_ADDR[k] |
| // for two separate values of k. |
| // |
| // Any attempt to access an address that is a hole in this |
| // address list will result in a returned xRESP value of |
| // INTERCONNECT_ERROR (2'b11) |
| parameter [NS*AW-1:0] SLAVE_ADDR = { |
| 3'b111, {(AW-3){1'b0}}, |
| 3'b110, {(AW-3){1'b0}}, |
| 3'b101, {(AW-3){1'b0}}, |
| 3'b100, {(AW-3){1'b0}}, |
| 3'b011, {(AW-3){1'b0}}, |
| 3'b010, {(AW-3){1'b0}}, |
| 4'b0001, {(AW-4){1'b0}}, |
| 4'b0000, {(AW-4){1'b0}} }, |
| // }}} |
| // |
| // SLAVE_MASK: is an array, much like SLAVE_ADDR, describing |
| // {{{ |
| // which of the bits in SLAVE_ADDR are relevant. It is |
| // important to maintain for every slave that |
| // (~SLAVE_MASK[i] & SLAVE_ADDR[i]) == 0. |
| // Verilator lint_off WIDTH |
| parameter [NS*AW-1:0] SLAVE_MASK = |
| (NS <= 1) ? { 4'b1111, {(AW-4){1'b0}} } |
| : { {(NS-2){ 3'b111, {(AW-3){1'b0}} }}, |
| {(2){ 4'b1111, {(AW-4){1'b0}} } } }, |
| // Verilator lint_on WIDTH |
| // }}} |
| // |
| // OPT_LOWPOWER: If set, it forces all unused values to zero, |
| // {{{ |
| // preventing them from unnecessarily toggling. This will |
| // raise the logic count of the core, but might also lower |
| // the power used by the interconnect and the bus driven wires |
| // which (in my experience) tend to have a high fan out. |
| parameter [0:0] OPT_LOWPOWER = 0, |
| // }}} |
| // |
| // OPT_LINGER: Set this to the number of clocks an idle |
| // {{{ |
| // channel shall be left open before being closed. Once |
| // closed, it will take a minimum of two clocks before the |
| // channel can be opened and data transmitted through it again. |
| parameter OPT_LINGER = 4, |
| // }}} |
| // |
| // [EXPERIMENTAL] OPT_QOS: If set, the QOS transmission values |
| // {{{ |
| // will be honored when determining who wins arbitration for |
| // accessing a given slave. (This feature has not yet been |
| // verified) |
| parameter [0:0] OPT_QOS = 0, |
| // }}} |
| // |
| // LGMAXBURST: Specifies the log based two of the maximum |
| // {{{ |
| // number of bursts transactions that may be outstanding at any |
| // given time. This is different from the maximum number of |
| // outstanding beats. |
| parameter LGMAXBURST = 3 |
| // }}} |
| // }}} |
| ) ( |
| // {{{ |
| input wire S_AXI_ACLK, |
| input wire S_AXI_ARESETN, |
| // Write slave channels from the controlling AXI masters |
| // {{{ |
| input wire [NM*C_AXI_ID_WIDTH-1:0] S_AXI_AWID, |
| input wire [NM*C_AXI_ADDR_WIDTH-1:0] S_AXI_AWADDR, |
| input wire [NM*8-1:0] S_AXI_AWLEN, |
| input wire [NM*3-1:0] S_AXI_AWSIZE, |
| input wire [NM*2-1:0] S_AXI_AWBURST, |
| input wire [NM-1:0] S_AXI_AWLOCK, |
| input wire [NM*4-1:0] S_AXI_AWCACHE, |
| input wire [NM*3-1:0] S_AXI_AWPROT, |
| input wire [NM*4-1:0] S_AXI_AWQOS, |
| input wire [NM-1:0] S_AXI_AWVALID, |
| output wire [NM-1:0] S_AXI_AWREADY, |
| // |
| input wire [NM*C_AXI_DATA_WIDTH-1:0] S_AXI_WDATA, |
| input wire [NM*C_AXI_DATA_WIDTH/8-1:0] S_AXI_WSTRB, |
| input wire [NM-1:0] S_AXI_WLAST, |
| input wire [NM-1:0] S_AXI_WVALID, |
| output wire [NM-1:0] S_AXI_WREADY, |
| // |
| output wire [NM*C_AXI_ID_WIDTH-1:0] S_AXI_BID, |
| output wire [NM*2-1:0] S_AXI_BRESP, |
| output wire [NM-1:0] S_AXI_BVALID, |
| input wire [NM-1:0] S_AXI_BREADY, |
| // }}} |
| // Read slave channels from the controlling AXI masters |
| // {{{ |
| input wire [NM*C_AXI_ID_WIDTH-1:0] S_AXI_ARID, |
| input wire [NM*C_AXI_ADDR_WIDTH-1:0] S_AXI_ARADDR, |
| input wire [NM*8-1:0] S_AXI_ARLEN, |
| input wire [NM*3-1:0] S_AXI_ARSIZE, |
| input wire [NM*2-1:0] S_AXI_ARBURST, |
| input wire [NM-1:0] S_AXI_ARLOCK, |
| input wire [NM*4-1:0] S_AXI_ARCACHE, |
| input wire [NM*3-1:0] S_AXI_ARPROT, |
| input wire [NM*4-1:0] S_AXI_ARQOS, |
| input wire [NM-1:0] S_AXI_ARVALID, |
| output wire [NM-1:0] S_AXI_ARREADY, |
| // |
| output wire [NM*C_AXI_ID_WIDTH-1:0] S_AXI_RID, |
| output wire [NM*C_AXI_DATA_WIDTH-1:0] S_AXI_RDATA, |
| output wire [NM*2-1:0] S_AXI_RRESP, |
| output wire [NM-1:0] S_AXI_RLAST, |
| output wire [NM-1:0] S_AXI_RVALID, |
| input wire [NM-1:0] S_AXI_RREADY, |
| // }}} |
| // Write channel master outputs to the connected AXI slaves |
| // {{{ |
| output wire [NS*C_AXI_ID_WIDTH-1:0] M_AXI_AWID, |
| output wire [NS*C_AXI_ADDR_WIDTH-1:0] M_AXI_AWADDR, |
| output wire [NS*8-1:0] M_AXI_AWLEN, |
| output wire [NS*3-1:0] M_AXI_AWSIZE, |
| output wire [NS*2-1:0] M_AXI_AWBURST, |
| output wire [NS-1:0] M_AXI_AWLOCK, |
| output wire [NS*4-1:0] M_AXI_AWCACHE, |
| output wire [NS*3-1:0] M_AXI_AWPROT, |
| output wire [NS*4-1:0] M_AXI_AWQOS, |
| output wire [NS-1:0] M_AXI_AWVALID, |
| input wire [NS-1:0] M_AXI_AWREADY, |
| // |
| // |
| output wire [NS*C_AXI_DATA_WIDTH-1:0] M_AXI_WDATA, |
| output wire [NS*C_AXI_DATA_WIDTH/8-1:0] M_AXI_WSTRB, |
| output wire [NS-1:0] M_AXI_WLAST, |
| output wire [NS-1:0] M_AXI_WVALID, |
| input wire [NS-1:0] M_AXI_WREADY, |
| // |
| input wire [NS*C_AXI_ID_WIDTH-1:0] M_AXI_BID, |
| input wire [NS*2-1:0] M_AXI_BRESP, |
| input wire [NS-1:0] M_AXI_BVALID, |
| output wire [NS-1:0] M_AXI_BREADY, |
| // }}} |
| // Read channel master outputs to the connected AXI slaves |
| // {{{ |
| output wire [NS*C_AXI_ID_WIDTH-1:0] M_AXI_ARID, |
| output wire [NS*C_AXI_ADDR_WIDTH-1:0] M_AXI_ARADDR, |
| output wire [NS*8-1:0] M_AXI_ARLEN, |
| output wire [NS*3-1:0] M_AXI_ARSIZE, |
| output wire [NS*2-1:0] M_AXI_ARBURST, |
| output wire [NS-1:0] M_AXI_ARLOCK, |
| output wire [NS*4-1:0] M_AXI_ARCACHE, |
| output wire [NS*4-1:0] M_AXI_ARQOS, |
| output wire [NS*3-1:0] M_AXI_ARPROT, |
| output wire [NS-1:0] M_AXI_ARVALID, |
| input wire [NS-1:0] M_AXI_ARREADY, |
| // |
| // |
| input wire [NS*C_AXI_ID_WIDTH-1:0] M_AXI_RID, |
| input wire [NS*C_AXI_DATA_WIDTH-1:0] M_AXI_RDATA, |
| input wire [NS*2-1:0] M_AXI_RRESP, |
| input wire [NS-1:0] M_AXI_RLAST, |
| input wire [NS-1:0] M_AXI_RVALID, |
| output wire [NS-1:0] M_AXI_RREADY |
| // }}} |
| // }}} |
| ); |
| // |
| // Local parameters, derived from those above |
| // {{{ |
| localparam LGLINGER = (OPT_LINGER>1) ? $clog2(OPT_LINGER+1) : 1; |
| // |
| localparam LGNM = (NM>1) ? $clog2(NM) : 1; |
| localparam LGNS = (NS>1) ? $clog2(NS+1) : 1; |
| // |
| // In order to use indexes, and hence fully balanced mux trees, it helps |
| // to make certain that we have a power of two based lookup. NMFULL |
| // is the number of masters in this lookup, with potentially some |
| // unused extra ones. NSFULL is defined similarly. |
| localparam NMFULL = (NM>1) ? (1<<LGNM) : 1; |
| localparam NSFULL = (NS>1) ? (1<<LGNS) : 2; |
| // |
| localparam [1:0] INTERCONNECT_ERROR = 2'b11; |
| // |
| // OPT_SKID_INPUT controls whether the input skid buffers register |
| // their outputs or not. If set, all skid buffers will cost one more |
| // clock of latency. It's not clear that there's a performance gain |
| // to be had by setting this. |
| localparam [0:0] OPT_SKID_INPUT = 0; |
| // |
| // OPT_BUFFER_DECODER determines whether or not the outputs of the |
| // address decoder will be buffered or not. If buffered, there will |
| // be an extra (registered) clock delay on each of the A* channels from |
| // VALID to issue. |
| localparam [0:0] OPT_BUFFER_DECODER = 1; |
| // |
| // OPT_AWW controls whether or not a W* beat may be issued to a slave |
| // at the same time as the first AW* beat gets sent to the slave. Set |
| // to 1'b1 for lower latency, at the potential cost of a greater |
| // combinatorial path length |
| localparam OPT_AWW = 1'b1; |
| // }}} |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Internal signal declarations and definitions |
| // {{{ |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // |
| genvar N,M; |
| integer iN, iM; |
| |
| reg [NSFULL-1:0] wrequest [0:NM-1]; |
| reg [NSFULL-1:0] rrequest [0:NM-1]; |
| reg [NSFULL-1:0] wrequested [0:NM]; |
| reg [NSFULL-1:0] rrequested [0:NM]; |
| reg [NS:0] wgrant [0:NM-1]; |
| reg [NS:0] rgrant [0:NM-1]; |
| reg [NM-1:0] mwgrant; |
| reg [NM-1:0] mrgrant; |
| reg [NS-1:0] swgrant; |
| reg [NS-1:0] srgrant; |
| |
| // verilator lint_off UNUSED |
| wire [LGMAXBURST-1:0] w_mawpending [0:NM-1]; |
| wire [LGMAXBURST-1:0] wlasts_pending [0:NM-1]; |
| wire [LGMAXBURST-1:0] w_mrpending [0:NM-1]; |
| // verilator lint_on UNUSED |
| reg [NM-1:0] mwfull; |
| reg [NM-1:0] mrfull; |
| reg [NM-1:0] mwempty; |
| reg [NM-1:0] mrempty; |
| // |
| reg [LGNS-1:0] mwindex [0:NMFULL-1]; |
| reg [LGNS-1:0] mrindex [0:NMFULL-1]; |
| reg [LGNM-1:0] swindex [0:NSFULL-1]; |
| reg [LGNM-1:0] srindex [0:NSFULL-1]; |
| |
| (* keep *) reg [NM-1:0] wdata_expected; |
| |
| // The shadow buffers |
| reg [NMFULL-1:0] m_awvalid, m_arvalid; |
| wire [NMFULL-1:0] m_wvalid; |
| wire [NM-1:0] dcd_awvalid, dcd_arvalid; |
| |
| wire [C_AXI_ID_WIDTH-1:0] m_awid [0:NMFULL-1]; |
| wire [C_AXI_ADDR_WIDTH-1:0] m_awaddr [0:NMFULL-1]; |
| wire [7:0] m_awlen [0:NMFULL-1]; |
| wire [2:0] m_awsize [0:NMFULL-1]; |
| wire [1:0] m_awburst [0:NMFULL-1]; |
| wire [NMFULL-1:0] m_awlock; |
| wire [3:0] m_awcache [0:NMFULL-1]; |
| wire [2:0] m_awprot [0:NMFULL-1]; |
| wire [3:0] m_awqos [0:NMFULL-1]; |
| // |
| wire [C_AXI_DATA_WIDTH-1:0] m_wdata [0:NMFULL-1]; |
| wire [C_AXI_DATA_WIDTH/8-1:0] m_wstrb [0:NMFULL-1]; |
| wire [NMFULL-1:0] m_wlast; |
| |
| wire [C_AXI_ID_WIDTH-1:0] m_arid [0:NMFULL-1]; |
| wire [C_AXI_ADDR_WIDTH-1:0] m_araddr [0:NMFULL-1]; |
| wire [8-1:0] m_arlen [0:NMFULL-1]; |
| wire [3-1:0] m_arsize [0:NMFULL-1]; |
| wire [2-1:0] m_arburst [0:NMFULL-1]; |
| wire [NMFULL-1:0] m_arlock; |
| wire [4-1:0] m_arcache [0:NMFULL-1]; |
| wire [2:0] m_arprot [0:NMFULL-1]; |
| wire [3:0] m_arqos [0:NMFULL-1]; |
| // |
| // |
| reg [NM-1:0] berr_valid; |
| reg [IW-1:0] berr_id [0:NM-1]; |
| // |
| reg [NM-1:0] rerr_none; |
| reg [NM-1:0] rerr_last; |
| reg [8:0] rerr_outstanding [0:NM-1]; |
| reg [IW-1:0] rerr_id [0:NM-1]; |
| |
| wire [NM-1:0] skd_awvalid, skd_awstall; |
| wire [NM-1:0] skd_arvalid, skd_arstall; |
| wire [IW-1:0] skd_awid [0:NM-1]; |
| wire [AW-1:0] skd_awaddr [0:NM-1]; |
| wire [8-1:0] skd_awlen [0:NM-1]; |
| wire [3-1:0] skd_awsize [0:NM-1]; |
| wire [2-1:0] skd_awburst [0:NM-1]; |
| wire [NM-1:0] skd_awlock; |
| wire [4-1:0] skd_awcache [0:NM-1]; |
| wire [3-1:0] skd_awprot [0:NM-1]; |
| wire [4-1:0] skd_awqos [0:NM-1]; |
| // |
| wire [IW-1:0] skd_arid [0:NM-1]; |
| wire [AW-1:0] skd_araddr [0:NM-1]; |
| wire [8-1:0] skd_arlen [0:NM-1]; |
| wire [3-1:0] skd_arsize [0:NM-1]; |
| wire [2-1:0] skd_arburst [0:NM-1]; |
| wire [NM-1:0] skd_arlock; |
| wire [4-1:0] skd_arcache [0:NM-1]; |
| wire [3-1:0] skd_arprot [0:NM-1]; |
| wire [4-1:0] skd_arqos [0:NM-1]; |
| |
| // Verilator lint_off UNUSED |
| reg [NSFULL-1:0] m_axi_awvalid; |
| reg [NSFULL-1:0] m_axi_awready; |
| reg [IW-1:0] m_axi_awid [0:NSFULL-1]; |
| reg [7:0] m_axi_awlen [0:NSFULL-1]; |
| |
| reg [NSFULL-1:0] m_axi_wvalid; |
| reg [NSFULL-1:0] m_axi_wready; |
| reg [NSFULL-1:0] m_axi_bvalid; |
| reg [NSFULL-1:0] m_axi_bready; |
| // Verilator lint_on UNUSED |
| reg [1:0] m_axi_bresp [0:NSFULL-1]; |
| reg [IW-1:0] m_axi_bid [0:NSFULL-1]; |
| |
| // Verilator lint_off UNUSED |
| reg [NSFULL-1:0] m_axi_arvalid; |
| reg [7:0] m_axi_arlen [0:NSFULL-1]; |
| reg [IW-1:0] m_axi_arid [0:NSFULL-1]; |
| reg [NSFULL-1:0] m_axi_arready; |
| // Verilator lint_on UNUSED |
| reg [NSFULL-1:0] m_axi_rvalid; |
| // Verilator lint_off UNUSED |
| reg [NSFULL-1:0] m_axi_rready; |
| // Verilator lint_on UNUSED |
| // |
| reg [IW-1:0] m_axi_rid [0:NSFULL-1]; |
| reg [DW-1:0] m_axi_rdata [0:NSFULL-1]; |
| reg [NSFULL-1:0] m_axi_rlast; |
| reg [2-1:0] m_axi_rresp [0:NSFULL-1]; |
| |
| reg [NM-1:0] slave_awaccepts; |
| reg [NM-1:0] slave_waccepts; |
| reg [NM-1:0] slave_raccepts; |
| |
| reg [NM-1:0] bskd_valid; |
| reg [NM-1:0] rskd_valid, rskd_rlast; |
| wire [NM-1:0] bskd_ready; |
| wire [NM-1:0] rskd_ready; |
| |
| reg [NMFULL-1:0] write_qos_lockout, |
| read_qos_lockout; |
| |
| reg [NSFULL-1:0] slave_awready, slave_wready, slave_arready; |
| // }}} |
| |
| // m_axi_* convenience signals (write side) |
| // {{{ |
| always @(*) |
| begin |
| m_axi_awvalid = -1; |
| m_axi_awready = -1; |
| m_axi_wvalid = -1; |
| m_axi_wready = -1; |
| m_axi_bvalid = 0; |
| m_axi_bready = -1; |
| |
| m_axi_awvalid[NS-1:0] = M_AXI_AWVALID; |
| m_axi_awready[NS-1:0] = M_AXI_AWREADY; |
| m_axi_wvalid[NS-1:0] = M_AXI_WVALID; |
| m_axi_wready[NS-1:0] = M_AXI_WREADY; |
| m_axi_bvalid[NS-1:0] = M_AXI_BVALID; |
| m_axi_bready[NS-1:0] = M_AXI_BREADY; |
| |
| for(iM=0; iM<NS; iM=iM+1) |
| begin |
| m_axi_awid[iM] = M_AXI_AWID[ iM*IW +: IW]; |
| m_axi_awlen[iM] = M_AXI_AWLEN[ iM* 8 +: 8]; |
| |
| m_axi_bid[iM] = M_AXI_BID[iM* IW +: IW]; |
| m_axi_bresp[iM] = M_AXI_BRESP[iM* 2 +: 2]; |
| |
| m_axi_rid[iM] = M_AXI_RID[ iM*IW +: IW]; |
| m_axi_rdata[iM] = M_AXI_RDATA[iM*DW +: DW]; |
| m_axi_rresp[iM] = M_AXI_RRESP[iM* 2 +: 2]; |
| m_axi_rlast[iM] = M_AXI_RLAST[iM]; |
| end |
| for(iM=NS; iM<NSFULL; iM=iM+1) |
| begin |
| m_axi_awid[iM] = 0; |
| m_axi_awlen[iM] = 0; |
| |
| m_axi_bresp[iM] = INTERCONNECT_ERROR; |
| m_axi_bid[iM] = 0; |
| |
| m_axi_rid[iM] = 0; |
| m_axi_rdata[iM] = 0; |
| m_axi_rresp[iM] = INTERCONNECT_ERROR; |
| m_axi_rlast[iM] = 1; |
| end |
| end |
| // }}} |
| |
| // m_axi_* convenience signals (read side) |
| // {{{ |
| always @(*) |
| begin |
| m_axi_arvalid = 0; |
| m_axi_arready = 0; |
| m_axi_rvalid = 0; |
| m_axi_rready = 0; |
| for(iM=0; iM<NS; iM=iM+1) |
| begin |
| m_axi_arlen[iM] = M_AXI_ARLEN[iM* 8 +: 8]; |
| m_axi_arid[iM] = M_AXI_ARID[ iM*IW +: IW]; |
| end |
| for(iM=NS; iM<NSFULL; iM=iM+1) |
| begin |
| m_axi_arlen[iM] = 0; |
| m_axi_arid[iM] = 0; |
| end |
| |
| m_axi_arvalid[NS-1:0] = M_AXI_ARVALID; |
| m_axi_arready[NS-1:0] = M_AXI_ARREADY; |
| m_axi_rvalid[NS-1:0] = M_AXI_RVALID; |
| m_axi_rready[NS-1:0] = M_AXI_RREADY; |
| end |
| // }}} |
| |
| // slave_*ready convenience signals |
| // {{{ |
| always @(*) |
| begin |
| // These are designed to keep us from doing things like |
| // m_axi_*[m?index[N]] && m_axi_*[m?index[N]] && .. etc |
| // |
| // First, we'll set bits for all slaves--to include those that |
| // are undefined (but required by our static analysis tools). |
| slave_awready = -1; |
| slave_wready = -1; |
| slave_arready = -1; |
| // |
| // Here we do all of the combinatoric calculations, so the |
| // master only needs to reference one bit of this signal |
| slave_awready[NS-1:0] = (~M_AXI_AWVALID | M_AXI_AWREADY); |
| slave_wready[NS-1:0] = (~M_AXI_WVALID | M_AXI_WREADY); |
| slave_arready[NS-1:0] = (~M_AXI_ARVALID | M_AXI_ARREADY); |
| end |
| // }}} |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Process our incoming signals: AW*, W*, and AR* |
| // {{{ |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // |
| |
| generate for(N=0; N<NM; N=N+1) |
| begin : W1_DECODE_WRITE_REQUEST |
| // {{{ |
| wire [NS:0] wdecode; |
| |
| // awskid, the skidbuffer for the incoming AW* channel |
| // {{{ |
| skidbuffer #(.DW(IW+AW+8+3+2+1+4+3+4), |
| .OPT_OUTREG(OPT_SKID_INPUT)) |
| awskid(S_AXI_ACLK, !S_AXI_ARESETN, |
| S_AXI_AWVALID[N], S_AXI_AWREADY[N], |
| { S_AXI_AWID[N*IW +: IW], S_AXI_AWADDR[N*AW +: AW], |
| S_AXI_AWLEN[N*8 +: 8], S_AXI_AWSIZE[N*3 +: 3], |
| S_AXI_AWBURST[N*2 +: 2], S_AXI_AWLOCK[N], |
| S_AXI_AWCACHE[N*4 +: 4], S_AXI_AWPROT[N*3 +: 3], |
| S_AXI_AWQOS[N*4 +: 4] }, |
| skd_awvalid[N], !skd_awstall[N], |
| { skd_awid[N], skd_awaddr[N], skd_awlen[N], |
| skd_awsize[N], skd_awburst[N], skd_awlock[N], |
| skd_awcache[N], skd_awprot[N], skd_awqos[N] }); |
| // }}} |
| |
| // wraddr, decode the write channel's address request to a |
| // particular slave index |
| // {{{ |
| addrdecode #(.AW(AW), .DW(IW+8+3+2+1+4+3+4), .NS(NS), |
| .SLAVE_ADDR(SLAVE_ADDR), |
| .SLAVE_MASK(SLAVE_MASK), |
| .OPT_REGISTERED(OPT_BUFFER_DECODER)) |
| wraddr(.i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN), |
| .i_valid(skd_awvalid[N]), .o_stall(skd_awstall[N]), |
| .i_addr(skd_awaddr[N]), .i_data({ skd_awid[N], |
| skd_awlen[N], skd_awsize[N], skd_awburst[N], |
| skd_awlock[N], skd_awcache[N], skd_awprot[N], |
| skd_awqos[N] }), |
| .o_valid(dcd_awvalid[N]), |
| .i_stall(!dcd_awvalid[N]||!slave_awaccepts[N]), |
| .o_decode(wdecode), .o_addr(m_awaddr[N]), |
| .o_data({ m_awid[N], m_awlen[N], m_awsize[N], |
| m_awburst[N], m_awlock[N], m_awcache[N], |
| m_awprot[N], m_awqos[N]})); |
| // }}} |
| |
| // wskid, the skid buffer for the incoming W* channel |
| // {{{ |
| skidbuffer #(.DW(DW+DW/8+1), |
| .OPT_OUTREG(OPT_SKID_INPUT || OPT_BUFFER_DECODER)) |
| wskid(S_AXI_ACLK, !S_AXI_ARESETN, |
| S_AXI_WVALID[N], S_AXI_WREADY[N], |
| { S_AXI_WDATA[N*DW +: DW], S_AXI_WSTRB[N*DW/8 +: DW/8], |
| S_AXI_WLAST[N] }, |
| m_wvalid[N], slave_waccepts[N], |
| { m_wdata[N], m_wstrb[N], m_wlast[N] }); |
| // }}} |
| |
| // slave_awaccepts |
| // {{{ |
| always @(*) |
| begin |
| slave_awaccepts[N] = 1'b1; |
| |
| // Cannot accept/forward a packet without a bus grant |
| // This handles whether or not write data is still |
| // pending. |
| if (!mwgrant[N]) |
| slave_awaccepts[N] = 1'b0; |
| if (write_qos_lockout[N]) |
| slave_awaccepts[N] = 1'b0; |
| if (mwfull[N]) |
| slave_awaccepts[N] = 1'b0; |
| // Don't accept a packet unless its to the same slave |
| // the grant is issued for |
| if (!wrequest[N][mwindex[N]]) |
| slave_awaccepts[N] = 1'b0; |
| if (!wgrant[N][NS]) |
| begin |
| if (!slave_awready[mwindex[N]]) |
| slave_awaccepts[N] = 1'b0; |
| end else if (!berr_valid[N] || !bskd_ready[N]) |
| begin |
| // Can't accept an write address channel request |
| // for the no-address-mapped channel if the |
| // B* channel is stalled, lest we lose the ID |
| // of the transaction |
| // |
| // !berr_valid[N] => we have to accept more |
| // write data before we can issue BVALID |
| slave_awaccepts[N] = 1'b0; |
| end |
| end |
| // }}} |
| |
| // slave_waccepts |
| // {{{ |
| always @(*) |
| begin |
| slave_waccepts[N] = 1'b1; |
| if (!mwgrant[N]) |
| slave_waccepts[N] = 1'b0; |
| if (!wdata_expected[N] && (!OPT_AWW || !slave_awaccepts[N])) |
| slave_waccepts[N] = 1'b0; |
| if (!wgrant[N][NS]) |
| begin |
| if (!slave_wready[mwindex[N]]) |
| slave_waccepts[N] = 1'b0; |
| end else if (berr_valid[N] && !bskd_ready[N]) |
| slave_waccepts[N] = 1'b0; |
| end |
| // }}} |
| |
| always @(*) |
| begin |
| m_awvalid[N]= dcd_awvalid[N] && !mwfull[N]; |
| wrequest[N]= 0; |
| if (!mwfull[N]) |
| wrequest[N][NS:0] = wdecode; |
| end |
| |
| // QOS handling via write_qos_lockout |
| // {{{ |
| if (!OPT_QOS || NM == 1) |
| begin : WRITE_NO_QOS |
| |
| // If we aren't using QOS, then never lock any packets |
| // out from arbitration |
| always @(*) |
| write_qos_lockout[N] = 0; |
| |
| end else begin : WRITE_QOS |
| |
| // Lock out a master based upon a second master having |
| // a higher QOS request level |
| // {{{ |
| initial write_qos_lockout[N] = 0; |
| always @(posedge S_AXI_ACLK) |
| if (!S_AXI_ARESETN) |
| write_qos_lockout[N] <= 0; |
| else begin |
| write_qos_lockout[N] <= 0; |
| |
| for(iN=0; iN<NM; iN=iN+1) |
| if (iN != N) |
| begin |
| if (m_awvalid[N] |
| &&(|(wrequest[iN][NS-1:0] |
| & wdecode[NS-1:0])) |
| &&(m_awqos[N] < m_awqos[iN])) |
| write_qos_lockout[N] <= 1; |
| end |
| end |
| // }}} |
| end |
| // }}} |
| |
| end for (N=NM; N<NMFULL; N=N+1) |
| begin : UNUSED_WSKID_BUFFERS |
| // {{{ |
| // The following values are unused. They need to be defined |
| // so that our indexing scheme will work, but indexes should |
| // never actually reference them |
| assign m_awid[N] = 0; |
| assign m_awaddr[N] = 0; |
| assign m_awlen[N] = 0; |
| assign m_awsize[N] = 0; |
| assign m_awburst[N] = 0; |
| assign m_awlock[N] = 0; |
| assign m_awcache[N] = 0; |
| assign m_awprot[N] = 0; |
| assign m_awqos[N] = 0; |
| |
| always @(*) |
| m_awvalid[N] = 0; |
| |
| assign m_wvalid[N] = 0; |
| // |
| assign m_wdata[N] = 0; |
| assign m_wstrb[N] = 0; |
| assign m_wlast[N] = 0; |
| |
| always @(*) |
| write_qos_lockout[N] = 0; |
| // }}} |
| // }}} |
| end endgenerate |
| |
| // Read skid buffers and address decoding, slave_araccepts logic |
| generate for(N=0; N<NM; N=N+1) |
| begin : R1_DECODE_READ_REQUEST |
| // {{{ |
| wire [NS:0] rdecode; |
| |
| // arskid |
| // {{{ |
| skidbuffer #(.DW(IW+AW+8+3+2+1+4+3+4), |
| .OPT_OUTREG(OPT_SKID_INPUT)) |
| arskid(S_AXI_ACLK, !S_AXI_ARESETN, |
| S_AXI_ARVALID[N], S_AXI_ARREADY[N], |
| { S_AXI_ARID[N*IW +: IW], S_AXI_ARADDR[N*AW +: AW], |
| S_AXI_ARLEN[N*8 +: 8], S_AXI_ARSIZE[N*3 +: 3], |
| S_AXI_ARBURST[N*2 +: 2], S_AXI_ARLOCK[N], |
| S_AXI_ARCACHE[N*4 +: 4], S_AXI_ARPROT[N*3 +: 3], |
| S_AXI_ARQOS[N*4 +: 4] }, |
| skd_arvalid[N], !skd_arstall[N], |
| { skd_arid[N], skd_araddr[N], skd_arlen[N], |
| skd_arsize[N], skd_arburst[N], skd_arlock[N], |
| skd_arcache[N], skd_arprot[N], skd_arqos[N] }); |
| // }}} |
| |
| // Read address decoder |
| // {{{ |
| addrdecode #(.AW(AW), .DW(IW+8+3+2+1+4+3+4), .NS(NS), |
| .SLAVE_ADDR(SLAVE_ADDR), |
| .SLAVE_MASK(SLAVE_MASK), |
| .OPT_REGISTERED(OPT_BUFFER_DECODER)) |
| rdaddr(.i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN), |
| .i_valid(skd_arvalid[N]), .o_stall(skd_arstall[N]), |
| .i_addr(skd_araddr[N]), .i_data({ skd_arid[N], |
| skd_arlen[N], skd_arsize[N], skd_arburst[N], |
| skd_arlock[N], skd_arcache[N], skd_arprot[N], |
| skd_arqos[N] }), |
| .o_valid(dcd_arvalid[N]), |
| .i_stall(!m_arvalid[N] || !slave_raccepts[N]), |
| .o_decode(rdecode), .o_addr(m_araddr[N]), |
| .o_data({ m_arid[N], m_arlen[N], m_arsize[N], |
| m_arburst[N], m_arlock[N], m_arcache[N], |
| m_arprot[N], m_arqos[N]})); |
| // }}} |
| |
| always @(*) |
| begin |
| m_arvalid[N] = dcd_arvalid[N] && !mrfull[N]; |
| rrequest[N] = 0; |
| if (!mrfull[N]) |
| rrequest[N][NS:0] = rdecode; |
| end |
| |
| // slave_raccepts decoding |
| // {{{ |
| always @(*) |
| begin |
| slave_raccepts[N] = 1'b1; |
| if (!mrgrant[N]) |
| slave_raccepts[N] = 1'b0; |
| if (read_qos_lockout[N]) |
| slave_raccepts[N] = 1'b0; |
| if (mrfull[N]) |
| slave_raccepts[N] = 1'b0; |
| // If we aren't requesting access to the channel we've |
| // been granted access to, then we can't accept this |
| // verilator lint_off WIDTH |
| if (!rrequest[N][mrindex[N]]) |
| slave_raccepts[N] = 1'b0; |
| // verilator lint_on WIDTH |
| if (!rgrant[N][NS]) |
| begin |
| if (!slave_arready[mrindex[N]]) |
| slave_raccepts[N] = 1'b0; |
| end else if (!mrempty[N] || !rerr_none[N] || rskd_valid[N]) |
| slave_raccepts[N] = 1'b0; |
| end |
| // }}} |
| |
| // Read QOS logic |
| // {{{ |
| // read_qos_lockout will get set if a master with a higher |
| // QOS number is requesting a given slave. It will not |
| // affect existing outstanding packets, but will be used to |
| // prevent further packets from being sent to a given slave. |
| if (!OPT_QOS || NM == 1) |
| begin : READ_NO_QOS |
| |
| // If we aren't implementing QOS, then the lockout |
| // signal is never set |
| always @(*) |
| read_qos_lockout[N] = 0; |
| |
| end else begin : READ_QOS |
| |
| // We set lockout if another master (with a higher |
| // QOS) is requesting this slave *and* the slave |
| // channel is currently stalled. |
| initial read_qos_lockout[N] = 0; |
| always @(posedge S_AXI_ACLK) |
| if (!S_AXI_ARESETN) |
| read_qos_lockout[N] <= 0; |
| else begin |
| read_qos_lockout[N] <= 0; |
| |
| for(iN=0; iN<NM; iN=iN+1) |
| if (iN != N) |
| begin |
| if (m_arvalid[iN] |
| && !slave_raccepts[N] |
| &&(|(rrequest[iN][NS-1:0] |
| & rdecode[NS-1:0])) |
| &&(m_arqos[N] < m_arqos[iN])) |
| read_qos_lockout[N] <= 1; |
| end |
| end |
| end |
| // }}} |
| |
| end for (N=NM; N<NMFULL; N=N+1) |
| begin : UNUSED_RSKID_BUFFERS |
| // {{{ |
| always @(*) |
| m_arvalid[N] = 0; |
| assign m_arid[N] = 0; |
| assign m_araddr[N] = 0; |
| assign m_arlen[N] = 0; |
| assign m_arsize[N] = 0; |
| assign m_arburst[N] = 0; |
| assign m_arlock[N] = 0; |
| assign m_arcache[N] = 0; |
| assign m_arprot[N] = 0; |
| assign m_arqos[N] = 0; |
| |
| always @(*) |
| read_qos_lockout[N] = 0; |
| // }}} |
| // }}} |
| end endgenerate |
| // }}} |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Channel arbitration |
| // {{{ |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // |
| |
| // wrequested |
| // {{{ |
| always @(*) |
| begin : W2_DECONFLICT_WRITE_REQUESTS |
| |
| for(iN=0; iN<=NM; iN=iN+1) |
| wrequested[iN] = 0; |
| |
| // Vivado may complain about too many bits for wrequested. |
| // This is (currrently) expected. mwindex is used to index |
| // into wrequested, and mwindex has LGNS bits, where LGNS |
| // is $clog2(NS+1) rather than $clog2(NS). The extra bits |
| // are defined to be zeros, but the point is they are defined. |
| // Therefore, no matter what mwindex is, it will always |
| // reference something valid. |
| wrequested[NM] = 0; |
| |
| for(iM=0; iM<NS; iM=iM+1) |
| begin |
| wrequested[0][iM] = 1'b0; |
| for(iN=1; iN<NM ; iN=iN+1) |
| begin |
| // Continue to request any channel with |
| // a grant and pending operations |
| if (wrequest[iN-1][iM] && wgrant[iN-1][iM]) |
| wrequested[iN][iM] = 1; |
| if (wrequest[iN-1][iM] && (!mwgrant[iN-1]||mwempty[iN-1])) |
| wrequested[iN][iM] = 1; |
| // Otherwise, if it's already claimed, then |
| // it can't be claimed again |
| if (wrequested[iN-1][iM]) |
| wrequested[iN][iM] = 1; |
| end |
| wrequested[NM][iM] = wrequest[NM-1][iM] || wrequested[NM-1][iM]; |
| end |
| end |
| // }}} |
| |
| // rrequested |
| // {{{ |
| always @(*) |
| begin : R2_DECONFLICT_READ_REQUESTS |
| |
| for(iN=0; iN<NM ; iN=iN+1) |
| rrequested[iN] = 0; |
| |
| // See the note above for wrequested. This applies to |
| // rrequested as well. |
| rrequested[NM] = 0; |
| |
| for(iM=0; iM<NS; iM=iM+1) |
| begin |
| rrequested[0][iM] = 0; |
| for(iN=1; iN<NM ; iN=iN+1) |
| begin |
| // Continue to request any channel with |
| // a grant and pending operations |
| if (rrequest[iN-1][iM] && rgrant[iN-1][iM]) |
| rrequested[iN][iM] = 1; |
| if (rrequest[iN-1][iM] && (!mrgrant[iN-1] || mrempty[iN-1])) |
| rrequested[iN][iM] = 1; |
| // Otherwise, if it's already claimed, then |
| // it can't be claimed again |
| if (rrequested[iN-1][iM]) |
| rrequested[iN][iM] = 1; |
| end |
| rrequested[NM][iM] = rrequest[NM-1][iM] || rrequested[NM-1][iM]; |
| end |
| end |
| // }}} |
| |
| |
| generate for(N=0; N<NM; N=N+1) |
| begin : W3_ARBITRATE_WRITE_REQUESTS |
| // {{{ |
| reg stay_on_channel; |
| reg requested_channel_is_available; |
| reg leave_channel; |
| reg [LGNS-1:0] requested_index; |
| reg linger; |
| |
| // The basic logic: |
| // 1. If we must stay_on_channel, then nothing changes |
| // 2. If the requested channel isn't available, then no grant |
| // is issued |
| // 3. Otherwise, if we need to leave this channel--such as if |
| // another master is requesting it, then we lose our grant |
| |
| // stay_on_channel |
| // {{{ |
| // We must stay on the channel if we aren't done working with it |
| // i.e. more writes requested, more acknowledgments expected, |
| // etc. |
| always @(*) |
| begin |
| stay_on_channel = |(wrequest[N][NS:0] & wgrant[N]); |
| if (write_qos_lockout[N]) |
| stay_on_channel = 0; |
| |
| // We must stay on this channel until we've received |
| // our last acknowledgment signal. Only then can we |
| // switch grants |
| if (mwgrant[N] && !mwempty[N]) |
| stay_on_channel = 1; |
| |
| // if berr_valid is true, we have a grant to the |
| // internal slave-error channel. While this grant |
| // exists, we cannot issue any others. |
| if (berr_valid[N]) |
| stay_on_channel = 1; |
| end |
| // }}} |
| |
| // requested_channel_is_available |
| // {{{ |
| always @(*) |
| begin |
| // The channel is available to us if 1) we want it, |
| // 2) no one else is using it, and 3) no one earlier |
| // has requested it |
| requested_channel_is_available = |
| |(wrequest[N][NS-1:0] & ~swgrant |
| & ~wrequested[N][NS-1:0]); |
| |
| // Of course, the error pseudo-channel is *always* |
| // available to us. |
| if (wrequest[N][NS]) |
| requested_channel_is_available = 1; |
| |
| // Likewise, if we are the only master, then the |
| // channel is always available on any request |
| if (NM < 2) |
| requested_channel_is_available = m_awvalid[N]; |
| end |
| // }}} |
| |
| // Linger option, and setting the "linger" flag |
| // {{{ |
| // If used, linger will hold on to a given channels grant |
| // for some number of clock ticks after the channel has become |
| // idle. This will spare future requests from the same master |
| // to the same slave from neding to go through the arbitration |
| // clock cycle again--potentially saving a clock period. If, |
| // however, the master in question requests a different slave |
| // or a different master requests this slave, then the linger |
| // option is voided and the grant given up anyway. |
| if (OPT_LINGER == 0) |
| begin |
| always @(*) |
| linger = 0; |
| end else begin : WRITE_LINGER |
| |
| reg [LGLINGER-1:0] linger_counter; |
| |
| initial linger = 0; |
| initial linger_counter = 0; |
| always @(posedge S_AXI_ACLK) |
| if (!S_AXI_ARESETN || wgrant[N][NS]) |
| begin |
| linger <= 0; |
| linger_counter <= 0; |
| end else if (!mwempty[N] || bskd_valid[N]) |
| begin |
| // While the channel is in use, we set the |
| // linger counter |
| linger_counter <= OPT_LINGER; |
| linger <= 1; |
| end else if (linger_counter > 0) |
| begin |
| // Otherwise, we decrement it until it reaches |
| // zero |
| linger <= (linger_counter > 1); |
| linger_counter <= linger_counter - 1; |
| end else |
| linger <= 0; |
| |
| end |
| // }}} |
| |
| // leave_channel |
| // {{{ |
| // True of another master is requesting access to this slave, |
| // or if we are requesting access to another slave. If QOS |
| // lockout is enabled, then we also leave the channel if a |
| // request with a higher QOS has arrived |
| always @(*) |
| begin |
| leave_channel = 0; |
| if (!m_awvalid[N] |
| && (!linger || wrequested[NM][mwindex[N]])) |
| // Leave the channel after OPT_LINGER counts |
| // of the channel being idle, or when someone |
| // else asks for the channel |
| leave_channel = 1; |
| if (m_awvalid[N] && !wrequest[N][mwindex[N]]) |
| // Need to leave this channel to connect |
| // to any other channel |
| leave_channel = 1; |
| if (write_qos_lockout[N]) |
| // Need to leave this channel for another higher |
| // priority request |
| leave_channel = 1; |
| end |
| // }}} |
| |
| // WRITE GRANT ALLOCATION |
| // {{{ |
| // Now that we've done our homework, we can switch grants |
| // if necessary |
| initial wgrant[N] = 0; |
| initial mwgrant[N] = 0; |
| always @(posedge S_AXI_ACLK) |
| if (!S_AXI_ARESETN) |
| begin |
| wgrant[N] <= 0; |
| mwgrant[N] <= 0; |
| end else if (!stay_on_channel) |
| begin |
| if (requested_channel_is_available) |
| begin |
| // Switch to a new channel |
| mwgrant[N] <= 1'b1; |
| wgrant[N] <= wrequest[N][NS:0]; |
| end else if (leave_channel) |
| begin |
| // Revoke the given grant |
| mwgrant[N] <= 1'b0; |
| wgrant[N] <= 0; |
| end |
| end |
| // }}} |
| |
| // mwindex (registered) |
| // {{{ |
| always @(*) |
| begin |
| requested_index = 0; |
| for(iM=0; iM<=NS; iM=iM+1) |
| if (wrequest[N][iM]) |
| requested_index= requested_index | iM[LGNS-1:0]; |
| end |
| |
| // Now for mwindex |
| initial mwindex[N] = 0; |
| always @(posedge S_AXI_ACLK) |
| if (!stay_on_channel && requested_channel_is_available) |
| mwindex[N] <= requested_index; |
| // }}} |
| |
| end for (N=NM; N<NMFULL; N=N+1) |
| begin |
| |
| always @(*) |
| mwindex[N] = 0; |
| // }}} |
| end endgenerate |
| |
| generate for(N=0; N<NM; N=N+1) |
| begin : R3_ARBITRATE_READ_REQUESTS |
| // {{{ |
| reg stay_on_channel; |
| reg requested_channel_is_available; |
| reg leave_channel; |
| reg [LGNS-1:0] requested_index; |
| reg linger; |
| |
| // The basic logic: |
| // 1. If we must stay_on_channel, then nothing changes |
| // 2. If the requested channel isn't available, then no grant |
| // is issued |
| // 3. Otherwise, if we need to leave this channel--such as if |
| // another master is requesting it, then we lose our grant |
| |
| // stay_on_channel |
| // {{{ |
| // We must stay on the channel if we aren't done working with it |
| // i.e. more reads requested, more acknowledgments expected, |
| // etc. |
| always @(*) |
| begin |
| stay_on_channel = |(rrequest[N][NS:0] & rgrant[N]); |
| if (read_qos_lockout[N]) |
| stay_on_channel = 0; |
| |
| // We must stay on this channel until we've received |
| // our last acknowledgment signal. Only then can we |
| // switch grants |
| if (mrgrant[N] && !mrempty[N]) |
| stay_on_channel = 1; |
| |
| // if we have a grant to the internal slave-error |
| // channel, then we cannot issue a grant to any other |
| // while this grant is active |
| if (rgrant[N][NS] && (!rerr_none[N] || rskd_valid[N])) |
| stay_on_channel = 1; |
| end |
| // }}} |
| |
| // requested_channel_is_available |
| // {{{ |
| always @(*) |
| begin |
| // The channel is available to us if 1) we want it, |
| // 2) no one else is using it, and 3) no one earlier |
| // has requested it |
| requested_channel_is_available = |
| |(rrequest[N][NS-1:0] & ~srgrant |
| & ~rrequested[N][NS-1:0]); |
| |
| // Of course, the error pseudo-channel is *always* |
| // available to us. |
| if (rrequest[N][NS]) |
| requested_channel_is_available = 1; |
| |
| // Likewise, if we are the only master, then the |
| // channel is always available on any request |
| if (NM < 2) |
| requested_channel_is_available = m_arvalid[N]; |
| end |
| // }}} |
| |
| // Linger option, and setting the "linger" flag |
| // {{{ |
| // If used, linger will hold on to a given channels grant |
| // for some number of clock ticks after the channel has become |
| // idle. This will spare future requests from the same master |
| // to the same slave from neding to go through the arbitration |
| // clock cycle again--potentially saving a clock period. If, |
| // however, the master in question requests a different slave |
| // or a different master requests this slave, then the linger |
| // option is voided and the grant given up anyway. |
| if (OPT_LINGER == 0) |
| begin |
| always @(*) |
| linger = 0; |
| end else begin : READ_LINGER |
| |
| reg [LGLINGER-1:0] linger_counter; |
| |
| initial linger = 0; |
| initial linger_counter = 0; |
| always @(posedge S_AXI_ACLK) |
| if (!S_AXI_ARESETN || rgrant[N][NS]) |
| begin |
| linger <= 0; |
| linger_counter <= 0; |
| end else if (!mrempty[N] || rskd_valid[N]) |
| begin |
| linger_counter <= OPT_LINGER; |
| linger <= 1; |
| end else if (linger_counter > 0) |
| begin |
| linger <= (linger_counter > 1); |
| linger_counter <= linger_counter - 1; |
| end else |
| linger <= 0; |
| |
| end |
| // }}} |
| |
| // leave_channel |
| // {{{ |
| // True of another master is requesting access to this slave, |
| // or if we are requesting access to another slave. If QOS |
| // lockout is enabled, then we also leave the channel if a |
| // request with a higher QOS has arrived |
| always @(*) |
| begin |
| leave_channel = 0; |
| if (!m_arvalid[N] |
| && (!linger || rrequested[NM][mrindex[N]])) |
| // Leave the channel after OPT_LINGER counts |
| // of the channel being idle, or when someone |
| // else asks for the channel |
| leave_channel = 1; |
| if (m_arvalid[N] && !rrequest[N][mrindex[N]]) |
| // Need to leave this channel to connect |
| // to any other channel |
| leave_channel = 1; |
| if (read_qos_lockout[N]) |
| leave_channel = 1; |
| end |
| // }}} |
| |
| |
| // READ GRANT ALLOCATION |
| // {{{ |
| initial rgrant[N] = 0; |
| initial mrgrant[N] = 0; |
| always @(posedge S_AXI_ACLK) |
| if (!S_AXI_ARESETN) |
| begin |
| rgrant[N] <= 0; |
| mrgrant[N] <= 0; |
| end else if (!stay_on_channel) |
| begin |
| if (requested_channel_is_available) |
| begin |
| // Switching channels |
| mrgrant[N] <= 1'b1; |
| rgrant[N] <= rrequest[N][NS:0]; |
| end else if (leave_channel) |
| begin |
| mrgrant[N] <= 1'b0; |
| rgrant[N] <= 0; |
| end |
| end |
| // }}} |
| |
| // mrindex (registered) |
| // {{{ |
| always @(*) |
| begin |
| requested_index = 0; |
| for(iM=0; iM<=NS; iM=iM+1) |
| if (rrequest[N][iM]) |
| requested_index = requested_index|iM[LGNS-1:0]; |
| end |
| |
| initial mrindex[N] = 0; |
| always @(posedge S_AXI_ACLK) |
| if (!stay_on_channel && requested_channel_is_available) |
| mrindex[N] <= requested_index; |
| // }}} |
| |
| end for (N=NM; N<NMFULL; N=N+1) |
| begin |
| |
| always @(*) |
| mrindex[N] = 0; |
| // }}} |
| end endgenerate |
| |
| // Calculate swindex (registered) |
| generate for (M=0; M<NS; M=M+1) |
| begin : W4_SLAVE_WRITE_INDEX |
| // {{{ |
| // swindex is a per slave index, containing the index of the |
| // master that has currently won write arbitration and so |
| // has permission to access this slave |
| if (NM <= 1) |
| begin |
| |
| // If there's only ever one master, that index is |
| // always the index of the one master. |
| always @(*) |
| swindex[M] = 0; |
| |
| end else begin : MULTIPLE_MASTERS |
| |
| reg [LGNM-1:0] reqwindex; |
| |
| // In the case of multiple masters, we follow the logic |
| // of the arbiter to generate the appropriate index |
| // here, and register it on the next clock cycle. If |
| // no slave has arbitration, the index will remain zero |
| always @(*) |
| begin |
| reqwindex = 0; |
| for(iN=0; iN<NM; iN=iN+1) |
| if ((!mwgrant[iN] || mwempty[iN]) |
| &&(wrequest[iN][M] && !wrequested[iN][M])) |
| reqwindex = reqwindex | iN[LGNM-1:0]; |
| end |
| |
| always @(posedge S_AXI_ACLK) |
| if (!swgrant[M]) |
| swindex[M] <= reqwindex; |
| end |
| |
| end for (M=NS; M<NSFULL; M=M+1) |
| begin |
| |
| always @(*) |
| swindex[M] = 0; |
| // }}} |
| end endgenerate |
| |
| // Calculate srindex (registered) |
| generate for (M=0; M<NS; M=M+1) |
| begin : R4_SLAVE_READ_INDEX |
| // {{{ |
| // srindex is an index to the master that has currently won |
| // read arbitration to the given slave. |
| |
| if (NM <= 1) |
| begin |
| // If there's only one master, srindex can always |
| // point to that master--no longic required |
| always @(*) |
| srindex[M] = 0; |
| |
| end else begin : MULTIPLE_MASTERS |
| |
| reg [LGNM-1:0] reqrindex; |
| |
| // In the case of multiple masters, we'll follow the |
| // read arbitration logic to generate the index--first |
| // combinatorially, then we'll register it. |
| always @(*) |
| begin |
| reqrindex = 0; |
| for(iN=0; iN<NM; iN=iN+1) |
| if ((!mrgrant[iN] || mrempty[iN]) |
| &&(rrequest[iN][M] && !rrequested[iN][M])) |
| reqrindex = reqrindex | iN[LGNM-1:0]; |
| end |
| |
| always @(posedge S_AXI_ACLK) |
| if (!srgrant[M]) |
| srindex[M] <= reqrindex; |
| end |
| |
| end for (M=NS; M<NSFULL; M=M+1) |
| begin |
| |
| always @(*) |
| srindex[M] = 0; |
| // }}} |
| end endgenerate |
| |
| // swgrant and srgrant (combinatorial) |
| generate for(M=0; M<NS; M=M+1) |
| begin : SGRANT |
| // {{{ |
| |
| // s?grant is a convenience to tell a slave that some master |
| // has won arbitration and so has a grant to that slave. |
| |
| // swgrant: write arbitration |
| always @(*) |
| begin |
| swgrant[M] = 0; |
| for(iN=0; iN<NM; iN=iN+1) |
| if (wgrant[iN][M]) |
| swgrant[M] = 1; |
| end |
| |
| // srgrant: read arbitration |
| always @(*) |
| begin |
| srgrant[M] = 0; |
| for(iN=0; iN<NM; iN=iN+1) |
| if (rgrant[iN][M]) |
| srgrant[M] = 1; |
| end |
| // }}} |
| end endgenerate |
| |
| // }}} |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Generate the signals for the various slaves--the forward channel |
| // {{{ |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Assign outputs to the various slaves |
| generate for(M=0; M<NS; M=M+1) |
| begin : W5_WRITE_SLAVE_OUTPUTS |
| // {{{ |
| reg axi_awvalid; |
| reg [IW-1:0] axi_awid; |
| reg [AW-1:0] axi_awaddr; |
| reg [7:0] axi_awlen; |
| reg [2:0] axi_awsize; |
| reg [1:0] axi_awburst; |
| reg axi_awlock; |
| reg [3:0] axi_awcache; |
| reg [2:0] axi_awprot; |
| reg [3:0] axi_awqos; |
| |
| reg axi_wvalid; |
| reg [DW-1:0] axi_wdata; |
| reg [DW/8-1:0] axi_wstrb; |
| reg axi_wlast; |
| // |
| reg axi_bready; |
| |
| reg sawstall, swstall; |
| reg awaccepts; |
| |
| // Control the slave's AW* channel |
| // {{{ |
| |
| // Personalize the slave_awaccepts signal |
| always @(*) |
| awaccepts = slave_awaccepts[swindex[M]]; |
| |
| always @(*) |
| sawstall= (M_AXI_AWVALID[M]&& !M_AXI_AWREADY[M]); |
| |
| initial axi_awvalid = 0; |
| always @(posedge S_AXI_ACLK) |
| if (!S_AXI_ARESETN || !swgrant[M]) |
| axi_awvalid <= 0; |
| else if (!sawstall) |
| begin |
| axi_awvalid <= m_awvalid[swindex[M]] &&(awaccepts); |
| end |
| |
| initial axi_awid = 0; |
| initial axi_awaddr = 0; |
| initial axi_awlen = 0; |
| initial axi_awsize = 0; |
| initial axi_awburst = 0; |
| initial axi_awlock = 0; |
| initial axi_awcache = 0; |
| initial axi_awprot = 0; |
| initial axi_awqos = 0; |
| always @(posedge S_AXI_ACLK) |
| if (OPT_LOWPOWER && (!S_AXI_ARESETN || !swgrant[M])) |
| begin |
| // Under the OPT_LOWPOWER option, we clear all signals |
| // we aren't using |
| axi_awid <= 0; |
| axi_awaddr <= 0; |
| axi_awlen <= 0; |
| axi_awsize <= 0; |
| axi_awburst <= 0; |
| axi_awlock <= 0; |
| axi_awcache <= 0; |
| axi_awprot <= 0; |
| axi_awqos <= 0; |
| end else if (!sawstall) |
| begin |
| if (!OPT_LOWPOWER||(m_awvalid[swindex[M]]&&awaccepts)) |
| begin |
| // swindex[M] is defined as 0 above in the |
| // case where NM <= 1 |
| axi_awid <= m_awid[ swindex[M]]; |
| axi_awaddr <= m_awaddr[ swindex[M]]; |
| axi_awlen <= m_awlen[ swindex[M]]; |
| axi_awsize <= m_awsize[ swindex[M]]; |
| axi_awburst <= m_awburst[swindex[M]]; |
| axi_awlock <= m_awlock[ swindex[M]]; |
| axi_awcache <= m_awcache[swindex[M]]; |
| axi_awprot <= m_awprot[ swindex[M]]; |
| axi_awqos <= m_awqos[ swindex[M]]; |
| end else begin |
| axi_awid <= 0; |
| axi_awaddr <= 0; |
| axi_awlen <= 0; |
| axi_awsize <= 0; |
| axi_awburst <= 0; |
| axi_awlock <= 0; |
| axi_awcache <= 0; |
| axi_awprot <= 0; |
| axi_awqos <= 0; |
| end |
| end |
| // }}} |
| |
| // Control the slave's W* channel |
| // {{{ |
| always @(*) |
| swstall = (M_AXI_WVALID[M] && !M_AXI_WREADY[M]); |
| |
| initial axi_wvalid = 0; |
| always @(posedge S_AXI_ACLK) |
| if (!S_AXI_ARESETN || !swgrant[M]) |
| axi_wvalid <= 0; |
| else if (!swstall) |
| begin |
| axi_wvalid <= (m_wvalid[swindex[M]]) |
| &&(slave_waccepts[swindex[M]]); |
| end |
| |
| initial axi_wdata = 0; |
| initial axi_wstrb = 0; |
| initial axi_wlast = 0; |
| always @(posedge S_AXI_ACLK) |
| if (OPT_LOWPOWER && !S_AXI_ARESETN) |
| begin |
| axi_wdata <= 0; |
| axi_wstrb <= 0; |
| axi_wlast <= 0; |
| end else if (OPT_LOWPOWER && !swgrant[M]) |
| begin |
| axi_wdata <= 0; |
| axi_wstrb <= 0; |
| axi_wlast <= 0; |
| end else if (!swstall) |
| begin |
| if (!OPT_LOWPOWER || (m_wvalid[swindex[M]]&&slave_waccepts[swindex[M]])) |
| begin |
| // If NM <= 1, swindex[M] is already defined |
| // to be zero above |
| axi_wdata <= m_wdata[swindex[M]]; |
| axi_wstrb <= m_wstrb[swindex[M]]; |
| axi_wlast <= m_wlast[swindex[M]]; |
| end else begin |
| axi_wdata <= 0; |
| axi_wstrb <= 0; |
| axi_wlast <= 0; |
| end |
| end |
| // }}} |
| |
| // |
| always @(*) |
| if (!swgrant[M]) |
| axi_bready = 1; |
| else |
| axi_bready = bskd_ready[swindex[M]]; |
| |
| // Combinatorial assigns |
| // {{{ |
| assign M_AXI_AWVALID[M] = axi_awvalid; |
| assign M_AXI_AWID[ M*IW +: IW] = axi_awid; |
| assign M_AXI_AWADDR[ M*AW +: AW] = axi_awaddr; |
| assign M_AXI_AWLEN[ M* 8 +: 8] = axi_awlen; |
| assign M_AXI_AWSIZE[ M* 3 +: 3] = axi_awsize; |
| assign M_AXI_AWBURST[M* 2 +: 2] = axi_awburst; |
| assign M_AXI_AWLOCK[ M] = axi_awlock; |
| assign M_AXI_AWCACHE[M* 4 +: 4] = axi_awcache; |
| assign M_AXI_AWPROT[ M* 3 +: 3] = axi_awprot; |
| assign M_AXI_AWQOS[ M* 4 +: 4] = axi_awqos; |
| // |
| // |
| assign M_AXI_WVALID[M] = axi_wvalid; |
| assign M_AXI_WDATA[M*DW +: DW] = axi_wdata; |
| assign M_AXI_WSTRB[M*DW/8 +: DW/8] = axi_wstrb; |
| assign M_AXI_WLAST[M] = axi_wlast; |
| // |
| // |
| assign M_AXI_BREADY[M] = axi_bready; |
| // }}} |
| // |
| // }}} |
| end endgenerate |
| |
| |
| generate for(M=0; M<NS; M=M+1) |
| begin : R5_READ_SLAVE_OUTPUTS |
| // {{{ |
| reg axi_arvalid; |
| reg [IW-1:0] axi_arid; |
| reg [AW-1:0] axi_araddr; |
| reg [7:0] axi_arlen; |
| reg [2:0] axi_arsize; |
| reg [1:0] axi_arburst; |
| reg axi_arlock; |
| reg [3:0] axi_arcache; |
| reg [2:0] axi_arprot; |
| reg [3:0] axi_arqos; |
| // |
| reg axi_rready; |
| reg arstall; |
| |
| always @(*) |
| arstall= axi_arvalid && !M_AXI_ARREADY[M]; |
| |
| initial axi_arvalid = 0; |
| always @(posedge S_AXI_ACLK) |
| if (!S_AXI_ARESETN || !srgrant[M]) |
| axi_arvalid <= 0; |
| else if (!arstall) |
| axi_arvalid <= m_arvalid[srindex[M]] && slave_raccepts[srindex[M]]; |
| else if (M_AXI_ARREADY[M]) |
| axi_arvalid <= 0; |
| |
| initial axi_arid = 0; |
| initial axi_araddr = 0; |
| initial axi_arlen = 0; |
| initial axi_arsize = 0; |
| initial axi_arburst = 0; |
| initial axi_arlock = 0; |
| initial axi_arcache = 0; |
| initial axi_arprot = 0; |
| initial axi_arqos = 0; |
| always @(posedge S_AXI_ACLK) |
| if (OPT_LOWPOWER && (!S_AXI_ARESETN || !srgrant[M])) |
| begin |
| axi_arid <= 0; |
| axi_araddr <= 0; |
| axi_arlen <= 0; |
| axi_arsize <= 0; |
| axi_arburst <= 0; |
| axi_arlock <= 0; |
| axi_arcache <= 0; |
| axi_arprot <= 0; |
| axi_arqos <= 0; |
| end else if (!arstall) |
| begin |
| if (!OPT_LOWPOWER || (m_arvalid[srindex[M]] && slave_raccepts[srindex[M]])) |
| begin |
| // If NM <=1, srindex[M] is defined to be zero |
| axi_arid <= m_arid[ srindex[M]]; |
| axi_araddr <= m_araddr[ srindex[M]]; |
| axi_arlen <= m_arlen[ srindex[M]]; |
| axi_arsize <= m_arsize[ srindex[M]]; |
| axi_arburst <= m_arburst[srindex[M]]; |
| axi_arlock <= m_arlock[ srindex[M]]; |
| axi_arcache <= m_arcache[srindex[M]]; |
| axi_arprot <= m_arprot[ srindex[M]]; |
| axi_arqos <= m_arqos[ srindex[M]]; |
| end else begin |
| axi_arid <= 0; |
| axi_araddr <= 0; |
| axi_arlen <= 0; |
| axi_arsize <= 0; |
| axi_arburst <= 0; |
| axi_arlock <= 0; |
| axi_arcache <= 0; |
| axi_arprot <= 0; |
| axi_arqos <= 0; |
| end |
| end |
| |
| always @(*) |
| if (!srgrant[M]) |
| axi_rready = 1; |
| else |
| axi_rready = rskd_ready[srindex[M]]; |
| |
| // |
| assign M_AXI_ARVALID[M] = axi_arvalid; |
| assign M_AXI_ARID[ M*IW +: IW] = axi_arid; |
| assign M_AXI_ARADDR[ M*AW +: AW] = axi_araddr; |
| assign M_AXI_ARLEN[ M* 8 +: 8] = axi_arlen; |
| assign M_AXI_ARSIZE[ M* 3 +: 3] = axi_arsize; |
| assign M_AXI_ARBURST[M* 2 +: 2] = axi_arburst; |
| assign M_AXI_ARLOCK[ M] = axi_arlock; |
| assign M_AXI_ARCACHE[M* 4 +: 4] = axi_arcache; |
| assign M_AXI_ARPROT[ M* 3 +: 3] = axi_arprot; |
| assign M_AXI_ARQOS[ M* 4 +: 4] = axi_arqos; |
| // |
| assign M_AXI_RREADY[M] = axi_rready; |
| // |
| // }}} |
| end endgenerate |
| // }}} |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Generate the signals for the various masters--the return channel |
| // {{{ |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Return values |
| generate for (N=0; N<NM; N=N+1) |
| begin : W6_WRITE_RETURN_CHANNEL |
| // {{{ |
| reg [1:0] i_axi_bresp; |
| reg [IW-1:0] i_axi_bid; |
| |
| // Write error (no slave selected) state machine |
| // {{{ |
| initial berr_valid[N] = 0; |
| always @(posedge S_AXI_ACLK) |
| if (!S_AXI_ARESETN) |
| berr_valid[N] <= 0; |
| else if (wgrant[N][NS] && m_wvalid[N] && m_wlast[N] |
| && slave_waccepts[N]) |
| berr_valid[N] <= 1; |
| else if (bskd_ready[N]) |
| berr_valid[N] <= 0; |
| |
| always @(*) |
| if (berr_valid[N]) |
| bskd_valid[N] = 1; |
| else |
| bskd_valid[N] = mwgrant[N]&&m_axi_bvalid[mwindex[N]]; |
| |
| always @(posedge S_AXI_ACLK) |
| if (m_awvalid[N] && slave_awaccepts[N]) |
| berr_id[N] <= m_awid[N]; |
| |
| always @(*) |
| if (wgrant[N][NS]) |
| begin |
| i_axi_bid = berr_id[N]; |
| i_axi_bresp = INTERCONNECT_ERROR; |
| end else begin |
| i_axi_bid = m_axi_bid[mwindex[N]]; |
| i_axi_bresp = m_axi_bresp[mwindex[N]]; |
| end |
| // }}} |
| |
| // bskid, the B* channel skidbuffer |
| // {{{ |
| skidbuffer #(.DW(IW+2), |
| .OPT_LOWPOWER(OPT_LOWPOWER), |
| .OPT_OUTREG(1)) |
| bskid(S_AXI_ACLK, !S_AXI_ARESETN, |
| bskd_valid[N], bskd_ready[N], |
| { i_axi_bid, i_axi_bresp }, |
| S_AXI_BVALID[N], S_AXI_BREADY[N], |
| { S_AXI_BID[N*IW +: IW], S_AXI_BRESP[N*2 +: 2] }); |
| // }}} |
| // }}} |
| end endgenerate |
| |
| // Return values |
| generate for (N=0; N<NM; N=N+1) |
| begin : R6_READ_RETURN_CHANNEL |
| // {{{ |
| |
| reg [DW-1:0] i_axi_rdata; |
| reg [IW-1:0] i_axi_rid; |
| reg [2-1:0] i_axi_rresp; |
| |
| // generate the read response |
| // {{{ |
| // Here we have two choices. We can either generate our |
| // response from the slave itself, or from our internally |
| // generated (no-slave exists) FSM. |
| always @(*) |
| if (rgrant[N][NS]) |
| rskd_valid[N] = !rerr_none[N]; |
| else |
| rskd_valid[N] = mrgrant[N] && m_axi_rvalid[mrindex[N]]; |
| |
| always @(*) |
| if (rgrant[N][NS]) |
| begin |
| i_axi_rid = rerr_id[N]; |
| i_axi_rdata = 0; |
| rskd_rlast[N] = rerr_last[N]; |
| i_axi_rresp = INTERCONNECT_ERROR; |
| end else begin |
| i_axi_rid = m_axi_rid[mrindex[N]]; |
| i_axi_rdata = m_axi_rdata[mrindex[N]]; |
| rskd_rlast[N]= m_axi_rlast[mrindex[N]]; |
| i_axi_rresp = m_axi_rresp[mrindex[N]]; |
| end |
| // }}} |
| |
| // rskid, the outgoing read skidbuffer |
| // {{{ |
| // Since our various read signals are all combinatorially |
| // determined, we'll throw them into an outgoing skid buffer |
| // to register them (per spec) and to make it easier to meet |
| // timing. |
| skidbuffer #(.DW(IW+DW+1+2), |
| .OPT_LOWPOWER(OPT_LOWPOWER), |
| .OPT_OUTREG(1)) |
| rskid(S_AXI_ACLK, !S_AXI_ARESETN, |
| rskd_valid[N], rskd_ready[N], |
| { i_axi_rid, i_axi_rdata, rskd_rlast[N], i_axi_rresp }, |
| S_AXI_RVALID[N], S_AXI_RREADY[N], |
| { S_AXI_RID[N*IW +: IW], S_AXI_RDATA[N*DW +: DW], |
| S_AXI_RLAST[N], S_AXI_RRESP[N*2 +: 2] }); |
| // }}} |
| // }}} |
| end endgenerate |
| // }}} |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Count pending transactions |
| // {{{ |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // |
| generate for (N=0; N<NM; N=N+1) |
| begin : W7_COUNT_PENDING_WRITES |
| // {{{ |
| |
| reg [LGMAXBURST-1:0] awpending, wpending; |
| reg r_wdata_expected; |
| |
| // awpending, and the associated flags mwempty and mwfull |
| // {{{ |
| // awpending is a count of all of the AW* packets that have |
| // been forwarded to the slave, but for which the slave has |
| // yet to return a B* response. This number can be as large |
| // as (1<<LGMAXBURST)-1. The two associated flags, mwempty |
| // and mwfull, are there to keep us from checking awempty==0 |
| // and &awempty respectively. |
| initial awpending = 0; |
| initial mwempty[N] = 1; |
| initial mwfull[N] = 0; |
| always @(posedge S_AXI_ACLK) |
| if (!S_AXI_ARESETN) |
| begin |
| awpending <= 0; |
| mwempty[N] <= 1; |
| mwfull[N] <= 0; |
| end else case ({(m_awvalid[N] && slave_awaccepts[N]), |
| (bskd_valid[N] && bskd_ready[N])}) |
| 2'b01: begin |
| awpending <= awpending - 1; |
| mwempty[N] <= (awpending <= 1); |
| mwfull[N] <= 0; |
| end |
| 2'b10: begin |
| awpending <= awpending + 1; |
| mwempty[N] <= 0; |
| mwfull[N] <= &awpending[LGMAXBURST-1:1]; |
| end |
| default: begin end |
| endcase |
| |
| // Just so we can access this counter elsewhere, let's make |
| // it available outside of this generate block. (The formal |
| // section uses this.) |
| assign w_mawpending[N] = awpending; |
| // }}} |
| |
| // r_wdata_expected and wdata_expected |
| // {{{ |
| // This section keeps track of whether or not we are expecting |
| // more W* data from the given burst. It's designed to keep us |
| // from accepting new W* information before the AW* portion |
| // has been routed to the new slave. |
| // |
| // Addition: wpending. wpending counts the number of write |
| // bursts that are pending, based upon the write channel. |
| // Bursts are counted from AWVALID & AWREADY, and decremented |
| // once we see the WVALID && WREADY signal. Packets should |
| // not be accepted without a prior (or concurrent) |
| // AWVALID && AWREADY. |
| initial r_wdata_expected = 0; |
| initial wpending = 0; |
| always @(posedge S_AXI_ACLK) |
| if (!S_AXI_ARESETN) |
| begin |
| r_wdata_expected <= 0; |
| wpending <= 0; |
| end else case ({(m_awvalid[N] && slave_awaccepts[N]), |
| (m_wvalid[N]&&slave_waccepts[N] && m_wlast[N])}) |
| 2'b01: begin |
| r_wdata_expected <= (wpending > 1); |
| wpending <= wpending - 1; |
| end |
| 2'b10: begin |
| wpending <= wpending + 1; |
| r_wdata_expected <= 1; |
| end |
| default: begin end |
| endcase |
| |
| always @(*) |
| wdata_expected[N] = r_wdata_expected; |
| |
| assign wlasts_pending[N] = wpending; |
| // }}} |
| // }}} |
| end endgenerate |
| |
| generate for (N=0; N<NM; N=N+1) |
| begin : R7_COUNT_PENDING_READS |
| // {{{ |
| |
| reg [LGMAXBURST-1:0] rpending; |
| |
| // rpending, and its associated mrempty and mrfull |
| // {{{ |
| // rpending counts the number of read transactions that have |
| // been accepted, but for which rlast has yet to be returned. |
| // This specifically counts grants to valid slaves. The error |
| // slave is excluded from this count. mrempty and mrfull have |
| // analogous definitions to mwempty and mwfull, being equal to |
| // rpending == 0 and (&rpending) respectfully. |
| initial rpending = 0; |
| initial mrempty[N] = 1; |
| initial mrfull[N] = 0; |
| always @(posedge S_AXI_ACLK) |
| if (!S_AXI_ARESETN) |
| begin |
| rpending <= 0; |
| mrempty[N]<= 1; |
| mrfull[N] <= 0; |
| end else case ({(m_arvalid[N] && slave_raccepts[N] && !rgrant[N][NS]), |
| (rskd_valid[N] && rskd_ready[N] |
| && rskd_rlast[N] && !rgrant[N][NS])}) |
| 2'b01: begin |
| rpending <= rpending - 1; |
| mrempty[N] <= (rpending == 1); |
| mrfull[N] <= 0; |
| end |
| 2'b10: begin |
| rpending <= rpending + 1; |
| mrfull[N] <= &rpending[LGMAXBURST-1:1]; |
| mrempty[N] <= 0; |
| end |
| default: begin end |
| endcase |
| |
| assign w_mrpending[N] = rpending; |
| // }}} |
| |
| // Read error state machine, rerr_outstanding and rerr_id |
| // {{{ |
| // rerr_outstanding is the count of read *beats* that remain |
| // to be returned to a master from a non-existent slave. |
| // rerr_last is true on the last of these read beats, |
| // equivalent to rerr_outstanding == 1, and rerr_none is true |
| // when the error state machine is idle |
| initial rerr_outstanding[N] = 0; |
| initial rerr_last[N] = 0; |
| initial rerr_none[N] = 1; |
| always @(posedge S_AXI_ACLK) |
| if (!S_AXI_ARESETN) |
| begin |
| rerr_outstanding[N] <= 0; |
| rerr_last[N] <= 0; |
| rerr_none[N] <= 1; |
| end else if (!rerr_none[N]) |
| begin |
| if (!rskd_valid[N] || rskd_ready[N]) |
| begin |
| rerr_none[N] <= (rerr_outstanding[N] == 1); |
| rerr_last[N] <= (rerr_outstanding[N] == 2); |
| rerr_outstanding[N] <= rerr_outstanding[N] - 1; |
| end |
| end else if (m_arvalid[N] && rrequest[N][NS] |
| && slave_raccepts[N]) |
| begin |
| rerr_none[N] <= 0; |
| rerr_last[N] <= (m_arlen[N] == 0); |
| rerr_outstanding[N] <= m_arlen[N] + 1; |
| end |
| |
| // rerr_id is the ARID field of the currently outstanding |
| // error. It's used when generating a read response to a |
| // non-existent slave. |
| initial rerr_id[N] = 0; |
| always @(posedge S_AXI_ACLK) |
| if (!S_AXI_ARESETN && OPT_LOWPOWER) |
| rerr_id[N] <= 0; |
| else if (m_arvalid[N] && slave_raccepts[N]) |
| begin |
| if (rrequest[N][NS] || !OPT_LOWPOWER) |
| // A low-logic definition |
| rerr_id[N] <= m_arid[N]; |
| else |
| rerr_id[N] <= 0; |
| end else if (OPT_LOWPOWER && rerr_last[N] |
| && (!rskd_valid[N] || rskd_ready[N])) |
| rerr_id[N] <= 0; |
| // }}} |
| |
| `ifdef FORMAL |
| always @(*) |
| assert(rerr_none[N] == (rerr_outstanding[N] == 0)); |
| always @(*) |
| assert(rerr_last[N] == (rerr_outstanding[N] == 1)); |
| always @(*) |
| if (OPT_LOWPOWER && rerr_none[N]) |
| assert(rerr_id[N] == 0); |
| `endif |
| // }}} |
| end endgenerate |
| // }}} |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // (Partial) Parameter validation |
| // {{{ |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // |
| initial begin |
| if (NM == 0) begin |
| $display("At least one master must be defined"); |
| $stop; |
| end |
| |
| if (NS == 0) begin |
| $display("At least one slave must be defined"); |
| $stop; |
| end |
| end |
| // }}} |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| //////////////////////////////////////////////////////////////////////////////// |
| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // Formal property verification section |
| // {{{ |
| //////////////////////////////////////////////////////////////////////////////// |
| //////////////////////////////////////////////////////////////////////////////// |
| //////////////////////////////////////////////////////////////////////////////// |
| `ifdef FORMAL |
| localparam F_LGDEPTH = LGMAXBURST+9; |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Declare signals used for formal checking |
| // {{{ |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // |
| // |
| // ... |
| // |
| // }}} |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Initial/reset value checking |
| // {{{ |
| initial assert(NS >= 1); |
| initial assert(NM >= 1); |
| // }}} |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Check the arbiter signals for consistency |
| // {{{ |
| generate for(N=0; N<NM; N=N+1) |
| begin : F1_CHECK_MASTER_GRANTS |
| // {{{ |
| // Write grants |
| always @(*) |
| for(iM=0; iM<=NS; iM=iM+1) |
| begin |
| if (wgrant[N][iM]) |
| begin |
| assert((wgrant[N] ^ (1<<iM))==0); |
| assert(mwgrant[N]); |
| assert(mwindex[N] == iM); |
| if (iM < NS) |
| begin |
| assert(swgrant[iM]); |
| assert(swindex[iM] == N); |
| end |
| end |
| end |
| |
| always @(*) |
| if (mwgrant[N]) |
| assert(wgrant[N] != 0); |
| |
| always @(*) |
| if (wrequest[N][NS]) |
| assert(wrequest[N][NS-1:0] == 0); |
| |
| |
| always @(posedge S_AXI_ACLK) |
| if (S_AXI_ARESETN && f_past_valid && bskd_valid[N]) |
| begin |
| assert($stable(wgrant[N])); |
| assert($stable(mwindex[N])); |
| end |
| |
| //////////////////////////////////////////////////////////////// |
| // |
| // Read grant checking |
| // |
| always @(*) |
| for(iM=0; iM<=NS; iM=iM+1) |
| begin |
| if (rgrant[N][iM]) |
| begin |
| assert((rgrant[N] ^ (1<<iM))==0); |
| assert(mrgrant[N]); |
| assert(mrindex[N] == iM); |
| if (iM < NS) |
| begin |
| assert(srgrant[iM]); |
| assert(srindex[iM] == N); |
| end |
| end |
| end |
| |
| always @(*) |
| if (mrgrant[N]) |
| assert(rgrant[N] != 0); |
| |
| always @(posedge S_AXI_ACLK) |
| if (S_AXI_ARESETN && f_past_valid && S_AXI_RVALID[N]) |
| begin |
| assert($stable(rgrant[N])); |
| assert($stable(mrindex[N])); |
| if (!rgrant[N][NS]) |
| assert(!mrempty[N]); |
| end |
| // }}} |
| end endgenerate |
| // }}} |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // AXI signaling check, (incoming) master side |
| // {{{ |
| //////////////////////////////////////////////////////////////////////// |
| // |
| generate for(N=0; N<NM; N=N+1) |
| begin : F2_CHECK_MASTERS |
| // {{{ |
| faxi_slave #( |
| .C_AXI_ID_WIDTH(IW), |
| .C_AXI_DATA_WIDTH(DW), |
| .C_AXI_ADDR_WIDTH(AW), |
| .F_OPT_ASSUME_RESET(1'b1), |
| .F_AXI_MAXSTALL(0), |
| .F_AXI_MAXRSTALL(2), |
| .F_AXI_MAXDELAY(0), |
| .F_OPT_READCHECK(0), |
| .F_OPT_NO_RESET(1), |
| .F_LGDEPTH(F_LGDEPTH)) |
| mstri(.i_clk(S_AXI_ACLK), |
| .i_axi_reset_n(S_AXI_ARESETN), |
| // |
| .i_axi_awid( S_AXI_AWID[ N*IW +:IW]), |
| .i_axi_awaddr( S_AXI_AWADDR[ N*AW +:AW]), |
| .i_axi_awlen( S_AXI_AWLEN[ N* 8 +: 8]), |
| .i_axi_awsize( S_AXI_AWSIZE[ N* 3 +: 3]), |
| .i_axi_awburst(S_AXI_AWBURST[N* 2 +: 2]), |
| .i_axi_awlock( S_AXI_AWLOCK[ N]), |
| .i_axi_awcache(S_AXI_AWCACHE[N* 4 +: 4]), |
| .i_axi_awprot( S_AXI_AWPROT[ N* 3 +: 3]), |
| .i_axi_awqos( S_AXI_AWQOS[ N* 4 +: 4]), |
| .i_axi_awvalid(S_AXI_AWVALID[N]), |
| .i_axi_awready(S_AXI_AWREADY[N]), |
| // |
| .i_axi_wdata( S_AXI_WDATA[ N*DW +: DW]), |
| .i_axi_wstrb( S_AXI_WSTRB[ N*DW/8 +: DW/8]), |
| .i_axi_wlast( S_AXI_WLAST[ N]), |
| .i_axi_wvalid(S_AXI_WVALID[N]), |
| .i_axi_wready(S_AXI_WREADY[N]), |
| // |
| .i_axi_bid( S_AXI_BID[ N*IW +:IW]), |
| .i_axi_bresp( S_AXI_BRESP[ N*2 +: 2]), |
| .i_axi_bvalid(S_AXI_BVALID[N]), |
| .i_axi_bready(S_AXI_BREADY[N]), |
| // |
| .i_axi_arid( S_AXI_ARID[ N*IW +:IW]), |
| .i_axi_arready(S_AXI_ARREADY[N]), |
| .i_axi_araddr( S_AXI_ARADDR[ N*AW +:AW]), |
| .i_axi_arlen( S_AXI_ARLEN[ N* 8 +: 8]), |
| .i_axi_arsize( S_AXI_ARSIZE[ N* 3 +: 3]), |
| .i_axi_arburst(S_AXI_ARBURST[N* 2 +: 2]), |
| .i_axi_arlock( S_AXI_ARLOCK[ N]), |
| .i_axi_arcache(S_AXI_ARCACHE[N* 4 +: 4]), |
| .i_axi_arprot( S_AXI_ARPROT[ N* 3 +: 3]), |
| .i_axi_arqos( S_AXI_ARQOS[ N* 4 +: 4]), |
| .i_axi_arvalid(S_AXI_ARVALID[N]), |
| // |
| // |
| .i_axi_rid( S_AXI_RID[ N*IW +: IW]), |
| .i_axi_rdata( S_AXI_RDATA[ N*DW +: DW]), |
| .i_axi_rresp( S_AXI_RRESP[ N* 2 +: 2]), |
| .i_axi_rlast( S_AXI_RLAST[ N]), |
| .i_axi_rvalid(S_AXI_RVALID[N]), |
| .i_axi_rready(S_AXI_RREADY[N]), |
| // |
| // ... |
| // |
| ); |
| |
| // |
| // ... |
| // |
| |
| // |
| // Check full/empty flags |
| // |
| |
| always @(*) |
| begin |
| assert(mwfull[N] == &w_mawpending[N]); |
| assert(mwempty[N] == (w_mawpending[N] == 0)); |
| end |
| |
| always @(*) |
| begin |
| assert(mrfull[N] == &w_mrpending[N]); |
| assert(mrempty[N] == (w_mrpending[N] == 0)); |
| end |
| // }}} |
| end endgenerate |
| // }}} |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // AXI signaling check, (outgoing) slave side |
| // {{{ |
| //////////////////////////////////////////////////////////////////////// |
| // |
| generate for(M=0; M<NS; M=M+1) |
| begin : F3_CHECK_SLAVES |
| // {{{ |
| faxi_master #( |
| .C_AXI_ID_WIDTH(IW), |
| .C_AXI_DATA_WIDTH(DW), |
| .C_AXI_ADDR_WIDTH(AW), |
| .F_OPT_ASSUME_RESET(1'b1), |
| .F_AXI_MAXSTALL(2), |
| .F_AXI_MAXRSTALL(0), |
| .F_AXI_MAXDELAY(2), |
| .F_OPT_READCHECK(0), |
| .F_OPT_NO_RESET(1), |
| .F_LGDEPTH(F_LGDEPTH)) |
| slvi(.i_clk(S_AXI_ACLK), |
| .i_axi_reset_n(S_AXI_ARESETN), |
| // |
| .i_axi_awid( M_AXI_AWID[ M*IW+:IW]), |
| .i_axi_awaddr( M_AXI_AWADDR[ M*AW +: AW]), |
| .i_axi_awlen( M_AXI_AWLEN[ M*8 +: 8]), |
| .i_axi_awsize( M_AXI_AWSIZE[ M*3 +: 3]), |
| .i_axi_awburst(M_AXI_AWBURST[M*2 +: 2]), |
| .i_axi_awlock( M_AXI_AWLOCK[ M]), |
| .i_axi_awcache(M_AXI_AWCACHE[M*4 +: 4]), |
| .i_axi_awprot( M_AXI_AWPROT[ M*3 +: 3]), |
| .i_axi_awqos( M_AXI_AWQOS[ M*4 +: 4]), |
| .i_axi_awvalid(M_AXI_AWVALID[M]), |
| .i_axi_awready(M_AXI_AWREADY[M]), |
| // |
| .i_axi_wready(M_AXI_WREADY[M]), |
| .i_axi_wdata( M_AXI_WDATA[ M*DW +: DW]), |
| .i_axi_wstrb( M_AXI_WSTRB[ M*DW/8 +: DW/8]), |
| .i_axi_wlast( M_AXI_WLAST[ M]), |
| .i_axi_wvalid(M_AXI_WVALID[M]), |
| // |
| .i_axi_bid( M_AXI_BID[ M*IW +: IW]), |
| .i_axi_bresp( M_AXI_BRESP[ M*2 +: 2]), |
| .i_axi_bvalid(M_AXI_BVALID[M]), |
| .i_axi_bready(M_AXI_BREADY[M]), |
| // |
| .i_axi_arid( M_AXI_ARID[ M*IW +:IW]), |
| .i_axi_araddr( M_AXI_ARADDR[ M*AW +:AW]), |
| .i_axi_arlen( M_AXI_ARLEN[ M*8 +: 8]), |
| .i_axi_arsize( M_AXI_ARSIZE[ M*3 +: 3]), |
| .i_axi_arburst(M_AXI_ARBURST[M*2 +: 2]), |
| .i_axi_arlock( M_AXI_ARLOCK[ M]), |
| .i_axi_arcache(M_AXI_ARCACHE[M* 4 +: 4]), |
| .i_axi_arprot( M_AXI_ARPROT[ M* 3 +: 3]), |
| .i_axi_arqos( M_AXI_ARQOS[ M* 4 +: 4]), |
| .i_axi_arvalid(M_AXI_ARVALID[M]), |
| .i_axi_arready(M_AXI_ARREADY[M]), |
| // |
| // |
| .i_axi_rresp( M_AXI_RRESP[ M*2 +: 2]), |
| .i_axi_rvalid(M_AXI_RVALID[M]), |
| .i_axi_rdata( M_AXI_RDATA[ M*DW +: DW]), |
| .i_axi_rready(M_AXI_RREADY[M]), |
| .i_axi_rlast( M_AXI_RLAST[ M]), |
| .i_axi_rid( M_AXI_RID[ M*IW +: IW]), |
| // |
| // ... |
| // |
| ); |
| |
| // |
| // ... |
| // |
| |
| always @(*) |
| if (M_AXI_AWVALID[M]) |
| assert(((M_AXI_AWADDR[M*AW +:AW]^SLAVE_ADDR[M*AW +:AW]) |
| & SLAVE_MASK[M*AW +: AW]) == 0); |
| |
| always @(*) |
| if (M_AXI_ARVALID[M]) |
| assert(((M_AXI_ARADDR[M*AW +:AW]^SLAVE_ADDR[M*AW +:AW]) |
| & SLAVE_MASK[M*AW +: AW]) == 0); |
| // }}} |
| end endgenerate |
| // }}} |
| |
| // m_axi_* convenience signals |
| // {{{ |
| // ... |
| // }}} |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // ... |
| // {{{ |
| //////////////////////////////////////////////////////////////////////// |
| // |
| generate for(N=0; N<NM; N=N+1) |
| begin : // ... |
| // {{{ |
| // }}} |
| end endgenerate |
| // }}} |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Double buffer checks |
| // {{{ |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // |
| generate for(N=0; N<NM; N=N+1) |
| begin : F4_DOUBLE_BUFFER_CHECKS |
| // {{{ |
| // ... |
| // }}} |
| end endgenerate |
| // }}} |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Cover properties |
| // {{{ |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Can every master reach every slave? |
| // Can things transition without dropping the request line(s)? |
| generate for(N=0; N<NM; N=N+1) |
| begin : F5_COVER_CONNECTIVITY_FROM_MASTER |
| // {{{ |
| // ... |
| // }}} |
| end endgenerate |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Focused check: How fast can one master talk to each of the slaves? |
| // {{{ |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // |
| // ... |
| // }}} |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Focused check: How fast can one master talk to a particular slave? |
| // We'll pick master 1 and slave 1. |
| // {{{ |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // |
| // ... |
| // }}} |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Poor man's cover check |
| // {{{ |
| // ... |
| // }}} |
| // }}} |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Negation check |
| // {{{ |
| // Pick a particular value. Assume the value doesn't show up on the |
| // input. Prove it doesn't show up on the output. This will check for |
| // ... |
| // 1. Stuck bits on the output channel |
| // 2. Cross-talk between channels |
| // |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // |
| // ... |
| // }}} |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Artificially constraining assumptions |
| // {{{ |
| // Ideally, this section should be empty--there should be no |
| // assumptions here. The existence of these assumptions should |
| // give you an idea of where I'm at with this project. |
| // |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // |
| generate for(N=0; N<NM; N=N+1) |
| begin : F6_LIMITING_ASSUMPTIONS |
| |
| if (!OPT_WRITES) |
| begin |
| always @(*) |
| assume(S_AXI_AWVALID[N] == 0); |
| always @(*) |
| assert(wgrant[N] == 0); |
| always @(*) |
| assert(mwgrant[N] == 0); |
| always @(*) |
| assert(S_AXI_BVALID[N]== 0); |
| end |
| |
| if (!OPT_READS) |
| begin |
| always @(*) |
| assume(S_AXI_ARVALID [N]== 0); |
| always @(*) |
| assert(rgrant[N] == 0); |
| always @(*) |
| assert(S_AXI_RVALID[N] == 0); |
| end |
| |
| end endgenerate |
| |
| always@(*) |
| assert(OPT_READS | OPT_WRITES); |
| // }}} |
| `endif |
| // }}} |
| endmodule |