diff --git a/components/esp_driver_uart/CMakeLists.txt b/components/esp_driver_uart/CMakeLists.txt index bfca544594..6f1bc93c21 100644 --- a/components/esp_driver_uart/CMakeLists.txt +++ b/components/esp_driver_uart/CMakeLists.txt @@ -13,7 +13,7 @@ endif() if(${target} STREQUAL "linux") set(priv_requires esp_ringbuf) else() - set(priv_requires esp_pm esp_driver_gpio esp_ringbuf esp_mm) + set(priv_requires esp_pm esp_driver_gpio esp_ringbuf esp_mm esp_psram) endif() idf_component_register( diff --git a/components/esp_driver_uart/src/uhci.c b/components/esp_driver_uart/src/uhci.c index e390d047c9..be937d796d 100644 --- a/components/esp_driver_uart/src/uhci.c +++ b/components/esp_driver_uart/src/uhci.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -34,6 +34,7 @@ #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" #include "esp_cache.h" @@ -107,6 +108,7 @@ static bool uhci_gdma_rx_callback_done(gdma_channel_handle_t dma_chan, gdma_even { bool need_yield = false; uhci_controller_handle_t uhci_ctrl = (uhci_controller_handle_t) user_data; + bool is_buf_from_psram = esp_ptr_external_ram(uhci_ctrl->rx_dir.buffer_pointers[uhci_ctrl->rx_dir.node_index]); // If the data is not all received, handle it in not normal_eof block. Otherwise, in eof block. if (!event_data->flags.normal_eof) { size_t rx_size = uhci_ctrl->rx_dir.buffer_size_per_desc_node[uhci_ctrl->rx_dir.node_index]; @@ -116,9 +118,8 @@ static bool uhci_gdma_rx_callback_done(gdma_channel_handle_t dma_chan, gdma_even .flags.totally_received = false, }; - bool need_cache_sync = esp_ptr_internal(uhci_ctrl->rx_dir.buffer_pointers[uhci_ctrl->rx_dir.node_index]) ? (uhci_ctrl->int_mem_cache_line_size > 0) : (uhci_ctrl->ext_mem_cache_line_size > 0); - if (need_cache_sync) { - esp_cache_msync(uhci_ctrl->rx_dir.buffer_pointers[uhci_ctrl->rx_dir.node_index], rx_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); + if (is_buf_from_psram) { + esp_psram_mspi_mb(); } if (uhci_ctrl->rx_dir.on_rx_trans_event) { need_yield |= uhci_ctrl->rx_dir.on_rx_trans_event(uhci_ctrl, &evt_data, uhci_ctrl->user_data); @@ -138,10 +139,8 @@ static bool uhci_gdma_rx_callback_done(gdma_channel_handle_t dma_chan, gdma_even .flags.totally_received = true, }; - bool need_cache_sync = esp_ptr_internal(uhci_ctrl->rx_dir.buffer_pointers[uhci_ctrl->rx_dir.node_index]) ? (uhci_ctrl->int_mem_cache_line_size > 0) : (uhci_ctrl->ext_mem_cache_line_size > 0); - size_t m2c_size = UHCI_ALIGN_UP(rx_size, uhci_ctrl->rx_dir.cache_line); - if (need_cache_sync) { - esp_cache_msync(uhci_ctrl->rx_dir.buffer_pointers[uhci_ctrl->rx_dir.node_index], m2c_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); + if (is_buf_from_psram) { + esp_psram_mspi_mb(); } // release power manager lock if (uhci_ctrl->pm_lock) { @@ -347,6 +346,13 @@ esp_err_t uhci_receive(uhci_controller_handle_t uhci_ctrl, uint8_t *read_buffer, gdma_link_mount_buffers(uhci_ctrl->rx_dir.dma_link, 0, mount_configs, node_count, NULL); + // 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) { + ESP_RETURN_ON_ERROR(esp_cache_msync(mount_configs[0].buffer, usable_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C), TAG, "cache sync failed"); + } + gdma_reset(uhci_ctrl->rx_dir.dma_chan); gdma_start(uhci_ctrl->rx_dir.dma_chan, gdma_link_get_head_addr(uhci_ctrl->rx_dir.dma_link)); diff --git a/components/esp_mm/esp_cache_msync.c b/components/esp_mm/esp_cache_msync.c index d296da1a9b..c94cb97eec 100644 --- a/components/esp_mm/esp_cache_msync.c +++ b/components/esp_mm/esp_cache_msync.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 */ diff --git a/components/esp_mm/include/esp_cache.h b/components/esp_mm/include/esp_cache.h index f8efcb979c..03db92f341 100644 --- a/components/esp_mm/include/esp_cache.h +++ b/components/esp_mm/include/esp_cache.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/components/esp_mm/include/esp_private/esp_cache_private.h b/components/esp_mm/include/esp_private/esp_cache_private.h index fc09ed44f1..2cd10d069c 100644 --- a/components/esp_mm/include/esp_private/esp_cache_private.h +++ b/components/esp_mm/include/esp_private/esp_cache_private.h @@ -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 */ diff --git a/components/esp_psram/CMakeLists.txt b/components/esp_psram/CMakeLists.txt index 0b7f4c961e..53e5f00d23 100644 --- a/components/esp_psram/CMakeLists.txt +++ b/components/esp_psram/CMakeLists.txt @@ -15,7 +15,7 @@ if(${target} STREQUAL "esp32") list(APPEND priv_requires bootloader_support esp_driver_spi esp_driver_gpio) endif() -set(srcs) +set(srcs "system_layer/esp_psram_mspi.c") if(CONFIG_SPIRAM) list(APPEND srcs "system_layer/esp_psram.c") diff --git a/components/esp_psram/include/esp_private/esp_psram_mspi.h b/components/esp_psram/include/esp_private/esp_psram_mspi.h new file mode 100644 index 0000000000..7610d99c14 --- /dev/null +++ b/components/esp_psram/include/esp_private/esp_psram_mspi.h @@ -0,0 +1,34 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "sdkconfig.h" +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ESP_PSRAM_MSPI_MB_WORKAROUND (CONFIG_IDF_TARGET_ESP32C5 && CONFIG_ESP32C5_REV_MIN_FULL < 102) || (CONFIG_IDF_TARGET_ESP32C61 && CONFIG_ESP32C61_REV_MIN_FULL < 101) + +/** + * @brief Initialize PSRAM MSPI memory barrier + * + * @return ESP_OK on success, otherwise an error code + */ +esp_err_t esp_psram_mspi_mb_init(void); + +/** + * @brief PSRAM MSPI memory barrier + */ +void esp_psram_mspi_mb(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_psram/system_layer/esp_psram.c b/components/esp_psram/system_layer/esp_psram.c index f236e03b96..076c5b9287 100644 --- a/components/esp_psram/system_layer/esp_psram.c +++ b/components/esp_psram/system_layer/esp_psram.c @@ -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 */ @@ -28,6 +28,7 @@ #include "esp_private/esp_psram_extram.h" #include "esp_private/esp_mmu_map_private.h" #include "esp_private/esp_psram_impl.h" +#include "esp_private/esp_psram_mspi.h" #include "esp_private/startup_internal.h" #if SOC_SPIRAM_XIP_SUPPORTED #include "esp_private/mmu_psram_flash.h" @@ -113,6 +114,8 @@ static const DRAM_ATTR char TAG[] = "esp_psram"; ESP_SYSTEM_INIT_FN(add_psram_to_heap, CORE, BIT(0), 103) { + esp_err_t ret = ESP_FAIL; + #if CONFIG_SPIRAM_BOOT_INIT && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC) #if (CONFIG_IDF_TARGET_ESP32C5 && CONFIG_ESP32C5_REV_MIN_FULL <= 100) || (CONFIG_IDF_TARGET_ESP32C61 && CONFIG_ESP32C61_REV_MIN_FULL <= 100) @@ -120,17 +123,24 @@ ESP_SYSTEM_INIT_FN(add_psram_to_heap, CORE, BIT(0), 103) ESP_EARLY_LOGW(TAG, "Please avoid using PSRAM for security sensitive data e.g., TLS stack allocations (CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC)"); #endif if (esp_psram_is_initialized()) { - esp_err_t r = esp_psram_extram_add_to_heap_allocator(); - if (r != ESP_OK) { + ret = esp_psram_extram_add_to_heap_allocator(); + if (ret != ESP_OK) { ESP_EARLY_LOGE(TAG, "External RAM could not be added to heap!"); - abort(); + return ret; } #if CONFIG_SPIRAM_USE_MALLOC heap_caps_malloc_extmem_enable(CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL); #endif } #endif - return ESP_OK; + + ret = esp_psram_mspi_mb_init(); + if (ret != ESP_OK) { + ESP_EARLY_LOGE(TAG, "Failed to initialize PSRAM MSPI memory barrier!"); + return ret; + } + + return ret; } #if CONFIG_IDF_TARGET_ESP32 diff --git a/components/esp_psram/system_layer/esp_psram_mspi.c b/components/esp_psram/system_layer/esp_psram_mspi.c new file mode 100644 index 0000000000..cb62d14729 --- /dev/null +++ b/components/esp_psram/system_layer/esp_psram_mspi.c @@ -0,0 +1,50 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD +* +* SPDX-License-Identifier: Apache-2.0 +*/ + +#include +#include +#include +#include +#include "sdkconfig.h" +#include "esp_attr.h" +#include "esp_log.h" +#include "esp_check.h" +#include "esp_intr_alloc.h" +#include "esp_cache.h" +#include "esp_heap_caps.h" +#include "esp_private/esp_psram_mspi.h" + +__attribute__((unused)) static DRAM_ATTR char TAG[] = "MSPI Timing"; + +#if ESP_PSRAM_MSPI_MB_WORKAROUND +static void *s_psram_mb_dummy_cacheline; //dummy cacheline for cache memory barrier +#endif + +esp_err_t esp_psram_mspi_mb_init(void) +{ +#if ESP_PSRAM_MSPI_MB_WORKAROUND + s_psram_mb_dummy_cacheline = heap_caps_calloc(1, CONFIG_CACHE_L1_CACHE_LINE_SIZE, MALLOC_CAP_SPIRAM | MALLOC_CAP_CACHE_ALIGNED); + if (!s_psram_mb_dummy_cacheline) { + ESP_EARLY_LOGE(TAG, "Failed to allocate dummy cacheline for PSRAM memory barrier!"); + } +#endif + + return ESP_OK; +} + +void IRAM_ATTR esp_psram_mspi_mb(void) +{ +#if ESP_PSRAM_MSPI_MB_WORKAROUND + if (s_psram_mb_dummy_cacheline) { + uint32_t *p = (uint32_t *)s_psram_mb_dummy_cacheline; + *p = (*p + 1) % UINT32_MAX; + __attribute__((unused)) esp_err_t ret = ESP_FAIL; + ret = esp_cache_msync(s_psram_mb_dummy_cacheline, sizeof(uint32_t), ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); //malloc is aligned, no need to writeback all + assert(ret == ESP_OK); + asm volatile("fence"); + } +#endif +} diff --git a/components/esp_psram/test_apps/psram/pytest_psram.py b/components/esp_psram/test_apps/psram/pytest_psram.py index 2cf712373d..124446d4ba 100644 --- a/components/esp_psram/test_apps/psram/pytest_psram.py +++ b/components/esp_psram/test_apps/psram/pytest_psram.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: CC0-1.0 import pytest from pytest_embedded import Dut