blob: 9b4072bb8c3f538895eef2a431f37fe3a71aec31 [file] [view]
# 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)
| Test | QEMU | Physical board |
|------|------|----------------|
| `interrupts/kernel:interrupts_test` | PASSED | TIMEOUT see note below |
| `interrupts/user:interrupts_test` | PASSED | PASSED |
| `ipc/user:ipc_test` | FAILED (hangs at `object_set_peer_user_signal`) | PASSED |
| `threads/kernel:threads_test` | PASSED | PASSED |
| `unittest_runner:unittest_runner` | PASSED | SKIPPED (qemu_only) |
| `usart:usart_test` | PASSED | SKIPPED (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.