diff --git a/components/esp_mm/Kconfig b/components/esp_mm/Kconfig new file mode 100644 index 0000000000..b9cb3c5d3b --- /dev/null +++ b/components/esp_mm/Kconfig @@ -0,0 +1,31 @@ +menu "ESP-MM: Memory Management Configurations" + + config ESP_MM_CACHE_MSYNC_C2M_CHUNKED_OPS + depends on SOC_CACHE_WRITEBACK_SUPPORTED + bool "Enable esp_cache_msync C2M chunked operation" + help + `esp_cache_msync` C2M direction takes critical sections, which means during + the operation, the interrupts are disabled. Whereas Cache writebacks for + large buffers could be especially time intensive, and might cause interrupts + to be disabled for a significant amount of time. + + Sometimes you want other ISRs to be responded during this C2M process. + This option is to slice one C2M operation into multiple chunks, + with CONFIG_ESP_MM_CACHE_MSYNC_C2M_CHUNKED_OPS_MAX_LEN max len. This will give you + a breath during the C2M process as sometimes the C2M process is quite long. + + Note if the buffer processed by the `esp_cache_msync` (C2M sliced) is interrupted by an ISR, + and this ISR also accesses this buffer, this may lead to data coherence issue. + + config ESP_MM_CACHE_MSYNC_C2M_CHUNKED_OPS_MAX_LEN + hex "Max len in bytes per C2M chunk" + depends on ESP_MM_CACHE_MSYNC_C2M_CHUNKED_OPS + range 0 0x80000 + default 0x20000 if IDF_TARGET_ESP32P4 + default 0x2000 if IDF_TARGET_ESP32S2 + default 0x8000 if IDF_TARGET_ESP32S3 + help + Max len in bytes per C2M chunk, operations with size over the max len will be + sliced into multiple chunks. + +endmenu diff --git a/components/esp_mm/esp_cache.c b/components/esp_mm/esp_cache.c index b87925bb7d..ea0cbfbedc 100644 --- a/components/esp_mm/esp_cache.c +++ b/components/esp_mm/esp_cache.c @@ -7,9 +7,11 @@ #include #include #include +#include "sys/lock.h" #include "sdkconfig.h" #include "esp_check.h" #include "esp_log.h" +#include "freertos/FreeRTOS.h" #include "esp_heap_caps.h" #include "esp_rom_caps.h" #include "soc/soc_caps.h" @@ -26,6 +28,58 @@ static const char *TAG = "cache"; #define ALIGN_UP_BY(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) DEFINE_CRIT_SECTION_LOCK_STATIC(s_spinlock); +#if CONFIG_ESP_MM_CACHE_MSYNC_C2M_CHUNKED_OPS +static _lock_t s_mutex; +#endif + +#if SOC_CACHE_WRITEBACK_SUPPORTED +static void s_c2m_ops(uint32_t vaddr, size_t size) +{ +#if CONFIG_ESP_MM_CACHE_MSYNC_C2M_CHUNKED_OPS + if (!xPortInIsrContext()) { + bool valid = true; + size_t offset = 0; + while (offset < size) { + size_t chunk_len = ((size - offset) > CONFIG_ESP_MM_CACHE_MSYNC_C2M_CHUNKED_OPS_MAX_LEN) ? CONFIG_ESP_MM_CACHE_MSYNC_C2M_CHUNKED_OPS_MAX_LEN : (size - offset); + esp_os_enter_critical_safe(&s_spinlock); + valid &= cache_hal_writeback_addr(vaddr + offset, chunk_len); + esp_os_exit_critical_safe(&s_spinlock); + offset += chunk_len; + } + assert(valid); + } else +#endif + { + bool valid = false; + esp_os_enter_critical_safe(&s_spinlock); + valid = cache_hal_writeback_addr(vaddr, size); + esp_os_exit_critical_safe(&s_spinlock); + assert(valid); + } +} +#endif + +//no ops if ISR context or critical section context +static void s_acquire_mutex_from_task_context(void) +{ +#if CONFIG_ESP_MM_CACHE_MSYNC_C2M_CHUNKED_OPS + if (xPortCanYield()) { + _lock_acquire(&s_mutex); + ESP_LOGD(TAG, "mutex is taken"); + } +#endif //#if CONFIG_ESP_MM_CACHE_MSYNC_C2M_CHUNKED_OPS +} + +//no ops if ISR context or critical section context +static void s_release_mutex_from_task_context(void) +{ +#if CONFIG_ESP_MM_CACHE_MSYNC_C2M_CHUNKED_OPS + if (xPortCanYield()) { + _lock_release(&s_mutex); + ESP_LOGD(TAG, "mutex is free"); + } +#endif //#if CONFIG_ESP_MM_CACHE_MSYNC_C2M_CHUNKED_OPS +} esp_err_t esp_cache_msync(void *addr, size_t size, int flags) { @@ -57,6 +111,7 @@ esp_err_t esp_cache_msync(void *addr, size_t size, int flags) ESP_RETURN_ON_FALSE_ISR(aligned_addr, ESP_ERR_INVALID_ARG, TAG, "start address: 0x%" PRIx32 ", or the size: 0x%" PRIx32 " is(are) not aligned with cache line size (0x%" PRIx32 ")B", (uint32_t)addr, (uint32_t)size, cache_line_size); } + s_acquire_mutex_from_task_context(); if (flags & ESP_CACHE_MSYNC_FLAG_DIR_M2C) { ESP_EARLY_LOGV(TAG, "M2C DIR"); @@ -76,11 +131,7 @@ esp_err_t esp_cache_msync(void *addr, size_t size, int flags) } #if SOC_CACHE_WRITEBACK_SUPPORTED - - esp_os_enter_critical_safe(&s_spinlock); - valid = cache_hal_writeback_addr(vaddr, size); - esp_os_exit_critical_safe(&s_spinlock); - assert(valid); + s_c2m_ops(vaddr, size); if (flags & ESP_CACHE_MSYNC_FLAG_INVALIDATE) { esp_os_enter_critical_safe(&s_spinlock); @@ -88,8 +139,9 @@ esp_err_t esp_cache_msync(void *addr, size_t size, int flags) esp_os_exit_critical_safe(&s_spinlock); } assert(valid); -#endif +#endif //#if SOC_CACHE_WRITEBACK_SUPPORTED } + s_release_mutex_from_task_context(); return ESP_OK; } diff --git a/components/esp_mm/test_apps/components/test_mm_utils/test_cache_utils.c b/components/esp_mm/test_apps/components/test_mm_utils/test_cache_utils.c index 964dc576df..92b2215853 100644 --- a/components/esp_mm/test_apps/components/test_mm_utils/test_cache_utils.c +++ b/components/esp_mm/test_apps/components/test_mm_utils/test_cache_utils.c @@ -13,7 +13,7 @@ const static char *TAG = "cache_utils"; esp_err_t test_set_buffer_dirty(intptr_t vaddr_start, size_t size) { if (((vaddr_start % 32) != 0) || ((size % 32) != 0)) { - ESP_LOGE(TAG, "addr not 4B aligned"); + ESP_LOGE(TAG, "addr or size not 4B aligned"); return ESP_ERR_INVALID_ARG; } diff --git a/components/esp_mm/test_apps/mm/main/test_cache_msync.c b/components/esp_mm/test_apps/mm/main/test_cache_msync.c index deffc6e051..c4c5d68330 100644 --- a/components/esp_mm/test_apps/mm/main/test_cache_msync.c +++ b/components/esp_mm/test_apps/mm/main/test_cache_msync.c @@ -33,12 +33,14 @@ const static char *TAG = "CACHE_TEST"; #define TEST_OFFSET 0x100000 #if CONFIG_IDF_TARGET_ESP32S2 #define TEST_SYNC_START (SOC_DPORT_CACHE_ADDRESS_LOW + TEST_OFFSET) +#define TEST_SYNC_SIZE CONFIG_ESP32S2_DATA_CACHE_SIZE #elif CONFIG_IDF_TARGET_ESP32S3 #define TEST_SYNC_START (SOC_DRAM0_CACHE_ADDRESS_LOW + TEST_OFFSET) +#define TEST_SYNC_SIZE CONFIG_ESP32S3_DATA_CACHE_SIZE #elif CONFIG_IDF_TARGET_ESP32P4 #define TEST_SYNC_START (SOC_DRAM_PSRAM_ADDRESS_LOW + TEST_OFFSET) +#define TEST_SYNC_SIZE CONFIG_CACHE_L2_CACHE_SIZE #endif -#define TEST_SYNC_SIZE 0x8000 #define RECORD_TIME_PREPARE() uint32_t __t1, __t2 #define RECORD_TIME_START() do {__t1 = esp_cpu_get_cycle_count();} while(0) diff --git a/components/esp_system/ld/esp32s2/memory.ld.in b/components/esp_system/ld/esp32s2/memory.ld.in index 8a732b2b09..ec64034637 100644 --- a/components/esp_system/ld/esp32s2/memory.ld.in +++ b/components/esp_system/ld/esp32s2/memory.ld.in @@ -15,20 +15,6 @@ #include "sdkconfig.h" #include "ld.common" -#ifdef CONFIG_ESP32S2_INSTRUCTION_CACHE_8KB -#define CONFIG_ESP32S2_INSTRUCTION_CACHE_SIZE 0x2000 -#else -#define CONFIG_ESP32S2_INSTRUCTION_CACHE_SIZE 0x4000 -#endif - -#ifdef CONFIG_ESP32S2_DATA_CACHE_0KB -#define CONFIG_ESP32S2_DATA_CACHE_SIZE 0 -#elif defined CONFIG_ESP32S2_DATA_CACHE_8KB -#define CONFIG_ESP32S2_DATA_CACHE_SIZE 0x2000 -#else -#define CONFIG_ESP32S2_DATA_CACHE_SIZE 0x4000 -#endif - #define RAM_IRAM_START 0x40020000 #define RAM_DRAM_START 0x3FFB0000 diff --git a/components/esp_system/port/soc/esp32s2/Kconfig.cache b/components/esp_system/port/soc/esp32s2/Kconfig.cache index fcb244ed31..06f0ebcdd1 100644 --- a/components/esp_system/port/soc/esp32s2/Kconfig.cache +++ b/components/esp_system/port/soc/esp32s2/Kconfig.cache @@ -14,6 +14,11 @@ menu "Cache config" bool "16KB" endchoice + config ESP32S2_INSTRUCTION_CACHE_SIZE + hex + default 0x2000 if ESP32S2_INSTRUCTION_CACHE_8KB + default 0x4000 if ESP32S2_INSTRUCTION_CACHE_16KB + choice ESP32S2_INSTRUCTION_CACHE_LINE_SIZE prompt "Instruction cache line size" default ESP32S2_INSTRUCTION_CACHE_LINE_32B @@ -43,6 +48,12 @@ menu "Cache config" bool "16KB" endchoice + config ESP32S2_DATA_CACHE_SIZE + hex + default 0 if ESP32S2_DATA_CACHE_0KB + default 0x2000 if ESP32S2_DATA_CACHE_8KB + default 0x4000 if ESP32S2_DATA_CACHE_16KB + choice ESP32S2_DATA_CACHE_LINE_SIZE prompt "Data cache line size" default ESP32S2_DATA_CACHE_LINE_32B