far-far-away-science / hab-v2

High Altitude v2
GNU General Public License v3.0
4 stars 3 forks source link

Migrate Copernicus GPS code to STM32 #24

Closed usaguerrilla closed 8 years ago

usaguerrilla commented 8 years ago

requirements:

recovery data requirements:

recovery record spec:

open items:

usaguerrilla commented 8 years ago

Unlock with wait until write is completed:

/* (1) Wait till no operation is on going */
/* (2) Check if the PELOCK is unlocked */
/* (3) Perform unlock sequence */
while ((FLASH->SR & FLASH_SR_BSY) != 0) /* (1) */
{
    /* For robust implementation, add here time-out management */
}
if ((FLASH->PECR & FLASH_PECR_PELOCK) != 0) /* (2) */
{
    FLASH->PEKEYR = FLASH_PEKEY1; /* (3) */
    FLASH->PEKEYR = FLASH_PEKEY2;
}
usaguerrilla commented 8 years ago

Lock with wait until write is completed:

/* (1) Wait till no operation is on going */
/* (2) Locks the NVM by setting PELOCK in PECR */
while ((FLASH->SR & FLASH_SR_BSY) != 0) /* (1) */
{
    /* For robust implementation, add here time-out management */
}
FLASH->PECR |= FLASH_PECR_PELOCK; /* (2) */
usaguerrilla commented 8 years ago

Write to data EEPROM code example

*(uint8_t *)(DATA_E2_ADDR+i) = DATA_BYTE;
*(uint16_t *)(DATA_E2_ADDR+j) = DATA_16B_WORD;
*(uint32_t *)(DATA_E2_ADDR) = DATA_32B_WORD;

DATA_E2_ADDR is an aligned address in the data EEPROM area. i can be any integer. j must be an even integer.

usaguerrilla commented 8 years ago

Erase to data EEPROM code example

/* (1) Set the ERASE and DATA bits in the FLASH_PECR register
to enable page erasing */
/* (2) Write a 32-bit word value at the desired address
to start the erase sequence */
/* (3) Enter in wait for interrupt. The EOP check is done in the Flash ISR
*/
/* (4) Reset the ERASE and DATA bits in the FLASH_PECR register
to disable the page erase */
FLASH->PECR |= FLASH_PECR_ERASE | FLASH_PECR_DATA; /* (1) */
*(__IO uint32_t *)addr = (uint32_t)0; /* (2) */
__WFI(); /* (3) */
FLASH->PECR &= ~(FLASH_PECR_ERASE | FLASH_PECR_DATA); /* (4) */
Scorillo47 commented 8 years ago

My suggestion would be something along these lines (pseudo-code only - I didn’t add CRC, sequence#, Flash synchronization, etc). Sorry for the Outlook-enforced capitalization:

Bool Write(data) { For(int = 0; I < maxAddressesToBeTried; i++) { addr = PickUpNextLocation(); // alternate banks round-robin, etc. For(int = 0; I < maxRetries; i++) { If (Write(data, addr) == success) { // wait a bit …

                                                            // Read after write to ensure the correct data is there
                                                            Data1 = Read(addr);
                                                            If (Data == Data1)
                                                            {
                                                                            Return true;
                                                            }
                                            }

                                            // Writing failed (bad block?)

                                            // wait a bit more …                                        

                                            // retry writing on the same location maxRetries
                            }
                            // give up on this address. Try writing on the next location (up to maxAddressesToBeTried tries)
            }

            Return false;

}

usaguerrilla commented 8 years ago

recovery algorithm in a nutshell:

Assumptions:

getRecord(address)
{
    result = Record()
    if not readFromEEPROM(address + A, result.sequenceId) or
       not readFromEEPROM(address + B, result.data) or
       not readFromEEPROM(address + C, result.crc)
        result.isValid = false
    else
        result.isValid = true if crc(result.sequenceId, result.data) == result.crc else false
    return result
}

recoverDataFromBank(bankAddress, currentMaxSequenceId)
{
    if not waitUntilBankIsAvailable(TIMEOUT)
        return (none, none)
    data = none
    maxSequenceId = currentMaxSequenceId
    foreach (address in bankAddress)
        currentRecord = getRecord(address)
        if currentRecord.isValid and currentRecord.sequenceId > maxSequenceId
            data = currentRecord.data
            maxSequenceId = currentRecord.sequenceId
    return (data, maxSequenceId)
}

recoverGpsData()
{
    (data, sequenceId) = recoverDataFromBank(Bank1Address, none)
    (data, sequenceId) = recoverDataFromBank(Bank2Address, sequenceId1)
    return (data, sequenceId)
}
usaguerrilla commented 8 years ago

update recovery algorithm in a nutshell:

Assumptions:

writeWasSuccessful()
{
    # check manual more on weather write returns status or we need to read data again to check
    # also need to check what will be read if write failed (in different conditions)
}

getNextAddress(currentAddress)
{
    # need to switch to different EEPROM memory bank (we do round robin of banks on each write)
    # continue from the next available address in a new memory bank (this is a ring buffer with N addresses)
}

EEPROMWriteISR()
{
    if writeWasSuccessful()
        g_data.isSaved = true
    else if g_currentAddress != g_previousAddress
        # try to write data into next bank/address
        updateRecoveryData()
    else
        # we tried all memory cells in all memory banks
        # not much else we can do but to give up (chances of this happening are small)
        previousAttemptFailed = true
}

updateRecoveryData()
{
    if g_previousAttemptFailed
        return
    g_currentAddress = getNextAddress(g_currentAddress)
    if waitUntilBankIsAvailable(g_currentAddress.bank, TIMEOUT)
        writeDataAsync(g_currentAddress, g_data)
    else
        # this will switch to next bank
        g_currentAddress = getNextAddress(g_currentAddress)
        if waitUntilBankIsAvailable(g_currentAddress.bank, TIMEOUT)
            writeDataAsync(g_currentAddress, g_data)
        else
            # we can give up here as main will call us again if current good data wasn't saved
            # or new good data is available (though we should give up trying after several attempts)
            # can use timer as well but Copernicus spits messages even if there is no lock
}

# we just got good data from Copernicus or previous attempt to save data timeout at some point
if not g_data.isSaved
    g_previousAddress = g_currentAddress
    updateRecoveryData()
usaguerrilla commented 8 years ago

This algorithm protects against the following problems:

N - size of a ring buffer in one EEPROM memory bank

(we cycle memory banks to increase EEPROM life time (which can be decreased at high temperatures >100C), potentially we can write same data to both banks but not sure if we really need to, also we can read from one bank while writing to another)

usaguerrilla commented 8 years ago

code migrated. gps backup will be done by #37