Merge branch 'feat/cache_msync_c2m_sliced_ops' into 'master'

cache: supported msync c2m sliced ops

Closes IDF-10510 and IDF-10414

See merge request espressif/esp-idf!32155
This commit is contained in:
Armando (Dou Yiwen) 2024-07-29 11:37:24 +08:00
commit 23ee0b65ff
6 changed files with 104 additions and 22 deletions

31
components/esp_mm/Kconfig Normal file
View File

@ -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

View File

@ -7,9 +7,11 @@
#include <sys/param.h>
#include <inttypes.h>
#include <string.h>
#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;
}

View File

@ -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;
}

View File

@ -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)

View File

@ -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

View File

@ -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