A library with drivers for different peripherals on the LPC4088 QuickStart Board or related add-on boards.

Dependencies:   FATFileSystem

Dependents:   LPC4088test LPC4088test_ledonly LPC4088test_deleteall LPC4088_RAMtest ... more

Files at this revision

API Documentation at this revision

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

QSPIFileSystem.cpp Show annotated file Show diff for this revision Revisions of this file
SPIFI.cpp Show annotated file Show diff for this revision Revisions of this file
SPIFI.h Show annotated file Show diff for this revision Revisions of this file
--- 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
+