summaryrefslogtreecommitdiff
path: root/rtl/wb2axip/wbarbiter.v
diff options
context:
space:
mode:
Diffstat (limited to 'rtl/wb2axip/wbarbiter.v')
-rw-r--r--rtl/wb2axip/wbarbiter.v404
1 files changed, 404 insertions, 0 deletions
diff --git a/rtl/wb2axip/wbarbiter.v b/rtl/wb2axip/wbarbiter.v
new file mode 100644
index 0000000..e068278
--- /dev/null
+++ b/rtl/wb2axip/wbarbiter.v
@@ -0,0 +1,404 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: wbarbiter.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: This is a priority bus arbiter. It allows two separate wishbone
+// masters to connect to the same bus, while also guaranteeing
+// that the last master can have the bus with no delay any time it is
+// idle. The goal is to minimize the combinatorial logic required in this
+// process, while still minimizing access time.
+//
+// The core logic works like this:
+//
+// 1. If 'A' or 'B' asserts the o_cyc line, a bus cycle will begin,
+// with acccess granted to whomever requested it.
+// 2. If both 'A' and 'B' assert o_cyc at the same time, only 'A'
+// will be granted the bus. (If the alternating parameter
+// is set, A and B will alternate who gets the bus in
+// this case.)
+// 3. The bus will remain owned by whomever the bus was granted to
+// until they deassert the o_cyc line.
+// 4. At the end of a bus cycle, o_cyc is guaranteed to be
+// deasserted (low) for one clock.
+// 5. On the next clock, bus arbitration takes place again. If
+// 'A' requests the bus, no matter how long 'B' was
+// waiting, 'A' will then be granted the bus. (Unless
+// again the alternating parameter is set, then the
+// access is guaranteed to switch to B.)
+//
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2015-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
+//
+`define WBA_ALTERNATING
+// }}}
+module wbarbiter #(
+ // {{{
+ parameter DW=32, AW=32,
+ parameter SCHEME="ALTERNATING",
+ parameter [0:0] OPT_ZERO_ON_IDLE = 1'b0,
+ parameter [31:0] F_MAX_STALL = 3,
+ parameter [31:0] F_MAX_ACK_DELAY = 3,
+ parameter [31:0] F_LGDEPTH=3
+ // }}}
+ ) (
+ // {{{
+ input wire i_clk, i_reset,
+ // Bus A
+ // {{{
+ input wire i_a_cyc, i_a_stb, i_a_we,
+ input wire [(AW-1):0] i_a_adr,
+ input wire [(DW-1):0] i_a_dat,
+ input wire [(DW/8-1):0] i_a_sel,
+ output wire o_a_ack, o_a_stall, o_a_err,
+ // }}}
+ // Bus B
+ // {{{
+ input wire i_b_cyc, i_b_stb, i_b_we,
+ input wire [(AW-1):0] i_b_adr,
+ input wire [(DW-1):0] i_b_dat,
+ input wire [(DW/8-1):0] i_b_sel,
+ output wire o_b_ack, o_b_stall, o_b_err,
+ // }}}
+ // Combined/arbitrated bus
+ // {{{
+ output wire o_cyc, o_stb, o_we,
+ output wire [(AW-1):0] o_adr,
+ output wire [(DW-1):0] o_dat,
+ output wire [(DW/8-1):0] o_sel,
+ input wire i_ack, i_stall, i_err
+ // }}}
+`ifdef FORMAL
+ // {{{
+ ,
+ output wire [(F_LGDEPTH-1):0]
+ f_nreqs, f_nacks, f_outstanding,
+ f_a_nreqs, f_a_nacks, f_a_outstanding,
+ f_b_nreqs, f_b_nacks, f_b_outstanding
+ // }}}
+`endif
+ // }}}
+ );
+ //
+
+ // Go high immediately (new cycle) if ...
+ // Previous cycle was low and *someone* is requesting a bus cycle
+ // Go low immadiately if ...
+ // We were just high and the owner no longer wants the bus
+ // WISHBONE Spec recommends no logic between a FF and the o_cyc
+ // This violates that spec. (Rec 3.15, p35)
+ reg r_a_owner;
+
+ assign o_cyc = (r_a_owner) ? i_a_cyc : i_b_cyc;
+ initial r_a_owner = 1'b1;
+
+ generate if (SCHEME == "PRIORITY")
+ begin : PRI
+
+ always @(posedge i_clk)
+ if (!i_b_cyc)
+ r_a_owner <= 1'b1;
+ // Allow B to set its CYC line w/o activating this
+ // interface
+ else if ((i_b_stb)&&(!i_a_cyc))
+ r_a_owner <= 1'b0;
+
+ end else if (SCHEME == "ALTERNATING")
+ begin : ALT
+
+ reg last_owner;
+ initial last_owner = 1'b0;
+ always @(posedge i_clk)
+ if ((i_a_cyc)&&(r_a_owner))
+ last_owner <= 1'b1;
+ else if ((i_b_cyc)&&(!r_a_owner))
+ last_owner <= 1'b0;
+
+ always @(posedge i_clk)
+ if ((!i_a_cyc)&&(!i_b_cyc))
+ r_a_owner <= !last_owner;
+ else if ((r_a_owner)&&(!i_a_cyc))
+ begin
+
+ if (i_b_stb)
+ r_a_owner <= 1'b0;
+
+ end else if ((!r_a_owner)&&(!i_b_cyc))
+ begin
+
+ if (i_a_stb)
+ r_a_owner <= 1'b1;
+
+ end
+
+ end else // if (SCHEME == "LAST")
+ begin : LST
+ always @(posedge i_clk)
+ if ((!i_a_cyc)&&(i_b_stb))
+ r_a_owner <= 1'b0;
+ else if ((!i_b_cyc)&&(i_a_stb))
+ r_a_owner <= 1'b1;
+ end endgenerate
+
+
+ // Realistically, if neither master owns the bus, the output is a
+ // don't care. Thus we trigger off whether or not 'A' owns the bus.
+ // If 'B' owns it all we care is that 'A' does not. Likewise, if
+ // neither owns the bus than the values on the various lines are
+ // irrelevant.
+ assign o_we = (r_a_owner) ? i_a_we : i_b_we;
+
+ generate if (OPT_ZERO_ON_IDLE)
+ begin : ZERO_IDLE
+ // {{{
+ //
+ // OPT_ZERO_ON_IDLE will use up more logic and may even slow
+ // down the master clock if set. However, it may also reduce
+ // the power used by the FPGA by preventing things from toggling
+ // when the bus isn't in use. The option is here because it
+ // also makes it a lot easier to look for when things happen
+ // on the bus via VERILATOR when timing and logic counts
+ // don't matter.
+ //
+ assign o_stb = (o_cyc)? ((r_a_owner) ? i_a_stb : i_b_stb):0;
+ assign o_adr = (o_stb)? ((r_a_owner) ? i_a_adr : i_b_adr):0;
+ assign o_dat = (o_stb)? ((r_a_owner) ? i_a_dat : i_b_dat):0;
+ assign o_sel = (o_stb)? ((r_a_owner) ? i_a_sel : i_b_sel):0;
+ assign o_a_ack = (o_cyc)&&( r_a_owner) ? i_ack : 1'b0;
+ assign o_b_ack = (o_cyc)&&(!r_a_owner) ? i_ack : 1'b0;
+ assign o_a_stall = (o_cyc)&&( r_a_owner) ? i_stall : 1'b1;
+ assign o_b_stall = (o_cyc)&&(!r_a_owner) ? i_stall : 1'b1;
+ assign o_a_err = (o_cyc)&&( r_a_owner) ? i_err : 1'b0;
+ assign o_b_err = (o_cyc)&&(!r_a_owner) ? i_err : 1'b0;
+ // }}}
+ end else begin : LOW_LOGIC
+ // {{{
+ assign o_stb = (r_a_owner) ? i_a_stb : i_b_stb;
+ assign o_adr = (r_a_owner) ? i_a_adr : i_b_adr;
+ assign o_dat = (r_a_owner) ? i_a_dat : i_b_dat;
+ assign o_sel = (r_a_owner) ? i_a_sel : i_b_sel;
+
+ // We cannot allow the return acknowledgement to ever go high if
+ // the master in question does not own the bus. Hence we force
+ // it low if the particular master doesn't own the bus.
+ assign o_a_ack = ( r_a_owner) ? i_ack : 1'b0;
+ assign o_b_ack = (!r_a_owner) ? i_ack : 1'b0;
+
+ // Stall must be asserted on the same cycle the input master
+ // asserts the bus, if the bus isn't granted to him.
+ assign o_a_stall = ( r_a_owner) ? i_stall : 1'b1;
+ assign o_b_stall = (!r_a_owner) ? i_stall : 1'b1;
+
+ //
+ //
+ assign o_a_err = ( r_a_owner) ? i_err : 1'b0;
+ assign o_b_err = (!r_a_owner) ? i_err : 1'b0;
+ // }}}
+ end endgenerate
+
+ // Make Verilator happy
+ // {{{
+ // verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, i_reset, F_LGDEPTH, F_MAX_STALL,
+ F_MAX_ACK_DELAY };
+ // verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+
+`ifdef WBARBITER
+
+`define ASSUME assume
+`else
+`define ASSUME assert
+`endif
+ reg f_prior_a_ack, f_prior_b_ack;
+
+
+ reg f_past_valid;
+ initial f_past_valid = 1'b0;
+ always @(posedge i_clk)
+ f_past_valid <= 1'b1;
+
+ initial `ASSUME(!i_a_cyc);
+ initial `ASSUME(!i_a_stb);
+
+ initial `ASSUME(!i_b_cyc);
+ initial `ASSUME(!i_b_stb);
+
+ initial `ASSUME(!i_ack);
+ initial `ASSUME(!i_err);
+
+ always @(*)
+ if (!f_past_valid)
+ `ASSUME(i_reset);
+
+ always @(posedge i_clk)
+ begin
+ if (o_cyc)
+ assert((i_a_cyc)||(i_b_cyc));
+ if ((f_past_valid)&&($past(o_cyc))&&(o_cyc))
+ assert($past(r_a_owner) == r_a_owner);
+ end
+
+ fwb_master #(
+ // {{{
+ .DW(DW), .AW(AW),
+ .F_MAX_STALL(F_MAX_STALL),
+ .F_LGDEPTH(F_LGDEPTH),
+ .F_MAX_ACK_DELAY(F_MAX_ACK_DELAY),
+ .F_OPT_RMW_BUS_OPTION(1),
+ .F_OPT_DISCONTINUOUS(1)
+ // }}}
+ ) f_wbm(
+ // {{{
+ i_clk, i_reset,
+ o_cyc, o_stb, o_we, o_adr, o_dat, o_sel,
+ i_ack, i_stall, 32'h0, i_err,
+ f_nreqs, f_nacks, f_outstanding
+ // }}}
+ );
+
+ fwb_slave #(
+ // {{{
+ .DW(DW), .AW(AW),
+ .F_MAX_STALL(0),
+ .F_LGDEPTH(F_LGDEPTH),
+ .F_MAX_ACK_DELAY(0),
+ .F_OPT_RMW_BUS_OPTION(1),
+ .F_OPT_DISCONTINUOUS(1)
+ // }}}
+ ) f_wba(
+ // {{{
+ i_clk, i_reset,
+ i_a_cyc, i_a_stb, i_a_we, i_a_adr, i_a_dat, i_a_sel,
+ o_a_ack, o_a_stall, 32'h0, o_a_err,
+ f_a_nreqs, f_a_nacks, f_a_outstanding
+ // }}}
+ );
+
+ fwb_slave #(
+ // {{{
+ .DW(DW), .AW(AW),
+ .F_MAX_STALL(0),
+ .F_LGDEPTH(F_LGDEPTH),
+ .F_MAX_ACK_DELAY(0),
+ .F_OPT_RMW_BUS_OPTION(1),
+ .F_OPT_DISCONTINUOUS(1)
+ // }}}
+ ) f_wbb(
+ // {{{
+ i_clk, i_reset,
+ i_b_cyc, i_b_stb, i_b_we, i_b_adr, i_b_dat, i_b_sel,
+ o_b_ack, o_b_stall, 32'h0, o_b_err,
+ f_b_nreqs, f_b_nacks, f_b_outstanding
+ // }}}
+ );
+
+ always @(posedge i_clk)
+ if (r_a_owner)
+ begin
+ assert(f_b_nreqs == 0);
+ assert(f_b_nacks == 0);
+ assert(f_a_outstanding == f_outstanding);
+ end else begin
+ assert(f_a_nreqs == 0);
+ assert(f_a_nacks == 0);
+ assert(f_b_outstanding == f_outstanding);
+ end
+
+ always @(posedge i_clk)
+ if ((f_past_valid)&&(!$past(i_reset))
+ &&($past(i_a_stb))&&(!$past(i_b_cyc)))
+ assert(r_a_owner);
+
+ always @(posedge i_clk)
+ if ((f_past_valid)&&(!$past(i_reset))
+ &&(!$past(i_a_cyc))&&($past(i_b_stb)))
+ assert(!r_a_owner);
+
+ always @(posedge i_clk)
+ if ((f_past_valid)&&(r_a_owner != $past(r_a_owner)))
+ assert(!$past(o_cyc));
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ initial f_prior_a_ack = 1'b0;
+ always @(posedge i_clk)
+ if ((i_reset)||(o_a_err)||(o_b_err))
+ f_prior_a_ack <= 1'b0;
+ else if ((o_cyc)&&(o_a_ack))
+ f_prior_a_ack <= 1'b1;
+
+ initial f_prior_b_ack = 1'b0;
+ always @(posedge i_clk)
+ if ((i_reset)||(o_a_err)||(o_b_err))
+ f_prior_b_ack <= 1'b0;
+ else if ((o_cyc)&&(o_b_ack))
+ f_prior_b_ack <= 1'b1;
+
+ always @(posedge i_clk)
+ begin
+ cover(f_prior_b_ack && o_cyc && o_a_ack);
+
+ cover((o_cyc && o_a_ack)
+ &&($past(o_cyc && o_a_ack))
+ &&($past(o_cyc && o_a_ack,2)));
+
+
+ cover(f_prior_a_ack && o_cyc && o_b_ack);
+
+ cover((o_cyc && o_b_ack)
+ &&($past(o_cyc && o_b_ack))
+ &&($past(o_cyc && o_b_ack,2)));
+ end
+
+ always @(*)
+ cover(o_cyc && o_b_ack);
+ // }}}
+// }}}
+`endif
+endmodule
+`ifndef YOSYS
+`default_nettype wire
+`endif