Closed usaguerrilla closed 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;
}
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) */
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.
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) */
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;
}
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)
}
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()
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)
code migrated. gps backup will be done by #37
requirements:
recovery data requirements:
recovery record spec:
open items: