From d0d22343c468363a59a3e95778f12b89825d9d97 Mon Sep 17 00:00:00 2001 From: Pat Pannuto Date: Wed, 26 Aug 2026 10:15:53 -0700 Subject: [PATCH] Add SPI loopback test app There is no example that exercises the current libtock/peripherals/ spi_controller.h API end-to-end -- the existing wip/spi/* examples predate it and don't build against it. Add a small standalone loopback test: write a known pattern, read it back over the same transfer, and print SPI PASS/FAIL depending on whether they match. Only meaningful on a board with SPI in hardware loopback (e.g. Tock's QEMU qemu_arm_mps2_an385/an386 boards); on a board with a real, non-looped-back peripheral, a mismatch is expected. Co-Authored-By: Claude Sonnet 5 --- examples/tests/spi_loopback/Makefile | 11 ++++ examples/tests/spi_loopback/README.md | 14 +++++ examples/tests/spi_loopback/main.c | 82 +++++++++++++++++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 examples/tests/spi_loopback/Makefile create mode 100644 examples/tests/spi_loopback/README.md create mode 100644 examples/tests/spi_loopback/main.c diff --git a/examples/tests/spi_loopback/Makefile b/examples/tests/spi_loopback/Makefile new file mode 100644 index 000000000..54d6a7969 --- /dev/null +++ b/examples/tests/spi_loopback/Makefile @@ -0,0 +1,11 @@ +# Makefile for user application + +# Specify this directory relative to the current application. +TOCK_USERLAND_BASE_DIR = ../../.. + +# Which files to compile. +C_SRCS := $(wildcard *.c) + +# Include userland master makefile. Contains rules and flags for actually +# building the application. +include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk diff --git a/examples/tests/spi_loopback/README.md b/examples/tests/spi_loopback/README.md new file mode 100644 index 000000000..c46194b57 --- /dev/null +++ b/examples/tests/spi_loopback/README.md @@ -0,0 +1,14 @@ +SPI Loopback Test +================= + +Writes a known pattern out over the SPI controller syscall interface and +reads it back in the same transfer, then checks the two match. + +This only produces a meaningful result on a board where SPI is configured +in hardware loopback mode (so the read data is expected to equal the write +data), such as the QEMU `qemu_arm_mps2_an385`/`qemu_arm_mps2_an386` boards. +On a board wired to a real, non-looped-back SPI peripheral, a mismatch is +expected and does not indicate a bug. + +Prints `SPI PASS` or `SPI FAIL: ` so the result can be checked from +a console transcript. diff --git a/examples/tests/spi_loopback/main.c b/examples/tests/spi_loopback/main.c new file mode 100644 index 000000000..265b748e5 --- /dev/null +++ b/examples/tests/spi_loopback/main.c @@ -0,0 +1,82 @@ +#include +#include + +#include +#include + +#define BUF_SIZE 32 + +// Arbitrary, conservative choice. As of 2026-08-26 this has only been +// tested on the QEMU ARM MPS2 AN385/AN386 boards; untested on other +// boards/hardware, so lower it if a real target can't keep up. +#define SPI_RATE_HZ 400000 + +static uint8_t write_buf[BUF_SIZE]; +static uint8_t read_buf[BUF_SIZE]; +static bool done = false; + +static void spi_cb(returncode_t ret __attribute__((unused))) { + done = true; +} + +static void print_buf(const char* label, const uint8_t* buf, size_t len) { + printf("%s:", label); + for (size_t i = 0; i < len; i++) { + printf(" %02x", buf[i]); + } + printf("\n"); +} + +int main(void) { + if (!libtock_spi_controller_exists()) { + printf("SPI FAIL: driver not present\n"); + return 1; + } + + // `read_buf` is pre-filled with a pattern disjoint from `write_buf`'s + // (offset by BUF_SIZE) so a no-op read_write (e.g. an unimplemented + // syscall that leaves the buffer untouched) can't accidentally match + // and produce a false PASS. + for (unsigned i = 0; i < BUF_SIZE; i++) { + write_buf[i] = (uint8_t) i; + read_buf[i] = (uint8_t) (i + BUF_SIZE); + } + + returncode_t err; + + // As of 2026-08-26, `set_chip_select()` is unimplemented on every Tock + // board -- the spi_controller capsule always returns NOSUPPORT here + // until multiple chip selects are supported. Check for exactly that so + // this test fails loudly if that ever changes, rather than silently + // masking a real error. + err = libtock_spi_controller_set_chip_select(0); + if (err != RETURNCODE_ENOSUPPORT) { + printf("SPI FAIL: set_chip_select returned %d, expected ENOSUPPORT\n", err); + return 1; + } + + err = libtock_spi_controller_set_rate(SPI_RATE_HZ); + if (err != RETURNCODE_SUCCESS) { + printf("SPI FAIL: set_rate returned %d\n", err); + return 1; + } + + err = libtock_spi_controller_read_write(write_buf, read_buf, BUF_SIZE, spi_cb); + if (err != RETURNCODE_SUCCESS) { + printf("SPI FAIL: read_write returned %d\n", err); + return 1; + } + + printf("SPI transaction initialized. Waiting for callback.\n"); + yield_for(&done); + + if (memcmp(write_buf, read_buf, BUF_SIZE) == 0) { + printf("SPI PASS\n"); + } else { + printf("SPI FAIL: loopback mismatch\n"); + print_buf("write", write_buf, BUF_SIZE); + print_buf("read ", read_buf, BUF_SIZE); + } + + return 0; +}