diff options
| author | Alejandro Soto <alejandro@34project.org> | 2024-03-06 02:38:24 -0600 |
|---|---|---|
| committer | Alejandro Soto <alejandro@34project.org> | 2024-03-06 02:38:24 -0600 |
| commit | 3038edc09a2eb15762f2e58533f429489107520b (patch) | |
| tree | f7a45e424d39e6fef0d59e329c1bf6ea206e2886 /rtl/wb2axip/demofull.v | |
| parent | 3b62399f92e9faa2602ac30865e5fc3c7c4e12b8 (diff) | |
rtl/wb2axip: add to version control
Diffstat (limited to 'rtl/wb2axip/demofull.v')
| -rw-r--r-- | rtl/wb2axip/demofull.v | 1285 |
1 files changed, 1285 insertions, 0 deletions
diff --git a/rtl/wb2axip/demofull.v b/rtl/wb2axip/demofull.v new file mode 100644 index 0000000..5d0c604 --- /dev/null +++ b/rtl/wb2axip/demofull.v @@ -0,0 +1,1285 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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-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 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, + parameter [0:0] OPT_LOCK = 1'b0, + parameter [0:0] OPT_LOCKID = 1'b1, + parameter [0:0] OPT_LOWPOWER = 1'b0, + // Some useful short-hand definitions + localparam LSB = $clog2(C_S_AXI_DATA_WIDTH)-3 + // }}} + ) ( + // {{{ + // User ports + // {{{ + // 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 + // AXI signals + // {{{ + // Global Clock Signal + input wire S_AXI_ACLK, + // Global Reset Signal. This Signal is Active LOW + input wire S_AXI_ARESETN, + + // Write address channel + // {{{ + // 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 channel + // {{{ + // 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, + // }}} + // Write response channel + // {{{ + // 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 channel + // {{{ + // 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 data (return) channel + // {{{ + // 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 + // }}} + // }}} + // }}} + ); + + // Local declarations + // {{{ + // More useful shorthand definitions + localparam AW = C_S_AXI_ADDR_WIDTH; + localparam DW = C_S_AXI_DATA_WIDTH; + localparam IW = C_S_AXI_ID_WIDTH; + // 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; + + wire m_awvalid, m_awlock; + 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; + + wire [AW-1:0] next_rd_addr; + + // 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 rlock; + reg axi_arready; + reg [8:0] axi_rlen; + reg [AW-1:0] raddr; + + // Read skid buffer + reg rskd_valid, rskd_last, rskd_lock; + wire rskd_ready; + reg [IW-1:0] rskd_id; + + // Exclusive address register checking + reg exclusive_write, block_write; + wire write_lock_valid; + reg axi_exclusive_write; + + + // }}} + //////////////////////////////////////////////////////////////////////// + // + // AW Skid buffer + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + skidbuffer #( + // {{{ + .DW(AW+2+3+1+8+IW), + .OPT_LOWPOWER(OPT_LOWPOWER), + .OPT_OUTREG(1'b0) + // }}} + ) awbuf( + // {{{ + .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN), + .i_valid(S_AXI_AWVALID), .o_ready(S_AXI_AWREADY), + .i_data({ S_AXI_AWADDR, S_AXI_AWBURST, S_AXI_AWSIZE, + S_AXI_AWLOCK, S_AXI_AWLEN, S_AXI_AWID }), + .o_valid(m_awvalid), .i_ready(m_awready), + .o_data({ m_awaddr, m_awburst, m_awsize, + m_awlock, m_awlen, m_awid }) + // }}} + ); + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Write processing + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + + // axi_awready, axi_wready + // {{{ + 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 + // }}} + + // Exclusive write calculation + // {{{ + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN || !OPT_LOCK) + begin + exclusive_write <= 0; + block_write <= 0; + end else if (m_awvalid && m_awready) + begin + exclusive_write <= 1'b0; + block_write <= 1'b0; + if (write_lock_valid) + exclusive_write <= 1'b1; + else if (m_awlock) + block_write <= 1'b1; + end else if (m_awready) + begin + exclusive_write <= 1'b0; + block_write <= 1'b0; + end + + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN || !OPT_LOCK) + axi_exclusive_write <= 0; + else if (!S_AXI_BVALID || S_AXI_BREADY) + begin + axi_exclusive_write <= exclusive_write; + if (OPT_LOWPOWER && (!S_AXI_WVALID || !S_AXI_WREADY || !S_AXI_WLAST) + && !r_bvalid) + axi_exclusive_write <= 0; + end + // }}} + + // Next write address calculation + // {{{ + 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 + // }}} + ); + // }}} + + // o_w* + // {{{ + always @(posedge S_AXI_ACLK) + begin + o_we <= (S_AXI_WVALID && S_AXI_WREADY); + o_waddr <= waddr[AW-1:LSB]; + o_wdata <= S_AXI_WDATA; + if (block_write) + o_wstrb <= 0; + else + o_wstrb <= S_AXI_WSTRB; + + if (!S_AXI_ARESETN) + o_we <= 0; + if (OPT_LOWPOWER && (!S_AXI_ARESETN || !S_AXI_WVALID + || !S_AXI_WREADY)) + begin + o_waddr <= 0; + o_wdata <= 0; + o_wstrb <= 0; + end + end + // }}} + + // + // Write return path + // {{{ + // r_bvalid + // {{{ + 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; + // }}} + + // r_bid, axi_bid + // {{{ + initial r_bid = 0; + initial axi_bid = 0; + always @(posedge S_AXI_ACLK) + begin + if (m_awready && (!OPT_LOWPOWER || m_awvalid)) + r_bid <= m_awid; + + if (!S_AXI_BVALID || S_AXI_BREADY) + axi_bid <= r_bid; + + if (OPT_LOWPOWER && !S_AXI_ARESETN) + begin + r_bid <= 0; + axi_bid <= 0; + end + end + // }}} + + // axi_bvalid + // {{{ + 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; + // }}} + + // m_awready + // {{{ + 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 = { 1'b0, axi_exclusive_write }; + // }}} + + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Read processing + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + + // axi_arready + // {{{ + 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 (o_rd) + axi_arready <= (axi_rlen <= 1); + // }}} + + // axi_rlen + // {{{ + 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 + (o_rd ? 0:1); + else if (o_rd) + axi_rlen <= axi_rlen - 1; + // }}} + + // Next read address calculation + // {{{ + always @(posedge S_AXI_ACLK) + if (o_rd) + raddr <= next_rd_addr; + else if (S_AXI_ARREADY) + begin + raddr <= S_AXI_ARADDR; + if (OPT_LOWPOWER && !S_AXI_ARVALID) + raddr <= 0; + end + + // r* + // {{{ + 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; + rlock <= S_AXI_ARLOCK && S_AXI_ARVALID && OPT_LOCK; + + if (OPT_LOWPOWER && !S_AXI_ARVALID) + begin + rburst <= 0; + rsize <= 0; + rlen <= 0; + rid <= 0; + rlock <= 0; + end + 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 + // }}} + ); + // }}} + + // o_rd, o_raddr + // {{{ + always @(*) + begin + o_rd = (S_AXI_ARVALID || !S_AXI_ARREADY); + if (S_AXI_RVALID && !S_AXI_RREADY) + o_rd = 0; + if (rskd_valid && !rskd_ready) + o_rd = 0; + o_raddr = (S_AXI_ARREADY ? S_AXI_ARADDR[AW-1:LSB] : raddr[AW-1:LSB]); + end + // }}} + + // rskd_valid + // {{{ + initial rskd_valid = 0; + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN) + rskd_valid <= 0; + else if (o_rd) + rskd_valid <= 1; + else if (rskd_ready) + rskd_valid <= 0; + // }}} + + // rskd_id + // {{{ + always @(posedge S_AXI_ACLK) + if (!rskd_valid || rskd_ready) + begin + if (S_AXI_ARVALID && S_AXI_ARREADY) + rskd_id <= S_AXI_ARID; + else + rskd_id <= rid; + end + // }}} + + // rskd_last + // {{{ + initial rskd_last = 0; + always @(posedge S_AXI_ACLK) + if (!rskd_valid || rskd_ready) + begin + rskd_last <= 0; + if (o_rd && axi_rlen == 1) + rskd_last <= 1; + if (S_AXI_ARVALID && S_AXI_ARREADY && S_AXI_ARLEN == 0) + rskd_last <= 1; + end + // }}} + + // rskd_lock + // {{{ + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN || !OPT_LOCK) + rskd_lock <= 1'b0; + else if (!rskd_valid || rskd_ready) + begin + rskd_lock <= 0; + if (!OPT_LOWPOWER || o_rd) + begin + if (S_AXI_ARVALID && S_AXI_ARREADY) + rskd_lock <= S_AXI_ARLOCK; + else + rskd_lock <= rlock; + end + end + // }}} + + + // Outgoing read skidbuffer + // {{{ + skidbuffer #( + // {{{ + .OPT_LOWPOWER(OPT_LOWPOWER), + .OPT_OUTREG(1), + .DW(IW+2+DW) + // }}} + ) rskid ( + // {{{ + .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN), + .i_valid(rskd_valid), .o_ready(rskd_ready), + .i_data({ rskd_id, rskd_lock, rskd_last, i_rdata }), + .o_valid(S_AXI_RVALID), .i_ready(S_AXI_RREADY), + .o_data({ S_AXI_RID, S_AXI_RRESP[0], S_AXI_RLAST, + S_AXI_RDATA }) + // }}} + ); + // }}} + + assign S_AXI_RRESP[1] = 1'b0; + assign S_AXI_ARREADY = axi_arready; + + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Exclusive address caching + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + + generate if (OPT_LOCK && !OPT_LOCKID) + begin : EXCLUSIVE_ACCESS_BLOCK + // {{{ + // The AXI4 specification requires that we check one address + // per ID. This isn't that. This algorithm checks one ID, + // whichever the last ID was. It's designed to be lighter on + // the logic requirements, and (unnoticably) not (fully) spec + // compliant. (The difference, if noticed at all, will be in + // performance when multiple masters try to perform an exclusive + // transaction at once.) + + // Local declarations + // {{{ + reg w_valid_lock_request, w_cancel_lock, + w_lock_request, + lock_valid, returned_lock_valid; + reg [AW-LSB-1:0] lock_start, lock_end; + reg [3:0] lock_len; + reg [1:0] lock_burst; + reg [2:0] lock_size; + reg [IW-1:0] lock_id; + reg w_write_lock_valid; + // }}} + + // w_lock_request + // {{{ + always @(*) + begin + w_lock_request = 0; + if (S_AXI_ARVALID && S_AXI_ARREADY && S_AXI_ARLOCK) + w_lock_request = 1; + end + // }}} + + // w_valid_lock_request + // {{{ + always @(*) + begin + w_valid_lock_request = 0; + if (w_lock_request) + w_valid_lock_request = 1; + if (o_we && o_waddr == S_AXI_ARADDR[AW-1:LSB]) + w_valid_lock_request = 0; + end + // }}} + + // returned_lock_valid + // {{{ + initial returned_lock_valid = 0; + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN) + returned_lock_valid <= 0; + else if (S_AXI_ARVALID && S_AXI_ARREADY + && S_AXI_ARLOCK && S_AXI_ARID== lock_id) + returned_lock_valid <= 0; + else if (w_cancel_lock) + returned_lock_valid <= 0; + else if (rskd_valid && rskd_lock && rskd_ready) + returned_lock_valid <= lock_valid; + // }}} + + // w_cancel_lock + // {{{ + always @(*) + w_cancel_lock = (lock_valid && w_lock_request) + || (lock_valid && o_we + && o_waddr >= lock_start + && o_waddr <= lock_end + && o_wstrb != 0); + // }}} + + // lock_valid + // {{{ + initial lock_valid = 0; + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN || !OPT_LOCK) + lock_valid <= 0; + else begin + if (S_AXI_ARVALID && S_AXI_ARREADY + && S_AXI_ARLOCK && S_AXI_ARID== lock_id) + lock_valid <= 0; + if (w_cancel_lock) + lock_valid <= 0; + if (w_valid_lock_request) + lock_valid <= 1; + end + // }}} + + // lock_start, lock_end, lock_len, lock_size, lock_id + // {{{ + always @(posedge S_AXI_ACLK) + if (w_valid_lock_request) + begin + lock_start <= S_AXI_ARADDR[C_S_AXI_ADDR_WIDTH-1:LSB]; + lock_end <= S_AXI_ARADDR[C_S_AXI_ADDR_WIDTH-1:LSB] + + ((S_AXI_ARBURST == 2'b00) ? 0 : S_AXI_ARLEN[3:0]); + lock_len <= S_AXI_ARLEN[3:0]; + lock_burst <= S_AXI_ARBURST; + lock_size <= S_AXI_ARSIZE; + lock_id <= S_AXI_ARID; + end + // }}} + + // w_write_lock_valid + // {{{ + always @(*) + begin + w_write_lock_valid = returned_lock_valid; + if (!m_awvalid || !m_awready || !m_awlock || !lock_valid) + w_write_lock_valid = 0; + if (m_awaddr[C_S_AXI_ADDR_WIDTH-1:LSB] != lock_start) + w_write_lock_valid = 0; + if (m_awid != lock_id) + w_write_lock_valid = 0; + if (m_awlen[3:0] != lock_len) // MAX transfer size is 16 beats + w_write_lock_valid = 0; + if (m_awburst != 2'b01 && lock_len != 0) + w_write_lock_valid = 0; + if (m_awsize != lock_size) + w_write_lock_valid = 0; + end + // }}} + + assign write_lock_valid = w_write_lock_valid; + // }}} + end else if (OPT_LOCK) // && OPT_LOCKID + begin : EXCLUSIVE_ACCESS_PER_ID + // {{{ + + genvar gk; + wire [(1<<IW)-1:0] write_lock_valid_per_id; + + for(gk=0; gk<(1<<IW); gk=gk+1) + begin : PER_ID_LOGIC + // {{{ + // Local declarations + // {{{ + reg w_valid_lock_request, + w_cancel_lock, + lock_valid, returned_lock_valid; + reg [1:0] lock_burst; + reg [2:0] lock_size; + reg [3:0] lock_len; + reg [AW-LSB-1:0] lock_start, lock_end; + reg w_write_lock_valid; + // }}} + + // valid_lock_request + // {{{ + always @(*) + begin + w_valid_lock_request = 0; + if (S_AXI_ARVALID && S_AXI_ARREADY + && S_AXI_ARID == gk[IW-1:0] + && S_AXI_ARLOCK) + w_valid_lock_request = 1; + if (o_we && o_waddr == S_AXI_ARADDR[AW-1:LSB]) + w_valid_lock_request = 0; + end + // }}} + + // returned_lock_valid + // {{{ + initial returned_lock_valid = 0; + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN) + returned_lock_valid <= 0; + else if (S_AXI_ARVALID && S_AXI_ARREADY + &&S_AXI_ARLOCK&&S_AXI_ARID== gk[IW-1:0]) + returned_lock_valid <= 0; + else if (w_cancel_lock) + returned_lock_valid <= 0; + else if (rskd_valid && rskd_lock && rskd_ready + && rskd_id == gk[IW-1:0]) + returned_lock_valid <= lock_valid; + // }}} + + // w_cancel_lock + // {{{ + always @(*) + w_cancel_lock=(lock_valid&&w_valid_lock_request) + || (lock_valid && o_we + && o_waddr >= lock_start + && o_waddr <= lock_end + && o_wstrb != 0); + // }}} + + // lock_valid + // {{{ + initial lock_valid = 0; + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN || !OPT_LOCK) + lock_valid <= 0; + else begin + if (S_AXI_ARVALID && S_AXI_ARREADY + && S_AXI_ARLOCK + && S_AXI_ARID == gk[IW-1:0]) + lock_valid <= 0; + if (w_cancel_lock) + lock_valid <= 0; + if (w_valid_lock_request) + lock_valid <= 1; + end + // }}} + + // lock_start, lock_end, lock_len, lock_size + // {{{ + always @(posedge S_AXI_ACLK) + if (w_valid_lock_request) + begin + lock_start <= S_AXI_ARADDR[C_S_AXI_ADDR_WIDTH-1:LSB]; + // Verilator lint_off WIDTH + lock_end <= S_AXI_ARADDR[C_S_AXI_ADDR_WIDTH-1:LSB] + + ((S_AXI_ARBURST == 2'b00) ? 4'h0 : S_AXI_ARLEN[3:0]); + // Verilator lint_on WIDTH + lock_len <= S_AXI_ARLEN[3:0]; + lock_size <= S_AXI_ARSIZE; + lock_burst <= S_AXI_ARBURST; + end + // }}} + + // w_write_lock_valid + // {{{ + always @(*) + begin + w_write_lock_valid = returned_lock_valid; + if (!m_awvalid || !m_awready || !m_awlock || !lock_valid) + w_write_lock_valid = 0; + if (m_awaddr[C_S_AXI_ADDR_WIDTH-1:LSB] != lock_start) + w_write_lock_valid = 0; + if (m_awid[IW-1:0] != gk[IW-1:0]) + w_write_lock_valid = 0; + if (m_awlen[3:0] != lock_len) // MAX transfer size is 16 beats + w_write_lock_valid = 0; + if (m_awburst != 2'b01 && lock_len != 0) + w_write_lock_valid = 0; + if (m_awsize != lock_size) + w_write_lock_valid = 0; + end + // }}} + + assign write_lock_valid_per_id[gk]= w_write_lock_valid; + // }}} + end + + assign write_lock_valid = |write_lock_valid_per_id; + // }}} + end else begin : NO_LOCKING + // {{{ + + assign write_lock_valid = 1'b0; + // Verilator lint_off UNUSED + wire unused_lock; + assign unused_lock = &{ 1'b0, S_AXI_ARLOCK, S_AXI_AWLOCK }; + // Verilator lint_on UNUSED + // }}} + end endgenerate + // }}} + + // Make Verilator happy + // {{{ + // Verilator lint_off UNUSED + wire unused; + assign unused = &{ 1'b0, S_AXI_AWCACHE, S_AXI_AWPROT, S_AXI_AWQOS, + S_AXI_ARCACHE, S_AXI_ARPROT, S_AXI_ARQOS }; + // Verilator lint_on UNUSED + // }}} +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// +// Formal properties +// {{{ +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +`ifdef FORMAL + // + // The following properties are only some of the properties used + // to verify this core + // + // Local (formal) register declarations + // {{{ + 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); + + // }}} + //////////////////////////////////////////////////////////////////////// + // + // AXI slave properties + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + 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( m_awlock), + .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), + // }}} + // 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 && axi_rlen == 0) + assert(S_AXI_ARREADY); + + always @(posedge S_AXI_ACLK) + assert(axi_rlen <= 256); + + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Lowpower checking + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + + always @(*) + if (OPT_LOWPOWER && S_AXI_ARESETN) + begin + if (!rskd_valid) + assert(!rskd_lock); + if (faxi_awr_nbursts == 0) + begin + assert(S_AXI_BRESP == 2'b00); + // assert(S_AXI_BID == 0); + end + + if (!S_AXI_RVALID) + begin + assert(S_AXI_RID == 0); + assert(S_AXI_RDATA == 0); + assert(S_AXI_RRESP == 2'b00); + end + + if (!o_we) + begin + assert(o_waddr == 0); + assert(o_wdata == 0); + assert(o_wstrb == 0); + end + end + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Contract checking + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // ... + // + + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Cover properties + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + reg f_wr_cvr_valid, f_rd_cvr_valid; + reg [4:0] f_dbl_rd_count, f_dbl_wr_count; + + 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. + // + + 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) + && (faxi_awr_nbursts == 0) + && (faxi_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); + + generate if (OPT_LOCK) + begin + always @(*) + cover(S_AXI_ARESETN && S_AXI_BVALID && S_AXI_BREADY + && S_AXI_BRESP == 2'b01); + end endgenerate + +`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) + ##1 S_AXI_ARVALID [*3]) [*3] + ##1 1 [*0:12] + // The return to idle + ##1 (!S_AXI_ARVALID && !o_rd && !rskd_valid && !S_AXI_RVALID) + ); +`endif + + // }}} + //////////////////////////////////////////////////////////////////////// + // + // "Careless" assumptions + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + + // }}} +`endif +// }}} +endmodule +`ifndef YOSYS +`default_nettype wire +`endif |
