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 Figure 1

'The two-step Process for Setting the Flash Adapter Mechanism'

Figure 1 The Two-Step Process for Setting the Flash Adapter Mechanism

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 DA1469x SoC.

  • There is no need to declare device parameters as it is assumed that only one device/flash is connected on the QSPI1 bus.

In both cases, the NVMS mechanism automatically configures the correct signals and bus parameters during NVMS operations.

2.1. Preparing an NVMS Operation

  1. The first step for configuring the NVMS adapter mechanism is to enable it by defining the following macros in /config/custom_config_xxx.h file:

 * Macros for enabling the NVMS adapter mechanism
#define dg_configFLASH_ADAPTER                  (1)
#define dg_configNVMS_ADAPTER                   (1)

 * This feature is required when accessing the [NVMS_PARAM_PART]
 * partition entry.
#define dg_configNVPARAM_ADAPTER                (0)

 * This feature is required when accessing the [NVMS_GENERIC_PART]
 * partition entry.
#define dg_configNVMS_VES                       (0)

The overall adapter implementation with all its integrated functions is now available.

  1. 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/erase memory operations. All NVMS related APIs can be found in /sdk/adapters/include/ad_nvms.h file.

    1. ad_nvms_init()

      This must be called once at either platform start (system_init()) or task initialization to perform all the necessary NVMS initialization, including discovering all the underlying storage partitions.

    2. ad_nvms_open()

      Before performing any read/write/erase activity in a FLASH partition entry, it must first be opened. Once the target FLASH partition is opended, there is no need for other application tasks to reopen it. Valid partition entries are those found in the nvms_partition_id_t enum in sdk/adapters/include/partition_def.h file.

    3. Call the appropriate APIs to write/read data to/from the target QSPI FLASH memory. Erase operations can be performed as well.


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 DA1469x 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 file.

  1. Call the ad_nvparam_open() API to open the NVMS_PARAM_PART partition entry. This must be done before any read or write activity can be performed.

  2. Call the appropriate APIs to perform read/write/erase operations.

  3. Call ad_nvparam_close() to release and free any resource previously allocated by the ad_nvparam_open() API.


The default name associated with this partition entry is ble_platform and is declared in sdk/adapters/inlcude/platform_nvparam.h file.

2.2. 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 NVMS operations are handled by the SDK. When the macro is set to zero (default value), all NVMS operations take place when the system is idle, that is, when there is no task in active state. This demonstrates the importance of giving the system the time to perform NVMS operations (when requested). The reasons for this approach are:

  • In general, FLASH operations are a slow process compared with other memory operations (RAM). In addition, when a write operation is attempted and the FLASH is not clear (that is, its content is not 0xFF), the entire sector where the data dwells must be erased. For example, the Macronix 32-Mbit FLASH, which is the default QSPI FLASH memory, 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:

    1. The whole sector is copied into a buffer in System RAM. All related settings for controlling this buffer are located in sdk/adapters/inlcude/ad_nvms_direct.h file. By default, a static buffer is used to ensure that the memory is always available.

    2. 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.

    3. The sector is erased.

    4. The sector is written with the contents of the previously written buffer.

  • When performing NVMS operations, the SDK disables all the interrupts of the system to prevent another task from accessing the FLASH while an NVMS operation is in progress. By default, the SDK has been configured to write 128 bytes (half page size) at a time to the FLASH page (Macronix 32-Mbit has a page of 256 bytes). The smaller this value is, the more time it takes for an NVMS operation to finish. On the other hand, the smaller the value, the more frequently the system suspends NVMS operations to serve pending interrupts (issued while an NVMS operation was in progress).

'NVMS Overview'

Figure 2 NVMS Overview

2.3. Modes of Operation

Before discussing the supported modes of operation, we need to mention some limitations introduced by FLASH memories:

  1. Data cannot always be written to FLASH without performing an erase cycle first.

  2. An erase cycle is limited to a full sector, that is, a whole sector must be erased (for the used Macronix, each sector is 4 kBytes in size which is a relatively big chunk of data).

  3. 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.3.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.


  • 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.3.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 sdk/adapters/inlcude/ad_nvms_ves.h file.

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.

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. On the other hand, the smaller the size of a container, the more chances an erase cycle to be issued.

'Virtual EEPROM Storage Model'

Figure 3 Virtual EEPROM Storage Model


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.