//////////////////////////////////////////////////////////////////////////////// // // Filename: qflexpress.v // // Project: A Set of Wishbone Controlled SPI Flash Controllers // // Purpose: To provide wishbone controlled read access (and read access // *only*) to the QSPI flash, using a flash clock equal to the // system clock, and nothing more. Indeed, this is designed to be a // *very* stripped down version of a flash driver, with the goal of // providing 1) very fast access for 2) very low logic count. // // Three modes/states of operation: // 1. Startup/maintenance, places the device in the Quad XIP mode // 2. Normal operations, takes 12+8N clocks to read a value // 3. Configuration--useful to allow an external controller issue erase // or program commands (or other) without requiring us to // clutter up the logic with a giant state machine // // STARTUP // 1. Waits for the flash to come on line // Start out idle for 300 uS // 2. Sends a signal to remove the flash from any QSPI read mode. In our // case, we'll send several clocks of an empty command. In SPI // mode, it'll get ignored. In QSPI mode, it'll remove us from // QSPI mode. // 3. Explicitly places and leaves the flash into QSPI mode // 0xEB 3(0x00) 0xa0 6(0x00) // 4. All done // // Creator: Dan Gisselquist, Ph.D. // Gisselquist Technology, LLC // //////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2018-2019, Gisselquist Technology, LLC // // This file is part of the set of Wishbone controlled SPI flash controllers // project // // The Wishbone SPI flash controller project is free software (firmware): // you can redistribute it and/or modify it under the terms of the GNU Lesser // General Public License as published by the Free Software Foundation, either // version 3 of the License, or (at your option) any later version. // // The Wishbone SPI flash controller project is distributed in the hope // that it will be useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with this program. (It's in the $(ROOT)/doc directory. Run make // with no target there if the PDF file isn't present.) If not, see // for a copy. // // License: LGPL, v3, as defined and found on www.gnu.org, // http://www.gnu.org/licenses/lgpl.html // // //////////////////////////////////////////////////////////////////////////////// // // `default_nettype none // // 290 raw, 372 w/ pipe, 410 cfg, 499 cfg w/pipe module qflexpress(i_clk, i_reset, i_wb_cyc, i_wb_stb, i_cfg_stb, i_wb_we, i_wb_addr, i_wb_data, o_wb_ack, o_wb_stall, o_wb_data, o_qspi_sck, o_qspi_cs_n, o_qspi_mod, o_qspi_dat, i_qspi_dat); // // LGFLASHSZ is the size of the flash memory. It defines the number // of bits in the address register and more. This controller will support // flash sizes up to 2^LGFLASHSZ, where LGFLASHSZ goes up to 32. parameter LGFLASHSZ=24; // // OPT_PIPE makes it possible to string multiple requests together, // with no intervening need to shutdown the QSPI connection and send a // new address parameter [0:0] OPT_PIPE = 1'b1; // // OPT_CFG enables the configuration logic port, and hence the // ability to erase and program the flash, as well as the ability // to perform other commands such as read-manufacturer ID, adjust // configuration registers, etc. parameter [0:0] OPT_CFG = 1'b1; // // OPT_STARTUP enables the startup logic parameter [0:0] OPT_STARTUP = 1'b1; // // OPT_ADDR32 enables 32 bit addressing, rather than 24bit // Control this by controlling the LGFLASHSZ parameter above. Anything // greater than 24 will use 32-bit addressing, otherwise the regular // 24-bit addressing localparam [0:0] OPT_ADDR32 = (LGFLASHSZ > 24); // parameter OPT_CLKDIV = 0; // // Normally, I place the first byte read from the flash, and the lowest // flash address, into bits [7:0], and then shift it up--to where upon // return it is found in bits [31:24]. This is ideal for a big endian // systems, not so much for little endian systems. The endian swap // allows the bus to swap the return values in order to support little // endian systems. parameter [0:0] OPT_ENDIANSWAP = 1'b1; // // OPT_ODDR will be true any time the clock has no clock division localparam [0:0] OPT_ODDR = (OPT_CLKDIV == 0); // // CKDV_BITS is the number of bits necessary to represent a counter // that can do the CLKDIV division localparam CKDV_BITS = (OPT_CLKDIV == 0) ? 0 : ((OPT_CLKDIV < 2) ? 1 : ((OPT_CLKDIV < 4) ? 2 : ((OPT_CLKDIV < 8) ? 3 : ((OPT_CLKDIV < 16) ? 4 : ((OPT_CLKDIV < 32) ? 5 : ((OPT_CLKDIV < 64) ? 6 : ((OPT_CLKDIV < 128) ? 7 : ((OPT_CLKDIV < 256) ? 8 : 9)))))))); // // RDDELAY is the number of clock cycles from when o_qspi_dat is valid // until i_qspi_dat is valid. Read delays from 0-4 have been verified. // DDR Registered I/O on a Xilinx device can be done with a RDDELAY=3 // On Intel/Altera devices, RDDELAY=2 works // I'm using RDDELAY=0 for my iCE40 devices // parameter RDDELAY = 0; // // NDUMMY is the number of "dummy" clock cycles between the 24-bits of // the Quad I/O address and the first data bits. This includes the // two clocks of the Quad output mode byte, 0xa0. The default is 10 // for a Micron device. Windbond seems to want 2. Note your flash // device carefully when you choose this value. // parameter NDUMMY = 6; // // For dealing with multiple flash devices, the OPT_STARTUP_FILE allows // a hex file to be provided containing the necessary script to place // the design into the proper initial configuration. parameter OPT_STARTUP_FILE=""; // // // // localparam [4:0] CFG_MODE = 12; localparam [4:0] QSPEED_BIT = 11; localparam [4:0] DSPEED_BIT = 10; // Not supported localparam [4:0] DIR_BIT = 9; localparam [4:0] USER_CS_n = 8; // localparam [1:0] NORMAL_SPI = 2'b00; localparam [1:0] QUAD_WRITE = 2'b10; localparam [1:0] QUAD_READ = 2'b11; // localparam [7:0] DIO_READ_CMD = 8'hbb; localparam [7:0] QIO_READ_CMD = OPT_ADDR32 ? 8'hec : 8'heb; // localparam AW=LGFLASHSZ-1; localparam DW=32; // `ifdef FORMAL localparam F_LGDEPTH=$clog2(3+RDDELAY+(OPT_ADDR32 ? 2:0)); reg f_past_valid; `endif // // input wire i_clk, i_reset; // input wire i_wb_cyc, i_wb_stb, i_cfg_stb, i_wb_we; input wire [(AW-1):0] i_wb_addr; input wire [(DW-1):0] i_wb_data; // output reg o_wb_ack, o_wb_stall; output reg [(DW-1):0] o_wb_data; // output reg o_qspi_sck; output reg o_qspi_cs_n; output reg [1:0] o_qspi_mod; output wire [3:0] o_qspi_dat; input wire [3:0] i_qspi_dat; // // Debugging port // output wire o_dbg_trigger; // output wire [31:0] o_debug; reg dly_ack, read_sck, xtra_stall; // clk_ctr must have enough bits for ... // 8 address clocks, 4-bits each // NDUMMY dummy clocks, including two mode bytes // 8 data clocks // (RDDELAY clocks not counted here) reg [4:0] clk_ctr; // // User override logic // reg cfg_mode, cfg_speed, cfg_dir, cfg_cs; wire cfg_write, cfg_hs_write, cfg_ls_write, cfg_hs_read, user_request, bus_request, pipe_req, cfg_noop, cfg_stb; // assign bus_request = (i_wb_stb)&&(!o_wb_stall) &&(!i_wb_we)&&(!cfg_mode); assign cfg_stb = (OPT_CFG)&&(i_cfg_stb)&&(!o_wb_stall); assign cfg_noop = ((cfg_stb)&&((!i_wb_we)||(!i_wb_data[CFG_MODE]) ||(i_wb_data[USER_CS_n]))) ||((!OPT_CFG)&&(i_cfg_stb)&&(!o_wb_stall)); assign user_request = (cfg_stb)&&(i_wb_we)&&(i_wb_data[CFG_MODE]); assign cfg_write = (user_request)&&(!i_wb_data[USER_CS_n]); assign cfg_hs_write = (cfg_write)&&(i_wb_data[QSPEED_BIT]) &&(i_wb_data[DIR_BIT]); assign cfg_hs_read = (cfg_write)&&(i_wb_data[QSPEED_BIT]) &&(!i_wb_data[DIR_BIT]); assign cfg_ls_write = (cfg_write)&&(!i_wb_data[QSPEED_BIT]); reg ckstb, ckpos, ckneg, ckpre; reg maintenance; reg [1:0] m_mod; reg m_cs_n; reg m_clk; reg [3:0] m_dat; generate if (OPT_ODDR) begin always @(*) begin ckstb = 1'b1; ckpos = 1'b1; ckneg = 1'b1; ckpre = 1'b1; end end else if (OPT_CLKDIV == 1) begin : CKSTB_ONE reg clk_counter; initial clk_counter = 1'b1; always @(posedge i_clk) if (i_reset) clk_counter <= 1'b1; else if (clk_counter != 0) clk_counter <= 1'b0; else if (bus_request) clk_counter <= (pipe_req); else if ((maintenance)||(!o_qspi_cs_n && o_wb_stall)) clk_counter <= 1'b1; always @(*) begin ckpre = (clk_counter == 1); ckstb = (clk_counter == 0); ckpos = (clk_counter == 1); ckneg = (clk_counter == 0); end end else begin : CKSTB_GEN reg [CKDV_BITS-1:0] clk_counter; initial clk_counter = OPT_CLKDIV; always @(posedge i_clk) if (i_reset) clk_counter <= OPT_CLKDIV; else if (clk_counter != 0) clk_counter <= clk_counter - 1; else if (bus_request) clk_counter <= (pipe_req ? OPT_CLKDIV : 0); else if ((maintenance)||(!o_qspi_cs_n && o_wb_stall)) clk_counter <= OPT_CLKDIV; initial ckpre = (OPT_CLKDIV == 1); initial ckstb = 1'b0; initial ckpos = (OPT_CLKDIV == 1); always @(posedge i_clk) if (i_reset) begin ckpre <= (OPT_CLKDIV == 1); ckstb <= 1'b0; ckpos <= (OPT_CLKDIV == 1); end else // if (OPT_CLKDIV > 1) begin ckpre <= (clk_counter == 2); ckstb <= (clk_counter == 1); ckpos <= (clk_counter == (OPT_CLKDIV+1)/2+1); end always @(*) ckneg = ckstb; `ifdef FORMAL always @(*) assert(!ckpos || !ckneg); always @(posedge i_clk) if ((f_past_valid)&&(!$past(i_reset))&&($past(ckpre))) assert(ckstb); `endif end endgenerate // // // Maintenance / startup portion // // generate if (OPT_STARTUP) begin : GEN_STARTUP localparam M_WAITBIT=10; localparam M_LGADDR=5; `ifdef FORMAL // For formal, jump into the middle of the startup localparam M_FIRSTIDX=9; `else localparam M_FIRSTIDX=0; `endif reg [M_WAITBIT:0] m_this_word; reg [M_WAITBIT:0] m_cmd_word [0:(1< 3) m_counter <= 3; `endif end end else begin m_midcount <= (m_counter > 1); if (m_counter > 0) m_counter <= m_counter - 1'b1; end initial m_cs_n = 1'b1; initial m_mod = NORMAL_SPI; always @(posedge i_clk) if (i_reset) begin m_cs_n <= 1'b1; m_mod <= NORMAL_SPI; m_bitcount <= 0; end else if (ckstb) begin if (m_bitcount != 0) m_bitcount <= m_bitcount - 1; else if ((m_ce)&&(m_final)) begin m_cs_n <= 1'b1; m_mod <= NORMAL_SPI; m_bitcount <= 0; end else if ((m_midcount)||(m_this_word[M_WAITBIT])) begin m_cs_n <= 1'b1; m_mod <= NORMAL_SPI; m_bitcount <= 0; end else begin m_cs_n <= 1'b0; m_mod <= m_this_word[M_WAITBIT-1:M_WAITBIT-2]; m_bitcount <= (!OPT_ODDR && m_cs_n) ? 4'h2 : 4'h1; if (!m_this_word[M_WAITBIT-1]) m_bitcount <= (!OPT_ODDR && m_cs_n) ? 4'h8 : 4'h7;//i.e.7 end end always @(posedge i_clk) if (m_ce) begin if (m_bitcount == 0) begin if (!OPT_ODDR && m_cs_n) begin m_dat <= {(4){m_this_word[7]}}; m_byte <= m_this_word[7:0]; end else begin m_dat <= m_this_word[7:4]; m_byte <= { m_this_word[3:0], 4'h0}; if (!m_this_word[M_WAITBIT-1]) begin // Slow speed m_dat[0] <= m_this_word[7]; m_byte <= { m_this_word[6:0], 1'b0 }; end end end else begin m_dat <= m_byte[7:4]; m_byte <= { m_byte[3:0], 4'h0 }; if (!m_mod[1]) begin // Slow speed m_dat[0] <= m_byte[7]; m_byte <= { m_byte[6:0], 1'b0 }; end else begin m_byte <= { m_byte[3:0], 4'b00 }; end end end if (OPT_ODDR) begin always @(*) m_clk = !m_cs_n; end else begin always @(posedge i_clk) if (i_reset) m_clk <= 1'b1; else if (m_cs_n) m_clk <= 1'b1; else if ((!m_clk)&&(ckpos)) m_clk <= 1'b1; else if (m_midcount) m_clk <= 1'b1; else if (new_word && m_this_word[M_WAITBIT]) m_clk <= 1'b1; else if (ckneg) m_clk <= 1'b0; end `ifdef FORMAL (* anyconst *) reg [M_LGADDR:0] f_const_addr; always @(*) begin assert((m_cmd_word[f_const_addr][M_WAITBIT]) ||(m_cmd_word[f_const_addr][9:8] != 2'b01)); if (m_cmd_word[f_const_addr][M_WAITBIT]) assert(m_cmd_word[f_const_addr][M_WAITBIT-3:0] > 0); end always @(*) begin if (m_cmd_index != f_const_addr) assume((m_cmd_word[m_cmd_index][M_WAITBIT])||(m_cmd_word[m_cmd_index][9:8] != 2'b01)); if (m_cmd_word[m_cmd_index][M_WAITBIT]) assume(m_cmd_word[m_cmd_index][M_WAITBIT-3:0]>0); end always @(*) begin assert((m_this_word[M_WAITBIT]) ||(m_this_word[9:8] != 2'b01)); if (m_this_word[M_WAITBIT]) assert(m_this_word[M_WAITBIT-3:0] > 0); end // Setting the last two command words to IDLE with maximum // counts is required by our implementation always @(*) assert(m_cmd_word[5'h1e] == 11'h7ff); always @(*) assert(m_cmd_word[5'h1f] == 11'h7ff); wire [M_LGADDR-1:0] last_index; assign last_index = m_cmd_index - 1; always @(posedge i_clk) if ((f_past_valid)&&(m_cmd_index != M_FIRSTIDX)) assert(m_this_word == m_cmd_word[last_index]); always @(posedge i_clk) assert(m_midcount == (m_counter != 0)); always @(posedge i_clk) begin cover(!maintenance); cover(m_cmd_index == 5'h0a); cover(m_cmd_index == 5'h0b); cover(m_cmd_index == 5'h0c); cover(m_cmd_index == 5'h0d); cover(m_cmd_index == 5'h0e); cover(m_cmd_index == 5'h0f); cover(m_cmd_index == 5'h10); cover(m_cmd_index == 5'h11); cover(m_cmd_index == 5'h12); cover(m_cmd_index == 5'h13); cover(m_cmd_index == 5'h14); cover(m_cmd_index == 5'h15); cover(m_cmd_index == 5'h16); cover(m_cmd_index == 5'h17); cover(m_cmd_index == 5'h18); cover(m_cmd_index == 5'h19); cover(m_cmd_index == 5'h1a); cover(m_cmd_index == 5'h1b); cover(m_cmd_index == 5'h1c); cover(m_cmd_index == 5'h1d); cover(m_cmd_index == 5'h1e); cover(m_cmd_index == 5'h1f); end reg [M_WAITBIT:0] f_last_word; reg [8:0] f_mspi; reg [2:0] f_mqspi; initial f_last_word = -1; always @(posedge i_clk) if (i_reset) f_last_word = -1; else if (new_word) f_last_word <= m_this_word; initial f_mspi = 0; always @(posedge i_clk) if (i_reset) f_mspi <= 0; else if (ckstb) begin f_mspi <= f_mspi << 1; if (maintenance && !m_final && new_word &&(!m_this_word[M_WAITBIT]) &&(m_this_word[9:8] == NORMAL_SPI)) begin if (m_cs_n && !OPT_ODDR) f_mspi[0] <= 1'b1; else f_mspi[1] <= 1'b1; end end initial f_mqspi = 0; always @(posedge i_clk) if (i_reset) f_mqspi <= 0; else if (ckstb) begin f_mqspi <= f_mqspi << 1; if (maintenance && !m_final && new_word &&(!m_this_word[M_WAITBIT]) &&(m_this_word[9])) begin if (m_cs_n && !OPT_ODDR) f_mqspi[0] <= 1'b1; else f_mqspi[1] <= 1'b1; end end always @(*) if (OPT_ODDR) assert(!f_mspi[0] && !f_mqspi[0]); always @(*) if ((|f_mspi) || (|f_mqspi)) begin assert(maintenance); assert(!m_cs_n); assert(m_mod == f_last_word[9:8]); assert(m_midcount == 1'b0); end else if (maintenance && o_qspi_cs_n) begin assert(f_last_word[M_WAITBIT]); assert(m_counter <= f_last_word[M_WAITBIT-1:0]); assert(m_midcount == (m_counter != 0)); assert(m_cs_n); end always @(*) assert((f_mspi == 0)||(f_mqspi == 0)); always @(*) if (|f_mspi) assert(m_mod == NORMAL_SPI); always @(*) case(f_mspi[8:1]) 8'h00: begin end 8'h01: assert(m_dat[0] == f_last_word[7]); 8'h02: assert(m_dat[0] == f_last_word[6]); 8'h04: assert(m_dat[0] == f_last_word[5]); 8'h08: assert(m_dat[0] == f_last_word[4]); 8'h10: assert(m_dat[0] == f_last_word[3]); 8'h20: assert(m_dat[0] == f_last_word[2]); 8'h40: assert(m_dat[0] == f_last_word[1]); 8'h80: assert(m_dat[0] == f_last_word[0]); default: begin assert(0); end endcase always @(*) if (|f_mqspi) assert(m_mod == QUAD_WRITE || m_mod == QUAD_READ); always @(*) case(f_mqspi[2:1]) 2'b00: begin end 2'b01: assert(m_dat[3:0] == f_last_word[7:4]); 2'b10: assert(m_dat[3:0] == f_last_word[3:0]); default: begin assert(f_mqspi != 2'b11); end endcase `endif end else begin : NO_STARTUP_OPT always @(*) begin maintenance = 0; m_mod = 2'b00; m_cs_n = 1'b1; m_clk = 1'b0; m_dat = 4'h0; end // verilator lint_off UNUSED wire [8:0] unused_maintenance; assign unused_maintenance = { maintenance, m_mod, m_cs_n, m_clk, m_dat }; // verilator lint_on UNUSED end endgenerate reg [32+(OPT_ADDR32 ? 8:0)+4*(OPT_ODDR ? 0:1)-1:0] data_pipe; reg pre_ack = 1'b0; reg actual_sck; // // // Data / access portion // // initial data_pipe = 0; always @(posedge i_clk) begin if (!o_wb_stall) begin // Set the high bits to zero initially data_pipe <= 0; data_pipe[8+LGFLASHSZ-1:0] <= { i_wb_addr, 1'b0, 4'ha, 4'h0 }; if (i_cfg_stb) // High speed configuration I/O data_pipe[24+(OPT_ADDR32 ? 8:0) +: 8] <= i_wb_data[7:0]; if ((i_cfg_stb)&&(!i_wb_data[QSPEED_BIT])) begin // Low speed configuration I/O data_pipe[28+(OPT_ADDR32 ? 8:0)]<= i_wb_data[7]; data_pipe[24+(OPT_ADDR32 ? 8:0)]<= i_wb_data[6]; end if (i_cfg_stb) begin // These can be set independent of speed data_pipe[20+(OPT_ADDR32 ? 8:0)]<= i_wb_data[5]; data_pipe[16+(OPT_ADDR32 ? 8:0)]<= i_wb_data[4]; data_pipe[12+(OPT_ADDR32 ? 8:0)]<= i_wb_data[3]; data_pipe[ 8+(OPT_ADDR32 ? 8:0)]<= i_wb_data[2]; data_pipe[ 4+(OPT_ADDR32 ? 8:0)]<= i_wb_data[1]; data_pipe[ 0+(OPT_ADDR32 ? 8:0)]<= i_wb_data[0]; end end else if (ckstb) data_pipe <= { data_pipe[(32+(OPT_ADDR32 ? 8:0)+4*((OPT_ODDR ? 0:1)-1))-1:0], 4'h0 }; if (maintenance) data_pipe[28+(OPT_ADDR32 ? 8:0)+4*(OPT_ODDR ? 0:1) +: 4] <= m_dat; end assign o_qspi_dat = data_pipe[28+(OPT_ADDR32 ? 8:0)+4*(OPT_ODDR ? 0:1) +: 4]; // Since we can't abort any transaction once started, without // risking losing XIP mode or any other mode we might be in, we'll // keep track of whether this operation should be ack'd upon // completion always @(posedge i_clk) if ((i_reset)||(!i_wb_cyc)) pre_ack <= 1'b0; else if ((bus_request)||(cfg_write)) pre_ack <= 1'b1; generate if (OPT_PIPE) begin : OPT_PIPE_BLOCK reg r_pipe_req; wire w_pipe_condition; reg [(AW-1):0] next_addr; always @(posedge i_clk) if (!o_wb_stall) next_addr <= i_wb_addr + 2'd2; assign w_pipe_condition = (i_wb_stb)&&(!i_wb_we)&&(pre_ack) &&(!maintenance) &&(!cfg_mode) &&(!o_qspi_cs_n) &&(|clk_ctr[2:0]) &&(next_addr == i_wb_addr); initial r_pipe_req = 1'b0; always @(posedge i_clk) if ((clk_ctr == 1)&&(ckstb)) r_pipe_req <= 1'b0; else r_pipe_req <= w_pipe_condition; assign pipe_req = r_pipe_req; end else begin assign pipe_req = 1'b0; end endgenerate initial clk_ctr = 0; always @(posedge i_clk) if ((i_reset)||(maintenance)) clk_ctr <= 0; else if ((bus_request)&&(!pipe_req)) // Notice that this is only for // regular bus reads, and so the check for // !pipe_req clk_ctr <= 5'd14 + NDUMMY + (OPT_ADDR32 ? 2:0)+(OPT_ODDR ? 0:1); else if (bus_request) // && pipe_req // Otherwise, if this is a piped read, we'll // reset the counter back to eight. clk_ctr <= 5'd8; else if (cfg_ls_write) clk_ctr <= 5'd8 + ((OPT_ODDR) ? 0:1); else if (cfg_write) clk_ctr <= 5'd2 + ((OPT_ODDR) ? 0:1); else if ((ckstb)&&(|clk_ctr)) clk_ctr <= clk_ctr - 1'b1; initial o_qspi_sck = (!OPT_ODDR); always @(posedge i_clk) if (i_reset) o_qspi_sck <= (!OPT_ODDR); else if (maintenance) o_qspi_sck <= m_clk; else if ((!OPT_ODDR)&&(bus_request)&&(pipe_req)) o_qspi_sck <= 1'b0; else if ((bus_request)||(cfg_write)) o_qspi_sck <= 1'b1; else if (OPT_ODDR) begin if ((cfg_mode)&&(clk_ctr <= 1)) // Config mode has no pipe instructions o_qspi_sck <= 1'b0; else if (clk_ctr[4:0] > 5'd1) o_qspi_sck <= 1'b1; else o_qspi_sck <= 1'b0; end else if (((ckpos)&&(!o_qspi_sck))||(o_qspi_cs_n)) begin o_qspi_sck <= 1'b1; end else if ((ckneg)&&(o_qspi_sck)) begin if ((cfg_mode)&&(clk_ctr <= 1)) // Config mode has no pipe instructions o_qspi_sck <= 1'b1; else if (clk_ctr[4:0] > 5'd1) o_qspi_sck <= 1'b0; else o_qspi_sck <= 1'b1; end initial o_qspi_cs_n = 1'b1; always @(posedge i_clk) if (i_reset) o_qspi_cs_n <= 1'b1; else if (maintenance) o_qspi_cs_n <= m_cs_n; else if ((cfg_stb)&&(i_wb_we)) o_qspi_cs_n <= (!i_wb_data[CFG_MODE])||(i_wb_data[USER_CS_n]); else if ((OPT_CFG)&&(cfg_cs)) o_qspi_cs_n <= 1'b0; else if ((bus_request)||(cfg_write)) o_qspi_cs_n <= 1'b0; else if (ckstb) o_qspi_cs_n <= (clk_ctr <= 1); // Control the mode of the external pins // NORMAL_SPI: i_miso is an input, o_mosi is an output // QUAD_READ: i_miso is an input, o_mosi is an input // QUAD_WRITE: i_miso is an output, o_mosi is an output initial o_qspi_mod = NORMAL_SPI; always @(posedge i_clk) if (i_reset) o_qspi_mod <= NORMAL_SPI; else if (maintenance) o_qspi_mod <= m_mod; else if ((bus_request)&&(!pipe_req)) o_qspi_mod <= QUAD_WRITE; else if ((bus_request)||(cfg_hs_read)) o_qspi_mod <= QUAD_READ; else if (cfg_hs_write) o_qspi_mod <= QUAD_WRITE; else if ((cfg_ls_write)||((cfg_mode)&&(!cfg_speed))) o_qspi_mod <= NORMAL_SPI; else if ((ckstb)&&(clk_ctr <= 5'd9)&&((!cfg_mode)||(!cfg_dir))) o_qspi_mod <= QUAD_READ; initial o_wb_stall = 1'b1; always @(posedge i_clk) if (i_reset) o_wb_stall <= 1'b1; else if (maintenance) o_wb_stall <= 1'b1; else if ((RDDELAY > 0)&&((i_cfg_stb)||(i_wb_stb))&&(!o_wb_stall)) o_wb_stall <= 1'b1; else if ((RDDELAY == 0)&&((cfg_write)||(bus_request))) o_wb_stall <= 1'b1; else if (ckstb || clk_ctr == 0) begin if (ckpre && (i_wb_stb)&&(pipe_req)&&(clk_ctr == 5'd2)) o_wb_stall <= 1'b0; else if ((clk_ctr > 1)||(xtra_stall)) o_wb_stall <= 1'b1; else o_wb_stall <= 1'b0; end else if (ckpre && (i_wb_stb)&&(pipe_req)&&(clk_ctr == 5'd1)) o_wb_stall <= 1'b0; initial dly_ack = 1'b0; always @(posedge i_clk) if (i_reset) dly_ack <= 1'b0; else if ((ckstb)&&(clk_ctr == 1)) dly_ack <= (i_wb_cyc)&&(pre_ack); else if ((i_wb_stb)&&(!o_wb_stall)&&(!bus_request)) dly_ack <= 1'b1; else if (cfg_noop) dly_ack <= 1'b1; else dly_ack <= 1'b0; generate if (OPT_ODDR) begin : SCK_ACTUAL always @(*) actual_sck = o_qspi_sck; end else if (OPT_CLKDIV == 1) begin : SCK_ONE initial actual_sck = 1'b0; always @(posedge i_clk) if (i_reset) actual_sck <= 1'b0; else actual_sck <= (!o_qspi_sck)&&(clk_ctr > 0); end else begin : SCK_ANY initial actual_sck = 1'b0; always @(posedge i_clk) if (i_reset) actual_sck <= 1'b0; else actual_sck <= (o_qspi_sck)&&(ckpre)&&(clk_ctr > 0); end endgenerate `ifdef FORMAL reg [F_LGDEPTH-1:0] f_extra; `endif generate if (RDDELAY == 0) begin : RDDELAY_NONE always @(*) begin read_sck = actual_sck; o_wb_ack = dly_ack; xtra_stall = 1'b0; end `ifdef FORMAL always @(*) f_extra = 0; `endif end else begin : RDDELAY_NONZERO reg [RDDELAY-1:0] sck_pipe, ack_pipe, stall_pipe; reg not_done; initial sck_pipe = 0; initial ack_pipe = 0; initial stall_pipe = -1; if (RDDELAY > 1) begin always @(posedge i_clk) if (i_reset) sck_pipe <= 0; else sck_pipe <= { sck_pipe[RDDELAY-2:0], actual_sck }; always @(posedge i_clk) if (i_reset || !i_wb_cyc) ack_pipe <= 0; else ack_pipe <= { ack_pipe[RDDELAY-2:0], dly_ack }; always @(posedge i_clk) if (i_reset) stall_pipe <= -1; else stall_pipe <= { stall_pipe[RDDELAY-2:0], not_done }; end else // if (RDDELAY > 0) begin always @(posedge i_clk) if (i_reset) sck_pipe <= 0; else sck_pipe <= actual_sck; always @(posedge i_clk) if (i_reset || !i_wb_cyc) ack_pipe <= 0; else ack_pipe <= dly_ack; always @(posedge i_clk) if (i_reset) stall_pipe <= -1; else stall_pipe <= not_done; end always @(*) begin not_done = (i_wb_stb || i_cfg_stb) && !o_wb_stall; if (clk_ctr > 1) not_done = 1'b1; if ((clk_ctr == 1)&&(!ckstb)) not_done = 1'b1; end always @(*) o_wb_ack = ack_pipe[RDDELAY-1]; always @(*) read_sck = sck_pipe[RDDELAY-1]; always @(*) xtra_stall = |stall_pipe; `ifdef FORMAL integer k; always @(*) if (!i_wb_cyc) f_extra = 0; else begin f_extra = 0; for(k=0; k 0) begin always @(posedge i_clk) assume(i_qspi_dat == $past(dly_idat,RDDELAY)); end else begin always @(posedge i_clk) assume(i_qspi_dat == dly_idat); end endgenerate // //////////////////////////////////////////////////////////////////////// // // Maintenance mode assertions // always @(*) if (maintenance) begin assume((!i_wb_stb)&&(!i_cfg_stb)); assert(f_outstanding == 0); assert(o_wb_stall); // assert(clk_ctr == 0); assert(cfg_mode == 1'b0); end always @(*) if (maintenance) begin assert(clk_ctr == 0); assert(!o_wb_ack); end //////////////////////////////////////////////////////////////////////// // // // always @(posedge i_clk) if (dly_ack) assert(clk_ctr[2:0] == 0); // Zero cycle requests always @(posedge i_clk) if ((f_past_valid)&&(!$past(i_reset))&&(($past(cfg_noop)) ||($past(i_wb_stb && i_wb_we && !o_wb_stall)))) assert((dly_ack)&&((!i_wb_cyc) ||(f_outstanding == 1 + f_extra))); always @(posedge i_clk) if ((f_outstanding > 0)&&(clk_ctr > 0)) assert(pre_ack); always @(posedge i_clk) if ((i_wb_cyc)&&(dly_ack)) assert(f_outstanding >= 1 + f_extra); always @(posedge i_clk) if ((f_past_valid)&&(clk_ctr == 0)&&(!dly_ack) &&((!$past(i_wb_stb|i_cfg_stb))||($past(o_wb_stall)))) assert(f_outstanding == f_extra); always @(*) if ((i_wb_cyc)&&(pre_ack)&&(!o_qspi_cs_n)) assert((f_outstanding >= 1 + f_extra)||((OPT_CFG)&&(cfg_mode))); always @(*) if ((cfg_mode)&&(!dly_ack)&&(clk_ctr == 0)) assert(f_outstanding == f_extra); always @(*) if (cfg_mode) assert(f_outstanding <= 1 + f_extra); ///////////////// // // Idle channel // ///////////////// always @(*) if (!maintenance) begin if (o_qspi_cs_n) begin assert(clk_ctr == 0); assert(o_qspi_sck == !OPT_ODDR); end else if (clk_ctr == 0) assert(o_qspi_sck == !OPT_ODDR); end always @(*) assert(o_qspi_mod != 2'b01); always @(*) if (clk_ctr > (5'h8 * (1+OPT_CLKDIV))) begin assert(!cfg_mode); assert(!cfg_cs); end always @(posedge i_clk) if ((OPT_CLKDIV==1)&&(!o_qspi_cs_n)&&(!$past(o_qspi_cs_n)) &&(!$past(o_qspi_cs_n,2))&&(!cfg_mode)) assert(o_qspi_sck != $past(o_qspi_sck)); ///////////////// // // Read requests // ///////////////// always @(posedge i_clk) if ((f_past_valid)&&(!$past(i_reset))&&($past(bus_request))) begin assert(!o_qspi_cs_n); if ((OPT_ODDR)||(!$past(pipe_req))) assert(o_qspi_sck == 1'b1); else assert(o_qspi_sck == 1'b0); // if (!$past(o_qspi_cs_n)) begin assert(clk_ctr == 5'd8); assert(o_qspi_mod == QUAD_READ); end else begin assert(clk_ctr == 5'd14 + NDUMMY + (OPT_ADDR32 ? 2:0) + (OPT_ODDR ? 0:1)); assert(o_qspi_mod == QUAD_WRITE); end end always @(*) assert(clk_ctr <= 5'd18 + NDUMMY + (OPT_ODDR ? 0:1)); always @(*) if ((OPT_ODDR)&&(!o_qspi_cs_n)) assert((o_qspi_sck)||(actual_sck)||(cfg_mode)||(maintenance)); always @(*) if ((RDDELAY == 0)&&((dly_ack)&&(clk_ctr == 0))) assert(!o_wb_stall); always @(*) if (!maintenance) begin if (cfg_mode) begin if (!cfg_cs) assert(o_qspi_cs_n); else if (!cfg_speed) assert(o_qspi_mod == NORMAL_SPI); else if ((cfg_dir)&&(clk_ctr > 0)) assert(o_qspi_mod == QUAD_WRITE); end else if (clk_ctr > 5'd8) assert(o_qspi_mod == QUAD_WRITE); else if (clk_ctr > 0) assert(o_qspi_mod == QUAD_READ); end always @(posedge i_clk) if (((!OPT_PIPE)&&(clk_ctr != 0))||(clk_ctr > 5'd1)) assert(o_wb_stall); always @(posedge i_clk) if ((OPT_CLKDIV>0)&&($past(o_qspi_cs_n))) assert(o_qspi_sck); ///////////////// // // User mode // ///////////////// always @(*) if ((maintenance)||(!OPT_CFG)) assert(!cfg_mode); always @(*) if ((OPT_CFG)&&(cfg_mode)) assert(o_qspi_cs_n == !cfg_cs); else assert(!cfg_cs); // // // // always @(posedge i_clk) if (bus_request) begin // Make sure all of the bits are set fv_addr <= 0; // Now set as many bits as we have address bits fv_addr[AW-1:0] <= i_wb_addr; end always @(posedge i_clk) if ((i_wb_stb || i_cfg_stb) && !o_wb_stall && i_wb_we) fv_data <= i_wb_data; // Memory reads initial f_memread = 0; generate if (RDDELAY == 0) begin always @(posedge i_clk) if (i_reset) f_memread <= 0; else begin if (ckstb) f_memread <= { f_memread[F_MEMACK-1:0],1'b0}; else if (!OPT_ODDR) f_memread[F_MEMACK] <= 1'b0; if ((bus_request)&&(o_qspi_cs_n)) f_memread[0] <= 1'b1; end end else begin always @(posedge i_clk) if (i_reset) f_memread <= 0; else begin if (ckstb) f_memread <= { f_memread[F_MEMACK-1:0],1'b0}; else if (!OPT_ODDR) f_memread[F_MEMACK:F_MEMDONE] <= { f_memread[F_MEMACK-1:F_MEMDONE],1'b0}; if ((bus_request)&&(o_qspi_cs_n)) f_memread[0] <= 1'b1; end end endgenerate always @(posedge i_clk) if ((OPT_ODDR)&&(|f_memread[F_MEMDONE-1:0])) assert(o_qspi_sck); always @(posedge i_clk) if (|f_memread[6+(OPT_ADDR32 ? 2:0)+(OPT_ODDR ? 0:1):0]) assert(o_qspi_mod == QUAD_WRITE); else if (|f_memread[(OPT_ODDR ? 0:1)+(OPT_ADDR32 ? 2:0) +7 +: NDUMMY]) // begin assert(1); end begin end else if (|f_memread) assert(o_qspi_mod == QUAD_READ); generate if (RDDELAY > 0) begin always @(posedge i_clk) if ($past(ckpos,RDDELAY)) begin if ($past(o_qspi_mod,RDDELAY) == NORMAL_SPI) f_past_data <= { f_past_data[31:0], i_qspi_dat[1] }; else if ($past(o_qspi_mod,RDDELAY) == QUAD_READ) f_past_data <= { f_past_data[28:0], i_qspi_dat[3:0] }; end end else begin always @(posedge i_clk) if (ckpos) begin if (o_qspi_mod == NORMAL_SPI) f_past_data <= { f_past_data[31:0], i_qspi_dat[1] }; else if (o_qspi_mod == QUAD_READ) f_past_data <= { f_past_data[28:0], i_qspi_dat[3:0] }; end end endgenerate always @(posedge i_clk) if (|f_memread[(OPT_ODDR ? 0:1) +: 7 + (OPT_ADDR32 ? 2:0)]) begin if (OPT_ADDR32) begin // Eight extra bits of address if (f_memread[(OPT_ODDR ? 0:1)]) assert(o_qspi_dat== fv_addr[29:26]); if (f_memread[1 + (OPT_ODDR ? 0:1)]) assert(o_qspi_dat== fv_addr[25:22]); end // 6 nibbles of address, one nibble of mode if (f_memread[(OPT_ODDR ? 0:1)+(OPT_ADDR32 ? 2:0)]) assert(o_qspi_dat== fv_addr[21:18]); if (f_memread[1+(OPT_ODDR ? 0:1)+(OPT_ADDR32 ? 2:0)]) assert(o_qspi_dat== fv_addr[17:14]); if (f_memread[2+(OPT_ODDR ? 0:1)+(OPT_ADDR32 ? 2:0)]) assert(o_qspi_dat== fv_addr[13:10]); if (f_memread[3+(OPT_ODDR ? 0:1)+(OPT_ADDR32 ? 2:0)]) assert(o_qspi_dat== fv_addr[ 9: 6]); if (f_memread[4+(OPT_ODDR ? 0:1)+(OPT_ADDR32 ? 2:0)]) assert(o_qspi_dat== fv_addr[ 5: 2]); if (f_memread[5+(OPT_ODDR ? 0:1)+(OPT_ADDR32 ? 2:0)]) assert(o_qspi_dat=={ fv_addr[1:0],2'b00 }); if (f_memread[6+(OPT_ODDR ? 0:1)+(OPT_ADDR32 ? 2:0)]) assert(o_qspi_dat == 4'ha); end always @(posedge i_clk) if (OPT_ODDR) begin if (f_memread[F_MEMACK] && OPT_ENDIANSWAP) begin assert(o_wb_data[ 7: 4] == $past(i_qspi_dat,8)); assert(o_wb_data[ 3: 0] == $past(i_qspi_dat,7)); assert(o_wb_data[15:12] == $past(i_qspi_dat,6)); assert(o_wb_data[11: 8] == $past(i_qspi_dat,5)); assert(o_wb_data[23:20] == $past(i_qspi_dat,4)); assert(o_wb_data[19:16] == $past(i_qspi_dat,3)); assert(o_wb_data[31:28] == $past(i_qspi_dat,2)); assert(o_wb_data[27:24] == $past(i_qspi_dat,1)); end else if (f_memread[F_MEMACK]) begin assert(o_wb_data[31:28] == $past(i_qspi_dat,8)); assert(o_wb_data[27:24] == $past(i_qspi_dat,7)); assert(o_wb_data[23:20] == $past(i_qspi_dat,6)); assert(o_wb_data[19:16] == $past(i_qspi_dat,5)); assert(o_wb_data[15:12] == $past(i_qspi_dat,4)); assert(o_wb_data[11: 8] == $past(i_qspi_dat,3)); assert(o_wb_data[ 7: 4] == $past(i_qspi_dat,2)); assert(o_wb_data[ 3: 0] == $past(i_qspi_dat,1)); end else if (|f_memread) begin if (!OPT_PIPE) assert(o_wb_stall); else if (!f_memread[F_MEMDONE-1]) assert(o_wb_stall); assert(!o_wb_ack); end end else if (f_memread[F_MEMACK] && OPT_ENDIANSWAP) assert((!o_wb_ack)||( o_wb_data[ 7: 0] == f_past_data[31:24] && o_wb_data[15: 8] == f_past_data[23:16] && o_wb_data[23:16] == f_past_data[15: 8] && o_wb_data[31:24] == f_past_data[ 7: 0])); else if (f_memread[F_MEMACK]) // 25 assert((!o_wb_ack)||(o_wb_data == f_past_data[31:0])); else if (|f_memread) begin if ((!OPT_PIPE)||(!ckstb)) assert(o_wb_stall); else if (!f_memread[F_MEMDONE-1]) assert(o_wb_stall); assert(!o_wb_ack); end always @(posedge i_clk) if (f_memread[F_MEMDONE]) assert((clk_ctr == 0)||((OPT_PIPE)&&(clk_ctr == F_PIPEDONE))); // else if (|f_memread[F_MEMDONE-1:0]) // assert(f_memread[F_MEMDONE-clk_ctr]); generate for(k=0; k 0) begin always @(posedge i_clk) if (|f_cfghswrite[F_CFGHSACK:F_CFGHSDONE]) assert(!actual_sck); end endgenerate always @(posedge i_clk) if (|f_cfghswrite) begin assert(!o_qspi_cs_n); assert(o_qspi_mod == QUAD_WRITE); end always @(posedge i_clk) if (|f_cfghswrite[1:0]) begin assert(!dly_ack); assert(o_wb_stall); end always @(posedge i_clk) if (f_cfghswrite[F_CFGHSACK]) begin assert((o_wb_ack)||(!$past(pre_ack))||(!$past(i_wb_cyc))); assert(o_qspi_mod == QUAD_WRITE); assert(!o_wb_stall); end else if (|f_cfghswrite) begin assert(o_wb_stall); assert(!o_wb_ack); end //////////////////////////////////////////////////////////////////////// // // High speed config read // initial f_cfghsread = 0; always @(posedge i_clk) if (i_reset) f_cfghsread <= 0; else begin if (ckstb) f_cfghsread <= { f_cfghsread[F_CFGHSACK-1:0], 1'b0 }; else if (!OPT_ODDR) f_cfghsread[F_CFGHSACK:F_CFGHSDONE] <={f_cfghsread[F_CFGHSACK:F_CFGHSDONE], 1'b0}; if (cfg_hs_read) f_cfghsread[0] <= 1'b1; end always @(*) if (OPT_ODDR) begin if (|f_cfghsread[1:0]) assert(o_qspi_sck); else if (|f_cfghsread) assert(!o_qspi_sck); end always @(posedge i_clk) if (|f_cfghsread[F_CFGHSACK:F_CFGHSDONE]) assert(o_qspi_sck == !OPT_ODDR); always @(*) if ((!maintenance)&&(o_qspi_cs_n)) assert(!actual_sck); always @(posedge i_clk) if (|f_cfghsread[F_CFGHSDONE-1:F_CFGHSDONE-2]) begin assert(!dly_ack); assert(!o_qspi_cs_n); assert(o_qspi_mod == QUAD_READ); assert(o_wb_stall); end generate if (RDDELAY > 0) begin always @(posedge i_clk) if (|f_cfghswrite[F_CFGHSACK:F_CFGHSDONE]) assert(!actual_sck); end endgenerate always @(posedge i_clk) if (f_cfghsread[F_CFGHSACK]) begin if (OPT_ODDR) begin assert(o_wb_data[7:4] == $past(i_qspi_dat[3:0],2)); assert(o_wb_data[3:0] == $past(i_qspi_dat[3:0],1)); end assert(o_wb_data[7:0] == f_past_data[7:0]); assert((o_wb_ack)||(!$past(pre_ack))||(!$past(i_wb_cyc))); assert(o_qspi_mod == QUAD_READ); assert(!o_wb_stall); end else if (|f_cfghsread) begin assert(o_wb_stall); assert(!o_wb_ack); end //////////////////////////////////////////////////////////////////////// // // Crossover checks // wire f_qspi_not_done, f_qspi_not_ackd, f_qspi_active, f_qspi_ack; assign f_qspi_not_done = (|f_memread[F_MEMDONE-1:0]) ||(|f_piperead[F_PIPEDONE-1:0]) ||(|f_cfglswrite[F_CFGLSDONE-1:0]) ||(|f_cfghswrite[F_CFGHSDONE-1:0]) ||(|f_cfghsread[F_CFGHSDONE-1:0]); assign f_qspi_active = (!maintenance)&&( (|f_memread[F_MEMACK-1:0]) ||(|f_piperead[F_PIPEACK-1:0]) ||(|f_cfglswrite[F_CFGLSACK-1:0]) ||(|f_cfghswrite[F_CFGHSACK-1:0]) ||(|f_cfghsread[F_CFGHSACK-1:0])); assign f_qspi_not_ackd = (!maintenance)&&(!f_qspi_not_done)&&( (|f_memread[F_MEMACK-1:0]) ||(|f_piperead[F_PIPEACK-1:0]) ||(|f_cfglswrite[F_CFGLSACK-1:0]) ||(|f_cfghswrite[F_CFGHSACK-1:0]) ||(|f_cfghsread[F_CFGHSACK-1:0])); assign f_qspi_ack = (!maintenance)&& (|f_memread[F_MEMACK:0]) ||(|f_piperead[F_PIPEACK:0]) ||(|f_cfglswrite[F_CFGLSACK:0]) ||(|f_cfghswrite[F_CFGHSACK:0]) ||(|f_cfghsread[F_CFGHSACK:0]); always @(*) begin if ((|f_memread[F_MEMDONE:0])||(|f_piperead[F_PIPEDONE:0])) begin assert(f_cfglswrite == 0); assert(f_cfghswrite == 0); assert(f_cfghsread == 0); end if (|f_cfglswrite[F_CFGLSDONE:0]) begin assert(f_cfghswrite[F_CFGHSDONE:0] == 0); assert(f_cfghsread[F_CFGHSDONE:0] == 0); end if (|f_cfghswrite[F_CFGHSDONE:0]) assert(f_cfghsread[F_CFGHSDONE:0] == 0); if (maintenance) begin assert(f_memread == 0); assert(f_piperead == 0); assert(f_cfglswrite == 0); assert(f_cfghswrite == 0); assert(f_cfghsread == 0); end assert(clk_ctr <= F_MEMDONE); end always @(posedge i_clk) if ((f_past_valid)&&(!f_qspi_ack)&&(!$past(i_reset)) &&(!$past(maintenance))) begin assert($stable(o_wb_data[7:0])); if (!cfg_mode && !$past(cfg_mode) && !$past(i_cfg_stb && !o_wb_stall) &&($past(f_past_valid)) && !$past(i_cfg_stb && !o_wb_stall,2)) assert($stable(o_wb_data)); end always @(*) if (!maintenance && actual_sck) begin assert(f_qspi_not_done); /* assert((|f_memread[F_MEMDONE:0]) ||(|f_piperead[F_PIPEDONE:0]) ||(|f_cfglswrite[F_CFGLSDONE:0]) ||(|f_cfghswrite[F_CFGHSDONE:0]) ||(|f_cfghsread[F_CFGHSDONE:0])); */ end always @(*) if (!maintenance && !o_qspi_cs_n && !cfg_mode) begin assert((|f_memread[F_MEMDONE:0]) ||(|f_piperead[F_PIPEDONE:0])); end else if (!maintenance && cfg_mode) begin if ((o_qspi_sck == OPT_ODDR)||(clk_ctr > 0)||(actual_sck)) begin assert( (|f_cfglswrite[F_CFGLSDONE-1:0]) ||(|f_cfghswrite[F_CFGHSDONE-1:0]) ||(|f_cfghsread[F_CFGHSDONE-1:0])); end end always @(posedge i_clk) if (o_wb_ack && !$past((i_wb_stb || i_cfg_stb)&&!o_wb_stall, 1+RDDELAY)) begin assert(f_memread[F_MEMACK] || f_piperead[F_PIPEACK] || f_cfglswrite[F_CFGLSACK] || f_cfghswrite[F_CFGHSACK] || f_cfghsread[F_CFGHSACK]); end //////////////////////////////////////////////////////////////////////// // // Cover Properties // //////////////////////////////////////////////////////////////////////// // // Due to the way the chip starts up, requiring 32k+ maintenance clocks, // these cover statements are not likely to be hit generate if (!OPT_STARTUP) begin // Why is this generate only if !OPT_STARTUP? For performance // reasons. The startup sequence can take a great many clocks. // cover() would never be able to work through all of those // clocks to find the following examples, and so it would fail. // By only checking these cover() statements if !OPT_STARTUP, // we give them an opportunity to succeed always @(posedge i_clk) cover(o_wb_ack && f_memread[ F_MEMACK]); if (OPT_CFG) begin always @(posedge i_clk) begin cover(cfg_ls_write); cover(cfg_hs_write); cover(cfg_hs_read); cover(|f_cfglswrite); cover(|f_cfghsread); cover(|f_cfghswrite); cover(o_wb_ack && |f_cfglswrite); cover(o_wb_ack && |f_cfghsread); cover(o_wb_ack && |f_cfghswrite); cover(f_cfglswrite[0]); cover(f_cfghsread[ 0]); cover(f_cfghswrite[0]); cover(f_cfglswrite[F_CFGLSACK-2]); cover(f_cfghsread[ F_CFGHSACK-2]); cover(f_cfghswrite[F_CFGHSACK-2]); cover(f_cfglswrite[F_CFGLSACK-1]); cover(f_cfghsread[ F_CFGHSACK-1]); cover(f_cfghswrite[F_CFGHSACK-1]); cover(f_cfglswrite[F_CFGLSACK]); cover(f_cfghsread[ F_CFGHSACK]); cover(f_cfghswrite[F_CFGHSACK]); cover(o_wb_ack && f_cfglswrite[F_CFGLSACK]); cover(o_wb_ack && f_cfghsread[ F_CFGHSACK]); cover(o_wb_ack && f_cfghswrite[F_CFGHSACK]); end end if (OPT_PIPE) begin always @(posedge i_clk) cover(o_wb_ack && f_piperead[F_PIPEACK]); end // // // always @(posedge i_clk) cover((o_wb_ack)&&(!cfg_mode)); always @(posedge i_clk) cover((o_wb_ack)&&(!cfg_mode)&&(!$past(o_qspi_cs_n))); always @(posedge i_clk) // Cover a piped transaction cover((o_wb_ack)&&(!cfg_mode)&&(!o_qspi_cs_n)); //! always @(posedge i_clk) cover((o_wb_ack)&&(cfg_mode)&&(cfg_speed)); always @(posedge i_clk) cover((o_wb_ack)&&(cfg_mode)&&(!cfg_speed)&&(cfg_dir)); always @(posedge i_clk) cover((o_wb_ack)&&(cfg_mode)&&(!cfg_speed)&&(!cfg_dir)); end else begin always @(posedge i_clk) cover(!maintenance); end endgenerate `endif endmodule