内存同步

[English]

简介

ESP32-P4 可以通过以下方式访问其连接的 PSRAM:

  • CPU

  • DMA

ESP32-P4 可以通过以下方式访问其内存:

  • CPU

  • DMA

默认情况下,CPU 通过 cache 访问上述内存,而 DMA 则可以直接访问内存。

这可能导致 cache 数据不一致的问题:

  • 当 DMA 事务更改内存块的内容,并且该内容已经加载到 cache 中时:

    • CPU 可能会读取陈旧数据。

    • cache 中的陈旧数据可能会被写回到内存中,而 DMA 事务更新的新数据将被覆盖。

  • 当 CPU 更改了地址的内容,内容已经加载至 cache 中,但还未存在于内存中(cache 会根据自己的策略将内容写回内存)时:

    • 下一个 DMA 事务从内存中读取此内容,将会获取陈旧数据。

解决此类 cache 数据不一致问题的常见方法有三种:

  1. 基于硬件的 cache 一致性互连,ESP32-P4 没有此功能。

  2. 使用来自 non-cacheable 内存的 DMA 缓冲区(CPU 绕过 cache 访问的内存被称为 non-cacheable 内存)。

  3. 显式调用内存同步 API 将 cache 中的内容写回到内存,或使 cache 中的内容无效化。

内存同步驱动程序

建议使用 ESP-IDF 的 esp_mm 组件所提供的内存同步 API esp_cache_msync() 来处理此类 cache 数据不一致的问题。

驱动程序的概念

cache 与内存同步的方向:

cache 与内存同步的类型:

驱动程序的行为

调用 esp_cache_msync(),可以同步 cache 和内存。第一个参数 addr 和第二个参数 size 共同描述了要同步的内存区域。关于第三个参数 flags

  • ESP_CACHE_MSYNC_FLAG_DIR_C2M。使用此标志,指定地址区域中的内容将被写回到内存中。这一方向通常在 CPU 更新地址内容 之后 使用(例如对地址执行 memset 后),且需要在 DMA 对同一地址进行操作 之前 使用。

  • ESP_CACHE_MSYNC_FLAG_DIR_M2C。使用此标志,指定地址区域中的内容在 cache 中将无效化。这一方向通常在 DMA 更新地址内容 之后 使用,且需要在 CPU 将操作读取到同一地址 之前 使用。

上述两个标志用于选择同步的方向,不能同时使用。如果两个标志都未使用, esp_cache_msync() 将默认选择 ESP_CACHE_MSYNC_FLAG_DIR_C2M

上述两个标志能帮助选择同步地址的类型,不能同时使用,且如果没有用其中任何一个,则 esp_cache_msync() 将默认选择 ESP_CACHE_MSYNC_FLAG_TYPE_DATA

地址对齐的要求

使用 esp_cache_msync() 时,对地址和大小(以字节为单位)存在来自 cache 的对齐要求。

  • 起始地址和大小都符合 cache 与内存同步对齐要求的地址区域被称为 对齐地址区域

  • 起始地址或大小不符合 cache 与内存同步对齐要求的地址区域被称为 非对齐地址区域

默认情况下,如果指定了非对齐地址区域,则 esp_cache_msync() 将报错 ESP_ERR_INVALID_ARG,并告知所需的对齐方式。

内存分配助手

在涉及 DMA 时通常会考虑同步 cache 和内存。ESP-IDF 提供了能进行内存分配的 API,可同时满足 cache 和 DMA 的对齐要求。

  • esp_dma_capable_malloc()。此 API 可分配一块满足 cache 和 DMA 对齐要求的内存块。

  • esp_dma_capable_calloc()。此 API 可分配一块满足 cache 和 DMA 对齐要求的内存块,且内存中的初始化值已设置为零。

也可以使用 ESP_DMA_MALLOC_FLAG_PSRAM, 从 PSRAM 中分配内存。

有关地址对齐要求的警告

可以通过设置 ESP_CACHE_MSYNC_FLAG_UNALIGNED 标志跳过此类检查。但请注意,使用此标志需谨慎,因为在非对齐地址区域内同步 cache 和内存可能会在无形中破坏内存。

举个例子,假设:

  • 对齐要求为 0x40 字节。

  • 调用 esp_cache_msync(),并使用 ESP_CACHE_MSYNC_FLAG_DIR_M2C | ESP_CACHE_MSYNC_FLAG_UNALIGNED 标志,指定的地址区域为 0x4000_0020 ~ 0x4000_0060(详见下图中的 data C)。

上述设置将触发地址区域 0x4000_0000 ~ 0x4000_0080 cache 失效,详见下图中的 sync item0sync item1

如果 0x4000_0000 ~ 0x4000_0020 中的内容(下图中的 data A)或 0x4000_0060 ~ 0x4000_0080 中的内容(下图中的 data B)尚未写回到内存,则 data Adata B 将被丢弃。

../../_images/cache_align_issue.png

API 参考

API 参考 - ESP Msync 驱动程序

Header File

  • components/esp_mm/include/esp_cache.h

  • This header file can be included with:

    #include "esp_cache.h"
    
  • This header file is a part of the API provided by the esp_mm component. To declare that your component depends on esp_mm, add the following to your CMakeLists.txt:

    REQUIRES esp_mm
    

    or

    PRIV_REQUIRES esp_mm
    

Functions

esp_err_t esp_cache_msync(void *addr, size_t size, int flags)

Memory sync between Cache and storage memory.

For cache-to-memory (C2M) direction:

  • For cache writeback supported chips (you can refer to SOC_CACHE_WRITEBACK_SUPPORTED in soc_caps.h)

    • This API will do a writeback to synchronise between cache and storage memory

    • With ESP_CACHE_MSYNC_FLAG_INVALIDATE, this API will also invalidate the values that just written

    • Note: although ESP32 is with PSRAM, but cache writeback isn't supported, so this API will do nothing on ESP32

  • For other chips, this API will do nothing. The out-of-sync should be already dealt by the SDK

For memory-to-cache (M2C) direction:

  • This API will by default do an invalidation

This API is cache-safe and thread-safe

备注

If you don't set direction (ESP_CACHE_MSYNC_FLAG_DIR_x flags), this API is by default C2M direction

备注

If you don't set type (ESP_CACHE_MSYNC_FLAG_TYPE_x flags), this API is by default doing msync for data

备注

You should not call this during any Flash operations (e.g. esp_flash APIs, nvs and some other APIs that are based on esp_flash APIs)

备注

If XIP_From_PSRAM is enabled (by enabling both CONFIG_SPIRAM_FETCH_INSTRUCTIONS and CONFIG_SPIRAM_RODATA), you can call this API during Flash operations

参数
  • addr -- [in] Starting address to do the msync

  • size -- [in] Size to do the msync

  • flags -- [in] Flags, see ESP_CACHE_MSYNC_FLAG_x

返回

  • ESP_OK:

    • Successful msync

    • For C2M direction, if this chip doesn't support cache writeback, if the input addr is a cache supported one, this API will return ESP_OK

  • ESP_ERR_INVALID_ARG: Invalid argument, not cache supported addr, see printed logs

Macros

ESP_CACHE_MSYNC_FLAG_INVALIDATE

Do an invalidation.

Cache msync flags

  • For cache-to-memory (C2M) direction: setting this flag will start an invalidation after the cache writeback operation

  • For memory-to-cache (M2C) direction: setting / unsetting this flag will behave similarly, trigger an invalidation

ESP_CACHE_MSYNC_FLAG_UNALIGNED

Allow msync to a address block that are not aligned to the data cache line size.

ESP_CACHE_MSYNC_FLAG_DIR_C2M

Cache msync direction: from Cache to memory.

备注

If you don't set direction (ESP_CACHE_MSYNC_FLAG_DIR_x flags), it is by default cache-to-memory (C2M) direction

ESP_CACHE_MSYNC_FLAG_DIR_M2C

Cache msync direction: from memory to Cache.

ESP_CACHE_MSYNC_FLAG_TYPE_DATA

Cache msync type: data.

备注

If you don't set type (ESP_CACHE_MSYNC_FLAG_TYPE_x flags), it is by default data type

ESP_CACHE_MSYNC_FLAG_TYPE_INST

Cache msync type: instruction.

API 参考 - ESP DMA 实用程序

Header File

Functions

esp_err_t esp_dma_capable_malloc(size_t size, const esp_dma_mem_info_t *dma_mem_info, void **out_ptr, size_t *actual_size)

Helper function for malloc a DMA capable memory buffer.

备注

This API will take care of the cache alignment internally, you will need to set esp_dma_mem_info_t: dma_alignment_bytes with either the custom alignment or DMA alignment of used peripheral driver.

参数
  • size -- [in] Size in bytes, the amount of memory to allocate

  • dma_mem_info -- [in] DMA and memory info, see esp_dma_mem_info_t

  • out_ptr -- [out] A pointer to the memory allocated successfully

  • actual_size -- [out] Actual size for allocation in bytes, when the size you specified doesn't meet the DMA alignment requirements, this value might be bigger than the size you specified. Set null if you don't care this value.

返回

  • ESP_OK:

  • ESP_ERR_INVALID_ARG: Invalid argument

  • ESP_ERR_NO_MEM: No enough memory for allocation

esp_err_t esp_dma_capable_calloc(size_t calloc_num, size_t size, const esp_dma_mem_info_t *dma_mem_info, void **out_ptr, size_t *actual_size)

Helper function for calloc a DMA capable memory buffer.

参数
  • calloc_num -- [in] Number of elements to allocate

  • size -- [in] Size in bytes, the amount of memory to allocate

  • dma_mem_info -- [in] DMA and memory info, see esp_dma_mem_info_t

  • out_ptr -- [out] A pointer to the memory allocated successfully

  • actual_size -- [out] Actual size for allocation in bytes, when the size you specified doesn't meet the DMA alignment requirements, this value might be bigger than the size you specified. Set null if you don't care this value.

返回

  • ESP_OK:

  • ESP_ERR_INVALID_ARG: Invalid argument

  • ESP_ERR_NO_MEM: No enough memory for allocation

bool esp_dma_is_buffer_alignment_satisfied(const void *ptr, size_t size, esp_dma_mem_info_t dma_mem_info)

Helper function to check if a DMA buffer pointer and size meet both hardware alignment requirements and custom alignment requirements.

参数
  • ptr -- [in] Pointer to the buffer

  • size -- [in] Size of the buffer

  • dma_mem_info -- [in] DMA and memory info, see esp_dma_mem_info_t

返回

  • True: Buffer is aligned

  • False: Buffer is not aligned, or buffer is not DMA capable

esp_err_t esp_dma_malloc(size_t size, uint32_t flags, void **out_ptr, size_t *actual_size)

备注

This API will use MAX alignment requirement

esp_err_t esp_dma_calloc(size_t n, size_t size, uint32_t flags, void **out_ptr, size_t *actual_size)

备注

This API will use MAX alignment requirement

bool esp_dma_is_buffer_aligned(const void *ptr, size_t size, esp_dma_buf_location_t location)

备注

This API will use MAX alignment requirement

Structures

struct esp_dma_mem_info_t

DMA Mem info.

Public Members

int extra_heap_caps

extra heap caps based on MALLOC_CAP_DMA

size_t dma_alignment_bytes

DMA alignment.

struct dma_alignment_info_t

Needed info to get GDMA alignment.

Public Members

bool is_desc

allocate DMA descriptor

bool on_psram

allocate DMA from the PSRAM

Macros

ESP_DMA_MALLOC_FLAG_PSRAM

Memory is in PSRAM.

DMA malloc flags

Enumerations

enum esp_dma_buf_location_t

DMA buffer location.

Values:

enumerator ESP_DMA_BUF_LOCATION_INTERNAL

DMA buffer is in internal memory.

enumerator ESP_DMA_BUF_LOCATION_PSRAM

DMA buffer is in PSRAM.

enumerator ESP_DMA_BUF_LOCATION_AUTO

Auto detect buffer location, under this condition API will loop to search the buffer location.