Merge branch 'fix/psram-ecc-mspi-dma-align' into 'master'

fix(mspi): handle PSRAM ECC DMA alignment across MSPI users

Closes IDF-15850, IDF-15910, and IDF-15700

See merge request espressif/esp-idf!49987
This commit is contained in:
morris
2026-08-20 11:51:46 +08:00
60 changed files with 829 additions and 467 deletions
@@ -10,6 +10,8 @@
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "esp_err.h"
#include "hal/dma2d_types.h"
@@ -269,6 +271,47 @@ typedef struct {
*/
esp_err_t dma2d_set_transfer_ability(dma2d_channel_handle_t dma2d_chan, const dma2d_transfer_ability_t *ability);
/**
* @brief Get DMA2D buffer alignment constraint for a specific buffer
*
* @note On invalid arguments, returns an impossible alignment (BIT(31)).
*
* @param[in] buffer Buffer address
* @return Alignment requirement in bytes
*/
size_t dma2d_get_buffer_alignment_constraint(const void *buffer);
/**
* @brief Get alignment required when allocating a buffer for DMA2D access
*
* Use this before the buffer exists (e.g. `heap_caps_aligned_calloc`).
* Unlike `dma2d_get_buffer_alignment_constraint`, this returns the worst-case
* DMA2D/MSPI alignment rather than treating a NULL pointer as invalid.
*
* @return Alignment requirement in bytes (1 if no strict alignment is needed)
*/
size_t dma2d_get_alloc_alignment(void);
/**
* @brief Check whether a 2D DMA transaction window satisfies DMA2D/MSPI alignment
*
* Under Flash Encryption / PSRAM ECC, MSPI requires each AXI access to be aligned in both
* address and size. For a 2D transfer that means:
* - buffer base address aligned to N bytes
* - bytes-per-line (`pic_width * bpp/8`) aligned, so every next line starts on an N-byte boundary
* - transfer width (`blk_width * bpp/8`) aligned
* - horizontal window offset (`offset_x * bpp/8`) aligned
*
* @param[in] buf Buffer base address
* @param[in] pic_width Picture / stride width in pixels
* @param[in] blk_width Transfer block width in pixels
* @param[in] offset_x Horizontal offset of the block in pixels
* @param[in] bit_depth Bits per pixel
* @return true if the transaction satisfies the alignment constraints
*/
bool dma2d_check_transaction_alignment_constraint(const void *buf, uint32_t pic_width, uint32_t blk_width,
uint32_t offset_x, uint32_t bit_depth);
/**
* @brief A collection of color space conversion (CSC) items that each 2D-DMA channel could apply
*/
@@ -7,6 +7,7 @@
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include "esp_etm.h"
#include "hal/gdma_types.h"
#include "esp_err.h"
@@ -221,23 +222,50 @@ typedef struct {
esp_err_t gdma_config_transfer(gdma_channel_handle_t dma_chan, const gdma_transfer_config_t *config);
/**
* @brief Get the alignment constraints for internal and external memory
* @brief Alignment constraints of a configured GDMA channel
*
* @note You should call this function after `gdma_config_transfer`, the later one can
* adjust the alignment constraints based on various conditions, e.g. burst size, memory encryption, etc.
* @note You can use returned alignment value to validate if a DMA buffer provided by the upper layer meets the constraints.
* @note The returned alignment doesn't take the cache line size into account, if you want to do aligned memory allocation,
* you should align the buffer size to the cache line size by yourself if the DMA buffer is behind a cache.
* @note Prefer this when allocating DMA buffers. Once a concrete buffer address is available,
* use `gdma_get_buffer_alignment_constraint` for the effective runtime constraint of that region.
* @note For allocation from external memory:
* - Use `ext_enc_mem_alignment` as the safe default (worst-case MSPI encryption/ECC).
* - Use `ext_no_enc_mem_alignment` when intentionally targeting no-encryption external memory (e.g. no-enc PSRAM).
* @note The returned alignment doesn't take the cache line size into account. If the DMA buffer is behind a cache,
* align the buffer size to the cache line size yourself when needed.
*/
typedef struct {
size_t int_mem_alignment; /*!< Alignment for internal memory */
size_t ext_enc_mem_alignment; /*!< Alignment for external memory including MSPI encryption/ECC constraints */
size_t ext_no_enc_mem_alignment; /*!< Alignment for external memory without MSPI region-specific constraints */
} gdma_channel_alignment_info_t;
/**
* @brief Get the alignment constraints for a configured GDMA channel
*
* @note Call this function after `gdma_config_transfer`.
*
* @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_ahb_channel/gdma_new_axi_channel`
* @param[out] int_mem_alignment Internal memory alignment
* @param[out] ext_mem_alignment External memory alignment
* @param[out] info Alignment constraints of the channel
* @return
* - ESP_OK: Get alignment constraints successfully
* - ESP_ERR_INVALID_ARG: Get alignment constraints failed because of invalid argument
* - ESP_FAIL: Get alignment constraints failed because of other error
*/
esp_err_t gdma_get_alignment_constraints(gdma_channel_handle_t dma_chan, size_t *int_mem_alignment, size_t *ext_mem_alignment);
esp_err_t gdma_get_channel_alignment_constraints(gdma_channel_handle_t dma_chan, gdma_channel_alignment_info_t *info);
/**
* @brief Get the effective alignment constraint for a specific DMA buffer
*
* @note Call this function after `gdma_config_transfer`.
* @note Combines GDMA channel constraints with MSPI constraints of the actual buffer region.
* External no-encryption PSRAM buffers can therefore use their real runtime constraint
* instead of a generic worst-case MSPI alignment.
* @note The returned alignment doesn't take the cache line size into account.
* @note On invalid arguments, returns an impossible value (BIT(31)).
*
* @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_ahb_channel/gdma_new_axi_channel`
* @param[in] buffer DMA buffer address
* @return Effective buffer alignment in bytes
*/
size_t gdma_get_buffer_alignment_constraint(gdma_channel_handle_t dma_chan, const void *buffer);
/**
* @brief Apply channel strategy for GDMA channel
@@ -82,7 +82,9 @@ typedef struct {
gdma_final_node_link_type_t mark_final: 2; /*!< Specify the next item of the final item of this mount.
For the other items that not the final one, it will be linked to the next item automatically and this field takes no effect.
Note, the final item here does not mean the last item in the link list. It is `start_item_index + num_items - 1` */
uint32_t bypass_buffer_align_check: 1; /*!< Whether to bypass the buffer alignment check.
uint32_t bypass_buffer_addr_align_check: 1; /*!< Whether to bypass the buffer address alignment check.
Only enable it when you know what you are doing. */
uint32_t bypass_buffer_size_align_check: 1; /*!< Whether to bypass the buffer size alignment check.
Only enable it when you know what you are doing. */
} flags; //!< Flags for buffer mount configurations
} gdma_buffer_mount_config_t;
+1
View File
@@ -11,6 +11,7 @@ entries:
gdma: gdma_stop (noflash)
gdma: gdma_append (noflash)
gdma: gdma_reset (noflash)
gdma: gdma_get_buffer_alignment_constraint (noflash)
[mapping:gdma_hal]
archive: libesp_hal_dma.a
@@ -6,6 +6,7 @@
#include <stdatomic.h>
#include <sys/queue.h>
#include <sys/param.h>
#include <inttypes.h>
#include <assert.h>
#include "freertos/FreeRTOS.h"
@@ -16,6 +17,7 @@
#include "esp_heap_caps.h"
#include "esp_memory_utils.h"
#include "esp_async_color_convert_priv.h"
#include "esp_private/dma2d.h"
#include "soc/dma2d_channel.h"
#include "hal/dma2d_types.h"
#include "hal/dma2d_ll.h"
@@ -207,6 +209,17 @@ static esp_err_t validate_request(const async_color_convert_request_t *request)
request->dst_height <= DMA2D_LL_DESC_2D_FIELD_MAX,
ESP_ERR_INVALID_ARG, TAG, "dimension exceeds DMA2D descriptor field limit");
uint32_t src_bit_depth = color_hal_pixel_format_fourcc_get_bit_depth(request->src_color_format);
uint32_t dst_bit_depth = color_hal_pixel_format_fourcc_get_bit_depth(request->dst_color_format);
ESP_RETURN_ON_FALSE(dma2d_check_transaction_alignment_constraint(request->src_buffer, request->src_stride,
request->copy_width, request->src_x,
src_bit_depth),
ESP_ERR_INVALID_ARG, TAG, "source buffer or window is not aligned to DMA2D alignment");
ESP_RETURN_ON_FALSE(dma2d_check_transaction_alignment_constraint(request->dst_buffer, request->dst_stride,
request->copy_width, request->dst_x,
dst_bit_depth),
ESP_ERR_INVALID_ARG, TAG, "destination buffer or window is not aligned to DMA2D alignment");
return ESP_OK;
}
+5 -12
View File
@@ -7,6 +7,7 @@
#include <string.h>
#include <stdatomic.h>
#include <sys/queue.h>
#include <sys/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_check.h"
@@ -51,8 +52,6 @@ typedef struct {
gdma_channel_handle_t rx_channel; // GDMA RX channel handle used to drain M2M data
portMUX_TYPE spin_lock; // Spinlock for synchronization
_Atomic async_crc_fsm_t fsm; // driver state machine, changing state should be atomic
size_t tx_int_mem_alignment; // Required DMA buffer alignment for internal TX memory
size_t tx_ext_mem_alignment; // Required DMA buffer alignment for external TX memory
uint8_t *rx_sink_buffer; // Sink buffer used to drain the M2M RX path
gdma_link_list_handle_t rx_link_list; // Self-loop DMA link list for the RX sink buffer, shared by all crc transactions
uint32_t gdma_bus_id; // GDMA bus id (AHB, AXI, etc.)
@@ -164,9 +163,9 @@ esp_err_t esp_async_crc_install_gdma_template(const async_crc_config_t *config,
ESP_GOTO_ON_ERROR(gdma_config_transfer(crc_gdma->rx_channel, &transfer_cfg), err, TAG, "config RX DMA transfer failed");
// Get buffer alignment required by GDMA channel
gdma_get_alignment_constraints(crc_gdma->tx_channel, &crc_gdma->tx_int_mem_alignment, &crc_gdma->tx_ext_mem_alignment);
size_t rx_int_mem_alignment = 0;
gdma_get_alignment_constraints(crc_gdma->rx_channel, &rx_int_mem_alignment, NULL);
gdma_channel_alignment_info_t rx_align_info;
gdma_get_channel_alignment_constraints(crc_gdma->rx_channel, &rx_align_info);
size_t rx_int_mem_alignment = rx_align_info.int_mem_alignment;
size_t rx_buffer_size = (rx_int_mem_alignment > CRC_DMA_RX_SINK_BUFFER_SIZE) ?
rx_int_mem_alignment : CRC_DMA_RX_SINK_BUFFER_SIZE;
crc_gdma->rx_sink_buffer = heap_caps_aligned_calloc(rx_int_mem_alignment, 1, rx_buffer_size,
@@ -324,13 +323,7 @@ static esp_err_t async_crc_prepare_transaction(async_crc_gdma_context_t *crc_gdm
uint32_t max_crc_bit_width = (crc_gdma->gdma_bus_id == SOC_GDMA_BUS_AXI) ? GDMA_LL_AXI_MAX_CRC_BIT_WIDTH : GDMA_LL_AHB_MAX_CRC_BIT_WIDTH;
ESP_RETURN_ON_FALSE(trans->params.width <= max_crc_bit_width, ESP_ERR_INVALID_ARG, TAG, "invalid crc bit width %"PRIu32, trans->params.width);
// Get buffer alignment based on memory type
size_t buffer_alignment = esp_ptr_internal(trans->data) ? crc_gdma->tx_int_mem_alignment : crc_gdma->tx_ext_mem_alignment;
// Verify user buffer satisfies DMA alignment requirements
ESP_RETURN_ON_FALSE(((uintptr_t)trans->data % buffer_alignment) == 0, ESP_ERR_INVALID_ARG, TAG,
"Data buffer not aligned to %zu bytes", buffer_alignment);
size_t buffer_alignment = gdma_get_buffer_alignment_constraint(crc_gdma->tx_channel, trans->data);
// Calculate number of DMA nodes needed
size_t tx_num_dma_nodes = esp_dma_calculate_node_count(trans->size, buffer_alignment, CRC_DMA_DESCRIPTOR_BUFFER_MAX_SIZE);
@@ -47,10 +47,6 @@ typedef struct async_memcpy_transaction_t {
/// @note - Number of transaction objects are determined by the backlog parameter
typedef struct {
async_memcpy_context_t parent; // Parent IO interface
size_t rx_int_mem_alignment; // Required DMA buffer alignment for internal RX memory
size_t rx_ext_mem_alignment; // Required DMA buffer alignment for external RX memory
size_t tx_int_mem_alignment; // Required DMA buffer alignment for internal TX memory
size_t tx_ext_mem_alignment; // Required DMA buffer alignment for external TX memory
int gdma_bus_id; // GDMA bus id (AHB, AXI, etc.)
gdma_channel_handle_t tx_channel; // GDMA TX channel handle
gdma_channel_handle_t rx_channel; // GDMA RX channel handle
@@ -149,10 +145,6 @@ static esp_err_t esp_async_memcpy_install_gdma_template(const async_memcpy_confi
ESP_GOTO_ON_ERROR(gdma_config_transfer(mcp_gdma->tx_channel, &transfer_cfg), err, TAG, "config transfer for tx channel failed");
ESP_GOTO_ON_ERROR(gdma_config_transfer(mcp_gdma->rx_channel, &transfer_cfg), err, TAG, "config transfer for rx channel failed");
// get the buffer alignment required by the GDMA channel
gdma_get_alignment_constraints(mcp_gdma->rx_channel, &mcp_gdma->rx_int_mem_alignment, &mcp_gdma->rx_ext_mem_alignment);
gdma_get_alignment_constraints(mcp_gdma->tx_channel, &mcp_gdma->tx_int_mem_alignment, &mcp_gdma->tx_ext_mem_alignment);
// register rx eof callback
gdma_rx_event_callbacks_t cbs = {
.on_recv_eof = mcp_gdma_rx_eof_callback,
@@ -281,30 +273,6 @@ static async_memcpy_transaction_t *try_pop_trans_from_idle_queue(async_memcpy_gd
return trans;
}
/// @brief Check if the address and size can meet the requirement of the DMA engine
static bool check_buffer_alignment(async_memcpy_gdma_context_t *mcp_gdma, void *src, void *dst, size_t n)
{
bool valid = true;
if (esp_ptr_external_ram(dst)) {
valid = valid && (((uint32_t)dst & (mcp_gdma->rx_ext_mem_alignment - 1)) == 0);
valid = valid && ((n & (mcp_gdma->rx_ext_mem_alignment - 1)) == 0);
} else {
valid = valid && (((uint32_t)dst & (mcp_gdma->rx_int_mem_alignment - 1)) == 0);
valid = valid && ((n & (mcp_gdma->rx_int_mem_alignment - 1)) == 0);
}
if (esp_ptr_external_ram(src)) {
valid = valid && (((uint32_t)src & (mcp_gdma->tx_ext_mem_alignment - 1)) == 0);
valid = valid && ((n & (mcp_gdma->tx_ext_mem_alignment - 1)) == 0);
} else {
valid = valid && (((uint32_t)src & (mcp_gdma->tx_int_mem_alignment - 1)) == 0);
valid = valid && ((n & (mcp_gdma->tx_int_mem_alignment - 1)) == 0);
}
return valid;
}
static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *src, size_t n, async_memcpy_isr_cb_t cb_isr, void *cb_args)
{
esp_err_t ret = ESP_OK;
@@ -335,8 +303,6 @@ static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *s
dma_link_item_alignment = GDMA_LL_AHB_DESC_ALIGNMENT;
}
#endif // SOC_HAS(LP_AHB_GDMA)
// alignment check
ESP_RETURN_ON_FALSE(check_buffer_alignment(mcp_gdma, src, dst, n), ESP_ERR_INVALID_ARG, TAG, "address|size not aligned: %p -> %p, sz=%zu", src, dst, n);
async_memcpy_transaction_t *trans = NULL;
// pick one transaction node from idle queue
@@ -364,12 +330,11 @@ static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *s
async_memcpy_split_t split;
ESP_GOTO_ON_ERROR(async_memcpy_split_cache_aligned(dst, src, n, &split), err, TAG, "failed to split buffer");
size_t buffer_alignment = 0;
size_t num_dma_nodes = 0;
// allocate gdma TX link, only the body is handled by the DMA
buffer_alignment = esp_ptr_internal(split.body_src) ? mcp_gdma->tx_int_mem_alignment : mcp_gdma->tx_ext_mem_alignment;
num_dma_nodes = esp_dma_calculate_node_count(split.body_len, buffer_alignment, MCP_DMA_DESCRIPTOR_BUFFER_MAX_SIZE);
size_t tx_buffer_alignment = gdma_get_buffer_alignment_constraint(mcp_gdma->tx_channel, split.body_src);
num_dma_nodes = esp_dma_calculate_node_count(split.body_len, tx_buffer_alignment, MCP_DMA_DESCRIPTOR_BUFFER_MAX_SIZE);
gdma_link_list_config_t tx_link_cfg = {
.item_alignment = dma_link_item_alignment,
.num_items = num_dma_nodes,
@@ -383,7 +348,7 @@ static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *s
gdma_buffer_mount_config_t tx_buf_mount_config[1] = {
[0] = {
.buffer = split.body_src,
.buffer_alignment = buffer_alignment,
.buffer_alignment = tx_buffer_alignment,
.length = split.body_len,
.flags = {
.mark_eof = true, // mark the last item as EOF, so the RX channel can also received an EOF list item
@@ -394,8 +359,8 @@ static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *s
gdma_link_mount_buffers(trans->tx_link_list, 0, tx_buf_mount_config, 1, NULL);
// allocate gdma RX link, only the body is handled by the DMA
buffer_alignment = esp_ptr_internal(split.body_dst) ? mcp_gdma->rx_int_mem_alignment : mcp_gdma->rx_ext_mem_alignment;
num_dma_nodes = esp_dma_calculate_node_count(split.body_len, buffer_alignment, MCP_DMA_DESCRIPTOR_BUFFER_MAX_SIZE);
size_t rx_buffer_alignment = gdma_get_buffer_alignment_constraint(mcp_gdma->rx_channel, split.body_dst);
num_dma_nodes = esp_dma_calculate_node_count(split.body_len, rx_buffer_alignment, MCP_DMA_DESCRIPTOR_BUFFER_MAX_SIZE);
gdma_link_list_config_t rx_link_cfg = {
.item_alignment = dma_link_item_alignment,
.num_items = num_dma_nodes,
@@ -409,7 +374,7 @@ static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *s
gdma_buffer_mount_config_t rx_buf_mount_config[1] = {
[0] = {
.buffer = split.body_dst,
.buffer_alignment = buffer_alignment,
.buffer_alignment = rx_buffer_alignment,
.length = split.body_len,
.flags = {
.mark_eof = true,
+54 -11
View File
@@ -26,7 +26,7 @@
#include "hal/dma2d_periph.h"
#include "soc/soc_caps.h"
#include "esp_bit_defs.h"
#include "esp_efuse.h"
#include "esp_private/esp_mspi_align.h"
#include "esp_private/sleep_retention.h"
/**
@@ -801,8 +801,12 @@ esp_err_t dma2d_set_desc_addr(dma2d_channel_handle_t dma2d_chan, intptr_t desc_b
addr_in_spm = esp_ptr_in_spm((void *)desc_base_addr);
#endif
ESP_GOTO_ON_FALSE_ISR((desc_base_addr & 0x7) == 0 && !addr_in_spm, ESP_ERR_INVALID_ARG, err, TAG, "invalid descriptor base addr");
// When flash encryption is enabled, the descriptor must be in internal RAM because descriptor size is not 16-byte aligned, which breaks flash encryption alignment restriction
ESP_GOTO_ON_FALSE_ISR(!esp_efuse_is_flash_encryption_enabled() || esp_ptr_internal((void *)desc_base_addr), ESP_ERR_INVALID_ARG, err, TAG, "invalid description base addr");
// If descriptors are placed in external memory, their size must meet MSPI alignment constraints;
// otherwise, descriptors must be located in internal RAM.
size_t mspi_align = esp_mspi_get_alignment((void *)desc_base_addr);
bool desc_size_mspi_aligned = (sizeof(dma2d_descriptor_t) & (mspi_align - 1)) == 0;
ESP_GOTO_ON_FALSE_ISR(desc_size_mspi_aligned || esp_ptr_internal((void *)desc_base_addr),
ESP_ERR_INVALID_ARG, err, TAG, "invalid description base addr");
dma2d_group_t *group = dma2d_chan->group;
int channel_id = dma2d_chan->channel_id;
@@ -920,20 +924,19 @@ esp_err_t dma2d_set_transfer_ability(dma2d_channel_handle_t dma2d_chan, const dm
ESP_GOTO_ON_FALSE_ISR(dma2d_chan && ability, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
ESP_GOTO_ON_FALSE_ISR(ability->data_burst_length && ((ability->data_burst_length & (ability->data_burst_length - 1)) == 0), ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); // burst size must be power of 2
ESP_GOTO_ON_FALSE_ISR(ability->mb_size < DMA2D_MACRO_BLOCK_SIZE_INVALID, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
ESP_GOTO_ON_FALSE_ISR(!ability->access_ext_mem || (SOC_PSRAM_DMA_CAPABLE || SOC_DMA_CAN_ACCESS_FLASH), ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
dma2d_group_t *group = dma2d_chan->group;
int channel_id = dma2d_chan->channel_id;
// When flash encryption is enabled, and the channel is accessing external memory, burst length has to be as least as the encryption alignment restriction size
uint32_t data_burst_length = ability->data_burst_length;
#if SOC_PSRAM_DMA_CAPABLE || SOC_DMA_CAN_ACCESS_FLASH
if (esp_efuse_is_flash_encryption_enabled() && ability->access_ext_mem) {
if (data_burst_length < SOC_MEMSPI_ENCRYPTION_ALIGNMENT) {
data_burst_length = SOC_MEMSPI_ENCRYPTION_ALIGNMENT;
ESP_LOGW(TAG, "channel access encrypted external memory, adjust burst size to %d", SOC_MEMSPI_ENCRYPTION_ALIGNMENT);
if (ability->access_ext_mem) {
// If channel is accessing external memory, burst length has to be at least the MSPI alignment restriction size
size_t mspi_alignment = dma2d_get_alloc_alignment();
if (data_burst_length < mspi_alignment) {
data_burst_length = mspi_alignment;
ESP_LOGW(TAG, "requested burst size does not meet MSPI alignment constraint, adjust to %d", (int)mspi_alignment);
}
}
#endif
if (dma2d_chan->direction == DMA2D_CHANNEL_DIRECTION_TX) {
dma2d_ll_tx_enable_descriptor_burst(group->hal.dev, channel_id, ability->desc_burst_en);
@@ -949,6 +952,46 @@ err:
return ret;
}
size_t dma2d_get_buffer_alignment_constraint(const void *buffer)
{
if (!buffer) {
return BIT(31);
}
return esp_mspi_get_alignment(buffer);
}
size_t dma2d_get_alloc_alignment(void)
{
// Worst-case alignment for buffers that may be accessed by DMA2D (MSPI FE/ECC, etc.)
return esp_mspi_get_alignment(NULL);
}
bool dma2d_check_transaction_alignment_constraint(const void *buf, uint32_t pic_width, uint32_t blk_width,
uint32_t offset_x, uint32_t bit_depth)
{
if (!buf || bit_depth == 0) {
return false;
}
size_t alignment = dma2d_get_buffer_alignment_constraint(buf);
if (alignment <= 1) {
return true;
}
// Under Flash Encryption / PSRAM ECC, MSPI requires each AXI access to be aligned in both address and size.
// For 2D DMA that means:
// - buffer base address aligned to N bytes
// - bytes-per-line (pic_width * bpp/8) aligned, so every next line starts on an N-byte boundary
// - transfer width (blk_width * bpp/8) aligned
// - horizontal window offset (offset_x * bpp/8) aligned, so the first pixel of the window is aligned
uint32_t alignment_bits = alignment * 8;
return (((uintptr_t)buf & (alignment - 1)) == 0) &&
(((uint64_t)pic_width * bit_depth) % alignment_bits == 0) &&
(((uint64_t)blk_width * bit_depth) % alignment_bits == 0) &&
(((uint64_t)offset_x * bit_depth) % alignment_bits == 0);
}
esp_err_t dma2d_configure_color_space_conversion(dma2d_channel_handle_t dma2d_chan, const dma2d_csc_config_t *config)
{
esp_err_t ret = ESP_OK;
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -43,7 +43,7 @@ esp_err_t esp_dma_split_rx_buffer_to_cache_aligned(void *rx_buffer, size_t buffe
split_line_size = int_mem_cache_line_size;
}
bool align_required = split_line_size > 0;
ESP_EARLY_LOGV(TAG, "split_line_size:%zu", split_line_size);
ESP_EARLY_LOGV(TAG, "split_line_size:%" PRIu32, (uint32_t)split_line_size);
if (*ret_stash_buffer == NULL) {
// If the stash buffer is not offered by the caller, allocate the stash buffer from internal RAM
@@ -67,10 +67,10 @@ esp_err_t esp_dma_split_rx_buffer_to_cache_aligned(void *rx_buffer, size_t buffe
// calculate head_overflow_len
size_t head_overflow_len = (uintptr_t)rx_buffer % split_line_size;
head_overflow_len = head_overflow_len ? split_line_size - head_overflow_len : 0;
ESP_EARLY_LOGV(TAG, "head_addr:%p head_overflow_len:%zu", rx_buffer, head_overflow_len);
ESP_EARLY_LOGV(TAG, "head_addr:%p head_overflow_len:%" PRIu32, rx_buffer, (uint32_t)head_overflow_len);
// calculate tail_overflow_len
size_t tail_overflow_len = ((uintptr_t)rx_buffer + buffer_len) % split_line_size;
ESP_EARLY_LOGV(TAG, "tail_addr:%p tail_overflow_len:%zu", rx_buffer + buffer_len - tail_overflow_len, tail_overflow_len);
ESP_EARLY_LOGV(TAG, "tail_addr:%p tail_overflow_len:%" PRIu32, rx_buffer + buffer_len - tail_overflow_len, (uint32_t)tail_overflow_len);
// special handling when input_buffer length is no more than buffer alignment
bool is_small_buf = head_overflow_len >= buffer_len || tail_overflow_len >= buffer_len;
+78 -54
View File
@@ -28,6 +28,7 @@
#include "gdma_priv.h"
#include "esp_memory_utils.h"
#include "esp_private/esp_mspi_align.h"
#define GDMA_INVALID_PERIPH_TRIG (0x3F)
#define SEARCH_REQUEST_RX_CHANNEL (1 << 0)
@@ -419,100 +420,123 @@ esp_err_t gdma_config_transfer(gdma_channel_handle_t dma_chan, const gdma_transf
if (!dma_chan || !config) {
return ESP_ERR_INVALID_ARG;
}
uint32_t max_data_burst_size = config->max_data_burst_size;
size_t int_mem_alignment = 1;
size_t ext_enc_mem_alignment = 1;
size_t ext_no_enc_mem_alignment = 1;
if (config->access_ext_mem) {
#if (SOC_PSRAM_DMA_CAPABLE || SOC_DMA_CAN_ACCESS_FLASH) && SOC_AHB_GDMA_VERSION != 1
// Under Flash Encryption/PSRAM ECC, DMA must use MSPI-aligned bursts.
size_t mspi_alignment = esp_mspi_get_alignment(NULL);
if (mspi_alignment > 1) {
if (max_data_burst_size < mspi_alignment) {
max_data_burst_size = mspi_alignment;
ESP_LOGW(TAG, "max_data_burst_size is less than mspi_alignment, adjusted to %d", max_data_burst_size);
}
}
#endif
#if GDMA_LL_GET(AHB_PSRAM_CAPABLE) || GDMA_LL_GET(AXI_PSRAM_CAPABLE) || GDMA_LL_GET(LP_AHB_PSRAM_CAPABLE)
ESP_RETURN_ON_FALSE(max_data_burst_size <= GDMA_LL_MAX_BURST_SIZE_PSRAM, ESP_ERR_INVALID_ARG,
TAG, "max_data_burst_size must not exceed %d when accessing external memory", GDMA_LL_MAX_BURST_SIZE_PSRAM);
#endif
}
if (max_data_burst_size) {
// burst size must be power of 2
ESP_RETURN_ON_FALSE((max_data_burst_size & (max_data_burst_size - 1)) == 0, ESP_ERR_INVALID_ARG,
TAG, "invalid max_data_burst_size: %"PRIu32, max_data_burst_size);
#if GDMA_LL_GET(AHB_PSRAM_CAPABLE) || GDMA_LL_GET(AXI_PSRAM_CAPABLE) || GDMA_LL_GET(LP_AHB_PSRAM_CAPABLE)
if (config->access_ext_mem) {
ESP_RETURN_ON_FALSE(max_data_burst_size <= GDMA_LL_MAX_BURST_SIZE_PSRAM, ESP_ERR_INVALID_ARG,
TAG, "max_data_burst_size must not exceed %d when accessing external memory", GDMA_LL_MAX_BURST_SIZE_PSRAM);
}
#endif
}
gdma_pair_t *pair = dma_chan->pair;
gdma_group_t *group = pair->group;
gdma_hal_context_t *hal = &group->hal;
size_t int_mem_alignment = 1;
size_t ext_mem_alignment = 1;
// always enable descriptor burst as the descriptor is always word aligned and is in the internal SRAM
bool en_desc_burst = true;
bool en_data_burst = max_data_burst_size > 0;
// There's auto alignment for AHB GDMA version 1, so we don't need to do anything here
// While, for AHB GDMA version 2 and AXI GDMA, we need to ensure the alignment by software
#if (SOC_PSRAM_DMA_CAPABLE || SOC_DMA_CAN_ACCESS_FLASH) && SOC_AHB_GDMA_VERSION != 1
bool ext_mem_needs_mspi_alignment = esp_efuse_is_flash_encryption_enabled();
#if CONFIG_SPIRAM_ECC_ENABLE
ext_mem_needs_mspi_alignment = true;
#endif
// When MSPI encryption or PSRAM ECC address conversion is enabled, DMA accesses to
// external memory need to follow the MSPI encryption alignment restriction.
if (ext_mem_needs_mspi_alignment && config->access_ext_mem) {
uint32_t mspi_mem_alignment = SOC_MEMSPI_ENCRYPTION_ALIGNMENT;
ext_mem_alignment = MAX(ext_mem_alignment, mspi_mem_alignment);
if (max_data_burst_size < mspi_mem_alignment) {
ESP_LOGW(TAG, "GDMA channel access encrypted/ECC external memory, adjust burst size to %d", mspi_mem_alignment);
en_data_burst = true;
max_data_burst_size = mspi_mem_alignment;
}
}
#endif // SOC_PSRAM_DMA_CAPABLE || SOC_DMA_CAN_ACCESS_FLASH
gdma_hal_enable_burst(hal, pair->pair_id, dma_chan->direction, en_data_burst, en_desc_burst);
if (en_data_burst) {
gdma_hal_set_burst_size(hal, pair->pair_id, dma_chan->direction, max_data_burst_size);
#if CONFIG_GDMA_ENABLE_WEIGHTED_ARBITRATION
// due to hardware limitation, if weighted arbitration is enabled, the data must be aligned to burst size
int_mem_alignment = MAX(int_mem_alignment, max_data_burst_size);
ext_mem_alignment = MAX(ext_mem_alignment, max_data_burst_size);
ext_enc_mem_alignment = MAX(ext_enc_mem_alignment, max_data_burst_size);
ext_no_enc_mem_alignment = MAX(ext_no_enc_mem_alignment, max_data_burst_size);
#endif
}
#if GDMA_LL_AHB_RX_BURST_NEEDS_ALIGNMENT
if (en_data_burst && dma_chan->direction == GDMA_CHANNEL_DIRECTION_RX) {
int_mem_alignment = MAX(int_mem_alignment, 4);
ext_mem_alignment = MAX(ext_mem_alignment, max_data_burst_size);
ext_enc_mem_alignment = MAX(ext_enc_mem_alignment, max_data_burst_size);
ext_no_enc_mem_alignment = MAX(ext_no_enc_mem_alignment, max_data_burst_size);
}
#endif
// if the channel is not allowed to access external memory, set a super big (meaningless) alignment value
// so when the upper layer checks the alignment with an external buffer, the check should fail
if (!config->access_ext_mem) {
ext_mem_alignment = BIT(31);
if (config->access_ext_mem) {
// ext_enc includes MSPI encryption/ECC constraints; ext_no_enc keeps DMA-only constraints.
size_t mspi_alignment = esp_mspi_get_alignment(NULL);
ext_enc_mem_alignment = MAX(ext_enc_mem_alignment, mspi_alignment);
} else {
// if the channel is not allowed to access external memory, set a super big (meaningless) alignment value
// so when the upper layer checks the alignment with an external buffer, the check should fail
ext_enc_mem_alignment = BIT(31);
ext_no_enc_mem_alignment = BIT(31);
}
gdma_pair_t *pair = dma_chan->pair;
gdma_group_t *group = pair->group;
gdma_hal_context_t *hal = &group->hal;
// always enable descriptor burst as the descriptor is always word aligned and is in the internal SRAM
bool en_desc_burst = true;
gdma_hal_enable_burst(hal, pair->pair_id, dma_chan->direction, en_data_burst, en_desc_burst);
if (en_data_burst) {
gdma_hal_set_burst_size(hal, pair->pair_id, dma_chan->direction, max_data_burst_size);
}
#if CONFIG_IDF_TARGET_ESP32S31 && SOC_HAS(LP_AHB_GDMA)
// ESP32-S31 LP AHB GDMA can't burst-access encrypted external memory.
// ESP32-S31 LP AHB GDMA can't burst-access external memory (with or without ECC/encryption).
// Keep configuration/installation permissive for callers that only intend to
// use internal buffers, but poison the external-memory alignment so any
// later PSRAM use fails the caller-side validation.
if (config->access_ext_mem && group->bus_id == SOC_GDMA_BUS_LP && esp_efuse_is_flash_encryption_enabled()) {
ext_mem_alignment = BIT(31);
if (config->access_ext_mem && group->bus_id == SOC_GDMA_BUS_LP) {
if (esp_efuse_is_flash_encryption_enabled()) {
ext_enc_mem_alignment = BIT(31);
}
#if CONFIG_SPIRAM_ECC_ENABLE
// ECC PSRAM is inaccessible to LP AHB regardless of flash encryption state.
ext_enc_mem_alignment = BIT(31);
ext_no_enc_mem_alignment = BIT(31);
#endif
}
#endif
dma_chan->int_mem_alignment = int_mem_alignment;
dma_chan->ext_mem_alignment = ext_mem_alignment;
dma_chan->ext_enc_mem_alignment = ext_enc_mem_alignment;
dma_chan->ext_no_enc_mem_alignment = ext_no_enc_mem_alignment;
return ESP_OK;
}
esp_err_t gdma_get_alignment_constraints(gdma_channel_handle_t dma_chan, size_t *int_mem_alignment, size_t *ext_mem_alignment)
esp_err_t gdma_get_channel_alignment_constraints(gdma_channel_handle_t dma_chan, gdma_channel_alignment_info_t *info)
{
if (!dma_chan) {
if (!dma_chan || !info) {
return ESP_ERR_INVALID_ARG;
}
if (int_mem_alignment) {
*int_mem_alignment = dma_chan->int_mem_alignment;
}
if (ext_mem_alignment) {
*ext_mem_alignment = dma_chan->ext_mem_alignment;
}
info->int_mem_alignment = dma_chan->int_mem_alignment;
info->ext_enc_mem_alignment = dma_chan->ext_enc_mem_alignment;
info->ext_no_enc_mem_alignment = dma_chan->ext_no_enc_mem_alignment;
return ESP_OK;
}
size_t gdma_get_buffer_alignment_constraint(gdma_channel_handle_t dma_chan, const void *buffer)
{
if (!dma_chan || !buffer) {
return BIT(31);
}
// Internal SRAM only needs the DMA-side constraint; MSPI rules apply to PSRAM/Flash.
if (!(esp_ptr_external_ram(buffer) || esp_ptr_in_drom(buffer))) {
return dma_chan->int_mem_alignment;
}
size_t mspi_alignment = esp_mspi_get_alignment(buffer);
return MAX(dma_chan->ext_no_enc_mem_alignment, mspi_alignment);
}
esp_err_t gdma_apply_strategy(gdma_channel_handle_t dma_chan, const gdma_strategy_config_t *config)
{
if (!dma_chan || !config) {
+11 -17
View File
@@ -8,6 +8,7 @@
#include <string.h>
#include <stdint.h>
#include <inttypes.h>
#include <sys/param.h>
#include <sys/cdefs.h>
#include "soc/soc_caps.h"
#include "esp_log.h"
@@ -15,10 +16,8 @@
#include "esp_memory_utils.h"
#include "esp_heap_caps.h"
#include "esp_private/gdma_link.h"
#include "hal/cache_hal.h"
#include "hal/efuse_hal.h"
#include "hal/cache_ll.h"
#include "esp_cache.h"
#include "esp_private/esp_mspi_align.h"
#include "esp_efuse.h"
#include "esp_macros.h"
@@ -76,10 +75,10 @@ esp_err_t gdma_new_link_list(const gdma_link_list_config_t *config, gdma_link_li
bool items_in_ext_mem = config->flags.items_in_ext_mem;
uint32_t list_items_mem_caps = MALLOC_CAP_8BIT | MALLOC_CAP_DMA;
if (items_in_ext_mem) {
if (esp_efuse_is_flash_encryption_enabled()) {
if (esp_mspi_get_alignment(NULL) > 1) {
items_in_ext_mem = false;
list_items_mem_caps |= MALLOC_CAP_INTERNAL;
ESP_LOGW(TAG, "DMA linked list items cannot be placed in PSRAM when external memory encryption is enabled, using internal memory instead");
ESP_LOGW(TAG, "DMA linked list items cannot be placed in PSRAM when MSPI strict alignment is required, using internal memory instead");
} else {
list_items_mem_caps |= MALLOC_CAP_SPIRAM;
}
@@ -90,12 +89,7 @@ esp_err_t gdma_new_link_list(const gdma_link_list_config_t *config, gdma_link_li
ESP_GOTO_ON_FALSE(items, ESP_ERR_NO_MEM, err, TAG, "no mem for link list items");
// do memory sync if the list items are in the cache
uint32_t data_cache_line_size = 0;
if (items_in_ext_mem) {
data_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA);
} else {
data_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA);
}
size_t data_cache_line_size = esp_cache_get_line_size_by_addr(items);
if (data_cache_line_size) {
// write back and then invalidate the cache, because later we will read/write the link list items by non-cached address
ESP_GOTO_ON_ERROR(esp_cache_msync(items, ESP_ALIGN_UP(num_items * item_size, data_cache_line_size),
@@ -191,12 +185,12 @@ esp_err_t gdma_link_mount_buffers(gdma_link_list_handle_t list, int start_item_i
// alignment must be a power of 2
ESP_RETURN_ON_FALSE_ISR((buffer_alignment & (buffer_alignment - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "align err idx=%"PRIu32" align=%"PRIu32, bi, buffer_alignment);
size_t max_buffer_mount_length = ESP_ALIGN_DOWN(GDMA_MAX_BUFFER_SIZE_PER_LINK_ITEM, buffer_alignment);
if (!config->flags.bypass_buffer_align_check) {
// Address and size alignment checks are independent; both use the caller-provided buffer_alignment.
if (!config->flags.bypass_buffer_addr_align_check) {
ESP_RETURN_ON_FALSE_ISR(((uintptr_t)buf & (buffer_alignment - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "buf misalign idx=%"PRIu32" align=%"PRIu32, bi, buffer_alignment);
if (esp_efuse_is_flash_encryption_enabled()) {
// buffer size must be aligned to the encryption alignment which should be provided by the upper buffer_alignment
ESP_RETURN_ON_FALSE_ISR((len & (buffer_alignment - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "buf len misalign idx=%"PRIu32" len=%"PRIu32" align=%"PRIu32"", bi, len, buffer_alignment);
}
}
if (!config->flags.bypass_buffer_size_align_check) {
ESP_RETURN_ON_FALSE_ISR((len & (buffer_alignment - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "buf len misalign idx=%"PRIu32" len=%"PRIu32" align=%"PRIu32"", bi, len, buffer_alignment);
}
size_t num_items_need = (len + max_buffer_mount_length - 1) / max_buffer_mount_length;
ESP_RETURN_ON_FALSE_ISR(num_items_need <= remaining, ESP_ERR_INVALID_ARG, TAG,
@@ -217,7 +211,6 @@ esp_err_t gdma_link_mount_buffers(gdma_link_list_handle_t list, int start_item_i
if (buffer_alignment == 0) {
buffer_alignment = 1;
}
size_t max_buffer_mount_length = ESP_ALIGN_DOWN(GDMA_MAX_BUFFER_SIZE_PER_LINK_ITEM, buffer_alignment);
// skip zero-length buffer but scrub any stale descriptor to keep ring clean; no slot consumption
if (len == 0 || buf == NULL) {
lli_nc = (gdma_link_list_item_t *)(list->items_nc + begin_item_idx % list_item_capacity * item_size);
@@ -225,6 +218,7 @@ esp_err_t gdma_link_mount_buffers(gdma_link_list_handle_t list, int start_item_i
memset(lli_nc, 0, item_size);
continue;
}
size_t max_buffer_mount_length = ESP_ALIGN_DOWN(GDMA_MAX_BUFFER_SIZE_PER_LINK_ITEM, buffer_alignment);
size_t num_items_need = (len + max_buffer_mount_length - 1) / max_buffer_mount_length;
// mount the buffer to the link list
for (size_t i = 0; i < num_items_need; i++) {
+2 -1
View File
@@ -90,7 +90,8 @@ struct gdma_channel_t {
int periph_id; // Peripheral instance ID, indicates which peripheral is connected to this GDMA channel
int intr_priority; // interrupt priority, if set to 0, the driver will use the default priority
size_t int_mem_alignment; // alignment for memory in internal memory
size_t ext_mem_alignment; // alignment for memory in external memory
size_t ext_enc_mem_alignment; // alignment for external memory including MSPI encryption/ECC constraints
size_t ext_no_enc_mem_alignment; // alignment for external memory without MSPI region-specific constraints
esp_err_t (*del)(gdma_channel_t *channel); // channel deletion function, it's polymorphic, see `gdma_del_tx_channel` or `gdma_del_rx_channel`
struct {
uint32_t start_stop_by_etm: 1; // whether the channel is started/stopped by ETM
@@ -10,16 +10,25 @@
#include <stddef.h>
#include "sdkconfig.h"
#include "esp_private/gdma.h"
#include "esp_private/esp_mspi_align.h"
#if CONFIG_IDF_TARGET_ESP32S31
// ESP32-S31 LP AHB GDMA can't burst-access external PSRAM. Skip the
// flash-encrypted PSRAM test paths because encrypted PSRAM requires burst
// accesses aligned to the encryption block size.
// MSPI-strict PSRAM test paths because Flash Encryption / PSRAM ECC require
// burst accesses aligned to the MSPI block size.
#define GDMA_TEST_LP_AHB_BURST_PSRAM_SUPPORTED 0
#else
#define GDMA_TEST_LP_AHB_BURST_PSRAM_SUPPORTED 1
#endif
/**
* @brief Whether MSPI strict alignment is required (Flash Encryption and/or PSRAM ECC)
*/
static inline bool gdma_test_mspi_strict_alignment_required(void)
{
return esp_mspi_get_alignment(NULL) > 1;
}
#ifdef __cplusplus
extern "C" {
#endif
@@ -15,8 +15,6 @@
#include "freertos/semphr.h"
#include "ccomp_timer.h"
#include "esp_async_memcpy.h"
#include "hal/efuse_hal.h"
#include "esp_efuse.h"
#include "gdma_test_utils.h"
#if SOC_GDMA_SUPPORTED
@@ -168,7 +166,7 @@ static void test_memory_copy_blocking(async_memcpy_handle_t driver)
for (int off = 0; off < 4; off++) {
test_context.buffer_size = test_buffer_size[i];
test_context.seed = i;
if (!esp_efuse_is_flash_encryption_enabled()) {
if (!gdma_test_mspi_strict_alignment_required()) {
test_context.src_offset = off;
test_context.dst_offset = off;
}
@@ -257,8 +255,8 @@ TEST_CASE("memory copy with dest address unaligned", "[async mcp]")
};
[[maybe_unused]] async_memcpy_handle_t driver = NULL;
if (esp_efuse_is_flash_encryption_enabled()) {
TEST_PASS_MESSAGE("Flash encryption is enabled, skip this test");
if (gdma_test_mspi_strict_alignment_required()) {
TEST_PASS_MESSAGE("MSPI strict alignment required (Flash Encryption / PSRAM ECC), skip this test");
}
#if SOC_CP_DMA_SUPPORTED
@@ -441,8 +439,8 @@ TEST_CASE("memory copy performance 40KB: PSRAM->PSRAM", "[async mcp]")
#if SOC_HAS(LP_AHB_GDMA)
#if GDMA_LL_GET(LP_AHB_PSRAM_CAPABLE)
if (esp_efuse_is_flash_encryption_enabled() && !GDMA_TEST_LP_AHB_BURST_PSRAM_SUPPORTED) {
TEST_IGNORE_MESSAGE("Skipping LP AHB GDMA PSRAM->PSRAM under flash encryption");
if (gdma_test_mspi_strict_alignment_required() && !GDMA_TEST_LP_AHB_BURST_PSRAM_SUPPORTED) {
TEST_IGNORE_MESSAGE("Skipping LP AHB GDMA PSRAM->PSRAM under Flash Encryption / PSRAM ECC");
} else {
printf("Testing memcpy by LP AHB GDMA\r\n");
TEST_ESP_OK(esp_async_memcpy_install_gdma_lp_ahb(&driver_config, &driver));
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -11,10 +11,9 @@
#include "unity.h"
#include "esp_private/dw_gdma.h"
#include "hal/dw_gdma_ll.h"
#include "hal/efuse_hal.h"
#include "esp_cache.h"
#include "esp_private/esp_cache_private.h"
#include "esp_efuse.h"
#include "esp_private/esp_mspi_align.h"
TEST_CASE("DW_GDMA channel allocation", "[DW_GDMA]")
{
@@ -542,8 +541,8 @@ TEST_CASE("DW_GDMA M2M Test: memory set with fixed address", "[DW_GDMA]")
size_t int_mem_alignment = 0;
TEST_ESP_OK(esp_cache_get_alignment(MALLOC_CAP_SPIRAM, &ext_mem_alignment));
TEST_ESP_OK(esp_cache_get_alignment(0, &int_mem_alignment));
if (esp_efuse_is_flash_encryption_enabled()) {
TEST_PASS_MESSAGE("Flash encryption is enabled, skip this test");
if (esp_mspi_get_alignment(NULL) > 1) {
TEST_PASS_MESSAGE("MSPI strict alignment required (Flash Encryption / PSRAM ECC), skip this test");
}
uint8_t *src_buf = heap_caps_aligned_calloc(ext_mem_alignment, 1, 256, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
uint8_t *dst_buf = heap_caps_aligned_calloc(int_mem_alignment, 1, 256, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
@@ -20,11 +20,9 @@
#include "hal/gdma_ll.h"
#include "hal/cache_ll.h"
#include "hal/cache_hal.h"
#include "hal/efuse_hal.h"
#include "esp_cache.h"
#include "esp_memory_utils.h"
#include "gdma_test_utils.h"
#include "esp_efuse.h"
#include "esp_macros.h"
TEST_CASE("GDMA channel allocation", "[GDMA]")
@@ -275,7 +273,7 @@ static void test_gdma_m2m_transaction(gdma_channel_handle_t tx_chan, gdma_channe
TEST_ASSERT_NOT_NULL(done_sem);
TEST_ESP_OK(gdma_register_rx_event_callbacks(rx_chan, &rx_cbs, done_sem));
if (esp_efuse_is_flash_encryption_enabled()) {
if (gdma_test_mspi_strict_alignment_required()) {
dma_link_in_ext_mem = false;
}
@@ -283,9 +281,10 @@ static void test_gdma_m2m_transaction(gdma_channel_handle_t tx_chan, gdma_channe
gdma_link_list_handle_t rx_link_list = NULL;
test_gdma_config_link_list(tx_chan, rx_chan, &tx_link_list, &rx_link_list, 16, dma_link_in_ext_mem);
size_t int_mem_alignment = 0;
size_t ext_mem_alignment = 0;
TEST_ESP_OK(gdma_get_alignment_constraints(tx_chan, &int_mem_alignment, &ext_mem_alignment));
gdma_channel_alignment_info_t tx_align_info;
TEST_ESP_OK(gdma_get_channel_alignment_constraints(tx_chan, &tx_align_info));
size_t int_mem_alignment = tx_align_info.int_mem_alignment;
size_t __attribute__((unused)) ext_mem_alignment = tx_align_info.ext_enc_mem_alignment;
// allocate the source buffer from SRAM
uint8_t *src_data = heap_caps_aligned_calloc(int_mem_alignment, 1, 128, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
@@ -424,8 +423,8 @@ static void test_gdma_m2m_mode(bool trig_retention_backup)
#endif // SOC_HAS(AXI_GDMA)
#if SOC_HAS(LP_AHB_GDMA)
if (esp_efuse_is_flash_encryption_enabled() && !GDMA_TEST_LP_AHB_BURST_PSRAM_SUPPORTED) {
TEST_IGNORE_MESSAGE("Skip LP-AHB-GDMA GDMA M2M Mode under flash encryption");
if (gdma_test_mspi_strict_alignment_required() && !GDMA_TEST_LP_AHB_BURST_PSRAM_SUPPORTED) {
TEST_IGNORE_MESSAGE("Skip LP-AHB-GDMA GDMA M2M Mode under Flash Encryption / PSRAM ECC");
} else {
printf("Testing GDMA M2M Mode by LP-AHB GDMA%s\n", trig_retention_backup ? " with retention backup" : "");
TEST_ESP_OK(gdma_new_lp_ahb_channel(&chan_alloc_config, &tx_chan, &rx_chan));
@@ -569,8 +568,20 @@ static void test_gdma_m2m_unaligned_buffer_test(uint8_t *dst_data, uint8_t *src_
gdma_link_list_handle_t rx_link_list = NULL;
test_gdma_config_link_list(tx_chan, rx_chan, &tx_link_list, &rx_link_list, 0, false);
gdma_transfer_config_t transfer_config = {
#if GDMA_LL_AHB_RX_BURST_NEEDS_ALIGNMENT || CONFIG_GDMA_ENABLE_WEIGHTED_ARBITRATION
.max_data_burst_size = 0,
#else
.max_data_burst_size = 16,
#endif
.access_ext_mem = false,
};
TEST_ESP_OK(gdma_config_transfer(rx_chan, &transfer_config));
size_t rx_mem_alignment = 0;
TEST_ESP_OK(gdma_get_alignment_constraints(rx_chan, &rx_mem_alignment, NULL));
gdma_channel_alignment_info_t rx_align_info;
TEST_ESP_OK(gdma_get_channel_alignment_constraints(rx_chan, &rx_align_info));
rx_mem_alignment = rx_align_info.int_mem_alignment;
// prepare the source data
for (int i = 0; i < data_length; i++) {
@@ -602,6 +613,7 @@ static void test_gdma_m2m_unaligned_buffer_test(uint8_t *dst_data, uint8_t *src_
rx_aligned_buf_mount_config[i].buffer = align_array.aligned_buffer[i].aligned_buffer;
rx_aligned_buf_mount_config[i].buffer_alignment = MAX(sram_alignment, rx_mem_alignment);
rx_aligned_buf_mount_config[i].length = align_array.aligned_buffer[i].length;
rx_aligned_buf_mount_config[i].flags.bypass_buffer_size_align_check = true; // head and tail buffer size is not aligned to the cache line size
}
TEST_ESP_OK(gdma_link_mount_buffers(rx_link_list, 0, rx_aligned_buf_mount_config, 3, NULL));
@@ -636,8 +648,8 @@ static void test_gdma_m2m_unaligned_buffer_test(uint8_t *dst_data, uint8_t *src_
TEST_CASE("GDMA M2M Unaligned RX Buffer Test", "[GDMA][M2M]")
{
if (esp_efuse_is_flash_encryption_enabled()) {
TEST_PASS_MESSAGE("Flash encryption is enabled, skip this test");
if (gdma_test_mspi_strict_alignment_required()) {
TEST_PASS_MESSAGE("MSPI strict alignment required (Flash Encryption / PSRAM ECC), skip this test");
}
uint8_t *sbuf = heap_caps_aligned_calloc(64, 1, 10240, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
@@ -845,8 +857,8 @@ TEST_CASE("GDMA memory copy SRAM->PSRAM->SRAM", "[GDMA][M2M]")
#if SOC_HAS(LP_AHB_GDMA)
#if GDMA_LL_GET(LP_AHB_PSRAM_CAPABLE)
if (esp_efuse_is_flash_encryption_enabled() && !GDMA_TEST_LP_AHB_BURST_PSRAM_SUPPORTED) {
TEST_IGNORE_MESSAGE("Skipping LP-AHB-GDMA SRAM->PSRAM->SRAM under flash encryption");
if (gdma_test_mspi_strict_alignment_required() && !GDMA_TEST_LP_AHB_BURST_PSRAM_SUPPORTED) {
TEST_IGNORE_MESSAGE("Skipping LP-AHB-GDMA SRAM->PSRAM->SRAM under Flash Encryption / PSRAM ECC");
} else {
printf("Testing LP-AHB-GDMA memory copy SRAM->PSRAM->SRAM\n");
TEST_ESP_OK(gdma_new_lp_ahb_channel(&chan_alloc_config, &tx_chan, &rx_chan));