blob: 895906aac697c6fb7575a39096ba730b0ac3ec73 [file] [log] [blame]
/*
---------------------------------------------------------------------------
Proprietary & Confidental, All right reserved, 2013
Silicon Storage Technology, Inc.
---------------------------------------------------------------------------
Description : SQI Serial Flash Memory, SST26WF080B 1MB Memory
Revision Status : 1.0
---------------------------------------------------------------------------
Important Notes Please Read
---------------------------
(1) This model has several non-volatile memorys.
These bits are initalized at time 0 to the default values. To
load these memorys with customer data load them after time 0;
The below example loads the main flash memory from a file.
Example: initial #1 $readmemh(<path>.I0.memory,<file name>); // 080=1M, 016=2M, 032=4M, 064=8M bytes memory
(2) This model has several non-volatial bits in the status registers. If you want them
set to a different initial condition then edit this model defparam commands to
change the values.
(3) For simulation purposes the erase times can be shortened using the 'defparam' command
do not shorten to less than 1uS
Example: defparam <path>.I0.Tbe = 25_000; // reduce block erase time from 25mS to 25uS
Example: defparam <path>.I0.Tse = 25_000; // reduce sector erase time from 25mS to 25uS
Example: defparam <path>.I0.Tsce = 25_000; // reduce chip erase time from 50mS to 25uS
Example: defparam <path>.I0.Tpp = 25_000; // reduce page program time from 1.5mS to 25uS
Example: defparam <path>.I0.Tws = 1000; // reduce suspend to ready time from 10uS to 1uS
(4) On timing errors all of flash memory will be set to 'xx', timming errors are a fatal problem.
(5) Parameter MASK_ERRORS is used to inhibit corruption of memory on timing errors at the start of simulation.
This allows the unknows to be cleared before corrupting memory on timing checks. The default value is 500nS.
To change this value use defparam to increase it to the value you need.
Example: defparam <path>.MASK_ERRORS = 1000; // increase time to 1uS
(6) This model uses a common module for all the SST26VF***B family of products. This
module is appended to the top level module code. The module name is "sst26wfxxxb".
If more than 1 instance of the SST26VF*B family of products is placed in a design
there will be a syntact error that module sst26wfxxxb is defined more than once.
if this happens delete the sst26wfxx module definitions from all but one of the Verilog models.
(7) The below code is the definition of the non-volatile memory used in the chip.
//---------------------------------------------------------------------------
// Define non-volatile Memory
//---------------------------------------------------------------------------
reg [7:0] memory[Memsize-1:0]; // define Flash memory array, non-volitable
reg [7:0] security_id[(Kilo*2)-1:0]; // Define secutity ID memory addr 0->7 are SST space the rest is user space, non-volitable
reg [7:0] SFDP[(Kilo*2)-1:0]; // serial flash discoverable parameters
reg [PROTECT_REG_MSB:0] wlldr_mem; // Define write-lock lock down reg, non-volitable
reg WPEN; // bit 7 of config register, non-volitable
reg SEC; // status[5] 1-bit Lockout Security ID, non-volitable
------------------------------------------------------------------------------------
Pin Descriptions
Symbol Pin Name Type Function
------- ------------ ------ -------------------------------------------
SCK Serial Clock input Provides clock to device
SIO[3:0] Serial Data I/O provide data input and output
CEb Chipenable input Active low chip enable
------------------------------------------------------------------------------------
*/
`timescale 1ns / 10 ps
module sst26wf080b(SCK,SIO,CEb);
input SCK; // device clock
input CEb; // chip enable active low
inout [3:0] SIO; // serial 4-bit bus I/O
reg [31:0] error_cnt; // count timing errors
parameter MASK_ERRORS=500; // mask any timing errors before this time
defparam I0.Ksize = 0; //Size of memory in Kilo bytes
defparam I0.Msize = 1; // Size of memory in Mega bites. I.E. 8-bit field size, use S080=1, S016=2, S032=4
defparam I0.ADDR_MSB=19; // most significant address bit, 32Mb=21,16Mb=20, 8Mb=19, 4Mb=18;
defparam I0.Memory_Capacity = 8'h58; // ID read memory size 32M=52,16M=51,8M=58, JEDEC read value
// change below values if you need non-default values on POR
defparam I0.WLLD_value = 32'h0000_0000; // init WLLD protection register non-volitale, default to all 0's
defparam I0.INIT_WPEN = 1'b0; // value of WPEN, configuration register bit 7 default on POR
defparam I0.SECURITY_LOCKOUT_VALUE=1'b0; // Value of SEC, status register bit 5 on POR
//--------------------------------------------------
// Place common sst26wfxxxB model
//--------------------------------------------------
sst26wfxxxb I0(SCK,SIO,CEb);
//--------------------------------------------------
// Timing checks
//--------------------------------------------------
wire HOLDb;
wire read_slow_flag;
wire read_dual_io_flag;
wire [3:0] Tds_inhibit;
reg notifer_Tsckh,notifer_Tsckl,notifier_Tces,notifier_Tceh,notifier_Tchs,notifier_Tchh;
reg notifier_Tcph,notifier_Tds;
reg notifier_Fclk;
reg notifier_Thls,notifier_Thhs,notifier_Thhh;
assign HOLDb = I0.HOLDb; // bring up lower level holdb net
assign read_slow_flag = I0.read_slow_flag; // slow timing on SPI read flag I.E. Read cmd '03h'
assign read_dual_io_flag = I0.SPI_SDIOR_active; // slow timing on SPI dual I/O read, i.e. cmd 'BBh'
assign Tds_inhibit[3] = I0.SIO_IO[3] | CEb; // i/o = input and CEb active
assign Tds_inhibit[2] = I0.SIO_IO[2] | CEb; // i/o = input and CEb active
assign Tds_inhibit[1] = I0.SIO_IO[1] | CEb; // i/o = input and CEb active
assign Tds_inhibit[0] = I0.SIO_IO[0] | CEb; // i/o = input and CEb active
specify
specparam CellType = "SST26WFxxx";
specparam Fclk_slow = 24.99; // Min serial clock period during '03h' Read command
specparam Tsckh_slow = 11.0; // Min serial clock high time 'SCK' during '03h' Read command
specparam Tsckl_slow = 11.0; // Min serial clock low time 'SCK' during '03h' Read command
specparam Fclk_dual_io = 12.49; // Min serial clock period during 'BBh' Read command
specparam Tsckh_dual_io = 5.5; // Min serial clock high time 'SCK' during 'BBh' Read command
specparam Tsckl_dual_io = 5.5; // Min serial clock low time 'SCK' during 'BBh' Read command
specparam Fclk = 9.59; // Min serial clock period
specparam Tsckh = 4.5; // Min serial clock high time 'SCK'
specparam Tsckl = 4.5; // Min serial clock low time 'SCK'
specparam Tces = 5; // CEb falling to SCK rising setup time
specparam Tceh = 5; // SCK rising to CEb rising hold time
specparam Tchs = 5; // CEb not active setup time
specparam Tchh = 5; // CEb not active hold time
specparam Tcph = 12.0; // Min CEb high time
specparam Tds = 3; // Data in setup time to SCK rising
specparam Tdh = 4; // Data in hold time to SCK rising
specparam Thls = 5.0; // HOLDb falling to SCK rising setup
specparam Thhs = 5.0; // HOLDb rising to SCK risinf setup
specparam Thhh = 5.0; // SCK to HOLDb hold time
// HOLDb timing tests
$setup(posedge SCK ,negedge HOLDb,Thls, notifier_Thls);
$setup(posedge SCK ,posedge HOLDb,Thhs, notifier_Thhs);
$hold (posedge SCK ,negedge HOLDb,Thhh, notifier_Thhh);
// 40Mhz, slow speed read timing checks opcode Read '03h' in SPI mode
$period(posedge SCK &&& read_slow_flag==1'b1 ,Fclk_slow,notifier_Fclk);
$period(negedge SCK &&& read_slow_flag==1'b1 ,Fclk_slow,notifier_Fclk);
$width(negedge SCK &&& read_slow_flag==1'b1,Tsckh_slow,0,notifer_Tsckh);
$width(posedge SCK &&& read_slow_flag==1'b1,Tsckl_slow,0,notifer_Tsckl);
// 80Mhz, read timing checks opcode Read 'BBh' in SPI mode
$period(posedge SCK &&& read_dual_io_flag==1'b1 ,Fclk_dual_io,notifier_Fclk);
$period(negedge SCK &&& read_dual_io_flag==1'b1 ,Fclk_dual_io,notifier_Fclk);
$width(negedge SCK &&& read_dual_io_flag==1'b1,Tsckh_dual_io,0,notifer_Tsckh);
$width(posedge SCK &&& read_dual_io_flag==1'b1,Tsckl_dual_io,0,notifer_Tsckl);
// 104 Mhz timing
$period(posedge SCK &&& CEb==1'b0 ,Fclk,notifier_Fclk);
$period(negedge SCK &&& CEb==1'b0 ,Fclk,notifier_Fclk);
$width(negedge SCK,Tsckh,0,notifer_Tsckh);
$width(posedge SCK,Tsckl,0,notifer_Tsckl);
$setup(negedge CEb,posedge SCK,Tces, notifier_Tces);
$setup(negedge CEb,posedge SCK,Tchh, notifier_Tchh);
$hold (posedge SCK,posedge CEb,Tceh, notifier_Tceh);
$setup(posedge SCK,posedge CEb,Tchs, notifier_Tchs);
$width(posedge CEb,Tcph,0,notifier_Tcph);
$setuphold(posedge SCK &&& Tds_inhibit[3]==1'b0, SIO[3],Tds,Tdh,notifier_Tds);
$setuphold(posedge SCK &&& Tds_inhibit[2]==1'b0, SIO[2],Tds,Tdh,notifier_Tds);
$setuphold(posedge SCK &&& Tds_inhibit[1]==1'b0, SIO[1],Tds,Tdh,notifier_Tds);
$setuphold(posedge SCK &&& Tds_inhibit[0]==1'b0, SIO[0],Tds,Tdh,notifier_Tds);
endspecify
always @(notifier_Thls or notifier_Thhs or notifier_Thhh) begin
if($realtime > MASK_ERRORS ) begin
$display("\t%m Fatal Timing Error for SIO[3] HOLDb timing to SCK rising time=%0.2f",$realtime);
corrupt_all; // corrupt memory
end
end
always @(notifier_Fclk) begin
if($realtime > MASK_ERRORS ) begin
$display("\t%m Fatal Timing Error Fclk time=%0.2f",$realtime);
corrupt_all; // corrupt memory
end
end
always @(notifier_Tds) begin
if($realtime > MASK_ERRORS ) begin
$display("\t%m Fatal Timing Error Tds/Tdh time=%0.2f",$realtime);
corrupt_all; // corrupt memory
end
end
always @(notifier_Tcph) begin
if($realtime > MASK_ERRORS ) begin
$display("\t%m Fatal Timing Error Tcph time=%0.2f",$realtime);
corrupt_all; // corrupt memory
end
end
always @(notifier_Tchh) begin
if($realtime > MASK_ERRORS ) begin
$display("\t%m Fatal Timing Error Tchh time=%0.2f",$realtime);
corrupt_all; // corrupt memory
end
end
always @(notifier_Tchs) begin
if($realtime > MASK_ERRORS ) begin
$display("\t%m Fatal Timing Error Tchs time=%0.2f",$realtime);
corrupt_all; // corrupt memory
end
end
always @(notifier_Tceh) begin
if($realtime > MASK_ERRORS ) begin
$display("\t%m Fatal Timing Error Tceh time=%0.2f",$realtime);
corrupt_all; // corrupt memory
end
end
always @(notifier_Tces) begin
if($realtime > MASK_ERRORS ) begin
$display("\t%m Fatal Timing Error Tces time=%0.2f",$realtime);
corrupt_all; // corrupt memory
end
end
always @( notifer_Tsckh or notifer_Tsckl) begin
if($realtime > MASK_ERRORS ) begin
$display("\t%m Fatal Timing Error Tsckh or Tsckl CEb width error time=%0.2f",$realtime);
corrupt_all; // corrupt memory
end
end
//---------------------------------------------------------------------------
// corrupt all of memory on timing error
//---------------------------------------------------------------------------
task corrupt_all;
reg [31:0] nn;
begin
error_cnt = error_cnt + 1; // count the number of timing errors
$display("\t%m Fatal Error all of memory being set to 'xx' error_cnt=%0d",error_cnt);
for(nn=0;nn<I0.Memsize;nn=nn+16) begin
I0.memory[nn+0] = 8'hxx; // clear main memory to 'xx' on error
I0.memory[nn+1] = 8'hxx; // clear main memory to 'xx' on error
I0.memory[nn+2] = 8'hxx; // clear main memory to 'xx' on error
I0.memory[nn+3] = 8'hxx; // clear main memory to 'xx' on error
I0.memory[nn+4] = 8'hxx; // clear main memory to 'xx' on error
I0.memory[nn+5] = 8'hxx; // clear main memory to 'xx' on error
I0.memory[nn+6] = 8'hxx; // clear main memory to 'xx' on error
I0.memory[nn+7] = 8'hxx; // clear main memory to 'xx' on error
I0.memory[nn+8] = 8'hxx; // clear main memory to 'xx' on error
I0.memory[nn+9] = 8'hxx; // clear main memory to 'xx' on error
I0.memory[nn+10] = 8'hxx; // clear main memory to 'xx' on error
I0.memory[nn+11] = 8'hxx; // clear main memory to 'xx' on error
I0.memory[nn+12] = 8'hxx; // clear main memory to 'xx' on error
I0.memory[nn+13] = 8'hxx; // clear main memory to 'xx' on error
I0.memory[nn+14] = 8'hxx; // clear main memory to 'xx' on error
I0.memory[nn+15] = 8'hxx; // clear main memory to 'xx' on error
end
end
endtask
endmodule
/* -------------------------------------------------------------------------------
Proprietary & Confidental, All right reserved, 2010
Silicon Storage Technology, Inc. a subsidiary of Microchip
----------------------------------------------------------------------------------
This is the common model for the SST26WFxxxB family of chips
This model is configured using defparam commands from the chip level module
This is a serial FLASH memory that can be configured for SPI or SQI bus formats.
----------------------------------------------------------------------------------
Block memory definisions
----------------------------------------------------------------------------------
top 4 * 8K parameter block
--- 1 * 32K block
--- 64k blocks
--- ...
--- ...
--- 64k blocks
--- 1 * 32K block
bottom 4 * 8K parameter block
---------------------------------------------------------------------------
Pin Descriptions
Symbol Pin Name Type Function
------- ------------ ------ -------------------------------------------
SCK Serial Clock input Provides clock to device
SIO[3:0] Serial Data I/O Provide data input and output, QUAD/SPI mode
SIO[0] = SPI I/O SIO[0]/SI
SIO[1] = SPI I/O SIO[1]/SO
SIO[2] Quad I/O SIO[2]/WPb
SIO[3] Quad I/O SIO[3]/HOLDb
CEb Chip Enable input Active low chip enable
------------------------------------------------------------------------------- */
`timescale 1ns / 10 ps
module sst26wfxxxb(SCK,SIO,CEb);
input SCK; // device clock
input CEb; // chip enable active low
inout [3:0] SIO; // serial 4-bit bus I/O
parameter True = 1'b1;
parameter False = 1'b0;
parameter Kilo = 1024; // size of kilo 2^10
parameter Mega = (Kilo * Kilo); // Size of mega 2^20
parameter S080B=1; // size of SST26WF080B 1M bytes
parameter S016B=2; // size of SST26WF016B 2M bytes
parameter S032B=4; // size of SST26WF032B 4M bytes
parameter S040B=0; //For memory size less than 1M
//--------------------------------------------------------------------------------
// change below parameters to change Memory size or init non-volitale registers
//--------------------------------------------------------------------------------
parameter Ksize = 0;
parameter Msize = S032B; // Size of Memory in Mega bites. use S080B, S016B, S032B
parameter Memsize = (Msize * Mega ) + (Ksize * Kilo); // Size of Memory in bytes
parameter ADDR_MSB=21; // most significant address bit, 32Mb=21,16Mb=20, 8Mbit=19
parameter INIT_WPEN = 1'b0; // For Configuration Register bit[7]/WPEN default value on por
parameter PROTECT_REG_MSB = ((Memsize/(Kilo*64))+(16-1)); // MSB of protection register, 1M=31, 2M=47, 4M=79
parameter WLLD_value = 80'h0000_0000_0000_0000_0000; // WLLD non-Volatile memory initial contents
parameter SECURITY_LOCKOUT_VALUE=1'b0; // value of security lockout bit on default startup, SEC status[5]
parameter Memory_Capacity=8'h52; // JEDEC device ID 32B=52,16B=51,080B=58
`protect
//---------------------------------------------------------------------------
parameter MANUFACTURE=8'hBF; // JEDEC manufacture ID for SST
parameter Memory_Type = 8'h26; // JEDEC define Memory type
parameter Sector_MSB=11; // 4k sector size MSB sector address = [11:0]
parameter Block08k_MSB=12; // MSB of 8K blocks
parameter Block32k_MSB=14; // MSB of 32K blocks
parameter Block64k_MSB=15; // MSB of 64K blocks
parameter SST_SEC_ID_LSB='h18; // bottom address of SST security id space that cannot be written
parameter ID_MSB=4; // MSB bit of security ID address max address 'h1F
parameter Burst8_MSB=2; // Burst MSB bit
parameter Burst16_MSB=3; // Burst MSB bit
parameter Burst32_MSB=4; // Burst MSB bit
parameter Burst64_MSB=5; // Burst MSB bit
parameter Burst8=8'h00; // burst values
parameter Burst16=8'h01; // burst values
parameter Burst32=8'h02; // burst values
parameter Burst64=8'h03; // burst values
parameter AF_MSB = 23; // MSB of serial address field [23:0] i.e. 3 bytes of address data
parameter Sector_Size = (4 * Kilo); // sector size;
parameter Block_64k = (64*Kilo); // normal block size
parameter Block_32k=(32*Kilo); // 32K->64K Memory space/top-32K-->top-64K Memory space
parameter Block_08k=(8*Kilo); // top/bottom 32k blocks of Memory are in 8K blocks
parameter Program_Page_Size=(Kilo/4); // program page size 256 bytes
//---------------------------------------------------------------------------
// SPI opcodes
//---------------------------------------------------------------------------
parameter SPI_NOP = 8'h00; // NOP command
parameter SPI_RSTEN = 8'h66; // Reset Enable
parameter SPI_RST = 8'h99; // Reset Chip
parameter SPI_EQIO = 8'h38; // Enable QUAD I/O
parameter SPI_RSTQIO = 8'hFF; // Restet QUAD I/O
parameter SPI_RDSR = 8'h05; // Read Status Register
parameter SPI_WRSR = 8'h01; // Write Status Register
parameter SPI_RDCR = 8'h35; // Read Configuration Register
parameter SPI_READ = 8'h03; // 50Mhz Read of Memory
parameter SPI_HS_READ = 8'h0B; // High Speed Read of Memory 80Mhz
parameter SPI_QUAD_READ = 8'h6B; // SPI QUAD Output Read
parameter SPI_QUAD_IO_READ = 8'hEB; // SPI QUAD I/O Read
parameter SPI_SDOR = 8'h3B; // SPI DUAL Output Read
parameter SPI_SDIOR = 8'hBB; // SPI DUAL I/O Read
parameter SPI_SB = 8'hC0; // Set Burst Length
parameter SPI_RBSPI = 8'hEC; // SPI nB Burst with Wrap
parameter SPI_JEDEC_ID = 8'h9F; // Jedec ID Read
parameter SPI_SFDP = 8'h5A; // Serial Flash Discoverable Parameters
parameter SPI_WREN = 8'h06; // Write Enable
parameter SPI_WRDI = 8'h04; // Write Disable
parameter SPI_SE = 8'h20; // Sector Erase 4K bytes size
parameter SPI_BE = 8'hD8; // Block Erase 64K,32K,8K erase
parameter SPI_CE = 8'hC7; // Chip Erase
parameter SPI_PP = 8'h02; // Page Program SIO[1] bit used as data in, SIO[0] used as data out
parameter SPI_QUAD_PP = 8'h32; // SPI QUAD Page Program
parameter SPI_WRSU = 8'hB0; // Suspend Program/Erase
parameter SPI_WRRE = 8'h30; // Resume Program/Erase
parameter SPI_RBPR = 8'h72; // Read Block Protection Register
parameter SPI_WBPR = 8'h42; // Write Block Protection Register
parameter SPI_LBPR = 8'h8D; // Lock Down Block Protection Register
parameter SPI_nVWLDR = 8'hE8; // Non-Volatile Write Lock down Register
parameter SPI_ULBPR = 8'h98; // Global Block Protection Unlock
parameter SPI_RSID = 8'h88; // Read Security ID
parameter SPI_PSID = 8'hA5; // Program User Security ID Area
parameter SPI_LSID = 8'h85; // Lock Out security ID Programing
parameter SPI_DPD = 8'hB9; // Enable Deep Power Down mode
parameter SPI_DPD_RST = 8'hAB; // Disable Deep Power Down mode; output device ID
//---------------------------------------------------------------------------
// SQI opcodes
//---------------------------------------------------------------------------
parameter SQI_NOP = SPI_NOP; // NOP command
parameter SQI_RSTEN = SPI_RSTEN; // Reset Enable
parameter SQI_RST = SPI_RST; // Reset Chip
parameter SQI_RSTQIO = SPI_RSTQIO; // Restet QUAD I/O
parameter SQI_RDSR = SPI_RDSR; // Read Status Register
parameter SQI_WRSR = SPI_WRSR; // Write Status Register
parameter SQI_RDCR = SPI_RDCR; // Read Configuration Register
parameter SQI_HS_READ = SPI_HS_READ; // High Speed Read of Memory 80Mhz
parameter SQI_SB = SPI_SB; // Set Burst Length
parameter SQI_RBSQI = 8'h0C; // SQI nB Burst with Wrap
parameter SQI_J_ID = 8'hAF; // Quad I/O J-ID Read
parameter SQI_WREN = SPI_WREN; // Write Enable
parameter SQI_WRDI = SPI_WRDI; // Write Disable
parameter SQI_SE = SPI_SE; // Sector Erase 4K bytes size
parameter SQI_BE = SPI_BE; // Block Erase 64K,32K,8K erase
parameter SQI_CE = SPI_CE; // Chip Erase
parameter SQI_PP = SPI_PP; // Page Program SIO[1] bit used as data in, SIO[0] used as data out
parameter SQI_WRSU = SPI_WRSU; // Suspend Program/Erase
parameter SQI_WRRE = SPI_WRRE; // Resume Program/Erase
parameter SQI_RBPR = SPI_RBPR; // Read Block Protection Register
parameter SQI_WBPR = SPI_WBPR; // Write Block Protection Register
parameter SQI_LBPR = SPI_LBPR; // Lock Down Block Protection Register
parameter SQI_nVWLDR = SPI_nVWLDR; // Non-Volatile Write Lock down Register
parameter SQI_ULBPR = SPI_ULBPR; // Global Block Protection Unlock
parameter SQI_RSID = SPI_RSID; // Read Security ID
parameter SQI_PSID = SPI_PSID; // Program User Security ID Area
parameter SQI_LSID = SPI_LSID; // Lock Out security ID Programing
parameter SQI_DPD = SPI_DPD; // Enable Deep Power Down mode
parameter SQI_DPD_RST = SPI_DPD_RST; // Disable Deep Power Down mode; output device ID
`endprotect
//---------------------------------------------------------------------------
// Define Timings
// You can use defparam to change the erase/program times to a shorter value.
// This may make your simulation run faster Tws, Tse, Tbe, Tpp, minimun value 1000,
// Tsce min value 2000
//---------------------------------------------------------------------------
parameter Tv = 5; // Output valid from SCK falling
parameter Tclz = 0; // SCK low to low-z output
parameter Tchz = 12.5; // Chip enable inactive to SIO z-stated
parameter Tse = 25_000_000; // Sector erase time 25mS
parameter Tbe = 25_000_000; // Block erase 25mS
parameter Tsce = 50_000_000; // chip erase time 50mS
parameter Tpp = 1_500_000; // page program time 1.5mS
parameter Tws = 10_000; // suspend to ready time 10uS
parameter Tpsid = 200_000; // Program Security ID time
parameter Tre = 1_000_000; // reset recovery when reset during program/erase
parameter Trp = 100_000; // reset recovery when reset during program/erase
parameter Thz = 7.0; // HOLD falling to SO z-state
parameter Tlz = 7.0; // HOLD rising to SO not z-state
parameter Tsbr = 10_000; // recovery from Deep Power Down mode
//---------------------------------------------------------------------------
// Define non-volatile Memory
//---------------------------------------------------------------------------
reg [7:0] memory[Memsize-1:0]; // define Flash Memory array, non-volitable, 1, 2 or 4 Megabytes
reg [7:0] security_id[(Kilo*2)-1:0]; // Define secutity ID Memory addr 0->7 are SST space the rest is user space, non-volitable
reg [7:0] SFDP[(Kilo*2)-1:0]; // serial flash discoverable parameters
reg [PROTECT_REG_MSB:0] wlldr_mem; // Define write-lock lock down reg, non-volitable, [31:0], [47:0], [79:0]
reg WPEN; // (bit 7 WPEN) of configuration register, non-volitable
reg SEC; // (status[5] SEC), 1-bit Lockout Security ID, non-volitable
`protect
//---------------------------------------------------------------------------
// reg/wire definishions
//---------------------------------------------------------------------------
reg [PROTECT_REG_MSB:0] t_wlldr_mem; // Define temp storage, write-lock lock down reg, non-volitable
reg [PROTECT_REG_MSB:0] protect; // protection register definishion max size for 32M-bit
wire [PROTECT_REG_MSB:0] protect_or; // combine protection bits protect | wlldr_mem
wire [7:0] status,config_reg; // status, configuration register
wire BUSY; // status reg bit 0,7 active high busy signal, program or erase in progress
reg RES; // reserved status bit
reg WPLD; // Write protection lock down, status bit 4
reg WSP; // Program Suspend status, Status Register
reg WEL; // status bit 1
reg WSE; // status bit 2, write suspend erase status, 1= suspend in progress
reg IOC; // config[1]
reg DPD;
wire BPNV; // config[3], Block Protection Volatility State
reg PE; // config[5]
reg EE; // config[6]
reg RSTEN; // enable reset command
reg [7:0] pmem[Program_Page_Size-1:0]; // storage for program Memory 256 bytes
reg [7:0] x_pmem[Program_Page_Size-1:0]; // tmp storage for program Memory 256 bytes
reg [7:0] s_pmem[Program_Page_Size-1:0]; // save suspended data here
reg read_slow_flag; // timing check flag for 40Mhz read
reg [3:0] SIO_IO; // True if outputing data on SIO[3:0]
reg [3:0] SIO_OUT; // output data for SIO
reg [31:0] cnt; // generic counter for loops
reg clock; // internal SCK
reg [7:0] spi_count,sqi_count; // SPI clock counter
reg [7:0] sqi_cmd,spi_cmd,l_spi_cmd; // command input storage
reg [7:0] RSTQIO_cmd; // storage for the SPI RSTQIO command in SQI mode
reg [7:0] l_sqi_cmd; // latched sqi command
wire suspend_act; // True if in suspend mode
reg SPI_SDIOR_active; // loop active
reg SPI_SDOR_active; // loop active
reg SQI_HS_READ_active; // SQI high speed read loop active
reg SPI_WRSU_active; // suspend active flag
reg SPI_nVWLDR_active; // loop active
reg SPI_WRSR_PGM_active; // used to set busy on config reg program
reg SPI_WRSR_active; // loop active
reg SPI_LSID_active; // loop active
reg SPI_PSID_active; // spi PSID loop active
reg SPI_PSID_ip; // security programing in progress
reg SPI_RSID_active; // spi read security id active
reg SPI_DPD_RST_RDID_active; // loop active
reg erase_active; // SE,BE,CE erase actice
reg erase_ip; // SE,BE,CE erase in progress
reg SPI_QUAD_PP_active; // loop active
reg SPI_RDCR_active; // loop active
reg SQI_PP_active; // loop active
reg SPI_PP_active; // loop active
reg SPI_RBPR_active; // loop active
reg SPI_WBPR_active; // loop active flag
reg SPI_nVWLDR_cmd_active; // loop active
reg SPI_RDSR_active; // loop active
reg SPI_SFDP_active; // loop active
reg SPI_JEDEC_ID_active; // loop active
reg SPI_RBSPI_active; // loop active flag
reg SPI_SB_active; // loop active flag
reg SPI_READ_active,SPI_READ_QUAD_active; // read loop is active ir True
reg SPI_QUAD_IO_READ_active; // loop active
reg SQI_SPI_mode; // True = QUAD mode False=SPI mode
reg [7:0] Mode_Configuration; // configuration data for continious read with no opcode
reg [7:0] burst_length; // burst length 00=8bytes, 01=16bytes, 02=32bytes, 03=64bytes
wire WBPR_protection_lck; // table 2 for WBPR cammond
wire WPb; // write protect bar signal
reg valid_addr; // program address valid, address was read in
reg valid_data; // program data valid, i.e. at least 1 clock of data
reg [AF_MSB:0] pgm_addr; // program address
reg [15:0] pgm_id_addr; // program security id address
reg [AF_MSB:0] erase_addr; // erase address
reg [31:0] erase_size,resume_size; // erase size
reg [AF_MSB:0] suspend_addr,resume_addr; // save program address on suspend
real start_erase; // start time of erase or program
real s_time_left; // save time left for nested suspends
real save_erase_time; // on suspend save realtime in here
real erase_time; // time needed to erase sector/block/chip
real time_left; // time left for program/erase to finish
reg page_program_active; // page program is active
reg [7:0] wsr_sreg,wsr_creg; // tmp data storage for write status/configuration registers
wire CONFIG_protection_lck; // configuration write protect
wire write_protect;
reg CE_flag,BE_flag,SE_flag; // erase type flags
reg s_BE_flag,s_SE_flag; // flags saved for suspend
wire HOLDb, HOLDb_IO; // composit hold control siginals
reg clockx; // internal clock
reg pgm_sus_reset;
event reset; // Trigger reset block
event SPI_READ_trg; // Start spi read operation 50Mhz
event SPI_QUAD_READ_trg; // Start SPI QUAD read
event SPI_QUAD_IO_READ_trg; // Start SPI QUAD IO READ
event SPI_SB_trg; // Start SPI Set Burst Count
event SPI_RBSPI_trg; // Start SPI Burst Read
event SPI_JEDEC_ID_trg; // Start JEDEC_ ID read
event SPI_SFDP_trg; // start SFDP , Serial Flash Discoverable Parameters
event SPI_RDSR_trg; // Read Status register
event SPI_WBPR_trg; // Trigger write block protection register
event SPI_RBPR_trg; // Read block protection register
event SPI_PP_trg; // Page program trigger
event SPI_RDCR_trg; // Start read of Configuration Register
event SPI_QUAD_PP_trg; // Start SPI QUAD page write
event SPI_SE_trg; // Start sector erase
event SPI_BE_trg; // Start block erase
event SPI_CE_trg; // Start Chip erase
event SPI_RSID_trg; // Spi read security id
event SPI_PSID_trg; // Spi program user security id space
event SPI_LSID_trg; // Security ID lockout
event SPI_DPD_RST_trg; // Deep Power Down Reset
event SPI_WRSR_trg; // Write status register
event SPI_nVWLDR_trg; // Write non-volatile block protection register
event SPI_ULBPR_trg; // Trigger Global Block Protection Unlock
event SPI_WRSU_trg; // Enter suspend mode
event SPI_WRRE_trg; // Exit suspend mode , resume
event SQI_HS_READ_trg; // SQI high speed read trigger
event SPI_SDOR_trg; // Spi dual output read
event SPI_SDIOR_trg; // Dual I/O read
event SPI_LBPR_trg; // Lock down block protection reg
event SPI_EQIO_trg; // Enable Quad I/O
//---------------------------------------------------------------------------
// Status/Configuration register definisions, Protection logic
//---------------------------------------------------------------------------
assign status = {BUSY,RES,SEC,WPLD,WSP,WSE,WEL,BUSY}; // create status register
assign config_reg = {WPEN,EE,PE,1'b0,BPNV,1'b0,IOC,1'b0}; // create configuration register
assign BUSY = page_program_active | erase_ip | SPI_LSID_active | SPI_WRSR_PGM_active | SPI_nVWLDR_active
| pgm_sus_reset;
assign BPNV = ~(|wlldr_mem); // block protection volatility state, configuration reg[3]
assign WPb = SIO[2]; // rename SIO[2] to WPb for code clairity
assign protect_or = protect | wlldr_mem; // combine non-volital protect with volital protect
assign suspend_act = (WSE | WSP); // suspend active net
//---------------------------------------------------------------------------
// write protection SPI mode configuration reg write only, protection = True
// Check Table 2 lines 2,4,5,6
//---------------------------------------------------------------------------
assign CONFIG_protection_lck = (SQI_SPI_mode===True) ? False : (~WPb & ~IOC & WPEN);
assign write_protect = ~SQI_SPI_mode & ~IOC & WPEN & ~WPb;
assign WBPR_protection_lck = (SQI_SPI_mode===True) ? WPLD : WPLD | write_protect;
//---------------------------------------------------------------------------
// I/O assignments
//---------------------------------------------------------------------------
assign SIO[0] = (SIO_IO[0]===True && HOLDb_IO===1'b1) ? SIO_OUT[0] : 1'bz; // output data on SIO ? SI
assign SIO[1] = (SIO_IO[1]===True && HOLDb_IO===1'b1) ? SIO_OUT[1] : 1'bz; // output data on SIO ? SO
assign SIO[2] = (SIO_IO[2]===True) ? SIO_OUT[2] : 1'bz; // output data on SIO ?
assign SIO[3] = (SIO_IO[3]===True) ? SIO_OUT[3] : 1'bz; // output data on SIO ?
//---------------------------------------------------------------------------
// HOLD# setup
//---------------------------------------------------------------------------
assign HOLDb = CEb | IOC | SIO[3] | SQI_SPI_mode ; // no timing HOLDb
assign #(Tlz,Thz) HOLDb_IO = HOLDb; // I/O control timing HOLDb signal
//-------------------------------------------------------
// Generate internal clock
//-------------------------------------------------------
always @(SCK) if(CEb === 1'b0) clockx = SCK;
else if (CEb === 1'b1 ) clockx = 1'b0;
else clockx = 1'bx;
always @(posedge clockx) if(HOLDb===1'b1) clock = clockx; else clock = 1'b0;
always @(negedge clockx) clock = clockx;
//-------------------------------------------------------
// Define begining of command operation
//-------------------------------------------------------
always @( negedge CEb ) begin
spi_count = 0; // clear spi clock counter
sqi_count = 0; // clear sqi clock counter
spi_cmd = SPI_NOP; // clear SPI command register
sqi_cmd = SQI_NOP; // clear SQI command register
RSTQIO_cmd = SPI_NOP; // clear command
end
//-------------------------------------------------------
// Terminate still runnung named blocks on CEb inactive
//-------------------------------------------------------
always @( posedge CEb ) begin
SIO_IO <= #Tchz {False,False,False,False}; // Turn off IO control SIO[3:0]
#0 if(SPI_READ_active==True) begin
disable SPI_READ_label;
SPI_READ_active = False; // read loop is inactive
read_slow_flag = False; // set timing checks back to normal
end
if(SPI_READ_QUAD_active==True) begin
disable SPI_QUAD_READ_label;
SPI_READ_QUAD_active = False;
end
if(SPI_QUAD_IO_READ_active===True) begin
disable SPI_QUAD_IO_READ_label;
SPI_QUAD_IO_READ_active = False;
end
if(SPI_SB_active===True) begin
disable SPI_SB_label;
SPI_SB_active = False;
end
if(SPI_RBSPI_active===True) begin
disable SPI_RBSPI_label;
SPI_RBSPI_active = False;
end
if(SPI_JEDEC_ID_active===True) begin
disable SPI_JEDEC_ID_label;
SPI_JEDEC_ID_active = False;
end
if(SPI_SFDP_active===True) begin
disable SPI_SFDP_label;
SPI_SFDP_active = False;
end
if(SPI_RDSR_active===True) begin
disable SPI_RDSR_label;
SPI_RDSR_active = False;
end
if(SPI_RDCR_active===True) begin
disable SPI_RDCR_label;
SPI_RDCR_active = False;
end
if(SPI_RBPR_active===True) begin
disable SPI_RBPR_label;
SPI_RBPR_active = False;
end
if(SPI_RSID_active ===True) begin
disable SPI_RSID_label;
SPI_RSID_active = False;
end
if(SPI_WBPR_active ===True) begin
disable SPI_WBPR_label;
SPI_WBPR_active = False;
end
if(SQI_HS_READ_active===True) begin
disable SQI_HS_READ_label;
SQI_HS_READ_active = False;
end
if(SPI_SDOR_active===True) begin
disable SPI_SDOR_label;
SPI_SDOR_active = False;
end
if(SPI_SDIOR_active ===True) begin
disable SPI_SDIOR_label;
SPI_SDIOR_active = False;
end
if(SPI_DPD_RST_RDID_active ===True) begin
disable SPI_DPD_RST_RDID_label;
SPI_DPD_RST_RDID_active = False;
end
end
//-----------------------------------------------------------
// Read in hex command stream, SQI mode commands
//-----------------------------------------------------------
always @( posedge clock && SQI_SPI_mode === True) begin
if(BUSY===False && Mode_Configuration[7:4]===4'hA && sqi_count===8'h00) begin // continue sqi_hs_read command ?
sqi_count = 8; // abort this command loop
-> SQI_HS_READ_trg; // continue from previous read
#1 Mode_Configuration = 8'hFF; // clear mode config
end
if(sqi_count < 2 ) begin // 1st 2 clocks are command
sqi_cmd = sqi_cmd <<4; // shift command data
sqi_cmd[3:0] = SIO[3:0]; // load in cmd data
end
if(sqi_count < 8 ) begin // look for SPI RSTIO cmd
RSTQIO_cmd = RSTQIO_cmd <<1; // shift command data
RSTQIO_cmd[0] = SIO[0]; // load in cmd data
if(BUSY===False && sqi_count===8'h07 && RSTQIO_cmd===SPI_RSTQIO)
@(posedge CEb) SQI_SPI_mode=False; // exit SQI mode while in SQI mode using SPI format
end
if(sqi_count === 8'h01) begin // start of SQI command interperter
l_sqi_cmd = sqi_cmd; // latch SQI command
if(DPD===False && RSTEN===True && l_sqi_cmd !== SQI_RST) RSTEN = False; // clear reset enable on incorrect sequence
if(DPD===False && l_sqi_cmd === SQI_RSTEN) RSTEN = True; // enable reset command
else if(DPD===False && l_sqi_cmd===SQI_RST && RSTEN===True) @(posedge CEb) -> reset; // reset chip
else if(DPD===False && l_sqi_cmd===SQI_NOP ) RSTEN=False; // NOP command
else if(DPD===False && l_sqi_cmd===SQI_RDSR) -> SPI_RDSR_trg; // SQI read status register
else if(DPD===False && l_sqi_cmd===SQI_RDCR) -> SPI_RDCR_trg; // SQI read configuration register
else if(DPD===False && BUSY===True && l_sqi_cmd===SQI_WRSU) -> SPI_WRSU_trg; // Enter suspend mode
else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_RSTQIO) @(posedge CEb) SQI_SPI_mode=False; // Reset to SPI mode
else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_WREN) @(posedge CEb) WEL = True; // Write Enable flag set
else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_WRDI) @(posedge CEb) WEL = False; // Write Enable flag cleared
else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_WRRE ) -> SPI_WRRE_trg; // exit suspend mode resume normal mode
else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_RBPR) -> SPI_RBPR_trg; // Read Block Protection Register
else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_HS_READ) -> SQI_HS_READ_trg; // normal read, 80Mhz
else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_SB) -> SPI_SB_trg; // Set Burst Count
else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_RBSQI) -> SPI_RBSPI_trg; // sqi nB Burst with Wrap
else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_J_ID) -> SPI_JEDEC_ID_trg; // Read JEDEC ID
else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_RSID) -> SPI_RSID_trg; // Read security ID
else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_CE && WEL===True) -> SPI_CE_trg; // Chip erase
else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_SE && WEL===True) -> SPI_SE_trg; // Sector erase
else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_BE && WEL===True) -> SPI_BE_trg; // Block erase
else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_LBPR && WEL===True) -> SPI_LBPR_trg; // Lock Down Block Protection Reg.
else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_ULBPR && WEL===True) -> SPI_ULBPR_trg; // Global Block Protection Unlock
else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_LSID && WEL===True) -> SPI_LSID_trg; // lockout security ID programing
else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_WRSR && WEL===True) -> SPI_WRSR_trg; // Write status register
else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_PP && WEL===True) -> SPI_QUAD_PP_trg; // SQI page program
else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_WBPR && WEL===True) -> SPI_WBPR_trg; // Write Block Protection Register
else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_nVWLDR && WEL===True) -> SPI_nVWLDR_trg; // write non-volatile block protection register
else if(DPD===False && BUSY===False && l_sqi_cmd===SQI_PSID && WEL===True) -> SPI_PSID_trg; // program Security ID space 2K
else if(BUSY===False && l_sqi_cmd===SQI_DPD) @(posedge CEb) DPD = True; // deep power down mode
else if(BUSY===False && l_sqi_cmd===SQI_DPD_RST) -> SPI_DPD_RST_trg; // exit deep power down mode
else begin
if({l_sqi_cmd[7],l_sqi_cmd[3]}!== 2'b11) // check for start of SPI RSTQIO cmd
$display("\t%m Warning Illegal SQI Instruction='%h' aborted, time=%0.2f",l_sqi_cmd,$realtime);
if(BUSY===True) $display("\t%m Check BUSY most commands don't run during busy");
end
end
if( sqi_count < 9 ) sqi_count = sqi_count + 1; // incremint bit counter for command sample
end
//-----------------------------------------------------------
// Read in serial command stream command, SPI mode commands
//-----------------------------------------------------------
always @( posedge clock && SQI_SPI_mode === False ) begin
if(BUSY===False && Mode_Configuration[7:4]===4'hA && spi_count===8'h00) begin // continue previous command ?
spi_count = 8; // abort command loop
if(l_spi_cmd === SPI_QUAD_IO_READ) -> SPI_QUAD_IO_READ_trg; // continue from previous read, quad read
else if(l_spi_cmd === SPI_SDIOR) -> SPI_SDIOR_trg; // continue from previous read, dual read
Mode_Configuration <= #1 8'hFF; // clear mode config
end
if(spi_count < 8 ) begin // 1st 8 clocks are command
spi_cmd = spi_cmd <<1; // shift command data
spi_cmd[0] = SIO[0]; // load in cmd data
end
if( spi_count === 8'h07) begin // start of SPI command interperter
l_spi_cmd = spi_cmd; // latch command
if(DPD===False && RSTEN===True && l_spi_cmd !== SPI_RST) RSTEN = False; // clear reset enable on incorrect sequence
if(DPD===False && l_spi_cmd === SPI_RSTEN) RSTEN = True; // enable reset command
else if(DPD===False && l_spi_cmd===SPI_NOP ) RSTEN=False; // NOP command
else if(DPD===False && l_spi_cmd===SPI_RST && RSTEN===True) @(posedge CEb) -> reset; // reset command
else if(DPD===False && l_spi_cmd===SPI_RDSR) -> SPI_RDSR_trg; // SPI read status register
else if(DPD===False && l_spi_cmd===SPI_RDCR) -> SPI_RDCR_trg; // SPI read configuration register
else if(DPD===False && BUSY===True && l_spi_cmd===SPI_WRSU) -> SPI_WRSU_trg; // Enter suspend mode
else if(DPD===False && BUSY===False && l_spi_cmd===SPI_READ) -> SPI_READ_trg; // normal read, 50Mhz
else if(DPD===False && BUSY===False && l_spi_cmd===SPI_HS_READ) -> SPI_READ_trg; // normal read, 80Mhz
else if(DPD===False && BUSY===False && l_spi_cmd===SPI_RSTQIO) @(posedge CEb) SQI_SPI_mode=False; // This cmd does nothing as already in SPI mode
else if(DPD===False && BUSY===False && l_spi_cmd===SPI_WREN) @(posedge CEb) WEL = True; // Write Enable flag set
else if(DPD===False && BUSY===False && l_spi_cmd===SPI_WRDI) @(posedge CEb) WEL = False; // Write Enable flag cleared
else if(DPD===False && BUSY===False && l_spi_cmd===SPI_SDOR) -> SPI_SDOR_trg; // dual output read
else if(DPD===False && BUSY===False && l_spi_cmd===SPI_SDIOR) -> SPI_SDIOR_trg; // dual I/O output read
else if(DPD===False && BUSY===False && l_spi_cmd===SPI_QUAD_READ) -> SPI_QUAD_READ_trg; // SPI QUAD read
else if(DPD===False && BUSY===False && l_spi_cmd===SPI_QUAD_IO_READ) -> SPI_QUAD_IO_READ_trg; // SPI QUAD IO READ
else if(DPD===False && BUSY===False && l_spi_cmd===SPI_RBSPI) -> SPI_RBSPI_trg; // SPI Burst read
else if(DPD===False && BUSY===False && l_spi_cmd===SPI_EQIO ) -> SPI_EQIO_trg; // enter SQI mode
else if(DPD===False && BUSY===False && l_spi_cmd===SPI_JEDEC_ID) -> SPI_JEDEC_ID_trg; // Read JEDEC ID
else if(DPD===False && BUSY===False && l_spi_cmd===SPI_RBPR) -> SPI_RBPR_trg; // Read Block Protection Register
else if(DPD===False && BUSY===False && l_spi_cmd===SPI_SFDP) -> SPI_SFDP_trg; // Read Serial Flash Discoverable Parameters
else if(DPD===False && BUSY===False && l_spi_cmd===SPI_RSID) -> SPI_RSID_trg; // SPI Read security ID
else if(DPD===False && BUSY===False && l_spi_cmd===SPI_SB) -> SPI_SB_trg; // SPI Set Burst Count
else if(DPD===False && BUSY===False && l_spi_cmd===SPI_WRRE) -> SPI_WRRE_trg; // exit suspend mode resume normal mode
else if(DPD===False && BUSY===False && l_spi_cmd===SPI_QUAD_PP && WEL===True) -> SPI_QUAD_PP_trg; // SPI QUAD page program
else if(DPD===False && BUSY===False && l_spi_cmd===SPI_ULBPR && WEL===True) -> SPI_ULBPR_trg; // Global Block Protection Unlock
else if(DPD===False && BUSY===False && l_spi_cmd===SPI_nVWLDR && WEL===True) -> SPI_nVWLDR_trg; // write non-volatile block protection register
else if(DPD===False && BUSY===False && l_spi_cmd===SPI_WRSR && WEL===True) -> SPI_WRSR_trg; // Write status register
else if(DPD===False && BUSY===False && l_spi_cmd===SPI_LSID && WEL===True) -> SPI_LSID_trg; // lockout security ID programing
else if(DPD===False && BUSY===False && l_spi_cmd===SPI_PP && WEL===True) -> SPI_PP_trg; // SPI page program
else if(DPD===False && BUSY===False && l_spi_cmd===SPI_WBPR && WEL===True) -> SPI_WBPR_trg; // Write Block Protection Register
else if(DPD===False && BUSY===False && l_spi_cmd===SPI_SE && WEL===True) -> SPI_SE_trg; // Sector erase
else if(DPD===False && BUSY===False && l_spi_cmd===SPI_BE && WEL===True) -> SPI_BE_trg; // Block erase
else if(DPD===False && BUSY===False && l_spi_cmd===SPI_CE && WEL===True) -> SPI_CE_trg; // Chip erase
else if(DPD===False && BUSY===False && l_spi_cmd===SPI_PSID && WEL===True) -> SPI_PSID_trg; // program Security ID space 2K
else if(DPD===False && BUSY===False && l_spi_cmd===SPI_LBPR && WEL===True) -> SPI_LBPR_trg; // Lock Down Block Protection Reg.
else if(BUSY===False && l_spi_cmd===SPI_DPD) @(posedge CEb) DPD = True; // deep power down mode
else if(BUSY===False && l_spi_cmd===SPI_DPD_RST) -> SPI_DPD_RST_trg; // exit deep power down mode
else begin
$display("\t%m Warning Illegal SPI Instruction='%h' aborted, time=%0.2f",l_spi_cmd,$realtime);
if(BUSY===True) $display("\t%m Check BUSY most commands not allowed during busy");
end
end
if( spi_count < 9 ) spi_count = spi_count + 1; // incremint bit counter for command sample
end
//---------------------------------------------------------------------------
// Enter SQI mode
//---------------------------------------------------------------------------
always @(SPI_EQIO_trg) begin
@(posedge CEb)
if (~write_protect) SQI_SPI_mode = True;
end
//---------------------------------------------------------------------------
// Lock Down Block Protection Reg.
//---------------------------------------------------------------------------
always @(SPI_LBPR_trg) begin
@(posedge CEb) begin
WPLD = True; // set WPLD (write protection lock down status[4])
WEL = False; // clear status[2] WEL
end
end
//---------------------------------------------------------------------------
// Resume, exit suspend mode
//---------------------------------------------------------------------------
always @(SPI_WRRE_trg) begin :SPI_WRRE_label
reg [8:0] pcount;
if(suspend_act===False) $display("\t%m Warning WRRE(h30) cmd ignnored not in suspend mode, time=%0.2f",$realtime);
else begin // suspend_act===True
if(WSP === True) begin // resume from Page program ?
WSP = False; // clear program suspend flag
page_program_active = True; // flags needed to continue PP program
valid_data = True; valid_addr = True; // flags needed to continue PP program
SPI_PP_active = True; // flags needed to continue PP program
time_left = s_time_left; // program time left
pgm_addr = resume_addr; // restort program address
for(pcount=0;pcount<Program_Page_Size;pcount = pcount+1) pmem[pcount] = s_pmem[pcount]; // restore suspended program data
end
else if(WSE===True) begin // Erase Suspended ?
erase_active = True; // restart erase on CEb inactive
WSE = False; // clear erase suspend flag
valid_addr = True; // set resume address as valid
valid_data = True; // set resume data as valid
erase_addr = resume_addr; // restore address
erase_size = resume_size; // restore size of erase area
time_left = s_time_left; // erase time left
BE_flag = s_BE_flag; // restore type of erase
SE_flag = s_SE_flag; // restore type of erase
end
@(posedge CEb) ; // wait for CEb to go inactive, starts erase/program loops
end
end
//---------------------------------------------------------------------------
// enter suspend mode, WEL=1 already to get here
//---------------------------------------------------------------------------
always @(SPI_WRSU_trg) begin :SPI_WRSU_label
reg [8:0] pcount;
@(posedge CEb) ; // wait for CEb to go inactive
#0 if((page_program_active === False && erase_ip === False) || SPI_PSID_ip===True ) begin
$display("\t%m Warning Write Suspend(hB0), only allowed during PP(h32),PP(h02),BE(hD8),SE(h20) cmds, cmd aborted time=%0.2f",$realtime);
end
else if(CE_flag===True) begin // no suspend during chip erase
$display("\t%m Warning Write Suspend(hB0), not allowed during CE(hC7) cmd, cmd aborted time=%0.2f",$realtime);
end
else if(suspend_act===True) begin
$display("\t%m Warning Write Suspend(hB0), nested suspends not allowed, WRSU(hB0) cmd aborted time=%0.2f",$realtime);
end
else begin // begin Suspend mode
SPI_WRSU_active = True; // this loop is active
if(page_program_active === True) begin
disable page_program_label; // abort programing on suspend
s_time_left = (time_left - ($realtime - start_erase)) + Tws;
resume_addr = suspend_addr; // save suspended address
for(pcount=0;pcount<Program_Page_Size;pcount = pcount+1) s_pmem[pcount] = pmem[pcount]; // save suspended data program data
WSP = True; // set WSE in status register
#Tws ; // wait for suspend program complete
page_program_active = False; // clear busy
WEL = False; // clear WEL write enable
end
else if( erase_ip === True) begin // Sector/Block erase in progress ?
disable erase_label; // abort erase on suspend
s_time_left = (time_left - ($realtime - start_erase)) + Tws;
resume_addr = suspend_addr; // save suspended address
resume_size = erase_size; // save block size to erase
WSE = True; // set WSE in status register
#Tws ; // wait for suspend of erase complete
erase_ip = False; // clear erase loop flag;
WEL = False;
s_BE_flag = BE_flag; // save type of erase
s_SE_flag = SE_flag; // save type of erase
BE_flag = False;
SE_flag = False;
end
SPI_WRSU_active = False; // this loop is inactive
end
end
//---------------------------------------------------------------------------
// Global Block Protection Unlock
//---------------------------------------------------------------------------
always @(SPI_ULBPR_trg) begin :SPI_ULBPR_label
reg [7:0] count; // counter
@(posedge CEb) begin
if(WBPR_protection_lck === False) begin // check block protect lock down register
// clear the block write protection
for(count=0;count<(PROTECT_REG_MSB-15);count=count+1) protect[count]=1'b0;
// clear the 8 8K block write protection bits
count = PROTECT_REG_MSB-15;
repeat(8) begin protect[count] = 1'b0; count = count + 2; end
WEL = False; // clear write enable in status reg
end
else begin
$display("\t%m Warning ULBPR(h98) cmd aborted time=%0.2f",$realtime);
end
end
end
//---------------------------------------------------------------------------
// program status/configuration register
//---------------------------------------------------------------------------
always @(posedge CEb) begin : SPI_WRSR_PGM
if(SPI_WRSR_active===True && suspend_act===False) begin
disable SPI_WRSR_label;
SPI_WRSR_active = False;
if(valid_data===True) begin // 16 clocks of data ?
IOC = wsr_creg[1]; // set IOC right away, don't wait for program to finish
if(wsr_creg[7]!==WPEN) begin // WPEN <- 0
SPI_WRSR_PGM_active = True; // set busy
if(wsr_creg[7]===False) begin // WPEN <- 0
#Tse WPEN = wsr_creg[7];// program WPEN
end
else begin // WPEN <- 1
#Tpp WPEN = wsr_creg[7];// Erase WPEN
end
end
WEL = False; // clear WEL
valid_data = False; // clear valid data flag
SPI_WRSR_PGM_active = False; // clear busy
end
else $display("\t%m Warning WRSR(h01) has invalid data, cmd aborted, time=%0.2f",$realtime);
end
end
//---------------------------------------------------------------------------
// write status register
//---------------------------------------------------------------------------
always @(SPI_WRSR_trg) begin :SPI_WRSR_label
if(suspend_act===False && CONFIG_protection_lck===False) begin
SPI_WRSR_active = True;
valid_data = False; // default valid data
if(SQI_SPI_mode === True) begin // SQI bus
$display("\t%m Warning do not run cmd WRSR(h01) in SQI mode time=%0.2f",$realtime);
@(posedge clock) wsr_sreg[7:4] = SIO[3:0]; // read in status register
@(posedge clock) wsr_sreg[3:0] = SIO[3:0];
@(posedge clock) wsr_creg[7:4] = SIO[3:0]; // read in configuration register
@(posedge clock) wsr_creg[3:0] = SIO[3:0];
end
else begin // SPI bus
repeat(8) @(posedge clock ) begin // read in status register
wsr_sreg = wsr_sreg <<1;
wsr_sreg[0] = SIO[0];
end
repeat(8) @(posedge clock ) begin // read in configuration register
wsr_creg = wsr_creg <<1;
wsr_creg[0] = SIO[0];
end
end
valid_data = True; // set valid data flag
forever @(posedge clock ) ; // wait here for CEb rising
end
else begin
if(CONFIG_protection_lck===True) begin
$display("\t%m Warning command WRSR('h01) aborted, configuration reg write protected time=%0.2f",$realtime);
end
if(suspend_act===True ) begin
$display("\t%m Warning command WRSR('h01) aborted, not a valid cmd is suspend mode, time=%0.2f",$realtime);
end
valid_data = False; // default valid data
SPI_WRSR_active = False;
end
end
//---------------------------------------------------------------------------
// Security ID program, lockout
//---------------------------------------------------------------------------
always @(SPI_LSID_trg) begin :SPI_LSID_label
@(posedge CEb) begin
if(suspend_act===False) begin
SPI_LSID_active = True; // set busy
SEC = True; // program SEC bit of status register
#Tpsid
WEL = False; // clear write enable in status reg
SPI_LSID_active = False; // clear busy
end
else begin
$display("\t%m Warning command LSID(h85) not allowed in suspend mode, aborted time=%0.2f",$realtime);
end
end
end
//---------------------------------------------------------------------------
// Security ID program, Program Security ID Memory when CEb inactive ?
//---------------------------------------------------------------------------
always @(posedge CEb) begin :Sec_ID_pgm_label
reg [AF_MSB:0] nn;
if(SPI_PSID_active === True && suspend_act===False ) begin // Page_Program of Security ID is active
disable SPI_PSID_label; // disable Security ID page program loop
SPI_PSID_active = False; // clear program loop
if(valid_data===True && valid_addr===True && suspend_act===False) begin
page_program_active = True; // set busy
valid_addr = False; // default valid address
valid_data = False; // default valid data
for(nn=0;nn<Program_Page_Size;nn=nn+1) begin // save current data in Memory
x_pmem[nn]=security_id[{pgm_id_addr[10:8],nn[7:0]}]; // save security_id data that will be written over
security_id[{pgm_id_addr[10:8],nn[7:0]}] = 8'hxx; // make data 'xx'
end
SPI_PSID_ip = True; // security programing in progress
#Tpp for(nn=0;nn<Program_Page_Size;nn=nn+1) begin // Wait Tpp time for program to finish, then update memory
security_id[{pgm_id_addr[10:8],nn[7:0]}] = x_pmem[nn] & pmem[nn[7:0]];
//$display("\tprogram security_id add=%h, data=%h time=%0.2f",{pgm_id_addr[ADDR_MSB:8],nn[7:0]},(x_pmem[nn] & pmem[nn[7:0]]),
//$realtime);
end
SPI_PSID_ip = False; // security programing complete
page_program_active = False; // clear busy
WEL = False;
end
else begin
$display("\t%m Warning PSID(hA5) Page Program error, PSID(hA5) cmd aborted time=%0.2f",$realtime);
end
end
end
//---------------------------------------------------------------------------
// Program Security ID , get address and data
//---------------------------------------------------------------------------
always @(SPI_PSID_trg) begin : SPI_PSID_label
reg [8:0] pcount;
reg [7:0] sdata;
valid_addr = False; // default valid address
valid_data = False; // default valid data
if(suspend_act === False) begin // check WREN flag, no program on suspend active
SPI_PSID_active = True; // program loop is active
for(pcount=0;pcount<Program_Page_Size;pcount = pcount+1) pmem[pcount] = 8'hFF; // clear program data to all 1's
if(SQI_SPI_mode === True) begin
repeat(4) begin // read in address, set valid address flag when complete
pgm_id_addr = pgm_id_addr <<4;
@(posedge clock) pgm_id_addr[3:0]=SIO[3:0]; // read in 1-nibble of address
end
end
else begin
repeat(16) begin // read in address, set valid address flag when complete
pgm_id_addr = pgm_id_addr <<1;
@(posedge clock) pgm_id_addr[0] = SIO[0]; // read in 1-bit of address
end
end
pgm_id_addr = pgm_id_addr & ((Kilo*2)-1); // clear unused upper address bits above 2K memory boundry
valid_addr = True; // address read complete set valid address flag
if(SEC===False) begin // check for proteced Memory
valid_data = False; // no data valid
forever begin // Read program data, loop through all data abort on CEb rising
if(SQI_SPI_mode === True) begin // SQI mode
@(posedge clock) sdata[7:4]=SIO[3:0]; // read high nibble
valid_data = True; // at least 1 valid data clock
@(posedge clock) sdata[3:0]=SIO[3:0]; // read low nibble
end
else begin // SPI mode
repeat(8) @(posedge clock) begin // read in byte of data
valid_data = True; // at least 1 valid data clock
sdata = sdata <<1;
sdata[0] = SIO[0];
end
end
if(pgm_id_addr >= 'h0008) begin // don't program SST Memory section
pmem[pgm_id_addr[7:0]] = sdata; // save byte of data page Memory
end
pgm_id_addr[7:0]=pgm_id_addr[7:0] + 1; // increment to next addr of page Memory, wrap on 256 byte bountry
end
end
else begin // protected Memory abort program
valid_addr = False; // default valid address
valid_data = False; // default valid data
SPI_PSID_active = False; // abort program on protected Memory
$display("\t%m Warning PSID(hA5) command aborted SEC=1 Locked Memory address=%h, time %0.2f",
pgm_id_addr,$realtime);
end
SPI_PSID_active = False;
end
else begin
$display("\t%m Warning PSID(hA5) command aborted, PSID does not work in Suspend Mode, time=%0.2f",$realtime);
end
end
//----------------------------------------------------------------------------
// SPI Mode Read Security ID space
//----------------------------------------------------------------------------
always @(SPI_RSID_trg ) begin :SPI_RSID_label
reg [7:0] data;
reg [15:0] addr; // max value 2K-1
SPI_RSID_active = True;
if(SQI_SPI_mode === True) begin // SQI mode
// read in address[15:0]
@(posedge clock) addr[15:12] = SIO[3:0];
@(posedge clock) addr[11:8] = SIO[3:0];
@(posedge clock) addr[7:4] = SIO[3:0];
@(posedge clock) addr[3:0] = SIO[3:0];
repeat(6) @(posedge clock) ; // 3 dummy cycles
forever begin // output SQI nibble data
data = security_id[addr[10:0]]; // read from RSID Memory, limit to 2K address range
addr[10:0] = addr[10:0] + 1; // increment address, wrap at 2k boundry
@(negedge clock) begin
SIO_IO <= #Tclz {True,True,True,True}; // Set I/O controls
#Tv SIO_OUT[3:0] = data[7:4]; // send high nibble
end
@(negedge clock)
#Tv SIO_OUT[3:0] = data[3:0]; // send low nibble
end
end
else begin // SPI mode
repeat(16) begin
@(posedge clock) begin // wait for clk rising
addr = addr <<1; // shift left address
addr[0] = SIO[0]; // read in address bit
end
end
repeat(8) @(posedge clock) ; // dummy cycle
forever begin // output SPI serial data
data = security_id[addr[10:0]]; // read from RSID Memory, limit to 2K address range
addr[10:0] = addr[10:0] + 1; // increment address, wrap at 2k boundry
repeat(8) begin
@(negedge clock) ; // wait here for clock falling
SIO_IO <= #Tclz {False,False,True,False}; // Turn on IO control SIO[1]
#Tv SIO_OUT[1] = data[7]; // output 1 bit data
data = data <<1; // shift data left
end
end
end
SPI_RSID_active = False;
end
//---------------------------------------------------------------------------
// erase Memory when CEb inactive ?
//---------------------------------------------------------------------------
always @(posedge CEb) begin :erase_label
reg [31:0] nn;
if(erase_active === True) begin
erase_ip = True; // set erase in progress flag
erase_active = False;
disable erase_setup_label;
if(valid_addr === True ) begin // check valid address and WSE
suspend_addr = erase_addr; // save erase address for possible suspend
start_erase = $realtime; // save time of program/erase start
for(nn=erase_addr;nn<(erase_addr+erase_size);nn=nn+8) begin // make unknown
memory[nn[ADDR_MSB:0]+0] = 8'hxx; memory[nn[ADDR_MSB:0]+1] = 8'hxx;
memory[nn[ADDR_MSB:0]+2] = 8'hxx; memory[nn[ADDR_MSB:0]+3] = 8'hxx;
memory[nn[ADDR_MSB:0]+4] = 8'hxx; memory[nn[ADDR_MSB:0]+5] = 8'hxx;
memory[nn[ADDR_MSB:0]+6] = 8'hxx; memory[nn[ADDR_MSB:0]+7] = 8'hxx;
end
#time_left for(nn=erase_addr;nn<(erase_addr+erase_size);nn=nn+8) begin // make known at completion of erase
memory[nn[ADDR_MSB:0]+0]=8'hFF; memory[nn[ADDR_MSB:0]+1]=8'hFF;
memory[nn[ADDR_MSB:0]+2]=8'hFF; memory[nn[ADDR_MSB:0]+3]=8'hFF;
memory[nn[ADDR_MSB:0]+4]=8'hFF; memory[nn[ADDR_MSB:0]+5]=8'hFF;
memory[nn[ADDR_MSB:0]+6]=8'hFF; memory[nn[ADDR_MSB:0]+7]=8'hFF;
WEL = False;
end
end
else if(valid_addr === False) begin
$display("\t%m Warning erase address error, erase cmd aborted time=%0.2f",$realtime);
end
CE_flag = False; BE_flag = False; SE_flag = False;
erase_ip = False;
end
end
//---------------------------------------------------------------------------
// Erase SE,BE,CE Memory
//---------------------------------------------------------------------------
always @(SPI_SE_trg or SPI_BE_trg or SPI_CE_trg) begin :erase_setup_label
if(WEL === True && WSE === False) begin // check no suspend of sector/block
erase_active = True; // erase loop is active
valid_addr = False; // default valid address as bad
if(l_spi_cmd===SPI_CE || l_sqi_cmd===SQI_CE) begin // chip erase
CE_flag = True; BE_flag=False; SE_flag=False; // set erase type
time_left = Tsce; // erase time
erase_addr = 0; // chip erase address starts at 0
erase_time = Tsce;
erase_size = Memsize;
if(Chip_proT(erase_addr)===False && suspend_act===False) begin // check protected areas
valid_addr = True; // set address as valid
end
else begin
$display("\t%m Warning chip erase error, trying to erase protected Memory cmd aborted time=%0.2f",$realtime);
valid_addr = False;
end
end
else begin // read in 24 bit address
if(SQI_SPI_mode === False) begin // SPI
repeat(24) begin // read in address, set valid address flag when complete
erase_addr = erase_addr <<1;
@(posedge clock) erase_addr[0] = SIO[0];
end
end
else begin // SQI
repeat(6) begin // read in address, set valid address flag when complete
erase_addr = erase_addr <<4;
@(posedge clock) erase_addr[3:0] = SIO[3:0];
end
end
if(Write_proT(erase_addr)===False && PGM_ERASE(erase_addr,resume_addr)===False) valid_addr = True;
else begin
$display("\t%m Warning erase error, trying to erase protected Memory cmd aborted time=%0.2f",
$realtime);
valid_addr = False;
end
end
erase_addr = erase_addr & (Memsize-1); // clear unused upper address bits if address is greater tham memory size
if(l_spi_cmd===SPI_SE || l_sqi_cmd===SQI_SE) begin // Sector Erase ?
time_left = Tse; // time left to program
SE_flag=True; BE_flag=False; CE_flag = False; // set erase flag for SE
erase_size = Sector_Size; // set erase size
erase_addr[Sector_MSB:0] = 0; // clear unused lower address bits to 0
end
else if(l_spi_cmd===SPI_BE || l_sqi_cmd===SQI_BE) begin// Block erase ?
BE_flag=True; SE_flag=False; CE_flag = False; // set erase flag for BE
time_left = Tbe; // time left to program
// set block size, clear unused lower address bits to 0
if(erase_addr < (Kilo * 32)) begin erase_size=Block_08k; erase_addr[Block08k_MSB:0]=0; end
else if(erase_addr < (Kilo * 64)) begin erase_size=Block_32k; erase_addr[Block32k_MSB:0]=0; end
else if(erase_addr >= (Memsize-(Kilo * 32))) begin erase_size=Block_08k; erase_addr[Block08k_MSB:0]=0; end
else if(erase_addr >= Memsize-(Kilo * 64)) begin erase_size=Block_32k; erase_addr[Block32k_MSB:0]=0; end
else begin erase_size=Block_64k; erase_addr[Block64k_MSB:0]=0; end
end
forever @(posedge clock) ; // wait here for CEb to become inactice
erase_active = False; // erase loop is active
end
else begin
$display("\t%m Warning erase error,nested erase not allowed in suspend mode, cmd aborted time=%0.2f",$realtime);
end
end
//---------------------------------------------------------------------------
// page program Memory when CEb inactive ?
//---------------------------------------------------------------------------
always @(posedge CEb) begin :page_program_label
reg [AF_MSB:0] nn;
if(SPI_PP_active === True || SPI_QUAD_PP_active === True) begin // Page_Program is active
if(SPI_PP_active === True) begin // Page_Program_label is active
disable SPI_PP_label; // disable page program loop
SPI_PP_active = False; // clear program loop
end
else if(SPI_QUAD_PP_active === True) begin // Page_Program_label is active
disable SPI_QUAD_PP_label; // disable page program loop
SPI_QUAD_PP_active = False; // clear program loop
end
if(valid_data===True && valid_addr===True ) begin
page_program_active = True; // set busy
valid_addr = False; // default valid address
valid_data = False; // default valid data
suspend_addr = {pgm_addr[ADDR_MSB:8],8'h00}; // save program address for possible suspend
start_erase = $realtime; // save time of program/erase start
if (time_left == Tpp) begin
for(nn=0;nn<Program_Page_Size;nn=nn+1) begin // save current data in Memory
x_pmem[nn] = memory[{pgm_addr[ADDR_MSB:8],nn[7:0]}]; // save Memory data that will be written over
memory[{pgm_addr[ADDR_MSB:8],nn[7:0]}] = 8'hxx; // make data 'xx'
end
end
#time_left for(nn=0;nn<Program_Page_Size;nn=nn+1) begin
memory[{pgm_addr[ADDR_MSB:8],nn[7:0]}] = x_pmem[nn] & pmem[nn[7:0]];
//$display("\tprogram Memory add=%h, data=%h time=%0.2f",{pgm_addr[ADDR_MSB:8],nn[7:0]},(x_pmem[nn] & pmem[nn[7:0]]),$realtime);
//$display("\tnn=%h, nn[7:0]=%h, x_pmem[nn]=%h, pmem[nn[7:0]]=%h time=%0.2f",nn,nn[7:0],x_pmem[nn], pmem[nn[7:0]],$realtime);
end
page_program_active = False; // clear busy
WEL = False;
end
else begin
$display("\t%m Warning Page Program error, PP(h02)/PP(h32) cmd aborted time=%0.2f",$realtime);
end
end
end
//---------------------------------------------------------------------------
// QUAD Page program read in address, data, place program data into pgm_addr array
// When CEb goes high program loop is called using pgm_addr and pmem
//---------------------------------------------------------------------------
always @(SPI_QUAD_PP_trg) begin : SPI_QUAD_PP_label
reg [8:0] pcount;
reg [7:0] sdata;
if((IOC === False) && (SQI_SPI_mode === False)) $display("\t%m Warning SPI QUAD PAGE READ(h32) command aborted when IOC=0 time=%0.2f",$realtime);
else begin
valid_addr = False; // default valid address
valid_data = False; // default valid data
if(WEL === True && WSP === False) begin // check WSP flag, no program on program suspend active
SPI_QUAD_PP_active = True; // program loop is active
time_left = Tpp; // time left to program
for(pcount=0;pcount<Program_Page_Size;pcount = pcount+1) pmem[pcount] = 8'hFF; // clear program data to all 1's
repeat(6) begin // read in address, set valid address flag when complete
pgm_addr = pgm_addr <<4;
@(posedge clock) pgm_addr[3:0] = SIO[3:0];
end
pgm_addr = pgm_addr & (Memsize-1); // clear upper unused address bits
valid_addr = True; // address read complete set valid address flag
if(Write_proT(pgm_addr)===False && ERASE_PGM(resume_addr,pgm_addr)===False) begin // check for proteced Memory
valid_data = False; // no data valid
forever begin // Read program data, loop through all data abort on CEb rising
repeat(2) @(posedge clock ) begin // read in byte of data
valid_data = True; // at least 1 data clock
sdata = sdata <<4;
sdata[3:0] = SIO[3:0]; // read data as nibbles
end
pmem[pgm_addr[7:0]] = sdata; // save byte of page data
pgm_addr[7:0] = pgm_addr[7:0] + 1; // increment to next addr of page Memory, wrap on 256 byte bountry
end
end
else begin // protected Memory abort program
valid_addr = False; // default valid address
valid_data = False; // default valid data
SPI_QUAD_PP_active = False; // abort program on protected Memory
$display("\t%m Warning attempting to program protected page address=%h, PP(h32) cmd aborted time=%0.2f",
pgm_addr,$realtime);
end
SPI_QUAD_PP_active = False;
end
else begin
$display("\t%m Warning Nested Page Program not allowed in program suspend mode time=%0.2f",$realtime);
end
end
end
//---------------------------------------------------------------------------
// Page program read in address, data, place program data into pgm_addr array
// When CEb goes high program loop is called using pgm_addr and pmem
//---------------------------------------------------------------------------
always @(SPI_PP_trg) begin : SPI_PP_label
reg [8:0] pcount;
reg [7:0] sdata;
valid_addr = False; // default valid address
valid_data = False; // default valid data
time_left = Tpp; // time left to program
if(WEL === True && WSP === False) begin // check WSP status before programing, no programing on program suspend active
SPI_PP_active = True; // program loop is active
for(pcount=0;pcount<Program_Page_Size;pcount = pcount+1) pmem[pcount] = 8'hFF; // clear program data to all 1's
repeat(24) begin // read in address, set valid address flag when complete
pgm_addr = pgm_addr <<1;
@(posedge clock) pgm_addr[0] = SIO[0];
end
pgm_addr = pgm_addr & (Memsize-1); // clear upper unused address bits
valid_addr = True; // address read complete set valid address flag
if(Write_proT(pgm_addr)===False && ERASE_PGM(resume_addr,pgm_addr)===False) begin // check for proteced Memory
valid_data = False; // no data valid
forever begin // Read program data, loop through all data abort on CEb rising
repeat(8) @(posedge clock ) begin // read in byte of data
valid_data = True; // at least 1 valid data clock
sdata = sdata <<1;
sdata[0] = SIO[0];
end
pmem[pgm_addr[7:0]] = sdata; // save byte of page data
pgm_addr[7:0] = pgm_addr[7:0] + 1; // increment to next addr of page Memory, wrap on 256 byte bountry
end
end
else begin // protected Memory abort program
valid_addr = False; // default valid address
valid_data = False; // default valid data
SPI_PP_active = False; // abort program on protected Memory
if(ERASE_PGM(resume_addr,pgm_addr)===True) begin
$display("\t%m Warning attempting to program erase suspended Memory address=%h, PP(h02) cmd aborted time=%0.2f",
pgm_addr,$realtime);
end
else $display("\t%m Warning attempting to program protected page address=%h, PP(h02) cmd aborted time=%0.2f",pgm_addr,$realtime);
end
SPI_PP_active = False;
end
else begin
$display("\t%m Warning Nested Page Program not allowed in program suspend mode time=%0.2f",$realtime);
end
end
//---------------------------------------------------------------------------
// SPI Read block protection register
//---------------------------------------------------------------------------
always @(SPI_RBPR_trg) begin :SPI_RBPR_label
reg [PROTECT_REG_MSB:0] tmp_protect; // protection register definishion max size for 32M-bit
SPI_RBPR_active = True; // read l status loop is active
tmp_protect = protect_or; // copy protection reg
if(SQI_SPI_mode === True) begin
@(negedge clock) ; // wait here for clock falling
@(negedge clock) ; // wait here for clock falling
forever begin // out put SPI data bit by
@(negedge clock) ; // wait here for clock falling
SIO_IO <= #Tclz {True,True,True,True}; // Turn on IO control SIO[3:0]
#Tv begin // output nibble of data
SIO_OUT[3]=tmp_protect[PROTECT_REG_MSB]; tmp_protect = tmp_protect <<1; // shift data left
SIO_OUT[2]=tmp_protect[PROTECT_REG_MSB]; tmp_protect = tmp_protect <<1; // shift data left
SIO_OUT[1]=tmp_protect[PROTECT_REG_MSB]; tmp_protect = tmp_protect <<1; // shift data left
SIO_OUT[0]=tmp_protect[PROTECT_REG_MSB]; tmp_protect = tmp_protect <<1; // shift data left
end
end
end
else begin
forever begin // out put SPI data bit by
@(negedge clock) ; // wait here for clock falling
SIO_IO <= #Tclz {False,False,True,False}; // Turn on IO control SIO[1]
#Tv SIO_OUT[1]=tmp_protect[PROTECT_REG_MSB]; // shift out protection data
tmp_protect = tmp_protect <<1; // shift data left
end
end
SPI_RBPR_active = False; // read status loop is inactive
end
//----------------------------------------------------------------------------
// Program Block Protection Register volatile -volitale
//----------------------------------------------------------------------------
always @(SPI_WBPR_trg ) begin :SPI_WBPR_label
reg [8:0] ncount;
reg [7:0] bit_count; // number of bits to read
if(suspend_act===False && WBPR_protection_lck===False && WPLD===False) begin // check truth table table 2 in spec
SPI_WBPR_active = True; // this loop is active
ncount = PROTECT_REG_MSB;
if(SQI_SPI_mode === True) begin // SQI mode
repeat((PROTECT_REG_MSB+1)/4) begin
@(posedge clock) begin
protect[ncount] = SIO[3]; ncount = ncount - 1;
protect[ncount] = SIO[2]; ncount = ncount - 1;
protect[ncount] = SIO[1]; ncount = ncount - 1;
protect[ncount] = SIO[0]; ncount = ncount - 1;
end
end
end
else begin // SPI mode
repeat(PROTECT_REG_MSB+1) begin
@(posedge clock) protect[ncount] = SIO[0]; // save protection data
ncount = ncount - 1; // count the number of clocks
end
end
WEL = False; // clear WEL on WBPR command
forever @(posedge clock) ; // if to many clocks wait here for CEb to go inactive
end
else begin
if(WEL === False)
$display("\t%m Warning status flag WEL=0, WBPR(h42) cmd aborted time=%0.2f",$realtime);
else if(suspend_act===True)
$display("\t%m Warning WBPR not allowed in suspend mode, WBPR[h42) cmd aborted time=%0.2f",$realtime);
else if(WBPR_protection_lck === True)
$display("\t%m Warning Block Protection Reg protected, WBPR(h42) cmd aborted time=%0.2f",$realtime);
end
SPI_WBPR_active = False; // this loop is inactive
end
//----------------------------------------------------------------------------
// Program Block Protection Register non-volitale
//----------------------------------------------------------------------------
always @(SPI_nVWLDR_trg) begin :SPI_nVWLDR_cmd_label
reg [8:0] ncount;
reg [7:0] bit_count; // number of bits to read
t_wlldr_mem = wlldr_mem; // save current value of wlldr_mem
if(suspend_act===False && WBPR_protection_lck === False) begin // check truth table table 2 in spec
SPI_nVWLDR_cmd_active = True; // this loop is active
ncount = PROTECT_REG_MSB;
if(SQI_SPI_mode === True) begin // SQI mode
repeat((PROTECT_REG_MSB+1)/4) begin
@(posedge clock) begin
t_wlldr_mem[ncount] = SIO[3]; ncount = ncount - 1;
t_wlldr_mem[ncount] = SIO[2]; ncount = ncount - 1;
t_wlldr_mem[ncount] = SIO[1]; ncount = ncount - 1;
t_wlldr_mem[ncount] = SIO[0]; ncount = ncount - 1;
end
end
end
else begin //SPI mode
repeat(PROTECT_REG_MSB+1) begin
@(posedge clock) t_wlldr_mem[ncount] = SIO[0]; // save non-volatile data
ncount = ncount - 1; // count the number of clocks
end
end
forever @(posedge clock) ; // if to many clocks wait here for CEb to go inactive
end
else begin
if(WBPR_protection_lck === True)
$display("\t%m Warning nVWLDR(hE8) cmd aborted (protected) time=%0.2f",$realtime);
else if(suspend_act===True)
$display("\t%m Warning nVWLDR(E8) not allowed in suspend mode, nVWLDR(hE8) cmd aborted time=%0.2f",$realtime);
end
SPI_nVWLDR_cmd_active = False; // this loop is inactive
end
//---------------------------------------------------------------------------
// nVWLDR program command, program wlldr_mem[], wait for program complete
//---------------------------------------------------------------------------
always @(posedge CEb) begin :SPI_nVWLDR_label
reg [7:0]nn;
if(SPI_nVWLDR_cmd_active===True && suspend_act===False) begin
disable SPI_nVWLDR_cmd_label;
SPI_nVWLDR_cmd_active = False;
SPI_nVWLDR_active = True; // set busy
// make sure read protect flags are never set, clear the read flags
nn=0; repeat(8) begin t_wlldr_mem[PROTECT_REG_MSB-nn]=False; nn=nn+2; end
#Tpp wlldr_mem = wlldr_mem | t_wlldr_mem; // copy tmp data to final data, wait for program to complete
SPI_nVWLDR_active = False; // clear busy
WEL = False; // clear WEL on WBPR command
end
end
//---------------------------------------------------------------------------
// SPI Read configuration register
//---------------------------------------------------------------------------
always @(SPI_RDCR_trg) begin :SPI_RDCR_label
reg [AF_MSB:0] addr; // address storage
reg [7:0] data; // tmp storage of data
SPI_RDCR_active = True; // read l status loop is active
if(SQI_SPI_mode === True) begin // SQI mode ?
repeat(2) @(negedge clock) ; // dummy cycle
forever begin // out put SPI data bit by
data = config_reg; // byte boundry, save config register
@(negedge clock) ; // wait here for clock falling
SIO_IO <= #Tclz {True,True,True,True}; // Turn on IO control SIO[1]
#Tv SIO_OUT[3:0] = data[7:4]; // output high nibble
@(negedge clock) ; // wait here for clock falling
#Tv SIO_OUT[3:0] = data[3:0]; // output low nibble
end
end
else begin // SPI mode
forever begin // out put SPI data bit by bit
data = config_reg; // byte boundry, read configuration register
repeat(8) begin
@(negedge clock) ; // wait here for clock falling
SIO_IO <= #Tclz {False,False,True,False}; // Turn on IO control SIO[1]
#Tv SIO_OUT[1] = data[7]; // output 1 bit data
data = data <<1; // shift data left
end
end
end
SPI_RDCR_active = False; // read status loop is inactive
end
//---------------------------------------------------------------------------
// SPI Read status register
//---------------------------------------------------------------------------
always @(SPI_RDSR_trg) begin :SPI_RDSR_label
reg [AF_MSB:0] addr; // address storage
reg [7:0] data; // tmp storage of data
SPI_RDSR_active = True; // read l status loop is active
if(SQI_SPI_mode === True) begin // SQI mode ?
repeat(2) @(negedge clock) ; // dummy cycle
forever begin // out put SPI data bit by
data = status; // byte boundry, save status register
@(negedge clock) ; // wait here for clock falling
SIO_IO <= #Tclz {True,True,True,True}; // Turn on IO control SIO[1]
#Tv SIO_OUT[3:0] = data[7:4]; // output high nibble data
@(negedge clock) ; // wait here for clock falling
#Tv SIO_OUT[3:0] = data[3:0]; // output low nibble data
end
end
else begin // SPI mode
forever begin // out put SPI data bit by
data = status; // byte boundry, save status register
repeat(8) begin
@(negedge clock) ; // wait here for clock falling
SIO_IO <= #Tclz {False,False,True,False}; // Turn on IO control SIO[1]
#Tv SIO_OUT[1] = data[7]; // output 1 bit data
data = data <<1; // shift data left
end
end
end
SPI_RDSR_active = False; // read status loop is inactive
end
//----------------------------------------------------------------------------
// SPI Mode Read Serial Flash Discoverable Parameters
//----------------------------------------------------------------------------
always @(SPI_SFDP_trg ) begin :SPI_SFDP_label
reg [7:0] data;
reg [10:0] addr; // max value 2K-1
SPI_SFDP_active = True;
repeat(24) begin
@(posedge clock) begin // wait for clk rising
addr = addr <<1; // shift left address
addr[0] = SIO[0]; // read in address bit
end
end
repeat(8) @(posedge clock) ; // dummy cycle
forever begin // output SPI serial data
data = SFDP[addr]; // read from SFDP Memory
addr = addr + 1; // increment address
repeat(8) begin
@(negedge clock) ; // wait here for clock falling
SIO_IO <= #Tclz {False,False,True,False}; // Turn on IO control SIO[1]
#Tv SIO_OUT[1] = data[7]; // output 1 bit data
data = data <<1; // shift data left
end
end
SPI_SFDP_active = False;
end
//----------------------------------------------------------------------------
// SPI Mode Read JDEC registers
//----------------------------------------------------------------------------
always @(SPI_JEDEC_ID_trg ) begin :SPI_JEDEC_ID_label
reg [1:0] ptr;
reg [7:0] data;
SPI_JEDEC_ID_active = True;
ptr = 0;
if(SQI_SPI_mode === True)
begin
@(negedge clock) ;
@(negedge clock) ;
end
forever begin // output SPI serial data
if(ptr === 2'b00) data = MANUFACTURE;
else if(ptr === 2'b01) data = Memory_Type;
else if(ptr === 2'b10 ) data = Memory_Capacity;
if( ptr === 2'b10) ptr = 0; else ptr = ptr + 1;
if(SQI_SPI_mode === True) begin // SQI mode
@(negedge clock) ; // wait here for clock falling
SIO_IO <= #Tclz {True,True,True,True}; // Turn on IO control SIO[3:0]
#Tv SIO_OUT[3:0] = data[7:4]; // output nibble bit data
@(negedge clock) ; // wait here for clock falling
#Tv SIO_OUT[3:0] = data[3:0]; // output 1 nibble data
end
else begin // SPI mode
repeat(8) begin
@(negedge clock) ; // wait here for clock falling
SIO_IO <= #Tclz {False,False,True,False}; // Turn on IO control SIO[1]
#Tv SIO_OUT[1] = data[7]; // output 1 bit data
data = data <<1; // shift data left
end
end
end
SPI_JEDEC_ID_active = False;
end
//----------------------------------------------------------------------------
// Deep Power Down Reset Read device ID
//----------------------------------------------------------------------------
always @(SPI_DPD_RST_trg ) begin :SPI_DPD_RST_RDID_label
reg [7:0] data;
SPI_DPD_RST_RDID_active = True;
forever begin // output SPI serial data
data = Memory_Capacity;
if(SQI_SPI_mode === True) begin // SQI mode
@(negedge clock) ; // wait here for clock falling
SIO_IO <= #Tclz {True,True,True,True}; // Turn on IO control SIO[3:0]
#Tv SIO_OUT[3:0] = data[7:4]; // output nibble bit data
@(negedge clock) ; // wait here for clock falling
#Tv SIO_OUT[3:0] = data[3:0]; // output 1 nibble data
end
else begin // SPI mode
repeat(8) begin
@(negedge clock) ; // wait here for clock falling
SIO_IO <= #Tclz {False,False,True,False}; // Turn on IO control SIO[1]
#Tv SIO_OUT[1] = data[7]; // output 1 bit data
data = data <<1; // shift data left
end
end
end
SPI_DPD_RST_RDID_active = False;
end
//----------------------------------------------------------------------------
// Deep Power Down Reset - Recovery from Deep Power Down Mode
//----------------------------------------------------------------------------
always @(SPI_DPD_RST_trg ) begin :SPI_DPD_RST_label
reg [7:0] data;
@(posedge CEb)
#Tsbr DPD = False;
end
//---------------------------------------------------------------------------
// SPI Read with Burst
//---------------------------------------------------------------------------
always @(SPI_RBSPI_trg) begin :SPI_RBSPI_label
reg [AF_MSB:0] addr; // address storage
reg [7:0] data; // tmp storage of data
if(IOC === False && SQI_SPI_mode === False) $display("\t%m Warning SPI BURST READ(hEC) command aborted when IOC=0 time=%0.2f",$realtime);
else begin
SPI_RBSPI_active = True; // read loop is active
repeat(6) begin
@(posedge clock) ; // wait for clk rising
addr = addr <<4; // shift left address
addr[3:0] = SIO[3:0]; // read in address nibble
end
// read mode
repeat(6) @(posedge clock) ; // 3 dummy cycles
forever begin // output SPI data 1 byte
data = (Read_proT(addr)===True) ? 8'h00 : memory[addr[ADDR_MSB:0]]; // get data at addr
@(negedge clock) ; // wait here for clock falling
SIO_IO <= #Tclz {True,True,True,True}; // Turn on IO control SIO[3:0]
#Tv SIO_OUT[3:0] = data[7:4]; // output 4 bit data
@(negedge clock) ; // wait here for clock falling
#Tv SIO_OUT[3:0] = data[3:0]; // output 4 bit data
if(burst_length===Burst8) addr[Burst8_MSB:0]=addr[Burst8_MSB:0] + 1; // inc address with wrap
else if(burst_length===Burst16) addr[Burst16_MSB:0]=addr[Burst16_MSB:0] + 1;
else if(burst_length===Burst32) addr[Burst32_MSB:0]=addr[Burst32_MSB:0] + 1;
else if(burst_length===Burst64) addr[Burst64_MSB:0]=addr[Burst64_MSB:0] + 1;
end
SPI_RBSPI_active = False; // read loop is inactive
end
end
//---------------------------------------------------------------------------
// SPI Set Burst Count
//---------------------------------------------------------------------------
always @(SPI_SB_trg) begin :SPI_SB_label
reg [7:0] bl;
SPI_SB_active = True;
if(SQI_SPI_mode === True) begin
@(posedge clock) bl[7:4] = SIO[3:0];
@(posedge clock) bl[3:0] = SIO[3:0];
end
else begin
@(posedge clock) bl[7] = SIO[0]; // MSB bit of burst count
@(posedge clock) bl[6] = SIO[0]; // --- bit of burst count
@(posedge clock) bl[5] = SIO[0]; // --- bit of burst count
@(posedge clock) bl[4] = SIO[0]; // --- bit of burst count
@(posedge clock) bl[3] = SIO[0]; // --- bit of burst count
@(posedge clock) bl[2] = SIO[0]; // --- bit of burst count
@(posedge clock) bl[1] = SIO[0]; // --- bit of burst count
@(posedge clock) bl[0] = SIO[0]; // LSB bit of burst count
end
burst_length = bl; // set register
if( |burst_length[7:2] !== 1'b0) begin // check for legal values of burst count
$display("\t%m Warning SPI Set Burst Instruction has invalid data=%h, time=%0.2f", burst_length,$realtime);
$display("\t%m Setting bits[7:2] of Burst Count Register to 0");
burst_length[7:2] = 6'b000000; // clear upper bits
end
forever @(posedge clock) ; // wait for end of operation, Disable cmd will exit this line
SPI_SB_active = False;
end
//---------------------------------------------------------------------------
// SQI High Speed Read
//---------------------------------------------------------------------------
always @(SQI_HS_READ_trg) begin :SQI_HS_READ_label
reg [AF_MSB:0] addr; // address storage
reg [7:0] data; // tmp storage of data
reg [7:0] count;
SQI_HS_READ_active = True; // Read loop is active
if(Mode_Configuration[7:4]===4'hA) begin // if no command header read in 1st address nibble
addr[3:0] = SIO[3:0]; count = 5; // read in first address nibble
end
else count = 6; // read 6 times if command header
repeat(count) begin
@(posedge clock) // wait for clk rising
addr = addr <<4; // shift left address
addr[3:0] = SIO[3:0]; // read in address nibble
end
// read mode
@(posedge clock) Mode_Configuration[7:4]=SIO[3:0]; // read in Mode configuration
@(posedge clock) Mode_Configuration[3:0]=SIO[3:0]; // read in Mode configuration
// 4 dummy nibbles
repeat(4) @(posedge clock) ; // 2 dummy bytes
forever begin // output SPI data 1 byte
data = (Read_proT(addr)===True) ? 8'h00 : memory[addr[ADDR_MSB:0]]; // get data at addr
@(negedge clock) ; // wait here for clock falling
SIO_IO <= #Tclz {True,True,True,True}; // Turn on IO control SIO[3:0]
#Tv SIO_OUT[3:0] = data[7:4]; // output 4 bit data
@(negedge clock) ; // wait here for clock falling
#Tv SIO_OUT[3:0] = data[3:0]; // output 4 bit data
addr = addr + 1; // increment to next address on byte boundry
end
SQI_HS_READ_active = False;
end
//---------------------------------------------------------------------------
// SPI_READ DUAL IO
//---------------------------------------------------------------------------
always @(SPI_SDIOR_trg) begin :SPI_SDIOR_label
reg [AF_MSB:0] addr; // address storage
reg [7:0] data; // tmp storage of data
reg [7:0] count;
SPI_SDIOR_active = True;
if(Mode_Configuration[7:4]===4'hA) begin // if no command header read in 1st address nibble
addr[1:0] = SIO[1:0]; count = 11; // read in first address 2-bits
end
else count = 12; // read 6 times if command header
repeat(count) begin
@(posedge clock) begin // wait for clk rising
addr = addr <<2; // shift left address
addr[1:0] = SIO[1:0]; // read in address nibble
end
end
// read mode
@(posedge clock) Mode_Configuration[7:6]=SIO[1:0]; // read in Mode configuration
@(posedge clock) Mode_Configuration[5:4]=SIO[1:0]; // read in Mode configuration
@(posedge clock) Mode_Configuration[3:2]=SIO[1:0]; // read in Mode configuration
@(posedge clock) Mode_Configuration[1:0]=SIO[1:0]; // read in Mode configuration
forever begin // output SPI data 1 byte
data = (Read_proT(addr)===True) ? 8'h00 : memory[addr[ADDR_MSB:0]]; // get data at addr
@(negedge clock) ; // wait here for clock falling
SIO_IO <= #Tclz {False,False,True,True}; // Turn on IO control SIO[3:0]
#Tv SIO_OUT[3:0] = data[7:6]; // output 4 bit data
@(negedge clock) #Tv SIO_OUT[3:0] = data[5:4];
@(negedge clock) #Tv SIO_OUT[3:0] = data[3:2];
@(negedge clock) #Tv SIO_OUT[3:0] = data[1:0];
addr = addr + 1; // increment to next address on byte boundry
end
SPI_SDIOR_active = False;
end
//---------------------------------------------------------------------------
// SPI_READ QUAD IO
//---------------------------------------------------------------------------
always @(SPI_QUAD_IO_READ_trg) begin :SPI_QUAD_IO_READ_label
reg [AF_MSB:0] addr; // address storage
reg [7:0] data; // tmp storage of data
reg [7:0] count;
if(IOC === False) $display("\t%m Warning SPI IO QUAD READ(hEB) command aborted when IOC=0 time=%0.2f",$realtime);
else begin
SPI_QUAD_IO_READ_active = True;
if(Mode_Configuration[7:4]===4'hA) begin // if no command header read in 1st address nibble
addr[3:0] = SIO[3:0]; count = 5; // read in first address nibble
end
else count = 6; // read 6 times if command header
repeat(count) begin
@(posedge clock) begin // wait for clk rising
addr = addr <<4; // shift left address
addr[3:0] = SIO[3:0]; // read in address nibble
end
end
// read mode
@(posedge clock) Mode_Configuration[7:4]=SIO[3:0]; // read in Mode configuration
@(posedge clock) Mode_Configuration[3:0]=SIO[3:0]; // read in Mode configuration
// 2 dummy bytes
repeat(4) @(posedge clock) ; // 2 dummy bytes
forever begin // output SPI data 1 byte
data = (Read_proT(addr)===True) ? 8'h00 : memory[addr[ADDR_MSB:0]]; // get data at addr
@(negedge clock) ; // wait here for clock falling
SIO_IO <= #Tclz {True,True,True,True}; // Turn on IO control SIO[3:0]
#Tv SIO_OUT[3:0] = data[7:4]; // output 4 bit data
@(negedge clock) ; // wait here for clock falling
#Tv SIO_OUT[3:0] = data[3:0]; // output 4 bit data
addr = addr + 1; // increment to next address on byte boundry
end
SPI_QUAD_IO_READ_active = False;
end
end
//---------------------------------------------------------------------------
// SPI_READ QUAD
//---------------------------------------------------------------------------
always @(SPI_QUAD_READ_trg) begin :SPI_QUAD_READ_label
reg [AF_MSB:0] addr; // address storage
reg [7:0] data; // tmp storage of data
if(IOC === False) $display("\t%m Warning SPI QUAD READ(h6B) command aborted when IOC=0 time=%0.2f",$realtime);
else begin
SPI_READ_QUAD_active = True; // this loop is active
repeat(24) begin
@(posedge clock) begin // wait for clk rising
addr = addr <<1; // shift left address
addr[0] = SIO[0]; // read in address bit
end
end
// run 8 dummy cycles
repeat(8) @(negedge clock) ;
forever begin // output SPI data 1 byte
data = (Read_proT(addr)===True) ? 8'h00 : memory[addr[ADDR_MSB:0]]; // get data at addr
@(negedge clock) ; // wait here for clock falling
SIO_IO <= #Tclz {True,True,True,True}; // Turn on IO control SIO[3:0]
#Tv SIO_OUT[3:0] = data[7:4]; // output 4 bit data
@(negedge clock) ; // wait here for clock falling
#Tv SIO_OUT[3:0] = data[3:0]; // output 4 bit data
addr = addr + 1; // increment to next address on byte boundry
end
SPI_READ_QUAD_active = False; // this loop is active
end
end
//---------------------------------------------------------------------------
// SPI_READ dual, SDOR
//---------------------------------------------------------------------------
always @(SPI_SDOR_trg) begin :SPI_SDOR_label
reg [AF_MSB:0] addr; // address storage
reg [7:0] data; // tmp storage of data
SPI_SDOR_active = True; // read loop is active
repeat(24) begin
@(posedge clock) begin // wait for clk rising
addr = addr <<1; // shift left address
addr[0] = SIO[0]; // read in address bit
end
end
repeat(8) @(negedge clock) ; // dummy cycle for read
forever begin // out put SPI data 2 bits at a time
data=(Read_proT(addr)===True) ? 8'h00 : memory[addr[ADDR_MSB:0]]; // get data at addr
addr = addr + 1; // increment to next address on byte boundry
@(negedge clock) ; // wait here for clock falling
SIO_IO <= #Tclz {False,False,True,True}; // Turn on IO control SIO[1:0]
#Tv SIO_OUT[1:0] = data[7:6]; // output 2 bits data
@(negedge clock) #Tv SIO_OUT[1:0] = data[5:4]; // output 2 bits data
@(negedge clock) #Tv SIO_OUT[1:0] = data[3:2]; // output 2 bits data
@(negedge clock) #Tv SIO_OUT[1:0] = data[1:0]; // output 2 bits data
end
SPI_SDOR_active = False; // read loop is inactive
end
//---------------------------------------------------------------------------
// SPI_READ 80/50Mhz
//---------------------------------------------------------------------------
always @(SPI_READ_trg) begin :SPI_READ_label
reg [AF_MSB:0] addr; // address storage
reg [7:0] data; // tmp storage of data
SPI_READ_active = True; // read loop is active
repeat(24) begin
@(posedge clock) begin // wait for clk rising
addr = addr <<1; // shift left address
addr[0] = SIO[0]; // read in address bit
end
end
if(l_spi_cmd === SPI_HS_READ) repeat(8) @(negedge clock) ; // added dummy cycle for high speed read
if(l_spi_cmd === SPI_READ) read_slow_flag = True; // set timing checks to slow read for SCK timing check
forever begin // out put SPI data bit by
data = (Read_proT(addr)===True) ? 8'h00 : memory[addr[ADDR_MSB:0]]; // get data at addr
addr = addr + 1; // increment to next address on byte boundry
repeat(8) begin
@(negedge clock) ; // wait here for clock falling
SIO_IO <= #Tclz {False,False,True,False}; // Turn on IO control SIO[1]
#Tv SIO_OUT[1] = data[7]; // output 1 bit data
data = data <<1; // shift data left
end
end
read_slow_flag = False; // set timing checks back to normal
SPI_READ_active = False; // read loop is inactive
end
//---------------------------------------------------------------------------
// chip por setup
//---------------------------------------------------------------------------
initial begin
for(cnt=0;cnt<(Kilo*2);cnt=cnt+1) security_id[cnt] = 8'hFF; // init security Memory
for(cnt=0;cnt<Memsize;cnt=cnt+8) begin // init flash Memory
memory[cnt+0] = 8'hFF; memory[cnt+1] = 8'hFF; memory[cnt+2] = 8'hFF; memory[cnt+3] = 8'hFF;
memory[cnt+4] = 8'hFF; memory[cnt+5] = 8'hFF; memory[cnt+6] = 8'hFF; memory[cnt+7] = 8'hFF;
end
wlldr_mem = WLLD_value; // set contents of write-lock lock down register, non-volatile
SEC = SECURITY_LOCKOUT_VALUE; // Security ID Status, non-volatile
WPLD = False; // write protection lockdown status, non-volatile
clock = 1'b0;
WPEN = INIT_WPEN; // write protect pin enable, non-volatile bit
PE = False; // default unused configuration register bits
EE = False; // default unused configuration register bits
DPD = False; // deep power down mode
pgm_sus_reset = False; // part is busy if reset while programming is suspended
#0 ->reset; // call reset block
// set volatile protect register to initial condition
for(cnt=0;cnt<=PROTECT_REG_MSB;cnt=cnt+1) protect[cnt] = 1'b1; // all protect bits set
for(cnt=PROTECT_REG_MSB;cnt > (PROTECT_REG_MSB-16); cnt=cnt-2) protect[cnt] = 1'b0; // read protect bits cleared
// init serial flash discoverable parameters
for(cnt=0;cnt<(Kilo*2);cnt=cnt+1) SFDP[cnt] = 8'hFF; // init to all FF
SFDP['h000] = 8'h53;
SFDP['h001] = 8'h46;
SFDP['h002] = 8'h44;
SFDP['h003] = 8'h50;
SFDP['h004] = 8'h06;
SFDP['h005] = 8'h01;
SFDP['h006] = 8'h02;
SFDP['h007] = 8'hFF;
SFDP['h008] = 8'h00;
SFDP['h009] = 8'h06;
SFDP['h00A] = 8'h01;
SFDP['h00B] = 8'h10;
SFDP['h00C] = 8'h30;
SFDP['h00D] = 8'h00;
SFDP['h00E] = 8'h00;
SFDP['h00F] = 8'hFF;
SFDP['h010] = 8'h81;
SFDP['h011] = 8'h00;
SFDP['h012] = 8'h01;
SFDP['h013] = 8'h06;
SFDP['h014] = 8'h00;
SFDP['h015] = 8'h01;
SFDP['h016] = 8'h00;
SFDP['h017] = 8'hFF;
SFDP['h018] = 8'hBF;
SFDP['h019] = 8'h00;
SFDP['h01A] = 8'h01;
SFDP['h01B] = 8'h18;
SFDP['h01C] = 8'h00;
SFDP['h01D] = 8'h02;
SFDP['h01E] = 8'h00;
SFDP['h01F] = 8'h01;
SFDP['h030] = 8'hFD;
SFDP['h031] = 8'h20;
SFDP['h032] = 8'hF1;
SFDP['h033] = 8'hFF;
SFDP['h034] = 8'hFF;
SFDP['h035] = 8'hFF;
SFDP['h036] = 8'h7F;
SFDP['h037] = 8'h00;
SFDP['h038] = 8'h44;
SFDP['h039] = 8'hEB;
SFDP['h03A] = 8'h08;
SFDP['h03B] = 8'h6B;
SFDP['h03C] = 8'h08;
SFDP['h03D] = 8'h3B;
SFDP['h03E] = 8'h80;
SFDP['h03F] = 8'hBB;
SFDP['h040] = 8'hFE;
SFDP['h041] = 8'hFF;
SFDP['h042] = 8'hFF;
SFDP['h043] = 8'hFF;
SFDP['h044] = 8'hFF;
SFDP['h045] = 8'hFF;
SFDP['h046] = 8'h00;
SFDP['h047] = 8'hFF;
SFDP['h048] = 8'hFF;
SFDP['h049] = 8'hFF;
SFDP['h04A] = 8'h44;
SFDP['h04B] = 8'h0B;
SFDP['h04C] = 8'h0C;
SFDP['h04D] = 8'h20;
SFDP['h04E] = 8'h0D;
SFDP['h04F] = 8'hD8;
SFDP['h050] = 8'h0F;
SFDP['h051] = 8'hD8;
SFDP['h052] = 8'h10;
SFDP['h053] = 8'hD8;
SFDP['h054] = 8'h20;
SFDP['h055] = 8'h91;
SFDP['h056] = 8'h48;
SFDP['h057] = 8'h24;
SFDP['h058] = 8'h80;
SFDP['h059] = 8'h6F;
SFDP['h05A] = 8'h1D;
SFDP['h05B] = 8'h81;
SFDP['h05C] = 8'hED;
SFDP['h05D] = 8'h0F;
SFDP['h05E] = 8'h77;
SFDP['h05F] = 8'h38;
SFDP['h060] = 8'h30;
SFDP['h061] = 8'hB0;
SFDP['h062] = 8'h30;
SFDP['h063] = 8'hB0;
SFDP['h064] = 8'hF7;
SFDP['h065] = 8'hA9;
SFDP['h066] = 8'hD5;
SFDP['h067] = 8'h5C;
SFDP['h068] = 8'h29;
SFDP['h069] = 8'hC2;
SFDP['h06A] = 8'h5C;
SFDP['h06B] = 8'hFF;
SFDP['h06C] = 8'hF0;
SFDP['h06D] = 8'h30;
SFDP['h06E] = 8'hC0;
SFDP['h06F] = 8'h80;
SFDP['h100] = 8'hFF;
SFDP['h101] = 8'h00;
SFDP['h102] = 8'h04;
SFDP['h103] = 8'hFF;
SFDP['h104] = 8'hF3;
SFDP['h105] = 8'h7F;
SFDP['h106] = 8'h00;
SFDP['h107] = 8'h00;
SFDP['h108] = 8'hF5;
SFDP['h109] = 8'h7F;
SFDP['h10A] = 8'h00;
SFDP['h10B] = 8'h00;
SFDP['h10C] = 8'hF9;
SFDP['h10D] = 8'hFF;
SFDP['h10E] = 8'h0D;
SFDP['h10F] = 8'h00;
SFDP['h110] = 8'hF5;
SFDP['h111] = 8'h7F;
SFDP['h112] = 8'h00;
SFDP['h113] = 8'h00;
SFDP['h114] = 8'hF3;
SFDP['h115] = 8'h7F;
SFDP['h116] = 8'h00;
SFDP['h117] = 8'h00;
SFDP['h200] = 8'hBF;
SFDP['h201] = 8'h26;
SFDP['h202] = 8'h58;
SFDP['h203] = 8'hFF;
SFDP['h204] = 8'hB9;
SFDP['h205] = 8'hDF;
SFDP['h206] = 8'hFD;
SFDP['h207] = 8'hFF;
SFDP['h208] = 8'h65;
SFDP['h209] = 8'hF1;
SFDP['h20A] = 8'h95;
SFDP['h20B] = 8'hF1;
SFDP['h20C] = 8'h32;
SFDP['h20D] = 8'hFF;
SFDP['h20E] = 8'h0A;
SFDP['h20F] = 8'h12;
SFDP['h210] = 8'h23;
SFDP['h211] = 8'h46;
SFDP['h212] = 8'hFF;
SFDP['h213] = 8'h0F;
SFDP['h214] = 8'h19;
SFDP['h215] = 8'h32;
SFDP['h216] = 8'h0F;
SFDP['h217] = 8'h19;
SFDP['h218] = 8'h19;
SFDP['h219] = 8'h03;
SFDP['h21A] = 8'h0A;
SFDP['h21B] = 8'hFF;
SFDP['h21C] = 8'hFF;
SFDP['h21D] = 8'hFF;
SFDP['h21E] = 8'hFF;
SFDP['h21F] = 8'hFF;
SFDP['h220] = 8'h00;
SFDP['h221] = 8'h66;
SFDP['h222] = 8'h99;
SFDP['h223] = 8'h38;
SFDP['h224] = 8'hFF;
SFDP['h225] = 8'h05;
SFDP['h226] = 8'h01;
SFDP['h227] = 8'h35;
SFDP['h228] = 8'h06;
SFDP['h229] = 8'h04;
SFDP['h22A] = 8'h02;
SFDP['h22B] = 8'h32;
SFDP['h22C] = 8'hB0;
SFDP['h22D] = 8'h30;
SFDP['h22E] = 8'h72;
SFDP['h22F] = 8'h42;
SFDP['h230] = 8'h8D;
SFDP['h231] = 8'hE8;
SFDP['h232] = 8'h98;
SFDP['h233] = 8'h88;
SFDP['h234] = 8'hA5;
SFDP['h235] = 8'h85;
SFDP['h236] = 8'hC0;
SFDP['h237] = 8'h9F;
SFDP['h238] = 8'hAF;
SFDP['h239] = 8'h5A;
SFDP['h23A] = 8'hB9;
SFDP['h23B] = 8'hAB;
SFDP['h23C] = 8'h06;
SFDP['h23D] = 8'hEC;
SFDP['h23E] = 8'h06;
SFDP['h23F] = 8'h0C;
SFDP['h240] = 8'h00;
SFDP['h241] = 8'h03;
SFDP['h242] = 8'h08;
SFDP['h243] = 8'h0B;
SFDP['h244] = 8'hFF;
SFDP['h245] = 8'hFF;
SFDP['h246] = 8'hFF;
SFDP['h247] = 8'hFF;
SFDP['h248] = 8'hFF;
SFDP['h249] = 8'h07;
SFDP['h24A] = 8'hFF;
SFDP['h24B] = 8'hFF;
SFDP['h24C] = 8'h02;
SFDP['h24D] = 8'h02;
SFDP['h24E] = 8'hFF;
SFDP['h24F] = 8'h06;
SFDP['h250] = 8'h03;
SFDP['h251] = 8'h00;
SFDP['h252] = 8'hFD;
SFDP['h253] = 8'hFD;
SFDP['h254] = 8'h04;
SFDP['h255] = 8'h04;
SFDP['h256] = 8'h00;
SFDP['h257] = 8'hFC;
SFDP['h258] = 8'h03;
SFDP['h259] = 8'h00;
SFDP['h25A] = 8'hFE;
SFDP['h25B] = 8'hFE;
SFDP['h25C] = 8'h02;
SFDP['h25D] = 8'h02;
SFDP['h25E] = 8'h07;
SFDP['h25F] = 8'h0E;
end
always @(reset) begin
IOC = True; //False; // clear IOC status
WSE = False; // erase suspend status
RSTEN = False; // enable reset disabled
read_slow_flag = False;
RES = False; // reserved status bit 6
SIO_OUT = 4'h0; // turn off SIO drivers
spi_count = 0; // clear spi clock counter
spi_cmd = SPI_NOP; // clear SPI command register
sqi_cmd = SQI_NOP; // clear SQI command register
l_sqi_cmd = SQI_NOP;
l_spi_cmd = SPI_NOP;
RSTQIO_cmd = SPI_NOP;
SQI_SPI_mode = False; // set to spi mode
s_BE_flag=False; s_SE_flag=False;
SPI_READ_active = False;
SPI_READ_QUAD_active=False;
SPI_SDOR_active = False;
SPI_RDSR_active = False;
SPI_QUAD_IO_READ_active=False;
Mode_Configuration = 8'hFF; // default Mode Configuration
CE_flag=False; SE_flag=False; BE_flag=False;
SPI_SB_active = False;
SPI_JEDEC_ID_active = False;
SPI_SFDP_active = False;
burst_length=0; // set burst length to 8
SPI_PSID_ip = False;
SQI_HS_READ_active=False;
SPI_WRSR_PGM_active = False;
SPI_RBSPI_active=False;
SPI_WBPR_active = False;
SPI_nVWLDR_cmd_active=False;
SPI_RBPR_active = False;
SPI_SDIOR_active = False;
SPI_PP_active = False;
SQI_PP_active = False;
valid_addr = False; // default valid address
valid_data = False; // default valid data
SPI_RDCR_active = False;
SPI_QUAD_PP_active = False;
SPI_RSID_active = False;
erase_active = False; // erase loop is active
WEL = False; // clear Write enable latch in status register
#0 if(BUSY===True) begin // if busy abort erase/program in progress
if(page_program_active === True) begin // abort PP program
disable page_program_label; // abort spi page program in progress
disable Sec_ID_pgm_label; // Security ID program
#Trp page_program_active = False; // clear busy on program , wait for program abort time
SPI_PSID_ip = False; // abort security ID program loop
end
else if( erase_ip === True) begin // abort erase SE,BE,CE
WSP = False; // write suspend status
disable erase_label;
#Tre erase_ip = False;
end
else if(SPI_LSID_active === True) begin // abort Security ID lockout
disable SPI_LSID_label;
#Trp SPI_LSID_active = False;
SEC = 1'bx;
end
else if(SPI_WRSR_active===True) begin // reset during status register programing time
disable SPI_WRSR_PGM;
if(wsr_creg[7]===False) begin // WPEN <- 0
#Trp SPI_WRSR_active=False;
end
else begin // WPEN <- 1
#Tre SPI_WRSR_active=False;
end
end
else if(SPI_nVWLDR_active===True) begin // reset during WLLDR programing time
disable SPI_nVWLDR_label;
#Trp SPI_nVWLDR_active=False;
end
else if(SPI_WRSU_active===True) begin // reset during suspend #Tws time
disable SPI_WRSU_label; // exit suspent loop
#Trp SPI_WRSU_active=False; // clear suspend loop active flag
end
end
if (WSP === True) begin
WSP = False; // write suspend status
pgm_sus_reset = True;
#Trp pgm_sus_reset = False;
end
WSP = False; // write suspend status
suspend_addr=0; resume_addr=0;
SPI_WRSU_active = False;
SPI_PSID_active = False;
page_program_active = False;
erase_ip = False;
SPI_LSID_active = False;
SPI_WRSR_active=False;
SPI_nVWLDR_active=False;
SPI_LSID_active = False;
WEL = False; // clear Write enable latch in status register
end
//-------------------------------------------------------------
// protection functions, return True/False for read protection
// given and address
//-------------------------------------------------------------
function Read_proT;
input [AF_MSB:0] addr; // address
reg return_value;
reg [AF_MSB+1:0] taddr;
begin
// clear upper address bits that are unused
taddr = addr & (Memsize-1);
return_value = True; // set default return value
if(taddr < (Memsize-(Kilo*32)) && taddr >= (Kilo*32)) return_value = False; // check Memory that has no protection
else if(taddr < (Kilo*8)) return_value = protect[PROTECT_REG_MSB-14]; // check lower 8K
else if(taddr < (Kilo*16)) return_value = protect[PROTECT_REG_MSB-12]; // next 8k
else if(taddr < (Kilo*24)) return_value = protect[PROTECT_REG_MSB-10]; // next 8k
else if(taddr < (Kilo*32)) return_value = protect[PROTECT_REG_MSB-8]; // next 8k
else if(taddr >= (Memsize-(Kilo*8))) return_value = protect[PROTECT_REG_MSB-0]; // check top 8K
else if(taddr >= (Memsize-(Kilo*16))) return_value = protect[PROTECT_REG_MSB-2]; // next 8K
else if(taddr >= (Memsize-(Kilo*24))) return_value = protect[PROTECT_REG_MSB-4]; // next 8K
else if(taddr >= (Memsize-(Kilo*32))) return_value = protect[PROTECT_REG_MSB-6]; // next 8K
Read_proT = return_value; // return True/False for read protection at this address
end
endfunction
//-------------------------------------------------------------
// protection functions, return True/False for write protection
// given and address True = protected
//-------------------------------------------------------------
function Write_proT;
input [AF_MSB:0] addr; // address
reg [AF_MSB+1:0] address; // address
reg return_value;
reg [7:0] index;
reg [AF_MSB+1:0] taddr;
begin
// clear upper address bits that are unused
taddr = addr; taddr = taddr & (Memsize-1);
return_value = True; // set default return value
if(taddr < (Memsize-(Kilo*64)) && taddr >= (Kilo*64)) begin // check address 64K --> Memsize-64K
index = 0; // index to bottom of table
address=(Kilo*64); // starting address at bottom of table
while(address < (Memsize-(Kilo*64)) && return_value===True) begin // loop through each 64K block
if(taddr >= address && taddr < (address + (Kilo*64))) begin
if(protect_or[index] === False) return_value = False; // check protect flag
end
index = index + 1; // increment protect array pointer
address = address+(Kilo*64); // increment to next 64K protection block
end
end
// check lower 64k of Memory
else if(taddr < (Kilo*8) ) begin if(protect_or[PROTECT_REG_MSB-15]==False) return_value = False; end // check lower 8K
else if(taddr < (Kilo*16)) begin if(protect_or[PROTECT_REG_MSB-13]==False) return_value = False; end // next 8k
else if(taddr < (Kilo*24)) begin if(protect_or[PROTECT_REG_MSB-11]==False) return_value = False; end // next 8k
else if(taddr < (Kilo*32)) begin if(protect_or[PROTECT_REG_MSB- 9]==False) return_value = False; end // next 8k
else if(taddr < (Kilo*64)) begin if(protect_or[PROTECT_REG_MSB-17]==False) return_value = False; end // next 32k
// check upper 64k of Memory
else if(taddr >= (Memsize-(Kilo*8)) ) begin if(protect_or[PROTECT_REG_MSB-1]==False) return_value = False; end // check top 8K
else if(taddr >= (Memsize-(Kilo*16))) begin if(protect_or[PROTECT_REG_MSB-3]==False) return_value = False; end // next 8K
else if(taddr >= (Memsize-(Kilo*24))) begin if(protect_or[PROTECT_REG_MSB-5]==False) return_value = False; end // next 8K
else if(taddr >= (Memsize-(Kilo*32))) begin if(protect_or[PROTECT_REG_MSB-7]==False) return_value = False; end // next 8K
else if(taddr >= (Memsize-(Kilo*64))) begin if(protect_or[PROTECT_REG_MSB-16]==False) return_value = False; end // next 32K
Write_proT = return_value; // return True/False for read protection at this address
end
endfunction
//----------------------------------------------------------------
// check chip for any protection return False if OK to erase chip
//----------------------------------------------------------------
function Chip_proT;
input [AF_MSB:0] addr; // address, always 0
reg return_value;
begin
return_value = |protect_or[PROTECT_REG_MSB:0];
Chip_proT = return_value;
end
endfunction
//----------------------------------------------------------------------------------------
// check for program address matches erase address during suspend of block or sector erase
// in suspend mode verify that the address to be programed does not match suspended
// sector or block, return True if match
//----------------------------------------------------------------------------------------
function ERASE_PGM;
input [AF_MSB:0] erase_address_in; // erase address of suspended block/sector
input [AF_MSB:0] pgm_address_in; // address to program, check if it's in suspended sector/block
reg return_value;
reg [AF_MSB+1:0] erase_address,pgm_address;
begin
// clear unused upper address bits
erase_address = erase_address_in & (Memsize-1);
pgm_address = pgm_address_in & (Memsize-1);
return_value = False; // default to no match
if(WSE===True) begin // make sure you are in erase suspend mode
if(s_SE_flag === True) begin // check if SE when suspended, I.E. 4K Sector Size
if(erase_address[ADDR_MSB:12] === pgm_address[ADDR_MSB:12]) return_value = True;
end
// ---------------------------------------------------------------------
// if block erase you must check address for block size
// ---------------------------------------------------------------------
else if(s_BE_flag === True) begin // check if BE when suspended
if(erase_address < (Kilo * 32)) begin // 8K block size
if(erase_address[ADDR_MSB:13] === pgm_address[ADDR_MSB:13]) return_value = True;
end
else if( erase_address < (Kilo * 64)) begin // 32k Block size
if(erase_address[ADDR_MSB:15] === pgm_address[ADDR_MSB:15]) return_value = True;
end
else if( erase_address >= (Memsize-(Kilo * 32))) begin // 8K block size
if(erase_address[ADDR_MSB:13] === pgm_address[ADDR_MSB:13]) return_value = True;
end
else if( erase_address >= Memsize-(Kilo * 64)) begin // 32k Block size
if(erase_address[ADDR_MSB:15] === pgm_address[ADDR_MSB:15]) return_value = True;
end
else begin // 64K block size
if(erase_address[ADDR_MSB:16] === pgm_address[ADDR_MSB:16]) return_value = True;
end
end
end
ERASE_PGM = return_value;
end
endfunction
//----------------------------------------------------------------------------------------
// check for program address matches erase address during suspend of page program.
// In suspend mode verify that the address to be erased does not match suspended
// program address, return True if addresses match I.E. abort programing
//----------------------------------------------------------------------------------------
function PGM_ERASE;
input [AF_MSB:0] erase_address_in; // erase address of block/sector to be erased
input [AF_MSB:0] pgm_address_in; // suspended page program address
reg return_value;
reg [AF_MSB+1:0] erase_address, pgm_address;
begin
// clear upper unused address bits
erase_address = erase_address_in & (Memsize-1);
pgm_address = pgm_address_in & (Memsize-1);
return_value = False; // default to no match
if(WSP===True) begin // make sure you are in program suspend mode
if(l_spi_cmd===SPI_SE || l_sqi_cmd===SQI_SE) begin // sector erase 4K size ?
if(erase_address[AF_MSB:12] === pgm_address[AF_MSB:12]) return_value = True;
end
// ---------------------------------------------------------------------
// if block erase you must check address for block size
// ---------------------------------------------------------------------
else if(l_spi_cmd===SPI_BE || l_sqi_cmd===SQI_BE) begin
if(erase_address < (Kilo * 32)) begin // 8K block size
if(erase_address[AF_MSB:13] === pgm_address[AF_MSB:13]) return_value = True;
end
else if( erase_address < (Kilo * 64)) begin // 32k Block size
if(erase_address[AF_MSB:15] === pgm_address[AF_MSB:15]) return_value = True;
end
else if( erase_address >= (Memsize-(Kilo * 32))) begin // 8K block size
if(erase_address[AF_MSB:13] === pgm_address[AF_MSB:13]) return_value = True;
end
else if( erase_address >= Memsize-(Kilo * 64)) begin // 32k Block size
if(erase_address[AF_MSB:15] === pgm_address[AF_MSB:15]) return_value = True;
end
else begin // 64K block size
if(erase_address[AF_MSB:16] === pgm_address[AF_MSB:16]) return_value = True;
end
end
end
PGM_ERASE = return_value;
end
endfunction
`endprotect
endmodule