Skip to content

Commit

Permalink
Merge branch 'feature/esp_cache_msync' into 'master'
Browse files Browse the repository at this point in the history
esp_mm: esp_cache_msync, a cache synchronise API

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

See merge request espressif/esp-idf!22380
  • Loading branch information
Icarus113 committed Feb 28, 2023
2 parents 146a5c4 + 4c0d6b6 commit a9ce760
Show file tree
Hide file tree
Showing 43 changed files with 857 additions and 73 deletions.
5 changes: 4 additions & 1 deletion components/esp_hw_support/dma/gdma.c
Expand Up @@ -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";

Expand Down Expand Up @@ -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
Expand All @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion components/esp_lcd/CMakeLists.txt
Expand Up @@ -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")
Expand Down
9 changes: 3 additions & 6 deletions components/esp_lcd/src/esp_lcd_panel_rgb.c
Expand Up @@ -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)
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down
17 changes: 8 additions & 9 deletions components/esp_mm/CMakeLists.txt
Expand Up @@ -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)
36 changes: 36 additions & 0 deletions components/esp_mm/cache_esp32.c
@@ -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
}
81 changes: 81 additions & 0 deletions components/esp_mm/esp_cache.c
@@ -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;
}
34 changes: 7 additions & 27 deletions components/esp_mm/esp_mmu_map.c
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down
58 changes: 58 additions & 0 deletions components/esp_mm/include/esp_cache.h
@@ -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
59 changes: 59 additions & 0 deletions components/esp_mm/include/esp_private/esp_cache_esp32_private.h
@@ -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
9 changes: 9 additions & 0 deletions components/esp_mm/linker.lf
@@ -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)

0 comments on commit a9ce760

Please sign in to comment.