Memory Interface

# Abstract interface

The bootloader uses a common, abstract interface to implement the memory read/write/fill commands. This is to keep the command layer from having to know the details of the memory map and special routines. This memory interface is documented in detail here.

A shared `memory_interface_t` struct is used for both the high-level abstract interface, as well as low-level entries in the memory map.

The global bootloader context contains a pointer to the high-level abstract memory interface, which is one of these `memory_interface_t` structs. The internal implementation of this abstract interface uses a memory map table, referenced from the global bootloader context, that describes the various regions of memory that are accessible and provides region-specific operations.

The high-level functions are implemented to iterate over the memory map entries until it finds the entry for the specified address range. Read and write operations are not permitted to cross region boundaries, and an error will be returned if such an attempt is made.

The `bootloader_context_t::memoryMap` member is set to an array of the `memory_map_entry_t` structures. This array must be terminated with an entry with all fields set to zero. The same `memory_interface_t` struct is also used to hold the memory-type-specific operations.

Note that the `memory_map_entry_t::endAddress` field must be set to the address of the last byte of the region, because a <= comparison is used.

Here is an example memory map:

memory_map_entry_t g_memoryMap[] = {
{0x00000000, 0x0001ffff, kMemoryIsExecutable, &g_flashMemoryInterface}, // Flash array (128KB)
{0x1fffe000, 0x20003fff, kMemoryIsExecutable, &g_normalMemoryInterface}, // SRAM (24KB)
{0x40000000, 0x4007ffff, kMemoryNotExecutable, &g_deviceMemoryInterface}, // AIPS peripherals
{0x400ff000, 0x400fffff, kMemoryNotExecutable, &g_deviceMemoryInterface}, // GPIO
{0xe0000000, 0xe00fffff, kMemoryNotExecutable, &g_deviceMemoryInterface}, // M4 private peripherals
{0} // Terminator
};

During bootloader startup, the memory map is copied into RAM and modified to match the actual sizes of flash and RAM on the chip, as determined by reading the `SIM_SDID` and `SIM_FCFG1/2` registers.

# Flash Driver Interface

The flash driver uses the common memory interface listed above to simplify the interaction with flash. It also takes care of high level features such as readback verification, flash protection awareness, and so on. The flash memory functions map to the interface functions as so:

const memory_region_interface_t g_flashMemoryInterface = {
.init = &flash_mem_init,
.read = &flash_mem_read,
.write = &flash_mem_write,
.fill = &flash_mem_fill,
.flush = flash_mem_flush,
.erase = flash_mem_erase,
};

API | Description
--- | ---
`flash_mem_init()` | Updates the flash memory map.
`flash_mem_read()` | Performs a normal memory read after verify access.
`flash_mem_write()` | Calls the low-level `flash_program()` API. Also performs program verification if enabled with the Set Property command.

`flash_mem_fill()`

Performs intelligent fill operations on flash memory ranges. If the fill pattern are all 1's then special action is taken. If the range is a whole number of sectors, then those sectors are erased rather than filled. Any part of an all-1's fill that is not sector-aligned and -sized is ignored (the assumption being that it has been erased to 1's already). Fills for patterns other than all 1's call into `flash_program()`.

`flash_mem_flush()` | Writes final flash sector, padded with 0xFFs.
`flash_mem_erase()` | Calls the low-level `flash_erase()` API. Also performs program verification if enabled with the Set Property command.

All routines check the flash protection status for the sectors being programmed or erased and return an appropriate error if the operation is not allowed.

Bootloader startup code is responsible for initializing the flash memory controller.

# Low level flash driver

The low level flash driver (LLFD) handles erase and write operations on a word basis. It cannot perform writes of less than a full word.

Bootloader startup code is responsible for initializing and shutting down the LLFD.

typedef struct FlashDriverInterface
{
standard_version_t version;
status_t (*flash_init)(flash_config_t *config);
status_t (*flash_erase_all)(flash_config_t *config, uint32_t key);
status_t (*flash_erase_all_unsecure)(flash_config_t *config, uint32_t key);
status_t (*flash_erase)(flash_config_t *config, uint32_t start, uint32_t lengthInBytes, uint32_t key);
status_t (*flash_program)(flash_config_t *config, uint32_t start, uint32_t *src, uint32_t lengthInBytes);
status_t (*flash_get_security_state)(flash_config_t *config, flash_security_state_t *state);
status_t (*flash_security_bypass)(flash_config_t *config, const uint8_t *backdoorKey);
status_t (*flash_verify_erase_all)(flash_config_t *config, flash_margin_value_t margin);
status_t (*flash_verify_erase)(flash_config_t *config,
uint32_t start,
uint32_t lengthInBytes,
flash_margin_value_t margin);
status_t (*flash_verify_program)(flash_config_t *config,
uint32_t start,
uint32_t lengthInBytes,
const uint32_t *expectedData,
flash_margin_value_t margin,
uint32_t *failedAddress,
uint32_t *failedData);
status_t (*flash_get_property)(flash_config_t *config, flash_property_tag_t whichProperty, uint32_t *value);
status_t (*flash_register_callback)(flash_config_t *config, flash_callback_t callback);
status_t (*flash_program_once)(flash_config_t *config, uint32_t index, uint32_t *src, uint32_t lengthInBytes);
status_t (*flash_read_once)(flash_config_t *config, uint32_t index, uint32_t *dst, uint32_t lengthInBytes);
status_t (*flash_read_resource)(flash_config_t *config,
uint32_t start,
uint32_t *dst,
uint32_t lengthInBytes,
flash_read_resource_option_t option);
status_t (*flash_prepare_execute_in_ram_functions)(flash_config_t *config);
status_t (*flash_is_execute_only)(flash_config_t *config,
uint32_t start,
uint32_t lengthInBytes,
flash_execute_only_access_state_t *access_state);
status_t (*flash_set_flexram_function)(flash_config_t *config, flash_flexram_function_option_t option);
status_t (*flash_program_section)(flash_config_t *config, uint32_t start, uint32_t *src, uint32_t lengthInBytes);
} flash_driver_interface_t;

The Flash Driver API can be called from an external application. For further information see the Flash Driver API example in `apps/flash_driver_api`.