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

This commit is contained in:
Song Ruo Jing
2026-07-03 20:36:32 +08:00
parent 69d6deb7e6
commit 9516083cb1
20 changed files with 569 additions and 118 deletions
@@ -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
PRIV_REQUIRES esp_driver_ppa esp_psram unity esp_mm esp_pm
WHOLE_ARCHIVE)
@@ -18,6 +18,12 @@
#include "hal/color_hal.h"
#include "esp_cache.h"
#include "esp_random.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))
@@ -263,7 +269,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 = 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;
@@ -304,59 +323,111 @@ 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 = {
.oper_type = PPA_OPERATION_SRM,
.max_pending_trans_num = 1,
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
.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 = {
.in.buffer = in_buf,
.in.pic_w = w,
.in.pic_h = h,
.in.block_w = block_w,
.in.block_h = block_h,
.in.block_offset_x = in_block_offset_x,
.in.block_offset_y = in_block_offset_y,
.in.srm_cm = cm,
#if SOC_LIGHT_SLEEP_SUPPORTED
esp_sleep_context_t sleep_ctx;
esp_sleep_set_sleep_context(&sleep_ctx);
#endif
.out.buffer = out_buf,
.out.buffer_size = out_buf_size,
.out.pic_w = w,
.out.pic_h = h,
.out.block_offset_x = out_block_offset_x,
.out.block_offset_y = out_block_offset_y,
.out.srm_cm = cm,
// 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
.rotation_angle = rotation,
.scale_x = scale_x,
.scale_y = scale_y,
if (auto_light_sleep) {
vTaskDelay(pdMS_TO_TICKS(1000)); // enter auto light sleep here if we are testing PPA sleep retention feature
.rgb_swap = 0,
.byte_swap = 0,
.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");
// 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_srm_oper_config_t oper_config = {
.in.buffer = in_buf,
.in.pic_w = w,
.in.pic_h = h,
.in.block_w = block_w,
.in.block_h = block_h,
.in.block_offset_x = in_block_offset_x,
.in.block_offset_y = in_block_offset_y,
.in.srm_cm = cm,
.out.buffer = out_buf,
.out.buffer_size = out_buf_size,
.out.pic_w = w,
.out.pic_h = h,
.out.block_offset_x = out_block_offset_x,
.out.block_offset_y = out_block_offset_y,
.out.srm_cm = cm,
.rotation_angle = rotation,
.scale_x = scale_x,
.scale_y = scale_y,
.rgb_swap = 0,
.byte_swap = 0,
.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);
memset(out_buf, 0, out_buf_size);
esp_cache_msync((void *)out_buf, out_buf_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
}
printf("\n");
TEST_ASSERT_EQUAL_UINT8_ARRAY((void *)out_buf_expected, (void *)out_buf, buf_len);
#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;
@@ -389,7 +460,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, /**/
@@ -397,6 +468,10 @@ TEST_CASE("ppa_blend_basic_data_correctness_check", "[PPA]")
// /*******************************/
[16 ... 63] = 0,
};
uint8_t *in_fg_buf = 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:
@@ -412,67 +487,121 @@ 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 = {
.oper_type = PPA_OPERATION_BLEND,
.max_pending_trans_num = 1,
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
.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 = {
.in_bg.buffer = in_bg_buf,
.in_bg.pic_w = w,
.in_bg.pic_h = h,
.in_bg.block_w = block_w,
.in_bg.block_h = block_h,
.in_bg.block_offset_x = block_offset_x,
.in_bg.block_offset_y = block_offset_y,
.in_bg.blend_cm = in_bg_cm,
#if SOC_LIGHT_SLEEP_SUPPORTED
esp_sleep_context_t sleep_ctx;
esp_sleep_set_sleep_context(&sleep_ctx);
#endif
.in_fg.buffer = in_fg_buf,
.in_fg.pic_w = w,
.in_fg.pic_h = h,
.in_fg.block_w = block_w,
.in_fg.block_h = block_h,
.in_fg.block_offset_x = block_offset_x,
.in_fg.block_offset_y = block_offset_y,
.in_fg.blend_cm = in_fg_cm,
// 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
.out.buffer = out_buf,
.out.buffer_size = out_buf_size,
.out.pic_w = w,
.out.pic_h = h,
.out.block_offset_x = block_offset_x,
.out.block_offset_y = block_offset_y,
.out.blend_cm = out_cm,
if (auto_light_sleep) {
vTaskDelay(pdMS_TO_TICKS(1000)); // enter auto light sleep here if we are testing PPA sleep retention feature
.bg_alpha_update_mode = PPA_ALPHA_SCALE,
.bg_alpha_scale_ratio = bg_alpha_scale_ratio,
.fg_alpha_update_mode = PPA_ALPHA_INVERT,
.bg_ck_en = false,
.fg_ck_en = false,
.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");
// 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 = {
.in_bg.buffer = in_bg_buf,
.in_bg.pic_w = w,
.in_bg.pic_h = h,
.in_bg.block_w = block_w,
.in_bg.block_h = block_h,
.in_bg.block_offset_x = block_offset_x,
.in_bg.block_offset_y = block_offset_y,
.in_bg.blend_cm = in_bg_cm,
.in_fg.buffer = in_fg_buf,
.in_fg.pic_w = w,
.in_fg.pic_h = h,
.in_fg.block_w = block_w,
.in_fg.block_h = block_h,
.in_fg.block_offset_x = block_offset_x,
.in_fg.block_offset_y = block_offset_y,
.in_fg.blend_cm = in_fg_cm,
.out.buffer = out_buf,
.out.buffer_size = out_buf_size,
.out.pic_w = w,
.out.pic_h = h,
.out.block_offset_x = block_offset_x,
.out.block_offset_y = block_offset_y,
.out.blend_cm = out_cm,
.bg_alpha_update_mode = PPA_ALPHA_SCALE,
.bg_alpha_scale_ratio = bg_alpha_scale_ratio,
.fg_alpha_update_mode = PPA_ALPHA_INVERT,
.bg_ck_en = false,
.fg_ck_en = false,
.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
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;
@@ -495,43 +624,93 @@ 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 = {
.oper_type = PPA_OPERATION_FILL,
.max_pending_trans_num = 1,
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
.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 = {
.out.buffer = out_buf,
.out.buffer_size = out_buf_size,
.out.pic_w = w,
.out.pic_h = h,
.out.block_offset_x = block_offset_x,
.out.block_offset_y = block_offset_y,
.out.fill_cm = out_cm,
#if SOC_LIGHT_SLEEP_SUPPORTED
esp_sleep_context_t sleep_ctx;
esp_sleep_set_sleep_context(&sleep_ctx);
#endif
.fill_block_w = block_w,
.fill_block_h = block_h,
.fill_argb_color = fill_color,
// 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
.mode = PPA_TRANS_MODE_BLOCKING,
};
if (auto_light_sleep) {
vTaskDelay(pdMS_TO_TICKS(1000)); // enter auto light sleep here if we are testing PPA sleep retention feature
TEST_ESP_OK(ppa_do_fill(ppa_client_handle, &oper_config));
// 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
// Check result
const color_pixel_rgb565_data_t fill_pixel_expected = {.r = fill_color.r >> 3,
.g = fill_color.g >> 2,
.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);
ppa_fill_oper_config_t oper_config = {
.out.buffer = out_buf,
.out.buffer_size = out_buf_size,
.out.pic_w = w,
.out.pic_h = h,
.out.block_offset_x = block_offset_x,
.out.block_offset_y = block_offset_y,
.out.fill_cm = out_cm,
.fill_block_w = block_w,
.fill_block_h = block_h,
.fill_argb_color = fill_color,
.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);
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
@@ -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