Merge branch 'fix/psram-ecc-mspi-dma-align_v6.1' into 'release/v6.1'

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

See merge request espressif/esp-idf!52363
This commit is contained in:
morris
2026-09-04 18:48:05 +08:00
59 changed files with 805 additions and 457 deletions

View File

@@ -9,6 +9,7 @@
#include "freertos/semphr.h"
#include "esp_private/gdma.h"
#include "esp_private/gdma_link.h"
#include "esp_private/esp_mspi_align.h"
#include "soc/ahb_dma_struct.h"
#include "hal/dma_types.h"
#include "esp_check.h"
@@ -180,8 +181,13 @@ esp_err_t asrc_hw_gdma_create_link_list(uint32_t byte_cnt, asrc_hw_gdma_link_lis
ESP_RETURN_ON_FALSE(list_hd, ESP_ERR_INVALID_ARG, TAG, "NULL pointer");
ESP_RETURN_ON_FALSE(max_desc_num, ESP_ERR_INVALID_ARG, TAG, "NULL pointer");
esp_err_t ret = ESP_OK;
int32_t desc_num = byte_cnt / ASRC_HW_GDMA_DESC_BUFFER_MAX_SIZE;
if (byte_cnt % ASRC_HW_GDMA_DESC_BUFFER_MAX_SIZE != 0) {
size_t mspi_align = esp_mspi_get_alignment(NULL);
uint32_t max_desc_size = ASRC_HW_GDMA_DESC_BUFFER_MAX_SIZE;
if (mspi_align > 1) {
max_desc_size &= ~(mspi_align - 1);
}
int32_t desc_num = byte_cnt / max_desc_size;
if (byte_cnt % max_desc_size != 0) {
desc_num++;
}
gdma_link_list_handle_t list = (gdma_link_list_handle_t)(*list_hd);
@@ -208,21 +214,30 @@ esp_err_t asrc_hw_gdma_mount_link_list(asrc_hw_gdma_link_list_handle_t list_hd,
ESP_RETURN_ON_FALSE(list_hd, ESP_ERR_INVALID_ARG, TAG, "NULL pointer");
esp_err_t ret = ESP_OK;
uint32_t remaining_byte_cnt = byte_cnt;
size_t mspi_align = esp_mspi_get_alignment(buf);
if (mspi_align > 1) {
ESP_RETURN_ON_FALSE((((uintptr_t)buf & (mspi_align - 1)) == 0) && ((byte_cnt & (mspi_align - 1)) == 0),
ESP_ERR_INVALID_ARG, TAG, "buffer addr or size not aligned to MSPI alignment");
}
uint32_t max_desc_size = ASRC_HW_GDMA_DESC_BUFFER_MAX_SIZE;
if (mspi_align > 1) {
max_desc_size &= ~(mspi_align - 1);
}
gdma_buffer_mount_config_t mount_config[desc_num] = {};
for (int i = 0; i < desc_num; i++) {
mount_config[i].buffer = buf;
mount_config[i].flags.bypass_buffer_align_check = true;
mount_config[i].buffer_alignment = mspi_align;
if ((i + 1) != desc_num) {
mount_config[i].length = ASRC_HW_GDMA_DESC_BUFFER_MAX_SIZE;
mount_config[i].length = max_desc_size;
mount_config[i].flags.mark_eof = 0;
mount_config[i].flags.mark_final = GDMA_FINAL_LINK_TO_DEFAULT;
remaining_byte_cnt -= ASRC_HW_GDMA_DESC_BUFFER_MAX_SIZE;
remaining_byte_cnt -= max_desc_size;
} else {
mount_config[i].length = remaining_byte_cnt;
mount_config[i].flags.mark_eof = 1;
mount_config[i].flags.mark_final = GDMA_FINAL_LINK_TO_NULL;
}
buf += ASRC_HW_GDMA_DESC_BUFFER_MAX_SIZE;
buf += max_desc_size;
}
ret = gdma_link_mount_buffers((gdma_link_list_handle_t)list_hd, 0, mount_config, desc_num, NULL);
if (ret != ESP_OK) {

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -221,10 +221,12 @@ esp_err_t bitscrambler_loopback_run(bitscrambler_handle_t bs, void *buffer_in, s
gdma_reset(bsl->tx_channel);
bitscrambler_reset(bs);
size_t in_alignment = gdma_get_buffer_alignment_constraint(bsl->tx_channel, buffer_in);
size_t out_alignment = gdma_get_buffer_alignment_constraint(bsl->rx_channel, buffer_out);
// mount in and out buffer to the DMA link list
gdma_buffer_mount_config_t in_buf_mount_config = {
.buffer = buffer_in,
.buffer_alignment = 4,
.buffer_alignment = in_alignment,
.length = length_bytes_in,
.flags = {
.mark_eof = true,
@@ -234,7 +236,7 @@ esp_err_t bitscrambler_loopback_run(bitscrambler_handle_t bs, void *buffer_in, s
gdma_link_mount_buffers(bsl->tx_link_list, 0, &in_buf_mount_config, 1, NULL);
gdma_buffer_mount_config_t out_buf_mount_config = {
.buffer = buffer_out,
.buffer_alignment = 4,
.buffer_alignment = out_alignment,
.length = length_bytes_out,
.flags = {
.mark_eof = false,

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -13,6 +13,7 @@
#include "esp_memory_utils.h"
#define ALIGN_UP_BY(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
#define ALIGN_DOWN_BY(num, align) ((num) & ~((align) - 1))
#if defined(SOC_GDMA_TRIG_PERIPH_CAM0_BUS) && (SOC_GDMA_TRIG_PERIPH_CAM0_BUS == SOC_GDMA_BUS_AHB)
#define DVP_GDMA_NEW_CHANNEL gdma_new_ahb_channel
@@ -39,12 +40,12 @@ static const char *TAG = "dvp_gdma";
* - ESP_OK on success
* - Others if failed
*/
static void IRAM_ATTR esp_cam_ctlr_dvp_config_dma_desc(esp_cam_ctlr_dvp_dma_desc_t *desc, uint8_t *buffer, uint32_t size)
static void IRAM_ATTR esp_cam_ctlr_dvp_config_dma_desc(esp_cam_ctlr_dvp_dma_desc_t *desc, uint8_t *buffer, uint32_t size, uint32_t max_desc_size)
{
size_t n = 0;
while (size) {
uint32_t node_size = MIN(size, ESP_CAM_CTLR_DVP_DMA_DESC_BUFFER_MAX_SIZE);
uint32_t node_size = MIN(size, max_desc_size);
desc[n].dw0.size = node_size;
desc[n].dw0.length = 0;
@@ -105,10 +106,18 @@ esp_err_t esp_cam_ctlr_dvp_dma_init(esp_cam_ctlr_dvp_dma_t *dma, uint32_t burst_
};
ESP_GOTO_ON_ERROR(gdma_config_transfer(dma->dma_chan, &transfer_config), fail1, TAG, "set trans ability failed");
gdma_get_alignment_constraints(dma->dma_chan, &dma->int_mem_align, &dma->ext_mem_align);
gdma_channel_alignment_info_t align_info;
gdma_get_channel_alignment_constraints(dma->dma_chan, &align_info);
dma->int_mem_align = align_info.int_mem_alignment;
dma->ext_mem_align = align_info.ext_enc_mem_alignment;
dma->desc_count = size / ESP_CAM_CTLR_DVP_DMA_DESC_BUFFER_MAX_SIZE;
if (size % ESP_CAM_CTLR_DVP_DMA_DESC_BUFFER_MAX_SIZE) {
size_t buffer_alignment = dma->ext_mem_align;
size_t desc_max_size = ESP_CAM_CTLR_DVP_DMA_DESC_BUFFER_MAX_SIZE;
if (buffer_alignment > 1) {
desc_max_size = ALIGN_DOWN_BY(desc_max_size, buffer_alignment);
}
dma->desc_count = size / desc_max_size;
if (size % desc_max_size) {
dma->desc_count++;
}
dma->size = size;
@@ -164,7 +173,14 @@ esp_err_t IRAM_ATTR esp_cam_ctlr_dvp_dma_start(esp_cam_ctlr_dvp_dma_t *dma, uint
ESP_RETURN_ON_FALSE_ISR(dma, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
ESP_RETURN_ON_FALSE_ISR(dma->size >= size, ESP_ERR_INVALID_ARG, TAG, "input buffer size is out of range");
esp_cam_ctlr_dvp_config_dma_desc(dma->desc, buffer, size);
size_t buffer_alignment = gdma_get_buffer_alignment_constraint(dma->dma_chan, buffer);
ESP_RETURN_ON_FALSE_ISR(((uintptr_t)buffer & (buffer_alignment - 1)) == 0 && (size & (buffer_alignment - 1)) == 0,
ESP_ERR_INVALID_ARG, TAG, "buffer addr or size not aligned");
uint32_t max_desc_size = ESP_CAM_CTLR_DVP_DMA_DESC_BUFFER_MAX_SIZE;
if (buffer_alignment > 1) {
max_desc_size = ALIGN_DOWN_BY(max_desc_size, buffer_alignment);
}
esp_cam_ctlr_dvp_config_dma_desc(dma->desc, buffer, size, max_desc_size);
if (esp_ptr_external_ram(dma->desc)) {
esp_err_t ret = esp_cache_msync(dma->desc, dma->desc_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE);

View File

@@ -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
*/

View File

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

View File

@@ -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;

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

View File

@@ -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;
}

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

View File

@@ -48,10 +48,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
@@ -153,10 +149,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
@@ -358,12 +324,11 @@ static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *s
trans->stash_buffer = NULL;
}
size_t buffer_alignment = 0;
size_t num_dma_nodes = 0;
// allocate gdma TX link
buffer_alignment = esp_ptr_internal(src) ? mcp_gdma->tx_int_mem_alignment : mcp_gdma->tx_ext_mem_alignment;
num_dma_nodes = esp_dma_calculate_node_count(n, buffer_alignment, MCP_DMA_DESCRIPTOR_BUFFER_MAX_SIZE);
size_t tx_buffer_alignment = gdma_get_buffer_alignment_constraint(mcp_gdma->tx_channel, src);
num_dma_nodes = esp_dma_calculate_node_count(n, 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,
@@ -377,7 +342,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 = src,
.buffer_alignment = buffer_alignment,
.buffer_alignment = tx_buffer_alignment,
.length = n,
.flags = {
.mark_eof = true, // mark the last item as EOF, so the RX channel can also received an EOF list item
@@ -395,8 +360,8 @@ static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *s
}
// allocate gdma RX link
buffer_alignment = esp_ptr_internal(dst) ? mcp_gdma->rx_int_mem_alignment : mcp_gdma->rx_ext_mem_alignment;
num_dma_nodes = esp_dma_calculate_node_count(n, buffer_alignment, MCP_DMA_DESCRIPTOR_BUFFER_MAX_SIZE);
size_t rx_buffer_alignment = gdma_get_buffer_alignment_constraint(mcp_gdma->rx_channel, dst);
num_dma_nodes = esp_dma_calculate_node_count(n, 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 + 3, // add 3 extra items for the cache aligned buffers
@@ -406,7 +371,6 @@ static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *s
},
};
ESP_GOTO_ON_ERROR(gdma_new_link_list(&rx_link_cfg, &trans->rx_link_list), err, TAG, "failed to create RX link list");
// if the destination buffer address is not cache line aligned, we need to split the buffer into cache line aligned ones
ESP_GOTO_ON_ERROR(esp_dma_split_rx_buffer_to_cache_aligned(dst, n, &trans->rx_buf_array, &trans->stash_buffer),
err, TAG, "failed to split RX buffer into aligned ones");
@@ -414,7 +378,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[3] = {0};
for (int i = 0; i < 3; i++) {
rx_buf_mount_config[i].buffer = trans->rx_buf_array.aligned_buffer[i].aligned_buffer;
rx_buf_mount_config[i].buffer_alignment = buffer_alignment;
rx_buf_mount_config[i].buffer_alignment = rx_buffer_alignment;
rx_buf_mount_config[i].length = trans->rx_buf_array.aligned_buffer[i].length;
}
gdma_link_mount_buffers(trans->rx_link_list, 0, rx_buf_mount_config, 3, NULL);

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;

View File

@@ -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
*/
@@ -45,7 +45,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
@@ -69,10 +69,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;

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

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"
ESP_LOG_ATTR_TAG(TAG, "gdma-link");
@@ -78,10 +77,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;
}
@@ -92,12 +91,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, ALIGN_UP(num_items * item_size, data_cache_line_size),
@@ -193,12 +187,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 = 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,
@@ -219,7 +213,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 = 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);
@@ -227,6 +220,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 = 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++) {

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

View File

@@ -9,16 +9,25 @@
#include <stdbool.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

View File

@@ -16,8 +16,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
@@ -172,7 +170,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;
}
@@ -259,8 +257,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
@@ -426,8 +424,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));

View File

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

View File

@@ -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"
#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
#define ALIGN_DOWN(num, align) ((num) & ~((align) - 1))
@@ -277,7 +275,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;
}
@@ -285,9 +283,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);
@@ -426,8 +425,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));
@@ -571,8 +570,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++) {
@@ -604,6 +615,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));
@@ -638,8 +650,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);
@@ -847,8 +859,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));

View File

@@ -369,9 +369,9 @@ static esp_err_t i3c_master_init_dma(i3c_master_bus_t *i3c_master_handle, const
ESP_GOTO_ON_ERROR(gdma_config_transfer(i3c_master_handle->dma_tx_chan, &transfer_cfg), err2, TAG, "Config DMA tx channel transfer failed");
// create DMA link list
size_t int_mem_align = 0;
gdma_get_alignment_constraints(i3c_master_handle->dma_tx_chan, &int_mem_align, NULL);
i3c_master_handle->dma_buffer_alignment = I3C_ALIGN_UP(int_mem_align, I3C_MASTER_DMA_INTERFACE_ALIGNMENT);
gdma_channel_alignment_info_t align_info;
gdma_get_channel_alignment_constraints(i3c_master_handle->dma_tx_chan, &align_info);
i3c_master_handle->dma_buffer_alignment = I3C_ALIGN_UP(align_info.int_mem_alignment, I3C_MASTER_DMA_INTERFACE_ALIGNMENT);
size_t num_dma_nodes = esp_dma_calculate_node_count(dma_config->max_transfer_size, i3c_master_handle->dma_buffer_alignment, DMA_DESCRIPTOR_BUFFER_MAX_SIZE);
gdma_link_list_config_t dma_link_config = {
.item_alignment = 4, // 4 bytes alignment for AHB-DMA

View File

@@ -10,7 +10,6 @@
#include "esp_private/periph_ctrl.h"
#include "jpeg_private.h"
#include "hal/jpeg_hal.h"
#include "esp_memory_utils.h"
#include "driver/jpeg_types.h"
#include "sys/lock.h"
#include "sys/queue.h"
@@ -23,7 +22,6 @@
#include "esp_log.h"
#include "esp_check.h"
#include "hal/jpeg_periph.h"
#include "esp_psram.h"
#if JPEG_USE_RETENTION_LINK
#include "esp_private/sleep_retention.h"
#endif
@@ -292,17 +290,3 @@ esp_err_t jpeg_check_intr_priority(jpeg_codec_handle_t jpeg_codec, int intr_prio
ESP_RETURN_ON_FALSE(!intr_priority_conflict, ESP_ERR_INVALID_STATE, TAG, "intr_priority conflict, already is %d but attempt to %d", jpeg_codec->intr_priority, intr_priority);
return ret;
}
bool jpeg_check_dma2d_buffer(const void *buffer)
{
#if CONFIG_SECURE_FLASH_ENC_ENABLED
// jpeg cannot handle encrypted data.
if (esp_ptr_external_ram(buffer) && !esp_psram_ptr_is_no_enc(buffer)) {
return false;
}
if (esp_ptr_in_drom(buffer)) {
return false;
}
#endif
return true;
}

View File

@@ -290,8 +290,10 @@ esp_err_t jpeg_decoder_process(jpeg_decoder_handle_t decoder_engine, const jpeg_
"jpeg decode decode_outbuf or out_buffer size is not aligned, please use jpeg_alloc_decoder_mem to malloc your buffer");
// both the bitstream and output buffer are accessed by the 2D-DMA
ESP_RETURN_ON_FALSE(jpeg_check_dma2d_buffer(bit_stream) && jpeg_check_dma2d_buffer(decode_outbuf), ESP_ERR_INVALID_ARG, TAG,
"jpeg decode buffer is not 16-byte aligned or not in unencrypted PSRAM, please use jpeg_alloc_decoder_mem to malloc your buffer");
size_t bit_stream_alignment = dma2d_get_buffer_alignment_constraint(bit_stream);
size_t decode_outbuf_alignment = dma2d_get_buffer_alignment_constraint(decode_outbuf);
ESP_RETURN_ON_FALSE(bit_stream_alignment <= 1 && decode_outbuf_alignment <= 1, ESP_ERR_INVALID_ARG, TAG,
"jpeg decode buffer doesn't satisfy DMA2D alignment constraints, please use jpeg_alloc_decoder_mem to malloc your buffer");
esp_err_t ret = ESP_OK;

View File

@@ -194,7 +194,10 @@ esp_err_t jpeg_encoder_process(jpeg_encoder_handle_t encoder_engine, const jpeg_
ESP_RETURN_ON_FALSE(out_size, ESP_ERR_INVALID_ARG, TAG, "jpeg encode picture out_size is null");
ESP_RETURN_ON_FALSE(((uintptr_t)bit_stream % cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA)) == 0, ESP_ERR_INVALID_ARG, TAG, "jpeg encode bit stream is not aligned, please use jpeg_alloc_encoder_mem to malloc your buffer");
// both the input picture and output bitstream are accessed by the 2D-DMA
ESP_RETURN_ON_FALSE(jpeg_check_dma2d_buffer(encode_inbuf) && jpeg_check_dma2d_buffer(bit_stream), ESP_ERR_INVALID_ARG, TAG, "jpeg encode buffer is not 16-byte aligned or not in unencrypted PSRAM, please use jpeg_alloc_encoder_mem to malloc your buffer");
size_t encode_inbuf_alignment = dma2d_get_buffer_alignment_constraint(encode_inbuf);
size_t bit_stream_alignment = dma2d_get_buffer_alignment_constraint(bit_stream);
ESP_RETURN_ON_FALSE(encode_inbuf_alignment <= 1 && bit_stream_alignment <= 1, ESP_ERR_INVALID_ARG, TAG,
"jpeg encode buffer doesn't satisfy DMA2D alignment constraints, please use jpeg_alloc_encoder_mem to malloc your buffer");
esp_err_t ret = ESP_OK;

View File

@@ -265,18 +265,6 @@ esp_err_t jpeg_isr_deregister(jpeg_codec_handle_t jpeg_codec, jpeg_isr_handler_t
*/
esp_err_t jpeg_check_intr_priority(jpeg_codec_handle_t jpeg_codec, int intr_priority);
/**
* @brief Validate a user buffer that will be accessed by the 2D-DMA
*
* The buffer must be 16-byte aligned. When CONFIG_SPIRAM_ENC_EXEMPT is enabled,
* a PSRAM buffer must reside in the unencrypted carve-out, since the 2D-DMA
* cannot access encrypted PSRAM. Internal RAM buffers are always accepted.
*
* @param buffer Buffer pointer provided by the user
* @return true if the buffer can be used by the 2D-DMA, false otherwise
*/
bool jpeg_check_dma2d_buffer(const void *buffer);
/**
* @brief Create sleep retention link
*

View File

@@ -152,7 +152,6 @@ size_t parlio_rx_mount_transaction_buffer(parlio_rx_unit_handle_t rx_unit, parli
mount_config[0].buffer = trans->aligned_payload.buf.head.aligned_buffer;
mount_config[0].buffer_alignment = trans->alignment;
mount_config[0].length = trans->aligned_payload.buf.head.length;
mount_config[0].flags.bypass_buffer_align_check = false;
mount_config[0].flags.mark_eof = false;
mount_config[0].flags.mark_final = GDMA_FINAL_LINK_TO_DEFAULT;
}
@@ -171,7 +170,6 @@ size_t parlio_rx_mount_transaction_buffer(parlio_rx_unit_handle_t rx_unit, parli
mount_config[i].buffer = (void *)((uint8_t *)trans->aligned_payload.buf.body.aligned_buffer + offset);
mount_config[i].buffer_alignment = trans->alignment;
mount_config[i].length = mount_size;
mount_config[i].flags.bypass_buffer_align_check = false;
mount_config[i].flags.mark_eof = false;
mount_config[i].flags.mark_final = GDMA_FINAL_LINK_TO_DEFAULT;
offset += mount_size;
@@ -182,7 +180,6 @@ size_t parlio_rx_mount_transaction_buffer(parlio_rx_unit_handle_t rx_unit, parli
mount_config[required_node_num - 1].buffer = trans->aligned_payload.buf.tail.aligned_buffer;
mount_config[required_node_num - 1].buffer_alignment = trans->alignment;
mount_config[required_node_num - 1].length = trans->aligned_payload.buf.tail.length;
mount_config[required_node_num - 1].flags.bypass_buffer_align_check = false;
}
/* For infinite transaction, link the node as a ring */
mount_config[required_node_num - 1].flags.mark_final = !trans->flags.infinite ? GDMA_FINAL_LINK_TO_NULL : GDMA_FINAL_LINK_TO_HEAD;
@@ -472,7 +469,10 @@ static esp_err_t parlio_rx_unit_init_dma(parlio_rx_unit_handle_t rx_unit, size_t
.access_ext_mem = true,
};
ESP_RETURN_ON_ERROR(gdma_config_transfer(rx_unit->dma_chan, &trans_cfg), TAG, "config DMA transfer failed");
ESP_RETURN_ON_ERROR(gdma_get_alignment_constraints(rx_unit->dma_chan, &rx_unit->int_mem_align, &rx_unit->ext_mem_align), TAG, "get alignment constraints failed");
gdma_channel_alignment_info_t align_info;
ESP_RETURN_ON_ERROR(gdma_get_channel_alignment_constraints(rx_unit->dma_chan, &align_info), TAG, "get alignment constraints failed");
rx_unit->int_mem_align = align_info.int_mem_alignment;
rx_unit->ext_mem_align = align_info.ext_enc_mem_alignment;
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
uint32_t cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA);
rx_unit->int_mem_align = rx_unit->int_mem_align > cache_line_size ? rx_unit->int_mem_align : cache_line_size;
@@ -984,6 +984,10 @@ esp_err_t parlio_rx_unit_receive(parlio_rx_unit_handle_t rx_unit,
ESP_RETURN_ON_FALSE(recv_cfg->delimiter, ESP_ERR_INVALID_ARG, TAG, "no delimiter specified");
ESP_RETURN_ON_FALSE(payload_size <= rx_unit->max_recv_size, ESP_ERR_INVALID_ARG, TAG, "trans length too large");
size_t alignment = rx_unit->int_mem_align;
// partial receive with indirect mount always use internal memory
if (!(recv_cfg->flags.partial_rx_en && recv_cfg->flags.indirect_mount)) {
alignment = gdma_get_buffer_alignment_constraint(rx_unit->dma_chan, payload);
}
if (recv_cfg->flags.partial_rx_en) {
ESP_RETURN_ON_FALSE(payload_size >= 2 * alignment, ESP_ERR_INVALID_ARG, TAG, "The payload size should greater than %"PRIu32, 2 * alignment);
}
@@ -1051,6 +1055,10 @@ esp_err_t parlio_rx_unit_receive_from_isr(parlio_rx_unit_handle_t rx_unit,
// Can only be called from ISR
PARLIO_RX_CHECK_ISR(xPortInIsrContext() == pdTRUE, ESP_ERR_INVALID_STATE);
size_t alignment = rx_unit->int_mem_align;
// partial receive with indirect mount always use internal memory
if (!(recv_cfg->flags.partial_rx_en && recv_cfg->flags.indirect_mount)) {
alignment = gdma_get_buffer_alignment_constraint(rx_unit->dma_chan, payload);
}
if (recv_cfg->flags.partial_rx_en) {
PARLIO_RX_CHECK_ISR(payload_size >= 2 * alignment, ESP_ERR_INVALID_ARG);
}
@@ -1070,6 +1078,7 @@ esp_err_t parlio_rx_unit_receive_from_isr(parlio_rx_unit_handle_t rx_unit,
}
dma_buffer_split_array_t dma_buf_array = {0};
/* Create the internal DMA buffer for the infinite transaction if indirect_mount is set */
if (recv_cfg->flags.partial_rx_en && recv_cfg->flags.indirect_mount) {
/* The internal DMA buffer should be allocated before calling this function */
PARLIO_RX_CHECK_ISR(rx_unit->dma_buf, ESP_ERR_INVALID_STATE);
@@ -1077,7 +1086,6 @@ esp_err_t parlio_rx_unit_receive_from_isr(parlio_rx_unit_handle_t rx_unit,
dma_buf_array.buf.body.recovery_address = rx_unit->dma_buf;
dma_buf_array.buf.body.length = payload_size;
} else {
/* Create the internal DMA buffer for the infinite transaction if indirect_mount is set */
esp_err_t esp_ret = esp_dma_split_rx_buffer_to_cache_aligned(payload, payload_size, &dma_buf_array, &rx_unit->stash_buf[rx_unit->stash_buf_idx]);
PARLIO_RX_CHECK_ISR(esp_ret == ESP_OK, esp_ret);
rx_unit->stash_buf_idx = !rx_unit->stash_buf_idx;

View File

@@ -163,7 +163,10 @@ static esp_err_t parlio_tx_unit_init_dma(parlio_tx_unit_t *tx_unit, const parlio
.access_ext_mem = true, // support transmit PSRAM buffer
};
ESP_RETURN_ON_ERROR(gdma_config_transfer(tx_unit->dma_chan, &trans_cfg), TAG, "config DMA transfer failed");
gdma_get_alignment_constraints(tx_unit->dma_chan, &tx_unit->int_mem_align, &tx_unit->ext_mem_align);
gdma_channel_alignment_info_t align_info;
gdma_get_channel_alignment_constraints(tx_unit->dma_chan, &align_info);
tx_unit->int_mem_align = align_info.int_mem_alignment;
tx_unit->ext_mem_align = align_info.ext_enc_mem_alignment;
// create DMA link list
size_t buffer_alignment = MAX(tx_unit->int_mem_align, tx_unit->ext_mem_align);
@@ -463,7 +466,7 @@ esp_err_t parlio_tx_unit_register_event_callbacks(parlio_tx_unit_handle_t tx_uni
static void parlio_mount_buffer(parlio_tx_unit_t *tx_unit, parlio_tx_trans_desc_t *t)
{
size_t buffer_alignment = esp_ptr_internal(t->payload) ? tx_unit->int_mem_align : tx_unit->ext_mem_align;
size_t buffer_alignment = gdma_get_buffer_alignment_constraint(tx_unit->dma_chan, t->payload);
// DMA transfer data based on bytes not bits, so convert the bit length to bytes, round up
size_t payload_bytes = (t->payload_bits + 7) / 8;
gdma_buffer_mount_config_t mount_config = {
@@ -701,7 +704,7 @@ esp_err_t parlio_tx_unit_transmit(parlio_tx_unit_handle_t tx_unit, const void *p
}
#endif // !PARLIO_LL_SUPPORT(TX_EOF_FROM_DMA)
size_t alignment = esp_ptr_external_ram(payload) ? tx_unit->ext_mem_align : tx_unit->int_mem_align;
size_t alignment = gdma_get_buffer_alignment_constraint(tx_unit->dma_chan, payload);
// check alignment
ESP_RETURN_ON_FALSE(((uint32_t)payload & (alignment - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "payload address %p not aligned to %d", payload, alignment);
ESP_RETURN_ON_FALSE((payload_bits & (alignment - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "payload size %d not aligned to %d", payload_bits, alignment);

View File

@@ -35,7 +35,6 @@
#include "hal/color_types.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/sleep_retention.h"
#include "esp_efuse.h"
#include "soc/soc_caps.h"
static const char *TAG = "ppa_core";
@@ -349,9 +348,6 @@ esp_err_t ppa_register_client(const ppa_client_config_t *config, ppa_client_hand
client->oper_type = config->oper_type;
client->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
client->data_burst_length = config->data_burst_length ? config->data_burst_length : PPA_DATA_BURST_LENGTH_128;
if (esp_efuse_is_flash_encryption_enabled() && (client->data_burst_length < SOC_MEMSPI_ENCRYPTION_ALIGNMENT)) {
ESP_LOGW(TAG, "flash encryption is enabled, but selected data burst length does not meet encryption alignment restriction if accessing external memory, will be automatically adjusted later on");
}
if (config->oper_type == PPA_OPERATION_SRM) {
ppa_engine_config_t engine_config = {
@@ -621,28 +617,24 @@ bool ppa_check_buffer_alignment(ppa_client_handle_t ppa_client, const void *pic_
}
}
// 2. check with mspi encryption alignment
// When flash encryption is enabled, and in/out buffer are in PSRAM (if located in internal RAM, there is no alignment restriction due to encryption):
// - The width of the window multiply byte number of one pixel should align to SOC_MEMSPI_ENCRYPTION_ALIGNMENT
// - The starting address of every row of the window should align to SOC_MEMSPI_ENCRYPTION_ALIGNMENT
// (which also implies the address and size of the in/out buffer will align to SOC_MEMSPI_ENCRYPTION_ALIGNMENT)
// check pic_width, block_width, block_head
// 2. check with DMA2D/MSPI 2D transaction alignment
// When MSPI strict alignment is required, and in/out buffer are in PSRAM (if located in internal RAM, there is no alignment restriction):
// - The width of the window multiply byte number of one pixel should align to MSPI alignment
// - The starting address of every row of the window should align to MSPI alignment
// (which also implies the address and size of the in/out buffer will align to MSPI alignment)
const void *buffer = (is_input) ? ((ppa_in_pic_blk_config_t *)pic_blk_config)->buffer : ((ppa_out_pic_blk_config_t *)pic_blk_config)->buffer;
if (esp_efuse_is_flash_encryption_enabled() && !esp_ptr_internal(buffer)) {
size_t dma2d_align = dma2d_get_buffer_alignment_constraint(buffer);
if (dma2d_align > 1) {
if (ppa_client->engine->type == PPA_ENGINE_TYPE_SRM) {
ESP_LOGE(TAG, "SRM processes by macro blocks, where alignment is uncontrollable, makes it unable to work with flash encrypted if buffer is in external memory");
ESP_LOGE(TAG, "SRM processes by macro blocks, where alignment is uncontrollable, makes it unable to work with MSPI strict alignment if buffer is in external memory");
return false;
}
uint32_t pic_width = (is_input) ? ((ppa_in_pic_blk_config_t *)pic_blk_config)->pic_w : ((ppa_out_pic_blk_config_t *)pic_blk_config)->pic_w;
uint32_t block_offset_x = (is_input) ? ((ppa_in_pic_blk_config_t *)pic_blk_config)->block_offset_x : ((ppa_out_pic_blk_config_t *)pic_blk_config)->block_offset_x;
esp_color_fourcc_t color_mode = (is_input) ? ((ppa_in_pic_blk_config_t *)pic_blk_config)->cm : ((ppa_out_pic_blk_config_t *)pic_blk_config)->cm;
uint32_t pixel_depth_bytes = color_hal_pixel_format_fourcc_get_bit_depth(color_mode) / 8;
if (((pic_width * pixel_depth_bytes) & (SOC_MEMSPI_ENCRYPTION_ALIGNMENT - 1)) != 0 ||
((block_width * pixel_depth_bytes) & (SOC_MEMSPI_ENCRYPTION_ALIGNMENT - 1)) != 0 ||
((block_offset_x * pixel_depth_bytes) & (SOC_MEMSPI_ENCRYPTION_ALIGNMENT - 1)) != 0) {
ESP_LOGE(TAG, "(pic_width/block_width/block_offset_x * pixel_depth_bytes) not aligned to SOC_MEMSPI_ENCRYPTION_ALIGNMENT");
uint32_t bit_depth = color_hal_pixel_format_fourcc_get_bit_depth(color_mode);
if (!dma2d_check_transaction_alignment_constraint(buffer, pic_width, block_width, block_offset_x, bit_depth)) {
ESP_LOGE(TAG, "buffer/pic_width/block_width/block_offset_x does not satisfy DMA2D/MSPI alignment (%zu)", dma2d_align);
return false;
}
}

View File

@@ -232,8 +232,6 @@ struct rmt_rx_channel_t {
void *user_data; // user context
rmt_rx_trans_desc_t trans_desc; // transaction description
size_t num_dma_nodes; // number of DMA nodes, determined by how big the memory block that user configures
size_t dma_int_mem_alignment; // DMA buffer alignment (both in size and address) for internal RX memory
size_t dma_ext_mem_alignment; // DMA buffer alignment (both in size and address) for external RX memory
gdma_link_list_handle_t dma_link; // DMA link list handle
};

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -53,8 +53,6 @@ static esp_err_t rmt_rx_init_dma_link(rmt_rx_channel_t *rx_channel, const rmt_rx
.max_data_burst_size = 32,
};
ESP_RETURN_ON_ERROR(gdma_config_transfer(rx_channel->base.dma_chan, &transfer_cfg), TAG, "config DMA transfer failed");
// get the alignment requirement from DMA
gdma_get_alignment_constraints(rx_channel->base.dma_chan, &rx_channel->dma_int_mem_alignment, &rx_channel->dma_ext_mem_alignment);
// register event callbacks
gdma_rx_event_callbacks_t cbs = {
@@ -63,7 +61,10 @@ static esp_err_t rmt_rx_init_dma_link(rmt_rx_channel_t *rx_channel, const rmt_rx
// register the DMA callbacks may fail if the interrupt service can not be installed successfully
ESP_RETURN_ON_ERROR(gdma_register_rx_event_callbacks(rx_channel->base.dma_chan, &cbs, rx_channel), TAG, "register DMA callbacks failed");
size_t buffer_alignment = MAX(rx_channel->dma_int_mem_alignment, rx_channel->dma_ext_mem_alignment);
// get the alignment requirement from DMA
gdma_channel_alignment_info_t align_info;
gdma_get_channel_alignment_constraints(rx_channel->base.dma_chan, &align_info);
size_t buffer_alignment = MAX(align_info.int_mem_alignment, align_info.ext_enc_mem_alignment);
rx_channel->num_dma_nodes = esp_dma_calculate_node_count(config->mem_block_symbols * sizeof(rmt_symbol_word_t),
buffer_alignment, DMA_DESCRIPTOR_BUFFER_MAX_SIZE);
rx_channel->num_dma_nodes = MAX(2, rx_channel->num_dma_nodes); // at least 2 DMA nodes for ping-pong
@@ -358,14 +359,10 @@ esp_err_t rmt_receive(rmt_channel_handle_t channel, void *buffer, size_t buffer_
size_t mem_alignment = sizeof(rmt_symbol_word_t);
#if SOC_RMT_SUPPORT_DMA
uint32_t int_mem_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA);
uint32_t ext_mem_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA);
if (channel->dma_chan) {
if (esp_ptr_external_ram(buffer)) {
mem_alignment = MAX(MAX(mem_alignment, rx_chan->dma_ext_mem_alignment), ext_mem_cache_line_size);
} else {
mem_alignment = MAX(MAX(mem_alignment, rx_chan->dma_int_mem_alignment), int_mem_cache_line_size);
}
size_t dma_alignment = gdma_get_buffer_alignment_constraint(channel->dma_chan, buffer);
size_t cache_line_size = esp_cache_get_line_size_by_addr(buffer);
mem_alignment = MAX(MAX(mem_alignment, dma_alignment), cache_line_size);
}
#endif // SOC_RMT_SUPPORT_DMA

View File

@@ -53,9 +53,10 @@ static esp_err_t rmt_tx_init_dma_link(rmt_tx_channel_t *tx_channel, const rmt_tx
// register the DMA callbacks may fail if the interrupt service can not be installed successfully
ESP_RETURN_ON_ERROR(gdma_register_tx_event_callbacks(tx_channel->base.dma_chan, &cbs, tx_channel), TAG, "register DMA callbacks failed");
size_t int_alignment = 0;
gdma_channel_alignment_info_t align_info;
// get the alignment requirement from DMA
gdma_get_alignment_constraints(tx_channel->base.dma_chan, &int_alignment, NULL);
gdma_get_channel_alignment_constraints(tx_channel->base.dma_chan, &align_info);
size_t int_alignment = align_info.int_mem_alignment;
// apply RMT hardware alignment requirement
int_alignment = MAX(int_alignment, sizeof(rmt_symbol_word_t));
// the memory returned by `heap_caps_aligned_calloc` also meets the cache alignment requirement (both address and size)

View File

@@ -320,8 +320,14 @@ static esp_err_t alloc_dma_chan(spi_host_device_t host_id, spi_dma_chan_t dma_ch
ESP_RETURN_ON_ERROR(gdma_config_transfer(dma_ctx->rx_dma_chan, &trans_cfg), SPI_TAG, "config gdma rx transfer failed");
// Get DMA alignment constraints
gdma_get_alignment_constraints(dma_ctx->tx_dma_chan, &dma_ctx->dma_align_tx_int, &dma_ctx->dma_align_tx_ext);
gdma_get_alignment_constraints(dma_ctx->rx_dma_chan, &dma_ctx->dma_align_rx_int, &dma_ctx->dma_align_rx_ext);
gdma_channel_alignment_info_t tx_align_info;
gdma_get_channel_alignment_constraints(dma_ctx->tx_dma_chan, &tx_align_info);
dma_ctx->dma_align_tx_int = tx_align_info.int_mem_alignment;
dma_ctx->dma_align_tx_ext = tx_align_info.ext_enc_mem_alignment;
gdma_channel_alignment_info_t rx_align_info;
gdma_get_channel_alignment_constraints(dma_ctx->rx_dma_chan, &rx_align_info);
dma_ctx->dma_align_rx_int = rx_align_info.int_mem_alignment;
dma_ctx->dma_align_rx_ext = rx_align_info.ext_enc_mem_alignment;
}
return ret;
}

View File

@@ -6,6 +6,7 @@
#include <string.h>
#include <stdint.h>
#include <sys/param.h>
#include "esp_intr_alloc.h"
#if CONFIG_UHCI_ENABLE_DEBUG_LOG
// The local log level must be defined before including esp_log.h
@@ -26,13 +27,10 @@
#include "hal/uhci_hal.h"
#include "hal/uhci_ll.h"
#include "hal/dma_types.h"
#include "hal/cache_hal.h"
#include "hal/cache_ll.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/gdma.h"
#include "esp_private/esp_dma_utils.h"
#include "esp_private/gdma_link.h"
#include "esp_private/esp_cache_private.h"
#include "esp_private/esp_psram_mspi.h"
#include "uhci_private.h"
#include "esp_memory_utils.h"
@@ -213,8 +211,9 @@ static esp_err_t uhci_gdma_initialize(uhci_controller_handle_t uhci_ctrl, const
gdma_apply_strategy(uhci_ctrl->tx_dir.dma_chan, &strategy_config);
// create DMA link list
gdma_get_alignment_constraints(uhci_ctrl->tx_dir.dma_chan, &uhci_ctrl->tx_dir.int_mem_align, &uhci_ctrl->tx_dir.ext_mem_align);
size_t buffer_alignment = UHCI_MAX(uhci_ctrl->tx_dir.int_mem_align, uhci_ctrl->tx_dir.ext_mem_align);
gdma_channel_alignment_info_t tx_align_info;
gdma_get_channel_alignment_constraints(uhci_ctrl->tx_dir.dma_chan, &tx_align_info);
size_t buffer_alignment = MAX(tx_align_info.int_mem_alignment, tx_align_info.ext_enc_mem_alignment);
// Given that the combined size of all buffers does not exceed `max_transmit_size` and
// the number of buffers does not exceed `max_transmit_buffer_count`, a single transfer
// requires at most `esp_dma_calculate_node_count(max_transmit_size) + max_transmit_buffer_count - 1` DMA descriptors.
@@ -237,8 +236,9 @@ static esp_err_t uhci_gdma_initialize(uhci_controller_handle_t uhci_ctrl, const
gdma_connect(uhci_ctrl->rx_dir.dma_chan, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UHCI, 0));
ESP_RETURN_ON_ERROR(gdma_config_transfer(uhci_ctrl->rx_dir.dma_chan, &transfer_cfg), TAG, "Config DMA rx channel transfer failed");
gdma_get_alignment_constraints(uhci_ctrl->rx_dir.dma_chan, &uhci_ctrl->rx_dir.int_mem_align, &uhci_ctrl->rx_dir.ext_mem_align);
buffer_alignment = UHCI_MAX(uhci_ctrl->rx_dir.int_mem_align, uhci_ctrl->rx_dir.ext_mem_align);
gdma_channel_alignment_info_t rx_align_info;
gdma_get_channel_alignment_constraints(uhci_ctrl->rx_dir.dma_chan, &rx_align_info);
buffer_alignment = MAX(rx_align_info.int_mem_alignment, rx_align_info.ext_enc_mem_alignment);
uhci_ctrl->rx_dir.rx_num_dma_nodes = esp_dma_calculate_node_count(config->max_receive_internal_mem, buffer_alignment, DMA_DESCRIPTOR_BUFFER_MAX_SIZE);
dma_link_config.num_items = uhci_ctrl->rx_dir.rx_num_dma_nodes;
ESP_RETURN_ON_ERROR(gdma_new_link_list(&dma_link_config, &uhci_ctrl->rx_dir.dma_link), TAG, "DMA rx link list alloc failed");
@@ -287,7 +287,7 @@ static void uhci_do_transmit(uhci_controller_handle_t uhci_ctrl, uhci_transactio
for (size_t i = 0; i < buf_count; i++) {
bool is_last = (i == buf_count - 1);
size_t buffer_alignment = esp_ptr_internal(trans->buf_info[i].write_buffer) ? uhci_ctrl->tx_dir.int_mem_align : uhci_ctrl->tx_dir.ext_mem_align;
size_t buffer_alignment = gdma_get_buffer_alignment_constraint(uhci_ctrl->tx_dir.dma_chan, trans->buf_info[i].write_buffer);
mount_configs[i] = (gdma_buffer_mount_config_t) {
.buffer = (void *)trans->buf_info[i].write_buffer,
.buffer_alignment = buffer_alignment,
@@ -324,9 +324,11 @@ static esp_err_t uhci_receive_internal(uhci_controller_handle_t uhci_ctrl, uint8
esp_err_t ret = ESP_OK;
const uint32_t mem_cache_line_size = esp_ptr_external_ram(read_buffer) ? uhci_ctrl->ext_mem_cache_line_size : uhci_ctrl->int_mem_cache_line_size;
// Must take cache line into consideration for C2M operation.
const uint32_t max_alignment_needed = UHCI_MAX(UHCI_MAX(uhci_ctrl->rx_dir.int_mem_align, uhci_ctrl->rx_dir.ext_mem_align), mem_cache_line_size);
const uint32_t mem_cache_line_size = esp_cache_get_line_size_by_addr(read_buffer);
size_t buffer_alignment = gdma_get_buffer_alignment_constraint(uhci_ctrl->rx_dir.dma_chan, read_buffer);
const uint32_t max_alignment_needed = MAX(buffer_alignment, mem_cache_line_size);
uhci_ctrl->rx_dir.cache_line = mem_cache_line_size;
// Align the read_buffer pointer to mem_cache_line_size
@@ -364,7 +366,7 @@ static esp_err_t uhci_receive_internal(uhci_controller_handle_t uhci_ctrl, uint8
ESP_GOTO_ON_FALSE_ISR(uhci_ctrl->rx_dir.buffer_size_per_desc_node[i] != 0 && uhci_ctrl->rx_dir.buffer_size_per_desc_node[i] <= DMA_DESCRIPTOR_BUFFER_MAX_SIZE,
ESP_ERR_INVALID_ARG, err, TAG, "buffer_size is too small or too large");
size_t buffer_alignment = esp_ptr_internal(read_buffer) ? uhci_ctrl->rx_dir.int_mem_align : uhci_ctrl->rx_dir.ext_mem_align;
buffer_alignment = gdma_get_buffer_alignment_constraint(uhci_ctrl->rx_dir.dma_chan, read_buffer);
mount_configs[i] = (gdma_buffer_mount_config_t) {
.buffer = read_buffer,
.buffer_alignment = buffer_alignment,
@@ -382,8 +384,7 @@ static esp_err_t uhci_receive_internal(uhci_controller_handle_t uhci_ctrl, uint8
// Invalidate cache before DMA starts to ensure no dirty cache lines.
// All DMA nodes (mount_configs) share the same contiguous user buffer, so checking mount_configs[0].buffer is sufficient.
bool need_cache_sync = esp_ptr_internal(mount_configs[0].buffer) ? (uhci_ctrl->int_mem_cache_line_size > 0) : (uhci_ctrl->ext_mem_cache_line_size > 0);
if (need_cache_sync) {
if (esp_cache_get_line_size_by_addr(mount_configs[0].buffer) > 0) {
ESP_GOTO_ON_ERROR_ISR(esp_cache_msync(mount_configs[0].buffer, usable_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C), err, TAG, "cache sync failed");
}
}
@@ -465,20 +466,7 @@ esp_err_t uhci_multi_buffer_transmit(uhci_controller_handle_t uhci_ctrl, const u
total_size += write_size;
size_t alignment = 0;
size_t cache_line_size = 0;
if (esp_ptr_external_ram(write_buffer)) {
alignment = uhci_ctrl->tx_dir.ext_mem_align;
cache_line_size = uhci_ctrl->ext_mem_cache_line_size;
} else {
alignment = uhci_ctrl->tx_dir.int_mem_align;
cache_line_size = uhci_ctrl->int_mem_cache_line_size;
}
ESP_RETURN_ON_FALSE(((((uintptr_t)write_buffer) & (alignment - 1)) == 0) && (((write_size) & (alignment - 1)) == 0), ESP_ERR_INVALID_ARG,
TAG, "buffer segment %zu address or size are not %zu bytes aligned", i, alignment);
if (cache_line_size > 0) {
if (esp_cache_get_line_size_by_addr(write_buffer) > 0) {
// Write back to cache to synchronize the cache before DMA start
ESP_RETURN_ON_ERROR(esp_cache_msync((void *)write_buffer, write_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED), TAG, "cache sync failed");
}
@@ -673,9 +661,6 @@ esp_err_t uhci_new_controller(const uhci_controller_config_t *config, uhci_contr
uhci_ll_rx_set_eof_mode(uhci_ctrl->hal.dev, UHCI_RX_BREAK_CHR_EOF);
}
esp_cache_get_alignment(MALLOC_CAP_SPIRAM, &uhci_ctrl->ext_mem_cache_line_size);
esp_cache_get_alignment(MALLOC_CAP_INTERNAL, &uhci_ctrl->int_mem_cache_line_size);
ESP_GOTO_ON_ERROR(uhci_gdma_initialize(uhci_ctrl, config), err, TAG, "uhci gdma initialize failed");
// rx_num_dma_nodes is only known after uhci_gdma_initialize() queried the DMA alignment, so the

View File

@@ -23,7 +23,6 @@ extern "C" {
typedef struct uhci_controller_t uhci_controller_t;
#define UHCI_ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
#define UHCI_MAX(a, b) (((a)>(b))?(a):(b))
#define UHCI_PM_LOCK_NAME_LEN_MAX 16
@@ -70,8 +69,6 @@ typedef struct {
uhci_transaction_desc_t *cur_trans; // pointer to current transaction
QueueHandle_t trans_queues[UHCI_TRANS_QUEUE_MAX]; // transaction queue
_Atomic uhci_tx_fsm_t tx_fsm; // channel life cycle specific FSM
size_t int_mem_align; // Alignment for internal memory
size_t ext_mem_align; // Alignment for external memory
atomic_int num_trans_inflight; // Indicates the number of transactions that are undergoing but not recycled to ready_queue
size_t max_transmit_size; // per-transaction max total size in bytes, from config->max_transmit_size; the DMA node pool is sized for this
size_t max_buf_count; // per-transaction max buffer segment count, from config->max_transmit_buffer_count (at least 1)
@@ -87,8 +84,6 @@ typedef struct {
uint8_t **buffer_pointers; // Pointer for saving buffer pointer
_Atomic uhci_rx_fsm_t rx_fsm; // channel life cycle specific FSM
size_t cache_line; // cache line size need to be aligned up.
size_t int_mem_align; // Alignment for internal memory
size_t ext_mem_align; // Alignment for external memory
size_t rx_num_dma_nodes; // rx dma number nodes
gdma_buffer_mount_config_t *mount_configs; // scratch array (capacity rx_num_dma_nodes) reused by every receive to mount buffer segments; avoids a VLA in ISR context
bool continuous; // continuous mode: keep DMA running across EOFs instead of stopping
@@ -100,8 +95,6 @@ struct uhci_controller_t {
uhci_tx_dir tx_dir; // tx direction structure
uhci_rx_dir rx_dir; // rx direction structure
void *user_data; // user data
size_t int_mem_cache_line_size; // internal memory cache line size
size_t ext_mem_cache_line_size; // external memory cache line size
#if CONFIG_PM_ENABLE
esp_pm_lock_handle_t pm_lock; // power management lock
char pm_lock_name[UHCI_PM_LOCK_NAME_LEN_MAX]; // pm lock name

View File

@@ -24,7 +24,8 @@ set(priv_requires efuse # only esp_hw_support/adc_share_hw_ctrl.
esp_hal_ana_conv # sleep process requires backup/restore some ADC, TSENS registers
)
set(srcs "cpu.c" "port/${IDF_TARGET}/esp_cpu_intr.c" "esp_memory_utils.c" "port/${IDF_TARGET}/cpu_region_protect.c")
set(srcs "cpu.c" "port/${IDF_TARGET}/esp_cpu_intr.c" "esp_memory_utils.c" "port/${IDF_TARGET}/cpu_region_protect.c"
"mspi/esp_mspi_align/esp_mspi_align.c")
if(NOT non_os_build)
list(APPEND srcs "esp_clk.c"
"clk_ctrl_os.c"
@@ -181,6 +182,7 @@ set(public_include_dirs "include" "include/soc"
"ldo/include" "debug_probe/include" "etm/include"
"mspi/mspi_timing_tuning/include" "mspi/mspi_timing_tuning/tuning_scheme_impl/include"
"mspi/mspi_intr/include"
"mspi/esp_mspi_align/include"
"power_supply/include" "modem/include")
if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/include/soc/${target}")

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2019-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -14,6 +14,7 @@
#include "hal/gdma_ll.h"
#include "hal/efuse_hal.h"
#include "esp_efuse.h"
#include "esp_private/esp_mspi_align.h"
#endif
#if CONFIG_HEAP_PLACE_FUNCTION_INTO_FLASH
@@ -83,8 +84,10 @@ HEAP_IRAM_ATTR void esp_heap_adjust_alignment_to_hw(size_t *p_alignment, size_t
#endif
#if SOC_HAS(GDMA) && (SOC_PSRAM_DMA_CAPABLE || SOC_DMA_CAN_ACCESS_FLASH)
if ((caps & MALLOC_CAP_DMA) && esp_efuse_is_flash_encryption_enabled()) {
alignment = (alignment > SOC_MEMSPI_ENCRYPTION_ALIGNMENT) ? alignment : SOC_MEMSPI_ENCRYPTION_ALIGNMENT;
// CPU access to PSRAM always via cache, so alignment to MSPI alignment is needed when DMA is enabled and PSRAM is enabled.
if ((caps & MALLOC_CAP_DMA) && (caps & MALLOC_CAP_SPIRAM)) {
size_t mspi_align = esp_mspi_get_alignment(NULL);
alignment = (alignment > mspi_align) ? alignment : mspi_align;
}
#endif

View File

@@ -0,0 +1,59 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <sys/param.h>
#include "sdkconfig.h"
#include "esp_efuse.h"
#include "esp_memory_utils.h"
#include "esp_private/esp_mspi_align.h"
#if CONFIG_SPIRAM
#include "esp_psram.h"
#endif /* CONFIG_SPIRAM */
#include "soc/soc_caps.h"
#define MSPI_FLASH_ENC_ALIGNMENT SOC_MEMSPI_ENCRYPTION_ALIGNMENT
#define MSPI_PSRAM_ECC_ALIGNMENT SOC_MEMSPI_ENCRYPTION_ALIGNMENT
size_t esp_mspi_get_alignment(const void *ptr)
{
size_t alignment = 1;
bool generic_query = ptr == NULL;
bool __attribute__((unused)) is_psram = esp_ptr_external_ram(ptr);
bool is_drom = esp_ptr_in_drom(ptr);
bool is_psram_enc = false;
#if CONFIG_SPIRAM
is_psram_enc = is_psram && !esp_psram_ptr_is_no_enc(ptr);
#endif /* CONFIG_SPIRAM */
if ((generic_query || is_drom || is_psram_enc) && esp_efuse_is_flash_encryption_enabled()) {
alignment = MAX(alignment, MSPI_FLASH_ENC_ALIGNMENT);
}
#if CONFIG_SPIRAM_ECC_ENABLE
if (generic_query || is_psram) {
alignment = MAX(alignment, MSPI_PSRAM_ECC_ALIGNMENT);
}
#endif
return alignment;
}
bool esp_mspi_buffer_alignment_satisfied(const void *ptr, size_t size)
{
// Zero-length is not a valid MSPI transfer, so it never satisfies the check.
if (ptr == NULL || size == 0) {
return false;
}
size_t alignment = esp_mspi_get_alignment(ptr);
if (alignment <= 1) {
return true;
}
uintptr_t addr = (uintptr_t)ptr;
return ((addr & (alignment - 1)) == 0) && ((size & (alignment - 1)) == 0);
}

View File

@@ -0,0 +1,46 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Get MSPI alignment requirement for an address
*
* The address is used so future targets can apply different alignment rules to
* different MSPI-backed regions. Pass NULL when only the generic external-memory
* requirement is needed and no concrete address is available yet.
*
* @param ptr Buffer pointer in the region to be accessed, or NULL for generic query
* @return Required alignment in bytes, or 1 when no extra MSPI alignment is needed
*/
size_t esp_mspi_get_alignment(const void *ptr);
/**
* @brief Check whether a buffer satisfies MSPI strict alignment requirements
*
* Returns false when @p ptr is NULL or @p size is 0 (not a valid transfer).
* When strict alignment is not required, returns true for any non-empty buffer.
* When required, both @p ptr and @p size must be aligned to the rule returned by
* @ref esp_mspi_get_alignment for that address.
*
* @param ptr Buffer pointer
* @param size Transfer size in bytes
* @return true if alignment requirements are satisfied
*/
bool esp_mspi_buffer_alignment_satisfied(const void *ptr, size_t size);
#ifdef __cplusplus
}
#endif

View File

@@ -1,3 +1,10 @@
[mapping:esp_mspi_align]
archive: libesp_hw_support.a
entries:
if APP_BUILD_TYPE_PURE_RAM_APP = n:
esp_mspi_align:esp_mspi_get_alignment (noflash)
esp_mspi_align:esp_mspi_buffer_alignment_satisfied (noflash)
[mapping:mspi_timing_tuning_driver]
archive: libesp_hw_support.a
entries:

View File

@@ -13,6 +13,7 @@
#include "esp_memory_utils.h"
#include "esp_private/async_memcpy_dma2d.h"
#include "esp_private/dw_gdma.h"
#include "esp_private/dma2d.h"
#include "hal/color_hal.h"
typedef struct esp_lcd_dpi_panel_t esp_lcd_dpi_panel_t;
@@ -223,17 +224,23 @@ esp_err_t esp_lcd_new_panel_dpi(esp_lcd_dsi_bus_handle_t bus, const esp_lcd_dpi_
dpi_panel->bus = bus;
dpi_panel->num_fbs = num_fbs;
// Although the DW-GDMA can handle unaligned data, frame buffer still may be the dst of DMA2D operation.
// Allocate FB with DMA2D alloc alignment so address is usable as a DMA2D destination when enabled later.
// Line-size / window misalignment will fail later in async_color_convert's DMA2D transaction check.
size_t dma2d_align = dma2d_get_alloc_alignment();
// allocate frame buffer from PSRAM
size_t fb_size = panel_config->video_timing.h_size * panel_config->video_timing.v_size * bits_per_pixel / 8;
for (int i = 0; i < num_fbs; i++) {
uint8_t *frame_buffer = heap_caps_calloc(1, fb_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT | MALLOC_CAP_DMA);
uint8_t *frame_buffer = heap_caps_aligned_calloc(dma2d_align, 1, fb_size,
MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT | MALLOC_CAP_DMA);
ESP_GOTO_ON_FALSE(frame_buffer, ESP_ERR_NO_MEM, err, TAG, "no memory for frame buffer");
dpi_panel->fbs[i] = frame_buffer;
ESP_LOGD(TAG, "fb[%d] @%p", i, frame_buffer);
// preset the frame buffer with black color
// the frame buffer address alignment is ensured by `heap_caps_calloc`
// the frame buffer address alignment is ensured by `heap_caps_aligned_calloc`
// while the value of the fb_size may not be aligned to the cache line size
// but that's not a problem because the `heap_caps_calloc` internally allocated a buffer whose size is aligned up to the cache line size
// but that's not a problem because the `heap_caps_aligned_calloc` internally allocated a buffer whose size is aligned up to the cache line size
ESP_GOTO_ON_ERROR(esp_cache_msync(frame_buffer, fb_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED),
err, TAG, "cache write back failed");
}

View File

@@ -540,11 +540,12 @@ static esp_err_t panel_io_i80_tx_param(esp_lcd_panel_io_t *io, int lcd_cmd, cons
trans_desc->data = (param && param_len) ? bus->format_buffer : NULL;
trans_desc->data_length = trans_desc->data ? param_len : 4;
trans_desc->trans_done_cb = NULL; // no callback for parameter transaction
size_t buffer_alignment = (trans_desc->data == NULL || esp_ptr_internal(trans_desc->data)) ? bus->int_mem_align : bus->ext_mem_align;
static uint32_t fake_trigger = 0;
void *mount_buffer = trans_desc->data ? (void *)trans_desc->data : &fake_trigger;
size_t buffer_alignment = gdma_get_buffer_alignment_constraint(bus->dma_chan, mount_buffer);
// mount data to DMA links
gdma_buffer_mount_config_t mount_config = {
.buffer = trans_desc->data ? (void *)trans_desc->data : (&fake_trigger),
.buffer = mount_buffer,
.buffer_alignment = buffer_alignment,
.length = trans_desc->data_length,
.flags = {
@@ -578,15 +579,6 @@ static esp_err_t panel_io_i80_tx_color(esp_lcd_panel_io_t *io, int lcd_cmd, cons
lcd_i80_trans_descriptor_t *trans_desc = NULL;
ESP_RETURN_ON_FALSE(color_size <= bus->max_transfer_bytes, ESP_ERR_INVALID_ARG, TAG,
"color bytes too long, enlarge max_transfer_bytes");
if (esp_ptr_external_ram(color)) {
// check alignment
ESP_RETURN_ON_FALSE(((uint32_t)color & (bus->ext_mem_align - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "color address not aligned");
ESP_RETURN_ON_FALSE((color_size & (bus->ext_mem_align - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "color size not aligned");
} else {
// check alignment
ESP_RETURN_ON_FALSE(((uint32_t)color & (bus->int_mem_align - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "color address not aligned");
ESP_RETURN_ON_FALSE((color_size & (bus->int_mem_align - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "color size not aligned");
}
if (esp_cache_get_line_size_by_addr(color) > 0) {
// flush data from cache to the physical memory
esp_cache_msync((void *)color, color_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED);
@@ -702,7 +694,10 @@ static esp_err_t lcd_i80_init_dma_link(esp_lcd_i80_bus_handle_t bus, const esp_l
.access_ext_mem = true, // the LCD can carry pixel buffer from the external memory
};
ESP_RETURN_ON_ERROR(gdma_config_transfer(bus->dma_chan, &trans_cfg), TAG, "config DMA transfer failed");
gdma_get_alignment_constraints(bus->dma_chan, &bus->int_mem_align, &bus->ext_mem_align);
gdma_channel_alignment_info_t align_info;
gdma_get_channel_alignment_constraints(bus->dma_chan, &align_info);
bus->int_mem_align = align_info.int_mem_alignment;
bus->ext_mem_align = align_info.ext_enc_mem_alignment;
size_t buffer_alignment = MAX(bus->int_mem_align, bus->ext_mem_align);
size_t num_dma_nodes = esp_dma_calculate_node_count(bus->max_transfer_bytes, buffer_alignment, LCD_DMA_DESCRIPTOR_BUFFER_MAX_SIZE);
@@ -899,8 +894,10 @@ IRAM_ATTR static void i80_lcd_default_isr_handler(void *args)
bus->cur_trans = trans_desc;
bus->cur_device = next_device;
// mount data to DMA links
size_t buffer_alignment = gdma_get_buffer_alignment_constraint(bus->dma_chan, trans_desc->data);
gdma_buffer_mount_config_t mount_config = {
.buffer = (void *)trans_desc->data,
.buffer_alignment = buffer_alignment,
.length = trans_desc->data_length,
.flags = {
.mark_eof = true,

View File

@@ -188,10 +188,6 @@ static esp_err_t lcd_rgb_panel_alloc_frame_buffers(esp_rgb_panel_t *rgb_panel, c
{
bool fb_in_psram = rgb_panel->flags.fb_in_psram;
// read the cache line size of internal and external memory, we use this information to check if the allocated memory is behind the cache
uint32_t int_mem_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA);
uint32_t ext_mem_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA);
// alloc frame buffer
uint8_t user_fb_count = 0;
for (int i = 0; i < rgb_panel->num_fbs; i++) {
@@ -202,16 +198,6 @@ static esp_err_t lcd_rgb_panel_alloc_frame_buffers(esp_rgb_panel_t *rgb_panel, c
if (!esp_ptr_dma_capable(panel_config->user_fbs[i]) && !esp_ptr_dma_ext_capable(panel_config->user_fbs[i])) {
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_ARG, TAG, "frame buffer %d is not DMA accessible", i);
}
// Check if user frame buffer is in PSRAM or internal memory
if (esp_ptr_external_ram(panel_config->user_fbs[i])) {
ESP_RETURN_ON_FALSE(((uintptr_t)panel_config->user_fbs[i] & (rgb_panel->ext_mem_align - 1)) == 0,
ESP_ERR_INVALID_ARG, TAG, "frame buffer %d is not aligned to "PRIu32"", i, rgb_panel->ext_mem_align);
rgb_panel->flags.fb_behind_cache = ext_mem_cache_line_size > 0;
} else {
ESP_RETURN_ON_FALSE(((uintptr_t)panel_config->user_fbs[i] & (rgb_panel->int_mem_align - 1)) == 0,
ESP_ERR_INVALID_ARG, TAG, "frame buffer %d is not aligned to "PRIu32"", i, rgb_panel->int_mem_align);
rgb_panel->flags.fb_behind_cache = int_mem_cache_line_size > 0;
}
rgb_panel->fbs[i] = (uint8_t *)panel_config->user_fbs[i];
user_fb_count++;
} else {
@@ -221,15 +207,14 @@ static esp_err_t lcd_rgb_panel_alloc_frame_buffers(esp_rgb_panel_t *rgb_panel, c
rgb_panel->fbs[i] = heap_caps_aligned_calloc(rgb_panel->ext_mem_align, 1, rgb_panel->fb_size,
MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
ESP_RETURN_ON_FALSE(rgb_panel->fbs[i], ESP_ERR_NO_MEM, TAG, "no mem for frame buffer");
rgb_panel->flags.fb_behind_cache = ext_mem_cache_line_size > 0;
} else {
rgb_panel->fbs[i] = heap_caps_aligned_calloc(rgb_panel->int_mem_align, 1, rgb_panel->fb_size,
MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
ESP_RETURN_ON_FALSE(rgb_panel->fbs[i], ESP_ERR_NO_MEM, TAG, "no mem for frame buffer");
rgb_panel->flags.fb_behind_cache = int_mem_cache_line_size > 0;
}
}
rgb_panel->flags.fb_behind_cache = esp_cache_get_line_size_by_addr(rgb_panel->fbs[i]) > 0;
// flush data from cache to the physical memory
if (rgb_panel->flags.fb_behind_cache) {
ESP_LOGD(TAG, "frame buffer %d at %p is behind the cache", i, rgb_panel->fbs[i]);
@@ -248,7 +233,7 @@ static esp_err_t lcd_rgb_panel_alloc_frame_buffers(esp_rgb_panel_t *rgb_panel, c
rgb_panel->bounce_buffer[i] = heap_caps_aligned_calloc(rgb_panel->int_mem_align, 1, rgb_panel->bb_size,
MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
ESP_RETURN_ON_FALSE(rgb_panel->bounce_buffer[i], ESP_ERR_NO_MEM, TAG, "no mem for bounce buffer");
if (int_mem_cache_line_size > 0) {
if (esp_cache_get_line_size_by_addr(rgb_panel->bounce_buffer[i]) > 0) {
// flush data from cache to the physical memory
esp_cache_msync(rgb_panel->bounce_buffer[i], rgb_panel->bb_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED);
rgb_panel->flags.bb_behind_cache = true;
@@ -1273,7 +1258,10 @@ static esp_err_t lcd_rgb_create_dma_channel(esp_rgb_panel_t *rgb_panel)
};
ESP_RETURN_ON_ERROR(gdma_config_transfer(rgb_panel->dma_chan, &trans_cfg), TAG, "config DMA transfer failed");
// get the memory alignment required by the DMA
gdma_get_alignment_constraints(rgb_panel->dma_chan, &rgb_panel->int_mem_align, &rgb_panel->ext_mem_align);
gdma_channel_alignment_info_t align_info;
gdma_get_channel_alignment_constraints(rgb_panel->dma_chan, &align_info);
rgb_panel->int_mem_align = align_info.int_mem_alignment;
rgb_panel->ext_mem_align = align_info.ext_enc_mem_alignment;
// register DMA event callbacks
gdma_tx_event_callbacks_t cbs = {
@@ -1387,7 +1375,9 @@ static esp_err_t lcd_rgb_panel_init_trans_link(esp_rgb_panel_t *rgb_panel)
.buffer = rgb_panel->fbs[0] + restart_skip_bytes,
.buffer_alignment = buffer_alignment,
.length = MIN(LCD_DMA_DESCRIPTOR_BUFFER_MAX_SIZE, rgb_panel->fb_size) - restart_skip_bytes,
.flags.bypass_buffer_align_check = true, // the restart buffer may doesn't match the buffer alignment but it doesn't really matter in this case
// the restart buffer may doesn't match the buffer alignment but it doesn't really matter in this case
.flags.bypass_buffer_addr_align_check = true,
.flags.bypass_buffer_size_align_check = true,
};
ESP_RETURN_ON_ERROR(gdma_link_mount_buffers(rgb_panel->dma_restart_link, 0, &restart_buffer_mount_cfg, 1, NULL),
TAG, "mount DMA restart buffer failed");

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -47,15 +47,13 @@ size_t esp_psram_get_size(void);
*
* When @c CONFIG_SPIRAM_ENC_EXEMPT is enabled, esp_psram reserves a region of PSRAM
* that is mapped without encryption and exposed through the @c MALLOC_CAP_SPIRAM_NO_ENC
* heap capability. This function lets drivers verify whether a buffer returned by the
* heap allocator actually lives in that unencrypted region — useful for example after
* a @c heap_caps_malloc_prefer() call that may have fallen back to encrypted PSRAM.
* heap capability.
*
* @param[in] p The pointer to check
*
* @return
* - true: the pointer is within the unencrypted PSRAM carve-out
* - false: the pointer is not in the carve-out, PSRAM is not initialized,
* - true: The pointer is within the unencrypted PSRAM carve-out
* - false: The pointer is not in the carve-out, PSRAM is not initialized,
* or @c CONFIG_SPIRAM_ENC_EXEMPT is disabled
*/
bool esp_psram_ptr_is_no_enc(const void *p);

View File

@@ -369,6 +369,8 @@ if(CONFIG_SOC_SHA_GDMA OR CONFIG_SOC_AES_GDMA)
endif()
if((SHA_PERIPHERAL_TYPE STREQUAL "core" AND CONFIG_SOC_SHA_SUPPORT_DMA) OR AES_PERIPHERAL_TYPE STREQUAL "dma")
target_link_libraries(tfpsacrypto PRIVATE idf::esp_hw_support)
target_link_libraries(builtin PRIVATE idf::esp_hw_support)
target_link_libraries(tfpsacrypto PRIVATE idf::esp_mm)
target_link_libraries(builtin PRIVATE idf::esp_mm)
if(CONFIG_SOC_SHA_GDMA OR CONFIG_SOC_AES_GDMA)

View File

@@ -12,6 +12,7 @@
#include "esp_intr_alloc.h"
#include "esp_log.h"
#include "esp_memory_utils.h"
#include "esp_private/esp_mspi_align.h"
#include "esp_private/periph_ctrl.h"
#include "soc/soc_caps.h"
#include "sdkconfig.h"
@@ -43,10 +44,6 @@
#include "aes/esp_aes_gcm.h"
#endif
#ifdef SOC_GDMA_EXT_MEM_ENC_ALIGNMENT
#include "hal/efuse_hal.h"
#endif /* SOC_GDMA_EXT_MEM_ENC_ALIGNMENT */
/* Max size of each chunk to process when output buffer is in unaligned external ram
must be a multiple of block size
*/
@@ -249,22 +246,33 @@ static int esp_aes_process_dma_ext_ram(esp_aes_context *ctx, const unsigned char
size_t input_alignment = 1;
size_t output_alignment = 1;
/* When AES-DMA operations are carried out using external memory with external memory encryption enabled,
we need to make sure that the addresses and the sizes of the buffers on which the DMA operates are 16 byte-aligned.
This is only applicable for ESP32-P4, as other targets use internal memory for DMA operations. */
#ifdef SOC_MEMSPI_ENCRYPTION_ALIGNMENT
/* When AES-DMA operations use external memory under MSPI strict alignment (FE or PSRAM ECC),
bounce buffers must be aligned to the MSPI requirement. On ESP32-P4, cache-line alignment
may also apply because DMA accesses cached external memory directly. */
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
if (efuse_hal_flash_encryption_enabled()) {
if (esp_ptr_external_ram(input) || esp_ptr_external_ram(output) || esp_ptr_in_drom(input) || esp_ptr_in_drom(output)) {
if (esp_ptr_external_ram(input) || esp_ptr_external_ram(output) || esp_ptr_in_drom(input) || esp_ptr_in_drom(output)) {
size_t input_mspi_align = esp_mspi_get_alignment(input);
size_t output_mspi_align = esp_mspi_get_alignment(output);
if (input_mspi_align > 1 || output_mspi_align > 1) {
size_t input_cache_line_size = get_cache_line_size(input);
size_t output_cache_line_size = get_cache_line_size(output);
input_alignment = MAX(input_cache_line_size, SOC_GDMA_EXT_MEM_ENC_ALIGNMENT);
output_alignment = MAX(output_cache_line_size, SOC_GDMA_EXT_MEM_ENC_ALIGNMENT);
input_alignment = MAX(input_cache_line_size, input_mspi_align);
output_alignment = MAX(output_cache_line_size, output_mspi_align);
input_heap_caps = MALLOC_CAP_8BIT | (esp_ptr_external_ram(input) ? MALLOC_CAP_SPIRAM : MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
output_heap_caps = MALLOC_CAP_8BIT | (esp_ptr_external_ram(output) ? MALLOC_CAP_SPIRAM : MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
}
}
#endif /* SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE */
#else
if (realloc_input && (esp_ptr_external_ram(input) || esp_ptr_in_drom(input))) {
input_alignment = esp_mspi_get_alignment(input);
}
if (realloc_output && (esp_ptr_external_ram(output) || esp_ptr_in_drom(output))) {
output_alignment = esp_mspi_get_alignment(output);
}
#endif
#endif /* SOC_MEMSPI_ENCRYPTION_ALIGNMENT */
if (realloc_input) {
input_buf = heap_caps_aligned_alloc(input_alignment, chunk_len, input_heap_caps);
@@ -280,7 +288,8 @@ static int esp_aes_process_dma_ext_ram(esp_aes_context *ctx, const unsigned char
if (output_buf == NULL) {
mbedtls_platform_zeroize(output, len);
ESP_LOGE(TAG, "Failed to allocate memory");
return -1;
ret = -1;
goto cleanup;
}
} else {
output_buf = output;
@@ -358,9 +367,9 @@ static inline void dma_desc_append(crypto_dma_desc_t **head, crypto_dma_desc_t *
#define ALIGN_DOWN(num, align) ((num) & ~((align) - 1))
#define AES_DMA_ALLOC_CAPS (MALLOC_CAP_DMA | MALLOC_CAP_8BIT)
static inline void *aes_dma_calloc(size_t num, size_t size, uint32_t caps, size_t *actual_size)
static inline void *aes_dma_calloc(size_t alignment, size_t num, size_t size, uint32_t caps, size_t *actual_size)
{
return heap_caps_aligned_calloc(DMA_DESC_MEM_ALIGN_SIZE, num, size, caps);
return heap_caps_aligned_calloc(alignment, num, size, caps);
}
static inline esp_err_t dma_desc_link(crypto_dma_desc_t *dmadesc, size_t crypto_dma_desc_num, size_t buffer_cache_line_size)
@@ -429,6 +438,14 @@ static esp_err_t generate_descriptor_list(const uint8_t *buffer, const size_t le
uint8_t *end_alignment_stream_buffer = NULL;
crypto_dma_desc_t *dma_descriptors = NULL;
size_t buffer_alignment = DMA_DESC_MEM_ALIGN_SIZE;
#ifdef SOC_MEMSPI_ENCRYPTION_ALIGNMENT
size_t mspi_alignment = esp_mspi_get_alignment(buffer);
buffer_alignment = MAX(buffer_alignment, mspi_alignment);
#endif
buffer_alignment = MAX(buffer_alignment, cache_line_size);
uint32_t alignment_buffer_caps = AES_DMA_ALLOC_CAPS |
(esp_ptr_external_ram(buffer) ? MALLOC_CAP_SPIRAM : MALLOC_CAP_INTERNAL);
if (len == 0) {
goto ret;
@@ -459,7 +476,8 @@ static esp_err_t generate_descriptor_list(const uint8_t *buffer, const size_t le
dma_descs_needed = (unaligned_start_bytes ? 1 : 0) + dma_desc_get_required_num(aligned_block_bytes, max_desc_size) + (unaligned_end_bytes ? 1 : 0);
/* Allocate memory for DMA descriptors of total size aligned up to a multiple of cache line size */
dma_descriptors = (crypto_dma_desc_t *) aes_dma_calloc(dma_descs_needed, sizeof(crypto_dma_desc_t), MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL, NULL);
dma_descriptors = (crypto_dma_desc_t *) aes_dma_calloc(DMA_DESC_MEM_ALIGN_SIZE, dma_descs_needed,
sizeof(crypto_dma_desc_t), MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL, NULL);
if (dma_descriptors == NULL) {
ESP_LOGE(TAG, "Failed to allocate memory for the array of DMA descriptors");
goto err;
@@ -468,7 +486,8 @@ static esp_err_t generate_descriptor_list(const uint8_t *buffer, const size_t le
size_t populated_dma_descs = 0;
if (unaligned_start_bytes) {
start_alignment_stream_buffer = aes_dma_calloc(alignment_buffer_size, sizeof(uint8_t), AES_DMA_ALLOC_CAPS | (esp_ptr_external_ram(buffer) ? MALLOC_CAP_SPIRAM : MALLOC_CAP_INTERNAL) , NULL);
start_alignment_stream_buffer = aes_dma_calloc(buffer_alignment, alignment_buffer_size,
sizeof(uint8_t), alignment_buffer_caps, NULL);
if (start_alignment_stream_buffer == NULL) {
ESP_LOGE(TAG, "Failed to allocate memory for start alignment buffer");
goto err;
@@ -490,7 +509,8 @@ static esp_err_t generate_descriptor_list(const uint8_t *buffer, const size_t le
}
if (unaligned_end_bytes) {
end_alignment_stream_buffer = aes_dma_calloc(alignment_buffer_size, sizeof(uint8_t), AES_DMA_ALLOC_CAPS | (esp_ptr_external_ram(buffer) ? MALLOC_CAP_SPIRAM : MALLOC_CAP_INTERNAL), NULL);
end_alignment_stream_buffer = aes_dma_calloc(buffer_alignment, alignment_buffer_size,
sizeof(uint8_t), alignment_buffer_caps, NULL);
if (end_alignment_stream_buffer == NULL) {
ESP_LOGE(TAG, "Failed to allocate memory for end alignment buffer");
goto err;
@@ -566,19 +586,17 @@ int esp_aes_process_dma(esp_aes_context *ctx, const unsigned char *input, unsign
return MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH;
}
#ifdef SOC_GDMA_EXT_MEM_ENC_ALIGNMENT
if (efuse_hal_flash_encryption_enabled()) {
if (esp_ptr_external_ram(input) || esp_ptr_external_ram(output) || esp_ptr_in_drom(input) || esp_ptr_in_drom(output)) {
if (((intptr_t)(input) & (SOC_GDMA_EXT_MEM_ENC_ALIGNMENT - 1)) != 0) {
input_needs_realloc = true;
}
#ifdef SOC_MEMSPI_ENCRYPTION_ALIGNMENT
if (esp_ptr_external_ram(input) || esp_ptr_external_ram(output) || esp_ptr_in_drom(input) || esp_ptr_in_drom(output)) {
if (!esp_mspi_buffer_alignment_satisfied(input, block_bytes)) {
input_needs_realloc = true;
}
if (((intptr_t)(output) & (SOC_GDMA_EXT_MEM_ENC_ALIGNMENT - 1)) != 0) {
output_needs_realloc = true;
}
if (!esp_mspi_buffer_alignment_satisfied(output, block_bytes)) {
output_needs_realloc = true;
}
}
#endif /* SOC_GDMA_EXT_MEM_ENC_ALIGNMENT */
#endif /* SOC_MEMSPI_ENCRYPTION_ALIGNMENT */
/* DMA cannot access memory in the iCache range, copy input to internal ram */
if (!s_check_dma_capable(input)) {
@@ -833,14 +851,14 @@ int esp_aes_process_dma_gcm(esp_aes_context *ctx, const unsigned char *input, un
out_desc_tail = &output_desc[output_dma_desc_num - 1];
len_desc = aes_dma_calloc(1, sizeof(crypto_dma_desc_t), AES_DMA_ALLOC_CAPS, NULL);
len_desc = aes_dma_calloc(DMA_DESC_MEM_ALIGN_SIZE, 1, sizeof(crypto_dma_desc_t), AES_DMA_ALLOC_CAPS, NULL);
if (len_desc == NULL) {
mbedtls_platform_zeroize(output, len);
ESP_LOGE(TAG, "Failed to allocate memory for len descriptor");
return -1;
}
uint32_t *len_buf = aes_dma_calloc(4, sizeof(uint32_t), AES_DMA_ALLOC_CAPS, NULL);
uint32_t *len_buf = aes_dma_calloc(DMA_DESC_MEM_ALIGN_SIZE, 4, sizeof(uint32_t), AES_DMA_ALLOC_CAPS, NULL);
if (len_buf == NULL) {
mbedtls_platform_zeroize(output, len);
ESP_LOGE(TAG, "Failed to allocate memory for len buffer");
@@ -1080,20 +1098,18 @@ int esp_aes_process_dma(esp_aes_context *ctx, const unsigned char *input, unsign
if (block_bytes > 0) {
/* Flush cache if input in external ram */
#if (CONFIG_SPIRAM && SOC_PSRAM_DMA_CAPABLE)
#ifdef SOC_GDMA_EXT_MEM_ENC_ALIGNMENT
if (efuse_hal_flash_encryption_enabled()) {
if (esp_ptr_external_ram(input) || esp_ptr_in_drom(input)) {
if (((intptr_t)(input) & (SOC_GDMA_EXT_MEM_ENC_ALIGNMENT - 1)) != 0) {
input_needs_realloc = true;
}
}
if (esp_ptr_external_ram(output) || esp_ptr_in_drom(output)) {
if (((intptr_t)(output) & (SOC_GDMA_EXT_MEM_ENC_ALIGNMENT - 1)) != 0) {
output_needs_realloc = true;
}
#ifdef SOC_MEMSPI_ENCRYPTION_ALIGNMENT
if (esp_ptr_external_ram(input) || esp_ptr_in_drom(input)) {
if (!esp_mspi_buffer_alignment_satisfied(input, block_bytes)) {
input_needs_realloc = true;
}
}
#endif /* SOC_GDMA_EXT_MEM_ENC_ALIGNMENT */
if (esp_ptr_external_ram(output) || esp_ptr_in_drom(output)) {
if (!esp_mspi_buffer_alignment_satisfied(output, block_bytes)) {
output_needs_realloc = true;
}
}
#endif /* SOC_MEMSPI_ENCRYPTION_ALIGNMENT */
if (esp_ptr_external_ram(input)) {
if (esp_cache_msync((void *)input, len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED) != ESP_OK) {

View File

@@ -44,9 +44,9 @@
#include "esp_sha_dma_priv.h"
#include "sdkconfig.h"
#ifdef SOC_GDMA_EXT_MEM_ENC_ALIGNMENT
#include "hal/efuse_hal.h"
#endif /* SOC_GDMA_EXT_MEM_ENC_ALIGNMENT */
#ifdef SOC_MEMSPI_ENCRYPTION_ALIGNMENT
#include "esp_private/esp_mspi_align.h"
#endif /* SOC_MEMSPI_ENCRYPTION_ALIGNMENT */
#if SOC_SHA_CRYPTO_DMA
#include "hal/crypto_dma_ll.h"
@@ -155,7 +155,7 @@ static DRAM_ATTR crypto_dma_desc_t s_dma_descr_buf;
static esp_err_t esp_sha_dma_process(esp_sha_type sha_type, const void *input, uint32_t ilen,
const void *buf, uint32_t buf_len, bool is_first_block);
#ifdef SOC_GDMA_EXT_MEM_ENC_ALIGNMENT
#ifdef SOC_MEMSPI_ENCRYPTION_ALIGNMENT
static esp_err_t esp_sha_dma_process_ext(esp_sha_type sha_type, const void *input, uint32_t ilen,
const void *buf, uint32_t buf_len, bool is_first_block,
bool realloc_input, bool realloc_buf)
@@ -171,7 +171,7 @@ static esp_err_t esp_sha_dma_process_ext(esp_sha_type sha_type, const void *inpu
if (realloc_input) {
heap_caps = MALLOC_CAP_8BIT | (esp_ptr_external_ram(input) ? MALLOC_CAP_SPIRAM : MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
input_copy = heap_caps_aligned_alloc(SOC_GDMA_EXT_MEM_ENC_ALIGNMENT, ilen, heap_caps);
input_copy = heap_caps_aligned_alloc(esp_mspi_get_alignment(input), ilen, heap_caps);
if (input_copy == NULL) {
ESP_LOGE(TAG, "Failed to allocate aligned SPIRAM memory");
return ret;
@@ -184,7 +184,7 @@ static esp_err_t esp_sha_dma_process_ext(esp_sha_type sha_type, const void *inpu
if (realloc_buf) {
heap_caps = MALLOC_CAP_8BIT | (esp_ptr_external_ram(buf) ? MALLOC_CAP_SPIRAM : MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
buf_copy = heap_caps_aligned_alloc(SOC_GDMA_EXT_MEM_ENC_ALIGNMENT, buf_len, heap_caps);
buf_copy = heap_caps_aligned_alloc(esp_mspi_get_alignment(buf), buf_len, heap_caps);
if (buf_copy == NULL) {
ESP_LOGE(TAG, "Failed to allocate aligned internal memory");
if (input_copy) {
@@ -213,7 +213,7 @@ static esp_err_t esp_sha_dma_process_ext(esp_sha_type sha_type, const void *inpu
return ret;
}
#endif /* SOC_GDMA_EXT_MEM_ENC_ALIGNMENT */
#endif /* SOC_MEMSPI_ENCRYPTION_ALIGNMENT */
/* Performs SHA on multiple blocks at a time */
static esp_err_t esp_sha_dma_process(esp_sha_type sha_type, const void *input, uint32_t ilen,
@@ -232,28 +232,27 @@ static esp_err_t esp_sha_dma_process(esp_sha_type sha_type, const void *input, u
memset(&s_dma_descr_input, 0, sizeof(crypto_dma_desc_t));
memset(&s_dma_descr_buf, 0, sizeof(crypto_dma_desc_t));
/* When SHA-DMA operations are carried out using external memory with external memory encryption enabled,
we need to make sure that the addresses and the sizes of the buffers on which the DMA operates are 16 byte-aligned. */
#ifdef SOC_GDMA_EXT_MEM_ENC_ALIGNMENT
if (efuse_hal_flash_encryption_enabled()) {
if (esp_ptr_external_ram(input) || esp_ptr_external_ram(buf) || esp_ptr_in_drom(input) || esp_ptr_in_drom(buf)) {
bool input_needs_realloc = false;
bool buf_needs_realloc = false;
/* When SHA-DMA operations are carried out using external memory with MSPI strict alignment enabled,
we need to make sure that the addresses and the sizes of the buffers on which the DMA operates are aligned. */
#ifdef SOC_MEMSPI_ENCRYPTION_ALIGNMENT
if (esp_ptr_external_ram(input) || esp_ptr_external_ram(buf) || esp_ptr_in_drom(input) || esp_ptr_in_drom(buf)) {
bool input_needs_realloc = false;
bool buf_needs_realloc = false;
if (ilen && ((intptr_t)(input) & (SOC_GDMA_EXT_MEM_ENC_ALIGNMENT - 1)) != 0) {
input_needs_realloc = true;
}
/* Skip when length is zero: buffer is unused and ptr may be NULL. */
if (ilen && !esp_mspi_buffer_alignment_satisfied(input, ilen)) {
input_needs_realloc = true;
}
if (buf_len && ((intptr_t)(buf) & (SOC_GDMA_EXT_MEM_ENC_ALIGNMENT - 1)) != 0) {
buf_needs_realloc = true;
}
if (buf_len && !esp_mspi_buffer_alignment_satisfied(buf, buf_len)) {
buf_needs_realloc = true;
}
if (input_needs_realloc || buf_needs_realloc) {
return esp_sha_dma_process_ext(sha_type, input, ilen, buf, buf_len, is_first_block, input_needs_realloc, buf_needs_realloc);
}
if (input_needs_realloc || buf_needs_realloc) {
return esp_sha_dma_process_ext(sha_type, input, ilen, buf, buf_len, is_first_block, input_needs_realloc, buf_needs_realloc);
}
}
#endif /* SOC_GDMA_EXT_MEM_ENC_ALIGNMENT */
#endif /* SOC_MEMSPI_ENCRYPTION_ALIGNMENT */
/* DMA descriptor for Memory to DMA-SHA transfer */
if (ilen) {

View File

@@ -5,6 +5,7 @@ components/mbedtls/test_apps/mbedtls_ut:
- if: CONFIG_NAME == "aes_no_hw" and SOC_AES_SUPPORTED != 1
- if: CONFIG_NAME == "psram" and SOC_SPIRAM_SUPPORTED != 1
- if: CONFIG_NAME == "psram_all_ext" and SOC_SPIRAM_SUPPORTED != 1
- if: CONFIG_NAME == "psram_ecc" and (SOC_SPIRAM_SUPPORTED != 1 or SOC_PSRAM_DMA_CAPABLE != 1 or SOC_AES_SUPPORT_DMA != 1 or IDF_TARGET == "esp32s2")
- if: CONFIG_NAME == "psram_all_ext_flash_enc" and SOC_SPIRAM_SUPPORTED != 1
- if: CONFIG_NAME == "psram_all_ext_flash_enc_f4r8" and IDF_TARGET != "esp32s3"
- if: CONFIG_NAME == "ecdsa_sign" and SOC_ECDSA_SUPPORTED != 1

View File

@@ -35,7 +35,7 @@
#define IDF_PERFORMANCE_MIN_AES_GCM_UPDATE_THROUGHPUT_MBSEC 2.1
// SHA256 hardware throughput at 240MHz, threshold set lower than worst case
#define IDF_PERFORMANCE_MIN_SHA256_THROUGHPUT_MBSEC 90.0
#define IDF_PERFORMANCE_MIN_SHA256_THROUGHPUT_MBSEC 88.0
// esp_sha() time to process 32KB of input data from RAM
#define IDF_PERFORMANCE_MAX_TIME_SHA1_32KB 900
#define IDF_PERFORMANCE_MAX_TIME_SHA512_32KB 900

View File

@@ -1846,6 +1846,105 @@ TEST_CASE("mbedtls AES internal mem alignment tests", "[aes]")
#ifdef CONFIG_SPIRAM_USE_MALLOC
#if CONFIG_SPIRAM_ECC_ENABLE && SOC_AES_SUPPORT_DMA
#define TEST_AES_PAYLOAD_LEN 53
struct aes_payload_sim_hdr {
uint8_t type;
uint8_t fc;
uint8_t seq;
uint8_t len;
uint8_t data[];
} __attribute__((packed));
static void aes_psram_ecc_cfb128_inplace_test(void)
{
const size_t pkt_len = sizeof(struct aes_payload_sim_hdr) + TEST_AES_PAYLOAD_LEN;
struct aes_payload_sim_hdr *pkt = heap_caps_aligned_alloc(16, pkt_len, PSRAM_DMA_CAPS);
uint8_t *backup = heap_caps_malloc(TEST_AES_PAYLOAD_LEN, INTERNAL_DMA_CAPS);
uint8_t key[16];
uint8_t iv[16];
psa_key_id_t key_id;
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_status_t status;
size_t output_len;
size_t total_len;
TEST_ASSERT_NOT_NULL(pkt);
TEST_ASSERT_NOT_NULL(backup);
TEST_ASSERT_TRUE(esp_ptr_external_ram(pkt));
TEST_ASSERT_EQUAL_UINT32(0, (uintptr_t)pkt & 0x0F);
TEST_ASSERT_EQUAL_UINT32(sizeof(struct aes_payload_sim_hdr) & 0x0F, (uintptr_t)pkt->data & 0x0F);
pkt->type = 0x13;
pkt->fc = 0x04;
pkt->seq = 1;
pkt->len = TEST_AES_PAYLOAD_LEN;
for (size_t i = 0; i < TEST_AES_PAYLOAD_LEN; i++) {
pkt->data[i] = 0x3C + (uint8_t)(i & 0x0F);
}
memcpy(backup, pkt->data, TEST_AES_PAYLOAD_LEN);
memset(key, 0x5A, sizeof(key));
memset(iv, 0xA5, sizeof(iv));
iv[0] = 3;
status = psa_crypto_init();
TEST_ASSERT_EQUAL(PSA_SUCCESS, status);
psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
psa_set_key_algorithm(&attributes, PSA_ALG_CFB);
psa_set_key_type(&attributes, PSA_KEY_TYPE_AES);
psa_set_key_bits(&attributes, 128);
status = psa_import_key(&attributes, key, sizeof(key), &key_id);
TEST_ASSERT_EQUAL(PSA_SUCCESS, status);
psa_cipher_operation_t operation = PSA_CIPHER_OPERATION_INIT;
status = psa_cipher_encrypt_setup(&operation, key_id, PSA_ALG_CFB);
TEST_ASSERT_EQUAL(PSA_SUCCESS, status);
status = psa_cipher_set_iv(&operation, iv, sizeof(iv));
TEST_ASSERT_EQUAL(PSA_SUCCESS, status);
total_len = 0;
status = psa_cipher_update(&operation, pkt->data, TEST_AES_PAYLOAD_LEN, pkt->data, TEST_AES_PAYLOAD_LEN, &output_len);
TEST_ASSERT_EQUAL(PSA_SUCCESS, status);
total_len += output_len;
status = psa_cipher_finish(&operation, pkt->data + output_len, TEST_AES_PAYLOAD_LEN - output_len, &output_len);
TEST_ASSERT_EQUAL(PSA_SUCCESS, status);
total_len += output_len;
TEST_ASSERT_EQUAL(TEST_AES_PAYLOAD_LEN, total_len);
memset(iv, 0xA5, sizeof(iv));
iv[0] = 3;
psa_cipher_abort(&operation);
operation = (psa_cipher_operation_t)PSA_CIPHER_OPERATION_INIT;
status = psa_cipher_decrypt_setup(&operation, key_id, PSA_ALG_CFB);
TEST_ASSERT_EQUAL(PSA_SUCCESS, status);
status = psa_cipher_set_iv(&operation, iv, sizeof(iv));
TEST_ASSERT_EQUAL(PSA_SUCCESS, status);
total_len = 0;
status = psa_cipher_update(&operation, pkt->data, TEST_AES_PAYLOAD_LEN, pkt->data, TEST_AES_PAYLOAD_LEN, &output_len);
TEST_ASSERT_EQUAL(PSA_SUCCESS, status);
total_len += output_len;
status = psa_cipher_finish(&operation, pkt->data + output_len, TEST_AES_PAYLOAD_LEN - output_len, &output_len);
TEST_ASSERT_EQUAL(PSA_SUCCESS, status);
total_len += output_len;
TEST_ASSERT_EQUAL(TEST_AES_PAYLOAD_LEN, total_len);
TEST_ASSERT_EQUAL_MEMORY(backup, pkt->data, TEST_AES_PAYLOAD_LEN);
psa_cipher_abort(&operation);
psa_destroy_key(key_id);
free(backup);
free(pkt);
}
TEST_CASE("mbedtls AES PSRAM ECC CFB128 in-place test", "[aes][psram_ecc_dma]")
{
aes_psram_ecc_cfb128_inplace_test();
}
#endif // CONFIG_SPIRAM_ECC_ENABLE && SOC_AES_SUPPORT_DMA
void aes_psram_one_buf_ctr_test(void)
{
psa_key_id_t key_id;

View File

@@ -5,6 +5,15 @@ from pytest_embedded import Dut
from pytest_embedded_idf.utils import idf_parametrize
from pytest_embedded_idf.utils import soc_filtered_targets
# esp32s2 has PSRAM DMA and AES DMA, but no PSRAM ECC Kconfig option.
PSRAM_ECC_DMA_TARGETS = [
target
for target in soc_filtered_targets(
'SOC_SPIRAM_SUPPORTED == 1 and SOC_PSRAM_DMA_CAPABLE == 1 and SOC_AES_SUPPORT_DMA == 1'
)
if target != 'esp32s2'
]
@pytest.mark.generic
@pytest.mark.temp_skip_ci(targets=['esp32h4'], reason='can not pass') # TODO: IDF-15675
@@ -53,6 +62,19 @@ def test_mbedtls_psram(dut: Dut) -> None:
dut.run_all_single_board_cases(timeout=180)
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
[
'psram_ecc',
],
indirect=True,
)
@idf_parametrize('target', PSRAM_ECC_DMA_TARGETS, indirect=['target'])
def test_mbedtls_psram_ecc(dut: Dut) -> None:
dut.run_all_single_board_cases(group='aes', timeout=180)
@pytest.mark.flash_encryption_psram
@pytest.mark.parametrize(
'config',

View File

@@ -0,0 +1,5 @@
CONFIG_SPIRAM=y
CONFIG_SPIRAM_ECC_ENABLE=y
CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=0
CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM=y
CONFIG_ESP_INT_WDT_TIMEOUT_MS=800

View File

@@ -551,10 +551,6 @@ config SOC_GDMA_SUPPORT_WEIGHTED_ARBITRATION
bool
default y
config SOC_GDMA_EXT_MEM_ENC_ALIGNMENT
int
default 16
config SOC_GPIO_PORT
int
default 1

View File

@@ -212,7 +212,6 @@
#define SOC_GDMA_SUPPORT_ETM 1
#define SOC_GDMA_SUPPORT_SLEEP_RETENTION 1
#define SOC_GDMA_SUPPORT_WEIGHTED_ARBITRATION 1
#define SOC_GDMA_EXT_MEM_ENC_ALIGNMENT (16)
/*-------------------------- GPIO CAPS ---------------------------------------*/
// ESP32-C5 has 1 GPIO peripheral

View File

@@ -423,10 +423,6 @@ config SOC_GDMA_SUPPORT_WEIGHTED_ARBITRATION
bool
default y
config SOC_GDMA_EXT_MEM_ENC_ALIGNMENT
int
default 16
config SOC_ETM_SUPPORT_SLEEP_RETENTION
bool
default y

View File

@@ -164,7 +164,6 @@
#define SOC_GDMA_SUPPORT_ETM 1 // Support ETM submodule
#define SOC_GDMA_SUPPORT_SLEEP_RETENTION 1
#define SOC_GDMA_SUPPORT_WEIGHTED_ARBITRATION 1
#define SOC_GDMA_EXT_MEM_ENC_ALIGNMENT (16)
/*-------------------------- ETM CAPS -----------------------------------*/
#define SOC_ETM_SUPPORT_SLEEP_RETENTION 1

View File

@@ -667,10 +667,6 @@ config SOC_GDMA_SUPPORT_SLEEP_RETENTION
bool
default y
config SOC_GDMA_EXT_MEM_ENC_ALIGNMENT
int
default 16
config SOC_GPIO_PORT
int
default 1

View File

@@ -242,7 +242,6 @@
#define SOC_GDMA_SUPPORT_CRC 1
#define SOC_GDMA_SUPPORT_ETM 1
#define SOC_GDMA_SUPPORT_SLEEP_RETENTION 1
#define SOC_GDMA_EXT_MEM_ENC_ALIGNMENT (16)
/*-------------------------- GPIO CAPS ---------------------------------------*/
// ESP32-P4 has 1 GPIO peripheral

View File

@@ -615,10 +615,6 @@ config SOC_GDMA_SUPPORT_SLEEP_RETENTION
bool
default y
config SOC_GDMA_EXT_MEM_ENC_ALIGNMENT
int
default 16
config SOC_MODEM_SUPPORT_ETM
bool
default y

View File

@@ -210,7 +210,6 @@
#define SOC_AHB_GDMA_VERSION 2
#define SOC_GDMA_SUPPORT_ETM 1
#define SOC_GDMA_SUPPORT_SLEEP_RETENTION 1
#define SOC_GDMA_EXT_MEM_ENC_ALIGNMENT (16)
/*-------------------------- MODEM CAPS --------------------------------------*/
#define SOC_MODEM_SUPPORT_ETM 1

View File

@@ -95,9 +95,12 @@ extern void example_lvgl_demo_ui(lv_display_t *disp);
#if CONFIG_EXAMPLE_USE_DMA2D_COPY_FRAME
void example_rounder_flush_area_cb(lv_event_t * event)
{
// Under flash encryption, DMA2D access to PSRAM must satisfy MSPI encryption
// alignment (typically 16 bytes) for both the buffer address and transfer size.
// Round the LVGL invalidate area so the flush region width meets that requirement.
lv_area_t * area = lv_event_get_invalidated_area(event);
area->x1 = ALIGN_DOWN(area->x1, 16);
area->x2 = ALIGN_UP(area->x2, 16) - 1;
area->x2 = ALIGN_UP(area->x2 + 1, 16) - 1;
}
#endif
@@ -108,7 +111,7 @@ static void example_lvgl_flush_cb(lv_display_t *disp, const lv_area_t *area, uin
int offsetx2 = area->x2;
int offsety1 = area->y1;
int offsety2 = area->y2;
// pass the draw buffer to the driver
// LVGL area coordinates are inclusive; panel draw_bitmap expects [start, end).
esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, px_map);
}
@@ -298,23 +301,14 @@ void app_main(void)
void *buf2 = NULL;
ESP_LOGI(TAG, "Allocate separate LVGL draw buffers");
size_t alignment = 1;
#if CONFIG_EXAMPLE_USE_DMA2D_COPY_FRAME
if (esp_efuse_is_flash_encryption_enabled()) {
alignment = SOC_GDMA_EXT_MEM_ENC_ALIGNMENT;
if (EXAMPLE_MIPI_DSI_LCD_H_RES % alignment != 0) {
ESP_LOGW(TAG, "EXAMPLE_MIPI_DSI_LCD_H_RES is not aligned to %d, may cause MSPI error", alignment);
}
}
#endif
size_t draw_buffer_sz = EXAMPLE_MIPI_DSI_LCD_H_RES * EXAMPLE_LVGL_DRAW_BUF_LINES * sizeof(lv_color_t);
// Note:
// Keep the display buffer in **internal** RAM can speed up the UI because LVGL uses it a lot and it should have a fast access time
// This example allocate the buffer from PSRAM mainly because we want to save the internal RAM
buf1 = heap_caps_aligned_calloc(alignment, 1, draw_buffer_sz, MALLOC_CAP_SPIRAM);
buf1 = heap_caps_aligned_calloc(16, 1, draw_buffer_sz, MALLOC_CAP_SPIRAM);
assert(buf1);
buf2 = heap_caps_aligned_calloc(alignment, 1, draw_buffer_sz, MALLOC_CAP_SPIRAM);
buf2 = heap_caps_aligned_calloc(16, 1, draw_buffer_sz, MALLOC_CAP_SPIRAM);
assert(buf2);
// initialize LVGL draw buffers
lv_display_set_buffers(display, buf1, buf2, draw_buffer_sz, LV_DISPLAY_RENDER_MODE_PARTIAL);
@@ -322,9 +316,13 @@ void app_main(void)
lv_display_set_flush_cb(display, example_lvgl_flush_cb);
#if CONFIG_EXAMPLE_USE_DMA2D_COPY_FRAME
// If flash encryption is enabled, DMA2D requires the flush buffer address and size to be aligned to 16 bytes.
// We need to round the flush area to the multiple of 16.
if (esp_efuse_is_flash_encryption_enabled()) {
// If Flash Encryption/ PSRAM ECC is enabled, DMA2D requires the flush buffer address and size to be aligned.
// Round the LVGL invalidate area accordingly (this is an LVGL integration hook, not a panel API).
bool need_rounder = esp_efuse_is_flash_encryption_enabled();
#if CONFIG_SPIRAM_ECC_ENABLE
need_rounder = true;
#endif
if (need_rounder) {
ESP_LOGI(TAG, "Register event callback for LVGL flush area rounding");
lv_display_add_event_cb(display, example_rounder_flush_area_cb, LV_EVENT_INVALIDATE_AREA, NULL);
}