Mirror with some correction

Dependencies:   mbed FastIO FastPWM USBDevice

nvm.h

Committer:
mjr
Date:
2016-02-03
Revision:
40:cc0d9814522b
Parent:
35:e959ffba78fd
Child:
59:94eb9265b6d7

File content as of revision 40:cc0d9814522b:

// 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 (or any
// other kind of formal structure) on the KL25Z flash; it simply 
// treats the flash as a raw storage space for linker output and 
// assumes that the linker is the only thing using it.  So ito 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 because 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.
// (And if we get to the point where we actually don't have space
// for our ~1K structure, we'll be up against the limits of the
// device anyway, so we'd have to rein in our ambitions or write
// more efficient code for deeper reasons than sharing this tiny
// sliver of memory.)
//
// 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 */