diff options
Diffstat (limited to 'rtl/wb2axip/axixbar.v')
| -rw-r--r-- | rtl/wb2axip/axixbar.v | 2585 |
1 files changed, 2585 insertions, 0 deletions
diff --git a/rtl/wb2axip/axixbar.v b/rtl/wb2axip/axixbar.v new file mode 100644 index 0000000..f2e1ac6 --- /dev/null +++ b/rtl/wb2axip/axixbar.v @@ -0,0 +1,2585 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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. +// +// Known issues: This module can be a challenge to wire up. +// +// In order to keep the build lint clean, it's important that every +// port be connected. In order to be flexible regarding the number of +// ports that can be connected, the various AXI signals, whether input +// or output, have been concatenated together across either all masters +// or all slaves. This can make the design a lesson in tediousness to +// wire up. +// +// I commonly wire this crossbar up using AutoFPGA--just to make certain +// that I do it right and don't make mistakes when wiring it up. This +// also handles the tediousness involved. +// +// I have also done this by hand. +// +// +// 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 +// }}} +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, driven + // by the master channels output from this IP. + parameter NS = 8, + // + // 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) + // + // NOTE: This is only a nominal address set. I expect that + // any design using the crossbar will need to adjust both + // SLAVE_ADDR and SLAVE_MASK, if not also NM and NS. + 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}} }, + // }}} + // + // 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. + // + // NOTE: This value should be overridden by any implementation. + // Verilator lint_off WIDTH + parameter [NS*C_AXI_ADDR_WIDTH-1:0] SLAVE_MASK = { + 3'b111, {(C_AXI_ADDR_WIDTH-3){1'b0}}, + 3'b111, {(C_AXI_ADDR_WIDTH-3){1'b0}}, + 3'b111, {(C_AXI_ADDR_WIDTH-3){1'b0}}, + 3'b111, {(C_AXI_ADDR_WIDTH-3){1'b0}}, + 3'b111, {(C_AXI_ADDR_WIDTH-3){1'b0}}, + 3'b111, {(C_AXI_ADDR_WIDTH-3){1'b0}}, + 4'b1111, {(C_AXI_ADDR_WIDTH-4){1'b0}}, + 4'b1111, {(C_AXI_ADDR_WIDTH-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-1:0] S_AXI_AWVALID, + output wire [NM-1:0] S_AXI_AWREADY, + 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, + // Verilator coverage_off + 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, + // Verilator coverage_on + // + input wire [NM-1:0] S_AXI_WVALID, + output wire [NM-1:0] S_AXI_WREADY, + 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, + // + output wire [NM-1:0] S_AXI_BVALID, + input wire [NM-1:0] S_AXI_BREADY, + output wire [NM*C_AXI_ID_WIDTH-1:0] S_AXI_BID, + output wire [NM*2-1:0] S_AXI_BRESP, + // }}} + // Read slave channels from the controlling AXI masters + // {{{ + input wire [NM-1:0] S_AXI_ARVALID, + output wire [NM-1:0] S_AXI_ARREADY, + 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, + // Verilator coverage_off + 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, + // Verilator coverage_on + // + output wire [NM-1:0] S_AXI_RVALID, + input wire [NM-1:0] S_AXI_RREADY, + 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, + // }}} + // Write channel master outputs to the connected AXI slaves + // {{{ + output wire [NS-1:0] M_AXI_AWVALID, + input wire [NS-1:0] M_AXI_AWREADY, + 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, + // Verilator coverage_off + 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, + // Verilator coverage_on + // + // + output wire [NS-1:0] M_AXI_WVALID, + input wire [NS-1:0] M_AXI_WREADY, + 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, + // + input wire [NS-1:0] M_AXI_BVALID, + output wire [NS-1:0] M_AXI_BREADY, + input wire [NS*C_AXI_ID_WIDTH-1:0] M_AXI_BID, + input wire [NS*2-1:0] M_AXI_BRESP, + // }}} + // Read channel master outputs to the connected AXI slaves + // {{{ + output wire [NS-1:0] M_AXI_ARVALID, + input wire [NS-1:0] M_AXI_ARREADY, + 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, + // Verilator coverage_off + 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, + // Verilator coverage_on + // + // + input wire [NS-1:0] M_AXI_RVALID, + output wire [NS-1:0] M_AXI_RREADY, + 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 + // }}} + // }}} + ); + + //////////////////////////////////////////////////////////////////////// + // + // Internal signal declarations and definitions + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + + // + // Local parameters, derived from those above + // {{{ + // 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; + // LGLINGER tells us how many bits we need for counting how long + // to keep an udle channel open. + 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; + // }}} + + 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; + // + wire [LGNS-1:0] mwindex [0:NMFULL-1]; + wire [LGNS-1:0] mrindex [0:NMFULL-1]; + wire [LGNM-1:0] swindex [0:NSFULL-1]; + wire [LGNM-1:0] srindex [0:NSFULL-1]; + + wire [NM-1:0] wdata_expected; + + // The shadow buffers + wire [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; + + wire [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( + // {{{ + .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN), + .i_valid(S_AXI_AWVALID[N]), .o_ready(S_AXI_AWREADY[N]), + .i_data( + { 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] }), + .o_valid(skd_awvalid[N]), .i_ready(!skd_awstall[N]), + .o_data( + { 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( + // {{{ + .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN), + .i_valid(S_AXI_WVALID[N]), .o_ready(S_AXI_WREADY[N]), + .i_data( + { S_AXI_WDATA[N*DW +: DW], S_AXI_WSTRB[N*DW/8 +: DW/8], + S_AXI_WLAST[N] }), + .o_valid(m_wvalid[N]), .i_ready(slave_waccepts[N]), + .o_data({ 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 + // }}} + + reg r_awvalid; + + always @(*) + begin + r_awvalid = dcd_awvalid[N] && !mwfull[N]; + wrequest[N]= 0; + if (!mwfull[N]) + wrequest[N][NS:0] = wdecode; + end + + assign m_awvalid[N] = r_awvalid; + + // 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 + assign 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 + // {{{ + reg r_write_qos_lockout; + + initial r_write_qos_lockout = 0; + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN) + r_write_qos_lockout <= 0; + else begin + r_write_qos_lockout <= 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])) + r_write_qos_lockout <= 1; + end + end + + assign write_qos_lockout[N] = r_write_qos_lockout; + // }}} + 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; + + assign m_awvalid[N] = 0; + + assign m_wvalid[N] = 0; + // + assign m_wdata[N] = 0; + assign m_wstrb[N] = 0; + assign m_wlast[N] = 0; + + assign 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 + // {{{ + reg r_arvalid; + wire [NS:0] rdecode; + + // arskid + // {{{ + skidbuffer #( + // {{{ + .DW(IW+AW+8+3+2+1+4+3+4), + .OPT_OUTREG(OPT_SKID_INPUT) + // }}} + ) arskid( + // {{{ + .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN), + .i_valid(S_AXI_ARVALID[N]), .o_ready(S_AXI_ARREADY[N]), + .i_data( + { 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] }), + .o_valid(skd_arvalid[N]), .i_ready(!skd_arstall[N]), + .o_data( + { 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 + r_arvalid = dcd_arvalid[N] && !mrfull[N]; + rrequest[N] = 0; + if (!mrfull[N]) + rrequest[N][NS:0] = rdecode; + end + + assign m_arvalid[N] = r_arvalid; + + // 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 + assign 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. + reg r_read_qos_lockout; + + initial r_read_qos_lockout = 0; + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN) + r_read_qos_lockout <= 0; + else begin + r_read_qos_lockout <= 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])) + r_read_qos_lockout <= 1; + end + end + + assign read_qos_lockout[N] = 0; + // }}} + end + // }}} + + end for (N=NM; N<NMFULL; N=N+1) + begin : UNUSED_RSKID_BUFFERS + // {{{ + assign 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; + + assign 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; + wire linger; + reg [LGNS-1:0] r_mwindex; + + // 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 : NO_LINGER + assign linger = 0; + end else begin : WRITE_LINGER + + reg [LGLINGER-1:0] linger_counter; + reg r_linger; + + initial r_linger = 0; + initial linger_counter = 0; + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN || wgrant[N][NS]) + begin + r_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; + r_linger <= 1; + end else if (linger_counter > 0) + begin + // Otherwise, we decrement it until it reaches + // zero + r_linger <= (linger_counter > 1); + linger_counter <= linger_counter - 1; + end else + r_linger <= 0; + + assign linger = r_linger; + 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 @(wrequest[N]) + 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 r_mwindex = 0; + always @(posedge S_AXI_ACLK) + if (!stay_on_channel && requested_channel_is_available) + r_mwindex <= requested_index; + + assign mwindex[N] = r_mwindex; + // }}} + + end for (N=NM; N<NMFULL; N=N+1) + begin : EMPTY_WRITE_REQUEST + + assign 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; + wire linger; + reg [LGNS-1:0] r_mrindex; + + + // 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 : NO_LINGER + assign linger = 0; + end else begin : READ_LINGER + reg r_linger; + reg [LGLINGER-1:0] linger_counter; + + initial r_linger = 0; + initial linger_counter = 0; + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN || rgrant[N][NS]) + begin + r_linger <= 0; + linger_counter <= 0; + end else if (!mrempty[N] || rskd_valid[N]) + begin + linger_counter <= OPT_LINGER; + r_linger <= 1; + end else if (linger_counter > 0) + begin + r_linger <= (linger_counter > 1); + linger_counter <= linger_counter - 1; + end else + r_linger <= 0; + + assign linger = r_linger; + 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 @(rrequest[N]) + 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 r_mrindex = 0; + always @(posedge S_AXI_ACLK) + if (!stay_on_channel && requested_channel_is_available) + r_mrindex <= requested_index; + + assign mrindex[N] = r_mrindex; + // }}} + + end for (N=NM; N<NMFULL; N=N+1) + begin : EMPTY_READ_REQUEST + + assign 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 : ONE_MASTER + + // If there's only ever one master, that index is + // always the index of the one master. + assign swindex[M] = 0; + + end else begin : MULTIPLE_MASTERS + + reg [LGNM-1:0] reqwindex, r_swindex; + + // 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]) + r_swindex <= reqwindex; + + assign swindex[M] = r_swindex; + end + + end for (M=NS; M<NSFULL; M=M+1) + begin : EMPTY_WRITE_INDEX + + assign 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 : ONE_MASTER + // If there's only one master, srindex can always + // point to that master--no longic required + assign srindex[M] = 0; + + end else begin : MULTIPLE_MASTERS + + reg [LGNM-1:0] reqrindex, r_srindex; + + // 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]) + r_srindex <= reqrindex; + + assign srindex[M] = r_srindex; + end + + end for (M=NS; M<NSFULL; M=M+1) + begin : EMPTY_READ_INDEX + + assign 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 + initial swgrant = 0; + always @(*) + begin + swgrant[M] = 0; + for(iN=0; iN<NM; iN=iN+1) + if (wgrant[iN][M]) + swgrant[M] = 1; + end + + initial srgrant = 0; + // 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; + wire arstall; + + assign 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( + // {{{ + .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN), + .i_valid(bskd_valid[N]), .o_ready(bskd_ready[N]), + .i_data({ i_axi_bid, i_axi_bresp }), + .o_valid(S_AXI_BVALID[N]), .i_ready(S_AXI_BREADY[N]), + .o_data({ 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( + // {{{ + .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN), + .i_valid(rskd_valid[N]), .o_ready(rskd_ready[N]), + .i_data({ i_axi_rid, i_axi_rdata, rskd_rlast[N], i_axi_rresp }), + .o_valid(S_AXI_RVALID[N]), .i_ready(S_AXI_RREADY[N]), + .o_data( + { 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 + + assign 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 |
