Mirror with some correction

Dependencies:   mbed FastIO FastPWM USBDevice

Revision:
35:e959ffba78fd
Child:
40:cc0d9814522b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nvm.h	Sat Dec 19 06:37:19 2015 +0000
@@ -0,0 +1,111 @@
+// NVM - Non-Volatile Memory
+//
+// This module handles the storage of our configuration settings
+// and calibration data in flash memory, which allows us to
+// retrieve these settings after each power cycle.
+
+
+#ifndef NVM_H
+#define NVM_H
+
+#include "config.h"
+#include "FreescaleIAP.h"
+
+
+// Non-volatile memory (NVM) structure
+//
+// This structure defines the layout of our saved configuration
+// and calibration data in flash memory.
+//
+// Hack alert!
+// Our use of flash for this purpose is ad hoc and not supported
+// by the mbed platform.  mbed doesn't impose a file system on the
+// KL25Z flash; it simply treats the flash as a raw storage space
+// and assumes that the linker output is the only thing using it.
+// So if we want to use the flash, we basically have to do it on 
+// the sly, by using space that the linker happens to leave unused.
+// Fortunately, it's fairly easy to do this, because the flash
+// is mapped in the obvious way, as a single contiguous block in 
+// the CPU memory space, and the linker does the obvious thing, 
+// storing its entire output in a single contiguous block starting
+// at the lowest flash address.  This means that all flash memory
+// from (lowest flash address + length of linker output) to
+// (highest flash address) is unused and available for our sneaky
+// system.  Unfortunately, there's no reliable way for the program
+// to determine the length of the linker output, so we can't know 
+// where our available region starts.  But we do know how much flash 
+// there is overall, so we know where the flash ends.  We can 
+// therefore align our storage region at the end of memory and hope 
+// that it's small enough not to encroach on the linker space.  We
+// can actually do a little better than hope: the mbed tools tell us 
+// at the UI level how much flash the linker is using, even though it 
+// doesn't expose that information to us programmatically, so we can 
+// manually check that we have enough room.  As of this writing, the 
+// configuration structure is much much smaller than the available 
+// leftover flash space, so we should be safe indefinitely, barring 
+// a major expansion of the configuration structure or code size.
+//
+// The boot loader seems to erase the entire flash space every time
+// we load new firmware, so our configuration structure is lost
+// when we update.  Furthermore, since we explicitly choose to put
+// the config structure in space that isn't initialized by the linker,
+// we can't specify the new contents stored on these erasure events.
+// To deal with this, we use a signature and checksum to check the
+// integrity of the stored data.  The erasure leaves deterministic
+// values in memory unused by the linker, so we'll always detect
+// an uninitialized config structure after an update.
+//
+struct NVM
+{
+public:
+    // checksum - we use this to determine if the flash record
+    // has been properly initialized
+    uint32_t checksum;
+
+    // signature and version reference values
+    static const uint32_t SIGNATURE = 0x4D4A522A;
+    static const uint16_t VERSION = 0x0003;
+    
+    // Is the data structure valid?  We test the signature and 
+    // checksum to determine if we've been properly stored.
+    int valid() const
+    {
+        return (d.sig == SIGNATURE 
+                && d.vsn == VERSION
+                && d.sz == sizeof(NVM)
+                && checksum == CRC32(&d, sizeof(d)));
+    }
+    
+    // save to non-volatile memory
+    void save(FreescaleIAP &iap, int addr)
+    {
+        // update the checksum and structure size
+        d.sig = SIGNATURE;
+        d.vsn = VERSION;
+        d.sz = sizeof(NVM);
+        checksum = CRC32(&d, sizeof(d));
+        
+        // figure the number of sectors required
+        int sectors = (sizeof(NVM) + SECTOR_SIZE - 1) / SECTOR_SIZE;
+        for (int i = 0 ; i < sectors ; ++i)
+            iap.erase_sector(addr + i*SECTOR_SIZE);
+
+        // save the data
+        iap.program_flash(addr, this, sizeof(*this));
+    }
+    
+    // stored data (excluding the checksum)
+    struct
+    {
+        // Signature, structure version, and structure size, as further
+        // verification that we have valid data.
+        uint32_t sig;
+        uint16_t vsn;
+        int sz;
+        
+        // configuration and calibration data
+        Config c;
+    } d;
+};
+
+#endif /* NVM_M */