Skip to content

Commit 31fd0b0

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

File tree

3 files changed

+202
-0
lines changed

3 files changed

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