diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8f38ce3b..ecbf2ec7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 diff --git a/.github/workflows/hil.yml b/.github/workflows/hil.yml index 495af01b..52448c99 100644 --- a/.github/workflows/hil.yml +++ b/.github/workflows/hil.yml @@ -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: Install cross + uses: taiki-e/install-action@v2 + with: + tool: cross - - name: Build espflash - run: $HOME/.cargo/bin/cargo build --release - working-directory: espflash + - name: Cache Dependencies + uses: Swatinem/rust-cache@v2 - - name: Build xtask - run: $HOME/.cargo/bin/cargo build --release --locked - working-directory: xtask + - name: Build binaries + run: cross build --release --target ${{ matrix.target.target }} -p espflash -p xtask - - uses: actions/upload-artifact@v4 + - name: Upload espflash-${{ matrix.target.host }} + uses: actions/upload-artifact@v4 with: - name: espflash - path: target/release/espflash + 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 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7ece1fac..dab69e7e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -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: diff --git a/espflash/resources/70-espflash.rules b/espflash/resources/70-espflash.rules new file mode 100644 index 00000000..e4079e6e --- /dev/null +++ b/espflash/resources/70-espflash.rules @@ -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" diff --git a/espflash/resources/bootloaders/esp32p4-bootloader.bin b/espflash/resources/bootloaders/esp32p4-bootloader.bin index 3fc5b489..2050ba5a 100644 Binary files a/espflash/resources/bootloaders/esp32p4-bootloader.bin and b/espflash/resources/bootloaders/esp32p4-bootloader.bin differ diff --git a/espflash/tests/data/README.md b/espflash/tests/data/README.md index 95697310..95a04afe 100644 --- a/espflash/tests/data/README.md +++ b/espflash/tests/data/README.md @@ -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 +``` diff --git a/espflash/tests/data/esp32c5 b/espflash/tests/data/esp32c5 new file mode 100755 index 00000000..142f4ecb Binary files /dev/null and b/espflash/tests/data/esp32c5 differ diff --git a/espflash/tests/data/esp32p4 b/espflash/tests/data/esp32p4 new file mode 100755 index 00000000..718e5b2b Binary files /dev/null and b/espflash/tests/data/esp32p4 differ diff --git a/xtask/src/test_runner.rs b/xtask/src/test_runner.rs index 9039a230..87208e44 100644 --- a/xtask/src/test_runner.rs +++ b/xtask/src/test_runner.rs @@ -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 { + 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 { 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(()) }