2. Flash Adapter Concept¶
This section explains the main features of Non-Volatile memory adapters as well as the steps to enable and correctly configure the peripheral adapters for accessing a memory through the QSPI controller. The procedure is a two-step process as illustrated in Fig. 2
In contrast with the serial peripheral and GPADC adapters, configuring the non-volatile memory storage (NVMS) adapters is a two-step process. The key differences with the aforementioned adapters are:
- The signals for accessing a flash memory through the QSPI controller are mapped on dedicated pins on the DA1468x SoC.
- There is no need to declare device parameters as it is assumed that only one device/flash is connected on the QSPI bus.
In both cases, the NVMS mechanism automatically configures the correct signals and bus parameters during NVMS operations.
2.1. Header Files¶
All the header files related to adapter functionality can be found in /sdk/adapters/include
. These files contain
the APIs and macros for configuring the majority of the available hardware blocks. In particular, this tutorial
focuses on the adapters that are responsible for the external Non-Volatile Flash Memory.
Table 1 briefly explains the header files related to flash adapters (red indicates the path
under which the files are stored, and green indicates which ones are used for flash operations).
Filename | Description |
---|---|
ad_flash.h | This file contains APIs for performing the actual flash operations. |
ad_nvms.h | This file contains APIs for handling all entries of a partition table. Use these APIs for accessing a region in the flash and performing read/write/erase operations. |
ad_nvparam.h | This file contains APIs for handling the NVMS_PARAM_PART partition entry. Use these APIs when accessing that specific region in the flash. Alternatively, use APIs from the previously described header file. |
ad_nvparam_defs.h | This file contains macros for declaring and configuring the area in the NVMS_PARAM_PART partition entry. Use these macros for declaring a custom structure within the NVMS_PARAM_PART partition entry. |
ad_nvms_direct.h | This file contains macros and APIs for handling flash in Direct mode. |
ad_nvms_ves.h | This file contains macros for handling flash in VES mode (Virtual EEPROM). |
partition_def.h | This file contains macros as well as structures with respect to the partition table. |
partition_table.h | This file selects the partition table used in the application, depending on the developer’s configurations. |
platform_nvparam.h | This file contains the default structure of the NVMS_PARAM_PART partition entry. |
platform_nvparam_values.h | This file contains field values for the NVMS_PARAM_PART partition entry. Use this file to write the required field values. Please note that this file is not used in a regular build. |
2.2. Preparing an NVMS Operation¶
- As illustrated in Fig. 4, to configure the NVMS adapter
mechanism you need to enable it by defining the following macros in the project’s
/config/custom_config_qspi.h
header file:
/*
* Macros for enabling the NVMS adapter mechanism
*/
#define dg_configFLASH_ADAPTER (1)
#define dg_configNVMS_ADAPTER (1)
/*
* Additional mechanism for accessing the [NVMS_PARAM_PART] partition entry.
*/
#define dg_configNVPARAM_ADAPTER (1)
/*
* This feature is only applied in [NVMS_GENERIC_PART] partition entry
* and is mandatory to enable it when accessing that specific area!
*/
#define dg_configNVMS_VES (0)
The overall adapter implementation with all its integrated functions is now available.
- As the NVMS adapter mechanism is enabled, the developer can use the available APIs to perform
NVMS operations. Carry out the following sequence of APIs in an application to successfully
perform read/write flash memory operations. The NVMS related APIs can be found in
/sdk/adapters/include/ad_nvms.h
.
- Call
ad_nvms_init()
once (for instance, at platform start insystem_init()
) to perform the necessary initialization routines, including discovering all the underlying storage partitions.- Call
ad_nvms_open()
to open a partition entry. This must be done before any read/write/delete activity can be performed. Valid partition entries are those found in thenvms_partition_id_t
enumeration.- Call the appropriate APIs to write/read/erase data in the flash memory.
Note
If several partitions are stored in one physical device (QSPI Flash), opening one partition will limit reads and writes to this partition only, making all addressing relative to the beginning of this particular partition and not to the whole flash.
The SDK for the DA1468x family of devices provides an additional layer for accessing the NVMS_PARAM_PART
partition entry.
To successfully access the aforementioned partition entry, carry out the following sequence of APIs in an application.
All the related APIs can be found in /sdk/adapters/include/ad_nvparam.h
.
- Call
ad_nvparam_open()
to open theNVMS_PARAM_PART
partition entry. This must be done before any read or write activity can be performed. - Call the appropriate APIs to access the specific partition entry.
- Call
ad_nvparam_close()
to release and free any resource previously allocated by thead_nvparam_open()
API.
Note
The default name associated with this partition entry is ble_platform and is declared in platform_nvparam.h
header file, that is,
NVPARAM_AREA( ble_platform, NVPARAM_PART, 0x0000 )
.
2.3. Handling NVMS Operations¶
This section briefly explains how NVMS adapters are managed by the SDK.
The macro dg_configDISABLE_BACKGROUND_FLASH_OPS
is used to define the way flash operations are handled by the SDK.
This macro is located in /sdk/config/bsp_defaults.h
. When the macro is set to zero (default value),
all flash operations take place when the system is idle, that is, when there is no task in progress. This demonstrates the
importance of giving the system the time to perform flash operations (when requested). The reasons for this approach are:
In general, flash operations are a slow process compared with other peripheral operations. In addition, when a write operation is attempted and the flash is not clear (that is, its content is not 0xFF), the application code must erase the entire sector where the data dwells. For example, the Winbond 8-Mbit Flash, which is the default flash, contains sectors of 4 KBytes each. This means that even if only one byte is ‘dirty’ and a write operation is performed in the area where that byte resides, the whole sector must be erased. Specifically, when an erase operation is issued due to a dirty region, the application code does the following:
- The whole sector is copied into a buffer in RAM. The three settings for controlling this buffer are located in the
ad_nvms_direct.h
header file. By default, a static buffer is used to ensure that the memory is always available. - A write operation is performed. This writes the requested data to the buffer at the point it would be written if it were in flash.
- The sector is erased.
- The sector is written with the contents of the previously written buffer.
- The whole sector is copied into a buffer in RAM. The three settings for controlling this buffer are located in the
When performing flash operations, the SDK disables all the interrupts of the system to prevent another task from accessing the flash while a flash operation is in progress. By default, the SDK has been configured to write 128 bytes at a time to the flash buffer (Winbond 8-Mbit has a buffer of 256 bytes). The smaller this value is, the more time it takes for a flash operation to finish. On the other hand, the smaller the value, the more frequently the system suspends flash operations to serve pending interrupts (issued while a flash operation was in progress).
2.4. Modes of Operation¶
Before discussing the supported modes of operation, we need to mention some limitations introduced by flash memories:
- Data cannot always be written to flash without performing an erase cycle first.
- An erase cycle is limited to a full sector, that is, a whole sector must be erased (for the used Winbond, each sector is 4 kBytes in size which is a relatively big chunk of data).
- A sector can be erased a limited number of times only (guaranteed by manufacturer). After that number of erase cycles, data storage is unreliable.
The SDK currently supports two different modes: Direct Access and VES (Virtual EEPROM).
2.4.1. Direct Access¶
Direct access drivers use relative addresses, from the beginning of a partition entry, without performing any address translation. This means that all writes are performed exactly at the requested address. If a write will not change data (that is, the same data is written) it will not be performed at all. If a write cannot be performed without an erase, then an erase operation is also initiated.
Disadvantages:
- Power failure during a write or erase operation will result in data loss, including data that was not touched by the last write.
- Writing small amounts of data at the same location many times, will result in wearing the flash (the number of write/erase cycles are device-specific, see the corresponding Flash datasheet).
2.4.2. VES¶
VES drivers provide access to a partition entry with power failure and wearing protection. To achieve this, VES drivers write data to random locations within the flash without needing to erase a whole sector when the same location is modified. This is accomplished by writing to different flash locations for the same user provided address. The VES driver provides virtual addressing, that is, the user specified address is translated to a real flash location before a read or write operation. For this to work, the flash size must be bigger than the addressing space visible to the user. A common rule of thumb is 8x the virtual EEPROM size needed.
This rule is employed in SDK using the AD_NVMS_VES_MULTIPLIER
macro, found in the ad_nvms_ves.h
header file. In
particular, for the 1 MByte flash model the generic partition is 128 kBytes (0x20000) hence the virtual address space is around:
partition_entry_size / AD_NVMS_VES_MULTIPLIER = 128 / 8 = 16 kBytes.
In addition, the flash sectors are divided into a number of containers, where each container holds the data
for a range of virtual EEPROM addresses. The size of a container is compile time configurable in 2n bytes and by default has
been configured to 64 Bytes using the AD_NVMS_VES_CONTAINER_SIZE
macro in ad_nvms_ves.h
.
A Container Allocation Table (CAT) stored in RAM, is used for tracking where valid containers are located and a Sector Allocation Table (SAT) holds the status (%dirty, free) of each sector. The selected size for a container is a trade-off between the amount of RAM needed for the CAT and the potential number of erase cycles per sector. The smaller the size of a container, the more RAM is occupied. In particular, each entry in the CAT consists of 2 bytes. Hence, in our case for the 12 kBytes of virtual EEPROM, the formula for calculating the occupied RAM is: 12288 / (64- 4) = 205 CAT entries = 205 * 2 = 410 bytes.
Note
The VES feature should only be used when a small chunk of data is written/modified frequently in
flash. As mentioned earlier, the only area marked as VES is the NVMS_GENERIC_PART
partition entry.
The BLE persistent storage mechanism provided by Dialog uses this partition entry and thus, the
VES feature must be enabled. Otherwise, all the related operations will fail.