feat(ppa): add sleep retention support for DMA2D and PPA

This commit is contained in:
Song Ruo Jing
2026-07-03 20:56:59 +08:00
parent 0362f806e1
commit c8a4073e2b
26 changed files with 692 additions and 157 deletions
+4
View File
@@ -7,6 +7,9 @@ if(CONFIG_SOC_PPA_SUPPORTED)
"src/ppa_srm.c"
"src/ppa_blend.c"
"src/ppa_fill.c")
if(CONFIG_SOC_PAU_SUPPORTED)
list(APPEND srcs "${target}/ppa_retention.c")
endif()
endif()
if(${target} STREQUAL "linux")
@@ -18,6 +21,7 @@ endif()
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS ${public_include}
PRIV_INCLUDE_DIRS "src"
REQUIRES esp_hal_ppa
PRIV_REQUIRES "${priv_requires}"
)
@@ -0,0 +1,39 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include "ppa_priv.h"
#include "soc/ppa_reg.h"
/** PPA Registers to be saved during sleep retention
*
* For CLUT mem, we shall choose not to power down the CLUT mem domain; Otherwise, there are two blocks of memory that needs to be restored by writing to mem addr directly.
* Since PPA driver is operation based, most of the registers are configured per operation, so no need to save/restore.
*
* PPA_CLUT_CONF_REG,
* PPA_INT_ENA_REG,
* PPA_SR_MEM_PD_REG, PPA_REG_CONF_REG, PPA_SRAM_CTRL_REG,
* PPA_RGB2GRAY_REG (> rev3 only, but ok to include it for < rev3 chips)
*/
#define PPA_RETENTION_REGS_CNT 6
#define PPA_RETENTION_REGS_BASE PPA_CLUT_CONF_REG
static const uint32_t ppa_regs_map[4] = {0x1800009, 0x9, 0x0, 0x0};
static const regdma_entries_config_t ppa_regdma_entries[] = {
[0] = {
.config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_PPA_LINK(0x00),
PPA_RETENTION_REGS_BASE, PPA_RETENTION_REGS_BASE,
PPA_RETENTION_REGS_CNT, 0, 0,
ppa_regs_map[0], ppa_regs_map[1],
ppa_regs_map[2], ppa_regs_map[3]),
.owner = ENTRY(0),
},
};
const ppa_retention_desc_t ppa_reg_retention_info = {
.module = SLEEP_RETENTION_MODULE_PPA,
.regdma_entry_array = ppa_regdma_entries,
.array_size = ARRAY_SIZE(ppa_regdma_entries),
};
@@ -0,0 +1,39 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include "ppa_priv.h"
#include "soc/ppa_reg.h"
/** PPA Registers to be saved during sleep retention
*
* For CLUT mem, we shall choose not to power down the CLUT mem domain; Otherwise, there are two blocks of memory that needs to be restored by writing to mem addr directly.
* Since PPA driver is operation based, most of the registers are configured per operation, so no need to save/restore.
*
* PPA_CLUT_CONF_REG,
* PPA_INT_ENA_REG,
* PPA_SR_MEM_PD_REG, PPA_REG_CONF_REG, PPA_SRAM_CTRL_REG,
* PPA_RGB2GRAY_REG
*/
#define PPA_RETENTION_REGS_CNT 6
#define PPA_RETENTION_REGS_BASE PPA_CLUT_CONF_REG
static const uint32_t ppa_regs_map[4] = {0x1800009, 0x9, 0x0, 0x0};
static const regdma_entries_config_t ppa_regdma_entries[] = {
[0] = {
.config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_PPA_LINK(0x00),
PPA_RETENTION_REGS_BASE, PPA_RETENTION_REGS_BASE,
PPA_RETENTION_REGS_CNT, 0, 0,
ppa_regs_map[0], ppa_regs_map[1],
ppa_regs_map[2], ppa_regs_map[3]),
.owner = ENTRY(0) | ENTRY(2),
},
};
const ppa_retention_desc_t ppa_reg_retention_info = {
.module = SLEEP_RETENTION_MODULE_PPA,
.regdma_entry_array = ppa_regdma_entries,
.array_size = ARRAY_SIZE(ppa_regdma_entries),
};
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -39,6 +39,11 @@ typedef struct {
ppa_data_burst_length_t data_burst_length; /*!< The desired data burst length for all the transactions of the client.
Use a small burst length will decrease PPA performance, but can save burst bandwidth for other peripheral usages.
By default, it will be at the maximum burst length, `PPA_DATA_BURST_LENGTH_128` */
struct {
uint32_t allow_pd: 1; /*!< If set, driver allows the power domain to be powered off when system enters sleep mode.
This can save power, but at the expense of more RAM being consumed to save register context.
All clients must have the same value for this flag. */
} flags; /*!< Configuration flags */
} ppa_client_config_t;
/**
+70
View File
@@ -34,6 +34,7 @@
#include "hal/color_hal.h"
#include "hal/color_types.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/sleep_retention.h"
#include "esp_efuse.h"
#include "soc/soc_caps.h"
@@ -55,6 +56,25 @@ const dma2d_trans_on_picked_callback_t ppa_oper_trans_on_picked_func[PPA_OPERATI
[PPA_OPERATION_FILL] = ppa_fill_transaction_on_picked,
};
/** PPA Power Management Strategy
*
* SRM and Blending have separate CPU_FREQ_MAX PM locks, and each PM lock is acquired whenever there is PPA operation in process.
*
* When no PPA operation is in process, sleep can happen, and PPA domain can be powered down if allow_pd is set.
* Necessary register context will be saved and restored by sleep retention.
*/
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
static esp_err_t ppa_create_sleep_retention_link_cb(void *arg)
{
sleep_retention_module_t module = ppa_reg_retention_info.module;
esp_err_t err = sleep_retention_entries_create(ppa_reg_retention_info.regdma_entry_array,
ppa_reg_retention_info.array_size,
REGDMA_LINK_PRI_PPA, module);
return err;
}
#endif
static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engine_t **ret_engine)
{
esp_err_t ret = ESP_OK;
@@ -187,6 +207,40 @@ static esp_err_t ppa_engine_acquire(const ppa_engine_config_t *config, ppa_engin
ESP_LOGE(TAG, "install 2D-DMA failed");
goto wrap_up;
}
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
s_platform.flags.allow_pd = config->flags.allow_pd; // Uses the first acquired engine's allow_pd flag as the platform's allow_pd flag
// Initialize sleep retention module for PPA
sleep_retention_module_t module = ppa_reg_retention_info.module;
sleep_retention_module_init_param_t init_param = {
.cbs = {
.create = {
.handle = ppa_create_sleep_retention_link_cb,
.arg = NULL,
},
},
.attribute = SLEEP_RETENTION_MODULE_ATTR_ATTACH,
.depends = RETENTION_MODULE_BITMAP_INIT(CLOCK_SYSTEM)
};
if (sleep_retention_module_init(module, &init_param) != ESP_OK) {
// even though the sleep retention module init failed, PPA driver should still work, so just warning here
ESP_LOGW(TAG, "init sleep retention failed, power domain may be turned off during sleep");
} else if (s_platform.flags.allow_pd) {
if (sleep_retention_module_allocate(module) != ESP_OK) {
ESP_LOGW(TAG, "fail to allocate retention link list");
// don't call sleep_retention_module_deinit here, otherwise PPA peripheral may be powered off during sleep
} else {
if (sleep_retention_module_attach(module) != ESP_OK) {
ESP_LOGW(TAG, "attach retention module failed, power domain can't turn off");
}
}
}
} else {
if (s_platform.flags.allow_pd != config->flags.allow_pd) {
ESP_LOGW(TAG, "allow_pd flag mismatch among clients, will follow flags.allow_pd = %d", s_platform.flags.allow_pd);
}
#endif
}
}
wrap_up:
@@ -247,6 +301,20 @@ static esp_err_t ppa_engine_release(ppa_engine_t *ppa_engine)
if (!s_platform.srm && !s_platform.blending) {
assert(s_platform.srm_engine_ref_count == 0 && s_platform.blend_engine_ref_count == 0);
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
sleep_retention_module_t module = ppa_reg_retention_info.module;
if (sleep_retention_is_module_attached(module)) {
sleep_retention_module_detach(module);
}
if (sleep_retention_is_module_created(module)) {
sleep_retention_module_free(module);
}
if (sleep_retention_is_module_inited(module)) {
sleep_retention_module_deinit(module);
}
s_platform.flags.allow_pd = false;
#endif
if (s_platform.dma2d_pool_handle) {
dma2d_release_pool(s_platform.dma2d_pool_handle); // TODO: check return value. If not ESP_OK, then must be error on other 2D-DMA clients :( Give a warning log?
s_platform.dma2d_pool_handle = NULL;
@@ -288,11 +356,13 @@ esp_err_t ppa_register_client(const ppa_client_config_t *config, ppa_client_hand
if (config->oper_type == PPA_OPERATION_SRM) {
ppa_engine_config_t engine_config = {
.engine = PPA_ENGINE_TYPE_SRM,
.flags.allow_pd = config->flags.allow_pd,
};
ESP_GOTO_ON_ERROR(ppa_engine_acquire(&engine_config, &client->engine), err, TAG, "unable to acquire SRM engine");
} else if (config->oper_type == PPA_OPERATION_BLEND || config->oper_type == PPA_OPERATION_FILL) {
ppa_engine_config_t engine_config = {
.engine = PPA_ENGINE_TYPE_BLEND,
.flags.allow_pd = config->flags.allow_pd,
};
ESP_GOTO_ON_ERROR(ppa_engine_acquire(&engine_config, &client->engine), err, TAG, "unable to acquire Blending engine");
}
+20 -1
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -18,6 +18,8 @@
#include "hal/ppa_types.h"
#include "hal/ppa_hal.h"
#include "esp_pm.h"
#include "soc/soc_caps.h"
#include "soc/regdma.h"
#ifdef __cplusplus
extern "C" {
@@ -81,6 +83,9 @@ typedef struct ppa_blend_engine_t {
typedef struct {
ppa_engine_type_t engine; // Engine type
struct {
uint32_t allow_pd: 1; // If set, engine allows the power domain to be powered off when system enters sleep mode
} flags; // Configuration flags
} ppa_engine_config_t;
/******************************** CLIENT *************************************/
@@ -247,8 +252,22 @@ struct ppa_platform_t {
size_t int_mem_align; // Alignment requirement for the internal outgoing buffer to satisfy cache line size
size_t ext_mem_align; // Alignment requirement for the external outgoing buffer to satisfy cache line size
uint32_t dma_desc_mem_size; // Alignment requirement for the 2D-DMA descriptor to satisfy cache line size
struct {
uint32_t allow_pd: 1; // If set, driver allows the power domain to be powered off when system enters sleep mode
} flags; // Configuration flags
};
#if SOC_PAU_SUPPORTED
#include "soc/retention_periph_defs.h"
typedef struct {
const periph_retention_module_t module;
const regdma_entries_config_t *regdma_entry_array;
uint32_t array_size;
} ppa_retention_desc_t;
extern const ppa_retention_desc_t ppa_reg_retention_info;
#endif // SOC_PAU_SUPPORTED
#ifdef __cplusplus
}
#endif
@@ -5,5 +5,5 @@ set(srcs "test_app_main.c"
# the component can be registered as WHOLE_ARCHIVE
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS "."
PRIV_REQUIRES esp_driver_ppa esp_psram unity esp_mm efuse
PRIV_REQUIRES esp_driver_ppa esp_psram unity esp_mm efuse esp_pm
WHOLE_ARCHIVE)
@@ -20,6 +20,12 @@
#include "ppa_performance.h"
#include "esp_random.h"
#include "esp_efuse.h"
#include "sdkconfig.h"
#include "soc/soc_caps.h"
#include "esp_pm.h"
#include "esp_clk_tree.h"
#include "esp_private/esp_sleep_internal.h"
#include "esp_private/esp_pmu.h"
#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
@@ -238,7 +244,20 @@ TEST_CASE("ppa_pending_transactions_in_queue", "[PPA]")
free(buf_2);
}
TEST_CASE("ppa_srm_basic_data_correctness_check", "[PPA]")
static __attribute__((unused)) void enable_pm_strategy(bool auto_light_sleep)
{
uint32_t xtal_hz = 0;
esp_clk_tree_src_get_freq_hz(SOC_MOD_CLK_XTAL, ESP_CLK_TREE_SRC_FREQ_PRECISION_EXACT, &xtal_hz);
esp_pm_config_t pm_config = {};
pm_config.max_freq_mhz = CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ;
pm_config.min_freq_mhz = static_cast<int>(xtal_hz / 1000000);
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
pm_config.light_sleep_enable = auto_light_sleep;
#endif
TEST_ESP_OK(esp_pm_configure(&pm_config));
}
static void ppa_srm_basic_data_correctness_check(bool auto_light_sleep)
{
const uint32_t w = 4;
const uint32_t h = 4;
@@ -276,86 +295,138 @@ TEST_CASE("ppa_srm_basic_data_correctness_check", "[PPA]")
0x0000, 0x0000, 0x0000, 0x0000
};
#if CONFIG_PM_ENABLE
enable_pm_strategy(auto_light_sleep);
#endif
ppa_client_handle_t ppa_client_handle;
ppa_client_config_t ppa_client_config = {};
ppa_client_config.oper_type = PPA_OPERATION_SRM;
ppa_client_config.max_pending_trans_num = 1;
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
ppa_client_config.flags.allow_pd = (auto_light_sleep ? true : false);
#endif
TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_handle));
ppa_srm_oper_config_t oper_config = {};
oper_config.in.buffer = in_buf;
oper_config.in.pic_w = w;
oper_config.in.pic_h = h;
oper_config.in.block_w = block_w;
oper_config.in.block_h = block_h;
oper_config.in.block_offset_x = in_block_offset_x;
oper_config.in.block_offset_y = in_block_offset_y;
oper_config.in.srm_cm = cm;
oper_config.out.buffer = out_buf;
oper_config.out.buffer_size = out_buf_size;
oper_config.out.pic_w = w;
oper_config.out.pic_h = h;
oper_config.out.block_offset_x = out_block_offset_x;
oper_config.out.block_offset_y = out_block_offset_y;
oper_config.out.srm_cm = cm;
oper_config.rotation_angle = rotation;
oper_config.scale_x = scale_x;
oper_config.scale_y = scale_y;
oper_config.rgb_swap = 0;
oper_config.byte_swap = 0;
oper_config.mode = PPA_TRANS_MODE_BLOCKING;
#if SOC_LIGHT_SLEEP_SUPPORTED
esp_sleep_context_t sleep_ctx;
esp_sleep_set_sleep_context(&sleep_ctx);
#endif
TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_handle, &oper_config));
// loop 3 times if we are testing PPA sleep retention feature
int cnt = (auto_light_sleep ? 3 : 1);
for (int i = 0; i < cnt; i++) {
#if SOC_LIGHT_SLEEP_SUPPORTED
sleep_ctx.sleep_request_result = ESP_FAIL; // set back to fail for new iteration
sleep_ctx.sleep_flags = 0; // clear sleep flags for new iteration
// Check result
for (int i = 0; i < buf_len; i++) {
if (i % 8 == 0) {
printf("\n");
if (auto_light_sleep) {
vTaskDelay(pdMS_TO_TICKS(1000)); // enter auto light sleep here if we are testing PPA sleep retention feature
// Check if the sleep happened as expected
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
TEST_ASSERT_EQUAL(PMU_SLEEP_PD_TOP, sleep_ctx.sleep_flags & PMU_SLEEP_PD_TOP);
#endif
TEST_ASSERT_EQUAL(ESP_OK, sleep_ctx.sleep_request_result);
}
printf("0x%02X ", out_buf[i]);
}
printf("\n");
TEST_ASSERT_EQUAL_UINT8_ARRAY((void *)out_buf_expected, (void *)out_buf, buf_len);
#endif
ppa_srm_oper_config_t oper_config = {};
oper_config.in.buffer = in_buf;
oper_config.in.pic_w = w;
oper_config.in.pic_h = h;
oper_config.in.block_w = block_w;
oper_config.in.block_h = block_h;
oper_config.in.block_offset_x = in_block_offset_x;
oper_config.in.block_offset_y = in_block_offset_y;
oper_config.in.srm_cm = cm;
oper_config.out.buffer = out_buf;
oper_config.out.buffer_size = out_buf_size;
oper_config.out.pic_w = w;
oper_config.out.pic_h = h;
oper_config.out.block_offset_x = out_block_offset_x;
oper_config.out.block_offset_y = out_block_offset_y;
oper_config.out.srm_cm = cm;
oper_config.rotation_angle = rotation;
oper_config.scale_x = scale_x;
oper_config.scale_y = scale_y;
oper_config.rgb_swap = 0;
oper_config.byte_swap = 0;
oper_config.mode = PPA_TRANS_MODE_BLOCKING;
TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_handle, &oper_config));
// Check result
for (int i = 0; i < buf_len; i++) {
if (i % 8 == 0) {
printf("\n");
}
printf("0x%02X ", out_buf[i]);
}
printf("\n");
TEST_ASSERT_EQUAL_UINT8_ARRAY((void *)out_buf_expected, (void *)out_buf, buf_len);
#if !(CONFIG_IDF_TARGET_ESP32P4 && CONFIG_ESP32P4_SELECTS_REV_LESS_V3)
// Test a rgb2gray color conversion
memset(out_buf, 0, out_buf_size);
esp_cache_msync((void *)out_buf, out_buf_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
// Test a rgb2gray color conversion
memset(out_buf, 0, out_buf_size);
esp_cache_msync((void *)out_buf, out_buf_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
const uint8_t r_weight = 100;
const uint8_t g_weight = 56;
const uint8_t b_weight = 100;
TEST_ESP_OK(ppa_set_rgb2gray_formula(r_weight, g_weight, b_weight));
oper_config.out.srm_cm = PPA_SRM_COLOR_MODE_GRAY8;
oper_config.rotation_angle = PPA_SRM_ROTATION_ANGLE_0;
uint8_t out_buf_expected_gray[16] = {};
for (int i = 0; i < block_w * block_h; i++) {
const uint16_t pix = in_buf[(i / block_w + in_block_offset_y) * w + (i % block_w + in_block_offset_x)];
uint8_t _r = ((pix >> 8) & 0xF8);
uint8_t _g = ((pix >> 3) & 0xFC);
uint8_t _b = ((pix << 3) & 0xF8);
out_buf_expected_gray[(i / block_w + out_block_offset_y) * w + (i % block_w + out_block_offset_x)] = (_r * r_weight + _g * g_weight + _b * b_weight) >> 8;
}
TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_handle, &oper_config));
// Check result
for (int i = 0; i < w * h; i++) {
if (i % 4 == 0) {
printf("\n");
const uint8_t r_weight = 100;
const uint8_t g_weight = 56;
const uint8_t b_weight = 100;
TEST_ESP_OK(ppa_set_rgb2gray_formula(r_weight, g_weight, b_weight));
oper_config.out.srm_cm = PPA_SRM_COLOR_MODE_GRAY8;
oper_config.rotation_angle = PPA_SRM_ROTATION_ANGLE_0;
uint8_t out_buf_expected_gray[16] = {};
for (int i = 0; i < block_w * block_h; i++) {
const uint16_t pix = in_buf[(i / block_w + in_block_offset_y) * w + (i % block_w + in_block_offset_x)];
uint8_t _r = ((pix >> 8) & 0xF8);
uint8_t _g = ((pix >> 3) & 0xFC);
uint8_t _b = ((pix << 3) & 0xF8);
out_buf_expected_gray[(i / block_w + out_block_offset_y) * w + (i % block_w + out_block_offset_x)] = (_r * r_weight + _g * g_weight + _b * b_weight) >> 8;
}
printf("0x%02X ", out_buf[i]);
}
printf("\n");
TEST_ASSERT_EQUAL_UINT8_ARRAY((void *)out_buf_expected_gray, (void *)out_buf, w * h);
TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_handle, &oper_config));
// Check result
for (int i = 0; i < w * h; i++) {
if (i % 4 == 0) {
printf("\n");
}
printf("0x%02X ", out_buf[i]);
}
printf("\n");
TEST_ASSERT_EQUAL_UINT8_ARRAY((void *)out_buf_expected_gray, (void *)out_buf, w * h);
#endif // !(CONFIG_IDF_TARGET_ESP32P4 && CONFIG_ESP32P4_SELECTS_REV_LESS_V3)
memset(out_buf, 0, out_buf_size);
esp_cache_msync((void *)out_buf, out_buf_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
}
#if SOC_LIGHT_SLEEP_SUPPORTED
esp_sleep_set_sleep_context(NULL);
#endif
TEST_ESP_OK(ppa_unregister_client(ppa_client_handle));
#if CONFIG_PM_ENABLE
enable_pm_strategy(false);
#endif
free(out_buf);
}
TEST_CASE("ppa_blend_basic_data_correctness_check", "[PPA]")
TEST_CASE("ppa_srm_basic_data_correctness_check", "[PPA]")
{
ppa_srm_basic_data_correctness_check(false);
#if CONFIG_PM_ENABLE
#if !(CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP && !SOC_PM_FPU_RETENTION_BY_SW) // SRM uses FPU
printf("\nTest again with auto light sleep...\n");
ppa_srm_basic_data_correctness_check(true);
#endif
#endif
}
static void ppa_blend_basic_data_correctness_check(bool auto_light_sleep)
{
const uint32_t w = 2;
const uint32_t h = 2;
@@ -382,7 +453,7 @@ TEST_CASE("ppa_blend_basic_data_correctness_check", "[PPA]")
0xFF, 0xFF, 0xFF, /**/ 0x80, 0x40, 0xA0, /**/
// /*************************/
};
uint8_t in_fg_buf[64] __attribute__((aligned(64))) = {
const uint8_t in_fg_buf_template[64] __attribute__((aligned(64))) = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
// /*******************************/
0xFF, 0xFF, 0xFF, 0xFF, /**/ 0x00, 0x80, 0x80, 0xC0, /**/
@@ -390,6 +461,10 @@ TEST_CASE("ppa_blend_basic_data_correctness_check", "[PPA]")
// /*******************************/
// remaining bytes [16 ... 63] are value-initialized to 0
};
uint8_t *in_fg_buf = static_cast<uint8_t *>(heap_caps_aligned_calloc(4, 64, sizeof(uint8_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA));
TEST_ASSERT_NOT_NULL(in_fg_buf);
memcpy(in_fg_buf, in_fg_buf_template, 64);
esp_cache_msync((void *)in_fg_buf, 64, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
uint8_t *out_buf = in_fg_buf;
// Expected blend output
// Alpha Blending calculation:
@@ -405,54 +480,91 @@ TEST_CASE("ppa_blend_basic_data_correctness_check", "[PPA]")
// /*******************************/
};
#if CONFIG_PM_ENABLE
enable_pm_strategy(auto_light_sleep);
#endif
ppa_client_handle_t ppa_client_handle;
ppa_client_config_t ppa_client_config = {};
ppa_client_config.oper_type = PPA_OPERATION_BLEND;
ppa_client_config.max_pending_trans_num = 1;
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
ppa_client_config.flags.allow_pd = (auto_light_sleep ? true : false);
#endif
TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_handle));
ppa_blend_oper_config_t oper_config = {};
oper_config.in_bg.buffer = in_bg_buf;
oper_config.in_bg.pic_w = w;
oper_config.in_bg.pic_h = h;
oper_config.in_bg.block_w = block_w;
oper_config.in_bg.block_h = block_h;
oper_config.in_bg.block_offset_x = block_offset_x;
oper_config.in_bg.block_offset_y = block_offset_y;
oper_config.in_bg.blend_cm = in_bg_cm;
oper_config.in_fg.buffer = in_fg_buf;
oper_config.in_fg.pic_w = w;
oper_config.in_fg.pic_h = h;
oper_config.in_fg.block_w = block_w;
oper_config.in_fg.block_h = block_h;
oper_config.in_fg.block_offset_x = block_offset_x;
oper_config.in_fg.block_offset_y = block_offset_y;
oper_config.in_fg.blend_cm = in_fg_cm;
oper_config.out.buffer = out_buf;
oper_config.out.buffer_size = out_buf_size;
oper_config.out.pic_w = w;
oper_config.out.pic_h = h;
oper_config.out.block_offset_x = block_offset_x;
oper_config.out.block_offset_y = block_offset_y;
oper_config.out.blend_cm = out_cm;
oper_config.bg_alpha_update_mode = PPA_ALPHA_SCALE;
oper_config.bg_alpha_scale_ratio = bg_alpha_scale_ratio;
oper_config.fg_alpha_update_mode = PPA_ALPHA_INVERT;
oper_config.bg_ck_en = false;
oper_config.fg_ck_en = false;
oper_config.mode = PPA_TRANS_MODE_BLOCKING;
#if SOC_LIGHT_SLEEP_SUPPORTED
esp_sleep_context_t sleep_ctx;
esp_sleep_set_sleep_context(&sleep_ctx);
#endif
TEST_ESP_OK(ppa_do_blend(ppa_client_handle, &oper_config));
// loop 3 times if we are testing PPA sleep retention feature
int cnt = (auto_light_sleep ? 3 : 1);
for (int i = 0; i < cnt; i++) {
#if SOC_LIGHT_SLEEP_SUPPORTED
sleep_ctx.sleep_request_result = ESP_FAIL; // set back to fail for new iteration
sleep_ctx.sleep_flags = 0; // clear sleep flags for new iteration
// Check result
for (int i = 0; i < out_buf_len; i++) {
if (i % 8 == 0) {
printf("\n");
if (auto_light_sleep) {
vTaskDelay(pdMS_TO_TICKS(1000)); // enter auto light sleep here if we are testing PPA sleep retention feature
// Check if the sleep happened as expected
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
TEST_ASSERT_EQUAL(PMU_SLEEP_PD_TOP, sleep_ctx.sleep_flags & PMU_SLEEP_PD_TOP);
#endif
TEST_ASSERT_EQUAL(ESP_OK, sleep_ctx.sleep_request_result);
}
printf("0x%02X ", out_buf[i]);
#endif
ppa_blend_oper_config_t oper_config = {};
oper_config.in_bg.buffer = in_bg_buf;
oper_config.in_bg.pic_w = w;
oper_config.in_bg.pic_h = h;
oper_config.in_bg.block_w = block_w;
oper_config.in_bg.block_h = block_h;
oper_config.in_bg.block_offset_x = block_offset_x;
oper_config.in_bg.block_offset_y = block_offset_y;
oper_config.in_bg.blend_cm = in_bg_cm;
oper_config.in_fg.buffer = in_fg_buf;
oper_config.in_fg.pic_w = w;
oper_config.in_fg.pic_h = h;
oper_config.in_fg.block_w = block_w;
oper_config.in_fg.block_h = block_h;
oper_config.in_fg.block_offset_x = block_offset_x;
oper_config.in_fg.block_offset_y = block_offset_y;
oper_config.in_fg.blend_cm = in_fg_cm;
oper_config.out.buffer = out_buf;
oper_config.out.buffer_size = out_buf_size;
oper_config.out.pic_w = w;
oper_config.out.pic_h = h;
oper_config.out.block_offset_x = block_offset_x;
oper_config.out.block_offset_y = block_offset_y;
oper_config.out.blend_cm = out_cm;
oper_config.bg_alpha_update_mode = PPA_ALPHA_SCALE;
oper_config.bg_alpha_scale_ratio = bg_alpha_scale_ratio;
oper_config.fg_alpha_update_mode = PPA_ALPHA_INVERT;
oper_config.bg_ck_en = false;
oper_config.fg_ck_en = false;
oper_config.mode = PPA_TRANS_MODE_BLOCKING;
TEST_ESP_OK(ppa_do_blend(ppa_client_handle, &oper_config));
// Check result
for (int i = 0; i < out_buf_len; i++) {
if (i % 8 == 0) {
printf("\n");
}
printf("0x%02X ", out_buf[i]);
}
printf("\n");
TEST_ASSERT_EQUAL_UINT8_ARRAY((void *)out_buf_expected, (void *)out_buf, out_buf_len);
memcpy(in_fg_buf, in_fg_buf_template, 64);
esp_cache_msync((void *)in_fg_buf, 64, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
}
printf("\n");
TEST_ASSERT_EQUAL_UINT8_ARRAY((void *)out_buf_expected, (void *)out_buf, out_buf_len);
#if SOC_LIGHT_SLEEP_SUPPORTED
esp_sleep_set_sleep_context(NULL);
#endif
#if !(CONFIG_IDF_TARGET_ESP32P4 && CONFIG_ESP32P4_SELECTS_REV_LESS_V3)
// Test YUV422/YUV420 blend
@@ -533,9 +645,26 @@ TEST_CASE("ppa_blend_basic_data_correctness_check", "[PPA]")
#endif // !(CONFIG_IDF_TARGET_ESP32P4 && CONFIG_ESP32P4_SELECTS_REV_LESS_V3)
TEST_ESP_OK(ppa_unregister_client(ppa_client_handle));
#if CONFIG_PM_ENABLE
enable_pm_strategy(false);
#endif
free(in_fg_buf);
}
TEST_CASE("ppa_fill_basic_data_correctness_check", "[PPA]")
TEST_CASE("ppa_blend_basic_data_correctness_check", "[PPA]")
{
ppa_blend_basic_data_correctness_check(false);
#if CONFIG_PM_ENABLE
#if !(CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP && !SOC_PM_FPU_RETENTION_BY_SW) // Blend uses FPU
printf("\nTest again with auto light sleep...\n");
ppa_blend_basic_data_correctness_check(true);
#endif
#endif
}
static void ppa_fill_basic_data_correctness_check(bool auto_light_sleep)
{
const uint32_t w = 80;
const uint32_t h = 120;
@@ -558,55 +687,105 @@ TEST_CASE("ppa_fill_basic_data_correctness_check", "[PPA]")
memset(out_buf, 0xFF, out_buf_len);
#if CONFIG_PM_ENABLE
enable_pm_strategy(auto_light_sleep);
#endif
ppa_client_handle_t ppa_client_handle;
ppa_client_config_t ppa_client_config = {};
ppa_client_config.oper_type = PPA_OPERATION_FILL;
ppa_client_config.max_pending_trans_num = 1;
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
ppa_client_config.flags.allow_pd = (auto_light_sleep ? true : false);
#endif
TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_handle));
ppa_fill_oper_config_t oper_config = {};
oper_config.out.buffer = out_buf;
oper_config.out.buffer_size = out_buf_size;
oper_config.out.pic_w = w;
oper_config.out.pic_h = h;
oper_config.out.block_offset_x = block_offset_x;
oper_config.out.block_offset_y = block_offset_y;
oper_config.out.fill_cm = out_cm;
oper_config.fill_block_w = block_w;
oper_config.fill_block_h = block_h;
oper_config.fill_argb_color = fill_color;
oper_config.mode = PPA_TRANS_MODE_BLOCKING;
#if SOC_LIGHT_SLEEP_SUPPORTED
esp_sleep_context_t sleep_ctx;
esp_sleep_set_sleep_context(&sleep_ctx);
#endif
TEST_ESP_OK(ppa_do_fill(ppa_client_handle, &oper_config));
// loop 3 times if we are testing PPA sleep retention feature
int cnt = (auto_light_sleep ? 3 : 1);
for (int i = 0; i < cnt; i++) {
#if SOC_LIGHT_SLEEP_SUPPORTED
sleep_ctx.sleep_request_result = ESP_FAIL; // set back to fail for new iteration
sleep_ctx.sleep_flags = 0; // clear sleep flags for new iteration
// Check result
color_pixel_rgb565_data_t fill_pixel_expected = {};
fill_pixel_expected.r = fill_color.r >> 3;
fill_pixel_expected.g = fill_color.g >> 2;
fill_pixel_expected.b = fill_color.b >> 3;
TEST_ASSERT_EACH_EQUAL_UINT16(fill_pixel_expected.val, (void *)((uint32_t)out_buf + w * block_offset_y * out_pixel_depth / 8), block_w * block_h);
if (auto_light_sleep) {
vTaskDelay(pdMS_TO_TICKS(1000)); // enter auto light sleep here if we are testing PPA sleep retention feature
// Check if the sleep happened as expected
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
TEST_ASSERT_EQUAL(PMU_SLEEP_PD_TOP, sleep_ctx.sleep_flags & PMU_SLEEP_PD_TOP);
#endif
TEST_ASSERT_EQUAL(ESP_OK, sleep_ctx.sleep_request_result);
}
#endif
ppa_fill_oper_config_t oper_config = {};
oper_config.out.buffer = out_buf;
oper_config.out.buffer_size = out_buf_size;
oper_config.out.pic_w = w;
oper_config.out.pic_h = h;
oper_config.out.block_offset_x = block_offset_x;
oper_config.out.block_offset_y = block_offset_y;
oper_config.out.fill_cm = out_cm;
oper_config.fill_block_w = block_w;
oper_config.fill_block_h = block_h;
oper_config.fill_argb_color = fill_color;
oper_config.mode = PPA_TRANS_MODE_BLOCKING;
TEST_ESP_OK(ppa_do_fill(ppa_client_handle, &oper_config));
// Check result
color_pixel_rgb565_data_t fill_pixel_expected = {};
fill_pixel_expected.r = fill_color.r >> 3;
fill_pixel_expected.g = fill_color.g >> 2;
fill_pixel_expected.b = fill_color.b >> 3;
TEST_ASSERT_EACH_EQUAL_UINT16(fill_pixel_expected.val, (void *)((uint32_t)out_buf + w * block_offset_y * out_pixel_depth / 8), block_w * block_h);
#if !(CONFIG_IDF_TARGET_ESP32P4 && CONFIG_ESP32P4_SELECTS_REV_LESS_V3)
// Test a yuv color fill
oper_config.out.fill_cm = PPA_FILL_COLOR_MODE_YUV422_UYVY; // output YUV422 is with UYVY packed order
color_macroblock_yuv_data_t fill_yuv_color = {};
fill_yuv_color.y = 0xFF;
fill_yuv_color.u = 0x55;
fill_yuv_color.v = 0xAA;
oper_config.fill_yuv_color = fill_yuv_color;
out_pixel_depth = color_hal_pixel_format_fourcc_get_bit_depth((esp_color_fourcc_t)PPA_FILL_COLOR_MODE_YUV422_UYVY); // bits
TEST_ESP_OK(ppa_do_fill(ppa_client_handle, &oper_config));
// Test a yuv color fill
oper_config.out.fill_cm = PPA_FILL_COLOR_MODE_YUV422_UYVY; // output YUV422 is with UYVY packed order
color_macroblock_yuv_data_t fill_yuv_color = {};
fill_yuv_color.y = 0xFF;
fill_yuv_color.u = 0x55;
fill_yuv_color.v = 0xAA;
oper_config.fill_yuv_color = fill_yuv_color;
out_pixel_depth = color_hal_pixel_format_fourcc_get_bit_depth((esp_color_fourcc_t)PPA_FILL_COLOR_MODE_YUV422_UYVY); // bits
TEST_ESP_OK(ppa_do_fill(ppa_client_handle, &oper_config));
// Check result (2 pixels per macro pixel)
const uint32_t fill_pixel_expected_yuv422 = ((fill_yuv_color.y << 24) | (fill_yuv_color.v << 16) | (fill_yuv_color.y << 8) | (fill_yuv_color.u));
TEST_ASSERT_EACH_EQUAL_UINT32(fill_pixel_expected_yuv422, (void *)((uint32_t)out_buf + w * block_offset_y * out_pixel_depth / 8), block_w * block_h / 2);
// Check result (2 pixels per macro pixel)
const uint32_t fill_pixel_expected_yuv422 = ((fill_yuv_color.y << 24) | (fill_yuv_color.v << 16) | (fill_yuv_color.y << 8) | (fill_yuv_color.u));
TEST_ASSERT_EACH_EQUAL_UINT32(fill_pixel_expected_yuv422, (void *)((uint32_t)out_buf + w * block_offset_y * out_pixel_depth / 8), block_w * block_h / 2);
#endif // !(CONFIG_IDF_TARGET_ESP32P4 && CONFIG_ESP32P4_SELECTS_REV_LESS_V3)
memset(out_buf, 0xFF, out_buf_size);
esp_cache_msync((void *)out_buf, out_buf_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
}
#if SOC_LIGHT_SLEEP_SUPPORTED
esp_sleep_set_sleep_context(NULL);
#endif
TEST_ESP_OK(ppa_unregister_client(ppa_client_handle));
#if CONFIG_PM_ENABLE
enable_pm_strategy(false);
#endif
free(out_buf);
}
TEST_CASE("ppa_fill_basic_data_correctness_check", "[PPA]")
{
ppa_fill_basic_data_correctness_check(false);
#if CONFIG_PM_ENABLE
printf("\nTest again with auto light sleep...\n");
ppa_fill_basic_data_correctness_check(true);
#endif
}
/* All performance tests are tested under the following situations:
* - Testing PPA speed where in_buffer(s) and out_buffer all located in PSRAM
* - Only 2D-DMA is using PSRAM
@@ -4,6 +4,7 @@ CONFIG_ESP32P4_SELECTS_REV_LESS_V3=y
CONFIG_PM_ENABLE=y
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
CONFIG_PM_DFS_INIT_AUTO=y
CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
@@ -1,6 +1,7 @@
CONFIG_PM_ENABLE=y
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
CONFIG_PM_DFS_INIT_AUTO=y
CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
@@ -1,3 +1,5 @@
CONFIG_FREERTOS_HZ=1000
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_IDF_EXPERIMENTAL_FEATURES=y
CONFIG_ESP_SLEEP_DEBUG=y