test(uhci): Add tests for uhci

This commit is contained in:
C.S.M
2025-04-24 14:13:14 +08:00
parent 1a3db8e025
commit 6b988d8a07
11 changed files with 409 additions and 0 deletions

View File

@@ -32,5 +32,6 @@ components/esp_driver_uart/test_apps/uart_vfs:
components/esp_driver_uart/test_apps/uhci:
disable:
- if: SOC_UHCI_SUPPORTED != 1
- if: CONFIG_NAME == "psram" and SOC_SPIRAM_SUPPORTED != 1
depends_components:
- esp_driver_uart

View File

@@ -0,0 +1,28 @@
# This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.16)
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
set(COMPONENTS main)
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(uhci_test)
if(CONFIG_COMPILER_DUMP_RTL_FILES)
add_custom_target(check_test_app_sections ALL
COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py
--rtl-dirs ${CMAKE_BINARY_DIR}/esp-idf/esp_driver_uart/,${CMAKE_BINARY_DIR}/esp-idf/hal/
--elf-file ${CMAKE_BINARY_DIR}/uhci_test.elf
find-refs
--from-sections=.iram0.text
--to-sections=.flash.text,.flash.rodata
--exit-code
DEPENDS ${elf}
)
endif()
message(STATUS "Checking uhci registers are not read-write by half-word")
include($ENV{IDF_PATH}/tools/ci/check_register_rw_half_word.cmake)
check_register_rw_half_word(SOC_MODULES "uhci" "pcr" "hp_sys_clkrst" "lpperi" "lp_clkrst"
HAL_MODULES "uhci")

View File

@@ -0,0 +1,2 @@
| Supported Targets | ESP32-C3 | ESP32-C6 | ESP32-P4 | ESP32-S3 |
| ----------------- | -------- | -------- | -------- | -------- |

View File

@@ -0,0 +1,8 @@
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
# the component can be registered as WHOLE_ARCHIVE
idf_component_register(
SRCS "test_app_main.c"
"test_uhci.c"
REQUIRES esp_driver_uart unity test_utils esp_psram
WHOLE_ARCHIVE
)

View File

@@ -0,0 +1,36 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "unity.h"
#include "unity_test_runner.h"
#include "unity_test_utils.h"
#include "esp_heap_caps.h"
#include "esp_newlib.h"
#define TEST_MEMORY_LEAK_THRESHOLD (500)
void setUp(void)
{
unity_utils_record_free_mem();
}
void tearDown(void)
{
esp_reent_cleanup(); //clean up some of the newlib's lazy allocations
unity_utils_evaluate_leaks_direct(TEST_MEMORY_LEAK_THRESHOLD);
}
void app_main(void)
{
printf(" __ __ __ __ ______ __ \n");
printf("| | | | | | | | / || |\n");
printf("| | | | | |__| | | ,----'| |\n");
printf("| | | | | __ | | | | |\n");
printf("| `--' | | | | | | `----.| |\n");
printf(" \\______/ |__| |__| \\______||__|\n");
unity_run_menu();
}

View File

@@ -0,0 +1,294 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <sys/param.h>
#include "unity.h"
#include "test_utils.h"
#include "driver/uart.h"
#include "driver/uhci.h"
#define DATA_LENGTH 1024
#define EX_UART_NUM 1
#define UART_TX_IO 2
#define UART_RX_IO 3
TEST_CASE("UHCI driver memory leaking check", "[uhci]")
{
uart_config_t uart_config = {
.baud_rate = 1 * 1000 * 1000,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_DEFAULT,
};
//UART parameter config
TEST_ESP_OK(uart_param_config(EX_UART_NUM, &uart_config));
TEST_ESP_OK(uart_set_pin(EX_UART_NUM, UART_TX_IO, UART_RX_IO, -1, -1));
uhci_controller_config_t uhci_cfg = {
.uart_port = EX_UART_NUM,
.tx_trans_queue_depth = 30,
.max_receive_internal_mem = 10 * 1024,
.max_transmit_size = 10 * 1024,
.dma_burst_size = 32,
.rx_eof_flags.idle_eof = 1,
};
uhci_controller_handle_t uhci_ctrl;
int size = esp_get_free_heap_size();
for (int i = 0; i < 5; i++) {
TEST_ESP_OK(uhci_new_controller(&uhci_cfg, &uhci_ctrl));
vTaskDelay(10 / portTICK_PERIOD_MS);
TEST_ESP_OK(uhci_del_controller(uhci_ctrl));
}
TEST_ASSERT_INT_WITHIN(300, size, esp_get_free_heap_size());
}
TEST_CASE("UHCI controller install-uninstall test", "[i2c]")
{
uhci_controller_config_t uhci_cfg = {
.uart_port = EX_UART_NUM,
.tx_trans_queue_depth = 30,
.max_receive_internal_mem = 10 * 1024,
.max_transmit_size = 10 * 1024,
.dma_burst_size = 32,
.rx_eof_flags.idle_eof = 1,
};
uhci_controller_handle_t uhci_ctrl;
uhci_controller_handle_t uhci_ctrl2;
TEST_ESP_OK(uhci_new_controller(&uhci_cfg, &uhci_ctrl));
TEST_ESP_ERR(ESP_ERR_NOT_FOUND, uhci_new_controller(&uhci_cfg, &uhci_ctrl2));
TEST_ESP_OK(uhci_del_controller(uhci_ctrl));
}
typedef enum {
UHCI_EVT_PARTIAL_DATA,
UHCI_EVT_EOF,
} uhci_event_t;
typedef struct {
QueueHandle_t uhci_queue;
size_t receive_size;
uint8_t *p_receive_data;
} uhci_context_t;
static void disp_buf(uint8_t *buf, int len)
{
int i;
for (i = 0; i < len; i++) {
printf("%02x ", buf[i]);
if ((i + 1) % 16 == 0) {
printf("\n");
}
}
printf("\n");
}
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)
{
uhci_context_t *ctx = (uhci_context_t *)user_ctx;
BaseType_t xTaskWoken = 0;
uhci_event_t evt = 0;
if (edata->flags.totally_received) {
evt = UHCI_EVT_EOF;
ctx->receive_size += edata->recv_size;
memcpy(ctx->p_receive_data, edata->data, edata->recv_size);
} else {
evt = UHCI_EVT_PARTIAL_DATA;
ctx->receive_size += edata->recv_size;
memcpy(ctx->p_receive_data, edata->data, edata->recv_size);
ctx->p_receive_data += edata->recv_size;
}
xQueueSendFromISR(ctx->uhci_queue, &evt, &xTaskWoken);
return xTaskWoken;
}
static void uhci_receive_test(void *arg)
{
uhci_controller_handle_t uhci_ctrl = ((uhci_controller_handle_t *)arg)[0];
SemaphoreHandle_t exit_sema = ((SemaphoreHandle_t *)arg)[1];
uhci_context_t *ctx = calloc(1, sizeof(uhci_context_t));
assert(ctx);
ctx->uhci_queue = xQueueCreate(15, sizeof(uhci_event_t));
assert(ctx->uhci_queue);
uint8_t *receive_data = heap_caps_calloc(1, DATA_LENGTH, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
assert(receive_data);
uint8_t *pdata = heap_caps_calloc(1, DATA_LENGTH / 4, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
assert(pdata);
ctx->p_receive_data = receive_data;
uhci_event_callbacks_t uhci_cbs = {
.on_rx_trans_event = s_uhci_rx_event_cbs,
};
TEST_ESP_OK(uhci_register_event_callbacks(uhci_ctrl, &uhci_cbs, ctx));
TEST_ESP_OK(uhci_receive(uhci_ctrl, pdata, DATA_LENGTH / 4));
uhci_event_t evt;
while (1) {
if (xQueueReceive(ctx->uhci_queue, &evt, portMAX_DELAY) == pdTRUE) {
if (evt == UHCI_EVT_EOF) {
disp_buf(receive_data, ctx->receive_size);
for (int i = 0; i < DATA_LENGTH; i++) {
TEST_ASSERT(receive_data[i] == (uint8_t)i);
}
printf("Received size: %d\n", ctx->receive_size);
break;
}
}
}
vQueueDelete(ctx->uhci_queue);
free(pdata);
free(receive_data);
free(ctx);
xSemaphoreGive(exit_sema);
vTaskDelete(NULL);
}
TEST_CASE("UHCI write and receive", "[uhci]")
{
uart_config_t uart_config = {
.baud_rate = 5 * 1000 * 1000,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_XTAL,
};
TEST_ESP_OK(uart_param_config(EX_UART_NUM, &uart_config));
// Connect TX and RX together for testing self send-receive
TEST_ESP_OK(uart_set_pin(EX_UART_NUM, UART_TX_IO, UART_TX_IO, -1, -1));
uhci_controller_config_t uhci_cfg = {
.uart_port = EX_UART_NUM,
.tx_trans_queue_depth = 30,
.max_receive_internal_mem = 10 * 1024,
.max_transmit_size = 10 * 1024,
.dma_burst_size = 32,
.rx_eof_flags.idle_eof = 1,
};
uhci_controller_handle_t uhci_ctrl;
SemaphoreHandle_t exit_sema = xSemaphoreCreateBinary();
TEST_ESP_OK(uhci_new_controller(&uhci_cfg, &uhci_ctrl));
void *args[] = { uhci_ctrl, exit_sema };
xTaskCreate(uhci_receive_test, "uhci_receive_test", 4096 * 2, args, 5, NULL);
uint8_t data_wr[DATA_LENGTH];
for (int i = 0; i < DATA_LENGTH; i++) {
data_wr[i] = i;
}
TEST_ESP_OK(uhci_transmit(uhci_ctrl, data_wr, DATA_LENGTH));
uhci_wait_all_tx_transaction_done(uhci_ctrl, portMAX_DELAY);
xSemaphoreTake(exit_sema, portMAX_DELAY);
vTaskDelay(2);
TEST_ESP_OK(uhci_del_controller(uhci_ctrl));
vSemaphoreDelete(exit_sema);
}
#if CONFIG_SPIRAM
#if CONFIG_IDF_TARGET_ESP32S3
static void uhci_receive_test_in_psram(void *arg)
{
uhci_controller_handle_t uhci_ctrl = ((uhci_controller_handle_t *)arg)[0];
SemaphoreHandle_t exit_sema = ((SemaphoreHandle_t *)arg)[1];
uhci_context_t *ctx = calloc(1, sizeof(uhci_context_t));
assert(ctx);
ctx->uhci_queue = xQueueCreate(15, sizeof(uhci_event_t));
assert(ctx->uhci_queue);
uint8_t *receive_data = heap_caps_calloc(1, DATA_LENGTH, MALLOC_CAP_DMA | MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
assert(receive_data);
uint8_t *pdata = heap_caps_calloc(1, DATA_LENGTH / 4, MALLOC_CAP_DMA | MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
assert(pdata);
ctx->p_receive_data = receive_data;
uhci_event_callbacks_t uhci_cbs = {
.on_rx_trans_event = s_uhci_rx_event_cbs,
};
TEST_ESP_OK(uhci_register_event_callbacks(uhci_ctrl, &uhci_cbs, ctx));
TEST_ESP_OK(uhci_receive(uhci_ctrl, pdata, DATA_LENGTH / 4));
uhci_event_t evt;
while (1) {
if (xQueueReceive(ctx->uhci_queue, &evt, portMAX_DELAY) == pdTRUE) {
if (evt == UHCI_EVT_EOF) {
disp_buf(receive_data, ctx->receive_size);
for (int i = 0; i < DATA_LENGTH; i++) {
TEST_ASSERT(receive_data[i] == (uint8_t)i);
}
printf("Received size: %d\n", ctx->receive_size);
break;
}
}
}
vQueueDelete(ctx->uhci_queue);
free(pdata);
free(receive_data);
free(ctx);
xSemaphoreGive(exit_sema);
vTaskDelete(NULL);
}
TEST_CASE("UHCI write and receive in psram", "[uhci]")
{
uart_config_t uart_config = {
.baud_rate = 5 * 1000 * 1000,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_XTAL,
};
TEST_ESP_OK(uart_param_config(EX_UART_NUM, &uart_config));
// Connect TX and RX together for testing self send-receive
TEST_ESP_OK(uart_set_pin(EX_UART_NUM, UART_TX_IO, UART_TX_IO, -1, -1));
uhci_controller_config_t uhci_cfg = {
.uart_port = EX_UART_NUM,
.tx_trans_queue_depth = 30,
.max_receive_internal_mem = 10 * 1024,
.max_transmit_size = 10 * 1024,
.dma_burst_size = 32,
.rx_eof_flags.idle_eof = 1,
};
uhci_controller_handle_t uhci_ctrl;
SemaphoreHandle_t exit_sema = xSemaphoreCreateBinary();
TEST_ESP_OK(uhci_new_controller(&uhci_cfg, &uhci_ctrl));
void *args[] = { uhci_ctrl, exit_sema };
xTaskCreate(uhci_receive_test_in_psram, "uhci_receive_test_in_psram", 4096 * 2, args, 5, NULL);
uint8_t *data_wr = heap_caps_calloc(1, DATA_LENGTH, MALLOC_CAP_DMA | MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
for (int i = 0; i < DATA_LENGTH; i++) {
data_wr[i] = i;
}
TEST_ESP_OK(uhci_transmit(uhci_ctrl, data_wr, DATA_LENGTH));
uhci_wait_all_tx_transaction_done(uhci_ctrl, portMAX_DELAY);
xSemaphoreTake(exit_sema, portMAX_DELAY);
vTaskDelay(2);
free(data_wr);
TEST_ESP_OK(uhci_del_controller(uhci_ctrl));
vSemaphoreDelete(exit_sema);
}
#endif
#endif // CONFIG_SPIRAM

View File

@@ -0,0 +1,20 @@
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
from pytest_embedded_idf.utils import idf_parametrize
from pytest_embedded_idf.utils import soc_filtered_targets
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
[
'release',
'cache_safe',
],
indirect=True,
)
@idf_parametrize('target', soc_filtered_targets('SOC_UHCI_SUPPORTED == 1'), indirect=['target'])
def test_uhci(dut: Dut) -> None:
dut.run_all_single_board_cases()

View File

@@ -0,0 +1,11 @@
CONFIG_COMPILER_DUMP_RTL_FILES=y
CONFIG_UHCI_ISR_CACHE_SAFE=y
CONFIG_GPIO_CTRL_FUNC_IN_IRAM=y
CONFIG_COMPILER_OPTIMIZATION_NONE=y
# place non-ISR FreeRTOS functions in Flash
CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y
# silent the error check, as the error string are stored in rodata, causing RTL check failure
CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
CONFIG_HAL_ASSERTION_SILENT=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE=y

View File

@@ -0,0 +1 @@
CONFIG_SPIRAM=y

View File

@@ -0,0 +1,6 @@
CONFIG_PM_ENABLE=y
CONFIG_PM_DFS_INIT_AUTO=y
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y

View File

@@ -0,0 +1,2 @@
CONFIG_FREERTOS_HZ=1000
CONFIG_ESP_TASK_WDT_EN=n