kopia lustrzana https://github.com/bristol-seds/pico-tracker
[loader] initial implementation of flash checking
rodzic
d7e28bed26
commit
91bfcb9d58
|
@ -25,11 +25,6 @@
|
|||
#ifndef FLASH_H
|
||||
#define FLASH_H
|
||||
|
||||
enum flash_state {
|
||||
FLASH_GOOD, /* checksum matches */
|
||||
FLASH_BAD_CSUM, /* mismatch */
|
||||
};
|
||||
|
||||
enum flash_state check_flash_state(void);
|
||||
uint32_t check_and_repair_memory(void);
|
||||
|
||||
#endif /* FLASH_H */
|
||||
|
|
|
@ -25,36 +25,15 @@
|
|||
#ifndef MEMORY_H
|
||||
#define MEMORY_H
|
||||
|
||||
/**
|
||||
* Memory layout:
|
||||
*
|
||||
* 64-byte pages
|
||||
* 256-byte rows (erase) - 4 pages
|
||||
*/
|
||||
|
||||
#define TOTAL_PAGES 0x100
|
||||
#define TOTAL_ROWS 0x40
|
||||
|
||||
#define PAGE_MASK 0x7FFC0
|
||||
#define ROW_MASK 0x7FF00
|
||||
|
||||
#define PAGE_SIZE 0x00040
|
||||
#define ROW_SIZE 0x00100
|
||||
|
||||
/**
|
||||
* Pages assigned to backlog. Currently 256 records
|
||||
*/
|
||||
#define BACKLOG_START_PAGE 0x00
|
||||
#define BACKLOG_END_PAGE 0xff
|
||||
#define SECTOR_SIZE 0x00100
|
||||
|
||||
|
||||
void mem_chip_erase(void);
|
||||
void mem_read_memory(uint32_t address, uint8_t* buffer, uint32_t length);
|
||||
void mem_write_word(uint32_t address, uint32_t word);
|
||||
void mem_write_page(uint32_t address, uint8_t* buffer, uint16_t length);
|
||||
void mem_erase_sector(uint32_t address);
|
||||
void mem_read_memory(unsigned int* address, uint8_t* buffer, uint32_t length);
|
||||
void mem_write_word(unsigned int* address, uint32_t word);
|
||||
void mem_write_sector(unsigned int* address, uint8_t* buffer);
|
||||
void mem_erase_sector(unsigned int* address);
|
||||
|
||||
uint8_t mem_power_on();
|
||||
void mem_power_off();
|
||||
|
||||
#endif
|
||||
|
|
|
@ -23,16 +23,46 @@
|
|||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "samd20.h"
|
||||
#include "memory.h"
|
||||
#include "flash.h"
|
||||
#include "watchdog.h"
|
||||
|
||||
/**
|
||||
* we put our checksum at the last address in flash
|
||||
*/
|
||||
volatile unsigned int* flash_checksum =
|
||||
(unsigned int*)(FLASH_ADDR + FLASH_SIZE - 4);
|
||||
#define APPLICATION_BASE (0x00004000) /* 16K */
|
||||
#define APPLICATION_LENGTH (112*1024) /* 112K */
|
||||
|
||||
#define D1_START (APPLICATION_BASE)
|
||||
#define D1_SECTORS (APPLICATION_LENGTH/256)
|
||||
#define D2_START (APPLICATION_BASE+APPLICATION_LENGTH)
|
||||
#define D2_SECTORS (D1_SECTORS)
|
||||
|
||||
/* Check these are multiples of 64 */
|
||||
#if (D1_SECTORS & 0x3F)
|
||||
#error D1_SECTORS _must_ be a mul 64, so checksums fill integer no. of sectors
|
||||
#endif
|
||||
#if (D2_SECTORS & 0x3F)
|
||||
#error D2_SECTORS _must_ be a mul 64, so checksums fill integer no. of sectors
|
||||
#endif
|
||||
|
||||
#define D1_CHECKSUMS_SECTORS (D1_SECTORS/64)
|
||||
#define D2_CHECKSUMS_SECTORS (D2_SECTORS/64)
|
||||
|
||||
/* ROM copy of checksums */
|
||||
const uint32_t d1_checksums_nvm[D1_SECTORS]
|
||||
__attribute__ ((aligned (256)))
|
||||
= { 0xFF };
|
||||
const uint32_t d2_checksums_nvm[D2_SECTORS]
|
||||
__attribute__ ((aligned (256)))
|
||||
= { 0xFF };
|
||||
|
||||
/* RAM copy of checksums */
|
||||
uint32_t d1_checksums_ram[D1_SECTORS];
|
||||
uint32_t d2_checksums_ram[D2_SECTORS];
|
||||
|
||||
/* crc32 calculation */
|
||||
uint32_t table[256] = { 0xFFFFFFFF };
|
||||
|
||||
|
||||
// ---------------------------- crc32cx --------------------------------
|
||||
|
@ -50,15 +80,15 @@ volatile unsigned int* flash_checksum =
|
|||
who got it from Linux Source base,
|
||||
www.gelato.unsw.edu.au/lxr/source/lib/crc32.c, lines 105-111. */
|
||||
|
||||
unsigned int crc32cx(unsigned int *ptr, size_t length)
|
||||
uint32_t crc32cx(unsigned int *ptr, size_t length)
|
||||
{
|
||||
int j;
|
||||
unsigned int byte, crc, mask;
|
||||
unsigned int table[256];
|
||||
uint32_t byte, crc, mask;
|
||||
|
||||
length &= ~0x3; /* must be mutiple of 4 */
|
||||
|
||||
/* Set up the table */
|
||||
if (table[0] == 0xFFFFFFFF) {
|
||||
for (byte = 0; byte <= 255; byte++) {
|
||||
crc = byte;
|
||||
for (j = 7; j >= 0; j--) { // Do eight times.
|
||||
|
@ -67,6 +97,7 @@ unsigned int crc32cx(unsigned int *ptr, size_t length)
|
|||
}
|
||||
table[byte] = crc;
|
||||
}
|
||||
}
|
||||
|
||||
/* Through with table setup, now calculate the CRC. */
|
||||
crc = 0xFFFFFFFF;
|
||||
|
@ -83,34 +114,137 @@ unsigned int crc32cx(unsigned int *ptr, size_t length)
|
|||
}
|
||||
|
||||
/**
|
||||
* 32 bit checksum for the whole memory space
|
||||
* 32 bit checksum for the given sector
|
||||
*/
|
||||
unsigned int checksum_memory(void)
|
||||
uint32_t checksum_sector(unsigned int* sector)
|
||||
{
|
||||
/* do crc */
|
||||
return crc32cx((unsigned int)FLASH_ADDR, /* start */
|
||||
FLASH_SIZE - 4); /* length */
|
||||
return crc32cx((unsigned int*)sector, /* start */
|
||||
SECTOR_SIZE); /* length */
|
||||
}
|
||||
|
||||
/* /\** */
|
||||
/* * checks if memory checksum is good */
|
||||
/* *\/ */
|
||||
/* enum flash_state check_flash_state(void) */
|
||||
/* { */
|
||||
/* unsigned int calculated = checksum_memory(); */
|
||||
|
||||
/* if (*flash_checksum == 0xFFFFFFFF) { /\* not written *\/ */
|
||||
/* /\* write it *\/ */
|
||||
/* mem_write_word((uint32_t)flash_checksum, calculated); */
|
||||
|
||||
/* return FLASH_GOOD; */
|
||||
|
||||
/* } else { /\* written *\/ */
|
||||
/* /\* check it *\/ */
|
||||
/* if (calculated == *flash_checksum) { */
|
||||
/* return FLASH_GOOD; */
|
||||
/* } else { */
|
||||
/* return FLASH_BAD_CSUM; */
|
||||
/* } */
|
||||
/* } */
|
||||
/* } */
|
||||
|
||||
|
||||
/**
|
||||
* checks if memory checksum is good
|
||||
* updates checksum records in nvm
|
||||
*/
|
||||
enum flash_state check_flash_state(void)
|
||||
void update_checksums(const uint32_t* nvm, uint32_t* ram, int sectors)
|
||||
{
|
||||
unsigned int calculated = checksum_memory();
|
||||
int i;
|
||||
|
||||
if (*flash_checksum == 0xFFFFFFFF) { /* not written */
|
||||
/* write it */
|
||||
mem_write_word((uint32_t)flash_checksum, calculated);
|
||||
for (i = 0; i < sectors; i++,
|
||||
nvm += SECTOR_SIZE, ram += SECTOR_SIZE) {
|
||||
mem_erase_sector((unsigned int*)nvm);
|
||||
mem_write_sector((unsigned int*)nvm, (uint8_t*)ram);
|
||||
|
||||
return FLASH_GOOD;
|
||||
|
||||
} else { /* written */
|
||||
/* check it */
|
||||
if (calculated == *flash_checksum) {
|
||||
return FLASH_GOOD;
|
||||
} else {
|
||||
return FLASH_BAD_CSUM;
|
||||
}
|
||||
kick_the_watchdog();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks and repairs application memory space
|
||||
*/
|
||||
uint32_t check_and_repair_memory(void)
|
||||
{
|
||||
unsigned int* address1 = (unsigned int*)D1_START;
|
||||
unsigned int* address2 = (unsigned int*)D2_START;
|
||||
unsigned int check1, check2;
|
||||
uint8_t update_d1_check = 0, update_d2_check = 0;
|
||||
uint32_t errors_found = 0;
|
||||
int i;
|
||||
|
||||
/* load checksums */
|
||||
memcpy(d1_checksums_ram, d1_checksums_nvm, D1_SECTORS * sizeof(uint32_t));
|
||||
memcpy(d2_checksums_ram, d2_checksums_nvm, D2_SECTORS * sizeof(uint32_t));
|
||||
|
||||
/* foreach sector */
|
||||
for (i = 0; i < D1_SECTORS; i++,
|
||||
address1 += SECTOR_SIZE, address2 += SECTOR_SIZE) {
|
||||
/* calculate checksums */
|
||||
check1 = checksum_sector(address1);
|
||||
check2 = checksum_sector(address2);
|
||||
|
||||
if ((check1 == d1_checksums_ram[i]) &&
|
||||
(check2 == d2_checksums_ram[i])) {
|
||||
/* all good */
|
||||
continue;
|
||||
|
||||
} else if ((check1 != d1_checksums_ram[i]) &&
|
||||
(check2 == d2_checksums_ram[i])) {
|
||||
/* restore d1 */
|
||||
mem_erase_sector(address1);
|
||||
mem_write_sector(address1, (uint8_t*)address2);
|
||||
|
||||
/* update d1 checksum */
|
||||
d1_checksums_ram[i] = check2;
|
||||
|
||||
/* flag checksum for write */
|
||||
update_d1_check = 1;
|
||||
errors_found++;
|
||||
|
||||
} else if ((check1 == d1_checksums_ram[i]) &&
|
||||
(check2 != d2_checksums_ram[i])) {
|
||||
/* restore d2 */
|
||||
mem_erase_sector(address2);
|
||||
mem_write_sector(address2, (uint8_t*)address1);
|
||||
|
||||
/* update d2 checksum */
|
||||
d2_checksums_ram[i] = check1;
|
||||
|
||||
/* flag checksum for write */
|
||||
update_d2_check = 1;
|
||||
errors_found++;
|
||||
|
||||
} else {
|
||||
/* both bad, restore d2 and calculate both checksums */
|
||||
/* restore d2 */
|
||||
mem_erase_sector(address2);
|
||||
mem_write_sector(address2, (uint8_t*)address1);
|
||||
|
||||
/* update both checksums */
|
||||
d1_checksums_ram[i] = check1;
|
||||
d2_checksums_ram[i] = check1;
|
||||
|
||||
/* flag checksums for write */
|
||||
update_d1_check = 1;
|
||||
update_d2_check = 1;
|
||||
/* don't count these as errors, probably happen when
|
||||
programming for first time */
|
||||
}
|
||||
|
||||
kick_the_watchdog();
|
||||
}
|
||||
|
||||
/* update checksums */
|
||||
if (update_d1_check) { update_checksums(d1_checksums_nvm,
|
||||
d1_checksums_ram,
|
||||
D1_CHECKSUMS_SECTORS); }
|
||||
if (update_d2_check) { update_checksums(d2_checksums_nvm,
|
||||
d2_checksums_ram,
|
||||
D2_CHECKSUMS_SECTORS); }
|
||||
|
||||
return errors_found;
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "analogue.h"
|
||||
#include "xosc.h"
|
||||
#include "loader.h"
|
||||
#include "flash.h"
|
||||
|
||||
/**
|
||||
* MAIN
|
||||
|
@ -56,11 +57,11 @@ int main(void)
|
|||
}
|
||||
|
||||
/* Check battery */
|
||||
} while (get_battery() < 4.0);
|
||||
} while (get_battery() < 3.0);
|
||||
|
||||
|
||||
/* Check and repair memory */
|
||||
|
||||
check_and_repair_memory();
|
||||
|
||||
/* Transfer control to application */
|
||||
transfer_to_application();
|
||||
|
|
|
@ -33,15 +33,6 @@
|
|||
*/
|
||||
#define FIX_ERRATA_REV_C_FLASH_10804
|
||||
|
||||
#define MEM_SIZE 16384 /* 16 KB */
|
||||
/**
|
||||
* Allocate a 16KB section of flash memory, aligned to an NVM row
|
||||
*/
|
||||
const uint8_t nvm_section[MEM_SIZE]
|
||||
__attribute__ ((aligned (256)))
|
||||
__attribute__ ((section (".eeprom")))
|
||||
= { 0xFF };
|
||||
|
||||
/**
|
||||
* Poll the status register until the busy bit is cleared
|
||||
*/
|
||||
|
@ -57,47 +48,17 @@ void _mem_wait_for_done(void)
|
|||
* =============================================================================
|
||||
*/
|
||||
|
||||
/**
|
||||
* Simple Commands
|
||||
*/
|
||||
void mem_chip_erase(void)
|
||||
{
|
||||
#ifdef FIX_ERRATA_REV_C_FLASH_10804
|
||||
/* save CTRLB and disable cache */
|
||||
uint32_t temp = NVMCTRL->CTRLB.reg;
|
||||
NVMCTRL->CTRLB.reg |= NVMCTRL_CTRLB_CACHEDIS;
|
||||
#endif
|
||||
|
||||
/* erase each row */
|
||||
for (int n = 0; n < TOTAL_ROWS; n++) {
|
||||
/* write address */
|
||||
NVMCTRL->ADDR.reg = (uint32_t)(nvm_section + (n*ROW_SIZE)) >> 1;
|
||||
/* unlock */
|
||||
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_UR;
|
||||
/* erase */
|
||||
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER;
|
||||
_mem_wait_for_done();
|
||||
/* lock */
|
||||
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_LR;
|
||||
}
|
||||
|
||||
#ifdef FIX_ERRATA_REV_C_FLASH_10804
|
||||
/* restore CTRLB */
|
||||
NVMCTRL->CTRLB.reg = temp;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Read memory
|
||||
*/
|
||||
void mem_read_memory(uint32_t address, uint8_t* buffer, uint32_t length)
|
||||
void mem_read_memory(unsigned int* address, uint8_t* buffer, uint32_t length)
|
||||
{
|
||||
memcpy(buffer, nvm_section + address, length);
|
||||
memcpy(buffer, address, length);
|
||||
}
|
||||
/**
|
||||
* Write single word
|
||||
*/
|
||||
void mem_write_word(uint32_t address, uint32_t word)
|
||||
void mem_write_word(unsigned int* address, uint32_t word)
|
||||
{
|
||||
#ifdef FIX_ERRATA_REV_C_FLASH_10804
|
||||
/* save CTRLB and disable cache */
|
||||
|
@ -123,21 +84,28 @@ void mem_write_word(uint32_t address, uint32_t word)
|
|||
#endif
|
||||
}
|
||||
/**
|
||||
* Write 64-byte page. Address should be page aligned
|
||||
* Write 256-byte sector. Address must be sector aligned
|
||||
*/
|
||||
void mem_write_page(uint32_t address, uint8_t* buffer, uint16_t length)
|
||||
void mem_write_sector(unsigned int* address, uint8_t* buffer)
|
||||
{
|
||||
int i;
|
||||
uint8_t tempbuf[0x40];
|
||||
|
||||
#ifdef FIX_ERRATA_REV_C_FLASH_10804
|
||||
/* save CTRLB and disable cache */
|
||||
uint32_t temp = NVMCTRL->CTRLB.reg;
|
||||
NVMCTRL->CTRLB.reg |= NVMCTRL_CTRLB_CACHEDIS;
|
||||
#endif
|
||||
|
||||
if ((address < MEM_SIZE) && (length <= PAGE_SIZE)) {
|
||||
/* align address with sector */
|
||||
address = (unsigned int*)((uint32_t)address & ~0xFF);
|
||||
|
||||
for (i = 0; i < 4; i++) { /* write by page */
|
||||
/* write address */
|
||||
NVMCTRL->ADDR.reg = (uint32_t)(nvm_section + address) >> 1;
|
||||
/* write data. length must be multiple of two */
|
||||
memcpy((void*)(nvm_section + address), buffer, length & ~0x1);
|
||||
NVMCTRL->ADDR.reg = (uint32_t)(address) >> 1;
|
||||
/* write page. length must be multiple of two */
|
||||
memcpy((void*)tempbuf, buffer, 0x40);
|
||||
memcpy((void*)address, tempbuf, 0x40);
|
||||
/* unlock */
|
||||
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_UR;
|
||||
/* write page */
|
||||
|
@ -145,8 +113,13 @@ void mem_write_page(uint32_t address, uint8_t* buffer, uint16_t length)
|
|||
_mem_wait_for_done();
|
||||
/* lock */
|
||||
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_LR;
|
||||
|
||||
/* next page */
|
||||
address += 0x40;
|
||||
buffer += 0x40;
|
||||
}
|
||||
|
||||
|
||||
#ifdef FIX_ERRATA_REV_C_FLASH_10804
|
||||
/* restore CTRLB */
|
||||
NVMCTRL->CTRLB.reg = temp;
|
||||
|
@ -155,7 +128,7 @@ void mem_write_page(uint32_t address, uint8_t* buffer, uint16_t length)
|
|||
/**
|
||||
* Erase 256-byte sector.
|
||||
*/
|
||||
void mem_erase_sector(uint32_t address)
|
||||
void mem_erase_sector(unsigned int* address)
|
||||
{
|
||||
#ifdef FIX_ERRATA_REV_C_FLASH_10804
|
||||
/* save CTRLB and disable cache */
|
||||
|
@ -163,18 +136,16 @@ void mem_erase_sector(uint32_t address)
|
|||
NVMCTRL->CTRLB.reg |= NVMCTRL_CTRLB_CACHEDIS;
|
||||
#endif
|
||||
|
||||
if (address < MEM_SIZE) {
|
||||
/* write address */
|
||||
NVMCTRL->ADDR.reg = (uint32_t)(nvm_section + address) >> 1;
|
||||
/* unlock */
|
||||
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_UR;
|
||||
_mem_wait_for_done();
|
||||
/* erase row */
|
||||
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER;
|
||||
_mem_wait_for_done();
|
||||
/* lock */
|
||||
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_LR;
|
||||
}
|
||||
/* write address */
|
||||
NVMCTRL->ADDR.reg = (uint32_t)(address) >> 1;
|
||||
/* unlock */
|
||||
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_UR;
|
||||
_mem_wait_for_done();
|
||||
/* erase row */
|
||||
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER;
|
||||
_mem_wait_for_done();
|
||||
/* lock */
|
||||
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_LR;
|
||||
|
||||
#ifdef FIX_ERRATA_REV_C_FLASH_10804
|
||||
/* restore CTRLB */
|
||||
|
|
Ładowanie…
Reference in New Issue