blob: cbd37f589718a27cdb581b7115708212127fb8d4 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// Filename: demofull.v
//
// Project: WB2AXIPSP: bus bridges and other odds and ends
//
// Purpose: Demonstrate a formally verified AXI4 core with a (basic)
// interface. This interface is explained below.
//
// Performance: This core has been designed for a total throughput of one beat
// per clock cycle. Both read and write channels can achieve
// this. The write channel will also introduce two clocks of latency,
// assuming no other latency from the master. This means it will take
// a minimum of 3+AWLEN clock cycles per transaction of (1+AWLEN) beats,
// including both address and acknowledgment cycles. The read channel
// will introduce a single clock of latency, requiring 2+ARLEN cycles
// per transaction of 1+ARLEN beats.
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2019-2020, 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 demofull #(
parameter integer C_S_AXI_ID_WIDTH = 2,
parameter integer C_S_AXI_DATA_WIDTH = 32,
parameter integer C_S_AXI_ADDR_WIDTH = 6,
// Some useful short-hand definitions
localparam AW = C_S_AXI_ADDR_WIDTH,
localparam DW = C_S_AXI_DATA_WIDTH,
localparam IW = C_S_AXI_ID_WIDTH,
localparam LSB = $clog2(C_S_AXI_DATA_WIDTH)-3,
parameter [0:0] OPT_NARROW_BURST = 1
) (
// Users to add ports here
// A very basic protocol-independent peripheral interface
// 1. A value will be written any time o_we is true
// 2. A value will be read any time o_rd is true
// 3. Such a slave might just as easily be written as:
//
// always @(posedge S_AXI_ACLK)
// if (o_we)
// begin
// for(k=0; k<C_S_AXI_DATA_WIDTH/8; k=k+1)
// begin
// if (o_wstrb[k])
// mem[o_waddr[AW-1:LSB]][k*8+:8] <= o_wdata[k*8+:8]
// end
// end
//
// always @(posedge S_AXI_ACLK)
// if (o_rd)
// i_rdata <= mem[o_raddr[AW-1:LSB]];
//
// 4. The rule on the input is that i_rdata must be registered,
// and that it must only change if o_rd is true. Violating
// this rule will cause this core to violate the AXI
// protocol standard, as this value is not registered within
// this core
output reg o_we,
output reg [C_S_AXI_ADDR_WIDTH-LSB-1:0] o_waddr,
output reg [C_S_AXI_DATA_WIDTH-1:0] o_wdata,
output reg [C_S_AXI_DATA_WIDTH/8-1:0] o_wstrb,
//
output reg o_rd,
output reg [C_S_AXI_ADDR_WIDTH-LSB-1:0] o_raddr,
input wire [C_S_AXI_DATA_WIDTH-1:0] i_rdata,
//
// User ports ends
// Do not modify the ports beyond this line
// Global Clock Signal
input wire S_AXI_ACLK,
// Global Reset Signal. This Signal is Active LOW
input wire S_AXI_ARESETN,
// Write Address ID
input wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_AWID,
// Write address
input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_AWADDR,
// Burst length. The burst length gives the exact number of
// transfers in a burst
input wire [7 : 0] S_AXI_AWLEN,
// Burst size. This signal indicates the size of each transfer
// in the burst
input wire [2 : 0] S_AXI_AWSIZE,
// Burst type. The burst type and the size information,
// determine how the address for each transfer within the burst
// is calculated.
input wire [1 : 0] S_AXI_AWBURST,
// Lock type. Provides additional information about the
// atomic characteristics of the transfer.
input wire S_AXI_AWLOCK,
// Memory type. This signal indicates how transactions
// are required to progress through a system.
input wire [3 : 0] S_AXI_AWCACHE,
// Protection type. This signal indicates the privilege
// and security level of the transaction, and whether
// the transaction is a data access or an instruction access.
input wire [2 : 0] S_AXI_AWPROT,
// Quality of Service, QoS identifier sent for each
// write transaction.
input wire [3 : 0] S_AXI_AWQOS,
// Region identifier. Permits a single physical interface
// on a slave to be used for multiple logical interfaces.
// Write address valid. This signal indicates that
// the channel is signaling valid write address and
// control information.
input wire S_AXI_AWVALID,
// Write address ready. This signal indicates that
// the slave is ready to accept an address and associated
// control signals.
output wire S_AXI_AWREADY,
// Write Data
input wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_WDATA,
// Write strobes. This signal indicates which byte
// lanes hold valid data. There is one write strobe
// bit for each eight bits of the write data bus.
input wire [(C_S_AXI_DATA_WIDTH/8)-1 : 0] S_AXI_WSTRB,
// Write last. This signal indicates the last transfer
// in a write burst.
input wire S_AXI_WLAST,
// Optional User-defined signal in the write data channel.
// Write valid. This signal indicates that valid write
// data and strobes are available.
input wire S_AXI_WVALID,
// Write ready. This signal indicates that the slave
// can accept the write data.
output wire S_AXI_WREADY,
// Response ID tag. This signal is the ID tag of the
// write response.
output wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_BID,
// Write response. This signal indicates the status
// of the write transaction.
output wire [1 : 0] S_AXI_BRESP,
// Optional User-defined signal in the write response channel.
// Write response valid. This signal indicates that the
// channel is signaling a valid write response.
output wire S_AXI_BVALID,
// Response ready. This signal indicates that the master
// can accept a write response.
input wire S_AXI_BREADY,
// Read address ID. This signal is the identification
// tag for the read address group of signals.
input wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_ARID,
// Read address. This signal indicates the initial
// address of a read burst transaction.
input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_ARADDR,
// Burst length. The burst length gives the exact number of
// transfers in a burst
input wire [7 : 0] S_AXI_ARLEN,
// Burst size. This signal indicates the size of each transfer
// in the burst
input wire [2 : 0] S_AXI_ARSIZE,
// Burst type. The burst type and the size information,
// determine how the address for each transfer within the
// burst is calculated.
input wire [1 : 0] S_AXI_ARBURST,
// Lock type. Provides additional information about the
// atomic characteristics of the transfer.
input wire S_AXI_ARLOCK,
// Memory type. This signal indicates how transactions
// are required to progress through a system.
input wire [3 : 0] S_AXI_ARCACHE,
// Protection type. This signal indicates the privilege
// and security level of the transaction, and whether
// the transaction is a data access or an instruction access.
input wire [2 : 0] S_AXI_ARPROT,
// Quality of Service, QoS identifier sent for each
// read transaction.
input wire [3 : 0] S_AXI_ARQOS,
// Region identifier. Permits a single physical interface
// on a slave to be used for multiple logical interfaces.
// Optional User-defined signal in the read address channel.
// Write address valid. This signal indicates that
// the channel is signaling valid read address and
// control information.
input wire S_AXI_ARVALID,
// Read address ready. This signal indicates that
// the slave is ready to accept an address and associated
// control signals.
output wire S_AXI_ARREADY,
// Read ID tag. This signal is the identification tag
// for the read data group of signals generated by the slave.
output wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_RID,
// Read Data
output wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_RDATA,
// Read response. This signal indicates the status of
// the read transfer.
output wire [1 : 0] S_AXI_RRESP,
// Read last. This signal indicates the last transfer
// in a read burst.
output wire S_AXI_RLAST,
// Optional User-defined signal in the read address channel.
// Read valid. This signal indicates that the channel
// is signaling the required read data.
output wire S_AXI_RVALID,
// Read ready. This signal indicates that the master can
// accept the read data and response information.
input wire S_AXI_RREADY
);
// Double buffer the write response channel only
reg [IW-1 : 0] r_bid;
reg r_bvalid;
reg [IW-1 : 0] axi_bid;
reg axi_bvalid;
reg axi_awready, axi_wready;
reg [AW-1:0] waddr;
wire [AW-1:0] next_wr_addr;
// Vivado will warn about wlen only using 4-bits. This is
// to be expected, since the axi_addr module only needs to use
// the bottom four bits of wlen to determine address increments
reg [7:0] wlen;
// Vivado will also warn about the top bit of wsize being unused.
// This is also to be expected for a DATA_WIDTH of 32-bits.
reg [2:0] wsize;
reg [1:0] wburst;
////////////////////////////////////////////////////////////////////////
//
// Skid buffer
//
wire m_awvalid;
reg m_awready;
wire [AW-1:0] m_awaddr;
wire [1:0] m_awburst;
wire [2:0] m_awsize;
wire [7:0] m_awlen;
wire [IW-1:0] m_awid;
//
skidbuffer #(.DW(AW+2+3+8+IW), .OPT_OUTREG(1'b0))
awbuf(S_AXI_ACLK, !S_AXI_ARESETN,
S_AXI_AWVALID, S_AXI_AWREADY,
{ S_AXI_AWADDR, S_AXI_AWBURST, S_AXI_AWSIZE,
S_AXI_AWLEN, S_AXI_AWID },
m_awvalid, m_awready,
{ m_awaddr, m_awburst, m_awsize, m_awlen, m_awid });
////////////////////////////////////////////////////////////////////////
//
// Write processing
//
wire [AW-1:0] next_rd_addr;
reg [IW-1:0] axi_rid;
reg [DW-1:0] axi_rdata;
initial axi_awready = 1;
initial axi_wready = 0;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN)
begin
axi_awready <= 1;
axi_wready <= 0;
end else if (m_awvalid && m_awready)
begin
axi_awready <= 0;
axi_wready <= 1;
end else if (S_AXI_WVALID && S_AXI_WREADY)
begin
axi_awready <= (S_AXI_WLAST)&&(!S_AXI_BVALID || S_AXI_BREADY);
axi_wready <= (!S_AXI_WLAST);
end else if (!axi_awready)
begin
if (S_AXI_WREADY)
axi_awready <= 1'b0;
else if (r_bvalid && !S_AXI_BREADY)
axi_awready <= 1'b0;
else
axi_awready <= 1'b1;
end
always @(posedge S_AXI_ACLK)
if (m_awready)
begin
waddr <= m_awaddr;
wburst <= m_awburst;
wsize <= m_awsize;
wlen <= m_awlen;
end else if (S_AXI_WVALID)
waddr <= next_wr_addr;
axi_addr #(.AW(AW), .DW(DW))
get_next_wr_addr(waddr, wsize, wburst, wlen,
next_wr_addr);
always @(*)
begin
o_we = (S_AXI_WVALID && S_AXI_WREADY);
o_waddr = waddr[AW-1:LSB];
o_wdata = S_AXI_WDATA;
o_wstrb = S_AXI_WSTRB;
end
initial r_bvalid = 0;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN)
r_bvalid <= 1'b0;
else if (S_AXI_WVALID && S_AXI_WREADY && S_AXI_WLAST
&&(S_AXI_BVALID && !S_AXI_BREADY))
r_bvalid <= 1'b1;
else if (S_AXI_BREADY)
r_bvalid <= 1'b0;
always @(posedge S_AXI_ACLK)
begin
if (m_awready)
r_bid <= m_awid;
if (!S_AXI_BVALID || S_AXI_BREADY)
axi_bid <= r_bid;
end
initial axi_bvalid = 0;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN)
axi_bvalid <= 0;
else if (S_AXI_WVALID && S_AXI_WREADY && S_AXI_WLAST)
axi_bvalid <= 1;
else if (S_AXI_BREADY)
axi_bvalid <= r_bvalid;
always @(*)
begin
m_awready = axi_awready;
if (S_AXI_WVALID && S_AXI_WREADY && S_AXI_WLAST
&& (!S_AXI_BVALID || S_AXI_BREADY))
m_awready = 1;
end
// At one time, axi_awready was the same as S_AXI_AWREADY. Now, though,
// with the extra write address skid buffer, this is no longer the case.
// S_AXI_AWREADY is handled/created/managed by the skid buffer.
//
// assign S_AXI_AWREADY = axi_awready;
//
// The rest of these signals can be set according to their registered
// values above.
assign S_AXI_WREADY = axi_wready;
assign S_AXI_BVALID = axi_bvalid;
assign S_AXI_BID = axi_bid;
//
// This core does not produce any bus errors, nor does it support
// exclusive access, so 2'b00 will always be the correct response.
assign S_AXI_BRESP = 0;
////////////////////////////////////////////////////////////////////////
//
// Read half
//
// Vivado will warn about rlen only using 4-bits. This is
// to be expected, since for a DATA_WIDTH of 32-bits, the axi_addr
// module only uses the bottom four bits of rlen to determine
// address increments
reg [7:0] rlen;
// Vivado will also warn about the top bit of wsize being unused.
// This is also to be expected for a DATA_WIDTH of 32-bits.
reg [2:0] rsize;
reg [1:0] rburst;
reg [IW-1:0] rid;
reg axi_arready, axi_rlast, axi_rvalid;
reg [8:0] axi_rlen;
reg [AW-1:0] raddr;
initial axi_arready = 1;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN)
axi_arready <= 1;
else if (S_AXI_ARVALID && S_AXI_ARREADY)
axi_arready <= (S_AXI_ARLEN==0)&&(o_rd);
else if (!S_AXI_RVALID || S_AXI_RREADY)
begin
if ((!axi_arready)&&(S_AXI_RVALID))
axi_arready <= (axi_rlen <= 2);
end
initial axi_rlen = 0;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN)
axi_rlen <= 0;
else if (S_AXI_ARVALID && S_AXI_ARREADY)
axi_rlen <= (S_AXI_ARLEN+1)
+ ((S_AXI_RVALID && !S_AXI_RREADY) ? 1:0);
else if (S_AXI_RREADY && S_AXI_RVALID)
axi_rlen <= axi_rlen - 1;
always @(posedge S_AXI_ACLK)
if (o_rd)
raddr <= next_rd_addr;
else if (S_AXI_ARREADY)
raddr <= S_AXI_ARADDR;
always @(posedge S_AXI_ACLK)
if (S_AXI_ARREADY)
begin
rburst <= S_AXI_ARBURST;
rsize <= S_AXI_ARSIZE;
rlen <= S_AXI_ARLEN;
rid <= S_AXI_ARID;
end
axi_addr #(.AW(AW), .DW(DW))
get_next_rd_addr((S_AXI_ARREADY ? S_AXI_ARADDR : raddr),
(S_AXI_ARREADY ? S_AXI_ARSIZE : rsize),
(S_AXI_ARREADY ? S_AXI_ARBURST: rburst),
(S_AXI_ARREADY ? S_AXI_ARLEN : rlen),
next_rd_addr);
always @(*)
begin
o_rd = (S_AXI_ARVALID || !S_AXI_ARREADY);
if (S_AXI_RVALID && !S_AXI_RREADY)
o_rd = 0;
o_raddr = (S_AXI_ARREADY ? S_AXI_ARADDR[AW-1:LSB] : raddr[AW-1:LSB]);
end
initial axi_rvalid = 0;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN)
axi_rvalid <= 0;
else if (o_rd)
axi_rvalid <= 1;
else if (S_AXI_RREADY)
axi_rvalid <= 0;
always @(posedge S_AXI_ACLK)
if (!S_AXI_RVALID || S_AXI_RREADY)
begin
if (S_AXI_ARVALID && S_AXI_ARREADY)
axi_rid <= S_AXI_ARID;
else
axi_rid <= rid;
end
initial axi_rlast = 0;
always @(posedge S_AXI_ACLK)
if (!S_AXI_RVALID || S_AXI_RREADY)
begin
if (S_AXI_ARVALID && S_AXI_ARREADY)
axi_rlast <= (S_AXI_ARLEN == 0);
else if (S_AXI_RVALID)
axi_rlast <= (axi_rlen == 2);
else
axi_rlast <= (axi_rlen == 1);
end
always @(*)
axi_rdata = i_rdata;
//
assign S_AXI_ARREADY = axi_arready;
assign S_AXI_RVALID = axi_rvalid;
assign S_AXI_RID = axi_rid;
assign S_AXI_RDATA = axi_rdata;
assign S_AXI_RRESP = 0;
assign S_AXI_RLAST = axi_rlast;
//
// Make Verilator happy
// Verilator lint_off UNUSED
wire [23:0] unused;
assign unused = { S_AXI_AWLOCK, S_AXI_AWCACHE, S_AXI_AWPROT,
S_AXI_AWQOS,
S_AXI_ARLOCK, S_AXI_ARCACHE, S_AXI_ARPROT, S_AXI_ARQOS };
// Verilator lint_on UNUSED
`ifdef FORMAL
//
// The following properties are only some of the properties used
// to verify this core
//
reg f_past_valid;
initial f_past_valid = 0;
always @(posedge S_AXI_ACLK)
f_past_valid <= 1;
always @(*)
if (!f_past_valid)
assume(!S_AXI_ARESETN);
faxi_slave #(
// {{{
.C_AXI_ID_WIDTH(C_S_AXI_ID_WIDTH),
.C_AXI_DATA_WIDTH(C_S_AXI_DATA_WIDTH),
.C_AXI_ADDR_WIDTH(C_S_AXI_ADDR_WIDTH))
// ...
// }}}
f_slave(
// {{{
.i_clk(S_AXI_ACLK),
.i_axi_reset_n(S_AXI_ARESETN),
//
// Address write channel
//
.i_axi_awvalid(m_awvalid),
.i_axi_awready(m_awready),
.i_axi_awid( m_awid),
.i_axi_awaddr( m_awaddr),
.i_axi_awlen( m_awlen),
.i_axi_awsize( m_awsize),
.i_axi_awburst(m_awburst),
.i_axi_awlock( 1'b0),
.i_axi_awcache(4'h0),
.i_axi_awprot( 3'h0),
.i_axi_awqos( 4'h0),
//
//
//
// Write Data Channel
//
// Write Data
.i_axi_wdata(S_AXI_WDATA),
.i_axi_wstrb(S_AXI_WSTRB),
.i_axi_wlast(S_AXI_WLAST),
.i_axi_wvalid(S_AXI_WVALID),
.i_axi_wready(S_AXI_WREADY),
//
//
// Response ID tag. This signal is the ID tag of the
// write response.
.i_axi_bvalid(S_AXI_BVALID),
.i_axi_bready(S_AXI_BREADY),
.i_axi_bid( S_AXI_BID),
.i_axi_bresp( S_AXI_BRESP),
//
//
//
// Read address channel
//
.i_axi_arvalid(S_AXI_ARVALID),
.i_axi_arready(S_AXI_ARREADY),
.i_axi_arid( S_AXI_ARID),
.i_axi_araddr( S_AXI_ARADDR),
.i_axi_arlen( S_AXI_ARLEN),
.i_axi_arsize( S_AXI_ARSIZE),
.i_axi_arburst(S_AXI_ARBURST),
.i_axi_arlock( S_AXI_ARLOCK),
.i_axi_arcache(S_AXI_ARCACHE),
.i_axi_arprot( S_AXI_ARPROT),
.i_axi_arqos( S_AXI_ARQOS),
//
//
//
// Read data return channel
//
.i_axi_rvalid(S_AXI_RVALID),
.i_axi_rready(S_AXI_RREADY),
.i_axi_rid(S_AXI_RID),
.i_axi_rdata(S_AXI_RDATA),
.i_axi_rresp(S_AXI_RRESP),
.i_axi_rlast(S_AXI_RLAST),
//
// ...
// }}}
);
//
////////////////////////////////////////////////////////////////////////
//
// Write induction properties
//
////////////////////////////////////////////////////////////////////////
//
// {{{
//
// ...
//
always @(*)
if (r_bvalid)
assert(S_AXI_BVALID);
always @(*)
assert(axi_awready == (!S_AXI_WREADY&& !r_bvalid));
always @(*)
if (axi_awready)
assert(!S_AXI_WREADY);
//
// ...
//
// }}}
////////////////////////////////////////////////////////////////////////
//
// Read induction properties
//
////////////////////////////////////////////////////////////////////////
//
// {{{
//
// ...
//
always @(posedge S_AXI_ACLK)
if (f_past_valid && $rose(S_AXI_RLAST))
assert(S_AXI_ARREADY);
// }}}
////////////////////////////////////////////////////////////////////////
//
// Contract checking
//
////////////////////////////////////////////////////////////////////////
//
// {{{
//
// ...
//
// }}}
////////////////////////////////////////////////////////////////////////
//
// Cover properties
//
////////////////////////////////////////////////////////////////////////
//
// {{{
reg f_wr_cvr_valid, f_rd_cvr_valid;
initial f_wr_cvr_valid = 0;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN)
f_wr_cvr_valid <= 0;
else if (S_AXI_AWVALID && S_AXI_AWREADY && S_AXI_AWLEN > 4)
f_wr_cvr_valid <= 1;
always @(*)
cover(!S_AXI_BVALID && axi_awready && !m_awvalid
&& f_wr_cvr_valid /* && ... */));
initial f_rd_cvr_valid = 0;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN)
f_rd_cvr_valid <= 0;
else if (S_AXI_ARVALID && S_AXI_ARREADY && S_AXI_ARLEN > 4)
f_rd_cvr_valid <= 1;
always @(*)
cover(S_AXI_ARREADY && f_rd_cvr_valid /* && ... */);
//
// Generate cover statements associated with multiple successive bursts
//
// These will be useful for demonstrating the throughput of the core.
//
reg [4:0] f_dbl_rd_count, f_dbl_wr_count;
initial f_dbl_wr_count = 0;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN)
f_dbl_wr_count = 0;
else if (S_AXI_AWVALID && S_AXI_AWREADY && S_AXI_AWLEN == 3)
begin
if (!(&f_dbl_wr_count))
f_dbl_wr_count <= f_dbl_wr_count + 1;
end
always @(*)
cover(S_AXI_ARESETN && (f_dbl_wr_count > 1)); //!
always @(*)
cover(S_AXI_ARESETN && (f_dbl_wr_count > 3)); //!
always @(*)
cover(S_AXI_ARESETN && (f_dbl_wr_count > 3) && !m_awvalid
&&(!S_AXI_AWVALID && !S_AXI_WVALID && !S_AXI_BVALID)
&& (f_axi_awr_nbursts == 0)
&& (f_axi_wr_pending == 0)); //!!
initial f_dbl_rd_count = 0;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN)
f_dbl_rd_count = 0;
else if (S_AXI_ARVALID && S_AXI_ARREADY && S_AXI_ARLEN == 3)
begin
if (!(&f_dbl_rd_count))
f_dbl_rd_count <= f_dbl_rd_count + 1;
end
always @(*)
cover(!S_AXI_ARESETN && (f_dbl_rd_count > 3)
/* && ... */
&& !S_AXI_ARVALID && !S_AXI_RVALID);
`ifdef VERIFIC
cover property (@(posedge S_AXI_ACLK)
disable iff (!S_AXI_ARESETN)
// Accept a burst request for 4 beats
S_AXI_ARVALID && S_AXI_ARREADY && (S_AXI_ARLEN == 3)
// The first three beats
##1 S_AXI_RVALID && S_AXI_RREADY [*3]
// The last read beat, where we accept the next request
##1 S_AXI_ARVALID && S_AXI_ARREADY && (S_AXI_ARLEN == 3)
&& S_AXI_RVALID && S_AXI_RDATA && S_AXI_RLAST
// The next three beats of data, and
##1 S_AXI_RVALID && S_AXI_RREADY [*3]
// The final beat of the transaction
##1 S_AXI_RVALID && S_AXI_RDATA && S_AXI_RLAST
// The return to idle
##1 !S_AXI_RVALID && !S_AXI_ARVALID);
`endif
// }}}
////////////////////////////////////////////////////////////////////////
//
// Assumptions necessary to pass a formal check
//
////////////////////////////////////////////////////////////////////////
//
//
always @(posedge S_AXI_ACLK)
if (S_AXI_RVALID && !$past(o_rd))
assume($stable(i_rdata));
`endif
endmodule
`ifndef YOSYS
`default_nettype wire
`endif