/* Device Driver in generic C language for SST36VF3203 working at x16 WORD Mode Jerry Deng, Silicon Storage Technology, Inc. Revision 1.0, March 2005 This code is for customer's reference ONLY and is provided AS IS, SST MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY WARRANTY OF MERCHANTABILITY OR FITNESS FOR PARTICULAR PURPOSE. IN NO EVENT SHALL SST BE LIABLE FOR ANY INCIDENTAL OR CONSEQUENTIAL DAMAGES WITH RESPECT TO THE USE OF THIS CODE AND/OR SST PRODUCTS. SST36VF3203 datasheet should be reviewed in conjunction with this code to completely understand the operation of this device. SST36VF3203 supports bottom boot block (Block 0) protection, 4 sectors in Block 0 are protected from being erased/programmed when WP# is low and unprotected when WP# is high. It's assumed that Byte# pin is always held stable high(Vih or VDD) during the execution of this code so that the device is working at x16 Word Mode. Otherwise, If Byte# is low(Vil or ground), the device is working at x8 Byte Mode, please reference to another device driver specially designed for SST36VF3203 at x8 Byte Mode. During execution of this code, it's also assumed that RST# signal on device is held in logic high state, otherwise the device will be put into reset state after Try(20us Max) once RST# changes from high to low. Customer may need to change the delay numbers in subroutines Delay_150Nanoseconds( ), Delay_10Microseconds( ) and Delay_20Microseconds( ) according to application system's speed in order to get highest write performance. */ #define Sector_Size 0x800 // 2K words / sector #define Block_Size 0x8000 // 32K words / block #define Chip_Size 0x200000 // 2M words / chip #define AddrsShift 2 // define 2 if it's true that A1 of system address bus has been shifted to A0 of device address, A2 to A1, etc. // otherwise, change it to look like: // #define AddrsShift 1 #define Bank2 0x80000 // Starting Word Address of Bank2 #define DefaultBaseAddress 0x0 // default device base address for SST36VF3203 #define ChipEraseTime 714286 // maximum timeout of read cycles for chip-erase, 50ms/70ns #define BlockEraseTime 357143 // maximum timeout of read cycles for sector-erase, 25ms/70ns #define SectorEraseTime 357143 // maximum timeout of read cycles for sector-erase, 25ms/70ns #define WordProgramTime 143 // maximum timeout of read cycles for WORD-program, 10us/70ns #define TRUE 1 #define FALSE 0 typedef unsigned long U32; typedef unsigned short int WORD; typedef unsigned char BYTE; U32 BaseAddrs=DefaultBaseAddress; // In some cases, system will assign a device base address for flash only at run time. // This is the reason that we define BaseAddrs as a variable rather than a CONSTANT. // The C code in this document contains the following routines in order: // Name Function U32 ReadID_FromBank2(void); // Check manufacturer ID and device ID from Bank2 U32 ReadID_FromBank1(void); // Check manufacturer ID and device ID from Bank1 void QueryCFI_FromBank2(WORD *); // Read CFI information from Bank2 void QueryCFI_FromBank1(WORD *); // Read CFI information from Bank1 void Query_SecID(WORD *, WORD *); // Read both SST SecID and User SecID information BYTE Program_One_Word(WORD, U32); // Program one WORD into device BYTE Erase_One_Sector(U32); // Erase one sector(2KWORD) BYTE Erase_One_Block(U32); // Erase one block (32KWORD) BYTE Erase_Entire_Chip(void); // Erase the whole chip BYTE SecID_Lock_Status(void); // Check User SecID lock status BYTE User_SecID_Word_Program(WORD *, U32, BYTE); // Program n words into User SecID segment BYTE User_SecID_Lockout(void); // Lock out User SecID segment void Erase_Suspend(void); // Temporarily suspend one sector-erase or block-erase BYTE Erase_Resume(U32); // Resume suspended sector-erase or block-erase BYTE Check_ToggleDQ6(U32, U32); // Wait until DQ6 stops toggling BYTE Check_DataPollingDQ7(U32, WORD, U32); // Wait until DQ7 outputs true data void Delay_150Nanoseconds(void); // delay 150ns void Delay_10Microseconds(void); // delay 10us void Delay_20Microseconds(void); // delay 20us /************************************************************************/ /* PROCEDURE: ReadID_FromBank1 */ /* */ /* This procedure reads ID from Bank1 of SST36VF3203. */ /* */ /* Input: None */ /* */ /* Output: 00BF7354h for SST36VF3203. */ /* Any other value indicates non SST36VF3203. */ /* High word is vendor ID, low word is device ID. */ /************************************************************************/ U32 ReadID_FromBank1(void) { U32 ReturnStatus=0; // Issue Software ID Entry command to SST36VF3203 *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0xAA; // 1st write data 0xAA to device addr 555H for A10 to A0 *(WORD *) (BaseAddrs + 0x2AA*AddrsShift) = 0x55; // 2nd write data 0x55 to device addr 2AAH for A10 to A0 *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0x90; // 3rd write data 0x90 to device addr 555H for A10 to A0 Delay_150Nanoseconds(); // delay Tida(max. 150ns) for SST36VF3203 ReturnStatus = *(WORD *) (BaseAddrs + 0*AddrsShift); ReturnStatus = (ReturnStatus << 16) | *(WORD *) (BaseAddrs + 1*AddrsShift); // Before exit Software ID mode, all subsequent read with address within Bank2 will return data in Bank2 // array, rather than IDs. // For example, next read will return data at address 80000h and 80001h, not vendor ID. // WORD ReadBank2; // ReadBank2 = *(WORD *) (BaseAddrs + (0 + Bank2)*AddrsShift); // read data at address 80000h in Bank2 // ReadBank2 = *(WORD *) (BaseAddrs + (1 + Bank2)*AddrsShift); // read data at address 80001h in Bank2 // Issue Software ID Exit command to put SST36VF3203 into normal read mode. // Two Exit commands ( 3 cycles Exit and shorter 1 cycle Exit command) are equivalent. *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0xAA; // 1st write data 0xAA to device addr 555H for A10 to A0 *(WORD *) (BaseAddrs + 0x2AA*AddrsShift) = 0x55; // 2nd write data 0x55 to device addr 2AAH for A10 to A0 *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0xF0; // 3rd write data 0xF0 to device addr 555H for A10 to A0 Delay_150Nanoseconds(); // then delay Tida(max. 150ns) for SST36VF3203 return ReturnStatus; } /************************************************************************/ /* PROCEDURE: ReadID_FromBank2 */ /* */ /* This procedure reads ID from Bank2 of SST36VF3203. */ /* */ /* Input: None */ /* */ /* Output: 00BF7354h for SST36VF3203. */ /* Any other value indicates non SST36VF3203. */ /* High word is vendor ID, low word is device ID. */ /************************************************************************/ U32 ReadID_FromBank2(void) { U32 ReturnStatus=0; // Issue Software ID Entry command to SST36VF3203 *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0xAA; // 1st write data 0xAA to device addr 555H for A10 to A0 *(WORD *) (BaseAddrs + 0x2AA*AddrsShift) = 0x55; // 2nd write data 0x55 to device addr 2AAH for A10 to A0 *(WORD *) (BaseAddrs + (0x555 + Bank2)*AddrsShift) = 0x90; // 3rd write data 0x90 to device addr 555H for A10 to A0 Delay_150Nanoseconds(); // delay Tida (max. 150ns) for SST36VF3203 ReturnStatus = *(WORD *) (BaseAddrs + (0 + Bank2)*AddrsShift); ReturnStatus = (ReturnStatus << 16) | *(WORD *) (BaseAddrs + (1 + Bank2)*AddrsShift); // Before exit Software ID mode, all subsequent read with address within Bank1 will return data in Bank1 // array, rather than IDs. // For example, next read will return data at address 000000h and 000001h, not vendor ID. // WORD ReadBank1; // ReadBank1 = *(WORD *) (BaseAddrs + 0*AddrsShift); // read data at address 000000h in Bank1 // ReadBank1 = *(WORD *) (BaseAddrs + 1*AddrsShift); // read data at address 000001h in Bank1 // Issue Software ID Exit command to put SST36VF3203 into normal read mode. // *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0xAA; // 1st write data 0xAA to device addr 555H for A10 to A0 // *(WORD *) (BaseAddrs + 0x2AA*AddrsShift) = 0x55; // 2nd write data 0x55 to device addr 2AAH for A10 to A0 *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0xF0; // 3rd write data 0xF0 to device addr 555H for A10 to A0 Delay_150Nanoseconds(); // then delay Tida (max. 150ns) for SST36VF3203 return ReturnStatus; } /************************************************************************/ /* PROCEDURE: QueryCFI_FromBank1 */ /* */ /* This procedure queries CFI information from Bank1. */ /* */ /* Input: *CFI A pointer to store CFI data string (total 37 words). */ /* */ /* Output: The pointer points to CFI data string. */ /************************************************************************/ void QueryCFI_FromBank1(WORD *CFI) { // Issue the CFI Query Entry command to SST36VF3203 // 3 Cycles CFI Query Entry is equivalent to 1 Cycle CFI Query Entry *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0xAA; // 1st write data 0xAA to device addr 555H for A10 to A0 *(WORD *) (BaseAddrs + 0x2AA*AddrsShift) = 0x55; // 2nd write data 0x55 to device addr 2AAH for A10 to A0 *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0x98; // 3rd write data 0x98 to device addr 555H for A10 to A0 Delay_150Nanoseconds( ); // delay Tida (150ns) // retrieve all CFI data string from word address between 0010H and 0034H. for (BYTE i=0x10; i<=0x34; CFI++, i++) *CFI = *(WORD *) (BaseAddrs + i*AddrsShift); // save CFI data into user-defined pointer. // Before exit CFI Query mode, all subsequent read with address within Bank2 will return // data in Bank2 array, rather than CFI string. // For example, next read will return data at address 80020h and 80022h, not CFI string. // WORD ReadBank2; // ReadBank2 = *(WORD *) (BaseAddrs + (0x20 + Bank2)*AddrsShift); // read data at address 80020h in Bank2 array // ReadBank2 = *(WORD *) (BaseAddrs + (0x22 + Bank2)*AddrsShift); // read data at address 80022h in Bank2 array // Issue CFI Exit command to return SST36VF3203 to read mode. // *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0xAA; // 1st write data 0xAA to device addr 555H for A10 to A0 // *(WORD *) (BaseAddrs + 0x2AA*AddrsShift) = 0x55; // 2nd write data 0x55 to device addr 2AAH for A10 to A0 *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0xF0; // 3rd write data 0xF0 to device addr 555H for A10 to A0 Delay_150Nanoseconds( ); // insert delay time Tida of 150ns } /************************************************************************/ /* PROCEDURE: QueryCFI_FromBank2 */ /* */ /* This procedure queries CFI information from Bank2. */ /* */ /* Input: *CFI A pointer to store CFI data string (total 37 words). */ /* */ /* Output: The pointer points to CFI data string. */ /************************************************************************/ void QueryCFI_FromBank2(WORD *CFI) { // Issue the CFI Query Entry command to SST36VF3203 // 3 Cycles CFI Query Entry is equivalent to 1 Cycle CFI Query Entry *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0xAA; // 1st write data 0xAA to device addr 555H for A10 to A0 *(WORD *) (BaseAddrs + 0x2AA*AddrsShift) = 0x55; // 2nd write data 0x55 to device addr 2AAH for A10 to A0 *(WORD *) (BaseAddrs + (Bank2 + 0x555)*AddrsShift) = 0x98; // 3rd write data 0x98 to device addr 555H for A10 to A0 Delay_150Nanoseconds( ); // delay Tida (150ns) // retrieve all CFI data string from word address between 180010H and 180034H. for (BYTE i=0x10; i<=0x34; CFI++, i++) *CFI = *(WORD *) (BaseAddrs + (i + Bank2)*AddrsShift); // save CFI data into user-defined pointer. // Before exit CFI Query mode, all subsequent read with address within Bank1 will return // data in Bank1 array, rather than CFI string. // For example, next read will return data at address 000020h and 000022h, not CFI string. // WORD ReadBank1; // ReadBank1 = *(WORD *) (BaseAddrs + 0x20*AddrsShift); // read data at address 000020h in Bank1 array // ReadBank1 = *(WORD *) (BaseAddrs + 0x22*AddrsShift); // read data at address 000022h in Bank1 array // Issue CFI Exit command to return SST36VF3203 to read mode. *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0xAA; // 1st write data 0xAA to device addr 555H for A10 to A0 *(WORD *) (BaseAddrs + 0x2AA*AddrsShift) = 0x55; // 2nd write data 0x55 to device addr 2AAH for A10 to A0 *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0xF0; // 3rd write data 0xF0 to device addr 555H for A10 to A0 Delay_150Nanoseconds( ); // insert delay time Tida of 150ns } /************************************************************************/ /* PROCEDURE: Query_SecID */ /* */ /* This procedure queries both SST Security ID and User Security ID. */ /* */ /* Input: *SST_SecID pointer to store SST SecID string (8 words). */ /* *User_SecID pointer to store User SecID string(128 words). */ /* */ /* Output: pointer SST_SecID points to SST security ID */ /* pointer User_SecID points to User security ID */ /************************************************************************/ void Query_SecID(WORD *SST_SecID, WORD *User_SecID) { // Issue Query SecID command to SST36VF3203 *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0xAA; // 1st write data 0xAA to device addr 555H for A10 to A0 *(WORD *) (BaseAddrs + 0x2AA*AddrsShift) = 0x55; // 2nd write data 0x55 to device addr 2AAH for A10 to A0 *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0x88; // 3rd write data 0x88 to device addr 555H for A10 to A0 Delay_150Nanoseconds( ); // insert delay time Tida of 150ns // read all Security ID information: // SST Security ID is from address 000000H to 000007H, // User Security ID is from address 000008H to 000087H. for (U32 i=0x100000; i<=0x100007; i++) { *SST_SecID = *(WORD *) (BaseAddrs + i*AddrsShift); // read & save SST Security ID ++SST_SecID; // point to next WORD } for (U32 i=0x100008; i<=0x100087; i++) { *User_SecID = *(WORD *) (BaseAddrs + i*AddrsShift); // read & save User Security ID ++User_SecID; // point to next WORD } // Issue Sec ID Exit command to return SST36VF3203 to read mode *(WORD *) (BaseAddrs + 0x12345*AddrsShift) = 0xF0; // writing F0h into any address such as 12345h Delay_150Nanoseconds( ); // insert delay time Tida of 150ns } /************************************************************************/ /* PROCEDURE: Program_One_Word */ /* */ /* This procedure programs one WORD of data into SST36VF3203. */ /* */ /* NOTE: Prior to being programmed, the WORD must be blank(FFFFh) state.*/ /* */ /* Input: SrcWord Data WORD to be written into SST36VF3203. */ /* Dst DeSTination address where to be written into. */ /* */ /* Output: TRUE if success, otherwise FALSE if timeout. */ /************************************************************************/ BYTE Program_One_Word(WORD SrcWord, U32 Dst) { BYTE ReturnStatus=TRUE; *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0xAA; // 1st write data 0xAA to device addr 555H for A10 to A0 *(WORD *) (BaseAddrs + 0x2AA*AddrsShift) = 0x55; // 2nd write data 0x55 to device addr 2AAH for A10 to A0 *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0xA0; // 3rd write data 0xA0 to device addr 555H for A10 to A0 *(WORD *) (BaseAddrs + Dst*AddrsShift) = SrcWord; // 4th write data WORD into destination address Dst // Then there are four means to determine when internal operation will be ready: // First check DQ6, Secondly check DQ7, delay 10us or check Rdy/Busy# pin(1 ready / 0 busy) ReturnStatus = Check_ToggleDQ6(Dst, WordProgramTime); // wait TOGGLE bit stops toggling // ReturnStatus = Check_DataPollingDQ7(Dst, SrcWord, WordProgramTime); // wait until DQ7 outputs true data return ReturnStatus; } /************************************************************************/ /* PROCEDURE: Erase_One_Sector */ /* */ /* This procedure erases a sector of 2KWORD. */ /* */ /* Input: Dst DESTINATION address of sector to be erased. */ /* */ /* Output: TRUE if success, otherwise FALSE if timeout. */ /************************************************************************/ BYTE Erase_One_Sector(U32 Dst) { BYTE ReturnStatus=TRUE; *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0xAA; // 1st write data 0xAA to device addr 555H for A10 to A0 *(WORD *) (BaseAddrs + 0x2AA*AddrsShift) = 0x55; // 2nd write data 0x55 to device addr 2AAH for A10 to A0 *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0x80; // 3rd write data 0x80 to device addr 555H for A10 to A0 *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0xAA; // 4th write data 0xAA to device addr 555H for A10 to A0 *(WORD *) (BaseAddrs + 0x2AA*AddrsShift) = 0x55; // 5th write data 0x55 to device addr 2AAH for A10 to A0 *(WORD *) (BaseAddrs + Dst*AddrsShift) = 0x50; // 6th write data 0x50 to device sector addr Dst // Then there are four means to determine when internal operation will be ready: // First check DQ6, Secondly check DQ7, delay 25ms or check Rdy/Busy# pin(1 ready / 0 busy) // ReturnStatus = Check_ToggleDQ6(Dst, SectorEraseTime); // wait TOGGLE bit stops toggling ReturnStatus = Check_DataPollingDQ7(Dst, 0xFFFF, SectorEraseTime); // wait until DQ7 outputs 1 return ReturnStatus; } /************************************************************************/ /* PROCEDURE: Erase_One_Block */ /* */ /* This procedure erases a block of 32KWORD. */ /* */ /* Input: Dst DESTINATION address of block to be erased. */ /* */ /* Output: TRUE if success, otherwise FALSE if timeout. */ /************************************************************************/ BYTE Erase_One_Block (U32 Dst) { BYTE ReturnStatus=TRUE; *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0xAA; // 1st write data 0xAA to device addr 555H for A10 to A0 *(WORD *) (BaseAddrs + 0x2AA*AddrsShift) = 0x55; // 2nd write data 0x55 to device addr 2AAH for A10 to A0 *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0x80; // 3rd write data 0x80 to device addr 555H for A10 to A0 *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0xAA; // 4th write data 0xAA to device addr 555H for A10 to A0 *(WORD *) (BaseAddrs + 0x2AA*AddrsShift) = 0x55; // 5th write data 0x55 to device addr 2AAH for A10 to A0 *(WORD *) (BaseAddrs + Dst*AddrsShift) = 0x30; // 6th write data 0x30 to device block addr Dst // Then there are four means to determine when internal operation will be ready: // First check DQ6, Secondly check DQ7, delay 25ms or check Rdy/Busy# pin(1 ready / 0 busy) ReturnStatus = Check_ToggleDQ6(Dst, BlockEraseTime); // wait TOGGLE bit stops toggling // ReturnStatus = Check_DataPollingDQ7(Dst, 0xFFFF, BlockEraseTime); // wait until DQ7 outputs 1 return ReturnStatus; } /************************************************************************/ /* PROCEDURE: Erase_Entire_Chip */ /* */ /* This procedure erases the entire chip. */ /* */ /* Input: NONE. */ /* */ /* Output: TRUE if success, otherwise FALSE if timeout. */ /************************************************************************/ BYTE Erase_Entire_Chip(void) { BYTE ReturnStatus=TRUE; *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0xAA; // write data 0xAA to device addr 555H for A10 to A0 *(WORD *) (BaseAddrs + 0x2AA*AddrsShift) = 0x55; // write data 0x55 to device addr 2AAH for A10 to A0 *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0x80; // write data 0x80 to device addr 555H for A10 to A0 *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0xAA; // write data 0xAA to device addr 555H for A10 to A0 *(WORD *) (BaseAddrs + 0x2AA*AddrsShift) = 0x55; // write data 0x55 to device addr 2AAH for A10 to A0 *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0x10; // write data 0x10 to device addr 555H for A10 to A0 // Then there are four means to determine when internal operation will be ready: // First check DQ6, Secondly check DQ7, delay 50ms or check Rdy/Busy# pin(1 ready / 0 busy) ReturnStatus = Check_ToggleDQ6(0, ChipEraseTime); // wait TOGGLE bit stops toggling // ReturnStatus = Check_DataPollingDQ7(0, 0xFFFF, ChipEraseTime); // wait until DQ7 outputs 1 return ReturnStatus; } /************************************************************************/ /* PROCEDURE: SecID_Lock_Status */ /* */ /* This procedure checks the lock status of User SecID. */ /* */ /* Input: NONE. */ /* */ /* Output: TRUE if Locked, FALSE if not locked. */ /************************************************************************/ BYTE SecID_Lock_Status(void) { WORD LockStatus; // Issue the Sec ID Entry code to 36VF3203 *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0xAA; // 1st write data 0xAA to device addr 555H for A10 to A0 *(WORD *) (BaseAddrs + 0x2AA*AddrsShift) = 0x55; // 2nd write data 0x55 to device addr 2AAH for A10 to A0 *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0x88; // 3rd write data 0x88 to device addr 555H for A10 to A0 Delay_150Nanoseconds(); // insert delay time Tida of 150ns // Read Lock Status of SecID segment LockStatus = *(WORD *) (BaseAddrs + 0x1000FF*AddrsShift); // Lock Status address at 0x1000FF LockStatus &= 0x08; // get data bit DQ3 only // Issue Sec ID Exit code for SST36VF3203 to return to read mode. // *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0xAA; // 1st write data 0xAA to device addr 555H for A10 to A0 // *(WORD *) (BaseAddrs + 0x2AA*AddrsShift) = 0x55; // 2nd write data 0x55 to device addr 2AAH for A10 to A0 *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0xF0; // 3rd write data 0xF0 to device addr 555H for A10 to A0 Delay_150Nanoseconds(); // insert delay time Tida of 150ns if (LockStatus==0) return TRUE; // User SecID segment is Locked. else return FALSE; // User SecID segment is NOT locked. } /************************************************************************/ /* PROCEDURE: User_SecID_Word_Program */ /* */ /* This procedure programs n words of data into the User SecID */ /* segment (from 000008H--000087H) in SST36VF3203. */ /* */ /* NOTE: */ /* 1. Before run this subroutine, call above SecID_Lock_Status( )to */ /* make sure User SecID segment is NOT locked. */ /* 2. Call Query_SecID(WORD *SST_SecID, WORD *User_SecID) to read back */ /* all programmed data to verify if they match original data. */ /* 3. It's recommended to lock out User SecID after programming. */ /* 4. There's no way to unlock User SecID once it's locked. */ /* */ /* Input: *SrcWord Pointer to source data */ /* Dst Valid address in User SecID from 000008h to 000087h.*/ /* length Number of words needs to be programmed. */ /* */ /* Output: TRUE for successful programming. */ /* FALSE for timeout error or invalid parameter Dst or length. */ /************************************************************************/ BYTE User_SecID_Word_Program(WORD *SrcWord, U32 Dst, BYTE length) { BYTE ReturnStatus=TRUE; if (Dst<0x100008 || Dst>0x100087) return FALSE; // wrong parameter Dst if (length>128 || (Dst+length)>0x100087) return FALSE; // wrong parameter length, too big while (length--) { *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0xAA; // 1st write data 0xAA to device addr 555H for A10 to A0 *(WORD *) (BaseAddrs + 0x2AA*AddrsShift) = 0x55; // 2nd write data 0x55 to device addr 2AAH for A10 to A0 *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0xA5; // 3rd write data 0xA5 to device addr 555H for A10 to A0 *(WORD *) (BaseAddrs + Dst*AddrsShift) = *SrcWord; // 4th write data WORD *SrcWord to destination Dst ++Dst; // point to next address ++SrcWord; // point to next data // Three options: (1)Read the toggle bit to detect completion of flash operation, // (2)delay 10us after this command, (3)check Rdy/Busy# pin(1 ready / 0 busy). // DO Not use Data# Polling DQ7 for User_SecID_Word_Program!! (Refer to datasheet.) ReturnStatus = Check_ToggleDQ6(Dst, WordProgramTime); // wait for TOGGLE bit to get ready if (!ReturnStatus) return FALSE; // User_SecID_Word_Program failed! Return false immediately. } return TRUE; // All data are successfully programmed. } /************************************************************************/ /* PROCEDURE: User_SecID_Lockout */ /* */ /* This procedure locks out User Security ID. */ /* */ /* NOTE: */ /* 1. Data in User SecID segment can't be erased, They are OTP. */ /* 2. User SecID segment can't be unlocked once it's locked. */ /* */ /* Input: None */ /* */ /* Output: TRUE if successfully locked after programming. */ /* FALSE if timeout error or failed to lock User SecID. */ /************************************************************************/ BYTE User_SecID_Lockout(void) { BYTE ReturnStatus=TRUE; if(SecID_Lock_Status( )) // double check if it's already locked. return TRUE; // return TRUE if locked. *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0xAA; // 1st write data 0xAA to device addr 555H for A10 to A0 *(WORD *) (BaseAddrs + 0x2AA*AddrsShift) = 0x55; // 2nd write data 0x55 to device addr 2AAH for A10 to A0 *(WORD *) (BaseAddrs + 0x555*AddrsShift) = 0x85; // 3rd write data 0x85 to device addr 555H for A10 to A0 *(WORD *) (BaseAddrs + 0x123*AddrsShift) = 0x00; // 4th write data 0x00 to any address such as 0x123 // Three options: (1)Read the toggle bit to detect completion of flash operation, // (2)delay 10us after this command, (3)check Rdy/Busy# pin(1 ready / 0 busy). // DO Not use Data# Polling DQ7 for User_SecID_Lockout!! (Refer to datasheet.) ReturnStatus = Check_ToggleDQ6(0, WordProgramTime); // wait for TOGGLE bit to get ready if(ReturnStatus && SecID_Lock_Status( )) // double check if it's really locked. return TRUE; // return TRUE if locked. else return FALSE; // otherwise, return FALSE. } /************************************************************************/ /* PROCEDURE: Erase_Suspend */ /* */ /* This procedure temporarily suspends a Sector/Block-Erase operation. */ /* */ /* Input: None */ /* */ /* Output: None */ /************************************************************************/ void Erase_Suspend(void) { *(WORD *) (BaseAddrs + 0x5678*AddrsShift) = 0xB0; // write data 0xB0 into any address, e.g.5678H Delay_20Microseconds(); // The device will be ready to read after 20us. } /************************************************************************/ /* PROCEDURE: Erase_Resume */ /* */ /* This procedure resumes the suspended Sector-Erase or Block-Erase. */ /* */ /* Input: Dst Any address within suspended Sector/Block-Erase. */ /* */ /* Output: TRUE if success, otherwise FALSE if timeout. */ /************************************************************************/ BYTE Erase_Resume(U32 Dst) { BYTE ReturnStatus=TRUE; *(WORD *) (BaseAddrs + 0x89ABC*AddrsShift) = 0x30; // write data 0x30 to any address, e.g.89ABCH // Then there are four means to determine when internal operation will be ready: // First check DQ6, Secondly check DQ7, delay 25ms or check Rdy/Busy# pin(1 ready / 0 busy) ReturnStatus=Check_ToggleDQ6(Dst, BlockEraseTime); // because BlockEraseTime=SectorEraseTime. // ReturnStatus = Check_DataPollingDQ7(Dst, 0xFFFF, BlockEraseTime); // wait until DQ7 outputs 1 return ReturnStatus; // Don't return to caller until erase is completed! } /************************************************************************/ /* PROCEDURE: Check_ToggleDQ6 */ /* */ /* During the internal erase/program, any consecutive read operation */ /* on DQ6 will produce alternating 0's and 1's, i.e. toggling between */ /* 0 and 1. When the operation is completed, DQ6 will stop toggling. */ /* */ /* Input:Dst Any valid device address of erase/programming. */ /* MaxCycles Maximum cycles allowed for this erase or programming.*/ /* */ /* Output: TRUE if success, otherwise FALSE if timeout. */ /************************************************************************/ BYTE Check_ToggleDQ6(U32 Dst, U32 MaxCycles) { WORD CurrData, PreData; U32 TimeOut = 0; PreData = *(WORD *) (BaseAddrs + Dst*AddrsShift); // read data PreData = PreData & 0x40; // get DQ6 while (TimeOut < MaxCycles) // check if time-out { CurrData = *(WORD *) (BaseAddrs + Dst*AddrsShift); // read again CurrData = CurrData & 0x40; // retrieve bit DQ6 if (PreData == CurrData) { CurrData = *(WORD *) (BaseAddrs + Dst*AddrsShift); // To make sure it's true that DQ6 stops toggling, CurrData = CurrData & 0x40; // read one more time before exit. (Refer to datasheet) if (PreData == CurrData) return TRUE; // return true if DQ6 stops toggling. } PreData = CurrData; // PreData keeps current DQ6 status. TimeOut++; // otherwise, continue to check DQ6 toggling. } return FALSE; // timeout error } /************************************************************************/ /* PROCEDURE: Check_DataPollingDQ7 */ /* */ /* During internal program, any attempt to read DQ7 of the last */ /* word data loaded will receive the complement of the true data. */ /* Once the program cycle is completed, DQ7 will show true data. */ /* */ /* For erase, DQ7 will output 0 during busy of internal operation. */ /* DQ7 will ouput 1 once the internal erase operation is completed. */ /* */ /* Input:Dst Any valid device address of erase/programming. */ /* TrueData This is the original (true) data. */ /* MaxCycles Maximum cycles allowed for this erase/programming. */ /* */ /* Output: TRUE if success, otherwise FALSE if timeout. */ /************************************************************************/ BYTE Check_DataPollingDQ7(U32 Dst, WORD TrueData, U32 MaxCycles) { WORD CurrData; U32 Timeout = 0; TrueData = TrueData & 0x80; // keep DQ7 only while (Timeout < MaxCycles) // compare if timeout { CurrData = *(WORD *) (BaseAddrs + Dst*AddrsShift); // read data word. CurrData = CurrData & 0x80; // get bit DQ7 if (CurrData == TrueData) return TRUE; // return TRUE if DQ7 outputs true data. Timeout++; } return FALSE; // otherwise, return FALSE if timeout error. } void Delay_150Nanoseconds(void) // delay 150ns. {for(int i=0; i<10; i++); // change the number 10 according to your system speed. } void Delay_10Microseconds(void) // delay 10us {for(int i=0; i<0x2500; i++); // change number 0x2500 according to your system speed. } void Delay_20Microseconds(void) // delay 20us. {for(int i=0; i<0x5000; i++); // change number 0x5000 according to your system speed. }