Merge branch 'feature/esp_cache_msync' into 'master'

esp_mm: esp_cache_msync, a cache synchronise API

Closes IDF-6587, IDF-6779, and IDF-6288

See merge request espressif/esp-idf!22380
This commit is contained in:
Armando (Dou Yiwen) 2023-02-28 15:17:49 +08:00
commit a9ce76096d
43 changed files with 857 additions and 73 deletions

View File

@ -18,6 +18,7 @@
#include "esp_memory_utils.h"
#include "esp_private/periph_ctrl.h"
#include "gdma_priv.h"
#include "hal/cache_hal.h"
static const char *TAG = "gdma";
@ -290,6 +291,7 @@ esp_err_t gdma_set_transfer_ability(gdma_channel_handle_t dma_chan, const gdma_t
ESP_GOTO_ON_FALSE((sram_alignment & (sram_alignment - 1)) == 0, ESP_ERR_INVALID_ARG, err, TAG, "invalid sram alignment: %zu", sram_alignment);
#if SOC_GDMA_SUPPORT_PSRAM
uint32_t data_cache_line_size = cache_hal_get_cache_line_size(CACHE_TYPE_DATA);
int block_size_index = 0;
switch (psram_alignment) {
case 64: // 64 Bytes alignment
@ -303,12 +305,13 @@ esp_err_t gdma_set_transfer_ability(gdma_channel_handle_t dma_chan, const gdma_t
break;
case 0: // no alignment is requirement
block_size_index = GDMA_LL_EXT_MEM_BK_SIZE_16B;
psram_alignment = SOC_GDMA_PSRAM_MIN_ALIGN; // fall back to minimal alignment
psram_alignment = data_cache_line_size; // fall back to use the same size of the psram data cache line size
break;
default:
ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "invalid psram alignment: %zu", psram_alignment);
break;
}
ESP_GOTO_ON_FALSE(((psram_alignment % data_cache_line_size) == 0), ESP_ERR_INVALID_ARG, err, TAG, "psram alignment (%d)B should be multiple of the data cache line size (%d)B", psram_alignment, data_cache_line_size);
#endif // #if SOC_GDMA_SUPPORT_PSRAM
if (dma_chan->direction == GDMA_CHANNEL_DIRECTION_TX) {

View File

@ -7,7 +7,7 @@ set(srcs "src/esp_lcd_common.c"
"src/esp_lcd_panel_st7789.c"
"src/esp_lcd_panel_ops.c")
set(includes "include" "interface")
set(priv_requires "driver")
set(priv_requires "driver" "esp_mm")
if(CONFIG_SOC_I2S_LCD_I80_VARIANT)
list(APPEND srcs "src/esp_lcd_panel_io_i2s.c")

View File

@ -42,6 +42,7 @@
#include "hal/lcd_ll.h"
#include "hal/gdma_ll.h"
#include "rom/cache.h"
#include "esp_cache.h"
#if CONFIG_LCD_RGB_ISR_IRAM_SAFE
#define LCD_RGB_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_INTRDISABLED)
@ -798,7 +799,7 @@ static esp_err_t rgb_panel_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int
if (rgb_panel->flags.fb_in_psram && !rgb_panel->bb_size) {
// CPU writes data to PSRAM through DCache, data in PSRAM might not get updated, so write back
// Note that if we use a bounce buffer, the data gets read by the CPU as well so no need to write back
Cache_WriteBack_Addr((uint32_t)(flush_ptr), bytes_to_flush);
esp_cache_msync((void *)(flush_ptr), (size_t)bytes_to_flush, 0);
}
if (!rgb_panel->bb_size) {
@ -954,11 +955,7 @@ static IRAM_ATTR bool lcd_rgb_panel_fill_bounce_buffer(esp_rgb_panel_t *panel, u
if (panel->flags.bb_invalidate_cache) {
// We don't need the bytes we copied from the psram anymore
// Make sure that if anything happened to have changed (because the line already was in cache) we write the data back.
Cache_WriteBack_Addr((uint32_t)&panel->fbs[panel->bb_fb_index][panel->bounce_pos_px * bytes_per_pixel], panel->bb_size);
// Invalidate the data.
// Note: possible race: perhaps something on the other core can squeeze a write between this and the writeback,
// in which case that data gets discarded.
Cache_Invalidate_Addr((uint32_t)&panel->fbs[panel->bb_fb_index][panel->bounce_pos_px * bytes_per_pixel], panel->bb_size);
esp_cache_msync(&panel->fbs[panel->bb_fb_index][panel->bounce_pos_px * bytes_per_pixel], (size_t)panel->bb_size, ESP_CACHE_MSYNC_FLAG_INVALIDATE);
}
}
panel->bounce_pos_px += panel->bb_size / bytes_per_pixel;

View File

@ -9,16 +9,15 @@ set(srcs)
if(NOT CONFIG_APP_BUILD_TYPE_PURE_RAM_APP)
set(srcs "esp_mmu_map.c"
"port/${target}/ext_mem_layout.c")
"port/${target}/ext_mem_layout.c"
"esp_cache.c")
if(CONFIG_IDF_TARGET_ESP32)
list(APPEND srcs "cache_esp32.c")
endif()
endif()
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS ${includes}
PRIV_REQUIRES ${priv_requires})
if(NOT BOOTLOADER_BUILD)
if(CONFIG_SPIRAM)
# Use esp_psram for `esp_psram_extram_writeback_cache()` on ESP32
idf_component_optional_requires(PRIVATE esp_psram)
endif()
endif()
PRIV_REQUIRES ${priv_requires}
LDFRAGMENTS linker.lf)

View File

@ -0,0 +1,36 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <sys/param.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "rom/cache.h"
#include "esp_private/esp_cache_esp32_private.h"
static cache_driver_t s_cache_drv = {
Cache_Flush,
NULL,
};
void cache_register_writeback(cache_driver_t *func)
{
s_cache_drv.cache_writeback_psram = func->cache_writeback_psram;
}
void cache_sync(void)
{
if (s_cache_drv.cache_writeback_psram) {
s_cache_drv.cache_writeback_psram();
}
s_cache_drv.cache_flush(0);
#if !CONFIG_FREERTOS_UNICORE
s_cache_drv.cache_flush(1);
#endif // !CONFIG_FREERTOS_UNICORE
}

View File

@ -0,0 +1,81 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <sys/param.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "esp_check.h"
#include "esp_log.h"
#include "soc/soc_caps.h"
#include "hal/mmu_hal.h"
#include "hal/cache_hal.h"
#include "esp_cache.h"
#include "esp_private/critical_section.h"
static const char *TAG = "cache";
#if SOC_CACHE_WRITEBACK_SUPPORTED
DEFINE_CRIT_SECTION_LOCK_STATIC(s_spinlock);
void s_cache_freeze(void)
{
#if SOC_CACHE_FREEZE_SUPPORTED
cache_hal_freeze(CACHE_TYPE_DATA | CACHE_TYPE_INSTRUCTION);
#endif
/**
* For writeback supported, but the freeze not supported chip (Now only S2),
* as it's single core, the critical section is enough to prevent preemption from an non-IRAM ISR
*/
}
void s_cache_unfreeze(void)
{
#if SOC_CACHE_FREEZE_SUPPORTED
cache_hal_unfreeze(CACHE_TYPE_DATA | CACHE_TYPE_INSTRUCTION);
#endif
/**
* Similarly, for writeback supported, but the freeze not supported chip (Now only S2),
* we don't need to do more
*/
}
#endif //#if SOC_CACHE_WRITEBACK_SUPPORTED
esp_err_t esp_cache_msync(void *addr, size_t size, int flags)
{
ESP_RETURN_ON_FALSE_ISR(addr, ESP_ERR_INVALID_ARG, TAG, "null pointer");
ESP_RETURN_ON_FALSE_ISR(mmu_hal_check_valid_ext_vaddr_region(0, (uint32_t)addr, size, MMU_VADDR_DATA), ESP_ERR_INVALID_ARG, TAG, "invalid address");
#if SOC_CACHE_WRITEBACK_SUPPORTED
if ((flags & ESP_CACHE_MSYNC_FLAG_UNALIGNED) == 0) {
esp_os_enter_critical_safe(&s_spinlock);
uint32_t data_cache_line_size = cache_hal_get_cache_line_size(CACHE_TYPE_DATA);
esp_os_exit_critical_safe(&s_spinlock);
ESP_RETURN_ON_FALSE_ISR(((uint32_t)addr % data_cache_line_size) == 0, ESP_ERR_INVALID_ARG, TAG, "start address isn't aligned with the data cache line size (%d)B", data_cache_line_size);
ESP_RETURN_ON_FALSE_ISR((size % data_cache_line_size) == 0, ESP_ERR_INVALID_ARG, TAG, "size isn't aligned with the data cache line size (%d)B", data_cache_line_size);
ESP_RETURN_ON_FALSE_ISR((((uint32_t)addr + size) % data_cache_line_size) == 0, ESP_ERR_INVALID_ARG, TAG, "end address isn't aligned with the data cache line size (%d)B", data_cache_line_size);
}
uint32_t vaddr = (uint32_t)addr;
esp_os_enter_critical_safe(&s_spinlock);
s_cache_freeze();
cache_hal_writeback_addr(vaddr, size);
if (flags & ESP_CACHE_MSYNC_FLAG_INVALIDATE) {
cache_hal_invalidate_addr(vaddr, size);
}
s_cache_unfreeze();
esp_os_exit_critical_safe(&s_spinlock);
#endif
return ESP_OK;
}

View File

@ -23,14 +23,8 @@
#include "hal/mmu_hal.h"
#include "hal/mmu_ll.h"
#if CONFIG_IDF_TARGET_ESP32
#include "esp32/rom/cache.h"
#endif
#include "esp_private/cache_utils.h"
#if CONFIG_SPIRAM
#include "esp_private/esp_psram_extram.h"
#endif
#include "esp_private/esp_cache_esp32_private.h"
#include "esp_private/esp_mmu_map_private.h"
#include "ext_mem_layout.h"
#include "esp_mmu_map.h"
@ -333,29 +327,15 @@ esp_err_t esp_mmu_map_reserve_block_with_caps(size_t size, mmu_mem_caps_t caps,
}
#if CONFIG_IDF_TARGET_ESP32
/**
* On ESP32, due to hardware limitation, we don't have an
* easy way to sync between cache and external memory wrt
* certain range. So we do a full sync here
*/
static void IRAM_ATTR NOINLINE_ATTR s_cache_sync(void)
{
#if CONFIG_SPIRAM
esp_psram_extram_writeback_cache();
#endif //#if CONFIG_SPIRAM
Cache_Flush(0);
#if !CONFIG_FREERTOS_UNICORE
Cache_Flush(1);
#endif // !CONFIG_FREERTOS_UNICORE
}
#endif //#if CONFIG_IDF_TARGET_ESP32
static void IRAM_ATTR NOINLINE_ATTR s_do_cache_invalidate(uint32_t vaddr_start, uint32_t size)
{
#if CONFIG_IDF_TARGET_ESP32
s_cache_sync();
/**
* On ESP32, due to hardware limitation, we don't have an
* easy way to sync between cache and external memory wrt
* certain range. So we do a full sync here
*/
cache_sync();
#else //Other chips
cache_hal_invalidate_addr(vaddr_start, size);
#endif // CONFIG_IDF_TARGET_ESP32

View File

@ -0,0 +1,58 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdlib.h>
#include <stdint.h>
#include "esp_err.h"
#include "esp_bit_defs.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* Cache msync flags
*/
/**
* @brief Do an invalidation with the values that just written
*/
#define ESP_CACHE_MSYNC_FLAG_INVALIDATE BIT(0)
/**
* @brief Allow writeback a block that are not aligned to the data cache line size
*/
#define ESP_CACHE_MSYNC_FLAG_UNALIGNED BIT(1)
/**
* @brief Memory sync between Cache and external memory
*
* - For cache writeback supported chips (you can refer to SOC_CACHE_WRITEBACK_SUPPORTED in soc_caps.h)
* - this API will do a writeback to synchronise between cache and the PSRAM
* - with ESP_CACHE_MSYNC_FLAG_INVALIDATE, this API will also invalidate the values that just written
* - note: although ESP32 is with PSRAM, but cache writeback isn't supported, so this API will do nothing on ESP32
* - For other chips, this API will do nothing. The out-of-sync should be already dealt by the SDK
*
* This API is cache-safe and thread-safe
*
* @note You should not call this during any Flash operations (e.g. esp_flash APIs, nvs and some other APIs that are based on esp_flash APIs)
* @note If XIP_From_PSRAM is enabled (by enabling both CONFIG_SPIRAM_FETCH_INSTRUCTIONS and CONFIG_SPIRAM_RODATA), you can call this API during Flash operations
*
* @param[in] Starting address to do the msync
* @param[in] Size to do the msync
* @param[in] Flags, see `ESP_CACHE_MSYNC_FLAG_x`
*
* @return
* - ESP_OK:
* - Successful msync
* - If this chip doesn't support cache writeback, if the input addr is a cache supported one, this API will return ESP_OK
* - ESP_ERR_INVALID_ARG: Invalid argument, not cache supported addr, see printed logs
*/
esp_err_t esp_cache_msync(void *addr, size_t size, int flags);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,59 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdlib.h>
#include <stdint.h>
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Private header for cache drivers, where cache functionality requires other components
*
* @note Now only esp32, can be applied to other similar chips
*/
typedef struct cache_driver_s cache_driver_t;
/**
* @brief Cache driver
*/
struct cache_driver_s {
/**
* @brief Cache flush
*
* @param[in] cpu_no CPU id
*/
void (*cache_flush)(int cpu_no);
/**
* @brief Cache writeback to psram
*/
void (*cache_writeback_psram)(void);
};
/**
* @brief Register cache writeback
*
* @param[in] func Cache driver
*/
void cache_register_writeback(cache_driver_t *func);
/**
* @brief Cache sync
*
* @note This API only do cache sync, but doesn't guarantee concurrent access to cache
* @note Do not use in your application
*/
void cache_sync(void);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,9 @@
[mapping:esp_mm]
archive: libesp_mm.a
entries:
if APP_BUILD_TYPE_PURE_RAM_APP = n:
esp_cache (noflash)
if IDF_TARGET_ESP32 = y:
cache_esp32 (noflash)

View File

@ -4,13 +4,13 @@ cmake_minimum_required(VERSION 3.16)
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(mmap_test)
project(mm_test)
if(CONFIG_COMPILER_DUMP_RTL_FILES)
add_custom_target(check_test_app_sections ALL
COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py
--rtl-dir ${CMAKE_BINARY_DIR}/esp-idf/esp_mm/
--elf-file ${CMAKE_BINARY_DIR}/mmap_test.elf
--elf-file ${CMAKE_BINARY_DIR}/mm_test.elf
find-refs
--from-sections=.iram0.text
--to-sections=.flash.text,.flash.rodata

View File

@ -0,0 +1,14 @@
set(srcs "test_app_main.c")
list(APPEND srcs "test_mmap.c")
if(CONFIG_SOC_CACHE_WRITEBACK_SUPPORTED)
list(APPEND srcs "test_cache_msync.c")
endif()
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
# the component can be registered as WHOLE_ARCHIVE
idf_component_register(SRCS ${srcs}
PRIV_REQUIRES test_utils spi_flash esp_mm driver esp_timer
WHOLE_ARCHIVE)

View File

@ -0,0 +1,66 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "unity.h"
#include "unity_test_utils.h"
#include "esp_heap_caps.h"
/**
* Hardware related tests, e.g.
* - traversing all vaddr range to check their attributes
*
* These tests need certain number of internal resources (heap memory), as they uses up the vaddr ranges
* On ESP32, it should be around 450
* On ESP32S2, it should be around 600
* On other chips, it should be around 400
*/
#define TEST_MEMORY_LEAK_THRESHOLD (-650)
static size_t before_free_8bit;
static size_t before_free_32bit;
static void check_leak(size_t before_free, size_t after_free, const char *type)
{
ssize_t delta = after_free - before_free;
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
}
void setUp(void)
{
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
}
void tearDown(void)
{
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
check_leak(before_free_8bit, after_free_8bit, "8BIT");
check_leak(before_free_32bit, after_free_32bit, "32BIT");
}
void app_main(void)
{
/*
_____ ___________ ___ ______ ___ ___ ______ _____ ___
| ___/ ___| ___ \ | \/ || \/ ||_ _| ___/ ___|_ _|
| |__ \ `--.| |_/ / | . . || . . | | | | |__ \ `--. | |
| __| `--. \ __/ | |\/| || |\/| | | | | __| `--. \ | |
| |___/\__/ / | | | | || | | | | | | |___/\__/ / | |
\____/\____/\_| \_| |_/\_| |_/ \_/ \____/\____/ \_/
*/
printf(" _____ ___________ ___ ______ ___ ___ ______ _____ ___\r\n");
printf("| ___/ ___| ___ \\ | \\/ || \\/ ||_ _| ___/ ___|_ _|\r\n");
printf("| |__ \\ `--.| |_/ / | . . || . . | | | | |__ \\ `--. | |\r\n");
printf("| __| `--. \\ __/ | |\\/| || |\\/| | | | | __| `--. \\ | |\r\n");
printf("| |___/\\__/ / | | | | || | | | | | | |___/\\__/ / | |\r\n");
printf("\\____/\\____/\\_| \\_| |_/\\_| |_/ \\_/ \\____/\\____/ \\_/\r\n");
unity_run_menu();
}

View File

@ -0,0 +1,207 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#include <sys/param.h>
#include <string.h>
#include "inttypes.h"
#include "esp_log.h"
#include "esp_attr.h"
#include "unity.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_rom_sys.h"
#include "esp_memory_utils.h"
#include "esp_heap_caps.h"
#include "esp_mmu_map.h"
#include "esp_cache.h"
#include "esp_timer.h"
#include "esp_partition.h"
#include "esp_flash.h"
const static char *TAG = "CACHE_TEST";
#define TEST_NUM 10
#define TEST_BUF {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9}
#if CONFIG_IDF_TARGET_ESP32S2
#define TEST_SYNC_START 0x3F500000
#define TEST_SYNC_SIZE 0xA80000
#elif CONFIG_IDF_TARGET_ESP32S3
#define TEST_SYNC_START 0x3C000000
#define TEST_SYNC_SIZE 0x2000000
#endif
#define RECORD_TIME_PREPARE() uint32_t __t1, __t2
#define RECORD_TIME_START() do {__t1 = esp_cpu_get_cycle_count();} while(0)
#define RECORD_TIME_END(p_time) do{__t2 = esp_cpu_get_cycle_count(); p_time = (__t2 - __t1);} while(0)
#define GET_US_BY_CCOUNT(t) ((double)(t)/CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ)
static const uint8_t s_test_buf[TEST_NUM] = TEST_BUF;
static DRAM_ATTR bool diff_res;
static DRAM_ATTR uint32_t s_check_times = 0;
static void NOINLINE_ATTR IRAM_ATTR s_test_rodata_cb(void *arg)
{
bool sync_flag = *(bool *)arg;
if (sync_flag) {
uint8_t cmp_buf[TEST_NUM] = TEST_BUF;
for (int i = 0; i < TEST_NUM; i++) {
if (cmp_buf[i] != s_test_buf[i]) {
diff_res |= true;
}
}
s_check_times++;
}
}
/**
* This test tests if the esp_cache_msync() suspending CPU->Cache access is short enough
* 1. Register an IRAM callback, but access rodata inside the callback
* 2. esp_cache_msync() will suspend the CPU access to the cache
* 3. Therefore the rodata access in `s_test_rodata_cb()` should be blocked, most of the times
* 4. Note if the callback frequency is less, there might be few successful rodata access, as code execution needs time
*/
TEST_CASE("test cache msync short enough when suspending an ISR", "[cache]")
{
uint32_t sync_time = 0;
uint32_t sync_time_us = 20;
RECORD_TIME_PREPARE();
//Do msync first, as the first writeback / invalidate takes long time, next msyncs will be shorter and they keep unchanged almost
RECORD_TIME_START();
TEST_ESP_OK(esp_cache_msync((void *)TEST_SYNC_START, TEST_SYNC_SIZE, ESP_CACHE_MSYNC_FLAG_INVALIDATE));
RECORD_TIME_END(sync_time);
sync_time_us = GET_US_BY_CCOUNT(sync_time);
printf("first sync_time_us: %"PRId32"\n", sync_time_us);
RECORD_TIME_START();
TEST_ESP_OK(esp_cache_msync((void *)TEST_SYNC_START, TEST_SYNC_SIZE, ESP_CACHE_MSYNC_FLAG_INVALIDATE));
RECORD_TIME_END(sync_time);
sync_time_us = GET_US_BY_CCOUNT(sync_time);
printf("sync_time_us: %"PRId32"\n", sync_time_us);
bool sync_flag = false;
esp_timer_handle_t timer;
const esp_timer_create_args_t oneshot_timer_args = {
.callback = &s_test_rodata_cb,
.arg = &sync_flag,
.dispatch_method = ESP_TIMER_ISR,
.name = "test_ro_suspend"
};
TEST_ESP_OK(esp_timer_create(&oneshot_timer_args, &timer));
uint32_t period = sync_time_us / 2;
TEST_ESP_OK(esp_timer_start_periodic(timer, period));
RECORD_TIME_START();
sync_flag = true;
TEST_ESP_OK(esp_cache_msync((void *)TEST_SYNC_START, TEST_SYNC_SIZE, ESP_CACHE_MSYNC_FLAG_INVALIDATE | ESP_CACHE_MSYNC_FLAG_UNALIGNED));
sync_flag = false;
RECORD_TIME_END(sync_time);
TEST_ESP_OK(esp_timer_stop(timer));
printf("s_check_times: %"PRId32"\n", s_check_times);
sync_time_us = GET_US_BY_CCOUNT(sync_time);
printf("sync time: %"PRId32" us\n", sync_time_us);
TEST_ASSERT((s_check_times < (sync_time_us / period)));
TEST_ASSERT(diff_res == false);
ESP_LOGI(TAG, "Finish");
TEST_ESP_OK(esp_timer_delete(timer));
}
static void s_test_with_msync_cb(void *arg)
{
(void)arg;
esp_cache_msync((void *)TEST_SYNC_START, TEST_SYNC_SIZE, ESP_CACHE_MSYNC_FLAG_INVALIDATE);
}
TEST_CASE("test cache msync short enough to be in an ISR", "[cache]")
{
uint32_t sync_time = 0;
uint32_t sync_time_us = 200;
RECORD_TIME_PREPARE();
RECORD_TIME_START();
TEST_ESP_OK(esp_cache_msync((void *)TEST_SYNC_START, TEST_SYNC_SIZE, ESP_CACHE_MSYNC_FLAG_INVALIDATE | ESP_CACHE_MSYNC_FLAG_UNALIGNED));
RECORD_TIME_END(sync_time);
sync_time_us = GET_US_BY_CCOUNT(sync_time);
printf("sync_time_us: %"PRId32"\n", sync_time_us);
esp_timer_handle_t timer;
const esp_timer_create_args_t oneshot_timer_args = {
.callback = &s_test_with_msync_cb,
.arg = NULL,
.dispatch_method = ESP_TIMER_ISR,
.name = "test_ro_suspend"
};
TEST_ESP_OK(esp_timer_create(&oneshot_timer_args, &timer));
uint32_t period = sync_time_us * 2;
TEST_ESP_OK(esp_timer_start_periodic(timer, period));
//1ms
esp_rom_delay_us(1000);
TEST_ESP_OK(esp_timer_stop(timer));
ESP_LOGI(TAG, "Finish");
TEST_ESP_OK(esp_timer_delete(timer));
}
#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS && CONFIG_SPIRAM_RODATA
static const esp_partition_t *s_get_partition(void)
{
//Find the "storage1" partition defined in `partitions.csv`
const esp_partition_t *result = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage1");
if (!result) {
ESP_LOGE(TAG, "Can't find the partition, please define it correctly in `partitions.csv`");
abort();
}
return result;
}
TEST_CASE("test cache msync work with Flash operation when XIP from PSRAM", "[cache]")
{
uint32_t sync_time = 0;
RECORD_TIME_PREPARE();
RECORD_TIME_START();
TEST_ESP_OK(esp_cache_msync((void *)TEST_SYNC_START, TEST_SYNC_SIZE, ESP_CACHE_MSYNC_FLAG_INVALIDATE));
RECORD_TIME_END(sync_time);
uint32_t sync_time_us = GET_US_BY_CCOUNT(sync_time);
printf("sync_time_us: %"PRId32"\n", sync_time_us);
//Get the partition used for SPI1 erase operation
const esp_partition_t *part = s_get_partition();
ESP_LOGI(TAG, "found partition '%s' at offset 0x%"PRIx32" with size 0x%"PRIx32, part->label, part->address, part->size);
//Erase whole region
TEST_ESP_OK(esp_flash_erase_region(part->flash_chip, part->address, part->size));
esp_timer_handle_t timer;
const esp_timer_create_args_t oneshot_timer_args = {
.callback = &s_test_with_msync_cb,
.arg = NULL,
.dispatch_method = ESP_TIMER_ISR,
.name = "test_ro_suspend"
};
TEST_ESP_OK(esp_timer_create(&oneshot_timer_args, &timer));
uint32_t period = sync_time_us * 2;
TEST_ESP_OK(esp_timer_start_periodic(timer, period));
ESP_ERROR_CHECK(esp_flash_erase_region(part->flash_chip, part->address, part->size));
TEST_ESP_OK(esp_timer_stop(timer));
ESP_LOGI(TAG, "Finish");
TEST_ESP_OK(esp_timer_delete(timer));
}
#endif //#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS && CONFIG_SPIRAM_RODATA

View File

@ -0,0 +1,95 @@
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
# normal mmu tests
@pytest.mark.supported_targets
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
[
'release',
],
indirect=True,
)
def test_mmap(dut: Dut) -> None:
dut.run_all_single_board_cases(group='mmu')
# mmu tests with psram enabled
@pytest.mark.esp32
@pytest.mark.esp32s2
@pytest.mark.esp32s3
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
[
'psram_release',
],
indirect=True,
)
def test_mmap_psram(dut: Dut) -> None:
dut.run_all_single_board_cases(group='mmu')
# mmu tests with xip_psram
@pytest.mark.esp32s2
@pytest.mark.esp32s3
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
[
'xip_psram'
],
indirect=True,
)
def test_mmap_xip_psram(dut: Dut) -> None:
dut.run_all_single_board_cases(group='mmu')
# normal cache tests
@pytest.mark.supported_targets
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
[
'release',
],
indirect=True,
)
def test_cache(dut: Dut) -> None:
dut.run_all_single_board_cases(group='cache')
# cache tests with psram enabled
@pytest.mark.esp32
@pytest.mark.esp32s2
@pytest.mark.esp32s3
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
[
'psram_release',
],
indirect=True,
)
def test_cache_psram(dut: Dut) -> None:
dut.run_all_single_board_cases(group='cache')
# cache tests with xip_psram
@pytest.mark.esp32s2
@pytest.mark.esp32s3
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
[
'xip_psram'
],
indirect=True,
)
def test_cache_xip_psram(dut: Dut) -> None:
dut.run_all_single_board_cases(group='cache')

View File

@ -0,0 +1,5 @@
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
CONFIG_SPIRAM=y

View File

@ -0,0 +1,7 @@
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
CONFIG_SPIRAM=y
CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y
CONFIG_SPIRAM_RODATA=y

View File

@ -0,0 +1,11 @@
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_COMPILER_DUMP_RTL_FILES=y
CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD=y
CONFIG_HAL_ASSERTION_SILENT=y

View File

@ -0,0 +1,20 @@
# This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.16)
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(mmap_hw_test)
if(CONFIG_COMPILER_DUMP_RTL_FILES)
add_custom_target(check_test_app_sections ALL
COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py
--rtl-dir ${CMAKE_BINARY_DIR}/esp-idf/esp_mm/
--elf-file ${CMAKE_BINARY_DIR}/mmap_hw_test.elf
find-refs
--from-sections=.iram0.text
--to-sections=.flash.text,.flash.rodata
--exit-code
DEPENDS ${elf}
)
endif()

View File

@ -0,0 +1,2 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- |

View File

@ -1,7 +1,6 @@
set(srcs "test_app_main.c")
list(APPEND srcs "test_mmap.c"
"test_mmap_hw.c")
list(APPEND srcs "test_mmap_hw.c")
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,

View File

@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 1M,
storage1, data, fat, , 512K,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
3 nvs, data, nvs, 0x9000, 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000,
5 factory, app, factory, 0x10000, 1M,
6 storage1, data, fat, , 512K,

View File

@ -14,5 +14,5 @@ from pytest_embedded import Dut
],
indirect=True,
)
def test_mmap(dut: Dut) -> None:
def test_mmap_hw(dut: Dut) -> None:
dut.run_all_single_board_cases(group='mmu', timeout=600)

View File

@ -0,0 +1,6 @@
# set compilier optimization level
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
# we can silent the assertion to save the binary footprint
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y

View File

@ -1,4 +1,4 @@
CONFIG_ESP_TASK_WDT=n
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"

View File

@ -32,6 +32,7 @@
#if CONFIG_IDF_TARGET_ESP32
#include "esp32/himem.h"
#include "esp32/rom/cache.h"
#include "esp_private/esp_cache_esp32_private.h"
#endif
@ -293,6 +294,15 @@ esp_err_t esp_psram_init(void)
s_psram_ctx.regions_to_heap[PSRAM_MEM_8BIT_ALIGNED].size -= esp_himem_reserved_area_size() - 1;
#endif
//will be removed, TODO: IDF-6944
#if CONFIG_IDF_TARGET_ESP32
cache_driver_t drv = {
NULL,
esp_psram_extram_writeback_cache,
};
cache_register_writeback(&drv);
#endif
return ESP_OK;
}

View File

@ -15,22 +15,7 @@
#include "hal/mmu_hal.h"
#include "hal/mmu_ll.h"
#include "soc/soc_caps.h"
#if CONFIG_IDF_TARGET_ESP32S2
#include "esp32s2/rom/cache.h"
#elif CONFIG_IDF_TARGET_ESP32S3
#include "esp32s3/rom/cache.h"
#elif CONFIG_IDF_TARGET_ESP32C3
#include "esp32c3/rom/cache.h"
#elif CONFIG_IDF_TARGET_ESP32C2
#include "esp32c2/rom/cache.h"
#elif CONFIG_IDF_TARGET_ESP32H4
#include "esp32h4/rom/cache.h"
#elif CONFIG_IDF_TARGET_ESP32C6
#include "esp32c6/rom/cache.h"
#elif CONFIG_IDF_TARGET_ESP32H2
#include "esp32h2/rom/cache.h"
#endif
#include "rom/cache.h"
/*------------------------------------------------------------------------------
* Unified Cache Control
@ -121,3 +106,63 @@ void cache_hal_invalidate_addr(uint32_t vaddr, uint32_t size)
HAL_ASSERT(mmu_hal_check_valid_ext_vaddr_region(0, vaddr, size, MMU_VADDR_DATA | MMU_VADDR_INSTRUCTION));
Cache_Invalidate_Addr(vaddr, size);
}
#if SOC_CACHE_WRITEBACK_SUPPORTED
void cache_hal_writeback_addr(uint32_t vaddr, uint32_t size)
{
HAL_ASSERT(mmu_hal_check_valid_ext_vaddr_region(0, vaddr, size, MMU_VADDR_DATA));
Cache_WriteBack_Addr(vaddr, size);
}
#endif //#if SOC_CACHE_WRITEBACK_SUPPORTED
#if SOC_CACHE_FREEZE_SUPPORTED
void cache_hal_freeze(cache_type_t type)
{
#if SOC_SHARED_IDCACHE_SUPPORTED
Cache_Freeze_ICache_Enable(CACHE_FREEZE_ACK_BUSY);
#else
if (type == CACHE_TYPE_DATA) {
Cache_Freeze_DCache_Enable(CACHE_FREEZE_ACK_BUSY);
} else if (type == CACHE_TYPE_INSTRUCTION) {
Cache_Freeze_ICache_Enable(CACHE_FREEZE_ACK_BUSY);
} else {
Cache_Freeze_ICache_Enable(CACHE_FREEZE_ACK_BUSY);
Cache_Freeze_DCache_Enable(CACHE_FREEZE_ACK_BUSY);
}
#endif
}
void cache_hal_unfreeze(cache_type_t type)
{
#if SOC_SHARED_IDCACHE_SUPPORTED
Cache_Freeze_ICache_Disable();
#else
if (type == CACHE_TYPE_DATA) {
Cache_Freeze_DCache_Disable();
} else if (type == CACHE_TYPE_INSTRUCTION) {
Cache_Freeze_ICache_Disable();
} else {
Cache_Freeze_DCache_Disable();
Cache_Freeze_ICache_Disable();
}
#endif
}
#endif //#if SOC_CACHE_FREEZE_SUPPORTED
uint32_t cache_hal_get_cache_line_size(cache_type_t type)
{
#if SOC_SHARED_IDCACHE_SUPPORTED
return Cache_Get_ICache_Line_Size();
#else
uint32_t size = 0;
if (type == CACHE_TYPE_DATA) {
size = Cache_Get_DCache_Line_Size();
} else if (type == CACHE_TYPE_INSTRUCTION) {
size = Cache_Get_ICache_Line_Size();
} else {
HAL_ASSERT(false);
}
return size;
#endif
}

View File

@ -15,11 +15,12 @@ extern "C" {
/**
* Cache init and cache hal context init
*
*/
void cache_hal_init(void);
/**
* @brief Disable cache
*
* Disable the ICache or DCache or both, all the items in the corresponding Cache(s) will be invalideated.
* Next request to these items will trigger a transaction to the external memory (flash / psram)
*
@ -30,6 +31,8 @@ void cache_hal_init(void);
void cache_hal_disable(cache_type_t type);
/**
* @brief Enable cache
*
* Enable the ICache or DCache or both.
*
* @param type see `cache_type_t`
@ -37,6 +40,8 @@ void cache_hal_disable(cache_type_t type);
void cache_hal_enable(cache_type_t type);
/**
* @brief Invalidate cache supported addr
*
* Invalidate a Cache item for either ICache or DCache.
*
* @param vaddr Start address of the region to be invalidated
@ -44,6 +49,47 @@ void cache_hal_enable(cache_type_t type);
*/
void cache_hal_invalidate_addr(uint32_t vaddr, uint32_t size);
#if SOC_CACHE_WRITEBACK_SUPPORTED
/**
* @brief Writeback cache supported addr
*
* Writeback the DCache item to external memory
*
* @param vaddr Start address of the region to writeback
* @param size Size of the region to writeback
*/
void cache_hal_writeback_addr(uint32_t vaddr, uint32_t size);
#endif //#if SOC_CACHE_WRITEBACK_SUPPORTED
#if SOC_CACHE_FREEZE_SUPPORTED
/**
* @brief Freeze cache
*
* Freeze cache, CPU access to cache will be suspended, until the cache is unfrozen.
*
* @param type see `cache_type_t`
*/
void cache_hal_freeze(cache_type_t type);
/**
* @brief Unfreeze cache
*
* Unfreeze cache, CPU access to cache will be restored
*
* @param type see `cache_type_t`
*/
void cache_hal_unfreeze(cache_type_t type);
#endif //#if SOC_CACHE_FREEZE_SUPPORTED
/**
* @brief Get cache line size, in bytes
*
* @param type see `cache_type_t`
*
* @return cache line size, in bytes
*/
uint32_t cache_hal_get_cache_line_size(cache_type_t type);
#ifdef __cplusplus
}
#endif

View File

@ -60,7 +60,6 @@ static esp_err_t crypto_shared_gdma_init(void)
gdma_transfer_ability_t transfer_ability = {
.sram_trans_align = 1,
.psram_trans_align = 16,
};

View File

@ -279,6 +279,10 @@ config SOC_SHARED_IDCACHE_SUPPORTED
bool
default y
config SOC_CACHE_FREEZE_SUPPORTED
bool
default y
config SOC_CPU_CORES_NUM
int
default 1

View File

@ -121,6 +121,7 @@
/*-------------------------- CACHE CAPS --------------------------------------*/
#define SOC_SHARED_IDCACHE_SUPPORTED 1 //Shared Cache for both instructions and data
#define SOC_CACHE_FREEZE_SUPPORTED 1
/*-------------------------- CPU CAPS ----------------------------------------*/
#define SOC_CPU_CORES_NUM (1U)

View File

@ -239,6 +239,10 @@ config SOC_SHARED_IDCACHE_SUPPORTED
bool
default y
config SOC_CACHE_FREEZE_SUPPORTED
bool
default y
config SOC_CPU_CORES_NUM
int
default 1

View File

@ -117,6 +117,7 @@
/*-------------------------- CACHE CAPS --------------------------------------*/
#define SOC_SHARED_IDCACHE_SUPPORTED 1 //Shared Cache for both instructions and data
#define SOC_CACHE_FREEZE_SUPPORTED 1
/*-------------------------- CPU CAPS ----------------------------------------*/
#define SOC_CPU_CORES_NUM (1U)

View File

@ -267,6 +267,10 @@ config SOC_BROWNOUT_RESET_SUPPORTED
bool
default y
config SOC_CACHE_WRITEBACK_SUPPORTED
bool
default y
config SOC_CP_DMA_MAX_BUFFER_SIZE
int
default 4095

View File

@ -122,6 +122,9 @@
/*-------------------------- BROWNOUT CAPS -----------------------------------*/
#define SOC_BROWNOUT_RESET_SUPPORTED 1
/*-------------------------- CACHE CAPS --------------------------------------*/
#define SOC_CACHE_WRITEBACK_SUPPORTED 1
/*-------------------------- CP-DMA CAPS -------------------------------------*/
#define SOC_CP_DMA_MAX_BUFFER_SIZE (4095) /*!< Maximum size of the buffer that can be attached to descriptor */

View File

@ -307,6 +307,14 @@ config SOC_BROWNOUT_RESET_SUPPORTED
bool
default y
config SOC_CACHE_WRITEBACK_SUPPORTED
bool
default y
config SOC_CACHE_FREEZE_SUPPORTED
bool
default y
config SOC_CPU_CORES_NUM
int
default 2
@ -355,10 +363,6 @@ config SOC_GDMA_SUPPORT_PSRAM
bool
default y
config SOC_GDMA_PSRAM_MIN_ALIGN
int
default 16
config SOC_GPIO_PORT
int
default 1

View File

@ -117,6 +117,10 @@
/*-------------------------- BROWNOUT CAPS -----------------------------------*/
#define SOC_BROWNOUT_RESET_SUPPORTED 1
/*-------------------------- CACHE CAPS --------------------------------------*/
#define SOC_CACHE_WRITEBACK_SUPPORTED 1
#define SOC_CACHE_FREEZE_SUPPORTED 1
/*-------------------------- CPU CAPS ----------------------------------------*/
#define SOC_CPU_CORES_NUM 2
#define SOC_CPU_INTR_NUM 32
@ -141,7 +145,6 @@
#define SOC_GDMA_GROUPS (1) // Number of GDMA groups
#define SOC_GDMA_PAIRS_PER_GROUP (5) // Number of GDMA pairs in each group
#define SOC_GDMA_SUPPORT_PSRAM (1) // GDMA can access external PSRAM
#define SOC_GDMA_PSRAM_MIN_ALIGN (16) // Minimal alignment for PSRAM transaction
/*-------------------------- GPIO CAPS ---------------------------------------*/
// ESP32-S3 has 1 GPIO peripheral