//Software Driver //SST39VF1681/SST39VF1682 //16 Mbit Multi-Purpose Flash Plus(MPF+) // //Sept. 2005 // //ABOUT THE SOFTWARE // //This application note provides a software driver example for 39VF1681/39VF1682 //16 Mbit Multi-Purpose Flash Plus(MPF+) that can be used in any microprocessor based //system. // //The SST39VF1681 supports bottom boot block protection, and the SST39VF1682 //supports top boot block protection. The boot block memory area is protected when //WP# is low and unprotected when WP# is high. // //Software driver example routines provided in this document utilize high-level //"C" programming language for broad platform support. In many cases, software //driver routines can be inserted "as is" into the main body of code being //developed by the system software developers. Extensive comments are included //in each routine to describe the function of each routine. The software driver //routines in "C" can be used with many microprocessors and microcontrollers. // //ABOUT THE SST39VF1681/SST39VF1682 // //Companion product datasheet for 39VF1681/39VF1682 should be reviewed in //conjunction with this application note for a complete understanding of the device. // //The C code in this document contains the following routines, which are listed //in this order: // //Name Function //------------------------------------------------------------------ //Check_SST_39VF168X Check manufacturer and device ID //CFI_Query CFI Query Entry/Exit command sequence //SecID_Query SecID Query Entry/Exit command sequence //Erase_One_Sector Erase a sector of 4096 bytes //Erase_One_Block Erase a block of 64K bytes //Erase_Entire_Chip Erase the contents of the entire chip //Program_One_Word Alter data in one word //Program_One_Sector Alter data in 4096 bytes sector //Program_One_Block Alter data in 64K bytes block //SecID_Lock_Status Check the Lock Status of Security ID segment //User_SecID_Word_Program Write data into User Security ID Segment //User_SecID_Lock_Out Lock out the User Security ID Segment //Erase_Suspend Suspend Sector/Block Erase operation //Erase_Resume Resume Sector/Block Erase operation //Check_Toggle_Ready End of internal program or erase detection using // Toggle bit //Check_Data_Polling End of internal program or erase detection using // Data# polling // //"C" LANGUAGE DRIVERS /***********************************************************************/ /* Copyright Silicon Storage Technology, Inc. (SST), 1994-2005 */ /* Example "C" language Driver of 39VF168X 16 Mbit MPF+ Device */ /* Verified by Yonglin, Silicon Storage Technology, Inc. */ /* */ /* Revision 1.0, Sept 20, 2005 */ /* */ /* This file requires these external "timing" routines: */ /* */ /* 1.) Delay_10_Micro_Seconds */ /* 2.) Delay_20_Micro_Seconds */ /* 3.) Delay_150_Nano_Seconds */ /* 4.) Delay_25_Milli_Seconds */ /* 5.) Delay_50_Milli_Seconds */ /***********************************************************************/ #define FALSE 0 #define TRUE 1 #define SECTOR_SIZE 4096 // Must be 4096 words for 39VF168X #define BLOCK_SIZE 65536 // Must be 64K words for 39VF168X #define SST_ID 0x00BF // SST Manufacturer's ID code #define SST_39VF1681 0xC8 // SST39VF1681 device code #define SST_39VF1682 0xC9 // SST39VF1682 device code typedef unsigned char BYTE; // BYTE is 8-bit in length //typedef unsigned int WORD; // WORD is 16-bit in length typedef unsigned short WORD; // WORD is 16-bit in length, ARM //typedef unsigned long int Uint32; // Uint32 is 32-bit in length typedef unsigned int Uint32; // Uint32 is 32-bit in length, ARM #define system_base 0x6000000 // This sample code uses 0x6000000 as the system_base address in ARM system. // The user should modify this address accordingly. #define sysAddress(offset) ((volatile BYTE *)(system_base + offset)) #define MAX_TIMEOUT 0x07FFFFFF // A ceiling constant used by Check_Toggle_ // Ready() and Check_Data_Polling(). // The user should modify this constant accordingly. // -------------------------------------------------------------------- // EXTERNAL ROUTINES // -------------------------------------------------------------------- extern void Delay_10_Micro_Seconds(); extern void Delay_20_Micro_Seconds(); extern void Delay_150_Nano_Seconds(); extern void Delay_25_Milli_Seconds(); extern void Delay_50_Milli_Seconds(); // -------------------------------------------------------------------- int Check_SST_39VF168X(void); void CFI_Query(BYTE*); void SecID_Query(BYTE*, BYTE*); int Erase_One_Sector(Uint32); int Erase_One_Block (Uint32); int Erase_Entire_Chip(void); int Program_One_BYTE (BYTE*, Uint32); int Program_One_Sector (BYTE*, Uint32); int Program_One_Block (BYTE *, Uint32); int SecID_Lock_Status(void); int User_SecID_BYTE_Program (BYTE*, BYTE*, int); void User_SecID_Lock_Out (void); void Erase_Suspend (void); void Erase_Resume (void); int Check_Toggle_Ready (Uint32); int Check_Data_Polling (Uint32, BYTE); /************************************************************************/ /* PROCEDURE: Check_SST_39VF168X */ /* */ /* This procedure decides whether a physical hardware device has a */ /* SST39VF168X 16 Mbit MPF+ Device installed or not. */ /* */ /* Input: */ /* None */ /* */ /* Output: */ /* return TRUE: indicates a SST39VF168X */ /* return FALSE: indicates not a SST39VF168X */ /************************************************************************/ int Check_SST_39VF168X(void) { BYTE SST_id1; BYTE SST_id2; int ReturnStatus; // Issue the Software Product ID code to 39VF168X *sysAddress(0xAAA) = 0xAA; // write data 0xAA to device addr 0xAAA *sysAddress(0x555) = 0x55; // write data 0x55 to device addr 0x555 *sysAddress(0xAAA) = 0x90; // write data 0x90 to device addr 0xAAA Delay_150_Nano_Seconds(); // Tida Max 150ns for 39VF168XB // Read the product ID from 39VF168X SST_id1 = *sysAddress(0x0000); // get first ID byte SST_id2 = *sysAddress(0x0001); // get second ID byte // ------------------------------------------------------------ // Determine whether there is a SST 39VF1681 installed or not // use the following code: if ((SST_id1 == SST_ID) && (SST_id2 == SST_39VF1681)) ReturnStatus = TRUE; else ReturnStatus = FALSE; // ------------------------------------------------------------ // Or determine whether there is a SST 39VF1682 installed or not // use the following code: //if ((SST_id1 == SST_ID) && (SST_id2 == SST_39VF1682)) // ReturnStatus = TRUE; //else // ReturnStatus = FALSE; // ------------------------------------------------------------ // Issue the Software Product ID Exit code, thus returning the // 39VF168X to the normal operation. *sysAddress(0xAAA) = 0xAA; // write data 0xAA to device addr 0xAAA *sysAddress(0x555) = 0x55; // write data 0x55 to device addr 0x555 *sysAddress(0xAAA) = 0xF0; // write data 0xF0 to device addr 0xAAA Delay_150_Nano_Seconds(); // Tida Max 150ns for 39VF168X return (ReturnStatus); } /************************************************************************/ /* PROCEDURE: CFI_Query */ /* */ /* This procedure should be used to query for CFI information */ /* */ /* Input: */ /* Src Source address to store CFI_Query data string */ /* */ /* Output: */ /* None */ /************************************************************************/ void CFI_Query(BYTE *Src) { BYTE index; // Issue the CFI Query entry code to 39VF168X *sysAddress(0xAAA) = 0xAA; // write data 0xAA to device addr 0xAAA *sysAddress(0x555) = 0x55; // write data 0x55 to device addr 0x555 *sysAddress(0xAAA) = 0x98; // write data 0x98 to device addr 0xAAA Delay_150_Nano_Seconds(); // insert delay time = Tida // ---------------------------------------------------------- // Perform all CFI operations here: // CFI_Query_address is from 10H--34H for ( index = 0x10; index <= 0x34; index++) { *Src = *sysAddress(index); ++Src; // CFI query data is stored in user-defined memory space. } // ---------------------------------------------------------- // Issue the CFI Exit code thus returning the 39VF168X // to the read operating mode *sysAddress(0xAAA) = 0xAA; // write data 0xAA to device addr 0xAAA *sysAddress(0x555) = 0x55; // write data 0x55 to device addr 0x555 *sysAddress(0xAAA) = 0xF0; // write data 0xF0 to device addr 0xAAA Delay_150_Nano_Seconds(); // insert delay time = Tida } /************************************************************************/ /* PROCEDURE: SecID_Query */ /* */ /* This procedure should be used to query for Security ID information. */ /* */ /* Input: */ /* SST_SecID Source address to store SST SecID string */ /* User_SecID Source address to store User SecID string */ /* */ /* Output: */ /* None */ /************************************************************************/ void SecID_Query(BYTE *SST_SecID, BYTE *User_SecID) { BYTE index; // Issue the SecID Entry code to 39VF168X *sysAddress(0xAAA) = 0xAA; // write data 0xAA to device addr 0xAAA *sysAddress(0x555) = 0x55; // write data 0x55 to device addr 0x555 *sysAddress(0xAAA) = 0x88; // write data 0x88 to device addr 0xAAA Delay_150_Nano_Seconds(); // insert delay time = Tida // Perform all Security ID operations here: // SST programmed segment is from address 00H--0FH, // User programmed segment is from address 10H--1FH. for (index = 0x00; index <= 0x0F; index++) { *SST_SecID = *sysAddress(index); ++SST_SecID; *User_SecID = *sysAddress(index+0x10); ++User_SecID; // Security query data is stored in user-defined memory space. } // Issue the Sec ID Exit code thus returning the 39VF168X // to the read operating mode *sysAddress(0xAAA) = 0xAA; // write data 0xAA to device addr 0xAAA *sysAddress(0x555) = 0x55; // write data 0x55 to device addr 0x555 *sysAddress(0xAAA) = 0xF0; // write data 0xF0 to device addr 0xAAA Delay_150_Nano_Seconds(); // insert delay time = Tida } /************************************************************************/ /* PROCEDURE: Erase_One_Sector */ /* */ /* This procedure can be used to erase a total of 4096 bytes. */ /* */ /* Input: */ /* Dst DESTINATION address where the erase operation starts */ /* */ /* Output: */ /* return TRUE: indicates success in sector-erase */ /* return FALSE: indicates failure in sector-erase */ /************************************************************************/ int Erase_One_Sector(Uint32 Dst) { Uint32 DestBuf = Dst; int ReturnStatus; // Issue the Sector Erase command to 39VF168X *sysAddress(0xAAA) = 0xAA; // write data 0xAA to device addr 0xAAA *sysAddress(0x555) = 0x55; // write data 0x55 to device addr 0x555 *sysAddress(0xAAA) = 0x80; // write data 0x80 to device addr 0xAAA *sysAddress(0xAAA) = 0xAA; // write data 0xAA to device addr 0xAAA *sysAddress(0x555) = 0x55; // write data 0x55 to device addr 0x555 *sysAddress(DestBuf) = 0x50; // write data 0x50 to device sector addr ReturnStatus = Check_Toggle_Ready(DestBuf); // wait for TOGGLE bit ready return ReturnStatus; } /************************************************************************/ /* PROCEDURE: Erase_One_Block */ /* */ /* This procedure can be used to erase a total of 64K Bytes. */ /* */ /* Input: */ /* Dst DESTINATION address where the erase operation starts */ /* */ /* Output: */ /* return TRUE: indicates success in block-erase */ /* return FALSE: indicates failure in block-erase */ /************************************************************************/ int Erase_One_Block (Uint32 Dst) { Uint32 DestBuf = Dst; int ReturnStatus; // Issue the Block Erase command to 39VF168X *sysAddress(0xAAA) = 0xAA; // write data 0xAA to device addr 0xAAA *sysAddress(0x555) = 0x55; // write data 0x55 to device addr 0x555 *sysAddress(0xAAA) = 0x80; // write data 0x80 to device addr 0xAAA *sysAddress(0xAAA) = 0xAA; // write data 0xAA to device addr 0xAAA *sysAddress(0x555) = 0x55; // write data 0x55 to device addr 0x555 *sysAddress(DestBuf) = 0x30; // write data 0x30 to device sector addr ReturnStatus = Check_Toggle_Ready(DestBuf); // wait for TOGGLE bit ready return ReturnStatus; } /************************************************************************/ /* PROCEDURE: Erase_Entire_Chip */ /* */ /* This procedure can be used to erase the entire chip. */ /* */ /* Input: */ /* NONE */ /* */ /* Output: */ /* NONE */ /************************************************************************/ int Erase_Entire_Chip(void) { // Issue the Chip Erase command to 39VF168X *sysAddress(0xAAA) = 0xAA; // write data 0xAA to device addr 0xAAA *sysAddress(0x555) = 0x55; // write data 0x55 to device addr 0x555 *sysAddress(0xAAA) = 0x80; // write data 0x80 to device addr 0xAAA *sysAddress(0xAAA) = 0xAA; // write data 0xAA to device addr 0xAAA *sysAddress(0x555) = 0x55; // write data 0x55 to device addr 0x555 *sysAddress(0xAAA) = 0x10; // write data 0x10 to device addr 0xAAA //Delay_50_Milli_Seconds(); // Delay Tsce time if (Check_Data_Polling (0,0xFF)) return TRUE; else return FALSE; } /************************************************************************/ /* PROCEDURE: Program_One_Byte */ /* */ /* This procedure can be used to program ONE byte of data to the */ /* 39VF168X. */ /* */ /* NOTE: It is necessary to first erase the sector containing the */ /* byte to be programmed. */ /* */ /* Input: */ /* SrcByte The BYTE which will be written to the 39VF168XB */ /* Dst DESTINATION address which will be written with the */ /* data passed in from Src */ /* */ /* Output: */ /* return TRUE: indicates success in byte-program */ /* return FALSE: indicates failure in byte-program */ /************************************************************************/ int Program_One_Byte (BYTE *SrcByte, Uint32 Dst) { Uint32 DestBuf = Dst; BYTE *SourceBuf = SrcByte; int ReturnStatus; *sysAddress(0xAAA) = 0xAA; // write data 0xAA to device addr 0xAAA *sysAddress(0x555) = 0x55; // write data 0x55 to device addr 0x555 *sysAddress(0xAAA) = 0xA0; // write data 0xA0 to device addr 0xAAA *sysAddress(DestBuf) = *SourceBuf; // transfer the BYTE to destination ReturnStatus = Check_Toggle_Ready(DestBuf); // wait for TOGGLE bit ready return ReturnStatus; } /************************************************************************/ /* PROCEDURE: Program_One_Sector */ /* */ /* This procedure can be used to program a total of 4096 bytes of data */ /* to the SST39VF168X. */ /* */ /* NOTES: 1. It is necessary to first erase the sector before the */ /* programming. */ /* 2. This sample code assumes the destination address passed */ /* from the calling function is the starting address of the */ /* sector. */ /* */ /* Input: */ /* Src SOURCE address containing the data which will be */ /* written to the 39VF168X */ /* Dst DESTINATION address which will be written with the */ /* data passed in from Src */ /* */ /* Output: */ /* return TRUE: indicates success in sector-program */ /* return FALSE: indicates failure in sector-program */ /************************************************************************/ int Program_One_Sector (BYTE *Src, Uint32 Dst) { BYTE *SourceBuf; Uint32 DestBuf; int Index, ReturnStatus; SourceBuf = Src; DestBuf = Dst; ReturnStatus = Erase_One_Sector(DestBuf); // erase the sector first if (!ReturnStatus) return ReturnStatus; for (Index = 0; Index < SECTOR_SIZE; Index++) { // transfer data from source to destination ReturnStatus = Program_One_Byte( SourceBuf, DestBuf); ++DestBuf; ++SourceBuf; if (!ReturnStatus) return ReturnStatus; } return ReturnStatus; } /************************************************************************/ /* PROCEDURE: Program_One_Block */ /* */ /* This procedure can be used to program a total of 64k bytes of data */ /* to the SST39VF168X. */ /* */ /* NOTES: 1. It is necessary to first erase the block before the */ /* programming. */ /* 2. This sample code assumes the destination address passed */ /* from the calling function is the starting address of the */ /* block. */ /* */ /* Input: */ /* Src SOURCE address containing the data which will be */ /* written to the 39VF168X */ /* Dst DESTINATION address which will be written with the */ /* data passed in from Src */ /* */ /* Output: */ /* return TRUE: indicates success in block-program */ /* return FALSE: indicates failure in block-program */ /************************************************************************/ int Program_One_Block (BYTE *Src, Uint32 Dst) { BYTE *SourceBuf; Uint32 DestBuf; int Index, ReturnStatus; SourceBuf = Src; DestBuf = Dst; ReturnStatus = Erase_One_Block(DestBuf); // erase the block first if (!ReturnStatus) return ReturnStatus; for (Index = 0; Index < BLOCK_SIZE; Index++) { // transfer data from source to destination ReturnStatus = Program_One_Byte( SourceBuf, DestBuf); ++DestBuf; ++SourceBuf; if (!ReturnStatus) return ReturnStatus; } return ReturnStatus; } /************************************************************************/ /* PROCEDURE: SecID_Lock_Status */ /* */ /* This procedure should be used to check the Lock Status of SecID */ /* */ /* Input: */ /* None */ /* */ /* Output: */ /* return TRUE: indicates SecID is Locked */ /* return FALSE: indicates SecID is Unlocked */ /************************************************************************/ int SecID_Lock_Status(void) { BYTE SecID_Status; // Issue the Sec ID Entry code to 39VF168X *sysAddress(0xAAA) = 0xAA; // write data 0xAA to device addr 0xAAA *sysAddress(0x555) = 0x55; // write data 0x55 to device addr 0x555 *sysAddress(0xAAA) = 0x88; // write data 0x88 to device addr 0xAAA Delay_150_Nano_Seconds(); // insert delay time = Tida // Read Lock Status of SecID segment SecID_Status = *sysAddress(0x00FF); SecID_Status &= 0x08; // Unlocked: DQ3=1; Locked: DQ3=0 // Issue the Sec ID Exit code thus returning the 39VF168X // to the read operating mode *sysAddress(0xAAA) = 0xAA; // write data 0xAA to device addr 0xAAA *sysAddress(0x555) = 0x55; // write data 0x55 to device addr 0x555 *sysAddress(0xAAA) = 0xF0; // write data 0xF0 to device addr 0xAAA Delay_150_Nano_Seconds(); // insert delay time = Tida if (!SecID_Status) return TRUE; // SecID segment is Locked return FALSE; // SecID segment is Unlocked } /************************************************************************/ /* PROCEDURE: User_SecID_Byte_Program */ /* */ /* This procedure can be used to program data into the User SecID */ /* segment (from 10H--1FH) in 39VF168X. */ /* */ /* NOTE: 1. It's recommended to lock out the SecID segment after the */ /* completion of program. */ /* 2. There's no way to unlock the SecID segment once it's */ /* locked. */ /* */ /* Input: */ /* SrcWord Source address to fetch data */ /* Dst Destination address to write data */ /* length number of byte needs to be programmed */ /* */ /* Output: */ /* return TRUE: indicates SecID program is successful */ /* return FALSE: indicates SecID program is failed or SecID */ /* is locked. */ /************************************************************************/ int User_SecID_Byte_Program (BYTE *SrcByte, BYTE *Dst, int length) { BYTE *DestBuf; BYTE *SourceBuf; int test, index=length; DestBuf = Dst; SourceBuf = SrcByte; test = SecID_Lock_Status (); // check whether the SecID is Locked or not if (test) // TRUE: SecID is Locked return FALSE; while (index--) { *sysAddress(0xAAA) = 0xAA; // write data 0xAA to device addr 0xAAA *sysAddress(0x555) = 0x55; // write data 0x55 to device addr 0x555 *sysAddress(0xAAA) = 0xA5; // write data 0xA5 to device addr 0xAAA *sysAddress(DestBuf) = *SourceBuf; // transfer the BYTE to destination ++DestBuf; ++SourceBuf; // Read the toggle bit to detect end-of-write for the Sec ID. // Do Not use Data# Polling for User_SecID_Byte_Program. test = Check_Toggle_Ready((Uint32)DestBuf); // wait for TOGGLE bit to get ready if (!test) return FALSE; // SecID Byte-Program failed! } return TRUE; } /************************************************************************/ /* PROCEDURE: User_SecID_Lock_Out */ /* */ /* This procedure can be used to Lock Out the User Seccurity ID. */ /* User Security ID segment, from 10H--1FH, in 39VF168X. */ /* */ /* NOTE: 1. Call SecID_Lock_Status() first to verify the SecID is */ /* unlocked. */ /* 2. SecID segment can't be erased. */ /* 3. SecID segment can't be unlocked once it's locked. */ /* */ /* Input: None */ /* */ /* Output: None */ /************************************************************************/ void User_SecID_Lock_Out (void) { *sysAddress(0xAAA) = 0xAA; // write data 0xAA to device addr 0xAAA *sysAddress(0x555) = 0x55; // write data 0x55 to device addr 0x555 *sysAddress(0xAAA) = 0x85; // write data 0x85 to device addr 0xAAA *sysAddress(0x2F) = 0x00; // write data 0x00 to any addr Delay_10_Micro_Seconds(); // Wait for Byte-Program timeout, Tbp=10us } /************************************************************************/ /* PROCEDURE: Erase_Suspend */ /* */ /* This procedure can be used to temporarily suspend a Sector/Block- */ /* Erase operation in 39VF168X. */ /* */ /* Input: None */ /* */ /* Output: None */ /************************************************************************/ void Erase_Suspend (void) { *sysAddress(0xAAA) = 0xB0; // write data 0xB0 to any addr, i.e. 0xAAA Delay_20_Micro_Seconds(); // The device automatically enters read mode // typically within 20 us after the Erase-Suspend command issued. } /************************************************************************/ /* PROCEDURE: Erase_Resume */ /* */ /* This procedure can be used to resume a Sector-Erase or Block-Erase */ /* operation that had been suspended in 39VF168X. */ /* */ /* Input: None */ /* */ /* Output: None */ /************************************************************************/ void Erase_Resume (void) { *sysAddress(0xAAA) = 0x30; // write data 0x30 to any addr, i.e. 0xAAA } /************************************************************************/ /* PROCEDURE: Check_Toggle_Ready */ /* */ /* During the internal program cycle, any consecutive read operation */ /* on DQ6 will produce alternating 0's and 1's i.e. toggling between */ /* 0 and 1. When the program cycle is completed, DQ6 of the data will */ /* stop toggling. After the DQ6 data bit stops toggling, the device is */ /* ready for next operation. */ /* */ /* Input: */ /* Dst must already be set-up by the caller */ /* */ /* Output: TRUE Data toggling success */ /* FALSE Time out */ /************************************************************************/ int Check_Toggle_Ready (Uint32 Dst) { BYTE PreData; BYTE CurrData; unsigned long TimeOut = 0; PreData = *sysAddress(Dst); PreData = PreData & 0x40; // read DQ6 while (TimeOut < MAX_TIMEOUT) // MAX_TIMEOUT=0x07FFFFFF { CurrData = *sysAddress(Dst); CurrData = CurrData & 0x40; // read DQ6 again if (PreData == CurrData) { return TRUE; } PreData = CurrData; TimeOut++; } return FALSE; } /************************************************************************/ /* PROCEDURE: Check_Data_Polling */ /* */ /* During the internal program cycle, any attempt to read DQ7 of the */ /* last byte loaded during the page/byte-load cycle will receive the */ /* complement of the true data. Once the program cycle is completed, */ /* DQ7 will show true data. */ /* */ /* Input: */ /* Dst must already be set-up by the caller */ /* TrueData this is the original (true) data */ /* */ /* Output: */ /* TRUE Data polling success */ /* FALSE Time out */ /************************************************************************/ int Check_Data_Polling (Uint32 Dst, BYTE TrueData) { BYTE CurrData; unsigned long int TimeOut = 0; TrueData = TrueData & 0x80; // read D7 while (TimeOut < MAX_TIMEOUT) // MAX_TIMEOUT=0x07FFFFFF { CurrData = *sysAddress(Dst); CurrData = CurrData & 0x80; // read DQ7 if (TrueData == CurrData) { return TRUE; } TimeOut++; } return FALSE; }