From ef4387144eebaf7b0e862ddc1d942785f3a5541c Mon Sep 17 00:00:00 2001 From: Chen Jichang Date: Mon, 13 Jul 2026 20:39:36 +0800 Subject: [PATCH 1/3] test(aes): add aes psram ecc test --- .../mbedtls/port/aes/dma/esp_aes_dma_core.c | 4 - .../mbedtls/test_apps/.build-test-rules.yml | 1 + .../test_apps/mbedtls_ut/main/test_psa_aes.c | 99 +++++++++++++++++++ .../test_apps/mbedtls_ut/pytest_mbedtls_ut.py | 22 +++++ .../mbedtls_ut/sdkconfig.ci.psram_ecc | 5 + 5 files changed, 127 insertions(+), 4 deletions(-) create mode 100644 components/mbedtls/test_apps/mbedtls_ut/sdkconfig.ci.psram_ecc 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 638cb90128c..f2fee0a16f5 100644 --- a/components/mbedtls/port/aes/dma/esp_aes_dma_core.c +++ b/components/mbedtls/port/aes/dma/esp_aes_dma_core.c @@ -43,10 +43,6 @@ #include "aes/esp_aes_gcm.h" #endif -#ifdef SOC_GDMA_EXT_MEM_ENC_ALIGNMENT -#include "hal/efuse_hal.h" -#endif /* SOC_GDMA_EXT_MEM_ENC_ALIGNMENT */ - /* Max size of each chunk to process when output buffer is in unaligned external ram must be a multiple of block size */ diff --git a/components/mbedtls/test_apps/.build-test-rules.yml b/components/mbedtls/test_apps/.build-test-rules.yml index 9eceef9148a..09991c37754 100644 --- a/components/mbedtls/test_apps/.build-test-rules.yml +++ b/components/mbedtls/test_apps/.build-test-rules.yml @@ -5,6 +5,7 @@ components/mbedtls/test_apps/mbedtls_ut: - if: CONFIG_NAME == "aes_no_hw" and SOC_AES_SUPPORTED != 1 - if: CONFIG_NAME == "psram" and SOC_SPIRAM_SUPPORTED != 1 - if: CONFIG_NAME == "psram_all_ext" and SOC_SPIRAM_SUPPORTED != 1 + - if: CONFIG_NAME == "psram_ecc" and (SOC_SPIRAM_SUPPORTED != 1 or SOC_PSRAM_DMA_CAPABLE != 1 or SOC_AES_SUPPORT_DMA != 1 or IDF_TARGET == "esp32s2") - if: CONFIG_NAME == "psram_all_ext_flash_enc" and SOC_SPIRAM_SUPPORTED != 1 - if: CONFIG_NAME == "psram_all_ext_flash_enc_f4r8" and IDF_TARGET != "esp32s3" - if: CONFIG_NAME == "ecdsa_sign" and SOC_ECDSA_SUPPORTED != 1 diff --git a/components/mbedtls/test_apps/mbedtls_ut/main/test_psa_aes.c b/components/mbedtls/test_apps/mbedtls_ut/main/test_psa_aes.c index e472b0f5c76..02a23d54bb5 100644 --- a/components/mbedtls/test_apps/mbedtls_ut/main/test_psa_aes.c +++ b/components/mbedtls/test_apps/mbedtls_ut/main/test_psa_aes.c @@ -1846,6 +1846,105 @@ TEST_CASE("mbedtls AES internal mem alignment tests", "[aes]") #ifdef CONFIG_SPIRAM_USE_MALLOC +#if CONFIG_SPIRAM_ECC_ENABLE && SOC_AES_SUPPORT_DMA +#define TEST_AES_PAYLOAD_LEN 53 + +struct aes_payload_sim_hdr { + uint8_t type; + uint8_t fc; + uint8_t seq; + uint8_t len; + uint8_t data[]; +} __attribute__((packed)); + +static void aes_psram_ecc_cfb128_inplace_test(void) +{ + const size_t pkt_len = sizeof(struct aes_payload_sim_hdr) + TEST_AES_PAYLOAD_LEN; + struct aes_payload_sim_hdr *pkt = heap_caps_aligned_alloc(16, pkt_len, PSRAM_DMA_CAPS); + uint8_t *backup = heap_caps_malloc(TEST_AES_PAYLOAD_LEN, INTERNAL_DMA_CAPS); + uint8_t key[16]; + uint8_t iv[16]; + psa_key_id_t key_id; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_status_t status; + size_t output_len; + size_t total_len; + + TEST_ASSERT_NOT_NULL(pkt); + TEST_ASSERT_NOT_NULL(backup); + TEST_ASSERT_TRUE(esp_ptr_external_ram(pkt)); + TEST_ASSERT_EQUAL_UINT32(0, (uintptr_t)pkt & 0x0F); + TEST_ASSERT_EQUAL_UINT32(sizeof(struct aes_payload_sim_hdr) & 0x0F, (uintptr_t)pkt->data & 0x0F); + + pkt->type = 0x13; + pkt->fc = 0x04; + pkt->seq = 1; + pkt->len = TEST_AES_PAYLOAD_LEN; + for (size_t i = 0; i < TEST_AES_PAYLOAD_LEN; i++) { + pkt->data[i] = 0x3C + (uint8_t)(i & 0x0F); + } + memcpy(backup, pkt->data, TEST_AES_PAYLOAD_LEN); + + memset(key, 0x5A, sizeof(key)); + memset(iv, 0xA5, sizeof(iv)); + iv[0] = 3; + + status = psa_crypto_init(); + TEST_ASSERT_EQUAL(PSA_SUCCESS, status); + + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT); + psa_set_key_algorithm(&attributes, PSA_ALG_CFB); + psa_set_key_type(&attributes, PSA_KEY_TYPE_AES); + psa_set_key_bits(&attributes, 128); + + status = psa_import_key(&attributes, key, sizeof(key), &key_id); + TEST_ASSERT_EQUAL(PSA_SUCCESS, status); + + psa_cipher_operation_t operation = PSA_CIPHER_OPERATION_INIT; + status = psa_cipher_encrypt_setup(&operation, key_id, PSA_ALG_CFB); + TEST_ASSERT_EQUAL(PSA_SUCCESS, status); + status = psa_cipher_set_iv(&operation, iv, sizeof(iv)); + TEST_ASSERT_EQUAL(PSA_SUCCESS, status); + total_len = 0; + status = psa_cipher_update(&operation, pkt->data, TEST_AES_PAYLOAD_LEN, pkt->data, TEST_AES_PAYLOAD_LEN, &output_len); + TEST_ASSERT_EQUAL(PSA_SUCCESS, status); + total_len += output_len; + status = psa_cipher_finish(&operation, pkt->data + output_len, TEST_AES_PAYLOAD_LEN - output_len, &output_len); + TEST_ASSERT_EQUAL(PSA_SUCCESS, status); + total_len += output_len; + TEST_ASSERT_EQUAL(TEST_AES_PAYLOAD_LEN, total_len); + + memset(iv, 0xA5, sizeof(iv)); + iv[0] = 3; + + psa_cipher_abort(&operation); + operation = (psa_cipher_operation_t)PSA_CIPHER_OPERATION_INIT; + status = psa_cipher_decrypt_setup(&operation, key_id, PSA_ALG_CFB); + TEST_ASSERT_EQUAL(PSA_SUCCESS, status); + status = psa_cipher_set_iv(&operation, iv, sizeof(iv)); + TEST_ASSERT_EQUAL(PSA_SUCCESS, status); + total_len = 0; + status = psa_cipher_update(&operation, pkt->data, TEST_AES_PAYLOAD_LEN, pkt->data, TEST_AES_PAYLOAD_LEN, &output_len); + TEST_ASSERT_EQUAL(PSA_SUCCESS, status); + total_len += output_len; + status = psa_cipher_finish(&operation, pkt->data + output_len, TEST_AES_PAYLOAD_LEN - output_len, &output_len); + TEST_ASSERT_EQUAL(PSA_SUCCESS, status); + total_len += output_len; + TEST_ASSERT_EQUAL(TEST_AES_PAYLOAD_LEN, total_len); + + TEST_ASSERT_EQUAL_MEMORY(backup, pkt->data, TEST_AES_PAYLOAD_LEN); + psa_cipher_abort(&operation); + psa_destroy_key(key_id); + free(backup); + free(pkt); +} + +TEST_CASE("mbedtls AES PSRAM ECC CFB128 in-place test", "[aes][psram_ecc_dma]") +{ + aes_psram_ecc_cfb128_inplace_test(); +} +#endif // CONFIG_SPIRAM_ECC_ENABLE && SOC_AES_SUPPORT_DMA + void aes_psram_one_buf_ctr_test(void) { psa_key_id_t key_id; diff --git a/components/mbedtls/test_apps/mbedtls_ut/pytest_mbedtls_ut.py b/components/mbedtls/test_apps/mbedtls_ut/pytest_mbedtls_ut.py index dbdb9bd2303..611c1bcfa3d 100644 --- a/components/mbedtls/test_apps/mbedtls_ut/pytest_mbedtls_ut.py +++ b/components/mbedtls/test_apps/mbedtls_ut/pytest_mbedtls_ut.py @@ -5,6 +5,15 @@ from pytest_embedded import Dut from pytest_embedded_idf.utils import idf_parametrize from pytest_embedded_idf.utils import soc_filtered_targets +# esp32s2 has PSRAM DMA and AES DMA, but no PSRAM ECC Kconfig option. +PSRAM_ECC_DMA_TARGETS = [ + target + for target in soc_filtered_targets( + 'SOC_SPIRAM_SUPPORTED == 1 and SOC_PSRAM_DMA_CAPABLE == 1 and SOC_AES_SUPPORT_DMA == 1' + ) + if target != 'esp32s2' +] + @pytest.mark.generic @pytest.mark.temp_skip_ci(targets=['esp32h4'], reason='can not pass') # TODO: IDF-15675 @@ -53,6 +62,19 @@ def test_mbedtls_psram(dut: Dut) -> None: dut.run_all_single_board_cases(timeout=180) +@pytest.mark.generic +@pytest.mark.parametrize( + 'config', + [ + 'psram_ecc', + ], + indirect=True, +) +@idf_parametrize('target', PSRAM_ECC_DMA_TARGETS, indirect=['target']) +def test_mbedtls_psram_ecc(dut: Dut) -> None: + dut.run_all_single_board_cases(group='aes', timeout=180) + + @pytest.mark.flash_encryption_psram @pytest.mark.parametrize( 'config', diff --git a/components/mbedtls/test_apps/mbedtls_ut/sdkconfig.ci.psram_ecc b/components/mbedtls/test_apps/mbedtls_ut/sdkconfig.ci.psram_ecc new file mode 100644 index 00000000000..a9b5d4c749c --- /dev/null +++ b/components/mbedtls/test_apps/mbedtls_ut/sdkconfig.ci.psram_ecc @@ -0,0 +1,5 @@ +CONFIG_SPIRAM=y +CONFIG_SPIRAM_ECC_ENABLE=y +CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=0 +CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM=y +CONFIG_ESP_INT_WDT_TIMEOUT_MS=800 From 305d434bda5b35237f0d674d91f5bf8cc2891274 Mon Sep 17 00:00:00 2001 From: Chen Jichang Date: Mon, 10 Aug 2026 20:35:56 +0800 Subject: [PATCH 2/3] 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 | 27 +++- .../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 | 40 +----- 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 | 4 +- components/esp_driver_uart/src/uhci.c | 48 +++---- components/esp_driver_uart/src/uhci_private.h | 7 - 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, 668 insertions(+), 420 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 4164d85fb8b..05b02cff3ce 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 */ @@ -13,6 +13,7 @@ #include "esp_memory_utils.h" #define ALIGN_UP_BY(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) +#define ALIGN_DOWN_BY(num, align) ((num) & ~((align) - 1)) #if defined(SOC_GDMA_TRIG_PERIPH_CAM0_BUS) && (SOC_GDMA_TRIG_PERIPH_CAM0_BUS == SOC_GDMA_BUS_AHB) #define DVP_GDMA_NEW_CHANNEL gdma_new_ahb_channel @@ -39,12 +40,12 @@ static const char *TAG = "dvp_gdma"; * - ESP_OK on success * - Others if failed */ -static void IRAM_ATTR esp_cam_ctlr_dvp_config_dma_desc(esp_cam_ctlr_dvp_dma_desc_t *desc, uint8_t *buffer, uint32_t size) +static void IRAM_ATTR esp_cam_ctlr_dvp_config_dma_desc(esp_cam_ctlr_dvp_dma_desc_t *desc, uint8_t *buffer, uint32_t size, uint32_t max_desc_size) { size_t n = 0; while (size) { - uint32_t node_size = MIN(size, ESP_CAM_CTLR_DVP_DMA_DESC_BUFFER_MAX_SIZE); + uint32_t node_size = MIN(size, max_desc_size); desc[n].dw0.size = node_size; desc[n].dw0.length = 0; @@ -105,10 +106,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 = ALIGN_DOWN_BY(desc_max_size, buffer_alignment); + } + dma->desc_count = size / desc_max_size; + if (size % desc_max_size) { dma->desc_count++; } dma->size = size; @@ -164,7 +170,14 @@ esp_err_t IRAM_ATTR esp_cam_ctlr_dvp_dma_start(esp_cam_ctlr_dvp_dma_t *dma, uint ESP_RETURN_ON_FALSE_ISR(dma, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); ESP_RETURN_ON_FALSE_ISR(dma->size >= size, ESP_ERR_INVALID_ARG, TAG, "input buffer size is out of range"); - esp_cam_ctlr_dvp_config_dma_desc(dma->desc, buffer, size); + size_t buffer_alignment = gdma_get_buffer_alignment_constraint(dma->dma_chan, buffer); + ESP_RETURN_ON_FALSE_ISR(((uintptr_t)buffer & (buffer_alignment - 1)) == 0 && (size & (buffer_alignment - 1)) == 0, + ESP_ERR_INVALID_ARG, TAG, "buffer addr or size not aligned"); + uint32_t max_desc_size = ESP_CAM_CTLR_DVP_DMA_DESC_BUFFER_MAX_SIZE; + if (buffer_alignment > 1) { + max_desc_size = ALIGN_DOWN_BY(max_desc_size, buffer_alignment); + } + esp_cam_ctlr_dvp_config_dma_desc(dma->desc, buffer, size, max_desc_size); if (esp_ptr_external_ram(dma->desc)) { esp_err_t ret = esp_cache_msync(dma->desc, dma->desc_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE); 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 2b06a499282..617ff133396 100644 --- a/components/esp_driver_dma/include/esp_private/gdma.h +++ b/components/esp_driver_dma/include/esp_private/gdma.h @@ -221,23 +221,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 ae0fc8ea77d..49241b1ca39 100644 --- a/components/esp_driver_dma/src/async_memcpy_gdma.c +++ b/components/esp_driver_dma/src/async_memcpy_gdma.c @@ -48,10 +48,6 @@ typedef struct async_memcpy_transaction_t { /// @note - Number of transaction objects are determined by the backlog parameter typedef struct { async_memcpy_context_t parent; // Parent IO interface - size_t rx_int_mem_alignment; // Required DMA buffer alignment for internal RX memory - size_t rx_ext_mem_alignment; // Required DMA buffer alignment for external RX memory - size_t tx_int_mem_alignment; // Required DMA buffer alignment for internal TX memory - size_t tx_ext_mem_alignment; // Required DMA buffer alignment for external TX memory int gdma_bus_id; // GDMA bus id (AHB, AXI, etc.) gdma_channel_handle_t tx_channel; // GDMA TX channel handle gdma_channel_handle_t rx_channel; // GDMA RX channel handle @@ -153,10 +149,6 @@ static esp_err_t esp_async_memcpy_install_gdma_template(const async_memcpy_confi ESP_GOTO_ON_ERROR(gdma_config_transfer(mcp_gdma->tx_channel, &transfer_cfg), err, TAG, "config transfer for tx channel failed"); ESP_GOTO_ON_ERROR(gdma_config_transfer(mcp_gdma->rx_channel, &transfer_cfg), err, TAG, "config transfer for rx channel failed"); - // get the buffer alignment required by the GDMA channel - gdma_get_alignment_constraints(mcp_gdma->rx_channel, &mcp_gdma->rx_int_mem_alignment, &mcp_gdma->rx_ext_mem_alignment); - gdma_get_alignment_constraints(mcp_gdma->tx_channel, &mcp_gdma->tx_int_mem_alignment, &mcp_gdma->tx_ext_mem_alignment); - // register rx eof callback gdma_rx_event_callbacks_t cbs = { .on_recv_eof = mcp_gdma_rx_eof_callback, @@ -281,30 +273,6 @@ static async_memcpy_transaction_t *try_pop_trans_from_idle_queue(async_memcpy_gd return trans; } -/// @brief Check if the address and size can meet the requirement of the DMA engine -static bool check_buffer_alignment(async_memcpy_gdma_context_t *mcp_gdma, void *src, void *dst, size_t n) -{ - bool valid = true; - - if (esp_ptr_external_ram(dst)) { - valid = valid && (((uint32_t)dst & (mcp_gdma->rx_ext_mem_alignment - 1)) == 0); - valid = valid && ((n & (mcp_gdma->rx_ext_mem_alignment - 1)) == 0); - } else { - valid = valid && (((uint32_t)dst & (mcp_gdma->rx_int_mem_alignment - 1)) == 0); - valid = valid && ((n & (mcp_gdma->rx_int_mem_alignment - 1)) == 0); - } - - if (esp_ptr_external_ram(src)) { - valid = valid && (((uint32_t)src & (mcp_gdma->tx_ext_mem_alignment - 1)) == 0); - valid = valid && ((n & (mcp_gdma->tx_ext_mem_alignment - 1)) == 0); - } else { - valid = valid && (((uint32_t)src & (mcp_gdma->tx_int_mem_alignment - 1)) == 0); - valid = valid && ((n & (mcp_gdma->tx_int_mem_alignment - 1)) == 0); - } - - return valid; -} - static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *src, size_t n, async_memcpy_isr_cb_t cb_isr, void *cb_args) { esp_err_t ret = ESP_OK; @@ -335,8 +303,6 @@ static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *s dma_link_item_alignment = GDMA_LL_AHB_DESC_ALIGNMENT; } #endif // SOC_HAS(LP_AHB_GDMA) - // alignment check - ESP_RETURN_ON_FALSE(check_buffer_alignment(mcp_gdma, src, dst, n), ESP_ERR_INVALID_ARG, TAG, "address|size not aligned: %p -> %p, sz=%zu", src, dst, n); async_memcpy_transaction_t *trans = NULL; // pick one transaction node from idle queue @@ -362,7 +328,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 - buffer_alignment = esp_ptr_internal(src) ? mcp_gdma->tx_int_mem_alignment : mcp_gdma->tx_ext_mem_alignment; + buffer_alignment = gdma_get_buffer_alignment_constraint(mcp_gdma->tx_channel, src); num_dma_nodes = esp_dma_calculate_node_count(n, buffer_alignment, MCP_DMA_DESCRIPTOR_BUFFER_MAX_SIZE); gdma_link_list_config_t tx_link_cfg = { .item_alignment = dma_link_item_alignment, @@ -395,7 +361,7 @@ static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *s } // allocate gdma RX link - buffer_alignment = esp_ptr_internal(dst) ? mcp_gdma->rx_int_mem_alignment : mcp_gdma->rx_ext_mem_alignment; + buffer_alignment = gdma_get_buffer_alignment_constraint(mcp_gdma->rx_channel, dst); num_dma_nodes = esp_dma_calculate_node_count(n, buffer_alignment, MCP_DMA_DESCRIPTOR_BUFFER_MAX_SIZE); gdma_link_list_config_t rx_link_cfg = { .item_alignment = dma_link_item_alignment, @@ -406,7 +372,6 @@ static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *s }, }; ESP_GOTO_ON_ERROR(gdma_new_link_list(&rx_link_cfg, &trans->rx_link_list), err, TAG, "failed to create RX link list"); - // if the destination buffer address is not cache line aligned, we need to split the buffer into cache line aligned ones ESP_GOTO_ON_ERROR(esp_dma_split_rx_buffer_to_cache_aligned(dst, n, &trans->rx_buf_array, &trans->stash_buffer), err, TAG, "failed to split RX buffer into aligned ones"); @@ -416,6 +381,7 @@ static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *s rx_buf_mount_config[i].buffer = trans->rx_buf_array.aligned_buffer[i].aligned_buffer; rx_buf_mount_config[i].buffer_alignment = buffer_alignment; rx_buf_mount_config[i].length = trans->rx_buf_array.aligned_buffer[i].length; + rx_buf_mount_config[i].flags.check_size_align = gdma_is_size_alignment_required(mcp_gdma->rx_channel); } gdma_link_mount_buffers(trans->rx_link_list, 0, rx_buf_mount_config, 3, NULL); 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 9836f7670a9..8319863f94c 100644 --- a/components/esp_driver_dma/src/esp_dma_utils.c +++ b/components/esp_driver_dma/src/esp_dma_utils.c @@ -45,7 +45,7 @@ esp_err_t esp_dma_split_rx_buffer_to_cache_aligned(void *rx_buffer, size_t buffe split_line_size = int_mem_cache_line_size; } bool align_required = split_line_size > 0; - ESP_EARLY_LOGV(TAG, "split_line_size:%zu", split_line_size); + ESP_EARLY_LOGV(TAG, "split_line_size:%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 4ac9fcac3f1..90e7b7d260a 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 4bd452e4d22..cbd528dd2f5 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" ESP_LOG_ATTR_TAG(TAG, "gdma-link"); @@ -78,10 +77,10 @@ esp_err_t gdma_new_link_list(const gdma_link_list_config_t *config, gdma_link_li bool items_in_ext_mem = config->flags.items_in_ext_mem; uint32_t list_items_mem_caps = MALLOC_CAP_8BIT | MALLOC_CAP_DMA; if (items_in_ext_mem) { - if (esp_efuse_is_flash_encryption_enabled()) { + if (esp_mspi_get_alignment(NULL) > 1) { items_in_ext_mem = false; list_items_mem_caps |= MALLOC_CAP_INTERNAL; - ESP_LOGW(TAG, "DMA linked list items cannot be placed in PSRAM when external memory encryption is enabled, using internal memory instead"); + ESP_LOGW(TAG, "DMA linked list items cannot be placed in PSRAM when MSPI strict alignment is required, using internal memory instead"); } else { list_items_mem_caps |= MALLOC_CAP_SPIRAM; } @@ -92,12 +91,7 @@ esp_err_t gdma_new_link_list(const gdma_link_list_config_t *config, gdma_link_li ESP_GOTO_ON_FALSE(items, ESP_ERR_NO_MEM, err, TAG, "no mem for link list items"); // do memory sync if the list items are in the cache - uint32_t data_cache_line_size = 0; - if (items_in_ext_mem) { - data_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA); - } else { - data_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA); - } + size_t data_cache_line_size = esp_cache_get_line_size_by_addr(items); if (data_cache_line_size) { // write back and then invalidate the cache, because later we will read/write the link list items by non-cached address ESP_GOTO_ON_ERROR(esp_cache_msync(items, ALIGN_UP(num_items * item_size, data_cache_line_size), @@ -192,12 +186,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 = 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 = 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; @@ -219,7 +217,6 @@ esp_err_t gdma_link_mount_buffers(gdma_link_list_handle_t list, int start_item_i if (buffer_alignment == 0) { buffer_alignment = 1; } - size_t max_buffer_mount_length = ALIGN_DOWN(GDMA_MAX_BUFFER_SIZE_PER_LINK_ITEM, buffer_alignment); // skip zero-length buffer but scrub any stale descriptor to keep ring clean; no slot consumption if (len == 0 || buf == NULL) { lli_nc = (gdma_link_list_item_t *)(list->items_nc + begin_item_idx % list_item_capacity * item_size); @@ -227,6 +224,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 = 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 7b6aec209d6..17796c1d39d 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 @@ -9,16 +9,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 71d390ba226..d8689a449ee 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 @@ -16,8 +16,6 @@ #include "freertos/semphr.h" #include "ccomp_timer.h" #include "esp_async_memcpy.h" -#include "hal/efuse_hal.h" -#include "esp_efuse.h" #include "gdma_test_utils.h" #if SOC_GDMA_SUPPORTED @@ -172,7 +170,7 @@ static void test_memory_copy_blocking(async_memcpy_handle_t driver) for (int off = 0; off < 4; off++) { test_context.buffer_size = test_buffer_size[i]; test_context.seed = i; - if (!esp_efuse_is_flash_encryption_enabled()) { + if (!gdma_test_mspi_strict_alignment_required()) { test_context.src_offset = off; test_context.dst_offset = off; } @@ -259,8 +257,8 @@ TEST_CASE("memory copy with dest address unaligned", "[async mcp]") }; [[maybe_unused]] async_memcpy_handle_t driver = NULL; - if (esp_efuse_is_flash_encryption_enabled()) { - TEST_PASS_MESSAGE("Flash encryption is enabled, skip this test"); + if (gdma_test_mspi_strict_alignment_required()) { + TEST_PASS_MESSAGE("MSPI strict alignment required (Flash Encryption / PSRAM ECC), skip this test"); } #if SOC_CP_DMA_SUPPORTED @@ -426,8 +424,8 @@ TEST_CASE("memory copy performance 40KB: PSRAM->PSRAM", "[async mcp]") #if SOC_HAS(LP_AHB_GDMA) #if GDMA_LL_GET(LP_AHB_PSRAM_CAPABLE) - if (esp_efuse_is_flash_encryption_enabled() && !GDMA_TEST_LP_AHB_BURST_PSRAM_SUPPORTED) { - TEST_IGNORE_MESSAGE("Skipping LP AHB GDMA PSRAM->PSRAM under flash encryption"); + if (gdma_test_mspi_strict_alignment_required() && !GDMA_TEST_LP_AHB_BURST_PSRAM_SUPPORTED) { + TEST_IGNORE_MESSAGE("Skipping LP AHB GDMA PSRAM->PSRAM under Flash Encryption / PSRAM ECC"); } else { printf("Testing memcpy by LP AHB GDMA\r\n"); TEST_ESP_OK(esp_async_memcpy_install_gdma_lp_ahb(&driver_config, &driver)); 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 e1c01944a55..95ff19c59e5 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 d718c3f6d3f..536792d8a51 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" #define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) #define ALIGN_DOWN(num, align) ((num) & ~((align) - 1)) @@ -277,7 +275,7 @@ static void test_gdma_m2m_transaction(gdma_channel_handle_t tx_chan, gdma_channe TEST_ASSERT_NOT_NULL(done_sem); TEST_ESP_OK(gdma_register_rx_event_callbacks(rx_chan, &rx_cbs, done_sem)); - if (esp_efuse_is_flash_encryption_enabled()) { + if (gdma_test_mspi_strict_alignment_required()) { dma_link_in_ext_mem = false; } @@ -287,7 +285,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); @@ -354,6 +352,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)); @@ -426,8 +427,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)); @@ -502,6 +503,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)); @@ -571,8 +573,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++) { @@ -604,6 +616,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)); @@ -638,8 +651,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); @@ -766,6 +779,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)); @@ -847,8 +861,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 43459200f4a..8c331fd10ba 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 7b853173411..c6ac21c4293 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 f4b9f54b36d..2ef1d6a08dc 100644 --- a/components/esp_driver_jpeg/jpeg_encode.c +++ b/components/esp_driver_jpeg/jpeg_encode.c @@ -193,7 +193,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 db51aca4cf3..4acd0ae1c01 100644 --- a/components/esp_driver_jpeg/jpeg_private.h +++ b/components/esp_driver_jpeg/jpeg_private.h @@ -265,18 +265,6 @@ esp_err_t jpeg_isr_deregister(jpeg_codec_handle_t jpeg_codec, jpeg_isr_handler_t */ esp_err_t jpeg_check_intr_priority(jpeg_codec_handle_t jpeg_codec, int intr_priority); -/** - * @brief Validate a user buffer that will be accessed by the 2D-DMA - * - * The buffer must be 16-byte aligned. When CONFIG_SPIRAM_ENC_EXEMPT is enabled, - * a PSRAM buffer must reside in the unencrypted carve-out, since the 2D-DMA - * cannot access encrypted PSRAM. Internal RAM buffers are always accepted. - * - * @param buffer Buffer pointer provided by the user - * @return true if the buffer can be used by the 2D-DMA, false otherwise - */ -bool jpeg_check_dma2d_buffer(const void *buffer); - /** * @brief Create sleep retention link * diff --git a/components/esp_driver_parlio/src/parlio_rx.c b/components/esp_driver_parlio/src/parlio_rx.c index 840414ec41f..0a25e22f23d 100644 --- a/components/esp_driver_parlio/src/parlio_rx.c +++ b/components/esp_driver_parlio/src/parlio_rx.c @@ -147,6 +147,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; @@ -155,6 +156,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; @@ -174,6 +176,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; } @@ -183,6 +186,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; @@ -472,7 +476,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; @@ -984,6 +988,10 @@ esp_err_t parlio_rx_unit_receive(parlio_rx_unit_handle_t rx_unit, ESP_RETURN_ON_FALSE(recv_cfg->delimiter, ESP_ERR_INVALID_ARG, TAG, "no delimiter specified"); ESP_RETURN_ON_FALSE(payload_size <= rx_unit->max_recv_size, ESP_ERR_INVALID_ARG, TAG, "trans length too large"); size_t alignment = rx_unit->int_mem_align; + // partial receive with indirect mount always use internal memory + if (!(recv_cfg->flags.partial_rx_en && recv_cfg->flags.indirect_mount)) { + alignment = gdma_get_buffer_alignment_constraint(rx_unit->dma_chan, payload); + } if (recv_cfg->flags.partial_rx_en) { ESP_RETURN_ON_FALSE(payload_size >= 2 * alignment, ESP_ERR_INVALID_ARG, TAG, "The payload size should greater than %"PRIu32, 2 * alignment); } @@ -1051,6 +1059,10 @@ esp_err_t parlio_rx_unit_receive_from_isr(parlio_rx_unit_handle_t rx_unit, // Can only be called from ISR PARLIO_RX_CHECK_ISR(xPortInIsrContext() == pdTRUE, ESP_ERR_INVALID_STATE); size_t alignment = rx_unit->int_mem_align; + // partial receive with indirect mount always use internal memory + if (!(recv_cfg->flags.partial_rx_en && recv_cfg->flags.indirect_mount)) { + alignment = gdma_get_buffer_alignment_constraint(rx_unit->dma_chan, payload); + } if (recv_cfg->flags.partial_rx_en) { PARLIO_RX_CHECK_ISR(payload_size >= 2 * alignment, ESP_ERR_INVALID_ARG); } @@ -1070,6 +1082,7 @@ esp_err_t parlio_rx_unit_receive_from_isr(parlio_rx_unit_handle_t rx_unit, } dma_buffer_split_array_t dma_buf_array = {0}; + /* Create the internal DMA buffer for the infinite transaction if indirect_mount is set */ if (recv_cfg->flags.partial_rx_en && recv_cfg->flags.indirect_mount) { /* The internal DMA buffer should be allocated before calling this function */ PARLIO_RX_CHECK_ISR(rx_unit->dma_buf, ESP_ERR_INVALID_STATE); @@ -1077,7 +1090,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 71070099e5d..ddd2a1c2252 100644 --- a/components/esp_driver_rmt/src/rmt_private.h +++ b/components/esp_driver_rmt/src/rmt_private.h @@ -232,8 +232,6 @@ struct rmt_rx_channel_t { void *user_data; // user context rmt_rx_trans_desc_t trans_desc; // transaction description size_t num_dma_nodes; // number of DMA nodes, determined by how big the memory block that user configures - size_t dma_int_mem_alignment; // DMA buffer alignment (both in size and address) for internal RX memory - size_t dma_ext_mem_alignment; // DMA buffer alignment (both in size and address) for external RX memory gdma_link_list_handle_t dma_link; // DMA link list handle }; diff --git a/components/esp_driver_rmt/src/rmt_rx.c b/components/esp_driver_rmt/src/rmt_rx.c index 51af2291c21..643f7a4786e 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 b0cd7683889..f765a02f053 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 86138df518b..ae6714e43aa 100644 --- a/components/esp_driver_spi/src/gpspi/spi_common.c +++ b/components/esp_driver_spi/src/gpspi/spi_common.c @@ -320,8 +320,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; } diff --git a/components/esp_driver_uart/src/uhci.c b/components/esp_driver_uart/src/uhci.c index 90d3bf63ff0..042e9c736ed 100644 --- a/components/esp_driver_uart/src/uhci.c +++ b/components/esp_driver_uart/src/uhci.c @@ -6,6 +6,7 @@ #include #include +#include #include "esp_intr_alloc.h" #if CONFIG_UHCI_ENABLE_DEBUG_LOG // The local log level must be defined before including esp_log.h @@ -26,13 +27,10 @@ #include "hal/uhci_hal.h" #include "hal/uhci_ll.h" #include "hal/dma_types.h" -#include "hal/cache_hal.h" -#include "hal/cache_ll.h" #include "esp_private/periph_ctrl.h" #include "esp_private/gdma.h" #include "esp_private/esp_dma_utils.h" #include "esp_private/gdma_link.h" -#include "esp_private/esp_cache_private.h" #include "esp_private/esp_psram_mspi.h" #include "uhci_private.h" #include "esp_memory_utils.h" @@ -213,8 +211,9 @@ static esp_err_t uhci_gdma_initialize(uhci_controller_handle_t uhci_ctrl, const gdma_apply_strategy(uhci_ctrl->tx_dir.dma_chan, &strategy_config); // create DMA link list - gdma_get_alignment_constraints(uhci_ctrl->tx_dir.dma_chan, &uhci_ctrl->tx_dir.int_mem_align, &uhci_ctrl->tx_dir.ext_mem_align); - size_t buffer_alignment = UHCI_MAX(uhci_ctrl->tx_dir.int_mem_align, uhci_ctrl->tx_dir.ext_mem_align); + 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 +236,9 @@ static esp_err_t uhci_gdma_initialize(uhci_controller_handle_t uhci_ctrl, const gdma_connect(uhci_ctrl->rx_dir.dma_chan, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UHCI, 0)); ESP_RETURN_ON_ERROR(gdma_config_transfer(uhci_ctrl->rx_dir.dma_chan, &transfer_cfg), TAG, "Config DMA rx channel transfer failed"); - gdma_get_alignment_constraints(uhci_ctrl->rx_dir.dma_chan, &uhci_ctrl->rx_dir.int_mem_align, &uhci_ctrl->rx_dir.ext_mem_align); - buffer_alignment = UHCI_MAX(uhci_ctrl->rx_dir.int_mem_align, uhci_ctrl->rx_dir.ext_mem_align); + 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 +284,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 +325,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 +367,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 +387,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 +469,7 @@ esp_err_t uhci_multi_buffer_transmit(uhci_controller_handle_t uhci_ctrl, const u total_size += write_size; - size_t alignment = 0; - size_t cache_line_size = 0; - if (esp_ptr_external_ram(write_buffer)) { - alignment = uhci_ctrl->tx_dir.ext_mem_align; - cache_line_size = uhci_ctrl->ext_mem_cache_line_size; - } else { - alignment = uhci_ctrl->tx_dir.int_mem_align; - cache_line_size = uhci_ctrl->int_mem_cache_line_size; - } - - ESP_RETURN_ON_FALSE(((((uintptr_t)write_buffer) & (alignment - 1)) == 0) && (((write_size) & (alignment - 1)) == 0), ESP_ERR_INVALID_ARG, - TAG, "buffer segment %zu address or size are not %zu bytes aligned", i, alignment); - - if (cache_line_size > 0) { + if (esp_cache_get_line_size_by_addr(write_buffer) > 0) { // Write back to cache to synchronize the cache before DMA start ESP_RETURN_ON_ERROR(esp_cache_msync((void *)write_buffer, write_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED), TAG, "cache sync failed"); } @@ -673,9 +664,6 @@ esp_err_t uhci_new_controller(const uhci_controller_config_t *config, uhci_contr uhci_ll_rx_set_eof_mode(uhci_ctrl->hal.dev, UHCI_RX_BREAK_CHR_EOF); } - esp_cache_get_alignment(MALLOC_CAP_SPIRAM, &uhci_ctrl->ext_mem_cache_line_size); - esp_cache_get_alignment(MALLOC_CAP_INTERNAL, &uhci_ctrl->int_mem_cache_line_size); - ESP_GOTO_ON_ERROR(uhci_gdma_initialize(uhci_ctrl, config), err, TAG, "uhci gdma initialize failed"); // rx_num_dma_nodes is only known after uhci_gdma_initialize() queried the DMA alignment, so the diff --git a/components/esp_driver_uart/src/uhci_private.h b/components/esp_driver_uart/src/uhci_private.h index 76e706f62fd..00a8f6c46cc 100644 --- a/components/esp_driver_uart/src/uhci_private.h +++ b/components/esp_driver_uart/src/uhci_private.h @@ -23,7 +23,6 @@ extern "C" { typedef struct uhci_controller_t uhci_controller_t; #define UHCI_ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) -#define UHCI_MAX(a, b) (((a)>(b))?(a):(b)) #define UHCI_PM_LOCK_NAME_LEN_MAX 16 @@ -70,8 +69,6 @@ typedef struct { uhci_transaction_desc_t *cur_trans; // pointer to current transaction QueueHandle_t trans_queues[UHCI_TRANS_QUEUE_MAX]; // transaction queue _Atomic uhci_tx_fsm_t tx_fsm; // channel life cycle specific FSM - size_t int_mem_align; // Alignment for internal memory - size_t ext_mem_align; // Alignment for external memory atomic_int num_trans_inflight; // Indicates the number of transactions that are undergoing but not recycled to ready_queue size_t max_transmit_size; // per-transaction max total size in bytes, from config->max_transmit_size; the DMA node pool is sized for this size_t max_buf_count; // per-transaction max buffer segment count, from config->max_transmit_buffer_count (at least 1) @@ -87,8 +84,6 @@ typedef struct { uint8_t **buffer_pointers; // Pointer for saving buffer pointer _Atomic uhci_rx_fsm_t rx_fsm; // channel life cycle specific FSM size_t cache_line; // cache line size need to be aligned up. - size_t int_mem_align; // Alignment for internal memory - size_t ext_mem_align; // Alignment for external memory size_t rx_num_dma_nodes; // rx dma number nodes gdma_buffer_mount_config_t *mount_configs; // scratch array (capacity rx_num_dma_nodes) reused by every receive to mount buffer segments; avoids a VLA in ISR context bool continuous; // continuous mode: keep DMA running across EOFs instead of stopping @@ -100,8 +95,6 @@ struct uhci_controller_t { uhci_tx_dir tx_dir; // tx direction structure uhci_rx_dir rx_dir; // rx direction structure void *user_data; // user data - size_t int_mem_cache_line_size; // internal memory cache line size - size_t ext_mem_cache_line_size; // external memory cache line size #if CONFIG_PM_ENABLE esp_pm_lock_handle_t pm_lock; // power management lock char pm_lock_name[UHCI_PM_LOCK_NAME_LEN_MAX]; // pm lock name diff --git a/components/esp_hw_support/CMakeLists.txt b/components/esp_hw_support/CMakeLists.txt index 5fdc7704ab3..818fcb105c6 100644 --- a/components/esp_hw_support/CMakeLists.txt +++ b/components/esp_hw_support/CMakeLists.txt @@ -24,7 +24,8 @@ set(priv_requires efuse # only esp_hw_support/adc_share_hw_ctrl. esp_hal_ana_conv # sleep process requires backup/restore some ADC, TSENS registers ) -set(srcs "cpu.c" "port/${IDF_TARGET}/esp_cpu_intr.c" "esp_memory_utils.c" "port/${IDF_TARGET}/cpu_region_protect.c") +set(srcs "cpu.c" "port/${IDF_TARGET}/esp_cpu_intr.c" "esp_memory_utils.c" "port/${IDF_TARGET}/cpu_region_protect.c" + "mspi/esp_mspi_align/esp_mspi_align.c") if(NOT non_os_build) list(APPEND srcs "esp_clk.c" "clk_ctrl_os.c" @@ -181,6 +182,7 @@ set(public_include_dirs "include" "include/soc" "ldo/include" "debug_probe/include" "etm/include" "mspi/mspi_timing_tuning/include" "mspi/mspi_timing_tuning/tuning_scheme_impl/include" "mspi/mspi_intr/include" + "mspi/esp_mspi_align/include" "power_supply/include" "modem/include") if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/include/soc/${target}") 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 ee44d9e3ca1..6441f268fa5 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 d4ccb44585a..ddc24156449 100644 --- a/components/esp_lcd/i80/esp_lcd_panel_io_i80.c +++ b/components/esp_lcd/i80/esp_lcd_panel_io_i80.c @@ -524,11 +524,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 = { @@ -562,15 +563,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); @@ -686,7 +678,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); @@ -883,8 +875,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 8a5a73d5e6a..b868cd39a04 100644 --- a/components/esp_lcd/rgb/esp_lcd_panel_rgb.c +++ b/components/esp_lcd/rgb/esp_lcd_panel_rgb.c @@ -185,10 +185,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++) { @@ -199,16 +195,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 { @@ -218,15 +204,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]); @@ -245,7 +230,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; @@ -1259,7 +1244,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 209313ff087..fd23858f121 100644 --- a/components/mbedtls/CMakeLists.txt +++ b/components/mbedtls/CMakeLists.txt @@ -369,6 +369,8 @@ if(CONFIG_SOC_SHA_GDMA OR CONFIG_SOC_AES_GDMA) endif() if((SHA_PERIPHERAL_TYPE STREQUAL "core" AND CONFIG_SOC_SHA_SUPPORT_DMA) OR AES_PERIPHERAL_TYPE STREQUAL "dma") + target_link_libraries(tfpsacrypto PRIVATE idf::esp_hw_support) + target_link_libraries(builtin PRIVATE idf::esp_hw_support) target_link_libraries(tfpsacrypto PRIVATE idf::esp_mm) target_link_libraries(builtin PRIVATE idf::esp_mm) if(CONFIG_SOC_SHA_GDMA OR CONFIG_SOC_AES_GDMA) 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 f2fee0a16f5..afed3d53da4 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" @@ -245,22 +246,33 @@ static int esp_aes_process_dma_ext_ram(esp_aes_context *ctx, const unsigned char size_t input_alignment = 1; size_t output_alignment = 1; -/* When AES-DMA operations are carried out using external memory with external memory encryption enabled, - we need to make sure that the addresses and the sizes of the buffers on which the DMA operates are 16 byte-aligned. - This is only applicable for ESP32-P4, as other targets use internal memory for DMA operations. */ +#ifdef SOC_MEMSPI_ENCRYPTION_ALIGNMENT +/* When AES-DMA operations use external memory under MSPI strict alignment (FE or PSRAM ECC), + bounce buffers must be aligned to the MSPI requirement. On ESP32-P4, cache-line alignment + may also apply because DMA accesses cached external memory directly. */ #if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE - if (efuse_hal_flash_encryption_enabled()) { - if (esp_ptr_external_ram(input) || esp_ptr_external_ram(output) || esp_ptr_in_drom(input) || esp_ptr_in_drom(output)) { + if (esp_ptr_external_ram(input) || esp_ptr_external_ram(output) || esp_ptr_in_drom(input) || esp_ptr_in_drom(output)) { + size_t input_mspi_align = esp_mspi_get_alignment(input); + size_t output_mspi_align = esp_mspi_get_alignment(output); + if (input_mspi_align > 1 || output_mspi_align > 1) { size_t input_cache_line_size = get_cache_line_size(input); size_t output_cache_line_size = get_cache_line_size(output); - input_alignment = MAX(input_cache_line_size, SOC_GDMA_EXT_MEM_ENC_ALIGNMENT); - output_alignment = MAX(output_cache_line_size, SOC_GDMA_EXT_MEM_ENC_ALIGNMENT); + input_alignment = MAX(input_cache_line_size, input_mspi_align); + output_alignment = MAX(output_cache_line_size, output_mspi_align); input_heap_caps = MALLOC_CAP_8BIT | (esp_ptr_external_ram(input) ? MALLOC_CAP_SPIRAM : MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL); output_heap_caps = MALLOC_CAP_8BIT | (esp_ptr_external_ram(output) ? MALLOC_CAP_SPIRAM : MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL); } } -#endif /* SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE */ +#else + if (realloc_input && (esp_ptr_external_ram(input) || esp_ptr_in_drom(input))) { + input_alignment = esp_mspi_get_alignment(input); + } + if (realloc_output && (esp_ptr_external_ram(output) || esp_ptr_in_drom(output))) { + output_alignment = esp_mspi_get_alignment(output); + } +#endif +#endif /* SOC_MEMSPI_ENCRYPTION_ALIGNMENT */ if (realloc_input) { input_buf = heap_caps_aligned_alloc(input_alignment, chunk_len, input_heap_caps); @@ -276,7 +288,8 @@ static int esp_aes_process_dma_ext_ram(esp_aes_context *ctx, const unsigned char if (output_buf == NULL) { mbedtls_platform_zeroize(output, len); ESP_LOGE(TAG, "Failed to allocate memory"); - return -1; + ret = -1; + goto cleanup; } } else { output_buf = output; @@ -354,9 +367,9 @@ static inline void dma_desc_append(crypto_dma_desc_t **head, crypto_dma_desc_t * #define ALIGN_DOWN(num, align) ((num) & ~((align) - 1)) #define AES_DMA_ALLOC_CAPS (MALLOC_CAP_DMA | MALLOC_CAP_8BIT) -static inline void *aes_dma_calloc(size_t num, size_t size, uint32_t caps, size_t *actual_size) +static inline void *aes_dma_calloc(size_t alignment, size_t num, size_t size, uint32_t caps, size_t *actual_size) { - return heap_caps_aligned_calloc(DMA_DESC_MEM_ALIGN_SIZE, num, size, caps); + return heap_caps_aligned_calloc(alignment, num, size, caps); } static inline esp_err_t dma_desc_link(crypto_dma_desc_t *dmadesc, size_t crypto_dma_desc_num, size_t buffer_cache_line_size) @@ -425,6 +438,14 @@ static esp_err_t generate_descriptor_list(const uint8_t *buffer, const size_t le uint8_t *end_alignment_stream_buffer = NULL; crypto_dma_desc_t *dma_descriptors = NULL; + size_t buffer_alignment = DMA_DESC_MEM_ALIGN_SIZE; +#ifdef SOC_MEMSPI_ENCRYPTION_ALIGNMENT + size_t mspi_alignment = esp_mspi_get_alignment(buffer); + buffer_alignment = MAX(buffer_alignment, mspi_alignment); +#endif + buffer_alignment = MAX(buffer_alignment, cache_line_size); + uint32_t alignment_buffer_caps = AES_DMA_ALLOC_CAPS | + (esp_ptr_external_ram(buffer) ? MALLOC_CAP_SPIRAM : MALLOC_CAP_INTERNAL); if (len == 0) { goto ret; @@ -455,7 +476,8 @@ static esp_err_t generate_descriptor_list(const uint8_t *buffer, const size_t le dma_descs_needed = (unaligned_start_bytes ? 1 : 0) + dma_desc_get_required_num(aligned_block_bytes, max_desc_size) + (unaligned_end_bytes ? 1 : 0); /* Allocate memory for DMA descriptors of total size aligned up to a multiple of cache line size */ - dma_descriptors = (crypto_dma_desc_t *) aes_dma_calloc(dma_descs_needed, sizeof(crypto_dma_desc_t), MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL, NULL); + dma_descriptors = (crypto_dma_desc_t *) aes_dma_calloc(DMA_DESC_MEM_ALIGN_SIZE, dma_descs_needed, + sizeof(crypto_dma_desc_t), MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL, NULL); if (dma_descriptors == NULL) { ESP_LOGE(TAG, "Failed to allocate memory for the array of DMA descriptors"); goto err; @@ -464,7 +486,8 @@ static esp_err_t generate_descriptor_list(const uint8_t *buffer, const size_t le size_t populated_dma_descs = 0; if (unaligned_start_bytes) { - start_alignment_stream_buffer = aes_dma_calloc(alignment_buffer_size, sizeof(uint8_t), AES_DMA_ALLOC_CAPS | (esp_ptr_external_ram(buffer) ? MALLOC_CAP_SPIRAM : MALLOC_CAP_INTERNAL) , NULL); + start_alignment_stream_buffer = aes_dma_calloc(buffer_alignment, alignment_buffer_size, + sizeof(uint8_t), alignment_buffer_caps, NULL); if (start_alignment_stream_buffer == NULL) { ESP_LOGE(TAG, "Failed to allocate memory for start alignment buffer"); goto err; @@ -486,7 +509,8 @@ static esp_err_t generate_descriptor_list(const uint8_t *buffer, const size_t le } if (unaligned_end_bytes) { - end_alignment_stream_buffer = aes_dma_calloc(alignment_buffer_size, sizeof(uint8_t), AES_DMA_ALLOC_CAPS | (esp_ptr_external_ram(buffer) ? MALLOC_CAP_SPIRAM : MALLOC_CAP_INTERNAL), NULL); + end_alignment_stream_buffer = aes_dma_calloc(buffer_alignment, alignment_buffer_size, + sizeof(uint8_t), alignment_buffer_caps, NULL); if (end_alignment_stream_buffer == NULL) { ESP_LOGE(TAG, "Failed to allocate memory for end alignment buffer"); goto err; @@ -562,19 +586,17 @@ int esp_aes_process_dma(esp_aes_context *ctx, const unsigned char *input, unsign return MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH; } -#ifdef SOC_GDMA_EXT_MEM_ENC_ALIGNMENT - if (efuse_hal_flash_encryption_enabled()) { - if (esp_ptr_external_ram(input) || esp_ptr_external_ram(output) || esp_ptr_in_drom(input) || esp_ptr_in_drom(output)) { - if (((intptr_t)(input) & (SOC_GDMA_EXT_MEM_ENC_ALIGNMENT - 1)) != 0) { - input_needs_realloc = true; - } +#ifdef SOC_MEMSPI_ENCRYPTION_ALIGNMENT + if (esp_ptr_external_ram(input) || esp_ptr_external_ram(output) || esp_ptr_in_drom(input) || esp_ptr_in_drom(output)) { + if (!esp_mspi_buffer_alignment_satisfied(input, block_bytes)) { + input_needs_realloc = true; + } - if (((intptr_t)(output) & (SOC_GDMA_EXT_MEM_ENC_ALIGNMENT - 1)) != 0) { - output_needs_realloc = true; - } + if (!esp_mspi_buffer_alignment_satisfied(output, block_bytes)) { + output_needs_realloc = true; } } -#endif /* SOC_GDMA_EXT_MEM_ENC_ALIGNMENT */ +#endif /* SOC_MEMSPI_ENCRYPTION_ALIGNMENT */ /* DMA cannot access memory in the iCache range, copy input to internal ram */ if (!s_check_dma_capable(input)) { @@ -829,14 +851,14 @@ int esp_aes_process_dma_gcm(esp_aes_context *ctx, const unsigned char *input, un out_desc_tail = &output_desc[output_dma_desc_num - 1]; - len_desc = aes_dma_calloc(1, sizeof(crypto_dma_desc_t), AES_DMA_ALLOC_CAPS, NULL); + len_desc = aes_dma_calloc(DMA_DESC_MEM_ALIGN_SIZE, 1, sizeof(crypto_dma_desc_t), AES_DMA_ALLOC_CAPS, NULL); if (len_desc == NULL) { mbedtls_platform_zeroize(output, len); ESP_LOGE(TAG, "Failed to allocate memory for len descriptor"); return -1; } - uint32_t *len_buf = aes_dma_calloc(4, sizeof(uint32_t), AES_DMA_ALLOC_CAPS, NULL); + uint32_t *len_buf = aes_dma_calloc(DMA_DESC_MEM_ALIGN_SIZE, 4, sizeof(uint32_t), AES_DMA_ALLOC_CAPS, NULL); if (len_buf == NULL) { mbedtls_platform_zeroize(output, len); ESP_LOGE(TAG, "Failed to allocate memory for len buffer"); @@ -1076,20 +1098,18 @@ int esp_aes_process_dma(esp_aes_context *ctx, const unsigned char *input, unsign if (block_bytes > 0) { /* Flush cache if input in external ram */ #if (CONFIG_SPIRAM && SOC_PSRAM_DMA_CAPABLE) -#ifdef SOC_GDMA_EXT_MEM_ENC_ALIGNMENT - if (efuse_hal_flash_encryption_enabled()) { - if (esp_ptr_external_ram(input) || esp_ptr_in_drom(input)) { - if (((intptr_t)(input) & (SOC_GDMA_EXT_MEM_ENC_ALIGNMENT - 1)) != 0) { - input_needs_realloc = true; - } - } - if (esp_ptr_external_ram(output) || esp_ptr_in_drom(output)) { - if (((intptr_t)(output) & (SOC_GDMA_EXT_MEM_ENC_ALIGNMENT - 1)) != 0) { - output_needs_realloc = true; - } +#ifdef SOC_MEMSPI_ENCRYPTION_ALIGNMENT + if (esp_ptr_external_ram(input) || esp_ptr_in_drom(input)) { + if (!esp_mspi_buffer_alignment_satisfied(input, block_bytes)) { + input_needs_realloc = true; } } -#endif /* SOC_GDMA_EXT_MEM_ENC_ALIGNMENT */ + if (esp_ptr_external_ram(output) || esp_ptr_in_drom(output)) { + if (!esp_mspi_buffer_alignment_satisfied(output, block_bytes)) { + output_needs_realloc = true; + } + } +#endif /* SOC_MEMSPI_ENCRYPTION_ALIGNMENT */ if (esp_ptr_external_ram(input)) { if (esp_cache_msync((void *)input, len, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED) != ESP_OK) { 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 057ec4326b7..ba7e694eaa5 100644 --- a/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in @@ -547,10 +547,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 45c721c44e1..f59af3dea15 100644 --- a/components/soc/esp32c5/include/soc/soc_caps.h +++ b/components/soc/esp32c5/include/soc/soc_caps.h @@ -210,7 +210,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 24d2ff9b3f3..47561913937 100644 --- a/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in @@ -419,10 +419,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 3ca95cd1b3c..217362bf33b 100644 --- a/components/soc/esp32c61/include/soc/soc_caps.h +++ b/components/soc/esp32c61/include/soc/soc_caps.h @@ -162,7 +162,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 c5e21f1b498..878a2ca1f83 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -667,10 +667,6 @@ config SOC_GDMA_SUPPORT_SLEEP_RETENTION bool default y -config SOC_GDMA_EXT_MEM_ENC_ALIGNMENT - int - default 16 - config SOC_GPIO_PORT int default 1 diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index 26ec357bbc2..e35993d66d5 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -242,7 +242,6 @@ #define SOC_GDMA_SUPPORT_CRC 1 #define SOC_GDMA_SUPPORT_ETM 1 #define SOC_GDMA_SUPPORT_SLEEP_RETENTION 1 -#define SOC_GDMA_EXT_MEM_ENC_ALIGNMENT (16) /*-------------------------- GPIO CAPS ---------------------------------------*/ // ESP32-P4 has 1 GPIO peripheral diff --git a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in index 30f72a7611e..e0643534c4c 100644 --- a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in @@ -615,10 +615,6 @@ config SOC_GDMA_SUPPORT_SLEEP_RETENTION bool default y -config SOC_GDMA_EXT_MEM_ENC_ALIGNMENT - int - default 16 - config SOC_MODEM_SUPPORT_ETM bool default y diff --git a/components/soc/esp32s31/include/soc/soc_caps.h b/components/soc/esp32s31/include/soc/soc_caps.h index cd54063c888..c2d9e6c2aff 100644 --- a/components/soc/esp32s31/include/soc/soc_caps.h +++ b/components/soc/esp32s31/include/soc/soc_caps.h @@ -210,7 +210,6 @@ #define SOC_AHB_GDMA_VERSION 2 #define SOC_GDMA_SUPPORT_ETM 1 #define SOC_GDMA_SUPPORT_SLEEP_RETENTION 1 -#define SOC_GDMA_EXT_MEM_ENC_ALIGNMENT (16) /*-------------------------- MODEM CAPS --------------------------------------*/ #define SOC_MODEM_SUPPORT_ETM 1 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 38918f0351d..34863c12e16 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 @@ -95,9 +95,12 @@ extern void example_lvgl_demo_ui(lv_display_t *disp); #if CONFIG_EXAMPLE_USE_DMA2D_COPY_FRAME void example_rounder_flush_area_cb(lv_event_t * event) { + // Under flash encryption, DMA2D access to PSRAM must satisfy MSPI encryption + // alignment (typically 16 bytes) for both the buffer address and transfer size. + // Round the LVGL invalidate area so the flush region width meets that requirement. lv_area_t * area = lv_event_get_invalidated_area(event); area->x1 = ALIGN_DOWN(area->x1, 16); - area->x2 = ALIGN_UP(area->x2, 16) - 1; + area->x2 = ALIGN_UP(area->x2 + 1, 16) - 1; } #endif @@ -108,7 +111,7 @@ static void example_lvgl_flush_cb(lv_display_t *disp, const lv_area_t *area, uin int offsetx2 = area->x2; int offsety1 = area->y1; int offsety2 = area->y2; - // pass the draw buffer to the driver + // LVGL area coordinates are inclusive; panel draw_bitmap expects [start, end). esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, px_map); } @@ -298,23 +301,14 @@ void app_main(void) void *buf2 = NULL; ESP_LOGI(TAG, "Allocate separate LVGL draw buffers"); - size_t alignment = 1; -#if CONFIG_EXAMPLE_USE_DMA2D_COPY_FRAME - if (esp_efuse_is_flash_encryption_enabled()) { - alignment = SOC_GDMA_EXT_MEM_ENC_ALIGNMENT; - if (EXAMPLE_MIPI_DSI_LCD_H_RES % alignment != 0) { - ESP_LOGW(TAG, "EXAMPLE_MIPI_DSI_LCD_H_RES is not aligned to %d, may cause MSPI error", alignment); - } - } -#endif size_t draw_buffer_sz = EXAMPLE_MIPI_DSI_LCD_H_RES * EXAMPLE_LVGL_DRAW_BUF_LINES * sizeof(lv_color_t); // Note: // Keep the display buffer in **internal** RAM can speed up the UI because LVGL uses it a lot and it should have a fast access time // This example allocate the buffer from PSRAM mainly because we want to save the internal RAM - buf1 = heap_caps_aligned_calloc(alignment, 1, draw_buffer_sz, MALLOC_CAP_SPIRAM); + buf1 = heap_caps_aligned_calloc(16, 1, draw_buffer_sz, MALLOC_CAP_SPIRAM); assert(buf1); - buf2 = heap_caps_aligned_calloc(alignment, 1, draw_buffer_sz, MALLOC_CAP_SPIRAM); + buf2 = heap_caps_aligned_calloc(16, 1, draw_buffer_sz, MALLOC_CAP_SPIRAM); assert(buf2); // initialize LVGL draw buffers lv_display_set_buffers(display, buf1, buf2, draw_buffer_sz, LV_DISPLAY_RENDER_MODE_PARTIAL); @@ -322,9 +316,13 @@ void app_main(void) lv_display_set_flush_cb(display, example_lvgl_flush_cb); #if CONFIG_EXAMPLE_USE_DMA2D_COPY_FRAME - // If flash encryption is enabled, DMA2D requires the flush buffer address and size to be aligned to 16 bytes. - // We need to round the flush area to the multiple of 16. - if (esp_efuse_is_flash_encryption_enabled()) { + // If Flash Encryption/ PSRAM ECC is enabled, DMA2D requires the flush buffer address and size to be aligned. + // Round the LVGL invalidate area accordingly (this is an LVGL integration hook, not a panel API). + bool need_rounder = esp_efuse_is_flash_encryption_enabled(); +#if CONFIG_SPIRAM_ECC_ENABLE + need_rounder = true; +#endif + if (need_rounder) { ESP_LOGI(TAG, "Register event callback for LVGL flush area rounding"); lv_display_add_event_cb(display, example_rounder_flush_area_cb, LV_EVENT_INVALIDATE_AREA, NULL); } From bd2d64bffbe2198a0d5f463d4c53ec810ab984a8 Mon Sep 17 00:00:00 2001 From: Chen Jichang Date: Mon, 10 Aug 2026 20:36:57 +0800 Subject: [PATCH 3/3] refactor(gdma): increase performance and optimize api --- .../src/bitscrambler_loopback.c | 9 ++-- .../dvp/src/esp_cam_ctlr_dvp_gdma.c | 5 +- .../esp_driver_dma/include/esp_private/gdma.h | 51 ++++++++----------- .../include/esp_private/gdma_link.h | 8 ++- components/esp_driver_dma/linker.lf | 1 - .../esp_driver_dma/src/async_crc_gdma.c | 6 +-- .../esp_driver_dma/src/async_memcpy_gdma.c | 14 +++-- components/esp_driver_dma/src/esp_dma_utils.c | 8 +-- components/esp_driver_dma/src/gdma.c | 34 ++++--------- components/esp_driver_dma/src/gdma_link.c | 22 +++----- components/esp_driver_dma/src/gdma_priv.h | 1 - .../test_apps/dma/main/test_gdma.c | 18 +++---- components/esp_driver_i3c/i3c_master.c | 7 ++- components/esp_driver_parlio/src/parlio_rx.c | 12 ++--- components/esp_driver_parlio/src/parlio_tx.c | 8 +-- components/esp_driver_rmt/src/rmt_rx.c | 7 ++- components/esp_driver_rmt/src/rmt_tx.c | 5 +- .../esp_driver_spi/src/gpspi/spi_common.c | 10 +++- components/esp_driver_uart/src/uhci.c | 17 +++---- .../mspi/esp_mspi_align/esp_mspi_align.c | 2 +- components/esp_lcd/i80/esp_lcd_panel_io_i80.c | 5 +- components/esp_lcd/rgb/esp_lcd_panel_rgb.c | 9 +++- .../mbedtls_ut/include/crypto_performance.h | 2 +- 23 files changed, 119 insertions(+), 142 deletions(-) diff --git a/components/esp_driver_bitscrambler/src/bitscrambler_loopback.c b/components/esp_driver_bitscrambler/src/bitscrambler_loopback.c index cd0070e7fe3..507d3741a1b 100644 --- a/components/esp_driver_bitscrambler/src/bitscrambler_loopback.c +++ b/components/esp_driver_bitscrambler/src/bitscrambler_loopback.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 */ @@ -221,10 +221,12 @@ esp_err_t bitscrambler_loopback_run(bitscrambler_handle_t bs, void *buffer_in, s gdma_reset(bsl->tx_channel); bitscrambler_reset(bs); + size_t in_alignment = gdma_get_buffer_alignment_constraint(bsl->tx_channel, buffer_in); + size_t out_alignment = gdma_get_buffer_alignment_constraint(bsl->rx_channel, buffer_out); // mount in and out buffer to the DMA link list gdma_buffer_mount_config_t in_buf_mount_config = { .buffer = buffer_in, - .buffer_alignment = 4, + .buffer_alignment = in_alignment, .length = length_bytes_in, .flags = { .mark_eof = true, @@ -234,12 +236,11 @@ esp_err_t bitscrambler_loopback_run(bitscrambler_handle_t bs, void *buffer_in, s gdma_link_mount_buffers(bsl->tx_link_list, 0, &in_buf_mount_config, 1, NULL); gdma_buffer_mount_config_t out_buf_mount_config = { .buffer = buffer_out, - .buffer_alignment = 4, + .buffer_alignment = out_alignment, .length = length_bytes_out, .flags = { .mark_eof = false, .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 05b02cff3ce..3467e9d48cc 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 @@ -106,7 +106,10 @@ 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_channel_alignment_constraints(dma->dma_chan, &dma->int_mem_align, &dma->ext_mem_align, NULL); + gdma_channel_alignment_info_t align_info; + gdma_get_channel_alignment_constraints(dma->dma_chan, &align_info); + dma->int_mem_align = align_info.int_mem_alignment; + dma->ext_mem_align = align_info.ext_enc_mem_alignment; size_t buffer_alignment = dma->ext_mem_align; size_t desc_max_size = ESP_CAM_CTLR_DVP_DMA_DESC_BUFFER_MAX_SIZE; diff --git a/components/esp_driver_dma/include/esp_private/gdma.h b/components/esp_driver_dma/include/esp_private/gdma.h index 617ff133396..2943914ecf2 100644 --- a/components/esp_driver_dma/include/esp_private/gdma.h +++ b/components/esp_driver_dma/include/esp_private/gdma.h @@ -7,6 +7,7 @@ #pragma once #include +#include #include "esp_etm.h" #include "hal/gdma_types.h" #include "esp_err.h" @@ -221,52 +222,44 @@ 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 a configured GDMA channel + * @brief Alignment constraints of a configured GDMA channel * - * @note You should call this function after `gdma_config_transfer`, the later one can - * adjust the alignment constraints based on 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. + * @note The returned alignment doesn't take the cache line size into account. If the DMA buffer is behind a cache, + * align the buffer size to the cache line size yourself when needed. + */ +typedef struct { + size_t int_mem_alignment; /*!< Alignment for internal memory */ + size_t ext_enc_mem_alignment; /*!< Alignment for external memory including MSPI encryption/ECC constraints */ + size_t ext_no_enc_mem_alignment; /*!< Alignment for external memory without MSPI region-specific constraints */ +} gdma_channel_alignment_info_t; + +/** + * @brief Get the alignment constraints for a configured GDMA channel + * + * @note Call this function after `gdma_config_transfer`. * * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_ahb_channel/gdma_new_axi_channel` - * @param[out] int_mem_alignment Internal memory alignment - * @param[out] ext_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. + * @param[out] info Alignment constraints of the channel * @return * - ESP_OK: Get alignment constraints successfully * - ESP_ERR_INVALID_ARG: Get alignment constraints failed because of invalid argument - * - ESP_FAIL: Get alignment constraints failed because of other error */ -esp_err_t gdma_get_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); +esp_err_t gdma_get_channel_alignment_constraints(gdma_channel_handle_t dma_chan, gdma_channel_alignment_info_t *info); /** * @brief Get the effective alignment constraint for a specific DMA buffer * - * @note 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 Call this function after `gdma_config_transfer`. + * @note Combines GDMA channel constraints with MSPI constraints of the actual buffer region. + * External no-encryption PSRAM buffers can therefore use their real runtime constraint + * instead of a generic worst-case MSPI alignment. * @note The returned alignment doesn't take the cache line size into account. - * @note On invalid arguments, returns an impossible alignment (BIT(31)). + * @note On invalid arguments, returns an impossible value (BIT(31)). * * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_ahb_channel/gdma_new_axi_channel` * @param[in] buffer DMA buffer address 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 93c6271ff0f..fab9624d2ef 100644 --- a/components/esp_driver_dma/include/esp_private/gdma_link.h +++ b/components/esp_driver_dma/include/esp_private/gdma_link.h @@ -82,12 +82,10 @@ typedef struct { gdma_final_node_link_type_t mark_final: 2; /*!< Specify the next item of the final item of this mount. For the other items that not the final one, it will be linked to the next item automatically and this field takes no effect. Note, the final item here does not mean the last item in the link list. It is `start_item_index + num_items - 1` */ - uint32_t bypass_buffer_align_check: 1; /*!< Whether to bypass the buffer alignment check. + uint32_t bypass_buffer_addr_align_check: 1; /*!< Whether to bypass the buffer address alignment check. + Only enable it when you know what you are doing. */ + uint32_t bypass_buffer_size_align_check: 1; /*!< Whether to bypass the buffer size alignment check. Only enable it when you know what you are doing. */ - 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 d025315bce1..f1d46436bbc 100644 --- a/components/esp_driver_dma/linker.lf +++ b/components/esp_driver_dma/linker.lf @@ -12,7 +12,6 @@ entries: 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_crc_gdma.c b/components/esp_driver_dma/src/async_crc_gdma.c index 1e1e49ae2b2..5a3079842e1 100644 --- a/components/esp_driver_dma/src/async_crc_gdma.c +++ b/components/esp_driver_dma/src/async_crc_gdma.c @@ -163,8 +163,9 @@ esp_err_t esp_async_crc_install_gdma_template(const async_crc_config_t *config, ESP_GOTO_ON_ERROR(gdma_config_transfer(crc_gdma->rx_channel, &transfer_cfg), err, TAG, "config RX DMA transfer failed"); // Get buffer alignment required by GDMA channel - size_t rx_int_mem_alignment = 0; - gdma_get_channel_alignment_constraints(crc_gdma->rx_channel, &rx_int_mem_alignment, NULL, NULL); + gdma_channel_alignment_info_t rx_align_info; + gdma_get_channel_alignment_constraints(crc_gdma->rx_channel, &rx_align_info); + size_t rx_int_mem_alignment = rx_align_info.int_mem_alignment; size_t rx_buffer_size = (rx_int_mem_alignment > CRC_DMA_RX_SINK_BUFFER_SIZE) ? rx_int_mem_alignment : CRC_DMA_RX_SINK_BUFFER_SIZE; crc_gdma->rx_sink_buffer = heap_caps_aligned_calloc(rx_int_mem_alignment, 1, rx_buffer_size, @@ -189,7 +190,6 @@ 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), diff --git a/components/esp_driver_dma/src/async_memcpy_gdma.c b/components/esp_driver_dma/src/async_memcpy_gdma.c index 49241b1ca39..26b073cc376 100644 --- a/components/esp_driver_dma/src/async_memcpy_gdma.c +++ b/components/esp_driver_dma/src/async_memcpy_gdma.c @@ -324,12 +324,11 @@ static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *s trans->stash_buffer = NULL; } - size_t buffer_alignment = 0; size_t num_dma_nodes = 0; // allocate gdma TX link - buffer_alignment = gdma_get_buffer_alignment_constraint(mcp_gdma->tx_channel, src); - num_dma_nodes = esp_dma_calculate_node_count(n, buffer_alignment, MCP_DMA_DESCRIPTOR_BUFFER_MAX_SIZE); + size_t tx_buffer_alignment = gdma_get_buffer_alignment_constraint(mcp_gdma->tx_channel, src); + num_dma_nodes = esp_dma_calculate_node_count(n, tx_buffer_alignment, MCP_DMA_DESCRIPTOR_BUFFER_MAX_SIZE); gdma_link_list_config_t tx_link_cfg = { .item_alignment = dma_link_item_alignment, .num_items = num_dma_nodes, @@ -343,7 +342,7 @@ static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *s gdma_buffer_mount_config_t tx_buf_mount_config[1] = { [0] = { .buffer = src, - .buffer_alignment = buffer_alignment, + .buffer_alignment = tx_buffer_alignment, .length = n, .flags = { .mark_eof = true, // mark the last item as EOF, so the RX channel can also received an EOF list item @@ -361,8 +360,8 @@ static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *s } // allocate gdma RX link - buffer_alignment = gdma_get_buffer_alignment_constraint(mcp_gdma->rx_channel, dst); - num_dma_nodes = esp_dma_calculate_node_count(n, buffer_alignment, MCP_DMA_DESCRIPTOR_BUFFER_MAX_SIZE); + size_t rx_buffer_alignment = gdma_get_buffer_alignment_constraint(mcp_gdma->rx_channel, dst); + num_dma_nodes = esp_dma_calculate_node_count(n, rx_buffer_alignment, MCP_DMA_DESCRIPTOR_BUFFER_MAX_SIZE); gdma_link_list_config_t rx_link_cfg = { .item_alignment = dma_link_item_alignment, .num_items = num_dma_nodes + 3, // add 3 extra items for the cache aligned buffers @@ -379,9 +378,8 @@ static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *s gdma_buffer_mount_config_t rx_buf_mount_config[3] = {0}; for (int i = 0; i < 3; i++) { rx_buf_mount_config[i].buffer = trans->rx_buf_array.aligned_buffer[i].aligned_buffer; - rx_buf_mount_config[i].buffer_alignment = buffer_alignment; + rx_buf_mount_config[i].buffer_alignment = rx_buffer_alignment; rx_buf_mount_config[i].length = trans->rx_buf_array.aligned_buffer[i].length; - rx_buf_mount_config[i].flags.check_size_align = gdma_is_size_alignment_required(mcp_gdma->rx_channel); } gdma_link_mount_buffers(trans->rx_link_list, 0, rx_buf_mount_config, 3, NULL); diff --git a/components/esp_driver_dma/src/esp_dma_utils.c b/components/esp_driver_dma/src/esp_dma_utils.c index 8319863f94c..ebccbbd3ede 100644 --- a/components/esp_driver_dma/src/esp_dma_utils.c +++ b/components/esp_driver_dma/src/esp_dma_utils.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -45,7 +45,7 @@ esp_err_t esp_dma_split_rx_buffer_to_cache_aligned(void *rx_buffer, size_t buffe split_line_size = int_mem_cache_line_size; } bool align_required = split_line_size > 0; - ESP_EARLY_LOGV(TAG, "split_line_size:%d", split_line_size); + ESP_EARLY_LOGV(TAG, "split_line_size:%" PRIu32, (uint32_t)split_line_size); if (*ret_stash_buffer == NULL) { // If the stash buffer is not offered by the caller, allocate the stash buffer from internal RAM @@ -69,10 +69,10 @@ esp_err_t esp_dma_split_rx_buffer_to_cache_aligned(void *rx_buffer, size_t buffe // calculate head_overflow_len size_t head_overflow_len = (uintptr_t)rx_buffer % split_line_size; head_overflow_len = head_overflow_len ? split_line_size - head_overflow_len : 0; - ESP_EARLY_LOGV(TAG, "head_addr:%p head_overflow_len:%zu", rx_buffer, head_overflow_len); + ESP_EARLY_LOGV(TAG, "head_addr:%p head_overflow_len:%" PRIu32, rx_buffer, (uint32_t)head_overflow_len); // calculate tail_overflow_len size_t tail_overflow_len = ((uintptr_t)rx_buffer + buffer_len) % split_line_size; - ESP_EARLY_LOGV(TAG, "tail_addr:%p tail_overflow_len:%zu", rx_buffer + buffer_len - tail_overflow_len, tail_overflow_len); + ESP_EARLY_LOGV(TAG, "tail_addr:%p tail_overflow_len:%" PRIu32, rx_buffer + buffer_len - tail_overflow_len, (uint32_t)tail_overflow_len); // special handling when input_buffer length is no more than buffer alignment bool is_small_buf = head_overflow_len >= buffer_len || tail_overflow_len >= buffer_len; diff --git a/components/esp_driver_dma/src/gdma.c b/components/esp_driver_dma/src/gdma.c index 90e7b7d260a..fcd4be042c0 100644 --- a/components/esp_driver_dma/src/gdma.c +++ b/components/esp_driver_dma/src/gdma.c @@ -428,7 +428,7 @@ esp_err_t gdma_config_transfer(gdma_channel_handle_t dma_chan, const gdma_transf 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. + // Under Flash Encryption/PSRAM ECC, DMA must use MSPI-aligned bursts. size_t mspi_alignment = esp_mspi_get_alignment(NULL); if (mspi_alignment > 1) { if (max_data_burst_size < mspi_alignment) { @@ -449,7 +449,6 @@ esp_err_t gdma_config_transfer(gdma_channel_handle_t dma_chan, const gdma_transf } bool en_data_burst = max_data_burst_size > 0; - dma_chan->flags.size_alignment_required = false; if (en_data_burst) { #if CONFIG_GDMA_ENABLE_WEIGHTED_ARBITRATION // due to hardware limitation, if weighted arbitration is enabled, the data must be aligned to burst size @@ -464,7 +463,6 @@ esp_err_t gdma_config_transfer(gdma_channel_handle_t dma_chan, const gdma_transf int_mem_alignment = MAX(int_mem_alignment, 4); 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 @@ -513,42 +511,30 @@ esp_err_t gdma_config_transfer(gdma_channel_handle_t dma_chan, const gdma_transf return ESP_OK; } -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) +esp_err_t gdma_get_channel_alignment_constraints(gdma_channel_handle_t dma_chan, gdma_channel_alignment_info_t *info) { - if (!dma_chan) { + if (!dma_chan || !info) { return ESP_ERR_INVALID_ARG; } - if (int_mem_alignment) { - *int_mem_alignment = dma_chan->int_mem_alignment; - } - if (ext_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; - } + info->int_mem_alignment = dma_chan->int_mem_alignment; + info->ext_enc_mem_alignment = dma_chan->ext_enc_mem_alignment; + info->ext_no_enc_mem_alignment = dma_chan->ext_no_enc_mem_alignment; return ESP_OK; } -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; + // Internal SRAM only needs the DMA-side constraint; MSPI rules apply to PSRAM/Flash. + if (!(esp_ptr_external_ram(buffer) || esp_ptr_in_drom(buffer))) { + return dma_chan->int_mem_alignment; } size_t mspi_alignment = esp_mspi_get_alignment(buffer); - return MAX(base_alignment, mspi_alignment); + return MAX(dma_chan->ext_no_enc_mem_alignment, mspi_alignment); } esp_err_t gdma_apply_strategy(gdma_channel_handle_t dma_chan, const gdma_strategy_config_t *config) diff --git a/components/esp_driver_dma/src/gdma_link.c b/components/esp_driver_dma/src/gdma_link.c index cbd528dd2f5..819f14ce223 100644 --- a/components/esp_driver_dma/src/gdma_link.c +++ b/components/esp_driver_dma/src/gdma_link.c @@ -186,17 +186,13 @@ 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 mspi_alignment = esp_mspi_get_alignment(buf); - size_t effective_alignment = MAX(buffer_alignment, mspi_alignment); - size_t max_buffer_mount_length = 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 & (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 max_buffer_mount_length = ALIGN_DOWN(GDMA_MAX_BUFFER_SIZE_PER_LINK_ITEM, buffer_alignment); + // Address and size alignment checks are independent; both use the caller-provided buffer_alignment. + if (!config->flags.bypass_buffer_addr_align_check) { + ESP_RETURN_ON_FALSE_ISR(((uintptr_t)buf & (buffer_alignment - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "buf misalign idx=%"PRIu32" align=%"PRIu32, bi, buffer_alignment); + } + if (!config->flags.bypass_buffer_size_align_check) { + ESP_RETURN_ON_FALSE_ISR((len & (buffer_alignment - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "buf len misalign idx=%"PRIu32" len=%"PRIu32" align=%"PRIu32"", bi, len, buffer_alignment); } size_t num_items_need = (len + max_buffer_mount_length - 1) / max_buffer_mount_length; ESP_RETURN_ON_FALSE_ISR(num_items_need <= remaining, ESP_ERR_INVALID_ARG, TAG, @@ -224,9 +220,7 @@ esp_err_t gdma_link_mount_buffers(gdma_link_list_handle_t list, int start_item_i memset(lli_nc, 0, item_size); continue; } - size_t mspi_alignment = esp_mspi_get_alignment(buf); - size_t effective_alignment = MAX(buffer_alignment, mspi_alignment); - size_t max_buffer_mount_length = ALIGN_DOWN(GDMA_MAX_BUFFER_SIZE_PER_LINK_ITEM, effective_alignment); + size_t max_buffer_mount_length = ALIGN_DOWN(GDMA_MAX_BUFFER_SIZE_PER_LINK_ITEM, buffer_alignment); size_t num_items_need = (len + max_buffer_mount_length - 1) / max_buffer_mount_length; // mount the buffer to the link list for (size_t i = 0; i < num_items_need; i++) { diff --git a/components/esp_driver_dma/src/gdma_priv.h b/components/esp_driver_dma/src/gdma_priv.h index 27b7caeb0e4..cff47c5b49c 100644 --- a/components/esp_driver_dma/src/gdma_priv.h +++ b/components/esp_driver_dma/src/gdma_priv.h @@ -96,7 +96,6 @@ struct gdma_channel_t { 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/test_gdma.c b/components/esp_driver_dma/test_apps/dma/main/test_gdma.c index 536792d8a51..14e3b40a4c2 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 @@ -283,9 +283,10 @@ static void test_gdma_m2m_transaction(gdma_channel_handle_t tx_chan, gdma_channe gdma_link_list_handle_t rx_link_list = NULL; test_gdma_config_link_list(tx_chan, rx_chan, &tx_link_list, &rx_link_list, 16, dma_link_in_ext_mem); - size_t int_mem_alignment = 0; - size_t ext_mem_alignment = 0; - TEST_ESP_OK(gdma_get_channel_alignment_constraints(tx_chan, &int_mem_alignment, &ext_mem_alignment, NULL)); + gdma_channel_alignment_info_t tx_align_info; + TEST_ESP_OK(gdma_get_channel_alignment_constraints(tx_chan, &tx_align_info)); + size_t int_mem_alignment = tx_align_info.int_mem_alignment; + size_t __attribute__((unused)) ext_mem_alignment = tx_align_info.ext_enc_mem_alignment; // allocate the source buffer from SRAM uint8_t *src_data = heap_caps_aligned_calloc(int_mem_alignment, 1, 128, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); @@ -352,9 +353,6 @@ 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)); @@ -503,7 +501,6 @@ 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)); @@ -584,7 +581,9 @@ static void test_gdma_m2m_unaligned_buffer_test(uint8_t *dst_data, uint8_t *src_ TEST_ESP_OK(gdma_config_transfer(rx_chan, &transfer_config)); size_t rx_mem_alignment = 0; - TEST_ESP_OK(gdma_get_channel_alignment_constraints(rx_chan, &rx_mem_alignment, NULL, NULL)); + gdma_channel_alignment_info_t rx_align_info; + TEST_ESP_OK(gdma_get_channel_alignment_constraints(rx_chan, &rx_align_info)); + rx_mem_alignment = rx_align_info.int_mem_alignment; // prepare the source data for (int i = 0; i < data_length; i++) { @@ -616,7 +615,7 @@ static void test_gdma_m2m_unaligned_buffer_test(uint8_t *dst_data, uint8_t *src_ rx_aligned_buf_mount_config[i].buffer = align_array.aligned_buffer[i].aligned_buffer; rx_aligned_buf_mount_config[i].buffer_alignment = MAX(sram_alignment, rx_mem_alignment); rx_aligned_buf_mount_config[i].length = align_array.aligned_buffer[i].length; - rx_aligned_buf_mount_config[i].flags.check_size_align = gdma_is_size_alignment_required(rx_chan); + rx_aligned_buf_mount_config[i].flags.bypass_buffer_size_align_check = true; // head and tail buffer size is not aligned to the cache line size } TEST_ESP_OK(gdma_link_mount_buffers(rx_link_list, 0, rx_aligned_buf_mount_config, 3, NULL)); @@ -779,7 +778,6 @@ 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)); diff --git a/components/esp_driver_i3c/i3c_master.c b/components/esp_driver_i3c/i3c_master.c index f86e4c650af..86d97dd7d01 100644 --- a/components/esp_driver_i3c/i3c_master.c +++ b/components/esp_driver_i3c/i3c_master.c @@ -369,9 +369,9 @@ static esp_err_t i3c_master_init_dma(i3c_master_bus_t *i3c_master_handle, const ESP_GOTO_ON_ERROR(gdma_config_transfer(i3c_master_handle->dma_tx_chan, &transfer_cfg), err2, TAG, "Config DMA tx channel transfer failed"); // create DMA link list - size_t int_mem_align = 0; - gdma_get_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); + gdma_channel_alignment_info_t align_info; + gdma_get_channel_alignment_constraints(i3c_master_handle->dma_tx_chan, &align_info); + i3c_master_handle->dma_buffer_alignment = I3C_ALIGN_UP(align_info.int_mem_alignment, I3C_MASTER_DMA_INTERFACE_ALIGNMENT); size_t num_dma_nodes = esp_dma_calculate_node_count(dma_config->max_transfer_size, i3c_master_handle->dma_buffer_alignment, DMA_DESCRIPTOR_BUFFER_MAX_SIZE); gdma_link_list_config_t dma_link_config = { .item_alignment = 4, // 4 bytes alignment for AHB-DMA @@ -581,7 +581,6 @@ 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_parlio/src/parlio_rx.c b/components/esp_driver_parlio/src/parlio_rx.c index 0a25e22f23d..4b2f4709927 100644 --- a/components/esp_driver_parlio/src/parlio_rx.c +++ b/components/esp_driver_parlio/src/parlio_rx.c @@ -147,16 +147,13 @@ 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; mount_config[0].buffer_alignment = trans->alignment; mount_config[0].length = trans->aligned_payload.buf.head.length; - mount_config[0].flags.bypass_buffer_align_check = false; mount_config[0].flags.mark_eof = false; mount_config[0].flags.mark_final = GDMA_FINAL_LINK_TO_DEFAULT; - mount_config[0].flags.check_size_align = size_alignment_required; } /* Mount body buffer */ size_t mount_size = 0; @@ -173,10 +170,8 @@ size_t parlio_rx_mount_transaction_buffer(parlio_rx_unit_handle_t rx_unit, parli mount_config[i].buffer = (void *)((uint8_t *)trans->aligned_payload.buf.body.aligned_buffer + offset); mount_config[i].buffer_alignment = trans->alignment; mount_config[i].length = mount_size; - mount_config[i].flags.bypass_buffer_align_check = false; mount_config[i].flags.mark_eof = false; mount_config[i].flags.mark_final = GDMA_FINAL_LINK_TO_DEFAULT; - mount_config[i].flags.check_size_align = size_alignment_required; offset += mount_size; rest_size -= mount_size; } @@ -185,8 +180,6 @@ size_t parlio_rx_mount_transaction_buffer(parlio_rx_unit_handle_t rx_unit, parli mount_config[required_node_num - 1].buffer = trans->aligned_payload.buf.tail.aligned_buffer; mount_config[required_node_num - 1].buffer_alignment = trans->alignment; mount_config[required_node_num - 1].length = trans->aligned_payload.buf.tail.length; - mount_config[required_node_num - 1].flags.bypass_buffer_align_check = false; - 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; @@ -476,7 +469,10 @@ static esp_err_t parlio_rx_unit_init_dma(parlio_rx_unit_handle_t rx_unit, size_t .access_ext_mem = true, }; ESP_RETURN_ON_ERROR(gdma_config_transfer(rx_unit->dma_chan, &trans_cfg), TAG, "config DMA transfer failed"); - ESP_RETURN_ON_ERROR(gdma_get_channel_alignment_constraints(rx_unit->dma_chan, &rx_unit->int_mem_align, &rx_unit->ext_mem_align, NULL), TAG, "get alignment constraints failed"); + gdma_channel_alignment_info_t align_info; + ESP_RETURN_ON_ERROR(gdma_get_channel_alignment_constraints(rx_unit->dma_chan, &align_info), TAG, "get alignment constraints failed"); + rx_unit->int_mem_align = align_info.int_mem_alignment; + rx_unit->ext_mem_align = align_info.ext_enc_mem_alignment; #if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE uint32_t cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA); rx_unit->int_mem_align = rx_unit->int_mem_align > cache_line_size ? rx_unit->int_mem_align : cache_line_size; diff --git a/components/esp_driver_parlio/src/parlio_tx.c b/components/esp_driver_parlio/src/parlio_tx.c index 8f572e3108f..af2ae427c0b 100644 --- a/components/esp_driver_parlio/src/parlio_tx.c +++ b/components/esp_driver_parlio/src/parlio_tx.c @@ -163,7 +163,10 @@ static esp_err_t parlio_tx_unit_init_dma(parlio_tx_unit_t *tx_unit, const parlio .access_ext_mem = true, // support transmit PSRAM buffer }; ESP_RETURN_ON_ERROR(gdma_config_transfer(tx_unit->dma_chan, &trans_cfg), TAG, "config DMA transfer failed"); - gdma_get_channel_alignment_constraints(tx_unit->dma_chan, &tx_unit->int_mem_align, &tx_unit->ext_mem_align, NULL); + gdma_channel_alignment_info_t align_info; + gdma_get_channel_alignment_constraints(tx_unit->dma_chan, &align_info); + tx_unit->int_mem_align = align_info.int_mem_alignment; + tx_unit->ext_mem_align = align_info.ext_enc_mem_alignment; // create DMA link list size_t buffer_alignment = MAX(tx_unit->int_mem_align, tx_unit->ext_mem_align); @@ -463,8 +466,7 @@ esp_err_t parlio_tx_unit_register_event_callbacks(parlio_tx_unit_handle_t tx_uni static void parlio_mount_buffer(parlio_tx_unit_t *tx_unit, parlio_tx_trans_desc_t *t) { - size_t buffer_alignment = 0; - buffer_alignment = gdma_get_buffer_alignment_constraint(tx_unit->dma_chan, t->payload); + size_t buffer_alignment = gdma_get_buffer_alignment_constraint(tx_unit->dma_chan, t->payload); // DMA transfer data based on bytes not bits, so convert the bit length to bytes, round up size_t payload_bytes = (t->payload_bits + 7) / 8; gdma_buffer_mount_config_t mount_config = { diff --git a/components/esp_driver_rmt/src/rmt_rx.c b/components/esp_driver_rmt/src/rmt_rx.c index 643f7a4786e..0c67ded014e 100644 --- a/components/esp_driver_rmt/src/rmt_rx.c +++ b/components/esp_driver_rmt/src/rmt_rx.c @@ -33,7 +33,6 @@ 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), } }; } @@ -63,9 +62,9 @@ static esp_err_t rmt_rx_init_dma_link(rmt_rx_channel_t *rx_channel, const rmt_rx ESP_RETURN_ON_ERROR(gdma_register_rx_event_callbacks(rx_channel->base.dma_chan, &cbs, rx_channel), TAG, "register DMA callbacks failed"); // 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); + gdma_channel_alignment_info_t align_info; + gdma_get_channel_alignment_constraints(rx_channel->base.dma_chan, &align_info); + size_t buffer_alignment = MAX(align_info.int_mem_alignment, align_info.ext_enc_mem_alignment); rx_channel->num_dma_nodes = esp_dma_calculate_node_count(config->mem_block_symbols * sizeof(rmt_symbol_word_t), buffer_alignment, DMA_DESCRIPTOR_BUFFER_MAX_SIZE); rx_channel->num_dma_nodes = MAX(2, rx_channel->num_dma_nodes); // at least 2 DMA nodes for ping-pong diff --git a/components/esp_driver_rmt/src/rmt_tx.c b/components/esp_driver_rmt/src/rmt_tx.c index f765a02f053..af6b8ec5e71 100644 --- a/components/esp_driver_rmt/src/rmt_tx.c +++ b/components/esp_driver_rmt/src/rmt_tx.c @@ -53,9 +53,10 @@ static esp_err_t rmt_tx_init_dma_link(rmt_tx_channel_t *tx_channel, const rmt_tx // register the DMA callbacks may fail if the interrupt service can not be installed successfully ESP_RETURN_ON_ERROR(gdma_register_tx_event_callbacks(tx_channel->base.dma_chan, &cbs, tx_channel), TAG, "register DMA callbacks failed"); - size_t int_alignment = 0; + gdma_channel_alignment_info_t align_info; // get the alignment requirement from DMA - gdma_get_channel_alignment_constraints(tx_channel->base.dma_chan, &int_alignment, NULL, NULL); + gdma_get_channel_alignment_constraints(tx_channel->base.dma_chan, &align_info); + size_t int_alignment = align_info.int_mem_alignment; // apply RMT hardware alignment requirement int_alignment = MAX(int_alignment, sizeof(rmt_symbol_word_t)); // the memory returned by `heap_caps_aligned_calloc` also meets the cache alignment requirement (both address and size) diff --git a/components/esp_driver_spi/src/gpspi/spi_common.c b/components/esp_driver_spi/src/gpspi/spi_common.c index ae6714e43aa..861d136ac5c 100644 --- a/components/esp_driver_spi/src/gpspi/spi_common.c +++ b/components/esp_driver_spi/src/gpspi/spi_common.c @@ -320,8 +320,14 @@ static esp_err_t alloc_dma_chan(spi_host_device_t host_id, spi_dma_chan_t dma_ch ESP_RETURN_ON_ERROR(gdma_config_transfer(dma_ctx->rx_dma_chan, &trans_cfg), SPI_TAG, "config gdma rx transfer failed"); // Get DMA alignment constraints - gdma_get_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); + gdma_channel_alignment_info_t tx_align_info; + gdma_get_channel_alignment_constraints(dma_ctx->tx_dma_chan, &tx_align_info); + dma_ctx->dma_align_tx_int = tx_align_info.int_mem_alignment; + dma_ctx->dma_align_tx_ext = tx_align_info.ext_enc_mem_alignment; + gdma_channel_alignment_info_t rx_align_info; + gdma_get_channel_alignment_constraints(dma_ctx->rx_dma_chan, &rx_align_info); + dma_ctx->dma_align_rx_int = rx_align_info.int_mem_alignment; + dma_ctx->dma_align_rx_ext = rx_align_info.ext_enc_mem_alignment; } return ret; } diff --git a/components/esp_driver_uart/src/uhci.c b/components/esp_driver_uart/src/uhci.c index 042e9c736ed..bcdd108842e 100644 --- a/components/esp_driver_uart/src/uhci.c +++ b/components/esp_driver_uart/src/uhci.c @@ -211,9 +211,9 @@ static esp_err_t uhci_gdma_initialize(uhci_controller_handle_t uhci_ctrl, const gdma_apply_strategy(uhci_ctrl->tx_dir.dma_chan, &strategy_config); // create DMA link list - 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); + gdma_channel_alignment_info_t tx_align_info; + gdma_get_channel_alignment_constraints(uhci_ctrl->tx_dir.dma_chan, &tx_align_info); + size_t buffer_alignment = MAX(tx_align_info.int_mem_alignment, tx_align_info.ext_enc_mem_alignment); // Given that the combined size of all buffers does not exceed `max_transmit_size` and // the number of buffers does not exceed `max_transmit_buffer_count`, a single transfer // requires at most `esp_dma_calculate_node_count(max_transmit_size) + max_transmit_buffer_count - 1` DMA descriptors. @@ -236,9 +236,9 @@ static esp_err_t uhci_gdma_initialize(uhci_controller_handle_t uhci_ctrl, const gdma_connect(uhci_ctrl->rx_dir.dma_chan, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UHCI, 0)); ESP_RETURN_ON_ERROR(gdma_config_transfer(uhci_ctrl->rx_dir.dma_chan, &transfer_cfg), TAG, "Config DMA rx channel transfer failed"); - 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); + gdma_channel_alignment_info_t rx_align_info; + gdma_get_channel_alignment_constraints(uhci_ctrl->rx_dir.dma_chan, &rx_align_info); + buffer_alignment = MAX(rx_align_info.int_mem_alignment, rx_align_info.ext_enc_mem_alignment); uhci_ctrl->rx_dir.rx_num_dma_nodes = esp_dma_calculate_node_count(config->max_receive_internal_mem, buffer_alignment, DMA_DESCRIPTOR_BUFFER_MAX_SIZE); dma_link_config.num_items = uhci_ctrl->rx_dir.rx_num_dma_nodes; ESP_RETURN_ON_ERROR(gdma_new_link_list(&dma_link_config, &uhci_ctrl->rx_dir.dma_link), TAG, "DMA rx link list alloc failed"); @@ -284,11 +284,10 @@ 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); - buffer_alignment = gdma_get_buffer_alignment_constraint(uhci_ctrl->tx_dir.dma_chan, trans->buf_info[i].write_buffer); + size_t buffer_alignment = gdma_get_buffer_alignment_constraint(uhci_ctrl->tx_dir.dma_chan, trans->buf_info[i].write_buffer); mount_configs[i] = (gdma_buffer_mount_config_t) { .buffer = (void *)trans->buf_info[i].write_buffer, .buffer_alignment = buffer_alignment, @@ -367,7 +366,6 @@ 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 = 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, @@ -375,7 +373,6 @@ static esp_err_t uhci_receive_internal(uhci_controller_handle_t uhci_ctrl, uint8 .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]); 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 index c12617cde09..52d7d83a1a1 100644 --- 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 @@ -30,7 +30,7 @@ size_t esp_mspi_get_alignment(const void *ptr) 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)) { + if ((generic_query || is_drom || is_psram_enc) && esp_efuse_is_flash_encryption_enabled()) { alignment = MAX(alignment, MSPI_FLASH_ENC_ALIGNMENT); } 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 ddc24156449..4796da73465 100644 --- a/components/esp_lcd/i80/esp_lcd_panel_io_i80.c +++ b/components/esp_lcd/i80/esp_lcd_panel_io_i80.c @@ -678,7 +678,10 @@ static esp_err_t lcd_i80_init_dma_link(esp_lcd_i80_bus_handle_t bus, const esp_l .access_ext_mem = true, // the LCD can carry pixel buffer from the external memory }; ESP_RETURN_ON_ERROR(gdma_config_transfer(bus->dma_chan, &trans_cfg), TAG, "config DMA transfer failed"); - gdma_get_channel_alignment_constraints(bus->dma_chan, &bus->int_mem_align, &bus->ext_mem_align, NULL); + gdma_channel_alignment_info_t align_info; + gdma_get_channel_alignment_constraints(bus->dma_chan, &align_info); + bus->int_mem_align = align_info.int_mem_alignment; + bus->ext_mem_align = align_info.ext_enc_mem_alignment; size_t buffer_alignment = MAX(bus->int_mem_align, bus->ext_mem_align); size_t num_dma_nodes = esp_dma_calculate_node_count(bus->max_transfer_bytes, buffer_alignment, LCD_DMA_DESCRIPTOR_BUFFER_MAX_SIZE); diff --git a/components/esp_lcd/rgb/esp_lcd_panel_rgb.c b/components/esp_lcd/rgb/esp_lcd_panel_rgb.c index b868cd39a04..d4dd8c37b73 100644 --- a/components/esp_lcd/rgb/esp_lcd_panel_rgb.c +++ b/components/esp_lcd/rgb/esp_lcd_panel_rgb.c @@ -1244,7 +1244,10 @@ static esp_err_t lcd_rgb_create_dma_channel(esp_rgb_panel_t *rgb_panel) }; ESP_RETURN_ON_ERROR(gdma_config_transfer(rgb_panel->dma_chan, &trans_cfg), TAG, "config DMA transfer failed"); // get the memory alignment required by the DMA - gdma_get_channel_alignment_constraints(rgb_panel->dma_chan, &rgb_panel->int_mem_align, &rgb_panel->ext_mem_align, NULL); + gdma_channel_alignment_info_t align_info; + gdma_get_channel_alignment_constraints(rgb_panel->dma_chan, &align_info); + rgb_panel->int_mem_align = align_info.int_mem_alignment; + rgb_panel->ext_mem_align = align_info.ext_enc_mem_alignment; // register DMA event callbacks gdma_tx_event_callbacks_t cbs = { @@ -1358,7 +1361,9 @@ static esp_err_t lcd_rgb_panel_init_trans_link(esp_rgb_panel_t *rgb_panel) .buffer = rgb_panel->fbs[0] + restart_skip_bytes, .buffer_alignment = buffer_alignment, .length = MIN(LCD_DMA_DESCRIPTOR_BUFFER_MAX_SIZE, rgb_panel->fb_size) - restart_skip_bytes, - .flags.bypass_buffer_align_check = true, // the restart buffer may doesn't match the buffer alignment but it doesn't really matter in this case + // the restart buffer may doesn't match the buffer alignment but it doesn't really matter in this case + .flags.bypass_buffer_addr_align_check = true, + .flags.bypass_buffer_size_align_check = true, }; ESP_RETURN_ON_ERROR(gdma_link_mount_buffers(rgb_panel->dma_restart_link, 0, &restart_buffer_mount_cfg, 1, NULL), TAG, "mount DMA restart buffer failed"); diff --git a/components/mbedtls/test_apps/mbedtls_ut/include/crypto_performance.h b/components/mbedtls/test_apps/mbedtls_ut/include/crypto_performance.h index 1eb7a87dd8b..747156279db 100644 --- a/components/mbedtls/test_apps/mbedtls_ut/include/crypto_performance.h +++ b/components/mbedtls/test_apps/mbedtls_ut/include/crypto_performance.h @@ -35,7 +35,7 @@ #define IDF_PERFORMANCE_MIN_AES_GCM_UPDATE_THROUGHPUT_MBSEC 2.1 // SHA256 hardware throughput at 240MHz, threshold set lower than worst case -#define IDF_PERFORMANCE_MIN_SHA256_THROUGHPUT_MBSEC 90.0 +#define IDF_PERFORMANCE_MIN_SHA256_THROUGHPUT_MBSEC 88.0 // esp_sha() time to process 32KB of input data from RAM #define IDF_PERFORMANCE_MAX_TIME_SHA1_32KB 900 #define IDF_PERFORMANCE_MAX_TIME_SHA512_32KB 900