diff --git a/bsp/qemu-vexpress-a9/.config b/bsp/qemu-vexpress-a9/.config index 018ef71802ad..651d89915ce2 100644 --- a/bsp/qemu-vexpress-a9/.config +++ b/bsp/qemu-vexpress-a9/.config @@ -436,7 +436,7 @@ CONFIG_RT_USING_POSIX_MESSAGE_SEMAPHORE=y # # CONFIG_RT_USING_RYM is not set # CONFIG_RT_USING_ULOG is not set -# CONFIG_RT_USING_UTEST is not set +CONFIG_RT_USING_UTEST=y # CONFIG_RT_USING_VAR_EXPORT is not set CONFIG_RT_USING_RESOURCE_ID=y CONFIG_RT_USING_ADT=y @@ -478,7 +478,15 @@ CONFIG_RT_PAGE_MAX_ORDER=11 # # RT-Thread Utestcases # -# CONFIG_RT_USING_UTESTCASES is not set +CONFIG_RT_USING_UTESTCASES=y + CONFIG_RT_UTEST_SPI_LOOPBACK=y + CONFIG_RT_UTEST_SPI_LOOPBACK_BUS_NAME="spi2" + CONFIG_RT_UTEST_SPI_LOOPBACK_DEV_NAME="spi_loop" + CONFIG_RT_UTEST_SPI_LOOPBACK_DATA_SIZE=512 + CONFIG_RT_UTEST_SPI_LOOPBACK_MAX_HZ=10000000 + # CONFIG_RT_UTEST_SPI_LOOPBACK_API_SMOKE is not set + CONFIG_RT_UTEST_SPI_LOOPBACK_TIMEOUT=60 + # CONFIG_RT_UTEST_SPI_FLASH is not set # end of RT-Thread Utestcases # diff --git a/bsp/qemu-vexpress-a9/rtconfig.h b/bsp/qemu-vexpress-a9/rtconfig.h index 3ca7223f4078..6c132276fc31 100644 --- a/bsp/qemu-vexpress-a9/rtconfig.h +++ b/bsp/qemu-vexpress-a9/rtconfig.h @@ -225,6 +225,21 @@ #define RT_USING_PIN /* end of Device Drivers */ +/* Utilities */ + +#define RT_USING_UTESTCASES +#define RT_USING_UTEST +#define UTEST_THR_STACK_SIZE 8192 +#define UTEST_THR_PRIORITY 20 +#define RT_UTEST_MAX_OPTIONS 64 +#define RT_UTEST_SPI_LOOPBACK +#define RT_UTEST_SPI_LOOPBACK_BUS_NAME "spi2" +#define RT_UTEST_SPI_LOOPBACK_DEV_NAME "spi_loop" +#define RT_UTEST_SPI_LOOPBACK_DATA_SIZE 512 +#define RT_UTEST_SPI_LOOPBACK_MAX_HZ 10000000 +#define RT_UTEST_SPI_LOOPBACK_TIMEOUT 60 +/* end of Utilities */ + /* C/C++ and POSIX layer */ /* ISO-ANSI C layer */ diff --git a/components/drivers/spi/utest/Kconfig b/components/drivers/spi/utest/Kconfig index 1638916ab5b7..d16a0e4e987e 100644 --- a/components/drivers/spi/utest/Kconfig +++ b/components/drivers/spi/utest/Kconfig @@ -15,6 +15,9 @@ if RT_UTEST_SPI_LOOPBACK config RT_UTEST_SPI_LOOPBACK_BUS_NAME string "SPI bus name for loopback test" default "spi2" + help + On BSPs without a real SPI controller (e.g. qemu-vexpress-a9) the + virtual loopback bus "spi2" is registered by spi_virt_bus.c. config RT_UTEST_SPI_LOOPBACK_DEV_NAME string "Temporary SPI device name for loopback test" diff --git a/components/drivers/spi/utest/SConscript b/components/drivers/spi/utest/SConscript index 8b7a049e5e2e..05b1196c1382 100644 --- a/components/drivers/spi/utest/SConscript +++ b/components/drivers/spi/utest/SConscript @@ -3,10 +3,11 @@ from building import * cwd = GetCurrentDir() src = [] -CPPPATH = [cwd, cwd + '/..', cwd + '/../sfud/inc'] +CPPPATH = [cwd, cwd + '/..', cwd + '/../../include', cwd + '/../sfud/inc', + cwd + '/../../../utilities/utest'] if GetDepend(['RT_UTEST_SPI_LOOPBACK']): - src += ['spi_loopback_tc.c'] + src += ['spi_loopback_tc.c', 'spi_virt_bus.c'] if GetDepend(['RT_UTEST_SPI_FLASH']): src += ['spi_flash_tc.c'] diff --git a/components/drivers/spi/utest/spi_virt_bus.c b/components/drivers/spi/utest/spi_virt_bus.c new file mode 100644 index 000000000000..82097f5c642d --- /dev/null +++ b/components/drivers/spi/utest/spi_virt_bus.c @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-11-02 add SPI virtual loopback bus for utest + * + * A pure-software SPI bus that feeds MOSI data back to MISO. It lets the + * SPI loopback utest run on BSPs (such as qemu-vexpress-a9) which have no + * real SPI controller wired to external pins. + */ + +#include +#include +#include +#include + +#ifdef RT_UTEST_SPI_LOOPBACK + +#ifndef RT_UTEST_SPI_LOOPBACK_BUS_NAME +#define RT_UTEST_SPI_LOOPBACK_BUS_NAME "spi2" +#endif + +#define SPI_VIRT_BUS_NAME RT_UTEST_SPI_LOOPBACK_BUS_NAME +#define SPI_VIRT_BUS_MAX_HZ 10000000 +#define SPI_VIRT_BUS_DEFAULT_HZ 1000000 + +static struct rt_spi_bus spi_virt_bus; + +static rt_err_t spi_virt_configure(struct rt_spi_device *device, struct rt_spi_configuration *configuration) +{ + if (configuration->max_hz == 0) + { + configuration->max_hz = SPI_VIRT_BUS_DEFAULT_HZ; + } + + if (configuration->max_hz > SPI_VIRT_BUS_MAX_HZ) + { + configuration->max_hz = SPI_VIRT_BUS_MAX_HZ; + } + + device->config = *configuration; + device->config.usage_freq = configuration->max_hz; + + return RT_EOK; +} + +static rt_ssize_t spi_virt_xfer(struct rt_spi_device *device, struct rt_spi_message *message) +{ + rt_size_t i; + + if (message == RT_NULL) + { + return 0; + } + + if (message->send_buf != RT_NULL && message->recv_buf != RT_NULL) + { + rt_memcpy(message->recv_buf, message->send_buf, message->length); + } + else if (message->send_buf != RT_NULL) + { + /* TX-only: drop the data, nothing to verify. */ + } + else if (message->recv_buf != RT_NULL) + { + /* RX-only: fill with a deterministic pattern. */ + rt_uint8_t *rx = (rt_uint8_t *)message->recv_buf; + + for (i = 0; i < message->length; i++) + { + rx[i] = (rt_uint8_t)(0xA5 + i); + } + } + + return (rt_ssize_t)message->length; +} + +static const struct rt_spi_ops spi_virt_ops = { + .configure = spi_virt_configure, + .xfer = spi_virt_xfer, +}; + +static int spi_virt_bus_init(void) +{ + return rt_spi_bus_register(&spi_virt_bus, SPI_VIRT_BUS_NAME, &spi_virt_ops); +} +INIT_BOARD_EXPORT(spi_virt_bus_init); + +#endif /* RT_UTEST_SPI_LOOPBACK */