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

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

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

'Headers for Flash Adapters'

Fig. 3 Headers for Flash Adapters.

Table 1 Header Files used by Flash Adapters
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

  1. 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)
'First Step for Configuring the NVMS Adapter Mechanism.'

Fig. 4 First Step for Configuring the NVMS Adapter Mechanism

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 flash memory operations. The NVMS related APIs can be found in /sdk/adapters/include/ad_nvms.h.
'First Step for Configuring the NVMS Adapter Mechanism.'

Fig. 5 Second Step for Configuring the NVMS Adapter Mechanism

  1. Call ad_nvms_init() once (for instance, at platform start in system_init()) to perform the necessary initialization routines, including discovering all the underlying storage partitions.
  2. 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 the nvms_partition_id_t enumeration.
  3. 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.

  1. Call ad_nvparam_open() 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 access the specific partition entry.
  3. Call ad_nvparam_close() to release and free any resource previously allocated by the ad_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:

    1. 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.
    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 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).

'NVMS Overview'

Fig. 6 NVMS Overview

2.4. 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 Winbond, 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.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.

'Virtual EEPROM Storage Model'

Fig. 7 Virtual EEPROM Storage Model

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.