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