Skip to content

UART to replace semihosting#223

Merged
wmaroneAMD merged 3 commits into
upstream-taskfrom
semihosting-to-UART
May 13, 2026
Merged

UART to replace semihosting#223
wmaroneAMD merged 3 commits into
upstream-taskfrom
semihosting-to-UART

Conversation

@JesseMelon
Copy link
Copy Markdown
Contributor

@JesseMelon JesseMelon commented May 11, 2026

This file is present under 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)

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 FAILED - see note below
usart:usart_test PASSED PASSED
*/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. Root cause is likely in the
codegen-generated startup code for the interrupt_table entry in
system.json5; the system.json5 itself carries a TODO to verify addresses
against the AST10x0 hardware datasheet.

unittest_runner:unittest_runner ΓÇö fails on physical board

unittest_runner is a rust_test, not a system_image_test. The run_under
mechanism expects a system_image output (co-located .elf and .bin under
the same stem). A raw rust_test binary staged in a Bazel temp directory has
no .bin counterpart, so test_runner.py exits immediately with an error.
This test is only meaningful under QEMU, where qemu_runner.py can execute
the ELF directly.

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.
The uart_upload_test.bzl rule file is retained in target/ast10x0/harness/
for reference.

@linux-foundation-easycla
Copy link
Copy Markdown

linux-foundation-easycla Bot commented May 11, 2026

CLA Signed
The committers listed above are authorized under a signed CLA.

  • ✅ login: JesseMelon / name: JesseMelon (615eef0)

Copy link
Copy Markdown
Collaborator

@wmaroneAMD wmaroneAMD left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good, but keeping the stand-alone uart-test-exec.py would be good as it's handy for manual test and debug.

Comment thread target/ast10x0/harness/uart_test_exec.py
@JesseMelon JesseMelon force-pushed the semihosting-to-UART branch from 26caccc to 2c37f27 Compare May 12, 2026 22:25
@JesseMelon JesseMelon force-pushed the semihosting-to-UART branch from 2c37f27 to 615eef0 Compare May 12, 2026 22:27
Copy link
Copy Markdown
Contributor

@CourtneyDrant CourtneyDrant left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Satisfies original mission, which was to converge ast1060_evb and qemu targets into a single target. Tests execute on HW targets. Code is well documented and structured.

@wmaroneAMD wmaroneAMD merged commit 73d3095 into upstream-task May 13, 2026
1 check passed
@JesseMelon JesseMelon deleted the semihosting-to-UART branch May 13, 2026 16:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants