test(lp_core): add LP UART read_bytes multi-device tests

Made-with: Cursor
This commit is contained in:
Sudeep Mohanty
2026-07-27 15:35:30 +02:00
parent 63e1ee965f
commit f5f061601a
3 changed files with 196 additions and 1 deletions
@@ -1,11 +1,14 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "hal/uart_types.h"
#include "hal/uart_ll.h"
#include "test_shared.h"
#include "ulp_lp_core_utils.h"
#include "ulp_lp_core_uart.h"
@@ -28,6 +31,12 @@ uint8_t rx_data[LP_UART_BUFFER_LEN] = {};
volatile uint8_t tx_len = 0;
volatile uint8_t rx_len = 0;
/* Last lp_core_uart_read_bytes return value for LP UART multi-device tests */
volatile int32_t read_return_value = 0;
/* Guarded buffer for read_bytes buffer-bounds test (pre + user + post) */
lp_uart_read_bounds_guard_t read_bounds_guard_region;
/* LP Core print test variables */
volatile char test_string[25];
volatile char test_long_string[200];
@@ -36,6 +45,17 @@ volatile uint32_t test_unsigned_integer;
volatile int test_hex;
volatile char test_character;
/* Wait until the HP peer has filled the LP UART RX FIFO with the full test burst,
* then allow the RX timeout condition to settle. This makes read_bytes behaviour
* deterministic for multi-device tests that depend on a single large FIFO drain. */
static void lp_uart_wait_rx_burst_ready(void)
{
uart_dev_t *dev = (uart_dev_t *)UART_LL_GET_HW(LP_UART_PORT_NUM);
while ((size_t)uart_ll_get_rxfifo_len(dev) < LP_UART_READ_RETURN_VALUE_BURST_LEN) {
}
ulp_lp_core_delay_us(400);
}
int main(void)
{
/* Enable interrupts.
@@ -89,6 +109,27 @@ int main(void)
}
}
if (test_cmd == LP_CORE_LP_UART_READ_BYTES_RETURN_VALUE_TEST) {
/* Read a burst that fits in hardware RX FIFO; HP asserts return value
* equals requested length (see test_lp_uart_read_bytes_return_value). */
lp_uart_wait_rx_burst_ready();
read_return_value = lp_core_uart_read_bytes(LP_UART_PORT_NUM, rx_data,
LP_UART_READ_RETURN_VALUE_BURST_LEN,
LP_UART_TRANS_WAIT_FOREVER);
}
if (test_cmd == LP_CORE_LP_UART_READ_BYTES_BOUNDS_TEST) {
/* Request a small user buffer while the link partner sends a larger burst;
* read_bytes must not corrupt guard regions past user_buf. */
memset(&read_bounds_guard_region, LP_UART_READ_BOUNDS_GUARD_PATTERN,
sizeof(read_bounds_guard_region));
lp_uart_wait_rx_burst_ready();
read_return_value = lp_core_uart_read_bytes(LP_UART_PORT_NUM,
read_bounds_guard_region.user_buf,
LP_UART_READ_BOUNDS_USER_BUF_LEN,
LP_UART_TRANS_WAIT_FOREVER);
}
if (test_cmd == LP_CORE_LP_UART_PRINT_TEST) {
/* Write various cases to test lp_core_printf to test various format specifiers */
lp_core_printf("%s\r\n", test_string);
@@ -5,6 +5,9 @@
*/
#pragma once
#include <stdint.h>
#include "soc/soc_caps.h"
#define XOR_MASK 0xDEADBEEF
/* I2C test params */
@@ -15,6 +18,37 @@
/* LP UART test param */
#define UART_BUF_SIZE 1024
/*
* LP UART read_bytes return-value test (HP + LP):
* The burst must fit in the hardware RX FIFO yet typically exceed the LP UART
* driver's default RX FIFO full threshold (LP_UART_FULL_THRESH_DEFAULT in
* components/ulp/lp_core/lp_core/lp_core_uart.c). If either value changes,
* revisit this guard.
*/
#if (SOC_LP_UART_FIFO_LEN) < 12
#error "SOC_LP_UART_FIFO_LEN too small for LP UART read_bytes return-value test"
#endif
#define LP_UART_READ_RETURN_VALUE_BURST_LEN ((SOC_LP_UART_FIFO_LEN) - 1)
/* Small user buffer for read_bytes buffer-bounds test; HP sends RETURN_VALUE_BURST_LEN. */
#define LP_UART_READ_BOUNDS_USER_BUF_LEN ((SOC_LP_UART_FIFO_LEN) / 4)
/* Guard memory on each side of the user buffer (worst case = full FIFO depth per side). */
#define LP_UART_READ_BOUNDS_GUARD_LEN (2 * (SOC_LP_UART_FIFO_LEN))
#define LP_UART_READ_BOUNDS_GUARD_PATTERN 0xAA
typedef struct __attribute__((packed))
{
uint8_t pre_guard[LP_UART_READ_BOUNDS_GUARD_LEN];
uint8_t user_buf[LP_UART_READ_BOUNDS_USER_BUF_LEN];
uint8_t post_guard[LP_UART_READ_BOUNDS_GUARD_LEN];
} lp_uart_read_bounds_guard_t;
_Static_assert(sizeof(lp_uart_read_bounds_guard_t) ==
(LP_UART_READ_BOUNDS_GUARD_LEN + LP_UART_READ_BOUNDS_USER_BUF_LEN + LP_UART_READ_BOUNDS_GUARD_LEN),
"lp_uart_read_bounds_guard_t layout must match LP/HP shared memory view");
typedef enum {
LP_CORE_READ_WRITE_TEST = 1,
LP_CORE_DELAY_TEST,
@@ -27,6 +61,8 @@ typedef enum {
LP_CORE_LP_UART_READ_TEST,
LP_CORE_LP_UART_MULTI_BYTE_READ_TEST,
LP_CORE_LP_UART_PRINT_TEST,
LP_CORE_LP_UART_READ_BYTES_RETURN_VALUE_TEST,
LP_CORE_LP_UART_READ_BYTES_BOUNDS_TEST,
LP_CORE_LP_SPI_WRITE_READ_TEST,
LP_CORE_NO_COMMAND,
} lp_core_test_commands_t;
@@ -945,3 +945,121 @@ TEST_CASE_MULTIPLE_DEVICES("LP-Core LP-UART read test - LP GPIO Matrix routing",
TEST_CASE_MULTIPLE_DEVICES("LP-Core LP-UART print test",
"[lp_core][uart][test_env=generic_multi_device][timeout=150]",
test_lp_uart_print, hp_uart_read_print);
/* HP peer: send one UART burst sized for LP read_bytes return-value / bounds tests. */
static void hp_uart_send_lp_read_test_burst(void)
{
unity_wait_for_signal("LP UART init done");
uart_config_t hp_uart_cfg = {
.baud_rate = lp_uart_cfg.uart_proto_cfg.baud_rate,
.data_bits = lp_uart_cfg.uart_proto_cfg.data_bits,
.parity = lp_uart_cfg.uart_proto_cfg.parity,
.stop_bits = lp_uart_cfg.uart_proto_cfg.stop_bits,
.flow_ctrl = lp_uart_cfg.uart_proto_cfg.flow_ctrl,
.source_clk = UART_SCLK_DEFAULT,
};
int intr_alloc_flags = 0;
#if CONFIG_UART_ISR_IN_IRAM
intr_alloc_flags = ESP_INTR_FLAG_IRAM;
#endif
ESP_ERROR_CHECK(uart_driver_install(UART_NUM_1, UART_BUF_SIZE, 0, 0, NULL, intr_alloc_flags));
ESP_ERROR_CHECK(uart_param_config(UART_NUM_1, &hp_uart_cfg));
ESP_ERROR_CHECK(uart_set_pin(UART_NUM_1, lp_uart_cfg.uart_pin_cfg.rx_io_num,
lp_uart_cfg.uart_pin_cfg.tx_io_num,
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
unity_send_signal("HP UART init done");
unity_wait_for_signal("LP UART recv ready");
ESP_ERROR_CHECK(uart_flush_input(UART_NUM_1));
uint8_t tx_buf[LP_UART_READ_RETURN_VALUE_BURST_LEN];
for (int i = 0; i < LP_UART_READ_RETURN_VALUE_BURST_LEN; i++) {
tx_buf[i] = (uint8_t)(0x30 + i);
}
uart_write_bytes(UART_NUM_1, (const char *)tx_buf, LP_UART_READ_RETURN_VALUE_BURST_LEN);
ESP_ERROR_CHECK(uart_wait_tx_done(UART_NUM_1, pdMS_TO_TICKS(500)));
unity_wait_for_signal("LP UART recv data done");
uart_driver_delete(UART_NUM_1);
vTaskDelay(1);
}
/* LP requests lp_core_uart_read_bytes(..., len = LP_UART_READ_RETURN_VALUE_BURST_LEN, ...).
* Asserts the return value matches len after the HP sends the same byte count. */
static void test_lp_uart_read_bytes_return_value(void)
{
TEST_ASSERT(ESP_OK == lp_core_uart_init(&lp_uart_cfg));
unity_send_signal("LP UART init done");
unity_wait_for_signal("HP UART init done");
ulp_lp_core_cfg_t lp_cfg = {
.wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU,
};
load_and_start_lp_core_firmware(&lp_cfg, lp_core_main_uart_bin_start, lp_core_main_uart_bin_end);
ulp_test_cmd = LP_CORE_LP_UART_READ_BYTES_RETURN_VALUE_TEST;
vTaskDelay(10);
unity_send_signal("LP UART recv ready");
while (ulp_test_cmd_reply != LP_CORE_COMMAND_OK) {
vTaskDelay(10);
}
int32_t ret_val = (int32_t)ulp_read_return_value;
ESP_LOGI(TAG, "read_bytes returned %ld, expected %d", (long)ret_val, LP_UART_READ_RETURN_VALUE_BURST_LEN);
TEST_ASSERT_EQUAL(LP_UART_READ_RETURN_VALUE_BURST_LEN, ret_val);
unity_send_signal("LP UART recv data done");
}
/* LP reads into a short user buffer while the HP sends LP_UART_READ_RETURN_VALUE_BURST_LEN
* bytes. Asserts return length is within the user buffer and guard memory is intact. */
static void test_lp_uart_read_bytes_buffer_bounds(void)
{
TEST_ASSERT(ESP_OK == lp_core_uart_init(&lp_uart_cfg));
unity_send_signal("LP UART init done");
unity_wait_for_signal("HP UART init done");
ulp_lp_core_cfg_t lp_cfg = {
.wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU,
};
load_and_start_lp_core_firmware(&lp_cfg, lp_core_main_uart_bin_start, lp_core_main_uart_bin_end);
ulp_test_cmd = LP_CORE_LP_UART_READ_BYTES_BOUNDS_TEST;
vTaskDelay(10);
unity_send_signal("LP UART recv ready");
while (ulp_test_cmd_reply != LP_CORE_COMMAND_OK) {
vTaskDelay(10);
}
int32_t ret_val = (int32_t)ulp_read_return_value;
ESP_LOGI(TAG, "read_bytes returned %ld, max allowed %d", (long)ret_val, LP_UART_READ_BOUNDS_USER_BUF_LEN);
TEST_ASSERT_LESS_OR_EQUAL(LP_UART_READ_BOUNDS_USER_BUF_LEN, ret_val);
TEST_ASSERT_GREATER_THAN(0, ret_val);
lp_uart_read_bounds_guard_t *region = (lp_uart_read_bounds_guard_t *)&ulp_read_bounds_guard_region;
for (int i = 0; i < LP_UART_READ_BOUNDS_GUARD_LEN; i++) {
TEST_ASSERT_EQUAL_HEX8_MESSAGE(LP_UART_READ_BOUNDS_GUARD_PATTERN, region->pre_guard[i],
"pre-guard memory corrupted");
}
for (int i = 0; i < LP_UART_READ_BOUNDS_GUARD_LEN; i++) {
TEST_ASSERT_EQUAL_HEX8_MESSAGE(LP_UART_READ_BOUNDS_GUARD_PATTERN, region->post_guard[i],
"post-guard memory corrupted");
}
unity_send_signal("LP UART recv data done");
}
/* read_bytes: return value and buffer bounds (LP UART driver) */
TEST_CASE_MULTIPLE_DEVICES("LP-Core LP-UART read_bytes return value test",
"[lp_core][uart][test_env=generic_multi_device][timeout=150]",
test_lp_uart_read_bytes_return_value, hp_uart_send_lp_read_test_burst);
TEST_CASE_MULTIPLE_DEVICES("LP-Core LP-UART read_bytes buffer bounds test",
"[lp_core][uart][test_env=generic_multi_device][timeout=150]",
test_lp_uart_read_bytes_buffer_bounds, hp_uart_send_lp_read_test_burst);