summaryrefslogtreecommitdiff
path: root/rtl/wb2axip/wbm2axisp.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/wbm2axisp.v
parent3b62399f92e9faa2602ac30865e5fc3c7c4e12b8 (diff)
rtl/wb2axip: add to version control
Diffstat (limited to 'rtl/wb2axip/wbm2axisp.v')
-rw-r--r--rtl/wb2axip/wbm2axisp.v1155
1 files changed, 1155 insertions, 0 deletions
diff --git a/rtl/wb2axip/wbm2axisp.v b/rtl/wb2axip/wbm2axisp.v
new file mode 100644
index 0000000..9c7909a
--- /dev/null
+++ b/rtl/wb2axip/wbm2axisp.v
@@ -0,0 +1,1155 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: wbm2axisp.v (Wishbone master to AXI slave, pipelined)
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: The B4 Wishbone SPEC allows transactions at a speed as fast as
+// one per clock. The AXI bus allows transactions at a speed of
+// one read and one write transaction per clock. These capabilities work
+// by allowing requests to take place prior to responses, such that the
+// requests might go out at once per clock and take several clocks, and
+// the responses may start coming back several clocks later. In other
+// words, both protocols allow multiple transactions to be "in flight" at
+// the same time. Current wishbone to AXI converters, however, handle only
+// one transaction at a time: initiating the transaction, and then waiting
+// for the transaction to complete before initiating the next.
+//
+// The purpose of this core is to maintain the speed of both buses, while
+// transiting from the Wishbone (as master) to the AXI bus (as slave) and
+// back again.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2016-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 wbm2axisp #(
+ // {{{
+ parameter C_AXI_DATA_WIDTH = 128,// Width of the AXI R&W data
+ parameter C_AXI_ADDR_WIDTH = 28, // AXI Address width (log wordsize)
+ parameter C_AXI_ID_WIDTH = 1,
+ parameter DW = 32, // Wishbone data width
+ parameter AW = 26, // Wishbone address width (log wordsize)
+ parameter [C_AXI_ID_WIDTH-1:0] AXI_WRITE_ID = 1'b0,
+ parameter [C_AXI_ID_WIDTH-1:0] AXI_READ_ID = 1'b1,
+ //
+ // OPT_LITTLE_ENDIAN controls which word has the greatest address
+ // when the bus size is adjusted. If OPT_LITTLE_ENDIAN is true,
+ // the biggest address is in the most significant word(s), otherwise
+ // the least significant word(s). This parameter is ignored if
+ // C_AXI_DATA_WIDTH == DW.
+ parameter [0:0] OPT_LITTLE_ENDIAN = 1'b1,
+ parameter LGFIFO = 6
+ // }}}
+ ) (
+ // {{{
+ input wire i_clk, // System clock
+ input wire i_reset,// Reset signal,drives AXI rst
+
+ // AXI write address channel signals
+ output reg o_axi_awvalid, // Write address valid
+ input wire i_axi_awready, // Slave is ready to accept
+ output wire [C_AXI_ID_WIDTH-1:0] o_axi_awid, // Write ID
+ output reg [C_AXI_ADDR_WIDTH-1:0] o_axi_awaddr, // Write address
+ output wire [7:0] o_axi_awlen, // Write Burst Length
+ output wire [2:0] o_axi_awsize, // Write Burst size
+ output wire [1:0] o_axi_awburst, // Write Burst type
+ output wire [0:0] o_axi_awlock, // Write lock type
+ output wire [3:0] o_axi_awcache, // Write Cache type
+ output wire [2:0] o_axi_awprot, // Write Protection type
+ output wire [3:0] o_axi_awqos, // Write Quality of Svc
+
+// AXI write data channel signals
+ output reg o_axi_wvalid, // Write valid
+ input wire i_axi_wready, // Write data ready
+ output reg [C_AXI_DATA_WIDTH-1:0] o_axi_wdata, // Write data
+ output reg [C_AXI_DATA_WIDTH/8-1:0] o_axi_wstrb, // Write strobes
+ output wire o_axi_wlast, // Last write transaction
+
+// AXI write response channel signals
+ input wire i_axi_bvalid, // Write reponse valid
+ output wire o_axi_bready, // Response ready
+ input wire [C_AXI_ID_WIDTH-1:0] i_axi_bid, // Response ID
+ input wire [1:0] i_axi_bresp, // Write response
+
+// AXI read address channel signals
+ output reg o_axi_arvalid, // Read address valid
+ input wire i_axi_arready, // Read address ready
+ output wire [C_AXI_ID_WIDTH-1:0] o_axi_arid, // Read ID
+ output reg [C_AXI_ADDR_WIDTH-1:0] o_axi_araddr, // Read address
+ output wire [7:0] o_axi_arlen, // Read Burst Length
+ output wire [2:0] o_axi_arsize, // Read Burst size
+ output wire [1:0] o_axi_arburst, // Read Burst type
+ output wire [0:0] o_axi_arlock, // Read lock type
+ output wire [3:0] o_axi_arcache, // Read Cache type
+ output wire [2:0] o_axi_arprot, // Read Protection type
+ output wire [3:0] o_axi_arqos, // Read Protection type
+
+// AXI read data channel signals
+ input wire i_axi_rvalid, // Read reponse valid
+ output wire o_axi_rready, // Read Response ready
+ input wire [C_AXI_ID_WIDTH-1:0] i_axi_rid, // Response ID
+ input wire [C_AXI_DATA_WIDTH-1:0] i_axi_rdata, // Read data
+ input wire [1:0] i_axi_rresp, // Read response
+ input wire i_axi_rlast, // Read last
+
+ // We'll share the clock and the reset
+ input wire i_wb_cyc,
+ input wire i_wb_stb,
+ input wire i_wb_we,
+ input wire [(AW-1):0] i_wb_addr,
+ input wire [(DW-1):0] i_wb_data,
+ input wire [(DW/8-1):0] i_wb_sel,
+ output reg o_wb_stall,
+ output reg o_wb_ack,
+ output reg [(DW-1):0] o_wb_data,
+ output reg o_wb_err
+ // }}}
+);
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Localparameter declarations, initial parameter consistency check
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ localparam LG_AXI_DW = $clog2(C_AXI_DATA_WIDTH);
+ localparam LG_WB_DW = $clog2(DW);
+ // localparam FIFOLN = (1<<LGFIFO);
+ localparam SUBW = LG_AXI_DW-LG_WB_DW;
+
+ // The various address widths must be properly related. We'll insist
+ // upon that relationship here.
+ initial begin
+ // This design can't (currently) handle WB widths wider than
+ // the AXI width it is driving. It can only handle widths
+ // mismatches in the other direction
+ if (C_AXI_DATA_WIDTH < DW)
+ $stop;
+ if (DW == 8 && AW != C_AXI_ADDR_WIDTH)
+ $stop;
+
+ // There must be a definitive relationship between the address
+ // widths of the AXI and WB, and that width is dependent upon
+ // the WB data width
+ if (C_AXI_ADDR_WIDTH != AW + $clog2(DW)-3)
+ $stop;
+ if ( (C_AXI_DATA_WIDTH / DW !=32)
+ &&(C_AXI_DATA_WIDTH / DW !=16)
+ &&(C_AXI_DATA_WIDTH / DW != 8)
+ &&(C_AXI_DATA_WIDTH / DW != 4)
+ &&(C_AXI_DATA_WIDTH / DW != 2)
+ &&(C_AXI_DATA_WIDTH != DW))
+ $stop;
+ end
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Internal register and wire declarations
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // Things we're not changing ...
+ localparam DWSIZE = $clog2(DW)-3;
+ assign o_axi_awid = AXI_WRITE_ID;
+ assign o_axi_awlen = 8'h0; // Burst length is one
+ assign o_axi_awsize = DWSIZE[2:0];
+ assign o_axi_wlast = 1;
+ assign o_axi_awburst = 2'b01; // Incrementing address (ignored)
+ assign o_axi_awlock = 1'b0; // Normal signaling
+ assign o_axi_arlock = 1'b0; // Normal signaling
+ assign o_axi_awcache = 4'h3; // Normal: no cache, modifiable
+ //
+ assign o_axi_arid = AXI_READ_ID;
+ assign o_axi_arlen = 8'h0; // Burst length is one
+ assign o_axi_arsize = DWSIZE[2:0];
+ assign o_axi_arburst = 2'b01; // Incrementing address (ignored)
+ assign o_axi_arcache = 4'h3; // Normal: no cache, modifiable
+ assign o_axi_awprot = 3'b010; // Unpriviledged, unsecure, data access
+ assign o_axi_arprot = 3'b010; // Unpriviledged, unsecure, data access
+ assign o_axi_awqos = 4'h0; // Lowest quality of service (unused)
+ assign o_axi_arqos = 4'h0; // Lowest quality of service (unused)
+
+ reg direction, full, empty, flushing, nearfull;
+ reg [LGFIFO:0] npending;
+ //
+ wire skid_ready, m_valid, m_we;
+ reg m_ready;
+ wire [AW-1:0] m_addr;
+ wire [DW-1:0] m_data;
+ wire [DW/8-1:0] m_sel;
+
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Overarching command logic
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ initial direction = 0;
+ always @(posedge i_clk)
+ if (empty)
+ direction <= m_we;
+
+ initial npending = 0;
+ initial empty = 1;
+ initial full = 0;
+ initial nearfull = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ begin
+ npending <= 0;
+ empty <= 1;
+ full <= 0;
+ nearfull <= 0;
+ end else case ({m_valid && m_ready, i_axi_bvalid||i_axi_rvalid})
+ 2'b10: begin
+ npending <= npending + 1;
+ empty <= 0;
+ nearfull <= &(npending[LGFIFO-1:1]);
+ full <= &(npending[LGFIFO-1:0]);
+ end
+ 2'b01: begin
+ nearfull <= full;
+ npending <= npending - 1;
+ empty <= (npending == 1);
+ full <= 0;
+ end
+ default: begin end
+ endcase
+
+ initial flushing = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ flushing <= 0;
+ else if ((i_axi_rvalid && i_axi_rresp[1])
+ ||(i_axi_bvalid && i_axi_bresp[1])
+ ||(!i_wb_cyc && !empty))
+ flushing <= 1'b1;
+ else if (empty)
+ flushing <= 1'b0;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Wishbone input skidbuffer
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ skidbuffer #(
+ // {{{
+ .DW(1+AW+DW+(DW/8)),
+ .OPT_OUTREG(1'b0)
+ // }}}
+ ) skid (
+ // {{{
+ .i_clk(i_clk), .i_reset(i_reset || !i_wb_cyc),
+ .i_valid(i_wb_stb), .o_ready(skid_ready),
+ .i_data({ i_wb_we, i_wb_addr, i_wb_data, i_wb_sel }),
+ .o_valid(m_valid), .i_ready(m_ready),
+ .o_data({ m_we, m_addr, m_data, m_sel })
+ // }}}
+ );
+
+ always @(*)
+ o_wb_stall = !skid_ready;
+
+ always @(*)
+ begin
+ m_ready = 1;
+
+ if (flushing || nearfull || ((m_we != direction)&&(!empty)))
+ m_ready = 1'b0;
+ if (o_axi_awvalid && !i_axi_awready)
+ m_ready = 1'b0;
+ if (o_axi_wvalid && !i_axi_wready)
+ m_ready = 1'b0;
+ if (o_axi_arvalid && !i_axi_arready)
+ m_ready = 1'b0;
+ end
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI Signaling
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // Write transactions
+ //
+
+ // awvalid, wvalid
+ // {{{
+ // Send write transactions
+ initial o_axi_awvalid = 0;
+ initial o_axi_wvalid = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ begin
+ o_axi_awvalid <= 0;
+ o_axi_wvalid <= 0;
+ end else if (m_valid && m_we && m_ready)
+ begin
+ o_axi_awvalid <= 1;
+ o_axi_wvalid <= 1;
+ end else begin
+ if (i_axi_awready)
+ o_axi_awvalid <= 0;
+ if (i_axi_wready)
+ o_axi_wvalid <= 0;
+ end
+ // }}}
+
+ // wdata
+ // {{{
+ always @(posedge i_clk)
+ if (!o_axi_wvalid || i_axi_wready)
+ o_axi_wdata <= {(C_AXI_DATA_WIDTH/DW){m_data}};
+ // }}}
+
+ // wstrb
+ // {{{
+ generate if (DW == C_AXI_DATA_WIDTH)
+ begin : NO_WSTRB_ADJUSTMENT
+ // {{{
+ always @(posedge i_clk)
+ if (!o_axi_wvalid || i_axi_wready)
+ o_axi_wstrb <= m_sel;
+ // }}}
+ end else if (OPT_LITTLE_ENDIAN)
+ begin : LITTLE_ENDIAN_WSTRB
+ // {{{
+ always @(posedge i_clk)
+ if (!o_axi_wvalid || i_axi_wready)
+ // Verilator lint_off WIDTH
+ o_axi_wstrb <= m_sel << ((DW/8) * m_addr[SUBW-1:0]);
+ // Verilator lint_on WIDTH
+ // }}}
+ end else begin : BIG_ENDIAN_WSTRB
+ // {{{
+ reg [SUBW-1:0] neg_addr;
+
+ always @(*)
+ neg_addr = ~m_addr[SUBW-1:0];
+
+ always @(posedge i_clk)
+ if (!o_axi_wvalid || i_axi_wready)
+ // Verilator lint_off WIDTH
+ o_axi_wstrb <= m_sel << ((DW/8)* neg_addr);
+ // Verilator lint_on WIDTH
+ // }}}
+ end endgenerate
+ // }}}
+
+ //
+ // Read transactions
+ //
+
+ // arvalid
+ // {{{
+ initial o_axi_arvalid = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ o_axi_arvalid <= 0;
+ else if (m_valid && !m_we && m_ready)
+ o_axi_arvalid <= 1;
+ else if (i_axi_arready)
+ begin
+ o_axi_arvalid <= 0;
+ end
+ // }}}
+
+ // awaddr, araddr
+ // {{{
+ generate if (OPT_LITTLE_ENDIAN || DW == C_AXI_DATA_WIDTH)
+ begin : GEN_ADDR_LSBS
+ // {{{
+ always @(posedge i_clk)
+ if (!o_axi_awvalid || i_axi_awready)
+ o_axi_awaddr <= { m_addr, {($clog2(DW)-3){1'b0}} };
+
+ always @(posedge i_clk)
+ if (!o_axi_arvalid || i_axi_arready)
+ o_axi_araddr <= { m_addr, {($clog2(DW)-3){1'b0}} };
+ // }}}
+ end else begin : OPT_BIG_ENDIAN
+ // {{{
+ reg [SUBW-1:0] neg_addr;
+
+ always @(*)
+ neg_addr = ~m_addr[SUBW-1:0];
+
+ always @(posedge i_clk)
+ if (!o_axi_awvalid || i_axi_awready)
+ begin
+ o_axi_awaddr <= 0;
+ o_axi_awaddr <= m_addr << ($clog2(DW)-3);
+ o_axi_awaddr[$clog2(DW)-3 +: SUBW] <= neg_addr;
+ end
+
+ always @(posedge i_clk)
+ if (!o_axi_arvalid || i_axi_arready)
+ begin
+ o_axi_araddr <= 0;
+ o_axi_araddr <= m_addr << ($clog2(DW)-3);
+ o_axi_araddr[$clog2(DW)-3 +: SUBW] <= neg_addr;
+ end
+ // }}}
+ end endgenerate
+ // }}}
+
+ // rdata, and returned o_wb_data, o_wb_ack, o_wb_err
+ // {{{
+ generate if (DW == C_AXI_DATA_WIDTH)
+ begin : NO_READ_DATA_SELECT_NECESSARY
+ // {{{
+ always @(*)
+ o_wb_data = i_axi_rdata;
+
+ always @(*)
+ o_wb_ack = !flushing&&((i_axi_rvalid && !i_axi_rresp[1])
+ ||(i_axi_bvalid && !i_axi_bresp[1]));
+
+ always @(*)
+ o_wb_err = !flushing&&((i_axi_rvalid && i_axi_rresp[1])
+ ||(i_axi_bvalid && i_axi_bresp[1]));
+ // }}}
+ end else begin : READ_FIFO_DATA_SELECT
+ // {{{
+
+ reg [SUBW-1:0] addr_fifo [0:(1<<LGFIFO)-1];
+ reg [SUBW-1:0] fifo_value;
+ reg [LGFIFO:0] wr_addr, rd_addr;
+ wire [C_AXI_DATA_WIDTH-1:0] return_data;
+
+ initial o_wb_ack = 0;
+ always @(posedge i_clk)
+ if (i_reset || !i_wb_cyc || flushing)
+ o_wb_ack <= 0;
+ else
+ o_wb_ack <= ((i_axi_rvalid && !i_axi_rresp[1])
+ ||(i_axi_bvalid && !i_axi_bresp[1]));
+
+ initial o_wb_err = 0;
+ always @(posedge i_clk)
+ if (i_reset || !i_wb_cyc || flushing)
+ o_wb_err <= 0;
+ else
+ o_wb_err <= ((i_axi_rvalid && i_axi_rresp[1])
+ ||(i_axi_bvalid && i_axi_bresp[1]));
+
+
+ initial wr_addr = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ wr_addr <= 0;
+ else if (m_valid && m_ready)
+ wr_addr <= wr_addr + 1;
+
+ always @(posedge i_clk)
+ if (m_valid && m_ready)
+ addr_fifo[wr_addr[LGFIFO-1:0]] <= m_addr[SUBW-1:0];
+
+ initial rd_addr = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ rd_addr <= 0;
+ else if (i_axi_bvalid || i_axi_rvalid)
+ rd_addr <= rd_addr + 1;
+
+ always @(*)
+ fifo_value = addr_fifo[rd_addr[LGFIFO-1:0]];
+
+ if (OPT_LITTLE_ENDIAN)
+ begin : LITTLE_ENDIAN_RDATA
+
+ assign return_data = i_axi_rdata >> (fifo_value * DW);
+
+ end else begin : BIG_ENDIAN_RDATA
+
+ reg [SUBW-1:0] neg_fifo_value;
+
+ always @(*)
+ neg_fifo_value = ~fifo_value;
+
+ assign return_data = i_axi_rdata
+ >> (neg_fifo_value * DW);
+
+ end
+
+ always @(posedge i_clk)
+ o_wb_data <= return_data[DW-1:0];
+
+ // Make Verilator happy here
+ // verilator lint_off UNUSED
+ if (C_AXI_DATA_WIDTH > DW)
+ begin : UNUSED_DATA
+ wire unused_data;
+ assign unused_data = &{ 1'b0,
+ return_data[C_AXI_DATA_WIDTH-1:DW] };
+ end
+ // verilator lint_on UNUSED
+`ifdef FORMAL
+ always @(*)
+ assert(wr_addr - rd_addr == npending);
+
+ always @(*)
+ assert(empty == (wr_addr == rd_addr));
+
+
+ //
+ // ...
+ //
+`endif
+ // }}}
+ end endgenerate
+ // }}}
+
+ // Read data channel / response logic
+ assign o_axi_rready = 1'b1;
+ assign o_axi_bready = 1'b1;
+ // }}}
+
+ // Make verilator's -Wall happy
+ // {{{
+ // verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, full, i_axi_bid, i_axi_bresp[0], i_axi_rid, i_axi_rresp[0], i_axi_rlast, m_data, m_sel };
+ generate if (C_AXI_DATA_WIDTH > DW)
+ begin : GEN_UNUSED_DW
+ wire [C_AXI_DATA_WIDTH-1:DW] unused_data;
+ assign unused_data = i_axi_rdata[C_AXI_DATA_WIDTH-1:DW];
+ end endgenerate
+ // verilator lint_on UNUSED
+ // }}}
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+//
+// Formal methods section
+// {{{
+// Below are a scattering of the formal properties used. They are not the
+// complete set of properties. Those are maintained elsewhere.
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ //
+ // ...
+ //
+
+ // Parameters
+ initial assert( (C_AXI_DATA_WIDTH / DW ==32)
+ ||(C_AXI_DATA_WIDTH / DW ==16)
+ ||(C_AXI_DATA_WIDTH / DW == 8)
+ ||(C_AXI_DATA_WIDTH / DW == 4)
+ ||(C_AXI_DATA_WIDTH / DW == 2)
+ ||(C_AXI_DATA_WIDTH == DW));
+ //
+ initial assert( C_AXI_ADDR_WIDTH == AW + (LG_WB_DW-3));
+
+
+ initial begin
+ assert(C_AXI_DATA_WIDTH >= DW);
+ assert((DW == 8) == (AW == C_AXI_ADDR_WIDTH));
+ assert(C_AXI_ADDR_WIDTH == AW + $clog2(DW)-3);
+ end
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Setup / f_past_valid
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ initial f_past_valid = 1'b0;
+ always @(posedge i_clk)
+ f_past_valid <= 1'b1;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Assumptions about the WISHBONE inputs
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ always @(*)
+ if (!f_past_valid)
+ assume(i_reset);
+
+ fwb_slave #(.DW(DW),.AW(AW),
+ .F_MAX_STALL(0),
+ .F_MAX_ACK_DELAY(0),
+ .F_LGDEPTH(F_LGDEPTH),
+ .F_MAX_REQUESTS(0))
+ f_wb(i_clk, i_reset, i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr,
+ i_wb_data, i_wb_sel,
+ o_wb_ack, o_wb_stall, o_wb_data, o_wb_err,
+ f_wb_nreqs, f_wb_nacks, f_wb_outstanding);
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Assumptions about the AXI inputs
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ faxi_master #(
+ // {{{
+ .C_AXI_ID_WIDTH(C_AXI_ID_WIDTH),
+ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH)
+ // ...
+ // }}}
+ ) f_axi(.i_clk(i_clk), .i_axi_reset_n(!i_reset),
+ // {{{
+ // Write address channel
+ .i_axi_awready(i_axi_awready),
+ .i_axi_awid( o_axi_awid),
+ .i_axi_awaddr( o_axi_awaddr),
+ .i_axi_awlen( o_axi_awlen),
+ .i_axi_awsize( o_axi_awsize),
+ .i_axi_awburst(o_axi_awburst),
+ .i_axi_awlock( o_axi_awlock),
+ .i_axi_awcache(o_axi_awcache),
+ .i_axi_awprot( o_axi_awprot),
+ .i_axi_awqos( o_axi_awqos),
+ .i_axi_awvalid(o_axi_awvalid),
+ // Write data channel
+ .i_axi_wready( i_axi_wready),
+ .i_axi_wdata( o_axi_wdata),
+ .i_axi_wstrb( o_axi_wstrb),
+ .i_axi_wlast( o_axi_wlast),
+ .i_axi_wvalid( o_axi_wvalid),
+ // Write response channel
+ .i_axi_bid( i_axi_bid),
+ .i_axi_bresp( i_axi_bresp),
+ .i_axi_bvalid( i_axi_bvalid),
+ .i_axi_bready( o_axi_bready),
+ // Read address channel
+ .i_axi_arready(i_axi_arready),
+ .i_axi_arid( o_axi_arid),
+ .i_axi_araddr( o_axi_araddr),
+ .i_axi_arlen( o_axi_arlen),
+ .i_axi_arsize( o_axi_arsize),
+ .i_axi_arburst(o_axi_arburst),
+ .i_axi_arlock( o_axi_arlock),
+ .i_axi_arcache(o_axi_arcache),
+ .i_axi_arprot( o_axi_arprot),
+ .i_axi_arqos( o_axi_arqos),
+ .i_axi_arvalid(o_axi_arvalid),
+ // Read data channel
+ .i_axi_rid( i_axi_rid),
+ .i_axi_rresp( i_axi_rresp),
+ .i_axi_rvalid( i_axi_rvalid),
+ .i_axi_rdata( i_axi_rdata),
+ .i_axi_rlast( i_axi_rlast),
+ .i_axi_rready( o_axi_rready),
+ // Counts
+ .f_axi_awr_nbursts(f_axi_awr_nbursts),
+ .f_axi_wr_pending(f_axi_wr_pending),
+ .f_axi_rd_nbursts(f_axi_rd_nbursts),
+ .f_axi_rd_outstanding(f_axi_rd_outstanding)
+ //
+ // ...
+ //
+ // }}}
+ );
+
+ always @(*)
+ if (!flushing && i_wb_cyc)
+ assert(f_wb_outstanding == npending + (r_stb ? 1:0)
+ + ( ((C_AXI_DATA_WIDTH != DW)
+ && (o_wb_ack|o_wb_err))? 1:0));
+ else if (flushing && i_wb_cyc && !o_wb_err)
+ assert(f_wb_outstanding == (r_stb ? 1:0));
+
+ always @(*)
+ if (f_axi_awr_nbursts > 0)
+ begin
+ assert(direction);
+ assert(f_axi_rd_nbursts == 0);
+ assert(f_axi_awr_nbursts + (o_axi_awvalid ? 1:0) == npending);
+ assert(f_axi_wr_pending == (o_axi_wvalid&&!o_axi_awvalid ? 1:0));
+
+ //
+ // ...
+ //
+ end
+ always @(*)
+ if (o_axi_awvalid)
+ assert(o_axi_wvalid);
+
+ // Some quick read checks
+ always @(*)
+ if (f_axi_rd_nbursts > 0)
+ begin
+ assert(!direction);
+ assert(f_axi_rd_nbursts+(o_axi_arvalid ? 1:0)
+ == npending);
+ assert(f_axi_awr_nbursts == 0);
+
+ //
+ // ...
+ //
+ end
+
+ always @(*)
+ if (direction)
+ begin
+ assert(npending == (o_axi_awvalid ? 1:0) + f_axi_awr_nbursts);
+ assert(!o_axi_arvalid);
+ assert(f_axi_rd_nbursts == 0);
+ assert(!i_axi_rvalid);
+ end else begin
+ assert(npending == (o_axi_arvalid ? 1:0) + f_axi_rd_nbursts);
+ assert(!o_axi_awvalid);
+ assert(!o_axi_wvalid);
+ assert(f_axi_awr_nbursts == 0);
+ end
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Pending counter properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ always @(*)
+ begin
+ assert(npending <= { 1'b1, {(LGFIFO){1'b0}} });
+ assert(empty == (npending == 0));
+ assert(full == (npending == {1'b1, {(LGFIFO){1'b0}} }));
+ assert(nearfull == (npending >= {1'b0, {(LGFIFO){1'b1}} }));
+ if (full)
+ assert(!m_ready);
+ end
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Assertions about the AXI4 ouputs
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // Write response channel
+ always @(posedge i_clk)
+ // We keep bready high, so the other condition doesn't
+ // need to be checked
+ assert(o_axi_bready);
+
+ // AXI read data channel signals
+ always @(posedge i_clk)
+ // We keep o_axi_rready high, so the other condition's
+ // don't need to be checked here
+ assert(o_axi_rready);
+
+ //
+ // AXI write address channel
+ //
+ //
+ always @(*)
+ begin
+ if (o_axi_awvalid || o_axi_wvalid || f_axi_awr_nbursts>0)
+ assert(direction);
+ //
+ // ...
+ //
+ end
+ //
+ // AXI read address channel
+ //
+ always @(*)
+ begin
+ if (o_axi_arvalid || i_axi_rvalid || f_axi_rd_nbursts > 0)
+ assert(!direction);
+ //
+ // ...
+ //
+ end
+
+ //
+ // AXI write response channel
+ //
+
+
+ //
+ // AXI read data channel signals
+ //
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Formal contract check
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Prove that a write to this address will change this value
+ //
+
+ // Some extra register declarations
+ // {{{
+ (* anyconst *) reg [C_AXI_ADDR_WIDTH-1:0] f_const_addr;
+ reg [C_AXI_DATA_WIDTH-1:0] f_data;
+ // }}}
+
+ //
+ // Assume a basic bus response to the given data and address
+ //
+ integer iN;
+
+ // f_data
+ // {{{
+ initial f_data = 0;
+ always @(posedge i_clk)
+ if (o_axi_wvalid && i_axi_wready && o_axi_awaddr == f_const_addr)
+ begin
+ for(iN=0; iN<C_AXI_DATA_WIDTH/8; iN=iN+1)
+ begin
+ if (o_axi_wstrb[iN])
+ f_data[8*iN +: 8] <= o_axi_wdata[8*iN +: 8];
+ end
+ end
+ // }}}
+
+ // Assume RDATA == f_data if appropriate
+ // {{{
+ always @(*)
+ if (i_axi_rvalid && o_axi_rready && f_axi_rd_ckvalid
+ && (f_axi_rd_ckaddr == f_const_addr))
+ assume(i_axi_rdata == f_data);
+ // }}}
+
+ // f_wb_addr -- A WB address designed to match f_const_addr (AXI addr)
+ // {{{
+ always @(*)
+ begin
+ f_wb_addr = f_const_addr[C_AXI_ADDR_WIDTH-1:DWSIZE];
+ if (!OPT_LITTLE_ENDIAN && SUBW > 0)
+ f_wb_addr[0 +: SUBW] = ~f_wb_addr[0 +: SUBW];
+ end
+ // }}}
+
+ // Assume the address is Wishbone word aligned
+ // {{{
+ generate if (DW > 8)
+ begin
+ always @(*)
+ assume(f_const_addr[$clog2(DW)-4:0] == 0);
+ end endgenerate
+ // }}}
+
+ // f_axi_data -- Replicate f_wb_data across the whole word
+ // {{{
+ always @(*)
+ f_axi_data = {(C_AXI_DATA_WIDTH/DW){f_wb_data}};
+ // }}}
+
+ //
+ // ...
+ //
+
+ always @(*)
+ begin
+ f_valid_wb_response = 1;
+ for(iN=0; iN<DW/8; iN=iN+1)
+ begin
+ if (f_wb_strb[iN] && (o_wb_data[iN*8 +: 8] != f_wb_data[iN*8 +: 8]))
+ f_valid_wb_response = 0;
+ end
+ end
+ // }}}
+
+ // f_valid_axi_data
+ // {{{
+ always @(*)
+ begin
+ f_valid_axi_data = 1;
+ for(iN=0; iN<C_AXI_DATA_WIDTH/8; iN=iN+1)
+ begin
+ if (f_axi_strb[iN] && (f_axi_data[iN*8 +: 8] != f_data[iN*8 +: 8]))
+ f_valid_axi_data = 0;
+ end
+ end
+ // }}}
+
+ // f_valid_axi_response
+ // {{{
+ always @(*)
+ begin
+ f_valid_axi_response = 1;
+ for(iN=0; iN<C_AXI_DATA_WIDTH/8; iN=iN+1)
+ begin
+ if (f_axi_strb[iN] && (i_axi_rdata[iN*8 +: 8] != f_data[iN*8 +: 8]))
+ f_valid_axi_response = 0;
+ end
+ end
+ // }}}
+
+ //
+ // ...
+ //
+
+ generate if (DW == C_AXI_DATA_WIDTH)
+ begin
+
+ always @(*)
+ f_axi_strb = f_wb_strb;
+
+ end else if (OPT_LITTLE_ENDIAN)
+ begin
+
+ always @(*)
+ f_axi_strb <= f_wb_strb << ( (DW/8) *
+ f_wb_addr[SUBW-1:0]);
+
+ end else // if (!OPT_LITTLE_ENDIAN)
+ begin
+ reg [SUBW-1:0] f_neg_addr;
+
+ always @(*)
+ f_neg_addr = ~f_wb_addr[SUBW-1:0];
+
+ always @(*)
+ f_axi_strb <= f_wb_strb << ( (DW/8) * f_neg_addr );
+
+ end endgenerate
+ // }}}
+
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Ad-hoc assertions
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ generate if (DW > 8)
+ begin
+ always @(*)
+ if (o_axi_awvalid)
+ assert(o_axi_awaddr[$clog2(DW)-4:0] == 0);
+
+ always @(*)
+ if (o_axi_arvalid)
+ assert(o_axi_araddr[$clog2(DW)-4:0] == 0);
+
+ end endgenerate
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ reg [F_LGDEPTH-1:0] r_hit_reads, r_hit_writes,
+ r_check_fault, check_fault,
+ cvr_nreads, cvr_nwrites;
+ reg cvr_flushed, cvr_read2write, cvr_write2read;
+
+ initial r_hit_reads = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ r_hit_writes <= 0;
+ else if (f_axi_awr_nbursts > 3)
+ r_hit_writes <= 1;
+
+ initial r_hit_reads = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ r_hit_reads <= 0;
+ else if (f_axi_rd_nbursts > 3)
+ r_hit_reads <= 1;
+
+ always @(*)
+ begin
+ check_fault = 0;
+ if (!i_wb_cyc && o_axi_awvalid)
+ check_fault = 1;
+ if (!i_wb_cyc && o_axi_wvalid)
+ check_fault = 1;
+ if (!i_wb_cyc && f_axi_awr_nbursts > 0)
+ check_fault = 1;
+ if (!i_wb_cyc && i_axi_bvalid)
+ check_fault = 1;
+ //
+ if (!i_wb_cyc && o_axi_arvalid)
+ check_fault = 1;
+ if (!i_wb_cyc && f_axi_rd_outstanding > 0)
+ check_fault = 1;
+ if (!i_wb_cyc && i_axi_rvalid)
+ check_fault = 1;
+ if (!i_wb_cyc && (o_wb_ack | o_wb_err))
+ check_fault = 1;
+
+ if (flushing)
+ check_fault = 1;
+ end
+
+ initial r_check_fault = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ r_check_fault <= 0;
+ else if (check_fault)
+ r_check_fault <= 1;
+
+ always @(*)
+ cover(r_hit_writes && r_hit_reads && f_axi_rd_nbursts == 0
+ && f_axi_awr_nbursts == 0
+ && !o_axi_awvalid && !o_axi_arvalid && !o_axi_wvalid
+ && !i_axi_bvalid && !i_axi_rvalid
+ && !o_wb_ack && !o_wb_stall && !i_wb_stb
+ && !check_fault && !r_check_fault);
+
+ //
+ // ...
+ //
+
+ initial cvr_flushed = 1'b0;
+ always @(posedge i_clk)
+ if (i_reset)
+ cvr_flushed <= 1'b0;
+ else if (flushing)
+ cvr_flushed <= 1'b1;
+
+ always @(*)
+ begin
+ cover(!i_reset && cvr_flushed && !flushing);
+ cover(!i_reset && cvr_flushed && !flushing && !o_wb_stall);
+ end
+
+ //
+ // Let's cover our ability to turn around, from reads to writes or from
+ // writes to reads.
+ //
+ // Note that without the RMW option above, switching direction requires
+ // dropping i_wb_cyc. Let's just make certain here, that if we do so,
+ // we don't drop it until after all of the returns come back.
+ //
+ initial cvr_read2write = 0;
+ always @(posedge i_clk)
+ if (i_reset || (!i_wb_cyc && f_wb_nreqs != f_wb_nacks))
+ cvr_read2write <= 0;
+ else if (!direction && !empty && m_we)
+ cvr_read2write <= 1;
+
+ initial cvr_write2read = 0;
+ always @(posedge i_clk)
+ if (i_reset || (!i_wb_cyc && f_wb_nreqs != f_wb_nacks))
+ cvr_write2read <= 0;
+ else if (direction && !empty && !m_we)
+ cvr_write2read <= 1;
+
+ always @(*)
+ begin
+ cover(cvr_read2write && direction && o_wb_ack && f_wb_outstanding == 1);
+ cover(cvr_write2read && !direction && o_wb_ack && f_wb_outstanding == 1);
+ end
+
+ reg [2:0] cvr_ack_after_abort;
+
+ initial cvr_ack_after_abort = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ cvr_ack_after_abort <= 0;
+ else begin
+ if (!i_wb_cyc)
+ cvr_ack_after_abort[2:0] <= (empty) ? 0 : 3'b01;
+ if (cvr_ack_after_abort[0] && i_wb_cyc && r_stb && flushing)
+ cvr_ack_after_abort[1] <= 1;
+ if (o_wb_ack && &cvr_ack_after_abort[1:0])
+ cvr_ack_after_abort[2] <= 1;
+ end
+
+ always @(*)
+ cover(&cvr_ack_after_abort[1:0]);
+ always @(*)
+ cover(!flushing && (&cvr_ack_after_abort[1:0]));
+ always @(*)
+ cover(&cvr_ack_after_abort[2:0]);
+ always @(*)
+ cover(!i_wb_cyc && &cvr_ack_after_abort[2:0]);
+
+ initial cvr_nwrites = 0;
+ always @(posedge i_clk)
+ if (i_reset || flushing || !i_wb_cyc || !i_wb_we || o_wb_err)
+ cvr_nwrites <= 0;
+ else if (i_axi_bvalid && o_axi_bready)
+ cvr_nwrites <= cvr_nwrites + 1;
+
+ initial cvr_nreads = 0;
+ always @(posedge i_clk)
+ if (i_reset || flushing || !i_wb_cyc || i_wb_we || o_wb_err)
+ cvr_nreads <= 0;
+ else if (i_axi_rvalid && o_axi_rready)
+ cvr_nreads <= cvr_nreads + 1;
+
+ always @(*)
+ cover(cvr_nwrites == 3 && !o_wb_ack && !o_wb_err && !i_wb_cyc);
+
+ always @(*)
+ cover(cvr_nreads == 3 && !o_wb_ack && !o_wb_err && !i_wb_cyc);
+
+ //
+ // Generate a cover that doesn't include an abort
+ // {{{
+ (* anyconst *) reg f_never_abort;
+
+ always @(*)
+ if (f_never_abort && f_wb_nacks != f_wb_nreqs)
+ assume(!i_reset && i_wb_cyc && !o_wb_err);
+
+ always @(posedge i_clk)
+ if (f_never_abort && $past(o_wb_ack) && o_wb_ack)
+ assume($changed(o_wb_data));
+
+ always @(*)
+ cover(cvr_nreads == 3 && !o_wb_ack && !o_wb_err && !i_wb_cyc
+ && f_never_abort);
+ // }}}
+
+ // }}}
+`endif // FORMAL
+// }}}
+endmodule