diff options
Diffstat (limited to 'rtl/wb2axip/wbxbar.v')
| -rw-r--r-- | rtl/wb2axip/wbxbar.v | 1790 |
1 files changed, 1790 insertions, 0 deletions
diff --git a/rtl/wb2axip/wbxbar.v b/rtl/wb2axip/wbxbar.v new file mode 100644 index 0000000..15d6587 --- /dev/null +++ b/rtl/wb2axip/wbxbar.v @@ -0,0 +1,1790 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Filename: wbxbar.v +// {{{ +// Project: WB2AXIPSP: bus bridges and other odds and ends +// +// Purpose: A Configurable wishbone cross-bar interconnect, conforming +// to the WB-B4 pipeline specification, as described on the +// ZipCPU blog. +// +// Performance: +// Throughput: One transaction per clock +// Latency: One clock to get access to an unused channel, another to +// place the results on the slave bus, and another to return, or a minimum +// of three clocks. +// +// Usage: To use, you'll need to set NM and NS to the number of masters +// (input ports) and the number of slaves respectively. You'll then +// want to set the addresses for the slaves in the SLAVE_ADDR array, +// together with the SLAVE_MASK array indicating which SLAVE_ADDRs +// are valid. Address and data widths should be adjusted at the same +// time. +// +// Voila, you are now set up! +// +// Now let's fine tune this: +// +// LGMAXBURST can be set to control the maximum number of outstanding +// transactions. An LGMAXBURST of 6 will allow 63 outstanding +// transactions. +// +// OPT_TIMEOUT, if set to a non-zero value, is a number of clock periods +// to wait for a slave to respond. Should the timeout expire and the +// slave not respond, a bus error will be returned and the slave will +// be issued a bus abort signal (CYC will be dropped). +// +// OPT_STARVATION_TIMEOUT, if set, applies the OPT_TIMEOUT counter to +// how long a particular master waits for arbitration. If the master is +// "starved", a bus error will be returned. +// +// OPT_DBLBUFFER is used to increase clock speed by registering all +// outputs. +// +// OPT_LOWPOWER is an experimental feature that, if set, will cause any +// unused FFs to be set to zero rather than flopping in the electronic +// wind, in an effort to minimize transitions over bus wires. This will +// cost some extra logic, for ... an uncertain power savings. +// +// +// 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 wbxbar #( + // {{{ + parameter NM = 4, NS=8, + parameter AW = 32, DW=32, + 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'b0010, {(AW-4){1'b0}} }, + { 4'b0000, {(AW-4){1'b0}} } }, + parameter [NS*AW-1:0] SLAVE_MASK = (NS <= 1) ? 0 + : { {(NS-2){ 3'b111, {(AW-3){1'b0}} }}, + {(2){ 4'b1111, {(AW-4){1'b0}} }} }, + // + // LGMAXBURST is the log_2 of the length of the longest burst + // that might be seen. It's used to set the size of the + // internal counters that are used to make certain that the + // cross bar doesn't switch while still waiting on a response. + parameter LGMAXBURST=6, + // + // OPT_TIMEOUT is used to help recover from a misbehaving slave. + // If set, this value will determine the number of clock cycles + // to wait for a misbehaving slave before returning a bus error. + // Alternatively, if set to zero, this functionality will be + // removed. + parameter OPT_TIMEOUT = 0, // 1023; + // + // If OPT_TIMEOUT is set, then OPT_STARVATION_TIMEOUT may also + // be set. The starvation timeout adds to the bus error timeout + // generation the possibility that a master will wait + // OPT_TIMEOUT counts without receiving the bus. This may be + // the case, for example, if one bus master is consuming a + // peripheral to such an extent that there's no time/room for + // another bus master to use it. In that case, when the timeout + // runs out, the waiting bus master will be given a bus error. + parameter [0:0] OPT_STARVATION_TIMEOUT = 1'b0 + && (OPT_TIMEOUT > 0), + // + // OPT_DBLBUFFER is used to register all of the outputs, and + // thus avoid adding additional combinational latency through + // the core that might require a slower clock speed. + parameter [0:0] OPT_DBLBUFFER = 1'b0, + // + // OPT_LOWPOWER adds logic to try to force unused values to + // zero, rather than to allow a variety of logic optimizations + // that could be used to reduce the logic count of the device. + // Hence, OPT_LOWPOWER will use more logic, but it won't drive + // bus wires unless there's a value to drive onto them. + parameter [0:0] OPT_LOWPOWER = 1'b1 + // }}} + ) ( + // {{{ + input wire i_clk, i_reset, + // + // Here are the bus inputs from each of the WB bus masters + input wire [NM-1:0] i_mcyc, i_mstb, i_mwe, + input wire [NM*AW-1:0] i_maddr, + input wire [NM*DW-1:0] i_mdata, + input wire [NM*DW/8-1:0] i_msel, + // + // .... and their return data + output wire [NM-1:0] o_mstall, + output wire [NM-1:0] o_mack, + output reg [NM*DW-1:0] o_mdata, + output wire [NM-1:0] o_merr, + // + // + // Here are the output ports, used to control each of the + // various slave ports that we are connected to + output reg [NS-1:0] o_scyc, o_sstb, o_swe, + output reg [NS*AW-1:0] o_saddr, + output reg [NS*DW-1:0] o_sdata, + output reg [NS*DW/8-1:0] o_ssel, + // + // ... and their return data back to us. + input wire [NS-1:0] i_sstall, i_sack, + input wire [NS*DW-1:0] i_sdata, + input wire [NS-1:0] i_serr + // }}} + ); + // + // + //////////////////////////////////////////////////////////////////////// + // + // Register declarations + // {{{ + // + // TIMEOUT_WIDTH is the number of bits in counter used to check + // on a timeout. + localparam TIMEOUT_WIDTH = $clog2(OPT_TIMEOUT); + // + // LGNM is the log (base two) of the number of bus masters + // connecting to this crossbar + localparam LGNM = (NM>1) ? $clog2(NM) : 1; + // + // LGNS is the log (base two) of the number of slaves plus one + // come out of the system. The extra "plus one" is used for a + // pseudo slave representing the case where the given address + // doesn't connect to any of the slaves. This address will + // generate a bus error. + localparam LGNS = $clog2(NS+1); + // At one time I used o_macc and o_sacc to put into the outgoing + // trace file, just enough logic to tell me if a transaction was + // taking place on the given clock. + // + // assign o_macc = (i_mstb & ~o_mstall); + // assign o_sacc = (o_sstb & ~i_sstall); + // + // These definitions work with Veri1ator, just not with Yosys + // reg [NM-1:0][NS:0] request; + // reg [NM-1:0][NS-1:0] requested; + // reg [NM-1:0][NS:0] grant; + // + // These definitions work with both + wire [NS:0] request [0:NM-1]; + reg [NS-1:0] requested [0:NM-1]; + reg [NS:0] grant [0:NM-1]; + reg [NM-1:0] mgrant; + reg [NS-1:0] sgrant; + + // Verilator lint_off UNUSED + wire [LGMAXBURST-1:0] w_mpending [0:NM-1]; + // Verilator lint_on UNUSED + reg [NM-1:0] mfull, mnearfull, mempty; + wire [NM-1:0] timed_out; + + localparam NMFULL = (NM > 1) ? (1<<LGNM) : 1; + localparam NSFULL = (1<<LGNS); + + wire [LGNS-1:0] mindex [0:NMFULL-1]; + wire [LGNM-1:0] sindex [0:NSFULL-1]; + + wire [NMFULL-1:0] m_cyc; + wire [NMFULL-1:0] m_stb; + wire [NMFULL-1:0] m_we; + wire [AW-1:0] m_addr [0:NMFULL-1]; + wire [DW-1:0] m_data [0:NMFULL-1]; + wire [DW/8-1:0] m_sel [0:NMFULL-1]; + reg [NM-1:0] m_stall; + // + wire [NSFULL-1:0] s_stall; + wire [DW-1:0] s_data [0:NSFULL-1]; + wire [NSFULL-1:0] s_ack; + wire [NSFULL-1:0] s_err; + wire [NM-1:0] dcd_stb; + + localparam [0:0] OPT_BUFFER_DECODER=(NS != 1 || SLAVE_MASK != 0); + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Incoming signal arbitration + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + + genvar N, M; + integer iN, iM; + generate for(N=0; N<NM; N=N+1) + begin : DECODE_REQUEST + // {{{ + // Register declarations + // {{{ + wire skd_stb, skd_stall; + wire skd_we; + wire [AW-1:0] skd_addr; + wire [DW-1:0] skd_data; + wire [DW/8-1:0] skd_sel; + wire [NS:0] decoded; + wire iskd_ready; + // }}} + + skidbuffer #( + // {{{ + // Can't run OPT_LOWPOWER here, less we mess up the + // consistency in skd_we following + // + // .OPT_LOWPOWER(OPT_LOWPOWER), + .DW(1+AW+DW+DW/8), +`ifdef FORMAL + .OPT_PASSTHROUGH(1), +`endif + .OPT_OUTREG(0) + // }}} + ) iskid ( + // {{{ + .i_clk(i_clk), + .i_reset(i_reset || !i_mcyc[N]), + .i_valid(i_mstb[N]), .o_ready(iskd_ready), + .i_data({ i_mwe[N], i_maddr[N*AW +: AW], + i_mdata[N*DW +: DW], + i_msel[N*DW/8 +: DW/8] }), + .o_valid(skd_stb), .i_ready(!skd_stall), + .o_data({ skd_we, skd_addr, skd_data, skd_sel }) + // }}} + ); + + assign o_mstall[N] = !iskd_ready; + + addrdecode #( + // {{{ + // Can't run OPT_LOWPOWER here, less we mess up the + // consistency in m_we following + // + // .OPT_LOWPOWER(OPT_LOWPOWER), + .NS(NS), .AW(AW), .DW(DW+DW/8+1), + .SLAVE_ADDR(SLAVE_ADDR), + .SLAVE_MASK(SLAVE_MASK), + .OPT_REGISTERED(OPT_BUFFER_DECODER) + // }}} + ) adcd( + // {{{ + .i_clk(i_clk), .i_reset(i_reset), + .i_valid(skd_stb && i_mcyc[N]), .o_stall(skd_stall), + .i_addr(skd_addr), + .i_data({ skd_we, skd_data, skd_sel }), + .o_valid(dcd_stb[N]), .i_stall(m_stall[N]&&i_mcyc[N]), + .o_decode(decoded), .o_addr(m_addr[N]), + .o_data({ m_we[N], m_data[N], m_sel[N] }) + // }}} + ); + + assign request[N] = (m_cyc[N] && dcd_stb[N]) ? decoded : 0; + + assign m_cyc[N] = i_mcyc[N]; + assign m_stb[N] = i_mcyc[N] && dcd_stb[N] && !mfull[N]; + // }}} + end for(N=NM; N<NMFULL; N=N+1) + begin : UNUSED_MASTER_SIGNALS + // {{{ + // in case NM isn't one less than a power of two, complete + // the set + assign m_cyc[N] = 0; + assign m_stb[N] = 0; + + assign m_we[N] = 0; + assign m_addr[N] = 0; + assign m_data[N] = 0; + assign m_sel[N] = 0; + // }}} + end endgenerate + + // requested + // {{{ + always @(*) + begin + for(iM=0; iM<NS; iM=iM+1) + begin + // For each slave + requested[0][iM] = 0; + for(iN=1; iN<NM; iN=iN+1) + begin + // This slave has been requested if a prior + // master has requested it + // + // This includes any master before the last one + requested[iN][iM] = requested[iN-1][iM]; + // + // As well as if the last master has requested + // this slave. Only count this request, though, + // if this master could act upon it. + if (request[iN-1][iM] && + (grant[iN-1][iM] + || (!mgrant[iN-1]||mempty[iN-1]))) + requested[iN][iM] = 1; + end + end + end + // }}} + + generate for(M=0; M<NS; M=M+1) + begin : SLAVE_GRANT + // {{{ +`define REGISTERED_SGRANT +`ifdef REGISTERED_SGRANT + // {{{ + reg drop_sgrant; + + // drop_sgrant + // {{{ + always @(*) + begin + drop_sgrant = !m_cyc[sindex[M]]; + if (!request[sindex[M]][M] && m_stb[sindex[M]] + && mempty[sindex[M]]) + drop_sgrant = 1; + if (!sgrant[M]) + drop_sgrant = 0; + if (i_reset) + drop_sgrant = 1; + end + // }}} + + // sgrant + // {{{ + initial sgrant[M] = 0; + always @(posedge i_clk) + begin + sgrant[M] <= sgrant[M]; + for(iN=0; iN<NM; iN=iN+1) + if (request[iN][M] && (!mgrant[iN] || mempty[iN])) + sgrant[M] <= 1; + if (drop_sgrant) + sgrant[M] <= 0; + end + // }}} + // }}} +`else + // {{{ + // sgrant + // {{{ + always @(*) + begin + sgrant[M] = 0; + for(iN=0; iN<NM; iN=iN+1) + if (grant[iN][M]) + sgrant[M] = 1; + end + // }}} + // }}} +`endif + + assign s_data[M] = i_sdata[M*DW +: DW]; + assign s_stall[M] = o_sstb[M] && i_sstall[M]; + assign s_ack[M] = o_scyc[M] && i_sack[M]; + assign s_err[M] = o_scyc[M] && i_serr[M]; + + // }}} + end for(M=NS; M<NSFULL; M=M+1) + begin : UNUSED_SLAVE_SIGNALS + // {{{ + assign s_data[M] = 0; + assign s_stall[M] = 1; + assign s_ack[M] = 0; + assign s_err[M] = 1; + // }}} + end endgenerate + + // + // Arbitrate among masters to determine who gets to access a given + // channel + generate for(N=0; N<NM; N=N+1) + begin : ARBITRATE_REQUESTS + // {{{ + + // Register declarations + // {{{ + wire [NS:0] regrant; + wire [LGNS-1:0] reindex; + + // This is done using a couple of variables. + // + // request[N][M] + // This is true if master N is requesting to access slave + // M. It is combinatorial, so it will be true if the + // request is being made on the current clock. + // + // requested[N][M] + // True if some other master, prior to N, has requested + // channel M. This creates a basic priority arbiter, + // such that lower numbered masters have access before + // a greater numbered master + // + // grant[N][M] + // True if a grant has been made for master N to access + // slave channel M + // + // mgrant[N] + // True if master N has been granted access to some slave + // channel, any channel. + // + // mindex[N] + // This is the number of the slave channel that master + // N has been given access to + // + // sgrant[M] + // True if there exists some master, N, that has been + // granted access to this slave, hence grant[N][M] must + // also be true + // + // sindex[M] + // This is the index of the master that has access to + // slave M, assuming sgrant[M]. Hence, if sgrant[M] + // then grant[sindex[M]][M] must be true + // + reg stay_on_channel; + reg requested_channel_is_available; + // }}} + + // stay_on_channel + // {{{ + always @(*) + begin + stay_on_channel = |(request[N] & grant[N]); + + if (mgrant[N] && !mempty[N]) + stay_on_channel = 1; + end + // }}} + + // requested_channel_is_available + // {{{ + always @(*) + begin + requested_channel_is_available = + |(request[N][NS-1:0]& ~sgrant & ~requested[N][NS-1:0]); + + if (request[N][NS]) + requested_channel_is_available = 1; + + if (NM < 2) + requested_channel_is_available = m_stb[N]; + end + // }}} + + // grant, mgrant + // {{{ + initial grant[N] = 0; + initial mgrant[N] = 0; + always @(posedge i_clk) + if (i_reset || !i_mcyc[N]) + begin + grant[N] <= 0; + mgrant[N] <= 0; + end else if (!stay_on_channel) + begin + if (requested_channel_is_available) + begin + mgrant[N] <= 1'b1; + grant[N] <= request[N]; + end else if (m_stb[N]) + begin + mgrant[N] <= 1'b0; + grant[N] <= 0; + end + end + // }}} + + if (NS == 1) + begin : MINDEX_ONE_SLAVE + // {{{ + assign mindex[N] = 0; + assign regrant = 0; + assign reindex = 0; + // }}} + end else begin : MINDEX_MULTIPLE_SLAVES + // {{{ + reg [LGNS-1:0] r_mindex; + +`define NEW_MINDEX_CODE +`ifdef NEW_MINDEX_CODE + // {{{ + reg [NS:0] r_regrant; + reg [LGNS-1:0] r_reindex; + + // r_regrant + // {{{ + always @(*) + begin + r_regrant = 0; + for(iM=0; iM<NS; iM=iM+1) + begin + if (grant[N][iM]) + // Maintain any open channels + r_regrant[iM] = 1'b1; + else if (!sgrant[iM]&&!requested[N][iM]) + r_regrant[iM] = 1'b1; + + if (!request[N][iM]) + r_regrant[iM] = 1'b0; + end + + if (grant[N][NS]) + r_regrant[NS] = 1; + if (!request[N][NS]) + r_regrant[NS] = 0; + + if (mgrant[N] && !mempty[N]) + r_regrant = 0; + end + // }}} + + // r_reindex + // {{{ + // Verilator lint_off BLKSEQ + always @(r_regrant, regrant) + begin + r_reindex = 0; + for(iM=0; iM<=NS; iM=iM+1) + if (r_regrant[iM]) + r_reindex = r_reindex | iM[LGNS-1:0]; + if (regrant == 0) + r_reindex = r_mindex; + end + // Verilator lint_on BLKSEQ + // }}} + + always @(posedge i_clk) + r_mindex <= reindex; + + assign reindex = r_reindex; + assign regrant = r_regrant; + // }}} +`else + // {{{ + always @(posedge i_clk) + if (!mgrant[N] || mempty[N]) + begin + + for(iM=0; iM<NS; iM=iM+1) + begin + if (request[N][iM] && grant[N][iM]) + begin + // Maintain any open channels + r_mindex <= iM; + end else if (request[N][iM] + && !sgrant[iM] + && !requested[N][iM]) + begin + // Open a new channel + // if necessary + r_mindex <= iM; + end + end + end + + // }}} +`endif // NEW_MINDEX_CODE + assign mindex[N] = r_mindex; + // }}} + end + // }}} + end for (N=NM; N<NMFULL; N=N+1) + begin : UNUSED_MINDEXES + // {{{ + assign mindex[N] = 0; + // }}} + end endgenerate + + // Calculate sindex. sindex[M] (indexed by slave ID) + // references the master controlling this slave. This makes for + // faster/cheaper logic on the return path, since we can now use + // a fully populated LUT rather than a priority based return scheme + generate for(M=0; M<NS; M=M+1) + begin : GEN_SINDEX + // {{{ + if (NM <= 1) + begin : SINDEX_SINGLE_MASTER + // {{{ + // If there will only ever be one master, then we + // can assume all slave indexes point to that master + assign sindex[M] = 0; + // }}} + end else begin : SINDEX_MORE_THAN_ONE_MASTER + // {{{ + reg [LGNM-1:0] r_sindex; +`define NEW_SINDEX_CODE +`ifdef NEW_SINDEX_CODE + // {{{ + reg [NM-1:0] regrant; + reg [LGNM-1:0] reindex; + + always @(*) + begin + regrant = 0; + for (iN=0; iN<NM; iN=iN+1) + begin + // Each bit depends upon 6 inputs, so + // one 6-LUT should be sufficient + if (grant[iN][M]) + regrant[iN] = 1; + else if (!sgrant[M]&& !requested[iN][M]) + regrant[iN] = 1; + + if (!request[iN][M]) + regrant[iN] = 0; + if (mgrant[iN] && !mempty[iN]) + regrant[iN] = 0; + end + end + + always @(*) + begin + reindex = 0; + // Each bit in reindex depends upon all of the + // bits in regrant--should still be one LUT + // per bit though + if (regrant == 0) + reindex = sindex[M]; + else for(iN=0; iN<NM; iN=iN+1) + if (regrant[iN]) + reindex = reindex | iN[LGNM-1:0]; + end + + always @(posedge i_clk) + r_sindex <= reindex; + + assign sindex[M] = r_sindex; + // }}} +`else + // {{{ + always @(posedge i_clk) + for (iN=0; iN<NM; iN=iN+1) + begin + if (!mgrant[iN] || mempty[iN]) + begin + if (request[iN][M] && grant[iN][M]) + r_sindex <= iN; + else if (request[iN][M] && !sgrant[M] + && !requested[iN][M]) + r_sindex <= iN; + end + end + + assign sindex[M] = r_sindex; + // }}} +`endif + // }}} + end + // }}} + end for(M=NS; M<NSFULL; M=M+1) + begin : UNUSED_SINDEXES + // {{{ + // Assign the unused slave indexes to zero + // + // Remember, to full out a full 2^something set of slaves, + // we may have more slave indexes than we actually have slaves + + assign sindex[M] = 0; + // }}} + end endgenerate + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Assign outputs to the slaves + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + + // + // Part one + // + // In this part, we assign the difficult outputs: o_scyc and o_sstb + generate for(M=0; M<NS; M=M+1) + begin : GEN_CYC_STB + // {{{ + initial o_scyc[M] = 0; + initial o_sstb[M] = 0; + always @(posedge i_clk) + begin + if (sgrant[M]) + begin + + if (!i_mcyc[sindex[M]]) + begin + o_scyc[M] <= 1'b0; + o_sstb[M] <= 1'b0; + end else begin + o_scyc[M] <= 1'b1; + + if (!o_sstb[M] || !s_stall[M]) + o_sstb[M]<=request[sindex[M]][M] + && !mfull[sindex[M]]; + end + end else begin + o_scyc[M] <= 1'b0; + o_sstb[M] <= 1'b0; + end + + if (i_reset || s_err[M]) + begin + o_scyc[M] <= 1'b0; + o_sstb[M] <= 1'b0; + end + end + // }}} + end endgenerate + + // + // Part two + // + // These are the easy(er) outputs, since there are fewer properties + // riding on them + generate if ((NM == 1) && (!OPT_LOWPOWER)) + begin : ONE_MASTER + // {{{ + reg r_swe; + reg [AW-1:0] r_saddr; + reg [DW-1:0] r_sdata; + reg [DW/8-1:0] r_ssel; + + // + // This is the low logic version of our bus data outputs. + // It only works if we only have one master. + // + // The basic idea here is that we share all of our bus outputs + // between all of the various slaves. Since we only have one + // bus master, this works. + // + always @(posedge i_clk) + begin + r_swe <= o_swe[0]; + r_saddr <= o_saddr[0+:AW]; + r_sdata <= o_sdata[0+:DW]; + r_ssel <=o_ssel[0+:DW/8]; + + // Verilator lint_off WIDTH + if (sgrant[mindex[0]] && !s_stall[mindex[0]]) + // Verilator lint_on WIDTH + begin + r_swe <= m_we[0]; + r_saddr <= m_addr[0]; + r_sdata <= m_data[0]; + r_ssel <= m_sel[0]; + end + end + + // + // The original version set o_s*[0] above, and then + // combinatorially the rest of o_s* here below. That broke + // Veri1ator. Hence, we're using r_s* and setting all of o_s* + // here. + for(M=0; M<NS; M=M+1) + begin : FOREACH_SLAVE_PORT + always @(*) + begin + o_swe[M] = r_swe; + o_saddr[M*AW +: AW] = r_saddr[AW-1:0]; + o_sdata[M*DW +: DW] = r_sdata[DW-1:0]; + o_ssel[M*DW/8+:DW/8]= r_ssel[DW/8-1:0]; + end + end + // }}} + end else begin : J + for(M=0; M<NS; M=M+1) + begin : GEN_DOWNSTREAM + // {{{ + always @(posedge i_clk) + begin + if (OPT_LOWPOWER && !sgrant[M]) + begin + o_swe[M] <= 1'b0; + o_saddr[M*AW +: AW] <= 0; + o_sdata[M*DW +: DW] <= 0; + o_ssel[M*(DW/8)+:DW/8]<= 0; + end else if (!s_stall[M]) begin + o_swe[M] <= m_we[sindex[M]]; + o_saddr[M*AW +: AW] <= m_addr[sindex[M]]; + if (OPT_LOWPOWER && !m_we[sindex[M]]) + o_sdata[M*DW +: DW] <= 0; + else + o_sdata[M*DW +: DW] <= m_data[sindex[M]]; + o_ssel[M*(DW/8)+:DW/8]<= m_sel[sindex[M]]; + end + + end + // }}} + end end endgenerate + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Assign return values to the masters + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + generate if (OPT_DBLBUFFER) + begin : DOUBLE_BUFFERRED_STALL + // {{{ + reg [NM-1:0] r_mack, r_merr; + + for(N=0; N<NM; N=N+1) + begin : FOREACH_MASTER_PORT + // m_stall isn't buffered, since it depends upon + // the already existing buffer within the address + // decoder + always @(*) + begin + if (grant[N][NS]) + m_stall[N] = 1; + else if (mgrant[N] && request[N][mindex[N]]) + m_stall[N] = mfull[N] || s_stall[mindex[N]]; + else + m_stall[N] = m_stb[N]; + + if (o_merr[N]) + m_stall[N] = 0; + end + + initial r_mack[N] = 0; + initial r_merr[N] = 0; + always @(posedge i_clk) + begin + // Verilator lint_off WIDTH + iM = mindex[N]; + // Verilator lint_on WIDTH + r_mack[N] <= mgrant[N] && s_ack[mindex[N]]; + r_merr[N] <= mgrant[N] && s_err[mindex[N]]; + if (OPT_LOWPOWER && !mgrant[N]) + o_mdata[N*DW +: DW] <= 0; + else + o_mdata[N*DW +: DW] <= s_data[mindex[N]]; + + if (grant[N][NS]||(timed_out[N] && !o_mack[N])) + begin + r_mack[N] <= 1'b0; + r_merr[N] <= !o_merr[N]; + end + + if (i_reset || !i_mcyc[N] || o_merr[N]) + begin + r_mack[N] <= 1'b0; + r_merr[N] <= 1'b0; + end + end + + assign o_mack[N] = r_mack[N]; + + assign o_merr[N] = (!OPT_STARVATION_TIMEOUT || i_mcyc[N]) && r_merr[N]; + + end + // }}} + end else if (NS == 1) // && !OPT_DBLBUFFER + begin : SINGLE_SLAVE + // {{{ + for(N=0; N<NM; N=N+1) + begin : FOREACH_MASTER_PORT + reg r_mack, r_merr; + + always @(*) + begin + m_stall[N] = !mgrant[N] || s_stall[0] + || (m_stb[N] && !request[N][0]); + r_mack = mgrant[N] && i_sack[0]; + r_merr = mgrant[N] && i_serr[0]; + o_mdata[N*DW +: DW] = (!mgrant[N] && OPT_LOWPOWER) + ? 0 : i_sdata; + + if (mfull[N]) + m_stall[N] = 1'b1; + + if (timed_out[N] && !r_mack) + begin + m_stall[N] = 1'b0; + r_mack = 1'b0; + r_merr = 1'b1; + end + + if (grant[N][NS] && m_stb[N]) + begin + m_stall[N] = 1'b0; + r_mack = 1'b0; + r_merr = 1'b1; + end + + if (!m_cyc[N]) + begin + r_mack = 1'b0; + r_merr = 1'b0; + end + end + + assign o_mack[N] = r_mack; + assign o_merr[N] = r_merr; + end + // }}} + end else begin : SINGLE_BUFFER_STALL + // {{{ + for(N=0; N<NM; N=N+1) + begin : FOREACH_MASTER_PORT + // initial o_mstall[N] = 0; + // initial o_mack[N] = 0; + reg r_mack, r_merr; + + always @(*) + begin + m_stall[N] = 1; + r_mack = mgrant[N] && s_ack[mindex[N]]; + r_merr = mgrant[N] && s_err[mindex[N]]; + if (OPT_LOWPOWER && !mgrant[N]) + o_mdata[N*DW +: DW] = 0; + else + o_mdata[N*DW +: DW] = s_data[mindex[N]]; + + if (mgrant[N]) + // Possibly lower the stall signal + m_stall[N] = s_stall[mindex[N]] + || !request[N][mindex[N]]; + + if (mfull[N]) + m_stall[N] = 1'b1; + + if (grant[N][NS] ||(timed_out[N] && !r_mack)) + begin + m_stall[N] = 1'b0; + r_mack = 1'b0; + r_merr = 1'b1; + end + + if (!m_cyc[N]) + begin + r_mack = 1'b0; + r_merr = 1'b0; + end + end + + assign o_mack[N] = r_mack; + assign o_merr[N] = r_merr; + end + // }}} + end endgenerate + + // + // Count the pending transactions per master + generate for(N=0; N<NM; N=N+1) + begin : COUNT_PENDING_TRANSACTIONS + // {{{ + reg [LGMAXBURST-1:0] lclpending; + initial lclpending = 0; + initial mempty[N] = 1; + initial mnearfull[N] = 0; + initial mfull[N] = 0; + always @(posedge i_clk) + if (i_reset || !i_mcyc[N] || o_merr[N]) + begin + lclpending <= 0; + mfull[N] <= 0; + mempty[N] <= 1'b1; + mnearfull[N]<= 0; + end else case({ (m_stb[N] && !m_stall[N]), o_mack[N] }) + 2'b01: begin + lclpending <= lclpending - 1'b1; + mnearfull[N]<= mfull[N]; + mfull[N] <= 1'b0; + mempty[N] <= (lclpending == 1); + end + 2'b10: begin + lclpending <= lclpending + 1'b1; + mnearfull[N]<= (&lclpending[LGMAXBURST-1:2])&&(lclpending[1:0] != 0); + mfull[N] <= mnearfull[N]; + mempty[N] <= 1'b0; + end + default: begin end + endcase + + assign w_mpending[N] = lclpending; + // }}} + end endgenerate + + generate if (OPT_TIMEOUT > 0) + begin : CHECK_TIMEOUT + // {{{ + for(N=0; N<NM; N=N+1) + begin : FOREACH_MASTER_PORT + + reg [TIMEOUT_WIDTH-1:0] deadlock_timer; + reg r_timed_out; + + initial deadlock_timer = OPT_TIMEOUT; + initial r_timed_out = 0; + always @(posedge i_clk) + if (i_reset || !i_mcyc[N] + ||((w_mpending[N] == 0) && !m_stb[N]) + ||(m_stb[N] && !m_stall[N]) + ||(o_mack[N] || o_merr[N]) + ||(!OPT_STARVATION_TIMEOUT&&!mgrant[N])) + begin + deadlock_timer <= OPT_TIMEOUT; + r_timed_out <= 0; + end else if (deadlock_timer > 0) + begin + deadlock_timer <= deadlock_timer - 1; + r_timed_out <= (deadlock_timer <= 1); + end + + assign timed_out[N] = r_timed_out; + end + // }}} + end else begin : NO_TIMEOUT + // {{{ + assign timed_out = 0; + // }}} + end endgenerate + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Parameter consistency check + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + initial begin : PARAMETER_CONSISTENCY_CHECK + // {{{ + if (NM == 0) + begin + $display("ERROR: At least one master must be defined"); + $stop; + end + + if (NS == 0) + begin + $display("ERROR: At least one slave must be defined"); + $stop; + end + + if (OPT_STARVATION_TIMEOUT != 0 && OPT_TIMEOUT == 0) + begin + $display("ERROR: The starvation timeout is implemented as part of the regular timeout"); + $display(" Without a timeout, the starvation timeout will not work"); + $stop; + end + // }}} + end + // }}} +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// +// Formal properties used to verify the core +// {{{ +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +`ifdef FORMAL + // Register declarations + // {{{ + localparam F_MAX_DELAY = 4; + localparam F_LGDEPTH = LGMAXBURST; + // + reg f_past_valid; + // + // Our bus checker keeps track of the number of requests, + // acknowledgments, and the number of outstanding transactions on + // every channel, both the masters driving us + wire [F_LGDEPTH-1:0] f_mreqs [0:NM-1]; + wire [F_LGDEPTH-1:0] f_macks [0:NM-1]; + wire [F_LGDEPTH-1:0] f_moutstanding [0:NM-1]; + // + // as well as the slaves that we drive ourselves + wire [F_LGDEPTH-1:0] f_sreqs [0:NS-1]; + wire [F_LGDEPTH-1:0] f_sacks [0:NS-1]; + wire [F_LGDEPTH-1:0] f_soutstanding [0:NS-1]; + // }}} + + initial assert(!OPT_STARVATION_TIMEOUT || OPT_TIMEOUT > 0); + + initial f_past_valid = 0; + always @(posedge i_clk) + f_past_valid = 1'b1; + + always @(*) + if (!f_past_valid) + assume(i_reset); + + generate for(N=0; N<NM; N=N+1) + begin : GRANT_CHECKING + // {{{ + reg checkgrant; + + always @(*) + if (f_past_valid) + for(iN=N+1; iN<NM; iN=iN+1) + // Can't grant the same channel to two separate + // masters. This applies to all but the error or + // no-slave-selected channel + assert((grant[N][NS-1:0] & grant[iN][NS-1:0])==0); + + for(M=1; M<=NS; M=M+1) + begin + // Can't grant two channels to the same master + always @(*) + if (f_past_valid && grant[N][M]) + assert(grant[N][M-1:0] == 0); + end + + + always @(*) + if (&w_mpending[N]) + assert(o_merr[N] || m_stall[N]); + + always @(*) + if (f_past_valid) + begin + checkgrant = 0; + for(iM=0; iM<NS; iM=iM+1) + if (grant[N][iM]) + checkgrant = 1; + if (grant[N][NS]) + checkgrant = 1; + + assert(checkgrant == mgrant[N]); + end + // }}} + end endgenerate + + // Double check the grant mechanism and its dependent variables + generate for(N=0; N<NM; N=N+1) + begin : CHECK_GRANTS + // {{{ + for(M=0; M<NS; M=M+1) + begin + always @(*) + if ((f_past_valid)&&grant[N][M]) + begin + assert(mgrant[N]); + assert(mindex[N] == M); + assert(sgrant[M]); + assert(sindex[M] == N); + end + end + // }}} + end endgenerate + + generate for(M=0; M<NS; M=M+1) + begin : CHECK_SGRANT + // {{{ + reg f_sgrant; + + always @(*) + if (sgrant[M]) + assert(grant[sindex[M]][M]); + + always @(*) + begin + f_sgrant = 0; + for(iN=0; iN<NM; iN=iN+1) + if (grant[iN][M]) + f_sgrant = 1; + end + + always @(*) + assert(sgrant[M] == f_sgrant); + // }}} + end endgenerate + + // Double check the timeout flags for consistency + generate for(N=0; N<NM; N=N+1) + begin : F_CHECK_TIMEOUT + // {{{ + always @(*) + if (f_past_valid) + begin + assert(mempty[N] == (w_mpending[N] == 0)); + assert(mnearfull[N]==(&w_mpending[N][LGMAXBURST-1:1])); + assert(mfull[N] == (&w_mpending[N])); + end + // }}} + end endgenerate + +`ifdef VERIFIC + // {{{ + // The Verific parser is currently broken, and doesn't allow + // initial assumes or asserts. The following lines get us around that + // + always @(*) + if (!f_past_valid) + assume(sgrant == 0); + + generate for(M=0; M<NS; M=M+1) + begin + always @(*) + if (!f_past_valid) + begin + assume(o_scyc[M] == 0); + assume(o_sstb[M] == 0); + assume(sgrant[M] == 0); + end + end endgenerate + + generate for(N=0; N<NM; N=N+1) + begin + always @(*) + if (!f_past_valid) + begin + assume(grant[N] == 0); + assume(mgrant[N] == 0); + end + end endgenerate + // }}} +`endif + + //////////////////////////////////////////////////////////////////////// + // + // BUS CHECK + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + // Verify that every channel, whether master or slave, follows the rules + // of the WB road. + // + generate for(N=0; N<NM; N=N+1) + begin : WB_SLAVE_CHECK + // {{{ + fwb_slave #( + .AW(AW), .DW(DW), + .F_LGDEPTH(LGMAXBURST), + .F_MAX_ACK_DELAY(0), + .F_MAX_STALL(0) + ) slvi(i_clk, i_reset, + i_mcyc[N], i_mstb[N], i_mwe[N], + i_maddr[N*AW +: AW], i_mdata[N*DW +: DW], + i_msel[N*(DW/8) +: (DW/8)], + o_mack[N], o_mstall[N], o_mdata[N*DW +: DW], o_merr[N], + f_mreqs[N], f_macks[N], f_moutstanding[N]); + + always @(*) + if ((f_past_valid)&&(grant[N][NS])) + assert(f_moutstanding[N] <= 1); + + always @(*) + if (f_past_valid && grant[N][NS] && i_mcyc[N]) + assert(m_stall[N] || o_merr[N]); + + always @(posedge i_clk) + if (f_past_valid && $past(!i_reset && i_mstb[N] && o_mstall[N])) + assume($stable(i_mdata[N*DW +: DW])); + // }}} + end endgenerate + + generate for(M=0; M<NS; M=M+1) + begin : WB_MASTER_CHECK + // {{{ + fwb_master #( + .AW(AW), .DW(DW), + .F_LGDEPTH(LGMAXBURST), + .F_MAX_ACK_DELAY(F_MAX_DELAY), + .F_MAX_STALL(2) + ) mstri(i_clk, i_reset, + o_scyc[M], o_sstb[M], o_swe[M], + o_saddr[M*AW +: AW], o_sdata[M*DW +: DW], + o_ssel[M*(DW/8) +: (DW/8)], + i_sack[M], i_sstall[M], s_data[M], i_serr[M], + f_sreqs[M], f_sacks[M], f_soutstanding[M]); + // }}} + end endgenerate + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Correlate outstanding numbers + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + generate for(N=0; N<NM; N=N+1) + begin : CHECK_OUTSTANDING + // {{{ + always @(*) + if (mfull[N]) + assert(m_stall[N]); + + always @(posedge i_clk) + if (i_mcyc[N]) + assert(f_moutstanding[N] == w_mpending[N] + +((OPT_BUFFER_DECODER & dcd_stb[N]) ? 1:0)); + + reg [LGMAXBURST:0] n_outstanding; + always @(*) + if (i_mcyc[N]) + assert(f_moutstanding[N] >= + ((OPT_BUFFER_DECODER && dcd_stb[N]) ? 1:0) + + (o_mack[N] && OPT_DBLBUFFER) ? 1:0); + + always @(*) + n_outstanding = f_moutstanding[N] + - ((OPT_BUFFER_DECODER && dcd_stb[N]) ? 1:0) + - ((o_mack[N] && OPT_DBLBUFFER) ? 1:0); + + always @(posedge i_clk) + if (i_mcyc[N] && !mgrant[N] && !o_merr[N]) + assert(f_moutstanding[N] + == ((OPT_BUFFER_DECODER & dcd_stb[N]) ? 1:0)); + + else if (i_mcyc[N] && mgrant[N] && !i_reset) + for(iM=0; iM<NS; iM=iM+1) + if (grant[N][iM] && o_scyc[iM] && !i_serr[iM] && !o_merr[N]) + assert(n_outstanding + == {1'b0,f_soutstanding[iM]} + +(o_sstb[iM] ? 1:0)); + + always @(*) + if (!i_reset) + begin + for(iM=0; iM<NS; iM=iM+1) + if (grant[N][iM] && i_mcyc[N]) + begin + if (f_soutstanding[iM] > 0) + assert(i_mwe[N] == o_swe[iM]); + if (o_sstb[iM]) + assert(i_mwe[N] == o_swe[iM]); + if (o_mack[N]) + assert(i_mwe[N] == o_swe[iM]); + if (o_scyc[iM] && i_sack[iM]) + assert(i_mwe[N] == o_swe[iM]); + if (o_merr[N] && !timed_out[N]) + assert(i_mwe[N] == o_swe[iM]); + if (o_scyc[iM] && i_serr[iM]) + assert(i_mwe[N] == o_swe[iM]); + end + end + + always @(*) + if (!i_reset && OPT_BUFFER_DECODER && i_mcyc[N]) + begin + if (dcd_stb[N]) + assert(i_mwe[N] == m_we[N]); + end + // }}} + end endgenerate + + generate for(M=0; M<NS; M=M+1) + begin : ASSERT_NOT_CYC_WO_GRANT + // {{{ + always @(posedge i_clk) + if (!$past(sgrant[M])) + assert(!o_scyc[M]); + // }}} + end endgenerate + // }}} + //////////////////////////////////////////////////////////////////////// + // + // CONTRACT SECTION + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + // Here's the contract, in two parts: + // {{{ + // 1. Should ever a master (any master) wish to read from a slave + // (any slave), he should be able to read a known value + // from that slave (any value) from any arbitrary address + // he might wish to read from (any address) + // + // 2. Should ever a master (any master) wish to write to a slave + // (any slave), he should be able to write the exact + // value he wants (any value) to the exact address he wants + // (any address) + // + // special_master is an arbitrary constant chosen by the solver, + // which can reference *any* possible master + // special_address is an arbitrary constant chosen by the solver, + // which can reference *any* possible address the master + // might wish to access + // special_value is an arbitrary value (at least during + // induction) representing the current value within the + // slave at the given address + // }}} + // + //////////////////////////////////////////////////////////////////////// + // + // Now let's pay attention to a special bus master and a special + // address referencing a special bus slave. We'd like to assert + // that we can access the values of every slave from every master. + (* anyconst *) reg [(NM<=1)?0:(LGNM-1):0] special_master; + reg [(NS<=1)?0:(LGNS-1):0] special_slave; + (* anyconst *) reg [AW-1:0] special_address; + reg [DW-1:0] special_value; + + always @(*) + if (NM <= 1) + assume(special_master == 0); + always @(*) + if (NS <= 1) + assume(special_slave == 0); + + // + // Decode the special address to discover the slave associated with it + always @(*) + begin + special_slave = NS; + for(iM=0; iM<NS; iM = iM+1) + begin + if (((special_address ^ SLAVE_ADDR[iM*AW +: AW]) + &SLAVE_MASK[iM*AW +: AW]) == 0) + special_slave = iM; + end + end + + generate if (NS > 1) + begin : DOUBLE_ADDRESS_CHECK + // {{{ + // + // Check that no slave address has been assigned twice. + // This check only needs to be done once at the beginning + // of the run, during the BMC section. + reg address_found; + + always @(*) + if (!f_past_valid) + begin + address_found = 0; + for(iM=0; iM<NS; iM = iM+1) + begin + if (((special_address ^ SLAVE_ADDR[iM*AW +: AW]) + &SLAVE_MASK[iM*AW +: AW]) == 0) + begin + assert(address_found == 0); + address_found = 1; + end + end + end + // }}} + end endgenerate + // + // Let's assume this slave will acknowledge any request on the next + // bus cycle after the stall goes low. Further, lets assume that + // it never creates an error, and that it always responds to our special + // address with the special data value given above. To do this, we'll + // also need to make certain that the special value will change + // following any write. + // + // These are the "assumptions" associated with our fictitious slave. +`ifdef VERIFIC + always @(*) + if (!f_past_valid) + assume(special_value == 0); +`else + initial assume(special_value == 0); +`endif + always @(posedge i_clk) + if (special_slave < NS) + begin + // {{{ + if ($past(o_sstb[special_slave] && !i_sstall[special_slave])) + begin + assume(i_sack[special_slave]); + + if ($past(!o_swe[special_slave]) + &&($past(o_saddr[special_slave*AW +: AW]) == special_address)) + assume(i_sdata[special_slave*DW+: DW] + == special_value); + end else + assume(!i_sack[special_slave]); + assume(!i_serr[special_slave]); + + if (o_scyc[special_slave]) + assert(f_soutstanding[special_slave] + == i_sack[special_slave]); + + if (o_sstb[special_slave] && !i_sstall[special_slave] + && o_swe[special_slave]) + begin + for(iM=0; iM < DW/8; iM=iM+1) + if (o_ssel[special_slave * DW/8 + iM]) + special_value[iM*8 +: 8] <= o_sdata[special_slave * DW + iM*8 +: 8]; + end + // }}} + end + + // + // Now its time to make some assertions. Specifically, we want to + // assert that any time we read from this special slave, the special + // value is returned. + reg [2:0] f_read_seq; + reg f_read_ack, f_read_sstall; + + initial f_read_sstall = 0; + always @(posedge i_clk) + f_read_sstall <= s_stall[special_slave]; + + always @(*) + f_read_ack = (f_read_seq[2] || ((!OPT_DBLBUFFER)&&f_read_seq[1] + && !f_read_sstall)); + initial f_read_seq = 0; + always @(posedge i_clk) + if ((special_master < NM)&&(special_slave < NS) + &&(i_mcyc[special_master]) + &&(!timed_out[special_master])) + begin + f_read_seq <= 0; + if ((grant[special_master][special_slave]) + &&(m_stb[special_master]) + &&(m_addr[special_master] == special_address) + &&(!m_we[special_master]) + ) + begin + f_read_seq[0] <= 1; + end + + if (|f_read_seq) + begin + assert(grant[special_master][special_slave]); + assert(mgrant[special_master]); + assert(sgrant[special_slave]); + assert(mindex[special_master] == special_slave); + assert(sindex[special_slave] == special_master); + assert(!o_merr[special_master]); + end + + if (f_read_seq[0] && !$past(s_stall[special_slave])) + begin + assert(o_scyc[special_slave]); + assert(o_sstb[special_slave]); + assert(!o_swe[special_slave]); + assert(o_saddr[special_slave*AW +: AW] == special_address); + + f_read_seq[1] <= 1; + + end else if (f_read_seq[0] && $past(s_stall[special_slave])) + begin + assert($stable(m_stb[special_master])); + assert(!m_we[special_master]); + assert(m_addr[special_master] == special_address); + + f_read_seq[0] <= 1; + end + + if (f_read_seq[1] && $past(s_stall[special_slave])) + begin + assert(o_scyc[special_slave]); + assert(o_sstb[special_slave]); + assert(!o_swe[special_slave]); + assert(o_saddr[special_slave*AW +: AW] == special_address); + f_read_seq[1] <= 1; + end else if (f_read_seq[1] && !$past(s_stall[special_slave])) + begin + assert(i_sack[special_slave]); + assert(i_sdata[special_slave*DW +: DW] == $past(special_value)); + if (OPT_DBLBUFFER) + f_read_seq[2] <= 1; + end + + if (f_read_ack) + begin + assert(o_mack[special_master]); + assert(o_mdata[special_master * DW +: DW] + == $past(special_value,2)); + end + end else + f_read_seq <= 0; + + always @(*) + cover(i_mcyc[special_master] && f_read_ack); + + // + // Let's try a write assertion now. Specifically, on any request to + // write to our special address, we want to assert that the special + // value at that address can be written. + reg [2:0] f_write_seq; + reg f_write_ack, f_write_sstall; + + initial f_write_sstall = 0; + always @(posedge i_clk) + f_write_sstall = s_stall[special_slave]; + + always @(*) + f_write_ack = (f_write_seq[2] + || ((!OPT_DBLBUFFER)&&f_write_seq[1] + && !f_write_sstall)); + initial f_write_seq = 0; + always @(posedge i_clk) + if ((special_master < NM)&&(special_slave < NS) + &&(i_mcyc[special_master]) + &&(!timed_out[special_master])) + begin + f_write_seq <= 0; + if ((grant[special_master][special_slave]) + &&(m_stb[special_master]) + &&(m_addr[special_master] == special_address) + &&(m_we[special_master])) + begin + // Our write sequence begins when our special master + // has access to the bus, *and* he is trying to write + // to our special address. + f_write_seq[0] <= 1; + end + + if (|f_write_seq) + begin + assert(grant[special_master][special_slave]); + assert(mgrant[special_master]); + assert(sgrant[special_slave]); + assert(mindex[special_master] == special_slave); + assert(sindex[special_slave] == special_master); + assert(!o_merr[special_master]); + end + + if (f_write_seq[0] && !$past(s_stall[special_slave])) + begin + assert(o_scyc[special_slave]); + assert(o_sstb[special_slave]); + assert(o_swe[special_slave]); + assert(o_saddr[special_slave*AW +: AW] == special_address); + assert(o_sdata[special_slave*DW +: DW] + == $past(m_data[special_master])); + assert(o_ssel[special_slave*DW/8 +: DW/8] + == $past(m_sel[special_master])); + + f_write_seq[1] <= 1; + + end else if (f_write_seq[0] && $past(s_stall[special_slave])) + begin + assert($stable(m_stb[special_master])); + assert(m_we[special_master]); + assert(m_addr[special_master] == special_address); + assert($stable(m_data[special_master])); + assert($stable(m_sel[special_master])); + + f_write_seq[0] <= 1; + end + + if (f_write_seq[1] && $past(s_stall[special_slave])) + begin + assert(o_scyc[special_slave]); + assert(o_sstb[special_slave]); + assert(o_swe[special_slave]); + assert(o_saddr[special_slave*AW +: AW] == special_address); + assert($stable(o_sdata[special_slave*DW +: DW])); + assert($stable(o_ssel[special_slave*DW/8 +: DW/8])); + f_write_seq[1] <= 1; + end else if (f_write_seq[1] && !$past(s_stall[special_slave])) + begin + for(iM=0; iM<DW/8; iM=iM+1) + begin + if ($past(o_ssel[special_slave * DW/8 + iM])) + assert(special_value[iM*8 +: 8] + == $past(o_sdata[special_slave*DW+iM*8 +: 8])); + end + + assert(i_sack[special_slave]); + if (OPT_DBLBUFFER) + f_write_seq[2] <= 1; + end + + if (f_write_seq[2] || ((!OPT_DBLBUFFER) && f_write_seq[1] + && !$past(s_stall[special_slave]))) + assert(o_mack[special_master]); + end else + f_write_seq <= 0; + + always @(*) + cover(i_mcyc[special_master] && f_write_ack); + // }}} + //////////////////////////////////////////////////////////////////////// + // + // COVER: Full connectivity check + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + reg [NM-1:0] f_m_ackd; + reg [NS-1:0] f_s_ackd; + reg f_cvr_aborted; + + initial f_cvr_aborted = 0; + always @(posedge i_clk) + if (!f_past_valid || i_reset) + f_cvr_aborted <= 0; + else for(iN=0; iN<NM; iN=iN+1) + begin + if (request[iN][NS]) + f_cvr_aborted = 1; + if ($fell(i_mcyc[iN])) + begin + if (f_macks[iN] != f_mreqs[iN]) + f_cvr_aborted = 1; + end + end + + initial f_m_ackd = 0; + generate for (N=0; N<NM; N=N+1) + begin : GEN_FM_ACKD + + always @(posedge i_clk) + if (i_reset) + f_m_ackd[N] <= 0; + else if (o_mack[N]) + f_m_ackd[N] <= 1'b1; + + end endgenerate + + always @(posedge i_clk) + cover(!f_cvr_aborted && (&f_m_ackd)); + + generate if (NM > 1) + begin + + always @(posedge i_clk) + cover(!f_cvr_aborted && (&f_m_ackd[1:0])); + + end endgenerate + + initial f_s_ackd = 0; + generate for (M=0; M<NS; M=M+1) + begin : GEN_FS_ACKD + + always @(posedge i_clk) + if (i_reset) + f_s_ackd[M] <= 1'b0; + else if (sgrant[M] && o_mack[sindex[M]]) + f_s_ackd[M] <= 1'b1; + + end endgenerate + + always @(posedge i_clk) + cover(!f_cvr_aborted && (&f_s_ackd[NS-1:0])); + + generate if (NS > 1) + begin + + always @(posedge i_clk) + cover(!f_cvr_aborted && (&f_s_ackd[NS-1:0])); + + end endgenerate + // }}} +`endif +// }}} +endmodule +`ifndef YOSYS +`default_nettype wire +`endif |
