From 2b8029c7755759be90005f1bb8b7f9415872cd8a Mon Sep 17 00:00:00 2001 From: Chen Jichang Date: Mon, 10 Aug 2026 20:35:56 +0800 Subject: [PATCH] feat(mspi): split dma and mspi alignment --- components/esp_asrc_adapter/asrc_adapter.c | 27 +++- .../src/bitscrambler_loopback.c | 1 + .../dvp/src/esp_cam_ctlr_dvp_gdma.c | 26 +++- .../include/esp_private/dma2d.h | 43 ++++++ .../esp_driver_dma/include/esp_private/gdma.h | 45 +++++- .../include/esp_private/gdma_link.h | 4 + components/esp_driver_dma/linker.lf | 2 + .../src/async_color_convert_dma2d.c | 13 ++ .../esp_driver_dma/src/async_crc_gdma.c | 15 +- .../esp_driver_dma/src/async_memcpy_gdma.c | 39 +---- components/esp_driver_dma/src/dma2d.c | 65 +++++++-- components/esp_driver_dma/src/esp_dma_utils.c | 2 +- components/esp_driver_dma/src/gdma.c | 136 +++++++++++------- components/esp_driver_dma/src/gdma_link.c | 34 ++--- components/esp_driver_dma/src/gdma_priv.h | 4 +- .../test_apps/dma/main/gdma_test_utils.h | 13 +- .../test_apps/dma/main/test_async_memcpy.c | 12 +- .../test_apps/dma/main/test_dw_gdma.c | 9 +- .../test_apps/dma/main/test_gdma.c | 36 +++-- components/esp_driver_i3c/i3c_master.c | 3 +- components/esp_driver_jpeg/jpeg_common.c | 16 --- components/esp_driver_jpeg/jpeg_decode.c | 6 +- components/esp_driver_jpeg/jpeg_encode.c | 5 +- components/esp_driver_jpeg/jpeg_private.h | 12 -- components/esp_driver_parlio/src/parlio_rx.c | 16 ++- components/esp_driver_parlio/src/parlio_tx.c | 7 +- components/esp_driver_ppa/src/ppa_core.c | 30 ++-- components/esp_driver_rmt/src/rmt_private.h | 2 - components/esp_driver_rmt/src/rmt_rx.c | 20 ++- components/esp_driver_rmt/src/rmt_tx.c | 2 +- .../esp_driver_spi/src/gpspi/spi_common.c | 25 +++- components/esp_driver_uart/src/uhci.c | 49 +++---- components/esp_driver_uart/src/uhci_private.h | 8 -- components/esp_hw_support/CMakeLists.txt | 4 +- components/esp_hw_support/heap_align_hw.c | 9 +- .../mspi/esp_mspi_align/esp_mspi_align.c | 59 ++++++++ .../include/esp_private/esp_mspi_align.h | 46 ++++++ components/esp_hw_support/mspi/linker.lf | 7 + components/esp_lcd/dsi/esp_lcd_panel_dpi.c | 13 +- components/esp_lcd/i80/esp_lcd_panel_io_i80.c | 18 +-- components/esp_lcd/rgb/esp_lcd_panel_rgb.c | 21 +-- components/esp_psram/include/esp_psram.h | 10 +- components/mbedtls/CMakeLists.txt | 2 + .../mbedtls/port/aes/dma/esp_aes_dma_core.c | 96 ++++++++----- components/mbedtls/port/sha/core/sha.c | 47 +++--- .../esp32c5/include/soc/Kconfig.soc_caps.in | 4 - components/soc/esp32c5/include/soc/soc_caps.h | 1 - .../esp32c61/include/soc/Kconfig.soc_caps.in | 4 - .../soc/esp32c61/include/soc/soc_caps.h | 1 - .../esp32p4/include/soc/Kconfig.soc_caps.in | 4 - components/soc/esp32p4/include/soc/soc_caps.h | 1 - .../esp32s31/include/soc/Kconfig.soc_caps.in | 4 - .../soc/esp32s31/include/soc/soc_caps.h | 1 - .../mipi_dsi/main/mipi_dsi_lcd_example_main.c | 30 ++-- 54 files changed, 685 insertions(+), 424 deletions(-) create mode 100644 components/esp_hw_support/mspi/esp_mspi_align/esp_mspi_align.c create mode 100644 components/esp_hw_support/mspi/esp_mspi_align/include/esp_private/esp_mspi_align.h diff --git a/components/esp_asrc_adapter/asrc_adapter.c b/components/esp_asrc_adapter/asrc_adapter.c index dcdeccbb8a5..d5ecf5f0f98 100644 --- a/components/esp_asrc_adapter/asrc_adapter.c +++ b/components/esp_asrc_adapter/asrc_adapter.c @@ -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) { diff --git a/components/esp_driver_bitscrambler/src/bitscrambler_loopback.c b/components/esp_driver_bitscrambler/src/bitscrambler_loopback.c index 8b9d78fc3f4..cd0070e7fe3 100644 --- a/components/esp_driver_bitscrambler/src/bitscrambler_loopback.c +++ b/components/esp_driver_bitscrambler/src/bitscrambler_loopback.c @@ -239,6 +239,7 @@ esp_err_t bitscrambler_loopback_run(bitscrambler_handle_t bs, void *buffer_in, s .flags = { .mark_eof = false, .mark_final = GDMA_FINAL_LINK_TO_NULL, + .check_size_align = gdma_is_size_alignment_required(bsl->rx_channel), } }; gdma_link_mount_buffers(bsl->rx_link_list, 0, &out_buf_mount_config, 1, NULL); diff --git a/components/esp_driver_cam/dvp/src/esp_cam_ctlr_dvp_gdma.c b/components/esp_driver_cam/dvp/src/esp_cam_ctlr_dvp_gdma.c index 41f4a670fcf..5c4a63200e1 100644 --- a/components/esp_driver_cam/dvp/src/esp_cam_ctlr_dvp_gdma.c +++ b/components/esp_driver_cam/dvp/src/esp_cam_ctlr_dvp_gdma.c @@ -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 */ @@ -38,12 +38,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; @@ -104,10 +104,15 @@ 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_get_channel_alignment_constraints(dma->dma_chan, &dma->int_mem_align, &dma->ext_mem_align, NULL); - 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 = ESP_ALIGN_DOWN(desc_max_size, buffer_alignment); + } + dma->desc_count = size / desc_max_size; + if (size % desc_max_size) { dma->desc_count++; } dma->size = size; @@ -163,7 +168,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 = ESP_ALIGN_DOWN(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); diff --git a/components/esp_driver_dma/include/esp_private/dma2d.h b/components/esp_driver_dma/include/esp_private/dma2d.h index ddaca01f6d1..8db3d2c7a03 100644 --- a/components/esp_driver_dma/include/esp_private/dma2d.h +++ b/components/esp_driver_dma/include/esp_private/dma2d.h @@ -10,6 +10,8 @@ #pragma once #include +#include +#include #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 */ diff --git a/components/esp_driver_dma/include/esp_private/gdma.h b/components/esp_driver_dma/include/esp_private/gdma.h index 23819760bbb..310043d6df8 100644 --- a/components/esp_driver_dma/include/esp_private/gdma.h +++ b/components/esp_driver_dma/include/esp_private/gdma.h @@ -219,23 +219,58 @@ 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 Get the alignment constraints for 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. + * adjust the alignment constraints based on GDMA-specific conditions, e.g. burst size. + * @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 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. * * @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] ext_enc_mem_alignment External memory alignment including MSPI encryption/ECC constraints + * @param[out] ext_no_enc_mem_alignment External memory alignment without MSPI region-specific constraints. + * Useful when allocating from no-encryption external memory. Set to NULL if unused. * @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, size_t *int_mem_alignment, + size_t *ext_enc_mem_alignment, size_t *ext_no_enc_mem_alignment); + +/** + * @brief Check whether buffer sizes must meet the configured channel alignment + * + * @note Call this function after `gdma_config_transfer`. + * @note This reports GDMA hardware constraints only. Region-specific MSPI constraints + * are enforced independently when buffers are mounted to a GDMA link list. + * + * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_ahb_channel/gdma_new_axi_channel` + * @return True when buffer sizes must be aligned, otherwise false + */ +bool gdma_is_size_alignment_required(gdma_channel_handle_t dma_chan); + +/** + * @brief Get the effective alignment constraint for a specific DMA buffer + * + * @note You should call this function after `gdma_config_transfer`. + * @note The returned alignment combines GDMA channel constraints with MSPI constraints + * of the actual buffer region. This lets external no-encryption PSRAM buffers 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 alignment (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 diff --git a/components/esp_driver_dma/include/esp_private/gdma_link.h b/components/esp_driver_dma/include/esp_private/gdma_link.h index ced2b811075..93c6271ff0f 100644 --- a/components/esp_driver_dma/include/esp_private/gdma_link.h +++ b/components/esp_driver_dma/include/esp_private/gdma_link.h @@ -84,6 +84,10 @@ typedef struct { 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. Only enable it when you know what you are doing. */ + uint32_t check_size_align: 1; /*!< Whether to check that `length` is aligned to the alignment. + RX callers can query `gdma_is_size_alignment_required` to determine whether + the configured channel requires this check. Under MSPI Flash Encryption / + PSRAM ECC, length alignment is always enforced regardless of this flag. */ } flags; //!< Flags for buffer mount configurations } gdma_buffer_mount_config_t; diff --git a/components/esp_driver_dma/linker.lf b/components/esp_driver_dma/linker.lf index 1f3f199872f..d025315bce1 100644 --- a/components/esp_driver_dma/linker.lf +++ b/components/esp_driver_dma/linker.lf @@ -11,6 +11,8 @@ entries: gdma: gdma_stop (noflash) gdma: gdma_append (noflash) gdma: gdma_reset (noflash) + gdma: gdma_get_buffer_alignment_constraint (noflash) + gdma: gdma_is_size_alignment_required (noflash) [mapping:gdma_hal] archive: libesp_hal_dma.a diff --git a/components/esp_driver_dma/src/async_color_convert_dma2d.c b/components/esp_driver_dma/src/async_color_convert_dma2d.c index 5bb403c6ff1..b6d0a996a08 100644 --- a/components/esp_driver_dma/src/async_color_convert_dma2d.c +++ b/components/esp_driver_dma/src/async_color_convert_dma2d.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #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; } diff --git a/components/esp_driver_dma/src/async_crc_gdma.c b/components/esp_driver_dma/src/async_crc_gdma.c index 7187cf607e4..1e1e49ae2b2 100644 --- a/components/esp_driver_dma/src/async_crc_gdma.c +++ b/components/esp_driver_dma/src/async_crc_gdma.c @@ -7,6 +7,7 @@ #include #include #include +#include #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,8 @@ 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_get_channel_alignment_constraints(crc_gdma->rx_channel, &rx_int_mem_alignment, NULL, NULL); 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, @@ -191,6 +189,7 @@ esp_err_t esp_async_crc_install_gdma_template(const async_crc_config_t *config, .length = rx_buffer_size, .flags = { .mark_final = GDMA_FINAL_LINK_TO_HEAD, + .check_size_align = gdma_is_size_alignment_required(crc_gdma->rx_channel), }, }; ESP_GOTO_ON_ERROR(gdma_link_mount_buffers(crc_gdma->rx_link_list, 0, &rx_buf_mount_config, 1, NULL), @@ -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); diff --git a/components/esp_driver_dma/src/async_memcpy_gdma.c b/components/esp_driver_dma/src/async_memcpy_gdma.c index 63ae2fdc72d..1b705b25006 100644 --- a/components/esp_driver_dma/src/async_memcpy_gdma.c +++ b/components/esp_driver_dma/src/async_memcpy_gdma.c @@ -47,10 +47,6 @@ typedef struct async_memcpy_transaction_t { /// @note - Number of transaction objects are determined by the backlog parameter typedef struct { async_memcpy_context_t parent; // Parent IO interface - size_t rx_int_mem_alignment; // Required DMA buffer alignment for internal RX memory - size_t rx_ext_mem_alignment; // Required DMA buffer alignment for external RX memory - size_t tx_int_mem_alignment; // Required DMA buffer alignment for internal TX memory - size_t tx_ext_mem_alignment; // Required DMA buffer alignment for external TX memory int gdma_bus_id; // GDMA bus id (AHB, AXI, etc.) gdma_channel_handle_t tx_channel; // GDMA TX channel handle gdma_channel_handle_t rx_channel; // GDMA RX channel handle @@ -149,10 +145,6 @@ static esp_err_t esp_async_memcpy_install_gdma_template(const async_memcpy_confi ESP_GOTO_ON_ERROR(gdma_config_transfer(mcp_gdma->tx_channel, &transfer_cfg), err, TAG, "config transfer for tx channel failed"); ESP_GOTO_ON_ERROR(gdma_config_transfer(mcp_gdma->rx_channel, &transfer_cfg), err, TAG, "config transfer for rx channel failed"); - // get the buffer alignment required by the GDMA channel - gdma_get_alignment_constraints(mcp_gdma->rx_channel, &mcp_gdma->rx_int_mem_alignment, &mcp_gdma->rx_ext_mem_alignment); - gdma_get_alignment_constraints(mcp_gdma->tx_channel, &mcp_gdma->tx_int_mem_alignment, &mcp_gdma->tx_ext_mem_alignment); - // register rx eof callback gdma_rx_event_callbacks_t cbs = { .on_recv_eof = mcp_gdma_rx_eof_callback, @@ -281,30 +273,6 @@ static async_memcpy_transaction_t *try_pop_trans_from_idle_queue(async_memcpy_gd return trans; } -/// @brief Check if the address and size can meet the requirement of the DMA engine -static bool check_buffer_alignment(async_memcpy_gdma_context_t *mcp_gdma, void *src, void *dst, size_t n) -{ - bool valid = true; - - if (esp_ptr_external_ram(dst)) { - valid = valid && (((uint32_t)dst & (mcp_gdma->rx_ext_mem_alignment - 1)) == 0); - valid = valid && ((n & (mcp_gdma->rx_ext_mem_alignment - 1)) == 0); - } else { - valid = valid && (((uint32_t)dst & (mcp_gdma->rx_int_mem_alignment - 1)) == 0); - valid = valid && ((n & (mcp_gdma->rx_int_mem_alignment - 1)) == 0); - } - - if (esp_ptr_external_ram(src)) { - valid = valid && (((uint32_t)src & (mcp_gdma->tx_ext_mem_alignment - 1)) == 0); - valid = valid && ((n & (mcp_gdma->tx_ext_mem_alignment - 1)) == 0); - } else { - valid = valid && (((uint32_t)src & (mcp_gdma->tx_int_mem_alignment - 1)) == 0); - valid = valid && ((n & (mcp_gdma->tx_int_mem_alignment - 1)) == 0); - } - - return valid; -} - static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *src, size_t n, async_memcpy_isr_cb_t cb_isr, void *cb_args) { esp_err_t ret = ESP_OK; @@ -335,8 +303,6 @@ static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *s dma_link_item_alignment = GDMA_LL_AHB_DESC_ALIGNMENT; } #endif // SOC_HAS(LP_AHB_GDMA) - // alignment check - ESP_RETURN_ON_FALSE(check_buffer_alignment(mcp_gdma, src, dst, n), ESP_ERR_INVALID_ARG, TAG, "address|size not aligned: %p -> %p, sz=%zu", src, dst, n); async_memcpy_transaction_t *trans = NULL; // pick one transaction node from idle queue @@ -368,7 +334,7 @@ static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *s size_t num_dma_nodes = 0; // allocate gdma TX link, only the body is handled by the DMA - buffer_alignment = esp_ptr_internal(split.body_src) ? mcp_gdma->tx_int_mem_alignment : mcp_gdma->tx_ext_mem_alignment; + buffer_alignment = gdma_get_buffer_alignment_constraint(mcp_gdma->tx_channel, split.body_src); num_dma_nodes = esp_dma_calculate_node_count(split.body_len, buffer_alignment, MCP_DMA_DESCRIPTOR_BUFFER_MAX_SIZE); gdma_link_list_config_t tx_link_cfg = { .item_alignment = dma_link_item_alignment, @@ -394,7 +360,7 @@ static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *s gdma_link_mount_buffers(trans->tx_link_list, 0, tx_buf_mount_config, 1, NULL); // allocate gdma RX link, only the body is handled by the DMA - buffer_alignment = esp_ptr_internal(split.body_dst) ? mcp_gdma->rx_int_mem_alignment : mcp_gdma->rx_ext_mem_alignment; + buffer_alignment = gdma_get_buffer_alignment_constraint(mcp_gdma->rx_channel, split.body_dst); num_dma_nodes = esp_dma_calculate_node_count(split.body_len, buffer_alignment, MCP_DMA_DESCRIPTOR_BUFFER_MAX_SIZE); gdma_link_list_config_t rx_link_cfg = { .item_alignment = dma_link_item_alignment, @@ -414,6 +380,7 @@ static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *s .flags = { .mark_eof = true, .mark_final = GDMA_FINAL_LINK_TO_NULL, + .check_size_align = gdma_is_size_alignment_required(mcp_gdma->rx_channel), } } }; diff --git a/components/esp_driver_dma/src/dma2d.c b/components/esp_driver_dma/src/dma2d.c index e30112ae82f..c620730503b 100644 --- a/components/esp_driver_dma/src/dma2d.c +++ b/components/esp_driver_dma/src/dma2d.c @@ -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; diff --git a/components/esp_driver_dma/src/esp_dma_utils.c b/components/esp_driver_dma/src/esp_dma_utils.c index 03a6b66630a..2d394864d52 100644 --- a/components/esp_driver_dma/src/esp_dma_utils.c +++ b/components/esp_driver_dma/src/esp_dma_utils.c @@ -43,7 +43,7 @@ esp_err_t esp_dma_split_rx_buffer_to_cache_aligned(void *rx_buffer, size_t buffe split_line_size = int_mem_cache_line_size; } bool align_required = split_line_size > 0; - ESP_EARLY_LOGV(TAG, "split_line_size:%zu", split_line_size); + ESP_EARLY_LOGV(TAG, "split_line_size:%d", 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 diff --git a/components/esp_driver_dma/src/gdma.c b/components/esp_driver_dma/src/gdma.c index 4991d93cf6a..865ce9da3c5 100644 --- a/components/esp_driver_dma/src/gdma.c +++ b/components/esp_driver_dma/src/gdma.c @@ -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,87 +420,101 @@ 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, external 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); + dma_chan->flags.size_alignment_required = false; 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); + dma_chan->flags.size_alignment_required = true; } #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, size_t *int_mem_alignment, + size_t *ext_enc_mem_alignment, size_t *ext_no_enc_mem_alignment) { if (!dma_chan) { return ESP_ERR_INVALID_ARG; @@ -507,12 +522,35 @@ esp_err_t gdma_get_alignment_constraints(gdma_channel_handle_t dma_chan, size_t if (int_mem_alignment) { *int_mem_alignment = dma_chan->int_mem_alignment; } - if (ext_mem_alignment) { - *ext_mem_alignment = dma_chan->ext_mem_alignment; + if (ext_enc_mem_alignment) { + *ext_enc_mem_alignment = dma_chan->ext_enc_mem_alignment; + } + if (ext_no_enc_mem_alignment) { + *ext_no_enc_mem_alignment = dma_chan->ext_no_enc_mem_alignment; } return ESP_OK; } +bool gdma_is_size_alignment_required(gdma_channel_handle_t dma_chan) +{ + return dma_chan && dma_chan->flags.size_alignment_required; +} + +size_t gdma_get_buffer_alignment_constraint(gdma_channel_handle_t dma_chan, const void *buffer) +{ + if (!dma_chan || !buffer) { + return BIT(31); + } + + size_t base_alignment = dma_chan->int_mem_alignment; + if (esp_ptr_external_ram(buffer) || esp_ptr_in_drom(buffer)) { + base_alignment = dma_chan->ext_no_enc_mem_alignment; + } + + size_t mspi_alignment = esp_mspi_get_alignment(buffer); + return MAX(base_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) { diff --git a/components/esp_driver_dma/src/gdma_link.c b/components/esp_driver_dma/src/gdma_link.c index 75d50aa0fe8..bb1db52cfe0 100644 --- a/components/esp_driver_dma/src/gdma_link.c +++ b/components/esp_driver_dma/src/gdma_link.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "soc/soc_caps.h" #include "esp_log.h" @@ -15,10 +16,8 @@ #include "esp_memory_utils.h" #include "esp_heap_caps.h" #include "esp_private/gdma_link.h" -#include "hal/cache_hal.h" -#include "hal/efuse_hal.h" -#include "hal/cache_ll.h" #include "esp_cache.h" +#include "esp_private/esp_mspi_align.h" #include "esp_efuse.h" #include "esp_macros.h" @@ -76,10 +75,10 @@ esp_err_t gdma_new_link_list(const gdma_link_list_config_t *config, gdma_link_li bool items_in_ext_mem = config->flags.items_in_ext_mem; uint32_t list_items_mem_caps = MALLOC_CAP_8BIT | MALLOC_CAP_DMA; if (items_in_ext_mem) { - if (esp_efuse_is_flash_encryption_enabled()) { + if (esp_mspi_get_alignment(NULL) > 1) { items_in_ext_mem = false; list_items_mem_caps |= MALLOC_CAP_INTERNAL; - ESP_LOGW(TAG, "DMA linked list items cannot be placed in PSRAM when external memory encryption is enabled, using internal memory instead"); + ESP_LOGW(TAG, "DMA linked list items cannot be placed in PSRAM when MSPI strict alignment is required, using internal memory instead"); } else { list_items_mem_caps |= MALLOC_CAP_SPIRAM; } @@ -90,12 +89,7 @@ esp_err_t gdma_new_link_list(const gdma_link_list_config_t *config, gdma_link_li ESP_GOTO_ON_FALSE(items, ESP_ERR_NO_MEM, err, TAG, "no mem for link list items"); // do memory sync if the list items are in the cache - uint32_t data_cache_line_size = 0; - if (items_in_ext_mem) { - data_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA); - } else { - data_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA); - } + size_t data_cache_line_size = esp_cache_get_line_size_by_addr(items); if (data_cache_line_size) { // write back and then invalidate the cache, because later we will read/write the link list items by non-cached address ESP_GOTO_ON_ERROR(esp_cache_msync(items, ESP_ALIGN_UP(num_items * item_size, data_cache_line_size), @@ -190,12 +184,16 @@ esp_err_t gdma_link_mount_buffers(gdma_link_list_handle_t list, int start_item_i } // alignment must be a power of 2 ESP_RETURN_ON_FALSE_ISR((buffer_alignment & (buffer_alignment - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "align err idx=%"PRIu32" align=%"PRIu32, bi, buffer_alignment); - size_t max_buffer_mount_length = ESP_ALIGN_DOWN(GDMA_MAX_BUFFER_SIZE_PER_LINK_ITEM, buffer_alignment); + size_t mspi_alignment = esp_mspi_get_alignment(buf); + size_t effective_alignment = MAX(buffer_alignment, mspi_alignment); + size_t max_buffer_mount_length = ESP_ALIGN_DOWN(GDMA_MAX_BUFFER_SIZE_PER_LINK_ITEM, effective_alignment); if (!config->flags.bypass_buffer_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); + ESP_RETURN_ON_FALSE_ISR(((uintptr_t)buf & (effective_alignment - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "buf misalign idx=%"PRIu32" align=%"PRIu32, bi, effective_alignment); + // Length alignment: + // - Always required under MSPI strict mode (Flash Encryption / PSRAM ECC): size must align. + // - Also when check_size_align is set by a caller whose configured channel requires size alignment. + if (mspi_alignment > 1 || config->flags.check_size_align) { + ESP_RETURN_ON_FALSE_ISR((len & (effective_alignment - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "buf len misalign idx=%"PRIu32" len=%"PRIu32" align=%"PRIu32"", bi, len, effective_alignment); } } size_t num_items_need = (len + max_buffer_mount_length - 1) / max_buffer_mount_length; @@ -217,7 +215,6 @@ esp_err_t gdma_link_mount_buffers(gdma_link_list_handle_t list, int start_item_i if (buffer_alignment == 0) { buffer_alignment = 1; } - size_t max_buffer_mount_length = ESP_ALIGN_DOWN(GDMA_MAX_BUFFER_SIZE_PER_LINK_ITEM, buffer_alignment); // skip zero-length buffer but scrub any stale descriptor to keep ring clean; no slot consumption if (len == 0 || buf == NULL) { lli_nc = (gdma_link_list_item_t *)(list->items_nc + begin_item_idx % list_item_capacity * item_size); @@ -225,6 +222,9 @@ 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 mspi_alignment = esp_mspi_get_alignment(buf); + size_t effective_alignment = MAX(buffer_alignment, mspi_alignment); + size_t max_buffer_mount_length = ESP_ALIGN_DOWN(GDMA_MAX_BUFFER_SIZE_PER_LINK_ITEM, effective_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++) { diff --git a/components/esp_driver_dma/src/gdma_priv.h b/components/esp_driver_dma/src/gdma_priv.h index e9785438e70..27b7caeb0e4 100644 --- a/components/esp_driver_dma/src/gdma_priv.h +++ b/components/esp_driver_dma/src/gdma_priv.h @@ -90,11 +90,13 @@ 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 uint32_t isr_cache_safe: 1; // whether the interrupt of this channel need to be cache safe + uint32_t size_alignment_required: 1; // whether buffer size must meet the channel alignment } flags; }; diff --git a/components/esp_driver_dma/test_apps/dma/main/gdma_test_utils.h b/components/esp_driver_dma/test_apps/dma/main/gdma_test_utils.h index 9b7708be8a4..d58a09cac9b 100644 --- a/components/esp_driver_dma/test_apps/dma/main/gdma_test_utils.h +++ b/components/esp_driver_dma/test_apps/dma/main/gdma_test_utils.h @@ -10,16 +10,25 @@ #include #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 diff --git a/components/esp_driver_dma/test_apps/dma/main/test_async_memcpy.c b/components/esp_driver_dma/test_apps/dma/main/test_async_memcpy.c index 624e3498647..f6f8970b0b7 100644 --- a/components/esp_driver_dma/test_apps/dma/main/test_async_memcpy.c +++ b/components/esp_driver_dma/test_apps/dma/main/test_async_memcpy.c @@ -15,8 +15,6 @@ #include "freertos/semphr.h" #include "ccomp_timer.h" #include "esp_async_memcpy.h" -#include "hal/efuse_hal.h" -#include "esp_efuse.h" #include "gdma_test_utils.h" #if SOC_GDMA_SUPPORTED @@ -168,7 +166,7 @@ static void test_memory_copy_blocking(async_memcpy_handle_t driver) for (int off = 0; off < 4; off++) { test_context.buffer_size = test_buffer_size[i]; test_context.seed = i; - if (!esp_efuse_is_flash_encryption_enabled()) { + if (!gdma_test_mspi_strict_alignment_required()) { test_context.src_offset = off; test_context.dst_offset = off; } @@ -257,8 +255,8 @@ TEST_CASE("memory copy with dest address unaligned", "[async mcp]") }; [[maybe_unused]] async_memcpy_handle_t driver = NULL; - if (esp_efuse_is_flash_encryption_enabled()) { - TEST_PASS_MESSAGE("Flash encryption is enabled, skip this test"); + if (gdma_test_mspi_strict_alignment_required()) { + TEST_PASS_MESSAGE("MSPI strict alignment required (Flash Encryption / PSRAM ECC), skip this test"); } #if SOC_CP_DMA_SUPPORTED @@ -441,8 +439,8 @@ TEST_CASE("memory copy performance 40KB: PSRAM->PSRAM", "[async mcp]") #if SOC_HAS(LP_AHB_GDMA) #if GDMA_LL_GET(LP_AHB_PSRAM_CAPABLE) - if (esp_efuse_is_flash_encryption_enabled() && !GDMA_TEST_LP_AHB_BURST_PSRAM_SUPPORTED) { - TEST_IGNORE_MESSAGE("Skipping LP AHB GDMA PSRAM->PSRAM under flash encryption"); + if (gdma_test_mspi_strict_alignment_required() && !GDMA_TEST_LP_AHB_BURST_PSRAM_SUPPORTED) { + TEST_IGNORE_MESSAGE("Skipping LP AHB GDMA PSRAM->PSRAM under Flash Encryption / PSRAM ECC"); } else { printf("Testing memcpy by LP AHB GDMA\r\n"); TEST_ESP_OK(esp_async_memcpy_install_gdma_lp_ahb(&driver_config, &driver)); diff --git a/components/esp_driver_dma/test_apps/dma/main/test_dw_gdma.c b/components/esp_driver_dma/test_apps/dma/main/test_dw_gdma.c index adba93d66f6..30bb86d1fb2 100644 --- a/components/esp_driver_dma/test_apps/dma/main/test_dw_gdma.c +++ b/components/esp_driver_dma/test_apps/dma/main/test_dw_gdma.c @@ -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); diff --git a/components/esp_driver_dma/test_apps/dma/main/test_gdma.c b/components/esp_driver_dma/test_apps/dma/main/test_gdma.c index 2bbf2b2db69..5019bc2857b 100644 --- a/components/esp_driver_dma/test_apps/dma/main/test_gdma.c +++ b/components/esp_driver_dma/test_apps/dma/main/test_gdma.c @@ -20,11 +20,9 @@ #include "hal/gdma_ll.h" #include "hal/cache_ll.h" #include "hal/cache_hal.h" -#include "hal/efuse_hal.h" #include "esp_cache.h" #include "esp_memory_utils.h" #include "gdma_test_utils.h" -#include "esp_efuse.h" #include "esp_macros.h" TEST_CASE("GDMA channel allocation", "[GDMA]") @@ -275,7 +273,7 @@ static void test_gdma_m2m_transaction(gdma_channel_handle_t tx_chan, gdma_channe TEST_ASSERT_NOT_NULL(done_sem); TEST_ESP_OK(gdma_register_rx_event_callbacks(rx_chan, &rx_cbs, done_sem)); - if (esp_efuse_is_flash_encryption_enabled()) { + if (gdma_test_mspi_strict_alignment_required()) { dma_link_in_ext_mem = false; } @@ -285,7 +283,7 @@ static void test_gdma_m2m_transaction(gdma_channel_handle_t tx_chan, gdma_channe 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)); + TEST_ESP_OK(gdma_get_channel_alignment_constraints(tx_chan, &int_mem_alignment, &ext_mem_alignment, NULL)); // 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); @@ -352,6 +350,9 @@ static void test_gdma_m2m_transaction(gdma_channel_handle_t tx_chan, gdma_channe .buffer = dst_data, .buffer_alignment = sram_alignment, // RX buffer should be aligned to the cache line size, because we will do cache invalidate later .length = 256, + .flags = { + .check_size_align = gdma_is_size_alignment_required(rx_chan), + }, }; TEST_ESP_OK(gdma_link_mount_buffers(rx_link_list, 0, &rx_buf_mount_config, 1, NULL)); @@ -424,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)); @@ -500,6 +501,7 @@ static void test_gdma_m2m_desc_empty_event(gdma_channel_handle_t tx_chan, gdma_c .length = 64, .flags = { .mark_final = GDMA_FINAL_LINK_TO_NULL, + .check_size_align = gdma_is_size_alignment_required(rx_chan), }, }; TEST_ESP_OK(gdma_link_mount_buffers(rx_link_list, 0, &rx_buf_mount_config, 1, NULL)); @@ -569,8 +571,18 @@ 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)); + TEST_ESP_OK(gdma_get_channel_alignment_constraints(rx_chan, &rx_mem_alignment, NULL, NULL)); // prepare the source data for (int i = 0; i < data_length; i++) { @@ -602,6 +614,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.check_size_align = gdma_is_size_alignment_required(rx_chan); } TEST_ESP_OK(gdma_link_mount_buffers(rx_link_list, 0, rx_aligned_buf_mount_config, 3, NULL)); @@ -636,8 +649,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); @@ -764,6 +777,7 @@ TEST_CASE("GDMA M2M Unaligned RX Buffer Test", "[GDMA][M2M]") .length = COPY_SIZE, .flags = { .mark_final = GDMA_FINAL_LINK_TO_NULL, // using singly list, so terminate the link here + .check_size_align = gdma_is_size_alignment_required(rx_chan), } }; TEST_ESP_OK(gdma_link_mount_buffers(rx_link_list, 0, &rx_buf_mount_config, 1, NULL)); @@ -845,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)); diff --git a/components/esp_driver_i3c/i3c_master.c b/components/esp_driver_i3c/i3c_master.c index 45d260db564..f86e4c650af 100644 --- a/components/esp_driver_i3c/i3c_master.c +++ b/components/esp_driver_i3c/i3c_master.c @@ -370,7 +370,7 @@ static esp_err_t i3c_master_init_dma(i3c_master_bus_t *i3c_master_handle, const // create DMA link list size_t int_mem_align = 0; - gdma_get_alignment_constraints(i3c_master_handle->dma_tx_chan, &int_mem_align, NULL); + gdma_get_channel_alignment_constraints(i3c_master_handle->dma_tx_chan, &int_mem_align, NULL, NULL); i3c_master_handle->dma_buffer_alignment = I3C_ALIGN_UP(int_mem_align, 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 = { @@ -581,6 +581,7 @@ static esp_err_t do_dma_transaction_handler(i3c_master_bus_handle_t bus_handle, .flags = { .mark_eof = true, .mark_final = GDMA_FINAL_LINK_TO_NULL, + .check_size_align = gdma_is_size_alignment_required(bus_handle->dma_rx_chan), } }; diff --git a/components/esp_driver_jpeg/jpeg_common.c b/components/esp_driver_jpeg/jpeg_common.c index 6efd77d63dd..ef2b6ee43f8 100644 --- a/components/esp_driver_jpeg/jpeg_common.c +++ b/components/esp_driver_jpeg/jpeg_common.c @@ -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; -} diff --git a/components/esp_driver_jpeg/jpeg_decode.c b/components/esp_driver_jpeg/jpeg_decode.c index 58b95ed2786..8cd7d62d067 100644 --- a/components/esp_driver_jpeg/jpeg_decode.c +++ b/components/esp_driver_jpeg/jpeg_decode.c @@ -289,8 +289,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; diff --git a/components/esp_driver_jpeg/jpeg_encode.c b/components/esp_driver_jpeg/jpeg_encode.c index 4d0a624b6bd..98d9e5982e0 100644 --- a/components/esp_driver_jpeg/jpeg_encode.c +++ b/components/esp_driver_jpeg/jpeg_encode.c @@ -177,7 +177,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; diff --git a/components/esp_driver_jpeg/jpeg_private.h b/components/esp_driver_jpeg/jpeg_private.h index 1a03c53f4ab..373e6f9928c 100644 --- a/components/esp_driver_jpeg/jpeg_private.h +++ b/components/esp_driver_jpeg/jpeg_private.h @@ -276,18 +276,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 * diff --git a/components/esp_driver_parlio/src/parlio_rx.c b/components/esp_driver_parlio/src/parlio_rx.c index 7ff7df39635..aee93bae195 100644 --- a/components/esp_driver_parlio/src/parlio_rx.c +++ b/components/esp_driver_parlio/src/parlio_rx.c @@ -145,6 +145,7 @@ size_t parlio_rx_mount_transaction_buffer(parlio_rx_unit_handle_t rx_unit, parli rx_unit->node_num = required_node_num; gdma_buffer_mount_config_t mount_config[required_node_num] = {}; + bool size_alignment_required = gdma_is_size_alignment_required(rx_unit->dma_chan); /* Mount head buffer */ if (head_node_num) { mount_config[0].buffer = trans->aligned_payload.buf.head.aligned_buffer; @@ -153,6 +154,7 @@ size_t parlio_rx_mount_transaction_buffer(parlio_rx_unit_handle_t rx_unit, parli 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; + mount_config[0].flags.check_size_align = size_alignment_required; } /* Mount body buffer */ size_t mount_size = 0; @@ -172,6 +174,7 @@ size_t parlio_rx_mount_transaction_buffer(parlio_rx_unit_handle_t rx_unit, parli 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; + mount_config[i].flags.check_size_align = size_alignment_required; offset += mount_size; rest_size -= mount_size; } @@ -181,6 +184,7 @@ size_t parlio_rx_mount_transaction_buffer(parlio_rx_unit_handle_t rx_unit, parli 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; + mount_config[required_node_num - 1].flags.check_size_align = size_alignment_required; } /* 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; @@ -470,7 +474,7 @@ 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"); + ESP_RETURN_ON_ERROR(gdma_get_channel_alignment_constraints(rx_unit->dma_chan, &rx_unit->int_mem_align, &rx_unit->ext_mem_align, NULL), TAG, "get alignment constraints failed"); #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; @@ -982,6 +986,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); } @@ -1049,6 +1057,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); } @@ -1068,6 +1080,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); @@ -1075,7 +1088,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; diff --git a/components/esp_driver_parlio/src/parlio_tx.c b/components/esp_driver_parlio/src/parlio_tx.c index 74eaa8382ff..8f572e3108f 100644 --- a/components/esp_driver_parlio/src/parlio_tx.c +++ b/components/esp_driver_parlio/src/parlio_tx.c @@ -163,7 +163,7 @@ 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_get_channel_alignment_constraints(tx_unit->dma_chan, &tx_unit->int_mem_align, &tx_unit->ext_mem_align, NULL); // create DMA link list size_t buffer_alignment = MAX(tx_unit->int_mem_align, tx_unit->ext_mem_align); @@ -463,7 +463,8 @@ 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 = 0; + 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 +702,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); diff --git a/components/esp_driver_ppa/src/ppa_core.c b/components/esp_driver_ppa/src/ppa_core.c index ec6f1e82cc7..43843f4bc53 100644 --- a/components/esp_driver_ppa/src/ppa_core.c +++ b/components/esp_driver_ppa/src/ppa_core.c @@ -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 = { @@ -620,28 +616,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; } } diff --git a/components/esp_driver_rmt/src/rmt_private.h b/components/esp_driver_rmt/src/rmt_private.h index 1332d5aebcf..dea1c4d0c47 100644 --- a/components/esp_driver_rmt/src/rmt_private.h +++ b/components/esp_driver_rmt/src/rmt_private.h @@ -230,8 +230,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 }; diff --git a/components/esp_driver_rmt/src/rmt_rx.c b/components/esp_driver_rmt/src/rmt_rx.c index e93bc4284ea..705003cc249 100644 --- a/components/esp_driver_rmt/src/rmt_rx.c +++ b/components/esp_driver_rmt/src/rmt_rx.c @@ -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 */ @@ -33,6 +33,7 @@ static inline void rmt_rx_mount_dma_buffer(rmt_rx_channel_t *rx_chan, const void .buffer_alignment = mem_alignment, .flags = { .mark_final = GDMA_FINAL_LINK_TO_DEFAULT, + .check_size_align = gdma_is_size_alignment_required(rx_chan->base.dma_chan), } }; } @@ -53,8 +54,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 +62,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 + size_t dma_int_mem_alignment = 0, dma_ext_mem_alignment = 0; + gdma_get_channel_alignment_constraints(rx_channel->base.dma_chan, &dma_int_mem_alignment, &dma_ext_mem_alignment, NULL); + size_t buffer_alignment = MAX(dma_int_mem_alignment, dma_ext_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 +360,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 diff --git a/components/esp_driver_rmt/src/rmt_tx.c b/components/esp_driver_rmt/src/rmt_tx.c index 37bccd0ce6c..240783761f9 100644 --- a/components/esp_driver_rmt/src/rmt_tx.c +++ b/components/esp_driver_rmt/src/rmt_tx.c @@ -55,7 +55,7 @@ static esp_err_t rmt_tx_init_dma_link(rmt_tx_channel_t *tx_channel, const rmt_tx size_t int_alignment = 0; // 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, &int_alignment, NULL, NULL); // 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) diff --git a/components/esp_driver_spi/src/gpspi/spi_common.c b/components/esp_driver_spi/src/gpspi/spi_common.c index e75f36d9c25..551b6f53443 100644 --- a/components/esp_driver_spi/src/gpspi/spi_common.c +++ b/components/esp_driver_spi/src/gpspi/spi_common.c @@ -14,6 +14,7 @@ #include "esp_cache.h" #include "esp_heap_caps.h" #include "esp_memory_utils.h" +#include "esp_macros.h" #include "driver/spi_master.h" #include "driver/gpio.h" #include "esp_private/gpio.h" @@ -22,6 +23,7 @@ #include "esp_private/spi_common_internal.h" #include "esp_private/spi_share_hw_ctrl.h" #include "esp_private/esp_cache_private.h" +#include "esp_private/esp_mspi_align.h" #include "esp_private/esp_dma_utils.h" #include "esp_private/gdma_link.h" #include "esp_private/sleep_retention.h" @@ -321,8 +323,8 @@ 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_get_channel_alignment_constraints(dma_ctx->tx_dma_chan, &dma_ctx->dma_align_tx_int, &dma_ctx->dma_align_tx_ext, NULL); + gdma_get_channel_alignment_constraints(dma_ctx->rx_dma_chan, &dma_ctx->dma_align_rx_int, &dma_ctx->dma_align_rx_ext, NULL); } return ret; } @@ -409,12 +411,23 @@ cleanup: void SPI_COMMON_ISR_ATTR spicommon_dma_desc_setup_link(const spi_dma_ctx_t *dma_ctx, int offset, const void *data, int len, bool is_rx) { - size_t buffer_alignment; - if (esp_ptr_internal(data)) { - buffer_alignment = is_rx ? dma_ctx->dma_align_rx_int : dma_ctx->dma_align_tx_int; + size_t buffer_alignment = 0; +#if SOC_GDMA_SUPPORTED + gdma_channel_handle_t dma_chan = is_rx ? dma_ctx->rx_dma_chan : dma_ctx->tx_dma_chan; + buffer_alignment = gdma_get_buffer_alignment_constraint(dma_chan, data); +#else + bool is_ptr_ext = esp_ptr_external_ram(data); + if (is_rx) { + buffer_alignment = is_ptr_ext ? dma_ctx->dma_align_rx_ext : dma_ctx->dma_align_rx_int; } else { - buffer_alignment = is_rx ? dma_ctx->dma_align_rx_ext : dma_ctx->dma_align_tx_ext; + buffer_alignment = is_ptr_ext ? dma_ctx->dma_align_tx_ext : dma_ctx->dma_align_tx_int; + } + if (is_ptr_ext || esp_ptr_in_drom(data)) { + size_t mspi_alignment = esp_mspi_get_alignment(data); + buffer_alignment = MAX(buffer_alignment, mspi_alignment); + } +#endif gdma_buffer_mount_config_t mount_config = { .buffer = (void *)data, diff --git a/components/esp_driver_uart/src/uhci.c b/components/esp_driver_uart/src/uhci.c index a27374d636b..4ae23b59e51 100644 --- a/components/esp_driver_uart/src/uhci.c +++ b/components/esp_driver_uart/src/uhci.c @@ -6,6 +6,7 @@ #include #include +#include #if CONFIG_UHCI_ENABLE_DEBUG_LOG // The local log level must be defined before including esp_log.h // Set the maximum log level for this source file @@ -13,6 +14,7 @@ #endif #include "sdkconfig.h" #include "esp_attr.h" +#include "esp_macros.h" #include "esp_log.h" #include "esp_check.h" #include "esp_macros.h" @@ -26,13 +28,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 +212,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); + size_t tx_dma_int_mem_alignment = 0, tx_dma_ext_mem_alignment = 0; + gdma_get_channel_alignment_constraints(uhci_ctrl->tx_dir.dma_chan, &tx_dma_int_mem_alignment, &tx_dma_ext_mem_alignment, NULL); + size_t buffer_alignment = MAX(tx_dma_int_mem_alignment, tx_dma_ext_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 +237,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); + size_t rx_dma_int_mem_alignment = 0, rx_dma_ext_mem_alignment = 0; + gdma_get_channel_alignment_constraints(uhci_ctrl->rx_dir.dma_chan, &rx_dma_int_mem_alignment, &rx_dma_ext_mem_alignment, NULL); + buffer_alignment = MAX(rx_dma_int_mem_alignment, rx_dma_ext_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"); @@ -284,10 +285,11 @@ static void uhci_do_transmit(uhci_controller_handle_t uhci_ctrl, uhci_transactio uhci_ctrl->tx_dir.cur_trans = trans; size_t buf_count = trans->buf_info_count; gdma_buffer_mount_config_t *mount_configs = uhci_ctrl->tx_dir.mount_configs; + size_t buffer_alignment = 0; 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; + 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 +326,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,13 +368,15 @@ 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; + size_t buffer_alignment = 0; + 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, .length = uhci_ctrl->rx_dir.buffer_size_per_desc_node[i], .flags = { .mark_final = GDMA_FINAL_LINK_TO_DEFAULT, + .check_size_align = gdma_is_size_alignment_required(uhci_ctrl->rx_dir.dma_chan), } }; ESP_DRAM_LOGD(TAG, "The DMA node %d has %d byte", i, uhci_ctrl->rx_dir.buffer_size_per_desc_node[i]); @@ -382,8 +388,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 +470,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"); } @@ -669,9 +661,6 @@ esp_err_t uhci_new_controller(const uhci_controller_config_t *config, uhci_contr uhci_ll_rx_set_packet_threshold(uhci_ctrl->hal.dev, config->max_packet_receive); } - 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 diff --git a/components/esp_driver_uart/src/uhci_private.h b/components/esp_driver_uart/src/uhci_private.h index b61be21ff12..4ef4bbce7a6 100644 --- a/components/esp_driver_uart/src/uhci_private.h +++ b/components/esp_driver_uart/src/uhci_private.h @@ -22,8 +22,6 @@ extern "C" { typedef struct uhci_controller_t uhci_controller_t; -#define UHCI_MAX(a, b) (((a)>(b))?(a):(b)) - #define UHCI_PM_LOCK_NAME_LEN_MAX 16 #if CONFIG_UHCI_ISR_HANDLER_IN_IRAM @@ -69,8 +67,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) @@ -86,8 +82,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 @@ -99,8 +93,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 diff --git a/components/esp_hw_support/CMakeLists.txt b/components/esp_hw_support/CMakeLists.txt index 8f31692ba13..fb96b522ace 100644 --- a/components/esp_hw_support/CMakeLists.txt +++ b/components/esp_hw_support/CMakeLists.txt @@ -16,6 +16,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}") @@ -52,7 +53,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" diff --git a/components/esp_hw_support/heap_align_hw.c b/components/esp_hw_support/heap_align_hw.c index 4b97c7e2cfe..a7bce89752b 100644 --- a/components/esp_hw_support/heap_align_hw.c +++ b/components/esp_hw_support/heap_align_hw.c @@ -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 diff --git a/components/esp_hw_support/mspi/esp_mspi_align/esp_mspi_align.c b/components/esp_hw_support/mspi/esp_mspi_align/esp_mspi_align.c new file mode 100644 index 00000000000..c12617cde09 --- /dev/null +++ b/components/esp_hw_support/mspi/esp_mspi_align/esp_mspi_align.c @@ -0,0 +1,59 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#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 (esp_efuse_is_flash_encryption_enabled() && (generic_query || is_drom || is_psram_enc)) { + 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); +} diff --git a/components/esp_hw_support/mspi/esp_mspi_align/include/esp_private/esp_mspi_align.h b/components/esp_hw_support/mspi/esp_mspi_align/include/esp_private/esp_mspi_align.h new file mode 100644 index 00000000000..79960a1710c --- /dev/null +++ b/components/esp_hw_support/mspi/esp_mspi_align/include/esp_private/esp_mspi_align.h @@ -0,0 +1,46 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include +#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 diff --git a/components/esp_hw_support/mspi/linker.lf b/components/esp_hw_support/mspi/linker.lf index 69d562586a4..b64138f4b4d 100644 --- a/components/esp_hw_support/mspi/linker.lf +++ b/components/esp_hw_support/mspi/linker.lf @@ -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: diff --git a/components/esp_lcd/dsi/esp_lcd_panel_dpi.c b/components/esp_lcd/dsi/esp_lcd_panel_dpi.c index 310237b10df..f1d236a221b 100644 --- a/components/esp_lcd/dsi/esp_lcd_panel_dpi.c +++ b/components/esp_lcd/dsi/esp_lcd_panel_dpi.c @@ -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"); } diff --git a/components/esp_lcd/i80/esp_lcd_panel_io_i80.c b/components/esp_lcd/i80/esp_lcd_panel_io_i80.c index c9bf64a61a8..33aca188a07 100644 --- a/components/esp_lcd/i80/esp_lcd_panel_io_i80.c +++ b/components/esp_lcd/i80/esp_lcd_panel_io_i80.c @@ -543,11 +543,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 = { @@ -581,15 +582,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); @@ -705,7 +697,7 @@ 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_get_channel_alignment_constraints(bus->dma_chan, &bus->int_mem_align, &bus->ext_mem_align, NULL); 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); @@ -902,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, diff --git a/components/esp_lcd/rgb/esp_lcd_panel_rgb.c b/components/esp_lcd/rgb/esp_lcd_panel_rgb.c index 118377eb672..19197ac8493 100644 --- a/components/esp_lcd/rgb/esp_lcd_panel_rgb.c +++ b/components/esp_lcd/rgb/esp_lcd_panel_rgb.c @@ -186,10 +186,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++) { @@ -200,16 +196,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 { @@ -219,15 +205,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]); @@ -246,7 +231,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; @@ -1253,7 +1238,7 @@ 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_get_channel_alignment_constraints(rgb_panel->dma_chan, &rgb_panel->int_mem_align, &rgb_panel->ext_mem_align, NULL); // register DMA event callbacks gdma_tx_event_callbacks_t cbs = { diff --git a/components/esp_psram/include/esp_psram.h b/components/esp_psram/include/esp_psram.h index 562e851d1e9..e71bc78394e 100644 --- a/components/esp_psram/include/esp_psram.h +++ b/components/esp_psram/include/esp_psram.h @@ -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); diff --git a/components/mbedtls/CMakeLists.txt b/components/mbedtls/CMakeLists.txt index 01aef49d3c2..68990298c42 100644 --- a/components/mbedtls/CMakeLists.txt +++ b/components/mbedtls/CMakeLists.txt @@ -370,6 +370,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) diff --git a/components/mbedtls/port/aes/dma/esp_aes_dma_core.c b/components/mbedtls/port/aes/dma/esp_aes_dma_core.c index 0277b6ebd4c..349d745c568 100644 --- a/components/mbedtls/port/aes/dma/esp_aes_dma_core.c +++ b/components/mbedtls/port/aes/dma/esp_aes_dma_core.c @@ -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" @@ -246,22 +247,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); @@ -277,7 +289,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; @@ -353,9 +366,9 @@ static inline void dma_desc_append(crypto_dma_desc_t **head, crypto_dma_desc_t * #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) @@ -424,6 +437,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; @@ -454,7 +475,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; @@ -463,7 +485,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; @@ -485,7 +508,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; @@ -561,19 +585,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)) { @@ -828,14 +850,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"); @@ -1055,20 +1077,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) { diff --git a/components/mbedtls/port/sha/core/sha.c b/components/mbedtls/port/sha/core/sha.c index 8544ddddb80..f971ca67507 100644 --- a/components/mbedtls/port/sha/core/sha.c +++ b/components/mbedtls/port/sha/core/sha.c @@ -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) { diff --git a/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in index d7912709516..9023a83634e 100644 --- a/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in @@ -527,10 +527,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 diff --git a/components/soc/esp32c5/include/soc/soc_caps.h b/components/soc/esp32c5/include/soc/soc_caps.h index 9c87c937a91..9b7f1644588 100644 --- a/components/soc/esp32c5/include/soc/soc_caps.h +++ b/components/soc/esp32c5/include/soc/soc_caps.h @@ -201,7 +201,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 diff --git a/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in index 4e9addb6b85..0c5a1c339ed 100644 --- a/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in @@ -399,10 +399,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 diff --git a/components/soc/esp32c61/include/soc/soc_caps.h b/components/soc/esp32c61/include/soc/soc_caps.h index 052421725a2..9c9797f7c6b 100644 --- a/components/soc/esp32c61/include/soc/soc_caps.h +++ b/components/soc/esp32c61/include/soc/soc_caps.h @@ -153,7 +153,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 diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index 1f58eb3ff43..e1b1bb4ca81 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -643,10 +643,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 diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index ff426d0dd10..27a5cf988e9 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -231,7 +231,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 diff --git a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in index dce605a45ec..e28071a4913 100644 --- a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in @@ -595,10 +595,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 diff --git a/components/soc/esp32s31/include/soc/soc_caps.h b/components/soc/esp32s31/include/soc/soc_caps.h index 29466ea4116..e99bd8e4f7a 100644 --- a/components/soc/esp32s31/include/soc/soc_caps.h +++ b/components/soc/esp32s31/include/soc/soc_caps.h @@ -204,7 +204,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 diff --git a/examples/peripherals/lcd/mipi_dsi/main/mipi_dsi_lcd_example_main.c b/examples/peripherals/lcd/mipi_dsi/main/mipi_dsi_lcd_example_main.c index 33d49a0b1f6..5a130982a26 100644 --- a/examples/peripherals/lcd/mipi_dsi/main/mipi_dsi_lcd_example_main.c +++ b/examples/peripherals/lcd/mipi_dsi/main/mipi_dsi_lcd_example_main.c @@ -93,9 +93,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 = ESP_ALIGN_DOWN(area->x1, 16); - area->x2 = ESP_ALIGN_UP(area->x2, 16) - 1; + area->x2 = ESP_ALIGN_UP(area->x2 + 1, 16) - 1; } #endif @@ -106,7 +109,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); } @@ -296,23 +299,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); @@ -320,9 +314,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); }