////////////////////////////////////////////////////////////////////////////// // File name : s25fl032p.v ////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2008-2011 Free Model Foundry; http://www.FreeModelFoundry.com // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // MODIFICATION HISTORY : // // version: | author: | mod date: | changes made: // V1.0 D.Stanojkovic 08 Jan 22 Inital Release // V1.1 D.Stanojkovic 08 Feb 20 BP bits setting corrected // V1.2 D.Stanojkovic 08 Mar 05 MPM mode corrected // V1.3 D.Stanojkovic 08 Mar 20 MPM mode removed // V1.4 J.Stoickov 08 Aug 07 WEL bit corrected // V1.5 J.Stoickov 08 Sep 16 Latest datasheet aligned // V1.6 J.Stoickov 08 Dec 10 Latest datasheet aligned // (S25FL129 064 032P) // Program Error bit will not be // set after programming in // protect memory region // V1.7 V.Mancev 10 Sep 29 Implementation of internal pull-up // for HOLDNeg and WPNeg pins // V1.8 V.Mancev 11 Dec 15 Latest datasheet aligned // (S25FL032P_00_05) ////////////////////////////////////////////////////////////////////////////// // PART DESCRIPTION: // // Library: FLASH // Technology: FLASH MEMORY // Part: S25FL032P // // Description: 32 Megabit Serial Flash Memory with 104 MHz SPI Bus Interface // Comments : // For correct simulation, simulator resolution should be set to 1 ps // ////////////////////////////////////////////////////////////////////////////// // Known Bugs: // ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // MODULE DECLARATION // ////////////////////////////////////////////////////////////////////////////// `timescale 1 ps/1 ps module s25fl032p ( SCK , SI , CSNeg , HOLDNeg , WPNeg , SO ); //////////////////////////////////////////////////////////////////////// // Port / Part SOIn Declarations //////////////////////////////////////////////////////////////////////// input SCK ; inout SI ; input CSNeg ; inout HOLDNeg ; inout WPNeg ; inout SO ; // interconnect path delay signals wire SCK_ipd ; wire SI_ipd ; wire SI_in ; assign SI_in = SI_ipd ; wire SI_out ; assign SI_out = SI ; wire CSNeg_ipd ; wire HOLDNeg_ipd ; wire WPNeg_ipd ; wire SO_ipd ; wire HOLDNeg_in ; //Internal pull-up assign HOLDNeg_in = (HOLDNeg_ipd === 1'bx) ? 1'b1 : HOLDNeg_ipd; wire HOLDNeg_out ; assign HOLDNeg_out = HOLDNeg ; wire WPNeg_in ; //Internal pull-up assign WPNeg_in = (WPNeg_ipd === 1'bx) ? 1'b1 : WPNeg_ipd; wire WPNeg_out ; assign WPNeg_out = WPNeg ; wire SOIn ; assign SOIn = SO_ipd ; wire SOut ; assign SOut = SO ; // internal delays reg PP_in ; reg PP_out ; reg PU_in ; reg PU_out ; reg SE_in ; reg SE_out ; reg BE_in ; reg BE_out ; reg PE_in ; reg PE_out ; reg WR_in ; reg WR_out ; reg DP_in ; reg DP_out ; reg EP_in ; reg EP_out ; reg RES_in ; reg RES_out ; reg SOut_zd = 1'bZ; reg SOut_z = 1'bZ; wire SI_z ; wire SO_z ; reg SIOut_zd = 1'bZ ; reg SIOut_z = 1'bZ ; reg WPNegOut_zd = 1'bZ ; reg HOLDNegOut_zd = 1'bZ ; assign SI_z = SIOut_z; assign SO_z = SOut_z; parameter UserPreload = 1; parameter mem_file_name = "none";//"s25fl032p.mem"; parameter otp_file_name = "none";//"s25fl032p_secsi.mem"; parameter TimingModel = "DefaultTimingModel"; parameter PartID = "s25fl032p"; parameter MaxData = 255; parameter SecSize = 16'hFFFF; parameter SecSize_4 = 4095; parameter SecSize_8 = 8191; parameter SecNum = 63; parameter PageNum = 16'h3FFF; parameter OTPSize = 511; parameter OTPLoAddr = 9'h100; parameter OTPHiAddr = 10'h2FF; parameter HiAddrBit = 23; parameter AddrRANGE = 24'h3FFFFF; parameter BYTE = 8; parameter Manuf_ID = 8'h01; parameter ES = 8'h15; parameter Jedec_ID = 8'h20; // first byte of Device ID parameter DeviceID = 16'h0215; parameter ExtendedBytes = 8'h4D; parameter ReservedBytes = 8'h00; // If speed simulation is needed uncomment following line // `define SPEEDSIM; // powerup reg PoweredUp; //FSM control signals reg PDONE ; ////Prog. Done reg PSTART ; ////Start Programming reg EDONE ; ////Era. Done reg ESTART ; ////Start Erasing reg WDONE ; //// Writing Done reg WSTART ; ////Start writing //Command Register reg write; reg cfg_write; reg read_out; //Status reg. reg[7:0] Status_reg = 8'b0; reg[7:0] Status_reg_in = 8'b0; reg[7:0] Sec_conf_reg = 8'b0; reg[7:0] Sec_conf_reg_in = 8'b0; wire FREEZE; wire QUAD; wire TBPARM; wire BPNV; wire TBPROT; assign FREEZE = Sec_conf_reg[0]; assign QUAD = Sec_conf_reg[1]; assign TBPARM = Sec_conf_reg[2]; assign BPNV = Sec_conf_reg[3]; assign TBPROT = Sec_conf_reg[5]; wire WIP; wire WEL; wire [2:0]BP; wire E_ERR; wire P_ERR; wire SRWD; assign WIP = Status_reg[0]; assign WEL = Status_reg[1]; assign BP = Status_reg[4:2]; assign E_ERR = Status_reg[5]; assign P_ERR = Status_reg[6]; assign SRWD = Status_reg[7]; integer SA = 0; // 0 TO SecNum+1 integer Byte_number = 0; integer sect; //Address integer Address = 0; // 0 - AddrRANGE reg change_addr; reg rd_fast; // = 1'b1; reg rd_slow; reg rd_jedec; reg change_BP = 0; reg rd_dual; reg dual; wire fast_rd; wire rd; wire rd_jid; wire RD_EQU_1; assign RD_EQU_1 = rd_slow; wire RD_EQU_0; assign RD_EQU_0 = ~rd_slow; reg hold_mode; reg mpm_mode = 0; wire hold; //Sector Protection Status reg [SecNum:0] Sec_Prot = 64'b0; //= SecNum'b0; // timing check violation reg Viol = 1'b0; integer Mem[0:AddrRANGE]; integer OTPMem[OTPLoAddr: OTPHiAddr]; integer WByte[0:255]; integer WOTPByte; integer AddrLo; integer AddrHi; reg[7:0] old_bit, new_bit; integer old_int, new_int; integer wr_cnt; integer cnt; integer read_cnt = 0; integer read_addr = 0; reg[7:0] data_out; reg[647:0] ident_out; reg oe = 1'b0; event oe_event; integer CFI_array[8'h07:8'h50]; reg [591:0] CFI_array_tmp; reg [7:0] CFI_tmp; reg[15:0] PR_LOCK1; reg[15:0] PR_LOCK2; reg[15:0] PR_LOCK3; /////////////////////////////////////////////////////////////////////////////// //Interconnect Path Delay Section /////////////////////////////////////////////////////////////////////////////// buf (SCK_ipd, SCK); buf (SI_ipd, SI); buf (CSNeg_ipd, CSNeg); buf (HOLDNeg_ipd, HOLDNeg); buf (WPNeg_ipd, WPNeg); buf (SO_ipd, SO); /////////////////////////////////////////////////////////////////////////////// // Propagation delay Section /////////////////////////////////////////////////////////////////////////////// nmos (SO, SO_z , 1); nmos (SI, SI_z , 1); nmos (HOLDNeg, HOLDNegOut_zd , 1); nmos (WPNeg, WPNegOut_zd , 1); wire deg_pin; wire deg_sin; wire deg_holdin; //VHDL VITAL CheckEnable equivalents wire dual_wr; assign dual_wr = deg_holdin && QUAD; wire dual_rd; assign dual_rd = dual; wire quad_wr; assign quad_wr = SRWD && WEL ; wire power; assign power = PoweredUp; specify // tipd delays: interconnect path delays , mapped to input port delays. // In Verilog is not necessary to declare any tipd_ delay variables, // they can be taken from SDF file // With all the other delays real delays would be taken from SDF file // tpd delays specparam tpd_SCK_SO =1; specparam tpd_SCK_SI =1; specparam tpd_CSNeg_SO =1; specparam tpd_HOLDNeg_SO =1; specparam tsetup_SI_SCK =1; //tsuDAT / specparam tsetup_CSNeg_SCK =1; // tCSS / specparam tsetup_HOLDNeg_SCK =1; //tHD / specparam tsetup_SCK_HOLDNeg =1; //tCH \ specparam tsetup_WPNeg_CSNeg =1; //tWPS \ // thold values: hold times specparam thold_SI_SCK =1; //thdDAT / specparam thold_CSNeg_SCK =1; //tCSH / specparam thold_HOLDNeg_SCK =1; //tCHHH / specparam thold_SCK_HOLDNeg =1; //tHC \ specparam thold_WPNeg_CSNeg =1; //tWPH \ // tpw values: pulse width specparam tpw_SCK_fast_posedge =1; //tWH specparam tpw_SCK_posedge =1; //tWH specparam tpw_SCK_dual_posedge =1; //tWH specparam tpw_SCK_rdid_posedge =1; //tWH specparam tpw_SCK_fast_negedge =1; //tWL specparam tpw_SCK_negedge =1; //tWL specparam tpw_SCK_dual_negedge =1; //tWL specparam tpw_SCK_rdid_negedge =1; //tWL specparam tpw_CSNeg_read_posedge =1; //tCS specparam tpw_CSNeg_pgm_posedge =1; //tCS // tperiod min (calculated as 1/max freq) specparam tperiod_SCK_rd =1; // fSCK = 40MHz specparam tperiod_SCK_fast_rd =1; // fSCK = 104MHz specparam tperiod_SCK_dual_fast_rd =1; // fSCK = 80MHz specparam tperiod_SCK_rdid =1; // fSCK = 50MHz // tdevice values: values for internal delays `ifdef SPEEDSIM // Page Program Operation specparam tdevice_PP = 3e7; //30 us; //Page Program Operation (ACC=9V)) specparam tdevice_EP = 2.4e9; //2.4 ms; //Sector Erase Operation specparam tdevice_SE = 2e9; //2 ms; //Bulk Erase Operation specparam tdevice_BE = 64e9; //64 ms; //Write Status Register Operation specparam tdevice_WR = 5e8; // 0.5 ms; //Software Protect Mode specparam tdevice_DP = 10000000; // 10 us; //Release from Software Protect Mode specparam tdevice_RES = 30000000; // 30 us; //Parameter block erase specparam tdevice_PE = 800e6; //800 us; //VCC (min) to CS# Low specparam tdevice_PU = 300000000; //300 us; `else // Page Program Operation specparam tdevice_PP = 3e9; //3 ms; //Page Program Operation (ACC=9V)) specparam tdevice_EP = 2.4e9; //2.4 ms; //Sector Erase Operation specparam tdevice_SE = 2e12; //2 s; //Bulk Erase Operation specparam tdevice_BE = 64e12; //64 sec; //Write Status Register Operation specparam tdevice_WR = 50e9; // 50 ms; //Software Protect Mode specparam tdevice_DP = 10000000; // 10 us; //Release from Software Protect Mode specparam tdevice_RES = 30000000; // 30 us; //Parameter block erase specparam tdevice_PE = 800e9; //800 ms; //VCC (min) to CS# Low specparam tdevice_PU = 300000000; //300 us; `endif//SPEEDSIM /////////////////////////////////////////////////////////////////////////////// // Input Port Delays don't require Verilog description /////////////////////////////////////////////////////////////////////////////// // Path delays // /////////////////////////////////////////////////////////////////////////////// if (~dual) (SCK => SO) = tpd_SCK_SO; if (dual) (SCK => SO) = tpd_SCK_SI; if (CSNeg)(CSNeg => SO) = tpd_CSNeg_SO; if (~dual) (HOLDNeg => SO) = tpd_HOLDNeg_SO; if (dual) (HOLDNeg => SO) = tpd_HOLDNeg_SO; if (dual)(SCK => SI) = tpd_SCK_SI; if (dual && CSNeg)(CSNeg => SI) = tpd_CSNeg_SO; if (dual)(HOLDNeg => SI) = tpd_HOLDNeg_SO; if (dual && QUAD)(SCK => HOLDNeg) = tpd_SCK_SI; if (dual && CSNeg && QUAD)(CSNeg => HOLDNeg) = tpd_CSNeg_SO; if (dual && QUAD)(SCK => WPNeg) = tpd_SCK_SI; if (dual && CSNeg && QUAD)(CSNeg => WPNeg) = tpd_CSNeg_SO; //////////////////////////////////////////////////////////////////////////////// // Timing Violation // //////////////////////////////////////////////////////////////////////////////// $setup ( SI , posedge SCK &&& deg_sin, tsetup_SI_SCK, Viol); $setup ( negedge HOLDNeg, posedge SCK &&& dual_wr, tsetup_HOLDNeg_SCK, Viol); $setup ( posedge SCK , posedge HOLDNeg &&& dual_wr, tsetup_SCK_HOLDNeg, Viol); $setup ( CSNeg , posedge SCK &&& power, tsetup_CSNeg_SCK, Viol); $setup ( WPNeg , negedge CSNeg &&& WPNeg, tsetup_WPNeg_CSNeg, Viol); $hold ( posedge SCK , SI &&& deg_sin, thold_SI_SCK, Viol); $hold ( posedge HOLDNeg , posedge SCK &&& dual_wr, thold_SCK_HOLDNeg, Viol); $hold ( posedge SCK , CSNeg &&& power, thold_CSNeg_SCK, Viol); $hold ( posedge CSNeg , WPNeg &&& quad_wr, thold_WPNeg_CSNeg, Viol); $hold ( posedge SCK , negedge HOLDNeg &&& dual_wr, thold_HOLDNeg_SCK, Viol); $width (posedge SCK &&& rd , tpw_SCK_posedge); $width (posedge SCK &&& fast_rd , tpw_SCK_fast_posedge); $width (posedge SCK &&& dual_rd , tpw_SCK_dual_posedge); $width (posedge SCK &&& rd_jid , tpw_SCK_rdid_posedge); $width (negedge SCK &&& rd , tpw_SCK_negedge); $width (negedge SCK &&& fast_rd , tpw_SCK_fast_negedge); $width (negedge SCK &&& dual_rd , tpw_SCK_dual_negedge); $width (negedge SCK &&& rd_jid , tpw_SCK_rdid_negedge); $width (posedge CSNeg &&& RD_EQU_0, tpw_CSNeg_pgm_posedge); $width (posedge CSNeg &&& RD_EQU_1, tpw_CSNeg_read_posedge); $period (posedge SCK &&& rd, tperiod_SCK_rd); $period (posedge SCK &&& fast_rd, tperiod_SCK_fast_rd); $period (posedge SCK &&& dual_rd, tperiod_SCK_dual_fast_rd); $period (posedge SCK &&& rd_jid, tperiod_SCK_rdid); endspecify //////////////////////////////////////////////////////////////////////////////// // Main Behavior Block // //////////////////////////////////////////////////////////////////////////////// // FSM states parameter IDLE =4'd0; parameter WRITE_SR =4'd1; parameter DP_DOWN_WAIT =4'd2; parameter DP_DOWN =4'd3; parameter SECTOR_ER =4'd4; parameter BULK_ER =4'd5; parameter PAGE_PG =4'd6; parameter OTP_PG =4'd7; parameter P4_ER =4'd8; parameter P8_ER =4'd9; reg [3:0] current_state; reg [3:0] next_state; // Instructions parameter NONE =5'd0; parameter WREN =5'd1; parameter WRDI =5'd2; parameter WRR =5'd3; parameter RDSR =5'd4; parameter READ =5'd5; parameter READ_ID =5'd6; parameter RDID =5'd7; parameter FAST_READ =5'd8; parameter DUAL_READ =5'd9; parameter QUAD_READ =5'd10; parameter DH_READ =5'd11; parameter QH_READ =5'd12; parameter SE =5'd13; parameter BE =5'd14; parameter PP =5'd15; parameter QPP =5'd16; parameter DP =5'd17; parameter RES_READ_ES =5'd18; parameter CLSR =5'd19; parameter RCR =5'd20; parameter P4E =5'd21; parameter P8E =5'd22; parameter OTPP =5'd23; parameter OTPR =5'd24; reg [4:0] Instruct; //Bus cycle states parameter STAND_BY =3'd0; parameter CODE_BYTE =3'd1; parameter ADDRESS_BYTES =3'd2; parameter DUMMY_BYTES =3'd3; parameter MODE_BYTE =3'd4; parameter DATA_BYTES =3'd5; reg [2:0] bus_cycle_state; reg deq_pin; always @(SOIn, SO_z) begin if (SOIn==SO_z) deq_pin=1'b0; else deq_pin=1'b1; end // chech when data is generated from model to avoid setuphold check in // those occasion assign deg_pin=deq_pin; reg deq_sin; always @(SI_in, SIOut_z) begin if (SI_in==SIOut_z) deq_sin=1'b0; else deq_sin=1'b1; end // chech when data is generated from model to avoid setuphold check in // those occasion assign deg_sin=deq_sin; reg deq_holdin; always @(HOLDNeg_ipd, HOLDNegOut_zd) begin if (HOLDNeg_ipd==HOLDNegOut_zd) deq_holdin=1'b0; else deq_holdin=1'b1; end // check when data is generated from model to avoid setuphold check in // those occasion assign deg_holdin=deq_holdin; initial begin : Init write = 1'b0; cfg_write = 1'b0; read_out = 1'b0; Address = 0; change_addr = 1'b0; PDONE = 1'b1; PSTART = 1'b0; EDONE = 1'b1; ESTART = 1'b0; WDONE = 1'b1; WSTART = 1'b0; DP_in = 1'b0; DP_out = 1'b0; RES_in = 1'b0; RES_out = 1'b0; Instruct = NONE; bus_cycle_state = STAND_BY; current_state = IDLE; next_state = IDLE; end //CFI initial begin: InitCFI integer i; integer j; CFI_array[8'h07] = 8'hFF; CFI_array[8'h08] = 8'hFF; CFI_array[8'h09] = 8'hFF; CFI_array[8'h0A] = 8'hFF; CFI_array[8'h0B] = 8'hFF; CFI_array[8'h0C] = 8'hFF; CFI_array[8'h0D] = 8'hFF; CFI_array[8'h0E] = 8'hFF; CFI_array[8'h0F] = 8'hFF; CFI_array[8'h10] = 8'h51; CFI_array[8'h11] = 8'h52; CFI_array[8'h12] = 8'h59; CFI_array[8'h13] = 8'h02; CFI_array[8'h14] = 8'h00; CFI_array[8'h15] = 8'h40; CFI_array[8'h16] = 8'h00; CFI_array[8'h17] = 8'h00; CFI_array[8'h18] = 8'h00; CFI_array[8'h19] = 8'h00; CFI_array[8'h1A] = 8'h00; //System interface string CFI_array[8'h1B] = 8'h27; CFI_array[8'h1C] = 8'h36; CFI_array[8'h1D] = 8'h00; CFI_array[8'h1E] = 8'h00; CFI_array[8'h1F] = 8'h0B; CFI_array[8'h20] = 8'h0B; CFI_array[8'h21] = 8'h09; CFI_array[8'h22] = 8'h0F; CFI_array[8'h23] = 8'h01; CFI_array[8'h24] = 8'h01; CFI_array[8'h25] = 8'h02; CFI_array[8'h26] = 8'h01; //device geometry definition CFI_array[8'h27] = 8'h16; CFI_array[8'h28] = 8'h05; CFI_array[8'h29] = 8'h05; CFI_array[8'h2A] = 8'h08; CFI_array[8'h2B] = 8'h00; CFI_array[8'h2C] = 8'h02; CFI_array[8'h2D] = 8'h1F; CFI_array[8'h2E] = 8'h00; CFI_array[8'h2F] = 8'h10; CFI_array[8'h30] = 8'h00; CFI_array[8'h31] = 8'h3D; CFI_array[8'h32] = 8'h00; CFI_array[8'h33] = 8'h00; CFI_array[8'h34] = 8'h01; CFI_array[8'h35] = 8'h00; CFI_array[8'h36] = 8'h00; CFI_array[8'h37] = 8'h00; CFI_array[8'h38] = 8'h00; CFI_array[8'h39] = 8'h00; CFI_array[8'h3A] = 8'h00; CFI_array[8'h3B] = 8'h00; CFI_array[8'h3C] = 8'h00; CFI_array[8'h3D] = 8'hFF; CFI_array[8'h3E] = 8'hFF; CFI_array[8'h3F] = 8'hFF; //primary vendor-specific extended query CFI_array[8'h40] = 8'h50; CFI_array[8'h41] = 8'h52; CFI_array[8'h42] = 8'h49; CFI_array[8'h43] = 8'h31; CFI_array[8'h44] = 8'h33; CFI_array[8'h45] = 8'h15; CFI_array[8'h46] = 8'h00; CFI_array[8'h47] = 8'h01; CFI_array[8'h48] = 8'h00; CFI_array[8'h49] = 8'h05; CFI_array[8'h4A] = 8'h00; CFI_array[8'h4B] = 8'h01; CFI_array[8'h4C] = 8'h03; CFI_array[8'h4D] = 8'h85; CFI_array[8'h4E] = 8'h95; CFI_array[8'h4F] = 8'h07; CFI_array[8'h50] = 8'h00; for(i=73;i>=0;i=i-1) begin CFI_tmp = CFI_array[8'h07-i+73]; for(j=7;j>=0;j=j-1) begin CFI_array_tmp[8*i+j] = CFI_tmp[j]; end end end // initialize memory initial begin: InitMemory integer i; for (i=0;i<=AddrRANGE;i=i+1) begin Mem[i] = MaxData; end if ((UserPreload) && !(mem_file_name == "none")) begin // Memory Preload // s25fl032p.mem, memory preload file // @aaaaaa - stands for address // dd -
is byte to be written at Mem(aaaaaa++) // (aaaaaa is incremented at every load) $readmemh(mem_file_name,Mem); end for (i=OTPLoAddr;i<=OTPHiAddr;i=i+1) begin OTPMem[i] = MaxData; end if (UserPreload && !(otp_file_name == "none")) begin //s25fl032p_secsi memory file // / - comment // @aaaaaa - stands for address within last defined // sector // dd -
is byte to be written at SecSi(aaaaaa++) // (aa is incremented at every load) // only first 1-5 columns are loaded. NO empty lines !!!!!!!!!!!!!!!! $readmemh(otp_file_name,OTPMem); end PR_LOCK1[15:8] = 16'h0; PR_LOCK1[7:0] = OTPMem[256]; PR_LOCK2[15:8] = OTPMem[275]; PR_LOCK2[7:0] = OTPMem[274]; PR_LOCK3[15:8] = OTPMem[533]; PR_LOCK3[7:0] = OTPMem[532]; end //Power Up time; initial begin PoweredUp = 1'b0; #tdevice_PU PoweredUp = 1'b1; end always @(posedge DP_in) begin:TDPr #tdevice_DP DP_out = DP_in; end always @(negedge DP_in) begin:TDPf #1 DP_out = DP_in; end always @(posedge RES_in) begin:TRESr #tdevice_RES RES_out = RES_in; end always @(negedge RES_in) begin:TRESf #1 RES_out = RES_in; end always @(next_state or PoweredUp) begin: StateTransition if (PoweredUp) begin current_state = next_state; end end always @(negedge CSNeg_ipd) begin:CheckCSOnPowerUP if (~PoweredUp) $display ("Device is selected during Power Up"); end // /////////////////////////////////////////////////////////////////////////// // // Instruction cycle decode // /////////////////////////////////////////////////////////////////////////// integer data_cnt = 0; integer addr_cnt = 0; integer code_cnt = 0; integer mode_cnt = 0; integer dummy_cnt = 0; integer bit_cnt = 0; reg[2047:0] Data_in = 2048'b0; integer quad_data_in [0:511]; reg[3:0] quad_nybble = 4'b0; reg[3:0] Quad_slv = 4'b0; reg[7:0] code = 8'b0; reg[7:0] code_in = 8'b0; reg[7:0] Byte_slv = 8'b0; reg[HiAddrBit:0] addr_bytes; reg[23:0] Address_in = 8'b0; reg[7:0] mode_bytes; reg[7:0] mode_in; reg rising_edge_CSNeg_ipd = 1'b0; reg falling_edge_CSNeg_ipd = 1'b0; reg rising_edge_SCK_ipd = 1'b0; reg falling_edge_SCK_ipd = 1'b0; always @(falling_edge_CSNeg_ipd or rising_edge_CSNeg_ipd or rising_edge_SCK_ipd or falling_edge_SCK_ipd) begin: Buscycle1 integer i; integer j; integer k; if (falling_edge_CSNeg_ipd) begin if (bus_cycle_state==STAND_BY) begin bus_cycle_state = CODE_BYTE; Instruct = NONE; write = 1'b1; code_cnt = 0; addr_cnt = 0; data_cnt = 0; mode_cnt = 0; dummy_cnt = 0; end end if (rising_edge_SCK_ipd) begin if (~CSNeg_ipd) begin case (bus_cycle_state) CODE_BYTE : begin if ((HOLDNeg_in && ~QUAD) || QUAD) begin code_in[code_cnt] = SI_in; code_cnt = code_cnt + 1; if (code_cnt == BYTE) begin for (i=0;i<=7;i=i+1) begin code[i] = code_in[7-i]; end case(code) 8'b00000110 : begin Instruct = WREN; bus_cycle_state = DATA_BYTES; end 8'b00000100 : begin Instruct = WRDI; bus_cycle_state = DATA_BYTES; end 8'b00000001 : begin Instruct = WRR; bus_cycle_state = DATA_BYTES; end 8'b00000101 : begin Instruct = RDSR; bus_cycle_state = DATA_BYTES; end 8'b00000011 : begin Instruct = READ; bus_cycle_state = ADDRESS_BYTES; end 8'b00001011 : begin Instruct = FAST_READ; bus_cycle_state = ADDRESS_BYTES; end 8'b10011111 : begin Instruct = RDID; bus_cycle_state = DATA_BYTES; end 8'b10010000 : begin Instruct = READ_ID; bus_cycle_state = ADDRESS_BYTES; end 8'b10101011 : begin Instruct = RES_READ_ES; bus_cycle_state = DUMMY_BYTES; end 8'b11011000 : begin Instruct = SE; bus_cycle_state = ADDRESS_BYTES; end 8'b11000111 , 8'b01100000: begin Instruct = BE; bus_cycle_state = DATA_BYTES; end 8'b00000010 : begin Instruct = PP; bus_cycle_state = ADDRESS_BYTES; end 8'b00110010 : begin Instruct = QPP; bus_cycle_state = ADDRESS_BYTES; end 8'b10111001 : begin Instruct = DP; bus_cycle_state = DATA_BYTES; end 8'b00110000 : begin Instruct = CLSR; bus_cycle_state = DATA_BYTES; end 8'b00110101 : begin Instruct = RCR; bus_cycle_state = DATA_BYTES; end 8'b00100000 : begin Instruct = P4E; bus_cycle_state = ADDRESS_BYTES; end 8'b01000000 : begin Instruct = P8E; bus_cycle_state = ADDRESS_BYTES; end 8'b01000010 : begin Instruct = OTPP; bus_cycle_state = ADDRESS_BYTES; end 8'b01001011 : begin Instruct = OTPR; bus_cycle_state = ADDRESS_BYTES; end 8'b00111011 : begin Instruct = DUAL_READ; bus_cycle_state = ADDRESS_BYTES; end 8'b01101011 : begin Instruct = QUAD_READ; bus_cycle_state = ADDRESS_BYTES; end 8'b10111011 : begin Instruct = DH_READ; bus_cycle_state = ADDRESS_BYTES; end 8'b11101011 : begin Instruct = QH_READ; bus_cycle_state = ADDRESS_BYTES; end endcase end end end ADDRESS_BYTES : begin if (((Instruct == FAST_READ || Instruct == OTPR || Instruct == DUAL_READ) && ((HOLDNeg_in && ~QUAD) || QUAD)) || ((Instruct == QUAD_READ) && QUAD)) begin Address_in[addr_cnt] = SI_in; addr_cnt = addr_cnt + 1; if (addr_cnt == 3*BYTE) begin for (i=23;i>=23-HiAddrBit;i=i-1) begin addr_bytes[23-i] = Address_in[i]; end Address = addr_bytes; change_addr = 1'b1; #1 change_addr = 1'b0; bus_cycle_state = DUMMY_BYTES; end end else if (Instruct == DH_READ && ((HOLDNeg_in && ~QUAD) || QUAD)) begin if (SOIn !== 1'b0 && SOIn !== 1'b1) begin mpm_mode = 0; bus_cycle_state = STAND_BY; end else begin Address_in[2*addr_cnt] = SOIn; Address_in[2*addr_cnt+1] = SI_in; read_cnt = 0; addr_cnt = addr_cnt + 1; if (addr_cnt == 12) begin addr_cnt = 0; for (i=23;i>=23-HiAddrBit;i=i-1) begin addr_bytes[23-i] = Address_in[i]; end Address = addr_bytes; change_addr = 1'b1; #1 change_addr = 1'b0; bus_cycle_state = MODE_BYTE; end end end else if (Instruct == QH_READ) begin if(QUAD) begin if (SOIn !== 1'b0 && SOIn !== 1'b1) begin mpm_mode = 0; bus_cycle_state = STAND_BY; end else begin Address_in[4*addr_cnt] = HOLDNeg_in; Address_in[4*addr_cnt+1] = WPNeg_in; Address_in[4*addr_cnt+2] = SOIn; Address_in[4*addr_cnt+3] = SI_in; read_cnt = 0; addr_cnt = addr_cnt + 1; if (addr_cnt == 6) begin addr_cnt = 0; for (i=23;i>=23-HiAddrBit;i=i-1) begin addr_bytes[23-i] = Address_in[i]; end Address = addr_bytes; change_addr = 1'b1; #1 change_addr = 1'b0; bus_cycle_state = MODE_BYTE; end end end else begin bus_cycle_state = STAND_BY; end end else if ((HOLDNeg_in && ~QUAD) || QUAD) begin Address_in[addr_cnt] = SI_in; addr_cnt = addr_cnt + 1; if (addr_cnt == 3*BYTE) begin for (i=23;i>=23-HiAddrBit;i=i-1) begin addr_bytes[23-i] = Address_in[i]; end Address = addr_bytes; change_addr = 1'b1; #1 change_addr = 1'b0; bus_cycle_state = DATA_BYTES; end end end MODE_BYTE : begin if((Instruct == DH_READ) && ((HOLDNeg_in && ~QUAD) || QUAD)) begin mode_in[2*mode_cnt] = SOIn; mode_in[2*mode_cnt+1] = SI_in; mode_cnt = mode_cnt + 1; if (mode_cnt == BYTE/2) begin mode_cnt = 0; for (i=7;i>=7-BYTE;i=i-1) begin mode_bytes[i] = mode_in[7-i]; end bus_cycle_state = DATA_BYTES; end end else if((Instruct == QH_READ) && QUAD) begin mode_in[4*mode_cnt] = HOLDNeg_in; mode_in[4*mode_cnt+1] = WPNeg_in; mode_in[4*mode_cnt+2] = SOIn; mode_in[4*mode_cnt+3] = SI_in; mode_cnt = mode_cnt + 1; if (mode_cnt == BYTE/4) begin mode_cnt = 0; for (i=7;i>=7-BYTE;i=i-1) begin mode_bytes[i] = mode_in[7-i]; end bus_cycle_state = DUMMY_BYTES; end end dummy_cnt = 0; end DUMMY_BYTES : begin if(QUAD && (Instruct == QUAD_READ || Instruct == QH_READ)) begin dummy_cnt = dummy_cnt + 1; if ((dummy_cnt == BYTE) && Instruct == QUAD_READ) bus_cycle_state = DATA_BYTES; else if ((dummy_cnt == BYTE/2) && Instruct == QH_READ) bus_cycle_state = DATA_BYTES; end else if((HOLDNeg_in && ~QUAD) || QUAD) begin dummy_cnt = dummy_cnt + 1; if (dummy_cnt == BYTE && (Instruct == FAST_READ || Instruct == OTPR || Instruct == DUAL_READ)) bus_cycle_state = DATA_BYTES; else if (dummy_cnt == 3*BYTE) bus_cycle_state = DATA_BYTES; end end DATA_BYTES : begin if(QUAD && Instruct == QPP) begin quad_nybble = {HOLDNeg_in, WPNeg_in, SOIn, SI_in}; if (data_cnt > 511) begin //In case of quad mode and QPP, //if more than 512 bytes are sent to the device for (i=0;i<=510;i=i+1) begin quad_data_in[i] = quad_data_in[i+1]; end quad_data_in[511] = quad_nybble; data_cnt = data_cnt + 1; end else begin if( quad_nybble !== 4'bZZZZ) begin quad_data_in[data_cnt] = quad_nybble; end data_cnt = data_cnt + 1; end end else if ((HOLDNeg_in && ~QUAD) || QUAD) begin if (data_cnt > 2047) //In case of serial mode and PP, if more than 256 //bytes are sent to the device begin if (bit_cnt == 0) begin for (i=0;i<=(255*BYTE-1);i=i+1) begin Data_in[i] = Data_in[i+8]; end end Data_in[2040 + bit_cnt] = SI_in; bit_cnt = bit_cnt + 1; if (bit_cnt == 8) begin bit_cnt = 0; end data_cnt = data_cnt + 1; end else begin Data_in[data_cnt] = SI_in; data_cnt = data_cnt + 1; bit_cnt = 0; end end end endcase end end if (falling_edge_SCK_ipd) begin if ((bus_cycle_state == DATA_BYTES) && (~CSNeg_ipd)) begin if (((Instruct == READ || Instruct == FAST_READ || Instruct == DUAL_READ || Instruct == DH_READ || Instruct == RES_READ_ES || Instruct == RDID || Instruct == RDSR || Instruct == READ_ID || Instruct == RCR || Instruct == OTPR) && ((HOLDNeg_in && ~QUAD) || QUAD)) || ((Instruct == QUAD_READ || Instruct == QH_READ) && QUAD)) begin read_out = 1'b1; #1 read_out = 1'b0; end end end if (rising_edge_CSNeg_ipd) begin if ((bus_cycle_state != DATA_BYTES) && (bus_cycle_state != DUMMY_BYTES)) begin bus_cycle_state = STAND_BY; end else begin if (bus_cycle_state == DATA_BYTES) begin if (mpm_mode && (mode_bytes[7:4] == 4'b1010) && (Instruct == DH_READ || Instruct == QH_READ)) begin bus_cycle_state = ADDRESS_BYTES; end else begin mpm_mode = 0; bus_cycle_state = STAND_BY; end if (Instruct == QPP) begin if (data_cnt > 0) begin if ((data_cnt % 2) == 0) begin write = 0; for(i=0;i<=255;i=i+1) begin for(j=1;j>=0;j=j-1) begin Quad_slv = quad_data_in[(i*2)+(1-j)]; for(k=3;k>=0;k=k-1) begin Byte_slv[4*j+k] = Quad_slv[k]; end end WByte[i] = Byte_slv; end if (data_cnt > 512) Byte_number = 255; else Byte_number = data_cnt/2 -1; end end end if ((HOLDNeg_in && ~QUAD) || QUAD) begin case (Instruct) WRDI, WREN, DP, BE, SE, P4E, P8E, CLSR: begin if (data_cnt == 0) write = 1'b0; end RES_READ_ES: begin write = 1'b0; end WRR : begin if((HOLDNeg_in && ~QUAD) || QUAD) begin if (data_cnt == 8) begin write = 0; cfg_write = 1'b0; for(i=0;i<=7;i=i+1) begin Status_reg_in[i]= Data_in[7-i]; end end else if (data_cnt == 16) begin write = 0; cfg_write = 1'b1; for(i=0;i<=7;i=i+1) begin Status_reg_in[i]= Data_in[7-i]; Sec_conf_reg_in[i]= Data_in[15-i]; end end end end PP : begin if (data_cnt > 0) begin if ((data_cnt % 8) == 0) begin write = 1'b0; for (i=0;i<=255;i=i+1) begin for (j=7;j>=0;j=j-1) begin Byte_slv[j] = Data_in[(i*8) + (7-j)]; end WByte[i] = Byte_slv; end if (data_cnt > 256*BYTE) Byte_number = 255; else Byte_number = ((data_cnt/8) - 1); end end end OTPP : begin if (data_cnt == 8) begin write = 1'b0; for (j=7;j>=0;j=j-1) begin Byte_slv[j] = Data_in[7-j]; end WOTPByte = Byte_slv; end end endcase end end else if (bus_cycle_state == DUMMY_BYTES) begin bus_cycle_state = STAND_BY; if (((HOLDNeg_in && ~QUAD) || QUAD) && (Instruct == RES_READ_ES) && (dummy_cnt == 0)) write = 1'b0; end end end end // ///////////////////////////////////////////////////////////////////////// // // Timing control for the Program Operations // // start // ///////////////////////////////////////////////////////////////////////// event pdone_event; always @(PSTART) begin if (PSTART && PDONE) if (Sec_Prot[SA] == 1'b0) begin PDONE = 1'b0; ->pdone_event; end end always @(pdone_event) begin:pdone_process PDONE = 1'b0; #tdevice_PP PDONE = 1'b1; end // ///////////////////////////////////////////////////////////////////////// // // Timing control for the Write Status Register Operation // // start // ///////////////////////////////////////////////////////////////////////// event wdone_event; always @(WSTART) begin if (WSTART && WDONE) begin WDONE = 1'b0; ->wdone_event; end end always @(wdone_event) begin:wdone_process WDONE = 1'b0; #tdevice_WR WDONE = 1'b1; end // ///////////////////////////////////////////////////////////////////////// // // Timing control for the Erase Operations // ///////////////////////////////////////////////////////////////////////// time duration_erase; event edone_event; always @(ESTART) begin: erase if (ESTART && EDONE) begin if (Instruct == BE) begin duration_erase = tdevice_BE; end else if (Instruct == P4E || Instruct == P8E) begin duration_erase = tdevice_PE; end else begin duration_erase = tdevice_SE; end EDONE = 1'b0; ->edone_event; end end always @(edone_event) begin : edone_process EDONE = 1'b0; #duration_erase EDONE = 1'b1; end // ///////////////////////////////////////////////////////////////////////// // // Main Behavior Process // // combinational process for next state generation // ///////////////////////////////////////////////////////////////////////// reg rising_edge_PDONE = 1'b0; reg rising_edge_EDONE = 1'b0; reg rising_edge_WDONE = 1'b0; reg rising_edge_DP_out = 1'b0; reg falling_edge_write = 1'b0; integer i; integer j; always @(falling_edge_write or rising_edge_PDONE or rising_edge_WDONE or rising_edge_EDONE or rising_edge_DP_out) begin: StateGen1 if (falling_edge_write) begin case (current_state) IDLE : begin if (~write) begin if ((Instruct == WRR) && WEL) begin if (~(Status_reg[7] && ~WPNeg_in && ~QUAD)) //HPM is disabled when the Quad I/O Mode is enabled //(Quad bit = 1 in the Configuration Register) next_state = WRITE_SR; end else if ((Instruct == PP || Instruct == QPP) && WEL) begin sect = Address / 24'h10000; if (Sec_Prot[sect] == 1'b0) next_state = PAGE_PG; end else if (Instruct == OTPP && WEL) begin if(Address == 256 || Address == 257 || ((Address >= 258 && Address <= 273) && PR_LOCK1[(Address-258)/8] == 1'b1) || Address == 274 || Address == 275 || ((Address >= 276 && Address <= 531) && PR_LOCK2[(Address-276)/16] ==1'b1) || Address == 532 || Address == 533 || ((Address >= 534 && Address <= 767) && PR_LOCK3[(Address-534)/16] == 1'b1)) next_state = OTP_PG; end else if (Instruct == SE && WEL) begin sect = Address / 24'h10000; if (Sec_Prot[sect] == 1'b0) next_state = SECTOR_ER; end else if (Instruct == P4E && WEL) begin sect = Address / 24'h10000; if (Sec_Prot[sect] == 1'b0 && (((sect == 0 || sect == 1) && ~TBPARM) || ((sect == SecNum || sect == SecNum-1) && TBPARM))) next_state = P4_ER; end else if (Instruct == P8E && WEL) begin sect = Address / 24'h10000; if (Sec_Prot[sect] == 1'b0 && (((sect == 0 || sect == 1) && ~TBPARM) || ((sect == SecNum || sect == SecNum-1) && TBPARM))) next_state = P8_ER; end else if (Instruct == BE && WEL) begin if (Status_reg[2]==1'b0 && Status_reg[3]==1'b0 && Status_reg[4]==1'b0) next_state = BULK_ER; end else if (Instruct == DP) next_state = DP_DOWN_WAIT; else next_state = IDLE; end end DP_DOWN : begin if (~write) begin if (Instruct == RES_READ_ES) next_state = IDLE; end end endcase end if (rising_edge_PDONE) begin if (current_state==PAGE_PG || current_state==OTP_PG) begin next_state = IDLE; end end if (rising_edge_WDONE) begin if (current_state==WRITE_SR) begin next_state = IDLE; end end if (rising_edge_EDONE) begin if (current_state==SECTOR_ER || current_state==BULK_ER || current_state==P4_ER || current_state==P8_ER) begin next_state = IDLE; end end if (rising_edge_DP_out) begin if (current_state==DP_DOWN_WAIT) next_state = DP_DOWN; end end /////////////////////////////////////////////////////////////////////////// //FSM Output generation and general functionality /////////////////////////////////////////////////////////////////////////// reg rising_edge_read_out = 1'b0; reg rising_edge_RES_out = 1'b0; reg Instruct_event = 1'b0; reg change_addr_event = 1'b0; reg rising_edge_powered = 1'b0; reg current_state_event = 1'b0; integer sector; integer WData [0:255]; integer WOTPData; integer Addr; integer Addr_tmp; always @(oe_event) begin oe = 1'b1; #1 oe = 1'b0; end always @(rising_edge_read_out or Instruct_event or change_addr_event or oe or current_state_event or falling_edge_write or EDONE or WDONE or PDONE or CSNeg_ipd or rising_edge_RES_out or rising_edge_powered or rising_edge_DP_out) begin: Functionality integer i,j; if (rising_edge_read_out) begin if (PoweredUp == 1'b1) ->oe_event; end if (Instruct_event) begin read_cnt = 0; rd_fast = 1'b1; rd_slow = 1'b0; rd_jedec = 1'b0; dual = 1'b0; if (current_state == DP_DOWN_WAIT) begin if (DP_in == 1'b1) begin $display ("Command results can be corrupted"); end end if (Instruct == DH_READ || Instruct == QH_READ) begin mpm_mode = 1; end end if (rising_edge_powered) begin //The Configuration Register FREEZE bit is cleared. Sec_conf_reg[0] = 1'b0; if (BPNV == 1 && FREEZE == 0) begin //When BPNV is set to ‘1’,the BP2-0 bits in the Status Register //are volatile and will be reset to binary 111 after power on reset. Status_reg[4] = 1'b1;// BP2 Status_reg[3] = 1'b1;// BP1 Status_reg[2] = 1'b1;// BP0 change_BP = 1'b1; #1 change_BP = 1'b0; end end if (change_addr_event) begin read_addr = Address; end if (oe || current_state_event) begin case (current_state) IDLE : begin if (oe && ~RES_in) begin if (Instruct == RDSR) begin //Read Status Register SOut_zd = Status_reg[7-read_cnt]; read_cnt = read_cnt + 1; if (read_cnt == 8) read_cnt = 0; end if (Instruct == RCR) begin //Read Security Conf. Register SOut_zd = Sec_conf_reg[7-read_cnt]; read_cnt = read_cnt + 1; if (read_cnt == 8) read_cnt = 0; end else if (Instruct == READ || Instruct == FAST_READ) begin //Read Memory array if (Instruct == READ) begin rd_fast = 1'b0; rd_jedec = 1'b0; rd_slow = 1'b1; end data_out[7:0] = Mem[read_addr]; SOut_zd = data_out[7-read_cnt]; read_cnt = read_cnt + 1; if (read_cnt == 8) begin read_cnt = 0; if (read_addr == AddrRANGE) read_addr = 0; else read_addr = read_addr + 1; end end else if (Instruct == DUAL_READ || Instruct == DH_READ) begin //Read Memory array rd_fast = 1'b0; rd_slow = 1'b0; rd_jedec = 1'b0; dual = 1'b1; data_out[7:0] = Mem[read_addr]; SOut_zd = data_out[7-2*read_cnt]; SIOut_zd = data_out[6-2*read_cnt]; read_cnt = read_cnt + 1; if (read_cnt == 4) begin read_cnt = 0; if (read_addr == AddrRANGE) read_addr = 0; else read_addr = read_addr + 1; end end else if (Instruct == QUAD_READ || Instruct == QH_READ) begin //Read Memory array rd_fast = 1'b0; rd_slow = 1'b0; rd_jedec = 1'b0; dual = 1'b1; data_out[7:0] = Mem[read_addr]; HOLDNegOut_zd = data_out[7-4*read_cnt]; WPNegOut_zd = data_out[6-4*read_cnt]; SOut_zd = data_out[5-4*read_cnt]; SIOut_zd = data_out[4-4*read_cnt]; read_cnt = read_cnt + 1; if (read_cnt == 2) begin read_cnt = 0; if (read_addr == AddrRANGE) read_addr = 0; else read_addr = read_addr + 1; end end else if (Instruct == OTPR) begin if(read_addr>=OTPLoAddr && read_addr<=OTPHiAddr) begin //Read OTP Memory array rd_fast = 1'b1; rd_slow = 1'b0; rd_jedec = 1'b0; dual = 1'b0; data_out = OTPMem[read_addr]; SOut_zd = data_out[7-read_cnt]; read_cnt = read_cnt + 1; if (read_cnt == 8) begin read_cnt = 0; read_addr = read_addr + 1; end end else begin //OTP Read operation will not wrap to the //starting address after the OTP address is at //its maximum; //instead, the data will be indeterminate. SOut_zd = 1'bX; read_cnt = read_cnt + 1; if (read_cnt == 8) read_cnt = 0; end end else if (Instruct == RDID) begin // Read ID rd_fast = 1'b0; rd_slow = 1'b0; rd_jedec = 1'b1; dual = 1'b0; ident_out = {Manuf_ID,DeviceID,ExtendedBytes, ReservedBytes,ReservedBytes, ReservedBytes,CFI_array_tmp}; SOut_zd = ident_out[647-read_cnt]; read_cnt = read_cnt + 1; if (read_cnt == 648) read_cnt = 0; end else if (Instruct == READ_ID) begin // --Read Manufacturer and Device ID if (read_addr % 2 == 0) begin data_out[7:0] = Manuf_ID; SOut_zd = data_out[7-read_cnt]; read_cnt = read_cnt + 1; if (read_cnt == 8) begin read_cnt = 0; read_addr = read_addr + 1; end end else if (read_addr % 2 == 1) begin data_out[7:0] = ES; SOut_zd = data_out[7-read_cnt]; read_cnt = read_cnt + 1; if (read_cnt == 8) begin read_cnt = 0; read_addr = 0; end end end end else if (oe && RES_in) begin $display ("Command results can be corrupted"); SOut_zd = 1'bX; read_cnt = read_cnt + 1; if (read_cnt == 8) read_cnt = 0; end end WRITE_SR, SECTOR_ER, BULK_ER, P4_ER, P8_ER, OTP_PG, PAGE_PG : begin if (oe && Instruct == RDSR) begin //Read Status Register SOut_zd = Status_reg[7-read_cnt]; read_cnt = read_cnt + 1; if (read_cnt == 8) read_cnt = 0; end if (oe && Instruct == RCR) begin //Read Security Conf. Register SOut_zd = Sec_conf_reg[7-read_cnt]; read_cnt = read_cnt + 1; if (read_cnt == 8) read_cnt = 0; end end DP_DOWN : begin if (oe && Instruct == RES_READ_ES) begin // Read ID data_out[7:0] = ES; SOut_zd = data_out[7-read_cnt]; read_cnt = read_cnt + 1; if (read_cnt == 8) read_cnt = 0; end end endcase end if (falling_edge_write) begin case (current_state) IDLE : begin if (~write) begin if (RES_in == 1'b1 && Instruct != DP) begin $display ("Command results can be corrupted"); end if (Instruct == WREN) Status_reg[1] = 1'b1; else if (Instruct == WRDI) Status_reg[1] = 1'b0; else if (Instruct == WRR && WEL) begin if (~(Status_reg[7] && ~WPNeg_in && ~QUAD)) begin WSTART = 1'b1; WSTART <= #1 1'b0; Status_reg[0] = 1'b1; end else begin Status_reg[1] = 0; end end else if ((Instruct == PP || Instruct == QPP) && WEL) begin sect = Address / 24'h10000; if (Sec_Prot[sect] == 1'b0) begin PSTART = 1'b1; PSTART <= #1 1'b0; Status_reg[0] = 1'b1; //WIP Addr = Address; Addr_tmp = Address; SA = sect; wr_cnt = Byte_number; for (i=0;i<=wr_cnt;i=i+1) begin if (Viol != 1'b0) WData[i] = -1; else WData[i] = WByte[i]; end end else begin Status_reg[1] = 1'b0; end end else if (Instruct == OTPP && WEL) begin if(Address == 256 || Address == 257 || ((Address >= 258 && Address <= 273) && PR_LOCK1[(Address-258)/8] == 1'b1) || Address == 274 || Address == 275 || ((Address >= 276 && Address <= 531) && PR_LOCK2[(Address-276)/16] ==1'b1) || Address == 532 || Address == 533 || ((Address >= 534 && Address <= 767) && PR_LOCK3[(Address-534)/16] == 1'b1)) begin PSTART = 1'b1; PSTART <= #1 1'b0; Status_reg[0] = 1'b1; //WIP Addr = Address; if (Viol != 1'b0 ) WOTPData = -1; else WOTPData = WOTPByte; end else if (Address < 100 || Address > 767 ) begin Status_reg[6] = 1'b1; Status_reg[1] = 1'b0; end else begin Status_reg[1] = 1'b0; end end else if (Instruct == SE && WEL) begin sect = Address / 24'h10000; if (Sec_Prot[sect] == 1'b0) begin ESTART = 1'b1; ESTART <= #1 1'b0; Status_reg[0] = 1'b1; Addr = Address; end else begin Status_reg[1] = 1'b0; end end else if (Instruct == BE && WEL) begin if(Status_reg[2]==1'b0 && Status_reg[3]==1'b0 && Status_reg[4]==1'b0) begin ESTART = 1'b1; ESTART <= #1 1'b0; Status_reg[0] = 1'b1; end else begin Status_reg[1] = 1'b0; end end else if (Instruct == P4E && WEL) begin sect = Address / 24'h10000; if (((sect == 0 || sect == 1) && ~TBPARM) || ((sect == SecNum || sect == SecNum-1) && TBPARM)) begin if (Sec_Prot[sect] == 1'b0) begin ESTART = 1'b1; ESTART <= #1 1'b0; Status_reg[0] = 1'b1; Addr = Address; end else begin Status_reg[1] = 1'b0; end end else begin Status_reg[5] = 1'b1; Status_reg[1] = 1'b0; end end else if (Instruct == P8E && WEL) begin sect = Address / 24'h10000; if (((sect == 0 || sect == 1) && ~TBPARM) || ((sect == SecNum || sect == SecNum-1) && TBPARM)) begin if (Sec_Prot[sect] == 1'b0) begin ESTART = 1'b1; ESTART <= #1 1'b0; Status_reg[0] = 1'b1; Addr = Address; end else begin Status_reg[1] = 1'b0; end end else begin Status_reg[5] = 1'b1; Status_reg[1] = 1'b0; end end else if (Instruct == CLSR) begin Status_reg[5] = 1'b0; Status_reg[6] = 1'b0; end else if (Instruct == DP) begin RES_in <= 1'b0; DP_in = 1'b1; end else if (Instruct == RES_READ_ES) begin RES_in <= 1'b1; end end end DP_DOWN : begin if (~write) begin if (Instruct == RES_READ_ES) RES_in = 1'b1; end end endcase end if(current_state_event || EDONE) begin case (current_state) SECTOR_ER : begin ADDRHILO_SEC(AddrLo, AddrHi, Addr); for (i=AddrLo;i<=AddrHi;i=i+1) begin Mem[i] = -1; end if (EDONE) begin Status_reg[0] = 1'b0; Status_reg[1] = 1'b0; for (i=AddrLo;i<=AddrHi;i=i+1) begin Mem[i] = MaxData; end end end BULK_ER : begin for (i=0;i<=AddrRANGE;i=i+1) begin Mem[i] = -1; end if (EDONE) begin Status_reg[0] = 1'b0; Status_reg[1] = 1'b0; for (i=0;i<=AddrRANGE;i=i+1) begin Mem[i] = MaxData; end end end P4_ER : begin ADDRHILO_PB4(AddrLo, AddrHi, Addr); for (i=AddrLo;i<=AddrHi;i=i+1) begin Mem[i] = -1; end if (EDONE) begin Status_reg[0] = 1'b0; Status_reg[1] = 1'b0; for (i=AddrLo;i<=AddrHi;i=i+1) begin Mem[i] = MaxData; end end end P8_ER : begin ADDRHILO_PB8(AddrLo, AddrHi, Addr); for (i=AddrLo;i<=AddrHi;i=i+1) begin Mem[i] = -1; end if (EDONE) begin Status_reg[0] = 1'b0; Status_reg[1] = 1'b0; for (i=AddrLo;i<=AddrHi;i=i+1) begin Mem[i] = MaxData; end end end endcase end if(current_state_event || WDONE) begin if (current_state == WRITE_SR) begin if (WDONE) begin Status_reg[0] = 1'b0;//WIP Status_reg[1] = 1'b0;//WEL //SRWD bit Status_reg[7] = Status_reg_in[7];//MSB first if (FREEZE == 0) //The Freeze Bit, when set to 1, locks the current //state of the BP2-0 bits in Status Register, //the TBPROT and TBPARM bits in the Config Register //As long as the FREEZE bit remains cleared to logic //'0', the other bits of the Configuration register //including FREEZE are writeable. begin Status_reg[4] = Status_reg_in[4];// BP2 Status_reg[3] = Status_reg_in[3];// BP1 Status_reg[2] = Status_reg_in[2];// BP0 if (cfg_write) begin Sec_conf_reg[0] = Sec_conf_reg_in[0];// FREEZE if (TBPARM == 1'b0) begin Sec_conf_reg[2] = Sec_conf_reg_in[2];//TBPARM end if (TBPROT == 1'b0) begin Sec_conf_reg[5] = Sec_conf_reg_in[5];//TBPROT end Sec_conf_reg[1] = Sec_conf_reg_in[1];// QUAD if (BPNV == 1'b0) begin Sec_conf_reg[3] = Sec_conf_reg_in[3];//BPNV end end change_BP = 1'b1; #1 change_BP = 1'b0; end end end end if(current_state_event || PDONE) begin if (current_state == PAGE_PG) begin ADDRHILO_PG(AddrLo, AddrHi, Addr); cnt = 0; for (i=0;i<=wr_cnt;i=i+1) begin new_int = WData[i]; old_int = Mem[Addr + i - cnt]; if (new_int > -1) begin new_bit = new_int; if (old_int > -1) begin old_bit = old_int; for(j=0;j<=7;j=j+1) if (~old_bit[j]) new_bit[j]=1'b0; new_int=new_bit; end WData[i]= new_int; end else begin WData[i] = -1; end Mem[Addr + i -cnt] = - 1; if ((Addr + i) == AddrHi) begin Addr = AddrLo; cnt = i + 1; end end cnt = 0; if (PDONE) begin Status_reg[0] = 1'b0;//wip Status_reg[1] = 1'b0; for (i=0;i<=wr_cnt;i=i+1) begin Mem[Addr_tmp + i - cnt] = WData[i]; if ((Addr_tmp + i) == AddrHi) begin Addr_tmp = AddrLo; cnt = i + 1; end end end end end if(current_state_event || PDONE) begin if (current_state == OTP_PG) begin new_int = WOTPData; old_int = OTPMem[Addr]; if (new_int > -1) begin new_bit = new_int; if (old_int > -1) begin old_bit = old_int; for(j=0;j<=7;j=j+1) begin if (~old_bit[j]) new_bit[j] = 1'b0; end new_int = new_bit; end WOTPData = new_int; end else begin WOTPData = -1; end OTPMem[Addr] = -1; if (PDONE) begin Status_reg[0] = 1'b0; Status_reg[1] = 1'b0; OTPMem[Addr] = WOTPData; PR_LOCK1 = {OTPMem[257],OTPMem[256]}; PR_LOCK1[15:8] = OTPMem[257]; PR_LOCK1[7:0] = OTPMem[256]; PR_LOCK2[15:8] = OTPMem[275]; PR_LOCK2[7:0] = OTPMem[274]; PR_LOCK3[15:8] = OTPMem[533]; PR_LOCK3[7:0] = OTPMem[532]; end end end //Output Disable Control if (CSNeg_ipd ) begin SIOut_zd = 1'bZ; HOLDNegOut_zd = 1'bZ; WPNegOut_zd = 1'bZ; SOut_zd = 1'bZ; end if (rising_edge_RES_out) begin if(RES_out) begin RES_in = 1'b0; end end if (rising_edge_DP_out) begin if (current_state == DP_DOWN_WAIT) begin DP_in = 1'b0; end end end always @(change_BP) begin if (change_BP) begin case (Status_reg[4:2]) 3'b000 : begin Sec_Prot = 256'h0; end 3'b001 : begin if (~Sec_conf_reg[5]) begin Sec_Prot[SecNum : (SecNum+1)*63/64] = 1'b1; Sec_Prot[(SecNum+1)*63/64 - 1 : 0] = 126'h0; end else begin Sec_Prot[(SecNum+1)/64 - 1 : 0] = 2'b11; Sec_Prot[SecNum : (SecNum+1)/64] = 126'h0; end end 3'b010 : begin if (~Sec_conf_reg[5]) begin Sec_Prot[SecNum : (SecNum+1)*31/32] = 4'hF; Sec_Prot[(SecNum+1)*31/32 - 1 : 0] = 124'h0; end else begin Sec_Prot[(SecNum+1)/32 - 1 : 0] = 4'hF; Sec_Prot[SecNum : (SecNum+1)/32] = 124'h0; end end 3'b011 : begin if (~Sec_conf_reg[5]) begin Sec_Prot[SecNum : (SecNum+1)*15/16] = 8'hFF; Sec_Prot[(SecNum+1)*15/16 - 1 : 0] = 120'h0; end else begin Sec_Prot[(SecNum+1)/16 - 1 : 0] = 8'hFF; Sec_Prot[SecNum : (SecNum+1)/16] = 120'h0; end end 3'b100 : begin if (~Sec_conf_reg[5]) begin Sec_Prot[SecNum : (SecNum+1)*7/8] = 16'hFFFF; Sec_Prot[(SecNum+1)*7/8 - 1 : 0] = 112'h0; end else begin Sec_Prot[(SecNum+1)/8 - 1 : 0] = 16'hFFFF; Sec_Prot[SecNum : (SecNum+1)/8] = 112'h0; end end 3'b101 : begin if (~Sec_conf_reg[5]) begin Sec_Prot[SecNum : (SecNum+1)*3/4] = 32'hFFFFFFFF; Sec_Prot[(SecNum+1)*3/4 - 1 : 0] = 96'h0; end else begin Sec_Prot[(SecNum+1)/4 - 1 : 0] = 32'hFFFFFFFF; Sec_Prot[SecNum : (SecNum+1)/4] = 96'h0; end end 3'b110 : begin if (~Sec_conf_reg[5]) begin Sec_Prot[SecNum : (SecNum+1)/2] = 64'hFFFFFFFFFFFFFFFF; Sec_Prot[(SecNum+1)/2 - 1 : 0] = 64'h0; end else begin Sec_Prot[(SecNum+1)/2 - 1 : 0] = 64'hFFFFFFFFFFFFFFFF; Sec_Prot[SecNum : (SecNum+1)/2] = 64'h0; end end 3'b111 : begin Sec_Prot = 128'hFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; end endcase end end assign fast_rd = rd_fast; assign rd = rd_slow; assign rd_jid = rd_jedec; always @(SOut_zd or HOLDNeg_in or SIOut_zd) begin if (~HOLDNeg_in && ~QUAD) begin hold_mode = 1'b1; SIOut_z = 1'bZ; SOut_z = 1'bZ; end else begin if (hold_mode) begin SIOut_z <= #(tpd_HOLDNeg_SO) SIOut_zd; SOut_z <= #(tpd_HOLDNeg_SO) SOut_zd; hold_mode = 1'b0; end else begin SIOut_z = SIOut_zd; SOut_z = SOut_zd; hold_mode = 1'b0; end end end // Procedure ADDRHILO_SEC task ADDRHILO_SEC; inout AddrLOW; inout AddrHIGH; input Addr; integer AddrLOW; integer AddrHIGH; integer Addr; integer sector; begin sector = Addr / 20'h10000; AddrLOW = sector * 20'h10000; AddrHIGH = sector * 20'h10000 + 16'hFFFF; end endtask // Procedure ADDRHILO_PG task ADDRHILO_PG; inout AddrLOW; inout AddrHIGH; input Addr; integer AddrLOW; integer AddrHIGH; integer Addr; integer page; begin page = Addr / 16'h100; AddrLOW = page * 16'h100; AddrHIGH = page * 16'h100 + 8'hFF; end endtask // Procedure ADDRHILO_PB4 task ADDRHILO_PB4; inout AddrLOW; inout AddrHIGH; input Addr; integer AddrLOW; integer AddrHIGH; integer Addr; integer sector; begin sector = Addr / 20'h10000; if (sector == 0 || sector == 1 || sector == SecNum || sector == SecNum-1) begin AddrLOW = (Address/(SecSize_4+1))*(SecSize_4+1); AddrHIGH = (Address/(SecSize_4+1))*(SecSize_4+1) + SecSize_4; end end endtask // Procedure ADDRHILO_PB8 task ADDRHILO_PB8; inout AddrLOW; inout AddrHIGH; input Addr; integer AddrLOW; integer AddrHIGH; integer Addr; integer sector; begin sector = Addr / 20'h10000; if (sector == 0 || sector == SecNum-1) begin AddrLOW = (Address/(SecSize_8+1))*(SecSize_8+1); AddrHIGH = (Address/(SecSize_8+1))*(SecSize_8+1) + SecSize_8; end if (sector == 1) begin AddrLOW = (Address/(SecSize_8+1))*(SecSize_8+1); AddrHIGH = (Address/(SecSize_8+1))*(SecSize_8+1) + SecSize_8; if (AddrHIGH > 20'h1FFFF) AddrHIGH = 20'h1FFFF; end if (sector == SecNum) begin AddrLOW = (Address/(SecSize_8+1))*(SecSize_8+1); AddrHIGH = (Address/(SecSize_8+1))*(SecSize_8+1) + SecSize_8; if (AddrHIGH > 24'h3FFFFF) AddrHIGH = 24'h3FFFFF; end end endtask always @(negedge CSNeg_ipd) begin falling_edge_CSNeg_ipd = 1'b1; #1 falling_edge_CSNeg_ipd = 1'b0; end always @(posedge SCK_ipd) begin rising_edge_SCK_ipd = 1'b1; #1 rising_edge_SCK_ipd = 1'b0; end always @(negedge SCK_ipd) begin falling_edge_SCK_ipd = 1'b1; #1 falling_edge_SCK_ipd = 1'b0; end always @(posedge CSNeg_ipd) begin rising_edge_CSNeg_ipd = 1'b1; #1 rising_edge_CSNeg_ipd = 1'b0; end always @(negedge write) begin falling_edge_write = 1'b1; #1 falling_edge_write = 1'b0; end always @(posedge PDONE) begin rising_edge_PDONE = 1'b1; #1 rising_edge_PDONE = 1'b0; end always @(posedge WDONE) begin rising_edge_WDONE = 1'b1; #1 rising_edge_WDONE = 1'b0; end always @(posedge EDONE) begin rising_edge_EDONE = 1'b1; #1 rising_edge_EDONE = 1'b0; end always @(posedge DP_out) begin rising_edge_DP_out = 1'b1; #1 rising_edge_DP_out = 1'b0; end always @(posedge RES_out) begin rising_edge_RES_out = 1'b1; #1 rising_edge_RES_out = 1'b0; end always @(posedge read_out) begin rising_edge_read_out = 1'b1; #1 rising_edge_read_out = 1'b0; end always @(posedge PoweredUp) begin rising_edge_powered = 1'b1; #1 rising_edge_powered = 1'b0; end always @(Instruct) begin Instruct_event = 1'b1; #1 Instruct_event = 1'b0; end always @(change_addr) begin change_addr_event = 1'b1; #1 change_addr_event = 1'b0; end always @(current_state) begin current_state_event = 1'b1; #1 current_state_event = 1'b0; end endmodule