UART to replace semihosting#223
Open
JesseMelon wants to merge 4 commits into
Open
Conversation
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
AST10x0 Test Infrastructure
Overview
Tests for the AST10x0 target are firmware images executed either under QEMU
(
--config=virt_ast10x0) or on physical hardware via UART upload(
*_uart_upload_testtargets). Both execution paths use the same firmwarebinary ΓÇö pass/fail is signalled by writing a sentinel string to UART rather
than by any emulator-specific mechanism.
How Pass/Fail Signalling Works
When a test completes, the firmware writes one of two sentinel strings directly
to UART via
console_backend_write_all:The QEMU test runner (
target/ast10x0/harness/qemu_runner.py) watches the rawserial stream and kills QEMU as soon as it sees either sentinel, then exits with
code 0 or 1 accordingly. On physical hardware the same sentinel appears on the
serial console, where it can be read by automated test fixture tooling.
Semihosting Migration
This infrastructure previously used ARM semihosting
(
cortex-m-semihosting::debug::exit) to signal pass/fail. Semihosting worksby triggering a
BKPT 0xABtrap that QEMU intercepts and translates into aprocess exit code. It has two hard constraints:
HardFault, so the firmware never produces a result.
is invisible to anything other than QEMU itself.
Replacing semihosting with a UART sentinel removes both constraints. The same
binary now runs identically under QEMU and on a physical board, which is a
prerequisite for the physical-device SSH test fixture.
Note: semihosting has no relationship to stepping through code in a debugger.
Line-by-line debugging uses GDB over JTAG/SWD, which is unaffected by this
change.
Changed Files
.bazelrcBefore:
After:
Switched
--run_underfrom Pigweed's upstream QEMU runner (which terminatesonly via semihosting exit) to our local runner, which terminates on UART
sentinel detection.
--semihostingis removed; QEMU no longer enables thesemihosting config.
target/ast10x0/harness/qemu_runner.py(new)Local QEMU runner that replaces
@pigweed//pw_kernel/tooling:qemu_runnerforthe
virt_ast10x0config. It is structurally identical to Pigweed's runner(same QEMU invocation, same detokenizer thread for display) with two additions:
file and scans for
TEST_RESULT:PASSorTEST_RESULT:FAIL. When found itkills QEMU and records the result.
runner from hanging indefinitely if firmware crashes before completing).
The process exit code (0 or 1) is driven by the sentinel, not by QEMU's exit
code. This is the core difference from the upstream runner.
The qemu-system-arm binary is located using
qemu.qemu_system_arm.RLOCATIONfrom the
qemu-system-arm-runfilesBazel target (canonical label@@pigweed++_repo_rules5+qemu). If this label breaks after a Pigweed upgrade,run
ls $(bazel info output_base)/external/ | grep qemuto find the newcanonical name.
target/ast10x0/harness/BUILD.bazelAdded a
py_binarytarget (qemu_runner_bin) and aplatform_datawrapper(
qemu_runner). Theplatform_datawrapper is required so that the Pythonbinary executes on the host platform even when the Bazel target platform is
ARM ΓÇö identical to how Pigweed exposes its own runner.
Dependencies:
@@pigweed++_repo_rules5+qemu//:qemu-system-arm-runfilesΓÇö provides theqemu.qemu_system_armPython importable used to locate the QEMU binary viaBazel runfiles.
@pigweed//pw_tokenizer/py:detokenizeΓÇö same detokenizer used by theupstream runner for display of
pw_logoutput.@rules_python//python/runfilesΓÇö standard Bazel runfiles library.tests/unittest_runner/target.rsBefore: imported and called
cortex_m_semihosting::debug::exit.After: calls
console_backend::console_backend_write_allwith theappropriate sentinel byte string, then enters
loop {}.The
console_backendcrate was already linked into this binary (previouslyimported as
console_backend as _for side effects). The import is nowexplicit so the public
console_backend_write_allfunction is accessible.tests/interrupts/kernel/target.rsSame pattern as
unittest_runner. The test result comes fromtest_interrupts::main::<Arch>(TEST_IRQ)returningOk(())orErr(_),which maps directly to the pass/fail sentinel.
tests/interrupts/user/target.rsUses the
shutdown(code: u32)hook rather than returning frommain. Thecodevalue (0 = pass, non-zero = fail) maps to the sentinel. Semihostingexit()is replaced withconsole_backend_write_all+loop {}.tests/ipc/user/target.rsSame
shutdown(code: u32)pattern asinterrupts/user. The existingpw_log::info!("Shutting down with code {}", code)line is retained forvisibility in the detokenized log output before the sentinel is written.
tests/threads/kernel/target.rsSame pattern as
unittest_runnerΓÇö result fromthreads::main(...)drivessentinel selection directly in
main().tests/usart/target.rsSame
shutdown(code: u32)pattern asinterrupts/user.tests/*/BUILD.bazel(6 files)Removed
"@rust_crates//:cortex-m-semihosting"from thedepslist of eachrust_binary(orrust_test) target. No other dependency changes were neededbecause
console_backendwas already present in every target's dep graph.Affected files:
tests/unittest_runner/BUILD.bazeltests/interrupts/kernel/BUILD.bazeltests/interrupts/user/BUILD.bazeltests/ipc/user/BUILD.bazeltests/threads/kernel/BUILD.bazeltests/usart/BUILD.bazelTest Results
The result of
bazel test --config=virt_ast10x0 //target/ast10x0/...isunchanged from before the migration:
interrupts_test(kernel)interrupts_test(user)ipc_testthreads_testunittest_runnerusart_test*_uart_upload_test(×5)*_no_panics_test(×5)The
uart_upload_testfailures previously exited with code 250 (the uploadscript bailing out immediately). They now exit with code 1 after the 30-second
runner timeout, since our runner starts QEMU and waits for a sentinel that
never arrives. The semantic result ΓÇö no physical board, test fails ΓÇö is the
same.
Next Steps
The physical-device test fixture (
ast1060-test-fixture-automationbranch)reads the same
TEST_RESULT:PASS/TEST_RESULT:FAILsentinel from theboard's serial console over SSH, completing the full hardware test loop.