A library with drivers for different peripherals on the LPC4088 QuickStart Board or related add-on boards.
Dependencies: FATFileSystem
Fork of EALib by
Revision 8:fe3cb3fbb64e, committed 2013-12-11
- Comitter:
- embeddedartists
- Date:
- Wed Dec 11 12:16:40 2013 +0000
- Parent:
- 7:e431d9d47db6
- Child:
- 9:3839113c5abc
- Commit message:
- Extracted SPIFI initialization code from QSPIFileSystem into new SPIFI class to allow spifi to be used without having to use the file system.
Changed in this revision
--- a/QSPIFileSystem.cpp Mon Nov 11 09:39:58 2013 +0000 +++ b/QSPIFileSystem.cpp Wed Dec 11 12:16:40 2013 +0000 @@ -1,7 +1,7 @@ #include "QSPIFileSystem.h" #include "mbed_debug.h" -#include "spifi_rom_api.h" +#include "SPIFI.h" /****************************************************************************** * Defines and typedefs @@ -9,27 +9,11 @@ #define QSPI_DBG 0 -/* - * The SPIFI_ROM_PTR (0x1FFF1FF8) points to an area where the pointers to - * different drivers in ROM are stored. - */ -typedef struct { - /*const*/ unsigned p_usbd; // USBROMD - /*const*/ unsigned p_clib; - /*const*/ unsigned p_cand; - /*const*/ unsigned p_pwrd; // PWRROMD - /*const*/ unsigned p_promd; // DIVROMD - /*const*/ SPIFI_RTNS *pSPIFID; // SPIFIROMD - /*const*/ unsigned p_dev3; - /*const*/ unsigned p_dev4; -} ROM; - -#define ROM_DRIVERS_PTR ((ROM *)(*((unsigned int *)SPIFI_ROM_PTR))) #define IS_ADDR_IN_SPIFI(__addr) ( (((uint32_t)(__addr)) & 0xff000000) == SPIFI_MEM_BASE ) -#define MEM_SIZE (memInfo.memSize) //(8*1024*1024) -#define ERASE_SIZE (memInfo.eraseBlockSize) //(64*1024) -#define NUM_BLOCKS (memInfo.numEraseBlocks) //(MEM_SIZE/ERASE_SIZE) +#define MEM_SIZE (memInfo.memSize) +#define ERASE_SIZE (memInfo.eraseBlockSize) +#define NUM_BLOCKS (memInfo.numEraseBlocks) typedef uint32_t toc_entry_t; @@ -130,11 +114,11 @@ * Local variables *****************************************************************************/ -static toc_entry_t* TOC = NULL;//[NUM_BLOCKS]; +static toc_entry_t* TOC = NULL; static int activeTOC = -1; static const SPIFI_RTNS *spifi = NULL; -static SPIFIobj obj; +static SPIFIobj* obj; static SPIFIopers opers; static char addr_conflict_buff[PROG_SIZE]; @@ -178,77 +162,65 @@ static fresult qspifs_init() { if (spifi == NULL) { - - // Turn on SPIFI block as it is disabled on reset - LPC_SC->PCONP |= 0x00010000; - - // pinsel for SPIFI - LPC_IOCON->P2_7 = 5; /* SPIFI_CSN @ P2.7 */ - LPC_IOCON->P0_22 = 5; /* SPIFI_CLK @ P0.22 */ - LPC_IOCON->P0_15 = 5; /* SPIFI_IO2 @ P0.15 */ - LPC_IOCON->P0_16 = 5; /* SPIFI_IO3 @ P0.16 */ - LPC_IOCON->P0_17 = 5; /* SPIFI_IO1 @ P0.17 */ - LPC_IOCON->P0_18 = 5; /* SPIFI_IO0 @ P0.18 */ + SPIFI::SpifiError err; + err = SPIFI::instance().init(); + if (err != SPIFI::Ok) { + spifi = NULL; + return FS_ERR_SPIFI; + } - uint32_t spifi_clk_div = (*((volatile uint32_t*)0x400FC1B4)) & 0x1f; - uint32_t spifi_clk_mhz = (SystemCoreClock / spifi_clk_div) / 1000000; - - spifi = ROM_DRIVERS_PTR->pSPIFID; - - /* Typical time tCS is 20 ns min, we give 200 ns to be on safer side */ - int rc = spifi->spifi_init (&obj, spifi_clk_mhz/5, S_FULLCLK+S_RCVCLK, spifi_clk_mhz); - if (rc) { - spifi = NULL; - return qspifs_translateSpifiError(rc); - } + SPIFI::instance().internalData(&obj, &spifi); /* Make sure it is a tested flash module */ - if ((obj.mfger == 1) && (obj.devType == 0x2) && (obj.devID == 0x15) && (obj.memSize > 0x100000)) - { - /* For the Spansion memory the TOC occupies 256bytes and the TOC block will - hold 256 TOCs. */ - strcpy(memInfo.memName, "Spansion S25FL032"); - memInfo.memSize = obj.memSize; - memInfo.eraseBlockSize = 64*1024; - memInfo.numEraseBlocks = memInfo.memSize / memInfo.eraseBlockSize; - memInfo.tocSizeInBytes = sizeof(toc_entry_t) * memInfo.numEraseBlocks; - memInfo.numTocs = memInfo.eraseBlockSize / memInfo.tocSizeInBytes; - memInfo.tocBlockAddr = SPIFI_MEM_BASE + (NUM_BLOCKS * ERASE_SIZE) - (memInfo.numTocs * memInfo.tocSizeInBytes); - } - else if ((obj.mfger == 0xef) && (obj.devType == 0x40) && (obj.devID == 0x17) && (obj.memSize > 0x100000)) - { - /* For the Winbond memory the TOC occupies 8192 bytes and that is bigger than - one erase block (which is 4096 bytes). It is possible to either keep only - one TOC or to create a couple to reduce wear on the memory. In this case - the multiple TOCs option is used. */ - strcpy(memInfo.memName, "Winbond W25Q64FV"); - memInfo.memSize = obj.memSize; - memInfo.eraseBlockSize = 4*1024; - memInfo.numEraseBlocks = memInfo.memSize / memInfo.eraseBlockSize; - memInfo.tocSizeInBytes = sizeof(toc_entry_t) * memInfo.numEraseBlocks; - memInfo.numTocs = 8; - memInfo.tocBlockAddr = SPIFI_MEM_BASE + (NUM_BLOCKS * ERASE_SIZE) - (memInfo.numTocs * memInfo.tocSizeInBytes); - } - else - { - debug("INIT: Memory is unknown and may not work as expected\n"); + switch (SPIFI::instance().device()) { + case SPIFI::Spansion_S25FL032: + /* For the Spansion memory the TOC occupies 256bytes and the TOC block will + hold 256 TOCs. */ + strcpy(memInfo.memName, "Spansion S25FL032"); + memInfo.memSize = obj->memSize; + memInfo.eraseBlockSize = 64*1024; + memInfo.numEraseBlocks = memInfo.memSize / memInfo.eraseBlockSize; + memInfo.tocSizeInBytes = sizeof(toc_entry_t) * memInfo.numEraseBlocks; + memInfo.numTocs = memInfo.eraseBlockSize / memInfo.tocSizeInBytes; + memInfo.tocBlockAddr = SPIFI_MEM_BASE + (NUM_BLOCKS * ERASE_SIZE) - (memInfo.numTocs * memInfo.tocSizeInBytes); + break; - // Asuming it has 64Kb erase blocks (i.e. same setup as the Spansion S25FL032 - strcpy(memInfo.memName, "Unknown - check ID"); - memInfo.memSize = obj.memSize; - memInfo.eraseBlockSize = 64*1024; - memInfo.numEraseBlocks = memInfo.memSize / memInfo.eraseBlockSize; - memInfo.tocSizeInBytes = sizeof(toc_entry_t) * memInfo.numEraseBlocks; - memInfo.numTocs = memInfo.eraseBlockSize / memInfo.tocSizeInBytes; - memInfo.tocBlockAddr = SPIFI_MEM_BASE + (NUM_BLOCKS * ERASE_SIZE) - (memInfo.numTocs * memInfo.tocSizeInBytes); + case SPIFI::Winbond_W25Q64FV: + /* For the Winbond memory the TOC occupies 8192 bytes and that is bigger than + one erase block (which is 4096 bytes). It is possible to either keep only + one TOC or to create a couple to reduce wear on the memory. In this case + the multiple TOCs option is used. */ + strcpy(memInfo.memName, "Winbond W25Q64FV"); + memInfo.memSize = obj->memSize; + memInfo.eraseBlockSize = 4*1024; + memInfo.numEraseBlocks = memInfo.memSize / memInfo.eraseBlockSize; + memInfo.tocSizeInBytes = sizeof(toc_entry_t) * memInfo.numEraseBlocks; + memInfo.numTocs = 8; + memInfo.tocBlockAddr = SPIFI_MEM_BASE + (NUM_BLOCKS * ERASE_SIZE) - (memInfo.numTocs * memInfo.tocSizeInBytes); + break; + + case SPIFI::UnknownDevice: + default: + debug("INIT: Memory is unknown and may not work as expected\n"); + + // Asuming it has 64Kb erase blocks (i.e. same setup as the Spansion S25FL032 + strcpy(memInfo.memName, "Unknown - check ID"); + memInfo.memSize = obj->memSize; + memInfo.eraseBlockSize = 64*1024; + memInfo.numEraseBlocks = memInfo.memSize / memInfo.eraseBlockSize; + memInfo.tocSizeInBytes = sizeof(toc_entry_t) * memInfo.numEraseBlocks; + memInfo.numTocs = memInfo.eraseBlockSize / memInfo.tocSizeInBytes; + memInfo.tocBlockAddr = SPIFI_MEM_BASE + (NUM_BLOCKS * ERASE_SIZE) - (memInfo.numTocs * memInfo.tocSizeInBytes); - /* - * If this happens, check the manufacturer and device information - * and compare with the data sheet for your chip. Also make sure - * that the sector sizes are the same (i.e. 64KB) for your chip. - * If everything is the same then add an exception for your chip. - */ + /* + * If this happens, check the manufacturer and device information + * and compare with the data sheet for your chip. Also make sure + * that the sector sizes are the same (i.e. 64KB) for your chip. + * If everything is the same then add an exception for your chip. + */ + break; } + debug_if(QSPI_DBG, "INIT: Found %dMB %s\n", memInfo.memSize/0x100000, memInfo.memName); if (TOC != NULL) { @@ -434,7 +406,7 @@ opers.scratch = NULL; opers.protect = 0; opers.options = S_NO_VERIFY; - rc = spifi->spifi_erase(&obj, &opers); + rc = spifi->spifi_erase(obj, &opers); if (rc) { return qspifs_translateSpifiError(rc); } @@ -456,7 +428,7 @@ for (int i = 0; i < (TOC_SIZE / PROG_SIZE); i++) { opers.dest = (char *)(TOC_BLOCK_ADDR + activeTOC*TOC_SIZE + i*PROG_SIZE); - rc = spifi->spifi_program(&obj, ((char*)TOC)+i*PROG_SIZE, &opers); + rc = spifi->spifi_program(obj, ((char*)TOC)+i*PROG_SIZE, &opers); if (rc) { break; @@ -579,7 +551,7 @@ opers.scratch = NULL; opers.protect = 0; opers.options = S_NO_VERIFY; - return qspifs_translateSpifiError(spifi->spifi_erase (&obj, &opers)); + return qspifs_translateSpifiError(spifi->spifi_erase (obj, &opers)); } /****************************************************************************** @@ -651,7 +623,7 @@ opers.protect = 0; opers.options = S_VERIFY_PROG | S_FORCE_ERASE;// S_CALLER_ERASE; opers.dest = (char *)(i*ERASE_SIZE); - rc = spifi->spifi_program(&obj, (char*)pSrc, &opers); + rc = spifi->spifi_program(obj, (char*)pSrc, &opers); if (rc) { return qspifs_translateSpifiError(rc); } @@ -827,7 +799,7 @@ opers.scratch = NULL; opers.protect = 0; opers.options = S_NO_VERIFY; - rc = spifi->spifi_erase(&obj, &opers); + rc = spifi->spifi_erase(obj, &opers); if (rc) { return qspifs_translateSpifiError(rc); } @@ -937,9 +909,9 @@ } if (IS_ADDR_IN_SPIFI(pData)) { memcpy(addr_conflict_buff, pSrc, opers.length); - rc = spifi->spifi_program(&obj, addr_conflict_buff, &opers); + rc = spifi->spifi_program(obj, addr_conflict_buff, &opers); } else { - rc = spifi->spifi_program(&obj, (char*) pSrc, &opers); + rc = spifi->spifi_program(obj, (char*) pSrc, &opers); } res = qspifs_translateSpifiError(rc); if ((res == FS_ERR_SPIFI_VERIFICATION) @@ -1278,23 +1250,6 @@ QSPIFileSystem::QSPIFileSystem(const char* name) : FileSystemLike(name) { - // Turn on SPIFI block as it is disabled on reset - LPC_SC->PCONP |= 0x00010000; - - // pinsel for SPIFI - LPC_IOCON->P2_7 &= ~0x07; - LPC_IOCON->P2_7 |= 0x05; /* SPIFI_CSN @ P2.7 */ - LPC_IOCON->P0_22 &= ~0x07; - LPC_IOCON->P0_22 |= 0x05; /* SPIFI_CLK @ P0.22 */ - LPC_IOCON->P0_15 &= ~0x07; - LPC_IOCON->P0_15 |= 0x5; /* SPIFI_IO2 @ P0.15 */ - LPC_IOCON->P0_16 &= ~0x07; - LPC_IOCON->P0_16 |= 0x5; /* SPIFI_IO3 @ P0.16 */ - LPC_IOCON->P0_17 &= ~0x07; - LPC_IOCON->P0_17 |= 0x5; /* SPIFI_IO1 @ P0.17 */ - LPC_IOCON->P0_18 &= ~0x07; - LPC_IOCON->P0_18 |= 0x5; /* SPIFI_IO0 @ P0.18 */ - activeTOC = -1; spifi = NULL; } @@ -1493,7 +1448,3 @@ } return false; } - - - -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SPIFI.cpp Wed Dec 11 12:16:40 2013 +0000 @@ -0,0 +1,234 @@ +#include "SPIFI.h" +#include "mbed_debug.h" + + +/****************************************************************************** + * Defines and typedefs + *****************************************************************************/ + +#define SPIFI_DBG 0 + +/* + * The SPIFI_ROM_PTR (0x1FFF1FF8) points to an area where the pointers to + * different drivers in ROM are stored. + */ +typedef struct { + /*const*/ unsigned p_usbd; // USBROMD + /*const*/ unsigned p_clib; + /*const*/ unsigned p_cand; + /*const*/ unsigned p_pwrd; // PWRROMD + /*const*/ unsigned p_promd; // DIVROMD + /*const*/ SPIFI_RTNS *pSPIFID; // SPIFIROMD + /*const*/ unsigned p_dev3; + /*const*/ unsigned p_dev4; +} ROM; + +#define ROM_DRIVERS_PTR ((ROM *)(*((unsigned int *)SPIFI_ROM_PTR))) +#define IS_ADDR_IN_SPIFI(__addr) ( (((uint32_t)(__addr)) & 0xff000000) == SPIFI_MEM_BASE ) + +#define SPIFI_MIN(__a, __b) (((__a) < (__b)) ? (__a) : (__b)) + +/****************************************************************************** + * Local variables + *****************************************************************************/ + +/****************************************************************************** + * Private Functions + *****************************************************************************/ + +SPIFI::SpifiError SPIFI::translateError(int err, bool verify) +{ + SpifiError res; + + _verError = 0; + + if (err == 0) + { + res = Ok; + } + else if ((err >= Uninitialized) && (err <= UnknownError)) + { + // This is a known error code + res = (SpifiError)err; + } + else if ((err >= InternalError) && (err <= EraseConflict)) + { + // This is a known error code + res = (SpifiError)err; + } + else if (verify) + { + // As verification was selected and err is not in the list of known + // codes this falls into this category in the User's Manual: + // + // "Other non-zero values can occur if options selects verification. + // They will be the address in the SPIFI memory area at which the + // first discrepancy was found." + _verError = err; + res = Verification; + } + else + { + // Should never happen :-) as all listed error codes are covered but + // to be on the safe side and not interpret this as a success, a generic + // error is set. + res = UnknownError; + } + return res; +} + +/****************************************************************************** + * Public Functions + *****************************************************************************/ + +SPIFI::SPIFI() +{ + _verError = 0; + _initialized = false; + _device = UnknownDevice; + _memorySize = 0; + _eraseBlockSize = 0; + + _romData = (SPIFIobj*)malloc(sizeof(SPIFIobj)); + if (_romData == NULL) { + debug("SPIFI: Failed to allocate memory for ROM data\n"); + } +} + +SPIFI::~SPIFI() +{ + if (_romData != NULL) { + free(_romData); + } +} + +SPIFI::SpifiError SPIFI::init() +{ + if (!_initialized) { + + // Turn on SPIFI block as it is disabled on reset + LPC_SC->PCONP |= 0x00010000; + + // pinsel for SPIFI + LPC_IOCON->P2_7 = 5; /* SPIFI_CSN @ P2.7 */ + LPC_IOCON->P0_22 = 5; /* SPIFI_CLK @ P0.22 */ + LPC_IOCON->P0_15 = 5; /* SPIFI_IO2 @ P0.15 */ + LPC_IOCON->P0_16 = 5; /* SPIFI_IO3 @ P0.16 */ + LPC_IOCON->P0_17 = 5; /* SPIFI_IO1 @ P0.17 */ + LPC_IOCON->P0_18 = 5; /* SPIFI_IO0 @ P0.18 */ + + uint32_t spifi_clk_div = (*((volatile uint32_t*)0x400FC1B4)) & 0x1f; + uint32_t spifi_clk_mhz = (SystemCoreClock / spifi_clk_div) / 1000000; + + _spifi = ROM_DRIVERS_PTR->pSPIFID; + + /* Typical time tCS is 20 ns min, we give 200 ns to be on safer side */ + int rc = _spifi->spifi_init (_romData, spifi_clk_mhz/5, S_FULLCLK+S_RCVCLK, spifi_clk_mhz); + if (rc) { + _spifi = NULL; + return translateError(rc); + } + + /* Make sure it is a tested flash module */ + if ((_romData->mfger == 1) && (_romData->devType == 0x2) && (_romData->devID == 0x15) && (_romData->memSize > 0x100000)) + { + _device = Spansion_S25FL032; + _memorySize = _romData->memSize; + _eraseBlockSize = 64*1024; + } + else if ((_romData->mfger == 0xef) && (_romData->devType == 0x40) && (_romData->devID == 0x17) && (_romData->memSize > 0x100000)) + { + _device = Winbond_W25Q64FV; + _memorySize = _romData->memSize; + _eraseBlockSize = 4*1024; + } + else + { + debug("SPIFI::init(): Memory is unknown and may not work as expected\n"); + + // Asuming it has 64Kb erase blocks (i.e. same setup as the Spansion S25FL032 + _device = UnknownDevice; + _memorySize = _romData->memSize; + _eraseBlockSize = 64*1024; + + /* + * If this happens, check the manufacturer and device information + * and compare with the data sheet for your chip. Also make sure + * that the sector sizes are the same (i.e. 64KB) for your chip. + * If everything is the same then add an exception for your chip. + */ + } + + _initialized = true; + } + return Ok; +} + +SPIFI::SpifiError SPIFI::program(uint32_t dest, unsigned len, char* src, Options options, bool verify, char* scratch) +{ + unsigned written = 0; + SPIFIopers opers; + opers.dest = (char *)dest; + opers.length = SPIFI_MIN(len, PROG_SIZE); + opers.scratch = scratch; + opers.protect = 0; + opers.options = options; + if (verify) { + opers.options |= S_VERIFY_PROG; + } + + if (IS_ADDR_IN_SPIFI(src)) + { + // The SPIFI ROM driver cannot write data from SPIFI into + // SPIFI (i.e. cannot read and write at the same time). + // The workaround is to copy the source data into a buffer + // in local memory and use that as source for the write + // instead. + while (written < len) { + memcpy(_addrConflictBuff, src + written, opers.length); + int rc = _spifi->spifi_program(_romData, _addrConflictBuff, &opers); + if (rc) + { + // got an error + return translateError(rc, verify); + } + written += opers.length; + opers.dest += opers.length; + opers.length = SPIFI_MIN(len - written, PROG_SIZE); + } + } + else + { + while (written < len) { + int rc = _spifi->spifi_program(_romData, src + written, &opers); + if (rc) + { + // got an error + return translateError(rc, verify); + } + written += opers.length; + opers.dest += opers.length; + opers.length = SPIFI_MIN(len - written, PROG_SIZE); + } + } + + return Ok; +} + +SPIFI::SpifiError SPIFI::erase(uint32_t dest, unsigned len, char* src, bool verify, char* scratch) +{ + SPIFIopers opers; + opers.dest = (char *)dest; + opers.length = len; + opers.scratch = scratch; + opers.protect = 0; + if (verify) { + opers.options = S_VERIFY_ERASE; + } else { + opers.options = S_NO_VERIFY; + } + int rc = _spifi->spifi_erase(_romData, &opers); + return translateError(rc); +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SPIFI.h Wed Dec 11 12:16:40 2013 +0000 @@ -0,0 +1,212 @@ + +#ifndef SPIFI_H +#define SPIFI_H + +#include "mbed.h" +#include "spifi_rom_api.h" + +/** + * SPIFI Example + * + * @code + * #include "mbed.h" + * #include "SPIFI.h" + * + * int main(void) { + * SPIFI::SpifiError err; + * + * err = SPIFI::instance().init(); + * if (err != SPIFI::Ok) { + * printf("Failed to initialize SPIFI, error %d\n", err); + * } + * + * // Write "Hello World!" into the first bytes of the SPIFI + * char buff[20] = "Hello World!"; + * err = SPIFI::instance().program(0, strlen(buff)+1, buff, SPIFI::EraseAsRequired); + * if (err != SPIFI::Ok) { + * printf("Failed to write to SPIFI, error %d\n", err); + * } + * + * // Now verify that it can be read + * if (memcmp((char*)SPIFI::SpifiMemBase, buff, strlen(buff)+1) == 0) { + * printf("Readback from memory OK: '%s'\n", SPIFI::SpifiMemBase); + * } else { + * printf("Spifi does not contain the correct data!\n"); + * } + * } + * @endcode + */ +class SPIFI { +public: + + enum Constants { + SpifiMemBase = 0x28000000 + }; + + enum Options { + ForceErase = (0<<2), + EraseAsRequired = (1<<2), + CallerErase = (1<<3) + }; + + enum SpifiError { + Ok = 0, + Uninitialized = 1, + Verification = 2, + SameAddress = 3, + UnknownError = 4, + InternalError = 0x20002, + Timeout = 0x20003, + OperandError = 0x20004, + Status = 0x20005, + ExtDeviceId = 0x20006, + DeviceId = 0x20007, + DeviceType = 0x20008, + Manufacturer = 0x20009, + InvalidJDECId = 0x2000A, + EraseConflict = 0x2000B, + }; + + enum Device { + Spansion_S25FL032, /* Manufacturer: 0x01, devType: 0x02, devID: 0x15 */ + Winbond_W25Q64FV, /* Manufacturer: 0xEF, devType: 0x40, devID: 0x17 */ + UnknownDevice + }; + + static SPIFI& instance() + { + static SPIFI singleton; + return singleton; + } + + + /** Initializes the SPIFI ROM driver making the content of the external serial flash available + * + * @returns + * Ok on success + * An error code on failure + */ + SpifiError init(); + + /** Returns the detected external serial flash + * + * @returns + * The detected device or UnknownDevice + */ + Device device() { return _device; } + + /** Returns the size (in bytes) of the external serial flash + * + * @returns + * The size in bytes + */ + uint32_t memorySize() { return _memorySize; } + + /** Returns the size of an erase block (in bytes) on the external serial flash + * + * @returns + * The erase block size in bytes + */ + uint32_t eraseBlockSize() { return _eraseBlockSize; } + + /** Returns the address where the verifcation failed. Use only after a Verification error code + * has been retured from one of the other functions + * + * @returns + * The address to where the program or erase operation failed + */ + uint32_t getVerificationErrorAddr() { return _verError; } + + /** Used by the QSPIFileSystem class to get access to all spifi internal data. + * + * @param initData The parameter returned by spifi_init + * @param romPtr The location of the SPIFI ROM driver in memory + */ + void internalData(SPIFIobj** initData, const SPIFI_RTNS** romPtr) { *initData = _romData; *romPtr = _spifi; } + + /** Copies len data from src to dest. This function will split len > 256 into writes of 256 bytes each. + * + * Programming means that a bit can either be left at 0, or programmed from 1 to 0. Changing bits from 0 to 1 requires an erase operation. + * While bits can be individually programmed from 1 to 0, erasing bits from 0 to 1 must be done in larger chunks (manufacturer dependant + * but typically 4Kbyte to 64KByte at a time). + * + * Specify how/when erasing is done with the options parameter: + * + * - ForceErase causes len bytes to be erased starting at dest. If a scratch buffer (must be the size of an erase block) is + * provided then only the len bytes will be erased. If no scratch buffer is provided then the erase block surrounding + * dest will be erased. + * + * - EraseAsRequired tests if the destination can be written to without erasing. If it can then no erasing is done. If it + * can't then behaviour is the same as if ForceErase was specified. + * + * - CallerErase tests if the destination can be written to without erasing. If it can then a data is written. If it + * can't then an error code is returned and nothing is written. + * + * Scratch should be NULL or the address of an area of RAM that the SPIFI driver can use + * to save data during erase operations. If provided, the scratch area should be as large + * as the smallest erase size that is available throughout the serial flash device. If scratch + * is NULL (zero) and an erase is necessary, any bytes in the first erase block before dest + * are left in erased state (all ones), as are any bytes in the last erase block after dest + len + * + * @param dest Address to write to, highest byte must be 0x00 or 0x28 as in 0x28000000 + * @param len Number of bytes to write + * @param src Data to write + * @param options How to handle content of destination + * @param verify Should all writes be verified or not + * @param scratch Used with ForceErase and EraseAsRequired option to preserve content of flash outside of + * written area. Size of buffer must be at least the size of one erase block. + * + * @returns + * Ok on success + * An error code on failure + */ + SpifiError program(uint32_t dest, unsigned len, char* src, Options options, bool verify=false, char* scratch=NULL); + + /** Erases the content of the memory at the specified address + * + * Scratch should be NULL or the address of an area of RAM that the SPIFI driver can use + * to save data during erase operations. If provided, the scratch area should be as large + * as the smallest erase size that is available throughout the serial flash device. If scratch + * is NULL (zero) and an erase is necessary, any bytes in the first erase block before dest + * are left in erased state (all ones), as are any bytes in the last erase block after dest + len + * + * @param dest Address to start erasing at, highest byte must be 0x00 or 0x28 as in 0x28000000 + * @param len Number of bytes to erase + * @param verify Should the erased area be verified to make sure it is all 0xff + * @param scratch Used to preserve content of flash outside of erased area. + * Size of buffer must be at least the size of one erase block. + * + * @returns + * Ok on success + * An error code on failure + */ + SpifiError erase(uint32_t dest, unsigned len, char* src, bool verify=false, char* scratch=NULL); + + +private: + + uint32_t _verError; + + bool _initialized; + + Device _device; + uint32_t _memorySize; + uint32_t _eraseBlockSize; + + SPIFIobj* _romData; + const SPIFI_RTNS* _spifi; + + char _addrConflictBuff[256]; + + explicit SPIFI(); + // hide copy constructor + SPIFI(const SPIFI&); + // hide assign operator + SPIFI& operator=(const SPIFI&); + ~SPIFI(); + + SpifiError translateError(int err, bool verify = false); +}; + +#endif +