├── .cargo └── config.toml ├── .gitattributes ├── .github ├── actions │ ├── package │ │ └── action.yml │ └── setup-target │ │ └── action.yml └── workflows │ ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ └── feature_request.md │ ├── changelog.yml │ ├── ci.yml │ ├── hil.yml │ ├── issue_handler.yml │ └── release.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── cargo-espflash ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src │ ├── cargo_config.rs │ ├── error.rs │ ├── main.rs │ └── package_metadata.rs ├── espflash ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── resources │ ├── README.md │ ├── bootloaders │ │ ├── esp32-bootloader.bin │ │ ├── esp32_26-bootloader.bin │ │ ├── esp32c2-bootloader.bin │ │ ├── esp32c2_26-bootloader.bin │ │ ├── esp32c3-bootloader.bin │ │ ├── esp32c5-bootloader.bin │ │ ├── esp32c6-bootloader.bin │ │ ├── esp32h2-bootloader.bin │ │ ├── esp32p4-bootloader.bin │ │ ├── esp32s2-bootloader.bin │ │ └── esp32s3-bootloader.bin │ └── stubs │ │ ├── esp32.toml │ │ ├── esp32c2.toml │ │ ├── esp32c3.toml │ │ ├── esp32c5.toml │ │ ├── esp32c6.toml │ │ ├── esp32h2.toml │ │ ├── esp32p4.toml │ │ ├── esp32s2.toml │ │ └── esp32s3.toml ├── src │ ├── bin │ │ └── espflash.rs │ ├── cli │ │ ├── config.rs │ │ ├── mod.rs │ │ ├── monitor │ │ │ ├── external_processors.rs │ │ │ ├── line_endings.rs │ │ │ ├── mod.rs │ │ │ ├── parser │ │ │ │ ├── esp_defmt.rs │ │ │ │ ├── mod.rs │ │ │ │ └── serial.rs │ │ │ └── symbols.rs │ │ └── serial.rs │ ├── connection │ │ ├── command.rs │ │ ├── mod.rs │ │ └── reset.rs │ ├── error.rs │ ├── flasher │ │ ├── mod.rs │ │ └── stubs.rs │ ├── image_format │ │ ├── esp_idf.rs │ │ ├── metadata.rs │ │ └── mod.rs │ ├── lib.rs │ └── targets │ │ ├── efuse │ │ ├── esp32.rs │ │ ├── esp32c2.rs │ │ ├── esp32c3.rs │ │ ├── esp32c5.rs │ │ ├── esp32c6.rs │ │ ├── esp32h2.rs │ │ ├── esp32p4.rs │ │ ├── esp32s2.rs │ │ ├── esp32s3.rs │ │ └── mod.rs │ │ ├── esp32.rs │ │ ├── esp32c2.rs │ │ ├── esp32c3.rs │ │ ├── esp32c5.rs │ │ ├── esp32c6.rs │ │ ├── esp32h2.rs │ │ ├── esp32p4.rs │ │ ├── esp32s2.rs │ │ ├── esp32s3.rs │ │ ├── flash_target │ │ ├── esp32.rs │ │ ├── mod.rs │ │ └── ram.rs │ │ └── mod.rs └── tests │ ├── data │ ├── README.md │ ├── esp32 │ ├── esp32c2 │ ├── esp32c3 │ ├── esp32c6 │ ├── esp32c6_defmt │ ├── esp32h2 │ ├── esp32s2 │ ├── esp32s3 │ ├── esp_idf_firmware_c6.elf │ └── partitions.csv │ └── scripts │ ├── board-info.sh │ ├── checksum-md5.sh │ ├── erase-flash.sh │ ├── erase-region.sh │ ├── flash.sh │ ├── hold-in-reset.sh │ ├── list-ports.sh │ ├── monitor.sh │ ├── read-flash.sh │ ├── reset.sh │ ├── save-image_write-bin.sh │ └── write-bin.sh ├── pre-commit ├── rustfmt.toml ├── taplo.toml └── xtask ├── Cargo.toml └── src └── main.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [alias] 2 | xtask = "run --package xtask --" 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | CHANGELOG.md merge=union 2 | -------------------------------------------------------------------------------- /.github/actions/package/action.yml: -------------------------------------------------------------------------------- 1 | name: Package Release 2 | inputs: 3 | arch: 4 | required: false 5 | github_token: 6 | required: true 7 | target: 8 | required: true 9 | runs_on: 10 | required: true 11 | 12 | runs: 13 | using: composite 14 | steps: 15 | - uses: ./.github/actions/setup-target 16 | with: 17 | arch: ${{ inputs.arch }} 18 | target: ${{ inputs.target }} 19 | 20 | - name: Build 21 | shell: bash 22 | run: | 23 | cargo build --release --all --target ${{ inputs.target }} 24 | 25 | - name: Compress (Unix) 26 | if: ${{ inputs.runs_on != 'windows-2022' }} 27 | shell: bash 28 | run: | 29 | zip -j cargo-espflash-${{ inputs.target }}.zip target/${{ inputs.target }}/release/cargo-espflash 30 | zip -j espflash-${{ inputs.target }}.zip target/${{ inputs.target }}/release/espflash 31 | 32 | - name: Compress (Windows) 33 | if: ${{ inputs.runs_on == 'windows-2022' }} 34 | shell: bash 35 | run: | 36 | 7z a -tzip cargo-espflash-${{ inputs.target }}.zip ./target/${{ inputs.target }}/release/cargo-espflash.exe 37 | 7z a -tzip espflash-${{ inputs.target }}.zip ./target/${{ inputs.target }}/release/espflash.exe 38 | 39 | - uses: svenstaro/upload-release-action@v2 40 | with: 41 | repo_token: ${{ inputs.github_token }} 42 | file: "*.zip" 43 | file_glob: true 44 | tag: ${{ github.ref }} 45 | -------------------------------------------------------------------------------- /.github/actions/setup-target/action.yml: -------------------------------------------------------------------------------- 1 | name: Setup target Build Environment 2 | description: Setup an ARM or x86_64 build environment 3 | 4 | inputs: 5 | arch: 6 | default: x86_64 7 | required: false 8 | target: 9 | default: x86_64-unknown-linux-gnu 10 | required: false 11 | toolchain: 12 | default: stable 13 | required: false 14 | components: 15 | required: false 16 | 17 | runs: 18 | using: composite 19 | steps: 20 | - if: inputs.arch != 'x86_64' 21 | name: Replace target string 22 | id: findandreplace 23 | uses: mad9000/actions-find-and-replace-string@5 24 | with: 25 | source: ${{ inputs.target }} 26 | find: "unknown-" 27 | replace: "" 28 | 29 | - name: Install toolchain 30 | uses: dtolnay/rust-toolchain@v1 31 | with: 32 | toolchain: ${{ inputs.toolchain }} 33 | target: ${{ inputs.target }} 34 | components: ${{ inputs.components }} 35 | 36 | - uses: Swatinem/rust-cache@v2 37 | 38 | - if: inputs.arch != 'x86_64' 39 | name: Install Cross-Compile Support 40 | uses: junelife/gha-ubuntu-cross@v6 41 | with: 42 | arch: ${{ inputs.arch }} 43 | 44 | - if: inputs.arch != 'x86_64' 45 | name: Install dependencies (Raspberry Pi) 46 | shell: bash 47 | run: | 48 | sudo apt-get update && sudo apt-get install -y \ 49 | curl \ 50 | gcc-aarch64-linux-gnu \ 51 | gcc-arm-linux-gnueabihf \ 52 | git \ 53 | libc-dev \ 54 | "libc6:${{ inputs.arch }}" \ 55 | "libgcc-s1:${{ inputs.arch }}" \ 56 | linux-libc-dev \ 57 | musl-tools \ 58 | pkg-config 59 | 60 | - if: inputs.target == 'x86_64-unknown-linux-gnu' || inputs.target == 'x86_64-unknown-linux-musl' 61 | name: Install dependencies (x86_64 linux) 62 | shell: bash 63 | run: | 64 | sudo apt-get update && sudo apt-get -y install musl-tools pkg-config 65 | 66 | - if: inputs.arch != 'x86_64' 67 | name: Set environment variables 68 | shell: bash 69 | run: | 70 | echo "PKG_CONFIG_ALLOW_SYSTEM_LIBS=0" >> $GITHUB_ENV 71 | echo "PKG_CONFIG_DIR=/opt/" >> $GITHUB_ENV 72 | echo "PKG_CONFIG_LIBDIR=/opt/usr/lib/pkgconfig:/opt/usr/share/pkgconfig" >> $GITHUB_ENV 73 | echo "PKG_CONFIG_ALLOW_CROSS=1" >> $GITHUB_ENV 74 | if [[ ${{ inputs.arch }} == arm64 ]]; then 75 | echo "PKG_CONFIG_PATH=/usr/lib/${{ steps.findandreplace.outputs.value }}/pkgconfig" >> $GITHUB_ENV 76 | echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=${{ steps.findandreplace.outputs.value }}-gcc" >> $GITHUB_ENV 77 | fi 78 | if [[ ${{ inputs.arch }} == armhf ]]; then 79 | echo "PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabihf/pkgconfig" >> $GITHUB_ENV 80 | echo "CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc" >> $GITHUB_ENV 81 | fi 82 | -------------------------------------------------------------------------------- /.github/workflows/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Bug description 11 | 12 | 13 | 14 | - [ ] I have searched the existing issues (closed & open) to make sure the issue hasn't already been reported 15 | - [ ] Check this box if you would like to work on a fix 16 | 17 | ## To Reproduce 18 | 19 | Steps to reproduce the behavior: 20 | 21 | 1. ... 22 | 2. ... 23 | 3. ... 24 | 4. ... 25 | 26 | - [ ] I have reproduced the bug in main branch too 27 | 28 | ## Expected behavior 29 | 30 | 31 | 32 | ## Screenshots 33 | 34 | 35 | 36 | ## Environment 37 | 38 | 39 | 40 | - OS: [e.g. Ubuntu 20.04] 41 | - espflash/cargo-espflash version: [e.g. 0.1.0] 42 | - Target: [e.g. ESP32-C3] 43 | 44 | ## Additional context 45 | 46 | 47 | -------------------------------------------------------------------------------- /.github/workflows/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | -------------------------------------------------------------------------------- /.github/workflows/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Motivations 11 | 12 | 15 | 16 | - Would you like to implement this feature? [y/n] 17 | 18 | ## Solution 19 | 20 | 21 | 22 | ## Alternatives 23 | 24 | 25 | 26 | ## Additional context 27 | 28 | 29 | -------------------------------------------------------------------------------- /.github/workflows/changelog.yml: -------------------------------------------------------------------------------- 1 | name: Changelog check 2 | 3 | on: 4 | pull_request: 5 | # Run on labeled/unlabeled in addition to defaults to detect 6 | # adding/removing skip-changelog labels. 7 | types: [opened, reopened, labeled, unlabeled, synchronize] 8 | 9 | jobs: 10 | changelog: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: dangoslen/changelog-enforcer@v3 14 | with: 15 | changeLogPath: CHANGELOG.md 16 | skipLabels: "skip-changelog" 17 | missingUpdateErrorMessage: "Please add a changelog entry in the CHANGELOG.md file." 18 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches-ignore: 7 | - "gh-readonly-queue/**" 8 | - "main" 9 | merge_group: 10 | workflow_dispatch: 11 | 12 | env: 13 | CARGO_TERM_COLOR: always 14 | MSRV: "1.85" 15 | 16 | # Cancel any currently running workflows from the same PR, branch, or 17 | # tag when a new workflow is triggered. 18 | # 19 | # https://stackoverflow.com/a/66336834 20 | concurrency: 21 | cancel-in-progress: true 22 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 23 | 24 | jobs: 25 | # -------------------------------------------------------------------------- 26 | # Check 27 | 28 | check: 29 | name: Check (${{ matrix.platform.target }}) 30 | runs-on: ${{ matrix.platform.os }} 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | platform: 36 | - os: "macos-13" 37 | target: "x86_64-apple-darwin" 38 | arch: "x86_64" 39 | - os: "ubuntu-22.04" 40 | target: "x86_64-unknown-linux-gnu" 41 | arch: "x86_64" 42 | - os: "ubuntu-22.04" 43 | target: "x86_64-unknown-linux-musl" 44 | arch: "x86_64" 45 | - os: "ubuntu-22.04" 46 | target: "aarch64-unknown-linux-gnu" 47 | arch: "arm64" 48 | - os: "ubuntu-22.04" 49 | target: "armv7-unknown-linux-gnueabihf" 50 | arch: "armhf" 51 | - os: "windows-2022" 52 | target: "x86_64-pc-windows-msvc" 53 | arch: "x86_64" 54 | 55 | steps: 56 | - uses: actions/checkout@v4 57 | 58 | - uses: ./.github/actions/setup-target 59 | with: 60 | arch: ${{ matrix.platform.arch }} 61 | target: ${{ matrix.platform.target }} 62 | 63 | - run: cargo run -p cargo-espflash -- espflash completions bash 64 | - run: cargo run -p espflash -- completions bash 65 | 66 | check-lib: 67 | name: Check lib (${{ matrix.platform.target }}) 68 | runs-on: ubuntu-22.04 69 | 70 | strategy: 71 | fail-fast: false 72 | matrix: 73 | platform: 74 | - target: "x86_64-unknown-linux-gnu" 75 | arch: "x86_64" 76 | - target: "aarch64-unknown-linux-gnu" 77 | arch: "arm64" 78 | - target: "armv7-unknown-linux-gnueabihf" 79 | arch: "armhf" 80 | 81 | steps: 82 | - uses: actions/checkout@v4 83 | 84 | - uses: ./.github/actions/setup-target 85 | with: 86 | arch: ${{ matrix.platform.arch }} 87 | target: ${{ matrix.platform.target }} 88 | 89 | - run: cargo check -p espflash --lib --no-default-features 90 | - run: cargo check -p espflash --lib --no-default-features --features serialport 91 | 92 | msrv: 93 | name: Check lib MSRV (${{ matrix.platform.target }}) 94 | runs-on: ubuntu-22.04 95 | 96 | strategy: 97 | fail-fast: false 98 | matrix: 99 | platform: 100 | - target: "x86_64-unknown-linux-gnu" 101 | arch: "x86_64" 102 | - target: "aarch64-unknown-linux-gnu" 103 | arch: "arm64" 104 | - target: "armv7-unknown-linux-gnueabihf" 105 | arch: "armhf" 106 | 107 | steps: 108 | - uses: actions/checkout@v4 109 | 110 | - uses: ./.github/actions/setup-target 111 | with: 112 | arch: ${{ matrix.platform.arch }} 113 | target: ${{ matrix.platform.target }} 114 | toolchain: ${{ env.MSRV }} 115 | 116 | - run: cargo check -p espflash --lib 117 | 118 | xtask: 119 | name: Check xtask 120 | runs-on: ubuntu-22.04 121 | 122 | steps: 123 | - uses: actions/checkout@v4 124 | - uses: dtolnay/rust-toolchain@stable 125 | 126 | - run: cargo check -p xtask 127 | 128 | # -------------------------------------------------------------------------- 129 | # Test 130 | 131 | test: 132 | name: Unit Tests 133 | runs-on: ubuntu-22.04 134 | 135 | steps: 136 | - uses: actions/checkout@v4 137 | - uses: ./.github/actions/setup-target 138 | 139 | - run: cargo test --lib 140 | 141 | # -------------------------------------------------------------------------- 142 | # Lint 143 | 144 | clippy: 145 | name: Clippy 146 | runs-on: ubuntu-22.04 147 | 148 | steps: 149 | - uses: actions/checkout@v4 150 | - uses: dtolnay/rust-toolchain@stable 151 | with: 152 | components: clippy 153 | - uses: Swatinem/rust-cache@v2 154 | 155 | - run: cargo clippy -- -D warnings 156 | 157 | rustfmt: 158 | name: Rustfmt 159 | runs-on: ubuntu-22.04 160 | 161 | steps: 162 | - uses: actions/checkout@v4 163 | - uses: dtolnay/rust-toolchain@nightly 164 | with: 165 | components: rustfmt 166 | - uses: Swatinem/rust-cache@v2 167 | 168 | - run: cargo fmt --all -- --check 169 | -------------------------------------------------------------------------------- /.github/workflows/hil.yml: -------------------------------------------------------------------------------- 1 | name: HIL 2 | 3 | on: 4 | pull_request: 5 | types: [opened, synchronize, reopened, ready_for_review] 6 | merge_group: 7 | workflow_dispatch: 8 | inputs: 9 | repository: 10 | description: "Owner and repository to test" 11 | required: true 12 | default: "esp-rs/espflash" 13 | branch: 14 | description: "Branch, tag or SHA to checkout." 15 | required: true 16 | default: "main" 17 | 18 | env: 19 | CARGO_TERM_COLOR: always 20 | 21 | # Cancel any currently running workflows from the same PR, branch, or 22 | # tag when a new workflow is triggered. 23 | # 24 | # https://stackoverflow.com/a/66336834 25 | concurrency: 26 | cancel-in-progress: true 27 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 28 | 29 | jobs: 30 | build-espflash: 31 | name: Build espflash 32 | runs-on: ubuntu-22.04 33 | container: 34 | image: ubuntu:20.04 35 | 36 | steps: 37 | - uses: actions/checkout@v4 38 | with: 39 | repository: ${{ github.event.inputs.repository || github.repository }} 40 | ref: ${{ github.event.inputs.branch || github.ref }} 41 | 42 | - name: Install dependencies 43 | env: 44 | DEBIAN_FRONTEND: noninteractive 45 | run: apt-get update && apt-get -y install curl musl-tools pkg-config 46 | 47 | - name: Install toolchain 48 | run: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y 49 | 50 | - name: Build espflash 51 | run: $HOME/.cargo/bin/cargo build --release 52 | working-directory: espflash 53 | 54 | - uses: actions/upload-artifact@v4 55 | with: 56 | name: espflash 57 | path: target/release/espflash 58 | if-no-files-found: error 59 | 60 | run-target: 61 | if: github.repository_owner == 'esp-rs' 62 | name: ${{ matrix.board.mcu }}${{ matrix.board.freq }} 63 | needs: build-espflash 64 | runs-on: 65 | [ 66 | self-hosted, 67 | linux, 68 | x64, 69 | "${{ matrix.board.mcu }}${{ matrix.board.freq }}", 70 | ] 71 | 72 | env: 73 | ESPFLASH_PORT: /dev/serial_ports/${{ matrix.board.mcu }} 74 | 75 | strategy: 76 | fail-fast: false 77 | matrix: 78 | board: 79 | - mcu: esp32 80 | - mcu: esp32c2 81 | freq: -26mhz 82 | flag: -x 26mhz 83 | - mcu: esp32c3 84 | - mcu: esp32c6 85 | - mcu: esp32h2 86 | - mcu: esp32s2 87 | - mcu: esp32s3 88 | 89 | steps: 90 | - uses: actions/checkout@v4 91 | 92 | - uses: actions/download-artifact@v4 93 | with: 94 | name: espflash 95 | path: espflash_app 96 | 97 | - name: Set up espflash binary 98 | run: | 99 | chmod +x espflash_app/espflash 100 | echo "$PWD/espflash_app" >> "$GITHUB_PATH" 101 | 102 | - name: board-info test 103 | run: timeout 10 bash espflash/tests/scripts/board-info.sh 104 | 105 | - name: flash test 106 | run: timeout 60 bash espflash/tests/scripts/flash.sh ${{ matrix.board.mcu }} 107 | 108 | - name: monitor test 109 | run: timeout 10 bash espflash/tests/scripts/monitor.sh 110 | 111 | - name: erase-flash test 112 | run: timeout 60 bash espflash/tests/scripts/erase-flash.sh 113 | 114 | - name: save-image/write-bin test 115 | run: | 116 | timeout 90 bash espflash/tests/scripts/save-image_write-bin.sh ${{ matrix.board.mcu }} 117 | 118 | - name: erase-region test 119 | run: timeout 15 bash espflash/tests/scripts/erase-region.sh 120 | 121 | - name: hold-in-reset test 122 | run: timeout 10 bash espflash/tests/scripts/hold-in-reset.sh 123 | 124 | - name: reset test 125 | run: timeout 10 bash espflash/tests/scripts/reset.sh 126 | 127 | - name: checksum-md5 test 128 | run: timeout 40 bash espflash/tests/scripts/checksum-md5.sh 129 | 130 | - name: list-ports test 131 | run: timeout 10 bash espflash/tests/scripts/list-ports.sh 132 | 133 | - name: write-bin test 134 | run: timeout 20 bash espflash/tests/scripts/write-bin.sh 135 | 136 | - name: read-flash test 137 | run: timeout 60 bash espflash/tests/scripts/read-flash.sh 138 | -------------------------------------------------------------------------------- /.github/workflows/issue_handler.yml: -------------------------------------------------------------------------------- 1 | name: Add new issues to project 2 | 3 | on: 4 | issues: 5 | types: 6 | - opened 7 | 8 | jobs: 9 | add-to-project: 10 | name: Add issue to project 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/add-to-project@v0.5.0 14 | with: 15 | project-url: https://github.com/orgs/esp-rs/projects/2 16 | github-token: ${{ secrets.PAT }} 17 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | env: 8 | CARGO_TERM_COLOR: always 9 | 10 | jobs: 11 | release: 12 | name: ${{ matrix.platform.target }} 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | platform: 17 | # Linux 18 | - os: "ubuntu-22.04" 19 | target: "x86_64-unknown-linux-gnu" 20 | arch: "x86_64" 21 | - os: "ubuntu-22.04" 22 | target: "x86_64-unknown-linux-musl" 23 | arch: "x86_64" 24 | - os: "ubuntu-22.04" 25 | target: "aarch64-unknown-linux-gnu" 26 | arch: "arm64" 27 | - os: "ubuntu-22.04" 28 | target: "armv7-unknown-linux-gnueabihf" 29 | arch: "armhf" 30 | # Windows 31 | - os: "windows-2022" 32 | target: "x86_64-pc-windows-msvc" 33 | arch: "x86_64" 34 | # macOs 35 | - os: "macos-13" 36 | target: "aarch64-apple-darwin" 37 | # This is not true, but simplifies the logic of the action. 38 | arch: "x86_64" 39 | - os: "macos-13" 40 | target: "x86_64-apple-darwin" 41 | arch: "x86_64" 42 | runs-on: ${{ matrix.platform.os }} 43 | steps: 44 | - uses: actions/checkout@v4 45 | 46 | - uses: ./.github/actions/package 47 | with: 48 | arch: ${{ matrix.platform.arch }} 49 | github_token: ${{ secrets.GITHUB_TOKEN }} 50 | target: ${{ matrix.platform.target }} 51 | runs_on: ${{ matrix.platform.os }} 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # These are backup files generated by rustfmt 7 | **/*.rs.bk 8 | 9 | # MSVC Windows builds of rustc generate these, which store debugging information 10 | *.pdb 11 | 12 | # Ignore any files used by text editors or operating systems. 13 | .vscode/ 14 | .DS_Store 15 | *.swp 16 | .idea/ 17 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = ["cargo-espflash", "espflash", "xtask"] 4 | 5 | [profile.release] 6 | lto = "thin" 7 | strip = true 8 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022-2025 The Espflash Project Developers 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # espflash 2 | 3 | [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/esp-rs/espflash/ci.yml?branch=main&labelColor=1C2C2E&logo=github&style=flat-square)](https://github.com/esp-rs/espflash/actions/workflows/ci.yml) 4 | ![Crates.io](https://img.shields.io/crates/l/espflash?labelColor=1C2C2E&style=flat-square) 5 | [![Matrix](https://img.shields.io/matrix/esp-rs:matrix.org?label=join%20matrix&color=BEC5C9&labelColor=1C2C2E&logo=matrix&style=flat-square)](https://matrix.to/#/#esp-rs:matrix.org) 6 | 7 | Serial flasher utilities for Espressif devices, based loosely on [esptool.py](https://github.com/espressif/esptool/). 8 | 9 | Supports the **ESP32**, **ESP32-C2/C3/C5/C6**, **ESP32-H2**, **ESP32-P4**, and **ESP32-S2/S3**. 10 | 11 | ## [cargo-espflash](./cargo-espflash/) 12 | 13 | A cargo extension for flashing Espressif devices. 14 | 15 | For more information and installation instructions, please refer to the `cargo-espflash` package's [README](./cargo-espflash/README.md). 16 | 17 | ## [espflash](./espflash/) 18 | 19 | A library and command-line tool for flashing Espressif devices. 20 | 21 | For more information and installation instructions, please refer to the `espflash` package's [README](./espflash/README.md). 22 | 23 | ## Git Hooks 24 | 25 | We provide a simple `pre-commit` hook to verify the formatting of each package prior to committing changes. This can be enabled by placing it in the `.git/hooks/` directory: 26 | 27 | ```bash 28 | $ cp pre-commit .git/hooks/pre-commit 29 | ``` 30 | 31 | When using this hook, you can choose to ignore its failure on a per-commit basis by committing with the `--no-verify` flag; however, you will need to be sure that all packages are formatted when submitting a pull request. 32 | 33 | ## License 34 | 35 | Licensed under either of: 36 | 37 | - Apache License, Version 2.0 ([LICENSE-APACHE](./LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 38 | - MIT license ([LICENSE-MIT](./LICENSE-MIT) or http://opensource.org/licenses/MIT) 39 | 40 | at your option. 41 | 42 | ### Contribution 43 | 44 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in 45 | the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without 46 | any additional terms or conditions. 47 | -------------------------------------------------------------------------------- /cargo-espflash/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cargo-espflash" 3 | version = "4.0.0-dev" 4 | edition = "2024" 5 | rust-version = "1.85" 6 | description = "Cargo subcommand for interacting with Espressif devices" 7 | repository = "https://github.com/esp-rs/espflash" 8 | license = "MIT OR Apache-2.0" 9 | 10 | keywords = ["cargo", "cli", "embedded", "esp"] 11 | categories = [ 12 | "command-line-utilities", 13 | "development-tools", 14 | "development-tools::cargo-plugins", 15 | "embedded", 16 | ] 17 | 18 | [package.metadata.binstall] 19 | pkg-url = "{ repo }/releases/download/v{ version }/{ name }-{ target }.{ archive-format }" 20 | bin-dir = "{ bin }{ binary-ext }" 21 | pkg-fmt = "zip" 22 | 23 | [dependencies] 24 | cargo_metadata = "0.19.1" 25 | clap = { version = "4.5.24", features = ["derive", "wrap_help"] } 26 | espflash = { version = "4.0.0-dev", path = "../espflash" } 27 | log = "0.4.22" 28 | miette = { version = "7.4.0", features = ["fancy"] } 29 | serde = { version = "1.0.217", features = ["derive"] } 30 | thiserror = "2.0.10" 31 | toml = "0.8.19" 32 | -------------------------------------------------------------------------------- /cargo-espflash/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /cargo-espflash/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /cargo-espflash/README.md: -------------------------------------------------------------------------------- 1 | 2 | # cargo-espflash 3 | 4 | [![Crates.io](https://img.shields.io/crates/v/cargo-espflash?labelColor=1C2C2E&color=C96329&logo=Rust&style=flat-square)](https://crates.io/crates/cargo-espflash) 5 | ![MSRV](https://img.shields.io/badge/MSRV-1.85-blue?labelColor=1C2C2E&logo=Rust&style=flat-square) 6 | ![Crates.io](https://img.shields.io/crates/l/cargo-espflash?labelColor=1C2C2E&style=flat-square) 7 | 8 | Cross-compiler and Cargo extension for flashing Espressif devices. 9 | 10 | Supports the **ESP32**, **ESP32-C2/C3/C5/C6**, **ESP32-H2**, **ESP32-P4**, and **ESP32-S2/S3**. 11 | 12 | 13 | ## Table of Contents 14 | 15 | - [Installation](#installation) 16 | - [Usage](#usage) 17 | - [Permissions on Linux](#permissions-on-linux) 18 | - [Windows Subsystem for Linux](#windows-subsystem-for-linux) 19 | - [Bootloader and Partition Table](#bootloader-and-partition-table) 20 | - [Configuration Files](#configuration-files) 21 | - [`espflash_ports.toml`](#espflash_portstoml) 22 | - [`espflash.toml`](#espflashtoml) 23 | - [Configuration Files Location](#configuration-files-location) 24 | - [Configuration Precedence](#configuration-precedence) 25 | - [Logging Format](#logging-format) 26 | - [Development Kit Support Policy](#development-kit-support-policy) 27 | - [License](#license) 28 | - [Contribution](#contribution) 29 | 30 | ## Installation 31 | 32 | If you are installing `cargo-espflash` from source (ie. using `cargo install`) then you must have `rustc>=1.85.0` installed on your system. 33 | 34 | To install: 35 | 36 | ```bash 37 | cargo install cargo-espflash --locked 38 | ``` 39 | 40 | Alternatively, you can use [cargo-binstall] to download pre-compiled artifacts from the [releases] and use them instead: 41 | 42 | ```bash 43 | cargo binstall cargo-espflash 44 | ``` 45 | 46 | [cargo-binstall]: https://github.com/cargo-bins/cargo-binstall 47 | [releases]: https://github.com/esp-rs/espflash/releases 48 | 49 | ## Usage 50 | 51 | ```text 52 | Cargo subcommand for interacting with Espressif devices 53 | 54 | Usage: cargo espflash [OPTIONS] 55 | 56 | Commands: 57 | board-info Print information about a connected target device 58 | checksum-md5 Calculate the MD5 checksum of the given region 59 | completions Generate completions for the given shell 60 | erase-flash Erase Flash entirely 61 | erase-parts Erase specified partitions 62 | erase-region Erase specified region 63 | flash Flash an application in ELF format to a target device 64 | hold-in-reset Hold the target device in reset 65 | list-ports List available serial ports 66 | monitor Open the serial monitor without flashing the connected target device 67 | partition-table Convert partition tables between CSV and binary format 68 | read-flash Read SPI flash content 69 | reset Reset the target device 70 | save-image Generate a binary application image and save it to a local disk 71 | write-bin Write a binary file to a specific address in a target device's flash 72 | help Print this message or the help of the given subcommand(s) 73 | 74 | Options: 75 | -S, --skip-update-check Do not check for updates 76 | -h, --help Print help 77 | -V, --version Print version 78 | ``` 79 | 80 | ### Permissions on Linux 81 | 82 | In Linux, when using any of the commands that requires using a serial port, the current user may not have access to serial ports and a "Permission Denied" or "Port doesn’t exist" errors may appear. 83 | 84 | On most Linux distributions, the solution is to add the user to the `dialout` group (check e.g. `ls -l /dev/ttyUSB0` to find the group) with a command like `sudo usermod -a -G dialout $USER`. You can call `su - $USER` to enable read and write permissions for the serial port without having to log out and back in again. 85 | 86 | Check your Linux distribution’s documentation for more information. 87 | 88 | ### Windows Subsystem for Linux 89 | 90 | It is _not_ currently possible to use `cargo-espflash` from within WSL1. There are no plans to add support for WSL1 at this time. 91 | 92 | It is also _not_ possible to flash chips using the built-in `USB_SERIAL_JTAG` peripheral when using WSL2, because resetting also resets `USB_SERIAL_JTAG` peripheral, which then disconnects the chip from WSL2. Chips _can_ be flashed via UART using WSL2, however. 93 | 94 | To be able to flash within WSL2, [systemd should be enabled](https://learn.microsoft.com/en-us/windows/wsl/wsl-config#systemd-support). To do so, create or edit `/etc/wsl.conf` using `sudo` for admin permissions and add the following: 95 | 96 | ``` 97 | [boot] 98 | systemd=true 99 | ``` 100 | 101 | and then close the WSL distribution on Windows side using PowerShell to restart WSL instances: 102 | 103 | ```pwsh 104 | wsl.exe --shutdown 105 | ``` 106 | 107 | For more information, please refer [here](https://github.com/esp-rs/espflash/issues/641#issuecomment-2408771592). 108 | 109 | ## Bootloader and Partition Table 110 | 111 | `cargo-espflash` is able to detect if the package being built and flashed depends on [esp-idf-sys]; if it does, then the bootloader and partition table built by the `esp-idf-sys` build script will be used, otherwise the bundled bootloader and partition tables will be used instead. 112 | 113 | If the `--bootloader` and/or `--partition-table` options are provided then these will be used regardless of whether or not the package depends on `esp-idf-sys`. 114 | 115 | [esp-idf-sys]: https://github.com/esp-rs/esp-idf-sys 116 | 117 | ## Configuration Files 118 | 119 | There are two configuration files allowing you to define various parameters for your application: 120 | 121 | - `espflash.toml`: Project configuration 122 | - `espflash_ports.toml`: Port configuration 123 | 124 | The reason to split configuration into two different files is to allow Git ignoring the Serial Port configuration, which is specific to the user (see [#727](https://github.com/esp-rs/espflash/issues/727)). 125 | 126 | ### `espflash_ports.toml` 127 | 128 | This file allows you to define the serial port connection parameters: 129 | - By name: 130 | ```toml 131 | [connection] 132 | serial = "/dev/ttyUSB0" 133 | ``` 134 | - By USB VID/PID values: 135 | ```toml 136 | [[usb_device]] 137 | vid = "303a" 138 | pid = "1001" 139 | ``` 140 | 141 | ### `espflash.toml` 142 | 143 | This file allows you to define different flash parameters: 144 | - Baudrate: 145 | ```toml 146 | baudrate = 460800 147 | ``` 148 | - Bootloader: 149 | ```toml 150 | bootloader = "path/to/custom/bootloader.bin" 151 | ``` 152 | - Partition table 153 | ```toml 154 | partition_table = "path/to/custom/partition-table.bin" 155 | ``` 156 | - Flash settings 157 | ```toml 158 | [flash] 159 | mode = "qio" 160 | size = "8MB" 161 | frequency = "80MHz" 162 | ``` 163 | 164 | ### Configuration Files Location 165 | You can have a local and/or a global configuration file(s): 166 | 167 | - For local configurations, store the file under the current working directory or in the parent directory (to support Cargo workspaces) with the name `espflash.toml` 168 | - Global file location differs based on your operating system: 169 | - Linux: `$HOME/.config/espflash/espflash.toml` or `$HOME/.config/espflash/espflash_ports.toml` 170 | - macOS: `$HOME/Library/Application Support/rs.esp.espflash/espflash.toml` or `$HOME/Library/Application Support/rs.esp.espflash/espflash_ports.toml` 171 | - Windows: `%APPDATA%\esp\espflash\espflash.toml` or `%APPDATA%\esp\espflash\espflash_ports.toml` 172 | 173 | ### Configuration Precedence 174 | 175 | 1. Environment variables: If `ESPFLASH_PORT`, `MONITOR_BAUD` or `ESPFLASH_BAUD` are set, the will be used instead of the config file value. 176 | 2. Local configuration file 177 | 3. Global configuration file 178 | 179 | ## Logging Format 180 | 181 | `cargo-espflash` `flash` and `monitor` subcommands support several logging formats using the `-L/--log-format` argument: 182 | 183 | - `serial`: Default logging format 184 | - `defmt`: Uses [`defmt`] logging framework. With logging format, logging strings have framing bytes to indicate that they are `defmt` messages. 185 | - See [`defmt` section] of `esp-println` readme. 186 | - For a detailed guide on how to use `defmt` in the `no_std` ecosystem, see [`defmt` project] of Embedded Rust (no_std) on Espressif book. 187 | 188 | [`defmt`]: https://defmt.ferrous-systems.com/ 189 | [`defmt` section]: https://github.com/esp-rs/esp-hal/tree/main/esp-println#defmt 190 | [`defmt` project]: https://docs.esp-rs.org/no_std-training/03_7_defmt.html 191 | 192 | ## Development Kit Support Policy 193 | 194 | While in an ideal world we would aim to provide full support for all available development kits, this is unfortunately not achievable in practice. Instead, we aim to ensure full compatibility with all [official Espressif development kits]. 195 | 196 | We do not expect issues with third-party kits, however occasion issues do crop up. When this happens, if users expect fixes to be made then they are expected to debug the issue themselves so that a fix can be formulated; we cannot always reproduce these problems ourselves, and it's not reasonable to expect us to purchase every development kit users may experience issues with. 197 | 198 | [official Espressif development kits]: https://www.espressif.com/en/products/devkits 199 | 200 | ## License 201 | 202 | Licensed under either of: 203 | 204 | - Apache License, Version 2.0 ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 205 | - MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT) 206 | 207 | at your option. 208 | 209 | ### Contribution 210 | 211 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in 212 | the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without 213 | any additional terms or conditions. 214 | -------------------------------------------------------------------------------- /cargo-espflash/src/cargo_config.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fs, 3 | path::{Path, PathBuf}, 4 | }; 5 | 6 | use miette::{Result, WrapErr}; 7 | use serde::Deserialize; 8 | 9 | use crate::error::TomlError; 10 | 11 | #[derive(Debug, Default, Deserialize)] 12 | #[serde(rename_all = "kebab-case")] 13 | pub struct Unstable { 14 | #[serde(default)] 15 | build_std: Vec, 16 | } 17 | 18 | #[derive(Debug, Default, Deserialize)] 19 | pub struct Build { 20 | target: Option, 21 | } 22 | 23 | #[derive(Debug, Deserialize, Default)] 24 | pub struct CargoConfig { 25 | #[serde(default)] 26 | unstable: Unstable, 27 | #[serde(default)] 28 | build: Build, 29 | } 30 | 31 | impl CargoConfig { 32 | pub fn load(workspace_root: &Path, package_root: &Path) -> Self { 33 | // If there is a Cargo configuration file in the current package, we will 34 | // deserialize and return it. 35 | // If the package is in a workspace and a Cargo configuration file is present in 36 | // that workspace we will deserialize and return that one instead. 37 | // Otherwise, there is no configuration present so we will return `None`. 38 | if let Ok(Some(package_config)) = load_cargo_config(package_root) { 39 | package_config 40 | } else if let Ok(Some(workspace_config)) = load_cargo_config(workspace_root) { 41 | workspace_config 42 | } else { 43 | Self::default() 44 | } 45 | } 46 | 47 | pub fn has_build_std(&self) -> bool { 48 | !self.unstable.build_std.is_empty() 49 | } 50 | 51 | pub fn target(&self) -> Option<&str> { 52 | self.build.target.as_deref() 53 | } 54 | } 55 | 56 | fn load_cargo_config(path: &Path) -> Result> { 57 | let config_path = match config_path(path) { 58 | Some(path) => path, 59 | None => { 60 | return Ok(None); 61 | } 62 | }; 63 | 64 | let content = match fs::read_to_string(&config_path) { 65 | Ok(content) => content, 66 | Err(_) => return Ok(None), 67 | }; 68 | 69 | let config = toml::from_str(&content) 70 | .map_err(move |e| TomlError::new(e, content)) 71 | .wrap_err_with(|| { 72 | format!( 73 | "Failed to parse {}", 74 | &config_path.as_path().to_string_lossy() 75 | ) 76 | })?; 77 | 78 | Ok(Some(config)) 79 | } 80 | 81 | fn config_path(path: &Path) -> Option { 82 | // Support for the .toml extension was added in version 1.39 and is the 83 | // preferred form. If both files exist, Cargo will use the file without the 84 | // extension. 85 | // https://doc.rust-lang.org/cargo/reference/config.html 86 | let bare = path.join(".cargo/config"); 87 | if bare.exists() { 88 | return Some(bare); 89 | } 90 | 91 | let toml = path.join(".cargo/config.toml"); 92 | if toml.exists() { Some(toml) } else { None } 93 | } 94 | -------------------------------------------------------------------------------- /cargo-espflash/src/error.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fmt::{Display, Formatter}, 3 | iter::once, 4 | }; 5 | 6 | use espflash::targets::Chip; 7 | use miette::{Diagnostic, LabeledSpan, SourceCode}; 8 | use thiserror::Error; 9 | 10 | #[derive(Debug, Diagnostic, Error)] 11 | #[non_exhaustive] 12 | pub enum Error { 13 | #[error("Multiple build artifacts found")] 14 | #[diagnostic( 15 | code(cargo_espflash::multiple_artifacts), 16 | help("Please specify which artifact to flash using `--bin`") 17 | )] 18 | MultipleArtifacts, 19 | 20 | #[error("No executable artifact found")] 21 | #[diagnostic( 22 | code(cargo_espflash::no_artifact), 23 | help( 24 | "If you're trying to run an example you need to specify it using the `--example` argument.\n\ 25 | If you're in a Cargo workspace, specify the binary package with `--package`." 26 | ) 27 | )] 28 | NoArtifact, 29 | 30 | #[error("'build-std' not configured")] 31 | #[diagnostic( 32 | code(cargo_espflash::no_build_std), 33 | help( 34 | "Cargo currently requires the unstable 'build-std' feature, ensure \ 35 | that `.cargo/config{{.toml}}` has the appropriate options." 36 | ), 37 | url("https://doc.rust-lang.org/cargo/reference/unstable.html#build-std") 38 | )] 39 | NoBuildStd, 40 | 41 | #[error("No package could be located in the current workspace")] 42 | #[diagnostic( 43 | code(cargo_espflash::no_package), 44 | help( 45 | "Ensure that you are executing from a valid package, and that the specified package name \ 46 | exists in the current workspace." 47 | ) 48 | )] 49 | NoPackage, 50 | 51 | #[error("No `Cargo.toml` found in the current directory")] 52 | #[diagnostic( 53 | code(cargo_espflash::no_project), 54 | help("Ensure that you're running the command from within a Cargo project") 55 | )] 56 | NoProject, 57 | } 58 | 59 | /// TOML deserialization error 60 | #[derive(Debug)] 61 | pub struct TomlError { 62 | err: toml::de::Error, 63 | source: String, 64 | } 65 | 66 | impl TomlError { 67 | pub fn new(err: toml::de::Error, source: String) -> Self { 68 | Self { err, source } 69 | } 70 | } 71 | 72 | impl Display for TomlError { 73 | fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { 74 | write!(f, "Failed to parse toml") 75 | } 76 | } 77 | 78 | impl Diagnostic for TomlError { 79 | fn source_code(&self) -> Option<&dyn SourceCode> { 80 | Some(&self.source) 81 | } 82 | 83 | fn labels(&self) -> Option + '_>> { 84 | Some(Box::new(once(LabeledSpan::new_with_span( 85 | Some(self.err.to_string()), 86 | self.err.span()?, 87 | )))) 88 | } 89 | } 90 | 91 | // NOTE: no `source` on purpose to prevent duplicating the message 92 | impl std::error::Error for TomlError {} 93 | 94 | /// Unsupported target error 95 | #[derive(Debug, Diagnostic, Error)] 96 | #[error("Target {target} is not supported by the {chip}")] 97 | #[diagnostic( 98 | code(cargo_espflash::unsupported_target), 99 | help("The following targets are supported by the {}: {}", self.chip, self.supported_targets()) 100 | )] 101 | pub struct UnsupportedTargetError { 102 | target: String, 103 | chip: Chip, 104 | } 105 | 106 | impl UnsupportedTargetError { 107 | pub fn new(target: &str, chip: Chip) -> Self { 108 | Self { 109 | target: target.into(), 110 | chip, 111 | } 112 | } 113 | 114 | fn supported_targets(&self) -> String { 115 | self.chip.into_target().supported_build_targets().join(", ") 116 | } 117 | } 118 | 119 | /// No target error 120 | #[derive(Debug, Error)] 121 | #[error("No target specified in cargo configuration")] 122 | pub struct NoTargetError { 123 | chip: Option, 124 | } 125 | 126 | impl NoTargetError { 127 | pub fn new(chip: Option) -> Self { 128 | Self { chip } 129 | } 130 | } 131 | 132 | impl Diagnostic for NoTargetError { 133 | fn code<'a>(&'a self) -> Option> { 134 | Some(Box::new("cargo_espflash::no_target")) 135 | } 136 | 137 | fn help<'a>(&'a self) -> Option> { 138 | Some(Box::new(match &self.chip { 139 | Some(chip) => format!( 140 | "Specify the target in `.cargo/config.toml`, the {} support the following targets: {}", 141 | chip, 142 | chip.into_target().supported_build_targets().join(", ") 143 | ), 144 | None => "Specify the target in `.cargo/config.toml`".into(), 145 | })) 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /cargo-espflash/src/package_metadata.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use cargo_metadata::{MetadataCommand, camino::Utf8PathBuf}; 4 | use miette::{IntoDiagnostic, Result}; 5 | use serde::Deserialize; 6 | 7 | use crate::error::Error; 8 | 9 | #[derive(Debug, Default, Clone, Deserialize)] 10 | pub struct PackageMetadata { 11 | pub workspace_root: PathBuf, 12 | pub package_root: PathBuf, 13 | } 14 | 15 | impl PackageMetadata { 16 | pub fn load(package_name: &Option) -> Result { 17 | // There MUST be a cargo manifest in the executing directory, regardless of 18 | // whether or not we are in a workspace. 19 | let manifest_path = PathBuf::from("Cargo.toml"); 20 | if !manifest_path.exists() { 21 | return Err(Error::NoProject.into()); 22 | } 23 | 24 | let manifest_path = manifest_path.canonicalize().into_diagnostic()?; 25 | let manifest_path = Utf8PathBuf::from_path_buf(manifest_path).unwrap(); 26 | 27 | let metadata = MetadataCommand::new() 28 | .no_deps() 29 | .manifest_path(&manifest_path) 30 | .exec() 31 | .into_diagnostic()?; 32 | 33 | let maybe_package = if let [package] = metadata.workspace_default_packages()[..] { 34 | Some(package.to_owned()) 35 | } else { 36 | metadata 37 | .packages 38 | .iter() 39 | .find(|package| Some(package.name.clone()) == *package_name) 40 | .cloned() 41 | }; 42 | 43 | let package = maybe_package.ok_or(Error::NoPackage).into_diagnostic()?; 44 | 45 | let package_metadata = Self { 46 | workspace_root: metadata.workspace_root.clone().into(), 47 | package_root: package.manifest_path.parent().unwrap().into(), 48 | }; 49 | 50 | Ok(package_metadata) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /espflash/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "espflash" 3 | version = "4.0.0-dev" 4 | edition = "2024" 5 | rust-version = "1.85" 6 | description = "A command-line tool for interacting with Espressif devices" 7 | repository = "https://github.com/esp-rs/espflash" 8 | license = "MIT OR Apache-2.0" 9 | keywords = ["cli", "embedded", "esp"] 10 | categories = ["command-line-utilities", "development-tools", "embedded"] 11 | 12 | [package.metadata.binstall] 13 | pkg-url = "{ repo }/releases/download/v{ version }/{ name }-{ target }.{ archive-format }" 14 | bin-dir = "{ bin }{ binary-ext }" 15 | pkg-fmt = "zip" 16 | 17 | [package.metadata.docs.rs] 18 | features = ["serialport"] 19 | no-default-features = true 20 | rustdoc-args = ["--cfg", "docsrs"] 21 | 22 | [[bin]] 23 | name = "espflash" 24 | path = "./src/bin/espflash.rs" 25 | required-features = ["cli", "serialport"] 26 | 27 | [dependencies] 28 | addr2line = { version = "0.24.2", optional = true } 29 | base64 = "0.22.1" 30 | bitflags = "2.9.0" 31 | bytemuck = { version = "1.21.0", features = ["derive"] } 32 | clap = { version = "4.5.24", features = ["derive", "env", "wrap_help"], optional = true } 33 | clap_complete = { version = "4.5.41", optional = true } 34 | comfy-table = { version = "7.1.3", optional = true } 35 | crossterm = { version = "0.28.1", optional = true } 36 | ctrlc = { version = "3.4.5", optional = true } 37 | defmt-decoder = { version = "=0.4.0", features = ["unstable"], optional = true } 38 | dialoguer = { version = "0.11.0", optional = true } 39 | directories = { version = "5.0.1", optional = true } 40 | env_logger = { version = "0.11.6", optional = true } 41 | esp-idf-part = "0.6.0" 42 | flate2 = "1.0.35" 43 | indicatif = { version = "0.17.9", optional = true } 44 | log = "0.4.22" 45 | md-5 = "0.10.6" 46 | miette = "7.4.0" 47 | object = "0.36.7" 48 | regex = { version = "1.11.1", optional = true } 49 | serde = { version = "1.0.217", features = ["derive"] } 50 | serialport = { version = "4.7.0", default-features = false, optional = true } 51 | sha2 = "0.10.8" 52 | slip-codec = { version = "0.4.0", optional = true } 53 | strum = { version = "0.26.3", features = ["derive"] } 54 | thiserror = "2.0.10" 55 | toml = { version = "0.8.19", optional = true } 56 | update-informer = { version = "1.2.0", optional = true } 57 | 58 | [target.'cfg(unix)'.dependencies] 59 | libc = "0.2.169" 60 | 61 | [features] 62 | default = ["cli"] 63 | cli = [ 64 | "dep:addr2line", 65 | "dep:clap", 66 | "dep:clap_complete", 67 | "dep:comfy-table", 68 | "dep:crossterm", 69 | "dep:ctrlc", 70 | "dep:defmt-decoder", 71 | "dep:dialoguer", 72 | "dep:directories", 73 | "dep:env_logger", 74 | "dep:indicatif", 75 | "dep:update-informer", 76 | "miette/fancy", 77 | "serialport", 78 | ] 79 | 80 | # Enables connecting to a device via serial port 81 | serialport = ["dep:regex", "dep:serialport", "dep:slip-codec", "dep:toml"] 82 | -------------------------------------------------------------------------------- /espflash/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /espflash/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /espflash/resources/README.md: -------------------------------------------------------------------------------- 1 | # Espflash Resources 2 | 3 | The listed bootloaders from `espressif/esp-idf` were built with `release/v5.4` at commit `3ad3632`, using default settings: 4 | https://github.com/espressif/esp-idf/tree/release/v5.4 5 | 6 | For now, `esp-hal` uses MMU page size as `0x10000` (64k) therefore the flash size has to be changed to 64MB. 7 | 8 | The flasher stubs are taken from the `espressif/esptool` repository: 9 | https://github.com/espressif/esptool/tree/master/esptool/targets/stub_flasher/1 10 | -------------------------------------------------------------------------------- /espflash/resources/bootloaders/esp32-bootloader.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esp-rs/espflash/b8eb162539cd95ed399243d30e9869eb407f265e/espflash/resources/bootloaders/esp32-bootloader.bin -------------------------------------------------------------------------------- /espflash/resources/bootloaders/esp32_26-bootloader.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esp-rs/espflash/b8eb162539cd95ed399243d30e9869eb407f265e/espflash/resources/bootloaders/esp32_26-bootloader.bin -------------------------------------------------------------------------------- /espflash/resources/bootloaders/esp32c2-bootloader.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esp-rs/espflash/b8eb162539cd95ed399243d30e9869eb407f265e/espflash/resources/bootloaders/esp32c2-bootloader.bin -------------------------------------------------------------------------------- /espflash/resources/bootloaders/esp32c2_26-bootloader.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esp-rs/espflash/b8eb162539cd95ed399243d30e9869eb407f265e/espflash/resources/bootloaders/esp32c2_26-bootloader.bin -------------------------------------------------------------------------------- /espflash/resources/bootloaders/esp32c3-bootloader.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esp-rs/espflash/b8eb162539cd95ed399243d30e9869eb407f265e/espflash/resources/bootloaders/esp32c3-bootloader.bin -------------------------------------------------------------------------------- /espflash/resources/bootloaders/esp32c5-bootloader.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esp-rs/espflash/b8eb162539cd95ed399243d30e9869eb407f265e/espflash/resources/bootloaders/esp32c5-bootloader.bin -------------------------------------------------------------------------------- /espflash/resources/bootloaders/esp32c6-bootloader.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esp-rs/espflash/b8eb162539cd95ed399243d30e9869eb407f265e/espflash/resources/bootloaders/esp32c6-bootloader.bin -------------------------------------------------------------------------------- /espflash/resources/bootloaders/esp32h2-bootloader.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esp-rs/espflash/b8eb162539cd95ed399243d30e9869eb407f265e/espflash/resources/bootloaders/esp32h2-bootloader.bin -------------------------------------------------------------------------------- /espflash/resources/bootloaders/esp32p4-bootloader.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esp-rs/espflash/b8eb162539cd95ed399243d30e9869eb407f265e/espflash/resources/bootloaders/esp32p4-bootloader.bin -------------------------------------------------------------------------------- /espflash/resources/bootloaders/esp32s2-bootloader.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esp-rs/espflash/b8eb162539cd95ed399243d30e9869eb407f265e/espflash/resources/bootloaders/esp32s2-bootloader.bin -------------------------------------------------------------------------------- /espflash/resources/bootloaders/esp32s3-bootloader.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esp-rs/espflash/b8eb162539cd95ed399243d30e9869eb407f265e/espflash/resources/bootloaders/esp32s3-bootloader.bin -------------------------------------------------------------------------------- /espflash/resources/stubs/esp32.toml: -------------------------------------------------------------------------------- 1 | entry = 1_074_521_580 2 | text = "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" 3 | text_start = 1_074_520_064 4 | data = "DMD8P+znC0B/6AtAZ+0LQAbpC0Cf6AtABukLQGXpC0CC6gtA9OoLQJ3qC0CV5wtAGuoLQHTqC0CI6QtAGOsLQLDpC0AY6wtAbegLQMroC0AG6QtAZekLQIXoC0DI6wtAKe0LQLjmC0BL7QtAuOYLQLjmC0C45gtAuOYLQLjmC0C45gtAuOYLQLjmC0Bv6wtAuOYLQEnsC0Ap7QtA" 5 | data_start = 1_073_605_544 6 | bss_start = 1_073_528_832 7 | -------------------------------------------------------------------------------- /espflash/resources/stubs/esp32c2.toml: -------------------------------------------------------------------------------- 1 | entry = 1_077_413_304 2 | text = "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" 3 | text_start = 1_077_411_840 4 | data = "DEDKP+AIOEAsCThAhAk4QFIKOEC+CjhAbAo4QKgHOEAOCjhATgo4QJgJOEBYBzhAzAk4QFgHOEC6CDhA/gg4QCwJOECECThAzAg4QBIIOEBCCDhAyAg4QBYNOEAsCThA1gs4QMoMOECkBjhA9Aw4QKQGOECkBjhApAY4QKQGOECkBjhApAY4QKQGOECkBjhAcgs4QKQGOEDyCzhAygw4QA==" 5 | data_start = 1_070_295_976 6 | bss_start = 1_070_219_264 7 | -------------------------------------------------------------------------------- /espflash/resources/stubs/esp32c3.toml: -------------------------------------------------------------------------------- 1 | entry = 1_077_413_584 2 | text = "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" 3 | text_start = 1_077_411_840 4 | data = "GEDIP8AKOEAQCzhAaAs4QDYMOECiDDhAUAw4QHIJOEDyCzhAMgw4QHwLOEAiCThAsAs4QCIJOECaCjhA4Ao4QBALOEBoCzhArAo4QNYJOEAgCjhAqAo4QPoOOEAQCzhAug04QLIOOEBiCDhA2g44QGIIOEBiCDhAYgg4QGIIOEBiCDhAYgg4QGIIOEBiCDhAVg04QGIIOEDYDThAsg44QA==" 5 | data_start = 1_070_164_916 6 | bss_start = 1_070_088_192 7 | -------------------------------------------------------------------------------- /espflash/resources/stubs/esp32c5.toml: -------------------------------------------------------------------------------- 1 | entry = 1_082_132_164 2 | text = "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" 3 | text_start = 1_082_130_432 4 | data = "FACEQG4KgEC+CoBAFguAQOQLgEBQDIBA/guAQDoJgECgC4BA4AuAQCoLgEDqCIBAXguAQOoIgEBICoBAjgqAQL4KgEAWC4BAWgqAQJ4JgEDOCYBAVgqAQKgOgEC+CoBAaA2AQGAOgEAqCIBAiA6AQCoIgEAqCIBAKgiAQCoIgEAqCIBAKgiAQCoIgEAqCIBABA2AQCoIgECGDYBAYA6AQA==" 5 | data_start = 1_082_469_296 6 | bss_start = 1_082_392_576 7 | -------------------------------------------------------------------------------- /espflash/resources/stubs/esp32c6.toml: -------------------------------------------------------------------------------- 1 | entry = 1_082_132_164 2 | text = "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" 3 | text_start = 1_082_130_432 4 | data = "FACEQHIKgEDCCoBAGguAQOgLgEBUDIBAAgyAQD4JgECkC4BA5AuAQC4LgEDuCIBAYguAQO4IgEBMCoBAkgqAQMIKgEAaC4BAXgqAQKIJgEDSCYBAWgqAQKwOgEDCCoBAbA2AQGQOgEAuCIBAjA6AQC4IgEAuCIBALgiAQC4IgEAuCIBALgiAQC4IgEAuCIBACA2AQC4IgECKDYBAZA6AQA==" 5 | data_start = 1_082_469_296 6 | bss_start = 1_082_392_576 7 | -------------------------------------------------------------------------------- /espflash/resources/stubs/esp32h2.toml: -------------------------------------------------------------------------------- 1 | entry = 1_082_132_164 2 | text = "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" 3 | text_start = 1_082_130_432 4 | data = "FACDQHIKgEDCCoBAGguAQOgLgEBUDIBAAgyAQD4JgECkC4BA5AuAQC4LgEDuCIBAYguAQO4IgEBMCoBAkgqAQMIKgEAaC4BAXgqAQKIJgEDSCYBAWgqAQKwOgEDCCoBAbA2AQGQOgEAuCIBAjA6AQC4IgEAuCIBALgiAQC4IgEAuCIBALgiAQC4IgEAuCIBACA2AQC4IgECKDYBAZA6AQA==" 5 | data_start = 1_082_403_760 6 | bss_start = 1_082_327_040 7 | -------------------------------------------------------------------------------- /espflash/resources/stubs/esp32p4.toml: -------------------------------------------------------------------------------- 1 | entry = 1_341_196_252 2 | text = "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" 3 | text_start = 1_341_194_240 4 | data = "XAD1T1QM8U+kDPFP/AzxT9IN8U8+DvFP7A3xTyAL8U+ODfFPzg3xTxAN8U/QCvFPRA3xT9AK8U8uDPFPdAzxT6QM8U/8DPFPQAzxT4QL8U+0C/FPPAzxT5YQ8U+kDPFPXg/xT1YQ8U9sCfFPehDxT2wJ8U9sCfFPbAnxT2wJ8U9sCfFPbAnxT2wJ8U9sCfFP8g7xT2wJ8U98D/FPVhDxTw==" 5 | data_start = 1_341_533_176 6 | bss_start = 1_341_456_384 7 | -------------------------------------------------------------------------------- /espflash/resources/stubs/esp32s2.toml: -------------------------------------------------------------------------------- 1 | entry = 1_073_907_716 2 | text = "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" 3 | text_start = 1_073_905_664 4 | data = "WAD9P0uLAkDdiwJA8pACQGaMAkD+iwJAZowCQMWMAkDejQJAUY4CQPmNAkDVigJAd40CQNCNAkDojAJAdI4CQBCNAkB0jgJAy4sCQCqMAkBmjAJAxYwCQOOLAkAXiwJAN48CQKqQAkDqiQJA0ZACQOqJAkDqiQJA6okCQOqJAkDqiQJA6okCQOqJAkDqiQJA1I4CQOqJAkDJjwJAqpACQA==" 5 | data_start = 1_073_622_012 6 | bss_start = 1_073_545_216 7 | -------------------------------------------------------------------------------- /espflash/resources/stubs/esp32s3.toml: -------------------------------------------------------------------------------- 1 | entry = 1_077_381_760 2 | text = "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" 3 | text_start = 1_077_379_072 4 | data = "XADKP16ON0AzjzdAR5Q3QL2PN0BTjzdAvY83QB2QN0A6kTdArJE3QFWRN0DpjTdA0JA3QCyRN0BAkDdA0JE3QGiQN0DQkTdAIY83QH6PN0C9jzdAHZA3QDmPN0AqjjdAkJI3QA2UN0AAjTdALZQ3QACNN0AAjTdAAI03QACNN0AAjTdAAI03QACNN0AAjTdAKpI3QACNN0AlkzdADZQ3QAQInwAAAAAAAAAYAQQIBQAAAAAAAAAIAQQIBgAAAAAAAAAAAQQIIQAAAAAAIAAAEQQI3AAAAAAAIAAAEQQIDAAAAAAAIAAAAQQIEgAAAAAAIAAAESAoDAAQAQAA" 5 | data_start = 1_070_279_676 6 | bss_start = 1_070_202_880 7 | -------------------------------------------------------------------------------- /espflash/src/cli/monitor/external_processors.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::needless_doctest_main)] 2 | //! External processor support 3 | //! 4 | //! Via the command line argument `--processors` you can instruct espflash to 5 | //! run external executables to pre-process the logs received from the target. 6 | //! Multiple processors are supported by separating them via `,`. Processors are 7 | //! executed in the specified order. 8 | //! 9 | //! You can use full-qualified paths or run an executable which is already in 10 | //! the search path. 11 | //! 12 | //! A processors reads from stdin and output to stdout. Be aware this runs 13 | //! before further processing by espflash. i.e. addresses are not resolved and 14 | //! when using `defmt` you will see encoded data. 15 | //! 16 | //! Additionally be aware that you might receive chunked data which is not 17 | //! always split at valid UTF character boundaries. 18 | //! 19 | //! The executable will get the path of the ELF file as the first argument if 20 | //! available. 21 | //! 22 | //! Example processor which turns some letters into uppercase 23 | //! ```rust,no_run 24 | //! use std::io::{Read, Write, stdin, stdout}; 25 | //! 26 | //! fn main() { 27 | //! let args: Vec = std::env::args().collect(); 28 | //! println!("ELF file: {:?}", args[1]); 29 | //! 30 | //! let mut buf = [0u8; 1024]; 31 | //! loop { 32 | //! if let Ok(len) = stdin().read(&mut buf) { 33 | //! for b in &mut buf[..len] { 34 | //! *b = if b"abdfeo".contains(b) { 35 | //! b.to_ascii_uppercase() 36 | //! } else { 37 | //! *b 38 | //! }; 39 | //! } 40 | //! 41 | //! stdout().write(&buf[..len]).unwrap(); 42 | //! stdout().flush().unwrap(); 43 | //! } else { 44 | //! // ignored 45 | //! } 46 | //! } 47 | //! } 48 | //! ``` 49 | 50 | use std::{ 51 | fmt::Display, 52 | io::{Read, Write}, 53 | path::PathBuf, 54 | process::{Child, ChildStdin, Stdio}, 55 | sync::mpsc, 56 | }; 57 | 58 | use miette::Diagnostic; 59 | 60 | #[derive(Debug)] 61 | pub struct Error { 62 | executable: String, 63 | } 64 | 65 | impl Display for Error { 66 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 67 | write!(f, "Failed to launch '{}'", self.executable) 68 | } 69 | } 70 | 71 | impl std::error::Error for Error {} 72 | 73 | impl Diagnostic for Error {} 74 | 75 | #[derive(Debug)] 76 | struct Processor { 77 | rx: mpsc::Receiver, 78 | stdin: ChildStdin, 79 | child: Child, 80 | } 81 | 82 | impl Processor { 83 | pub fn new(child: Child) -> Self { 84 | let mut child = child; 85 | let (tx, rx) = mpsc::channel::(); 86 | 87 | let mut stdout = child.stdout.take().unwrap(); 88 | let stdin = child.stdin.take().unwrap(); 89 | 90 | std::thread::spawn(move || { 91 | let mut buffer = [0u8; 1024]; 92 | loop { 93 | if let Ok(len) = stdout.read(&mut buffer) { 94 | for b in &buffer[..len] { 95 | if tx.send(*b).is_err() { 96 | break; 97 | } 98 | } 99 | } 100 | } 101 | }); 102 | 103 | Self { rx, stdin, child } 104 | } 105 | 106 | pub fn try_receive(&mut self) -> Vec { 107 | let mut res = Vec::new(); 108 | while let Ok(b) = self.rx.try_recv() { 109 | res.push(b); 110 | } 111 | res 112 | } 113 | 114 | pub fn send(&mut self, data: Vec) { 115 | let _ignored = self.stdin.write(&data).ok(); 116 | } 117 | } 118 | 119 | impl Drop for Processor { 120 | fn drop(&mut self) { 121 | self.child.kill().unwrap(); 122 | } 123 | } 124 | 125 | #[derive(Debug)] 126 | pub struct ExternalProcessors { 127 | processors: Vec, 128 | } 129 | 130 | impl ExternalProcessors { 131 | pub fn new(processors: Option, elf: Option) -> Result { 132 | let mut args = Vec::new(); 133 | 134 | if let Some(elf) = elf { 135 | args.push(elf.as_os_str().to_str().unwrap().to_string()); 136 | }; 137 | 138 | let mut spawned = Vec::new(); 139 | if let Some(processors) = processors { 140 | for processor in processors.split(",") { 141 | let processor = std::process::Command::new(processor) 142 | .args(args.clone()) 143 | .stdin(Stdio::piped()) 144 | .stdout(Stdio::piped()) 145 | .stderr(Stdio::inherit()) 146 | .spawn() 147 | .map_err(|_| Error { 148 | executable: processor.to_string(), 149 | })?; 150 | spawned.push(Processor::new(processor)); 151 | } 152 | } 153 | 154 | Ok(Self { 155 | processors: spawned, 156 | }) 157 | } 158 | 159 | pub fn process(&mut self, read: &[u8]) -> Vec { 160 | let mut buffer = Vec::new(); 161 | buffer.extend_from_slice(read); 162 | 163 | for processor in &mut self.processors { 164 | processor.send(buffer); 165 | buffer = processor.try_receive(); 166 | } 167 | 168 | buffer 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /espflash/src/cli/monitor/line_endings.rs: -------------------------------------------------------------------------------- 1 | // Adapted from: https://github.com/derekdreery/normalize-line-endings 2 | 3 | struct Normalized { 4 | iter: I, 5 | prev_was_cr: bool, 6 | peeked: Option, 7 | } 8 | 9 | impl Iterator for Normalized 10 | where 11 | I: Iterator, 12 | { 13 | type Item = u8; 14 | 15 | fn next(&mut self) -> Option { 16 | if let Some(peeked) = self.peeked.take() { 17 | return Some(peeked); 18 | } 19 | 20 | match self.iter.next() { 21 | Some(b'\n') if !self.prev_was_cr => { 22 | self.peeked = Some(b'\n'); 23 | self.prev_was_cr = false; 24 | Some(b'\r') 25 | } 26 | Some(b'\r') => { 27 | self.prev_was_cr = true; 28 | Some(b'\r') 29 | } 30 | any => { 31 | self.prev_was_cr = false; 32 | any 33 | } 34 | } 35 | } 36 | } 37 | 38 | pub(crate) fn normalized(iter: impl Iterator) -> impl Iterator { 39 | Normalized { 40 | iter, 41 | prev_was_cr: false, 42 | peeked: None, 43 | } 44 | } 45 | 46 | #[cfg(test)] 47 | mod tests { 48 | use std::iter::FromIterator; 49 | 50 | use super::*; 51 | 52 | #[test] 53 | fn test_normalized() { 54 | let input = b"This is a string \n with \n some \n\r\n random newlines\r\n\n"; 55 | assert_eq!( 56 | &Vec::from_iter(normalized(input.iter().copied())), 57 | b"This is a string \r\n with \r\n some \r\n\r\n random newlines\r\n\r\n" 58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /espflash/src/cli/monitor/parser/serial.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use crate::cli::monitor::parser::InputParser; 4 | 5 | #[derive(Debug)] 6 | pub struct Serial; 7 | 8 | impl InputParser for Serial { 9 | fn feed(&mut self, bytes: &[u8], out: &mut dyn Write) { 10 | out.write_all(bytes).unwrap(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /espflash/src/cli/monitor/symbols.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | 3 | use addr2line::{ 4 | Context, 5 | LookupResult, 6 | gimli::{self, Dwarf, EndianSlice, LittleEndian, SectionId}, 7 | }; 8 | use object::{Object, ObjectSection, ObjectSegment, ObjectSymbol, read::File}; 9 | 10 | // Wrapper around addr2line that allows to look up function names and 11 | // locations from a given address. 12 | pub(crate) struct Symbols<'sym> { 13 | object: File<'sym, &'sym [u8]>, 14 | ctx: Context>, 15 | } 16 | 17 | impl<'sym> Symbols<'sym> { 18 | pub fn try_from(bytes: &'sym [u8]) -> Result> { 19 | let object = File::parse(bytes)?; 20 | let dwarf = Dwarf::load( 21 | |id: SectionId| -> Result, gimli::Error> { 22 | let data = object 23 | .section_by_name(id.name()) 24 | .and_then(|section| section.data().ok()) 25 | .unwrap_or(&[][..]); 26 | Ok(EndianSlice::new(data, LittleEndian)) 27 | }, 28 | )?; 29 | 30 | let ctx = Context::from_dwarf(dwarf)?; 31 | 32 | Ok(Self { object, ctx }) 33 | } 34 | 35 | /// Returns the name of the function at the given address, if one can be 36 | /// found. 37 | pub fn name(&self, addr: u64) -> Option { 38 | // No need to try an address not contained in any segment: 39 | if !self.object.segments().any(|segment| { 40 | (segment.address()..(segment.address() + segment.size())).contains(&addr) 41 | }) { 42 | return None; 43 | } 44 | 45 | // The basic steps here are: 46 | // 1. Find which frame `addr` is in 47 | // 2. Look up and demangle the function name 48 | // 3. If no function name is found, try to look it up in the object file 49 | // directly 50 | // 4. Return a demangled function name, if one was found 51 | let mut frames = match self.ctx.find_frames(addr) { 52 | LookupResult::Output(result) => result.unwrap(), 53 | LookupResult::Load { .. } => unimplemented!(), 54 | }; 55 | 56 | frames 57 | .next() 58 | .ok() 59 | .flatten() 60 | .and_then(|frame| { 61 | frame 62 | .function 63 | .and_then(|name| name.demangle().map(|s| s.into_owned()).ok()) 64 | }) 65 | .or_else(|| { 66 | // Don't use `symbol_map().get(addr)` - it's documentation says "Get the symbol 67 | // before the given address." which might be totally wrong 68 | let symbol = self.object.symbols().find(|symbol| { 69 | (symbol.address()..=(symbol.address() + symbol.size())).contains(&addr) 70 | }); 71 | 72 | if let Some(symbol) = symbol { 73 | match symbol.name() { 74 | Ok(name) if !name.is_empty() => Some( 75 | addr2line::demangle_auto(std::borrow::Cow::Borrowed(name), None) 76 | .to_string(), 77 | ), 78 | _ => None, 79 | } 80 | } else { 81 | None 82 | } 83 | }) 84 | } 85 | 86 | /// Returns the file name and line number of the function at the given 87 | /// address, if one can be. 88 | pub fn location(&self, addr: u64) -> Option<(String, u32)> { 89 | // Find the location which `addr` is in. If we can dedetermine a file name and 90 | // line number for this function we will return them both in a tuple. 91 | self.ctx.find_location(addr).ok()?.map(|location| { 92 | let file = location.file.map(|f| f.to_string()); 93 | let line = location.line; 94 | 95 | match (file, line) { 96 | (Some(file), Some(line)) => Some((file, line)), 97 | _ => None, 98 | } 99 | })? 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /espflash/src/flasher/stubs.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | use base64::{Engine as _, engine::general_purpose}; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | use crate::targets::Chip; 7 | 8 | /// Flash stub object (deserialized from TOML, converted from JSON as used by 9 | /// `esptool.py`) 10 | #[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] 11 | pub struct FlashStub { 12 | /// Entry point (address) 13 | entry: u32, 14 | /// Text (base64 encoded) 15 | text: String, 16 | /// Start of text section address 17 | text_start: u32, 18 | /// Data 19 | data: String, 20 | /// Start of data section address 21 | data_start: u32, 22 | } 23 | 24 | pub(crate) const CHIP_DETECT_MAGIC_REG_ADDR: u32 = 0x40001000; 25 | pub(crate) const DEFAULT_TIMEOUT: Duration = Duration::from_secs(3); 26 | pub(crate) const EXPECTED_STUB_HANDSHAKE: &str = "OHAI"; 27 | 28 | pub(crate) const FLASH_SECTOR_SIZE: usize = 0x1000; 29 | pub(crate) const FLASH_WRITE_SIZE: usize = 0x400; 30 | 31 | // Include stub objects in binary 32 | const STUB_32: &str = include_str!("../../resources/stubs/esp32.toml"); 33 | const STUB_32C2: &str = include_str!("../../resources/stubs/esp32c2.toml"); 34 | const STUB_32C3: &str = include_str!("../../resources/stubs/esp32c3.toml"); 35 | const STUB_32C5: &str = include_str!("../../resources/stubs/esp32c5.toml"); 36 | const STUB_32C6: &str = include_str!("../../resources/stubs/esp32c6.toml"); 37 | const STUB_32H2: &str = include_str!("../../resources/stubs/esp32h2.toml"); 38 | const STUB_32P4: &str = include_str!("../../resources/stubs/esp32p4.toml"); 39 | const STUB_32S2: &str = include_str!("../../resources/stubs/esp32s2.toml"); 40 | const STUB_32S3: &str = include_str!("../../resources/stubs/esp32s3.toml"); 41 | 42 | impl FlashStub { 43 | /// Fetch flash stub for the provided chip 44 | pub fn get(chip: Chip) -> FlashStub { 45 | let s = match chip { 46 | Chip::Esp32 => STUB_32, 47 | Chip::Esp32c2 => STUB_32C2, 48 | Chip::Esp32c3 => STUB_32C3, 49 | Chip::Esp32c5 => STUB_32C5, 50 | Chip::Esp32c6 => STUB_32C6, 51 | Chip::Esp32h2 => STUB_32H2, 52 | Chip::Esp32p4 => STUB_32P4, 53 | Chip::Esp32s2 => STUB_32S2, 54 | Chip::Esp32s3 => STUB_32S3, 55 | }; 56 | 57 | let stub: FlashStub = toml::from_str(s).unwrap(); 58 | 59 | stub 60 | } 61 | 62 | /// Fetch stub entry point 63 | pub fn entry(&self) -> u32 { 64 | self.entry 65 | } 66 | 67 | /// Fetch text start address and bytes 68 | pub fn text(&self) -> (u32, Vec) { 69 | let v = general_purpose::STANDARD.decode(&self.text).unwrap(); 70 | (self.text_start, v) 71 | } 72 | 73 | /// Fetch data start address and bytes 74 | pub fn data(&self) -> (u32, Vec) { 75 | let v = general_purpose::STANDARD.decode(&self.data).unwrap(); 76 | (self.data_start, v) 77 | } 78 | } 79 | 80 | #[cfg(test)] 81 | mod tests { 82 | use strum::IntoEnumIterator; 83 | 84 | use super::FlashStub; 85 | use crate::targets::Chip; 86 | 87 | #[test] 88 | fn check_stub_encodings() { 89 | for c in Chip::iter() { 90 | // Stub must be valid JSON: 91 | let s = FlashStub::get(c); 92 | 93 | // Data decoded from b64 94 | let _ = s.text(); 95 | let _ = s.data(); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /espflash/src/image_format/metadata.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, error::Error}; 2 | 3 | use object::{File, Object, ObjectSection, ObjectSymbol}; 4 | 5 | #[derive(Debug, Clone)] 6 | pub struct Metadata { 7 | symbols: HashMap>, 8 | } 9 | 10 | impl Metadata { 11 | fn empty() -> Self { 12 | Self { 13 | symbols: HashMap::new(), 14 | } 15 | } 16 | 17 | pub fn from_bytes(bytes: Option<&[u8]>) -> Self { 18 | match Self::try_from(bytes) { 19 | Ok(metadata) => metadata, 20 | Err(_) => Self::empty(), 21 | } 22 | } 23 | 24 | pub fn try_from(bytes: Option<&[u8]>) -> Result> { 25 | const METADATA_SECTION: &str = ".espressif.metadata"; 26 | 27 | let Some(bytes) = bytes else { 28 | return Ok(Self::empty()); 29 | }; 30 | 31 | let object = File::parse(bytes)?; 32 | if object.section_by_name(METADATA_SECTION).is_none() { 33 | return Ok(Self::empty()); 34 | } 35 | 36 | let mut this = Self::empty(); 37 | for symbol in object.symbols() { 38 | let Some(sym_section_idx) = symbol.section_index() else { 39 | continue; 40 | }; 41 | let sym_section = object.section_by_index(sym_section_idx)?; 42 | if sym_section.name().ok() != Some(METADATA_SECTION) { 43 | // Skip symbols that are not in the metadata section. 44 | continue; 45 | } 46 | 47 | let name = symbol.name()?.to_string(); 48 | let data = sym_section 49 | .data_range(symbol.address(), symbol.size())? 50 | .map(|b| b.to_vec()); 51 | 52 | if let Some(data) = data { 53 | this.symbols.insert(name, data); 54 | } 55 | } 56 | 57 | Ok(this) 58 | } 59 | 60 | fn read_string<'f>(&'f self, name: &str) -> Option<&'f str> { 61 | self.symbols 62 | .get(name) 63 | .and_then(|data| std::str::from_utf8(data).ok()) 64 | } 65 | 66 | pub fn chip_name(&self) -> Option<&str> { 67 | self.read_string("build_info.CHIP_NAME") 68 | } 69 | 70 | pub fn log_format(&self) -> Option<&str> { 71 | self.read_string("espflash.LOG_FORMAT") 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /espflash/src/image_format/mod.rs: -------------------------------------------------------------------------------- 1 | //! Binary application image formats 2 | 3 | use std::{ 4 | borrow::Cow, 5 | cmp::Ordering, 6 | fmt::{Debug, Formatter}, 7 | mem::take, 8 | ops::AddAssign, 9 | }; 10 | 11 | use object::{ 12 | Endianness, 13 | Object as _, 14 | ObjectSection as _, 15 | elf::SHT_PROGBITS, 16 | read::elf::{ElfFile32 as ElfFile, SectionHeader}, 17 | }; 18 | 19 | pub use self::{esp_idf::IdfBootloaderFormat, metadata::Metadata}; 20 | use crate::targets::Chip; 21 | 22 | mod esp_idf; 23 | mod metadata; 24 | 25 | /// A segment of code from the source ELF 26 | #[derive(Default, Clone, Eq)] 27 | pub struct Segment<'a> { 28 | /// Base address of the code segment 29 | pub addr: u32, 30 | /// Segment data 31 | pub data: Cow<'a, [u8]>, 32 | } 33 | 34 | impl<'a> Segment<'a> { 35 | pub fn new(addr: u32, data: &'a [u8]) -> Self { 36 | // Do not pad the data here, as it might result in overlapping segments 37 | // in the ELF file. The padding should be done after merging adjacent segments. 38 | Segment { 39 | addr, 40 | data: Cow::Borrowed(data), 41 | } 42 | } 43 | 44 | /// Split of the first `count` bytes into a new segment, adjusting the 45 | /// remaining segment as needed 46 | pub fn split_off(&mut self, count: usize) -> Self { 47 | if count < self.data.len() { 48 | let (head, tail) = match take(&mut self.data) { 49 | Cow::Borrowed(data) => { 50 | let (head, tail) = data.split_at(count); 51 | (Cow::Borrowed(head), Cow::Borrowed(tail)) 52 | } 53 | Cow::Owned(mut data) => { 54 | let tail = data.split_off(count); 55 | (Cow::Owned(data), Cow::Owned(tail)) 56 | } 57 | }; 58 | let new = Segment { 59 | addr: self.addr, 60 | data: head, 61 | }; 62 | self.addr += count as u32; 63 | self.data = tail; 64 | new 65 | } else { 66 | let new = self.clone(); 67 | self.addr += self.size(); 68 | self.data = Cow::Borrowed(&[]); 69 | new 70 | } 71 | } 72 | 73 | /// Return the size of the segment 74 | pub fn size(&self) -> u32 { 75 | self.data.len() as u32 76 | } 77 | 78 | /// Return the data of the segment 79 | pub fn data(&self) -> &[u8] { 80 | self.data.as_ref() 81 | } 82 | 83 | /// Pad the segment to the given alignment 84 | pub fn pad_align(&mut self, align: usize) { 85 | let padding = (align - self.data.len() % align) % align; 86 | if padding > 0 { 87 | let mut data = take(&mut self.data).into_owned(); 88 | data.extend_from_slice(&[0; 4][0..padding]); 89 | self.data = Cow::Owned(data); 90 | } 91 | } 92 | 93 | /// Borrow the segment for the given lifetime 94 | pub fn borrow<'b>(&'b self) -> Segment<'b> 95 | where 96 | 'a: 'b, 97 | { 98 | Segment { 99 | addr: self.addr, 100 | data: Cow::Borrowed(self.data.as_ref()), 101 | } 102 | } 103 | } 104 | 105 | impl AddAssign<&'_ [u8]> for Segment<'_> { 106 | fn add_assign(&mut self, rhs: &'_ [u8]) { 107 | let mut data = take(&mut self.data).into_owned(); 108 | data.extend_from_slice(rhs); 109 | self.data = Cow::Owned(data); 110 | } 111 | } 112 | 113 | #[allow(clippy::suspicious_op_assign_impl)] 114 | impl AddAssign<&'_ Segment<'_>> for Segment<'_> { 115 | fn add_assign(&mut self, rhs: &'_ Segment<'_>) { 116 | let mut data = take(&mut self.data).into_owned(); 117 | // Pad or truncate: 118 | data.resize((rhs.addr - self.addr) as usize, 0); 119 | data.extend_from_slice(rhs.data()); 120 | self.data = Cow::Owned(data); 121 | } 122 | } 123 | 124 | impl Debug for Segment<'_> { 125 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 126 | f.debug_struct("CodeSegment") 127 | .field("addr", &self.addr) 128 | .field("size", &self.size()) 129 | .finish() 130 | } 131 | } 132 | 133 | impl PartialEq for Segment<'_> { 134 | fn eq(&self, other: &Self) -> bool { 135 | self.addr.eq(&other.addr) 136 | } 137 | } 138 | 139 | impl PartialOrd for Segment<'_> { 140 | fn partial_cmp(&self, other: &Self) -> Option { 141 | Some(self.cmp(other)) 142 | } 143 | } 144 | 145 | impl Ord for Segment<'_> { 146 | fn cmp(&self, other: &Self) -> Ordering { 147 | self.addr.cmp(&other.addr) 148 | } 149 | } 150 | 151 | /// Returns an iterator over all RAM segments for a given chip and ELF file. 152 | pub(crate) fn ram_segments<'a>( 153 | chip: Chip, 154 | elf: &'a ElfFile<'a>, 155 | ) -> Box> + 'a> { 156 | Box::new(segments(elf).filter(move |segment| !chip.into_target().addr_is_flash(segment.addr))) 157 | } 158 | 159 | /// Returns an iterator over all ROM segments for a given chip and ELF file. 160 | pub(crate) fn rom_segments<'a>( 161 | chip: Chip, 162 | elf: &'a ElfFile<'a>, 163 | ) -> Box> + 'a> { 164 | Box::new(segments(elf).filter(move |segment| chip.into_target().addr_is_flash(segment.addr))) 165 | } 166 | 167 | fn segments<'a>(elf: &'a ElfFile<'a>) -> Box> + 'a> { 168 | Box::new( 169 | elf.sections() 170 | .filter(|section| { 171 | let header = section.elf_section_header(); 172 | 173 | section.size() > 0 174 | && header.sh_type(Endianness::Little) == SHT_PROGBITS 175 | && header.sh_offset.get(Endianness::Little) > 0 176 | && section.address() > 0 177 | }) 178 | .flat_map(move |section| match section.data() { 179 | Ok(data) => Some(Segment::new(section.address() as u32, data)), 180 | _ => None, 181 | }), 182 | ) 183 | } 184 | -------------------------------------------------------------------------------- /espflash/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A library and application for flashing Espressif devices over Serial 2 | //! 3 | //! ## As an application 4 | //! 5 | //! [espflash] can be installed using `cargo install`, and additionally supports installation via [cargo-binstall]: 6 | //! 7 | //! ```bash 8 | //! $ cargo install espflash 9 | //! $ cargo binstall espflash 10 | //! ``` 11 | //! 12 | //! ## As a library 13 | //! 14 | //! [espflash] can also be used as a library: 15 | //! 16 | //! ```toml 17 | //! espflash = { version = "4.0.0-dev", default-features = false } 18 | //! ``` 19 | //! 20 | //! We add `default-features` here to disable the `cli` feature, which is 21 | //! enabled by default. Its important to note that the cli module does not 22 | //! provide SemVer guarantees. You likely will not need any of these types or 23 | //! functions in your application so there's no use pulling in the extra 24 | //! dependencies. 25 | //! 26 | //! [espflash]: https://crates.io/crates/espflash 27 | //! [cargo-binstall]: https://github.com/cargo-bins/cargo-binstall 28 | 29 | #![cfg_attr(docsrs, feature(doc_cfg))] 30 | #![deny(missing_debug_implementations, rust_2018_idioms)] 31 | 32 | pub use self::{error::Error, image_format::Segment}; 33 | 34 | #[cfg(feature = "serialport")] 35 | #[cfg_attr(docsrs, doc(cfg(feature = "serialport")))] 36 | pub mod connection; 37 | pub mod flasher; 38 | pub mod image_format; 39 | pub mod targets; 40 | 41 | mod error; 42 | 43 | // Command-line interface 44 | #[cfg(feature = "cli")] 45 | pub mod cli; 46 | 47 | // Logging utilities 48 | #[cfg(feature = "cli")] 49 | pub mod logging { 50 | use env_logger::{Builder, Env}; 51 | use log::LevelFilter; 52 | 53 | /// Initialize the logger with the given [LevelFilter] 54 | pub fn initialize_logger(filter: LevelFilter) { 55 | Builder::from_env(Env::default().default_filter_or(filter.as_str())) 56 | .format_target(false) 57 | .init(); 58 | } 59 | } 60 | 61 | // Check for updates 62 | #[cfg(feature = "cli")] 63 | pub mod update { 64 | use std::time::Duration; 65 | 66 | use log::info; 67 | use update_informer::{Check, registry::Crates}; 68 | 69 | pub fn check_for_update(name: &str, version: &str) { 70 | // By setting the interval to 0 seconds we invalidate the cache with each 71 | // invocation and ensure we're getting up-to-date results 72 | let informer = update_informer::new(Crates, name, version).interval(Duration::from_secs(0)); 73 | 74 | if let Some(version) = informer.check_version().ok().flatten() { 75 | info!("🚀 A new version of {name} is available: {version}"); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /espflash/src/targets/efuse/esp32.rs: -------------------------------------------------------------------------------- 1 | //! This file was automatically generated, please do not edit it manually! 2 | //! 3 | //! Generated: 2025-05-30 12:24 4 | //! Version: 369d2d860d34e777c0f7d545a7dfc3c4 5 | 6 | #![allow(unused)] 7 | 8 | use super::EfuseField; 9 | 10 | /// Total size in bytes of each block 11 | pub(crate) const BLOCK_SIZES: &[u32] = &[28, 32, 32, 32]; 12 | 13 | /// Efuse write disable mask 14 | pub(crate) const WR_DIS: EfuseField = EfuseField::new(0, 0, 0, 16); 15 | /// Disable reading from BlOCK1-3 16 | pub(crate) const RD_DIS: EfuseField = EfuseField::new(0, 0, 16, 4); 17 | /// Flash encryption is enabled if this field has an odd number of bits set 18 | pub(crate) const FLASH_CRYPT_CNT: EfuseField = EfuseField::new(0, 0, 20, 7); 19 | /// Disable UART download mode. Valid for ESP32 V3 and newer; only 20 | pub(crate) const UART_DOWNLOAD_DIS: EfuseField = EfuseField::new(0, 0, 27, 1); 21 | /// reserved 22 | pub(crate) const RESERVED_0_28: EfuseField = EfuseField::new(0, 0, 28, 4); 23 | /// MAC address 24 | pub(crate) const MAC0: EfuseField = EfuseField::new(0, 1, 32, 32); 25 | /// MAC address 26 | pub(crate) const MAC1: EfuseField = EfuseField::new(0, 2, 64, 16); 27 | /// CRC8 for MAC address 28 | pub(crate) const MAC_CRC: EfuseField = EfuseField::new(0, 2, 80, 8); 29 | /// Reserved; it was created by set_missed_fields_in_regs func 30 | pub(crate) const RESERVE_0_88: EfuseField = EfuseField::new(0, 2, 88, 8); 31 | /// Disables APP CPU 32 | pub(crate) const DISABLE_APP_CPU: EfuseField = EfuseField::new(0, 3, 96, 1); 33 | /// Disables Bluetooth 34 | pub(crate) const DISABLE_BT: EfuseField = EfuseField::new(0, 3, 97, 1); 35 | /// Chip package identifier #4bit 36 | pub(crate) const CHIP_PACKAGE_4BIT: EfuseField = EfuseField::new(0, 3, 98, 1); 37 | /// Disables cache 38 | pub(crate) const DIS_CACHE: EfuseField = EfuseField::new(0, 3, 99, 1); 39 | /// read for SPI_pad_config_hd 40 | pub(crate) const SPI_PAD_CONFIG_HD: EfuseField = EfuseField::new(0, 3, 100, 5); 41 | /// Chip package identifier 42 | pub(crate) const CHIP_PACKAGE: EfuseField = EfuseField::new(0, 3, 105, 3); 43 | /// If set alongside EFUSE_RD_CHIP_CPU_FREQ_RATED; the ESP32's max CPU frequency 44 | /// is rated for 160MHz. 240MHz otherwise 45 | pub(crate) const CHIP_CPU_FREQ_LOW: EfuseField = EfuseField::new(0, 3, 108, 1); 46 | /// If set; the ESP32's maximum CPU frequency has been rated 47 | pub(crate) const CHIP_CPU_FREQ_RATED: EfuseField = EfuseField::new(0, 3, 109, 1); 48 | /// BLOCK3 partially served for ADC calibration data 49 | pub(crate) const BLK3_PART_RESERVE: EfuseField = EfuseField::new(0, 3, 110, 1); 50 | /// bit is set to 1 for rev1 silicon 51 | pub(crate) const CHIP_VER_REV1: EfuseField = EfuseField::new(0, 3, 111, 1); 52 | /// Reserved; it was created by set_missed_fields_in_regs func 53 | pub(crate) const RESERVE_0_112: EfuseField = EfuseField::new(0, 3, 112, 16); 54 | /// 8MHz clock freq override 55 | pub(crate) const CLK8M_FREQ: EfuseField = EfuseField::new(0, 4, 128, 8); 56 | /// True ADC reference voltage 57 | pub(crate) const ADC_VREF: EfuseField = EfuseField::new(0, 4, 136, 5); 58 | /// Reserved; it was created by set_missed_fields_in_regs func 59 | pub(crate) const RESERVE_0_141: EfuseField = EfuseField::new(0, 4, 141, 1); 60 | /// read for XPD_SDIO_REG 61 | pub(crate) const XPD_SDIO_REG: EfuseField = EfuseField::new(0, 4, 142, 1); 62 | /// If XPD_SDIO_FORCE & XPD_SDIO_REG 63 | pub(crate) const XPD_SDIO_TIEH: EfuseField = EfuseField::new(0, 4, 143, 1); 64 | /// Ignore MTDI pin (GPIO12) for VDD_SDIO on reset 65 | pub(crate) const XPD_SDIO_FORCE: EfuseField = EfuseField::new(0, 4, 144, 1); 66 | /// Reserved; it was created by set_missed_fields_in_regs func 67 | pub(crate) const RESERVE_0_145: EfuseField = EfuseField::new(0, 4, 145, 15); 68 | /// Override SD_CLK pad (GPIO6/SPICLK) 69 | pub(crate) const SPI_PAD_CONFIG_CLK: EfuseField = EfuseField::new(0, 5, 160, 5); 70 | /// Override SD_DATA_0 pad (GPIO7/SPIQ) 71 | pub(crate) const SPI_PAD_CONFIG_Q: EfuseField = EfuseField::new(0, 5, 165, 5); 72 | /// Override SD_DATA_1 pad (GPIO8/SPID) 73 | pub(crate) const SPI_PAD_CONFIG_D: EfuseField = EfuseField::new(0, 5, 170, 5); 74 | /// Override SD_CMD pad (GPIO11/SPICS0) 75 | pub(crate) const SPI_PAD_CONFIG_CS0: EfuseField = EfuseField::new(0, 5, 175, 5); 76 | /// 77 | pub(crate) const CHIP_VER_REV2: EfuseField = EfuseField::new(0, 5, 180, 1); 78 | /// Reserved; it was created by set_missed_fields_in_regs func 79 | pub(crate) const RESERVE_0_181: EfuseField = EfuseField::new(0, 5, 181, 1); 80 | /// This field stores the voltage level for CPU to run at 240 MHz; or for 81 | /// flash/PSRAM to run at 80 MHz.0x0: level 7; 0x1: level 6; 0x2: level 5; 0x3: 82 | /// level 4. (RO) 83 | pub(crate) const VOL_LEVEL_HP_INV: EfuseField = EfuseField::new(0, 5, 182, 2); 84 | /// 85 | pub(crate) const WAFER_VERSION_MINOR: EfuseField = EfuseField::new(0, 5, 184, 2); 86 | /// Reserved; it was created by set_missed_fields_in_regs func 87 | pub(crate) const RESERVE_0_186: EfuseField = EfuseField::new(0, 5, 186, 2); 88 | /// Flash encryption config (key tweak bits) 89 | pub(crate) const FLASH_CRYPT_CONFIG: EfuseField = EfuseField::new(0, 5, 188, 4); 90 | /// Efuse variable block length scheme 91 | pub(crate) const CODING_SCHEME: EfuseField = EfuseField::new(0, 6, 192, 2); 92 | /// Disable ROM BASIC interpreter fallback 93 | pub(crate) const CONSOLE_DEBUG_DISABLE: EfuseField = EfuseField::new(0, 6, 194, 1); 94 | /// 95 | pub(crate) const DISABLE_SDIO_HOST: EfuseField = EfuseField::new(0, 6, 195, 1); 96 | /// Secure boot V1 is enabled for bootloader image 97 | pub(crate) const ABS_DONE_0: EfuseField = EfuseField::new(0, 6, 196, 1); 98 | /// Secure boot V2 is enabled for bootloader image 99 | pub(crate) const ABS_DONE_1: EfuseField = EfuseField::new(0, 6, 197, 1); 100 | /// Disable JTAG 101 | pub(crate) const JTAG_DISABLE: EfuseField = EfuseField::new(0, 6, 198, 1); 102 | /// Disable flash encryption in UART bootloader 103 | pub(crate) const DISABLE_DL_ENCRYPT: EfuseField = EfuseField::new(0, 6, 199, 1); 104 | /// Disable flash decryption in UART bootloader 105 | pub(crate) const DISABLE_DL_DECRYPT: EfuseField = EfuseField::new(0, 6, 200, 1); 106 | /// Disable flash cache in UART bootloader 107 | pub(crate) const DISABLE_DL_CACHE: EfuseField = EfuseField::new(0, 6, 201, 1); 108 | /// Usage of efuse block 3 (reserved) 109 | pub(crate) const KEY_STATUS: EfuseField = EfuseField::new(0, 6, 202, 1); 110 | /// Reserved; it was created by set_missed_fields_in_regs func 111 | pub(crate) const RESERVE_0_203: EfuseField = EfuseField::new(0, 6, 203, 21); 112 | /// Flash encryption key 113 | pub(crate) const BLOCK1: EfuseField = EfuseField::new(1, 0, 0, 256); 114 | /// Security boot key 115 | pub(crate) const BLOCK2: EfuseField = EfuseField::new(2, 0, 0, 256); 116 | /// CRC8 for custom MAC address 117 | pub(crate) const CUSTOM_MAC_CRC: EfuseField = EfuseField::new(3, 0, 0, 8); 118 | /// Custom MAC address 119 | pub(crate) const CUSTOM_MAC: EfuseField = EfuseField::new(3, 0, 8, 48); 120 | /// reserved 121 | pub(crate) const RESERVED_3_56: EfuseField = EfuseField::new(3, 1, 56, 8); 122 | /// read for BLOCK3 123 | pub(crate) const BLK3_RESERVED_2: EfuseField = EfuseField::new(3, 2, 64, 32); 124 | /// ADC1 Two Point calibration low point. Only valid if 125 | /// EFUSE_RD_BLK3_PART_RESERVE 126 | pub(crate) const ADC1_TP_LOW: EfuseField = EfuseField::new(3, 3, 96, 7); 127 | /// ADC1 Two Point calibration high point. Only valid if 128 | /// EFUSE_RD_BLK3_PART_RESERVE 129 | pub(crate) const ADC1_TP_HIGH: EfuseField = EfuseField::new(3, 3, 103, 9); 130 | /// ADC2 Two Point calibration low point. Only valid if 131 | /// EFUSE_RD_BLK3_PART_RESERVE 132 | pub(crate) const ADC2_TP_LOW: EfuseField = EfuseField::new(3, 3, 112, 7); 133 | /// ADC2 Two Point calibration high point. Only valid if 134 | /// EFUSE_RD_BLK3_PART_RESERVE 135 | pub(crate) const ADC2_TP_HIGH: EfuseField = EfuseField::new(3, 3, 119, 9); 136 | /// Secure version for anti-rollback 137 | pub(crate) const SECURE_VERSION: EfuseField = EfuseField::new(3, 4, 128, 32); 138 | /// reserved 139 | pub(crate) const RESERVED_3_160: EfuseField = EfuseField::new(3, 5, 160, 24); 140 | /// Version of the MAC field 141 | pub(crate) const MAC_VERSION: EfuseField = EfuseField::new(3, 5, 184, 8); 142 | /// read for BLOCK3 143 | pub(crate) const BLK3_RESERVED_6: EfuseField = EfuseField::new(3, 6, 192, 32); 144 | /// read for BLOCK3 145 | pub(crate) const BLK3_RESERVED_7: EfuseField = EfuseField::new(3, 7, 224, 32); 146 | -------------------------------------------------------------------------------- /espflash/src/targets/efuse/esp32c2.rs: -------------------------------------------------------------------------------- 1 | //! This file was automatically generated, please do not edit it manually! 2 | //! 3 | //! Generated: 2025-05-30 12:24 4 | //! Version: 897499b0349a608b895d467abbcf006b 5 | 6 | #![allow(unused)] 7 | 8 | use super::EfuseField; 9 | 10 | /// Total size in bytes of each block 11 | pub(crate) const BLOCK_SIZES: &[u32] = &[8, 11, 32, 32]; 12 | 13 | /// Disable programming of individual eFuses 14 | pub(crate) const WR_DIS: EfuseField = EfuseField::new(0, 0, 0, 8); 15 | /// 16 | pub(crate) const RESERVED_0_8: EfuseField = EfuseField::new(0, 0, 8, 24); 17 | /// Disable reading from BlOCK3 18 | pub(crate) const RD_DIS: EfuseField = EfuseField::new(0, 1, 32, 2); 19 | /// RTC watchdog timeout threshold; in unit of slow clock cycle 20 | pub(crate) const WDT_DELAY_SEL: EfuseField = EfuseField::new(0, 1, 34, 2); 21 | /// Set this bit to disable pad jtag 22 | pub(crate) const DIS_PAD_JTAG: EfuseField = EfuseField::new(0, 1, 36, 1); 23 | /// The bit be set to disable icache in download mode 24 | pub(crate) const DIS_DOWNLOAD_ICACHE: EfuseField = EfuseField::new(0, 1, 37, 1); 25 | /// The bit be set to disable manual encryption 26 | pub(crate) const DIS_DOWNLOAD_MANUAL_ENCRYPT: EfuseField = EfuseField::new(0, 1, 38, 1); 27 | /// Enables flash encryption when 1 or 3 bits are set and disables otherwise 28 | pub(crate) const SPI_BOOT_CRYPT_CNT: EfuseField = EfuseField::new(0, 1, 39, 3); 29 | /// Flash encryption key length 30 | pub(crate) const XTS_KEY_LENGTH_256: EfuseField = EfuseField::new(0, 1, 42, 1); 31 | /// Set the default UARTboot message output mode 32 | pub(crate) const UART_PRINT_CONTROL: EfuseField = EfuseField::new(0, 1, 43, 2); 33 | /// Set this bit to force ROM code to send a resume command during SPI boot 34 | pub(crate) const FORCE_SEND_RESUME: EfuseField = EfuseField::new(0, 1, 45, 1); 35 | /// Set this bit to disable download mode (boot_mode[3:0] = 0; 1; 2; 4; 5; 6; 7) 36 | pub(crate) const DIS_DOWNLOAD_MODE: EfuseField = EfuseField::new(0, 1, 46, 1); 37 | /// This bit set means disable direct_boot mode 38 | pub(crate) const DIS_DIRECT_BOOT: EfuseField = EfuseField::new(0, 1, 47, 1); 39 | /// Set this bit to enable secure UART download mode 40 | pub(crate) const ENABLE_SECURITY_DOWNLOAD: EfuseField = EfuseField::new(0, 1, 48, 1); 41 | /// Configures flash waiting time after power-up; in unit of ms. If the value is 42 | /// less than 15; the waiting time is the configurable value. Otherwise; the 43 | /// waiting time is twice the configurable value 44 | pub(crate) const FLASH_TPUW: EfuseField = EfuseField::new(0, 1, 49, 4); 45 | /// The bit be set to enable secure boot 46 | pub(crate) const SECURE_BOOT_EN: EfuseField = EfuseField::new(0, 1, 53, 1); 47 | /// Secure version for anti-rollback 48 | pub(crate) const SECURE_VERSION: EfuseField = EfuseField::new(0, 1, 54, 4); 49 | /// True if MAC_CUSTOM is burned 50 | pub(crate) const CUSTOM_MAC_USED: EfuseField = EfuseField::new(0, 1, 58, 1); 51 | /// Disables check of wafer version major 52 | pub(crate) const DISABLE_WAFER_VERSION_MAJOR: EfuseField = EfuseField::new(0, 1, 59, 1); 53 | /// Disables check of blk version major 54 | pub(crate) const DISABLE_BLK_VERSION_MAJOR: EfuseField = EfuseField::new(0, 1, 60, 1); 55 | /// reserved 56 | pub(crate) const RESERVED_0_61: EfuseField = EfuseField::new(0, 1, 61, 3); 57 | /// Custom MAC address 58 | pub(crate) const CUSTOM_MAC: EfuseField = EfuseField::new(1, 0, 0, 48); 59 | /// reserved 60 | pub(crate) const RESERVED_1_48: EfuseField = EfuseField::new(1, 1, 48, 16); 61 | /// Stores the bits [64:87] of system data 62 | pub(crate) const SYSTEM_DATA2: EfuseField = EfuseField::new(1, 2, 64, 24); 63 | /// MAC address 64 | pub(crate) const MAC0: EfuseField = EfuseField::new(2, 0, 0, 32); 65 | /// MAC address 66 | pub(crate) const MAC1: EfuseField = EfuseField::new(2, 1, 32, 16); 67 | /// WAFER_VERSION_MINOR 68 | pub(crate) const WAFER_VERSION_MINOR: EfuseField = EfuseField::new(2, 1, 48, 4); 69 | /// WAFER_VERSION_MAJOR 70 | pub(crate) const WAFER_VERSION_MAJOR: EfuseField = EfuseField::new(2, 1, 52, 2); 71 | /// EFUSE_PKG_VERSION 72 | pub(crate) const PKG_VERSION: EfuseField = EfuseField::new(2, 1, 54, 3); 73 | /// Minor version of BLOCK2 74 | pub(crate) const BLK_VERSION_MINOR: EfuseField = EfuseField::new(2, 1, 57, 3); 75 | /// Major version of BLOCK2 76 | pub(crate) const BLK_VERSION_MAJOR: EfuseField = EfuseField::new(2, 1, 60, 2); 77 | /// OCode 78 | pub(crate) const OCODE: EfuseField = EfuseField::new(2, 1, 62, 7); 79 | /// Temperature calibration data 80 | pub(crate) const TEMP_CALIB: EfuseField = EfuseField::new(2, 2, 69, 9); 81 | /// ADC1 init code at atten0 82 | pub(crate) const ADC1_INIT_CODE_ATTEN0: EfuseField = EfuseField::new(2, 2, 78, 8); 83 | /// ADC1 init code at atten3 84 | pub(crate) const ADC1_INIT_CODE_ATTEN3: EfuseField = EfuseField::new(2, 2, 86, 5); 85 | /// ADC1 calibration voltage at atten0 86 | pub(crate) const ADC1_CAL_VOL_ATTEN0: EfuseField = EfuseField::new(2, 2, 91, 8); 87 | /// ADC1 calibration voltage at atten3 88 | pub(crate) const ADC1_CAL_VOL_ATTEN3: EfuseField = EfuseField::new(2, 3, 99, 6); 89 | /// BLOCK2 digital dbias when hvt 90 | pub(crate) const DIG_DBIAS_HVT: EfuseField = EfuseField::new(2, 3, 105, 5); 91 | /// BLOCK2 DIG_LDO_DBG0_DBIAS2 92 | pub(crate) const DIG_LDO_SLP_DBIAS2: EfuseField = EfuseField::new(2, 3, 110, 7); 93 | /// BLOCK2 DIG_LDO_DBG0_DBIAS26 94 | pub(crate) const DIG_LDO_SLP_DBIAS26: EfuseField = EfuseField::new(2, 3, 117, 8); 95 | /// BLOCK2 DIG_LDO_ACT_DBIAS26 96 | pub(crate) const DIG_LDO_ACT_DBIAS26: EfuseField = EfuseField::new(2, 3, 125, 6); 97 | /// BLOCK2 DIG_LDO_ACT_STEPD10 98 | pub(crate) const DIG_LDO_ACT_STEPD10: EfuseField = EfuseField::new(2, 4, 131, 4); 99 | /// BLOCK2 DIG_LDO_SLP_DBIAS13 100 | pub(crate) const RTC_LDO_SLP_DBIAS13: EfuseField = EfuseField::new(2, 4, 135, 7); 101 | /// BLOCK2 DIG_LDO_SLP_DBIAS29 102 | pub(crate) const RTC_LDO_SLP_DBIAS29: EfuseField = EfuseField::new(2, 4, 142, 9); 103 | /// BLOCK2 DIG_LDO_SLP_DBIAS31 104 | pub(crate) const RTC_LDO_SLP_DBIAS31: EfuseField = EfuseField::new(2, 4, 151, 6); 105 | /// BLOCK2 DIG_LDO_ACT_DBIAS31 106 | pub(crate) const RTC_LDO_ACT_DBIAS31: EfuseField = EfuseField::new(2, 4, 157, 6); 107 | /// BLOCK2 DIG_LDO_ACT_DBIAS13 108 | pub(crate) const RTC_LDO_ACT_DBIAS13: EfuseField = EfuseField::new(2, 5, 163, 8); 109 | /// reserved 110 | pub(crate) const RESERVED_2_171: EfuseField = EfuseField::new(2, 5, 171, 21); 111 | /// Store the bit [86:96] of ADC calibration data 112 | pub(crate) const ADC_CALIBRATION_3: EfuseField = EfuseField::new(2, 6, 192, 11); 113 | /// Store the bit [0:20] of block2 reserved data 114 | pub(crate) const BLK2_RESERVED_DATA_0: EfuseField = EfuseField::new(2, 6, 203, 21); 115 | /// Store the bit [21:52] of block2 reserved data 116 | pub(crate) const BLK2_RESERVED_DATA_1: EfuseField = EfuseField::new(2, 7, 224, 32); 117 | /// BLOCK_KEY0 - 256-bits. 256-bit key of Flash Encryption 118 | pub(crate) const BLOCK_KEY0: EfuseField = EfuseField::new(3, 0, 0, 256); 119 | -------------------------------------------------------------------------------- /espflash/src/targets/efuse/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::empty_docs)] 2 | 3 | pub(crate) mod esp32; 4 | pub(crate) mod esp32c2; 5 | pub(crate) mod esp32c3; 6 | pub(crate) mod esp32c5; 7 | pub(crate) mod esp32c6; 8 | pub(crate) mod esp32h2; 9 | pub(crate) mod esp32p4; 10 | pub(crate) mod esp32s2; 11 | pub(crate) mod esp32s3; 12 | 13 | #[allow(unused)] 14 | #[derive(Debug, Clone, serde::Deserialize)] 15 | pub struct EfuseField { 16 | pub(crate) block: u32, 17 | pub(crate) word: u32, 18 | pub(crate) bit_start: u32, 19 | pub(crate) bit_count: u32, 20 | } 21 | 22 | impl EfuseField { 23 | const fn new(block: u32, word: u32, bit_start: u32, bit_count: u32) -> Self { 24 | Self { 25 | block, 26 | word, 27 | bit_start, 28 | bit_count, 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /espflash/src/targets/esp32.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Range; 2 | 3 | use super::{ 4 | Chip, 5 | Esp32Params, 6 | ReadEFuse, 7 | SpiRegisters, 8 | Target, 9 | XtalFrequency, 10 | efuse::esp32 as efuse, 11 | }; 12 | #[cfg(feature = "serialport")] 13 | use crate::connection::Connection; 14 | use crate::{ 15 | Error, 16 | flasher::{FlashData, FlashFrequency}, 17 | image_format::IdfBootloaderFormat, 18 | }; 19 | 20 | pub(crate) const CHIP_ID: u16 = 0; 21 | 22 | const CHIP_DETECT_MAGIC_VALUES: &[u32] = &[0x00f0_1d83]; 23 | 24 | const FLASH_RANGES: &[Range] = &[ 25 | 0x400d_0000..0x4040_0000, // IROM 26 | 0x3f40_0000..0x3f80_0000, // DROM 27 | ]; 28 | 29 | #[cfg(feature = "serialport")] 30 | const UART_CLKDIV_REG: u32 = 0x3ff4_0014; // UART0_BASE_REG + 0x14 31 | #[cfg(feature = "serialport")] 32 | const UART_CLKDIV_MASK: u32 = 0xfffff; 33 | #[cfg(feature = "serialport")] 34 | const XTAL_CLK_DIVIDER: u32 = 1; 35 | 36 | /// ESP32 Target 37 | pub struct Esp32; 38 | 39 | impl Esp32 { 40 | /// Check if the magic value contains the specified value 41 | pub fn has_magic_value(value: u32) -> bool { 42 | CHIP_DETECT_MAGIC_VALUES.contains(&value) 43 | } 44 | 45 | /// Return the package version based on the eFuses 46 | #[cfg(feature = "serialport")] 47 | fn package_version(&self, connection: &mut Connection) -> Result { 48 | let word3 = self.read_efuse_raw(connection, 0, 3)?; 49 | 50 | let pkg_version = (word3 >> 9) & 0x7; 51 | let pkg_version = pkg_version + (((word3 >> 2) & 0x1) << 3); 52 | 53 | Ok(pkg_version) 54 | } 55 | } 56 | 57 | impl ReadEFuse for Esp32 { 58 | fn efuse_reg(&self) -> u32 { 59 | 0x3FF5_A000 60 | } 61 | 62 | fn block0_offset(&self) -> u32 { 63 | 0x0 64 | } 65 | 66 | fn block_size(&self, block: usize) -> u32 { 67 | efuse::BLOCK_SIZES[block] 68 | } 69 | } 70 | 71 | impl Target for Esp32 { 72 | fn chip(&self) -> Chip { 73 | Chip::Esp32 74 | } 75 | 76 | fn addr_is_flash(&self, addr: u32) -> bool { 77 | FLASH_RANGES.iter().any(|range| range.contains(&addr)) 78 | } 79 | 80 | #[cfg(feature = "serialport")] 81 | fn chip_features(&self, connection: &mut Connection) -> Result, Error> { 82 | let mut features = vec!["WiFi"]; 83 | 84 | let disable_bt = self.read_efuse(connection, efuse::DISABLE_BT)?; 85 | if disable_bt == 0 { 86 | features.push("BT"); 87 | } 88 | 89 | let disable_app_cpu = self.read_efuse(connection, efuse::DISABLE_APP_CPU)?; 90 | if disable_app_cpu == 0 { 91 | features.push("Dual Core"); 92 | } else { 93 | features.push("Single Core"); 94 | } 95 | 96 | let chip_cpu_freq_rated = self.read_efuse(connection, efuse::CHIP_CPU_FREQ_RATED)?; 97 | if chip_cpu_freq_rated != 0 { 98 | let chip_cpu_freq_low = self.read_efuse(connection, efuse::CHIP_CPU_FREQ_LOW)?; 99 | if chip_cpu_freq_low != 0 { 100 | features.push("160MHz"); 101 | } else { 102 | features.push("240MHz"); 103 | } 104 | } 105 | 106 | let pkg_version = self.package_version(connection)?; 107 | if [2, 4, 5, 6].contains(&pkg_version) { 108 | features.push("Embedded Flash"); 109 | } 110 | if pkg_version == 6 { 111 | features.push("Embedded PSRAM"); 112 | } 113 | 114 | let adc_vref = self.read_efuse(connection, efuse::ADC_VREF)?; 115 | if adc_vref != 0 { 116 | features.push("VRef calibration in efuse"); 117 | } 118 | 119 | let blk3_part_reserve = self.read_efuse(connection, efuse::BLK3_PART_RESERVE)?; 120 | if blk3_part_reserve != 0 { 121 | features.push("BLK3 partially reserved"); 122 | } 123 | 124 | let coding_scheme = self.read_efuse(connection, efuse::CODING_SCHEME)?; 125 | features.push(match coding_scheme { 126 | 0 => "Coding Scheme None", 127 | 1 => "Coding Scheme 3/4", 128 | 2 => "Coding Scheme Repeat (UNSUPPORTED)", 129 | _ => "Coding Scheme Invalid", 130 | }); 131 | 132 | Ok(features) 133 | } 134 | 135 | #[cfg(feature = "serialport")] 136 | fn major_chip_version(&self, connection: &mut Connection) -> Result { 137 | let apb_ctl_date = connection.read_reg(0x3FF6_607C)?; 138 | 139 | let word3 = self.read_efuse_raw(connection, 0, 3)?; 140 | let word5 = self.read_efuse_raw(connection, 0, 5)?; 141 | 142 | let rev_bit0 = (word3 >> 15) & 0x1; 143 | let rev_bit1 = (word5 >> 20) & 0x1; 144 | let rev_bit2 = (apb_ctl_date >> 31) & 0x1; 145 | 146 | let combine_value = (rev_bit2 << 2) | (rev_bit1 << 1) | rev_bit0; 147 | 148 | match combine_value { 149 | 1 => Ok(1), 150 | 3 => Ok(2), 151 | 7 => Ok(3), 152 | _ => Ok(0), 153 | } 154 | } 155 | 156 | #[cfg(feature = "serialport")] 157 | fn minor_chip_version(&self, connection: &mut Connection) -> Result { 158 | self.read_efuse(connection, efuse::WAFER_VERSION_MINOR) 159 | } 160 | 161 | #[cfg(feature = "serialport")] 162 | fn crystal_freq(&self, connection: &mut Connection) -> Result { 163 | let uart_div = connection.read_reg(UART_CLKDIV_REG)? & UART_CLKDIV_MASK; 164 | let est_xtal = (connection.baud()? * uart_div) / 1_000_000 / XTAL_CLK_DIVIDER; 165 | let norm_xtal = if est_xtal > 33 { 166 | XtalFrequency::_40Mhz 167 | } else { 168 | XtalFrequency::_26Mhz 169 | }; 170 | 171 | Ok(norm_xtal) 172 | } 173 | 174 | fn flash_image<'a>( 175 | &self, 176 | elf_data: &'a [u8], 177 | flash_data: FlashData, 178 | _chip_revision: Option<(u32, u32)>, 179 | xtal_freq: XtalFrequency, 180 | ) -> Result, Error> { 181 | let bootloader: &'static [u8] = match xtal_freq { 182 | XtalFrequency::_40Mhz => { 183 | include_bytes!("../../resources/bootloaders/esp32-bootloader.bin") 184 | } 185 | XtalFrequency::_26Mhz => { 186 | include_bytes!("../../resources/bootloaders/esp32_26-bootloader.bin") 187 | } 188 | _ => { 189 | return Err(Error::UnsupportedFeature { 190 | chip: Chip::Esp32, 191 | feature: "the selected crystal frequency".into(), 192 | }); 193 | } 194 | }; 195 | 196 | let params = Esp32Params::new( 197 | 0x1000, 198 | 0x1_0000, 199 | 0x3f_0000, 200 | CHIP_ID, 201 | FlashFrequency::_40Mhz, 202 | bootloader, 203 | None, 204 | ); 205 | 206 | IdfBootloaderFormat::new(elf_data, Chip::Esp32, flash_data, params) 207 | } 208 | 209 | fn spi_registers(&self) -> SpiRegisters { 210 | SpiRegisters { 211 | base: 0x3ff4_2000, 212 | usr_offset: 0x1c, 213 | usr1_offset: 0x20, 214 | usr2_offset: 0x24, 215 | w0_offset: 0x80, 216 | mosi_length_offset: Some(0x28), 217 | miso_length_offset: Some(0x2c), 218 | } 219 | } 220 | 221 | fn supported_build_targets(&self) -> &[&str] { 222 | &["xtensa-esp32-none-elf", "xtensa-esp32-espidf"] 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /espflash/src/targets/esp32c2.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, ops::Range}; 2 | 3 | use log::debug; 4 | 5 | use super::{ 6 | Chip, 7 | Esp32Params, 8 | ReadEFuse, 9 | SpiRegisters, 10 | Target, 11 | XtalFrequency, 12 | efuse::esp32c2 as efuse, 13 | }; 14 | #[cfg(feature = "serialport")] 15 | use crate::connection::Connection; 16 | use crate::{ 17 | Error, 18 | flasher::{FlashData, FlashFrequency}, 19 | image_format::IdfBootloaderFormat, 20 | }; 21 | 22 | pub(crate) const CHIP_ID: u16 = 12; 23 | 24 | const CHIP_DETECT_MAGIC_VALUES: &[u32] = &[ 25 | 0x6f51_306f, // ECO0 26 | 0x7c41_a06f, // ECO1 27 | ]; 28 | 29 | const FLASH_RANGES: &[Range] = &[ 30 | 0x4200_0000..0x4240_0000, // IROM 31 | 0x3c00_0000..0x3c40_0000, // DROM 32 | ]; 33 | 34 | #[cfg(feature = "serialport")] 35 | const UART_CLKDIV_REG: u32 = 0x6000_0014; // UART0_BASE_REG + 0x14 36 | #[cfg(feature = "serialport")] 37 | const UART_CLKDIV_MASK: u32 = 0xfffff; 38 | 39 | #[cfg(feature = "serialport")] 40 | const XTAL_CLK_DIVIDER: u32 = 1; 41 | 42 | /// ESP32-C2 Target 43 | pub struct Esp32c2; 44 | 45 | impl Esp32c2 { 46 | /// Check if the magic value contains the specified value 47 | pub fn has_magic_value(value: u32) -> bool { 48 | CHIP_DETECT_MAGIC_VALUES.contains(&value) 49 | } 50 | } 51 | 52 | impl ReadEFuse for Esp32c2 { 53 | fn efuse_reg(&self) -> u32 { 54 | 0x6000_8800 55 | } 56 | 57 | fn block0_offset(&self) -> u32 { 58 | 0x35 59 | } 60 | 61 | fn block_size(&self, block: usize) -> u32 { 62 | efuse::BLOCK_SIZES[block] 63 | } 64 | } 65 | 66 | impl Target for Esp32c2 { 67 | fn chip(&self) -> Chip { 68 | Chip::Esp32c2 69 | } 70 | 71 | fn addr_is_flash(&self, addr: u32) -> bool { 72 | FLASH_RANGES.iter().any(|range| range.contains(&addr)) 73 | } 74 | 75 | #[cfg(feature = "serialport")] 76 | fn chip_features(&self, _connection: &mut Connection) -> Result, Error> { 77 | Ok(vec!["WiFi", "BLE"]) 78 | } 79 | 80 | #[cfg(feature = "serialport")] 81 | fn major_chip_version(&self, connection: &mut Connection) -> Result { 82 | self.read_efuse(connection, efuse::WAFER_VERSION_MAJOR) 83 | } 84 | 85 | #[cfg(feature = "serialport")] 86 | fn minor_chip_version(&self, connection: &mut Connection) -> Result { 87 | self.read_efuse(connection, efuse::WAFER_VERSION_MINOR) 88 | } 89 | 90 | #[cfg(feature = "serialport")] 91 | fn crystal_freq(&self, connection: &mut Connection) -> Result { 92 | let uart_div = connection.read_reg(UART_CLKDIV_REG)? & UART_CLKDIV_MASK; 93 | let est_xtal = (connection.baud()? * uart_div) / 1_000_000 / XTAL_CLK_DIVIDER; 94 | let norm_xtal = if est_xtal > 33 { 95 | XtalFrequency::_40Mhz 96 | } else { 97 | XtalFrequency::_26Mhz 98 | }; 99 | 100 | Ok(norm_xtal) 101 | } 102 | 103 | fn flash_frequency_encodings(&self) -> HashMap { 104 | use FlashFrequency::*; 105 | 106 | let encodings = [(_15Mhz, 0x2), (_20Mhz, 0x1), (_30Mhz, 0x0), (_60Mhz, 0xF)]; 107 | 108 | HashMap::from(encodings) 109 | } 110 | 111 | fn flash_image<'a>( 112 | &self, 113 | elf_data: &'a [u8], 114 | flash_data: FlashData, 115 | _chip_revision: Option<(u32, u32)>, 116 | xtal_freq: XtalFrequency, 117 | ) -> Result, Error> { 118 | let bootloader: &'static [u8] = match xtal_freq { 119 | XtalFrequency::_40Mhz => { 120 | debug!("Using 40MHz bootloader"); 121 | include_bytes!("../../resources/bootloaders/esp32c2-bootloader.bin") 122 | } 123 | XtalFrequency::_26Mhz => { 124 | debug!("Using 26MHz bootloader"); 125 | include_bytes!("../../resources/bootloaders/esp32c2_26-bootloader.bin") 126 | } 127 | _ => { 128 | return Err(Error::UnsupportedFeature { 129 | chip: Chip::Esp32c2, 130 | feature: "the selected crystal frequency".into(), 131 | }); 132 | } 133 | }; 134 | 135 | let params = Esp32Params::new( 136 | 0x0, 137 | 0x1_0000, 138 | 0x1f_0000, 139 | CHIP_ID, 140 | FlashFrequency::_30Mhz, 141 | bootloader, 142 | Some(&[16 * 1024, 32 * 1024, 64 * 1024]), 143 | ); 144 | 145 | IdfBootloaderFormat::new(elf_data, Chip::Esp32c2, flash_data, params) 146 | } 147 | 148 | fn spi_registers(&self) -> SpiRegisters { 149 | SpiRegisters { 150 | base: 0x6000_2000, 151 | usr_offset: 0x18, 152 | usr1_offset: 0x1C, 153 | usr2_offset: 0x20, 154 | w0_offset: 0x58, 155 | mosi_length_offset: Some(0x24), 156 | miso_length_offset: Some(0x28), 157 | } 158 | } 159 | 160 | fn supported_build_targets(&self) -> &[&str] { 161 | &[ 162 | "riscv32imac-unknown-none-elf", 163 | "riscv32imc-esp-espidf", 164 | "riscv32imc-unknown-none-elf", 165 | ] 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /espflash/src/targets/esp32c3.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Range; 2 | 3 | use super::{ 4 | Chip, 5 | Esp32Params, 6 | ReadEFuse, 7 | SpiRegisters, 8 | Target, 9 | XtalFrequency, 10 | efuse::esp32c3 as efuse, 11 | }; 12 | #[cfg(feature = "serialport")] 13 | use crate::connection::Connection; 14 | use crate::{ 15 | Error, 16 | flasher::{FlashData, FlashFrequency}, 17 | image_format::IdfBootloaderFormat, 18 | }; 19 | 20 | pub(crate) const CHIP_ID: u16 = 5; 21 | 22 | const CHIP_DETECT_MAGIC_VALUES: &[u32] = &[ 23 | 0x6921_506f, // ECO1 + ECO2 24 | 0x1b31_506f, // ECO3 25 | 0x4881_606F, // ECO6 26 | 0x4361_606f, // ECO7 27 | ]; 28 | 29 | const FLASH_RANGES: &[Range] = &[ 30 | 0x4200_0000..0x4280_0000, // IROM 31 | 0x3c00_0000..0x3c80_0000, // DROM 32 | ]; 33 | 34 | const PARAMS: Esp32Params = Esp32Params::new( 35 | 0x0, 36 | 0x1_0000, 37 | 0x3f_0000, 38 | CHIP_ID, 39 | FlashFrequency::_40Mhz, 40 | include_bytes!("../../resources/bootloaders/esp32c3-bootloader.bin"), 41 | None, 42 | ); 43 | 44 | /// ESP32-C3 Target 45 | pub struct Esp32c3; 46 | 47 | impl Esp32c3 { 48 | /// Check if the magic value contains the specified value 49 | pub fn has_magic_value(value: u32) -> bool { 50 | CHIP_DETECT_MAGIC_VALUES.contains(&value) 51 | } 52 | } 53 | 54 | impl ReadEFuse for Esp32c3 { 55 | fn efuse_reg(&self) -> u32 { 56 | 0x6000_8800 57 | } 58 | 59 | fn block0_offset(&self) -> u32 { 60 | 0x2D 61 | } 62 | 63 | fn block_size(&self, block: usize) -> u32 { 64 | efuse::BLOCK_SIZES[block] 65 | } 66 | } 67 | 68 | impl Target for Esp32c3 { 69 | fn chip(&self) -> Chip { 70 | Chip::Esp32c3 71 | } 72 | 73 | fn addr_is_flash(&self, addr: u32) -> bool { 74 | FLASH_RANGES.iter().any(|range| range.contains(&addr)) 75 | } 76 | 77 | #[cfg(feature = "serialport")] 78 | fn chip_features(&self, _connection: &mut Connection) -> Result, Error> { 79 | Ok(vec!["WiFi", "BLE"]) 80 | } 81 | 82 | #[cfg(feature = "serialport")] 83 | fn major_chip_version(&self, connection: &mut Connection) -> Result { 84 | self.read_efuse(connection, efuse::WAFER_VERSION_MAJOR) 85 | } 86 | 87 | #[cfg(feature = "serialport")] 88 | fn minor_chip_version(&self, connection: &mut Connection) -> Result { 89 | let hi = self.read_efuse(connection, efuse::WAFER_VERSION_MINOR_HI)?; 90 | let lo = self.read_efuse(connection, efuse::WAFER_VERSION_MINOR_LO)?; 91 | 92 | Ok((hi << 3) + lo) 93 | } 94 | 95 | #[cfg(feature = "serialport")] 96 | fn crystal_freq(&self, _connection: &mut Connection) -> Result { 97 | // The ESP32-C3's XTAL has a fixed frequency of 40MHz. 98 | Ok(XtalFrequency::_40Mhz) 99 | } 100 | 101 | fn flash_image<'a>( 102 | &self, 103 | elf_data: &'a [u8], 104 | flash_data: FlashData, 105 | _chip_revision: Option<(u32, u32)>, 106 | xtal_freq: XtalFrequency, 107 | ) -> Result, Error> { 108 | if xtal_freq != XtalFrequency::_40Mhz { 109 | return Err(Error::UnsupportedFeature { 110 | chip: Chip::Esp32c3, 111 | feature: "the selected crystal frequency".into(), 112 | }); 113 | } 114 | 115 | IdfBootloaderFormat::new(elf_data, Chip::Esp32c3, flash_data, PARAMS) 116 | } 117 | 118 | fn spi_registers(&self) -> SpiRegisters { 119 | SpiRegisters { 120 | base: 0x6000_2000, 121 | usr_offset: 0x18, 122 | usr1_offset: 0x1C, 123 | usr2_offset: 0x20, 124 | w0_offset: 0x58, 125 | mosi_length_offset: Some(0x24), 126 | miso_length_offset: Some(0x28), 127 | } 128 | } 129 | 130 | fn supported_build_targets(&self) -> &[&str] { 131 | &[ 132 | "riscv32imac-unknown-none-elf", 133 | "riscv32imc-esp-espidf", 134 | "riscv32imc-unknown-none-elf", 135 | ] 136 | } 137 | } 138 | 139 | #[cfg(feature = "serialport")] 140 | impl super::RtcWdtReset for Esp32c3 { 141 | fn wdt_wprotect(&self) -> u32 { 142 | 0x6000_80A8 143 | } 144 | 145 | fn wdt_config0(&self) -> u32 { 146 | 0x6000_8090 147 | } 148 | 149 | fn wdt_config1(&self) -> u32 { 150 | 0x6000_8094 151 | } 152 | 153 | fn can_rtc_wdt_reset(&self, _connection: &mut Connection) -> Result { 154 | Ok(true) 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /espflash/src/targets/esp32c5.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Range; 2 | 3 | use super::{ 4 | Chip, 5 | Esp32Params, 6 | ReadEFuse, 7 | SpiRegisters, 8 | Target, 9 | XtalFrequency, 10 | efuse::esp32c5 as efuse, 11 | }; 12 | #[cfg(feature = "serialport")] 13 | use crate::connection::Connection; 14 | use crate::{ 15 | Error, 16 | flasher::{FlashData, FlashFrequency}, 17 | image_format::IdfBootloaderFormat, 18 | }; 19 | 20 | pub(crate) const CHIP_ID: u16 = 23; 21 | 22 | const CHIP_DETECT_MAGIC_VALUES: &[u32] = &[]; 23 | 24 | const FLASH_RANGES: &[Range] = &[ 25 | 0x42000000..0x44000000, // IROM 26 | 0x42000000..0x44000000, // DROM 27 | ]; 28 | 29 | #[cfg(feature = "serialport")] 30 | const UART_CLKDIV_REG: u32 = 0x6000_0014; // UART0_BASE_REG + 0x14 31 | #[cfg(feature = "serialport")] 32 | const UART_CLKDIV_MASK: u32 = 0xfffff; 33 | #[cfg(feature = "serialport")] 34 | const XTAL_CLK_DIVIDER: u32 = 1; 35 | 36 | const PARAMS: Esp32Params = Esp32Params::new( 37 | 0x2000, 38 | 0x1_0000, 39 | 0x3f_0000, 40 | CHIP_ID, 41 | FlashFrequency::_40Mhz, 42 | include_bytes!("../../resources/bootloaders/esp32c5-bootloader.bin"), 43 | None, 44 | ); 45 | 46 | /// ESP32-C5 Target 47 | pub struct Esp32c5; 48 | 49 | impl Esp32c5 { 50 | /// Check if the magic value contains the specified value 51 | pub fn has_magic_value(value: u32) -> bool { 52 | CHIP_DETECT_MAGIC_VALUES.contains(&value) 53 | } 54 | } 55 | 56 | impl ReadEFuse for Esp32c5 { 57 | fn efuse_reg(&self) -> u32 { 58 | 0x600B4800 59 | } 60 | 61 | fn block0_offset(&self) -> u32 { 62 | 0x2C 63 | } 64 | 65 | fn block_size(&self, block: usize) -> u32 { 66 | efuse::BLOCK_SIZES[block] 67 | } 68 | } 69 | 70 | impl Target for Esp32c5 { 71 | fn chip(&self) -> Chip { 72 | Chip::Esp32c5 73 | } 74 | 75 | fn addr_is_flash(&self, addr: u32) -> bool { 76 | FLASH_RANGES.iter().any(|range| range.contains(&addr)) 77 | } 78 | 79 | #[cfg(feature = "serialport")] 80 | fn chip_features(&self, _connection: &mut Connection) -> Result, Error> { 81 | Ok(vec![ 82 | "Wi-Fi 6 (dual-band)", 83 | "BT 5 (LE)", 84 | "IEEE802.15.4", 85 | "Single Core + LP Core", 86 | "240MHz", 87 | ]) 88 | } 89 | 90 | #[cfg(feature = "serialport")] 91 | fn major_chip_version(&self, connection: &mut Connection) -> Result { 92 | self.read_efuse(connection, efuse::WAFER_VERSION_MAJOR) 93 | } 94 | 95 | #[cfg(feature = "serialport")] 96 | fn minor_chip_version(&self, connection: &mut Connection) -> Result { 97 | self.read_efuse(connection, efuse::WAFER_VERSION_MINOR) 98 | } 99 | 100 | #[cfg(feature = "serialport")] 101 | fn crystal_freq(&self, connection: &mut Connection) -> Result { 102 | let uart_div = connection.read_reg(UART_CLKDIV_REG)? & UART_CLKDIV_MASK; 103 | let est_xtal = (connection.baud()? * uart_div) / 1_000_000 / XTAL_CLK_DIVIDER; 104 | let norm_xtal = if est_xtal > 45 { 105 | XtalFrequency::_48Mhz 106 | } else { 107 | XtalFrequency::_40Mhz 108 | }; 109 | 110 | Ok(norm_xtal) 111 | } 112 | 113 | fn flash_image<'a>( 114 | &self, 115 | elf_data: &'a [u8], 116 | flash_data: FlashData, 117 | _chip_revision: Option<(u32, u32)>, 118 | xtal_freq: XtalFrequency, 119 | ) -> Result, Error> { 120 | if !matches!(xtal_freq, XtalFrequency::_40Mhz | XtalFrequency::_48Mhz) { 121 | return Err(Error::UnsupportedFeature { 122 | chip: Chip::Esp32c5, 123 | feature: "the selected crystal frequency".into(), 124 | }); 125 | } 126 | 127 | IdfBootloaderFormat::new(elf_data, Chip::Esp32c5, flash_data, PARAMS) 128 | } 129 | 130 | fn spi_registers(&self) -> SpiRegisters { 131 | SpiRegisters { 132 | base: 0x6000_3000, 133 | usr_offset: 0x18, 134 | usr1_offset: 0x1c, 135 | usr2_offset: 0x20, 136 | w0_offset: 0x58, 137 | mosi_length_offset: Some(0x24), 138 | miso_length_offset: Some(0x28), 139 | } 140 | } 141 | 142 | fn supported_build_targets(&self) -> &[&str] { 143 | &["riscv32imac-esp-espidf", "riscv32imac-unknown-none-elf"] 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /espflash/src/targets/esp32c6.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Range; 2 | 3 | use super::{ 4 | Chip, 5 | Esp32Params, 6 | ReadEFuse, 7 | SpiRegisters, 8 | Target, 9 | XtalFrequency, 10 | efuse::esp32c6 as efuse, 11 | }; 12 | #[cfg(feature = "serialport")] 13 | use crate::connection::Connection; 14 | use crate::{ 15 | Error, 16 | flasher::{FlashData, FlashFrequency}, 17 | image_format::IdfBootloaderFormat, 18 | }; 19 | 20 | pub(crate) const CHIP_ID: u16 = 13; 21 | 22 | const CHIP_DETECT_MAGIC_VALUES: &[u32] = &[0x2CE0_806F]; 23 | 24 | const FLASH_RANGES: &[Range] = &[ 25 | 0x4200_0000..0x4280_0000, // IROM 26 | 0x4280_0000..0x4300_0000, // DROM 27 | ]; 28 | 29 | const PARAMS: Esp32Params = Esp32Params::new( 30 | 0x0, 31 | 0x1_0000, 32 | 0x3f_0000, 33 | CHIP_ID, 34 | FlashFrequency::_40Mhz, 35 | include_bytes!("../../resources/bootloaders/esp32c6-bootloader.bin"), 36 | Some(&[8 * 1024, 16 * 1024, 32 * 1024, 64 * 1024]), 37 | ); 38 | 39 | /// ESP32-C6 Target 40 | pub struct Esp32c6; 41 | 42 | impl Esp32c6 { 43 | /// Check if the magic value contains the specified value 44 | pub fn has_magic_value(value: u32) -> bool { 45 | CHIP_DETECT_MAGIC_VALUES.contains(&value) 46 | } 47 | } 48 | 49 | impl ReadEFuse for Esp32c6 { 50 | fn efuse_reg(&self) -> u32 { 51 | 0x600B_0800 52 | } 53 | 54 | fn block0_offset(&self) -> u32 { 55 | 0x2C 56 | } 57 | 58 | fn block_size(&self, block: usize) -> u32 { 59 | efuse::BLOCK_SIZES[block] 60 | } 61 | } 62 | 63 | impl Target for Esp32c6 { 64 | fn chip(&self) -> Chip { 65 | Chip::Esp32c6 66 | } 67 | 68 | fn addr_is_flash(&self, addr: u32) -> bool { 69 | FLASH_RANGES.iter().any(|range| range.contains(&addr)) 70 | } 71 | 72 | #[cfg(feature = "serialport")] 73 | fn chip_features(&self, _connection: &mut Connection) -> Result, Error> { 74 | Ok(vec!["WiFi 6", "BT 5"]) 75 | } 76 | 77 | #[cfg(feature = "serialport")] 78 | fn major_chip_version(&self, connection: &mut Connection) -> Result { 79 | self.read_efuse(connection, efuse::WAFER_VERSION_MAJOR) 80 | } 81 | 82 | #[cfg(feature = "serialport")] 83 | fn minor_chip_version(&self, connection: &mut Connection) -> Result { 84 | self.read_efuse(connection, efuse::WAFER_VERSION_MINOR) 85 | } 86 | 87 | #[cfg(feature = "serialport")] 88 | fn crystal_freq(&self, _connection: &mut Connection) -> Result { 89 | // The ESP32-C6's XTAL has a fixed frequency of 40MHz. 90 | Ok(XtalFrequency::_40Mhz) 91 | } 92 | 93 | fn flash_image<'a>( 94 | &self, 95 | elf_data: &'a [u8], 96 | flash_data: FlashData, 97 | _chip_revision: Option<(u32, u32)>, 98 | xtal_freq: XtalFrequency, 99 | ) -> Result, Error> { 100 | if xtal_freq != XtalFrequency::_40Mhz { 101 | return Err(Error::UnsupportedFeature { 102 | chip: Chip::Esp32c6, 103 | feature: "the selected crystal frequency".into(), 104 | }); 105 | } 106 | 107 | IdfBootloaderFormat::new(elf_data, Chip::Esp32c6, flash_data, PARAMS) 108 | } 109 | 110 | fn spi_registers(&self) -> SpiRegisters { 111 | SpiRegisters { 112 | base: 0x6000_3000, 113 | usr_offset: 0x18, 114 | usr1_offset: 0x1c, 115 | usr2_offset: 0x20, 116 | w0_offset: 0x58, 117 | mosi_length_offset: Some(0x24), 118 | miso_length_offset: Some(0x28), 119 | } 120 | } 121 | 122 | fn supported_build_targets(&self) -> &[&str] { 123 | &["riscv32imac-esp-espidf", "riscv32imac-unknown-none-elf"] 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /espflash/src/targets/esp32h2.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, ops::Range}; 2 | 3 | use super::{ 4 | Chip, 5 | Esp32Params, 6 | ReadEFuse, 7 | SpiRegisters, 8 | Target, 9 | XtalFrequency, 10 | efuse::esp32h2 as efuse, 11 | }; 12 | #[cfg(feature = "serialport")] 13 | use crate::connection::Connection; 14 | use crate::{ 15 | Error, 16 | flasher::{FlashData, FlashFrequency}, 17 | image_format::IdfBootloaderFormat, 18 | }; 19 | 20 | pub(crate) const CHIP_ID: u16 = 16; 21 | 22 | const CHIP_DETECT_MAGIC_VALUES: &[u32] = &[0xD7B7_3E80]; 23 | 24 | const FLASH_RANGES: &[Range] = &[ 25 | 0x4200_0000..0x4280_0000, // IROM 26 | 0x4280_0000..0x4300_0000, // DROM 27 | ]; 28 | 29 | const PARAMS: Esp32Params = Esp32Params::new( 30 | 0x0, 31 | 0x1_0000, 32 | 0x3f_0000, 33 | CHIP_ID, 34 | FlashFrequency::_24Mhz, 35 | include_bytes!("../../resources/bootloaders/esp32h2-bootloader.bin"), 36 | Some(&[8 * 1024, 16 * 1024, 32 * 1024, 64 * 1024]), 37 | ); 38 | 39 | /// ESP32-H2 Target 40 | pub struct Esp32h2; 41 | 42 | impl Esp32h2 { 43 | pub fn has_magic_value(value: u32) -> bool { 44 | CHIP_DETECT_MAGIC_VALUES.contains(&value) 45 | } 46 | } 47 | 48 | impl ReadEFuse for Esp32h2 { 49 | fn efuse_reg(&self) -> u32 { 50 | 0x600B_0800 51 | } 52 | 53 | fn block0_offset(&self) -> u32 { 54 | 0x2C 55 | } 56 | 57 | fn block_size(&self, block: usize) -> u32 { 58 | efuse::BLOCK_SIZES[block] 59 | } 60 | } 61 | 62 | impl Target for Esp32h2 { 63 | fn chip(&self) -> Chip { 64 | Chip::Esp32h2 65 | } 66 | 67 | fn addr_is_flash(&self, addr: u32) -> bool { 68 | FLASH_RANGES.iter().any(|range| range.contains(&addr)) 69 | } 70 | 71 | #[cfg(feature = "serialport")] 72 | fn chip_features(&self, _connection: &mut Connection) -> Result, Error> { 73 | Ok(vec!["BLE"]) 74 | } 75 | 76 | #[cfg(feature = "serialport")] 77 | fn major_chip_version(&self, connection: &mut Connection) -> Result { 78 | self.read_efuse(connection, efuse::WAFER_VERSION_MAJOR) 79 | } 80 | 81 | #[cfg(feature = "serialport")] 82 | fn minor_chip_version(&self, connection: &mut Connection) -> Result { 83 | self.read_efuse(connection, efuse::WAFER_VERSION_MINOR) 84 | } 85 | 86 | #[cfg(feature = "serialport")] 87 | fn crystal_freq(&self, _connection: &mut Connection) -> Result { 88 | // The ESP32-H2's XTAL has a fixed frequency of 32MHz. 89 | Ok(XtalFrequency::_32Mhz) 90 | } 91 | 92 | fn flash_frequency_encodings(&self) -> HashMap { 93 | use FlashFrequency::*; 94 | 95 | let encodings = [(_12Mhz, 0x2), (_16Mhz, 0x1), (_24Mhz, 0x0), (_48Mhz, 0xF)]; 96 | 97 | HashMap::from(encodings) 98 | } 99 | 100 | fn flash_image<'a>( 101 | &self, 102 | elf_data: &'a [u8], 103 | flash_data: FlashData, 104 | _chip_revision: Option<(u32, u32)>, 105 | xtal_freq: XtalFrequency, 106 | ) -> Result, Error> { 107 | if xtal_freq != XtalFrequency::_32Mhz { 108 | return Err(Error::UnsupportedFeature { 109 | chip: Chip::Esp32h2, 110 | feature: "the selected crystal frequency".into(), 111 | }); 112 | } 113 | 114 | IdfBootloaderFormat::new(elf_data, Chip::Esp32h2, flash_data, PARAMS) 115 | } 116 | 117 | fn spi_registers(&self) -> SpiRegisters { 118 | SpiRegisters { 119 | base: 0x6000_3000, 120 | usr_offset: 0x18, 121 | usr1_offset: 0x1c, 122 | usr2_offset: 0x20, 123 | w0_offset: 0x58, 124 | mosi_length_offset: Some(0x24), 125 | miso_length_offset: Some(0x28), 126 | } 127 | } 128 | 129 | fn supported_build_targets(&self) -> &[&str] { 130 | &["riscv32imac-esp-espidf", "riscv32imac-unknown-none-elf"] 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /espflash/src/targets/esp32p4.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Range; 2 | 3 | use super::{ 4 | Chip, 5 | Esp32Params, 6 | ReadEFuse, 7 | SpiRegisters, 8 | Target, 9 | XtalFrequency, 10 | efuse::esp32p4 as efuse, 11 | }; 12 | #[cfg(feature = "serialport")] 13 | use crate::connection::Connection; 14 | use crate::{ 15 | Error, 16 | flasher::{FlashData, FlashFrequency}, 17 | image_format::IdfBootloaderFormat, 18 | }; 19 | 20 | pub(crate) const CHIP_ID: u16 = 18; 21 | 22 | const CHIP_DETECT_MAGIC_VALUES: &[u32] = &[0x0, 0x0ADDBAD0]; 23 | 24 | const FLASH_RANGES: &[Range] = &[ 25 | 0x4000_0000..0x4C00_0000, // IROM 26 | 0x4000_0000..0x4C00_0000, // DROM 27 | ]; 28 | 29 | const PARAMS: Esp32Params = Esp32Params::new( 30 | 0x2000, 31 | 0x1_0000, 32 | 0x3f_0000, 33 | CHIP_ID, 34 | FlashFrequency::_40Mhz, 35 | include_bytes!("../../resources/bootloaders/esp32p4-bootloader.bin"), 36 | None, 37 | ); 38 | 39 | /// ESP32-P4 Target 40 | pub struct Esp32p4; 41 | 42 | impl Esp32p4 { 43 | /// Check if the magic value contains the specified value 44 | pub fn has_magic_value(value: u32) -> bool { 45 | CHIP_DETECT_MAGIC_VALUES.contains(&value) 46 | } 47 | } 48 | 49 | impl ReadEFuse for Esp32p4 { 50 | fn efuse_reg(&self) -> u32 { 51 | 0x5012_D000 52 | } 53 | 54 | fn block0_offset(&self) -> u32 { 55 | 0x2C 56 | } 57 | 58 | fn block_size(&self, block: usize) -> u32 { 59 | efuse::BLOCK_SIZES[block] 60 | } 61 | } 62 | 63 | impl Target for Esp32p4 { 64 | fn chip(&self) -> Chip { 65 | Chip::Esp32p4 66 | } 67 | 68 | fn addr_is_flash(&self, addr: u32) -> bool { 69 | FLASH_RANGES.iter().any(|range| range.contains(&addr)) 70 | } 71 | 72 | #[cfg(feature = "serialport")] 73 | fn chip_features(&self, _connection: &mut Connection) -> Result, Error> { 74 | Ok(vec!["High-Performance MCU"]) 75 | } 76 | 77 | #[cfg(feature = "serialport")] 78 | fn major_chip_version(&self, connection: &mut Connection) -> Result { 79 | self.read_efuse(connection, efuse::WAFER_VERSION_MAJOR) 80 | } 81 | 82 | #[cfg(feature = "serialport")] 83 | fn minor_chip_version(&self, connection: &mut Connection) -> Result { 84 | self.read_efuse(connection, efuse::WAFER_VERSION_MINOR) 85 | } 86 | 87 | #[cfg(feature = "serialport")] 88 | fn crystal_freq(&self, _connection: &mut Connection) -> Result { 89 | // The ESP32-P4's XTAL has a fixed frequency of 40MHz. 90 | Ok(XtalFrequency::_40Mhz) 91 | } 92 | 93 | fn flash_image<'a>( 94 | &self, 95 | elf_data: &'a [u8], 96 | flash_data: FlashData, 97 | _chip_revision: Option<(u32, u32)>, 98 | xtal_freq: XtalFrequency, 99 | ) -> Result, Error> { 100 | if xtal_freq != XtalFrequency::_40Mhz { 101 | return Err(Error::UnsupportedFeature { 102 | chip: Chip::Esp32p4, 103 | feature: "the selected crystal frequency".into(), 104 | }); 105 | } 106 | 107 | IdfBootloaderFormat::new(elf_data, Chip::Esp32p4, flash_data, PARAMS) 108 | } 109 | 110 | fn spi_registers(&self) -> SpiRegisters { 111 | SpiRegisters { 112 | base: 0x5008_D000, 113 | usr_offset: 0x18, 114 | usr1_offset: 0x1c, 115 | usr2_offset: 0x20, 116 | w0_offset: 0x58, 117 | mosi_length_offset: Some(0x24), 118 | miso_length_offset: Some(0x28), 119 | } 120 | } 121 | 122 | fn supported_build_targets(&self) -> &[&str] { 123 | &["riscv32imafc-esp-espidf", "riscv32imafc-unknown-none-elf"] 124 | } 125 | } 126 | 127 | #[cfg(feature = "serialport")] 128 | impl super::RtcWdtReset for Esp32p4 { 129 | fn wdt_wprotect(&self) -> u32 { 130 | 0x5011_6018 131 | } 132 | 133 | fn wdt_config0(&self) -> u32 { 134 | 0x5011_6000 135 | } 136 | 137 | fn wdt_config1(&self) -> u32 { 138 | 0x5011_6004 139 | } 140 | 141 | fn can_rtc_wdt_reset(&self, _connection: &mut Connection) -> Result { 142 | Ok(true) 143 | } 144 | } 145 | 146 | #[cfg(feature = "serialport")] 147 | impl super::UsbOtg for Esp32p4 { 148 | fn uartdev_buf_no(&self) -> u32 { 149 | 0x4FF3_FEC8 150 | } 151 | 152 | fn uartdev_buf_no_usb_otg(&self) -> u32 { 153 | 5 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /espflash/src/targets/esp32s2.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Range; 2 | 3 | #[cfg(feature = "serialport")] 4 | use super::flash_target::MAX_RAM_BLOCK_SIZE; 5 | use super::{ 6 | Chip, 7 | Esp32Params, 8 | ReadEFuse, 9 | SpiRegisters, 10 | Target, 11 | XtalFrequency, 12 | efuse::esp32s2 as efuse, 13 | }; 14 | use crate::{ 15 | Error, 16 | flasher::{FlashData, FlashFrequency}, 17 | image_format::IdfBootloaderFormat, 18 | }; 19 | #[cfg(feature = "serialport")] 20 | use crate::{connection::Connection, flasher::FLASH_WRITE_SIZE}; 21 | 22 | pub(crate) const CHIP_ID: u16 = 2; 23 | 24 | const CHIP_DETECT_MAGIC_VALUES: &[u32] = &[0x0000_07c6]; 25 | 26 | const FLASH_RANGES: &[Range] = &[ 27 | 0x4008_0000..0x40b8_0000, // IROM 28 | 0x3f00_0000..0x3f3f_0000, // DROM 29 | ]; 30 | 31 | #[cfg(feature = "serialport")] 32 | const MAX_USB_BLOCK_SIZE: usize = 0x800; 33 | 34 | const PARAMS: Esp32Params = Esp32Params::new( 35 | 0x1000, 36 | 0x1_0000, 37 | 0x10_0000, 38 | CHIP_ID, 39 | FlashFrequency::_40Mhz, 40 | include_bytes!("../../resources/bootloaders/esp32s2-bootloader.bin"), 41 | None, 42 | ); 43 | 44 | /// ESP32-S2 Target 45 | pub struct Esp32s2; 46 | 47 | impl Esp32s2 { 48 | /// Return the block2 version based on eFuses 49 | #[cfg(feature = "serialport")] 50 | fn block2_version(&self, connection: &mut Connection) -> Result { 51 | self.read_efuse(connection, efuse::BLK_VERSION_MINOR) 52 | } 53 | 54 | /// Return the flash version based on eFuses 55 | #[cfg(feature = "serialport")] 56 | fn flash_version(&self, connection: &mut Connection) -> Result { 57 | self.read_efuse(connection, efuse::FLASH_VERSION) 58 | } 59 | 60 | /// Return the PSRAM version based on eFuses 61 | #[cfg(feature = "serialport")] 62 | fn psram_version(&self, connection: &mut Connection) -> Result { 63 | self.read_efuse(connection, efuse::PSRAM_VERSION) 64 | } 65 | 66 | /// Check if the magic value contains the specified value 67 | pub fn has_magic_value(value: u32) -> bool { 68 | CHIP_DETECT_MAGIC_VALUES.contains(&value) 69 | } 70 | } 71 | 72 | impl ReadEFuse for Esp32s2 { 73 | fn efuse_reg(&self) -> u32 { 74 | 0x3F41_A000 75 | } 76 | 77 | fn block0_offset(&self) -> u32 { 78 | 0x2C 79 | } 80 | 81 | fn block_size(&self, block: usize) -> u32 { 82 | efuse::BLOCK_SIZES[block] 83 | } 84 | } 85 | 86 | impl Target for Esp32s2 { 87 | fn chip(&self) -> Chip { 88 | Chip::Esp32s2 89 | } 90 | 91 | fn addr_is_flash(&self, addr: u32) -> bool { 92 | FLASH_RANGES.iter().any(|range| range.contains(&addr)) 93 | } 94 | 95 | #[cfg(feature = "serialport")] 96 | fn chip_features(&self, connection: &mut Connection) -> Result, Error> { 97 | let mut features = vec!["WiFi"]; 98 | 99 | let flash_version = match self.flash_version(connection)? { 100 | 0 => "No Embedded Flash", 101 | 1 => "Embedded Flash 2MB", 102 | 2 => "Embedded Flash 4MB", 103 | _ => "Unknown Embedded Flash", 104 | }; 105 | features.push(flash_version); 106 | 107 | let psram_version = match self.psram_version(connection)? { 108 | 0 => "No Embedded PSRAM", 109 | 1 => "Embedded PSRAM 2MB", 110 | 2 => "Embedded PSRAM 4MB", 111 | _ => "Unknown Embedded PSRAM", 112 | }; 113 | features.push(psram_version); 114 | 115 | let block2_version = match self.block2_version(connection)? { 116 | 0 => "No calibration in BLK2 of efuse", 117 | 1 => "ADC and temperature sensor calibration in BLK2 of efuse V1", 118 | 2 => "ADC and temperature sensor calibration in BLK2 of efuse V2", 119 | _ => "Unknown Calibration in BLK2", 120 | }; 121 | features.push(block2_version); 122 | 123 | Ok(features) 124 | } 125 | 126 | #[cfg(feature = "serialport")] 127 | fn major_chip_version(&self, connection: &mut Connection) -> Result { 128 | self.read_efuse(connection, efuse::WAFER_VERSION_MAJOR) 129 | } 130 | 131 | #[cfg(feature = "serialport")] 132 | fn minor_chip_version(&self, connection: &mut Connection) -> Result { 133 | let hi = self.read_efuse(connection, efuse::WAFER_VERSION_MINOR_HI)?; 134 | let lo = self.read_efuse(connection, efuse::WAFER_VERSION_MINOR_LO)?; 135 | 136 | Ok((hi << 3) + lo) 137 | } 138 | 139 | #[cfg(feature = "serialport")] 140 | fn crystal_freq(&self, _connection: &mut Connection) -> Result { 141 | // The ESP32-S2's XTAL has a fixed frequency of 40MHz. 142 | Ok(XtalFrequency::_40Mhz) 143 | } 144 | 145 | #[cfg(feature = "serialport")] 146 | fn flash_write_size(&self, connection: &mut Connection) -> Result { 147 | use super::UsbOtg; 148 | 149 | Ok(if self.is_using_usb_otg(connection)? { 150 | MAX_USB_BLOCK_SIZE 151 | } else { 152 | FLASH_WRITE_SIZE 153 | }) 154 | } 155 | 156 | fn flash_image<'a>( 157 | &self, 158 | elf_data: &'a [u8], 159 | flash_data: FlashData, 160 | _chip_revision: Option<(u32, u32)>, 161 | xtal_freq: XtalFrequency, 162 | ) -> Result, Error> { 163 | if xtal_freq != XtalFrequency::_40Mhz { 164 | return Err(Error::UnsupportedFeature { 165 | chip: Chip::Esp32s2, 166 | feature: "the selected crystal frequency".into(), 167 | }); 168 | } 169 | 170 | IdfBootloaderFormat::new(elf_data, Chip::Esp32s2, flash_data, PARAMS) 171 | } 172 | 173 | #[cfg(feature = "serialport")] 174 | fn max_ram_block_size(&self, connection: &mut Connection) -> Result { 175 | use super::UsbOtg; 176 | 177 | Ok(if self.is_using_usb_otg(connection)? { 178 | MAX_USB_BLOCK_SIZE 179 | } else { 180 | MAX_RAM_BLOCK_SIZE 181 | }) 182 | } 183 | 184 | fn spi_registers(&self) -> SpiRegisters { 185 | SpiRegisters { 186 | base: 0x3f40_2000, 187 | usr_offset: 0x18, 188 | usr1_offset: 0x1C, 189 | usr2_offset: 0x20, 190 | w0_offset: 0x58, 191 | mosi_length_offset: Some(0x24), 192 | miso_length_offset: Some(0x28), 193 | } 194 | } 195 | 196 | fn supported_build_targets(&self) -> &[&str] { 197 | &["xtensa-esp32s2-none-elf", "xtensa-esp32s2-espidf"] 198 | } 199 | } 200 | 201 | #[cfg(feature = "serialport")] 202 | impl super::RtcWdtReset for Esp32s2 { 203 | fn wdt_wprotect(&self) -> u32 { 204 | 0x3F40_80AC 205 | } 206 | 207 | fn wdt_config0(&self) -> u32 { 208 | 0x3F40_8094 209 | } 210 | 211 | fn wdt_config1(&self) -> u32 { 212 | 0x3F40_8098 213 | } 214 | 215 | fn can_rtc_wdt_reset(&self, connection: &mut Connection) -> Result { 216 | const GPIO_STRAP: u32 = 0x3F40_4038; 217 | const OPTION1: u32 = 0x3F40_8128; 218 | const GPIO_STRAP_SPI_BOOT_MASK: u32 = 1 << 3; 219 | const FORCE_DOWNLOAD_BOOT_MASK: u32 = 0x1; 220 | 221 | Ok( 222 | connection.read_reg(GPIO_STRAP)? & GPIO_STRAP_SPI_BOOT_MASK == 0 // GPIO0 low 223 | && connection.read_reg(OPTION1)? & FORCE_DOWNLOAD_BOOT_MASK == 0, 224 | ) 225 | } 226 | } 227 | 228 | #[cfg(feature = "serialport")] 229 | impl super::UsbOtg for Esp32s2 { 230 | fn uartdev_buf_no(&self) -> u32 { 231 | 0x3FFF_FD14 232 | } 233 | 234 | fn uartdev_buf_no_usb_otg(&self) -> u32 { 235 | 2 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /espflash/src/targets/esp32s3.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Range; 2 | 3 | use super::{ 4 | Chip, 5 | Esp32Params, 6 | ReadEFuse, 7 | SpiRegisters, 8 | Target, 9 | XtalFrequency, 10 | efuse::esp32s3 as efuse, 11 | }; 12 | #[cfg(feature = "serialport")] 13 | use crate::connection::Connection; 14 | use crate::{ 15 | Error, 16 | flasher::{FlashData, FlashFrequency}, 17 | image_format::IdfBootloaderFormat, 18 | }; 19 | 20 | pub(crate) const CHIP_ID: u16 = 9; 21 | 22 | const CHIP_DETECT_MAGIC_VALUES: &[u32] = &[0x9]; 23 | 24 | const FLASH_RANGES: &[Range] = &[ 25 | 0x4200_0000..0x4400_0000, // IROM 26 | 0x3c00_0000..0x3e00_0000, // DROM 27 | ]; 28 | 29 | const PARAMS: Esp32Params = Esp32Params::new( 30 | 0x0, 31 | 0x1_0000, 32 | 0x10_0000, 33 | CHIP_ID, 34 | FlashFrequency::_40Mhz, 35 | include_bytes!("../../resources/bootloaders/esp32s3-bootloader.bin"), 36 | None, 37 | ); 38 | 39 | /// ESP32-S2 Target 40 | pub struct Esp32s3; 41 | 42 | impl Esp32s3 { 43 | #[cfg(feature = "serialport")] 44 | /// Return the major BLK version based on eFuses 45 | fn blk_version_major(&self, connection: &mut Connection) -> Result { 46 | self.read_efuse(connection, efuse::BLK_VERSION_MAJOR) 47 | } 48 | 49 | #[cfg(feature = "serialport")] 50 | /// Return the minor BLK version based on eFuses 51 | fn blk_version_minor(&self, connection: &mut Connection) -> Result { 52 | self.read_efuse(connection, efuse::BLK_VERSION_MINOR) 53 | } 54 | 55 | /// Check if the magic value contains the specified value 56 | pub fn has_magic_value(value: u32) -> bool { 57 | CHIP_DETECT_MAGIC_VALUES.contains(&value) 58 | } 59 | } 60 | 61 | impl ReadEFuse for Esp32s3 { 62 | fn efuse_reg(&self) -> u32 { 63 | 0x6000_7000 64 | } 65 | 66 | fn block0_offset(&self) -> u32 { 67 | 0x2D 68 | } 69 | 70 | fn block_size(&self, block: usize) -> u32 { 71 | efuse::BLOCK_SIZES[block] 72 | } 73 | } 74 | 75 | impl Target for Esp32s3 { 76 | fn chip(&self) -> Chip { 77 | Chip::Esp32s3 78 | } 79 | 80 | fn addr_is_flash(&self, addr: u32) -> bool { 81 | FLASH_RANGES.iter().any(|range| range.contains(&addr)) 82 | } 83 | 84 | #[cfg(feature = "serialport")] 85 | fn chip_features(&self, _connection: &mut Connection) -> Result, Error> { 86 | Ok(vec!["WiFi", "BLE"]) 87 | } 88 | 89 | #[cfg(feature = "serialport")] 90 | fn major_chip_version(&self, connection: &mut Connection) -> Result { 91 | // Workaround: The major version field was allocated to other purposes when 92 | // block version is v1.1. Luckily only chip v0.0 have this kind of block version 93 | // and efuse usage. 94 | if self.minor_chip_version(connection)? == 0 95 | && self.blk_version_major(connection)? == 1 96 | && self.blk_version_minor(connection)? == 1 97 | { 98 | Ok(0) 99 | } else { 100 | self.read_efuse(connection, efuse::WAFER_VERSION_MAJOR) 101 | } 102 | } 103 | 104 | #[cfg(feature = "serialport")] 105 | fn minor_chip_version(&self, connection: &mut Connection) -> Result { 106 | let hi = self.read_efuse(connection, efuse::WAFER_VERSION_MINOR_HI)?; 107 | let lo = self.read_efuse(connection, efuse::WAFER_VERSION_MINOR_LO)?; 108 | 109 | Ok((hi << 3) + lo) 110 | } 111 | 112 | #[cfg(feature = "serialport")] 113 | fn crystal_freq(&self, _connection: &mut Connection) -> Result { 114 | // The ESP32-S3's XTAL has a fixed frequency of 40MHz. 115 | Ok(XtalFrequency::_40Mhz) 116 | } 117 | 118 | fn flash_image<'a>( 119 | &self, 120 | elf_data: &'a [u8], 121 | flash_data: FlashData, 122 | _chip_revision: Option<(u32, u32)>, 123 | xtal_freq: XtalFrequency, 124 | ) -> Result, Error> { 125 | if xtal_freq != XtalFrequency::_40Mhz { 126 | return Err(Error::UnsupportedFeature { 127 | chip: Chip::Esp32s3, 128 | feature: "the selected crystal frequency".into(), 129 | }); 130 | } 131 | 132 | IdfBootloaderFormat::new(elf_data, Chip::Esp32s3, flash_data, PARAMS) 133 | } 134 | 135 | fn spi_registers(&self) -> SpiRegisters { 136 | SpiRegisters { 137 | base: 0x6000_2000, 138 | usr_offset: 0x18, 139 | usr1_offset: 0x1C, 140 | usr2_offset: 0x20, 141 | w0_offset: 0x58, 142 | mosi_length_offset: Some(0x24), 143 | miso_length_offset: Some(0x28), 144 | } 145 | } 146 | 147 | fn supported_build_targets(&self) -> &[&str] { 148 | &["xtensa-esp32s3-none-elf", "xtensa-esp32s3-espidf"] 149 | } 150 | } 151 | 152 | #[cfg(feature = "serialport")] 153 | impl super::RtcWdtReset for Esp32s3 { 154 | fn wdt_wprotect(&self) -> u32 { 155 | 0x6000_80B0 156 | } 157 | 158 | fn wdt_config0(&self) -> u32 { 159 | 0x6000_8098 160 | } 161 | 162 | fn wdt_config1(&self) -> u32 { 163 | 0x6000_809C 164 | } 165 | 166 | fn can_rtc_wdt_reset(&self, connection: &mut Connection) -> Result { 167 | const GPIO_STRAP: u32 = 0x6000_4038; 168 | const OPTION1: u32 = 0x6000_812C; 169 | const GPIO_STRAP_SPI_BOOT_MASK: u32 = 1 << 3; // Not download mode 170 | const FORCE_DOWNLOAD_BOOT_MASK: u32 = 0x1; 171 | 172 | Ok( 173 | connection.read_reg(GPIO_STRAP)? & GPIO_STRAP_SPI_BOOT_MASK == 0 // GPIO0 low 174 | && connection.read_reg(OPTION1)? & FORCE_DOWNLOAD_BOOT_MASK == 0, 175 | ) 176 | } 177 | } 178 | 179 | #[cfg(feature = "serialport")] 180 | impl super::UsbOtg for Esp32s3 { 181 | fn uartdev_buf_no(&self) -> u32 { 182 | 0x3FCE_F14C 183 | } 184 | 185 | fn uartdev_buf_no_usb_otg(&self) -> u32 { 186 | 3 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /espflash/src/targets/flash_target/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) use self::ram::MAX_RAM_BLOCK_SIZE; 2 | pub use self::{esp32::Esp32Target, ram::RamTarget}; 3 | use crate::{Error, connection::Connection, image_format::Segment}; 4 | 5 | mod esp32; 6 | mod ram; 7 | 8 | /// Progress update callbacks 9 | pub trait ProgressCallbacks { 10 | /// Initialize some progress report 11 | fn init(&mut self, addr: u32, total: usize); 12 | /// Update some progress report 13 | fn update(&mut self, current: usize); 14 | /// Finish some progress report 15 | fn finish(&mut self); 16 | } 17 | 18 | /// Operations for interacting with a flash target 19 | pub trait FlashTarget { 20 | /// Begin the flashing operation 21 | fn begin(&mut self, connection: &mut Connection) -> Result<(), Error>; 22 | 23 | /// Write a segment to the target device 24 | fn write_segment( 25 | &mut self, 26 | connection: &mut Connection, 27 | segment: Segment<'_>, 28 | progress: &mut Option<&mut dyn ProgressCallbacks>, 29 | ) -> Result<(), Error>; 30 | 31 | /// Complete the flashing operation 32 | fn finish(&mut self, connection: &mut Connection, reboot: bool) -> Result<(), Error>; 33 | } 34 | -------------------------------------------------------------------------------- /espflash/src/targets/flash_target/ram.rs: -------------------------------------------------------------------------------- 1 | use crate::{Error, image_format::Segment}; 2 | #[cfg(feature = "serialport")] 3 | use crate::{ 4 | connection::{ 5 | Connection, 6 | command::{Command, CommandType}, 7 | }, 8 | flasher::ProgressCallbacks, 9 | targets::FlashTarget, 10 | }; 11 | 12 | pub const MAX_RAM_BLOCK_SIZE: usize = 0x1800; 13 | 14 | /// Applications running in the target device's RAM 15 | #[derive(Debug)] 16 | pub struct RamTarget { 17 | entry: Option, 18 | block_size: usize, 19 | } 20 | 21 | impl RamTarget { 22 | pub fn new(entry: Option, block_size: usize) -> Self { 23 | RamTarget { entry, block_size } 24 | } 25 | } 26 | 27 | impl Default for RamTarget { 28 | fn default() -> Self { 29 | Self::new(None, MAX_RAM_BLOCK_SIZE) 30 | } 31 | } 32 | 33 | #[cfg(feature = "serialport")] 34 | impl FlashTarget for RamTarget { 35 | fn begin(&mut self, _connection: &mut Connection) -> Result<(), Error> { 36 | Ok(()) 37 | } 38 | 39 | fn write_segment( 40 | &mut self, 41 | connection: &mut Connection, 42 | segment: Segment<'_>, 43 | progress: &mut Option<&mut dyn ProgressCallbacks>, 44 | ) -> Result<(), Error> { 45 | let addr = segment.addr; 46 | 47 | let padding = 4 - segment.data.len() % 4; 48 | let block_count = (segment.data.len() + padding).div_ceil(self.block_size); 49 | 50 | connection.command(Command::MemBegin { 51 | size: segment.data.len() as u32, 52 | blocks: block_count as u32, 53 | block_size: self.block_size as u32, 54 | offset: addr, 55 | supports_encryption: false, 56 | })?; 57 | 58 | let chunks = segment.data.chunks(self.block_size); 59 | let num_chunks = chunks.len(); 60 | 61 | if let Some(cb) = progress.as_mut() { 62 | cb.init(addr, num_chunks) 63 | } 64 | 65 | for (i, block) in chunks.enumerate() { 66 | connection.command(Command::MemData { 67 | sequence: i as u32, 68 | pad_to: 4, 69 | pad_byte: 0, 70 | data: block, 71 | })?; 72 | 73 | if let Some(cb) = progress.as_mut() { 74 | cb.update(i + 1) 75 | } 76 | } 77 | 78 | if let Some(cb) = progress.as_mut() { 79 | cb.finish() 80 | } 81 | 82 | Ok(()) 83 | } 84 | 85 | fn finish(&mut self, connection: &mut Connection, reboot: bool) -> Result<(), Error> { 86 | if reboot { 87 | let entry = self.entry.unwrap_or_default(); 88 | connection.with_timeout(CommandType::MemEnd.timeout(), |connection| { 89 | connection.command(Command::MemEnd { 90 | no_entry: entry == 0, 91 | entry, 92 | }) 93 | })?; 94 | } 95 | 96 | Ok(()) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /espflash/tests/data/README.md: -------------------------------------------------------------------------------- 1 | The `$CHIP` elf files under this folder have been generated using `esp-generate@0.3.1`, `esp-hal@5c97eaf`, `esp-println@ab18d89`: 2 | 3 | ``` 4 | esp-generate --chip=$CHIP --headless $CHIP 5 | cd $CHIP 6 | cargo build --release 7 | ``` 8 | 9 | The `esp32c6_defmt` elf file under this folder has been generated using `esp-generate@0.3.1`, `esp-hal@5c97eaf`, `esp-println@ab18d89`: 10 | 11 | > TODO: this part needs to be updated, and the elf re-created once ESP-HAL beta.1 is out. 12 | 13 | ``` 14 | esp-generate --chip=esp32c6 -o defmt --headless esp32c6_defmt 15 | cd esp32c6_defmt 16 | DEFMT_LOG=info cargo build --release 17 | ``` 18 | -------------------------------------------------------------------------------- /espflash/tests/data/esp32: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esp-rs/espflash/b8eb162539cd95ed399243d30e9869eb407f265e/espflash/tests/data/esp32 -------------------------------------------------------------------------------- /espflash/tests/data/esp32c2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esp-rs/espflash/b8eb162539cd95ed399243d30e9869eb407f265e/espflash/tests/data/esp32c2 -------------------------------------------------------------------------------- /espflash/tests/data/esp32c3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esp-rs/espflash/b8eb162539cd95ed399243d30e9869eb407f265e/espflash/tests/data/esp32c3 -------------------------------------------------------------------------------- /espflash/tests/data/esp32c6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esp-rs/espflash/b8eb162539cd95ed399243d30e9869eb407f265e/espflash/tests/data/esp32c6 -------------------------------------------------------------------------------- /espflash/tests/data/esp32c6_defmt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esp-rs/espflash/b8eb162539cd95ed399243d30e9869eb407f265e/espflash/tests/data/esp32c6_defmt -------------------------------------------------------------------------------- /espflash/tests/data/esp32h2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esp-rs/espflash/b8eb162539cd95ed399243d30e9869eb407f265e/espflash/tests/data/esp32h2 -------------------------------------------------------------------------------- /espflash/tests/data/esp32s2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esp-rs/espflash/b8eb162539cd95ed399243d30e9869eb407f265e/espflash/tests/data/esp32s2 -------------------------------------------------------------------------------- /espflash/tests/data/esp32s3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esp-rs/espflash/b8eb162539cd95ed399243d30e9869eb407f265e/espflash/tests/data/esp32s3 -------------------------------------------------------------------------------- /espflash/tests/data/esp_idf_firmware_c6.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esp-rs/espflash/b8eb162539cd95ed399243d30e9869eb407f265e/espflash/tests/data/esp_idf_firmware_c6.elf -------------------------------------------------------------------------------- /espflash/tests/data/partitions.csv: -------------------------------------------------------------------------------- 1 | # Name, Type, SubType, Offset, Size, Flags 2 | nvs, data, nvs, 0x9000, 0x6000, 3 | phy_init, data, phy, 0xf000, 0x1000, 4 | factory, app, factory, 0x10000, 4M, -------------------------------------------------------------------------------- /espflash/tests/scripts/board-info.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Run the command and capture output and exit code 4 | result=$(espflash board-info) 5 | exit_code=$? 6 | echo "$result" 7 | 8 | # Extract chip type 9 | chip_type=$(awk -F': *' '/Chip type:/ {print $2}' <<< "$result" | awk '{print $1}') 10 | 11 | if [[ "$chip_type" == "esp32" ]]; then 12 | # ESP32 doesn't support get_security_info 13 | [[ $exit_code -eq 0 && "$result" =~ "Security features: None" ]] || { 14 | echo "Expected Security features: None" 15 | exit 1 16 | } 17 | else 18 | # Non-ESP32 should contain required info 19 | [[ $exit_code -eq 0 && "$result" =~ "Security Information:" && "$result" =~ "Flags" ]] || { 20 | echo "Expected 'Security Information:' and 'Flags' in output but did not find them" 21 | exit 1 22 | } 23 | fi 24 | -------------------------------------------------------------------------------- /espflash/tests/scripts/checksum-md5.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | result=$(espflash erase-flash 2>&1) 4 | echo "$result" 5 | if [[ ! $result =~ "Flash has been erased!" ]]; then 6 | exit 1 7 | fi 8 | result=$(espflash checksum-md5 0x1000 0x100 2>&1) 9 | echo "$result" 10 | if [[ ! $result =~ "0x827f263ef9fb63d05499d14fcef32f60" ]]; then 11 | exit 1 12 | fi 13 | -------------------------------------------------------------------------------- /espflash/tests/scripts/erase-flash.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | result=$(espflash erase-flash 2>&1) 4 | echo "$result" 5 | if [[ ! $result =~ "Flash has been erased!" ]]; then 6 | exit 1 7 | fi 8 | result=$(espflash read-flash 0 0x4000 flash_content.bin 2>&1) 9 | echo "$result" 10 | if [[ ! $result =~ "Flash content successfully read and written to" ]]; then 11 | exit 1 12 | fi 13 | echo "Checking if flash is empty" 14 | if hexdump -v -e '/1 "%02x"' "flash_content.bin" | grep -qv '^ff*$'; then 15 | exit 1 16 | fi 17 | echo "Flash is empty!" 18 | -------------------------------------------------------------------------------- /espflash/tests/scripts/erase-region.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Function to check expected failure for unaligned erase arguments 4 | check_unaligned_erase() { 5 | local address=$1 6 | local size=$2 7 | result=$(espflash erase-region "$address" "$size" 2>&1) 8 | echo "$result" 9 | 10 | if [[ $result =~ "Invalid `address`" ]]; then 11 | echo "Unaligned erase correctly rejected: address=$address, size=$size" 12 | else 13 | echo "Test failed: unaligned erase was not rejected!" 14 | exit 1 15 | fi 16 | } 17 | 18 | # Unaligned address (not a multiple of 4096) 19 | check_unaligned_erase 0x1001 0x1000 20 | 21 | # Unaligned size (not a multiple of 4096) 22 | check_unaligned_erase 0x1000 0x1001 23 | 24 | # Both address and size unaligned 25 | check_unaligned_erase 0x1003 0x1005 26 | 27 | # Valid erase - should succeed 28 | result=$(espflash erase-region 0x1000 0x1000 2>&1) 29 | echo "$result" 30 | if [[ ! $result =~ "Erasing region at" ]]; then 31 | exit 1 32 | fi 33 | result=$(espflash read-flash 0x1000 0x2000 flash_content.bin 2>&1) 34 | echo "$result" 35 | if [[ ! $result =~ "Flash content successfully read and written to" ]]; then 36 | echo "Failed to read flash contents" 37 | exit 1 38 | fi 39 | # Check first 0x1000 bytes are FF 40 | if head -c 4096 flash_content.bin | hexdump -v -e '/1 "%02x"' | grep -qv '^ff*$'; then 41 | echo "First 0x1000 bytes should be empty (FF)" 42 | exit 1 43 | fi 44 | # Check next 0x1000 bytes contain some non-FF bytes 45 | if ! tail -c 4096 flash_content.bin | hexdump -v -e '/1 "%02x"' | grep -q '[0-e]'; then 46 | echo "Next 0x1000 bytes should contain some non-FF bytes" 47 | exit 1 48 | fi 49 | echo "Flash contents verified!" 50 | -------------------------------------------------------------------------------- /espflash/tests/scripts/flash.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | app="espflash/tests/data/$1" 3 | part_table="espflash/tests/data/partitions.csv" 4 | 5 | # espflash should not flash a partition table that is too big 6 | result=$(timeout 15s espflash flash --no-skip --monitor --non-interactive $app --flash-size 2mb --partition-table $part_table 2>&1) 7 | echo "$result" 8 | if [[ $result =~ "Flashing has completed!" ]]; then 9 | echo "Flashing should have failed!" 10 | exit 1 11 | fi 12 | if [[ $result =~ "espflash::partition_table::does_not_fit" ]]; then 13 | echo "Flashing failed as expected!" 14 | else 15 | echo "Flashing has failed but not with the expected error!" 16 | exit 1 17 | fi 18 | 19 | if [[ "$1" == "esp32c6" ]]; then 20 | # With manual log-format 21 | app_defmt="${app}_defmt" 22 | result=$(timeout 15s espflash flash --no-skip --monitor --non-interactive $app_defmt --log-format defmt 2>&1) 23 | echo "$result" 24 | if [[ ! $result =~ "Flashing has completed!" ]]; then 25 | echo "Flashing failed!" 26 | exit 1 27 | fi 28 | if ! echo "$result" | grep -q "Hello world!"; then 29 | echo "Monitoring failed!" 30 | exit 1 31 | fi 32 | 33 | # With auto-detected log-format 34 | result=$(timeout 15s espflash flash --no-skip --monitor --non-interactive $app_defmt 2>&1) 35 | echo "$result" 36 | if [[ ! $result =~ "Flashing has completed!" ]]; then 37 | echo "Flashing failed!" 38 | exit 1 39 | fi 40 | if ! echo "$result" | grep -q "Hello world!"; then 41 | echo "Monitoring failed!" 42 | exit 1 43 | fi 44 | fi 45 | 46 | result=$(timeout 15s espflash flash --no-skip --monitor --non-interactive $app 2>&1) 47 | echo "$result" 48 | if [[ ! $result =~ "Flashing has completed!" ]]; then 49 | echo "Flashing failed!" 50 | exit 1 51 | fi 52 | if ! echo "$result" | grep -q "Hello world!"; then 53 | echo "Monitoring failed!" 54 | exit 1 55 | fi 56 | -------------------------------------------------------------------------------- /espflash/tests/scripts/hold-in-reset.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | result=$(espflash hold-in-reset 2>&1) 4 | echo "$result" 5 | if [[ ! $result =~ "Holding target device in reset" ]]; then 6 | exit 1 7 | fi 8 | -------------------------------------------------------------------------------- /espflash/tests/scripts/list-ports.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | result=$(espflash list-ports 2>&1) 4 | echo "$result" 5 | if [[ ! $result =~ "Silicon Labs" ]]; then 6 | exit 1 7 | fi 8 | -------------------------------------------------------------------------------- /espflash/tests/scripts/monitor.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo "Monitoring..." 4 | result=$(timeout 5s espflash monitor --non-interactive || true) 5 | echo "$result" 6 | if ! echo "$result" | grep -q "Hello world!"; then 7 | exit 1 8 | fi 9 | -------------------------------------------------------------------------------- /espflash/tests/scripts/read-flash.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | KNOWN_PATTERN=$'\x01\xa0\x02\xB3\x04\xC4\x08\xD5\x10\xE6\x20\xF7\x40\x88\x50\x99' 4 | KNOWN_PATTERN+=$'\x60\xAA\x70\xBB\x80\xCC\x90\xDD\xA0\xEE\xB0\xFF\xC0\x11\xD0\x22' 5 | KNOWN_PATTERN+=$'\xE0\x33\xF0\x44\x05\x55\x15\x66\x25\x77\x35\x88\x45\x99\x55\xAA' 6 | KNOWN_PATTERN+=$'\x65\xBB\x75\xCC\x85\xDD\x95\xEE\xA5\xFF\xB5\x00\xC5\x11\xD5\x22' 7 | KNOWN_PATTERN+=$'\xE5\x33\xF5\x44\x06\x55\x16\x66\x26\x77\x36\x88\x46\x99\x56\xAA' 8 | KNOWN_PATTERN+=$'\x66\xBB\x76\xCC\x86\xDD\x96\xEE\xA6\xFF\xB6\x00\xC6\x11\xD6\x22' 9 | 10 | echo -ne "$KNOWN_PATTERN" > pattern.bin 11 | result=$(espflash write-bin 0x0 pattern.bin 2>&1) 12 | echo "$result" 13 | if [[ ! $result =~ "Binary successfully written to flash!" ]]; then 14 | echo "Failed to write binary to flash" 15 | exit 1 16 | fi 17 | 18 | lengths=(2 5 10 26 44 86) 19 | 20 | for len in "${lengths[@]}"; do 21 | echo "Testing read-flash with length: $len" 22 | 23 | result=$(espflash read-flash 0 "$len" flash_content.bin 2>&1) 24 | echo "$result" 25 | if [[ ! $result =~ "Flash content successfully read and written to" ]]; then 26 | echo "Failed to read $len bytes from flash" 27 | exit 1 28 | fi 29 | 30 | EXPECTED=$(echo -ne "$KNOWN_PATTERN" | head -c "$len") 31 | 32 | if ! cmp -s <(echo -ne "$EXPECTED") flash_content.bin; then 33 | echo "Verification failed: content does not match expected for length" 34 | exit 1 35 | fi 36 | 37 | echo "Testing ROM read-flash with length: $len" 38 | result=$(espflash read-flash --no-stub 0 "$len" flash_content.bin 2>&1) 39 | echo "$result" 40 | 41 | if ! cmp -s <(echo -ne "$EXPECTED") flash_content.bin; then 42 | echo "Verification failed: content does not match expected for length" 43 | exit 1 44 | fi 45 | 46 | if [[ ! $result =~ "Flash content successfully read and written to" ]]; then 47 | echo "Failed to read $len bytes from flash" 48 | exit 1 49 | fi 50 | 51 | done 52 | 53 | echo "All read-flash tests passed!" 54 | -------------------------------------------------------------------------------- /espflash/tests/scripts/reset.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | result=$(espflash reset 2>&1) 4 | echo "$result" 5 | if [[ ! $result =~ "Resetting target device" ]]; then 6 | exit 1 7 | fi 8 | -------------------------------------------------------------------------------- /espflash/tests/scripts/save-image_write-bin.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | app="espflash/tests/data/$1" 3 | 4 | # if $1 is esp32c2, create an variable that contains `-x 26mhz` 5 | if [[ $1 == "esp32c2" ]]; then 6 | freq="-x 26mhz" 7 | fi 8 | 9 | result=$(espflash save-image --merge --chip $1 $freq $app app.bin 2>&1) 10 | echo "$result" 11 | if [[ ! $result =~ "Image successfully saved!" ]]; then 12 | exit 1 13 | fi 14 | echo "Writing binary" 15 | result=$(timeout 45 espflash write-bin --monitor 0x0 app.bin --non-interactive 2>&1) 16 | echo "$result" 17 | if [[ ! $result =~ "Binary successfully written to flash!" ]]; then 18 | exit 1 19 | fi 20 | 21 | if ! echo "$result" | grep -q "Hello world!"; then 22 | exit 1 23 | fi 24 | 25 | if [[ $1 == "esp32c6" ]]; then 26 | # Regression test for https://github.com/esp-rs/espflash/issues/741 27 | app="espflash/tests/data/esp_idf_firmware_c6.elf" 28 | 29 | result=$(espflash save-image --merge --chip esp32c6 $app app.bin 2>&1) 30 | echo "$result" 31 | if [[ ! $result =~ "Image successfully saved!" ]]; then 32 | exit 1 33 | fi 34 | 35 | echo "Checking that app descriptor is first" 36 | # Read 4 bytes from 0x10020, it needs to be 0xABCD5432 37 | 38 | app_descriptor=$(xxd -p -s 0x10020 -l 4 app.bin) 39 | if [[ $app_descriptor != "3254cdab" ]]; then 40 | echo "App descriptor magic word is not correct: $app_descriptor" 41 | exit 1 42 | fi 43 | fi 44 | -------------------------------------------------------------------------------- /espflash/tests/scripts/write-bin.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | part_table="espflash/tests/data/partitions.csv" 3 | 4 | # https://github.com/esp-rs/espflash/issues/622 reproducer 5 | echo -ne "\x01\xa0" >binary_file.bin 6 | result=$(espflash write-bin 0x0 binary_file.bin 2>&1) 7 | echo "$result" 8 | if [[ ! $result =~ "Binary successfully written to flash!" ]]; then 9 | echo "Failed to write binary" 10 | exit 1 11 | fi 12 | 13 | result=$(espflash read-flash 0 64 flash_content.bin 2>&1) 14 | echo "$result" 15 | if [[ ! $result =~ "Flash content successfully read and written to" ]]; then 16 | echo "Failed to read flash content" 17 | exit 1 18 | fi 19 | # Check that the flash_content.bin contains the '01 a0' bytes 20 | if ! grep -q -a -F $'\x01\xa0' flash_content.bin; then 21 | echo "Failed verifying content" 22 | exit 1 23 | fi 24 | 25 | result=$(espflash write-bin nvs binary_file.bin --partition-table $part_table 2>&1) 26 | echo "$result" 27 | if [[ ! $result =~ "Binary successfully written to flash!" ]]; then 28 | echo "Failed to write binary to the nvs partition label" 29 | exit 1 30 | fi 31 | 32 | result=$(espflash read-flash 0x9000 64 flash_content.bin 2>&1) 33 | echo "$result" 34 | if [[ ! $result =~ "Flash content successfully read and written to" ]]; then 35 | echo "Failed to read flash content" 36 | exit 1 37 | fi 38 | # Check that the flash_content.bin contains the '01 a0' bytes 39 | if ! grep -q -a -F $'\x01\xa0' flash_content.bin; then 40 | echo "Failed verifying content" 41 | exit 1 42 | fi 43 | -------------------------------------------------------------------------------- /pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Exit immediately on first non-zero status from a command in the script. 4 | set -o errexit 5 | 6 | # Ensure all tools being used are available. 7 | command -v "cargo" >/dev/null 2>&1 || { echo >&2 "The 'cargo' command is not installed, exiting"; exit 1; } 8 | command -v "rustfmt" >/dev/null 2>&1 || { echo >&2 "The 'rustfmt' command is not installed, exiting"; exit 1; } 9 | 10 | # Check the formatting of all Rust code in the workspace. 11 | cargo fmt --all -- --check 12 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | # Comments 2 | format_code_in_doc_comments = true 3 | normalize_comments = true 4 | wrap_comments = true 5 | 6 | # Imports 7 | group_imports = "StdExternalCrate" 8 | imports_granularity = "Crate" 9 | imports_layout = "HorizontalVertical" 10 | -------------------------------------------------------------------------------- /taplo.toml: -------------------------------------------------------------------------------- 1 | [formatting] 2 | align_entries = true 3 | allowed_blank_lines = 1 4 | column_width = 100 5 | reorder_arrays = true 6 | -------------------------------------------------------------------------------- /xtask/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xtask" 3 | version = "0.0.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [dependencies] 8 | chrono = "0.4.40" 9 | clap = { version = "4.5.36", features = ["derive"] } 10 | env_logger = "0.11.8" 11 | log = "0.4.27" 12 | serde = { version = "1.0.217", features = ["derive"] } 13 | serde_yaml = "0.9.34" 14 | -------------------------------------------------------------------------------- /xtask/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | cmp::Ordering, 3 | collections::{BTreeMap, HashMap}, 4 | ffi::OsStr, 5 | fs::{self, OpenOptions}, 6 | io::{BufWriter, Write}, 7 | path::{Path, PathBuf}, 8 | process::Command, 9 | }; 10 | 11 | use clap::{Args, Parser}; 12 | 13 | type Result = std::result::Result>; 14 | 15 | // ---------------------------------------------------------------------------- 16 | // Command-line Interface 17 | 18 | #[derive(Debug, Parser)] 19 | enum Cli { 20 | /// Generate eFuse field definitions 21 | GenerateEfuseFields(GenerateEfuseFieldsArgs), 22 | } 23 | 24 | #[derive(Debug, Args)] 25 | struct GenerateEfuseFieldsArgs { 26 | /// Local path to the `esptool` repository 27 | esptool_path: PathBuf, 28 | } 29 | 30 | // ---------------------------------------------------------------------------- 31 | // Application 32 | 33 | fn main() -> Result<()> { 34 | env_logger::Builder::new() 35 | .filter_module("xtask", log::LevelFilter::Info) 36 | .init(); 37 | 38 | // The directory containing the cargo manifest for the 'xtask' package is a 39 | // subdirectory within the cargo workspace: 40 | let workspace = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 41 | let workspace = workspace.parent().unwrap().canonicalize()?; 42 | 43 | match Cli::parse() { 44 | Cli::GenerateEfuseFields(args) => generate_efuse_fields(&workspace, args), 45 | } 46 | } 47 | 48 | // ---------------------------------------------------------------------------- 49 | // Generate eFuse Fields 50 | 51 | const HEADER: &str = r#" 52 | //! This file was automatically generated, please do not edit it manually! 53 | //! 54 | //! Generated: $DATE 55 | //! Version: $VERSION 56 | 57 | #![allow(unused)] 58 | 59 | use super::EfuseField; 60 | 61 | "#; 62 | 63 | type EfuseFields = HashMap; 64 | 65 | #[derive(Debug, serde::Deserialize)] 66 | struct EfuseYaml { 67 | #[serde(rename = "VER_NO")] 68 | version: String, 69 | #[serde(rename = "EFUSES")] 70 | fields: HashMap, 71 | } 72 | 73 | #[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize)] 74 | struct EfuseAttrs { 75 | #[serde(rename = "blk")] 76 | block: u32, 77 | word: u32, 78 | len: u32, 79 | start: u32, 80 | #[serde(rename = "desc")] 81 | description: String, 82 | } 83 | 84 | impl PartialOrd for EfuseAttrs { 85 | fn partial_cmp(&self, other: &Self) -> Option { 86 | Some(self.cmp(other)) 87 | } 88 | } 89 | 90 | impl Ord for EfuseAttrs { 91 | fn cmp(&self, other: &Self) -> Ordering { 92 | match self.block.cmp(&other.block) { 93 | Ordering::Equal => {} 94 | ord => return ord, 95 | } 96 | 97 | match self.word.cmp(&other.word) { 98 | Ordering::Equal => {} 99 | ord => return ord, 100 | } 101 | 102 | self.start.cmp(&other.start) 103 | } 104 | } 105 | 106 | fn generate_efuse_fields(workspace: &Path, args: GenerateEfuseFieldsArgs) -> Result<()> { 107 | let efuse_yaml_path = args 108 | .esptool_path 109 | .join("espefuse") 110 | .join("efuse_defs") 111 | .canonicalize()?; 112 | 113 | let espflash_path = workspace.join("espflash").canonicalize()?; 114 | 115 | let mut efuse_fields = parse_efuse_fields(&efuse_yaml_path)?; 116 | process_efuse_definitions(&mut efuse_fields)?; 117 | generate_efuse_definitions(&espflash_path, efuse_fields)?; 118 | 119 | Command::new("cargo") 120 | .args(["+nightly", "fmt"]) 121 | .current_dir(workspace) 122 | .output()?; 123 | 124 | Ok(()) 125 | } 126 | 127 | fn parse_efuse_fields(efuse_yaml_path: &Path) -> Result { 128 | // TODO: We can probably handle this better, e.g. by defining a `Chip` enum 129 | // which can be iterated over, but for now this is good enough. 130 | const CHIPS: &[&str] = &[ 131 | "esp32", "esp32c2", "esp32c3", "esp32c5", "esp32c6", "esp32h2", "esp32p4", "esp32s2", 132 | "esp32s3", 133 | ]; 134 | 135 | let mut efuse_fields = EfuseFields::new(); 136 | 137 | for result in fs::read_dir(efuse_yaml_path)? { 138 | let path = result?.path(); 139 | if path.extension().is_none_or(|ext| ext != OsStr::new("yaml")) { 140 | continue; 141 | } 142 | 143 | let chip = path.file_stem().unwrap().to_string_lossy().to_string(); 144 | if !CHIPS.contains(&chip.as_str()) { 145 | continue; 146 | } 147 | 148 | let efuse_yaml = fs::read_to_string(&path)?; 149 | let efuse_yaml: EfuseYaml = serde_yaml::from_str(&efuse_yaml)?; 150 | 151 | efuse_fields.insert(chip.to_string(), efuse_yaml); 152 | } 153 | 154 | Ok(efuse_fields) 155 | } 156 | 157 | fn process_efuse_definitions(efuse_fields: &mut EfuseFields) -> Result<()> { 158 | // This is all a special case for the MAC field, which is larger than a single 159 | // word (i.e. 32-bits) in size. To handle this, we just split it up into two 160 | // separate fields, and update the fields' attributes accordingly. 161 | for yaml in (*efuse_fields).values_mut() { 162 | let mac_attrs = yaml.fields.get("MAC").unwrap(); 163 | 164 | let mut mac0_attrs = mac_attrs.clone(); 165 | mac0_attrs.len = 32; 166 | 167 | let mut mac1_attrs = mac_attrs.clone(); 168 | mac1_attrs.start = mac0_attrs.start + 32; 169 | mac1_attrs.word = mac1_attrs.start / 32; 170 | mac1_attrs.len = 16; 171 | 172 | yaml.fields.remove("MAC").unwrap(); 173 | yaml.fields.insert("MAC0".into(), mac0_attrs); 174 | yaml.fields.insert("MAC1".into(), mac1_attrs); 175 | } 176 | 177 | // The ESP32-S2 seems to be missing a reserved byte at the end of BLOCK0 178 | // (Or, something else weird is going on). 179 | efuse_fields.entry("esp32s2".into()).and_modify(|yaml| { 180 | yaml.fields 181 | .entry("RESERVED_0_162".into()) 182 | .and_modify(|field| field.len = 30); 183 | }); 184 | 185 | Ok(()) 186 | } 187 | 188 | fn generate_efuse_definitions(espflash_path: &Path, efuse_fields: EfuseFields) -> Result<()> { 189 | let targets_efuse_path = espflash_path 190 | .join("src") 191 | .join("targets") 192 | .join("efuse") 193 | .canonicalize()?; 194 | 195 | for (chip, yaml) in efuse_fields { 196 | let f = OpenOptions::new() 197 | .create(true) 198 | .write(true) 199 | .truncate(true) 200 | .open(targets_efuse_path.join(format!("{chip}.rs")))?; 201 | 202 | let mut writer = BufWriter::new(f); 203 | 204 | write!( 205 | writer, 206 | "{}", 207 | HEADER 208 | .replace( 209 | "$DATE", 210 | &chrono::Utc::now().format("%Y-%m-%d %H:%M").to_string() 211 | ) 212 | .replace("$VERSION", &yaml.version) 213 | .trim_start() 214 | )?; 215 | 216 | generate_efuse_block_sizes(&mut writer, &yaml.fields)?; 217 | generate_efuse_constants(&mut writer, &yaml.fields)?; 218 | } 219 | 220 | Ok(()) 221 | } 222 | 223 | fn generate_efuse_block_sizes( 224 | writer: &mut dyn Write, 225 | fields: &HashMap, 226 | ) -> Result<()> { 227 | let mut field_attrs = fields.values().collect::>(); 228 | field_attrs.sort(); 229 | 230 | let block_sizes = field_attrs 231 | .chunk_by(|a, b| a.block == b.block) 232 | .enumerate() 233 | .map(|(block, attrs)| { 234 | let last = attrs.last().unwrap(); 235 | let size_bits = last.start + last.len; 236 | assert!(size_bits % 8 == 0); 237 | 238 | (block, size_bits / 8) 239 | }) 240 | .collect::>(); 241 | 242 | writeln!(writer, "/// Total size in bytes of each block")?; 243 | writeln!( 244 | writer, 245 | "pub(crate) const BLOCK_SIZES: &[u32] = &[{}];\n", 246 | block_sizes 247 | .values() 248 | .map(|v| v.to_string()) 249 | .collect::>() 250 | .join(", ") 251 | )?; 252 | 253 | Ok(()) 254 | } 255 | 256 | fn generate_efuse_constants( 257 | writer: &mut dyn Write, 258 | fields: &HashMap, 259 | ) -> Result<()> { 260 | let mut sorted = fields.iter().collect::>(); 261 | sorted.sort_by(|a, b| (a.1).cmp(b.1)); 262 | 263 | for (name, attrs) in sorted { 264 | let EfuseAttrs { 265 | block, 266 | word, 267 | len, 268 | start, 269 | description, 270 | } = attrs; 271 | 272 | writeln!(writer, "/// {description}")?; 273 | writeln!( 274 | writer, 275 | "pub(crate) const {}: EfuseField = EfuseField::new({}, {}, {}, {});", 276 | name, block, word, start, len 277 | )?; 278 | } 279 | 280 | Ok(()) 281 | } 282 | --------------------------------------------------------------------------------