Skip to content

Commit d14fb88

Browse files
committed
feature(esp_tinyusb): Added CPU load test case in runtime_config test
1 parent aed6a02 commit d14fb88

File tree

3 files changed

+201
-0
lines changed

3 files changed

+201
-0
lines changed
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include "soc/soc_caps.h"
8+
9+
#if SOC_USB_OTG_SUPPORTED
10+
11+
#include <stdio.h>
12+
#include <string.h>
13+
#include "freertos/FreeRTOS.h"
14+
#include "freertos/task.h"
15+
#include "esp_log.h"
16+
#include "esp_err.h"
17+
#include "unity.h"
18+
#include "tinyusb.h"
19+
#include "tinyusb_default_config.h"
20+
#include "device_handling.h"
21+
22+
// Enable this, if you need to see the stat for all tasks
23+
#define STAT_CONFIG_ALL_TASKS 0
24+
// Increase this if print_real_time_stats returns ESP_ERR_INVALID_SIZE
25+
#define STAT_CONFIG_ARRAY_SIZE_OFFSET 5
26+
27+
// TinyUSB task related variables
28+
const static char *TINYUSB_TASK_NAME = "TinyUSB";
29+
static uint32_t _tinyusb_run_time = 0;
30+
static uint32_t _tinyusb_cpu_load = 0;
31+
32+
// Arrays and variables for CPU load measurement
33+
static TaskStatus_t *start_array = NULL;
34+
static TaskStatus_t *end_array = NULL;
35+
static UBaseType_t start_array_size;
36+
static UBaseType_t end_array_size;
37+
static configRUN_TIME_COUNTER_TYPE start_run_time;
38+
static configRUN_TIME_COUNTER_TYPE end_run_time;
39+
40+
/**
41+
* @brief Initialize CPU load measurement
42+
*/
43+
static void test_cpu_load_init(void)
44+
{
45+
printf("Starting TinyUSB load measurement test...\n");
46+
47+
// Allocate array to store current task states
48+
start_array_size = uxTaskGetNumberOfTasks() + STAT_CONFIG_ARRAY_SIZE_OFFSET;
49+
start_array = malloc(sizeof(TaskStatus_t) * start_array_size);
50+
TEST_ASSERT_NOT_NULL_MESSAGE(start_array, "Insufficient memory to allocated internal arrays");
51+
52+
// Get current task states
53+
start_array_size = uxTaskGetSystemState(start_array, start_array_size, &start_run_time);
54+
TEST_ASSERT_MESSAGE(start_array_size != 0, "Insufficient array size for uxTaskGetSystemState. Trying increasing STAT_CONFIG_ARRAY_SIZE_OFFSET");
55+
56+
}
57+
58+
/**
59+
* @brief Measure CPU load since initialization
60+
*
61+
* Note: test_cpu_load_init() must be called before this function
62+
*/
63+
static void test_cpu_load_measure(void)
64+
{
65+
end_array_size = uxTaskGetNumberOfTasks() + STAT_CONFIG_ARRAY_SIZE_OFFSET;
66+
end_array = malloc(sizeof(TaskStatus_t) * end_array_size);
67+
TEST_ASSERT_NOT_NULL_MESSAGE(end_array, "Insufficient memory to allocated internal arrays");
68+
69+
// Get post delay task states
70+
end_array_size = uxTaskGetSystemState(end_array, end_array_size, &end_run_time);
71+
TEST_ASSERT_MESSAGE(end_array_size != 0, "Insufficient array size for uxTaskGetSystemState. Trying increasing STAT_CONFIG_ARRAY_SIZE_OFFSET");
72+
73+
// Calculate total_elapsed_time in units of run time stats clock period.
74+
uint32_t total_elapsed_time = (end_run_time - start_run_time);
75+
TEST_ASSERT_MESSAGE(total_elapsed_time != 0, "Delay duration too short");
76+
77+
#if (STAT_CONFIG_ALL_TASKS)
78+
printf("\n%-20s %10s %8s\n", "Name", "Run time", "CPU load");
79+
#endif // STAT_CONFIG_ALL_TASKS
80+
// Print TinyUSB statistics only
81+
for (int i = 0; i < start_array_size; i++) {
82+
int k = -1;
83+
for (int j = 0; j < end_array_size; j++) {
84+
if (start_array[i].xHandle == end_array[j].xHandle) {
85+
k = j;
86+
// Mark that task have been matched by overwriting their handles
87+
start_array[i].xHandle = NULL;
88+
end_array[j].xHandle = NULL;
89+
break;
90+
}
91+
}
92+
// Check if matching task found
93+
if (k >= 0) {
94+
uint32_t task_elapsed_time = end_array[k].ulRunTimeCounter - start_array[i].ulRunTimeCounter;
95+
uint32_t percentage_time = (task_elapsed_time * 100UL) / (total_elapsed_time * CONFIG_FREERTOS_NUMBER_OF_CORES);
96+
#if (STAT_CONFIG_ALL_TASKS)
97+
printf("%-20.20s %10" PRIu32 " %7" PRIu32 "%%\n",
98+
start_array[i].pcTaskName, // left-aligned, max 20 chars
99+
task_elapsed_time, // right-aligned, width 10
100+
percentage_time); // right-aligned, width 7 + '%' char
101+
#endif // STAT_CONFIG_ALL_TASKS
102+
// Save the TinyUSB task stats for test validation
103+
if (strcmp(start_array[i].pcTaskName, TINYUSB_TASK_NAME) == 0) {
104+
_tinyusb_run_time = task_elapsed_time;
105+
_tinyusb_cpu_load = percentage_time;
106+
}
107+
}
108+
}
109+
#if (STAT_CONFIG_ALL_TASKS)
110+
// Print unmatched tasks
111+
for (int i = 0; i < start_array_size; i++) {
112+
if (start_array[i].xHandle != NULL) {
113+
printf("%-20.20s %10s\n",
114+
start_array[i].pcTaskName, // left-aligned, max 20 chars
115+
"Deleted"); // right-aligned, width 10
116+
}
117+
}
118+
for (int i = 0; i < end_array_size; i++) {
119+
if (end_array[i].xHandle != NULL) {
120+
printf("%-20.20s %10s\n",
121+
start_array[i].pcTaskName, // left-aligned, max 20 chars
122+
"Created"); // right-aligned, width 10
123+
}
124+
}
125+
#endif // STAT_CONFIG_ALL_TASKS
126+
free(start_array);
127+
free(end_array);
128+
printf("CPU load measurement test completed.\n");
129+
}
130+
131+
/**
132+
* @brief Test TinyUSB CPU load measurement
133+
*
134+
* Scenario:
135+
* - Install TinyUSB driver with default configuration
136+
* - wait for device connection
137+
* - measure CPU load
138+
* - uninstall driver
139+
* - show results
140+
*/
141+
TEST_CASE("[CPU load] Install & Uninstall, default configuration", "[cpu_load]")
142+
{
143+
#if (!CONFIG_FREERTOS_UNICORE)
144+
// Allow other core to finish initialization
145+
vTaskDelay(pdMS_TO_TICKS(100));
146+
#endif // (!CONFIG_FREERTOS_UNICORE)
147+
148+
// Install TinyUSB driver
149+
tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG(test_device_event_handler);
150+
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_install(&tusb_cfg));
151+
152+
// Initialize CPU load measurement
153+
test_cpu_load_init();
154+
155+
// Wait for the device to be mounted and enumerated by the Host
156+
test_device_wait();
157+
printf("\t -> Device connected\n");
158+
159+
// Measure CPU load
160+
test_cpu_load_measure();
161+
162+
// Uninstall TinyUSB driver
163+
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_uninstall());
164+
165+
// Show results
166+
printf("TinyUSB Run time: %" PRIu32 " ticks\n", _tinyusb_run_time);
167+
printf("TinyUSB CPU load: %" PRIu32 " %%\n", _tinyusb_cpu_load);
168+
}
169+
170+
#endif // SOC_USB_OTG_SUPPORTED

device/esp_tinyusb/test_apps/runtime_config/pytest_runtime_config.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,30 @@ def test_usb_device_runtime_config(dut: IdfDut) -> None:
2020

2121
for periph in peripherals:
2222
dut.run_all_single_board_cases(group=periph)
23+
24+
@pytest.mark.esp32s2
25+
@pytest.mark.esp32s3
26+
@pytest.mark.esp32p4
27+
@pytest.mark.usb_device
28+
def test_cpu_load_task_stat_print(dut: IdfDut) -> None:
29+
'''
30+
Test to verify that Run time and CPU load measurement for TinyUSB task is working.
31+
This test runs only on runtime_config test app.
32+
33+
Test procedure:
34+
1. Run the test on the DUT
35+
2. Expect to see TinyUSB task CPU load printed in the output
36+
3. Expect TinyUSB task CPU load to be not greater than 0%
37+
'''
38+
dut.expect_exact('Press ENTER to see the list of tests.')
39+
dut.write('[cpu_load]')
40+
dut.expect_exact('Starting TinyUSB load measurement test...')
41+
dut.expect_exact('CPU load measurement test completed.')
42+
43+
line = dut.expect(r'TinyUSB Run time: (\d+) ticks')
44+
run_time = int(line.group(1))
45+
assert run_time < 1800, f'Unexpected TinyUSB Run time: {run_time}'
46+
47+
line = dut.expect(r'TinyUSB CPU load: (\d+) %')
48+
cpu_load = int(line.group(1))
49+
assert cpu_load == 0, f'Unexpected TinyUSB CPU load: {cpu_load} %'

device/esp_tinyusb/test_apps/runtime_config/sdkconfig.defaults

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ CONFIG_TINYUSB_CDC_COUNT=2
55
# Disable watchdogs, they'd get triggered during unity interactive menu
66
# CONFIG_ESP_TASK_WDT_INIT is not set
77

8+
# For CPU load measurement
9+
CONFIG_FREERTOS_USE_TRACE_FACILITY=y
10+
CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y
11+
812
# Run-time checks of Heap and Stack
913
CONFIG_HEAP_POISONING_COMPREHENSIVE=y
1014
CONFIG_COMPILER_STACK_CHECK_MODE_STRONG=y

0 commit comments

Comments
 (0)