1 of 13

Hardware CI for Eclipse ThreadX on RISC-V

Boot ThreadX tests on real RISC-V boards, automatically, with the Cloud-V KernelCI/LAVA lab.

Akif Ejaz

Engineer @ 10xEngineers | Cloud-V developer | CM @ Eclipse ThreadX

Banana Pi F3

SpacemiT K1 — 8× RV64GC. The first ThreadX target board in the lab.

2 of 13

WHY NOW

ThreadX runs on RISC-V. CI does not see it.

2

RISC-V ports

risc-v32 and risc-v64; GNU, Clang, IAR

98

regression tests

build as bare-metal RISC-V programs

5

example targets

QEMU virt, CVA6, Core-V MCU, E906, BPI-F3

0

boards in CI

all RISC-V testing stops at emulation

What exists today on the working fork

  • Full ports with an S-mode build option (TX_RISCV_SMODE) for boards where OpenSBI owns M-mode.
  • A QEMU regression harness. Each test is one ELF file. ctest runs it and writes JUnit XML.
  • A RISC-V CI job exists in regression_test.yml. It is disabled for now.
  • GitHub Actions runs the main suite on the Linux port only. That is fast, but it is x86.

The gap

No ThreadX code runs on real RISC-V silicon in CI. Real interrupt timing, real UART, and real SoC behavior stay untested until a person tests them by hand.

2

3 of 13

WHO WE ARE

Cloud-V: free RISC-V hardware for developers

  • 10x Engineers builds and operates this community platform.
  • Cloud-V provides real RISC-V hardware through SSH, GitHub and GitLab CI runners, Jenkins, and KernelCI (LAVA).
  • Cloud-V is an official RISC-V Ecosystem Lab Partner.
  • More than 100 developers, organizations, and institutes use it.

Boards in the farm

VisionFive 1 & 2

Banana Pi F3

Milk-V Jupiter

Milk-V Pioneer

Milk-V Megrez

Scaleway EM-RV1

Users include

One board farm, four access paths: SSH, GitHub CI, GitLab CI, KernelCI.

3

4 of 13

THE SERVICE

The LAVA lab: boot-level testing on real boards

  • LAVA (Linaro Automated Validation Architecture) deploys images to boards, drives the boot over serial, and records results.
  • A test is a YAML job definition with three actions: deploy, boot, test. You submit it. The lab does the rest.
  • The server is kernelci-1.cloud-v.co. You get access with a user account and an API token.
  • Cloud-V built the lab for KernelCI. It already validates U-Boot, OpenSBI, and the Linux kernel on RISC-V hardware.

Available to ThreadX :

banana-pi-f3

riscv-virt-qemu

This flow runs in production today

1

Submit

lavacli -i cloudv jobs submit job.yaml

2

Schedule

The master queues the job on a healthy board.

3

Deploy

The worker downloads the images over HTTPS.

4

Boot

Power cycle, bootloader, kernel: each step is scripted.

5

Report

Live serial log and pass/fail results in the web UI.

4

5 of 13

INSIDE THE LAB

Every boot step is scripted. No manual steps.

LAVA master

kernelci-1.cloud-v.co

Scheduler, web UI, results database

Worker

An x86 host next to the boards. It runs the dispatcher.

Serial console

USB serial, shared through ser2net as telnet. The lab captures all boot output.

Power control

An Arduino with a relay PDU. Each job can switch the board on, off, or reset it.

File serving

TFTP and NFS servers on the worker. U-Boot downloads the test image from them.

BPI-F3

Health checks

A scheduled Linux boot proves each board healthy. An infrastructure fault never looks like a ThreadX fault.

Per-job bootloader

The BPI-F3 first-stage loader accepts U-Boot over UART on each reset. A job can also supply its own bootloader.

Full logs

The lab stores every action, command, and serial line with the job. You can debug a red run without board access.

5

6 of 13

THE OTHER HALF

ThreadX already has the hard parts

In the tree today (working fork)

  • risc-v32 and risc-v64 ports, with shared common drivers (UART, PLIC, trap).
  • The TX_RISCV_SMODE build option: the same kernel runs in S-mode under OpenSBI.
  • 98 regression tests build as one bare-metal ELF each. ctest runs them on QEMU and writes JUnit XML.
  • The CI job for all of this exists in regression_test.yml. ( but It is disabled for now.)

Proven by hand on the Banana Pi F3

  • The ThreadX demo boots on the K1 today from the U-Boot prompt.
  • S-mode payload at 0x0020_0000, SBI timer, PLIC and UART drivers in the example BSP.

The exact manual commands:

=> tftpboot ${loadaddr} kernel.elf

=> bootelf ${loadaddr}

The only missing piece: automation between GitHub Actions and the board. That is what we are proposing.

6

7 of 13

THE PROPOSAL

One automated loop from pull request to silicon

GITHUB (THREADX REPO)

CLOUD-V LAB

1

Merge or nightly trigger

push to master, cron, or manual

2

Build test ELFs

GNU toolchain, rv64 regression set

3

Publish artifacts

ELF files at a public HTTPS URL

4

Submit the LAVA job

lavacli with an API token

5

Board runs each ELF

power cycle, U-Boot, tftpboot, bootelf

6

Parse serial output

a monitor matches the pass and fail markers

7

Report back

the workflow polls, converts to JUnit, sets the check

green check

7

8 of 13

FITTING THE CI BUDGET

Right-sized tiers: fast PRs, deep nightlies

Every pull request

QEMU on the GitHub runner

Full RISC-V regression in emulation. Enable the job that already exists. No board time, no queue.

minutes

Every merge to master

Smoke set on the Banana Pi F3

About 5 ELF files, one per kernel object family. This catches hardware breakage on the day it lands.

10 to 25 min

Every night

Full suite on the board

All 98 tests, one boot each. The board queue serializes the runs. The lab retries each failure once.

2 to 8 h

  • Why not the full suite per PR? One boot cycle costs minutes on real hardware. 98 boots do not fit a PR gate.
  • Each boot becomes about 3 times faster when U-Boot stays persistent on the board. This lab-side change is planned.

9

9 of 13

HOW A TEST RUNS

Anatomy of one hardware test job

1. Deploy

The worker downloads the test ELF from the artifact URL and stages it for TFTP.

deploy:

to: tftp

kernel:

url: https://<artifacts>/

threadx_mutex_basic_test.elf

2. Boot

LAVA power-cycles the board and types the same commands a developer types.

boot:

method: u-boot

commands:

- tftpboot 0x00200000 {KERNEL}

- bootelf 0x00200000

3. Test

A monitor watches the serial console and turns marker lines into test results.

monitors:

- start: "ThreadX Validation"

end: "Testing Complete"

pattern: TX_TEST_RESULT:

(?P<test_case_id>\S+)

(?P<result>PASS|FAIL)

One small ThreadX change makes this work

On QEMU, ctest reads an exit code. A board has no exit code. It only has serial output. So the test BSP prints one line per run, for example TX_TEST_RESULT: threadx_mutex_basic_test PASS. LAVA turns that line into a test result.

8

10 of 13

SCOPE OF CHANGE

Small, contained changes on both sides

ThreadX repository

  • Enable the existing RISC-V QEMU job in regression_test.yml.
  • Add the TX_TEST_RESULT marker line to the test BSP, behind a define.
  • Add a BPI-F3 variant of the test BSP (S-mode entry, SBI timer, board UART).
  • Add .github/lava/ job templates and one hardware_test.yml workflow.
  • Add a submit-and-poll script (lavacli, about 100 lines).

Cloud-V lab

  • Create a threadx-ci account and API token. Store the token only in GitHub secrets.
  • Serve per-job files to U-Boot over TFTP on the worker.
  • Keep U-Boot persistent on the board. This makes each boot about 3 times faster.
  • Keep the existing Linux health checks. They already guard the infrastructure.
  • Later: a second board and more device types.

10

11 of 13

ROLLOUT

Six checkpoints, each one testable

0

M0: Access

Account, token, and artifact host are ready.

1

M1: QEMU loop

A workflow run submits a LAVA QEMU job. The check goes green.

2

M2: First silicon

One regression ELF boots on the BPI-F3 with no manual steps.

3

M3: Results land

JUnit from the lab appears in the workflow. A forced failure turns the check red.

4

M4: Steady state

Smoke on merge and full nightly run for one week. We record the failure rate.

5

M5: Upstream

The workflow and templates merge into eclipse-threadx/threadx. The lab adds boards.

This sequence removes risk early

  • M1 needs no board time. It proves tokens, templates, submission, and polling against the lab's QEMU device first.
  • The pilot runs on the working fork. Upstreaming (M5) starts only after one week of steady green runs.

11

12 of 13

GUARDRAILS

Safe by design

Fork PRs never touch hardware

Hardware jobs run on merge and nightly only. Fork PRs get no secrets and no board access. Code reaches silicon after review, not before.

Credentials stay contained

A dedicated threadx-ci lab account. Its token lives only in GitHub environment secrets. One click revokes it.

Infrastructure faults stay visible

Scheduled Linux health checks prove the board, power, serial, and network before any ThreadX job. A red ThreadX run points to ThreadX, not to cables.

Flaky runs are contained

The workflow resubmits a failed job once. Two identical failures fail the check. The QEMU suite uses the same repeat policy today.

What the community gains: boots on real silicon back every RISC-V claim. Every PR shows the result in its checks.

12

13 of 13

DISCUSSION

What I ask from this community

1

Feedback on the design

Focus areas: the test tiers and the marker-line approach.

2

Approval to pilot

M0 to M4 run on the working fork. Nothing changes upstream yet.

3

Review when it lands

Ports, harness, and workflow arrive as normal PRs after the pilot.

First three actions after a yes

  • Land the pending RISC-V work on the fork.
  • Create the threadx-ci lab account and secrets.
  • Submit the first QEMU job (milestone M1).

Pointers

Lab server

kernelci-1.cloud-v.co

Partner docs

10x-engineers.github.io/riscv-ci-partners

Lab internals

github.com/alitariq4589/lava-webserver-riscv

ThreadX work

github.com/akifejaz/threadx (dev)

Get access

cloud-v.co/contactus

Thank you. Questions are welcome.

13