Use esp-hal runners for HIL (#996)

* ci: Test esp-hal runners for c6

* ci: Test esp32s2

* ci: Remove unused code

* ci: Enable all the targets

* feat: Add C5 and P4 HIL

* feat: Add udev rules with symlink

* ci: Run tests on multiple ports

* ci: Unfold matrix to have better logging

* ci: Enable S2

* feat: Use port aliases

* feat: Add c5 elf file

* ci: Enable P4

* feat: Avoid code duplication

* ci: Remove p4 uart

* feat: Update p4 bootloader
This commit is contained in:
Sergio Gasquez Arcos 2026-01-29 14:16:01 +01:00 committed by GitHub
parent 5419c6c7ba
commit 15ecc03b8e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 190 additions and 96 deletions

View File

@ -53,7 +53,7 @@ jobs:
arch: "x86_64"
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: ./.github/actions/setup-target
with:
@ -79,7 +79,7 @@ jobs:
arch: "armhf"
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: ./.github/actions/setup-target
with:
@ -105,7 +105,7 @@ jobs:
arch: "armhf"
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: ./.github/actions/setup-target
with:
@ -120,7 +120,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
- run: cargo check -p xtask --all-features
@ -133,7 +133,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: ./.github/actions/setup-target
- run: cargo test --lib
@ -146,7 +146,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
@ -159,7 +159,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@nightly
with:
components: rustfmt

View File

@ -28,95 +28,138 @@ concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
jobs:
build-espflash:
name: Build espflash
runs-on: ubuntu-22.04
container:
image: ubuntu:20.04
build-packages:
name: Build packages | (${{ matrix.target.host }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
target:
- host: aarch64
target: aarch64-unknown-linux-gnu
- host: armv7
target: armv7-unknown-linux-gnueabihf
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
repository: ${{ github.event.inputs.repository || github.repository }}
ref: ${{ github.event.inputs.branch || github.ref }}
- name: Install dependencies
env:
DEBIAN_FRONTEND: noninteractive
run: apt-get update && apt-get -y install curl musl-tools pkg-config
- name: Install toolchain
run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
- name: Build espflash
run: $HOME/.cargo/bin/cargo build --release
working-directory: espflash
- name: Build xtask
run: $HOME/.cargo/bin/cargo build --release --locked
working-directory: xtask
- uses: actions/upload-artifact@v4
- name: Install cross
uses: taiki-e/install-action@v2
with:
name: espflash
path: target/release/espflash
tool: cross
- name: Cache Dependencies
uses: Swatinem/rust-cache@v2
- name: Build binaries
run: cross build --release --target ${{ matrix.target.target }} -p espflash -p xtask
- name: Upload espflash-${{ matrix.target.host }}
uses: actions/upload-artifact@v4
with:
name: espflash-${{ matrix.target.host }}
path: target/${{ matrix.target.target }}/release/espflash
if-no-files-found: error
- uses: actions/upload-artifact@v4
- name: Upload xtask-${{ matrix.target.host }}
uses: actions/upload-artifact@v4
with:
name: xtask
path: target/release/xtask
name: xtask-${{ matrix.target.host }}
path: target/${{ matrix.target.target }}/release/xtask
if-no-files-found: error
run-target:
run-packages:
if: github.repository_owner == 'esp-rs'
name: ${{ matrix.board.mcu }}${{ matrix.board.freq }}
needs: build-espflash
runs-on:
[
self-hosted,
linux,
x64,
"${{ matrix.board.mcu }}${{ matrix.board.freq }}",
]
name: HIL | ${{ matrix.target.soc }} | ${{ matrix.target.port }}
needs: build-packages
runs-on: [self-hosted, "${{ matrix.target.runner }}"]
env:
ESPFLASH_PORT: /dev/serial_ports/${{ matrix.board.mcu }}
ESPFLASH_PORT: /dev/serial_ports/${{ matrix.target.port }}
strategy:
fail-fast: false
matrix:
board:
- mcu: esp32
- mcu: esp32c2
freq: -26mhz
flag: -x 26mhz
- mcu: esp32c3
- mcu: esp32c6
- mcu: esp32h2
- mcu: esp32s2
- mcu: esp32s3
target:
- soc: esp32c2
runner: esp32c2-jtag
port: uart
host: aarch64
- soc: esp32c3
runner: esp32c3-usb
port: usb
host: armv7
- soc: esp32c5
runner: esp32c5-usb
port: usb
host: aarch64
- soc: esp32c5
runner: esp32c5-usb
port: uart
host: aarch64
- soc: esp32c6
runner: esp32c6-usb
port: usb
host: armv7
- soc: esp32c6
runner: esp32c6-usb
port: uart
host: armv7
- soc: esp32h2
runner: esp32h2-usb
port: usb
host: armv7
- soc: esp32h2
runner: esp32h2-usb
port: uart
host: armv7
- soc: esp32p4
runner: esp32p4
port: usb
host: aarch64
- soc: esp32
runner: esp32-jtag
port: uart
host: aarch64
- soc: esp32s2
runner: esp32s2-jtag
port: uart
host: armv7
- soc: esp32s3
runner: esp32s3-usb
port: usb
host: armv7
- soc: esp32s3
runner: esp32s3-usb
port: uart
host: armv7
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v7
with:
name: espflash
name: espflash-${{ matrix.target.host }}
path: espflash_app
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v7
with:
name: xtask
name: xtask-${{ matrix.target.host }}
path: xtask_app
- name: Set up espflash binary
- name: Set up binaries
run: |
chmod +x espflash_app/espflash
echo "$PWD/espflash_app" >> "$GITHUB_PATH"
chmod +x xtask_app/xtask
echo "$PWD/espflash_app" >> "$GITHUB_PATH"
echo "$PWD/xtask_app" >> "$GITHUB_PATH"
- name: Reset device
run: |
espflash reset
sleep 5
- name: Run all tests
run: xtask run-tests --chip ${{ matrix.board.mcu }} -t 60 --no-build
run: xtask run-tests --chip ${{ matrix.target.soc }} -t 60 --no-build

View File

@ -41,7 +41,7 @@ jobs:
arch: "x86_64"
runs-on: ${{ matrix.platform.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: ./.github/actions/package
with:

View File

@ -0,0 +1,24 @@
# Copy this file to /etc/udev/rules.d/
# If rules fail to reload automatically, you can refresh udev rules
# with the command "udevadm control --reload"
# This rules are based on the udev rules from the OpenOCD project, with unsupported probes removed.
# See http://openocd.org/ for more details.
#
# This file is available under the GNU General Public License v2.0
ACTION!="add|change", GOTO="espflash_rules_end"
SUBSYSTEM=="gpio", MODE="0660", GROUP="plugdev", TAG+="uaccess"
SUBSYSTEM!="usb|tty|hidraw", GOTO="espflash_rules_end"
# Please keep this list sorted by VID:PID
# Espressif USB JTAG/serial debug unit and USB Bridge
ATTRS{idVendor}=="303a", ATTRS{idProduct}=="1001", MODE="660", GROUP="plugdev", TAG+="uaccess", SYMLINK+="serial_ports/usb"
ATTRS{idVendor}=="303a", ATTRS{idProduct}=="1002", MODE="660", GROUP="plugdev", TAG+="uaccess", SYMLINK+="serial_ports/usb"
# Silicon Labs CP210x UART Bridge
ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", GROUP="plugdev", MODE="660", TAG+="uaccess", SYMLINK+="serial_ports/uart"
LABEL="espflash_rules_end"

View File

@ -37,3 +37,13 @@ cargo build --release
`esp_hal_binary_with_overlapping_defmt_and_embedded_test_sections` is the ESP-HAL `gpio_unstable` test built for ESP32.
This file is used in a unit test in espflash, and is not flashed as a HIL test.
The `esp32c5` and `esp32p4` elf files under this folder have been generated using `esp-idf@v5.5.2`:
```
git clone -b v5.5.2 --recursive https://github.com/espressif/esp-idf.git
cd esp-idf/
./install.sh all
cd examples/get-started/hello_world/
idf.py set-target $CHIP
idf.py build
```

BIN
espflash/tests/data/esp32c5 Executable file

Binary file not shown.

BIN
espflash/tests/data/esp32p4 Executable file

Binary file not shown.

View File

@ -130,6 +130,36 @@ impl TestRunner {
Ok((child, output, stdout_handle, stderr_handle))
}
fn run_command_capture_output_with_timeout(
cmd: &mut Command,
timeout: Duration,
test_name: &str,
) -> Result<String> {
let (mut child, output, h1, h2) = Self::spawn_and_capture_output(cmd)?;
let start_time = Instant::now();
let mut terminated_naturally = false;
while start_time.elapsed() < timeout {
if let Ok(Some(_)) = child.try_wait() {
terminated_naturally = true;
break;
}
thread::sleep(Duration::from_millis(100));
}
if !terminated_naturally {
log::warn!("{test_name} test timed out after {timeout:?}, terminating process");
let _ = child.kill();
let _ = child.wait();
}
let _ = h1.join();
let _ = h2.join();
let output = output.lock().unwrap();
Ok(output.clone())
}
/// Runs a command with a timeout, returning the exit code
pub fn run_command_with_timeout(&self, cmd: &mut Command, timeout: Duration) -> Result<i32> {
log::debug!("Running command: {cmd:?}");
@ -254,29 +284,8 @@ impl TestRunner {
let mut cmd = self.create_espflash_command(args);
if let Some(expected) = expected_contains {
let (mut child, output, h1, h2) = Self::spawn_and_capture_output(&mut cmd)?;
let start_time = Instant::now();
let mut terminated_naturally = false;
while start_time.elapsed() < timeout {
if let Ok(Some(_)) = child.try_wait() {
terminated_naturally = true;
break;
}
thread::sleep(Duration::from_millis(100));
}
// If still running, kill it
if !terminated_naturally {
log::warn!("{test_name} test timed out after {timeout:?}, terminating process");
let _ = child.kill();
let _ = child.wait();
}
let _ = h1.join();
let _ = h2.join();
let output = output.lock().unwrap();
let output =
Self::run_command_capture_output_with_timeout(&mut cmd, timeout, test_name)?;
for &expected in expected {
if !output.contains(expected) {
Self::restore_terminal();
@ -538,12 +547,20 @@ impl TestRunner {
/// Tests listing available ports
pub fn test_list_ports(&self) -> Result<()> {
self.run_simple_command_test(
&["list-ports"],
Some(&["Silicon Labs"]),
Duration::from_secs(10),
"list-ports",
)?;
log::info!("Running list-ports test");
let mut cmd = self.create_espflash_command(&["list-ports"]);
let timeout = Duration::from_secs(10);
let output =
Self::run_command_capture_output_with_timeout(&mut cmd, timeout, "list-ports")?;
// Accept either "Silicon Labs" or "Espressif" in the output
if !output.contains("Silicon Labs") && !output.contains("Espressif") {
Self::restore_terminal();
return Err(
"Missing expected output: neither 'Silicon Labs' nor 'Espressif' found".into(),
);
}
log::info!("list-ports test passed and output verified");
Ok(())
}