Skip to content

Added a workflow that runs the Cortex-R52 images on the Armv8-R AEM FVP - #688

Merged
fdesbiens merged 3 commits into
eclipse-threadx:devfrom
fdesbiens:feature/r52-fvp-ci
Sep 9, 2026
Merged

Added a workflow that runs the Cortex-R52 images on the Armv8-R AEM FVP#688
fdesbiens merged 3 commits into
eclipse-threadx:devfrom
fdesbiens:feature/r52-fvp-ci

Conversation

@fdesbiens

Copy link
Copy Markdown
Contributor

Nothing in CI executes a single instruction of any ThreadX port.

gcc_check compiles and links — its own header says it "executes nothing" — and its
CMake stage configures exactly one Cortex-R52 combination, the base FVP example with
-DTX_R52_ENABLE_MPU=ON. So a change that assembles cleanly, links cleanly and then
hangs on the first context switch passes every required check today.

This adds a workflow that builds both supported R52 configurations and runs them on the
Armv8-R AEM FVP
, asserting each image's self-reported result. A missing result line is a
failure, never a pass, so a silent hang cannot masquerade as green.

The gap is wider than "nothing is executed"

gcc_check's CMake stage builds five images. The feature configuration builds eight:

Configuration Images New coverage
default 5 executed rather than only linked
feature — TX_R52_ENABLE_VFP + TX_R52_FLOAT_ABI=hard, TX_R52_ENABLE_FIQ, TX_R52_ENABLE_IRQ_NESTING, TX_R52_ENABLE_FIQ_NESTING 8 demo_m5, demo_fiq and demo_nesting are built nowhere in CI today

Those four options gate whole assembly blocks — the VMRS/VSTMDB/VLDMIA sequences, the
FIQ context paths, the nesting start/end pairs — that the default configuration never
assembles, let alone runs.

Where the model comes from

Arm distributes the Armv8-R AEM FVP free of charge but behind a click-through licence, and
there is no stable unauthenticated download URL to hard-code: the
developer.arm.com/-/cdn-downloads permalink forms all return 404 for this model.

So the location is a repository variable rather than a literal:

  • FVP_AEMV8R_URL — a .tgz of the model. Required for the execution lanes.
  • FVP_AEMV8R_SHA256 — optional. Set, the download is verified; unset, the run says so
    rather than pretending it verified anything.

When FVP_AEMV8R_URL is unset the build lanes still run and still gate the pull
request
— only execution is skipped, and it says so with a ::warning:: and a job summary
entry. A silent green there would recreate the exact hole this workflow exists to close, and
contributor pull requests from forks keep working either way.

Details worth reviewing

The image list is read from the generated ninja graph, not kept in the workflow. The
images are EXCLUDE_FROM_ALL, so a bare build reports "no work to do" and would compare
nothing. Reading the graph means a target added to CMakeLists.txt cannot escape the check
because nobody remembered to list it here. Same technique, same reasoning, as
scripts/check_gcc.sh's CMake stage.

One job, not one per configuration, following the note in gcc_check.yml about folding
its two toolchains together: the checks list stays short and a cross-configuration
regression appears in one log rather than two.

The toolchain cache path and key match gcc_check.yml's AArch32 entry exactly, so the
two workflows share one cache entry instead of each holding its own copy of the same
archive. Change them together or the sharing silently stops.

find_program is overridden rather than patched. The example CMakeLists.txt locates
the model with HINTS $ENV{HOME}/FVP_Base_AEMv8R_11.32_19/bin, which is right on a
developer's machine and meaningless on a runner, so the resolved path is passed as
-DFVP_BASER_AEMV8R=<path>. No CMake file changes.

Validation

All three paths were exercised locally against the real model, GNU Arm 14.3.1:

Path Result
model present default 5/5, feature 8/8, step exit 0
FVP_AEMV8R_URL unset both configurations built, execution skipped, exit 0, warning + summary emitted
a test fails (stub model) ::error:: per configuration, both still reported rather than stopping at the first, step exit 1

Not covered

The module manager port and the S32Z280 targets are not on dev yet — they are #639. Their
lanes belong in this workflow when they land, not in a second one; the matrix already has
the shape for them. The S32Z280 targets will be build-only in CI regardless, since a hosted
runner has no silicon.

Nothing in CI executed a single instruction of any ThreadX port. gcc_check
compiles and links -- its own header says it "executes nothing" -- and its
CMake stage covers one R52 configuration, the base FVP example. A change that
assembles cleanly, links cleanly and then hangs on the first context switch
passed every required check.

This workflow builds both supported configurations and runs them on the
Armv8-R AEM FVP, asserting each image's self-reported result. The feature
configuration matters as much as the default one: VFP with the hard float ABI,
FIQ, IRQ nesting and FIQ nesting gate whole assembly blocks that the default
configuration never assembles, and it builds eight images where the default
builds five.

Arm distributes the model free of charge but behind a click-through licence
with no stable unauthenticated URL -- the developer.arm.com permalink forms
all 404 for it -- so the download location is a repository variable rather
than a literal. When it is unset the build lanes still run and still gate the
pull request; only execution is skipped, and it says so in the log and in the
job summary rather than passing quietly.

The image list is read from the generated ninja graph rather than kept in the
workflow. The images are EXCLUDE_FROM_ALL, so a bare build reports "no work to
do", and reading the graph means a target added to CMakeLists.txt cannot
escape the check by nobody remembering to list it here.

The module manager port and the S32Z280 targets are not on dev yet. Their
lanes belong in this workflow when they land, not in a second one.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
@fdesbiens fdesbiens changed the title Ran the Cortex-R52 images instead of only linking them Added a workflow that runs the Cortex-R52 images on the Armv8-R AEM FVP Sep 1, 2026
r and others added 2 commits September 9, 2026 12:05
Two corrections to the new workflow.

The FVP cache key named the URL and hashed the workflow file. hashFiles()
reads files, so it cannot hash a repository variable, and pointing it at
this workflow keyed the cache on the file's own contents instead. That is
wrong in both directions: repointing FVP_AEMV8R_URL at a different model
kept serving the old one from cache, which is exactly what the comment
claimed the key prevented, and editing anything in this file forced a
needless refetch of a large archive. A small step hashes the URL and the
key uses that, so it now tracks what it names.

ctest reported success when it found nothing to run. Its default is to
pass an empty suite, and the example registers its tests inside both
if(FVP found) and if(Python3_FOUND). Either one going unmet on a runner
would have left this stage green having executed no image at all, which
is the hole the workflow exists to close. --no-tests=error makes an empty
suite a failure.

Verified with the pinned toolchain, Arm GNU 14.3.rel1: both configurations
configure, enumerate and build, and the image lists are the ones claimed.

  default: 5 images  boot_check demo_m2 demo_m3 demo_mpu demo_threadx
  feature: 8 images  the above plus demo_fiq demo_m5 demo_nesting

Also checked that the target enumeration survives the real ninja graph:
the generated graph offers twenty .elf paths, and the filter reduces them
to those eight, dropping the cmake_object_order_depends_target aliases and
the CMakeFiles copies.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
@fdesbiens

Copy link
Copy Markdown
Contributor Author

Merged dev and pushed two corrections. Verified with the pinned toolchain, Arm GNU 14.3.rel1.

The FVP cache key named the URL and hashed this file

# The key is the URL's own hash ...
key: fvp-aemv8r-${{ hashFiles('.github/workflows/r52_fvp.yml') }}-...

hashFiles() reads files, so it cannot hash a repository variable; pointing it here keyed the cache on the workflow's own contents. That is wrong in both directions — repointing FVP_AEMV8R_URL at a different model kept serving the old one from cache, which is precisely what the comment said the key prevented, and editing anything in this file forced a needless refetch of a large archive.

A small step now hashes the URL and the key uses that, so it tracks what it names.

ctest reported success when it found nothing to run

Its default is to pass an empty suite — confirmed on CMake 4.4.3, No tests were found!!! and exit 0; with --no-tests=error, exit 8.

That matters here because the example registers its tests inside two conditionals, if(FVP found) and if(Python3_FOUND). Either going unmet on a runner would leave this stage green having executed no image at all — the hole this workflow exists to close, one flag away. Added --no-tests=error.

Verified, no change needed

  • The target enumeration survives the real ninja graph. Configured the example and ran the pipeline: the graph offers twenty .elf paths and the filter reduces them to the right ones, dropping the cmake_object_order_depends_target_* aliases and the CMakeFiles/ copies.
  • Both configurations build, with the image counts claimed.
    default: 5  boot_check demo_m2 demo_m3 demo_mpu demo_threadx
    feature: 8  the above plus demo_fiq demo_m5 demo_nesting
    
    demo_fiq, demo_m5 and demo_nesting are indeed built nowhere in CI today.
  • The per-image verdict is sound. run_fvp_test.py uses a 180 s timeout, keys on explicit ALL CHECKS PASSED / RESULT: FAILED marks, and treats a missing result line as failure. The "a hang cannot pass" claim holds.
  • The toolchain cache genuinely shares with gcc_check.yml — same path, same key, same GCC_VERSION.
  • Actions pinned to SHAs; pull_request rather than pull_request_target; apt calls guarded.

One thing to line up with the merge

FVP_AEMV8R_URL is not currently set on the repository, so merging this gives the build lanes — including the eight-image feature configuration — but no execution. Every run will emit the ::warning:: and a SKIPPED job summary, which is the designed behaviour and still closes real coverage, but the headline claim only becomes true once the variable points somewhere a runner can reach.

The runner work is tracked publicly at https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/work_items/7864.

On ordering with #639

No need to wait for it. This builds ports/cortex_r52, already on dev; the ports_module/cortex_r52/** path entry simply never matches until that port exists. The dependency runs the other way and favours merging this first: #639 touches ports/cortex_r52/gnu including the FVP example, so with this in place the largest R52 change in the queue arrives under execution gating rather than compile-and-link alone.

@fdesbiens
fdesbiens merged commit 16297d7 into eclipse-threadx:dev Sep 9, 2026
9 checks passed
@fdesbiens
fdesbiens deleted the feature/r52-fvp-ci branch September 9, 2026 18:39
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.

1 participant