Step 54 of 56
The runtime: how it is built, ported and proven conformant
Three profiles generated from a forked bb-runtimes board, a porting checklist that is shorter than you would expect — and an ACATS sweep that grades the result one test per image.
Where the runtime comes from
Step 8 covered choosing a profile.
This is where they come from. The runtime is not vendored as a binary: it is
generated from a fork of AdaCore's bb-runtimes
carrying a new esp32s3 board, and built on demand by
gen_runtime.sh as the crate's Alire pre-build action.
ESP32S3_RTS_PROFILE=embedded # light-tasking | embedded | full
# -> crates/esp32s3_rts/embedded-esp32s3/ (adainclude + adalib)
Two things it needs, both supplied for you:
XTENSA_GNU_CONFIG, set by the xtensa_dynconfig
dependency, which selects the ESP32-S3 core configuration (little-endian, 64
address registers, FPU); and the cross toolchain plus gprbuild on
PATH. It is idempotent — it regenerates only when the output
is missing.
The crate is not published. You consume it via
an Alire pin, which is why every example's alire.toml
carries a [[pins]] entry pointing at
crates/esp32s3_rts, and why an out-of-tree project resolves it
through GPR_PROJECT_PATH instead
(step 10).
The trap that will bite you
Editing a runtime source does not rebuild
anything. The heavy compile-and-archive step runs only when the output
is missing, so changing a file under bb-runtimes/ or
full_overlay/ leaves the previous archive in place — the next
build links the old one and your change silently does nothing. The
book records that this has caught the authors more than once.
Delete the archives to force the rebuild:
rm crates/esp32s3_rts/<profile>-esp32s3/adalib/libgnat.a crates/esp32s3_rts/<profile>-esp32s3/adalib/libgnarl.a
Or remove the generated tree entirely, as step 7's tips suggest.
Three profiles, three system.ads
The profiles differ by restriction set, expressed as different
system-xi-xtensa-*.ads variants:
light-tasking adds No_Exception_Propagation and
No_Finalization; embedded lifts both (ZCX);
full is the unrestricted GNARL, built from an overlay plus
patches on top of the same board.
The register packages are separate again: ESP32S3_Registers.* is
generated from the chip's SVD by svd2ada, not hand-written — which is why
the drivers get typed record fields with
representation clauses instead of shift-and-mask.
Porting to another Xtensa SoC
The structure makes the checklist short:
- Add a board class under
bb-runtimes/xtensa/— target triple, clock, interrupt range, SMP flag. - Supply the five
__<board>bodies: parameters, board support, console, reset, interrupt names. - Write the two
system-xi-xtensa-*.adsvariants, or reuse these if the restriction set is the same. - Regenerate the register packages from that chip's SVD.
- Point
gen_runtime.shat the new board name.
What that list deliberately excludes is the boot path — the 2nd-stage loader, clock and cache bring-up, PSRAM (step 6). Those are chip-specific and are the larger job; the runtime port is the small half.
ACATS: conformance, not confidence
A hand-written runtime either implements Ada or approximates it, and the difference is not something a test suite of your own devising can settle. So the runtime is run against the official ACATS 4.2 suite on real hardware.
| Profile | Test list | PASS | Genuine failures |
|---|---|---|---|
light-tasking | jorvik_hw_runnable (846) | ~700 | 0 |
embedded | jorvik_hw_runnable (846) | 840+ | 0 |
full | full_applicable (1,518) | 1,286+ | 0 |
"Zero genuine failures" needs its qualifier stated, not buried:
every non-passing test is an interactive test needing a bench-generated
stimulus, a build-drop (a library unit the bare runtime omits), a correct
NOT-APPLICABLE, or a documented limitation. That is a different
claim from "everything passes", and the honest version is the useful one.
How the sweep works
The interesting engineering is the harness. Each test becomes one image containing exactly one test, built in parallel, then flashed and graded across a pool of boards. Two details make it work:
- The grade has to come off the chip. ACATS'
Reportpackage printsPASSED/FAILED, andACATS/target/report.adbroutes each grade line through the USB-Serial-JTAG console so the runner can read it. - The grader must not trust the test id printed in the output — the suffix letter breaks an exact-id match. Because each image contains exactly one test, the runner knows which test it flashed and grades on that instead.
The ACATS suite and its sweep harness
(acats_build.py, acats_run.py, the
ACATS/ tree) are not shipped in this distribution
— they live in the development repository. The book's ACATS chapter has the
full breakdown if you want the detail.