Skip to content

Commit 6b988d8

Browse files
committed
test(uhci): Add tests for uhci
1 parent 1a3db8e commit 6b988d8

File tree

11 files changed

+409
-0
lines changed

11 files changed

+409
-0
lines changed

components/esp_driver_uart/test_apps/.build-test-rules.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,6 @@ components/esp_driver_uart/test_apps/uart_vfs:
3232
components/esp_driver_uart/test_apps/uhci:
3333
disable:
3434
- if: SOC_UHCI_SUPPORTED != 1
35+
- if: CONFIG_NAME == "psram" and SOC_SPIRAM_SUPPORTED != 1
3536
depends_components:
3637
- esp_driver_uart
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# This is the project CMakeLists.txt file for the test subproject
2+
cmake_minimum_required(VERSION 3.16)
3+
4+
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
5+
set(COMPONENTS main)
6+
7+
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components")
8+
9+
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
10+
project(uhci_test)
11+
12+
if(CONFIG_COMPILER_DUMP_RTL_FILES)
13+
add_custom_target(check_test_app_sections ALL
14+
COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py
15+
--rtl-dirs ${CMAKE_BINARY_DIR}/esp-idf/esp_driver_uart/,${CMAKE_BINARY_DIR}/esp-idf/hal/
16+
--elf-file ${CMAKE_BINARY_DIR}/uhci_test.elf
17+
find-refs
18+
--from-sections=.iram0.text
19+
--to-sections=.flash.text,.flash.rodata
20+
--exit-code
21+
DEPENDS ${elf}
22+
)
23+
endif()
24+
25+
message(STATUS "Checking uhci registers are not read-write by half-word")
26+
include($ENV{IDF_PATH}/tools/ci/check_register_rw_half_word.cmake)
27+
check_register_rw_half_word(SOC_MODULES "uhci" "pcr" "hp_sys_clkrst" "lpperi" "lp_clkrst"
28+
HAL_MODULES "uhci")
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
| Supported Targets | ESP32-C3 | ESP32-C6 | ESP32-P4 | ESP32-S3 |
2+
| ----------------- | -------- | -------- | -------- | -------- |
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
2+
# the component can be registered as WHOLE_ARCHIVE
3+
idf_component_register(
4+
SRCS "test_app_main.c"
5+
"test_uhci.c"
6+
REQUIRES esp_driver_uart unity test_utils esp_psram
7+
WHOLE_ARCHIVE
8+
)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include "unity.h"
8+
#include "unity_test_runner.h"
9+
#include "unity_test_utils.h"
10+
#include "esp_heap_caps.h"
11+
#include "esp_newlib.h"
12+
13+
#define TEST_MEMORY_LEAK_THRESHOLD (500)
14+
15+
void setUp(void)
16+
{
17+
unity_utils_record_free_mem();
18+
}
19+
20+
void tearDown(void)
21+
{
22+
esp_reent_cleanup(); //clean up some of the newlib's lazy allocations
23+
unity_utils_evaluate_leaks_direct(TEST_MEMORY_LEAK_THRESHOLD);
24+
}
25+
26+
void app_main(void)
27+
{
28+
printf(" __ __ __ __ ______ __ \n");
29+
printf("| | | | | | | | / || |\n");
30+
printf("| | | | | |__| | | ,----'| |\n");
31+
printf("| | | | | __ | | | | |\n");
32+
printf("| `--' | | | | | | `----.| |\n");
33+
printf(" \\______/ |__| |__| \\______||__|\n");
34+
35+
unity_run_menu();
36+
}
Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <string.h>
8+
#include <sys/param.h>
9+
#include "unity.h"
10+
#include "test_utils.h"
11+
#include "driver/uart.h"
12+
#include "driver/uhci.h"
13+
14+
#define DATA_LENGTH 1024
15+
#define EX_UART_NUM 1
16+
#define UART_TX_IO 2
17+
#define UART_RX_IO 3
18+
19+
TEST_CASE("UHCI driver memory leaking check", "[uhci]")
20+
{
21+
uart_config_t uart_config = {
22+
.baud_rate = 1 * 1000 * 1000,
23+
.data_bits = UART_DATA_8_BITS,
24+
.parity = UART_PARITY_DISABLE,
25+
.stop_bits = UART_STOP_BITS_1,
26+
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
27+
.source_clk = UART_SCLK_DEFAULT,
28+
};
29+
30+
//UART parameter config
31+
TEST_ESP_OK(uart_param_config(EX_UART_NUM, &uart_config));
32+
TEST_ESP_OK(uart_set_pin(EX_UART_NUM, UART_TX_IO, UART_RX_IO, -1, -1));
33+
34+
uhci_controller_config_t uhci_cfg = {
35+
.uart_port = EX_UART_NUM,
36+
.tx_trans_queue_depth = 30,
37+
.max_receive_internal_mem = 10 * 1024,
38+
.max_transmit_size = 10 * 1024,
39+
.dma_burst_size = 32,
40+
.rx_eof_flags.idle_eof = 1,
41+
};
42+
43+
uhci_controller_handle_t uhci_ctrl;
44+
45+
int size = esp_get_free_heap_size();
46+
for (int i = 0; i < 5; i++) {
47+
TEST_ESP_OK(uhci_new_controller(&uhci_cfg, &uhci_ctrl));
48+
vTaskDelay(10 / portTICK_PERIOD_MS);
49+
TEST_ESP_OK(uhci_del_controller(uhci_ctrl));
50+
}
51+
52+
TEST_ASSERT_INT_WITHIN(300, size, esp_get_free_heap_size());
53+
}
54+
55+
TEST_CASE("UHCI controller install-uninstall test", "[i2c]")
56+
{
57+
uhci_controller_config_t uhci_cfg = {
58+
.uart_port = EX_UART_NUM,
59+
.tx_trans_queue_depth = 30,
60+
.max_receive_internal_mem = 10 * 1024,
61+
.max_transmit_size = 10 * 1024,
62+
.dma_burst_size = 32,
63+
.rx_eof_flags.idle_eof = 1,
64+
};
65+
66+
uhci_controller_handle_t uhci_ctrl;
67+
uhci_controller_handle_t uhci_ctrl2;
68+
69+
TEST_ESP_OK(uhci_new_controller(&uhci_cfg, &uhci_ctrl));
70+
71+
TEST_ESP_ERR(ESP_ERR_NOT_FOUND, uhci_new_controller(&uhci_cfg, &uhci_ctrl2));
72+
73+
TEST_ESP_OK(uhci_del_controller(uhci_ctrl));
74+
}
75+
76+
typedef enum {
77+
UHCI_EVT_PARTIAL_DATA,
78+
UHCI_EVT_EOF,
79+
} uhci_event_t;
80+
81+
typedef struct {
82+
QueueHandle_t uhci_queue;
83+
size_t receive_size;
84+
uint8_t *p_receive_data;
85+
} uhci_context_t;
86+
87+
static void disp_buf(uint8_t *buf, int len)
88+
{
89+
int i;
90+
for (i = 0; i < len; i++) {
91+
printf("%02x ", buf[i]);
92+
if ((i + 1) % 16 == 0) {
93+
printf("\n");
94+
}
95+
}
96+
printf("\n");
97+
}
98+
99+
IRAM_ATTR static bool s_uhci_rx_event_cbs(uhci_controller_handle_t uhci_ctrl, const uhci_rx_event_data_t *edata, void *user_ctx)
100+
{
101+
uhci_context_t *ctx = (uhci_context_t *)user_ctx;
102+
BaseType_t xTaskWoken = 0;
103+
uhci_event_t evt = 0;
104+
if (edata->flags.totally_received) {
105+
evt = UHCI_EVT_EOF;
106+
ctx->receive_size += edata->recv_size;
107+
memcpy(ctx->p_receive_data, edata->data, edata->recv_size);
108+
} else {
109+
evt = UHCI_EVT_PARTIAL_DATA;
110+
ctx->receive_size += edata->recv_size;
111+
memcpy(ctx->p_receive_data, edata->data, edata->recv_size);
112+
ctx->p_receive_data += edata->recv_size;
113+
}
114+
115+
xQueueSendFromISR(ctx->uhci_queue, &evt, &xTaskWoken);
116+
return xTaskWoken;
117+
}
118+
119+
static void uhci_receive_test(void *arg)
120+
{
121+
uhci_controller_handle_t uhci_ctrl = ((uhci_controller_handle_t *)arg)[0];
122+
SemaphoreHandle_t exit_sema = ((SemaphoreHandle_t *)arg)[1];
123+
124+
uhci_context_t *ctx = calloc(1, sizeof(uhci_context_t));
125+
assert(ctx);
126+
ctx->uhci_queue = xQueueCreate(15, sizeof(uhci_event_t));
127+
assert(ctx->uhci_queue);
128+
129+
uint8_t *receive_data = heap_caps_calloc(1, DATA_LENGTH, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
130+
assert(receive_data);
131+
uint8_t *pdata = heap_caps_calloc(1, DATA_LENGTH / 4, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
132+
assert(pdata);
133+
ctx->p_receive_data = receive_data;
134+
135+
uhci_event_callbacks_t uhci_cbs = {
136+
.on_rx_trans_event = s_uhci_rx_event_cbs,
137+
};
138+
TEST_ESP_OK(uhci_register_event_callbacks(uhci_ctrl, &uhci_cbs, ctx));
139+
TEST_ESP_OK(uhci_receive(uhci_ctrl, pdata, DATA_LENGTH / 4));
140+
141+
uhci_event_t evt;
142+
while (1) {
143+
if (xQueueReceive(ctx->uhci_queue, &evt, portMAX_DELAY) == pdTRUE) {
144+
if (evt == UHCI_EVT_EOF) {
145+
disp_buf(receive_data, ctx->receive_size);
146+
for (int i = 0; i < DATA_LENGTH; i++) {
147+
TEST_ASSERT(receive_data[i] == (uint8_t)i);
148+
}
149+
printf("Received size: %d\n", ctx->receive_size);
150+
break;
151+
}
152+
}
153+
}
154+
155+
vQueueDelete(ctx->uhci_queue);
156+
free(pdata);
157+
free(receive_data);
158+
free(ctx);
159+
xSemaphoreGive(exit_sema);
160+
vTaskDelete(NULL);
161+
}
162+
163+
TEST_CASE("UHCI write and receive", "[uhci]")
164+
{
165+
uart_config_t uart_config = {
166+
.baud_rate = 5 * 1000 * 1000,
167+
.data_bits = UART_DATA_8_BITS,
168+
.parity = UART_PARITY_DISABLE,
169+
.stop_bits = UART_STOP_BITS_1,
170+
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
171+
.source_clk = UART_SCLK_XTAL,
172+
};
173+
TEST_ESP_OK(uart_param_config(EX_UART_NUM, &uart_config));
174+
// Connect TX and RX together for testing self send-receive
175+
TEST_ESP_OK(uart_set_pin(EX_UART_NUM, UART_TX_IO, UART_TX_IO, -1, -1));
176+
177+
uhci_controller_config_t uhci_cfg = {
178+
.uart_port = EX_UART_NUM,
179+
.tx_trans_queue_depth = 30,
180+
.max_receive_internal_mem = 10 * 1024,
181+
.max_transmit_size = 10 * 1024,
182+
.dma_burst_size = 32,
183+
.rx_eof_flags.idle_eof = 1,
184+
};
185+
186+
uhci_controller_handle_t uhci_ctrl;
187+
SemaphoreHandle_t exit_sema = xSemaphoreCreateBinary();
188+
TEST_ESP_OK(uhci_new_controller(&uhci_cfg, &uhci_ctrl));
189+
190+
void *args[] = { uhci_ctrl, exit_sema };
191+
xTaskCreate(uhci_receive_test, "uhci_receive_test", 4096 * 2, args, 5, NULL);
192+
193+
uint8_t data_wr[DATA_LENGTH];
194+
for (int i = 0; i < DATA_LENGTH; i++) {
195+
data_wr[i] = i;
196+
}
197+
TEST_ESP_OK(uhci_transmit(uhci_ctrl, data_wr, DATA_LENGTH));
198+
uhci_wait_all_tx_transaction_done(uhci_ctrl, portMAX_DELAY);
199+
xSemaphoreTake(exit_sema, portMAX_DELAY);
200+
vTaskDelay(2);
201+
TEST_ESP_OK(uhci_del_controller(uhci_ctrl));
202+
vSemaphoreDelete(exit_sema);
203+
}
204+
205+
#if CONFIG_SPIRAM
206+
#if CONFIG_IDF_TARGET_ESP32S3
207+
static void uhci_receive_test_in_psram(void *arg)
208+
{
209+
uhci_controller_handle_t uhci_ctrl = ((uhci_controller_handle_t *)arg)[0];
210+
SemaphoreHandle_t exit_sema = ((SemaphoreHandle_t *)arg)[1];
211+
212+
uhci_context_t *ctx = calloc(1, sizeof(uhci_context_t));
213+
assert(ctx);
214+
ctx->uhci_queue = xQueueCreate(15, sizeof(uhci_event_t));
215+
assert(ctx->uhci_queue);
216+
217+
uint8_t *receive_data = heap_caps_calloc(1, DATA_LENGTH, MALLOC_CAP_DMA | MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
218+
assert(receive_data);
219+
uint8_t *pdata = heap_caps_calloc(1, DATA_LENGTH / 4, MALLOC_CAP_DMA | MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
220+
assert(pdata);
221+
ctx->p_receive_data = receive_data;
222+
223+
uhci_event_callbacks_t uhci_cbs = {
224+
.on_rx_trans_event = s_uhci_rx_event_cbs,
225+
};
226+
TEST_ESP_OK(uhci_register_event_callbacks(uhci_ctrl, &uhci_cbs, ctx));
227+
TEST_ESP_OK(uhci_receive(uhci_ctrl, pdata, DATA_LENGTH / 4));
228+
229+
uhci_event_t evt;
230+
while (1) {
231+
if (xQueueReceive(ctx->uhci_queue, &evt, portMAX_DELAY) == pdTRUE) {
232+
if (evt == UHCI_EVT_EOF) {
233+
disp_buf(receive_data, ctx->receive_size);
234+
for (int i = 0; i < DATA_LENGTH; i++) {
235+
TEST_ASSERT(receive_data[i] == (uint8_t)i);
236+
}
237+
printf("Received size: %d\n", ctx->receive_size);
238+
break;
239+
}
240+
}
241+
}
242+
243+
vQueueDelete(ctx->uhci_queue);
244+
free(pdata);
245+
free(receive_data);
246+
free(ctx);
247+
xSemaphoreGive(exit_sema);
248+
vTaskDelete(NULL);
249+
}
250+
251+
TEST_CASE("UHCI write and receive in psram", "[uhci]")
252+
{
253+
uart_config_t uart_config = {
254+
.baud_rate = 5 * 1000 * 1000,
255+
.data_bits = UART_DATA_8_BITS,
256+
.parity = UART_PARITY_DISABLE,
257+
.stop_bits = UART_STOP_BITS_1,
258+
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
259+
.source_clk = UART_SCLK_XTAL,
260+
};
261+
TEST_ESP_OK(uart_param_config(EX_UART_NUM, &uart_config));
262+
// Connect TX and RX together for testing self send-receive
263+
TEST_ESP_OK(uart_set_pin(EX_UART_NUM, UART_TX_IO, UART_TX_IO, -1, -1));
264+
265+
uhci_controller_config_t uhci_cfg = {
266+
.uart_port = EX_UART_NUM,
267+
.tx_trans_queue_depth = 30,
268+
.max_receive_internal_mem = 10 * 1024,
269+
.max_transmit_size = 10 * 1024,
270+
.dma_burst_size = 32,
271+
.rx_eof_flags.idle_eof = 1,
272+
};
273+
274+
uhci_controller_handle_t uhci_ctrl;
275+
SemaphoreHandle_t exit_sema = xSemaphoreCreateBinary();
276+
TEST_ESP_OK(uhci_new_controller(&uhci_cfg, &uhci_ctrl));
277+
278+
void *args[] = { uhci_ctrl, exit_sema };
279+
xTaskCreate(uhci_receive_test_in_psram, "uhci_receive_test_in_psram", 4096 * 2, args, 5, NULL);
280+
281+
uint8_t *data_wr = heap_caps_calloc(1, DATA_LENGTH, MALLOC_CAP_DMA | MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
282+
for (int i = 0; i < DATA_LENGTH; i++) {
283+
data_wr[i] = i;
284+
}
285+
TEST_ESP_OK(uhci_transmit(uhci_ctrl, data_wr, DATA_LENGTH));
286+
uhci_wait_all_tx_transaction_done(uhci_ctrl, portMAX_DELAY);
287+
xSemaphoreTake(exit_sema, portMAX_DELAY);
288+
vTaskDelay(2);
289+
free(data_wr);
290+
TEST_ESP_OK(uhci_del_controller(uhci_ctrl));
291+
vSemaphoreDelete(exit_sema);
292+
}
293+
#endif
294+
#endif // CONFIG_SPIRAM

0 commit comments

Comments
 (0)