mirror of
https://github.com/Polprzewodnikowy/SummerCart64.git
synced 2024-11-26 07:34:15 +01:00
2240 lines
57 KiB
Coq
2240 lines
57 KiB
Coq
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// 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
|
||
|
// <http://www.gnu.org/licenses/> 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<<M_LGADDR)-1];
|
||
|
reg [M_LGADDR-1:0] m_cmd_index;
|
||
|
|
||
|
reg [M_WAITBIT-1:0] m_counter;
|
||
|
reg m_midcount;
|
||
|
reg [3:0] m_bitcount;
|
||
|
reg [7:0] m_byte;
|
||
|
|
||
|
// Let's script our startup with a series of commands.
|
||
|
// These commands are specific to the Micron Serial NOR flash
|
||
|
// memory that was on the original Arty A7 board. Switching
|
||
|
// from one memory to another should only require adjustments
|
||
|
// to this startup sequence, and to the flashdrvr.cpp module
|
||
|
// found in sw/host.
|
||
|
//
|
||
|
// The format of the data words is ...
|
||
|
// 1'bit (MSB) to indicate this is a counter word.
|
||
|
// Counter words count a number of idle cycles,
|
||
|
// in which the port is unused (CSN is high)
|
||
|
//
|
||
|
// 2'bit mode. This is either ...
|
||
|
// NORMAL_SPI, for a normal SPI interaction:
|
||
|
// MOSI, MISO, WPn and HOLD
|
||
|
// QUAD_READ, all four pins set as inputs. In this
|
||
|
// startup, the input values will be
|
||
|
// ignored.
|
||
|
// or QUAD_WRITE, all four pins are outputs. This is
|
||
|
// important for getting the flash into
|
||
|
// an XIP mode that we can then use for
|
||
|
// all reads following.
|
||
|
//
|
||
|
// 8'bit data To be sent 1-bit at a time in NORMAL_SPI
|
||
|
// mode, or 4-bits at a time in QUAD_WRITE
|
||
|
// mode. Ignored otherwis
|
||
|
//
|
||
|
integer k;
|
||
|
initial
|
||
|
// if (OPT_STARTUP_FILE)
|
||
|
// $readmemh(OPT_STARTUP_FILE, m_cmd_word); else
|
||
|
begin
|
||
|
for(k=0; k<(1<<M_LGADDR); k=k+1)
|
||
|
m_cmd_word[k] = -1;
|
||
|
// cmd_word= m_ctr_flag, m_mod[1:0],
|
||
|
// m_cs_n, m_clk, m_data[3:0]
|
||
|
// Start off idle
|
||
|
// This is really redundant since all of our commands are
|
||
|
// idle's.
|
||
|
m_cmd_word[5'h07] = -1;
|
||
|
//
|
||
|
// Since we don't know what mode we started in, whether the
|
||
|
// device was left in XIP mode or some other mode, we'll start
|
||
|
// by exiting any mode we might have been in.
|
||
|
//
|
||
|
// The key to doing this is to issue a non-command, that can
|
||
|
// also be interpreted as an XIP address with an incorrect
|
||
|
// mode bit. That will get us out of any XIP mode, and back
|
||
|
// into a SPI mode we might use. The command is issued in
|
||
|
// NORMAL_SPI mode, however, since we don't know if the device
|
||
|
// is initially in XIP or not.
|
||
|
//
|
||
|
// Exit any QSPI mode we might've been in
|
||
|
m_cmd_word[5'h08] = { 1'b0, NORMAL_SPI, 8'hff }; // Addr 1
|
||
|
m_cmd_word[5'h09] = { 1'b0, NORMAL_SPI, 8'hff }; // Addr 2
|
||
|
m_cmd_word[5'h0a] = { 1'b0, NORMAL_SPI, 8'hff }; // Addr 2
|
||
|
// Idle
|
||
|
m_cmd_word[5'h0b] = { 1'b1, 10'h3f };
|
||
|
//
|
||
|
// Write configuration register
|
||
|
//
|
||
|
// The write enable must come first: 06
|
||
|
// m_cmd_word[5'h0c] = { 1'b0, NORMAL_SPI, 8'h06 };
|
||
|
m_cmd_word[5'h0c] = { 1'b0, NORMAL_SPI, 8'h50 };
|
||
|
//
|
||
|
// Idle
|
||
|
m_cmd_word[5'h0d] = { 1'b1, 10'h3ff };
|
||
|
//
|
||
|
// Write configuration register, follows a write-register
|
||
|
m_cmd_word[5'h0e] = { 1'b0, NORMAL_SPI, 8'h01 }; // WRR
|
||
|
m_cmd_word[5'h0f] = { 1'b0, NORMAL_SPI, 8'h00 }; // status register
|
||
|
m_cmd_word[5'h10] = { 1'b0, NORMAL_SPI, 8'h02 }; // Config register
|
||
|
//
|
||
|
// Idle
|
||
|
m_cmd_word[5'h11] = { 1'b1, 10'h3ff };
|
||
|
m_cmd_word[5'h12] = { 1'b1, 10'h3ff };
|
||
|
//
|
||
|
//
|
||
|
// WRDI: write disable: 04
|
||
|
m_cmd_word[5'h13] = { 1'b0, NORMAL_SPI, 8'h04 };
|
||
|
//
|
||
|
// Idle
|
||
|
m_cmd_word[5'h14] = { 1'b1, 10'h3ff };
|
||
|
//
|
||
|
// Enter into QSPI mode, 0xeb, 0,0,0
|
||
|
// 0xeb
|
||
|
m_cmd_word[5'h15] = { 1'b0, NORMAL_SPI, 8'heb };
|
||
|
// Addr #1
|
||
|
m_cmd_word[5'h16] = { 1'b0, QUAD_WRITE, 8'h00 };
|
||
|
// Addr #2
|
||
|
m_cmd_word[5'h17] = { 1'b0, QUAD_WRITE, 8'h00 };
|
||
|
// Addr #3
|
||
|
m_cmd_word[5'h18] = { 1'b0, QUAD_WRITE, 8'h00 };
|
||
|
// Mode byte
|
||
|
m_cmd_word[5'h19] = { 1'b0, QUAD_WRITE, 8'ha0 };
|
||
|
// Dummy clocks, x4 for this flash
|
||
|
m_cmd_word[5'h1a] = { 1'b0, QUAD_WRITE, 8'h00 };
|
||
|
m_cmd_word[5'h1b] = { 1'b0, QUAD_WRITE, 8'h00 };
|
||
|
// Now read a byte for form
|
||
|
m_cmd_word[5'h1c] = { 1'b0, QUAD_READ, 8'h00 };
|
||
|
//
|
||
|
// Idle -- These last two idles are *REQUIRED* and not optional
|
||
|
// (although they might be able to be trimmed back a bit...)
|
||
|
m_cmd_word[5'h1d] = -1;
|
||
|
m_cmd_word[5'h1e] = -1;
|
||
|
// Then we are in business!
|
||
|
end
|
||
|
|
||
|
reg m_final;
|
||
|
|
||
|
wire m_ce, new_word;
|
||
|
assign m_ce = (!m_midcount)&&(ckstb);
|
||
|
assign new_word = (m_ce && m_bitcount == 0);
|
||
|
|
||
|
//
|
||
|
initial maintenance = 1'b1;
|
||
|
initial m_cmd_index = M_FIRSTIDX;
|
||
|
always @(posedge i_clk)
|
||
|
if (i_reset)
|
||
|
begin
|
||
|
m_cmd_index <= M_FIRSTIDX;
|
||
|
maintenance <= 1'b1;
|
||
|
end else if (new_word)
|
||
|
begin
|
||
|
maintenance <= (maintenance)&&(!m_final);
|
||
|
if (!(&m_cmd_index))
|
||
|
m_cmd_index <= m_cmd_index + 1'b1;
|
||
|
end
|
||
|
|
||
|
initial m_this_word = -1;
|
||
|
always @(posedge i_clk)
|
||
|
if (new_word)
|
||
|
m_this_word <= m_cmd_word[m_cmd_index];
|
||
|
|
||
|
initial m_final = 1'b0;
|
||
|
always @(posedge i_clk)
|
||
|
if (i_reset)
|
||
|
m_final <= 1'b0;
|
||
|
else if (new_word)
|
||
|
m_final <= (m_final || (&m_cmd_index));
|
||
|
|
||
|
//
|
||
|
// m_midcount .. are we in the middle of a counter/pause?
|
||
|
//
|
||
|
initial m_midcount = 1;
|
||
|
initial m_counter = -1;
|
||
|
always @(posedge i_clk)
|
||
|
if (i_reset)
|
||
|
begin
|
||
|
m_midcount <= 1'b1;
|
||
|
`ifdef FORMAL
|
||
|
m_counter <= 3;
|
||
|
`else
|
||
|
m_counter <= -1;
|
||
|
`endif
|
||
|
end else if (new_word)
|
||
|
begin
|
||
|
m_midcount <= m_this_word[M_WAITBIT]
|
||
|
&& (|m_this_word[M_WAITBIT-1:0]);
|
||
|
if (m_this_word[M_WAITBIT])
|
||
|
begin
|
||
|
m_counter <= m_this_word[M_WAITBIT-1:0];
|
||
|
`ifdef FORMAL
|
||
|
if (m_this_word[M_WAITBIT-1:0] > 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<RDDELAY; k=k+1)
|
||
|
f_extra = f_extra + (ack_pipe[k] ? 1 : 0);
|
||
|
end
|
||
|
`endif // FORMAL
|
||
|
|
||
|
end endgenerate
|
||
|
|
||
|
always @(posedge i_clk)
|
||
|
begin
|
||
|
if (read_sck)
|
||
|
begin
|
||
|
if (OPT_ENDIANSWAP)
|
||
|
begin
|
||
|
if (!o_qspi_mod[1])
|
||
|
begin
|
||
|
o_wb_data <= { o_wb_data[30:24], i_qspi_dat[1],
|
||
|
o_wb_data[22:16], o_wb_data[31],
|
||
|
o_wb_data[14:8], o_wb_data[23],
|
||
|
o_wb_data[6:0], o_wb_data[15] };
|
||
|
end else begin
|
||
|
o_wb_data <= { o_wb_data[27:24], i_qspi_dat,
|
||
|
o_wb_data[19:16], o_wb_data[31:28],
|
||
|
o_wb_data[11:8], o_wb_data[23:20],
|
||
|
o_wb_data[3:0], o_wb_data[15:12]};
|
||
|
end
|
||
|
|
||
|
if (cfg_mode)
|
||
|
begin
|
||
|
// No endian-swapping in config mode
|
||
|
if (!o_qspi_mod[1])
|
||
|
o_wb_data[7:0]<= { o_wb_data[6:0], i_qspi_dat[1] };
|
||
|
else
|
||
|
o_wb_data[7:0]<= { o_wb_data[3:0], i_qspi_dat };
|
||
|
end
|
||
|
|
||
|
end else if (!o_qspi_mod[1])
|
||
|
// No endian-swapping
|
||
|
o_wb_data <= { o_wb_data[30:0], i_qspi_dat[1] };
|
||
|
else
|
||
|
o_wb_data <= { o_wb_data[27:0], i_qspi_dat };
|
||
|
end // read_sck
|
||
|
|
||
|
if ((OPT_CFG)&&(cfg_mode))
|
||
|
o_wb_data[16:8] <= { 4'b0, cfg_mode, cfg_speed, 1'b0,
|
||
|
cfg_dir, cfg_cs };
|
||
|
end
|
||
|
|
||
|
|
||
|
//
|
||
|
//
|
||
|
// User override access
|
||
|
//
|
||
|
//
|
||
|
initial cfg_mode = 1'b0;
|
||
|
always @(posedge i_clk)
|
||
|
if ((i_reset)||(!OPT_CFG))
|
||
|
cfg_mode <= 1'b0;
|
||
|
else if ((i_cfg_stb)&&(!o_wb_stall)&&(i_wb_we))
|
||
|
cfg_mode <= i_wb_data[CFG_MODE];
|
||
|
|
||
|
initial cfg_cs = 1'b0;
|
||
|
always @(posedge i_clk)
|
||
|
if ((i_reset)||(!OPT_CFG))
|
||
|
cfg_cs <= 1'b0;
|
||
|
else if ((i_cfg_stb)&&(!o_wb_stall)&&(i_wb_we))
|
||
|
cfg_cs <= (!i_wb_data[USER_CS_n])&&(i_wb_data[CFG_MODE]);
|
||
|
|
||
|
initial cfg_speed = 1'b0;
|
||
|
initial cfg_dir = 1'b0;
|
||
|
always @(posedge i_clk)
|
||
|
if (!OPT_CFG)
|
||
|
begin
|
||
|
cfg_speed <= 1'b0;
|
||
|
cfg_dir <= 1'b0;
|
||
|
end else if ((i_cfg_stb)&&(!o_wb_stall)&&(i_wb_we))
|
||
|
begin
|
||
|
cfg_speed <= i_wb_data[QSPEED_BIT];
|
||
|
cfg_dir <= i_wb_data[DIR_BIT];
|
||
|
end
|
||
|
|
||
|
/*
|
||
|
reg r_last_cfg;
|
||
|
|
||
|
initial r_last_cfg = 1'b0;
|
||
|
always @(posedge i_clk)
|
||
|
r_last_cfg <= cfg_mode;
|
||
|
assign o_dbg_trigger = (!cfg_mode)&&(r_last_cfg);
|
||
|
assign o_debug = { o_dbg_trigger,
|
||
|
i_wb_cyc, i_cfg_stb, i_wb_stb, o_wb_ack, o_wb_stall,//6
|
||
|
o_qspi_cs_n, o_qspi_sck, o_qspi_dat, o_qspi_mod,// 8
|
||
|
i_qspi_dat, cfg_mode, cfg_cs, cfg_speed, cfg_dir,// 8
|
||
|
actual_sck, i_wb_we,
|
||
|
(((i_wb_stb)||(i_cfg_stb))
|
||
|
&&(i_wb_we)&&(!o_wb_stall)&&(!o_wb_ack))
|
||
|
? i_wb_data[7:0] : o_wb_data[7:0]
|
||
|
};
|
||
|
*/
|
||
|
|
||
|
// verilator lint_off UNUSED
|
||
|
wire [19:0] unused;
|
||
|
assign unused = { i_wb_data[31:12] };
|
||
|
// verilator lint_on UNUSED
|
||
|
|
||
|
`ifdef FORMAL
|
||
|
localparam F_MEMDONE = NDUMMY+6+8+(OPT_ADDR32 ? 2:0)+(OPT_ODDR ? 0:1);
|
||
|
localparam F_MEMACK = F_MEMDONE + RDDELAY;
|
||
|
localparam F_PIPEDONE = 8;
|
||
|
localparam F_PIPEACK = F_PIPEDONE + RDDELAY;
|
||
|
localparam F_CFGLSDONE = 8+(OPT_ODDR ? 0:1);
|
||
|
localparam F_CFGLSACK = F_CFGLSDONE + RDDELAY;
|
||
|
localparam F_CFGHSDONE = 2+(OPT_ODDR ? 0:1);
|
||
|
localparam F_CFGHSACK = RDDELAY+F_CFGHSDONE;
|
||
|
localparam F_ACKCOUNT = (15+NDUMMY+RDDELAY)
|
||
|
*(OPT_ODDR ? 1 : (OPT_CLKDIV+1));
|
||
|
genvar k;
|
||
|
|
||
|
wire [(F_LGDEPTH-1):0] f_nreqs, f_nacks,
|
||
|
f_outstanding;
|
||
|
reg [(AW-1):0] f_req_addr;
|
||
|
reg [(OPT_ADDR32 ? 29:21):0] fv_addr;
|
||
|
reg [31:0] fv_data;
|
||
|
reg [F_MEMACK:0] f_memread;
|
||
|
reg [32:0] f_past_data;
|
||
|
reg [F_PIPEACK:0] f_piperead;
|
||
|
reg [F_CFGHSACK:0] f_cfghsread;
|
||
|
reg [F_CFGHSACK:0] f_cfghswrite;
|
||
|
reg [F_CFGLSACK:0] f_cfglswrite;
|
||
|
|
||
|
//
|
||
|
//
|
||
|
// Generic setup
|
||
|
//
|
||
|
//
|
||
|
`ifdef QFLEXPRESS
|
||
|
`define ASSUME assume
|
||
|
`else
|
||
|
`define ASSUME assert
|
||
|
`endif
|
||
|
|
||
|
// Keep track of a flag telling us whether or not $past()
|
||
|
// will return valid results
|
||
|
initial f_past_valid = 1'b0;
|
||
|
always @(posedge i_clk)
|
||
|
f_past_valid = 1'b1;
|
||
|
|
||
|
always @(*)
|
||
|
if (!f_past_valid)
|
||
|
`ASSUME(i_reset);
|
||
|
|
||
|
/////////////////////////////////////////////////
|
||
|
//
|
||
|
//
|
||
|
// Assumptions about our inputs
|
||
|
//
|
||
|
//
|
||
|
/////////////////////////////////////////////////
|
||
|
|
||
|
always @(*)
|
||
|
`ASSUME((!i_wb_stb)||(!i_cfg_stb));
|
||
|
|
||
|
always @(posedge i_clk)
|
||
|
if ((f_past_valid)&&(!$past(i_reset))
|
||
|
&&($past(i_wb_stb))&&($past(o_wb_stall)))
|
||
|
`ASSUME(i_wb_stb);
|
||
|
|
||
|
always @(posedge i_clk)
|
||
|
if ((f_past_valid)&&(!$past(i_reset))
|
||
|
&&($past(i_cfg_stb))&&($past(o_wb_stall)))
|
||
|
`ASSUME(i_cfg_stb);
|
||
|
|
||
|
fwb_slave #(.AW(AW), .DW(DW),.F_LGDEPTH(F_LGDEPTH),
|
||
|
.F_MAX_STALL(((OPT_CLKDIV<3) ? (F_ACKCOUNT+1):0)
|
||
|
+(OPT_ADDR32 ? 2:0)),
|
||
|
.F_MAX_ACK_DELAY(((OPT_CLKDIV<3) ? F_ACKCOUNT : 0)
|
||
|
+(OPT_ADDR32 ? 2:0)),
|
||
|
.F_OPT_RMW_BUS_OPTION(0),
|
||
|
.F_OPT_CLK2FFLOGIC(1'b0),
|
||
|
.F_OPT_DISCONTINUOUS(1))
|
||
|
f_wbm(i_clk, i_reset,
|
||
|
i_wb_cyc, (i_wb_stb)||(i_cfg_stb), i_wb_we, i_wb_addr,
|
||
|
i_wb_data, 4'hf,
|
||
|
o_wb_ack, o_wb_stall, o_wb_data, 1'b0,
|
||
|
f_nreqs, f_nacks, f_outstanding);
|
||
|
|
||
|
always @(*)
|
||
|
assert(f_outstanding <= 2 + f_extra);
|
||
|
|
||
|
always @(posedge i_clk)
|
||
|
if ((f_past_valid)&&(!$past(i_wb_stb))||($past(o_wb_stall)))
|
||
|
assert(f_outstanding <= 1 + f_extra);
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// Assumptions about i_qspi_dat
|
||
|
//
|
||
|
// 1. On output, i_qspi_dat equals the input
|
||
|
// 2. Otherwise when implementing multi-cycle clocks, i_qspi_dat only
|
||
|
// changes on a negative edge
|
||
|
//
|
||
|
(* anyseq *) reg [3:0] dly_idat;
|
||
|
always @(posedge i_clk)
|
||
|
if (o_qspi_mod == NORMAL_SPI)
|
||
|
begin
|
||
|
assume(dly_idat[3:2] == 2'b11);
|
||
|
assume(dly_idat[0] == o_qspi_dat[0]);
|
||
|
if ((!OPT_ODDR)&&((o_qspi_sck)||(!$past(o_qspi_sck))))
|
||
|
assume($stable(dly_idat[1]));
|
||
|
end else if (o_qspi_mod == QUAD_WRITE)
|
||
|
assume(dly_idat == o_qspi_dat);
|
||
|
else if ((!OPT_ODDR)&&((o_qspi_sck)||(!$past(o_qspi_sck))))
|
||
|
assume($stable(dly_idat));
|
||
|
|
||
|
generate if (RDDELAY > 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<F_MEMACK-1; k=k+1)
|
||
|
begin : ONEHOT_MEMREAD
|
||
|
always @(*)
|
||
|
if (f_memread[k])
|
||
|
assert((f_memread ^ (1<<k)) == 0);
|
||
|
end endgenerate
|
||
|
|
||
|
|
||
|
generate if (RDDELAY == 0)
|
||
|
begin
|
||
|
|
||
|
initial f_piperead = 0;
|
||
|
always @(posedge i_clk)
|
||
|
if ((i_reset)||(!OPT_PIPE))
|
||
|
f_piperead <= 0;
|
||
|
else if (ckstb) begin
|
||
|
f_piperead <= { f_piperead[F_PIPEACK-1:0],1'b0};
|
||
|
f_piperead[0] <= (bus_request)&&(!o_qspi_cs_n);
|
||
|
end else if (!OPT_ODDR)
|
||
|
f_piperead[F_PIPEACK] <= 1'b0;
|
||
|
|
||
|
end else begin
|
||
|
|
||
|
initial f_piperead = 0;
|
||
|
always @(posedge i_clk)
|
||
|
if ((i_reset)||(!OPT_PIPE))
|
||
|
f_piperead <= 0;
|
||
|
else if (ckstb) begin
|
||
|
f_piperead <= { f_piperead[F_PIPEACK-1:0],1'b0};
|
||
|
f_piperead[0] <= (bus_request)&&(!o_qspi_cs_n);
|
||
|
end else if (!OPT_ODDR)
|
||
|
f_piperead[F_PIPEACK:F_PIPEDONE] <= { f_piperead[F_PIPEACK-1:F_PIPEDONE], 1'b0 };
|
||
|
|
||
|
end endgenerate
|
||
|
|
||
|
always @(posedge i_clk)
|
||
|
if (OPT_ODDR)
|
||
|
begin
|
||
|
if (f_piperead[F_PIPEACK] && 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_piperead[F_PIPEACK])
|
||
|
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));
|
||
|
end else if ((|f_piperead)&&(!f_piperead[RDDELAY]))
|
||
|
assert(!o_wb_ack);
|
||
|
end else if (f_piperead[F_PIPEACK] && OPT_ENDIANSWAP)
|
||
|
begin
|
||
|
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]));
|
||
|
end else if (f_piperead[F_PIPEACK]) // 25
|
||
|
assert((!o_wb_ack)||(o_wb_data == f_past_data[31:0]));
|
||
|
else if (|f_piperead)
|
||
|
begin
|
||
|
if ((!OPT_PIPE)||(!ckstb))
|
||
|
assert(o_wb_stall);
|
||
|
else if (!f_piperead[F_PIPEDONE-1])
|
||
|
assert(o_wb_stall);
|
||
|
if (!f_memread[F_MEMACK])
|
||
|
assert(!o_wb_ack);
|
||
|
end
|
||
|
|
||
|
always @(posedge i_clk)
|
||
|
if (f_piperead[F_PIPEDONE])
|
||
|
assert(clk_ctr == 0 || clk_ctr == F_PIPEDONE);
|
||
|
else if (|f_piperead[F_PIPEDONE-1:0])
|
||
|
assert(f_piperead[F_PIPEDONE-clk_ctr]);
|
||
|
|
||
|
always @(*)
|
||
|
if (i_cfg_stb && !o_wb_stall)
|
||
|
begin
|
||
|
assert(|{ cfg_noop, cfg_hs_write, cfg_hs_read, cfg_ls_write });
|
||
|
|
||
|
if (cfg_noop)
|
||
|
assert({ cfg_hs_write, cfg_hs_read, cfg_ls_write }==0);
|
||
|
else if (cfg_hs_write)
|
||
|
assert({ cfg_hs_read, cfg_ls_write }==0);
|
||
|
else if (cfg_hs_read)
|
||
|
assert({ cfg_ls_write }==0);
|
||
|
end
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// Lowspeed config write
|
||
|
//
|
||
|
initial f_cfglswrite = 0;
|
||
|
always @(posedge i_clk)
|
||
|
if (i_reset)
|
||
|
f_cfglswrite <= 0;
|
||
|
else begin
|
||
|
if (ckstb)
|
||
|
f_cfglswrite <= { f_cfglswrite[F_CFGLSACK-1:0], 1'b0 };
|
||
|
else if (!OPT_ODDR)
|
||
|
f_cfglswrite[F_CFGLSACK:F_CFGLSDONE]
|
||
|
<= { f_cfglswrite[F_CFGLSACK:F_CFGLSDONE],1'b0 };
|
||
|
if (cfg_ls_write)
|
||
|
f_cfglswrite[0] <= 1'b1;
|
||
|
end
|
||
|
|
||
|
always @(*)
|
||
|
if (OPT_ODDR)
|
||
|
begin
|
||
|
if (|f_cfglswrite[7:0])
|
||
|
assert(o_qspi_sck);
|
||
|
else if (|f_cfglswrite)
|
||
|
assert(!o_qspi_sck);
|
||
|
end
|
||
|
|
||
|
always @(posedge i_clk)
|
||
|
if (|f_cfglswrite[7:0])
|
||
|
begin
|
||
|
assert(!dly_ack);
|
||
|
if (f_cfglswrite[F_CFGLSDONE-8])
|
||
|
begin
|
||
|
assert(o_qspi_dat[0] == fv_data[7]);
|
||
|
assert(clk_ctr == 8);
|
||
|
end
|
||
|
if (f_cfglswrite[F_CFGLSDONE-7])
|
||
|
begin
|
||
|
assert(o_qspi_dat[0] == fv_data[6]);
|
||
|
assert(clk_ctr == 7);
|
||
|
end
|
||
|
if (f_cfglswrite[F_CFGLSDONE-6])
|
||
|
begin
|
||
|
assert(o_qspi_dat[0] == fv_data[5]);
|
||
|
assert(clk_ctr == 6);
|
||
|
end
|
||
|
if (f_cfglswrite[F_CFGLSDONE-5])
|
||
|
begin
|
||
|
assert(o_qspi_dat[0] == fv_data[4]);
|
||
|
assert(clk_ctr == 5);
|
||
|
end
|
||
|
if (f_cfglswrite[F_CFGLSDONE-4])
|
||
|
begin
|
||
|
assert(o_qspi_dat[0] == fv_data[3]);
|
||
|
assert(clk_ctr == 4);
|
||
|
end
|
||
|
if (f_cfglswrite[F_CFGLSDONE-3])
|
||
|
begin
|
||
|
assert(o_qspi_dat[0] == fv_data[2]);
|
||
|
assert(clk_ctr == 3);
|
||
|
end
|
||
|
if (f_cfglswrite[F_CFGLSDONE-2])
|
||
|
begin
|
||
|
assert(o_qspi_dat[0] == fv_data[1]);
|
||
|
assert(clk_ctr == 2);
|
||
|
end
|
||
|
if (f_cfglswrite[F_CFGLSDONE-1])
|
||
|
begin
|
||
|
assert(o_qspi_dat[0] == fv_data[0]);
|
||
|
assert(clk_ctr == 1);
|
||
|
end
|
||
|
end
|
||
|
|
||
|
always @(posedge i_clk)
|
||
|
if (|f_cfglswrite)
|
||
|
begin
|
||
|
assert(!o_qspi_cs_n);
|
||
|
assert(o_qspi_mod == NORMAL_SPI);
|
||
|
end
|
||
|
|
||
|
always @(posedge i_clk)
|
||
|
if (|f_cfglswrite[F_CFGLSACK:F_CFGLSDONE])
|
||
|
assert(o_qspi_sck == !OPT_ODDR);
|
||
|
|
||
|
always @(posedge i_clk)
|
||
|
if ((OPT_ODDR)&&(f_cfglswrite[F_CFGLSACK]))
|
||
|
begin
|
||
|
assert((o_wb_ack)||(!$past(pre_ack))||(!$past(i_wb_cyc)));
|
||
|
assert(o_wb_data[7] == $past(i_qspi_dat[1],8));
|
||
|
assert(o_wb_data[6] == $past(i_qspi_dat[1],7));
|
||
|
assert(o_wb_data[5] == $past(i_qspi_dat[1],6));
|
||
|
assert(o_wb_data[4] == $past(i_qspi_dat[1],5));
|
||
|
assert(o_wb_data[3] == $past(i_qspi_dat[1],4));
|
||
|
assert(o_wb_data[2] == $past(i_qspi_dat[1],3));
|
||
|
assert(o_wb_data[1] == $past(i_qspi_dat[1],2));
|
||
|
assert(o_wb_data[0] == $past(i_qspi_dat[1],1));
|
||
|
assert(!o_wb_stall);
|
||
|
end else if (f_cfglswrite[F_CFGLSACK])
|
||
|
assert(o_wb_data[7:0] == f_past_data[7:0]);
|
||
|
else if (|f_cfglswrite[F_CFGLSACK-1:0])
|
||
|
assert(o_wb_stall);
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// High speed config write
|
||
|
//
|
||
|
generate if (RDDELAY == 0)
|
||
|
begin
|
||
|
initial f_cfghswrite = 0;
|
||
|
always @(posedge i_clk)
|
||
|
if (i_reset)
|
||
|
f_cfghswrite <= 0;
|
||
|
else begin
|
||
|
if (ckstb)
|
||
|
f_cfghswrite <= { f_cfghswrite[F_CFGHSACK-1:0], 1'b0 };
|
||
|
else if (!OPT_ODDR)
|
||
|
f_cfghswrite[F_CFGHSACK] <= 1'b0;
|
||
|
if (cfg_hs_write)
|
||
|
f_cfghswrite[0] <= 1'b1;
|
||
|
end
|
||
|
|
||
|
end else begin
|
||
|
|
||
|
initial f_cfghswrite = 0;
|
||
|
always @(posedge i_clk)
|
||
|
if (i_reset)
|
||
|
f_cfghswrite <= 0;
|
||
|
else begin
|
||
|
if (ckstb)
|
||
|
f_cfghswrite <= { f_cfghswrite[F_CFGHSACK-1:0], 1'b0 };
|
||
|
else if (!OPT_ODDR)
|
||
|
f_cfghswrite[F_CFGHSACK:F_CFGHSDONE]
|
||
|
<= { f_cfghswrite[F_CFGHSACK:F_CFGHSDONE], 1'b0 };
|
||
|
if (cfg_hs_write)
|
||
|
f_cfghswrite[0] <= 1'b1;
|
||
|
end
|
||
|
end endgenerate
|
||
|
|
||
|
always @(*)
|
||
|
if (OPT_ODDR)
|
||
|
begin
|
||
|
if (|f_cfghswrite[F_CFGHSDONE-1:0])
|
||
|
assert(o_qspi_sck);
|
||
|
else if (|f_cfghswrite)
|
||
|
assert(!o_qspi_sck);
|
||
|
end
|
||
|
|
||
|
always @(posedge i_clk)
|
||
|
if ((OPT_ODDR) && (f_cfghswrite[0]))
|
||
|
assert(o_qspi_dat == $past(i_wb_data[7:4]));
|
||
|
else if ((!OPT_ODDR) && (f_cfghswrite[1]))
|
||
|
assert(o_qspi_dat == fv_data[7:4]);
|
||
|
|
||
|
always @(posedge i_clk)
|
||
|
if ((OPT_ODDR)&&f_cfghswrite[1])
|
||
|
assert(o_qspi_dat == $past(i_wb_data[3:0],2));
|
||
|
else if ((!OPT_ODDR) && (f_cfghswrite[2]))
|
||
|
assert(o_qspi_dat == fv_data[3:0]);
|
||
|
|
||
|
always @(posedge i_clk)
|
||
|
if (OPT_ODDR)
|
||
|
begin
|
||
|
if (|f_cfghswrite[F_CFGHSDONE-1:0])
|
||
|
assert(o_qspi_sck);
|
||
|
else if (|f_cfghswrite)
|
||
|
assert(!o_qspi_sck);
|
||
|
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_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
|