//////////////////////////////////////////////////////////////////////////////// // // 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= 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<= 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