1ОЋY#HИИИИИЙ ////////////////////////////////////////////////////////////////////////////// // INTEL DEVELOPER'S SOFTWARE LICENSE AGREEMENT // // BY USING THIS SOFTWARE, YOU ARE AGREEING TO BE BOUND // BY THE TERMS OFTHIS AGREEMENT. DO NOT USE THE SOFTWARE // UNTIL YOU HAVE CAREFULLY READAND AGREED TO THE FOLLOWING // TERMS AND CONDITIONS. IF YOU DO NOT AGREETO THE TERMS // OF THIS AGREEMENT, PROMPTLY RETURN THE SOFTWARE PACKAGE // ANDANY ACCOMPANYING ITEMS. // // IF YOU USE THIS SOFTWARE, YOU WILL BE BOUND BY THE TERMS // OF THIS AGREEMENT // // LICENSE: Intel Corporation ("Intel") grants you the non-exclusive right // to use the enclosed software program ("Software"). You will not use, // copy, modify, rent, sell or transfer the Software or any portion // thereof, except as provided in this Agreement. // // System OEM Developers may: // 1. Copy the Software for support, backup or archival purposes; // 2. Install, use, or distribute Intel owned Software in object code // only; // 3. Modify and/or use Software source code that Intel directly makes // available to you as an OEM Developer; // 4. Install, use, modify, distribute, and/or make or have made // derivatives ("Derivatives") of Intel owned Software under the // terms and conditions in this Agreement, ONLY if you are a System // OEM Developer and NOT an end-user. // // RESTRICTIONS: // // YOU WILL NOT: // 1. Copy the Software, in whole or in part, except as provided for // in this Agreement; // 2. Decompile or reverse engineer any Software provided in object // code format; // 3. Distribute any Software or Derivative code to any end-users, // unless approved by Intel in a prior writing. // // TRANSFER: You may transfer the Software to another OEM // Developer if thereceiving party agrees to the terms of this // Agreement at the sole risk of any receiving party. // // OWNERSHIP AND COPYRIGHT OF SOFTWARE: // Title to the Software and all copies thereof remain with Intel // or its vendors. The Software iscopyrighted and is protected by // United States and international copyright laws. You will not // remove the copyright notice from the Software. You agree to // prevent any unauthorized copying of the Software. // // DERIVATIVE WORK: OEM Developers that make or have made // Derivatives will not be required to provide Intel with a copy of the // source or object code. OEM Developers shall be authorized to use, // market, sell, and/or distribute Derivatives to other OEM Developers // at their own risk and expense. Title to Derivatives and all copies // thereof shall be in the particular OEM Developer creating the // Derivative. Such OEMs shall remove the Intel copyright notice // from all Derivatives if such notice is contained in the Software // source code. // // DUAL MEDIA SOFTWARE: If the Software package contains // multiple media, you may only use the medium appropriate for // your system. // // WARRANTY: Intel warrants that it has the right to license you to use, // modify, or distribute the Software as provided in this Agreement. The // Software is provided "AS IS". Intel makes no representations to // upgrade, maintain, or support the Software at any time. Intel warrants // that the media on which the Software is furnished will be free from // defects in material and workmanship for a period of one (1) year from // the date of purchase. Upon return of such defective media, Intel's // entire liability and your exclusive remedy shall be the replacement of // the Software. // // THE ABOVE WARRANTIES ARE THE ONLY WARRANTIES OF // ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING // WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY // PARTICULAR PURPOSE. // // LIMITATION OF LIABILITY: NEITHER INTEL NOR ITS VENDORS // OR AGENTS SHALL BE LIABLE FOR ANY LOSS OF PROFITS, // LOSS OF USE, LOSS OF DATA, INTERRUPTION OF BUSINESS, // NOR FOR INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL // DAMAGES OF ANY KIND WHETHER UNDER THIS AGREEMENT OR // OTHERWISE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. // // TERMINATION OF THIS LICENSE: Intel reserves the right to conduct or have // conducted audits to verify your compliance with this Agreement. Intel // may terminate this Agreement at any time if you are in breach of any of // its terms and conditions. Upon termination, you will immediately // destroy, and certify in writing the destruction of, the Software or // return all copies of the Software and documentation to Intel. // // U.S. GOVERNMENT RESTRICTED RIGHTS: The Software and // documentation were developed at private expense and are provided // with "RESTRICTED RIGHTS". Use, duplication or disclosure by // the Government is subject to restrictions as set forth in FAR52.227-14 // and DFAR252.227-7013 et seq. or its successor. // // EXPORT LAWS: You agree that the distribution and export/re-export of the // Software is in compliance with the laws, regulations, orders or other // restrictions of the U.S. Export Administration Regulations. // // APPLICABLE LAW: This Agreement is governed by the laws of the State of // California and the United States, including patent and copyright laws. // Any claim arising out of this Agreement will be brought in Santa Clara // County, California. // // Copyright 1996, Intel Corporation, All Rights Reserved ////////////////////////////////////////////////////////////////////////////// // Copyright 1994, Intel Corporation // Functionality and specifications based on 28F016SA datasheet // revision 1 (order number 290489). // Please contact Intel or distribution sales office for // up-to-date specifications on Intel flash memory products. // // Revision History // Date Revision Comment // Nov 14, 1994 0.5beta Initial Attempt // Nov 23, 1994 0.9beta Completed Queueing Sims // Jan 18, 1995 1.0 Fixed TOE issue. `timescale 1ns/1ps `define FALSE 1'b0 `define TRUE 1'b1 //The following constants control how long it take an algorithm to run // to scale all times together (for making simulation run faster // change the constant later listed as TimerPeriod. `define AC_ProgramTime 'h21 //`define AC_EraseTime 'h299D69 // 1.2 sec takes too long `define AC_EraseTime 'h100 // dummy for simulation purposes. `define AC_DeviceConfigTime 'h3 `define AC_LockBlockTime 'h25 `define AC_StatUploadCmd 'h02 `define AC_TwoByteProgramTime 'h21 `define AC_UpDevInfoTime 'h0f `define AC_PBProgramTime 'h17 `define BlockRowSize 'h7FFF `define PBWordSize 'h7F `define uCodeRevAdd 7'b11 `define uCodeRevData 'h08 `define ID_DeviceCodeB 'ha0 `define ID_ManufacturerB 'h89 `define ID_DeviceCodeW 'h66a0 `define ID_ManufacturerW 'h89 `define Vcc5vThres 4.0 // These constants are the actual command codes `define AbortCmd 'h80 `define DeviceConfigCmd 'h96 `define EraseAllBlocksCmd 'hA7 `define EraseCmd 'hEC `define LockBlockCmd 'h77 `define PBWriteFlashCmd 'h0C `define ClearSRCmd 'h50 `define ProgramCmd 'h10 `define Program2Cmd 'h40 `define EraseSingleBlockCmd 'h20 `define ReadArrayCmd 'hFF `define ReadCSRCmd 'h70 `define ReadIDCmd 'h90 `define SuspendCmd 'hB0 `define ResumeCmd 'hD0 `define ReadESRCmd 'h71 `define ReadPBCmd 'h75 `define SeqPBWriteCmd 'hE0 `define SleepCmd 'hF0 `define StatusUploadCmd 'h97 `define SwapPBCmd 'h72 `define TwoByteWriteCmd 'hFB `define UpDevInfoCmd 'h99 `define WritePBCmd 'h74 `define TGHIL_5v 0 // Read Recovery before Write (CE or WE) `define TPHIL_5v 480 // RP# High Recovery to WE# or CE# Going low `define TILIH_5v 40 // WE# or CE# pulse width `define TVPIH_5v 100 // Vpp Setup to WE# or CE# Going High `define TAVIH_5v 50 // Address Setup to WE# or CE# Going High `define TDVIH_5v 50 // Data Setup to WE# or CE# Going High `define TIHDX_5v 0 // Data Hold from WE# or CE# High `define TIHAX_5v 10 // Address Hold from WE# or CE# High `define TIHIL_5v 30 // WE# or CE# Pulse Width High `define TIHRL_5v 100 // WE# or CE# High to RY/BY# Going Low `define TIHGL_5v 60 // Write Recovery before Read `define TAVQV_5v 70 // Address to Ouput Delay `define TGHQZ_5v 25 // OE# to Output in High Z `define TGLQX_5v 0 // OE# to Output in Low Z `define TRLRZ_5v 220 // RY/BY# Pulse Width //`define TimerPeriod_5v 220 `define TimerPeriod_5v 220 /5 `define TGHIL_3v 0 // Read Recovery before Write (CE or WE) `define TPHIL_3v 480 // RP# High Recovery to WE# or CE# Going low `define TILIH_3v 75 // WE# or CE# pulse width `define TVPIH_3v 100 // Vpp Setup to WE# or CE# Going High `define TAVIH_3v 75 // Address Setup to WE# or CE# Going High `define TDVIH_3v 75 // Data Setup to WE# or CE# Going High `define TIHDX_3v 10 // Data Hold from WE# or CE# High `define TIHAX_3v 10 // Address Hold from WE# or CE# High `define TIHIL_3v 45 // WE# or CE# Pulse Width High `define TIHRL_3v 100 // WE# or CE# High to RY/BY# Going Low `define TIHGL_3v 95 // Write Recovery before Read `define TAVQV_3v 120 // Address to Ouput Delay `define TGHQZ_3v 30 // OE# to Output in High Z `define TGLQX_3v 0 // OE# to Output in Low Z `define TRLRZ_3v 250 // RY/BY# Pulse Width //`define TimerPeriod_3v 250 `define TimerPeriod_3v 250 /5 // array of bits `define Word 15:0 `define DoubleWord 31:0 `define Byte 7:0 `define Address_T 20:0 `define ReadMode_T 2:0 `define rdARRAY 3'b000 `define rdCSR 3'b011 `define rdID 3'b100 `define rdPB 3'b001 `define rdESR 3'b010 `define NVLockBit_T 31:0 `define WritePtr_T 1:0 `define NewCmd 2'b01 `define CmdField 2'b10 `define toPB 2'b00 `define COV_T 1:0 `define Unknown 2'b00 `define FiveVolt 2'b01 `define ThreeVolt 2'b10 // type RdyBsy_T `define Rdy 1'b0 `define Bsy 1'b1 // memory array defs `define RowRange 14:0 // `define MainArray_T 1048578:0 `define Top 1048578 `define PBPtr_T 2 `define PBplane_T 128 // array of integer `define PageBuffer_T (`PBPtr_T*`PBplane_T)-1 : 0 `define BSRmem_T 31:0 // array of Byte `define Add_T 2:1 // array of Address_T `define Data_T 2:1 // array of Word // type Byte_T `define By8 0 `define By16_Byte 1 `define BytePtr_T 1:0 `define By16_BytePtr 2'b00 `define By8H 2'b01 `define By8L 2'b10 `define BytePin_T 2:1 // array of type Byte_T `define OpType_T 1:0 `define Program 2'b00 `define Erase 2'b01 `define Operation 2'b10 `define edge_T 2:0 `define RisingEdge 3'b000 `define RE 3'b001 `define FallingEdge 3'b010 `define FE 3'b011 `define AnyEdge 3'b100 `define AE 3'b101 // Cmd_T record `define Cmd_T 175:0 `define CmdAdd_1 175:155 // 21 `define CmdAdd_2 154:134 // 21 `define Add 133:113 // 21 `define CmdData_1 112:97 `define CmdData_2 96:81 `define UsesPB 80 // 1 `define PBPtr 79 // 1 `define BytePin 78 // 1 `define CmdByte_1 77 // 1 `define CmdByte_2 76 // 1 `define Cmd 75:68 // 8 `define Count 67:36 // 32 `define Time 35:8 // 28 `define Confirm 7 // 1 `define OpBlock 6:2 // 5 `define OpType 1:0 // 2 `define CmdData1_lo 97 `define CmdData1Fx8 104:97 `define CmdData2_lo 81 `define CmdData2Fx8 88:81 `define CmdAdd1 175 `define CmdAdd1_lo 155 `define CmdAdd2_lo 134 `define CmdAdd1RowMSB 170 `define CmdAdd1RowLSB 156 module Intel28F016SA (dq, addr, ceb0, ceb1, byteb, wpb, rpb, oeb, web, ry_byb, vpp_b, vcc_b) ; input [63:0] vpp_b, vcc_b ; input [20:0] addr ; inout [`Word] dq ; input byteb; input wpb; input ceb0; input ceb1; input rpb; input oeb; input web ; output ry_byb ; reg ry_byb ; // input generics parameter LoadOnPowerUp = `FALSE ; parameter LoadFileName = "" ; // Place filename in "" parameter SaveOnPowerDown = `FALSE ; parameter SaveFileName = "" ; // Place filename in "" // Flag to show the running algorithm is done. reg AlgDone ; // Flag to show that a Cmd has been written // and needs predecoding reg CmdValid ; // Number of addition writes necessary to // supply the current command information. // When it hits zero it goes to Decode integer DataPtr ; // Represents CSR bit. wire DeviceOperationError ; // Internal representation of CSR bit reg OperationError ; // Internal representation of CSR bit reg EraseError ; // Flag that determines if the chip is driving // the outputs reg DriveOutputs ; reg GoingToHighZ ; // Internal value of the out data. If DriveOutputs // is active this value will be placed on the // outputs. -1 == Unknown or XXXX integer InternalOutput ; // Master internal write enable wire Internal_WE ; // Master internal output enable wire Internal_OE ; // Master internal read enable wire Internal_RE ; reg ProgramError ; // Internal flag to tell if an algorithm is running wire RdyBsy ; // Flag from decoding to show that the // current command needs to be placed in // the algorithm queue. reg Enqueue ; // Internal representation of the ESR bit wire PBavailStatus ; // Support information for the page buffers reg [1:0] PBInUse ; // Pointer to currently active page buffer reg PBPtr ; // Flag to tell if Queue Slot 1 is valid reg Q1Valid ; // Flag to tell if Queue Slot 2 is valid reg Q2Valid ; // Flag to tell if Queue Slot 3 is valid reg Q3Valid ; // Flag to tell if in the process of queuing a command. reg QueueCmd ; // Internal representation of ESR bit. wire QueueFull ; // Flag for if the part is in Sleep reg Sleep ; // Flag to tell executer to process the command // in the second Queue position. reg GoTwoCmd ; // Algorithm Timer reg TimerClk ; reg LV_ceb; reg LV_ReadMode; reg LV_ceb0; reg LV_ceb1; reg LV_WE; // Flag to represent if the chip is suspended reg Suspended ; // Current Vpp Range (five volt/ 12 volt) reg VppLevel ; reg VppError ; // Internal representation of GSR bit reg [`ReadMode_T] ReadMode ; // Combined chip enables (active low) wire ceb ; // Current value of the CSR wire [`Byte] CSR ; // Current value of the BSR reg [`Byte] BSR [`BSRmem_T] ; // Current value of the GSR wire [`Byte] GSR ; // This is slot 1 (first) of the chip algorithm queue reg [`Cmd_T] Queue1 ; // This is slot 2 (middle) of the chip algorithm queue reg [`Cmd_T] Queue2 ; // This is slot 3 (last) of the chip algorithm queue reg [`Cmd_T] Queue3 ; // Points to the block currently being erased // -1 is No Block -2 is Block to be determined integer ErasingBlock ; // Contains the NonVolatile Block lock bits reg [`NVLockBit_T] NVLockBit ; // Current Ready Busy pin configuration integer RdyBsyConfig ; // Internal flag for Rdy Bsy generation. reg EndOfProgramPB ; // Internal flag for Rdy Bsy generation. reg EndOfErase ; // Startup Flag phase reg StartUpFlag ; reg ClearVppFlag ; reg VppFlag ; // Contains all current timing values // Global Reset Flag reg Reset ; reg [`Word] MainArray [`MainArray_T] ; // Number of timer cycles remaining for the // current algorithm integer AlgTime; // This records where the algorithm is when the // the chip suspends or Queue slot 1 is interrupted. integer PauseTime ; // This points to where data written to the part will // go. By default it is to NewCmd. CmdField means the // chip is waiting on more data for the cmd (ie confirm) // ToPB mean to the Page Buffer . reg [`WritePtr_T] WriteToPtr ; // Contains the current executing command and all its // support information. reg [`Cmd_T] Cmd ; // This contains the contents of the page buffers reg [31 : 0] PageBuffer [`PageBuffer_T] ; // This flag determines if after the algorithms in // the queue finish, the chips sleeps reg PendingSleep ; // Points to the next block to be erased if there is // something waiting on the block, it will set this // and execute after the erase of that block is done. integer AwaitingErase ; // Number of words to go into to PageBuffer integer WordCount ; // Row pointer in the PageBuffer reg [6:0] PBindex ; reg TheByte ; integer LowByte ; // Current output of the Page Buffer integer PBOut ; // Current output of the Block Status Registers reg [`Byte] BSROut ; // Current output of the Extend status register integer ESROut ; // Current output of the Main Array // The currently inputing command and its support information reg [`Cmd_T] TheCmd ; integer ArrayOut ; // Current output of the Compatible status register integer CSROut ; // Current output of the Intelligent Identifer (tm) integer IDOut ; // Generic temporary varible integer LoopCntr ; // Flag for if the chip is suspended reg Suspend ; // Generic temporary varible integer LoopCounter ; // Pointer to a Block integer BlockNum ; // Pointer to a Row reg [`RowRange] RowNum ; // Another pointer to a Blcok integer BlockPtr ; // Generic temporary variable integer Tmp1 ; // Generic temporary variable integer Tmp2 ; // Generic temporary variable reg [6:0] Index ; // Generic reg Other ; // Generic variable integer LoopCount ; // Generic variable integer NewData ; // A Generic fail flag reg Fail ; time ToOut ; time LastWE ; // Contains all current timing values time TPHIL ; time TIHIL ; time TILIH ; time TAVIH ; time TDVIH ; time TIHAX ; time TIHDX ; time TGHIL ; time TIHGL ; time TAVQV ; time TGHQZ ; time TGLQX ; time TIHRL ; time TRLRZ ; time TimerPeriod ; time last_addr_time ,curr_addr_time; time last_dq_time ,curr_dq_time; time last_ReadMode_time, curr_ReadMode_time ; time last_Internal_RE_time, curr_Internal_RE_time ; time last_Internal_WE_time, curr_Internal_WE_time ; time last_Internal_OE_time, curr_Internal_OE_time ; time last_rpb_time, curr_rpb_time ; time WriteRecovery ; reg Internal_OE_flag ; reg VppErrFlag ; reg Q1Valid_event, Q2Valid_event ; reg [`COV_T] CurrOperatingVoltage ; real vpp, vcc ; //----------------------------------------------------------- // BlockLocked // Description: Determines it the current block is locked -- //----------------------------------------------------------- function BlockLocked; input [31:0] TheBlock ; reg [`Byte] temp ; begin if (TheBlock <= 31) begin temp = BSR[TheBlock] ; if (((temp[6] == 1'b0) || (NVLockBit[TheBlock] == 1'b1)) && (wpb == 1'b0)) begin BlockLocked = `TRUE; end else begin BlockLocked = `FALSE; end end else BlockLocked = `FALSE; end endfunction task TimingInit; input [`COV_T] OpVolts; begin if (OpVolts == `ThreeVolt) begin TPHIL = `TPHIL_3v; TIHIL = `TIHIL_3v; TILIH = `TILIH_3v; TAVIH = `TAVIH_3v; TDVIH = `TDVIH_3v; TIHAX = `TIHAX_3v; TIHDX = `TIHDX_3v; TGHIL = `TGHIL_3v; TIHGL = `TGHIL_3v; TAVQV = `TAVQV_3v; TGHQZ = `TGHQZ_3v; TGLQX = `TGLQX_3v; TIHRL = `TIHRL_3v; TRLRZ = `TRLRZ_3v; TimerPeriod = `TimerPeriod_3v; end else begin TPHIL = `TPHIL_5v; TIHIL = `TIHIL_5v; TILIH = `TILIH_5v; TAVIH = `TAVIH_5v; TDVIH = `TDVIH_5v; TIHAX = `TIHAX_5v; TIHDX = `TIHDX_5v; TGHIL = `TGHIL_5v; TIHGL = `TGHIL_5v; TAVQV = `TAVQV_5v; TGHQZ = `TGHQZ_5v; TGLQX = `TGLQX_5v; TIHRL = `TIHRL_5v; TRLRZ = `TRLRZ_5v; TimerPeriod = `TimerPeriod_5v; end end endtask //----------------------------------------------------------------- // LoadAll // This is used when the generic flag is set so that the Main Ar // ray contains code at startup. Basically it loads the // array from data in a file . //---------------------------------------------------------------- task LoadAll ; output [`NVLockBit_T] NVLockBit ; reg [31:0] ArrayInFile, DataIn ; integer BlockPtr,RowPtr ; begin $readmemh(LoadFileName,MainArray); DataIn[15:0] = MainArray[`Top-1]; DataIn[31:16] = MainArray[`Top]; for (BlockPtr = 0 ; BlockPtr <= 31; BlockPtr = BlockPtr + 1) begin if (DataIn [BlockPtr]) NVLockBit[BlockPtr] = 1'b1 ; else NVLockBit[BlockPtr] = 1'b0 ; end end endtask //----------------------------------------------------------------- // StoreAll // This is used when the generic flag is set so that the Main // Array stores code at powerdown. Basically it stores the // array into a file //----------------------------------------------------------------- task StoreAll; input [`NVLockBit_T] NVLockBit ; reg [31:0] DataOut ; reg [15:0] outfile ; integer BlockPtr,RowPtr ; begin outfile = $fopen(SaveFileName) ; if (outfile == 0) $display("Oops, cannot open output file %s",SaveFileName) ; for (BlockPtr = 0 ; BlockPtr <= 31; BlockPtr = BlockPtr + 1) begin for (RowPtr = 0 ; RowPtr <= `BlockRowSize; RowPtr = RowPtr + 1) $fdisplay(outfile,"%h",MainArray[{BlockPtr,RowPtr}]); end $fdisplay (outfile,"%h",NVLockBit[31:16]); $fdisplay (outfile,"%h",NVLockBit[15:0]); end endtask //------------------------------------------------------ // Program // -- Description: Programs new values in to the array -- //------------------------------------------------------ task Program ; inout [`Word] TheArrayValue ; input [`Word] DataIn ; input [1:0] BytePtr_T ; reg [31:0] OldData, NewData, LowByte, temp ; begin OldData = TheArrayValue; LowByte = OldData % 256; temp = DataIn ; if (temp [15:8] === 8'hx) temp [15:8] = 0 ; if (temp [7:0] === 8'hx) temp [7:0] = 0 ; case (BytePtr_T) `By16_BytePtr : begin NewData = temp; end `By8H : begin NewData = temp*256 + LowByte; end `By8L : begin NewData = temp + OldData - LowByte; end endcase TheArrayValue = NewData & OldData; end endtask //-------------------------------------------------------- // UploadStatus // - Description: Read nonvolitile lock bits in to the BSR //-------------------------------------------------------- task UploadStatus ; input [`NVLockBit_T] NVLockBit ; integer LoopCount ; reg [`Byte] temp; begin for (LoopCount = 0 ; LoopCount <= 31 ; LoopCount = LoopCount + 1) begin temp = BSR[LoopCount] ; temp[6] = ! NVLockBit[LoopCount] ; BSR[LoopCount] = temp ; end end endtask assign ceb = ceb0 | ceb1 ; assign Internal_OE = !(ceb | oeb | !rpb) ; assign Internal_RE = (((RdyBsy == `Rdy) || (ReadMode != `rdARRAY)) && !ceb && !Reset) ; assign Internal_WE = !(ceb | web | !rpb) ; // Determine if the algorithm engine is operating assign RdyBsy = ((Q1Valid | Q2Valid | Q3Valid) & !Suspended) ? `Bsy : `Rdy; assign QueueFull = Q3Valid | QueueCmd ; // register definitions // // Compatible Status Register assign CSR [7] = (RdyBsy == `Bsy) ? 1'b0 : 1'b1 ; assign CSR [6] = Suspended ; assign CSR [5] = EraseError ; assign CSR [4] = ProgramError ; assign CSR [3] = VppError ; assign CSR [2:0] = 3'b000; assign DeviceOperationError = ProgramError || EraseError || OperationError; assign PBavailStatus = ~ (PBInUse[0] & PBInUse[1]); // Global Status Register (same as Extended Status Register) assign GSR[7] = (RdyBsy == `Bsy) ? 1'b0 : 1'b1 ; assign GSR[6] = Suspended ; assign GSR[5] = DeviceOperationError ; assign GSR[4] = Sleep | PendingSleep ; assign GSR[3] = QueueFull ; assign GSR[2] = PBavailStatus ; assign GSR[1] = !PBInUse[PBPtr] ; assign GSR[0] = (PBPtr == 1) ? 1'b1 : 1'b0 ; // Output Drivers // assign dq [15 : 8] = (DriveOutputs && (byteb == 1'b1) && (InternalOutput > -1)) ? InternalOutput / 256 : ((DriveOutputs && (byteb == 1'b1) && (InternalOutput < 0)) ? 8'hx : 8'hz) ; assign dq [7 : 0] = (DriveOutputs && !((byteb == 1'b0) && addr [0] == 1'b1) && (InternalOutput > -1)) ? InternalOutput % 256 : ((DriveOutputs && ((byteb == 1'b0) && addr [0] == 1'b1) && (InternalOutput > -1)) ? InternalOutput / 256 : ((DriveOutputs && (InternalOutput < 0)) ? 8'hx : 8'hz)) ; initial begin AlgDone = `FALSE ; CmdValid = `FALSE ; DataPtr = 0 ; OperationError = `FALSE ; EraseError = `FALSE ; DriveOutputs = `FALSE ; Enqueue = `FALSE ; Q1Valid = `FALSE ; Q2Valid = `FALSE ; Q3Valid = `FALSE ; QueueCmd = `FALSE ; Sleep = `FALSE ; GoTwoCmd = `FALSE ; RdyBsyConfig = 1 ; EndOfProgramPB = `FALSE ; EndOfErase = `FALSE ; ErasingBlock = -1 ; PendingSleep = `FALSE ; PBOut = 0 ; ESROut = 0 ; LowByte = 0 ; PBindex = 0 ; AwaitingErase = -2 ; WordCount = 0 ; CurrOperatingVoltage = `Unknown; Suspended = `FALSE ; TimerClk = 1'b0; VppLevel = `FALSE ; VppError = `FALSE ; StartUpFlag = `TRUE ; StartUpFlag <= #2 `FALSE ; ClearVppFlag = `FALSE ; VppFlag = `FALSE ; Reset = 1'bx ; Reset <= `TRUE ; PauseTime = 0 ; WriteToPtr = `NewCmd ; ArrayOut = 0 ; CSROut = 0 ; IDOut = 0 ; LoopCntr = 0 ; Suspend = `FALSE ; LoopCounter = 0 ; BlockNum = 0 ; RowNum = 0 ; BlockPtr = 0 ; Tmp1 = 0 ; Tmp2 = 0 ; Index = 0 ; Other = `FALSE ; LoopCount = 0 ; NewData = 0 ; Fail = `FALSE ; ToOut = 0 ; LastWE = 0 ; Internal_OE_flag = `FALSE ; VppErrFlag = `FALSE ; GoingToHighZ = `FALSE ; last_dq_time = 0 ; curr_dq_time = 0 ; last_addr_time = 0 ; curr_addr_time = 0 ; last_rpb_time = 0 ; curr_rpb_time = 0 ; last_ReadMode_time = 0 ; curr_ReadMode_time = 0 ; last_Internal_RE_time = 0 ; curr_Internal_RE_time = 0 ; last_Internal_OE_time = 0 ; curr_Internal_OE_time = 0 ; last_Internal_WE_time = 0 ; curr_Internal_WE_time = 0 ; Q1Valid_event = `FALSE ; Q2Valid_event = `FALSE ; InternalOutput = -1 ; WriteRecovery = 0 ; // // Array Init // // if (LoadOnPowerUp) LoadAll(NVLockBit) ; else begin for (LoopCntr = 0; LoopCntr <= 31; LoopCntr = LoopCntr + 1) begin NVLockBit[LoopCntr] = 1'b0; for(LoopCount = 0; LoopCount <= `BlockRowSize; LoopCount = LoopCount + 1) begin MainArray [{LoopCntr,LoopCount[14:0]}] = 'hFFFF ; end end end end always @(vpp_b) vpp = $bitstoreal(vpp_b) ; always @(vcc_b) vcc = $bitstoreal(vcc_b) ; // record the time for addr changes . always @(addr) begin if($time > 0) begin curr_addr_time = $time ; end end // record the time for rpb changes . always @(rpb) begin if ($time > 0) begin curr_rpb_time = $time ; end end // record the time for ReadMode changes . always @(ReadMode) begin if ($time > 0) begin curr_ReadMode_time = $time ; end end // record the time for Internal_RE changes . always @(Internal_RE) begin if($time > 0) begin curr_Internal_RE_time = $time ; end end always @(Q1Valid) begin Q1Valid_event = `TRUE ; #0 Q1Valid_event = `FALSE ; end always @(Q2Valid) begin Q2Valid_event = `TRUE ; #0 Q2Valid_event = `FALSE ; end always @(Reset) begin : Reset_process if (Reset) begin ClearVppFlag <= #1 `TRUE ; ClearVppFlag <= #9 `FALSE; AlgDone = `FALSE ; CmdValid = `FALSE ; DataPtr = 0 ; EraseError = `FALSE ; ProgramError = `FALSE ; Enqueue = `FALSE ; InternalOutput = -1 ; OperationError = `FALSE ; Suspended = `FALSE ; PBInUse[0] = `FALSE ; PBInUse[1] = `FALSE ; PBPtr = 0 ; Q1Valid = `FALSE ; Q2Valid = `FALSE ; Q3Valid = `FALSE ; QueueCmd = `FALSE ; Sleep = `FALSE ; GoTwoCmd = `FALSE ; RdyBsyConfig = 1 ; EndOfProgramPB = `FALSE ; EndOfErase = `FALSE ; ErasingBlock = -1 ; PendingSleep = `FALSE ; AwaitingErase = -1 ; ArrayOut = 0 ; ESROut = 0 ; PBOut = 0 ; for (LoopCntr = 0; LoopCntr <= 31; LoopCntr = LoopCntr + 1) begin BSR[LoopCntr] = 0 ; end VppError = `FALSE ; ReadMode = `rdARRAY ; AlgTime = 0 ; PauseTime = 0 ; WriteToPtr = `NewCmd ; CSROut = 0 ; IDOut = 0 ; Suspend = `FALSE ; end end always @(Internal_RE or ReadMode or addr) begin : array_read // // array reads // if (Internal_RE && ReadMode == `rdARRAY) begin // Get Block and RowNum, then read the array BlockNum = addr[20:16] ; RowNum = addr[15:1] ; ArrayOut = MainArray[{BlockNum,RowNum}] ; end else if (Internal_RE && ReadMode == `rdPB) begin PBindex = addr[7:1] ; PBOut = PageBuffer[{PBPtr,PBindex}] ; end end always @(Internal_RE or ReadMode or addr or Internal_OE) begin // output mux // Determine and generate the access time . #0 if (Internal_OE_flag == `FALSE) begin if ((ReadMode == `rdARRAY) || (ReadMode == `rdPB)) ToOut = TAVQV; else ToOut = 0.001 ; end else begin if ((ReadMode == `rdARRAY) || (ReadMode == `rdPB)) ToOut = 1 ; else ToOut = 30 ; if ($time > TAVQV) begin last_addr_time = $time - curr_addr_time; if ((last_addr_time < TAVQV) && ((TAVQV - last_addr_time) > ToOut)) ToOut = TAVQV - last_addr_time ; last_ReadMode_time = $time - curr_ReadMode_time; if ((last_ReadMode_time < TAVQV) && ((TAVQV - last_ReadMode_time) > ToOut)) ToOut = TAVQV - last_ReadMode_time ; last_Internal_RE_time = $time - curr_Internal_RE_time ; if ((last_Internal_RE_time < TAVQV) && ((TAVQV - last_Internal_RE_time) > ToOut)) begin ToOut = TAVQV - last_Internal_RE_time ; end end Internal_OE_flag = `FALSE ; end // Output Mux with timing if (!StartUpFlag) begin InternalOutput <= -1 ; #0 case (ReadMode) `rdARRAY : begin InternalOutput <= #ToOut ArrayOut ; end `rdCSR : begin if (byteb == 1'b0) InternalOutput <= #ToOut CSROut + 256*CSROut ; else InternalOutput <= #ToOut CSROut ; end `rdID : begin if (byteb == 1'b0) InternalOutput <= #ToOut IDOut + 256*IDOut ; else InternalOutput <= #ToOut IDOut ; end `rdESR : begin if (byteb == 1'b0) InternalOutput <= #ToOut ESROut + 256*ESROut ; else InternalOutput <= #ToOut ESROut ; end `rdPB : begin if (byteb == 1'b0) InternalOutput <= #ToOut PBOut ; else InternalOutput <= #ToOut PBOut ; end endcase end end // // other reads // always @(Internal_OE) begin : other_read if (!Reset) begin Internal_OE_flag = `TRUE ; if (ReadMode != `rdARRAY) begin CSROut = CSR ; if ((addr[15:3] == 0) && (byteb || !addr[0])) begin // Create the correct BSR output if (addr[2:1] == 2'b01) begin BlockPtr = addr[20:16] ; BSROut = BSR[BlockPtr] ; BSROut[1] = 0; if (QueueFull) BSROut[3] = 1'b1 ; else BSROut[3] = 1'b0 ; if ((( (Q1Valid && (Queue1[`OpBlock] == BlockPtr)) || (Q2Valid && (Queue2[`OpBlock] == BlockPtr)) || (Q3Valid && (Queue3[`OpBlock] == BlockPtr)) ) && ( Queue1[`OpType] != `Erase || GoTwoCmd)) || BSROut[7]) BSROut[7] = 1'b0 ; else BSROut[7] = 1'b1 ; ESROut = BSROut ; end else if (addr[2:1] == 2'b10) ESROut = GSR ; else ESROut = 0 ; // RESERVED end else ESROut = 0 ; // RESERVED if (byteb == 1'b0) begin if (addr[0] == 1'b0) IDOut = `ID_ManufacturerB ; else IDOut = `ID_DeviceCodeB ; end else begin if (addr[1] == 1'b0) IDOut = `ID_ManufacturerW; else IDOut = `ID_DeviceCodeW; end end end end // Handle Write to Part always @(negedge Internal_WE) begin : handle_write reg [`Word] temp ; // temporary variable needed for double // indexing CmdData. if (!Reset) begin if (byteb == 1'b0) // Record state of byte pin TheByte = `By8; else TheByte = `By16_Byte; case (WriteToPtr) // Where are we writting to ? `NewCmd : begin // This is a new command. Cmd[`Cmd] = dq[7:0] ; Cmd[`Add] = addr[20:0] ; Cmd[`BytePin] = TheByte ; CmdValid <= `TRUE ; // CmdValid sends it to the Predecode section DataPtr <= -1 ; end `CmdField : begin // This is data used by another command if (DataPtr == 1) begin temp = Cmd[`CmdData_1] ; Cmd[`CmdData_1] = (TheByte == `By16_Byte) ? dq[15:0] : {temp[15:8],dq [7:0]} ; Cmd[`CmdByte_1] = TheByte ; Cmd[`CmdAdd_1] = addr [20:0] ; end else if (DataPtr == 2) begin temp = Cmd[`CmdData_2] ; Cmd[`CmdData_2] = (TheByte == `By16_Byte) ? dq[15:0] : {temp[15:8],dq[7:0]} ; Cmd[`CmdByte_2] = TheByte ; Cmd[`CmdAdd_2] = addr[20:0] ; end else $display("DataPtr out of range") ; DataPtr <= #1 DataPtr - 1 ; // When DataPtr hits zero the command goes to the // Decode section end `toPB : begin // This is when we are writing to the page buffer Index = addr [7:1] ; DataPtr <= DataPtr - 1 ; // When DataPtr hits zero the write mode will be // reset in the decode section if (byteb == 1'b0) begin if (PageBuffer[{PBPtr,Index}] === 32'hx) PageBuffer[{PBPtr,Index}] = 0 ; LowByte = PageBuffer[{PBPtr,Index}] % 256; if (addr[0] == 1'b0) PageBuffer[{PBPtr,Index}] = PageBuffer[{PBPtr,Index}] - LowByte + dq[7:0] ; else PageBuffer[{PBPtr,Index}] = LowByte + 256*dq[7:0] ; end else PageBuffer [{PBPtr,Index}] = dq[`Word] ; end endcase end end // // Predecode Command // always @(posedge CmdValid) begin : predecode reg [`Byte] temp; // temporary variable needed for double // indexing BSR. if (!Reset) begin // Set Defaults Cmd [`UsesPB] = `FALSE ; Cmd [`OpType] = `Program ; WriteToPtr = `NewCmd ; DataPtr <= 0 ; case (Cmd [`Cmd]) // Handle the basic read mode commands // READ ARRAY COMMAND -- `ReadArrayCmd : begin // Read Flash Array CmdValid <= `FALSE ; if (Sleep) // Read Array take the part out of Sleep Sleep <= `FALSE ; if (PendingSleep || (RdyBsy == `Bsy)) // Can not read array when running an algorithm ReadMode <= `rdCSR ; else ReadMode <= `rdARRAY ; end // READ INTELLIGENT IDENTIFIER COMMAND -- `ReadIDCmd : begin // Read Intelligent ID ReadMode <= `rdID ; CmdValid <= `FALSE ; end // READ COMPATIBLE STATUS REGISTER COMMAND -- `ReadCSRCmd : begin // Read CSR ReadMode <= `rdCSR ; CmdValid <= `FALSE ; end // READ EXTEND STATUS REGISTER COMMAND -- `ReadESRCmd : begin // Read ESR ReadMode <= `rdESR ; CmdValid <= `FALSE ; end default : begin Other = `TRUE ; // Other flag marks commands that are algorithms Cmd [`Confirm] = `FALSE ; // Defaults Cmd [`UsesPB] = `FALSE ; case (Cmd [`Cmd]) // PROGRAM WORD/BYTE COMMAND -- `ProgramCmd : begin // Program Word/Byte WriteToPtr = `CmdField ; DataPtr <= 1 ; Cmd [`Time] = `AC_ProgramTime ; end // PROGRAM WORD/BYTE COMMAND -- `Program2Cmd : begin // Program Word/Byte Cmd [`Cmd] = `ProgramCmd ; WriteToPtr = `CmdField ; DataPtr <= 1 ; Cmd [`Time] = `AC_ProgramTime ; end // ERASE BLOCK COMMAND -- `EraseSingleBlockCmd : begin // Single Block Erase WriteToPtr = `CmdField ; DataPtr <= 1 ; Cmd [`Time] = 1 ; Cmd [`OpType] = `Erase ; Cmd [`Confirm] = `TRUE ; end // TWO BYTE PROGRAM COMMAND -- `TwoByteWriteCmd : begin // Two Byte Write WriteToPtr = `CmdField ; DataPtr <= 2 ; Cmd [`Time] = `AC_TwoByteProgramTime ; end // ERASE ALL UNLOCKED BLOCKS COMMAND -- `EraseAllBlocksCmd : begin // Erase All Unlocked Blocks WriteToPtr = `CmdField ; Cmd [`OpType] = `Erase ; DataPtr <= 1 ; // ErasingBlock = -2; // Jump to execute to find highest block Cmd [`Time] = 1 ; Cmd [`Confirm] = `TRUE ; end // PAGE BUFFER PROGRAM TO FLASH COMMAND -- `PBWriteFlashCmd : begin // Page Buffer Write to Flash WriteToPtr = `CmdField ; DataPtr <= 2 ; Cmd [`UsesPB] = `TRUE ; end // LOCK BLOCK COMMAND -- `LockBlockCmd : begin // Lock Block WriteToPtr = `CmdField ; DataPtr <= 1 ; Cmd [`Time] = `AC_LockBlockTime ; Cmd [`Confirm] = `TRUE ; end // UPLOAD STATUS COMMAND -- `StatusUploadCmd : begin // Status Up load WriteToPtr = `CmdField ; DataPtr <= 1 ; Cmd [`Time] = `AC_StatUploadCmd ; Cmd [`Confirm] = `TRUE ; end // DEVICE CONFIGURE COMMAND -- `DeviceConfigCmd : begin // Device Config WriteToPtr = `CmdField ; DataPtr <= 1 ; Cmd [`Time] = `AC_DeviceConfigTime ; end // UPLOAD DEVICE INFORMATOIN COMMAND -- `UpDevInfoCmd : begin // Upload Device Information WriteToPtr = `CmdField ; DataPtr <= 1 ; Cmd [`UsesPB] = `TRUE ; Cmd [`Time] = `AC_UpDevInfoTime ; Cmd [`Confirm] = `TRUE ; end default : begin // The remaining commands are complex non-algorithm commands Other = `FALSE ; CmdValid = `FALSE ; // SWAP PAGE BUFFER COMMAND -- if (Cmd [`Cmd] == `SwapPBCmd) begin PBPtr <= 1 - PBPtr ; ReadMode <= `rdESR ; end // COMMANDS: SEQUENTIAL WRITE TO PAGE BUFFER, WRITE TO PAGE BUFFER, and READ PAGE BUFFER -- else if ((Cmd [`Cmd] == `SeqPBWriteCmd) || (Cmd [`Cmd] == `WritePBCmd) || (Cmd [`Cmd] == `ReadPBCmd)) begin if (Sleep) // Check if Part is sleep (legal for ReadPB) if (Cmd [`Cmd] == `ReadPBCmd) ReadMode <= `rdPB ; else ReadMode <= `rdCSR ; else begin // Are we waiting to sleep, if so kill it if ((Cmd [`Cmd] != `ReadPBCmd) && PendingSleep && !PBInUse [PBPtr]) PendingSleep = `FALSE ; // Is the pagebuffer availible ? if (PBInUse [PBPtr]) if (Suspend || (Cmd [`Cmd] == `ReadPBCmd)) ReadMode <= `rdARRAY ; else ReadMode <= `rdESR ; // execute command else if (Cmd [`Cmd] == `ReadPBCmd) ReadMode <= `rdPB ; else if (Cmd [`Cmd] == `WritePBCmd) begin WriteToPtr = `toPB ; DataPtr <= 1 ; end else begin WriteToPtr = `CmdField ; CmdValid = `TRUE ; DataPtr <= 2 ; end end end // COMMANDS: CLEAR STATUS REGISTERS, RESUME, and SLEEP -- else if ((Cmd[`Cmd] == `ClearSRCmd)||(Cmd[`Cmd] == `ResumeCmd)||(Cmd[`Cmd] == `SleepCmd)) begin CmdValid <= `FALSE ; // Check for illegal conditions if (Sleep || PendingSleep) ReadMode = `rdCSR ; else if (Cmd [`Cmd] == `SleepCmd) begin if (Suspend) ReadMode <= `rdARRAY ; else if (RdyBsy == `Bsy) begin PendingSleep = `TRUE ; ReadMode <= `rdCSR ; end else begin Sleep <= `TRUE ; ReadMode <= `rdCSR ; end end else if (Cmd [`Cmd] == `ClearSRCmd) begin if (Suspend) ReadMode <= `rdARRAY ; else if (RdyBsy == `Bsy) ReadMode <= `rdCSR ; else begin EraseError <= `FALSE ; OperationError <= `FALSE; ProgramError <= `FALSE ; VppError <= `FALSE ; for (LoopCntr = 0; LoopCntr <= 31; LoopCntr = LoopCntr + 1) BSR [LoopCntr] = 0 ; ReadMode <= `rdARRAY ; end end else if (Cmd [`Cmd] == `ResumeCmd) begin if (Suspended) ReadMode <= `rdCSR ; Suspend = `FALSE ; Suspended <= `FALSE ; end end // SUSPEND COMMAND -- else if (Cmd [`Cmd] == `SuspendCmd) begin if (PendingSleep) PendingSleep = `FALSE ; if (Sleep) ReadMode <= `rdCSR ; else if (RdyBsy == `Rdy) ReadMode <= `rdARRAY ; else begin ReadMode <= `rdCSR ; Suspend = `TRUE ; end CmdValid <= `FALSE ; end // ABORT COMMAND -- else if (Cmd [`Cmd] == `AbortCmd) begin if (Q2Valid) begin temp = BSR[Queue2[`OpBlock]] ; temp [5:4] = 2'b11 ; BSR[Queue2[`OpBlock]] = temp ; end if (Q3Valid) begin temp = BSR[Queue3[`OpBlock]] ; temp [5:4] = 2'b11 ; BSR[Queue3[`OpBlock]] = temp ; end CmdValid <= `FALSE ; if (Queue1[`OpType] == `Erase) begin Q1Valid <= `FALSE ; for (Tmp1 = 0 ; Tmp1 <= 31 ; Tmp1 = Tmp1 + 1) begin temp = BSR [Tmp1] ; if (temp [7]) begin temp [5:4] = 2'b11 ; temp [7] = 1'b0 ; end BSR [Tmp1] = temp ; $display("IN ABORT SETTING BSR(%h) to %h",Tmp1,temp); end end Q2Valid <= `FALSE ; Q3Valid <= `FALSE ; Sleep <= `TRUE ; PendingSleep = `FALSE ; Suspend = `FALSE ; if (Queue1 [`OpType] == `Program) ProgramError <= `TRUE ; else if (Queue1[`OpType] == `Erase) EraseError <= `TRUE ; else OperationError <= `TRUE ; end else begin CmdValid <= `FALSE ; $display("Warning:Illegal Command"); end end endcase // HANDLE ALGORITHMS if (Other) begin if (PendingSleep) PendingSleep = `FALSE ; if (Sleep) begin $display("Attempted to issue command during sleep. Command was Ignored"); CmdValid <= `FALSE ; WriteToPtr = `NewCmd ; ReadMode <= `rdCSR ; end if (Suspended) begin $display("Attempted to issue command during suspend. Command was Ignored"); CmdValid <= `FALSE ; WriteToPtr = `NewCmd ; ReadMode <= `rdARRAY ; end else if ((Cmd[`UsesPB] && PBInUse [PBPtr]) || QueueFull) begin WriteToPtr = `NewCmd ; CmdValid <= `FALSE ; if (RdyBsy == `Rdy) ReadMode <= `rdARRAY ; else ReadMode <= `rdCSR ; end end end endcase end end // // Command Decode // always @(DataPtr) begin : command reg [`Byte] temp ; // temporary variable needed for double indexing BSR. if (!Reset && (DataPtr == 0) && (WriteToPtr != `NewCmd)) begin // When DataPtr hits zero it means that all the // additional data has been given to the current command if (WriteToPtr == `toPB) // If we were writing to the page buffer than we just finished WriteToPtr = `NewCmd; else if (CmdValid && (WriteToPtr == `CmdField)) begin WriteToPtr = `NewCmd; // Just finish a multi-cycle command. Determine which block the command uses Cmd [`OpBlock] = Cmd [`CmdAdd1 : `CmdAdd1-4] ; if (Cmd [`Cmd] == `EraseSingleBlockCmd) begin // If in erase mark block as locked Tmp1 = 2; if (RdyBsy == `Rdy) ErasingBlock = -2; temp = BSR [Cmd [`OpBlock]] ; // if (BlockLocked(Cmd[`OpBlock])) begin // EraseError = 1'b1 ; // temp[5] = 1; // end // else temp[7] = 1'b1 ; BSR[Cmd [`OpBlock]] = temp ; end else if (Cmd [`Cmd] == `EraseAllBlocksCmd) begin UploadStatus(NVLockBit); if (RdyBsy == `Rdy) ErasingBlock = -2; for (Tmp1 = 0 ; Tmp1 <= 31 ; Tmp1 = Tmp1 + 1) if (!BlockLocked(Tmp1)) begin temp = BSR [Tmp1] ; temp [7] = 1'b1 ; BSR [Tmp1] = temp ; end end // If the is a page buffer write to flash then we must determine how long the // algoritm will take. This depends on the number of bytes which was just given. else if (Cmd [`Cmd] == `PBWriteFlashCmd) begin // Page Buffer Write to Flash if (Cmd [`BytePin] == `By8) begin // Cmd[`CmdData_1] [7 : 0] Tmp1 = Cmd [`CmdData1_lo + 7 : `CmdData1_lo] ; // Cmd [`CmdData_2] [7 : 0] Tmp2 = Cmd [`CmdData2_lo + 7 : `CmdData2_lo] ; if (Cmd [`CmdAdd2_lo] == 1'b1) // Cmd [`CmdAdd_1] [0] = 1 Cmd [`Count] = ((256 * Tmp2) + Tmp1) + 1; else Cmd [`Count] = ((256 * Tmp1) + Tmp2) + 1; Cmd [`Time] = (Cmd [`Count] / 2) * `AC_PBProgramTime; end else begin Cmd [`Count] = Cmd [`CmdData_2] + 1 ; Cmd [`Time] = `AC_PBProgramTime * Cmd [`Count] ; end end else if (Cmd [`Cmd] == `SeqPBWriteCmd) begin Tmp1 = Cmd [`CmdData2Fx8] ; // Cmd[`CmdData_2] [7 : 0] Tmp2 = Cmd [`CmdData1Fx8] ; // Cmd[`CmdData_1] [7 : 0] DataPtr <= ((256 * Tmp2) + Tmp1) +1; WriteToPtr = `toPB; CmdValid <= `FALSE; end // If this command needs a confirm // (flaged at predecode) then check if confirm was received if (Cmd [`Confirm]) begin if (Cmd[`CmdData1Fx8] == 8'hd0) begin // If the command is still valid put it in the queue and deactivate the array // If this command uses a page buffer swap // it out and mark it used if (Cmd [`UsesPB]) begin PBInUse [PBPtr] <= `TRUE ; Cmd[`PBPtr] = PBPtr; PBPtr <= 1 - PBPtr ; end Enqueue <= #1 `TRUE ; ReadMode <= `rdCSR; end else begin OperationError <= `TRUE; if (Cmd [`OpType] == `Erase ) begin ProgramError <= `TRUE; EraseError <= `TRUE; end CmdValid <= `FALSE; end end else if (Cmd [`Cmd] != `SeqPBWriteCmd) begin // If this command uses a page buffer swap // it out and mark it used if (Cmd [`UsesPB]) begin PBInUse [PBPtr] <= `TRUE ; Cmd[`PBPtr] = PBPtr; PBPtr <= 1 - PBPtr ; end Enqueue <= #1 `TRUE ; ReadMode <= `rdCSR; end end end end // // Program Timer -- // always @(RdyBsy) begin if ((!Reset) && (RdyBsy == `Bsy)) begin // If the algorithm engine just started, start the clock TimerClk <= #1 1'b1 ; TimerClk <= #TimerPeriod 1'b0 ; end else if ((!Reset) && (PendingSleep) && (RdyBsy == `Rdy)) begin Sleep <= `TRUE; PendingSleep <= `FALSE; end end always @(TimerClk) begin if ((!Reset) && (RdyBsy == `Bsy) && (TimerClk == 1'b0)) begin // Reschedule clock and decrement algorithm count TimerClk <= #1 1'b1 ; TimerClk <= #TimerPeriod 1'b0 ; if (Suspend) begin // Is the chip pending suspend? If so do it Suspend = `FALSE; Suspended <= `TRUE; end if (!Suspended && RdyBsy == `Bsy && Q1Valid) begin AlgTime = AlgTime - 1; if (AlgTime <= 0) begin // Check if the algorithm is done AlgDone <= #1 `TRUE ; AlgDone <= #10 `FALSE ; end end end end // // -- Erase Interrupt -- // always @(posedge Q2Valid) begin : erase reg [`Byte] temp ; // temporary variable needed for double indexing BSR. if (!Reset) begin if (Q2Valid && (Queue1 [`OpType] == `Erase)) begin // Check if we are in an interruptable condition temp = BSR [Queue2 [`OpBlock]] ; // If we are currently erasing (from previous if then) and another erase // erase is waiting right after us, then mark the block to be erased after // the current one and remove it from the queue. if ((Queue2 [`Cmd] == `EraseSingleBlockCmd) && Q2Valid) begin temp [7] = 1'b1 ; BSR [Queue2 [`OpBlock]] = temp ; Q2Valid <= #1 `FALSE ; end // If we are currently erasing (from previous if then) and an erase all // is waiting right after us, then mark all blocks to be erased after // the current one and remove it from the queue. else if ((Queue2 [`Cmd] == `EraseAllBlocksCmd) && Q2Valid) begin for (LoopCntr = 0; LoopCntr <= 31; LoopCntr = LoopCntr+1) begin // Mark Block if (!BlockLocked (LoopCntr)) begin temp = BSR [LoopCntr] ; temp [7] = 1'b1 ; BSR [LoopCntr] = temp ; end end Q2Valid <= `FALSE ; end else if (temp [7] == 1'b1) // Check to see if the command waiting can be executed now. Is the command's // block pointer points to a block we plan to erase ? If so mark the block // to be erased next, else pause and do the waiting command. AwaitingErase = Queue2 [`OpBlock] ; else begin GoTwoCmd <= `TRUE; PauseTime = AlgTime; AlgTime = Queue2 [`Time] ; end end end end ////////////// // Execution // ////////////// always @(posedge AlgDone) begin : execution reg [`Byte] temp ; // temporary variable needed for double indexing BSR. if (!Reset) begin if (AlgDone) begin // When the algorithm finishes // if chips is executing during an erase interrupt // then execute out of queue slot 2 if (GoTwoCmd) TheCmd = Queue2; else TheCmd = Queue1; if (TheCmd[`Cmd] != `DeviceConfigCmd) UploadStatus(NVLockBit); if (TheCmd [`OpType] == `Erase) begin if (VppFlag || BlockLocked(ErasingBlock)) begin if (VppFlag && Cmd [`Cmd] == `EraseAllBlocksCmd) begin for (Tmp1 = 0 ; Tmp1 <= 31 ; Tmp1 = Tmp1 + 1) if (temp [7] == 1'b1) begin temp = BSR [Tmp1] ; temp[5] = 1; temp[7] = 1'b0 ; temp[2] = 1; BSR [Tmp1] = temp ; end end else begin temp = BSR [ErasingBlock] ; temp[5] = 1; temp[7] = 1'b0 ; if (VppFlag) begin VppError <= `TRUE ; temp[2] = 1; end BSR [ErasingBlock] = temp ; end EraseError <= `TRUE; EndOfErase <= `TRUE ; EndOfErase <= #10 `FALSE ; Q1Valid <= #1 `FALSE ; // ERASE Terminates end else begin // ERASE COMMAND // if (ErasingBlock != -2) begin // Do ERASE to OpBlock for (LoopCount = 0; LoopCount <= `BlockRowSize; LoopCount = LoopCount + 1) MainArray [{ErasingBlock,LoopCount[14:0]}] = 'hFFFF ; // Erase Non Volatile Lock bit if ((ErasingBlock <= 31) && (ErasingBlock > -1)) NVLockBit [ErasingBlock] = 1'b0 ; else $display ("Illegal ErasingBlock = %d for NVLockBit",ErasingBlock) ; temp = BSR [ErasingBlock] ; temp [6] = 1'b1 ; // Unlock OpBlock temp [7] = 1'b0 ; BSR [ErasingBlock] = temp ; EndOfErase <= `TRUE ; EndOfErase <= #10 `FALSE ; end // Check if the command in slot 2 is waiting on the block just erased if (ErasingBlock == AwaitingErase) begin AwaitingErase = -1 ; GoTwoCmd <= `TRUE ; AlgTime = Queue2 [`Time] ; // When queue 2 cmd is done go back into erase after one clock and // search for the next block to erase. ErasingBlock = -2 ; PauseTime = 1 ; end else if (AwaitingErase != -1) begin // Check if the command in slot 2 is waiting on a block // needing erase if so erase in now ErasingBlock = AwaitingErase ; AlgTime = `AC_EraseTime ; end else begin // Search for next block to erase. Set Block Ptr to -1 to represent // not found BlockPtr = -1 ; for (LoopCount = 0; LoopCount <= 31; LoopCount = LoopCount + 1) begin temp = BSR [LoopCount] ; if (temp [7] == 1'b1) BlockPtr = LoopCount ; end // Did we find a block if so start erase algorithm else terminate // the erase mechanism. if (BlockPtr != -1) begin AlgTime = `AC_EraseTime ; ErasingBlock = BlockPtr ; end else Q1Valid <= #1 `FALSE ; // ERASE Terminates end end end else begin case (TheCmd [`Cmd]) // PROGRAM COMMAND // `ProgramCmd : begin if (VppFlag || BlockLocked(TheCmd [`OpBlock])) begin temp = BSR [TheCmd [`OpBlock]] ; temp[5] = 1; if (VppFlag) begin VppError <= `TRUE ; temp[2] = 1; end else EraseError <= `TRUE; BSR [TheCmd [`OpBlock]] = temp ; ProgramError <= `TRUE; end else begin RowNum = TheCmd [`CmdAdd1RowMSB : `CmdAdd1RowLSB] ; NewData = TheCmd [`CmdData_1] ; if (TheCmd [`BytePin] == `By8) begin if (TheCmd [`CmdAdd1_lo] == 1'b1) Program (MainArray[{TheCmd [`OpBlock], RowNum}],NewData,`By8H) ; else Program (MainArray[{TheCmd [`OpBlock], RowNum}],NewData,`By8L) ; end else Program (MainArray[{TheCmd [`OpBlock], RowNum}],NewData,`By16_BytePtr) ; end end // TWO BYTE WRITE COMMAND // `TwoByteWriteCmd : begin if (VppFlag || BlockLocked(TheCmd [`OpBlock])) begin ProgramError <= `TRUE; temp = BSR [TheCmd [`OpBlock]] ; temp[5] = 1; if (VppFlag) begin VppError <= `TRUE ; temp[2] = 1; end BSR [TheCmd [`OpBlock]] = temp ; end else begin RowNum = TheCmd [`CmdAdd1RowMSB : `CmdAdd1RowLSB] ; if (TheCmd [`CmdAdd2_lo] == 1'b0) begin NewData = 256* TheCmd[`CmdData1_lo + 7:`CmdData1_lo] + TheCmd[`CmdData2_lo + 7:`CmdData2_lo]; end else begin NewData = 256* TheCmd[`CmdData2_lo + 7:`CmdData2_lo] + TheCmd[`CmdData1_lo + 7:`CmdData1_lo] ; end Program (MainArray[{TheCmd [`OpBlock], RowNum}],NewData,`By16_BytePtr) ; end end // LOCK BLOCK COMMAND // `LockBlockCmd : begin if (VppFlag) begin OperationError <= `TRUE; temp = BSR[TheCmd [`OpBlock]] ; temp[5] = 1; temp[2] = 1; BSR[TheCmd [`OpBlock]] = temp ; VppError <= `TRUE ; end else begin temp = BSR[TheCmd [`OpBlock]] ; NVLockBit [TheCmd [`OpBlock]] = 1'b1 ; temp[6] = ! NVLockBit[TheCmd [`OpBlock]] ; BSR[TheCmd [`OpBlock]] = temp ; end end // UPLOAD STATUS COMMAND // `StatusUploadCmd : UploadStatus(NVLockBit) ; // WRITE PAGE BUFFER TO FLASH COMMAND // `PBWriteFlashCmd : begin if (VppFlag || BlockLocked(TheCmd [`OpBlock])) begin ProgramError <= `TRUE; temp = BSR [TheCmd [`OpBlock]] ; temp[5] = 1; if (VppFlag) begin VppError <= `TRUE ; temp[2] = 1; end BSR [TheCmd [`OpBlock]] = temp ; end else begin // Determine start point Tmp1 = 1; RowNum = TheCmd[`CmdAdd1RowMSB : `CmdAdd1RowLSB] ; PBindex = TheCmd [`CmdAdd1_lo+7 : `CmdAdd1_lo+1] ; if (TheCmd [`BytePin] == `By8) begin // If in by eight mode, handle the possible odd byte if (TheCmd [`CmdAdd1_lo] == 1'b1) begin NewData = PageBuffer [{TheCmd [`PBPtr],PBindex}] / 256 ; Program ( MainArray [{TheCmd [`OpBlock], RowNum}],NewData,`By8H) ; RowNum = RowNum + Tmp1 ; PBindex = PBindex + 1 ; TheCmd [`Count] = TheCmd [`Count] - 1 ; end WordCount = TheCmd [`Count] / 2 ; end else WordCount = TheCmd [`Count] ; // Program main section in a by 16 path (regardless of byte pin) Fail = `FALSE ; for (LoopCount = 1; LoopCount <= WordCount; LoopCount = LoopCount + 1) begin if (!Fail) begin NewData = PageBuffer [{TheCmd [`PBPtr],PBindex}] ; Program ( MainArray [{TheCmd [`OpBlock], RowNum}],NewData,`By16_BytePtr) ; PBindex = PBindex + 1 ; RowNum = RowNum + Tmp1 ; // Check to see if we have hit the boundry of the page buffer if so return to bottom. if (PBindex > `PBWordSize) PBindex = 0 ; // Check to see if we have hit the boundry of a block if so fail and quit programming. if (RowNum > `BlockRowSize) Fail = `TRUE ; end end if (!Fail && (TheCmd [`BytePin] == `By8) && (TheCmd [`Count] % 2 == 1)) begin NewData = PageBuffer [{TheCmd [`PBPtr],PBindex}] % 256 ; Program ( MainArray [{TheCmd [`OpBlock], RowNum}],NewData,`By8L) ; end if (Fail) $display ("Hit block boundry during PB write to Flash Cmd"); end EndOfProgramPB <= `TRUE; EndOfProgramPB <= #10 `FALSE; PBInUse [TheCmd [`PBPtr]] <= `FALSE ; end // DEVICE CONFIGURATION COMMAND // `DeviceConfigCmd : begin RdyBsyConfig <= TheCmd [`CmdData1_lo + 2 : `CmdData1_lo] ; end // UPLOAD DEVICE STATUS COMMAND // `UpDevInfoCmd :begin UploadStatus(NVLockBit) ; PageBuffer[{TheCmd [`PBPtr],`uCodeRevAdd}] = `uCodeRevData; PBInUse [TheCmd [`PBPtr]] <= `FALSE ; end default : ; endcase end // If we just finished executing queue slot 2 then kill it // and restart queue slot 1 if (GoTwoCmd) begin //Still old value since signal Q2Valid <= #1 `FALSE ; AlgTime = PauseTime ; GoTwoCmd <= `FALSE ; end // If algorithm does not run multiple time then when we get // here the algorithm is done. else if (Queue1 [`OpType] != `Erase) begin Q1Valid <= `FALSE ; Suspend <= `FALSE ; end end end end always @( Enqueue or Q1Valid or Q2Valid or Q3Valid) begin // // Operation Queue -- // // Enqueuer if (Enqueue) begin // If cmd is ready for placement in the queue then reset write ptr WriteToPtr = `NewCmd; Enqueue <= `FALSE; CmdValid <= #1 `FALSE ; // Find open slot if (!Q1Valid && !Q1Valid_event) begin Queue1 = Cmd; Q1Valid <= `TRUE; QueueCmd <= `TRUE ; QueueCmd <= #(4*TimerPeriod) `FALSE ; ClearVppFlag <= #1 `TRUE ; ClearVppFlag <= #9 `FALSE ; AlgTime = Cmd [`Time] ; end else if (!Q2Valid && !Q2Valid_event) begin Queue2 = Cmd; Q2Valid <= `TRUE; QueueCmd <= `TRUE ; QueueCmd <= #(4*TimerPeriod) `FALSE ; end else begin Queue3 = Cmd; Q3Valid <= `TRUE; end end // Dequeuer // Check to see if any slots should be moved up if (!Q2Valid && Q3Valid) begin Queue2 = Queue3; Q2Valid <= `TRUE ; Q3Valid <= #1 `FALSE ; end if (!Q1Valid && Q2Valid) begin Queue1 = Queue2; Q1Valid <= `TRUE ; Q2Valid <= #1 `FALSE ; ClearVppFlag <= #1 `TRUE ; ClearVppFlag <= #9 `FALSE ; AlgTime = Queue2 [`Time]; end end // // VccMonitor // always @(Reset or vcc) begin : VccMonitor if (!Reset) begin // Save the array when chip is powered off if (vcc == 0.0 && SaveOnPowerDown) StoreAll(NVLockBit) ; if (CurrOperatingVoltage == `ThreeVolt) begin if (!((vcc > 3.0) && (vcc < 3.6))) $display( "Vcc is out of operating range for 3 volt mode") ; end else if (CurrOperatingVoltage == `FiveVolt) if (!((vcc > 4.5) && (vcc < 5.5))) $display ("Vcc is out of operating range for 5 volt mode") ; end if (vcc > `Vcc5vThres) begin if (CurrOperatingVoltage != `FiveVolt) begin TimingInit (`FiveVolt) ; CurrOperatingVoltage = `FiveVolt; if (!(Reset || StartUpFlag)) $display ("Vcc Level Changed when not in powerdown/reset") ; end end else begin if (CurrOperatingVoltage != `ThreeVolt) begin TimingInit(`ThreeVolt) ; CurrOperatingVoltage = `ThreeVolt; if (!(Reset || StartUpFlag)) $display ("Vcc Level Changed when not in powerdown/reset") ; end end end // // VppMonitor // always @(VppFlag or ClearVppFlag or vpp) begin : VppMonitor if (ClearVppFlag) begin VppErrFlag = `FALSE ; end else if ((vpp > 12.6) || (vpp < 11.4)) begin VppErrFlag = `TRUE ; end VppFlag <= VppErrFlag; end //////////////////////- // Reset Controller // //////////////////////- always @(rpb or vcc) begin : ResetPowerdownMonitor // Go into reset if reset powerdown pin is active or // the vcc is too low if ((rpb != 1'b1) || (vcc < 2.5)) begin // Low Vcc protection Reset <= `TRUE ; if (!((vcc >= 2.5) || StartUpFlag)) $display ("Low Vcc: Chip Reseting") ; end else // Coming out of reset takes time Reset <= #TPHIL `FALSE ; end always @(RdyBsyConfig or StartUpFlag or EndOfProgramPB or EndOfErase or RdyBsy) begin : ReadyBusyMonitor // Generate Ready busy pin responses depending of the current // Rdy bsy configuration // Note output is open drain. (It needs an external pull up of drive 'H'.) // RdyBsyConfig // 2 = Pulse On Write // 3 = Pulse on Erase // 4 = Disable // 5 = Pulse on Erase // Others = Normal Operation if (!StartUpFlag) begin // wait till timing constants assigned #0 if ((RdyBsyConfig == 2) || (RdyBsyConfig == 3) || (RdyBsyConfig == 5)) begin if (((RdyBsyConfig == 2) && EndOfProgramPB ) || ((RdyBsyConfig == 3) && EndOfErase ) || ((RdyBsyConfig == 5) && (EndOfProgramPB || EndOfErase))) begin ry_byb <= 1'b0 ; ry_byb <= #TRLRZ 1'bz ; end else ry_byb <= 1'bz ; end else if (RdyBsyConfig == 4) ry_byb <= 1'bz ; else begin if (RdyBsy == `Bsy) ry_byb <= #TIHRL 1'b0 ; else ry_byb <= #TIHRL 1'bz ; end end end always @(StartUpFlag or Internal_OE) begin : OEMonitor // This section generated DriveOutputs which is the main signal that // controls the state of the output drivers if (!StartUpFlag) begin WriteRecovery = 0 ; last_Internal_WE_time = $time - curr_Internal_WE_time; last_Internal_OE_time = $time - curr_Internal_OE_time; curr_Internal_OE_time = $time; if (!($time < TIHGL) && (last_Internal_WE_time < TIHGL)) WriteRecovery = TIHGL - last_Internal_WE_time ; if (Internal_OE) begin WriteRecovery = WriteRecovery + TGLQX ; DriveOutputs <= #WriteRecovery `TRUE ; end else begin GoingToHighZ <= #TGHQZ `TRUE; // DriveOutputs <= #WriteRecovery `FALSE ; end end else DriveOutputs <= `FALSE ; end always @(GoingToHighZ) begin : HighZmonitor if (GoingToHighZ ) begin last_Internal_OE_time = $time - curr_Internal_OE_time; GoingToHighZ <= `FALSE; if (last_Internal_OE_time >= TGHQZ) DriveOutputs <= `FALSE ; end end /////// Timing Checks ///////////// always @(Internal_WE) begin : Timing_chk reg [`edge_T] edges ; reg e ; if ($time > 0) begin // pulse chk if (Internal_WE) begin if ((($time - LastWE) < TIHIL) && (TIHIL > 0 )) begin $display("[",$time,"] Timing Violation: Internal Write Enable Insufficient High Time") ; end end else if ((($time - LastWE) < TILIH) && (TILIH > 0 )) $display("[",$time,"] Timing Violation: Internal Write Enable Insufficient Low Time") ; LastWE = $time ; // timing_chk - addr last_dq_time = $time - curr_dq_time; last_rpb_time = $time - curr_rpb_time; last_addr_time = $time - curr_addr_time; if (Internal_WE == 0) begin if ((last_addr_time < TAVIH) && (last_addr_time > 0)) $display("[",$time,"] Timing Violation: Address setup time during write, Last Event %d",last_addr_time) ; if ((last_rpb_time < TGHIL) && (last_rpb_time > 0)) $display("[",$time,"] Timing Violation: Writing while coming out of powerdown, Last Event %d",last_rpb_time) ; if ((last_dq_time < TAVIH) && (last_dq_time > 0)) $display("[",$time,"] Timing Violation: Data setup time during write, Last Event %d",last_dq_time) ; end end end always @(addr) begin last_Internal_WE_time = $time - curr_Internal_WE_time; if (($time > 0) && !Internal_WE) //timing chk if ((last_Internal_WE_time < TIHAX) && (last_Internal_WE_time > 0)) $display("[",$time,"] Timing Violation:Address hold time after write, Last Event %d",last_Internal_WE_time) ; end always @(dq) begin curr_dq_time = $time ; last_Internal_WE_time = $time - curr_Internal_WE_time; if (($time > 0) && !Internal_WE) begin if ((last_Internal_WE_time < TIHDX) && (last_Internal_WE_time > 0)) $display("[",$time,"] Timing Violation:Data hold time after write, Last Event %d",last_Internal_WE_time) ; end end endmodule 5 = Pulse on Erase // Others = Norma€Y#wY#B€џџбџџџџџџ?џџ{џџИџџѓџџ/џџNџџRџџџџЅџџЉџџѕџџ?џџ„џџЗџџЛџџкџџк"џџnџџ€џџЭџџџџџFџџџџнџџ џџџџ"џџ&џџ8џџƒџџЂџџьџџџџNџџ‡џџ‹џџ‹Цџџџџ=џџAџџjџџ­џџёџџ3 џџt џџЊ џџЎ џџщ џџ2 џџy џџС џџ џџJ џџ џџв џџу џџу ч џџ! џџa џџr џџv џџР џџ џџO џџš џџт џџ,џџtџџПџџбџџеџџ џџ<џџqџџ‰џџџџШџџџџџ8џџsџџЋџџюџџђџџ?џџŠџџжџџџџdџџІџџЊџџтџџ'џџhџџГџџцџџъџџъ7џџџџСџџХџџџџ[џџІџџОџџТџџ§џџLџџqџџБџџжџџџџLџџMџџRџџhџџџџСџџњџџ,џџ-џџBџџCџџ]џџwџџxџџПџџџџ4џџ5џџXџџšџџтџџџџ'џџJџџmџџmџџГџџДџџзџџљџџњџџџџ=џџ>џџ?џџ`џџ‚џџЅџџЧџџШџџъџџыџџџџ=џџ_џџ_џџЃџџХџџчџџ џџ+џџMџџoџџ‘џџГџџеџџїџџџџ;џџ]џџџџЁџџУџџхџџџџ)џџKџџLџџ˜џџшџџ% џџn џџЛ џџ!џџJ!џџ’!џџд!џџ"џџ_"џџœ"џџк"џџ#џџP#џџr#џџ”#џџ”#•#џџс#џџ1$џџn$џџЗ$џџ%џџN%џџ“%џџл%џџ&џџg&џџЈ&џџх&џџ#'џџ`'џџ™'џџЛ'џџн'џџо'џџя'џџя'(џџ3(џџU(џџw(џџx(џџš(џџМ(џџо(џџ)џџ")џџD)џџE)џџg)џџh)џџŠ)џџЌ)џџЮ)џџ№)џџё)џџ*џџ*5*џџW*џџy*џџz*џџ‹*џџ­*џџЯ*џџа*џџх*џџ+џџ+џџ(+џџH+џџI+џџn+џџЇ+џџн+џџо+џџ,џџ,џџ,N,џџƒ,џџ„,џџ“,џџЕ,џџз,џџи,џџњ,џџ-џџA-џџc-џџd-џџ -џџЁ-џџУ-џџх-џџ.џџ).џџ*.џџL.џџL.n.џџ.џџВ.џџд.џџі.џџ/џџ/џџ/џџ*/џџJ/џџw/џџЄ/џџЮ/џџя/џџ0џџ90џџc0џџ0џџЗ0џџс0џџс0 1џџ51џџ]1џџ†1џџЎ1џџж1џџє1џџ2џџ42џџU2џџu2џџ”2џџГ2џџД2џџд2џџѓ2џџє2џџS3џџw3џџ‘3џџ‘3Љ3џџЧ3џџо3џџі3џџ4џџ%4џџ<4џџT4џџo4џџp4џџˆ4џџ‰4џџœ4џџЧ4џџ5џџ15џџp5џџq5џџЁ5џџЕ5џџЕ5Й5џџц5џџ6џџ6џџ6џџE6џџq6џџ™6џџБ6џџЕ6џџЭ6џџя6џџѓ6џџ7џџ67џџ:7џџa7џџy7џџ}7џџ­7џџ­7Н7џџз7џџё7џџѕ7џџ*8џџY8џџ}8џџ™8џџ8џџО8џџз8џџл8џџ§8џџ9џџ9џџ:9џџS9џџW9џџq9џџu9џџu9Њ9џџО9џџП9џџч9џџ:џџ*:џџ?:џџC:џџn:џџ‰:џџ:џџК:џџЯ:џџг:џџџ:џџ;џџ;џџ=;џџR;џџV;џџV;€;џџ•;џџ™;џџУ;џџи;џџм;џџ<џџ+<џџ/<џџW<џџn<џџr<џџ—<џџЋ<џџЏ<џџр<џџ=џџ=џџ=џџ0=џџ0=F=џџG=џџS=џџd=џџq=џџ~=џџ‰=џџŠ=џџЙ=џџа=џџд=џџџ=џџ>џџ+>џџ,>џџS>џџp>џџt>џџ›>џџЌ>џџЌ>А>џџЭ>џџр>џџс>џџў>џџ?џџ!?џџ>?џџQ?џџR?џџS?џџ‰?џџ ?џџЁ?џџи?џџя?џџ№?џџ%@џџ<@џџ=@џџ=@k@џџš@џџД@џџЕ@џџс@џџAџџAџџ-AџџGAџџHAџџrAџџŽAџџAџџЙAџџбAџџвAџџгAџџъAџџBџџBџџBBџџBџџ4Bџџ5Bџџ\Bџџ]BџџsBџџ†Bџџ‡BџџЏBџџАBџџнBџџђBџџCџџCџџ8CџџmCџџ„Cџџ‡CџџНCџџНCєCџџ-DџџODџџoDџџpDџџЇDџџПDџџиDџџйDџџ Eџџ;EџџXџџJXџџŠXџџШXџџоXџџ"Yџџ#Yџџ3YџџaYџџbYџџYџџ YџџЧYџџЭYџџёYџџZџџJZџџZџџзZџџ[џџ["[џџP[џџ}[џџ‚[џџŠ[џџ‹[џџФ[џџб[џџ \џџD\џџE\џџU\џџw\џџ“\џџА\џџБ\џџч\џџэ\џџ ]џџ%]џџ%]8]џџU]џџm]џџ‰]џџ ]џџД]џџа]џџщ]џџѓ]џџ^џџ6^џџ@^џџ\^џџ‰^џџ“^џџž^џџФ^џџЩ^џџб^џџв^џџв^ _џџ._џџi_џџЄ_џџЅ_џџК_џџп_џџр_џџћ_џџ`џџ`џџa`џџ`џџЈ`џџЦ`џџЭ`џџв`џџк`џџл`џџї`џџї`#aџџ|aџџЈaџџЉaџџлaџџ(bџџQbџџRbџџmbџџnbџџŒbџџПbџџнbџџќbџџcџџ:cџџVcџџXcџџЅcџџкcџџкcмcџџdџџKdџџhdџџdџџИdџџеdџџіdџџeџџGeџџHeџџ]eџџ^eџџteџџДeџџсeџџ"fџџ?fџџ\fџџqfџџqfЩfџџѕfџџMgџџygџџЇgџџУgџџсgџџтgџџ№gџџhџџ8hџџZhџџ~hџџЂhџџЦhџџъhџџiџџ2iџџViџџziџџzižiџџТiџџсiџџjџџ)jџџIjџџmjџџŒjџџЋjџџЪjџџщjџџ kџџ(kџџMkџџqkџџ’kџџЖkџџкkџџ§kџџ$lџџ$lHlџџllџџŽlџџБlџџаlџџѕlџџmџџ3mџџRmџџqmџџ•mџџДmџџгmџџђmџџnџџ0nџџOnџџnnџџ’nџџБnџџБnаnџџєnџџoџџ9oџџ]oџџoџџЅoџџФoџџуoџџpџџ!pџџ@pџџbpџџpџџ pџџПpџџоpџџ§pџџqџџ;qџџ;qZqџџ~qџџЅqџџХqџџфqџџхqџџшqџџљqџџќqџџrџџ.rџџ„џџO„џџ„џџŒ„џџЇ„џџЦ„џџ…џџ……џџF…џџS…џџm…џџŒ…џџМ…џџЭ…џџў…џџ†џџ†џџ#†џџ*†џџ+†џџ.†џџ>†џџA†џџj†џџ†џџ †џџЦ†џџЦ†о†џџ‡џџC‡џџm‡џџ•‡џџН‡џџл‡џџњ‡џџˆџџ3ˆџџXˆџџˆџџтˆџџ*‰џџy‰џџž‰џџВ‰џџз‰џџј‰џџŠџџŠ1ŠџџOŠџџ`ŠџџˆŠџџ•ŠџџЃŠџџЩŠџџыŠџџ ‹џџ8‹џџI‹џџr‹џџ‹џџ“‹џџД‹џџо‹џџя‹џџŒџџ$Œџџ.Œџџ.Œ5Œџџ9Œџџ:ŒџџRŒџџSŒџџ†Œџџ‡ŒџџЮŒџџџџџџKџџdџџoџџŽџџпџџ,ŽџџNŽџџsŽџџ™ŽџџчŽџџчŽџџџџUџџyџџЁџџУџџћџџ%џџPџџ~џџŽџџЗџџпџџ‘џџ7‘џџ`‘џџ‹‘џџИ‘џџЫ‘џџм‘џџм‘’џџi’џџЃ’џџА’џџ “џџ,“џџ†“џџЭ“џџђ“џџ+”џџ]”џџš”џџС”џџ"•џџ9•џџ‚•џџ’•џџЃ•џџл•џџш•џџш•і•џџ§•џџ–џџ–џџ–џџ–џџ–џџJ–џџŽ–џџК–џџЛ–џџа–џџц–џџ—џџ&—џџC—џџX—џџ —џџЂ—џџР—џџР—Т—џџі—џџ˜џџU˜џџs˜џџв˜џџѓ˜џџ™џџ$™џџ.™џџ0™џџb™џџd™џџ›™џџИ™џџж™џџс™џџу™џџšџџšџџšEšџџcšџџšџџŒšџџОšџџРšџџђšџџ›џџ.›џџ9›џџQ›џџІ›џџж›џџљ›џџœџџœџџ7œџџ9œџџˆœџџЎœџџЎœЪœџџіœџџџџџџ%џџ'џџaџџˆџџ­џџШџџєџџžџџžџџžџџžџџ^žџџƒžџџžžџџМžџџсžџџсžŸџџŸџџŸџџ4Ÿџџ6ŸџџpŸџџ•ŸџџАŸџџуŸџџёŸџџєŸџџ џџ џџc џџˆ џџЎ џџЩ џџЁџџ4ЁџџYЁџџYЁgЁџџkЁџџ–Ёџџ˜ЁџџоЁџџЂџџЂџџBЂџџOЂџџQЂџџjЂџџlЂџџŸЂџџФЂџџпЂџџ Ѓџџ2Ѓџџ@ЃџџGЃџџcЃџџcЃeЃџџ ЃџџХЃџџрЃџџЄџџ3ЄџџAЄџџPЄџџoЄџџqЄџџЈЄџџЭЄџџшЄџџЅџџ'Ѕџџ6Ѕџџ^Ѕџџ`ЅџџІЅџџЫЅџџЫЅцЅџџ Іџџ8Іџџ]ІџџjІџџkІџџСІџџоІџџўІџџџІџџЇџџ ЇџџPЇџџtЇџџ˜ЇџџЉЇџџЛЇџџЈџџЈџџ’Јџџ’ЈмЈџџ Љџџ6ЉџџMЉџџwЉџџЛЉџџЊџџ?ЊџџrЊџџšЊџџкЊџџ Ћџџ#ЋџџPЋџџtЋџџЊЋџџгЋџџЌџџ9Ќџџ]Ќџџ]ЌsЌџџ‘ЌџџПЌџџчЌџџ ­џџ!­џџ5­џџF­џџX­џџ’­џџ“­џџџ­џџ#ЎџџTЎџџ~ЎџџЄЎџџлЎџџћЎџџ'ЏџџXЏџџXЏ„ЏџџЎЏџџФЏџџсЏџџАџџ1АџџGАџџZАџџ“АџџВАџџоАџџ Бџџ4БџџQБџџ}БџџЌБџџкБџџВџџUВџџ‚Вџџ‚ВЎВџџХВџџйВџџГџџ3Гџџ]Гџџ‚ГџџЊГџџОГџџЮГџџвГџџшГџџъГџџ ДџџAДџџkДџџ…ДџџЌДџџдДџџ§Дџџ§ДЕџџ>ЕџџbЕџџuЕџџ™ЕџџЊЕџџЎЕџџТЕџџУЕџџїЕџџЖџџJЖџџqЖџџЂЖџџЕЖџџзЖџџЗџџ/Зџџ`ЗџџsЗџџsЗ—ЗџџЫЗџџёЗџџ7Иџџ`ИџџŠИџџЗИџџсИџџњИџџ#ЙџџnЙџџ„Йџџ˜ЙџџЛЙџџоЙџџўЙџџ%КџџGКџџxКџџЂКџџЂКеКџџ§КџџЛџџ=ЛџџMЛџџdЛџџˆЛџџМЛџџЬЛџџйЛџџыЛџџэЛџџМџџМџџМџџ<МџџcМџџМџџлМџџџМџџџМ%НџџIНџџYНџџ{НџџзНџџћНџџ!ОџџGОџџWОџџЂОџџШОџџьОџџПџџ9ПџџMПџџtПџџ…Пџџ“ПџџПџџЋПџџЋПВПџџЖПџџЗПџџКПџџЬПџџЯПџџаПџџђПџџHРџџIРџџМРџџСџџ{СџџšСџџжСџџѕСџџKТџџƒТџџпТџџѕТџџѕТУџџ7УџџaУџџ•УџџКУџџиУџџъУџџ§УџџФџџCФџџPФџџŠФџџЏФџџЯФџџёФџџ+ХџџXХџџ~ХџџЂХџџШХџџШХмХџџщХџџ=Цџџ•ЦџџыЦџџЧџџ<Чџџ|Чџџ ЧџџоЧџџ)ШџџeШџџyШџџЕШџџњШџџ Щџџ!ЩџџVЩџџ–ЩџџЇЩџџЇЩЕЩџџыЩџџ2ЪџџxЪџџЉЪџџЩЪџџщЪџџіЪџџ!ЫџџdЫџџ‡ЫџџЙЫџџЬџџDЬџџjЬџџ’ЬџџПЬџџцЬџџ Эџџ Эџџ ЭEЭџџhЭџџxЭџџЭџџЗЭџџыЭџџЮџџ;ЮџџNЮџџqЮџџ„Юџџ‘ЮџџЧЮџџ§Юџџ#ЯџџHЯџџrЯџџ–ЯџџКЯџџЪЯџџЪЯьЯџџ аџџаџџ$аџџ+аџџ/аџџ0аџџ3аџџGаџџJаџџKаџџbаџџШаџџфаџџ бџџбџџбџџ[бџџtбџџ•бџџ•бŸбџџЃбџџЄбџџНбџџ1вџџMвџџtвџџЛвџџжвџџѓвџџ§вџџ6гџџVгџџ™гџџЛгџџпгџџэгџџїгџџџгџџдџџддџџдџџ"дџџ%дџџ&дџџOдџџЁдџџЖдџџ еџџJеџџœеџџ№еџџ*жџџqжџџжџџМжџџпжџџьжџџ=зџџŒзџџŒзЦзџџиџџnиџџЁиџџЫиџџяиџџйџџ-йџџ=йџџ]йџџjйџџйџџпйџџ-кџџlкџџnкџџžкџџВкџџбкџџђкџџђклџџ'лџџ2лџџ9лџџ=лџџ?лџџNлџџ^лџџmлџџ›лџџёлџџмџџ@мџџŽмџџЭмџџфмџџнџџнџџ,нџџ\нџџ\н‚нџџБнџџюнџџ4оџџtоџџЅоџџбоџџіоџџпџџDпџџpпџџ‰пџџœпџџЖпџџфпџџрџџ&рџџKрџџtрџџ–рџџ–рЌрџџкрџџэрџџсџџ6сџџ`сџџšсџџЊсџџСсџџУсџџзсџџисџџтџџ"тџџтџџЪтџџятџџ6уџџmуџџ„уџџ„унуџџ фџџAфџџfфџџ”фџџМфџџщфџџ§фџџIхџџхџџЈхџџЮхџџќхџџEцџџrцџџ˜цџџКцџџЭцџџчџџ>чџџ>чhчџџ™чџџХчџџичџџђчџџ=шџџPшџџrшџџЪшџџјшџџ$щџџSщџџjщџџБщџџЮщџџњщџџ)ъџџXъџџnъџџ…ъџџ…ъХъџџйъџџщъџџіъџџ ыџџ,ыџџ.ыџџFыџџHыџџiыџџЎыџџтыџџьџџ(ьџџRьџџuьџџŒьџџЄьџџЯьџџэџџэ-эџџAэџџ\эџџЃэџџжэџџюџџFюџџ юџџЛюџџяџџ,яџџhяџџЧяџџмяџџэяџџяяџџ№џџ№џџ6№џџ{№џџ{№Ѕ№џџй№џџљ№џџёџџIёџџlёџџƒёџџЗёџџЫёџџцёџџ-ђџџhђџџмђџџѓђџџѓџџ†ѓџџѓџџљѓџџєџџ єџџ є!єџџ<єџџ>єџџ`єџџ‚єџџ­єџџпєџџўєџџѕџџOѕџџuѕџџˆѕџџЂѕџџдѕџџ іџџJіџџ|іџџіџџŸіџџЁіџџЁіОіџџРіџџњіџџќіџџ&їџџ(їџџNїџџ“їџџНїџџёїџџјџџ7јџџaјџџ„јџџ›јџџЯјџџујџџљџџ:љџџ€љџџ€љЦљџџўљџџAњџџњџџбњџџ-ћџџ_ћџџћџџбћџџьћџџ$ќџџ<ќџџTќџџˆќџџзќџџњќџџZ§џџ§џџЭ§џџ1ўџџ1ў2ўџџcўџџ•ўџџљўџџ-џџџWџџџНџџџђџџџџџ7џџNџџЏџџўџџWџџoџџџџрџџѕџџџџLџџL‚џџ”џџ•џџКџџМџџтџџ-џџ=џџbџџdџџ†џџАџџќџџ2џџCџџ^џџtџџ‚џџПџџнџџнџџCџџhџџŒџџœџџкџџћџџ2џџUџџxџџˆџџ“џџšџџžџџŸџџйџџмџџђџџїџџљџџљ џџ џџgџџƒџџœџџКџџаџџќџџџџ.џџKџџzџџžџџУџџфџџюџџ џџ6 џџQ џџn џџn  џџЇ џџИ џџЯ џџъ џџє џџќ џџў џџ џџ@ џџA џџd џџ{ џџ” џџБ џџИ џџк џџё џџ џџ) џџ) J џџl џџŒ џџ” џџ˜ џџ™ џџœ џџЊ џџ­ џџЎ џџи џџэ џџ џџG џџf џџš џџЦ џџ џџ џџK џџK w џџР џџШ џџЩ џџщ џџџџ>џџiџџџџиџџтџџщџџїџџ+џџMџџyџџŸџџшџџѓџџњџџњўџџџџџџџџџџџOџџjџџ‰џџџџ˜џџЦџџчџџёџџ џџџџџџ(џџ@џџXџџYџџYџџФџџмџџџџ4џџ^џџџџ”џџœџџРџџрџџфџџхџџNџџOџџ‘џџ­џџќџџџџ(џџ(AџџSџџlџџŒџџвџџеџџ)џџbџџ›џџчџџџџ(џџ5џџCџџ`џџjџџŒџџІџџИџџжџџжњџџџџ-џџ7џџFџџKџџLџџƒџџЫџџњџџћџџџџ1џџnџџЋџџаџџџџIџџfџџ—џџ—Чџџбџџтџџ џџ<џџFџџNџџVџџuџџyџџzџџІџџТџџџџџџџHџџjџџqџџuџџvџџv{џџ|џџ џџЁџџЪџџЫџџфџџќџџ§џџџџџџ&џџCџџ‚џџчџџєџџўџџ9џџšџџБџџБВџџЪџџѕџџ"џџQџџRџџuџџДџџ*џџgџџуџџ џџ џџš џџЁ џџЇ џџЈ џџН џџї џџ-!џџ-!w!џџю!џџя!џџѓ!џџє!џџ"џџ!"џџ["џџ…"џџЯ"џџC#џџJ#џџN#џџO#џџY#џџ[#џџЈ џџН џџї џџ-!џџ Arialџџаџџ$аџџ+аџџ/аџџ0аџџ3аџџGаџџJаџџKаџџbаџџШаџџфаџџ бџџбџџбџџ[бџџtбџџ•бџџ