Skip to content

Commit fd54244

Browse files
committed
feature(esp_tinyusb): Added CPU load test case in runtime_config test
1 parent 0c6cf9e commit fd54244

File tree

3 files changed

+196
-0
lines changed

3 files changed

+196
-0
lines changed
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
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 test_cpu_load_init or test_cpu_load_measure fail due to insufficient array 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 allocate 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. Try increasing STAT_CONFIG_ARRAY_SIZE_OFFSET");
55+
}
56+
57+
/**
58+
* @brief Measure CPU load since initialization
59+
*
60+
* Note: test_cpu_load_init() must be called before this function
61+
*/
62+
static void test_cpu_load_measure(void)
63+
{
64+
end_array_size = uxTaskGetNumberOfTasks() + STAT_CONFIG_ARRAY_SIZE_OFFSET;
65+
end_array = malloc(sizeof(TaskStatus_t) * end_array_size);
66+
TEST_ASSERT_NOT_NULL_MESSAGE(end_array, "Insufficient memory to allocate internal arrays");
67+
68+
// Get post delay task states
69+
end_array_size = uxTaskGetSystemState(end_array, end_array_size, &end_run_time);
70+
TEST_ASSERT_MESSAGE(end_array_size != 0, "Insufficient array size for uxTaskGetSystemState. Try increasing STAT_CONFIG_ARRAY_SIZE_OFFSET");
71+
72+
// Calculate total_elapsed_time in units of run time stats clock period.
73+
uint32_t total_elapsed_time = (end_run_time - start_run_time);
74+
TEST_ASSERT_MESSAGE(total_elapsed_time != 0, "Delay duration too short");
75+
76+
#if (STAT_CONFIG_ALL_TASKS)
77+
printf("\n%-20s %10s %8s\n", "Name", "Run time", "CPU load");
78+
#endif // STAT_CONFIG_ALL_TASKS
79+
// Print TinyUSB statistics only
80+
for (int i = 0; i < start_array_size; i++) {
81+
int k = -1;
82+
for (int j = 0; j < end_array_size; j++) {
83+
if (start_array[i].xHandle == end_array[j].xHandle) {
84+
k = j;
85+
// Mark that task have been matched by overwriting their handles
86+
start_array[i].xHandle = NULL;
87+
end_array[j].xHandle = NULL;
88+
break;
89+
}
90+
}
91+
// Check if matching task found
92+
if (k >= 0) {
93+
uint32_t task_elapsed_time = end_array[k].ulRunTimeCounter - start_array[i].ulRunTimeCounter;
94+
uint32_t percentage_time = (task_elapsed_time * 100UL) / (total_elapsed_time * CONFIG_FREERTOS_NUMBER_OF_CORES);
95+
#if (STAT_CONFIG_ALL_TASKS)
96+
printf("%-20.20s %10" PRIu32 " %7" PRIu32 "%%\n",
97+
start_array[i].pcTaskName, // left-aligned, max 20 chars
98+
task_elapsed_time, // right-aligned, width 10
99+
percentage_time); // right-aligned, width 7 + '%' char
100+
#endif // STAT_CONFIG_ALL_TASKS
101+
// Save the TinyUSB task stats for test validation
102+
if (strcmp(start_array[i].pcTaskName, TINYUSB_TASK_NAME) == 0) {
103+
_tinyusb_run_time = task_elapsed_time;
104+
_tinyusb_cpu_load = percentage_time;
105+
}
106+
}
107+
}
108+
#if (STAT_CONFIG_ALL_TASKS)
109+
// Print unmatched tasks
110+
for (int i = 0; i < start_array_size; i++) {
111+
if (start_array[i].xHandle != NULL) {
112+
printf("%-20.20s %10s\n",
113+
start_array[i].pcTaskName, // left-aligned, max 20 chars
114+
"Deleted"); // right-aligned, width 10
115+
}
116+
}
117+
for (int i = 0; i < end_array_size; i++) {
118+
if (end_array[i].xHandle != NULL) {
119+
printf("%-20.20s %10s\n",
120+
end_array[i].pcTaskName, // left-aligned, max 20 chars
121+
"Created"); // right-aligned, width 10
122+
}
123+
}
124+
#endif // STAT_CONFIG_ALL_TASKS
125+
free(start_array);
126+
free(end_array);
127+
printf("CPU load measurement test completed.\n");
128+
}
129+
130+
/**
131+
* @brief Test TinyUSB CPU load measurement
132+
*
133+
* Scenario:
134+
* - Install TinyUSB driver with default configuration
135+
* - wait for device connection
136+
* - measure CPU load
137+
* - uninstall driver
138+
* - show results
139+
*/
140+
TEST_CASE("[CPU load] Install & Uninstall, default configuration", "[cpu_load]")
141+
{
142+
#if (!CONFIG_FREERTOS_UNICORE)
143+
// Allow other core to finish initialization
144+
vTaskDelay(pdMS_TO_TICKS(100));
145+
#endif // (!CONFIG_FREERTOS_UNICORE)
146+
147+
// Install TinyUSB driver
148+
tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG(test_device_event_handler);
149+
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_install(&tusb_cfg));
150+
151+
// Initialize CPU load measurement
152+
test_cpu_load_init();
153+
154+
// Wait for the device to be mounted and enumerated by the Host
155+
test_device_wait();
156+
printf("\t -> Device connected\n");
157+
158+
// Measure CPU load
159+
test_cpu_load_measure();
160+
161+
// Uninstall TinyUSB driver
162+
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_uninstall());
163+
164+
// Show results
165+
printf("TinyUSB Run time: %" PRIu32 " ticks\n", _tinyusb_run_time);
166+
printf("TinyUSB CPU load: %" PRIu32 " %%\n", _tinyusb_cpu_load);
167+
}
168+
169+
#endif // SOC_USB_OTG_SUPPORTED

device/esp_tinyusb/test_apps/runtime_config/pytest_runtime_config.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,26 @@ 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 0 < run_time < 1800, f'Unexpected TinyUSB Run time: {run_time}'

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)