From edbb5fe9d046fecddbec6f39ec0b142accd3d1b2 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Sat, 11 Jul 2020 23:46:11 +0200 Subject: [PATCH 1/9] cpu/samd5x: add NVM User Page Mapping --- cpu/samd5x/include/periph_cpu.h | 37 +++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/cpu/samd5x/include/periph_cpu.h b/cpu/samd5x/include/periph_cpu.h index 21b80c7cdf5d..1d6e00214705 100644 --- a/cpu/samd5x/include/periph_cpu.h +++ b/cpu/samd5x/include/periph_cpu.h @@ -125,6 +125,43 @@ static const gpio_t rtc_tamper_pins[RTC_NUM_OF_TAMPERS] = { GPIO_PIN(PC, 0), GPIO_PIN(PC, 1) }; +/** + * @brief NVM User Page Mapping - Dedicated Entries + * Config values will be applied at power-on. + * @{ + */ +struct sam0_aux_cfg_mapping { + /* config word 0 */ + uint32_t bod33_disable : 1; /**< BOD33 Disable at power-on. */ + uint32_t bod33_level : 8; /**< BOD33 threshold level at power-on. */ + uint32_t bod33_action : 2; /**< BOD33 Action at power-on. */ + uint32_t bod33_hysteresis : 4; /**< BOD33 Hysteresis configuration */ + const uint32_t bod12_calibration : 11; /**< Factory settings - do not change. */ + uint32_t nvm_boot_size : 4; /**< NVM Bootloader Size */ + uint32_t reserved_0 : 2; /**< Factory settings - do not change. */ + /* config word 1 */ + uint32_t smart_eeprom_blocks : 4; /**< NVM Blocks per SmartEEPROM sector */ + uint32_t smart_eeprom_page_size : 3; /**< SmartEEPROM Page Size */ + uint32_t ram_eccdis : 1; /**< RAM ECC Disable */ + uint32_t reserved_1 : 8; /**< Factory settings - do not change. */ + uint32_t wdt_enable : 1; /**< WDT Enable at power-on. */ + uint32_t wdt_always_on : 1; /**< WDT Always-On at power-on. */ + uint32_t wdt_period : 4; /**< WDT Period at power-on. */ + uint32_t wdt_window : 4; /**< WDT Window at power-on. */ + uint32_t wdt_ewoffset : 4; /**< WDT Early Warning Interrupt Offset */ + uint32_t wdt_window_enable : 1; /**< WDT Window mode enabled on power-on */ + uint32_t reserved_2 : 1; /**< Factory settings - do not change. */ + /* config word 2 */ + uint32_t nvm_locks; /**< NVM Region Lock Bits. */ + /* config word 3 */ + uint32_t user_page; /**< User page */ + /* config word 4 */ + uint32_t reserved_3; /**< Factory settings - do not change. */ + /* config words 5,6,7 */ + uint32_t user_pages[3]; /**< User pages */ +}; +/** @} */ + #ifdef __cplusplus } #endif From c5f87428770c3f2598e74984e203b1a097d5f2c6 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Sat, 11 Jul 2020 23:59:07 +0200 Subject: [PATCH 2/9] cpu/samd21: add NVM User Page Mapping --- cpu/samd21/include/periph_cpu.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/cpu/samd21/include/periph_cpu.h b/cpu/samd21/include/periph_cpu.h index cd1c76d1b19d..e52e21d2c604 100644 --- a/cpu/samd21/include/periph_cpu.h +++ b/cpu/samd21/include/periph_cpu.h @@ -122,6 +122,33 @@ typedef enum { #define RTT_MAX_FREQUENCY (RTT_CLOCK_FREQUENCY) /* in Hz */ /** @} */ +/** + * @brief NVM User Row Mapping - Dedicated Entries + * Config values will be applied at power-on. + * @{ + */ +struct sam0_aux_cfg_mapping { + uint64_t bootloader_size : 3; /**< BOOTPROT: Bootloader Size */ + uint64_t reserved_0 : 1; /**< Factory settings - do not change. */ + uint64_t eeprom_size : 3; /**< one of eight different EEPROM sizes */ + uint64_t reserved_1 : 1; /**< Factory settings - do not change. */ + uint64_t bod33_level : 6; /**< BOD33 threshold level at power-on. */ + uint64_t bod33_enable : 1; /**< BOD33 Enable at power-on. */ + uint64_t bod33_action : 2; /**< BOD33 Action at power-on. */ + uint64_t reserved_2 : 8; /**< Factory settings - do not change. */ + uint64_t wdt_enable : 1; /**< WDT Enable at power-on. */ + uint64_t wdt_always_on : 1; /**< WDT Always-On at power-on. */ + uint64_t wdt_period : 4; /**< WDT Period at power-on. */ + uint64_t wdt_window : 4; /**< WDT Window at power-on. */ + uint64_t wdt_ewoffset : 4; /**< WDT Early Warning Interrupt Offset */ + uint64_t wdt_window_enable : 1; /**< WDT Window mode enabled on power-on */ + uint64_t bod33_hysteresis : 1; /**< BOD33 Hysteresis configuration */ + const uint64_t bod12_calibration : 1; /**< Factory settings - do not change. */ + uint64_t reserved_3 : 6; /**< Factory settings - do not change. */ + uint64_t nvm_locks : 16; /**< NVM Region Lock Bits. */ +}; +/** @} */ + #ifdef __cplusplus } #endif From fecc5bdcff5a84a13a06a327b437a10069408747 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Sun, 12 Jul 2020 16:15:59 +0200 Subject: [PATCH 3/9] cpu/saml21: add NVM User Page Mapping --- cpu/saml21/include/periph_cpu.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/cpu/saml21/include/periph_cpu.h b/cpu/saml21/include/periph_cpu.h index 9071ea409924..e383212ac3e0 100644 --- a/cpu/saml21/include/periph_cpu.h +++ b/cpu/saml21/include/periph_cpu.h @@ -85,6 +85,32 @@ typedef enum { #define RTT_MIN_OFFSET (8U) /** @} */ +/** + * @brief NVM User Row Mapping - Dedicated Entries + * Config values will be applied at power-on. + * @{ + */ +struct sam0_aux_cfg_mapping { + uint64_t bootloader_size : 3; /**< BOOTPROT: Bootloader Size */ + uint64_t reserved_0 : 1; /**< Factory settings - do not change. */ + uint64_t eeprom_size : 3; /**< one of eight different EEPROM sizes */ + uint64_t reserved_1 : 1; /**< Factory settings - do not change. */ + uint64_t bod33_level : 6; /**< BOD33 threshold level at power-on. */ + uint64_t bod33_enable : 1; /**< BOD33 Enable at power-on. */ + uint64_t bod33_action : 2; /**< BOD33 Action at power-on. */ + uint64_t reserved_2 : 9; /**< Factory settings - do not change. */ + uint64_t wdt_enable : 1; /**< WDT Enable at power-on. */ + uint64_t wdt_always_on : 1; /**< WDT Always-On at power-on. */ + uint64_t wdt_period : 4; /**< WDT Period at power-on. */ + uint64_t wdt_window : 4; /**< WDT Window at power-on. */ + uint64_t wdt_ewoffset : 4; /**< WDT Early Warning Interrupt Offset */ + uint64_t wdt_window_enable : 1; /**< WDT Window mode enabled on power-on */ + uint64_t bod33_hysteresis : 1; /**< BOD33 Hysteresis configuration */ + uint64_t reserved_3 : 6; /**< Factory settings - do not change. */ + uint64_t nvm_locks : 16; /**< NVM Region Lock Bits. */ +}; +/** @} */ + #ifdef __cplusplus } #endif From 360c0b27fccbeab4c65155d03a93a0cb2a60a681 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Sun, 12 Jul 2020 16:42:34 +0200 Subject: [PATCH 4/9] cpu/saml1x: add NVM User Page Mapping --- cpu/saml1x/include/periph_cpu.h | 49 +++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/cpu/saml1x/include/periph_cpu.h b/cpu/saml1x/include/periph_cpu.h index 2429ae464838..6a060ef98a55 100644 --- a/cpu/saml1x/include/periph_cpu.h +++ b/cpu/saml1x/include/periph_cpu.h @@ -91,6 +91,55 @@ static const gpio_t rtc_tamper_pins[RTC_NUM_OF_TAMPERS] = { GPIO_PIN(PA, 17) }; +/** + * @brief NVM User Row Mapping - Dedicated Entries + * Config values will be applied at power-on. + * @{ + */ +struct sam0_aux_cfg_mapping { + /* config word 0 */ + uint32_t secure_region_unlock : 3; /**< NVM Secure Region UnLock Bits */ + uint32_t non_secure_region_unlock : 3; /**< NVM Non-Secure Region UnLock Bits */ + uint32_t reserved_0 : 1; /**< Reserved */ + uint32_t bod33_level : 6; /**< BOD33 threshold level at power-on. */ + uint32_t bod33_disable : 1; /**< BOD33 Disable at power-on. */ + uint32_t bod33_action : 2; /**< BOD33 Action at power-on. */ + const uint32_t bod12_calibration : 9; /**< Factory settings - do not change. */ + uint32_t wdt_run_standby : 1; /**< WDT Runstdby at power-on */ + uint32_t wdt_enable : 1; /**< WDT Enable at power-on. */ + uint32_t wdt_always_on : 1; /**< WDT Always-On at power-on. */ + uint32_t wdt_period : 4; /**< WDT Period at power-on. */ + /* config word 1 */ + uint32_t wdt_window : 4; /**< WDT Window at power-on. */ + uint32_t wdt_ewoffset : 4; /**< WDT Early Warning Interrupt Offset */ + uint32_t wdt_window_enable : 1; /**< WDT Window mode enabled on power-on */ + uint32_t bod33_hysteresis : 1; /**< BOD33 Hysteresis configuration */ + uint32_t reserved_1 : 1; /**< Reserved */ + uint32_t ram_execute_never : 1; /**< RAM is eXecute Never */ + uint32_t data_execute_never : 1; /**< Data Flash is eXecute Never */ + uint32_t reserved_2 : 19; /**< Reserved */ + /* config word 2 */ + uint32_t secure_flash_as_size : 8; /**< Secure Flash (AS region) Size = AS*0x100 */ + uint32_t nsc_size : 6; /**< Non-Secure Callable Flash (APPLICATION region) Size = ANSC*0x20 */ + uint32_t reserved_3 : 2; /**< Reserved */ + uint32_t secure_flash_data_size : 4; /**< Secure Data Flash Size = DS*0x100 */ + uint32_t reserved_4 : 4; /**< Reserved */ + uint32_t secure_ram_size : 7; /**< Secure SRAM Size = RS*0x80 */ + uint32_t reserved_5 : 1; /**< Reserved */ + /* config word 3 */ + uint32_t user_row_write_enable : 1; /**< User Row Write Enable */ + uint32_t reserved_6 : 31; /**< Reserved */ + /* config word 4 */ + uint32_t nonsec_a; /**< Peripherals Non-Secure Status Fuses for Bridge A */ + /* config word 5 */ + uint32_t nonsec_b; /**< Peripherals Non-Secure Status Fuses for Bridge B */ + /* config word 6 */ + uint32_t nonsec_c; /**< Peripherals Non-Secure Status Fuses for Bridge C */ + /* config word 7 */ + uint32_t user_crc; /**< CRC of NVM User Row bits 223:64 (words 2…6) */ +}; +/** @} */ + #ifdef __cplusplus } #endif From 20f093ede6dc60aa0fccac8c6db1ca7ff9cfab9e Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Tue, 14 Jul 2020 08:57:27 +0200 Subject: [PATCH 5/9] cpu/sam0_common: flashpage: introduce functions to write to user page --- cpu/sam0_common/include/periph_cpu_common.h | 72 +++++++++++++++++++++ cpu/sam0_common/periph/flashpage.c | 65 +++++++++++++++++++ 2 files changed, 137 insertions(+) diff --git a/cpu/sam0_common/include/periph_cpu_common.h b/cpu/sam0_common/include/periph_cpu_common.h index 96e77e3d0292..c348f69c46ab 100644 --- a/cpu/sam0_common/include/periph_cpu_common.h +++ b/cpu/sam0_common/include/periph_cpu_common.h @@ -1095,6 +1095,78 @@ int rtc_tamper_register(gpio_t pin, gpio_flank_t flank); void rtc_tamper_enable(void); /** @} */ +/** + * @name sam0 User Configuration + * + * The MCUs of this family contain a region of memory that is used to store + * CPU configuration & calibration data. + * It can be used to set persistent settings and has some additional space + * to store user configuration data. + * @{ + */ + +/** + * @brief MCU configuration applied on start. The contents of this struct differ + * between families. + */ +typedef struct sam0_aux_cfg_mapping nvm_user_page_t; + +/** + * @brief Size of the free to use auxiliary area in the user page + */ +#ifdef FLASH_USER_PAGE_SIZE +#define FLASH_USER_PAGE_AUX_SIZE (FLASH_USER_PAGE_SIZE - sizeof(nvm_user_page_t)) +#else +#define FLASH_USER_PAGE_AUX_SIZE (AUX_PAGE_SIZE * AUX_NB_OF_PAGES - sizeof(nvm_user_page_t)) +#endif + +/** + * @brief Reset the configuration area, apply a new configuration. + * + * + * @param cfg New MCU configuration, may be NULL. + * If cfg is NULL, this will clear the configuration area + * and apply the current configuration again. + */ +void sam0_flashpage_aux_reset(const nvm_user_page_t *cfg); + +/** + * @brief Write data to the user configuration area. + * This will write data to the remaining space after @see nvm_user_page_t + * The size of this area depends on the MCU family used. + * + * Will only write bits 1 -> 0. To reset bits to 1, call @see sam0_flashpage_aux_reset + * This will reset the whole user area configuration. + * + * Arbitrary data lengths and offsets are supported. + * + * @param offset Byte offset after @see nvm_user_page_t + * must be less than `FLASH_USER_PAGE_AUX_SIZE` + * @param data The data to write + * @param len Size of the data + */ +void sam0_flashpage_aux_write_raw(uint32_t offset, const void *data, size_t len); + +/** + * @brief Get pointer to data in the user configuration area. + * + * @param offset Byte offset after @see nvm_user_page_t + * must be less than `FLASH_USER_PAGE_AUX_SIZE` + * @return Pointer to the data in the User Page + */ +#define sam0_flashpage_aux_get(offset) \ + (const void*)((uint8_t*)NVMCTRL_USER + sizeof(nvm_user_page_t) + (offset)) + +/** + * @brief Get pointer to data in the CPU configuration struct + * + * @return Pointer to the @ref nvm_user_page_t structure + */ +#define sam0_flashpage_aux_cfg() \ + ((const nvm_user_page_t*)NVMCTRL_USER) + +/** @} */ + #ifdef __cplusplus } #endif diff --git a/cpu/sam0_common/periph/flashpage.c b/cpu/sam0_common/periph/flashpage.c index f3517a60068c..fa6a40bcc958 100644 --- a/cpu/sam0_common/periph/flashpage.c +++ b/cpu/sam0_common/periph/flashpage.c @@ -27,6 +27,7 @@ */ #include +#include #include "cpu.h" #include "periph/flashpage.h" @@ -36,6 +37,15 @@ #define MIN(x, y) (((x) < (y)) ? (x) : (y)) +/* Write Quad Word is the only allowed operation on AUX pages */ +#if defined(NVMCTRL_CTRLB_CMD_WQW) +#define AUX_CHUNK_SIZE (4 * sizeof(uint32_t)) +#elif defined(AUX_PAGE_SIZE) +#define AUX_CHUNK_SIZE AUX_PAGE_SIZE +#else +#define AUX_CHUNK_SIZE FLASH_USER_PAGE_SIZE +#endif + /** * @brief NVMCTRL selection macros */ @@ -87,6 +97,21 @@ static void _cmd_clear_page_buffer(void) #endif } +static void _cmd_erase_aux(void) +{ + wait_nvm_is_ready(); + + /* send Erase Page/Auxiliary Row command */ +#if defined(NVMCTRL_CTRLB_CMD_EP) + _NVMCTRL->CTRLB.reg = (NVMCTRL_CTRLB_CMDEX_KEY | NVMCTRL_CTRLB_CMD_EP); +#elif defined(NVMCTRL_CTRLA_CMD_EAR) + _NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_EAR); +#else + /* SAML1x uses same command for all areas */ + _NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER); +#endif +} + static void _cmd_erase_row(void) { wait_nvm_is_ready(); @@ -99,6 +124,21 @@ static void _cmd_erase_row(void) #endif } +static void _cmd_write_aux(void) +{ + wait_nvm_is_ready(); + + /* write auxiliary page */ +#if defined(NVMCTRL_CTRLA_CMD_WAP) + _NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WAP); +#elif defined(NVMCTRL_CTRLB_CMD_WQW) + _NVMCTRL->CTRLB.reg = (NVMCTRL_CTRLB_CMDEX_KEY | NVMCTRL_CTRLB_CMD_WQW); +#else + /* SAML1x uses same command for all areas */ + _NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP); +#endif +} + static void _cmd_write_page(void) { wait_nvm_is_ready(); @@ -202,7 +242,32 @@ void flashpage_write_raw(void *target_addr, const void *data, size_t len) (CPU_FLASH_BASE + (FLASHPAGE_SIZE * FLASHPAGE_NUMOF))); _write_page(target_addr, data, len, _cmd_write_page); +} + +void sam0_flashpage_aux_write_raw(uint32_t offset, const void *data, size_t len) +{ + uintptr_t dst = NVMCTRL_USER + sizeof(nvm_user_page_t) + offset; + +#ifdef FLASH_USER_PAGE_SIZE + assert(dst + len <= NVMCTRL_USER + FLASH_USER_PAGE_SIZE); +#else + assert(dst + len <= NVMCTRL_USER + AUX_PAGE_SIZE * AUX_NB_OF_PAGES); +#endif + + _write_row((void*)dst, data, len, AUX_CHUNK_SIZE, _cmd_write_aux); +} + +void sam0_flashpage_aux_reset(const nvm_user_page_t *cfg) +{ + nvm_user_page_t old_cfg; + + if (cfg == NULL) { + cfg = &old_cfg; + memcpy(&old_cfg, (void*)NVMCTRL_USER, sizeof(*cfg)); + } + _erase_page((void*)NVMCTRL_USER, _cmd_erase_aux); + _write_row((void*)NVMCTRL_USER, cfg, sizeof(*cfg), AUX_CHUNK_SIZE, _cmd_write_aux); } #ifdef FLASHPAGE_RWWEE_NUMOF From e6192a13bda08fc4ddb60836f05fe7d09c18f494 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Tue, 14 Jul 2020 15:21:37 +0200 Subject: [PATCH 6/9] cpu/sam0_common: flashpage: support unaligned writes We can pad the unaligned bytes with 0xFF to make up a whole word that can be written. --- cpu/sam0_common/periph/flashpage.c | 123 ++++++++++++++++++++++------- 1 file changed, 93 insertions(+), 30 deletions(-) diff --git a/cpu/sam0_common/periph/flashpage.c b/cpu/sam0_common/periph/flashpage.c index fa6a40bcc958..2590d97adcce 100644 --- a/cpu/sam0_common/periph/flashpage.c +++ b/cpu/sam0_common/periph/flashpage.c @@ -151,13 +151,78 @@ static void _cmd_write_page(void) #endif } +/* We have to write whole words, but writing 0xFF is basically a no-op + * so fill the unaligned bytes with 0xFF to get a whole extra word. + */ +static uint32_t unaligned_pad_start(const void *_data, uint8_t len) +{ + const uint8_t *data = _data; + union { + uint32_t u32; + uint8_t u8[4]; + } buffer = {.u32 = ~0}; + + switch (len) { + case 3: + buffer.u8[1] = *data++; + /* fall-through */ + case 2: + buffer.u8[2] = *data++; + /* fall-through */ + case 1: + buffer.u8[3] = *data++; + } + + return buffer.u32; +} + +/* We have to write whole words, but writing 0xFF is basically a no-op + * so fill the unaligned bytes with 0xFF to get a whole extra word. + */ +static uint32_t unaligned_pad_end(const void *_data, uint8_t len) +{ + const uint8_t *data = _data; + union { + uint32_t u32; + uint8_t u8[4]; + } buffer = {.u32 = ~0}; + + switch (len) { + case 3: + buffer.u8[2] = data[2]; + /* fall-through */ + case 2: + buffer.u8[1] = data[1]; + /* fall-through */ + case 1: + buffer.u8[0] = data[0]; + } + + return buffer.u32; +} + static void _write_page(void* dst, const void *data, size_t len, void (*cmd_write)(void)) { - uint32_t *dst32 = dst; + /* set bytes in the first, unaligned word */ + uint8_t unaligned_start = (4 - ((uintptr_t)dst & 0x3)) & 0x3; + len -= unaligned_start; + + /* set bytes in the last, unaligned word */ + uint8_t unaligned_end = len & 0x3; + len -= unaligned_end; + + /* word align destination address */ + uint32_t *dst32 = (void*)((uintptr_t)dst & ~0x3); _unlock(); _cmd_clear_page_buffer(); + /* write the first, unaligned bytes */ + if (unaligned_start) { + *dst32++ = unaligned_pad_start(data, unaligned_start); + data = (uint8_t*)data + unaligned_start; + } + /* copy whole words */ const uint32_t *data32 = data; while (len) { @@ -165,6 +230,11 @@ static void _write_page(void* dst, const void *data, size_t len, void (*cmd_writ len -= sizeof(uint32_t); } + /* write the last, unaligned bytes */ + if (unaligned_end) { + *dst32 = unaligned_pad_end(data32, unaligned_end); + } + cmd_write(); _lock(); } @@ -190,21 +260,18 @@ static void _erase_page(void* page, void (*cmd_erase)(void)) _lock(); } -/* dst must be row-aligned */ static void _write_row(uint8_t *dst, const void *_data, size_t len, size_t chunk_size, void (*cmd_write)(void)) { const uint8_t *data = _data; - /* One RIOT page is FLASHPAGE_PAGES_PER_ROW SAM0 flash pages (a row) as - * defined in the file cpu/sam0_common/include/cpu_conf.h, therefore we - * have to split the write into FLASHPAGE_PAGES_PER_ROW raw calls - * underneath, each writing a physical page in chunks of 4 bytes (see - * flashpage_write_raw) - * The erasing is done once as a full row is always erased. - */ + size_t next_chunk = chunk_size - ((uintptr_t)dst & (chunk_size - 1)); + next_chunk = next_chunk ? next_chunk : chunk_size; + while (len) { - size_t chunk = MIN(len, chunk_size); + size_t chunk = MIN(len, next_chunk); + next_chunk = chunk_size; + _write_page(dst, data, chunk, cmd_write); data += chunk; dst += chunk; @@ -222,26 +289,24 @@ void flashpage_write(int page, const void *data) return; } + /* One RIOT page is FLASHPAGE_PAGES_PER_ROW SAM0 flash pages (a row) as + * defined in the file cpu/sam0_common/include/cpu_conf.h, therefore we + * have to split the write into FLASHPAGE_PAGES_PER_ROW raw calls + * underneath, each writing a physical page in chunks of 4 bytes (see + * flashpage_write_raw) + * The erasing is done once as a full row is always erased. + */ _write_row(flashpage_addr(page), data, FLASHPAGE_SIZE, NVMCTRL_PAGE_SIZE, _cmd_write_page); } void flashpage_write_raw(void *target_addr, const void *data, size_t len) { - /* The actual minimal block size for writing is 16B, thus we - * assert we write on multiples and no less of that length. - */ - assert(!(len % FLASHPAGE_RAW_BLOCKSIZE)); - - /* ensure 4 byte aligned writes */ - assert(!(((unsigned)target_addr % FLASHPAGE_RAW_ALIGNMENT) || - ((unsigned)data % FLASHPAGE_RAW_ALIGNMENT))); - /* ensure the length doesn't exceed the actual flash size */ assert(((unsigned)target_addr + len) <= (CPU_FLASH_BASE + (FLASHPAGE_SIZE * FLASHPAGE_NUMOF))); - _write_page(target_addr, data, len, _cmd_write_page); + _write_row(target_addr, data, len, NVMCTRL_PAGE_SIZE, _cmd_write_page); } void sam0_flashpage_aux_write_raw(uint32_t offset, const void *data, size_t len) @@ -300,19 +365,10 @@ static void _cmd_write_page_rwwee(void) void flashpage_rwwee_write_raw(void *target_addr, const void *data, size_t len) { - /* The actual minimal block size for writing is 16B, thus we - * assert we write on multiples and no less of that length. - */ - assert(!(len % FLASHPAGE_RAW_BLOCKSIZE)); - - /* ensure 4 byte aligned writes */ - assert(!(((unsigned)target_addr % FLASHPAGE_RAW_ALIGNMENT) || - ((unsigned)data % FLASHPAGE_RAW_ALIGNMENT))); - assert(((unsigned)target_addr + len) <= (CPU_FLASH_RWWEE_BASE + (FLASHPAGE_SIZE * FLASHPAGE_RWWEE_NUMOF))); - _write_page(target_addr, data, len, _cmd_write_page_rwwee); + _write_row(target_addr, data, len, NVMCTRL_PAGE_SIZE, _cmd_write_page_rwwee); } void flashpage_rwwee_write(int page, const void *data) @@ -325,6 +381,13 @@ void flashpage_rwwee_write(int page, const void *data) return; } + /* One RIOT page is FLASHPAGE_PAGES_PER_ROW SAM0 flash pages (a row) as + * defined in the file cpu/sam0_common/include/cpu_conf.h, therefore we + * have to split the write into FLASHPAGE_PAGES_PER_ROW raw calls + * underneath, each writing a physical page in chunks of 4 bytes (see + * flashpage_write_raw) + * The erasing is done once as a full row is always erased. + */ _write_row(flashpage_rwwee_addr(page), data, FLASHPAGE_SIZE, NVMCTRL_PAGE_SIZE, _cmd_write_page_rwwee); } From 3a20beb53a35cdc64b63a3396debb0721de4937f Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Tue, 25 Aug 2020 18:11:54 +0200 Subject: [PATCH 7/9] tests/periph_flashpage: add test for AUX page --- tests/periph_flashpage/Makefile | 1 + tests/periph_flashpage/app.config.test | 1 + tests/periph_flashpage/main.c | 71 ++++++++++++++++++++++++++ 3 files changed, 73 insertions(+) diff --git a/tests/periph_flashpage/Makefile b/tests/periph_flashpage/Makefile index cb1a1bfa788d..597cc58d9dc5 100644 --- a/tests/periph_flashpage/Makefile +++ b/tests/periph_flashpage/Makefile @@ -5,6 +5,7 @@ FEATURES_REQUIRED += periph_flashpage FEATURES_OPTIONAL += periph_flashpage_raw FEATURES_OPTIONAL += periph_flashpage_rwee +USEMODULE += od USEMODULE += shell # avoid running Kconfig by default diff --git a/tests/periph_flashpage/app.config.test b/tests/periph_flashpage/app.config.test index 7629aaf1196c..04b2ea10334c 100644 --- a/tests/periph_flashpage/app.config.test +++ b/tests/periph_flashpage/app.config.test @@ -1,5 +1,6 @@ # this file enables modules defined in Kconfig. Do not use this file for # application configuration. This is only needed during migration. CONFIG_MODULE_TEST_UTILS_INTERACTIVE_SYNC=y +CONFIG_MODULE_OD=y CONFIG_MODULE_PERIPH_FLASHPAGE=y CONFIG_MODULE_SHELL=y diff --git a/tests/periph_flashpage/main.c b/tests/periph_flashpage/main.c index d59c5682a3b3..a9833b6c1503 100644 --- a/tests/periph_flashpage/main.c +++ b/tests/periph_flashpage/main.c @@ -23,6 +23,7 @@ #include #include +#include "od.h" #include "shell.h" #include "periph/flashpage.h" @@ -114,6 +115,11 @@ static int cmd_info(int argc, char **argv) printf("RWWEE Number of pages:\t%i\n", (int)FLASHPAGE_RWWEE_NUMOF); #endif +#ifdef NVMCTRL_USER + printf("AUX page size:\t%i\n", FLASH_USER_PAGE_AUX_SIZE + sizeof(nvm_user_page_t)); + printf(" user area:\t%i\n", FLASH_USER_PAGE_AUX_SIZE); +#endif + return 0; } @@ -535,6 +541,67 @@ static int cmd_test_last_rwwee_raw(int argc, char **argv) #endif +#ifdef NVMCTRL_USER +static int cmd_dump_config(int argc, char **argv) +{ + (void) argc; + (void) argv; + +#ifdef FLASH_USER_PAGE_SIZE + od_hex_dump((void*)NVMCTRL_USER, FLASH_USER_PAGE_SIZE, 0); +#else + od_hex_dump((void*)NVMCTRL_USER, AUX_PAGE_SIZE * AUX_NB_OF_PAGES, 0); +#endif + + return 0; +} + +static int cmd_test_config(int argc, char **argv) +{ + (void) argc; + (void) argv; + + const uint16_t single_data = 0x1234; + const uint8_t test_data[] = { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE }; + uint32_t dst = FLASH_USER_PAGE_AUX_SIZE - (sizeof(test_data) + 2); + + puts("[START]"); + + sam0_flashpage_aux_reset(NULL); + + /* check if the AUX page has been cleared */ + for (uint32_t i = 0; i < FLASH_USER_PAGE_AUX_SIZE; ++i) { + if (*(uint8_t*)sam0_flashpage_aux_get(i) != 0xFF) { + printf("user page not cleared at offset 0x%"PRIx32"\n", i); + return -1; + } + } + + /* write test data */ + sam0_flashpage_aux_write_raw(dst, test_data, sizeof(test_data)); + + /* write single half-word */ + sam0_flashpage_aux_write_raw(dst + sizeof(test_data), &single_data, sizeof(single_data)); + + /* check if half-word was written correctly */ + uint16_t data_in = *(uint16_t*)sam0_flashpage_aux_get(dst + sizeof(test_data)); + if (data_in != single_data) { + printf("%x != %x, offset = 0x%"PRIx32"\n", single_data, data_in, dst + sizeof(test_data)); + return -1; + } + + /* check if test data was written correctly */ + if (memcmp(sam0_flashpage_aux_get(dst), test_data, sizeof(test_data))) { + printf("write test_data failed, offset = 0x%"PRIx32"\n", dst); + return -1; + } + + puts("[SUCCESS]"); + + return 0; +} +#endif /* NVMCTRL_USER */ + static const shell_command_t shell_commands[] = { { "info", "Show information about pages", cmd_info }, { "dump", "Dump the selected page to STDOUT", cmd_dump }, @@ -559,6 +626,10 @@ static const shell_command_t shell_commands[] = { #ifdef MODULE_PERIPH_FLASHPAGE_RAW { "test_last_rwwee_raw", "Write and verify raw short write on last RWWEE page available", cmd_test_last_rwwee_raw }, #endif +#endif +#ifdef NVMCTRL_USER + { "dump_config_page", "Dump the content of the MCU configuration page", cmd_dump_config }, + { "test_config_page", "Test writing config page. (!DANGER ZONE!)", cmd_test_config }, #endif { NULL, NULL, NULL } }; From 4e8c461f4667c7e3799fafc2c1f22bca5d7506bb Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Wed, 28 Oct 2020 23:34:31 +0100 Subject: [PATCH 8/9] cpu/sam0_common: flashpage: invalidate cache on _lock() --- cpu/sam0_common/periph/flashpage.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cpu/sam0_common/periph/flashpage.c b/cpu/sam0_common/periph/flashpage.c index 2590d97adcce..814415ee05a8 100644 --- a/cpu/sam0_common/periph/flashpage.c +++ b/cpu/sam0_common/periph/flashpage.c @@ -84,6 +84,11 @@ static void _lock(void) #else PAC1->WPSET.reg = PAC1_WPROT_DEFAULT_VAL; #endif + + /* cached flash contents may have changed - invalidate cache */ +#ifdef CMCC + CMCC->MAINT0.bit.INVALL = 1; +#endif } static void _cmd_clear_page_buffer(void) From b9e61fd102faf5c01c83560930958ad7ad85269c Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Tue, 10 Nov 2020 12:53:06 +0100 Subject: [PATCH 9/9] sys/od: add Kconfig --- sys/Kconfig | 1 + sys/od/Kconfig | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 100644 sys/od/Kconfig diff --git a/sys/Kconfig b/sys/Kconfig index 993dcf149788..d7dd3d86bd54 100644 --- a/sys/Kconfig +++ b/sys/Kconfig @@ -15,6 +15,7 @@ rsource "isrpipe/Kconfig" rsource "net/Kconfig" rsource "Kconfig.newlib" rsource "Kconfig.stdio" +rsource "od/Kconfig" rsource "pm_layered/Kconfig" rsource "schedstatistics/Kconfig" rsource "shell/Kconfig" diff --git a/sys/od/Kconfig b/sys/od/Kconfig new file mode 100644 index 000000000000..3859c7b41b58 --- /dev/null +++ b/sys/od/Kconfig @@ -0,0 +1,10 @@ +# Copyright (c) 2020 HAW Hamburg +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + +config MODULE_OD + bool "OD Hex Dump" + select MODULE_FMT + depends on TEST_KCONFIG