# Himem

[English]

## 概述

对于小于等于 4 MiB 大小的外部内存，MMU 配置为使用“统一映射”，即每个 CPU 地址一对一地映射到外部 SPI RAM 地址，从而透明地访问外部内存。但是，由于外部内存的地址空间只有 4 MiB，因此只有小于等于 4 MiB 大小的 SPI RAM 芯片才能完全透明地使用。

ESP32 仍可使用大于等于 4 MiB 大小的 SPI RAM 芯片。不过，这些芯片上的内存需要通过存储体切换方案 (bank switching) 来访问。ESP-IDF 提供了 Himem API 来控制此类存储体切换。具体而言，Himem API 允许在运行时切换地址映射，将特定的 32 K 存储体映射到 4 MiB 地址空间中，从而访问超过 4 MiB 的外部内存。

## 使用注意事项

使用 Himem API 前，必须在 menuconfig 中启用 CONFIG_SPIRAM_BANKSWITCH_ENABLE，并在 CONFIG_SPIRAM_BANKSWITCH_RESERVE 中设置为此预留的储存体数量。这会减少由 `malloc()` 等函数分配的外部内存量，但允许使用 Himem API 将任何剩余内存映射到预留的存储体中。

Himem API 可以看作是存储体切换方案的一个抽象。具体而言，该 API 允许声明一个或多个地址空间存储体（在 API 中称为“regions”），以及一个或多个需映射到此范围的内存存储体。

## 示例

ESP-IDF 中提供了对大内存范围进行简单内存测试的示例，请前往 system/himem 查看。

## API 参考

### Header File

- components/esp_psram/include/esp32/himem.h

- This header file can be included with:

  >     #include "esp32/himem.h"

- This header file is a part of the API provided by the `esp_psram` component. To declare that your component depends on `esp_psram`, add the following to your CMakeLists.txt:

  >     REQUIRES esp_psram

  > or

  >     PRIV_REQUIRES esp_psram

### Functions

esp_err_t esp_himem_alloc(size_t size, esp_himem_handle_t *handle_out)

  Allocate a block in high memory.

  参数:

    - 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

  返回:

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

  参数:

    - 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

  返回:

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

  备注

  The region to be mapped needs to have offsets and sizes that are aligned to the SPI RAM MMU block size (32K)

  参数:

    - 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

  返回:

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

  参数:

    handle -- Handle to the block of memory, as given by esp_himem_alloc

  返回:

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

  参数:

    handle -- Handle to the range block, as given by esp_himem_alloc_map_range

  返回:

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

  参数:

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

  返回:

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

  返回:

    Amount of memory, in bytes

size_t esp_himem_get_free_size(void)

  Get free amount of memory under control of himem API.

  返回:

    Amount of free memory, in bytes

size_t esp_himem_reserved_area_size(void)

  Get amount of SPI memory address space needed for bankswitching.

  备注

  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.

  返回:

    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

提供有关此文档的反馈