From 9b3d29fa58e3c6a628cc5e4140d6d7af84376763 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Desbiens?= Date: Tue, 13 Oct 2026 14:40:33 -0400 Subject: [PATCH] Enabled FVP execution in the ZoneX workflow Fixes #6 zx_fvp only ran the FVP when a repository variable pointed it at the model, and none was set, so every run built the images and executed nothing. The workflow also expected an unpacked model, but Arm ships the FVP as an installer script. The workflow now downloads the 11.32_19 archive from Arm's public permalink, checks it against a pinned SHA-256, and runs Arm's installer with its EULA accepted. The cache is keyed on the checksum. A missing model now fails the job instead of skipping execution, and CTest fails if it finds no tests. CONTRIBUTING.md no longer describes the repository variable. The workflow's install, locate, and build-and-run steps, run locally as written against the pinned ThreadX commit, download and verify the archive, install 11.32+19, build 27 images and pass 24/24 on the FVP. check_terminology.sh and check_references.sh pass. Assisted-by: Claude Code (Opus 5.5) --- .github/workflows/zx_fvp.yml | 99 ++++++++++++------------------------ CONTRIBUTING.md | 2 +- 2 files changed, 33 insertions(+), 68 deletions(-) diff --git a/.github/workflows/zx_fvp.yml b/.github/workflows/zx_fvp.yml index e27e89c..4517417 100644 --- a/.github/workflows/zx_fvp.yml +++ b/.github/workflows/zx_fvp.yml @@ -129,26 +129,18 @@ jobs: # Matches gcc_check.yml so the two share one toolchain cache entry. GCC_VERSION: 14.3.rel1 - # Where the Armv8-R AEM FVP comes from. + # The Armv8-R AEM FVP, from Arm's unauthenticated download permalink. + # Only the _Linux_x86.tar.gz name resolves; the _Linux64.tgz name of + # older releases returns 404. The archive holds Arm's installer, which + # refuses to run until its EULA is accepted, so the install step accepts + # it. The model is fetched from Arm on every cache miss and never + # re-hosted. # - # Arm distributes it free of charge but behind a click-through licence, - # with no stable unauthenticated download URL that can be hard-coded -- - # the permalink forms under developer.arm.com/-/cdn-downloads all 404 for - # this model. So the location is a repository variable rather than a - # literal, and a maintainer sets it once to somewhere the runner can - # reach: an Eclipse-hosted mirror, a release asset, or Arm's own URL if a - # durable one ever appears. - # - # Set FVP_AEMV8R_URL to a .tgz/.tar.gz of the model, and optionally - # FVP_AEMV8R_SHA256 to its checksum. With the checksum set the download - # is verified; without it the run says so rather than pretending. - # - # When the variable is unset the build lanes still run and still gate the - # pull request -- only execution is skipped, loudly, in the log and in the - # job summary. A silent green here would recreate the exact hole this - # workflow exists to close. - FVP_URL: ${{ vars.FVP_AEMV8R_URL }} - FVP_SHA256: ${{ vars.FVP_AEMV8R_SHA256 }} + # Pinned with its checksum so that a change of model is a reviewed diff. + # Keep the version in step with the find_program hint in + # examples/fvp_baser_aemv8r/CMakeLists.txt. + FVP_URL: https://developer.arm.com/-/cdn-downloads/permalink/FVPs-Architecture/FM-11.32/FVP_Base_AEMv8R_11.32_19_Linux_x86.tar.gz + FVP_SHA256: e6d046946529db86129ee6d8e7a2d5c3667d6c4c98365ad815586db8bca06613 steps: - name: Check out the repository @@ -205,36 +197,32 @@ jobs: sudo apt-get install -y --no-install-recommends ninja-build fi - # Keyed on the URL's own hash rather than a version string, so pointing the - # variable at a different model fetches it instead of serving the old one - # from cache. A raw URL may contain characters a cache key cannot. + # Keyed on the archive's checksum, which changes exactly when the model + # does. - name: Cache the Armv8-R AEM FVP id: cache-fvp - if: env.FVP_URL != '' uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: fvp - key: fvp-aemv8r-${{ hashFiles('.github/workflows/zx_fvp.yml') }}-${{ env.FVP_SHA256 || 'nosum' }} + key: fvp-aemv8r-${{ env.FVP_SHA256 }} - name: Install the Armv8-R AEM FVP - if: env.FVP_URL != '' && steps.cache-fvp.outputs.cache-hit != 'true' + if: steps.cache-fvp.outputs.cache-hit != 'true' run: | set -eu - mkdir -p fvp && cd fvp - curl -fsSL -o fvp.tgz "$FVP_URL" - if [ -n "${FVP_SHA256:-}" ]; then - echo "$FVP_SHA256 fvp.tgz" | sha256sum -c - - else - echo "::warning::FVP_AEMV8R_SHA256 is not set; the download was not verified." - fi - tar xf fvp.tgz - rm -f fvp.tgz + mkdir -p fvp-download + curl -fsSL --retry 3 -o fvp-download/fvp.tar.gz "$FVP_URL" + echo "$FVP_SHA256 fvp-download/fvp.tar.gz" | sha256sum -c - + tar xzf fvp-download/fvp.tar.gz -C fvp-download + sh fvp-download/FVP_Base_AEMv8R_*_Linux_x86.sh \ + --i-agree-to-the-contained-eula --no-interactive \ + -d "$GITHUB_WORKSPACE/fvp" + rm -rf fvp-download # The model links against libatomic. Everything else it needs is already - # on the runner image, and its own libscxframework/libsystemc ship inside - # the tarball. Guarded for the same reason the ninja step is. + # on the runner image, and its own libscxframework/libsystemc ship with + # the model. Guarded for the same reason the ninja step is. - name: Ensure the FVP's runtime dependencies are present - if: env.FVP_URL != '' run: | set -eu if ldconfig -p | grep -q 'libatomic\.so\.1'; then @@ -252,33 +240,19 @@ jobs: # ZX_FVP_BASER_AEMV8R, which means -DZX_FVP_BASER_AEMV8R= overrides # it with no change to the CMake files. # - # "present", not a bare "did the variable exist", so that a URL which - # downloads something that is not the model fails here rather than silently - # skipping every test later. + # A missing binary fails here, rather than leaving CMake without a model + # and CTest with no tests to run. - name: Locate the FVP id: fvp run: | set -eu - if [ -z "${FVP_URL:-}" ]; then - echo "present=false" >>"$GITHUB_OUTPUT" - echo "::warning::FVP_AEMV8R_URL is not set for this repository, so the ZoneX images were built but NOT executed. Set the repository variable to enable the execution lanes." - { - echo "### ZoneX FVP execution: SKIPPED" - echo "" - echo "\`FVP_AEMV8R_URL\` is not set, so this run built the images but executed none of them." - echo "Set the repository variable (Settings -> Secrets and variables -> Actions -> Variables)" - echo "to a download URL for the Armv8-R AEM FVP to turn the execution lanes on." - } >>"$GITHUB_STEP_SUMMARY" - exit 0 - fi binary="$(find fvp -type f -name FVP_BaseR_AEMv8R -perm -u+x | head -1)" if [ -z "$binary" ]; then - echo "FVP_AEMV8R_URL was set, but no FVP_BaseR_AEMv8R binary was found in the archive." >&2 + echo "::error::The FVP install produced no FVP_BaseR_AEMv8R binary." find fvp -maxdepth 3 -type d | sed 's/^/ /' >&2 exit 1 fi binary="$(cd "$(dirname "$binary")" && pwd)/$(basename "$binary")" - echo "present=true" >>"$GITHUB_OUTPUT" echo "binary=$binary" >>"$GITHUB_OUTPUT" "$binary" --version @@ -288,21 +262,15 @@ jobs: export PATH="$GITHUB_WORKSPACE/toolchain/arm-gnu-toolchain-${GCC_VERSION}-x86_64-arm-none-eabi/bin:$PATH" arm-none-eabi-gcc --version | head -1 - fvp_present='${{ steps.fvp.outputs.present }}' fvp_binary='${{ steps.fvp.outputs.binary }}' - fvp_arg="" - if [ "$fvp_present" = "true" ]; then - fvp_arg="-DZX_FVP_BASER_AEMV8R=$fvp_binary" - fi - cmake -S . -B build/fvp -G Ninja \ -DCMAKE_TOOLCHAIN_FILE=cmake/cortex_r52.cmake \ -DCMAKE_COMPILE_WARNING_AS_ERROR=ON \ -DZX_BUILD_FVP_EXAMPLE=ON \ -DZX_BUILD_S32Z280_EXAMPLE=OFF \ -DZX_THREADX_SOURCE_DIR="$GITHUB_WORKSPACE/threadx" \ - $fvp_arg + -DZX_FVP_BASER_AEMV8R="$fvp_binary" cmake --build build/fvp @@ -380,9 +348,6 @@ jobs: # shellcheck disable=SC2086 cmake --build build/fvp --target $images - if [ "$fvp_present" != "true" ]; then - echo "Built, not executed (no FVP configured)." - exit 0 - fi - - ctest --test-dir build/fvp --output-on-failure + # --no-tests=error, because ctest reports success when it finds + # nothing to run. + ctest --test-dir build/fvp --output-on-failure --no-tests=error diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index edffb60..d0e47fa 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -143,7 +143,7 @@ Five GitHub Actions workflows. **Every one triggers on `pull_request` against `d | `clang_check.yml` | The same sources with Arm Toolchain for Embedded. GNU `as` accepts non-canonical assembly forms that LLVM's assembler rejects, and ZoneX is going to be substantially assembly. | | `zx_fvp.yml` | Builds the Cortex-R52 images and **executes** them on the Armv8-R AEM FVP, judging each by its self-reported result. There is no static check for "the partition still runs". | -Arm distributes the Armv8-R AEM FVP free of charge but behind a click-through licence with no stable unauthenticated URL, so `zx_fvp.yml` takes the download location from the `FVP_AEMV8R_URL` repository variable (with an optional `FVP_AEMV8R_SHA256`). 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. +`zx_fvp.yml` downloads the Armv8-R AEM FVP from Arm's public permalink, verifies it against a SHA-256 pinned in the workflow, and installs it with Arm's installer, so every run executes the images. A missing model fails the job. Toolchain versions are pinned explicitly, so a bump is a reviewable commit. Third-party actions are pinned to a commit SHA rather than a tag, and `.github/dependabot.yml` keeps those pins moving — a SHA pin without Dependabot freezes CI on whatever was current the day it was written.