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 shared memory interface structure is used for both the high-level abstract interface, as well as low-level entries in the memory map.

struct MemoryInterface
{
    status_t (*init)(void);
    status_t (*read)(uint32_t address, uint32_t length, uint8_t * buffer);
    status_t (*write)(uint32_t address, uint32_t length, const uint8_t * buffer);
    status_t (*fill)(uint32_t address, uint32_t length, uint32_t pattern);
    status_t (*flush)(void);
    status_t (*erase)(uint32_t address, uint32_t length)
}

The global bootloader context contains a pointer to the high-level abstract memory interface, which is one of the MemoryInterface structures. 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 is returned if such an attempt is made.

The BootloaderContext::memoryMap member is set to an array of these structures:

struct MemoryMapEntry
{
    uint32_t startAddress;
    uint32_t endAddress;
    bool isExecutable;
    const MemoryInterface * interface;
};

This array must be terminated with an entry with all fields set to zero.

The same MemoryInterface structure is also used to hold the memory-type-specific operations.

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

During bootloader startup, the memory map is copied into RAM and modified to match the actual sizes of flash and RAM on the chip.

Parent topic:Memory interface

Flash driver interface

The flash driver uses the common memory interface to simplify the interaction with flash. It takes care of high level features such as read back 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 =
{
    .read = &flash_mem_read,
    .write = &flash_mem_write,
    .fill = &flash_mem_fill,
    .flush = NULL,
    .erase = flash_mem_erase
};

Bootloader startup code is responsible for initializing the flash memory.

API

Description

flash_mem_read()

Performs a normal memory read if the specified region isn’t protected from reading.

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 patterns are all 1’s, 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_erase()

Calls the low-level flash_erase() API. Also performs erasure verification if enabled with the Set Property command (Enabled by default).

All flash_mem_read(), flash_mem_write(), flash_mem_fill(), and flash_mem_erase() check the flash protection status for the sectors being read or programmed or erased and return an appropriate error if the operation is not allowed.

Parent topic:Memory interface

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.

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

Parent topic:Memory interface