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
11 changes: 11 additions & 0 deletions examples/tests/spi_loopback/Makefile
Original file line number Diff line number Diff line change
@@ -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
14 changes: 14 additions & 0 deletions examples/tests/spi_loopback/README.md
Original file line number Diff line number Diff line change
@@ -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: <reason>` so the result can be checked from
a console transcript.
82 changes: 82 additions & 0 deletions examples/tests/spi_loopback/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#include <stdio.h>
#include <string.h>

#include <libtock/peripherals/spi_controller.h>
#include <libtock/tock.h>

#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;
}

Comment thread
ppannuto marked this conversation as resolved.
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;
}
Loading