System Management code

Dependencies:   mbed CANBuffer Watchdog MODSERIAL mbed-rtos xbeeRelay IAP

Fork of SystemManagement by Martin Deng

DataStructures/DataStructures.cpp

Committer:
pspatel321
Date:
2015-02-11
Revision:
39:ddf38df9699e
Parent:
38:8efacce315ae

File content as of revision 39:ddf38df9699e:

#include "DataStructures.h"
#include "DefaultProfile.h"
#include "IAP.h"

#define HEADER_SECTOR           27                                                                  // Where the NV_Data header information is stored
#define HEADER_PTR              (NV_Data*)((uint32_t*)(sector_start_adress[HEADER_SECTOR]))
#define ID_MARKER               0x5a5a5a5a                                                          // Marker that identifies whether the flash was erased from programming a new .bin file
#define FRAME_SECTOR            28                                                                  // First sector for freeze frame storage (swapspace)
#define FRAME_PTR               (FreezeFrame*)((uint32_t*)(sector_start_adress[FRAME_SECTOR]))
#define PROFILE_SECTOR          29                                                                  // The flash sector where profiles are stored, 29 is the last 32kB size sector in memory
#define PROFILE_START_PTR       (Profile_checkSum*)((uint32_t*)(sector_start_adress[PROFILE_SECTOR]))        // Pointer to the first profile object in flash

// OPERATING DATA OBJECTS
FreezeFrame frame;                            // Current working object in RAM, contains both param and freeze frame
OperatingInfo *op = &(frame.op.op);           // Handle to just the op section
Profile *param = &(frame.param.param);        // Handle to just the param section
TemporaryData tempData;                       // Temporary data object in RAM

IAP iap;                                      // Access to In-Application-Programming --> flash memory programming routines
Profile_checkSum copyBuffer[NUM_STORED_PROFILES];      // Scratch-space in RAM for saving profiles, since flash writes must erase the entire sector and only RAM is usable during flash write

// Non-volatile data holder class
class NV_Data
{
public:
    NV_Data() {
        id = ID_MARKER;
        frameStoredFlag = 0;
        usingProfile = 0;
    }
    int frameStoredFlag;
    int usingProfile;
    
    int id;
};

// Construct new freeze frames with empty OperatingInfo and default Profile
FreezeFrame::FreezeFrame()
{
    memcpy(&(this->param), &defaultProfile, sizeof(Profile));
    memset(&(this->op), 0, sizeof(OperatingInfo));
}

// Helper function to compute checksums using the BSD checksum algorithim for checking validity of flash contents
// Arguments:  start location of the block, size in bytes     Returns:  checksum
uint32_t checksum(const void* ptr, unsigned int size)
{
    const char* start = (char*)(ptr);
    uint32_t checksum = 0;
    for (uint32_t i = 0; i < size; i++) {   // Skip last 4 bytes which holds the checksum itsel
        // BSD checksum algorithm
        checksum = (checksum >> 1) + ((checksum & 1) << 15);
        checksum += start[i];   // Accumulate
        checksum &= 0xffff;     // Time to 16 bits
    }
    return checksum;
}

// Helper function to streamline flash memory prep-erase-prep-write
// Takes start pointer (RAM), size of object to copy, start pointer (flash), and sector number
int flashWrite(void* start, int size, void* flash_start, int sector)
{
    for (int i = 256; i <= 8192; i *= 2) {  // Count by powers of 2
        if (size <= i) {                    // Stop when oversize or equal
            size = i;
            break;
        }
    }
    if (size > 4096) return -1;           // Chunk too big to copy
    if (size == 2048) size = 4096;  // 2048 does not exist, next size up

    __disable_irq();                      // Flash operations are non-interruptable

    // Flash prepare, erase, prepare, write - (r == 0) means successful, non-zero = error code
    int r = iap.prepare(sector, sector);
    if (r == 0) {
        r = iap.erase(sector, sector);
        if (r == 0) {
            r = iap.prepare(sector, sector);
            if (r == 0) {
                r = iap.write((char*)start, (char*)flash_start, size);
            }
        }
    }
    __enable_irq();     // Re-enable interrupts
    return r;           // 0 if success, otherwise see fault codes in LPC1768 User Manual
}
bool setProfile(int index)
{
    bool passed = false;
    if (index == 0) passed = true;
    else if (index == -1) passed = true;
    else if (index <= NUM_STORED_PROFILES && index > 0) passed = true;

    if (!passed) return false;
    NV_Data temp;
    NV_Data* header = HEADER_PTR;
    
    __disable_irq();
    memcpy(&temp, header, sizeof(NV_Data));    // Atomic copy to RAM
    __enable_irq();
    
    temp.usingProfile = index;

    // Write it back to flash
    int i = flashWrite(&temp, sizeof(NV_Data), sector_start_adress[HEADER_SECTOR], HEADER_SECTOR);
    return (i == 0);
}

// Retrieve the index number of the currently active profile (last profile loaded/saved)
int Profile::usingProfile()
{
    NV_Data* header = HEADER_PTR;
    return header->usingProfile;
}

// Startup procedure run on chip reset to fetch last used profile
bool Profile::loadStartUp()
{
    // Check health of the header
    NV_Data *header = HEADER_PTR;
    bool passed=true;
    if (header->id != ID_MARKER) passed = false;
    if (header->usingProfile > NUM_STORED_PROFILES || header->usingProfile < -1) passed = false;
    if (header->frameStoredFlag != 0 && header->frameStoredFlag != ~0) passed = false;

    // Write a new, blank header    
    if (!passed) {
        NV_Data temp;
        int i = flashWrite(&temp, sizeof(NV_Data), sector_start_adress[HEADER_SECTOR], HEADER_SECTOR);
    }
    
    if (loadProfile(usingProfile())) {   // Try to fetch the profile that is currently marked
        return true;
    } else {                            // If fail, revert to default
        loadProfile(0);
        return false;
    }
}

// Attempt to copy profile from flash to RAM object as indicated by int index
bool Profile::loadProfile(int index)
{
    Profile *p;
    if (!getProfile(&p, index)) return false;
    
    __disable_irq();
    memcpy(param, p, sizeof(Profile));
    __enable_irq();

    if (setProfile(index)) return true;
    return false;                        // Bad sum, no copy was done
}

// Attempt to fetch the profile pointer only, no copy to RAM
bool Profile::getProfile(Profile **ptr, int index)
{
    Profile_checkSum *p;

    // If default profile requested
    if (index == 0) {
        *ptr = &defaultProfile;                 // Set pointer to default, return
        return true;

        // If freeze profile requested
    } else if (index == -1) {
        FreezeFrame *freeze;
        if (!FreezeFrame::getFrame(&freeze)) {  // Attempt to fetch frame
            *ptr = NULL;                        // Not available, set NULL
            return false;
        }
        p = &(freeze->param);

        // Profile from flash storage requested
    } else if (index <= NUM_STORED_PROFILES && index > 0) {
        p = PROFILE_START_PTR + (index - 1);  // Set a pointer to the profile

        // Bad command, invalid index
    } else {
        *ptr = NULL;
        return false;
    }
    // Validate contents by computing checksum
    if (p->BSDchecksum == checksum(&(p->param), sizeof(Profile))) {         // Check checksum
        *ptr = &(p->param);           // Fill out the pointer, checksum passed
        return true;
    }
    *ptr = NULL;
    return false;    // Bad checksum, ptr set to NULL
}
// Take the current RAM object contents and save it to flash at location given by index
// Flash write requires the sector to be erased first, so the function will copy out and re-wrtie the profiles
bool Profile::saveProfile(int index)
{
    if (index > NUM_STORED_PROFILES || index <= 0) return false;            // Index invalid
    frame.param.BSDchecksum = checksum(param, sizeof(Profile));             // Add the checksum

    // Copy out all of the old profiles
    for (int i = 0; i < NUM_STORED_PROFILES; i++) {
        if (i == (index - 1)) {     // Exception, replace this slot with the RAM profile
            __disable_irq();
            memcpy(&copyBuffer[i], &(frame.param), sizeof(Profile_checkSum));
            __enable_irq();
            continue;               // Skip to next
        }
        __disable_irq();
        memcpy(&copyBuffer[i], PROFILE_START_PTR+i, sizeof(Profile_checkSum));   // Copy profile from flash to RAM
        __enable_irq();
    }
    int r = flashWrite(copyBuffer, sizeof(copyBuffer), sector_start_adress[PROFILE_SECTOR], PROFILE_SECTOR);

    if (r == 0) {
        return setProfile(index);
    }
    return false;
}

// Fetch pointer to the last written freeze frame object if available, do not copy contents to RAM
bool FreezeFrame::getFrame(FreezeFrame **frame)
{    
    if (FreezeFrame::getError() == false) {
        *frame = NULL;    // No frame available, return
        return false;
    }
    
    FreezeFrame* ptr = FRAME_PTR;                                                               // Set pointer to frame
    uint32_t checksumOp = checksum(&(ptr->op.op), sizeof(OperatingInfo));                       // Compute checksum for operatingInfo section
    uint32_t checksumParam = checksum(&(ptr->param.param), sizeof(Profile));                    // Compute checksum for profile section

    if (checksumOp == ptr->op.BSDchecksum && checksumParam == ptr->param.BSDchecksum) {         // Compare to stored checksum
        *frame = ptr;       // Checksum passed, pass out the ptr
        return true;
    }
    *frame = NULL;          // Checksum failed, write NULL
    return false;
}

// Copy contents of RAM to flash, usually called on error to record the internal state upon enetering fault conditions
bool FreezeFrame::writeFrame()
{
    // Compute and record the checksums
    frame.param.BSDchecksum = checksum(&(frame.param.param), sizeof(Profile));
    frame.op.BSDchecksum = checksum(&(frame.op.op), sizeof(OperatingInfo));

    int r = flashWrite(&frame, sizeof(FreezeFrame), sector_start_adress[FRAME_SECTOR], FRAME_SECTOR);
    if (r == 0) {
        // Set the error flag in the header
        NV_Data temp;
        NV_Data* header = HEADER_PTR;
        __disable_irq();
        memcpy(&temp, header, sizeof(NV_Data));
        __enable_irq();
        temp.frameStoredFlag = ~0;
        int i = flashWrite(&temp, sizeof(NV_Data), sector_start_adress[HEADER_SECTOR], HEADER_SECTOR);
        return (i == 0);
    }
    return false;                               // False if flash operations failed
}

// Get the non-volatile freeze Frame recorded flag that indicates whether a freeze frame is available
bool FreezeFrame::getError()
{
    NV_Data* header = HEADER_PTR;
    return header->frameStoredFlag;
}
// Clear the frame error, for ex. when the frame is read by user
bool FreezeFrame::clearError()
{
    NV_Data temp;
    NV_Data* header = HEADER_PTR;
    __disable_irq();
    memcpy(&temp, header, sizeof(NV_Data));
    __enable_irq();
    temp.frameStoredFlag = 0;
    int i = flashWrite(&temp, sizeof(NV_Data), sector_start_adress[HEADER_SECTOR], HEADER_SECTOR);
    return (i == 0);
}