[loader] initial implementation of flash checking

main-solar-only
Richard Meadows 2016-08-05 16:10:39 +01:00
rodzic d7e28bed26
commit 91bfcb9d58
5 zmienionych plików z 202 dodań i 122 usunięć

Wyświetl plik

@ -25,11 +25,6 @@
#ifndef FLASH_H #ifndef FLASH_H
#define FLASH_H #define FLASH_H
enum flash_state { uint32_t check_and_repair_memory(void);
FLASH_GOOD, /* checksum matches */
FLASH_BAD_CSUM, /* mismatch */
};
enum flash_state check_flash_state(void);
#endif /* FLASH_H */ #endif /* FLASH_H */

Wyświetl plik

@ -25,36 +25,15 @@
#ifndef MEMORY_H #ifndef MEMORY_H
#define 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 PAGE_SIZE 0x00040
#define ROW_SIZE 0x00100 #define SECTOR_SIZE 0x00100
/**
* Pages assigned to backlog. Currently 256 records
*/
#define BACKLOG_START_PAGE 0x00
#define BACKLOG_END_PAGE 0xff
void mem_chip_erase(void); void mem_read_memory(unsigned int* address, uint8_t* buffer, uint32_t length);
void mem_read_memory(uint32_t address, uint8_t* buffer, uint32_t length); void mem_write_word(unsigned int* address, uint32_t word);
void mem_write_word(uint32_t address, uint32_t word); void mem_write_sector(unsigned int* address, uint8_t* buffer);
void mem_write_page(uint32_t address, uint8_t* buffer, uint16_t length); void mem_erase_sector(unsigned int* address);
void mem_erase_sector(uint32_t address);
uint8_t mem_power_on();
void mem_power_off();
#endif #endif

Wyświetl plik

@ -23,16 +23,46 @@
*/ */
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include "samd20.h" #include "samd20.h"
#include "memory.h" #include "memory.h"
#include "flash.h" #include "flash.h"
#include "watchdog.h"
/** #define APPLICATION_BASE (0x00004000) /* 16K */
* we put our checksum at the last address in flash #define APPLICATION_LENGTH (112*1024) /* 112K */
*/
volatile unsigned int* flash_checksum = #define D1_START (APPLICATION_BASE)
(unsigned int*)(FLASH_ADDR + FLASH_SIZE - 4); #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 -------------------------------- // ---------------------------- crc32cx --------------------------------
@ -50,15 +80,15 @@ volatile unsigned int* flash_checksum =
who got it from Linux Source base, who got it from Linux Source base,
www.gelato.unsw.edu.au/lxr/source/lib/crc32.c, lines 105-111. */ 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; int j;
unsigned int byte, crc, mask; uint32_t byte, crc, mask;
unsigned int table[256];
length &= ~0x3; /* must be mutiple of 4 */ length &= ~0x3; /* must be mutiple of 4 */
/* Set up the table */ /* Set up the table */
if (table[0] == 0xFFFFFFFF) {
for (byte = 0; byte <= 255; byte++) { for (byte = 0; byte <= 255; byte++) {
crc = byte; crc = byte;
for (j = 7; j >= 0; j--) { // Do eight times. 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; table[byte] = crc;
} }
}
/* Through with table setup, now calculate the CRC. */ /* Through with table setup, now calculate the CRC. */
crc = 0xFFFFFFFF; 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 */ /* do crc */
return crc32cx((unsigned int)FLASH_ADDR, /* start */ return crc32cx((unsigned int*)sector, /* start */
FLASH_SIZE - 4); /* length */ 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 */ for (i = 0; i < sectors; i++,
/* write it */ nvm += SECTOR_SIZE, ram += SECTOR_SIZE) {
mem_write_word((uint32_t)flash_checksum, calculated); mem_erase_sector((unsigned int*)nvm);
mem_write_sector((unsigned int*)nvm, (uint8_t*)ram);
return FLASH_GOOD; kick_the_watchdog();
} else { /* written */
/* check it */
if (calculated == *flash_checksum) {
return FLASH_GOOD;
} else {
return FLASH_BAD_CSUM;
}
} }
} }
/**
* 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;
}

Wyświetl plik

@ -33,6 +33,7 @@
#include "analogue.h" #include "analogue.h"
#include "xosc.h" #include "xosc.h"
#include "loader.h" #include "loader.h"
#include "flash.h"
/** /**
* MAIN * MAIN
@ -56,11 +57,11 @@ int main(void)
} }
/* Check battery */ /* Check battery */
} while (get_battery() < 4.0); } while (get_battery() < 3.0);
/* Check and repair memory */ /* Check and repair memory */
check_and_repair_memory();
/* Transfer control to application */ /* Transfer control to application */
transfer_to_application(); transfer_to_application();

Wyświetl plik

@ -33,15 +33,6 @@
*/ */
#define FIX_ERRATA_REV_C_FLASH_10804 #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 * 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 * 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 * 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 #ifdef FIX_ERRATA_REV_C_FLASH_10804
/* save CTRLB and disable cache */ /* save CTRLB and disable cache */
@ -123,21 +84,28 @@ void mem_write_word(uint32_t address, uint32_t word)
#endif #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 #ifdef FIX_ERRATA_REV_C_FLASH_10804
/* save CTRLB and disable cache */ /* save CTRLB and disable cache */
uint32_t temp = NVMCTRL->CTRLB.reg; uint32_t temp = NVMCTRL->CTRLB.reg;
NVMCTRL->CTRLB.reg |= NVMCTRL_CTRLB_CACHEDIS; NVMCTRL->CTRLB.reg |= NVMCTRL_CTRLB_CACHEDIS;
#endif #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 */ /* write address */
NVMCTRL->ADDR.reg = (uint32_t)(nvm_section + address) >> 1; NVMCTRL->ADDR.reg = (uint32_t)(address) >> 1;
/* write data. length must be multiple of two */ /* write page. length must be multiple of two */
memcpy((void*)(nvm_section + address), buffer, length & ~0x1); memcpy((void*)tempbuf, buffer, 0x40);
memcpy((void*)address, tempbuf, 0x40);
/* unlock */ /* unlock */
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_UR; NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_UR;
/* write page */ /* write page */
@ -145,8 +113,13 @@ void mem_write_page(uint32_t address, uint8_t* buffer, uint16_t length)
_mem_wait_for_done(); _mem_wait_for_done();
/* lock */ /* lock */
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_LR; NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_LR;
/* next page */
address += 0x40;
buffer += 0x40;
} }
#ifdef FIX_ERRATA_REV_C_FLASH_10804 #ifdef FIX_ERRATA_REV_C_FLASH_10804
/* restore CTRLB */ /* restore CTRLB */
NVMCTRL->CTRLB.reg = temp; 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. * 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 #ifdef FIX_ERRATA_REV_C_FLASH_10804
/* save CTRLB and disable cache */ /* save CTRLB and disable cache */
@ -163,9 +136,8 @@ void mem_erase_sector(uint32_t address)
NVMCTRL->CTRLB.reg |= NVMCTRL_CTRLB_CACHEDIS; NVMCTRL->CTRLB.reg |= NVMCTRL_CTRLB_CACHEDIS;
#endif #endif
if (address < MEM_SIZE) {
/* write address */ /* write address */
NVMCTRL->ADDR.reg = (uint32_t)(nvm_section + address) >> 1; NVMCTRL->ADDR.reg = (uint32_t)(address) >> 1;
/* unlock */ /* unlock */
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_UR; NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_UR;
_mem_wait_for_done(); _mem_wait_for_done();
@ -174,7 +146,6 @@ void mem_erase_sector(uint32_t address)
_mem_wait_for_done(); _mem_wait_for_done();
/* lock */ /* lock */
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_LR; NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_LR;
}
#ifdef FIX_ERRATA_REV_C_FLASH_10804 #ifdef FIX_ERRATA_REV_C_FLASH_10804
/* restore CTRLB */ /* restore CTRLB */