/****************************************************************************** | |
Winbond Electronics Corporation | |
Verilog Model for W25Q32JVxxIM Serial Flash Memory | |
V1.2-beta | |
Copyright (c) 2001-2018 Winbond Electronics Corporation | |
All Rights Reserved. | |
Versions: | |
01/30/2016 V1.0-beta Initial Version | |
02/07/2018 V1.1 Added /RESET pin function | |
02/12/2018 V1.2 Corrected number of dummy clocks for EBh in QPI mode, added ADDRESS_MASK in 02h | |
******************************************************************************/ | |
`timescale 1ns / 1ns | |
module W25Q32JVxxIM (CSn, CLK, DIO, DO, WPn, HOLDn, RESETn); | |
input CSn, CLK, RESETn; | |
inout DIO; | |
inout WPn; | |
inout HOLDn; | |
inout DO; | |
parameter NUM_PAGES = 16384; | |
parameter NUM_SEC_PAGES = 3; | |
parameter PAGESIZE = 256; | |
parameter SECTORSIZE = 4096; | |
parameter HALFBLOCKSIZE = 32768; | |
parameter BLOCKSIZE = 65536; | |
parameter NUM_BLOCKS = NUM_PAGES * PAGESIZE / BLOCKSIZE; | |
parameter NUM_LOCKBITS = NUM_BLOCKS - 2 + 32; | |
parameter MANUFACTURER = 8'hEF; | |
parameter DEVICE_ID = 8'h15; | |
parameter JEDEC_ID_HI = 8'h70; | |
parameter JEDEC_ID_LO = 8'h16; | |
parameter UNIQUE_ID = 64'h5501020304050607; | |
parameter ADDRESS_MASK = (NUM_PAGES * PAGESIZE) - 1; // Note that NUM_PAGES must be a power of 2 for this simulation to work properly. | |
`define MEM_FILENAME "/home/siva/Documents/caravel_user_project_extended/verilog/user_behav_models/MEM.txt" // Memory contents file(s) | |
`define SECSI_FILENAME "SECSI.TXT" | |
`define SFDP_FILENAME "SFDP.TXT" | |
`define SREG_FILENAME "SREG.TXT" | |
// The following registers define the main memory spaces of the device. | |
// | |
// | |
reg [7:0] memory [0:(NUM_PAGES * PAGESIZE) - 1]; // Main Memory Array | |
reg [7:0] page_latch [0:PAGESIZE-1]; // Page Latch for Program Sector commands. | |
reg [7:0] secsi[0:(NUM_SEC_PAGES * (SECTORSIZE/PAGESIZE) * PAGESIZE) - 1]; // Security Sector Array | |
reg [7:0] sfdp[0:PAGESIZE]; // Serial Flash Discoverable Parameter | |
reg lock_array[0:NUM_LOCKBITS-1]; // 510 blocks + 32 sectors | |
reg [23:0] status_reg; // Status Register | |
reg [23:0] status_reg_shadow; // Status Register Shadow Register | |
reg status_reg_otp [23:0]; // Status Register OTP Bits | |
reg [23:0] byte_address; // Page address used for reading pages. | |
reg [23:0] prog_byte_address; // Page address used for writing pages. | |
reg [7:0] mode_reg; // Mode Register | |
reg [7:0] wrap_reg; // burst-wrap register | |
reg [7:0] read_param_reg; // Read parameters register | |
reg [7:0] read_param_reg_shadow; // Shadow register | |
reg flag_prog_page; // Flag used to start the program process for sram 0 | |
reg flag_prog_secsi_page; // Flag used to program a SECSI page | |
reg flag_erase_sector; // Flag used to erase a sector (4KB) | |
reg flag_erase_secsi_sector; | |
reg flag_erase_half_block; // Flag to erase half a block (32KB) | |
reg flag_erase_block; // Flag used to erase a block (64KB) | |
reg flag_erase_bulk; // Flag used to erase the chip | |
reg flag_power_up_exec; | |
reg flag_power_down; | |
reg flag_power_up_sig_read; | |
reg flag_write_status_reg; | |
reg flag_suspend; // Flag used to start an erase suspend process. | |
reg flag_resume; // Flag used to resume from and erase suspend | |
reg flag_suspend_enabled; // Flag used to stop the erase process. | |
reg flag_slow_read_reg; | |
wire flag_slow_read = flag_slow_read_reg; | |
reg flag_volatile_sr_write; // flag for volatile status register write. | |
reg flag_read_op_reg; | |
wire #1 flag_read_op = flag_read_op_reg; // Delay avoids race condition | |
reg flag_qpi_mode; // flag used for QPI Mode. | |
reg flag_enable_reset; // flag used to enable the software reset command. | |
reg flag_reset; // flag to execute the chip reset routine | |
reg flag_reset_condition; // Flag to stop delay loops | |
reg flag_set_read_param; // Flag to set read parameters | |
reg timing_error; // Register for identifying timing errors when they occur | |
reg [7:0] in_byte; // These two variables are used for debug purposes. | |
reg [7:0] out_byte; | |
// Logic output for WPn pin. | |
// | |
reg WPn_Reg, WPn_Output_Enable_reg; | |
wire WPn_Output_Enable = WPn_Output_Enable_reg; | |
// Logic output for HOLDn pin. | |
// | |
reg HOLDn_Reg, HOLDn_Output_Enable; | |
// Logic output for DO pin. | |
// | |
reg DO_Reg, DO_Output_Enable, temp_DO_Output_Enable; | |
// Logic output for DIO pin. | |
// | |
reg DIO_Reg, DIO_Output_Enable_reg, temp_DIO_Output_Enable_reg; | |
wire DIO_Output_Enable = DIO_Output_Enable_reg; | |
// Flag to signal that HOLDn goes active while CSn is low | |
reg HOLDn_Active; | |
// The following are working variables for the simulation program | |
// | |
reg [7:0] cmd_byte; | |
reg [7:0] null_reg; | |
reg [7:0] temp; | |
integer x; | |
integer fileno; | |
reg [15:0] file_sector; | |
reg [15:0] file_length; | |
// The following macro's define the supported commands in this | |
// Simulation | |
// | |
`define CMD_WRITE_DISABLE 8'h04 | |
`define CMD_WRITE_ENABLE 8'h06 | |
`define CMD_READ_STATUS 8'h05 | |
`define CMD_WRITE_STATUS 8'h01 | |
`define CMD_READ_STATUS2 8'h35 | |
`define CMD_WRITE_STATUS2 8'h31 | |
`define CMD_READ_STATUS3 8'h15 | |
`define CMD_WRITE_STATUS3 8'h11 | |
`define CMD_READ_DATA 8'h03 | |
`define CMD_READ_DATA_FAST 8'h0B | |
`define CMD_READ_DATA_FAST_WRAP 8'h0C | |
`define CMD_READ_DATA_FAST_DTR 8'h0D | |
`define CMD_READ_DATA_FAST_DTR_WRAP 8'h0E | |
`define CMD_READ_DATA_FAST_DUAL 8'h3B | |
`define CMD_READ_DATA_FAST_DUAL_IO 8'hBB | |
`define CMD_READ_DATA_FAST_DUAL_IO_DTR 8'hBD | |
`define CMD_READ_DATA_FAST_QUAD 8'h6B | |
`define CMD_READ_DATA_FAST_QUAD_IO 8'hEB | |
`define CMD_READ_DATA_FAST_QUAD_IO_DTR 8'hED | |
`define CMD_PAGE_PROGRAM 8'h02 | |
`define CMD_PAGE_PROGRAM_QUAD 8'h32 | |
`define CMD_BLOCK_ERASE 8'hD8 | |
`define CMD_HALF_BLOCK_ERASE 8'h52 | |
`define CMD_SECTOR_ERASE 8'h20 | |
`define CMD_BULK_ERASE 8'hC7 | |
`define CMD_BULK_ERASE2 8'h60 | |
`define CMD_DEEP_POWERDOWN 8'hB9 | |
`define CMD_READ_SIGNATURE 8'hAB | |
`define CMD_READ_ID 8'h90 | |
`define CMD_READ_ID_DUAL 8'h92 | |
`define CMD_READ_ID_QUAD 8'h94 | |
`define CMD_READ_JEDEC_ID 8'h9F | |
`define CMD_READ_UNIQUE_ID 8'h4B | |
`define CMD_SUSPEND 8'h75 | |
`define CMD_RESUME 8'h7A | |
`define CMD_SET_BURST_WRAP 8'h77 | |
`define CMD_MODE_RESET 8'hFF | |
`define CMD_DISABLE_QPI 8'hFF | |
`define CMD_ENABLE_QPI 8'h38 | |
`define CMD_ENABLE_RESET 8'h66 | |
`define CMD_CHIP_RESET 8'h99 | |
`define CMD_SET_READ_PARAM 8'hC0 | |
`define CMD_SREG_PROGRAM 8'h42 | |
`define CMD_SREG_ERASE 8'h44 | |
`define CMD_SREG_READ 8'h48 | |
`define CMD_WRITE_ENABLE_VSR 8'h50 | |
`define CMD_READ_SFDP 8'h5A | |
`define CMD_INDIVIDUAL_LOCK 8'h36 | |
`define CMD_INDIVIDUAL_UNLOCK 8'h39 | |
`define CMD_READ_BLOCK_LOCK 8'h3D | |
`define CMD_GLOBAL_BLOCK_LOCK 8'h7E | |
`define CMD_GLOBAL_BLOCK_UNLOCK 8'h98 | |
// Status register definitions | |
// | |
`define STATUS_HLD_RST 24'h800000 | |
`define STATUS_DRV1 24'h400000 | |
`define STATUS_DRV0 24'h200000 | |
`define STATUS_WPS 24'h040000 | |
`define STATUS_SUS 24'h008000 | |
`define STATUS_CMP 24'h004000 | |
`define STATUS_LB3 24'h002000 | |
`define STATUS_LB2 24'h001000 | |
`define STATUS_LB1 24'h000800 | |
`define STATUS_QE 24'h000200 | |
`define STATUS_SRL 24'h000100 | |
`define STATUS_SRP 24'h000080 | |
`define STATUS_SEC 24'h000040 | |
`define STATUS_TB 24'h000020 | |
`define STATUS_BP2 24'h000010 | |
`define STATUS_BP1 24'h000008 | |
`define STATUS_BP0 24'h000004 | |
`define STATUS_WEL 24'h000002 | |
`define STATUS_WIP 24'h000001 | |
`define HLD_RST 23 | |
`define DRV1 22 | |
`define DRV0 21 | |
`define WPS 18 | |
`define SUS 15 | |
`define CMP 14 | |
`define LB3 13 | |
`define LB2 12 | |
`define LB1 11 | |
`define QE 9 | |
`define SRL 8 | |
`define SRP 7 | |
`define SEC 6 | |
`define TB 5 | |
`define BP2 4 | |
`define BP1 3 | |
`define BP0 2 | |
`define WEL 1 | |
`define WIP 0 | |
// Required for specify block | |
wire flag_quad_mode = status_reg[`QE]; | |
wire flag_quad_mode_cs = !flag_quad_mode & !CSn; | |
specify | |
specparam tReset_Suspend_Max = 1000; // Reset / Suspend Granularity. Controls maximum amount of time for model to recognize a Suspend command. | |
// Also controls amount of time it takes to recognize a reset command | |
// CSn timing checks | |
specparam tSLCH = 5; | |
$setup(negedge CSn, posedge CLK, tSLCH, timing_error); | |
specparam tCHSL = 5; | |
$setup(posedge CLK, negedge CSn, tCHSL, timing_error); | |
specparam tSHSL_R = 10; | |
specparam tSHSL_W = 50; | |
$width(posedge CSn &&& flag_read_op, tSHSL_R, 0, timing_error); | |
$width(posedge CSn &&& (~flag_read_op), tSHSL_W, 0, timing_error); | |
specparam tCHSH = 5; | |
$setup(posedge CLK, posedge CSn, tCHSH, timing_error); | |
specparam tSHCH = 5; | |
$setup(posedge CSn, posedge CLK, tSHCH, timing_error); | |
// CLK timing checks | |
specparam tCYC = 10; // Minimum CLK period for all instructions but READ_PAGE | |
specparam tCLH = 4; // Minimum CLK high time for all instructions but READ_PAGE | |
specparam tCLL = 4; // Minimum CLK low time for all instructions but READ_PAGE | |
$period(posedge CLK &&& (~flag_slow_read), tCYC, timing_error); | |
$width(posedge CLK &&& (~flag_slow_read), tCLH, 0, timing_error); | |
$width(negedge CLK &&& (~flag_slow_read), tCLL, 0, timing_error); | |
specparam tCYCR = 20; // Minimum CLK period READ_PAGE | |
specparam tCRLH = 8; // Minimum CLK high time READ_PAGE | |
specparam tCRLL = 8; // Minimum CLK low time READ_PAGE | |
$period(posedge CLK &&& (flag_slow_read), tCYCR, timing_error); | |
$width(posedge CLK &&& (flag_slow_read), tCRLH, 0, timing_error); | |
$width(negedge CLK &&& (flag_slow_read), tCRLL, 0, timing_error); | |
// DIO timing checks | |
specparam tDVCH = 2; // DIO Data setup time | |
specparam tCHDX = 5; // DIO Data hold time | |
// Make sure to turn off DIO input timing checks when outputing data in dual / quad output mode. | |
$setup(DIO, posedge CLK &&& (~DIO_Output_Enable), tDVCH, timing_error); | |
$hold(posedge CLK, DIO &&& (~DIO_Output_Enable), tCHDX, timing_error); | |
// WPn timing checks | |
specparam tWHSL = 20; | |
specparam tSHWL = 100; | |
// Make sure to turn off WPn timing checks when outputing data in quad output mode | |
// As well, there is no way to detect setup time of CSn and WPn relative to the Write Status Register | |
// command. Disable timing checks | |
//$setup(posedge WPn, negedge CSn &&& (~flag_quad_mode), tWHSL, timing_error); | |
//$setup(posedge CSn, negedge WPn &&& (~flag_quad_mode), tSHWL, timing_error); | |
// HOLDn timing checks | |
specparam tHLQZ = 12; | |
specparam tHHQX = 7; | |
specparam tSUS = 20000; | |
specparam tCHHL = 5; | |
$setup(posedge CLK, negedge HOLDn &&& (flag_quad_mode_cs),tCHHL, timing_error); | |
specparam tHLCH = 5; // Need to verify from datasheet | |
$hold(posedge CLK, negedge HOLDn &&& (flag_quad_mode_cs),tHLCH, timing_error); | |
specparam tCHHH = 5; | |
$setup(posedge CLK, posedge HOLDn &&& (flag_quad_mode_cs),tCHHH, timing_error); | |
specparam tHHCH = 5; | |
$hold(posedge CLK, posedge HOLDn &&& (flag_quad_mode_cs), tHHCH, timing_error); | |
endspecify | |
parameter tCLQV = 7; // Time to DO output valid. | |
parameter tSHQZ = 7; // Data output disable time. | |
parameter tW = 1000; // Write Status Register Write Time | |
parameter tRESET = 1000; // Reset timing | |
parameter tRES1 = 30000; // Release from power down time 1 | |
parameter tRES2 = 30000; // Release from power down time 2 | |
parameter tDP = 3000; // Time for device to enter deep power down. | |
parameter tPP = 700000; // Page Program Time | |
parameter tSE = 30000000; // Sector Erase Time. | |
parameter tBE1 = 120000000; // Block Erase Time. 32KB | |
parameter tBE2 = 150000000; // Block Erase Time. 64KB | |
parameter tCE_40 = 250000000; // Chip Erase Time. This constant should be repeated 40 times. | |
/****************************************************************************** | |
The following code is the initialization code run at the beginning of the | |
simulation. | |
******************************************************************************/ | |
initial | |
begin :initialization | |
// Erase memory array to FFh state. | |
// for(x = 0; x < (NUM_PAGES * PAGESIZE); x=x+1) | |
// memory[x] = 8'hff; | |
// | |
// dump_mem(); | |
$readmemh(`MEM_FILENAME,memory); | |
$readmemh(`SECSI_FILENAME,secsi); | |
$readmemh(`SFDP_FILENAME,sfdp); | |
$readmemh(`SREG_FILENAME,status_reg_otp); | |
chip_reset(); | |
end | |
/****************************************************************************** | |
The following continuous assignment statement assigns the DIO_Reg register to the | |
DIO inout wire and so on for the rest of the quad outputs. | |
******************************************************************************/ | |
assign DIO = DIO_Output_Enable_reg ? DIO_Reg : 1'bz; | |
assign DO = DO_Output_Enable ? DO_Reg : 1'bz; | |
assign WPn = WPn_Output_Enable_reg ? WPn_Reg : 1'bz; | |
assign HOLDn = HOLDn_Output_Enable ? HOLDn_Reg : 1'bz; | |
/****************************************************************************** | |
The following routine occurs when CSn goes low. | |
The following routine reads the opcode in from the SPI port and starts command | |
execution. All commands execute the following flow: | |
Command Dispatch ==> Command Protocol Handler ==> Command State Machine (if necessary) | |
Whenever CSn goes high, the Command Dispatch and Command Protocol Handler functions | |
stop execution. The Individual Command State Machines continue to run until | |
completion, regardless of the state of CSn. | |
******************************************************************************/ | |
always @(negedge CSn) // When CSn goes low, device becomes active | |
begin :read_opcode | |
flag_read_op_reg = 1'b1; // Assume a read command first. If write, update variable in case statement. | |
mode_reg = mode_reg & 8'hf0; | |
if((mode_reg & 8'h30) != 8'h20) // If we are in mode 0x20, skip inputing command byte, execute last command | |
input_byte(cmd_byte); // Read Opcode from SPI Port | |
if(!is_qpi(cmd_byte)) | |
begin | |
$display("WARNING: Non-QPI command was executed in QPI mode"); | |
$stop; | |
end | |
if(cmd_byte != `CMD_CHIP_RESET) | |
flag_enable_reset = 0; // Ensure that ENABLE_RESET immediately precedes CHIP_RESET | |
$display("\nCommand = %h", cmd_byte); | |
case (cmd_byte) // Now dispatch the correct function | |
// Mode and Reset commands. | |
`CMD_SET_READ_PARAM : | |
begin | |
if(!status_reg[`WIP] && !flag_power_down && flag_qpi_mode) | |
begin | |
input_byte(read_param_reg_shadow); | |
flag_set_read_param = 1; | |
get_posclk_holdn; | |
flag_set_read_param = 0; | |
end | |
end | |
`CMD_ENABLE_QPI : | |
begin | |
if(!status_reg[`WIP] && !flag_power_down) | |
begin | |
if(status_reg[`QE] == 1) | |
flag_qpi_mode = 1; | |
end | |
end | |
`CMD_MODE_RESET : | |
begin | |
if(!flag_power_down) | |
flag_qpi_mode = 0; | |
end | |
`CMD_ENABLE_RESET : // Reset can happen at any time. | |
begin | |
flag_enable_reset = 1; | |
end | |
`CMD_CHIP_RESET : | |
begin | |
if(flag_enable_reset == 1) | |
begin | |
flag_reset = 1; | |
@(posedge CLK); | |
flag_reset = 0; | |
end | |
end | |
// Power, ID and Extended Address commands | |
`CMD_DEEP_POWERDOWN : | |
begin | |
if(!status_reg[`WIP] && !flag_power_down) | |
begin | |
flag_power_down = 1; | |
@(posedge CLK); | |
flag_power_down = 0; | |
end | |
end | |
`CMD_READ_SIGNATURE : | |
begin | |
if(!status_reg[`WIP]) | |
begin | |
flag_power_up_exec = 1; | |
input_byte(null_reg); | |
input_byte(null_reg); | |
input_byte(null_reg); | |
forever | |
begin | |
output_byte(DEVICE_ID); | |
flag_power_up_sig_read = 1; | |
end | |
end | |
end | |
`CMD_READ_JEDEC_ID : | |
begin | |
if(!status_reg[`WIP] && !flag_power_down) | |
begin | |
output_byte(MANUFACTURER); | |
output_byte(JEDEC_ID_HI); | |
output_byte(JEDEC_ID_LO); | |
end | |
end | |
`CMD_READ_ID : | |
begin | |
if(!status_reg[`WIP] && !flag_power_down) | |
begin | |
byte_address = 0; | |
input_byte(byte_address[23:16]); | |
input_byte(byte_address[15:8]); | |
input_byte(byte_address[7:0]); | |
forever | |
begin | |
if(byte_address[0]) | |
begin | |
output_byte(DEVICE_ID); | |
output_byte(MANUFACTURER); | |
end | |
else | |
begin | |
output_byte(MANUFACTURER); | |
output_byte(DEVICE_ID); | |
end | |
end | |
end | |
end | |
`CMD_READ_ID_DUAL : | |
begin | |
if(!status_reg[`WIP] && !flag_power_down) | |
begin | |
byte_address = 0; | |
input_byte_dual(byte_address[23:16]); | |
input_byte_dual(byte_address[15:8]); | |
input_byte_dual(byte_address[7:0]); | |
input_byte_dual(mode_reg[7:0]); | |
forever | |
begin | |
if(byte_address[0]) | |
begin | |
output_byte_dual(DEVICE_ID); | |
output_byte_dual(MANUFACTURER); | |
end | |
else | |
begin | |
output_byte_dual(MANUFACTURER); | |
output_byte_dual(DEVICE_ID); | |
end | |
end | |
end | |
end | |
`CMD_READ_ID_QUAD : | |
begin | |
if(!status_reg[`WIP] && !flag_power_down && status_reg[`QE]) | |
begin | |
byte_address = 0; | |
input_byte_quad(byte_address[23:16]); | |
input_byte_quad(byte_address[15:8]); | |
input_byte_quad(byte_address[7:0]); | |
input_byte_quad(mode_reg[7:0]); | |
input_byte_quad(temp[7:0]); | |
input_byte_quad(temp[7:0]); | |
forever | |
begin | |
if(byte_address[0]) | |
begin | |
output_byte_quad(DEVICE_ID); | |
output_byte_quad(MANUFACTURER); | |
end | |
else | |
begin | |
output_byte_quad(MANUFACTURER); | |
output_byte_quad(DEVICE_ID); | |
end | |
end | |
end | |
end | |
`CMD_READ_UNIQUE_ID : | |
begin | |
if(!status_reg[`WIP] && !flag_power_down) | |
begin | |
input_byte(null_reg); | |
input_byte(null_reg); | |
input_byte(null_reg); | |
input_byte(null_reg); | |
output_byte(UNIQUE_ID[63:56]); | |
output_byte(UNIQUE_ID[55:48]); | |
output_byte(UNIQUE_ID[47:40]); | |
output_byte(UNIQUE_ID[39:32]); | |
output_byte(UNIQUE_ID[31:24]); | |
output_byte(UNIQUE_ID[23:16]); | |
output_byte(UNIQUE_ID[15:8]); | |
output_byte(UNIQUE_ID[7:0]); | |
end | |
end | |
// Status Register commands | |
`CMD_WRITE_ENABLE : | |
begin | |
if((!flag_power_down) && (WPn || status_reg[`QE])) | |
status_reg[`WEL] = 1; | |
end | |
`CMD_WRITE_ENABLE_VSR : | |
begin | |
if(!flag_power_down) | |
flag_volatile_sr_write = 1; | |
end | |
`CMD_WRITE_DISABLE : | |
begin | |
if(!flag_power_down) | |
status_reg[`WEL] = 0; | |
end | |
`CMD_READ_STATUS : | |
begin | |
if(!flag_power_down) | |
begin | |
forever | |
begin | |
output_byte(status_reg[7:0]); | |
end | |
end | |
end | |
`CMD_READ_STATUS2 : | |
begin | |
if(!flag_power_down) | |
begin | |
forever | |
begin | |
output_byte(status_reg[15:8]); | |
end | |
end | |
end | |
`CMD_READ_STATUS3 : | |
begin | |
if(!flag_power_down) | |
begin | |
forever | |
begin | |
output_byte(status_reg[23:16]); | |
end | |
end | |
end | |
`CMD_WRITE_STATUS : | |
begin | |
if(!status_reg[`WIP] && (status_reg[`WEL] || flag_volatile_sr_write) && !flag_power_down && !status_reg[`SUS]) | |
begin | |
flag_read_op_reg = 1'b0; | |
case ({status_reg[`SRL],status_reg[`SRP]}) | |
2'b00, 2'b01 : | |
begin | |
if((status_reg[`SRP] && WPn) || !status_reg[`SRP] || status_reg[`QE]) | |
begin | |
// Zero out high order byte of status register | |
status_reg_shadow[23:8] = status_reg[23:8]; | |
input_byte(status_reg_shadow[7:0]); | |
// flag write now that we have 8 bits....2nd 8 bits is optional | |
// command must bail if /CS does not go high at 8th or 16th clock | |
flag_write_status_reg = 1; | |
// 2nd byte is optional | |
if(flag_qpi_mode == 1) | |
@(posedge CLK); | |
else | |
get_posclk_holdn; // If an extra clock comes before CSn goes high, kill command read 2nd byte. | |
flag_write_status_reg = 0; | |
input_byte_no1stclock(temp); | |
flag_write_status_reg = 1; | |
status_reg_shadow[15:8] = temp; | |
if(flag_qpi_mode == 1) | |
@(posedge CLK); | |
else | |
get_posclk_holdn; // If an extra clock comes before CSn goes high, kill command read 2nd byte. | |
flag_write_status_reg = 0; | |
end | |
end | |
endcase | |
end | |
end | |
`CMD_WRITE_STATUS2 : | |
begin | |
if(!status_reg[`WIP] && (status_reg[`WEL] || flag_volatile_sr_write) && !flag_power_down && !status_reg[`SUS]) | |
begin | |
flag_read_op_reg = 1'b0; | |
case ({status_reg[`SRL],status_reg[`SRP]}) | |
2'b00, 2'b01 : | |
begin | |
if((status_reg[`SRP] && WPn) || !status_reg[`SRP] || status_reg[`QE]) | |
begin | |
status_reg_shadow[23:16] = status_reg[23:16]; | |
status_reg_shadow[7:0] = status_reg[7:0]; | |
input_byte(status_reg_shadow[15:8]); | |
// flag write now that we have 8 bits.... | |
// command must bail if /CS does not go high at 8th clock | |
flag_write_status_reg = 1; | |
// 2nd byte is optional | |
if(flag_qpi_mode == 1) | |
@(posedge CLK); | |
else | |
get_posclk_holdn; // If an extra clock comes before CSn goes high, kill command read 2nd byte. | |
flag_write_status_reg = 0; | |
end | |
end | |
endcase | |
end | |
end | |
`CMD_WRITE_STATUS3 : | |
begin | |
if(!status_reg[`WIP] && (status_reg[`WEL] || flag_volatile_sr_write) && !flag_power_down && !status_reg[`SUS]) | |
begin | |
flag_read_op_reg = 1'b0; | |
case ({status_reg[`SRL],status_reg[`SRP]}) | |
2'b00, 2'b01 : | |
begin | |
if((status_reg[`SRP] && WPn) || !status_reg[`SRP] || status_reg[`QE]) | |
begin | |
status_reg_shadow[15:0] = status_reg[15:0]; | |
input_byte(status_reg_shadow[23:16]); | |
// flag write now that we have 8 bits.... | |
// command must bail if /CS does not go high at 8th clock | |
flag_write_status_reg = 1; | |
// 2nd byte is optional | |
if(flag_qpi_mode == 1) | |
@(posedge CLK); | |
else | |
get_posclk_holdn; // If an extra clock comes before CSn goes high, kill command read 2nd byte. | |
flag_write_status_reg = 0; | |
end | |
end | |
endcase | |
end | |
end | |
// Write Page Commands | |
`CMD_PAGE_PROGRAM : | |
begin | |
if(status_reg[`WEL] && !status_reg[`SUS] && !flag_power_down) | |
begin | |
flag_read_op_reg = 1'b0; | |
write_page(0); | |
end | |
end | |
`CMD_PAGE_PROGRAM_QUAD : | |
begin | |
if(status_reg[`WEL]&& !status_reg[`SUS] && status_reg[`QE] && !flag_power_down) | |
begin | |
flag_read_op_reg = 1'b0; | |
write_page(1); | |
end | |
end | |
// Suspend / Resume Commands | |
`CMD_SUSPEND : | |
begin | |
if(!flag_power_down && !flag_erase_bulk) | |
begin | |
if((!flag_suspend) && (flag_erase_sector || flag_erase_secsi_sector || flag_erase_half_block || flag_erase_block || flag_prog_page || flag_prog_secsi_page)) | |
begin | |
flag_suspend = 1'b1; | |
get_posclk_holdn; // If an extra clock comes before CSn goes high, kill command. | |
flag_suspend = 1'b0; | |
end | |
end | |
end | |
`CMD_RESUME : | |
begin | |
if(!flag_power_down) | |
begin | |
if(flag_suspend) | |
begin | |
flag_resume = 1'b1; | |
get_posclk_holdn; // If an extra clock comes before CSn goes high, kill command. | |
flag_resume = 1'b0; | |
end | |
end | |
end | |
// Erase Commands | |
`CMD_SECTOR_ERASE : | |
begin | |
if(status_reg[`WEL] && !flag_power_down && !status_reg[`SUS]) | |
begin | |
flag_read_op_reg = 1'b0; | |
input_byte(byte_address[23:16]); | |
input_byte(byte_address[15:8]); | |
input_byte(byte_address[7:0]); | |
if(!write_protected(byte_address)) | |
begin | |
flag_erase_sector = 1; | |
get_posclk_holdn; // If an extra clock comes before CSn goes high, kill command. | |
flag_erase_sector = 0; | |
end | |
end | |
end | |
`CMD_HALF_BLOCK_ERASE : | |
begin | |
if(status_reg[`WEL] && !flag_power_down && !status_reg[`SUS]) | |
begin | |
flag_read_op_reg = 1'b0; | |
input_byte(byte_address[23:16]); | |
input_byte(byte_address[15:8]); | |
input_byte(byte_address[7:0]); | |
if(!write_protected(byte_address)) | |
begin | |
flag_erase_half_block = 1; | |
get_posclk_holdn; // If an extra clock comes before CSn goes high, kill command. | |
flag_erase_half_block = 0; | |
end | |
end | |
end | |
`CMD_BLOCK_ERASE : | |
begin | |
if(status_reg[`WEL] && !flag_power_down && !status_reg[`SUS]) | |
begin | |
flag_read_op_reg = 1'b0; | |
input_byte(byte_address[23:16]); | |
input_byte(byte_address[15:8]); | |
input_byte(byte_address[7:0]); | |
if(!write_protected(byte_address)) | |
begin | |
flag_erase_block = 1; | |
get_posclk_holdn; // If an extra clock comes before CSn goes high, kill command. | |
flag_erase_block = 0; | |
end | |
end | |
end | |
`CMD_BULK_ERASE, `CMD_BULK_ERASE2 : | |
begin | |
if(status_reg[`WEL] && !flag_power_down && !status_reg[`SUS]) | |
begin | |
flag_read_op_reg = 1'b0; | |
case ({status_reg[`BP0],status_reg[`BP1]}) | |
2'b00 : | |
begin | |
flag_erase_bulk = 1; | |
get_posclk_holdn; | |
flag_erase_bulk = 0; | |
end | |
endcase | |
end | |
end | |
// Read Page Commands | |
`CMD_READ_DATA : | |
begin | |
if(!status_reg[`WIP] && !flag_power_down) | |
begin | |
flag_slow_read_reg = 1'b1; | |
input_byte(byte_address[23:16]); | |
input_byte(byte_address[15:8]); | |
input_byte(byte_address[7:0]); | |
forever | |
begin | |
byte_address = byte_address & ADDRESS_MASK; | |
output_byte(memory[byte_address]); | |
byte_address = byte_address + 1; | |
end | |
end | |
end | |
`CMD_READ_DATA_FAST : | |
begin | |
if(!flag_power_down) | |
read_page(1,0); | |
end | |
`CMD_READ_DATA_FAST_DTR : | |
begin | |
if(!status_reg[`WIP] && !flag_power_down) | |
begin | |
input_byte_DTR(byte_address[23:16]); | |
input_byte_DTR(byte_address[15:8]); | |
input_byte_DTR(byte_address[7:0]); | |
for(x = 5; x >= 0; x=x-1) | |
begin | |
get_posclk_holdn; | |
null_reg[x] = DIO; | |
end | |
forever | |
begin | |
byte_address = byte_address & ADDRESS_MASK; | |
output_byte_DTR(memory[byte_address]); | |
byte_address = byte_address + 1; | |
end | |
end | |
end | |
`CMD_READ_DATA_FAST_DUAL_IO_DTR : | |
begin | |
if(!status_reg[`WIP] && !flag_power_down) | |
begin | |
input_byte_dual_DTR(byte_address[23:16]); | |
input_byte_dual_DTR(byte_address[15:8]); | |
input_byte_dual_DTR(byte_address[7:0]); | |
input_byte_dual_DTR(mode_reg[7:0]); | |
for(x = 3; x >= 0; x=x-1) | |
begin | |
get_posclk_holdn; | |
null_reg[x] = DIO; | |
end | |
forever | |
begin | |
byte_address = byte_address & ADDRESS_MASK; | |
output_byte_dual_DTR(memory[byte_address]); | |
byte_address = byte_address + 1; | |
end | |
end | |
end | |
`CMD_READ_DATA_FAST_QUAD_IO_DTR : | |
begin | |
if(!status_reg[`WIP] && !flag_power_down && status_reg[`QE]) | |
begin | |
input_byte_quad_DTR(byte_address[23:16]); | |
input_byte_quad_DTR(byte_address[15:8]); | |
input_byte_quad_DTR(byte_address[7:0]); | |
input_byte_quad_DTR(mode_reg[7:0]); | |
for(x = 6; x >= 0; x=x-1) | |
begin | |
get_posclk_holdn; | |
null_reg[x] = DIO; | |
end | |
forever | |
begin | |
byte_address = byte_address & ADDRESS_MASK; | |
output_byte_quad_DTR(memory[byte_address]); | |
byte_address = byte_address + 1; | |
end | |
end | |
end | |
`CMD_READ_DATA_FAST_DTR_WRAP : | |
begin | |
if(!status_reg[`WIP] && !flag_power_down) | |
begin | |
input_byte_quad_DTR(byte_address[23:16]); | |
input_byte_quad_DTR(byte_address[15:8]); | |
input_byte_quad_DTR(byte_address[7:0]); | |
input_byte_quad_DTR(null_reg); | |
forever | |
begin | |
byte_address = byte_address & ADDRESS_MASK; | |
output_byte_quad_DTR(memory[byte_address]); | |
case ({read_param_reg[1],read_param_reg[0]}) | |
2'b00 : | |
byte_address[2:0] = byte_address[2:0] + 1; | |
2'b01 : | |
byte_address[3:0] = byte_address[3:0] + 1; | |
2'b10 : | |
byte_address[4:0] = byte_address[4:0] + 1; | |
2'b11 : | |
byte_address[5:0] = byte_address[5:0] + 1; | |
endcase | |
end | |
end | |
end | |
`CMD_READ_DATA_FAST_WRAP : | |
begin | |
if(!flag_power_down) | |
begin | |
if(flag_qpi_mode) | |
read_page_quadio(cmd_byte); | |
end | |
end | |
`CMD_READ_DATA_FAST_DUAL : | |
begin | |
if(!flag_power_down) | |
read_page(2,0); | |
end | |
`CMD_READ_DATA_FAST_QUAD : | |
begin | |
if(!flag_power_down && status_reg[`QE]) | |
read_page(3,0); | |
end | |
`CMD_READ_DATA_FAST_DUAL_IO : | |
begin | |
if(!flag_power_down) | |
read_page_dualio; | |
end | |
`CMD_READ_DATA_FAST_QUAD_IO : | |
begin | |
if(!flag_power_down && status_reg[`QE]) | |
read_page_quadio(cmd_byte); | |
end | |
`CMD_SET_BURST_WRAP : | |
begin | |
if(!status_reg[`WIP] && !flag_power_down && status_reg[`QE]) | |
begin | |
input_byte_quad(temp[7:0]); | |
input_byte_quad(temp[7:0]); | |
input_byte_quad(temp[7:0]); | |
input_byte_quad(wrap_reg[7:0]); | |
end | |
end | |
`CMD_READ_SFDP : | |
begin | |
if(!flag_power_down) | |
begin | |
flag_slow_read_reg = 1'b1; | |
read_page(0,2); | |
end | |
end | |
// Security Register Opcodes | |
`CMD_SREG_READ : | |
begin | |
if(!flag_power_down) | |
begin | |
read_page(0,1); | |
end | |
end | |
`CMD_SREG_ERASE : | |
begin | |
if(status_reg[`WEL] && !flag_power_down && !status_reg[`SUS]) | |
begin | |
flag_read_op_reg = 1'b0; | |
input_byte(byte_address[23:16]); | |
input_byte(byte_address[15:8]); | |
input_byte(byte_address[7:0]); | |
case (byte_address[23:8]) | |
16'h10 : | |
begin | |
if(!status_reg[`LB1]) | |
begin | |
flag_erase_secsi_sector = 1; | |
get_posclk_holdn; | |
flag_erase_secsi_sector = 0; | |
end | |
end | |
16'h20 : | |
begin | |
if(!status_reg[`LB2]) | |
begin | |
flag_erase_secsi_sector = 1; | |
get_posclk_holdn; | |
flag_erase_secsi_sector = 0; | |
end | |
end | |
16'h30 : | |
begin | |
if(!status_reg[`LB3]) | |
begin | |
flag_erase_secsi_sector = 1; | |
get_posclk_holdn; | |
flag_erase_secsi_sector = 0; | |
end | |
end | |
endcase | |
end | |
end | |
`CMD_SREG_PROGRAM : | |
begin | |
if(status_reg[`WEL] && !flag_power_down && !status_reg[`SUS]) | |
begin | |
flag_read_op_reg = 1'b0; | |
begin | |
if(!status_reg[`WIP]) | |
begin | |
input_byte(prog_byte_address[23:16]); | |
input_byte(prog_byte_address[15:8]); | |
input_byte(prog_byte_address[7:0]); | |
case (byte_address[23:8]) | |
16'h10 : | |
if(!status_reg[`LB1]) | |
fill_page_latch(0,prog_byte_address,1); | |
16'h20 : | |
if(!status_reg[`LB2]) | |
fill_page_latch(0,prog_byte_address,1); | |
16'h30 : | |
if(!status_reg[`LB3]) | |
fill_page_latch(0,prog_byte_address,1); | |
endcase | |
end | |
end | |
end | |
end | |
// Lock Bit commands | |
`CMD_GLOBAL_BLOCK_LOCK : | |
begin | |
if(!status_reg[`WIP] && !flag_power_down) | |
begin | |
for(x = 0; x < NUM_LOCKBITS; x=x+1) | |
lock_array[x] = 1; | |
end | |
end | |
`CMD_GLOBAL_BLOCK_UNLOCK : | |
begin | |
if(!status_reg[`WIP] && !flag_power_down) | |
begin | |
for(x = 0; x < NUM_LOCKBITS; x=x+1) | |
lock_array[x] = 0; | |
end | |
end | |
`CMD_INDIVIDUAL_LOCK : | |
begin | |
if(!status_reg[`WIP] && !flag_power_down) | |
begin | |
input_byte(byte_address[23:16]); | |
input_byte(byte_address[15:8]); | |
input_byte(byte_address[7:0]); | |
lock_array[lockbit_index(byte_address)] = 1; | |
end | |
end | |
`CMD_INDIVIDUAL_UNLOCK : | |
begin | |
if(!status_reg[`WIP] && !flag_power_down) | |
begin | |
input_byte(byte_address[23:16]); | |
input_byte(byte_address[15:8]); | |
input_byte(byte_address[7:0]); | |
lock_array[lockbit_index(byte_address)] = 0; | |
end | |
end | |
`CMD_READ_BLOCK_LOCK : | |
begin | |
if(!status_reg[`WIP] && !flag_power_down) | |
begin | |
input_byte(byte_address[23:16]); | |
input_byte(byte_address[15:8]); | |
input_byte(byte_address[7:0]); | |
null_reg[7:1] = 0; | |
null_reg[0] = lock_array[lockbit_index(byte_address)]; | |
output_byte(null_reg); | |
end | |
end | |
default : | |
begin | |
$display("Invalid Opcode. (%0h)",cmd_byte); | |
$stop; | |
end | |
endcase | |
end | |
/****************************************************************************** | |
The following routine occurs when CSn goes high. | |
In the case, all communications activities inside the chip must halt. | |
Transfer and Program/Erase conditions will continue. | |
******************************************************************************/ | |
always @(posedge CSn) // When CSn goes high, device becomes in-active | |
begin :disable_interface | |
#tSHQZ; // Data-output disable time | |
HOLDn_Active = 1'b0; // Disable HOLDn from mucking with output registers. | |
DO_Output_Enable = 1'b0; // Tri-state DO output. | |
DIO_Output_Enable_reg = 1'b0; // Tri-state DIO output. | |
WPn_Output_Enable_reg = 1'b0; // Tri-state WPn output | |
HOLDn_Output_Enable = 1'b0; // Tri-state HOLDn output | |
flag_slow_read_reg = 1'b0; // Initiate normal timing checks | |
disable input_byte; | |
disable input_byte_dual; | |
disable input_mode_dual; | |
disable input_byte_quad; | |
disable output_byte; | |
disable output_byte_dual; | |
disable output_byte_quad; | |
disable read_opcode; | |
disable write_page; | |
disable fill_page_latch; | |
disable read_page; | |
disable read_page_dualio; | |
disable read_page_quadio; | |
disable get_posclk_holdn; | |
disable get_negclk_holdn; | |
end | |
/****************************************************************************** | |
The following routine occurs when HOLDn goes low. | |
******************************************************************************/ | |
always @(negedge (HOLDn & !status_reg[`QE] & !CSn)) | |
begin | |
if(!HOLDn) | |
begin | |
#tHLQZ; | |
temp_DIO_Output_Enable_reg = DIO_Output_Enable_reg; | |
temp_DO_Output_Enable = DO_Output_Enable; | |
DIO_Output_Enable_reg = 1'b0; // Set DIO and DO to an input state | |
DO_Output_Enable = 1'b0; | |
HOLDn_Active = 1'b1; | |
end | |
end | |
/****************************************************************************** | |
The following routine occurs when HOLDn goes high. | |
******************************************************************************/ | |
always @(posedge HOLDn) | |
begin | |
if(HOLDn_Active == 1'b1) | |
begin | |
#tHHQX; | |
DIO_Output_Enable_reg = temp_DIO_Output_Enable_reg; | |
DO_Output_Enable = temp_DO_Output_Enable; | |
HOLDn_Active = 1'b0; | |
end | |
end | |
/****************************************************************************** | |
The following routine occurs when RESETn goes low. | |
******************************************************************************/ | |
always @(negedge RESETn) | |
begin | |
status_reg[`WIP] = 1; | |
flag_reset_condition = 1; | |
#tRESET; | |
chip_reset(); | |
end | |
/****************************************************************************** | |
GENERAL PURPOSE SUBROUTINES | |
******************************************************************************/ | |
task chip_reset; | |
integer x; | |
begin | |
// Tri-state all outputs | |
temp_DIO_Output_Enable_reg = 1'b0; | |
DIO_Output_Enable_reg = 1'b0; // Tri-state DIO Output | |
temp_DO_Output_Enable = 1'b0; | |
DO_Output_Enable = 1'b0; // Tri-state DO Output. | |
WPn_Output_Enable_reg = 1'b0; // Tri-state WPn Output | |
HOLDn_Output_Enable = 1'b0; // Tri-state HOLDn Output | |
HOLDn_Active = 1'b0; | |
// Set all output registers to default to 0 | |
DIO_Reg = 1'b0; | |
DO_Reg = 1'b0; | |
WPn_Reg = 1'b0; | |
HOLDn_Reg = 1'b0; | |
mode_reg = 8'h00; // Setup null mode register | |
wrap_reg = 8'b00010000; // Setup Bit 4 of wrap register to 1 as default | |
read_param_reg = 8'h00; // Reset defaults for read parameter register | |
status_reg = 0; // Status register default OTP values. | |
status_reg[`QE] = status_reg_otp[`QE]; | |
status_reg[`SRL] = status_reg_otp[`SRL]; | |
status_reg[`SRP] = status_reg_otp[`SRP]; | |
status_reg[`BP0] = status_reg_otp[`BP0]; | |
status_reg[`BP1] = status_reg_otp[`BP1]; | |
status_reg[`BP2] = status_reg_otp[`BP2]; | |
status_reg[`SEC] = status_reg_otp[`SEC]; | |
status_reg[`TB] = status_reg_otp[`TB]; | |
status_reg[`CMP] = status_reg_otp[`CMP]; | |
status_reg[`WPS] = status_reg_otp[`WPS]; | |
status_reg[`DRV0] = status_reg_otp[`DRV0]; | |
status_reg[`DRV1] = status_reg_otp[`DRV1]; | |
status_reg[`HLD_RST] = status_reg_otp[`HLD_RST]; | |
status_reg[`LB1] = status_reg_otp[`LB1]; | |
status_reg[`LB2] = status_reg_otp[`LB2]; | |
status_reg[`LB3] = status_reg_otp[`LB3]; | |
flag_prog_page = 0; | |
flag_prog_secsi_page = 0; | |
flag_erase_sector = 0; | |
flag_erase_half_block = 0; | |
flag_erase_block = 0; | |
flag_erase_secsi_sector = 0; | |
flag_erase_bulk = 0; | |
flag_power_down = 0; | |
flag_power_up_exec = 0; | |
flag_power_up_sig_read = 0; | |
flag_write_status_reg = 0; | |
flag_slow_read_reg = 1'b0; // Flag for standard read command....i.e. different timings | |
flag_read_op_reg = 1'b0; // Flag for any read command....i.e. different timings. | |
flag_suspend = 1'b0; // clear erase suspend status | |
flag_suspend_enabled = 1'b0; | |
flag_volatile_sr_write = 1'b0; | |
flag_qpi_mode = 0; | |
flag_enable_reset = 0; | |
flag_reset = 0; | |
flag_reset_condition = 0; | |
flag_set_read_param = 0; | |
timing_error = 0; | |
cmd_byte = 0; | |
null_reg = 0; | |
in_byte = 0; | |
out_byte = 0; | |
// Reset the Lock Array to full write protect | |
for(x = 0; x < NUM_LOCKBITS; x=x+1) | |
lock_array[x] = 1; | |
end | |
endtask | |
/****************************************************************************** | |
The following routine will input 1 byte from the SPI DIO pin and place | |
the results in the input_data register. | |
******************************************************************************/ | |
task input_byte; | |
output [7:0] input_data; | |
integer x; | |
begin | |
if(flag_qpi_mode == 1) | |
input_byte_quad(input_data); | |
else | |
begin | |
// Set the DIO output register high-Z | |
if(DIO_Output_Enable_reg != 1'b0) | |
DIO_Output_Enable_reg = 1'b0; | |
for(x = 7; x >= 0; x=x-1) | |
begin | |
get_posclk_holdn; | |
input_data[x] = DIO; | |
end | |
in_byte = input_data; | |
end | |
end | |
endtask | |
/****************************************************************************** | |
The following routine will input 1 byte from the SPI DIO pin and place | |
the results in the input_data register in DTR mode. | |
******************************************************************************/ | |
task input_byte_DTR; | |
output [7:0] input_data; | |
integer x; | |
begin | |
if(flag_qpi_mode == 1) | |
input_byte_quad(input_data); | |
else | |
begin | |
// Set the DIO output register high-Z | |
if(DIO_Output_Enable_reg != 1'b0) | |
DIO_Output_Enable_reg = 1'b0; | |
for(x = 7; x >= 0; x=x-2) | |
begin | |
get_posclk_holdn; | |
input_data[x] = DIO; | |
get_negclk_holdn; | |
input_data[x-1] = DIO; | |
end | |
in_byte = input_data; | |
end | |
end | |
endtask | |
/****************************************************************************** | |
The following routine will input 1 byte from the SPI DIO pin and place | |
the results in the input_data register. | |
******************************************************************************/ | |
task input_byte_no1stclock; | |
output [7:0] input_data; | |
integer x; | |
begin | |
if(flag_qpi_mode == 1) | |
input_byte_quad_no1stclock(input_data); | |
else | |
begin | |
// Set the DIO output register high-Z | |
if(DIO_Output_Enable_reg != 1'b0) | |
DIO_Output_Enable_reg = 1'b0; | |
for(x = 7; x >= 0; x=x-1) | |
begin | |
if(x != 7) | |
get_posclk_holdn; | |
input_data[x] = DIO; | |
end | |
in_byte = input_data; | |
end | |
end | |
endtask | |
/****************************************************************************** | |
The following routine will input 1 byte from the SPI DIO pin and DO pin and place | |
the results in the input_data register. | |
******************************************************************************/ | |
task input_byte_dual; | |
output [7:0] input_data; | |
integer x; | |
begin | |
// Set the DIO output register high-Z | |
if(DIO_Output_Enable_reg != 1'b0) | |
DIO_Output_Enable_reg = 1'b0; | |
// Set the DO output register high-Z | |
if(DO_Output_Enable != 1'b0) | |
DO_Output_Enable = 1'b0; | |
for(x = 7; x >= 0; x=x-2) | |
begin | |
get_posclk_holdn; | |
input_data[x-1] = DIO; | |
input_data[x] = DO; | |
end | |
in_byte = input_data; | |
end | |
endtask | |
/****************************************************************************** | |
The following routine will input 1 byte from the SPI DIO pin and DO pin and place | |
the results in the input_data register in DTR mode. | |
******************************************************************************/ | |
task input_byte_dual_DTR; | |
output [7:0] input_data; | |
integer x; | |
begin | |
// Set the DIO output register high-Z | |
if(DIO_Output_Enable_reg != 1'b0) | |
DIO_Output_Enable_reg = 1'b0; | |
// Set the DO output register high-Z | |
if(DO_Output_Enable != 1'b0) | |
DO_Output_Enable = 1'b0; | |
for(x = 7; x >= 0; x=x-4) | |
begin | |
get_posclk_holdn; | |
input_data[x-1] = DIO; | |
input_data[x] = DO; | |
get_negclk_holdn; | |
input_data[x-3] = DIO; | |
input_data[x-2] = DO; | |
end | |
in_byte = input_data; | |
end | |
endtask | |
/****************************************************************************** | |
The following routine will input 1 nibble from the SPI DIO pin and DO pin and place | |
the results in the input_data register. | |
******************************************************************************/ | |
task input_mode_dual; | |
output [5:0] input_data; | |
integer x; | |
begin | |
// Set the DIO output register high-Z | |
if(DIO_Output_Enable_reg != 1'b0) | |
DIO_Output_Enable_reg = 1'b0; | |
// Set the DO output register high-Z | |
if(DO_Output_Enable != 1'b0) | |
DO_Output_Enable = 1'b0; | |
for(x = 5; x >= 0; x=x-2) | |
begin | |
get_posclk_holdn; | |
input_data[x-1] = DIO; | |
input_data[x] = DO; | |
end | |
end | |
endtask | |
/****************************************************************************** | |
The following routine will input 1 byte from the SPI DIO and DO and WPn and HOLDn pins | |
and place the results in the input_data register. | |
******************************************************************************/ | |
task input_byte_quad; | |
output [7:0] input_data; | |
integer x; | |
begin | |
// Set the DIO output register high-Z | |
if(DIO_Output_Enable_reg != 1'b0) | |
DIO_Output_Enable_reg = 1'b0; | |
// Set the DO output register high-Z | |
if(DO_Output_Enable != 1'b0) | |
DO_Output_Enable = 1'b0; | |
// Set the WPn output register high-Z | |
if(WPn_Output_Enable_reg != 1'b0) | |
WPn_Output_Enable_reg = 1'b0; | |
// Set the HOLDn output register high-Z | |
if(HOLDn_Output_Enable != 1'b0) | |
DO_Output_Enable = 1'b0; | |
for(x = 7; x >= 0; x=x-4) | |
begin | |
@(posedge CLK); | |
input_data[x-3] = DIO; | |
input_data[x-2] = DO; | |
input_data[x-1] = WPn; | |
input_data[x] = HOLDn; | |
end | |
in_byte = input_data; | |
end | |
endtask | |
/****************************************************************************** | |
The following routine will input 1 byte from the SPI DIO and DO and WPn and HOLDn pins | |
and place the results in the input_data register in DTR mode. | |
******************************************************************************/ | |
task input_byte_quad_DTR; | |
output [7:0] input_data; | |
integer x; | |
begin | |
// Set the DIO output register high-Z | |
if(DIO_Output_Enable_reg != 1'b0) | |
DIO_Output_Enable_reg = 1'b0; | |
// Set the DO output register high-Z | |
if(DO_Output_Enable != 1'b0) | |
DO_Output_Enable = 1'b0; | |
// Set the WPn output register high-Z | |
if(WPn_Output_Enable_reg != 1'b0) | |
WPn_Output_Enable_reg = 1'b0; | |
// Set the HOLDn output register high-Z | |
if(HOLDn_Output_Enable != 1'b0) | |
DO_Output_Enable = 1'b0; | |
for(x = 7; x >= 0; x=x-8) | |
begin | |
@(posedge CLK); | |
input_data[x-3] = DIO; | |
input_data[x-2] = DO; | |
input_data[x-1] = WPn; | |
input_data[x] = HOLDn; | |
@(negedge CLK); | |
input_data[x-7] = DIO; | |
input_data[x-6] = DO; | |
input_data[x-5] = WPn; | |
input_data[x-4] = HOLDn; | |
end | |
in_byte = input_data; | |
end | |
endtask | |
/****************************************************************************** | |
The following routine will input 1 byte from the SPI DIO and DO and WPn and HOLDn pins | |
and place the results in the input_data register. | |
******************************************************************************/ | |
task input_byte_quad_no1stclock; | |
output [7:0] input_data; | |
integer x; | |
begin | |
// Set the DIO output register high-Z | |
if(DIO_Output_Enable_reg != 1'b0) | |
DIO_Output_Enable_reg = 1'b0; | |
// Set the DO output register high-Z | |
if(DO_Output_Enable != 1'b0) | |
DO_Output_Enable = 1'b0; | |
// Set the WPn output register high-Z | |
if(WPn_Output_Enable_reg != 1'b0) | |
WPn_Output_Enable_reg = 1'b0; | |
// Set the HOLDn output register high-Z | |
if(HOLDn_Output_Enable != 1'b0) | |
DO_Output_Enable = 1'b0; | |
for(x = 7; x >= 0; x=x-4) | |
begin | |
if(x != 7) | |
@(posedge CLK); | |
input_data[x-3] = DIO; | |
input_data[x-2] = DO; | |
input_data[x-1] = WPn; | |
input_data[x] = HOLDn; | |
end | |
in_byte = input_data; | |
end | |
endtask | |
/****************************************************************************** | |
The following routine will output 1 byte to the DO pin. | |
******************************************************************************/ | |
task output_byte; | |
input [7:0] output_data; | |
integer x; | |
begin | |
if(flag_qpi_mode == 1) | |
output_byte_quad(output_data); | |
else | |
begin | |
out_byte = output_data; | |
for(x = 7; x >= 0; x=x-1) | |
begin | |
get_negclk_holdn; | |
if(DO_Output_Enable == 1'b0) // If the bus is not enabled, enable it now. | |
DO_Output_Enable = 1'b1; | |
#tCLQV DO_Reg = output_data[x]; | |
end | |
end | |
end | |
endtask | |
/****************************************************************************** | |
The following routine will output 1 byte to the DO pin in DTR mode. | |
******************************************************************************/ | |
task output_byte_DTR; | |
input [7:0] output_data; | |
integer x; | |
begin | |
if(flag_qpi_mode == 1) | |
output_byte_quad(output_data); | |
else | |
begin | |
out_byte = output_data; | |
for(x = 7; x >= 0; x=x-2) | |
begin | |
get_negclk_holdn; | |
if(DO_Output_Enable == 1'b0) // If the bus is not enabled, enable it now. | |
DO_Output_Enable = 1'b1; | |
#tCLQV DO_Reg = output_data[x]; | |
get_posclk_holdn; | |
if(DO_Output_Enable == 1'b0) // If the bus is not enabled, enable it now. | |
DO_Output_Enable = 1'b1; | |
#tCLQV DO_Reg = output_data[x-1]; | |
end | |
end | |
end | |
endtask | |
/****************************************************************************** | |
The following routine will output 1 byte to the DO and DIO pin. | |
******************************************************************************/ | |
task output_byte_dual; | |
input [7:0] output_data; | |
integer x; | |
begin | |
out_byte = output_data; | |
for(x = 7; x >= 0; x=x-2) | |
begin | |
get_negclk_holdn; | |
if(DO_Output_Enable == 1'b0) // If the bus is not enabled, enable it now. | |
DO_Output_Enable = 1'b1; | |
if(DIO_Output_Enable_reg == 1'b0) | |
DIO_Output_Enable_reg = 1'b1; | |
#tCLQV ; | |
DIO_Reg = output_data[x-1]; | |
DO_Reg = output_data[x]; | |
end | |
end | |
endtask | |
/****************************************************************************** | |
The following routine will output 1 byte to the DO and DIO pin in DTR mode. | |
******************************************************************************/ | |
task output_byte_dual_DTR; | |
input [7:0] output_data; | |
integer x; | |
begin | |
out_byte = output_data; | |
for(x = 7; x >= 0; x=x-4) | |
begin | |
get_negclk_holdn; | |
if(DO_Output_Enable == 1'b0) // If the bus is not enabled, enable it now. | |
DO_Output_Enable = 1'b1; | |
if(DIO_Output_Enable_reg == 1'b0) | |
DIO_Output_Enable_reg = 1'b1; | |
#tCLQV ; | |
DIO_Reg = output_data[x-1]; | |
DO_Reg = output_data[x]; | |
get_posclk_holdn; | |
if(DO_Output_Enable == 1'b0) // If the bus is not enabled, enable it now. | |
DO_Output_Enable = 1'b1; | |
if(DIO_Output_Enable_reg == 1'b0) | |
DIO_Output_Enable_reg = 1'b1; | |
#tCLQV ; | |
DIO_Reg = output_data[x-3]; | |
DO_Reg = output_data[x-2]; | |
end | |
end | |
endtask | |
/****************************************************************************** | |
The following routine will output 1 byte to the DO and DIO and WPn and HOLDn pin. | |
******************************************************************************/ | |
task output_byte_quad; | |
input [7:0] output_data; | |
integer x; | |
begin | |
out_byte = output_data; | |
for(x = 7; x >= 0; x=x-4) | |
begin | |
@(negedge CLK); | |
if(DO_Output_Enable == 1'b0) // If the bus is not enabled, enable it now. | |
DO_Output_Enable = 1'b1; | |
if(DIO_Output_Enable_reg == 1'b0) | |
DIO_Output_Enable_reg = 1'b1; | |
if(WPn_Output_Enable_reg == 1'b0) | |
WPn_Output_Enable_reg = 1'b1; | |
if(HOLDn_Output_Enable == 1'b0) | |
HOLDn_Output_Enable = 1'b1; | |
#tCLQV; | |
DIO_Reg = output_data[x-3]; | |
DO_Reg = output_data[x-2]; | |
WPn_Reg = output_data[x-1]; | |
HOLDn_Reg = output_data[x]; | |
end | |
end | |
endtask | |
/****************************************************************************** | |
The following routine will output 1 byte to the DO and DIO and WPn and HOLDn pin in DTR mode. | |
******************************************************************************/ | |
task output_byte_quad_DTR; | |
input [7:0] output_data; | |
integer x; | |
begin | |
out_byte = output_data; | |
for(x = 7; x >= 0; x=x-8) | |
begin | |
@(negedge CLK); | |
if(DO_Output_Enable == 1'b0) // If the bus is not enabled, enable it now. | |
DO_Output_Enable = 1'b1; | |
if(DIO_Output_Enable_reg == 1'b0) | |
DIO_Output_Enable_reg = 1'b1; | |
if(WPn_Output_Enable_reg == 1'b0) | |
WPn_Output_Enable_reg = 1'b1; | |
if(HOLDn_Output_Enable == 1'b0) | |
HOLDn_Output_Enable = 1'b1; | |
#tCLQV; | |
DIO_Reg = output_data[x-3]; | |
DO_Reg = output_data[x-2]; | |
WPn_Reg = output_data[x-1]; | |
HOLDn_Reg = output_data[x]; | |
@(posedge CLK); | |
if(DO_Output_Enable == 1'b0) // If the bus is not enabled, enable it now. | |
DO_Output_Enable = 1'b1; | |
if(DIO_Output_Enable_reg == 1'b0) | |
DIO_Output_Enable_reg = 1'b1; | |
if(WPn_Output_Enable_reg == 1'b0) | |
WPn_Output_Enable_reg = 1'b1; | |
if(HOLDn_Output_Enable == 1'b0) | |
HOLDn_Output_Enable = 1'b1; | |
#tCLQV; | |
DIO_Reg = output_data[x-7]; | |
DO_Reg = output_data[x-6]; | |
WPn_Reg = output_data[x-5]; | |
HOLDn_Reg = output_data[x-4]; | |
end | |
end | |
endtask | |
/****************************************************************************** | |
The following routine will return when a negative edge happens on CLK with respect | |
to the HOLDn signal. | |
******************************************************************************/ | |
task get_negclk_holdn; | |
begin | |
if(status_reg[`QE]) // Quad bus is enabled, HOLD condition does not exist | |
@(negedge CLK); // Therefore return negedge CLK | |
else | |
@(negedge (CLK & HOLDn)); // If Quad bus is disabled, return CLK only when HOLDn is high. | |
end | |
endtask | |
/****************************************************************************** | |
The following routine will return when a positive edge happens on CLK with respect | |
to the HOLDn signal. | |
******************************************************************************/ | |
task get_posclk_holdn; | |
begin | |
if(status_reg[`QE]) // Quad bus is enabled, HOLD condition does not exist | |
@(posedge CLK); // Therefore return negedge CLK | |
else | |
@(posedge (CLK & HOLDn)); // If Quad bus is disabled, return CLK only when HOLDn is high. | |
end | |
endtask | |
/****************************************************************************** | |
The following routine will delay the specified amount of time while waiting for the | |
flag_suspend_enabled signal or the flag_reset_condition signal. If the flag_suspend_enable asserts, the delay routine will wait indefinetly | |
until flag_suspend_enable deasserts; if flag_reset_condition asserts, the routine exits immediately | |
******************************************************************************/ | |
task wait_reset_suspend; | |
input [31:0] delay; | |
integer waitx; | |
integer num_iterations; | |
begin | |
// Warning, this routine is not recursive and cannot be called while suspend is enabled! | |
// This function delays while optionally waiting for the suspend signal. To reduce CPU simulation resources, | |
// This routine will count in tReset_Suspend_Max increments down to a value less than tReset_Suspend_Max wait and then | |
// look every nanosecond to finish. It exists under any circumstance if the chip is reset. | |
waitx = 0; | |
if(delay >= tReset_Suspend_Max) | |
begin | |
num_iterations = delay / tReset_Suspend_Max; | |
for(waitx = 0; waitx < num_iterations; waitx=waitx+1) // check for erase suspend while part is programming | |
begin | |
if(flag_reset_condition) // If chip is reset, exit loop | |
waitx = num_iterations; // Break the loop | |
else | |
begin | |
wait(!flag_suspend_enabled || flag_reset_condition); | |
#tReset_Suspend_Max; | |
end | |
end | |
end | |
num_iterations = delay % tReset_Suspend_Max; | |
for(waitx = 0; waitx < num_iterations; waitx=waitx+1) | |
begin | |
if(flag_reset_condition) // check for chip reset while part is programming | |
waitx = num_iterations; | |
else | |
begin | |
wait(!flag_suspend_enabled || flag_reset_condition); // check for erase suspend while waiting | |
#1; | |
end | |
end | |
end | |
endtask | |
/****************************************************************************** | |
The following routine will delay the specified amount of time while waiting for the | |
flag_reset_condition signal. If flag_reset_condition is asserted, the routine aborts because the chip has been reset | |
******************************************************************************/ | |
task wait_reset; | |
input [31:0] delay; | |
integer waitx; | |
integer num_iterations; | |
begin | |
// This task delays while waiting for the reset signal. To reduce CPU simulation resources, | |
// This routine will count in tReset_Suspend_Max increments down to a value less than tReset_Suspend_Max wait and then | |
// look every nanosecond to finish. It exists under any circumstance if the chip is reset. | |
waitx = 0; | |
if(delay >= tReset_Suspend_Max) | |
begin | |
num_iterations = delay / tReset_Suspend_Max; | |
for(waitx = 0; waitx < num_iterations; waitx=waitx+1) // check for erase suspend while part is programming | |
begin | |
if(flag_reset_condition) // If chip is reset, exit loop | |
waitx = num_iterations; // Break the loop | |
else | |
#tReset_Suspend_Max; // else delay | |
end | |
end | |
num_iterations = delay % tReset_Suspend_Max; | |
for(waitx = 0; waitx < num_iterations; waitx=waitx+1) // check for erase suspend while part is programming | |
begin | |
if(flag_reset_condition) // check for chip reset while part is programming | |
waitx = num_iterations; | |
else | |
#1; | |
end | |
end | |
endtask | |
/****************************************************************************** | |
The following function returns whether or not the current page is write | |
protected based upon the status register protect bits. | |
******************************************************************************/ | |
function write_protected; | |
input [23:0] byte_address; | |
begin | |
if(status_reg[`WPS]) | |
write_protected = lock_array[lockbit_index(byte_address)]; | |
else | |
begin | |
casez ({status_reg[`SEC], status_reg[`TB],status_reg[`BP2],status_reg[`BP1],status_reg[`BP0]}) | |
5'b??000 : | |
write_protected = 1'b0 ^ status_reg[`CMP]; | |
5'b00001 : | |
begin | |
if(byte_address >= (NUM_PAGES * 63 / 64 * PAGESIZE)) | |
write_protected = 1'b1 ^ status_reg[`CMP]; | |
else | |
write_protected = 1'b0 ^ status_reg[`CMP]; | |
end | |
5'b00010 : | |
begin | |
if(byte_address >= (NUM_PAGES * 31 / 32 * PAGESIZE)) | |
write_protected = 1'b1 ^ status_reg[`CMP]; | |
else | |
write_protected = 1'b0 ^ status_reg[`CMP]; | |
end | |
5'b00011 : | |
begin | |
if(byte_address >= (NUM_PAGES * 15 / 16 * PAGESIZE)) | |
write_protected = 1'b1 ^ status_reg[`CMP]; | |
else | |
write_protected = 1'b0 ^ status_reg[`CMP]; | |
end | |
5'b00100 : | |
begin | |
if(byte_address >= (NUM_PAGES * 7 / 8 * PAGESIZE)) | |
write_protected = 1'b1 ^ status_reg[`CMP]; | |
else | |
write_protected = 1'b0 ^ status_reg[`CMP]; | |
end | |
5'b00101 : | |
begin | |
if(byte_address >= (NUM_PAGES * 3 / 4 * PAGESIZE)) | |
write_protected = 1'b1 ^ status_reg[`CMP]; | |
else | |
write_protected = 1'b0 ^ status_reg[`CMP]; | |
end | |
5'b00110 : | |
begin | |
if(byte_address >= (NUM_PAGES * 1 / 2 * PAGESIZE)) | |
write_protected = 1'b1 ^ status_reg[`CMP]; | |
else | |
write_protected = 1'b0 ^ status_reg[`CMP]; | |
end | |
5'b01001 : | |
begin | |
if(byte_address < (NUM_PAGES * 1 / 64 * PAGESIZE)) | |
write_protected = 1'b1 ^ status_reg[`CMP]; | |
else | |
write_protected = 1'b0 ^ status_reg[`CMP]; | |
end | |
5'b01010 : | |
begin | |
if(byte_address < (NUM_PAGES * 1 / 32 * PAGESIZE)) | |
write_protected = 1'b1 ^ status_reg[`CMP]; | |
else | |
write_protected = 1'b0 ^ status_reg[`CMP]; | |
end | |
5'b01011 : | |
begin | |
if(byte_address < (NUM_PAGES * 1 / 16 * PAGESIZE)) | |
write_protected = 1'b1 ^ status_reg[`CMP]; | |
else | |
write_protected = 1'b0 ^ status_reg[`CMP]; | |
end | |
5'b01100: | |
begin | |
if(byte_address < (NUM_PAGES * 1 / 8 * PAGESIZE)) | |
write_protected = 1'b1 ^ status_reg[`CMP]; | |
else | |
write_protected = 1'b0 ^ status_reg[`CMP]; | |
end | |
5'b01101 : | |
begin | |
if(byte_address < (NUM_PAGES * 1 / 4 * PAGESIZE)) | |
write_protected = 1'b1 ^ status_reg[`CMP]; | |
else | |
write_protected = 1'b0 ^ status_reg[`CMP]; | |
end | |
5'b01110 : | |
begin | |
if(byte_address < (NUM_PAGES * 1 / 2 * PAGESIZE)) | |
write_protected = 1'b1 ^ status_reg[`CMP]; | |
else | |
write_protected = 1'b0 ^ status_reg[`CMP]; | |
end | |
5'b10001 : | |
begin | |
if(byte_address >= (NUM_PAGES * 1023 / 1024 * PAGESIZE)) | |
write_protected = 1'b1 ^ status_reg[`CMP]; | |
else | |
write_protected = 1'b0 ^ status_reg[`CMP]; | |
end | |
5'b10010 : | |
begin | |
if(byte_address >= (NUM_PAGES * 511 / 512 * PAGESIZE)) | |
write_protected = 1'b1 ^ status_reg[`CMP]; | |
else | |
write_protected = 1'b0 ^ status_reg[`CMP]; | |
end | |
5'b10011 : | |
begin | |
if(byte_address >= (NUM_PAGES * 255 / 256 * PAGESIZE)) | |
write_protected = 1'b1 ^ status_reg[`CMP]; | |
else | |
write_protected = 1'b0 ^ status_reg[`CMP]; | |
end | |
5'b1010? : | |
begin | |
if(byte_address >= (NUM_PAGES * 127 / 128 * PAGESIZE)) | |
write_protected = 1'b1 ^ status_reg[`CMP]; | |
else | |
write_protected = 1'b0 ^ status_reg[`CMP]; | |
end | |
5'b11001 : | |
begin | |
if(byte_address < (NUM_PAGES * 1 / 1024 * PAGESIZE)) | |
write_protected = 1'b1 ^ status_reg[`CMP]; | |
else | |
write_protected = 1'b0 ^ status_reg[`CMP]; | |
end | |
5'b11010 : | |
begin | |
if(byte_address < (NUM_PAGES * 1 / 512 * PAGESIZE)) | |
write_protected = 1'b1 ^ status_reg[`CMP]; | |
else | |
write_protected = 1'b0 ^ status_reg[`CMP]; | |
end | |
5'b11011 : | |
begin | |
if(byte_address < (NUM_PAGES * 1 / 256 * PAGESIZE)) | |
write_protected = 1'b1 ^ status_reg[`CMP]; | |
else | |
write_protected = 1'b0 ^ status_reg[`CMP]; | |
end | |
5'b1110? : | |
begin | |
if(byte_address < (NUM_PAGES * 1 / 128 * PAGESIZE)) | |
write_protected = 1'b1 ^ status_reg[`CMP]; | |
else | |
write_protected = 1'b0 ^ status_reg[`CMP]; | |
end | |
5'b??111 : | |
begin | |
write_protected = 1'b1 ^ status_reg[`CMP]; | |
end | |
endcase | |
end | |
end | |
endfunction | |
/****************************************************************************** | |
The following function returns whether passed command is valid in QPI mode. | |
******************************************************************************/ | |
function is_qpi; | |
input [7:0] cmd_byte; | |
begin | |
if(flag_qpi_mode == 1) | |
begin | |
case (cmd_byte) // check command byte for being valid in QPI mode. | |
`CMD_WRITE_ENABLE, `CMD_WRITE_ENABLE_VSR, `CMD_WRITE_DISABLE, `CMD_READ_STATUS, | |
`CMD_READ_STATUS2, `CMD_READ_STATUS3, `CMD_WRITE_STATUS, `CMD_WRITE_STATUS2, `CMD_WRITE_STATUS3, `CMD_PAGE_PROGRAM, | |
`CMD_INDIVIDUAL_LOCK, `CMD_INDIVIDUAL_UNLOCK, `CMD_READ_BLOCK_LOCK, `CMD_GLOBAL_BLOCK_LOCK, `CMD_GLOBAL_BLOCK_UNLOCK, | |
`CMD_SECTOR_ERASE,`CMD_HALF_BLOCK_ERASE, `CMD_BLOCK_ERASE, `CMD_BULK_ERASE, `CMD_BULK_ERASE2, | |
`CMD_SUSPEND, `CMD_RESUME, `CMD_DEEP_POWERDOWN, `CMD_SET_READ_PARAM, `CMD_READ_DATA_FAST, | |
`CMD_READ_DATA_FAST_WRAP, `CMD_READ_DATA_FAST_QUAD_IO, `CMD_READ_SIGNATURE, `CMD_READ_ID, `CMD_READ_JEDEC_ID, | |
`CMD_READ_UNIQUE_ID, `CMD_DISABLE_QPI, `CMD_ENABLE_RESET, `CMD_CHIP_RESET, `CMD_READ_DATA_FAST_DTR_WRAP, | |
`CMD_READ_DATA_FAST_DTR, `CMD_READ_DATA_FAST_QUAD_IO_DTR : | |
begin | |
is_qpi = 1; | |
end | |
default : | |
begin | |
is_qpi = 0; | |
$display("Invalid Opcode for QPI mode. (%0h)",cmd_byte); | |
end | |
endcase | |
end | |
else | |
is_qpi = 1; | |
end | |
endfunction | |
/****************************************************************************** | |
The following function returns the lockbit index based on the passed byte address | |
The array is indexed with BLOCK 0 sectors, then last Block sectors, | |
then block 1 - 510 | |
******************************************************************************/ | |
function integer lockbit_index; | |
input [23:0] byte_address; | |
begin | |
if((byte_address[23:16] == 0) || (byte_address[23:16] == (NUM_BLOCKS-1))) | |
begin | |
if(byte_address[23:16] == 0) | |
lockbit_index = byte_address[15:0] / SECTORSIZE; | |
else | |
lockbit_index = (byte_address[15:0] / SECTORSIZE) + 16; | |
end | |
else | |
lockbit_index = byte_address[23:16] - 1 + 32; | |
end | |
endfunction | |
/****************************************************************************** | |
****************************************************************************** | |
****************************************************************************** | |
COMMAND PROTOCOL HANDLERS | |
The following functions execute the command protocol for the selected function | |
before enabling the internal state machine to handle the function | |
****************************************************************************** | |
****************************************************************************** | |
******************************************************************************/ | |
/****************************************************************************** | |
READ/WRITE PAGE PROTOCOL HANDLERS | |
******************************************************************************/ | |
/****************************************************************************** | |
The following routine will execute the Read Page command 03h, | |
Read Page Fast command 0bh, Read Page Fast Dual 3bh and Read Page Fast Quad 6b. | |
and Read Security Registers 48h and SFDP Read 5Ah | |
******************************************************************************/ | |
task read_page; | |
input [1:0] fast_read; // 0 = normal, 2 = dual, 3 = quad | |
input [1:0] mem_read; // 0 = main array, 1 = Security Pages, 2 = SFDP | |
integer x; | |
begin | |
if(!status_reg[`WIP]) | |
begin | |
input_byte(byte_address[23:16]); | |
input_byte(byte_address[15:8]); | |
input_byte(byte_address[7:0]); | |
if(fast_read) | |
begin | |
if(flag_qpi_mode == 1) | |
begin | |
for(x = 0; x <= read_param_reg[5:4];x = x + 1) | |
input_byte(null_reg); | |
end | |
else | |
input_byte(null_reg); | |
end | |
if(mem_read) // SFDP Read and Security Register Read, input 8 dummy bytes. | |
input_byte(null_reg); | |
forever | |
begin | |
if(mem_read == 1) // If we're reading a security sector, process output here. | |
begin | |
case(byte_address[23:8]) | |
16'h10 : | |
output_byte(secsi[byte_address[7:0]]); | |
16'h20 : | |
output_byte(secsi[byte_address[7:0]+PAGESIZE]); | |
16'h30 : | |
output_byte(secsi[byte_address[7:0]+(2*PAGESIZE)]); | |
default : | |
begin | |
$display("Invalid Security Page Address (%x)",byte_address); | |
$stop; | |
end | |
endcase | |
end | |
else if(mem_read == 2) //If we're reading SFDP, process output here. | |
begin | |
if(byte_address[23:8] == 0) | |
output_byte(sfdp[byte_address[7:0]]); | |
else | |
begin | |
$display("Invalid SFDP Page Address (%x)", byte_address); | |
$stop; | |
end | |
end | |
else // Main Array Reads | |
begin | |
byte_address = byte_address & ADDRESS_MASK; | |
if(fast_read == 2) | |
output_byte_dual(memory[byte_address]); | |
else if(fast_read == 3) | |
output_byte_quad(memory[byte_address]); | |
else | |
output_byte(memory[byte_address]); | |
end | |
// If security register or SFDP, wrap at 256 byte boundry. Only allow to read page. | |
if(mem_read) | |
byte_address[7:0] = byte_address[7:0] + 1; | |
else | |
byte_address = byte_address + 1; | |
end | |
end | |
end | |
endtask | |
/****************************************************************************** | |
The following routine will execute the Read Page Fast Dual IO command bbh | |
******************************************************************************/ | |
task read_page_dualio; | |
begin | |
if(!status_reg[`WIP]) | |
begin | |
input_byte_dual(byte_address[23:16]); | |
input_byte_dual(byte_address[15:8]); | |
input_byte_dual(byte_address[7:0]); | |
input_mode_dual(mode_reg[7:2]); // Ensure that mode_reg is setup on posedge clock 22. | |
get_posclk_holdn; // Get dummy last clock. | |
forever | |
begin | |
byte_address = byte_address & ADDRESS_MASK; | |
output_byte_dual(memory[byte_address]); | |
byte_address = byte_address + 1; | |
end | |
end | |
end | |
endtask | |
/****************************************************************************** | |
The following routine will execute the Read Page Fast Quad IO command ebh and 0ch | |
For the following commands, fast_read == the command being sent. | |
`define CMD_READ_DATA_FAST_QUAD_IO 8'heb | |
******************************************************************************/ | |
task read_page_quadio; | |
input [7:0] cmd; | |
integer x; | |
begin | |
input_byte_quad(byte_address[23:16]); | |
input_byte_quad(byte_address[15:8]); | |
input_byte_quad(byte_address[7:0]); | |
if(!status_reg[`WIP]) | |
begin | |
case (cmd) | |
`CMD_READ_DATA_FAST_QUAD_IO : | |
begin | |
input_byte_quad(mode_reg[7:0]); | |
if(flag_qpi_mode == 1) | |
begin | |
for(x = 1; x <= read_param_reg[5:4];x = x + 1) | |
input_byte_quad(null_reg); | |
end | |
else | |
begin | |
input_byte_quad(null_reg); | |
input_byte_quad(null_reg); | |
end | |
end | |
`CMD_READ_DATA_FAST_WRAP : | |
begin | |
for(x = 0; x <= read_param_reg[5:4];x = x + 1) | |
input_byte_quad(null_reg); | |
end | |
endcase | |
forever | |
begin | |
byte_address = byte_address & ADDRESS_MASK; | |
output_byte_quad(memory[byte_address]); | |
if(cmd == `CMD_READ_DATA_FAST_WRAP) | |
begin | |
case ({read_param_reg[1],read_param_reg[0]}) | |
2'b00 : | |
byte_address[2:0] = byte_address[2:0] + 1; | |
2'b01 : | |
byte_address[3:0] = byte_address[3:0] + 1; | |
2'b10 : | |
byte_address[4:0] = byte_address[4:0] + 1; | |
2'b11 : | |
byte_address[5:0] = byte_address[5:0] + 1; | |
endcase | |
end | |
else | |
if(!wrap_reg[4] && ( cmd == `CMD_READ_DATA_FAST_QUAD_IO )) | |
begin | |
case ({wrap_reg[6],wrap_reg[5]}) | |
2'b00 : | |
byte_address[2:0] = byte_address[2:0] + 1; | |
2'b01 : | |
byte_address[3:0] = byte_address[3:0] + 1; | |
2'b10 : | |
byte_address[4:0] = byte_address[4:0] + 1; | |
2'b11 : | |
byte_address[5:0] = byte_address[5:0] + 1; | |
endcase | |
end | |
else | |
byte_address = byte_address + 1; | |
end | |
end | |
end | |
endtask | |
/****************************************************************************** | |
The following routine will execute the Write to Page command 02h. | |
******************************************************************************/ | |
task write_page; | |
input quadio; | |
integer x; | |
integer address; | |
begin | |
if(!status_reg[`WIP]) | |
begin | |
input_byte(prog_byte_address[23:16]); | |
input_byte(prog_byte_address[15:8]); | |
input_byte(prog_byte_address[7:0]); | |
prog_byte_address = prog_byte_address & ADDRESS_MASK; | |
if(!write_protected(prog_byte_address)) | |
fill_page_latch(quadio,prog_byte_address,0); | |
end | |
end | |
endtask | |
/****************************************************************************** | |
The following routine will fill the page_latch with input data in either regular or quad io. | |
******************************************************************************/ | |
task fill_page_latch; | |
input quadio; | |
input [23:0] prog_address; | |
input flag_secsi; | |
integer x; | |
integer address; | |
begin | |
// Move memory page into page latch | |
if(flag_secsi) | |
begin | |
address = (prog_address >> 4) - 23'h100; | |
address[7:0] = 0; | |
end | |
else | |
begin | |
address = prog_address; | |
address[7:0] = 0; | |
end | |
for(x = 0; x < PAGESIZE; x=x+1) | |
page_latch[x] = flag_secsi ? secsi[address+x] : memory[address+x]; | |
// Now update page latch with input data and signal a page_program operation | |
forever | |
begin | |
if(quadio) | |
input_byte_quad(temp); | |
else | |
input_byte(temp); | |
page_latch[prog_address[7:0]] = temp; | |
if(flag_secsi) | |
flag_prog_secsi_page = 1; | |
else | |
flag_prog_page = 1; | |
prog_address[7:0] = prog_address[7:0] + 1; | |
end | |
end | |
endtask | |
//////////////////////////////////////////////////////////////////////////////// | |
// This routine dumps the main memory array to the MEM_FILENAME file. | |
// | |
task dump_mem; | |
integer x; | |
integer file; | |
begin | |
file = $fopen(`MEM_FILENAME); | |
$fwrite(file,"/* Contents of Memory Array starting from address 0. This is a standard Verilog readmemh format. */"); | |
// Erase memory array to FFh state. | |
for(x = 0; x < (NUM_PAGES * PAGESIZE); x=x+1) | |
begin | |
if(x % 16) | |
$fwrite(file,"%h ", memory[x]); | |
else | |
$fwrite(file,"\n%h ", memory[x]); | |
end | |
$fclose(file); | |
end | |
endtask | |
/****************************************************************************** | |
****************************************************************************** | |
****************************************************************************** | |
COMMAND STATE MACHINES | |
****************************************************************************** | |
******************************************************************************/ | |
/****************************************************************************** | |
The following routine occurs when flag_set_read_param goes high. | |
This starts the main process for the set read param process. | |
******************************************************************************/ | |
always @(posedge flag_set_read_param) | |
begin :set_read_param | |
@(posedge CSn); | |
if(flag_set_read_param == 1) | |
begin | |
read_param_reg = read_param_reg_shadow; | |
end | |
flag_set_read_param = 0; | |
end | |
/****************************************************************************** | |
The following routine occurs when flag_reset goes high. | |
This starts the main process for the reset process. | |
******************************************************************************/ | |
always @(posedge flag_reset) | |
begin :reset | |
@(posedge CSn); | |
if((flag_reset == 1) && (flag_write_status_reg == 0)) // Execute command, except if within a write status register command. | |
begin | |
status_reg[`WIP] = 1; | |
flag_reset_condition = 1; | |
#tRES1; | |
chip_reset(); | |
end | |
flag_reset = 0; | |
end | |
/****************************************************************************** | |
POWER UP/DOWN STATE MACHINES | |
******************************************************************************/ | |
/****************************************************************************** | |
The following routine occurs when flag_power_up_exec goes high. | |
This starts the main process for the power up process. | |
******************************************************************************/ | |
always @(posedge flag_power_up_exec) | |
begin :power_up | |
@(posedge CSn); | |
if(flag_power_up_exec == 1) | |
begin | |
if(flag_power_up_sig_read == 1) | |
#tRES2; | |
else | |
#tRES1; | |
flag_power_down = 0; | |
flag_power_up_exec = 0; | |
flag_power_up_sig_read = 0; | |
flag_suspend = 0; | |
end | |
end | |
/****************************************************************************** | |
The following routine occurs when flag_power_down_exec goes high. | |
This starts the main process for the power down process. | |
******************************************************************************/ | |
always @(posedge flag_power_down) | |
begin :power_down | |
@(posedge CSn); | |
if(flag_power_down == 1) | |
begin | |
#tDP; | |
end | |
end | |
/****************************************************************************** | |
ERASE PAGE/BLOCK COMMAND STATE MACHINES | |
******************************************************************************/ | |
/****************************************************************************** | |
The following routine occurs when flag_suspend goes high. | |
This starts the main erase process for the handling erase suspend. | |
******************************************************************************/ | |
always @(posedge flag_suspend) | |
begin :erase_suspend | |
@(posedge CSn); // Wait for CSn to go high | |
if(flag_suspend == 1) | |
begin | |
status_reg[`SUS] = 1; | |
wait_reset(tSUS); | |
flag_suspend_enabled = 1'b1; | |
status_reg[`WIP] = 0; | |
status_reg[`WEL] = 0; | |
end | |
end | |
/****************************************************************************** | |
The following routine occurs when flag_resume goes high. | |
This starts the main erase process for the handling erase resume. | |
******************************************************************************/ | |
always @(posedge flag_resume) | |
begin :erase_resume | |
@(posedge CSn); // Wait for CSn to go high | |
if(flag_resume == 1) | |
begin | |
status_reg[`SUS] = 0; | |
flag_suspend_enabled = 1'b0; | |
flag_suspend = 1'b0; | |
flag_resume = 1'b0; | |
status_reg[`WEL] = 1; | |
status_reg[`WIP] = 1; | |
end | |
end | |
/****************************************************************************** | |
The following routine occurs when flag_erase_sector goes high. | |
This starts the main erase process for the defined sector. | |
******************************************************************************/ | |
always @(posedge flag_erase_sector) // When flag_erase_sector goes high, device becomes active | |
// and starts erasing the sector defined by byte_address | |
begin :erase_sector | |
integer x; | |
@(posedge CSn); // Wait for CSn to go high | |
if(flag_erase_sector == 1) | |
begin | |
status_reg[`WIP] = 1; | |
wait_reset_suspend(tSE); | |
for(x = 0; x < SECTORSIZE; x=x+1) | |
memory[(byte_address[23:12] * SECTORSIZE) + x] = 8'hff; | |
status_reg[`WIP] = 0; | |
status_reg[`WEL] = 0; | |
end | |
flag_erase_sector = 0; | |
end | |
/****************************************************************************** | |
The following routine occurs when flag_erase_secsi_sector goes high. | |
This starts the main erase process for the defined secsi sector. | |
******************************************************************************/ | |
always @(posedge flag_erase_secsi_sector) // When flag_erase_secsi_sector goes high, device becomes active | |
// and starts erasing the sector defined by byte_address | |
begin :erase_secsi_sector | |
integer x; | |
@(posedge CSn); // Wait for CSn to go high | |
if(flag_erase_secsi_sector == 1) | |
begin | |
status_reg[`WIP] = 1; | |
case(byte_address[23:8]) | |
16'h10 : | |
begin | |
wait_reset_suspend(tSE); | |
for(x = 0; x < PAGESIZE; x=x+1) | |
secsi[x] = 8'hff; | |
end | |
16'h20 : | |
begin | |
wait_reset_suspend(tSE); | |
for(x = 0; x < PAGESIZE; x=x+1) | |
secsi[x+PAGESIZE] = 8'hff; | |
end | |
16'h30 : | |
begin | |
wait_reset_suspend(tSE); | |
for(x = 0; x < PAGESIZE; x=x+1) | |
secsi[x+(PAGESIZE * 2)] = 8'hff; | |
end | |
default : | |
begin | |
$display("Invalid Security Page Erase Address (%x)",byte_address); | |
$stop; | |
end | |
endcase | |
status_reg[`WIP] = 0; | |
status_reg[`WEL] = 0; | |
end | |
flag_erase_secsi_sector = 0; | |
end | |
/****************************************************************************** | |
The following routine occurs when flag_erase_block goes high. | |
This starts the main erase process for the defined block. | |
******************************************************************************/ | |
always @(posedge flag_erase_block) // When flag_erase_block goes high, device becomes active | |
// and starts erasing the block defined by byte_address | |
begin :erase_block | |
integer x; | |
@(posedge CSn); // Wait for CSn to go high | |
if(flag_erase_block == 1) | |
begin | |
status_reg[`WIP] = 1; | |
wait_reset_suspend(tBE2); | |
for(x = 0; x < BLOCKSIZE; x=x+1) | |
memory[(byte_address[23:16] * BLOCKSIZE) + x] = 8'hff; | |
status_reg[`WIP] = 0; | |
status_reg[`WEL] = 0; | |
end | |
flag_erase_block = 0; | |
end | |
/****************************************************************************** | |
The following routine occurs when flag_erase_half_block goes high. | |
This starts the main erase process for the defined block. | |
******************************************************************************/ | |
always @(posedge flag_erase_half_block) // When flag_erase_block goes high, device becomes active | |
// and starts erasing the block defined by byte_address | |
begin :erase_half_block | |
integer x; | |
@(posedge CSn); // Wait for CSn to go high | |
if(flag_erase_half_block == 1) | |
begin | |
status_reg[`WIP] = 1; | |
wait_reset_suspend(tBE1); | |
for(x = 0; x < HALFBLOCKSIZE; x=x+1) | |
memory[(byte_address[23:15] * HALFBLOCKSIZE) + x] = 8'hff; | |
status_reg[`WIP] = 0; | |
status_reg[`WEL] = 0; | |
end | |
flag_erase_half_block = 0; | |
end | |
/****************************************************************************** | |
The following routine occurs when flag_erase_bulk goes high. | |
This starts the main erase process for the entire chip. | |
******************************************************************************/ | |
always @(posedge flag_erase_bulk) // When flag_erase_block goes high, device becomes active | |
// and starts erasing the block defined by page_address | |
begin :erase_bulk | |
integer x; | |
@(posedge CSn); // Wait for CSn to go high | |
if(flag_erase_bulk == 1) | |
begin | |
status_reg[`WIP] = 1; | |
for(x = 0; x < 40; x=x+1) | |
wait_reset(tCE_40); | |
for(x = 0; x < PAGESIZE * NUM_PAGES; x=x+1) | |
memory[x] = 8'hff; | |
status_reg[`WIP] = 0; | |
status_reg[`WEL] = 0; | |
end | |
flag_erase_bulk = 0; | |
end | |
/****************************************************************************** | |
PROGRAMMING PAGE COMMAND STATE MACHINES | |
******************************************************************************/ | |
/****************************************************************************** | |
The following routine occurs when flag_prog_page goes high. | |
This starts the program page process. | |
******************************************************************************/ | |
always @(posedge flag_prog_page) // When flag_prog_page goes high, device becomes active | |
// and starts programming the page defined by page_address | |
begin :program_to_page | |
integer x; // Local loop variable only to be used here. | |
@(posedge CSn); // Wait for CSn to go high | |
begin | |
status_reg[`WIP] = 1; | |
prog_byte_address[7:0] = 0; | |
for(x = 0; x < PAGESIZE; x=x+1) | |
begin | |
memory[prog_byte_address+x] = page_latch[x] & memory[prog_byte_address+x]; | |
wait_reset_suspend(tPP / PAGESIZE); | |
end | |
status_reg[`WIP] = 0; | |
status_reg[`WEL] = 0; | |
end | |
flag_prog_page = 0; | |
end | |
/****************************************************************************** | |
The following routine occurs when flag_prog_secsi_page goes high. | |
This starts the program secsi page process. | |
******************************************************************************/ | |
always @(posedge flag_prog_secsi_page) // When flag_prog_page goes high, device becomes active | |
// and starts programming the page defined by page_address | |
begin :program_to_secsi_page | |
integer x; // Local loop variable only to be used here. | |
@(posedge CSn); // Wait for CSn to go high | |
begin | |
status_reg[`WIP] = 1; | |
prog_byte_address[7:0] = 0; | |
case(prog_byte_address[23:8]) | |
16'h10 : | |
begin | |
for(x = 0; x < PAGESIZE; x=x+1) | |
begin | |
secsi[x] = page_latch[x] & secsi[x]; | |
wait_reset_suspend(tPP / PAGESIZE); | |
end | |
end | |
16'h20 : | |
begin | |
for(x = 0; x < PAGESIZE; x=x+1) | |
begin | |
secsi[x+PAGESIZE] = page_latch[x] & secsi[x+PAGESIZE]; | |
wait_reset_suspend(tPP / PAGESIZE); | |
end | |
end | |
16'h30 : | |
begin | |
for(x = 0; x < PAGESIZE; x=x+1) | |
begin | |
secsi[x+(PAGESIZE*2)] = page_latch[x] & secsi[x+(PAGESIZE*2)]; | |
wait_reset_suspend(tPP / PAGESIZE); | |
end | |
end | |
default : | |
begin | |
$display("Invalid Security Page Program Address (%x)",prog_byte_address); | |
$stop; | |
end | |
endcase | |
status_reg[`WIP] = 0; | |
status_reg[`WEL] = 0; | |
end | |
flag_prog_secsi_page = 0; | |
end | |
/****************************************************************************** | |
CONFIGURATION REGISTER COMMAND STATE MACHINES | |
******************************************************************************/ | |
/****************************************************************************** | |
The following routine occurs when flag_write_status_reg goes high. | |
This starts the main write status register process. | |
******************************************************************************/ | |
always @(posedge flag_write_status_reg) // When flag_write_status_reg goes high, device becomes active | |
// and starts writing the status_reg_shadow register into the | |
begin :write_status_reg | |
@(posedge CSn); // Wait for CSn to go high | |
if(flag_write_status_reg == 1) | |
begin | |
status_reg[`WIP] = 1; | |
status_reg[`QE] = status_reg_shadow[`QE]; | |
status_reg[`SRL] = status_reg_shadow[`SRL]; | |
status_reg[`SRP] = status_reg_shadow[`SRP]; | |
status_reg[`BP0] = status_reg_shadow[`BP0]; | |
status_reg[`BP1] = status_reg_shadow[`BP1]; | |
status_reg[`BP2] = status_reg_shadow[`BP2]; | |
status_reg[`SEC] = status_reg_shadow[`SEC]; | |
status_reg[`TB] = status_reg_shadow[`TB]; | |
status_reg[`CMP] = status_reg_shadow[`CMP]; | |
status_reg[`WPS] = status_reg_shadow[`WPS]; | |
status_reg[`DRV0] = status_reg_shadow[`DRV0]; | |
status_reg[`DRV1] = status_reg_shadow[`DRV1]; | |
status_reg[`HLD_RST] = status_reg_shadow[`HLD_RST]; | |
// One time program OTP bits to 1. | |
status_reg[`LB1] = status_reg[`LB1] | status_reg_shadow[`LB1]; | |
status_reg[`LB2] = status_reg[`LB2] | status_reg_shadow[`LB2]; | |
status_reg[`LB3] = status_reg[`LB3] | status_reg_shadow[`LB3]; | |
if(!flag_volatile_sr_write) | |
begin | |
// Now set OTP bits | |
status_reg_otp[`QE] = status_reg_shadow[`QE]; | |
status_reg_otp[`SRL] = status_reg_shadow[`SRL]; | |
status_reg_otp[`SRP] = status_reg_shadow[`SRP]; | |
status_reg_otp[`BP0] = status_reg_shadow[`BP0]; | |
status_reg_otp[`BP1] = status_reg_shadow[`BP1]; | |
status_reg_otp[`BP2] = status_reg_shadow[`BP2]; | |
status_reg_otp[`SEC] = status_reg_shadow[`SEC]; | |
status_reg_otp[`TB] = status_reg_shadow[`TB]; | |
status_reg_otp[`CMP] = status_reg_shadow[`CMP]; | |
status_reg_otp[`WPS] = status_reg_shadow[`WPS]; | |
status_reg_otp[`DRV0] = status_reg_shadow[`DRV0]; | |
status_reg_otp[`DRV1] = status_reg_shadow[`DRV1]; | |
status_reg_otp[`HLD_RST] = status_reg_shadow[`HLD_RST]; | |
// One time program OTP bits to 1. | |
status_reg_otp[`LB1] = status_reg[`LB1] | status_reg_shadow[`LB1]; | |
status_reg_otp[`LB2] = status_reg[`LB2] | status_reg_shadow[`LB2]; | |
status_reg_otp[`LB3] = status_reg[`LB3] | status_reg_shadow[`LB3]; | |
end | |
// If QE bit got reset, make sure QPI mode is disabled. | |
if(status_reg[`QE] == 0) | |
flag_qpi_mode = 0; | |
if(status_reg[`WEL]) | |
wait_reset(tW); | |
status_reg[`WIP] = 0; | |
status_reg[`WEL] = 0; | |
flag_volatile_sr_write = 0; | |
flag_write_status_reg = 0; | |
end | |
end | |
endmodule // W25Q32JVxxIM |