mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
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:
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
#include "hal/dma2d_types.h"
|
||||
|
||||
@@ -269,6 +271,47 @@ typedef struct {
|
||||
*/
|
||||
esp_err_t dma2d_set_transfer_ability(dma2d_channel_handle_t dma2d_chan, const dma2d_transfer_ability_t *ability);
|
||||
|
||||
/**
|
||||
* @brief Get DMA2D buffer alignment constraint for a specific buffer
|
||||
*
|
||||
* @note On invalid arguments, returns an impossible alignment (BIT(31)).
|
||||
*
|
||||
* @param[in] buffer Buffer address
|
||||
* @return Alignment requirement in bytes
|
||||
*/
|
||||
size_t dma2d_get_buffer_alignment_constraint(const void *buffer);
|
||||
|
||||
/**
|
||||
* @brief Get alignment required when allocating a buffer for DMA2D access
|
||||
*
|
||||
* Use this before the buffer exists (e.g. `heap_caps_aligned_calloc`).
|
||||
* Unlike `dma2d_get_buffer_alignment_constraint`, this returns the worst-case
|
||||
* DMA2D/MSPI alignment rather than treating a NULL pointer as invalid.
|
||||
*
|
||||
* @return Alignment requirement in bytes (1 if no strict alignment is needed)
|
||||
*/
|
||||
size_t dma2d_get_alloc_alignment(void);
|
||||
|
||||
/**
|
||||
* @brief Check whether a 2D DMA transaction window satisfies DMA2D/MSPI alignment
|
||||
*
|
||||
* Under Flash Encryption / PSRAM ECC, MSPI requires each AXI access to be aligned in both
|
||||
* address and size. For a 2D transfer that means:
|
||||
* - buffer base address aligned to N bytes
|
||||
* - bytes-per-line (`pic_width * bpp/8`) aligned, so every next line starts on an N-byte boundary
|
||||
* - transfer width (`blk_width * bpp/8`) aligned
|
||||
* - horizontal window offset (`offset_x * bpp/8`) aligned
|
||||
*
|
||||
* @param[in] buf Buffer base address
|
||||
* @param[in] pic_width Picture / stride width in pixels
|
||||
* @param[in] blk_width Transfer block width in pixels
|
||||
* @param[in] offset_x Horizontal offset of the block in pixels
|
||||
* @param[in] bit_depth Bits per pixel
|
||||
* @return true if the transaction satisfies the alignment constraints
|
||||
*/
|
||||
bool dma2d_check_transaction_alignment_constraint(const void *buf, uint32_t pic_width, uint32_t blk_width,
|
||||
uint32_t offset_x, uint32_t bit_depth);
|
||||
|
||||
/**
|
||||
* @brief A collection of color space conversion (CSC) items that each 2D-DMA channel could apply
|
||||
*/
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include "esp_etm.h"
|
||||
#include "hal/gdma_types.h"
|
||||
#include "esp_err.h"
|
||||
@@ -221,23 +222,50 @@ typedef struct {
|
||||
esp_err_t gdma_config_transfer(gdma_channel_handle_t dma_chan, const gdma_transfer_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Get the alignment constraints for internal and external memory
|
||||
* @brief Alignment constraints of a configured GDMA channel
|
||||
*
|
||||
* @note You should call this function after `gdma_config_transfer`, the later one can
|
||||
* adjust the alignment constraints based on various conditions, e.g. burst size, memory encryption, etc.
|
||||
* @note You can use returned alignment value to validate if a DMA buffer provided by the upper layer meets the constraints.
|
||||
* @note The returned alignment doesn't take the cache line size into account, if you want to do aligned memory allocation,
|
||||
* you should align the buffer size to the cache line size by yourself if the DMA buffer is behind a cache.
|
||||
* @note Prefer this when allocating DMA buffers. Once a concrete buffer address is available,
|
||||
* use `gdma_get_buffer_alignment_constraint` for the effective runtime constraint of that region.
|
||||
* @note For allocation from external memory:
|
||||
* - Use `ext_enc_mem_alignment` as the safe default (worst-case MSPI encryption/ECC).
|
||||
* - Use `ext_no_enc_mem_alignment` when intentionally targeting no-encryption external memory (e.g. no-enc PSRAM).
|
||||
* @note The returned alignment doesn't take the cache line size into account. If the DMA buffer is behind a cache,
|
||||
* align the buffer size to the cache line size yourself when needed.
|
||||
*/
|
||||
typedef struct {
|
||||
size_t int_mem_alignment; /*!< Alignment for internal memory */
|
||||
size_t ext_enc_mem_alignment; /*!< Alignment for external memory including MSPI encryption/ECC constraints */
|
||||
size_t ext_no_enc_mem_alignment; /*!< Alignment for external memory without MSPI region-specific constraints */
|
||||
} gdma_channel_alignment_info_t;
|
||||
|
||||
/**
|
||||
* @brief Get the alignment constraints for a configured GDMA channel
|
||||
*
|
||||
* @note Call this function after `gdma_config_transfer`.
|
||||
*
|
||||
* @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_ahb_channel/gdma_new_axi_channel`
|
||||
* @param[out] int_mem_alignment Internal memory alignment
|
||||
* @param[out] ext_mem_alignment External memory alignment
|
||||
* @param[out] info Alignment constraints of the channel
|
||||
* @return
|
||||
* - ESP_OK: Get alignment constraints successfully
|
||||
* - ESP_ERR_INVALID_ARG: Get alignment constraints failed because of invalid argument
|
||||
* - ESP_FAIL: Get alignment constraints failed because of other error
|
||||
*/
|
||||
esp_err_t gdma_get_alignment_constraints(gdma_channel_handle_t dma_chan, size_t *int_mem_alignment, size_t *ext_mem_alignment);
|
||||
esp_err_t gdma_get_channel_alignment_constraints(gdma_channel_handle_t dma_chan, gdma_channel_alignment_info_t *info);
|
||||
|
||||
/**
|
||||
* @brief Get the effective alignment constraint for a specific DMA buffer
|
||||
*
|
||||
* @note Call this function after `gdma_config_transfer`.
|
||||
* @note Combines GDMA channel constraints with MSPI constraints of the actual buffer region.
|
||||
* External no-encryption PSRAM buffers can therefore use their real runtime constraint
|
||||
* instead of a generic worst-case MSPI alignment.
|
||||
* @note The returned alignment doesn't take the cache line size into account.
|
||||
* @note On invalid arguments, returns an impossible value (BIT(31)).
|
||||
*
|
||||
* @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_ahb_channel/gdma_new_axi_channel`
|
||||
* @param[in] buffer DMA buffer address
|
||||
* @return Effective buffer alignment in bytes
|
||||
*/
|
||||
size_t gdma_get_buffer_alignment_constraint(gdma_channel_handle_t dma_chan, const void *buffer);
|
||||
|
||||
/**
|
||||
* @brief Apply channel strategy for GDMA channel
|
||||
|
||||
@@ -82,7 +82,9 @@ typedef struct {
|
||||
gdma_final_node_link_type_t mark_final: 2; /*!< Specify the next item of the final item of this mount.
|
||||
For the other items that not the final one, it will be linked to the next item automatically and this field takes no effect.
|
||||
Note, the final item here does not mean the last item in the link list. It is `start_item_index + num_items - 1` */
|
||||
uint32_t bypass_buffer_align_check: 1; /*!< Whether to bypass the buffer alignment check.
|
||||
uint32_t bypass_buffer_addr_align_check: 1; /*!< Whether to bypass the buffer address alignment check.
|
||||
Only enable it when you know what you are doing. */
|
||||
uint32_t bypass_buffer_size_align_check: 1; /*!< Whether to bypass the buffer size alignment check.
|
||||
Only enable it when you know what you are doing. */
|
||||
} flags; //!< Flags for buffer mount configurations
|
||||
} gdma_buffer_mount_config_t;
|
||||
|
||||
@@ -11,6 +11,7 @@ entries:
|
||||
gdma: gdma_stop (noflash)
|
||||
gdma: gdma_append (noflash)
|
||||
gdma: gdma_reset (noflash)
|
||||
gdma: gdma_get_buffer_alignment_constraint (noflash)
|
||||
|
||||
[mapping:gdma_hal]
|
||||
archive: libesp_hal_dma.a
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <stdatomic.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/param.h>
|
||||
#include <inttypes.h>
|
||||
#include <assert.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
@@ -16,6 +17,7 @@
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "esp_async_color_convert_priv.h"
|
||||
#include "esp_private/dma2d.h"
|
||||
#include "soc/dma2d_channel.h"
|
||||
#include "hal/dma2d_types.h"
|
||||
#include "hal/dma2d_ll.h"
|
||||
@@ -207,6 +209,17 @@ static esp_err_t validate_request(const async_color_convert_request_t *request)
|
||||
request->dst_height <= DMA2D_LL_DESC_2D_FIELD_MAX,
|
||||
ESP_ERR_INVALID_ARG, TAG, "dimension exceeds DMA2D descriptor field limit");
|
||||
|
||||
uint32_t src_bit_depth = color_hal_pixel_format_fourcc_get_bit_depth(request->src_color_format);
|
||||
uint32_t dst_bit_depth = color_hal_pixel_format_fourcc_get_bit_depth(request->dst_color_format);
|
||||
ESP_RETURN_ON_FALSE(dma2d_check_transaction_alignment_constraint(request->src_buffer, request->src_stride,
|
||||
request->copy_width, request->src_x,
|
||||
src_bit_depth),
|
||||
ESP_ERR_INVALID_ARG, TAG, "source buffer or window is not aligned to DMA2D alignment");
|
||||
ESP_RETURN_ON_FALSE(dma2d_check_transaction_alignment_constraint(request->dst_buffer, request->dst_stride,
|
||||
request->copy_width, request->dst_x,
|
||||
dst_bit_depth),
|
||||
ESP_ERR_INVALID_ARG, TAG, "destination buffer or window is not aligned to DMA2D alignment");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#include "hal/dma2d_periph.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_bit_defs.h"
|
||||
#include "esp_efuse.h"
|
||||
#include "esp_private/esp_mspi_align.h"
|
||||
#include "esp_private/sleep_retention.h"
|
||||
|
||||
/**
|
||||
@@ -801,8 +801,12 @@ esp_err_t dma2d_set_desc_addr(dma2d_channel_handle_t dma2d_chan, intptr_t desc_b
|
||||
addr_in_spm = esp_ptr_in_spm((void *)desc_base_addr);
|
||||
#endif
|
||||
ESP_GOTO_ON_FALSE_ISR((desc_base_addr & 0x7) == 0 && !addr_in_spm, ESP_ERR_INVALID_ARG, err, TAG, "invalid descriptor base addr");
|
||||
// When flash encryption is enabled, the descriptor must be in internal RAM because descriptor size is not 16-byte aligned, which breaks flash encryption alignment restriction
|
||||
ESP_GOTO_ON_FALSE_ISR(!esp_efuse_is_flash_encryption_enabled() || esp_ptr_internal((void *)desc_base_addr), ESP_ERR_INVALID_ARG, err, TAG, "invalid description base addr");
|
||||
// If descriptors are placed in external memory, their size must meet MSPI alignment constraints;
|
||||
// otherwise, descriptors must be located in internal RAM.
|
||||
size_t mspi_align = esp_mspi_get_alignment((void *)desc_base_addr);
|
||||
bool desc_size_mspi_aligned = (sizeof(dma2d_descriptor_t) & (mspi_align - 1)) == 0;
|
||||
ESP_GOTO_ON_FALSE_ISR(desc_size_mspi_aligned || esp_ptr_internal((void *)desc_base_addr),
|
||||
ESP_ERR_INVALID_ARG, err, TAG, "invalid description base addr");
|
||||
|
||||
dma2d_group_t *group = dma2d_chan->group;
|
||||
int channel_id = dma2d_chan->channel_id;
|
||||
@@ -920,20 +924,19 @@ esp_err_t dma2d_set_transfer_ability(dma2d_channel_handle_t dma2d_chan, const dm
|
||||
ESP_GOTO_ON_FALSE_ISR(dma2d_chan && ability, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE_ISR(ability->data_burst_length && ((ability->data_burst_length & (ability->data_burst_length - 1)) == 0), ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); // burst size must be power of 2
|
||||
ESP_GOTO_ON_FALSE_ISR(ability->mb_size < DMA2D_MACRO_BLOCK_SIZE_INVALID, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE_ISR(!ability->access_ext_mem || (SOC_PSRAM_DMA_CAPABLE || SOC_DMA_CAN_ACCESS_FLASH), ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
|
||||
dma2d_group_t *group = dma2d_chan->group;
|
||||
int channel_id = dma2d_chan->channel_id;
|
||||
|
||||
// When flash encryption is enabled, and the channel is accessing external memory, burst length has to be as least as the encryption alignment restriction size
|
||||
uint32_t data_burst_length = ability->data_burst_length;
|
||||
#if SOC_PSRAM_DMA_CAPABLE || SOC_DMA_CAN_ACCESS_FLASH
|
||||
if (esp_efuse_is_flash_encryption_enabled() && ability->access_ext_mem) {
|
||||
if (data_burst_length < SOC_MEMSPI_ENCRYPTION_ALIGNMENT) {
|
||||
data_burst_length = SOC_MEMSPI_ENCRYPTION_ALIGNMENT;
|
||||
ESP_LOGW(TAG, "channel access encrypted external memory, adjust burst size to %d", SOC_MEMSPI_ENCRYPTION_ALIGNMENT);
|
||||
if (ability->access_ext_mem) {
|
||||
// If channel is accessing external memory, burst length has to be at least the MSPI alignment restriction size
|
||||
size_t mspi_alignment = dma2d_get_alloc_alignment();
|
||||
if (data_burst_length < mspi_alignment) {
|
||||
data_burst_length = mspi_alignment;
|
||||
ESP_LOGW(TAG, "requested burst size does not meet MSPI alignment constraint, adjust to %d", (int)mspi_alignment);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (dma2d_chan->direction == DMA2D_CHANNEL_DIRECTION_TX) {
|
||||
dma2d_ll_tx_enable_descriptor_burst(group->hal.dev, channel_id, ability->desc_burst_en);
|
||||
@@ -949,6 +952,46 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t dma2d_get_buffer_alignment_constraint(const void *buffer)
|
||||
{
|
||||
if (!buffer) {
|
||||
return BIT(31);
|
||||
}
|
||||
|
||||
return esp_mspi_get_alignment(buffer);
|
||||
}
|
||||
|
||||
size_t dma2d_get_alloc_alignment(void)
|
||||
{
|
||||
// Worst-case alignment for buffers that may be accessed by DMA2D (MSPI FE/ECC, etc.)
|
||||
return esp_mspi_get_alignment(NULL);
|
||||
}
|
||||
|
||||
bool dma2d_check_transaction_alignment_constraint(const void *buf, uint32_t pic_width, uint32_t blk_width,
|
||||
uint32_t offset_x, uint32_t bit_depth)
|
||||
{
|
||||
if (!buf || bit_depth == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t alignment = dma2d_get_buffer_alignment_constraint(buf);
|
||||
if (alignment <= 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Under Flash Encryption / PSRAM ECC, MSPI requires each AXI access to be aligned in both address and size.
|
||||
// For 2D DMA that means:
|
||||
// - buffer base address aligned to N bytes
|
||||
// - bytes-per-line (pic_width * bpp/8) aligned, so every next line starts on an N-byte boundary
|
||||
// - transfer width (blk_width * bpp/8) aligned
|
||||
// - horizontal window offset (offset_x * bpp/8) aligned, so the first pixel of the window is aligned
|
||||
uint32_t alignment_bits = alignment * 8;
|
||||
return (((uintptr_t)buf & (alignment - 1)) == 0) &&
|
||||
(((uint64_t)pic_width * bit_depth) % alignment_bits == 0) &&
|
||||
(((uint64_t)blk_width * bit_depth) % alignment_bits == 0) &&
|
||||
(((uint64_t)offset_x * bit_depth) % alignment_bits == 0);
|
||||
}
|
||||
|
||||
esp_err_t dma2d_configure_color_space_conversion(dma2d_channel_handle_t dma2d_chan, const dma2d_csc_config_t *config)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -11,10 +11,9 @@
|
||||
#include "unity.h"
|
||||
#include "esp_private/dw_gdma.h"
|
||||
#include "hal/dw_gdma_ll.h"
|
||||
#include "hal/efuse_hal.h"
|
||||
#include "esp_cache.h"
|
||||
#include "esp_private/esp_cache_private.h"
|
||||
#include "esp_efuse.h"
|
||||
#include "esp_private/esp_mspi_align.h"
|
||||
|
||||
TEST_CASE("DW_GDMA channel allocation", "[DW_GDMA]")
|
||||
{
|
||||
@@ -542,8 +541,8 @@ TEST_CASE("DW_GDMA M2M Test: memory set with fixed address", "[DW_GDMA]")
|
||||
size_t int_mem_alignment = 0;
|
||||
TEST_ESP_OK(esp_cache_get_alignment(MALLOC_CAP_SPIRAM, &ext_mem_alignment));
|
||||
TEST_ESP_OK(esp_cache_get_alignment(0, &int_mem_alignment));
|
||||
if (esp_efuse_is_flash_encryption_enabled()) {
|
||||
TEST_PASS_MESSAGE("Flash encryption is enabled, skip this test");
|
||||
if (esp_mspi_get_alignment(NULL) > 1) {
|
||||
TEST_PASS_MESSAGE("MSPI strict alignment required (Flash Encryption / PSRAM ECC), skip this test");
|
||||
}
|
||||
uint8_t *src_buf = heap_caps_aligned_calloc(ext_mem_alignment, 1, 256, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
uint8_t *dst_buf = heap_caps_aligned_calloc(int_mem_alignment, 1, 256, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||
|
||||
@@ -20,11 +20,9 @@
|
||||
#include "hal/gdma_ll.h"
|
||||
#include "hal/cache_ll.h"
|
||||
#include "hal/cache_hal.h"
|
||||
#include "hal/efuse_hal.h"
|
||||
#include "esp_cache.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "gdma_test_utils.h"
|
||||
#include "esp_efuse.h"
|
||||
|
||||
#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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}")
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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:
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user