diff --git a/loader/inc/flash.h b/loader/inc/flash.h index 94cf407..b3e7784 100644 --- a/loader/inc/flash.h +++ b/loader/inc/flash.h @@ -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 */ diff --git a/loader/inc/memory.h b/loader/inc/memory.h index 9de0fa1..e60fb76 100644 --- a/loader/inc/memory.h +++ b/loader/inc/memory.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 diff --git a/loader/src/flash.c b/loader/src/flash.c index b03f647..2d48a13 100644 --- a/loader/src/flash.c +++ b/loader/src/flash.c @@ -23,16 +23,46 @@ */ #include +#include #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; +} diff --git a/loader/src/main.c b/loader/src/main.c index b383a53..1fcf7f6 100644 --- a/loader/src/main.c +++ b/loader/src/main.c @@ -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(); diff --git a/loader/src/memory.c b/loader/src/memory.c index 492cafd..d3ef172 100644 --- a/loader/src/memory.c +++ b/loader/src/memory.c @@ -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 */