summaryrefslogtreecommitdiff
path: root/rtl/wb2axip/addrdecode.v
diff options
context:
space:
mode:
authorAlejandro Soto <alejandro@34project.org>2024-03-06 02:38:24 -0600
committerAlejandro Soto <alejandro@34project.org>2024-03-06 02:38:24 -0600
commit3038edc09a2eb15762f2e58533f429489107520b (patch)
treef7a45e424d39e6fef0d59e329c1bf6ea206e2886 /rtl/wb2axip/addrdecode.v
parent3b62399f92e9faa2602ac30865e5fc3c7c4e12b8 (diff)
rtl/wb2axip: add to version control
Diffstat (limited to 'rtl/wb2axip/addrdecode.v')
-rw-r--r--rtl/wb2axip/addrdecode.v461
1 files changed, 461 insertions, 0 deletions
diff --git a/rtl/wb2axip/addrdecode.v b/rtl/wb2axip/addrdecode.v
new file mode 100644
index 0000000..2f3cac6
--- /dev/null
+++ b/rtl/wb2axip/addrdecode.v
@@ -0,0 +1,461 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: addrdecode.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Supports bus crossbars by answering the question, which slave
+// does the current address need to be routed to? Requests are
+// pipelined using valid/!stall handshaking. For those familiar with
+// AXI, READY=!STALL. The outgoing stream is identical to the incoming
+// one, save for the (new) o_decode field. This is a bitmask containing
+// one bit for each slave that the request might be routed to, and one
+// extra bit to indicate no slaves matched.
+//
+// The keys to the operation of this module are found in the two
+// parameters, SLAVE_ADDR and SLAVE_MASK.
+//
+// SLAVE_ADDR specifies the address of the slave in question. It's a large
+// array, with one address (i.e. one set of AW bits) for each
+// potential slave address region.
+//
+// SLAVE_MASK specifies which of the bits in SLAVE_ADDR need to match in
+// order to route a request to a that slave.
+//
+// It's important to guarantee that no two slaves will ever map to the
+// same address, and likewise any given slave may only map to a single
+// address region.
+//
+// Incidentally, the algorithm forces all slaves to have an address
+// aligned with their memory size. Hence a 2GB memory must have an
+// address (range) of either 0-2GB, 2GB-4GB, 4GB-6GB, etc. However, a
+// second slave having only 8kB of memory may be placed at 0-8kB,
+// 8-16kB, 16-24kB, etc. For logic minimization purposes, it is often to
+// the advantage of the bus compositor to minimize the number of mask
+// bits, and hence 8kB slaves may be aliased to many places in memory.
+// Bus composition and address assignment, however, are both outside of
+// the scope of the operation of this module.
+//
+//
+// 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 addrdecode #(
+ // {{{
+ parameter NS=8,
+ parameter AW = 32, DW=32+32/8+1+1,
+ //
+ // SLAVE_ADDR contains address assignments for each of the
+ // various slaves we are adjudicating between.
+ 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}} }},
+ //
+ // SLAVE_MASK contains a mask of those address bits in
+ // SLAVE_ADDR which are relevant. It shall be true that if
+ // !SLAVE_MASK[k] then !SLAVE_ADDR[k], for any bits of k
+ 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}} }} },
+ //
+ // ACCESS_ALLOWED is a bit-wise mask indicating which slaves
+ // may get access to the bus. If ACCESS_ALLOWED[slave] is true,
+ // then a master can connect to the slave via this method. This
+ // parameter is primarily here to support AXI (or other similar
+ // buses) which may have separate accesses for both read and
+ // write. By using this, a read-only slave can be connected,
+ // which would also naturally create an error on any attempt to
+ // write to it.
+ parameter [NS-1:0] ACCESS_ALLOWED = -1,
+ //
+ // If OPT_REGISTERED is set, address decoding will take an extra
+ // clock, and will register the results of the decoding
+ // operation.
+ parameter [0:0] OPT_REGISTERED = 0,
+ //
+ // If OPT_LOWPOWER is set, then whenever the output is not
+ // valid, any respective data linse will also be forced to zero
+ // in an effort to minimize power.
+ parameter [0:0] OPT_LOWPOWER = 0
+ // }}}
+ ) (
+ // {{{
+ input wire i_clk, i_reset,
+ //
+ input wire i_valid,
+ output reg o_stall,
+ input wire [AW-1:0] i_addr,
+ input wire [DW-1:0] i_data,
+ //
+ output reg o_valid,
+ input wire i_stall,
+ output reg [NS:0] o_decode,
+ output reg [AW-1:0] o_addr,
+ output reg [DW-1:0] o_data
+ // }}}
+ );
+
+ // Local declarations
+ // {{{
+ //
+ // OPT_NONESEL controls whether or not the address lines are fully
+ // proscribed, or whether or not a "no-slave identified" slave should
+ // be created. To avoid a "no-slave selected" output, slave zero must
+ // have no mask bits set (and therefore no address bits set), and it
+ // must also allow access.
+ localparam [0:0] OPT_NONESEL = (!ACCESS_ALLOWED[0])
+ || (SLAVE_MASK[AW-1:0] != 0);
+ //
+ wire [NS:0] request;
+ reg [NS-1:0] prerequest;
+ integer iM;
+ // }}}
+
+ // prerequest
+ // {{{
+ always @(*)
+ for(iM=0; iM<NS; iM=iM+1)
+ prerequest[iM] = (((i_addr ^ SLAVE_ADDR[iM*AW +: AW])
+ &SLAVE_MASK[iM*AW +: AW])==0)
+ &&(ACCESS_ALLOWED[iM]);
+ // }}}
+
+ // request
+ // {{{
+ generate if (OPT_NONESEL)
+ begin : NO_DEFAULT_REQUEST
+ // {{{
+ reg [NS-1:0] r_request;
+
+ // Need to create a slave to describe when nothing is selected
+ //
+ always @(*)
+ begin
+ for(iM=0; iM<NS; iM=iM+1)
+ r_request[iM] = i_valid && prerequest[iM];
+ if (!OPT_NONESEL && (NS > 1 && |prerequest[NS-1:1]))
+ r_request[0] = 1'b0;
+ end
+
+ assign request[NS-1:0] = r_request;
+ // }}}
+ end else if (NS == 1)
+ begin : SINGLE_SLAVE
+ // {{{
+ assign request[0] = i_valid;
+ // }}}
+ end else begin : GENERAL_CASE
+ // {{{
+ reg [NS-1:0] r_request;
+
+ always @(*)
+ begin
+ for(iM=0; iM<NS; iM=iM+1)
+ r_request[iM] = i_valid && prerequest[iM];
+ if (!OPT_NONESEL && (NS > 1 && |prerequest[NS-1:1]))
+ r_request[0] = 1'b0;
+ end
+
+ assign request[NS-1:0] = r_request;
+ // }}}
+ end endgenerate
+ // }}}
+
+ // request[NS]
+ // {{{
+ generate if (OPT_NONESEL)
+ begin : GENERATE_NONSEL_SLAVE
+ reg r_request_NS, r_none_sel;
+
+ always @(*)
+ begin
+ // Let's assume nothing's been selected, and then check
+ // to prove ourselves wrong.
+ //
+ // Note that none_sel will be considered an error
+ // condition in the follow-on processing. Therefore
+ // it's important to clear it if no request is pending.
+ r_none_sel = i_valid && (prerequest == 0);
+ //
+ // request[NS] indicates a request for a non-existent
+ // slave. A request that should (eventually) return a
+ // bus error
+ //
+ r_request_NS = r_none_sel;
+ end
+
+ assign request[NS] = r_request_NS;
+ end else begin : NO_NONESEL_SLAVE
+ assign request[NS] = 1'b0;
+ end endgenerate
+ // }}}
+
+ // o_valid, o_addr, o_data, o_decode, o_stall
+ // {{{
+ generate if (OPT_REGISTERED)
+ begin : GEN_REG_OUTPUTS
+
+ // o_valid
+ // {{{
+ initial o_valid = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ o_valid <= 0;
+ else if (!o_stall)
+ o_valid <= i_valid;
+ // }}}
+
+ // o_addr, o_data
+ // {{{
+ initial o_addr = 0;
+ initial o_data = 0;
+ always @(posedge i_clk)
+ if (i_reset && OPT_LOWPOWER)
+ begin
+ o_addr <= 0;
+ o_data <= 0;
+ end else if ((!o_valid || !i_stall)
+ && (i_valid || !OPT_LOWPOWER))
+ begin
+ o_addr <= i_addr;
+ o_data <= i_data;
+ end else if (OPT_LOWPOWER && !i_stall)
+ begin
+ o_addr <= 0;
+ o_data <= 0;
+ end
+ // }}}
+
+ // o_decode
+ // {{{
+ initial o_decode = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ o_decode <= 0;
+ else if ((!o_valid || !i_stall)
+ && (i_valid || !OPT_LOWPOWER))
+ o_decode <= request;
+ else if (OPT_LOWPOWER && !i_stall)
+ o_decode <= 0;
+ // }}}
+
+ // o_stall
+ // {{{
+ always @(*)
+ o_stall = (o_valid && i_stall);
+ // }}}
+ end else begin : GEN_COMBINATORIAL_OUTPUTS
+
+ always @(*)
+ begin
+ o_valid = i_valid;
+ o_stall = i_stall;
+ o_addr = i_addr;
+ o_data = i_data;
+
+ o_decode = request;
+ end
+
+ // Make Verilator happy
+ // {{{
+ // verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0,
+`ifdef VERILATOR
+ // Can't declare the clock as unused for formal,
+ // lest it not be recognized as *the* clock
+ i_clk,
+`endif
+ i_reset };
+ // verilator lint_on UNUSED
+ // }}}
+ end endgenerate
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ reg f_past_valid;
+ initial f_past_valid = 0;
+ always @(posedge i_clk)
+ f_past_valid <= 1;
+
+ reg [AW+DW-1:0] f_idata;
+ always @(*)
+ f_idata = { i_addr, i_data };
+
+`ifdef ADDRDECODE
+ always @(posedge i_clk)
+ if (!f_past_valid)
+ assume(i_reset);
+`else
+ always @(posedge i_clk)
+ if (!f_past_valid)
+ assert(i_reset);
+
+`endif // ADDRDECODE
+ always @(posedge i_clk)
+ if (OPT_REGISTERED && (!f_past_valid || $past(i_reset)))
+ begin
+ assert(!o_valid);
+ assert(o_decode == 0);
+ end else if ($past(o_valid && i_stall) && OPT_REGISTERED)
+ begin
+ assert($stable(o_addr));
+ assert($stable(o_decode));
+ assert($stable(o_data));
+ end
+
+ // If the output is ever valid, there must be at least one
+ // decoded output
+ always @(*)
+ assert(o_valid == (o_decode != 0));
+
+ always @(*)
+ for(iM=0; iM<NS; iM=iM+1)
+ if (o_decode[iM])
+ begin
+ // The address must match
+ assert((((o_addr ^ SLAVE_ADDR[iM*AW +: AW])
+ & SLAVE_MASK[iM*AW +: AW])==0)
+ && ACCESS_ALLOWED[iM]);
+ //
+ // And nothing else must match
+ assert(o_decode == (1<<iM));
+ end
+
+ always @(*)
+ for(iM=0; iM<NS; iM=iM+1)
+ if (!ACCESS_ALLOWED[iM])
+ assert(!o_decode[iM]);
+
+ // LOWPOWER check
+ // {{{
+ generate if (OPT_LOWPOWER && OPT_REGISTERED)
+ begin
+ always @(*)
+ if (!o_valid)
+ begin
+ assert(o_addr == 0);
+ assert(o_decode == 0);
+ assert(o_data == 0);
+ end
+ end endgenerate
+ // }}}
+
+ //
+ // The output decoded value may only ever have one value high,
+ // never more--i.e. $onehot0
+ // {{{
+`ifdef VERIFIC
+ always @(*)
+ assert($onehot0(request));
+`else
+ reg onehot_request;
+ always @(*)
+ begin
+ onehot_request = 0;
+ for(iM=0; iM<NS+1; iM=iM+1)
+ if ((request ^ (1<<iM))==0)
+ onehot_request = 1;
+ end
+
+ always @(*)
+ if (request != 0)
+ assert(onehot_request);
+`endif
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // Make sure all addresses are reachable
+ //
+ reg [NS:0] f_reached;
+
+ always @(posedge i_clk)
+ cover(i_valid);
+
+ always @(posedge i_clk)
+ cover(o_valid);
+
+ always @(posedge i_clk)
+ cover(o_valid && !i_stall);
+
+ initial f_reached = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ f_reached = 0;
+ else if (o_valid)
+ f_reached = f_reached | o_decode;
+
+ generate if (!OPT_NONESEL && ACCESS_ALLOWED[0]
+ && SLAVE_MASK == 0 && NS == 1)
+ begin
+
+ always @(*)
+ cover(f_reached[0]);
+
+ always @(posedge i_clk)
+ if (f_past_valid && $stable(o_valid))
+ assert($stable(o_decode));
+
+ end else begin
+
+ always @(*)
+ cover(&f_reached);
+
+ always @(posedge i_clk)
+ if (f_past_valid && $stable(o_valid))
+ cover($changed(o_decode));
+
+ end endgenerate
+ // }}}
+`endif // FORMAL
+// }}}
+endmodule
+`ifndef YOSYS
+`default_nettype wire
+`endif