1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-01-18 12:52:44 +01:00

Merge pull request #10884 from fedepell/sam_rwee_support

sam0 flashpage RWWEE flash support
This commit is contained in:
Dylan Laduranty 2019-03-28 11:00:25 +01:00 committed by GitHub
commit ccf12c57a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 437 additions and 11 deletions

View File

@ -1,6 +1,7 @@
FEATURES_PROVIDED += periph_cpuid
FEATURES_PROVIDED += periph_flashpage
FEATURES_PROVIDED += periph_flashpage_raw
FEATURES_PROVIDED += periph_flashpage_rwee
FEATURES_PROVIDED += periph_gpio periph_gpio_irq
-include $(RIOTCPU)/cortexm_common/Makefile.features

View File

@ -39,6 +39,12 @@ extern "C" {
#define CPU_DEFAULT_IRQ_PRIO (1U)
#define CPU_IRQ_NUMOF PERIPH_COUNT_IRQn
#define CPU_FLASH_BASE FLASH_ADDR
#ifdef CPU_SAML1X
#define CPU_FLASH_RWWEE_BASE DATAFLASH_ADDR
#else
#define CPU_FLASH_RWWEE_BASE NVMCTRL_RWW_EEPROM_ADDR
#endif
/** @} */
/**
@ -59,6 +65,16 @@ extern "C" {
#define FLASHPAGE_RAW_BLOCKSIZE (16)
/* Writing should be always 4 byte aligned */
#define FLASHPAGE_RAW_ALIGNMENT (4)
/* Add RWWEE memory if supported by revision of the chip
* On some chips it is called RWW EEPROM while on some DATAFLASH, try to
* catch all without relying on the CPU model but on the named defines
*/
#ifdef NVMCTRL_RWW_EEPROM_SIZE
#define FLASHPAGE_RWWEE_NUMOF (NVMCTRL_RWWEE_PAGES / FLASHPAGE_PAGES_PER_ROW)
#endif
#ifdef DATAFLASH_SIZE
#define FLASHPAGE_RWWEE_NUMOF (DATAFLASH_NB_OF_PAGES / FLASHPAGE_PAGES_PER_ROW)
#endif
/** @} */
#ifdef __cplusplus

View File

@ -32,6 +32,9 @@
#define NVMCTRL_PAC_BIT (0x00000002)
#define FLASH_MAIN 0
#define FLASH_RWWEE 1
/**
* @brief NVMCTRL selection macros
*/
@ -75,7 +78,11 @@ static void _lock(void)
#endif
}
#ifdef FLASHPAGE_RWWEE_NUMOF
void flashpage_write_raw_internal(void *target_addr, const void *data, size_t len, int flash_type)
#else
void flashpage_write_raw(void *target_addr, const void *data, size_t len)
#endif
{
/* The actual minimal block size for writing is 16B, thus we
* assert we write on multiples and no less of that length.
@ -87,8 +94,17 @@ void flashpage_write_raw(void *target_addr, const void *data, size_t len)
((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)));
#ifdef FLASHPAGE_RWWEE_NUMOF
if (flash_type == FLASH_RWWEE) {
assert(((unsigned)target_addr + len) <=
(CPU_FLASH_RWWEE_BASE + (FLASHPAGE_SIZE * FLASHPAGE_RWWEE_NUMOF)));
} else {
#endif
assert(((unsigned)target_addr + len) <=
(CPU_FLASH_BASE + (FLASHPAGE_SIZE * FLASHPAGE_NUMOF)));
#ifdef FLASHPAGE_RWWEE_NUMOF
}
#endif
uint32_t *dst = (uint32_t *)target_addr;
const uint32_t *data_addr = data;
@ -97,22 +113,46 @@ void flashpage_write_raw(void *target_addr, const void *data, size_t len)
len /= 4;
_unlock();
_NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_PBC);
wait_nvm_is_ready();
for (unsigned i = 0; i < len; i++) {
*dst++ = *data_addr++;
}
_NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP);
#ifdef FLASHPAGE_RWWEE_NUMOF
if (flash_type == FLASH_RWWEE) {
#ifdef CPU_SAML1X
/* SAML1X use the same Write Page command for both flash memories */
_NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP);
#else
_NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_RWWEEWP);
#endif
} else {
#endif
_NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP);
#ifdef FLASHPAGE_RWWEE_NUMOF
}
#endif
wait_nvm_is_ready();
_lock();
}
#ifdef FLASHPAGE_RWWEE_NUMOF
void flashpage_write_internal(int page, const void *data, int flash_type)
#else
void flashpage_write(int page, const void *data)
#endif
{
assert((uint32_t)page < FLASHPAGE_NUMOF);
uint32_t *page_addr;
uint32_t *page_addr = (uint32_t *)flashpage_addr(page);
#ifdef FLASHPAGE_RWWEE_NUMOF
if (flash_type == FLASH_RWWEE) {
page_addr = (uint32_t *)flashpage_rwwee_addr(page);
} else {
#endif
page_addr = (uint32_t *)flashpage_addr(page);
#ifdef FLASHPAGE_RWWEE_NUMOF
}
#endif
/* erase given page (the ADDR register uses 16-bit addresses) */
_unlock();
@ -122,7 +162,20 @@ void flashpage_write(int page, const void *data)
#else
_NVMCTRL->ADDR.reg = (((uint32_t)page_addr) >> 1);
#endif
_NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER);
#ifdef FLASHPAGE_RWWEE_NUMOF
if (flash_type == FLASH_RWWEE) {
#ifdef CPU_SAML1X
/* SAML1X use the same Erase command for both flash memories */
_NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER);
#else
_NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_RWWEEER);
#endif
} else {
#endif
_NVMCTRL->CTRLA.reg = (NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER);
#ifdef FLASHPAGE_RWWEE_NUMOF
}
#endif
wait_nvm_is_ready();
_lock();
@ -136,10 +189,48 @@ void flashpage_write(int page, const void *data)
* The erasing is done once as a full row is always reased.
*/
for (unsigned curpage = 0; curpage < FLASHPAGE_PAGES_PER_ROW; curpage++) {
#ifdef FLASHPAGE_RWWEE_NUMOF
flashpage_write_raw_internal(page_addr + (curpage * NVMCTRL_PAGE_SIZE / 4),
(void *) ((uint32_t *) data + (curpage * NVMCTRL_PAGE_SIZE / 4)),
NVMCTRL_PAGE_SIZE, flash_type);
#else
flashpage_write_raw(page_addr + (curpage * NVMCTRL_PAGE_SIZE / 4),
(void *) ((uint32_t *) data + (curpage * NVMCTRL_PAGE_SIZE / 4)),
NVMCTRL_PAGE_SIZE);
(void *) ((uint32_t *) data + (curpage * NVMCTRL_PAGE_SIZE / 4)),
NVMCTRL_PAGE_SIZE);
#endif
}
}
}
#ifdef FLASHPAGE_RWWEE_NUMOF
/*
* If RWWEE flash is present then we create an additional layer for the write functions
* so we can specify the type (either MAIN or RWWEE) we want to access, keeping the
* standard API unchanged and code for systems without RWWEE at a minimum at the cost
* of some more #defines in the code
*/
void flashpage_write_raw(void *target_addr, const void *data, size_t len)
{
flashpage_write_raw_internal(target_addr, data, len, FLASH_MAIN);
}
void flashpage_write(int page, const void *data)
{
assert((uint32_t)page < FLASHPAGE_NUMOF);
flashpage_write_internal(page, data, FLASH_MAIN);
}
void flashpage_rwwee_write_raw(void *target_addr, const void *data, size_t len)
{
flashpage_write_raw_internal(target_addr, data, len, FLASH_RWWEE);
}
void flashpage_rwwee_write(int page, const void *data)
{
assert((uint32_t)page < FLASHPAGE_RWWEE_NUMOF);
flashpage_write_internal(page, data, FLASH_RWWEE);
}
#endif

View File

@ -31,6 +31,7 @@
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
* @author Kaspar Schleiser <kaspar@schleiser.de>
* @author Francisco Acosta <francisco.acosta@inria.fr>
* @author Federico Pellegrin <fede@evolware.org>
*
*/
@ -39,6 +40,7 @@
#include <stdint.h>
#include "cpu_conf.h"
#include "periph_cpu.h"
#ifdef __cplusplus
@ -190,6 +192,122 @@ int flashpage_verify(int page, const void *data);
*/
int flashpage_write_and_verify(int page, const void *data);
/**
* @brief Functions to support additional RWWEE flash section
*
* Some CPUs (ie. SAML21, SAML11) have an additional separated flash section
* named RWWEE (Read While Write EEPROM Emulation) that can be accessed from
* RIOT in a similar fashion but with a set of functions ending with a
* _rwwee name variant.
*
* The support of this feature is determined by the FLASHPAGE_RWWEE_NUMOF
* define that, if defined, contains the number of RWWEE sectors in the CPU.
* This is defined in the CPU specific headers (ie. for the ATMEL SAM family
* in cpu/sam0_common/include/cpu_conf.h)
*/
#ifdef FLASHPAGE_RWWEE_NUMOF
/**
* @brief Translate the given RWEE page number into the page's starting address
*
* @note The given page MUST be valid, otherwise the returned address points
* to an undefined memory location!
*
* @param[in] page RWWEE page number to get the address of
*
* @return starting memory address of the given RWWEE page
*/
static inline void *flashpage_rwwee_addr(int page)
{
return (void *)(CPU_FLASH_RWWEE_BASE + (page * FLASHPAGE_SIZE));
}
/**
* @brief Translate the given address into the corresponding RWWEE page number
*
* The given address can be any address inside a RWWEE page.
*
* @note The given address MUST be a valid RWWEE flash address!
*
* @param[in] addr address inside the targeted RWWEE page
*
* @return RWWEE page containing the given address
*/
static inline int flashpage_rwwee_page(void *addr)
{
return (int)(((int)addr - CPU_FLASH_RWWEE_BASE) / FLASHPAGE_SIZE);
}
/**
* @brief Write the given RWWEE page with the given data
*
* @param[in] page RWWEE page to write
* @param[in] data data to write to the RWWEE page, MUST be FLASHPAGE_SIZE
* byte. Set to NULL for RWWEE page erase only.
*/
void flashpage_rwwee_write(int page, const void *data);
/**
* @brief Write any number of data bytes to a given location in the
* RWWEE flash memory
*
* @warning Make sure the targeted memory area is erased before calling
* this function
*
* Both target address and data address must be aligned to
* FLASHPAGE_RAW_ALIGN. @p len must be a multiple of FLASHPAGE_RAW_BLOCKSIZE.
* This function doesn't erase any area in flash, thus be sure the targeted
* memory area is erased before writing on it (using the flashpage_write function).
*
* @param[in] target_addr RWWEE address in flash to write to. MUST be aligned
* to FLASHPAGE_RAW_ALIGNMENT.
* @param[in] data data to write to the address. MUST be aligned
* to FLASHPAGE_RAW_ALIGNMENT.
* @param[in] len length of the data to be written. It MUST be
* multiple of FLASHPAGE_RAW_BLOCKSIZE. Also,
* ensure it doesn't exceed the actual RWWEE flash
* memory size.
*/
void flashpage_rwwee_write_raw(void *target_addr, const void *data, size_t len);
/**
* @brief Read the given RWWEE page into the given memory location
*
* @param[in] page RWWEE page to read
* @param[out] data memory to write the RWWEE page to, MUST be FLASHPAGE_SIZE
* byte
*/
void flashpage_rwwee_read(int page, void *data);
/**
* @brief Verify the given RWWEE page against the given data
*
* @param[in] page RWWEE page to verify
* @param[in] data data to compare RWWEE page against, MUST be FLASHPAGE_SIZE
* byte of data
*
* @return FLASHPAGE_OK if data in the RWWEE page is identical to @p data
* @return FLASHPAGE_NOMATCH if data and RWWEE page content diverge
*/
int flashpage_rwwee_verify(int page, const void *data);
/**
* @brief Write the given RWWEE page and verify the results
*
* This is a convenience function wrapping flashpage_rwwee_write and
* flashpage_rwwee_verify.
*
* @param[in] page RWWEE page to write
* @param[in] data data to write to the RWWEE page, MUST be FLASHPAGE_SIZE
* byte.
*
* @return FLASHPAGE_OK on success
* @return FLASHPAGE_NOMATCH if data and RWWEE page content diverge
*/
int flashpage_rwwee_write_and_verify(int page, const void *data);
#endif /* FLASHPAGE_RWWEE_NUMOF */
#ifdef __cplusplus
}
#endif

View File

@ -53,4 +53,34 @@ int flashpage_write_and_verify(int page, const void *data)
return flashpage_verify(page, data);
}
#if defined(FLASHPAGE_RWWEE_NUMOF)
void flashpage_rwwee_read(int page, void *data)
{
assert(page < (int)FLASHPAGE_RWWEE_NUMOF);
memcpy(data, flashpage_rwwee_addr(page), FLASHPAGE_SIZE);
}
int flashpage_rwwee_verify(int page, const void *data)
{
assert(page < (int)FLASHPAGE_RWWEE_NUMOF);
if (memcmp(flashpage_rwwee_addr(page), data, FLASHPAGE_SIZE) == 0) {
return FLASHPAGE_OK;
}
else {
return FLASHPAGE_NOMATCH;
}
}
int flashpage_rwwee_write_and_verify(int page, const void *data)
{
flashpage_rwwee_write(page, data);
return flashpage_rwwee_verify(page, data);
}
#endif
#endif

View File

@ -3,6 +3,7 @@ include ../Makefile.tests_common
FEATURES_REQUIRED += periph_flashpage
FEATURES_OPTIONAL += periph_flashpage_raw
FEATURES_OPTIONAL += periph_flashpage_rwee
USEMODULE += shell

View File

@ -31,7 +31,6 @@
/* When writing raw bytes on flash, data must be correctly aligned. */
#ifdef MODULE_PERIPH_FLASHPAGE_RAW
#define ALIGNMENT_ATTR __attribute__ ((aligned (FLASHPAGE_RAW_ALIGNMENT)))
/*
* @brief Allocate an aligned buffer for raw writings
*/
@ -102,6 +101,11 @@ static int cmd_info(int argc, char **argv)
printf("Page size:\t\t%i\n", (int)FLASHPAGE_SIZE);
printf("Number of pages:\t%i\n", (int)FLASHPAGE_NUMOF);
#ifdef FLASHPAGE_RWWEE_NUMOF
printf("RWWEE Flash start addr:\t0x%08x\n", (int)CPU_FLASH_RWWEE_BASE);
printf("RWWEE Number of pages:\t%i\n", (int)FLASHPAGE_RWWEE_NUMOF);
#endif
return 0;
}
@ -351,6 +355,162 @@ static int cmd_test_last_raw(int argc, char **argv)
}
#endif
#ifdef FLASHPAGE_RWWEE_NUMOF
static int getpage_rwwee(const char *str)
{
int page = atoi(str);
if ((page >= (int)FLASHPAGE_RWWEE_NUMOF) || (page < 0)) {
printf("error: RWWEE page %i is invalid\n", page);
return -1;
}
return page;
}
static int cmd_read_rwwee(int argc, char **argv)
{
int page;
if (argc < 2) {
printf("usage: %s <page>\n", argv[0]);
return 1;
}
page = getpage_rwwee(argv[1]);
if (page < 0) {
return 1;
}
flashpage_rwwee_read(page, page_mem);
printf("Read RWWEE flash page %i into local page buffer\n", page);
dump_local();
return 0;
}
static int cmd_write_rwwee(int argc, char **argv)
{
int page;
if (argc < 2) {
printf("usage: %s <page>\n", argv[0]);
return 1;
}
page = getpage_rwwee(argv[1]);
if (page < 0) {
return 1;
}
if (flashpage_rwwee_write_and_verify(page, page_mem) != FLASHPAGE_OK) {
printf("error: verification for RWWEE page %i failed\n", page);
return 1;
}
printf("wrote local page buffer to RWWEE flash page %i at addr %p\n",
page, flashpage_rwwee_addr(page));
return 0;
}
static int cmd_test_rwwee(int argc, char **argv)
{
int page;
char fill = 'a';
if (argc < 2) {
printf("usage: %s <page>\n", argv[0]);
return 1;
}
page = getpage_rwwee(argv[1]);
if (page < 0) {
return 1;
}
fill += (page % ('z' - 'a')); // Make each page slightly different by changing starting char for easier comparison by eye
for (unsigned i = 0; i < sizeof(page_mem); i++) {
page_mem[i] = (uint8_t)fill++;
if (fill > 'z') {
fill = 'a';
}
}
if (flashpage_rwwee_write_and_verify(page, page_mem) != FLASHPAGE_OK) {
printf("error verifying the content of RWWEE page %i\n", page);
return 1;
}
printf("wrote local page buffer to RWWEE flash page %i at addr %p\n",
page, flashpage_rwwee_addr(page));
return 0;
}
/**
* @brief Does a write and verify test on last page available
*
* @note Since every hardware can have different flash layouts for
* automated testing we always write to the last page available
* so we are independent of the size or layout
*/
static int cmd_test_last_rwwee(int argc, char **argv)
{
(void) argc;
(void) argv;
char fill = 'a';
for (unsigned i = 0; i < sizeof(page_mem); i++) {
page_mem[i] = (uint8_t)fill++;
if (fill > 'z') {
fill = 'a';
}
}
if (flashpage_rwwee_write_and_verify((int)FLASHPAGE_RWWEE_NUMOF - 1, page_mem) != FLASHPAGE_OK) {
puts("error verifying the content of last RWWEE page");
return 1;
}
puts("wrote local page buffer to last RWWEE flash page");
return 0;
}
#ifdef MODULE_PERIPH_FLASHPAGE_RAW
/**
* @brief Does a short raw write on last page available
*
* @note Since every hardware can have different flash layouts for
* automated testing we always write to the last page available
* so we are independent of the size or layout
*/
static int cmd_test_last_rwwee_raw(int argc, char **argv)
{
(void) argc;
(void) argv;
/* try to align */
memcpy(raw_buf, "test12344321tset", 16);
/* erase the page first */
flashpage_rwwee_write(((int)FLASHPAGE_RWWEE_NUMOF - 1), NULL);
flashpage_rwwee_write_raw(flashpage_rwwee_addr((int)FLASHPAGE_RWWEE_NUMOF - 1), raw_buf, strlen(raw_buf));
/* verify that previous write_raw effectively wrote the desired data */
if (memcmp(flashpage_rwwee_addr((int)FLASHPAGE_RWWEE_NUMOF - 1), raw_buf, strlen(raw_buf)) != 0) {
puts("error verifying the content of last RWWEE page");
return 1;
}
puts("wrote raw short buffer to last RWWEE flash page");
return 0;
}
#endif
#endif
static const shell_command_t shell_commands[] = {
{ "info", "Show information about pages", cmd_info },
{ "dump", "Dump the selected page to STDOUT", cmd_dump },
@ -366,6 +526,15 @@ static const shell_command_t shell_commands[] = {
{ "test_last", "Write and verify test pattern on last page available", cmd_test_last },
#ifdef MODULE_PERIPH_FLASHPAGE_RAW
{ "test_last_raw", "Write and verify raw short write on last page available", cmd_test_last_raw },
#endif
#ifdef FLASHPAGE_RWWEE_NUMOF
{ "read_rwwee", "Copy the given page from RWWEE to the local page buffer and dump to STDOUT", cmd_read_rwwee },
{ "write_rwwee", "Write the local page buffer to the given RWWEE page", cmd_write_rwwee },
{ "test_rwwee", "Write and verify test pattern to RWWEE", cmd_test_rwwee },
{ "test_last_rwwee", "Write and verify test pattern on last RWWEE page available", cmd_test_last_rwwee },
#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
{ NULL, NULL, NULL }
};