Skip to content

Commit

Permalink
tests: modem: backends: uart: Add UART backend test suite
Browse files Browse the repository at this point in the history
The UART backend test suite performs 9 iterations of:

1. Open UART backend pipe
2. Transmit 8192 bytes of pseudo random data
3. Receive and validate bytes
4. close UART backend pipe

The test is run on real hardware, with the TX/RX pins
connected to each other to provide loopback functionality.

The test suite has been run on a STM32 and an nRF5340 board.
The test suite tests both the UART interrupt driven and
async APIs.

Signed-off-by: Bjarki Arge Andreasen <bjarki@arge-andreasen.me>
  • Loading branch information
bjarki-trackunit committed Nov 16, 2023
1 parent 0df16b9 commit 8c3b4de
Show file tree
Hide file tree
Showing 6 changed files with 325 additions and 0 deletions.
8 changes: 8 additions & 0 deletions tests/subsys/modem/backends/uart/CMakeLists.txt
@@ -0,0 +1,8 @@
# Copyright (c) 2023 Trackunit Corporation
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(modem_backend_uart_test)

target_sources(app PRIVATE src/main.c)
36 changes: 36 additions & 0 deletions tests/subsys/modem/backends/uart/boards/b_u585i_iot02a.overlay
@@ -0,0 +1,36 @@
/*
* Pins 2 and 3 must be connected to each other on the STMOD+1 connector to
* loopback RX/TX.
*/

/ {
aliases {
test-uart = &usart2;
};
};

&gpioh {
misc_fixed_usart2 {
gpio-hog;
gpios = <13 GPIO_ACTIVE_HIGH>;
output-high;
};
};

&gpdma1 {
status = "okay";
};

/* BG95 */
&usart2 {
pinctrl-0 = <&usart2_tx_pa2 &usart2_rx_pa3 &usart2_rts_pa1 &usart2_cts_pa0>;
pinctrl-names = "default";
current-speed = <115200>;
/* hw-flow-control; */

dmas = <&gpdma1 0 27 STM32_DMA_PERIPH_TX
&gpdma1 1 26 STM32_DMA_PERIPH_RX>;
dma-names = "tx", "rx";

status = "okay";
};
@@ -0,0 +1,38 @@
/*
* Pins P1.10 and P1.11 must be connected to each other to loopback RX/TX.
*/

/ {
aliases {
test-uart = &uart1;
};
};

&uart1 {
status = "okay";
current-speed = <115200>;
pinctrl-0 = <&uart1_default>;
pinctrl-1 = <&uart1_sleep>;
hw-flow-control;
pinctrl-names = "default", "sleep";
};

&pinctrl {
uart1_default: uart1_default {
group1 {
psels = <NRF_PSEL(UART_TX, 1, 11)>;
};
group2 {
psels = <NRF_PSEL(UART_RX, 1, 10)>;
bias-pull-up;
};
};

uart1_sleep: uart1_sleep {
group1 {
psels = <NRF_PSEL(UART_TX, 1, 10)>,
<NRF_PSEL(UART_RX, 1, 11)>;
low-power-enable;
};
};
};
10 changes: 10 additions & 0 deletions tests/subsys/modem/backends/uart/prj.conf
@@ -0,0 +1,10 @@
# Copyright (c) 2023 Trackunit Corporation
# SPDX-License-Identifier: Apache-2.0

CONFIG_MODEM_MODULES=y
CONFIG_MODEM_BACKEND_UART=y
CONFIG_SERIAL=y
CONFIG_ZTEST=y
CONFIG_LOG=y
CONFIG_ZTEST_SHUFFLE=y
CONFIG_ZTEST_SHUFFLE_TEST_REPEAT_COUNT=3
212 changes: 212 additions & 0 deletions tests/subsys/modem/backends/uart/src/main.c
@@ -0,0 +1,212 @@
/*
* Copyright (c) 2023 Trackunit Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/

/*
* This test suite sets up a modem_backend_uart instance connected to a UART which has its
* RX and TX pins wired together to provide loopback functionality. A large number of bytes
* containing a sequence of pseudo random numbers are then transmitted, received, and validated.
*
* The test suite repeats three times, opening and clsoing the modem_pipe attached to the
* modem_backend_uart instance before and after the tests respectively.
*/

/*************************************************************************************************/
/* Dependencies */
/*************************************************************************************************/
#include <zephyr/ztest.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/atomic.h>
#include <zephyr/modem/backend/uart.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*************************************************************************************************/
/* Mock pipe */
/*************************************************************************************************/
static const struct device *uart = DEVICE_DT_GET(DT_ALIAS(test_uart));
static struct modem_backend_uart uart_backend;
static struct modem_pipe *pipe;
K_SEM_DEFINE(receive_ready_sem, 0, 1);

/*************************************************************************************************/
/* Buffers */
/*************************************************************************************************/
static uint8_t backend_receive_buffer[4096];
static uint8_t backend_transmit_buffer[4096];
RING_BUF_DECLARE(transmit_ring_buf, 4096);
static uint8_t receive_buffer[4096];

/*************************************************************************************************/
/* Modem pipe callback */
/*************************************************************************************************/
static void modem_pipe_callback_handler(struct modem_pipe *pipe, enum modem_pipe_event event,
void *user_data)
{
switch (event) {
case MODEM_PIPE_EVENT_RECEIVE_READY:
k_sem_give(&receive_ready_sem);
break;
default:
break;
}
}

/*************************************************************************************************/
/* Helpers */
/*************************************************************************************************/
static uint32_t transmit_prng_state = 1234;
static uint32_t receive_prng_state = 1234;
static uint32_t transmit_size_prng_state;

static uint8_t transmit_prng_random(void)
{
transmit_prng_state = ((1103515245 * transmit_prng_state) + 12345) % (1U << 31);
return (uint8_t)(transmit_prng_state & 0xFF);
}

static uint8_t receive_prng_random(void)
{
receive_prng_state = ((1103515245 * receive_prng_state) + 12345) % (1U << 31);
return (uint8_t)(receive_prng_state & 0xFF);
}

static void prng_reset(void)
{
transmit_prng_state = 1234;
receive_prng_state = 1234;
transmit_size_prng_state = 0;
}

static void fill_transmit_ring_buf(void)
{
uint32_t space = ring_buf_space_get(&transmit_ring_buf);
uint8_t data;

for (uint32_t i = 0; i < space; i++) {
data = transmit_prng_random();
ring_buf_put(&transmit_ring_buf, &data, 1);
}
}

static uint32_t transmit_size_prng_random(void)
{
uint32_t size = 1;

for (uint8_t i = 0; i < transmit_size_prng_state; i++) {
size = size * 2;
}

transmit_size_prng_state = transmit_size_prng_state == 11
? 0
: transmit_size_prng_state + 1;

return size;
}

static int transmit_prng(uint32_t remaining)
{
uint8_t *reserved;
uint32_t reserved_size;
uint32_t transmit_size;
int ret;

fill_transmit_ring_buf();
reserved_size = ring_buf_get_claim(&transmit_ring_buf, &reserved, UINT32_MAX);
transmit_size = MIN(transmit_size_prng_random(), reserved_size);
transmit_size = MIN(remaining, transmit_size);
ret = modem_pipe_transmit(pipe, reserved, transmit_size);
if (ret < 0) {
return ret;
}
printk("TX: %u,%u\n", transmit_size, (uint32_t)ret);
__ASSERT(ret <= remaining, "Impossible number of bytes sent %u", (uint32_t)ret);
ring_buf_get_finish(&transmit_ring_buf, ret);
return ret;
}

static int receive_prng(void)
{
int ret = 0;

if (k_sem_take(&receive_ready_sem, K_NO_WAIT) == 0) {
ret = modem_pipe_receive(pipe, receive_buffer, sizeof(receive_buffer));
if (ret < 0) {
return -EFAULT;
}
for (uint32_t i = 0; i < (uint32_t)ret; i++) {
if (receive_prng_random() != receive_buffer[i]) {
return -EFAULT;
}
}
printk("RX: %u\n", (uint32_t)ret);
}

return ret;
}

/*************************************************************************************************/
/* Test setup */
/*************************************************************************************************/
static void *test_modem_backend_uart_setup(void)
{
const struct modem_backend_uart_config config = {
.uart = uart,
.receive_buf = backend_receive_buffer,
.receive_buf_size = 1024,
.transmit_buf = backend_transmit_buffer,
.transmit_buf_size = 1024,
};

pipe = modem_backend_uart_init(&uart_backend, &config);
modem_pipe_attach(pipe, modem_pipe_callback_handler, NULL);
k_msleep(10);

return NULL;
}

static void test_modem_backend_uart_before(void *f)
{
prng_reset();
ring_buf_reset(&transmit_ring_buf);
k_sem_reset(&receive_ready_sem);
__ASSERT_NO_MSG(modem_pipe_open(pipe) == 0);
}

static void test_modem_backend_uart_after(void *f)
{
__ASSERT_NO_MSG(modem_pipe_close(pipe) == 0);
}

/*************************************************************************************************/
/* Tests */
/*************************************************************************************************/
ZTEST(modem_backend_uart_suite, test_transmit_receive)
{
int32_t remaining = 8192;
uint32_t received = 0;
uint32_t transmitted = 0;
int ret;

while ((remaining != 0) || (received < 8192)) {
ret = transmit_prng(remaining);
zassert(ret > -1, "Failed to transmit data");
remaining -= (uint32_t)ret;
transmitted += (uint32_t)ret;
printk("TX ACC: %u\n", transmitted);

while (received < transmitted) {
ret = receive_prng();
zassert(ret > -1, "Recevied data is corrupted");

Check warning on line 204 in tests/subsys/modem/backends/uart/src/main.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

TYPO_SPELLING

tests/subsys/modem/backends/uart/src/main.c:204 'Recevied' may be misspelled - perhaps 'Received'?
received += (uint32_t)ret;
k_yield();
}
}
}

ZTEST_SUITE(modem_backend_uart_suite, NULL, test_modem_backend_uart_setup,
test_modem_backend_uart_before, test_modem_backend_uart_after, NULL);
21 changes: 21 additions & 0 deletions tests/subsys/modem/backends/uart/testcase.yaml
@@ -0,0 +1,21 @@
# Copyright (c) 2023 Trackunit Corporation
# SPDX-License-Identifier: Apache-2.0

tests:
modem.backends.uart.async:
tags: modem_backend
harness: ztest
platform_allow:
- b_u585i_iot02a
- nrf5340dk_nrf5340_cpuapp
extra_configs:
- CONFIG_UART_ASYNC_API=y

modem.backends.uart.isr:
tags: modem_backend
harness: ztest
platform_allow:
- b_u585i_iot02a
- nrf5340dk_nrf5340_cpuapp
extra_configs:
- CONFIG_UART_INTERRUPT_DRIVEN=y

0 comments on commit 8c3b4de

Please sign in to comment.