tree: e5dfea5db215a4624cc6ad97d46336972beb69ca
  1. interrupts/
  2. ipc/
  3. peripherals/
  4. stress/
  5. threads/
  6. unittest_runner/
  7. usart/
  8. BUILD.bazel
  9. README.md
target/ast10x0/tests/README.md

AST10x0 Test Infrastructure

Overview

Tests for the AST10x0 target are firmware images that run identically under QEMU or on a physical board. Pass/fail is signalled by writing a sentinel string to UART:

TEST_RESULT:PASS\n
TEST_RESULT:FAIL\n

The same system_image_test target is used for both execution environments — no separate hardware-only test targets exist.

Running Tests

QEMU (no hardware required)

bazel test --config=virt_ast10x0 //target/ast10x0/tests/...

Physical AST1060 EVB via Raspberry Pi SSH fixture

AST1060_EVB_PI_HOST=<pi-hostname> bazel test --config=k_ast1060_evb //target/ast10x0/tests/...

or inline without modifying the shell environment:

bazel test --config=k_ast1060_evb --test_env=AST1060_EVB_PI_HOST=<pi-hostname> //target/ast10x0/tests/...

Key-based SSH auth is required (ssh-copy-id <user>@<pi-host>). The Pi runs pi_test_runner.py, which handles GPIO reset sequencing, firmware upload over the UART bootloader, and sentinel detection. UART output is streamed back to the host for detokenization and display. Tests no longer use cortex_m_semihosting.

Physical AST1060 EVB wired (not yet implemented)

A wired mode — where the host connects to the Pi fixture over a local serial port rather than SSH — is not yet implemented. The physical connection type between the host and the Pi has not been defined (e.g. Pi serial console over UART, USB serial gadget, or USB networking), so the host-side protocol cannot be specified. Omitting AST1060_EVB_PI_HOST should default to using a wired connection, but currently logs an unimplemented error.

Test Results (2026-05-12)

TestQEMUPhysical board
interrupts/kernel:interrupts_testPASSEDTIMEOUT — see note below
interrupts/user:interrupts_testPASSEDPASSED
ipc/user:ipc_testFAILED (hangs at object_set_peer_user_signal)PASSED
threads/kernel:threads_testPASSEDPASSED
unittest_runner:unittest_runnerPASSEDSKIPPED (qemu_only)
usart:usart_testPASSEDSKIPPED (qemu_only)
*/no_panics_test (×5)SKIPPED (host-only)SKIPPED

interrupts/kernel:interrupts_test — times out on physical board

The firmware produces no UART output after upload, indicating a crash before UART initialisation. The same binary passes in QEMU. The interrupts/user variant (which manages IRQ 42 through the kernel IPC abstraction rather than raw NVIC manipulation) passes on both.

The suspected cause is the interrupt_table entry in system.json5: the codegen for that entry installs an NVIC handler via early_init(), which runs before UART is initialised. If early_init() faults (e.g. invalid vector table layout, bad IRQ number on hardware), the firmware crashes with no UART output and no way to signal failure. This cannot be verified without a hardware debugger (GDB via OpenOCD or J-Link) attached to the board.

How Pass/Fail Signalling Works

Firmware writes the sentinel via console_backend_write_all, which calls Usart::write_all directly, bypassing pw_log and the tokenizer. This means the sentinel is always plain ASCII regardless of whether the rest of the log output is tokenized, and it can be detected without an ELF for detokenization.

QEMU

qemu_runner.py starts QEMU with a PTY for serial I/O and a named pipe for the raw byte stream. A sentinel watcher thread scans the raw stream; when a sentinel is found QEMU is killed and the runner exits 0 or 1. A 30-second watchdog kills QEMU if no sentinel arrives.

Physical board

pi_test_runner.py (running on the Raspberry Pi) sequences the GPIO reset lines to enter UART bootloader mode, uploads the firmware binary, then streams raw UART bytes to stdout while scanning for the sentinel. It exits 0 (PASS) or 1 (FAIL/timeout). test_runner.py on the host SCP‘s the script to the Pi, streams the output back for detokenization and display, and reports the Pi’s exit code to Bazel.

Because the Pi is a shared fixture, test_runner.py holds an atomic noclobber lock file at /tmp/ast1060_evb.lock on the Pi for the duration of each test, preventing multiple users from driving the board over SSH simultaneously. The lock is touched every 10 seconds by a background thread and considered stale after 60 seconds of inactivity (e.g. after a crash). If the lock cannot be acquired within 120 seconds the run is aborted.

Semihosting Migration

This infrastructure previously used ARM semihosting to signal pass/fail. On real hardware with no attached debugger, a semihosting trap causes a HardFault, so hardware testing was impossible. Replacing semihosting with UART sentinels removed that constraint and enabled the Pi SSH test fixture.

uart_upload_test Targets (removed)

The five *_uart_upload_test targets that previously existed in these BUILD files have been removed. They used an earlier harness (uart_upload_test.bzl) that predated the run_under approach. The system_image_test targets cover both QEMU and hardware execution; no separate hardware-only test rule is needed.