The himem allocation API
Overview
The ESP32 can access external SPI RAM transparently, so you can use it as normal memory in your program code. However, because the address space for external memory is limited in size, only the first 4MiB can be used as such. Access to the remaining memory is still possible, however this needs to go through a bankswitching scheme controlled by the himem API.
Specifically, what is implemented by the himem API is a bankswitching scheme. Hardware-wise, the 4MiB region for external SPI RAM is mapped into the CPU address space by a MMU, which maps a configurable 32K bank/page of external SPI RAM into each of the 32K pages in the 4MiB region accessed by the CPU. For external memories that are <=4MiB, this MMU is configured to unity mapping, effectively mapping each CPU address 1-to-1 to the external SPI RAM address.
In order to use the himem API, you have to enable it in the menuconfig using CONFIG_SPIRAM_BANKSWITCH_ENABLE, as well as set the amount
of banks reserved for this in CONFIG_SPIRAM_BANKSWITCH_RESERVE. This decreases
the amount of external memory allocated by functions like malloc()
, but it allows you to use the himem api to map any of the remaining memory
into the reserved banks.
The himem API is more-or-less an abstraction of the bankswitching scheme: it allows you to claim one or more banks of address space (called ‘regions’ in the API) as well as one or more of banks of memory to map into the ranges.
Example
An example doing a simple memory test of the high memory range is available in esp-idf: system/himem
API Reference
Header File
Functions
-
esp_err_t esp_himem_alloc(size_t size, esp_himem_handle_t *handle_out)
Allocate a block in high memory.
- Parameters
size – Size of the to-be-allocated block, in bytes. Note that this needs to be a multiple of the external RAM mmu block size (32K).
handle_out – [out] Handle to be returned
- Returns
- ESP_OK if succesful
ESP_ERR_NO_MEM if out of memory
ESP_ERR_INVALID_SIZE if size is not a multiple of 32K
-
esp_err_t esp_himem_alloc_map_range(size_t size, esp_himem_rangehandle_t *handle_out)
Allocate a memory region to map blocks into.
This allocates a contiguous CPU memory region that can be used to map blocks of physical memory into.
- Parameters
size – Size of the range to be allocated. Note this needs to be a multiple of the external RAM mmu block size (32K).
handle_out – [out] Handle to be returned
- Returns
- ESP_OK if succesful
ESP_ERR_NO_MEM if out of memory or address space
ESP_ERR_INVALID_SIZE if size is not a multiple of 32K
-
esp_err_t esp_himem_map(esp_himem_handle_t handle, esp_himem_rangehandle_t range, size_t ram_offset, size_t range_offset, size_t len, int flags, void **out_ptr)
Map a block of high memory into the CPUs address space.
This effectively makes the block available for read/write operations.
Note
The region to be mapped needs to have offsets and sizes that are aligned to the SPI RAM MMU block size (32K)
- Parameters
handle – Handle to the block of memory, as given by esp_himem_alloc
range – Range handle to map the memory in
ram_offset – Offset into the block of physical memory of the block to map
range_offset – Offset into the address range where the block will be mapped
len – Length of region to map
flags – One of ESP_HIMEM_MAPFLAG_*
out_ptr – [out] Pointer to variable to store resulting memory pointer in
- Returns
- ESP_OK if the memory could be mapped
ESP_ERR_INVALID_ARG if offset, range or len aren’t MMU-block-aligned (32K)
ESP_ERR_INVALID_SIZE if the offsets/lengths don’t fit in the allocated memory or range
ESP_ERR_INVALID_STATE if a block in the selected ram offset/length is already mapped, or if a block in the selected range offset/length already has a mapping.
-
esp_err_t esp_himem_free(esp_himem_handle_t handle)
Free a block of physical memory.
This clears out the associated handle making the memory available for re-allocation again. This will only succeed if none of the memory blocks currently have a mapping.
- Parameters
handle – Handle to the block of memory, as given by esp_himem_alloc
- Returns
- ESP_OK if the memory is succesfully freed
ESP_ERR_INVALID_ARG if the handle still is (partially) mapped
-
esp_err_t esp_himem_free_map_range(esp_himem_rangehandle_t handle)
Free a mapping range.
This clears out the associated handle making the range available for re-allocation again. This will only succeed if none of the range blocks currently are used for a mapping.
- Parameters
handle – Handle to the range block, as given by esp_himem_alloc_map_range
- Returns
- ESP_OK if the memory is succesfully freed
ESP_ERR_INVALID_ARG if the handle still is (partially) mapped to
-
esp_err_t esp_himem_unmap(esp_himem_rangehandle_t range, void *ptr, size_t len)
Unmap a region.
- Parameters
range – Range handle
ptr – Pointer returned by esp_himem_map
len – Length of the block to be unmapped. Must be aligned to the SPI RAM MMU blocksize (32K)
- Returns
- ESP_OK if the memory is succesfully unmapped,
ESP_ERR_INVALID_ARG if ptr or len are invalid.
-
size_t esp_himem_get_phys_size(void)
Get total amount of memory under control of himem API.
- Returns
Amount of memory, in bytes
-
size_t esp_himem_get_free_size(void)
Get free amount of memory under control of himem API.
- Returns
Amount of free memory, in bytes
-
size_t esp_himem_reserved_area_size(void)
Get amount of SPI memory address space needed for bankswitching.
Note
This is also weakly defined in esp32/spiram.c and returns 0 there, so if no other function in this file is used, no memory is reserved.
- Returns
Amount of reserved area, in bytes
Macros
-
ESP_HIMEM_BLKSZ
-
ESP_HIMEM_MAPFLAG_RO
Indicates that a mapping will only be read from. Note that this is unused for now.
Type Definitions
-
typedef struct esp_himem_ramdata_t *esp_himem_handle_t
-
typedef struct esp_himem_rangedata_t *esp_himem_rangehandle_t