Skip to content

Commit

Permalink
Merge branch 'feat/cache_msync_c2m_sliced_ops' into 'master'
Browse files Browse the repository at this point in the history
cache: supported msync c2m sliced ops

Closes IDF-10510 and IDF-10414

See merge request espressif/esp-idf!32155
  • Loading branch information
Icarus113 committed Jul 29, 2024
2 parents c7d15c1 + 23fcfca commit 23ee0b6
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 22 deletions.
31 changes: 31 additions & 0 deletions components/esp_mm/Kconfig
Original file line number Diff line number Diff line change
@@ -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
64 changes: 58 additions & 6 deletions components/esp_mm/esp_cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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)
{
Expand Down Expand Up @@ -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");

Expand All @@ -76,20 +131,17 @@ 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);
valid &= cache_hal_invalidate_addr(vaddr, size);
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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
4 changes: 3 additions & 1 deletion components/esp_mm/test_apps/mm/main/test_cache_msync.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
14 changes: 0 additions & 14 deletions components/esp_system/ld/esp32s2/memory.ld.in
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
11 changes: 11 additions & 0 deletions components/esp_system/port/soc/esp32s2/Kconfig.cache
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 23ee0b6

Please sign in to comment.