Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/utest/configs/components/spi.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# dependencies
CONFIG_RT_CONSOLEBUF_SIZE=1024
CONFIG_RT_USING_CI_ACTION=y
CONFIG_RT_USING_UTEST=y
CONFIG_RT_USING_UTESTCASES=y

CONFIG_RT_UTEST_SPI_LOOPBACK=y
CONFIG_RT_UTEST_SPI_LOOPBACK_BUS_NAME="spi_virt"
CONFIG_RT_UTEST_SPI_VIRT_BUS=y
CONFIG_RT_UTEST_SPI_VIRT_BUS_NAME="spi_virt"
2 changes: 2 additions & 0 deletions .github/workflows/utest_auto_run.yml
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ jobs:
config_file: "components/dfs.cfg"
- platform: { UTEST: "A9", RTT_BSP: "bsp/qemu-vexpress-a9", QEMU_ARCH: "arm", QEMU_MACHINE: "vexpress-a9", SD_FILE: "sd.bin", KERNEL: "standard", "SMP_RUN":"" }
config_file: "components/libc.cfg"
- platform: { UTEST: "A9", RTT_BSP: "bsp/qemu-vexpress-a9", QEMU_ARCH: "arm", QEMU_MACHINE: "vexpress-a9", SD_FILE: "sd.bin", KERNEL: "standard", "SMP_RUN":"" }
config_file: "components/spi.cfg"

env:
TEST_QEMU_ARCH: ${{ matrix.platform.QEMU_ARCH }}
Expand Down
8 changes: 8 additions & 0 deletions bsp/qemu-vexpress-a9/.ci/attachconfig/ci.attachconfig.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ peripheral.LVGL:
- CONFIG_RT_USING_POSIX_SOCKET=y
- ONFIG_RT_USING_LWIP=y
- CONFIG_BSP_USING_LVGL=y
peripheral.SPI_VIRT_LOOPBACK:
kconfig:
- CONFIG_RT_USING_UTEST=y
- CONFIG_RT_USING_UTESTCASES=y
- CONFIG_RT_UTEST_SPI_LOOPBACK=y
- CONFIG_RT_UTEST_SPI_LOOPBACK_BUS_NAME="spi_virt"
- CONFIG_RT_UTEST_SPI_VIRT_BUS=y
- CONFIG_RT_UTEST_SPI_VIRT_BUS_NAME="spi_virt"

# ------ online-packages CI ------
online-packages.tools.coremark:
Expand Down
29 changes: 29 additions & 0 deletions components/drivers/spi/utest/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ if RT_UTEST_SPI_LOOPBACK
config RT_UTEST_SPI_LOOPBACK_BUS_NAME
string "SPI bus name for loopback test"
default "spi2"
help
The real SPI bus the loopback test attaches to. On boards with a
wired MOSI/MISO loopback this must be the hardware controller name.
BSPs without a real SPI controller can instead enable
RT_UTEST_SPI_VIRT_BUS and point this name at the virtual bus.

config RT_UTEST_SPI_LOOPBACK_DEV_NAME
string "Temporary SPI device name for loopback test"
Expand Down Expand Up @@ -48,6 +53,30 @@ config RT_UTEST_SPI_LOOPBACK_TIMEOUT

endif

config RT_UTEST_SPI_VIRT_BUS
bool "SPI virtual loopback bus (software)"
select RT_USING_SPI
default n
help
Compile and register a pure software SPI bus that feeds MOSI data
back to MISO. It is meant for BSPs without a real SPI controller,
such as QEMU targets, or for CI runs that have no wired loopback.
Keep it disabled on boards with a hardware SPI controller: enabling
it would add a second bus next to the real one.

if RT_UTEST_SPI_VIRT_BUS

config RT_UTEST_SPI_VIRT_BUS_NAME
string "SPI virtual bus device name"
default "spi_virt"
help
Name of the software loopback bus registered by spi_virt_bus.c.
Use a dedicated name that does not collide with any real SPI
controller of the board, and set RT_UTEST_SPI_LOOPBACK_BUS_NAME
to the same value so the test attaches to it.

endif

config RT_UTEST_SPI_FLASH
bool "SPI Flash SFUD Read/Write Test"
select RT_USING_SPI
Expand Down
6 changes: 5 additions & 1 deletion components/drivers/spi/utest/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ 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']

if GetDepend(['RT_UTEST_SPI_VIRT_BUS']):
src += ['spi_virt_bus.c']

if GetDepend(['RT_UTEST_SPI_FLASH']):
src += ['spi_flash_tc.c']

Expand Down
92 changes: 92 additions & 0 deletions components/drivers/spi/utest/spi_virt_bus.c
Original file line number Diff line number Diff line change
@@ -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 <rthw.h>
#include <rtthread.h>
#include <rtdevice.h>
#include <drivers/dev_spi.h>

#ifdef RT_UTEST_SPI_VIRT_BUS

#ifndef RT_UTEST_SPI_VIRT_BUS_NAME
#define RT_UTEST_SPI_VIRT_BUS_NAME "spi_virt"
#endif

#define SPI_VIRT_BUS_NAME RT_UTEST_SPI_VIRT_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_VIRT_BUS */
Loading