From 5ef14ccc9c832899457ef01a84b89b4e786a8c0e Mon Sep 17 00:00:00 2001 From: garyButt <50915020+garyButt@users.noreply.github.com> Date: Thu, 2 Apr 2020 16:33:33 +0100 Subject: [PATCH] [efr32] add nvm3 support (#4521) silabs nvm3 support is selected by setting following macro in openthread-core-efr32-config.h :- #define OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE 0 --- examples/Makefile-efr32mg13 | 1 - examples/platforms/efr32mg12/Makefile.am | 2 + .../platforms/efr32mg12/Makefile.platform.am | 3 +- examples/platforms/efr32mg12/efr32mg12.ld | 246 ++++++++++++ examples/platforms/efr32mg12/flash.c | 364 +++++++++++++++-- .../efr32mg12/openthread-core-efr32-config.h | 2 +- examples/platforms/efr32mg13/Makefile.am | 2 + .../platforms/efr32mg13/Makefile.platform.am | 3 +- examples/platforms/efr32mg13/efr32mg13.ld | 246 ++++++++++++ examples/platforms/efr32mg13/flash.c | 366 ++++++++++++++++-- .../efr32mg13/openthread-core-efr32-config.h | 2 +- examples/platforms/efr32mg21/Makefile.am | 2 + .../platforms/efr32mg21/Makefile.platform.am | 5 +- examples/platforms/efr32mg21/efr32mg21.ld | 246 ++++++++++++ examples/platforms/efr32mg21/flash.c | 366 ++++++++++++++++-- .../efr32mg21/openthread-core-efr32-config.h | 2 +- third_party/silabs/Makefile.am | 4 + 17 files changed, 1738 insertions(+), 124 deletions(-) create mode 100644 examples/platforms/efr32mg12/efr32mg12.ld create mode 100644 examples/platforms/efr32mg13/efr32mg13.ld create mode 100644 examples/platforms/efr32mg21/efr32mg21.ld diff --git a/examples/Makefile-efr32mg13 b/examples/Makefile-efr32mg13 index bd2d951c3..e1ef7078f 100644 --- a/examples/Makefile-efr32mg13 +++ b/examples/Makefile-efr32mg13 @@ -70,7 +70,6 @@ else $(error Please provide a value for BOARD variable e.g BOARD=BRD4168A (currently supported BRD4168A)) endif - EFR32_MBEDTLS_CPPFLAGS = -DMBEDTLS_CONFIG_FILE='\"mbedtls-config.h\"' EFR32_MBEDTLS_CPPFLAGS += -DMBEDTLS_USER_CONFIG_FILE='\"efr32-mbedtls-config.h\"' EFR32_MBEDTLS_CPPFLAGS += -D$(MCU) diff --git a/examples/platforms/efr32mg12/Makefile.am b/examples/platforms/efr32mg12/Makefile.am index 3b62f745f..6243d9e9a 100644 --- a/examples/platforms/efr32mg12/Makefile.am +++ b/examples/platforms/efr32mg12/Makefile.am @@ -76,6 +76,8 @@ libopenthread_efr32mg12_a_CPPFLAGS = -I$(SDK_SRC_DIR)/platform/emdrv/ustimer/inc \ -I$(SDK_SRC_DIR)/platform/emdrv/dmadrv/inc \ -I$(SDK_SRC_DIR)/platform/emdrv/dmadrv/config \ + -I$(SDK_SRC_DIR)/platform/emdrv/nvm3/inc \ + -I$(SDK_SRC_DIR)/platform/emdrv/nvm3/config \ -I$(SDK_SRC_DIR)/platform/emlib/inc \ -I$(SDK_SRC_DIR)/platform/halconfig/inc/hal-config \ -I$(SDK_SRC_DIR)/platform/radio/rail_lib/chip/efr32 \ diff --git a/examples/platforms/efr32mg12/Makefile.platform.am b/examples/platforms/efr32mg12/Makefile.platform.am index fe7f00672..fc310526c 100644 --- a/examples/platforms/efr32mg12/Makefile.platform.am +++ b/examples/platforms/efr32mg12/Makefile.platform.am @@ -41,8 +41,9 @@ LDADD_COMMON += $(top_builddir)/examples/platforms/efr32mg12/libopenthread-efr32mg12.a \ $(top_builddir)/third_party/silabs/libsilabs-efr32mg12-sdk.a \ $(top_srcdir)/third_party/silabs/gecko_sdk_suite/v2.7/platform/radio/rail_lib/autogen/librail_release/$(LIBRAIL) \ + $(top_srcdir)/third_party/silabs/gecko_sdk_suite/v2.7/platform/emdrv/nvm3/lib/libnvm3_CM4_gcc.a \ $(NULL) LDFLAGS_COMMON += \ - -T $(top_srcdir)/third_party/silabs/gecko_sdk_suite/v2.7/platform/Device/SiliconLabs/EFR32MG12P/Source/GCC/efr32mg12p.ld \ + -T $(top_srcdir)/examples/platforms/efr32mg12/efr32mg12.ld \ $(NULL) diff --git a/examples/platforms/efr32mg12/efr32mg12.ld b/examples/platforms/efr32mg12/efr32mg12.ld new file mode 100644 index 000000000..803ea0023 --- /dev/null +++ b/examples/platforms/efr32mg12/efr32mg12.ld @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2020, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + /** + * @file + * This file implements the OpenThread linker script for the + * Silicon Labs efr32mg12 platform. + * + */ + +MEMORY +{ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 1024K + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 256K +} + +/* Linker script to place sections and symbol values. Should be used together + * with other linker script that defines memory regions FLASH and RAM. + * It references following symbols, which must be defined in code: + * Reset_Handler : Entry of reset handler + * + * It defines following symbols, which code can use without definition: + * __exidx_start + * __exidx_end + * __copy_table_start__ + * __copy_table_end__ + * __zero_table_start__ + * __zero_table_end__ + * __etext + * __data_start__ + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __data_end__ + * __bss_start__ + * __bss_end__ + * __end__ + * end + * __HeapBase + * __HeapLimit + * __StackLimit + * __StackTop + * __stack + * __Vectors_End + * __Vectors_Size + */ +ENTRY(Reset_Handler) + +SECTIONS +{ + .text : + { + KEEP(*(.vectors)) + __Vectors_End = .; + __Vectors_Size = __Vectors_End - __Vectors; + __end__ = .; + + *(.text*) + + KEEP(*(.init)) + KEEP(*(.fini)) + + /* .ctors */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + + /* .dtors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + *(.rodata*) + + KEEP(*(.eh_frame*)) + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + /* To copy multiple ROM to RAM sections, + * uncomment .copy.table section and, + * define __STARTUP_COPY_MULTIPLE in startup_ARMCMx.S */ + /* + .copy.table : + { + . = ALIGN(4); + __copy_table_start__ = .; + LONG (__etext) + LONG (__data_start__) + LONG (__data_end__ - __data_start__) + LONG (__etext2) + LONG (__data2_start__) + LONG (__data2_end__ - __data2_start__) + __copy_table_end__ = .; + } > FLASH + */ + + /* To clear multiple BSS sections, + * uncomment .zero.table section and, + * define __STARTUP_CLEAR_BSS_MULTIPLE in startup_ARMCMx.S */ + /* + .zero.table : + { + . = ALIGN(4); + __zero_table_start__ = .; + LONG (__bss_start__) + LONG (__bss_end__ - __bss_start__) + LONG (__bss2_start__) + LONG (__bss2_end__ - __bss2_start__) + __zero_table_end__ = .; + } > FLASH + */ + + __etext = .; + + .data : AT (__etext) + { + __data_start__ = .; + *(vtable) + *(.data*) + . = ALIGN (4); + PROVIDE (__ram_func_section_start = .); + *(.ram) + PROVIDE (__ram_func_section_end = .); + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + + KEEP(*(.jcr*)) + . = ALIGN(4); + /* All data end */ + __data_end__ = .; + + } > RAM + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (COPY): + { + __HeapBase = .; + __end__ = .; + end = __end__; + _end = __end__; + KEEP(*(.heap*)) + __HeapLimit = .; + } > RAM + + /* .stack_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later */ + .stack_dummy (COPY): + { + KEEP(*(.stack*)) + } > RAM + + /* Set stack top to end of RAM, and stack limit move down by + * size of stack_dummy section */ + __StackTop = ORIGIN(RAM) + LENGTH(RAM); + __StackLimit = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + /*******************************************************************/ + /* Define 8K (2000H) byte flash block for OT nvm3- */ + /* ( == 4 x Flash Pages as configured in flash.c) */ + .otNvm3_block (LENGTH(FLASH) - 0x2000) (NOLOAD): + { + . = ALIGN (8192); + __nvm3OtBase = .; + KEEP(*(otNvm3_section)); + __nvm3OtCeiling = .; + } > FLASH + /*******************************************************************/ + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack") + + /* Check if FLASH usage exceeds FLASH size */ + ASSERT( LENGTH(FLASH) >= (__etext + SIZEOF(.data)), "FLASH memory overflowed !") +} diff --git a/examples/platforms/efr32mg12/flash.c b/examples/platforms/efr32mg12/flash.c index 368c6ac2d..88ee4ca7a 100644 --- a/examples/platforms/efr32mg12/flash.c +++ b/examples/platforms/efr32mg12/flash.c @@ -28,69 +28,357 @@ /** * @file - * This file implements the OpenThread platform abstraction for the non-volatile storage. + * This file implements the OpenThread platform abstraction for the non-volatile + * storage for the EFR32 platform using the Silabs Nvm3 interface. */ +#include +#include + +#if !OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE + +#include "nvm3.h" +#include "nvm3_hal_flash.h" #include +#include "common/code_utils.hpp" -#include +// Two macros are provided to support the creation of the Silicon Labs NVM3 area and +// initialization data- NVM3_DEFINE_SECTION_STATIC_DATA() and NVM3_DEFINE_SECTION_INIT_DATA(). +// A linker section called 'name'_section is defined by NVM3_DEFINE_SECTION_STATIC_DATA(). +// The NVM3 area is placed at the top of the device FLASH section by the linker script +// file: e.g. efr32mgxx.ld +// An error is returned by nvm3_open() on alignment or size violation. -#include "em_msc.h" +// Local version of SDK macro (avoids uninitialized var compile error). +#define OT_NVM3_DEFINE_SECTION_STATIC_DATA(name, nvmSize, cacheSize) \ + static nvm3_CacheEntry_t name##_cache[cacheSize]; \ + static uint8_t name##_nvm[nvmSize] SL_ATTRIBUTE_SECTION(STRINGIZE(name##_section)) -#define FLASH_PAGE_NUM 4 -#define FLASH_DATA_END_ADDR (FLASH_BASE + FLASH_SIZE) -#define FLASH_DATA_START_ADDR (FLASH_DATA_END_ADDR - (FLASH_PAGE_SIZE * FLASH_PAGE_NUM)) -#define FLASH_SWAP_PAGE_NUM (FLASH_PAGE_NUM / 2) -#define FLASH_SWAP_SIZE (FLASH_PAGE_SIZE * FLASH_SWAP_PAGE_NUM) - -static inline uint32_t mapAddress(uint8_t aSwapIndex, uint32_t aOffset) -{ - uint32_t address; - - address = FLASH_DATA_START_ADDR + aOffset; - - if (aSwapIndex) - { - address += FLASH_SWAP_SIZE; +// Local version of SDK macro (allows OT to configure the maximum nvm3 object size and headroom). +#define OT_NVM3_DEFINE_SECTION_INIT_DATA(name, maxObjectSize, repackHeadroom) \ + static nvm3_Init_t name = { \ + (nvm3_HalPtr_t)name##_nvm, \ + sizeof(name##_nvm), \ + name##_cache, \ + sizeof(name##_cache) / sizeof(nvm3_CacheEntry_t), \ + maxObjectSize, \ + repackHeadroom, \ + &nvm3_halFlashHandle, \ } - return address; -} +#define NUM_SETTINGS_OBJECTS 7 // == number of enumerated Settings key types (in settings.hpp). +#define NUM_INDEXED_SETTINGS \ + OPENTHREAD_CONFIG_MLE_MAX_CHILDREN // Indexed key types are only supported for kKeyChildInfo (=='child table'). +#define NUM_USER_OBJECTS 16 // User nvm3 objects (nvm3 key range 0x0000 -> 0xDFFF is available for user data). -void otPlatFlashInit(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); -} +#define OT_NVM3_FLASH_NUM_PAGES 4 // Note- nvm3 requires minimum of 3 pages. +#define OT_NVM3_FLASH_PAGE_SIZE FLASH_PAGE_SIZE // Device flash page size (MG12/MG13=2K, MG21=8K) +#define OT_NVM3_FLASH_SIZE OT_NVM3_FLASH_NUM_PAGES *OT_NVM3_FLASH_PAGE_SIZE +#define OT_NVM3_MAX_NUM_OBJECTS NUM_SETTINGS_OBJECTS + NUM_INDEXED_SETTINGS + NUM_USER_OBJECTS +#define OT_NVM3_MAX_OBJECT_SIZE 256 +#define OT_NVM3_REPACK_HEADROOM 64 // Threshold for automatic nvm3 flash repacking. -uint32_t otPlatFlashGetSwapSize(otInstance *aInstance) +#define OT_NVM3_SETTINGS_KEY_PREFIX 0xE000 // (=> key range 0x0000-0xDFFF is available for OT User nvm3 objects). + +#define ENUM_NVM3_KEY_LIST_SIZE 4 // List size used when enumerating nvm3 keys. + +static nvm3_Handle_t handle = {NULL}; + +static otError addSetting(uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength); +static nvm3_ObjectKey_t makeNvm3ObjKey(uint16_t otSettingsKey, int index); +static otError mapNvm3Error(Ecode_t nvm3Res); + +// Declare OT NVM3 data area and cache. +// OT data area should be enough for OT 'Settings' and whatwever User data is required. + +OT_NVM3_DEFINE_SECTION_STATIC_DATA(otNvm3, OT_NVM3_FLASH_SIZE, OT_NVM3_MAX_NUM_OBJECTS); + +OT_NVM3_DEFINE_SECTION_INIT_DATA(otNvm3, OT_NVM3_MAX_OBJECT_SIZE, OT_NVM3_REPACK_HEADROOM); + +void otPlatSettingsInit(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); - return FLASH_SWAP_SIZE; -} + otError err; + bool needClose = false; -void otPlatFlashErase(otInstance *aInstance, uint8_t aSwapIndex) -{ - OT_UNUSED_VARIABLE(aInstance); + err = mapNvm3Error(nvm3_open(&handle, &otNvm3)); + SuccessOrExit(err); + needClose = true; - uint32_t address = mapAddress(aSwapIndex, 0); - - for (uint32_t n = 0; n < FLASH_SWAP_PAGE_NUM; n++, address += FLASH_PAGE_SIZE) +exit: + if (needClose) { - MSC_ErasePage((uint32_t *)address); + nvm3_close(&handle); } } -void otPlatFlashWrite(otInstance *aInstance, uint8_t aSwapIndex, uint32_t aOffset, const void *aData, uint32_t aSize) +void otPlatSettingsDeinit(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); - MSC_WriteWord((uint32_t *)mapAddress(aSwapIndex, aOffset), aData, aSize); + nvm3_close(&handle); } -void otPlatFlashRead(otInstance *aInstance, uint8_t aSwapIndex, uint32_t aOffset, void *aData, uint32_t aSize) +otError otPlatSettingsGet(otInstance *aInstance, uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength) +{ + // Searches through all matching nvm3 keys to find the one with the required + // 'index', then reads the nvm3 data into the destination buffer. + // (Repeatedly enumerates a list of matching keys from the nvm3 until the + // required index is found). + + OT_UNUSED_VARIABLE(aInstance); + + otError err; + bool needClose = false; + uint16_t valueLength = 0; + + err = mapNvm3Error(nvm3_open(&handle, &otNvm3)); + SuccessOrExit(err); + needClose = true; + + nvm3_ObjectKey_t nvm3Key = makeNvm3ObjKey(aKey, 0); // The base nvm3 key value. + bool idxFound = false; + int idx = 0; + err = OT_ERROR_NOT_FOUND; + while ((idx <= NUM_INDEXED_SETTINGS) && (!idxFound)) + { + // Get the next nvm3 key list. + nvm3_ObjectKey_t keys[ENUM_NVM3_KEY_LIST_SIZE]; // List holds the next set of nvm3 keys. + size_t objCnt = nvm3_enumObjects(&handle, keys, ENUM_NVM3_KEY_LIST_SIZE, nvm3Key, + makeNvm3ObjKey(aKey, NUM_INDEXED_SETTINGS)); + for (size_t i = 0; i < objCnt; ++i) + { + nvm3Key = keys[i]; + if (idx == aIndex) + { + uint32_t objType; + size_t objLen; + err = mapNvm3Error(nvm3_getObjectInfo(&handle, nvm3Key, &objType, &objLen)); + if (err == OT_ERROR_NONE) + { + valueLength = objLen; + + // Only perform read if an input buffer was passed in. + if ((aValue != NULL) && (aValueLength != NULL)) + { + // Read all nvm3 obj bytes into a tmp buffer, then copy the required + // number of bytes to the read destination buffer. + uint8_t *buf = malloc(valueLength); + err = mapNvm3Error(nvm3_readData(&handle, nvm3Key, buf, valueLength)); + if (err == OT_ERROR_NONE) + { + memcpy(aValue, buf, (valueLength < *aValueLength) ? valueLength : *aValueLength); + } + free(buf); + SuccessOrExit(err); + } + } + idxFound = true; + break; + } + ++idx; + } + if (objCnt < ENUM_NVM3_KEY_LIST_SIZE) + { + // Stop searching (there are no more matching nvm3 objects). + break; + } + ++nvm3Key; // Inc starting value for next nvm3 key list enumeration. + } + +exit: + if (needClose) + { + nvm3_close(&handle); + } + + if (aValueLength != NULL) + { + *aValueLength = valueLength; // always return actual nvm3 object length. + } + + return err; +} + +otError otPlatSettingsSet(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength) { OT_UNUSED_VARIABLE(aInstance); - memcpy(aData, (const uint8_t *)mapAddress(aSwapIndex, aOffset), aSize); + otError err; + + // Delete all nvm3 objects matching the input key (i.e. the 'setting indexes' of the key). + err = otPlatSettingsDelete(aInstance, aKey, -1); + if ((err == OT_ERROR_NONE) || (err == OT_ERROR_NOT_FOUND)) + { + // Add new setting object (i.e. 'index0' of the key). + err = addSetting(aKey, aValue, aValueLength); + SuccessOrExit(err); + } + +exit: + return err; } + +otError otPlatSettingsAdd(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength) +{ + OT_UNUSED_VARIABLE(aInstance); + + return addSetting(aKey, aValue, aValueLength); +} + +otError otPlatSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex) +{ + // Searches through all matching nvm3 keys to find the one with the required + // 'index' (or index = -1 to delete all), then deletes the nvm3 object. + // (Repeatedly enumerates a list of matching keys from the nvm3 until the + // required index is found). + + OT_UNUSED_VARIABLE(aInstance); + + otError err; + bool needClose = false; + + err = mapNvm3Error(nvm3_open(&handle, &otNvm3)); + SuccessOrExit(err); + needClose = true; + + nvm3_ObjectKey_t nvm3Key = makeNvm3ObjKey(aKey, 0); // The base nvm3 key value. + bool idxFound = false; + int idx = 0; + err = OT_ERROR_NOT_FOUND; + while ((idx <= NUM_INDEXED_SETTINGS) && (!idxFound)) + { + // Get the next nvm3 key list. + nvm3_ObjectKey_t keys[ENUM_NVM3_KEY_LIST_SIZE]; // List holds the next set of nvm3 keys. + size_t objCnt = nvm3_enumObjects(&handle, keys, ENUM_NVM3_KEY_LIST_SIZE, nvm3Key, + makeNvm3ObjKey(aKey, NUM_INDEXED_SETTINGS)); + for (size_t i = 0; i < objCnt; ++i) + { + nvm3Key = keys[i]; + if ((idx == aIndex) || (aIndex == -1)) + { + uint32_t objType; + size_t objLen; + err = mapNvm3Error(nvm3_getObjectInfo(&handle, nvm3Key, &objType, &objLen)); + if (err == OT_ERROR_NONE) + { + // Delete the nvm3 object. + err = mapNvm3Error(nvm3_deleteObject(&handle, nvm3Key)); + SuccessOrExit(err); + } + if (aIndex != -1) + { + idxFound = true; + break; + } + } + ++idx; + } + if (objCnt < ENUM_NVM3_KEY_LIST_SIZE) + { + // Stop searching (there are no more matching nvm3 objects). + break; + } + ++nvm3Key; // Inc starting value for next nvm3 key list enumeration. + } + +exit: + if (needClose) + { + nvm3_close(&handle); + } + + return err; +} + +void otPlatSettingsWipe(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + + // Delete nvm3 objects for all OT Settings keys (and any of their associated 'indexes'). + // Note- any OT User nvm3 objects in the OT nvm3 area are NOT be erased. + for (uint16_t aKey = 0; aKey < 8; ++aKey) + { + otPlatSettingsDelete(NULL, aKey, -1); + } +} + +// Local functions.. + +static otError addSetting(uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength) +{ + // Helper function- writes input buffer data to a NEW nvm3 object. + // nvm3 object is created at the first available Key + index. + + otError err; + bool needClose = false; + + err = mapNvm3Error(nvm3_open(&handle, &otNvm3)); + SuccessOrExit(err); + needClose = true; + + if ((aValueLength == 0) || (aValue == NULL)) + { + err = OT_ERROR_INVALID_ARGS; + } + else + { + for (int idx = 0; idx <= NUM_INDEXED_SETTINGS; ++idx) + { + nvm3_ObjectKey_t nvm3Key; + nvm3Key = makeNvm3ObjKey(aKey, idx); + + uint32_t objType; + size_t objLen; + err = mapNvm3Error(nvm3_getObjectInfo(&handle, nvm3Key, &objType, &objLen)); + if (err == OT_ERROR_NOT_FOUND) + { + // Use this index for the new nvm3 object. + // Write the binary data to nvm3 (Creates nvm3 object if required). + err = mapNvm3Error(nvm3_writeData(&handle, nvm3Key, aValue, aValueLength)); + break; + } + else if (err != OT_ERROR_NONE) + { + break; + } + } + } + +exit: + if (needClose) + { + nvm3_close(&handle); + } + return err; +} + +static nvm3_ObjectKey_t makeNvm3ObjKey(uint16_t otSettingsKey, int index) +{ + return (OT_NVM3_SETTINGS_KEY_PREFIX | (otSettingsKey << 8) | (index & 0xFF)); +} + +static otError mapNvm3Error(Ecode_t nvm3Res) +{ + otError err; + + switch (nvm3Res) + { + case ECODE_NVM3_OK: + err = OT_ERROR_NONE; + break; + + case ECODE_NVM3_ERR_KEY_NOT_FOUND: + err = OT_ERROR_NOT_FOUND; + break; + + default: + err = OT_ERROR_FAILED; + break; + } + + return err; +} + +#endif // !OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE diff --git a/examples/platforms/efr32mg12/openthread-core-efr32-config.h b/examples/platforms/efr32mg12/openthread-core-efr32-config.h index 0b8a50d71..d7eead986 100644 --- a/examples/platforms/efr32mg12/openthread-core-efr32-config.h +++ b/examples/platforms/efr32mg12/openthread-core-efr32-config.h @@ -111,7 +111,7 @@ * When defined to 1, the platform MUST implement the otPlatFlash* APIs instead of the otPlatSettings* APIs. * */ -#define OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE 1 +#define OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE 0 /** * @def OPENTHREAD_CONFIG_NCP_UART_ENABLE diff --git a/examples/platforms/efr32mg13/Makefile.am b/examples/platforms/efr32mg13/Makefile.am index aa633102b..2a54b242c 100644 --- a/examples/platforms/efr32mg13/Makefile.am +++ b/examples/platforms/efr32mg13/Makefile.am @@ -76,6 +76,8 @@ libopenthread_efr32mg13_a_CPPFLAGS = -I$(SDK_SRC_DIR)/platform/emdrv/ustimer/inc \ -I$(SDK_SRC_DIR)/platform/emdrv/dmadrv/inc \ -I$(SDK_SRC_DIR)/platform/emdrv/dmadrv/config \ + -I$(SDK_SRC_DIR)/platform/emdrv/nvm3/inc \ + -I$(SDK_SRC_DIR)/platform/emdrv/nvm3/config \ -I$(SDK_SRC_DIR)/platform/emlib/inc \ -I$(SDK_SRC_DIR)/platform/halconfig/inc/hal-config \ -I$(SDK_SRC_DIR)/platform/radio/rail_lib/chip/efr32 \ diff --git a/examples/platforms/efr32mg13/Makefile.platform.am b/examples/platforms/efr32mg13/Makefile.platform.am index c65e045d7..5addd35d9 100644 --- a/examples/platforms/efr32mg13/Makefile.platform.am +++ b/examples/platforms/efr32mg13/Makefile.platform.am @@ -41,8 +41,9 @@ LDADD_COMMON += $(top_builddir)/examples/platforms/efr32mg13/libopenthread-efr32mg13.a \ $(top_builddir)/third_party/silabs/libsilabs-efr32mg13-sdk.a \ $(top_srcdir)/third_party/silabs/gecko_sdk_suite/v2.7/platform/radio/rail_lib/autogen/librail_release/$(LIBRAIL) \ + $(top_srcdir)/third_party/silabs/gecko_sdk_suite/v2.7/platform/emdrv/nvm3/lib/libnvm3_CM4_gcc.a \ $(NULL) LDFLAGS_COMMON += \ - -T $(top_srcdir)/third_party/silabs/gecko_sdk_suite/v2.7/platform/Device/SiliconLabs/EFR32MG13P/Source/GCC/efr32mg13p.ld \ + -T $(top_srcdir)/examples/platforms/efr32mg13/efr32mg13.ld \ $(NULL) diff --git a/examples/platforms/efr32mg13/efr32mg13.ld b/examples/platforms/efr32mg13/efr32mg13.ld new file mode 100644 index 000000000..ce380febe --- /dev/null +++ b/examples/platforms/efr32mg13/efr32mg13.ld @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2020, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + /** + * @file + * This file implements the OpenThread linker script for the + * Silicon Labs efr32mg13 platform. + * + */ + +MEMORY +{ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 512K + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K +} + +/* Linker script to place sections and symbol values. Should be used together + * with other linker script that defines memory regions FLASH and RAM. + * It references following symbols, which must be defined in code: + * Reset_Handler : Entry of reset handler + * + * It defines following symbols, which code can use without definition: + * __exidx_start + * __exidx_end + * __copy_table_start__ + * __copy_table_end__ + * __zero_table_start__ + * __zero_table_end__ + * __etext + * __data_start__ + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __data_end__ + * __bss_start__ + * __bss_end__ + * __end__ + * end + * __HeapBase + * __HeapLimit + * __StackLimit + * __StackTop + * __stack + * __Vectors_End + * __Vectors_Size + */ +ENTRY(Reset_Handler) + +SECTIONS +{ + .text : + { + KEEP(*(.vectors)) + __Vectors_End = .; + __Vectors_Size = __Vectors_End - __Vectors; + __end__ = .; + + *(.text*) + + KEEP(*(.init)) + KEEP(*(.fini)) + + /* .ctors */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + + /* .dtors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + *(.rodata*) + + KEEP(*(.eh_frame*)) + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + /* To copy multiple ROM to RAM sections, + * uncomment .copy.table section and, + * define __STARTUP_COPY_MULTIPLE in startup_ARMCMx.S */ + /* + .copy.table : + { + . = ALIGN(4); + __copy_table_start__ = .; + LONG (__etext) + LONG (__data_start__) + LONG (__data_end__ - __data_start__) + LONG (__etext2) + LONG (__data2_start__) + LONG (__data2_end__ - __data2_start__) + __copy_table_end__ = .; + } > FLASH + */ + + /* To clear multiple BSS sections, + * uncomment .zero.table section and, + * define __STARTUP_CLEAR_BSS_MULTIPLE in startup_ARMCMx.S */ + /* + .zero.table : + { + . = ALIGN(4); + __zero_table_start__ = .; + LONG (__bss_start__) + LONG (__bss_end__ - __bss_start__) + LONG (__bss2_start__) + LONG (__bss2_end__ - __bss2_start__) + __zero_table_end__ = .; + } > FLASH + */ + + __etext = .; + + .data : AT (__etext) + { + __data_start__ = .; + *(vtable) + *(.data*) + . = ALIGN (4); + PROVIDE (__ram_func_section_start = .); + *(.ram) + PROVIDE (__ram_func_section_end = .); + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + + KEEP(*(.jcr*)) + . = ALIGN(4); + /* All data end */ + __data_end__ = .; + + } > RAM + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (COPY): + { + __HeapBase = .; + __end__ = .; + end = __end__; + _end = __end__; + KEEP(*(.heap*)) + __HeapLimit = .; + } > RAM + + /* .stack_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later */ + .stack_dummy (COPY): + { + KEEP(*(.stack*)) + } > RAM + + /* Set stack top to end of RAM, and stack limit move down by + * size of stack_dummy section */ + __StackTop = ORIGIN(RAM) + LENGTH(RAM); + __StackLimit = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + /*******************************************************************/ + /* Define 8K (2000H) byte flash block for OT nvm3- */ + /* ( == 4 x Flash Pages as configured in flash.c) */ + .otNvm3_block (LENGTH(FLASH) - 0x2000) (NOLOAD): + { + . = ALIGN (8192); + __nvm3OtBase = .; + KEEP(*(otNvm3_section)); + __nvm3OtCeiling = .; + } > FLASH + /*******************************************************************/ + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack") + + /* Check if FLASH usage exceeds FLASH size */ + ASSERT( LENGTH(FLASH) >= (__etext + SIZEOF(.data)), "FLASH memory overflowed !") +} diff --git a/examples/platforms/efr32mg13/flash.c b/examples/platforms/efr32mg13/flash.c index 1c63c65c8..88ee4ca7a 100644 --- a/examples/platforms/efr32mg13/flash.c +++ b/examples/platforms/efr32mg13/flash.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, The OpenThread Authors. + * Copyright (c) 2020, The OpenThread Authors. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,69 +28,357 @@ /** * @file - * This file implements the OpenThread platform abstraction for the non-volatile storage. + * This file implements the OpenThread platform abstraction for the non-volatile + * storage for the EFR32 platform using the Silabs Nvm3 interface. */ +#include +#include + +#if !OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE + +#include "nvm3.h" +#include "nvm3_hal_flash.h" #include +#include "common/code_utils.hpp" -#include +// Two macros are provided to support the creation of the Silicon Labs NVM3 area and +// initialization data- NVM3_DEFINE_SECTION_STATIC_DATA() and NVM3_DEFINE_SECTION_INIT_DATA(). +// A linker section called 'name'_section is defined by NVM3_DEFINE_SECTION_STATIC_DATA(). +// The NVM3 area is placed at the top of the device FLASH section by the linker script +// file: e.g. efr32mgxx.ld +// An error is returned by nvm3_open() on alignment or size violation. -#include "em_msc.h" +// Local version of SDK macro (avoids uninitialized var compile error). +#define OT_NVM3_DEFINE_SECTION_STATIC_DATA(name, nvmSize, cacheSize) \ + static nvm3_CacheEntry_t name##_cache[cacheSize]; \ + static uint8_t name##_nvm[nvmSize] SL_ATTRIBUTE_SECTION(STRINGIZE(name##_section)) -#define FLASH_PAGE_NUM 4 -#define FLASH_DATA_END_ADDR (FLASH_BASE + FLASH_SIZE) -#define FLASH_DATA_START_ADDR (FLASH_DATA_END_ADDR - (FLASH_PAGE_SIZE * FLASH_PAGE_NUM)) -#define FLASH_SWAP_PAGE_NUM (FLASH_PAGE_NUM / 2) -#define FLASH_SWAP_SIZE (FLASH_PAGE_SIZE * FLASH_SWAP_PAGE_NUM) - -static inline uint32_t mapAddress(uint8_t aSwapIndex, uint32_t aOffset) -{ - uint32_t address; - - address = FLASH_DATA_START_ADDR + aOffset; - - if (aSwapIndex) - { - address += FLASH_SWAP_SIZE; +// Local version of SDK macro (allows OT to configure the maximum nvm3 object size and headroom). +#define OT_NVM3_DEFINE_SECTION_INIT_DATA(name, maxObjectSize, repackHeadroom) \ + static nvm3_Init_t name = { \ + (nvm3_HalPtr_t)name##_nvm, \ + sizeof(name##_nvm), \ + name##_cache, \ + sizeof(name##_cache) / sizeof(nvm3_CacheEntry_t), \ + maxObjectSize, \ + repackHeadroom, \ + &nvm3_halFlashHandle, \ } - return address; -} +#define NUM_SETTINGS_OBJECTS 7 // == number of enumerated Settings key types (in settings.hpp). +#define NUM_INDEXED_SETTINGS \ + OPENTHREAD_CONFIG_MLE_MAX_CHILDREN // Indexed key types are only supported for kKeyChildInfo (=='child table'). +#define NUM_USER_OBJECTS 16 // User nvm3 objects (nvm3 key range 0x0000 -> 0xDFFF is available for user data). -void otPlatFlashInit(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); -} +#define OT_NVM3_FLASH_NUM_PAGES 4 // Note- nvm3 requires minimum of 3 pages. +#define OT_NVM3_FLASH_PAGE_SIZE FLASH_PAGE_SIZE // Device flash page size (MG12/MG13=2K, MG21=8K) +#define OT_NVM3_FLASH_SIZE OT_NVM3_FLASH_NUM_PAGES *OT_NVM3_FLASH_PAGE_SIZE +#define OT_NVM3_MAX_NUM_OBJECTS NUM_SETTINGS_OBJECTS + NUM_INDEXED_SETTINGS + NUM_USER_OBJECTS +#define OT_NVM3_MAX_OBJECT_SIZE 256 +#define OT_NVM3_REPACK_HEADROOM 64 // Threshold for automatic nvm3 flash repacking. -uint32_t otPlatFlashGetSwapSize(otInstance *aInstance) +#define OT_NVM3_SETTINGS_KEY_PREFIX 0xE000 // (=> key range 0x0000-0xDFFF is available for OT User nvm3 objects). + +#define ENUM_NVM3_KEY_LIST_SIZE 4 // List size used when enumerating nvm3 keys. + +static nvm3_Handle_t handle = {NULL}; + +static otError addSetting(uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength); +static nvm3_ObjectKey_t makeNvm3ObjKey(uint16_t otSettingsKey, int index); +static otError mapNvm3Error(Ecode_t nvm3Res); + +// Declare OT NVM3 data area and cache. +// OT data area should be enough for OT 'Settings' and whatwever User data is required. + +OT_NVM3_DEFINE_SECTION_STATIC_DATA(otNvm3, OT_NVM3_FLASH_SIZE, OT_NVM3_MAX_NUM_OBJECTS); + +OT_NVM3_DEFINE_SECTION_INIT_DATA(otNvm3, OT_NVM3_MAX_OBJECT_SIZE, OT_NVM3_REPACK_HEADROOM); + +void otPlatSettingsInit(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); - return FLASH_SWAP_SIZE; -} + otError err; + bool needClose = false; -void otPlatFlashErase(otInstance *aInstance, uint8_t aSwapIndex) -{ - OT_UNUSED_VARIABLE(aInstance); + err = mapNvm3Error(nvm3_open(&handle, &otNvm3)); + SuccessOrExit(err); + needClose = true; - uint32_t address = mapAddress(aSwapIndex, 0); - - for (uint32_t n = 0; n < FLASH_SWAP_PAGE_NUM; n++, address += FLASH_PAGE_SIZE) +exit: + if (needClose) { - MSC_ErasePage((uint32_t *)address); + nvm3_close(&handle); } } -void otPlatFlashWrite(otInstance *aInstance, uint8_t aSwapIndex, uint32_t aOffset, const void *aData, uint32_t aSize) +void otPlatSettingsDeinit(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); - MSC_WriteWord((uint32_t *)mapAddress(aSwapIndex, aOffset), aData, aSize); + nvm3_close(&handle); } -void otPlatFlashRead(otInstance *aInstance, uint8_t aSwapIndex, uint32_t aOffset, void *aData, uint32_t aSize) +otError otPlatSettingsGet(otInstance *aInstance, uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength) +{ + // Searches through all matching nvm3 keys to find the one with the required + // 'index', then reads the nvm3 data into the destination buffer. + // (Repeatedly enumerates a list of matching keys from the nvm3 until the + // required index is found). + + OT_UNUSED_VARIABLE(aInstance); + + otError err; + bool needClose = false; + uint16_t valueLength = 0; + + err = mapNvm3Error(nvm3_open(&handle, &otNvm3)); + SuccessOrExit(err); + needClose = true; + + nvm3_ObjectKey_t nvm3Key = makeNvm3ObjKey(aKey, 0); // The base nvm3 key value. + bool idxFound = false; + int idx = 0; + err = OT_ERROR_NOT_FOUND; + while ((idx <= NUM_INDEXED_SETTINGS) && (!idxFound)) + { + // Get the next nvm3 key list. + nvm3_ObjectKey_t keys[ENUM_NVM3_KEY_LIST_SIZE]; // List holds the next set of nvm3 keys. + size_t objCnt = nvm3_enumObjects(&handle, keys, ENUM_NVM3_KEY_LIST_SIZE, nvm3Key, + makeNvm3ObjKey(aKey, NUM_INDEXED_SETTINGS)); + for (size_t i = 0; i < objCnt; ++i) + { + nvm3Key = keys[i]; + if (idx == aIndex) + { + uint32_t objType; + size_t objLen; + err = mapNvm3Error(nvm3_getObjectInfo(&handle, nvm3Key, &objType, &objLen)); + if (err == OT_ERROR_NONE) + { + valueLength = objLen; + + // Only perform read if an input buffer was passed in. + if ((aValue != NULL) && (aValueLength != NULL)) + { + // Read all nvm3 obj bytes into a tmp buffer, then copy the required + // number of bytes to the read destination buffer. + uint8_t *buf = malloc(valueLength); + err = mapNvm3Error(nvm3_readData(&handle, nvm3Key, buf, valueLength)); + if (err == OT_ERROR_NONE) + { + memcpy(aValue, buf, (valueLength < *aValueLength) ? valueLength : *aValueLength); + } + free(buf); + SuccessOrExit(err); + } + } + idxFound = true; + break; + } + ++idx; + } + if (objCnt < ENUM_NVM3_KEY_LIST_SIZE) + { + // Stop searching (there are no more matching nvm3 objects). + break; + } + ++nvm3Key; // Inc starting value for next nvm3 key list enumeration. + } + +exit: + if (needClose) + { + nvm3_close(&handle); + } + + if (aValueLength != NULL) + { + *aValueLength = valueLength; // always return actual nvm3 object length. + } + + return err; +} + +otError otPlatSettingsSet(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength) { OT_UNUSED_VARIABLE(aInstance); - memcpy(aData, (const uint8_t *)mapAddress(aSwapIndex, aOffset), aSize); + otError err; + + // Delete all nvm3 objects matching the input key (i.e. the 'setting indexes' of the key). + err = otPlatSettingsDelete(aInstance, aKey, -1); + if ((err == OT_ERROR_NONE) || (err == OT_ERROR_NOT_FOUND)) + { + // Add new setting object (i.e. 'index0' of the key). + err = addSetting(aKey, aValue, aValueLength); + SuccessOrExit(err); + } + +exit: + return err; } + +otError otPlatSettingsAdd(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength) +{ + OT_UNUSED_VARIABLE(aInstance); + + return addSetting(aKey, aValue, aValueLength); +} + +otError otPlatSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex) +{ + // Searches through all matching nvm3 keys to find the one with the required + // 'index' (or index = -1 to delete all), then deletes the nvm3 object. + // (Repeatedly enumerates a list of matching keys from the nvm3 until the + // required index is found). + + OT_UNUSED_VARIABLE(aInstance); + + otError err; + bool needClose = false; + + err = mapNvm3Error(nvm3_open(&handle, &otNvm3)); + SuccessOrExit(err); + needClose = true; + + nvm3_ObjectKey_t nvm3Key = makeNvm3ObjKey(aKey, 0); // The base nvm3 key value. + bool idxFound = false; + int idx = 0; + err = OT_ERROR_NOT_FOUND; + while ((idx <= NUM_INDEXED_SETTINGS) && (!idxFound)) + { + // Get the next nvm3 key list. + nvm3_ObjectKey_t keys[ENUM_NVM3_KEY_LIST_SIZE]; // List holds the next set of nvm3 keys. + size_t objCnt = nvm3_enumObjects(&handle, keys, ENUM_NVM3_KEY_LIST_SIZE, nvm3Key, + makeNvm3ObjKey(aKey, NUM_INDEXED_SETTINGS)); + for (size_t i = 0; i < objCnt; ++i) + { + nvm3Key = keys[i]; + if ((idx == aIndex) || (aIndex == -1)) + { + uint32_t objType; + size_t objLen; + err = mapNvm3Error(nvm3_getObjectInfo(&handle, nvm3Key, &objType, &objLen)); + if (err == OT_ERROR_NONE) + { + // Delete the nvm3 object. + err = mapNvm3Error(nvm3_deleteObject(&handle, nvm3Key)); + SuccessOrExit(err); + } + if (aIndex != -1) + { + idxFound = true; + break; + } + } + ++idx; + } + if (objCnt < ENUM_NVM3_KEY_LIST_SIZE) + { + // Stop searching (there are no more matching nvm3 objects). + break; + } + ++nvm3Key; // Inc starting value for next nvm3 key list enumeration. + } + +exit: + if (needClose) + { + nvm3_close(&handle); + } + + return err; +} + +void otPlatSettingsWipe(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + + // Delete nvm3 objects for all OT Settings keys (and any of their associated 'indexes'). + // Note- any OT User nvm3 objects in the OT nvm3 area are NOT be erased. + for (uint16_t aKey = 0; aKey < 8; ++aKey) + { + otPlatSettingsDelete(NULL, aKey, -1); + } +} + +// Local functions.. + +static otError addSetting(uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength) +{ + // Helper function- writes input buffer data to a NEW nvm3 object. + // nvm3 object is created at the first available Key + index. + + otError err; + bool needClose = false; + + err = mapNvm3Error(nvm3_open(&handle, &otNvm3)); + SuccessOrExit(err); + needClose = true; + + if ((aValueLength == 0) || (aValue == NULL)) + { + err = OT_ERROR_INVALID_ARGS; + } + else + { + for (int idx = 0; idx <= NUM_INDEXED_SETTINGS; ++idx) + { + nvm3_ObjectKey_t nvm3Key; + nvm3Key = makeNvm3ObjKey(aKey, idx); + + uint32_t objType; + size_t objLen; + err = mapNvm3Error(nvm3_getObjectInfo(&handle, nvm3Key, &objType, &objLen)); + if (err == OT_ERROR_NOT_FOUND) + { + // Use this index for the new nvm3 object. + // Write the binary data to nvm3 (Creates nvm3 object if required). + err = mapNvm3Error(nvm3_writeData(&handle, nvm3Key, aValue, aValueLength)); + break; + } + else if (err != OT_ERROR_NONE) + { + break; + } + } + } + +exit: + if (needClose) + { + nvm3_close(&handle); + } + return err; +} + +static nvm3_ObjectKey_t makeNvm3ObjKey(uint16_t otSettingsKey, int index) +{ + return (OT_NVM3_SETTINGS_KEY_PREFIX | (otSettingsKey << 8) | (index & 0xFF)); +} + +static otError mapNvm3Error(Ecode_t nvm3Res) +{ + otError err; + + switch (nvm3Res) + { + case ECODE_NVM3_OK: + err = OT_ERROR_NONE; + break; + + case ECODE_NVM3_ERR_KEY_NOT_FOUND: + err = OT_ERROR_NOT_FOUND; + break; + + default: + err = OT_ERROR_FAILED; + break; + } + + return err; +} + +#endif // !OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE diff --git a/examples/platforms/efr32mg13/openthread-core-efr32-config.h b/examples/platforms/efr32mg13/openthread-core-efr32-config.h index 0b8a50d71..d7eead986 100644 --- a/examples/platforms/efr32mg13/openthread-core-efr32-config.h +++ b/examples/platforms/efr32mg13/openthread-core-efr32-config.h @@ -111,7 +111,7 @@ * When defined to 1, the platform MUST implement the otPlatFlash* APIs instead of the otPlatSettings* APIs. * */ -#define OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE 1 +#define OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE 0 /** * @def OPENTHREAD_CONFIG_NCP_UART_ENABLE diff --git a/examples/platforms/efr32mg21/Makefile.am b/examples/platforms/efr32mg21/Makefile.am index 589f0e5f9..ef1eab658 100644 --- a/examples/platforms/efr32mg21/Makefile.am +++ b/examples/platforms/efr32mg21/Makefile.am @@ -72,6 +72,8 @@ libopenthread_efr32mg21_a_CPPFLAGS = -I$(SDK_SRC_DIR)/platform/emdrv/ustimer/inc \ -I$(SDK_SRC_DIR)/platform/emdrv/dmadrv/inc \ -I$(SDK_SRC_DIR)/platform/emdrv/dmadrv/config \ + -I$(SDK_SRC_DIR)/platform/emdrv/nvm3/inc \ + -I$(SDK_SRC_DIR)/platform/emdrv/nvm3/config \ -I$(SDK_SRC_DIR)/platform/emlib/inc \ -I$(SDK_SRC_DIR)/platform/halconfig/inc/hal-config \ -I$(SDK_SRC_DIR)/platform/radio/rail_lib/chip/efr32 \ diff --git a/examples/platforms/efr32mg21/Makefile.platform.am b/examples/platforms/efr32mg21/Makefile.platform.am index adcfac409..03a85ae19 100644 --- a/examples/platforms/efr32mg21/Makefile.platform.am +++ b/examples/platforms/efr32mg21/Makefile.platform.am @@ -41,8 +41,9 @@ LDADD_COMMON += $(top_builddir)/examples/platforms/efr32mg21/libopenthread-efr32mg21.a \ $(top_builddir)/third_party/silabs/libsilabs-efr32mg21-sdk.a \ $(top_srcdir)/third_party/silabs/gecko_sdk_suite/v2.7/platform/radio/rail_lib/autogen/librail_release/$(LIBRAIL) \ + $(top_srcdir)/third_party/silabs/gecko_sdk_suite/v2.7/platform/emdrv/nvm3/lib/libnvm3_CM33_gcc.a \ $(NULL) - + LDFLAGS_COMMON += \ - -T $(top_srcdir)/third_party/silabs/gecko_sdk_suite/v2.7/platform/Device/SiliconLabs/EFR32MG21/Source/GCC/efr32mg21.ld \ + -T $(top_srcdir)/examples/platforms/efr32mg21/efr32mg21.ld \ $(NULL) diff --git a/examples/platforms/efr32mg21/efr32mg21.ld b/examples/platforms/efr32mg21/efr32mg21.ld new file mode 100644 index 000000000..48e66e204 --- /dev/null +++ b/examples/platforms/efr32mg21/efr32mg21.ld @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2020, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + /** + * @file + * This file implements the OpenThread linker script for the + * Silicon Labs efr32mg21 platform. + * + */ + +MEMORY +{ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 1024K + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 96K +} + +/* Linker script to place sections and symbol values. Should be used together + * with other linker script that defines memory regions FLASH and RAM. + * It references following symbols, which must be defined in code: + * Reset_Handler : Entry of reset handler + * + * It defines following symbols, which code can use without definition: + * __exidx_start + * __exidx_end + * __copy_table_start__ + * __copy_table_end__ + * __zero_table_start__ + * __zero_table_end__ + * __etext + * __data_start__ + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __data_end__ + * __bss_start__ + * __bss_end__ + * __end__ + * end + * __HeapBase + * __HeapLimit + * __StackLimit + * __StackTop + * __stack + * __Vectors_End + * __Vectors_Size + */ +ENTRY(Reset_Handler) + +SECTIONS +{ + .text : + { + KEEP(*(.vectors)) + __Vectors_End = .; + __Vectors_Size = __Vectors_End - __Vectors; + __end__ = .; + + *(.text*) + + KEEP(*(.init)) + KEEP(*(.fini)) + + /* .ctors */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + + /* .dtors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + *(.rodata*) + + KEEP(*(.eh_frame*)) + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + /* To copy multiple ROM to RAM sections, + * uncomment .copy.table section and, + * define __STARTUP_COPY_MULTIPLE in startup_ARMCMx.S */ + /* + .copy.table : + { + . = ALIGN(4); + __copy_table_start__ = .; + LONG (__etext) + LONG (__data_start__) + LONG (__data_end__ - __data_start__) + LONG (__etext2) + LONG (__data2_start__) + LONG (__data2_end__ - __data2_start__) + __copy_table_end__ = .; + } > FLASH + */ + + /* To clear multiple BSS sections, + * uncomment .zero.table section and, + * define __STARTUP_CLEAR_BSS_MULTIPLE in startup_ARMCMx.S */ + /* + .zero.table : + { + . = ALIGN(4); + __zero_table_start__ = .; + LONG (__bss_start__) + LONG (__bss_end__ - __bss_start__) + LONG (__bss2_start__) + LONG (__bss2_end__ - __bss2_start__) + __zero_table_end__ = .; + } > FLASH + */ + + __etext = .; + + .data : AT (__etext) + { + __data_start__ = .; + *(vtable) + *(.data*) + . = ALIGN (4); + PROVIDE (__ram_func_section_start = .); + *(.ram) + PROVIDE (__ram_func_section_end = .); + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + + KEEP(*(.jcr*)) + . = ALIGN(4); + /* All data end */ + __data_end__ = .; + + } > RAM + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (COPY): + { + __HeapBase = .; + __end__ = .; + end = __end__; + _end = __end__; + KEEP(*(.heap*)) + __HeapLimit = .; + } > RAM + + /* .stack_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later */ + .stack_dummy (COPY): + { + KEEP(*(.stack*)) + } > RAM + + /* Set stack top to end of RAM, and stack limit move down by + * size of stack_dummy section */ + __StackTop = ORIGIN(RAM) + LENGTH(RAM); + __StackLimit = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + /*******************************************************************/ + /* Define 32K (8000H) byte flash block for OT nvm3- */ + /* ( == 4 x Flash Pages as configured in flash.c) */ + .otNvm3_block (LENGTH(FLASH) - 0x8000) (NOLOAD): + { + . = ALIGN (8192); + __nvm3OtBase = .; + KEEP(*(otNvm3_section)); + __nvm3OtCeiling = .; + } > FLASH + /*******************************************************************/ + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack") + + /* Check if FLASH usage exceeds FLASH size */ + /*ASSERT( LENGTH(FLASH) >= (__etext + SIZEOF(.data)), "FLASH memory overflowed !")*/ +} \ No newline at end of file diff --git a/examples/platforms/efr32mg21/flash.c b/examples/platforms/efr32mg21/flash.c index 1c63c65c8..88ee4ca7a 100644 --- a/examples/platforms/efr32mg21/flash.c +++ b/examples/platforms/efr32mg21/flash.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, The OpenThread Authors. + * Copyright (c) 2020, The OpenThread Authors. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,69 +28,357 @@ /** * @file - * This file implements the OpenThread platform abstraction for the non-volatile storage. + * This file implements the OpenThread platform abstraction for the non-volatile + * storage for the EFR32 platform using the Silabs Nvm3 interface. */ +#include +#include + +#if !OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE + +#include "nvm3.h" +#include "nvm3_hal_flash.h" #include +#include "common/code_utils.hpp" -#include +// Two macros are provided to support the creation of the Silicon Labs NVM3 area and +// initialization data- NVM3_DEFINE_SECTION_STATIC_DATA() and NVM3_DEFINE_SECTION_INIT_DATA(). +// A linker section called 'name'_section is defined by NVM3_DEFINE_SECTION_STATIC_DATA(). +// The NVM3 area is placed at the top of the device FLASH section by the linker script +// file: e.g. efr32mgxx.ld +// An error is returned by nvm3_open() on alignment or size violation. -#include "em_msc.h" +// Local version of SDK macro (avoids uninitialized var compile error). +#define OT_NVM3_DEFINE_SECTION_STATIC_DATA(name, nvmSize, cacheSize) \ + static nvm3_CacheEntry_t name##_cache[cacheSize]; \ + static uint8_t name##_nvm[nvmSize] SL_ATTRIBUTE_SECTION(STRINGIZE(name##_section)) -#define FLASH_PAGE_NUM 4 -#define FLASH_DATA_END_ADDR (FLASH_BASE + FLASH_SIZE) -#define FLASH_DATA_START_ADDR (FLASH_DATA_END_ADDR - (FLASH_PAGE_SIZE * FLASH_PAGE_NUM)) -#define FLASH_SWAP_PAGE_NUM (FLASH_PAGE_NUM / 2) -#define FLASH_SWAP_SIZE (FLASH_PAGE_SIZE * FLASH_SWAP_PAGE_NUM) - -static inline uint32_t mapAddress(uint8_t aSwapIndex, uint32_t aOffset) -{ - uint32_t address; - - address = FLASH_DATA_START_ADDR + aOffset; - - if (aSwapIndex) - { - address += FLASH_SWAP_SIZE; +// Local version of SDK macro (allows OT to configure the maximum nvm3 object size and headroom). +#define OT_NVM3_DEFINE_SECTION_INIT_DATA(name, maxObjectSize, repackHeadroom) \ + static nvm3_Init_t name = { \ + (nvm3_HalPtr_t)name##_nvm, \ + sizeof(name##_nvm), \ + name##_cache, \ + sizeof(name##_cache) / sizeof(nvm3_CacheEntry_t), \ + maxObjectSize, \ + repackHeadroom, \ + &nvm3_halFlashHandle, \ } - return address; -} +#define NUM_SETTINGS_OBJECTS 7 // == number of enumerated Settings key types (in settings.hpp). +#define NUM_INDEXED_SETTINGS \ + OPENTHREAD_CONFIG_MLE_MAX_CHILDREN // Indexed key types are only supported for kKeyChildInfo (=='child table'). +#define NUM_USER_OBJECTS 16 // User nvm3 objects (nvm3 key range 0x0000 -> 0xDFFF is available for user data). -void otPlatFlashInit(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); -} +#define OT_NVM3_FLASH_NUM_PAGES 4 // Note- nvm3 requires minimum of 3 pages. +#define OT_NVM3_FLASH_PAGE_SIZE FLASH_PAGE_SIZE // Device flash page size (MG12/MG13=2K, MG21=8K) +#define OT_NVM3_FLASH_SIZE OT_NVM3_FLASH_NUM_PAGES *OT_NVM3_FLASH_PAGE_SIZE +#define OT_NVM3_MAX_NUM_OBJECTS NUM_SETTINGS_OBJECTS + NUM_INDEXED_SETTINGS + NUM_USER_OBJECTS +#define OT_NVM3_MAX_OBJECT_SIZE 256 +#define OT_NVM3_REPACK_HEADROOM 64 // Threshold for automatic nvm3 flash repacking. -uint32_t otPlatFlashGetSwapSize(otInstance *aInstance) +#define OT_NVM3_SETTINGS_KEY_PREFIX 0xE000 // (=> key range 0x0000-0xDFFF is available for OT User nvm3 objects). + +#define ENUM_NVM3_KEY_LIST_SIZE 4 // List size used when enumerating nvm3 keys. + +static nvm3_Handle_t handle = {NULL}; + +static otError addSetting(uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength); +static nvm3_ObjectKey_t makeNvm3ObjKey(uint16_t otSettingsKey, int index); +static otError mapNvm3Error(Ecode_t nvm3Res); + +// Declare OT NVM3 data area and cache. +// OT data area should be enough for OT 'Settings' and whatwever User data is required. + +OT_NVM3_DEFINE_SECTION_STATIC_DATA(otNvm3, OT_NVM3_FLASH_SIZE, OT_NVM3_MAX_NUM_OBJECTS); + +OT_NVM3_DEFINE_SECTION_INIT_DATA(otNvm3, OT_NVM3_MAX_OBJECT_SIZE, OT_NVM3_REPACK_HEADROOM); + +void otPlatSettingsInit(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); - return FLASH_SWAP_SIZE; -} + otError err; + bool needClose = false; -void otPlatFlashErase(otInstance *aInstance, uint8_t aSwapIndex) -{ - OT_UNUSED_VARIABLE(aInstance); + err = mapNvm3Error(nvm3_open(&handle, &otNvm3)); + SuccessOrExit(err); + needClose = true; - uint32_t address = mapAddress(aSwapIndex, 0); - - for (uint32_t n = 0; n < FLASH_SWAP_PAGE_NUM; n++, address += FLASH_PAGE_SIZE) +exit: + if (needClose) { - MSC_ErasePage((uint32_t *)address); + nvm3_close(&handle); } } -void otPlatFlashWrite(otInstance *aInstance, uint8_t aSwapIndex, uint32_t aOffset, const void *aData, uint32_t aSize) +void otPlatSettingsDeinit(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); - MSC_WriteWord((uint32_t *)mapAddress(aSwapIndex, aOffset), aData, aSize); + nvm3_close(&handle); } -void otPlatFlashRead(otInstance *aInstance, uint8_t aSwapIndex, uint32_t aOffset, void *aData, uint32_t aSize) +otError otPlatSettingsGet(otInstance *aInstance, uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength) +{ + // Searches through all matching nvm3 keys to find the one with the required + // 'index', then reads the nvm3 data into the destination buffer. + // (Repeatedly enumerates a list of matching keys from the nvm3 until the + // required index is found). + + OT_UNUSED_VARIABLE(aInstance); + + otError err; + bool needClose = false; + uint16_t valueLength = 0; + + err = mapNvm3Error(nvm3_open(&handle, &otNvm3)); + SuccessOrExit(err); + needClose = true; + + nvm3_ObjectKey_t nvm3Key = makeNvm3ObjKey(aKey, 0); // The base nvm3 key value. + bool idxFound = false; + int idx = 0; + err = OT_ERROR_NOT_FOUND; + while ((idx <= NUM_INDEXED_SETTINGS) && (!idxFound)) + { + // Get the next nvm3 key list. + nvm3_ObjectKey_t keys[ENUM_NVM3_KEY_LIST_SIZE]; // List holds the next set of nvm3 keys. + size_t objCnt = nvm3_enumObjects(&handle, keys, ENUM_NVM3_KEY_LIST_SIZE, nvm3Key, + makeNvm3ObjKey(aKey, NUM_INDEXED_SETTINGS)); + for (size_t i = 0; i < objCnt; ++i) + { + nvm3Key = keys[i]; + if (idx == aIndex) + { + uint32_t objType; + size_t objLen; + err = mapNvm3Error(nvm3_getObjectInfo(&handle, nvm3Key, &objType, &objLen)); + if (err == OT_ERROR_NONE) + { + valueLength = objLen; + + // Only perform read if an input buffer was passed in. + if ((aValue != NULL) && (aValueLength != NULL)) + { + // Read all nvm3 obj bytes into a tmp buffer, then copy the required + // number of bytes to the read destination buffer. + uint8_t *buf = malloc(valueLength); + err = mapNvm3Error(nvm3_readData(&handle, nvm3Key, buf, valueLength)); + if (err == OT_ERROR_NONE) + { + memcpy(aValue, buf, (valueLength < *aValueLength) ? valueLength : *aValueLength); + } + free(buf); + SuccessOrExit(err); + } + } + idxFound = true; + break; + } + ++idx; + } + if (objCnt < ENUM_NVM3_KEY_LIST_SIZE) + { + // Stop searching (there are no more matching nvm3 objects). + break; + } + ++nvm3Key; // Inc starting value for next nvm3 key list enumeration. + } + +exit: + if (needClose) + { + nvm3_close(&handle); + } + + if (aValueLength != NULL) + { + *aValueLength = valueLength; // always return actual nvm3 object length. + } + + return err; +} + +otError otPlatSettingsSet(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength) { OT_UNUSED_VARIABLE(aInstance); - memcpy(aData, (const uint8_t *)mapAddress(aSwapIndex, aOffset), aSize); + otError err; + + // Delete all nvm3 objects matching the input key (i.e. the 'setting indexes' of the key). + err = otPlatSettingsDelete(aInstance, aKey, -1); + if ((err == OT_ERROR_NONE) || (err == OT_ERROR_NOT_FOUND)) + { + // Add new setting object (i.e. 'index0' of the key). + err = addSetting(aKey, aValue, aValueLength); + SuccessOrExit(err); + } + +exit: + return err; } + +otError otPlatSettingsAdd(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength) +{ + OT_UNUSED_VARIABLE(aInstance); + + return addSetting(aKey, aValue, aValueLength); +} + +otError otPlatSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex) +{ + // Searches through all matching nvm3 keys to find the one with the required + // 'index' (or index = -1 to delete all), then deletes the nvm3 object. + // (Repeatedly enumerates a list of matching keys from the nvm3 until the + // required index is found). + + OT_UNUSED_VARIABLE(aInstance); + + otError err; + bool needClose = false; + + err = mapNvm3Error(nvm3_open(&handle, &otNvm3)); + SuccessOrExit(err); + needClose = true; + + nvm3_ObjectKey_t nvm3Key = makeNvm3ObjKey(aKey, 0); // The base nvm3 key value. + bool idxFound = false; + int idx = 0; + err = OT_ERROR_NOT_FOUND; + while ((idx <= NUM_INDEXED_SETTINGS) && (!idxFound)) + { + // Get the next nvm3 key list. + nvm3_ObjectKey_t keys[ENUM_NVM3_KEY_LIST_SIZE]; // List holds the next set of nvm3 keys. + size_t objCnt = nvm3_enumObjects(&handle, keys, ENUM_NVM3_KEY_LIST_SIZE, nvm3Key, + makeNvm3ObjKey(aKey, NUM_INDEXED_SETTINGS)); + for (size_t i = 0; i < objCnt; ++i) + { + nvm3Key = keys[i]; + if ((idx == aIndex) || (aIndex == -1)) + { + uint32_t objType; + size_t objLen; + err = mapNvm3Error(nvm3_getObjectInfo(&handle, nvm3Key, &objType, &objLen)); + if (err == OT_ERROR_NONE) + { + // Delete the nvm3 object. + err = mapNvm3Error(nvm3_deleteObject(&handle, nvm3Key)); + SuccessOrExit(err); + } + if (aIndex != -1) + { + idxFound = true; + break; + } + } + ++idx; + } + if (objCnt < ENUM_NVM3_KEY_LIST_SIZE) + { + // Stop searching (there are no more matching nvm3 objects). + break; + } + ++nvm3Key; // Inc starting value for next nvm3 key list enumeration. + } + +exit: + if (needClose) + { + nvm3_close(&handle); + } + + return err; +} + +void otPlatSettingsWipe(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + + // Delete nvm3 objects for all OT Settings keys (and any of their associated 'indexes'). + // Note- any OT User nvm3 objects in the OT nvm3 area are NOT be erased. + for (uint16_t aKey = 0; aKey < 8; ++aKey) + { + otPlatSettingsDelete(NULL, aKey, -1); + } +} + +// Local functions.. + +static otError addSetting(uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength) +{ + // Helper function- writes input buffer data to a NEW nvm3 object. + // nvm3 object is created at the first available Key + index. + + otError err; + bool needClose = false; + + err = mapNvm3Error(nvm3_open(&handle, &otNvm3)); + SuccessOrExit(err); + needClose = true; + + if ((aValueLength == 0) || (aValue == NULL)) + { + err = OT_ERROR_INVALID_ARGS; + } + else + { + for (int idx = 0; idx <= NUM_INDEXED_SETTINGS; ++idx) + { + nvm3_ObjectKey_t nvm3Key; + nvm3Key = makeNvm3ObjKey(aKey, idx); + + uint32_t objType; + size_t objLen; + err = mapNvm3Error(nvm3_getObjectInfo(&handle, nvm3Key, &objType, &objLen)); + if (err == OT_ERROR_NOT_FOUND) + { + // Use this index for the new nvm3 object. + // Write the binary data to nvm3 (Creates nvm3 object if required). + err = mapNvm3Error(nvm3_writeData(&handle, nvm3Key, aValue, aValueLength)); + break; + } + else if (err != OT_ERROR_NONE) + { + break; + } + } + } + +exit: + if (needClose) + { + nvm3_close(&handle); + } + return err; +} + +static nvm3_ObjectKey_t makeNvm3ObjKey(uint16_t otSettingsKey, int index) +{ + return (OT_NVM3_SETTINGS_KEY_PREFIX | (otSettingsKey << 8) | (index & 0xFF)); +} + +static otError mapNvm3Error(Ecode_t nvm3Res) +{ + otError err; + + switch (nvm3Res) + { + case ECODE_NVM3_OK: + err = OT_ERROR_NONE; + break; + + case ECODE_NVM3_ERR_KEY_NOT_FOUND: + err = OT_ERROR_NOT_FOUND; + break; + + default: + err = OT_ERROR_FAILED; + break; + } + + return err; +} + +#endif // !OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE diff --git a/examples/platforms/efr32mg21/openthread-core-efr32-config.h b/examples/platforms/efr32mg21/openthread-core-efr32-config.h index 48a979453..244e13823 100644 --- a/examples/platforms/efr32mg21/openthread-core-efr32-config.h +++ b/examples/platforms/efr32mg21/openthread-core-efr32-config.h @@ -108,7 +108,7 @@ * When defined to 1, the platform MUST implement the otPlatFlash* APIs instead of the otPlatSettings* APIs. * */ -#define OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE 1 +#define OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE 0 /** * @def RADIO_CONFIG_SRC_MATCH_SHORT_ENTRY_NUM diff --git a/third_party/silabs/Makefile.am b/third_party/silabs/Makefile.am index 187b465bb..47b9c59c1 100644 --- a/third_party/silabs/Makefile.am +++ b/third_party/silabs/Makefile.am @@ -90,6 +90,8 @@ COMMONCPPFLAGS = -I$(SDK_SRC_DIR)/platform/emdrv/ustimer/inc \ -I$(SDK_SRC_DIR)/platform/emdrv/dmadrv/inc \ -I$(SDK_SRC_DIR)/platform/emdrv/dmadrv/config \ + -I$(SDK_SRC_DIR)/platform/emdrv/nvm3/inc \ + -I$(SDK_SRC_DIR)/platform/emdrv/nvm3/config \ -I$(SDK_SRC_DIR)/platform/emlib/inc \ -I$(SDK_SRC_DIR)/platform/halconfig/inc/hal-config \ -I$(SDK_SRC_DIR)/platform/radio/rail_lib/chip/efr32 \ @@ -134,6 +136,8 @@ SILABS_COMMON_SOURCES gecko_sdk_suite/v2.7/hardware/kit/common/bsp/bsp_stk_leds.c \ gecko_sdk_suite/v2.7/platform/emdrv/dmadrv/src/dmadrv.c \ gecko_sdk_suite/v2.7/platform/emdrv/gpiointerrupt/src/gpiointerrupt.c \ + gecko_sdk_suite/v2.7/platform/emdrv/nvm3/src/nvm3_hal_flash.c \ + gecko_sdk_suite/v2.7/platform/emdrv/nvm3/src/nvm3_lock.c \ gecko_sdk_suite/v2.7/platform/emdrv/uartdrv/src/uartdrv.c \ gecko_sdk_suite/v2.7/platform/emdrv/ustimer/src/ustimer.c \ gecko_sdk_suite/v2.7/platform/emlib/src/em_adc.c \