diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 00000000..43ca37f1 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,5 @@ +[target.aarch64-unknown-linux-gnu] +linker = "aarch64-linux-gnu-gcc" + +[alias] +regress = "run -p svd2rust-regress --" diff --git a/.github/bors.toml b/.github/bors.toml deleted file mode 100644 index 78caad29..00000000 --- a/.github/bors.toml +++ /dev/null @@ -1,8 +0,0 @@ -block_labels = ["needs-decision", "S-waiting-on-team"] -delete_merged_branches = true -required_approvals = 1 -status = [ - "Rustfmt", - "CI", -] -timeout_sec = 14400 diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index c0d72f31..87d77ef4 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -1,5 +1,6 @@ on: pull_request_target: + types: [labeled, unlabeled, opened, synchronize, reopened] name: Changelog check @@ -9,10 +10,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout sources - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Changelog updated - uses: Zomzog/changelog-checker@v1.2.0 + uses: Zomzog/changelog-checker@v1.3.0 with: fileName: CHANGELOG.md env: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5add5af5..92d9873a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,7 +1,8 @@ on: push: - branches: [ staging, trying, master ] + branches: [master] pull_request: + merge_group: name: Continuous integration @@ -9,85 +10,99 @@ jobs: ci: name: CI runs-on: ubuntu-latest - needs: [check, ci-linux, ci-clippy, ci-serde] + needs: [check, ci-linux, ci-docs-clippy, ci-serde] + if: always() steps: - name: Done - run: exit 0 + run: jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' check: - name: Cargo check + name: Cargo check / clippy runs-on: ubuntu-latest strategy: matrix: - TARGET: [x86_64-unknown-linux-gnu, x86_64-apple-darwin, x86_64-pc-windows-msvc] + TARGET: + [ + x86_64-unknown-linux-gnu, + x86_64-apple-darwin, + x86_64-pc-windows-msvc, + ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - uses: actions-rs/toolchain@v1 + - uses: dtolnay/rust-toolchain@master with: - profile: minimal toolchain: stable - override: true - target: ${{ matrix.TARGET }} + targets: ${{ matrix.TARGET }} + components: clippy - name: Cache Dependencies uses: Swatinem/rust-cache@v2 with: key: ${{ matrix.TARGET }} - - uses: actions-rs/cargo@v1 - with: - command: check - args: --target ${{ matrix.TARGET }} + - run: cargo check --target ${{ matrix.TARGET }} --no-default-features + env: + RUSTFLAGS: -D warnings + - run: cargo check --target ${{ matrix.TARGET }} + env: + RUSTFLAGS: -D warnings + - run: cargo clippy --target ${{ matrix.TARGET }} ci-linux: runs-on: ubuntu-latest needs: [check] strategy: + fail-fast: false matrix: - # All generated code should be running on stable now - rust: [stable] - - # All vendor files we want to test on stable - vendor: [Atmel, Freescale, Fujitsu, GD32, Holtek, Microchip, Nordic, Nuvoton, NXP, RISC-V, SiliconLabs, Spansion, STMicro, Toshiba] - - # Options are all, none, strict and const - options: [all, none] - exclude: - - vendor: Fujitsu - options: all - - vendor: Spansion - options: all - - vendor: STMicro - options: all - - vendor: Nuvoton - options: all - - vendor: Microchip - options: all - - vendor: RISC-V - options: all include: - # Test MSRV - - rust: 1.60.0 - vendor: Nordic - - # Use nightly for architectures which don't support stable - - rust: nightly - vendor: OTHER - - # Use nightly for architectures which don't support stable - - rust: nightly - vendor: Espressif + - { vendor: Atmel } + - { vendor: Atmel, options: "-- --strict --atomics" } + - { vendor: Freescale } + - { vendor: Freescale, options: "-- --strict --atomics" } + - { vendor: Fujitsu } + - { vendor: Fujitsu, options: "-- --atomics" } + - { vendor: Holtek } + - { vendor: Holtek, options: "-- --strict --atomics" } + - { vendor: Atmel } + - { vendor: Atmel, options: "-- --strict --atomics" } + - { vendor: Microchip } + - { vendor: Microchip, options: "-- --atomics" } + - { vendor: Nordic } + - { vendor: Nordic, options: "-- --strict --atomics" } + - { vendor: Nuvoton } + - { vendor: Nuvoton, options: "-- --atomics" } + - { vendor: NXP } + - { vendor: NXP, options: "-- --strict --atomics" } + - { vendor: SiFive } + - { vendor: SiFive, options: "-- --atomics" } + - { vendor: SiliconLabs, options: "" } + - { vendor: SiliconLabs, options: "-- --strict --atomics" } + - { vendor: Spansion } + - { vendor: Spansion, options: "-- --atomics" } + - { vendor: STMicro } + - { vendor: STMicro, options: "-- --atomics" } + - { + vendor: STMicro, + options: "-- --strict -f enum_value::p: --max-cluster-size --atomics --atomics-feature atomics --impl-debug --impl-defmt defmt", + } + - { vendor: Toshiba } + - { vendor: Toshiba, options: "-- --strict --atomics" } + - { vendor: TexasInstruments } + - { vendor: TexasInstruments, options: "-- --atomics" } + - { vendor: Espressif } + - { vendor: Espressif, options: "-- --atomics" } + - { vendor: Vorago } + - { vendor: Vorago, options: "-- --strict --atomics" } + - { vendor: Renesas } + - { vendor: RaspberryPi } + - { vendor: RaspberryPi, options: "-- --atomics" } steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: ${{ matrix.rust }} - override: true + - uses: dtolnay/rust-toolchain@stable - name: Cache uses: Swatinem/rust-cache@v2 @@ -96,24 +111,52 @@ jobs: run: | cargo install svd2rust --path . - - name: Run CI script for `${{ matrix.vendor }}` under rust `${{ matrix.rust }}` with options=`${{ matrix.options }}` - env: - VENDOR: ${{ matrix.vendor }} - OPTIONS: ${{ matrix.options }} - COMMAND: check - run: bash ci/script.sh + - name: Run regression tool + run: cargo regress tests -m ${{ matrix.vendor }} ${{ matrix.options }} - ci-clippy: + ci-msrv-check: runs-on: ubuntu-latest needs: [check] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true + - name: Self install + run: | + cargo install svd2rust --path . + + # Install the MSRV toolchain + - uses: dtolnay/rust-toolchain@1.76.0 + - name: Run reression tool with MSRV + # The MSRV only applies to the generated crate. The regress tool should still be run with + # stable. + run: cargo +stable regress tests --toolchain 1.76.0 -m Nordic -- --strict --atomics + + ci-docs-clippy: + runs-on: ubuntu-latest + needs: [check] + strategy: + fail-fast: false + matrix: + include: + # STMicro + - { chip: STM32F030 } + - { chip: STM32F410 } + - { chip: STM32L1xx } + # Espressif + - { chip: esp32c3 } + # Freescale + - { chip: MKW22D5 } + - { chip: MK02F12810 } + # Silicon Labs + - { chip: SIM3L1x8_SVD } + # Nordic chips + - { chip: nrf51, options: "-- -f register_mod::s:_mod" } + - { chip: nrf52, options: "-- -f register_mod::s:_mod" } + steps: + - uses: actions/checkout@v4 + + - uses: dtolnay/rust-toolchain@nightly + - uses: dtolnay/rust-toolchain@stable - name: Cache uses: Swatinem/rust-cache@v2 @@ -122,32 +165,26 @@ jobs: run: | cargo install svd2rust --path . - - name: Run CI script - env: - VENDOR: RISC-V - OPTIONS: all - COMMAND: clippy - run: bash ci/script.sh + - name: Check docs and clippy on generated PACs + run: cargo regress test -c ${{ matrix.chip }} --docs-stable --docs-nightly --clippy ${{ matrix.options }} ci-serde: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - uses: actions-rs/toolchain@v1 + - uses: dtolnay/rust-toolchain@master with: - profile: minimal toolchain: stable - override: true - name: Cache uses: Swatinem/rust-cache@v2 with: - key: svdtools-0.2.3 + key: svdtools-0.4.6 - name: Install svdtools run: | - cargo install svdtools --version 0.2.3 --target-dir target + cargo install svdtools --version 0.4.6 --target-dir target - name: Run CI script run: | @@ -159,19 +196,51 @@ jobs: name: Rustfmt runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - uses: actions-rs/toolchain@v1 + - uses: dtolnay/rust-toolchain@master with: - profile: minimal toolchain: stable - override: true components: rustfmt - name: Cache Dependencies uses: Swatinem/rust-cache@v2 - - uses: actions-rs/cargo@v1 + - run: cargo fmt --all -- --check + + artifact: + name: Build svd2rust artifact + if: github.event_name == 'pull_request' + needs: [check] + runs-on: ${{ matrix.runs-on }} + strategy: + matrix: + include: + - target: x86_64-unknown-linux-gnu + runs-on: ubuntu-latest + - target: aarch64-apple-darwin + runs-on: macos-latest + - target: x86_64-pc-windows-msvc + runs-on: windows-latest + suffix: .exe + steps: + - uses: actions/checkout@v4 + + - uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + targets: ${{ matrix.target }} + + - name: Cache Dependencies + uses: Swatinem/rust-cache@v2 + + - name: Build svd2rust artifact + run: cargo build --release --target ${{ matrix.target }} + + - run: mv target/${{ matrix.target }}/release/svd2rust${{ matrix.suffix || '' }} svd2rust-${{ matrix.target }}-$(git rev-parse --short HEAD)${{ matrix.suffix || '' }} + + - name: Upload artifact + uses: actions/upload-artifact@v4 with: - command: fmt - args: --all -- --check + name: artifact-svd2rust-${{ matrix.target }} + path: svd2rust-${{ matrix.target }}* diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml deleted file mode 100644 index 993b8b64..00000000 --- a/.github/workflows/clippy.yml +++ /dev/null @@ -1,22 +0,0 @@ -on: [push, pull_request] - -name: Clippy check -jobs: - clippy_check: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - components: clippy - - - name: Cache Dependencies - uses: Swatinem/rust-cache@v2 - - - uses: actions-rs/clippy-check@v1 - with: - token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/diff.yml b/.github/workflows/diff.yml new file mode 100644 index 00000000..a055ddec --- /dev/null +++ b/.github/workflows/diff.yml @@ -0,0 +1,83 @@ +name: Diff +on: + issue_comment: + types: [created] + +jobs: + generate: + name: | + Generate matrix. + runs-on: ubuntu-latest + outputs: + diffs: ${{ steps.regress-ci.outputs.diffs }} + if: contains(toJson(github.event.comment.body), '\n/ci') || startsWith(github.event.comment.body, '/ci') + steps: + - name: Is member + run: | + if [[ "${{ github.event.comment.author_association }}" != "MEMBER" && "${{ github.event.comment.author_association }}" != "OWNER" ]]; then + exit 1 + fi + + - uses: actions/checkout@v4 + + - uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + + - name: Cache + uses: Swatinem/rust-cache@v2 + with: + shared-key: "diff" + + - run: cargo regress ci + id: regress-ci + env: + GITHUB_COMMENT: ${{ github.event.comment.body }} + GITHUB_COMMENT_PR: ${{ github.event.issue.number }} + diff: + runs-on: ubuntu-latest + needs: [generate] + if: needs.generate.outputs.diffs != '{}' && needs.generate.outputs.diffs != '[]' && needs.generate.outputs.diffs != '' + strategy: + matrix: + include: ${{ fromJson(needs.generate.outputs.diffs) }} + steps: + - uses: actions/checkout@v4 + + - uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + + - name: Cache + uses: Swatinem/rust-cache@v2 + with: + shared-key: "diff" + + - uses: taiki-e/install-action@v2 + if: matrix.needs_semver_checks + with: + tool: cargo-semver-checks + + - uses: taiki-e/install-action@v2 + with: + tool: git-delta + + # if a new line is added here, make sure to update the `summary` job to reference the new step index + - run: cargo regress diff --use-pager-directly ${{ matrix.command }} + env: + GH_TOKEN: ${{ github.token }} + GITHUB_PR: ${{ matrix.pr }} + GIT_PAGER: delta --raw + summary: + runs-on: ubuntu-latest + needs: [diff, generate] + if: always() && needs.generate.outputs.diffs != '{}' && needs.generate.outputs.diffs != '[]' && needs.generate.outputs.diffs != '' && needs.generate.result == 'success' + steps: + - uses: actions/checkout@v4 + + - run: | + gh run view ${{ github.run_id }} --json jobs | \ + jq -r '"Diff for [comment]("+$comment+")\n\n" + ([.jobs[] | select(.name | startswith("diff")) | "- [" + (.name | capture("\\((?[^,]+),.*") | .name) + "](" + .url + "?pr=" + $pr_id + "#step:7:47)"] | join("\n"))' --arg pr_id "${{ github.event.issue.number }}" --arg comment "${{ github.event.comment.url }}"| \ + gh pr comment "${{ github.event.issue.number }}" --body "$(< /dev/stdin)" + env: + GH_TOKEN: ${{ github.token }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1f0ddbf4..c9ce19be 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,6 +15,9 @@ jobs: - os: ubuntu-latest target: x86_64-unknown-linux-gnu suffix: ".gz" + - os: ubuntu-latest + target: aarch64-unknown-linux-gnu + suffix: ".gz" - os: macos-latest target: x86_64-apple-darwin suffix: ".gz" @@ -26,21 +29,21 @@ jobs: suffix: ".exe" runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master with: toolchain: stable - profile: minimal - target: ${{ matrix.target }} - override: true + targets: ${{ matrix.target }} - name: Cache Dependencies uses: Swatinem/rust-cache@v2 with: key: ${{ matrix.target }} - - uses: actions-rs/cargo@v1 - with: - command: build - args: --target ${{ matrix.target }} --release + + - name: Install Dependencies + if: ${{ matrix.target == 'aarch64-unknown-linux-gnu' }} + run: sudo apt install -y gcc-aarch64-linux-gnu + + - run: cargo build --target ${{ matrix.target }} --release - name: Compress and rename executable if: ${{ matrix.os != 'windows-latest' }} @@ -49,7 +52,7 @@ jobs: if: ${{ matrix.os == 'windows-latest' }} run: mv target/${{ matrix.target }}/release/svd2rust${{ matrix.suffix }} svd2rust-${{ matrix.target }}${{ matrix.suffix }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: svd2rust-${{ matrix.target }} path: svd2rust-${{ matrix.target }}${{ matrix.suffix }} @@ -59,8 +62,8 @@ jobs: runs-on: ubuntu-latest needs: [build] steps: - - uses: actions/checkout@v3 - - uses: actions/download-artifact@v3 + - uses: actions/checkout@v4 + - uses: actions/download-artifact@v4 with: path: artifacts - run: ls -R ./artifacts @@ -69,11 +72,11 @@ jobs: run: echo "CURRENT_DATE=$(date +'%Y-%m-%d')" >> $GITHUB_ENV - id: changelog-reader - uses: mindsers/changelog-reader-action@v2.0.0 + uses: mindsers/changelog-reader-action@v2.2.2 with: version: ${{ (github.ref_type == 'tag' && github.ref_name) || 'Unreleased' }} - - uses: softprops/action-gh-release@v1 + - uses: softprops/action-gh-release@v2 with: tag_name: ${{ steps.changelog-reader.outputs.version }} name: ${{ (github.ref_type == 'tag' && steps.changelog-reader.outputs.version) || format('Prereleased {0}', env.CURRENT_DATE) }} diff --git a/.gitignore b/.gitignore index a67b8428..33b07514 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ *.rs.bk *.svd target -Cargo.lock +output diff --git a/CHANGELOG.md b/CHANGELOG.md index 782c8a60..8985ad60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,204 @@ All notable changes to this project will be documented in this file. -The format is based on [Keep a Changelog](http://keepachangelog.com/) -and this project adheres to [Semantic Versioning](http://semver.org/). +The format is based on [Keep a Changelog](https://keepachangelog.com/) +and this project adheres to [Semantic Versioning](https://semver.org/). ## [Unreleased] +## [v0.36.1] - 2025-04-04 + +- Update `irx-config` +- Fix register array derive regression +- Sanitize `gen` keyword (new in Rust 2024 edition) + +## [v0.36.0] - 2025-03-09 + +- Generic `Periph` +- use `ConstZero::ZERO` instead of `Default::default()` to force const +- Add `mtvec_align` field to `riscv_config` to configure the byte alignment of interrupt vector table. +- Fix reexport path when "%s" inside "derivedFrom" +- Force using rust edition 2021 in CI +- Added lifetime ellision for `FieldWriter` where the explicit lifetimes are not necessary, which + fixes the `clippy::needless_lifetimes` warning on rustc 1.84 +- Some fixes for the `svd2rust-regress` tool and update of its documentation +- Other internal clippy fixes for `clippy::manual_div_ceil`, `clippy::nonminimal_bool` and + `clippy::needless_lifetimes` +- Add missing `escape_special_chars` for peripheral description +- Update `svd-rs` to 0.14.11 +- Added `#![cfg_attr(docsrs, feature(doc_auto_cfg))]` to the generated library code. This + adds a display of the feature gates in the documentation of the generated library +- Split on the start of attribute instead of the end + +## [v0.35.0] - 2024-11-12 + +- Add `crate_path` setting +- Inline `Settings` into `Config`, add `settings_file` +- Fix MSP430 PAC inner attribute generation when used with the `-m` switch. + +## [v0.34.0] - 2024-11-05 + +- Revert #711 +- Add `defmt` impls for `TryFromInterruptError`, riscv interrupt enums +- Fix calculating `modifiedWriteValues` bitmasks with field arrays +- Fix building without `yaml` feature +- Compatibility with `riscv` 0.12 and `riscv-rt` 0.13 +- Add `riscv_config` section in `settings.yaml` + It uses `riscv-pac` traits and standard `riscv-peripheral` peripherals. +- Add `settings.yaml` file for target-specific settings. +- Add warning about indexing register arrays +- Skip generating `.add(0)` and `1 *` in accessors +- Bump MSRV of generated code to 1.76 +- move `must_use` from methods to generic type +- *breaking change* Return raw writtened value +- Add `from_write`, `from_write_with_zero`, and `from_modify` register modifiers + with generic return value +- `InterruptNumber` is no longer implemented for Xtensa peripheral interrupts + +## [v0.33.5] - 2024-10-12 + +- Fix STM32-patched CI +- Fix `enumeratedValues` with `isDefault` only +- Fix invalid `Punct` error from `proc_macro2` +- Run espressif tests on nightly-2024-09-25 to workaround CI failures + +## [v0.33.4] - 2024-06-16 + +- Add `html-url` option to access `svdtools html` files from docs +- Move `Reg` in separate file +- Use `warning` class in docs +- Refactor `Accessor` + +## [v0.33.3] - 2024-05-10 + +- Yet more clean field & register `Debug` + +## [v0.33.2] - 2024-05-07 + +- Remove unneeded `format_args` in register `Debug` impl + +## [v0.33.1] - 2024-04-20 + +- Add checked `set` for not full safe fields + +## [v0.33.0] - 2024-03-26 + +- Add `IsEnum` constraint for `FieldWriter`s (fix `variant` safety) +- Make field writer `bits` always `unsafe`, add `set` for safe writing +- Fix bit writer type for `ModifiedWriteValues::ZeroToSet` + +## [v0.32.0] - 2024-02-26 + +- Bump MSRV to 1.74 +- generic unsafe `W::bits` + safe `W::set` +- Add `base-address-shift` config flag +- Use `PascalCase` for type idents, fix case changing bugs, add `--ident-format` (`-f`) option flag +- Add `enum_read_name` for `read-only` enums, `RWEnum` helper +- Reexport enums inside register again +- Add `DimSuffix` helper trait + +## [v0.31.5] - 2024-01-04 + +- `move` in `RegisterBlock::reg_iter` implementation (iterator of register/cluster array) +- Fix `cargo doc` constants generation + +## [v0.31.4] - 2024-01-03 + +- Custom prefix/case/suffix for identifiers (by `svd2rust.toml` config file) + +## [v0.31.3] - 2023-12-25 + +- Add `svd::Device` validation after parsing by `serde` +- Add `skip-crate-attributes` config flag +- Better display parsing errors +- `move` in `R::field_iter` implementation (iterator of field array values) + +## [v0.31.2] - 2023-11-29 + +- Add iterators for register/cluster/field arrays +- Use parentheses instead of square brackets in docs for field arrays + +## [v0.31.1] - 2023-11-27 + +- Fix cluster arrays +- Remove needless reference in `ArrayElemAccessor` + +## [v0.31.0] - 2023-11-24 + +- Use methods to access any register or cluster +- Remove all deny lints from generated crate +- Add `reexport-core-peripherals` and `reexport-interrupt` features disabled by default +- remove `ArrayProxy` and `const_generic` feature +- `FieldWriter` takes offset as struct field instead of const generic. + Improves SVD field array access + Add `width`, `offset` methods +- *breaking change* Always numerates field arrays from 0 +- Support of default value for `EnumeratedValues` +- move `Config` to `config` module +- add `impl-defmt` config flag +- Use dash instead of underscore in flag names + +## [v0.30.3] - 2023-11-19 + +- Remove unstable lints +- Mark `Vector` union as `repr(C)` +- Support `dimArrayIndex` for array names and descriptions + +## [v0.30.2] - 2023-10-22 + +- Fix documentation warnings +- Use `ArrayProxy` for memory disjoined register arrays +- Use `const fn` where allowed + +## [v0.30.1] - 2023-10-01 + +- Fix clippy lints on `nightly` +- Bump MSRV to 1.70 +- Fix `derivedFrom` on field + +## [v0.30.0] - 2023-08-16 + +- Add `aarch64` target for releases, more readme badges +- Fix when `atomics` features is generated but not enabled +- move hidden structs into module, add register reader/writer links into `SPEC` docs (#736) +- removed register writer & reader wrappers, generic `REG` in field writers (#731) +- Updated syn to version 2 (#732) +- Let readable field fetch doc from svd description (#734) +- Add `steal()` for each peripheral + +## [v0.29.0] - 2023-06-05 + +- `FieldFpec` instead or `fty` generic (#722) +- print error on ci `curl` request fail (#725) +- removed `rty` generic in `FieldWriter` (#721) +- `bool` and `u8` as default generics for `BitReader/Writer` and `FieldReader/Writer` (#720) +- Bump MSRV to 1.65 (#711) +- Optimize case change/sanitize (#715) +- Fix dangling implicit derives (#703) +- Fix escaping <> and & characters in doc attributes (#711) +- Add `interrupt_link_section` config parameter for controlling the `#[link_section = "..."]` attribute of `__INTERRUPTS` (#718) +- Add option to implement Debug for readable registers (#716) +- Add `atomics-feature` (#729) + +## [v0.28.0] - 2022-12-25 + +- Generate atomic register code for non-MSP430 targets +- Change --nightly flag to --atomics +- Add handling for disjoint register arrays and validation of derives + +## [v0.27.2] - 2022-11-06 + +- mark alternate register accessors with `const`, bump `pac` MSRV to 1.61 +- `fields` fn refactoring +- Test patched STM32 +- simplify ci strategy +- Fix generated code for MSP430 atomics + +## [v0.27.1] - 2022-10-25 + +- Fix cli error with --help/version +- Don't cast fields with width 17-31 and non-zero offset. + ## [v0.27.0] - 2022-10-24 - Manually inline set/clear_bit @@ -761,7 +954,32 @@ peripheral.register.write(|w| w.field().set()); - Initial version of the `svd2rust` tool -[Unreleased]: https://github.com/rust-embedded/svd2rust/compare/v0.27.0...HEAD +[Unreleased]: https://github.com/rust-embedded/svd2rust/compare/v0.36.1...HEAD +[v0.36.1]: https://github.com/rust-embedded/svd2rust/compare/v0.36.0...v0.36.1 +[v0.36.0]: https://github.com/rust-embedded/svd2rust/compare/v0.35.0...v0.36.0 +[v0.35.0]: https://github.com/rust-embedded/svd2rust/compare/v0.34.0...v0.35.0 +[v0.34.0]: https://github.com/rust-embedded/svd2rust/compare/v0.33.5...v0.34.0 +[v0.33.5]: https://github.com/rust-embedded/svd2rust/compare/v0.33.4...v0.33.5 +[v0.33.4]: https://github.com/rust-embedded/svd2rust/compare/v0.33.3...v0.33.4 +[v0.33.3]: https://github.com/rust-embedded/svd2rust/compare/v0.33.2...v0.33.3 +[v0.33.2]: https://github.com/rust-embedded/svd2rust/compare/v0.33.1...v0.33.2 +[v0.33.1]: https://github.com/rust-embedded/svd2rust/compare/v0.33.0...v0.33.1 +[v0.33.0]: https://github.com/rust-embedded/svd2rust/compare/v0.32.0...v0.33.0 +[v0.32.0]: https://github.com/rust-embedded/svd2rust/compare/v0.31.5...v0.32.0 +[v0.31.5]: https://github.com/rust-embedded/svd2rust/compare/v0.31.4...v0.31.5 +[v0.31.4]: https://github.com/rust-embedded/svd2rust/compare/v0.31.3...v0.31.4 +[v0.31.3]: https://github.com/rust-embedded/svd2rust/compare/v0.31.2...v0.31.3 +[v0.31.2]: https://github.com/rust-embedded/svd2rust/compare/v0.31.1...v0.31.2 +[v0.31.1]: https://github.com/rust-embedded/svd2rust/compare/v0.31.0...v0.31.1 +[v0.31.0]: https://github.com/rust-embedded/svd2rust/compare/v0.30.3...v0.31.0 +[v0.30.3]: https://github.com/rust-embedded/svd2rust/compare/v0.30.2...v0.30.3 +[v0.30.2]: https://github.com/rust-embedded/svd2rust/compare/v0.30.1...v0.30.2 +[v0.30.1]: https://github.com/rust-embedded/svd2rust/compare/v0.30.0...v0.30.1 +[v0.30.0]: https://github.com/rust-embedded/svd2rust/compare/v0.29.0...v0.30.0 +[v0.29.0]: https://github.com/rust-embedded/svd2rust/compare/v0.28.0...v0.29.0 +[v0.28.0]: https://github.com/rust-embedded/svd2rust/compare/v0.27.2...v0.28.0 +[v0.27.2]: https://github.com/rust-embedded/svd2rust/compare/v0.27.1...v0.27.2 +[v0.27.1]: https://github.com/rust-embedded/svd2rust/compare/v0.27.0...v0.27.1 [v0.27.0]: https://github.com/rust-embedded/svd2rust/compare/v0.26.0...v0.27.0 [v0.26.0]: https://github.com/rust-embedded/svd2rust/compare/v0.25.1...v0.26.0 [v0.25.1]: https://github.com/rust-embedded/svd2rust/compare/v0.25.0...v0.25.1 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index a30c4c0d..bd8a797e 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -32,6 +32,6 @@ And if someone takes issue with something you said or did, resist the urge to be The enforcement policies listed above apply to all official embedded WG venues; including official IRC channels (#rust-embedded); GitHub repositories under rust-embedded; and all forums under rust-embedded.org (forum.rust-embedded.org). -*Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) as well as the [Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).* +*Adapted from the [Node.js Policy on Trolling](https://blog.izs.me/2012/08/policy-on-trolling/) as well as the [Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).* [team]: https://github.com/rust-embedded/wg#the-tools-team diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 00000000..f87d1e9c --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2426 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +dependencies = [ + "anstyle", + "once_cell", + "windows-sys 0.59.0", +] + +[[package]] +name = "anyhow" +version = "1.0.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" + +[[package]] +name = "blake2b_simd" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06e903a20b159e944f91ec8499fe1e55651480c541ea0a584f5d967c49ad9d99" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cc" +version = "1.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.5.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "constant_time_eq" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "env_filter" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "h2" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +dependencies = [ + "futures-util", + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "libc", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "inflections" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a" + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "irx-config" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6266a086f9c5635dffbfd7bd49262b2a40e0a580ab34e85d88dc59ed8133976a" +dependencies = [ + "blake2b_simd", + "cfg-if", + "clap", + "derive_builder", + "serde", + "serde_json", + "serde_yaml", + "thiserror", + "toml", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jiff" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c102670231191d07d37a35af3eb77f1f0dbf7a71be51a962dcd57ea607be7260" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde", +] + +[[package]] +name = "jiff-static" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cdde31a9d349f1b1f51a0b3714a5940ac022976f4b49485fc04be052b183b4c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" + +[[package]] +name = "litemap" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.52.0", +] + +[[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "openssl" +version = "0.10.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e14130c6a98cd258fdcb0fb6d744152343ff729cbfcb28c656a9d12b999fbcd" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-src" +version = "300.4.2+3.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168ce4e058f975fe43e89d9ccf78ca668601887ae736090aacc23ae353c298e2" +dependencies = [ + "cc", +] + +[[package]] +name = "openssl-sys" +version = "0.9.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bb61ea9811cc39e3c2069f40b8b8e2e70d8569b361f879786cc7ed48b777cdd" +dependencies = [ + "cc", + "libc", + "openssl-src", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "portable-atomic" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "prettyplease" +version = "0.2.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5316f57387668042f561aae71480de936257848f9c43ce528e311d89a07cadeb" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "reqwest" +version = "0.12.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-registry", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.15", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "roxmltree" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys 0.9.3", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustls" +version = "0.23.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "822ee9188ac4ec04a2f0531e55d035fb2de73f18b41a63c70c2712503b6fb13c" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" + +[[package]] +name = "rustls-webpki" +version = "0.103.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" + +[[package]] +name = "socket2" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "svd-parser" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a41fe17e46dee363a7d3b20e878bbf6d6b4e839ae010ff07ef0e05d41d201811" +dependencies = [ + "anyhow", + "roxmltree", + "svd-rs", + "thiserror", +] + +[[package]] +name = "svd-rs" +version = "0.14.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9b88ee2e82f09623ff76965587dc15a2e7150a3126854899c86e94dab777458" +dependencies = [ + "once_cell", + "regex", + "serde", + "thiserror", +] + +[[package]] +name = "svd2rust" +version = "0.36.1" +dependencies = [ + "anyhow", + "clap", + "env_logger", + "inflections", + "irx-config", + "log", + "proc-macro2", + "quote", + "regex", + "serde", + "serde_json", + "serde_yaml", + "svd-parser", + "svd-rs", + "syn", + "thiserror", + "url", +] + +[[package]] +name = "svd2rust-regress" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "prettyplease", + "rayon", + "reqwest", + "serde", + "serde_json", + "serde_yaml", + "shell-words", + "svd2rust", + "syn", + "thiserror", + "tracing", + "tracing-subscriber", + "which", + "wildmatch", +] + +[[package]] +name = "syn" +version = "2.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +dependencies = [ + "fastrand", + "getrandom 0.3.2", + "once_cell", + "rustix 1.0.5", + "windows-sys 0.59.0", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.44.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "which" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bf3ea8596f3a0dd5980b46430f2058dfe2c36a27ccfbb1845d6fbfcd9ba6e14" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.44", + "windows-sys 0.48.0", +] + +[[package]] +name = "wildmatch" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ce1ab1f8c62655ebe1350f589c61e505cf94d385bc6a12899442d9081e71fd" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-registry" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.53.0", +] + +[[package]] +name = "windows-result" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "winnow" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index ed09bd49..88daf5ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,19 +13,13 @@ categories = [ ] description = "Generate Rust register maps (`struct`s) from SVD files" documentation = "https://docs.rs/svd2rust" -keywords = [ - "svd", - "embedded", - "register", - "map", - "generator", -] +keywords = ["svd", "embedded", "register", "map", "generator"] license = "MIT OR Apache-2.0" name = "svd2rust" repository = "https://github.com/rust-embedded/svd2rust/" -version = "0.27.0" +version = "0.36.1" readme = "README.md" -rust-version = "1.60" +rust-version = "1.74" [package.metadata.deb] section = "rust" @@ -44,8 +38,12 @@ yaml = ["dep:serde_yaml"] [dependencies] clap = { version = "4.0", optional = true } -irx-config = { version = "3.1", features = ["cmd", "toml-parser"], optional = true } -env_logger = { version = "0.9", optional = true } +irx-config = { version = "3.5.0", features = [ + "cmd", + "toml-parser", + "yaml", +], optional = true } +env_logger = { version = "0.11", optional = true } inflections = "1.1" log = { version = "~0.4", features = ["std"] } quote = "1.0" @@ -55,15 +53,28 @@ thiserror = "1.0" serde = { version = "1.0", optional = true } serde_json = { version = "1.0.85", optional = true } serde_yaml = { version = "0.9.11", optional = true } +regex = "1.10.0" +url = { version = "2.5", features = ["serde"] } [dependencies.svd-parser] features = ["expand"] -version = "0.14.1" +version = "0.14.9" [dependencies.svd-rs] features = ["serde"] -version = "0.14.1" +version = "0.14.12" [dependencies.syn] -version = "1.0" -features = ["full","extra-traits"] +version = "2.0" +features = ["full", "extra-traits"] + +[workspace] +members = ["svd2rust-regress"] +default-members = ["."] +exclude = [ + "output", + # workaround for https://github.com/rust-lang/cargo/pull/12779, doesn't work for output though + # see https://github.com/rust-lang/cargo/issues/6009#issuecomment-1925445245 + "output/baseline/**", + "output/current/**", +] diff --git a/README.md b/README.md index f8182757..d0bf6fb5 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,11 @@ -[![crates.io](https://img.shields.io/crates/d/svd2rust.svg)](https://crates.io/crates/svd2rust) +![GitHub top language](https://img.shields.io/github/languages/top/rust-embedded/svd2rust) +![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.74+-blue.svg) [![crates.io](https://img.shields.io/crates/v/svd2rust.svg)](https://crates.io/crates/svd2rust) +[![crates.io](https://img.shields.io/crates/d/svd2rust.svg)](https://crates.io/crates/svd2rust) +[![Released API docs](https://docs.rs/svd2rust/badge.svg)](https://docs.rs/svd2rust) +![Crates.io](https://img.shields.io/crates/l/svd2rust) +[![dependency status](https://deps.rs/repo/github/rust-embedded/svd2rust/status.svg)](https://deps.rs/repo/github/rust-embedded/svd2rust) +[![Continuous integration](https://github.com/rust-embedded/svd2rust/workflows/Continuous%20integration/badge.svg)](https://github.com/rust-embedded/svd2rust) # `svd2rust` @@ -9,13 +15,11 @@ This project is developed and maintained by the [Tools team][team]. # [Documentation](https://docs.rs/svd2rust) -# [API](https://docs.rs/svd2rust) - ## Minimum Supported Rust Version (MSRV) -The **generated code** is guaranteed to compile on stable Rust 1.60.0 and up. +The **generated code** is guaranteed to compile on stable Rust 1.76.0 and up. -If you encounter compilation errors on any stable version newer than 1.60.0, please open an issue. +If you encounter compilation errors on any stable version newer than 1.76.0, please open an issue. # Testing Locally @@ -28,8 +32,8 @@ Check out the [svd2rust-regress README](ci/svd2rust-regress/README.md) for infor Licensed under either of - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or - http://www.apache.org/licenses/LICENSE-2.0) -- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + https://www.apache.org/licenses/LICENSE-2.0) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT) at your option. diff --git a/build.rs b/build.rs index 56ee3e0a..34f44d51 100644 --- a/build.rs +++ b/build.rs @@ -35,7 +35,7 @@ fn commit_info() -> String { fn commit_hash() -> Result { Ok(String::from_utf8( Command::new("git") - .args(&["rev-parse", "--short", "HEAD"]) + .args(["rev-parse", "--short", "HEAD"]) .output()? .stdout, )?) @@ -44,7 +44,7 @@ fn commit_hash() -> Result { fn commit_date() -> Result { Ok(String::from_utf8( Command::new("git") - .args(&["log", "-1", "--date=short", "--pretty=format:%cd"]) + .args(["log", "-1", "--date=short", "--pretty=format:%cd"]) .output()? .stdout, )?) diff --git a/ci/before_deploy.sh b/ci/before_deploy.sh deleted file mode 100644 index 41d6879c..00000000 --- a/ci/before_deploy.sh +++ /dev/null @@ -1,17 +0,0 @@ -set -euxo pipefail - -main() { - cargo rustc --bin svd2rust --target $TARGET --release -- -C lto - - rm -rf stage - mkdir stage - cp target/$TARGET/release/svd2rust stage - - pushd stage - tar czf ../$CRATE_NAME-$TRAVIS_TAG-$TARGET.tar.gz * - popd - - rm -rf stage -} - -main diff --git a/ci/script.sh b/ci/script.sh deleted file mode 100755 index 351ba907..00000000 --- a/ci/script.sh +++ /dev/null @@ -1,624 +0,0 @@ -set -euxo pipefail - -test_svd() { - ( - cd $td && - curl -LO \ - https://raw.githubusercontent.com/posborne/cmsis-svd/master/data/$VENDOR/${1}.svd - ) - - # NOTE we care about errors in svd2rust, but not about errors / warnings in rustfmt - pushd $td - RUST_BACKTRACE=1 svd2rust $strict $const_generic $derive_more -i ${1}.svd - - mv lib.rs src/lib.rs - - popd - - cargo $COMMAND --manifest-path $td/Cargo.toml -} - -test_svd_for_target() { - curl -L --output $td/input.svd $2 - - # NOTE we care about errors in svd2rust, but not about errors / warnings in rustfmt - pushd $td - RUST_BACKTRACE=1 svd2rust --target $1 -i input.svd - - mv lib.rs src/lib.rs - - popd - - cargo $COMMAND --manifest-path $td/Cargo.toml -} - -main() { - if [ -z ${VENDOR-} ]; then - return - fi - - td=$(mktemp -d) - - case $OPTIONS in - all) - const_generic="--const_generic" - strict="--strict" - derive_more="--derive_more" - ;; - strict) - const_generic="" - strict="--strict" - derive_more="" - ;; - const) - const_generic="--const_generic" - strict="" - derive_more="" - ;; - derive_more) - const_generic="" - strict="" - derive_more="--derive_more" - ;; - *) - const_generic="" - strict="" - derive_more="" - ;; - esac - - # test crate - cargo init --name foo $td - echo 'cortex-m = "0.7.4"' >> $td/Cargo.toml - echo 'cortex-m-rt = "0.7.1"' >> $td/Cargo.toml - echo 'vcell = "0.1.3"' >> $td/Cargo.toml - if [ $derive_more ]; then - echo 'derive_more = "0.99"' >> $td/Cargo.toml - fi - echo '[profile.dev]' >> $td/Cargo.toml - echo 'incremental = false' >> $td/Cargo.toml - - case $VENDOR in - Atmel) - # BAD-SVD missing resetValue - # test_svd AT91SAM9CN11 - # test_svd AT91SAM9CN12 - # test_svd AT91SAM9G10 - # test_svd AT91SAM9G15 - # test_svd AT91SAM9G20 - # test_svd AT91SAM9G25 - # test_svd AT91SAM9G35 - # test_svd AT91SAM9M10 - # test_svd AT91SAM9M11 - # test_svd AT91SAM9N12 - # test_svd AT91SAM9X25 - # test_svd AT91SAM9X35 - # test_svd ATSAM3A4C - # test_svd ATSAM3A8C - # test_svd ATSAM3N00A - # test_svd ATSAM3N00B - # test_svd ATSAM3N0A - # test_svd ATSAM3N0B - # test_svd ATSAM3N0C - # test_svd ATSAM3N1A - # test_svd ATSAM3N1B - # test_svd ATSAM3N1C - # test_svd ATSAM3N2A - # test_svd ATSAM3N2B - # test_svd ATSAM3N2C - # test_svd ATSAM3N4A - # test_svd ATSAM3N4B - # test_svd ATSAM3N4C - # test_svd ATSAM3S1A - # test_svd ATSAM3S1B - # test_svd ATSAM3S1C - # test_svd ATSAM3S2A - # test_svd ATSAM3S2B - # test_svd ATSAM3S2C - # test_svd ATSAM3S4A - # test_svd ATSAM3S4B - # test_svd ATSAM3S4C - # test_svd ATSAM3S8B - # test_svd ATSAM3S8C - # test_svd ATSAM3SD8B - # test_svd ATSAM3SD8C - # test_svd ATSAM3U1C - # test_svd ATSAM3U1E - # test_svd ATSAM3U2C - # test_svd ATSAM3U2E - # test_svd ATSAM3U4C - # test_svd ATSAM3U4E - # test_svd ATSAM3X4C - # test_svd ATSAM3X4E - # test_svd ATSAM3X8C - # test_svd ATSAM3X8E - # test_svd ATSAM4S16B - # test_svd ATSAM4S16C - # test_svd ATSAM4S8B - # test_svd ATSAM4S8C - # test_svd ATSAM4SD32B - # test_svd ATSAM4SD32C - # test_svd ATSAMA5D31 - # test_svd ATSAMA5D33 - # test_svd ATSAMA5D34 - # test_svd ATSAMA5D35 - - # FIXME(#107) "failed to resolve. Use of undeclared type or module `sercom0`" - # test_svd ATSAMD21E15A - # test_svd ATSAMD21E16A - # test_svd ATSAMD21E17A - # test_svd ATSAMD21E18A - # test_svd ATSAMD21G16A - # test_svd ATSAMD21G17A - # test_svd ATSAMD21G18A - # test_svd ATSAMD21J16A - # test_svd ATSAMD21J17A - # test_svd ATSAMD21J18A - # test_svd ATSAMR21E16A - # test_svd ATSAMR21E17A - # test_svd ATSAMR21E18A - # test_svd ATSAMR21G16A - # test_svd ATSAMR21G17A - # test_svd ATSAMR21G18A - ;; - - Freescale) - # BAD-SVD bad enumeratedValue value - # test_svd MKV56F20 - # test_svd MKV56F22 - # test_svd MKV56F24 - # test_svd MKV58F20 - # test_svd MKV58F22 - # test_svd MKV58F24 - - # BAD-SVD field names are equivalent when case is ignored - # test_svd MK61F15 - # test_svd MK61F15WS - # test_svd MK70F12 - # test_svd MK70F15 - # test_svd MK70F15WS - - # OK - # NOTE it would take too long to test all these so we only a few of each family - test_svd MK02F12810 - # test_svd MK10D10 - # test_svd MK10D5 - test_svd MK10D7 - # test_svd MK10DZ10 - # test_svd MK10F12 - # test_svd MK11D5 - # test_svd MK11D5WS - # test_svd MK11DA5 - test_svd MK12D5 - # test_svd MK20D10 - # test_svd MK20D5 - # test_svd MK20D7 - # test_svd MK20DZ10 - # test_svd MK20F12 - test_svd MK21D5 - # test_svd MK21D5WS - # test_svd MK21DA5 - test_svd MK21F12 - # test_svd MK21FA12 - # test_svd MK22D5 - # test_svd MK22F12 - # test_svd MK22F12810 - # test_svd MK22F25612 - # test_svd MK22F51212 - # test_svd MK22FA12 - # test_svd MK24F12 - # test_svd MK24F25612 - # test_svd MK26F18 - # test_svd MK30D10 - test_svd MK30D7 - # test_svd MK30DZ10 - # test_svd MK40D10 - test_svd MK40D7 - # test_svd MK40DZ10 - # test_svd MK50D10 - # test_svd MK50D7 - # test_svd MK50DZ10 - # test_svd MK51D10 - # test_svd MK51D7 - # test_svd MK51DZ10 - # test_svd MK52D10 - test_svd MK52DZ10 - # test_svd MK53D10 - # test_svd MK53DZ10 - # test_svd MK60D10 - # test_svd MK60DZ10 - # test_svd MK60F15 - # test_svd MK63F12 - # test_svd MK64F12 - # test_svd MK65F18 - test_svd MK66F18 - # test_svd MK80F25615 - # test_svd MK81F25615 - test_svd MK82F25615 - # test_svd MKE14F16 - # test_svd MKE14Z7 - test_svd MKE15Z7 - # test_svd MKE16F16 - # test_svd MKE18F16 - test_svd MKL28T7_CORE0 - # test_svd MKL28T7_CORE1 - # test_svd MKL28Z7 - test_svd MKL81Z7 - # test_svd MKL82Z7 - # test_svd MKS22F12 - test_svd MKV10Z1287 - # test_svd MKV10Z7 - # test_svd MKV11Z7 - # test_svd MKV30F12810 - # test_svd MKV31F12810 - # test_svd MKV31F25612 - test_svd MKV31F51212 - # test_svd MKV40F15 - # test_svd MKV42F16 - # test_svd MKV43F15 - # test_svd MKV44F15 - # test_svd MKV44F16 - test_svd MKV45F15 - # test_svd MKV46F15 - # test_svd MKV46F16 - # test_svd MKW20Z4 - # test_svd MKW21D5 - # test_svd MKW21Z4 - test_svd MKW22D5 - # test_svd MKW24D5 - # test_svd MKW30Z4 - # test_svd MKW31Z4 - # test_svd MKW40Z4 - # test_svd MKW41Z4 - - # #92 regression tests - # NOTE it would take too long to test all these so we only a few of each family - test_svd MKE02Z4 - # test_svd MKE04Z1284 - # test_svd MKE04Z4 - test_svd MKE06Z4 - # test_svd MKE14D7 - # test_svd MKE15D7 - # test_svd MKL02Z4 - # test_svd MKL03Z4 - # test_svd MKL04Z4 - test_svd MKL05Z4 - # test_svd MKL13Z644 - # test_svd MKL14Z4 - # test_svd MKL15Z4 - # test_svd MKL16Z4 - # test_svd MKL17Z4 - test_svd MKL17Z644 - # test_svd MKL24Z4 - # test_svd MKL25Z4 - # test_svd MKL26Z4 - # test_svd MKL27Z4 - # test_svd MKL27Z644 - # test_svd MKL33Z4 - # test_svd MKL33Z644 - # test_svd MKL34Z4 - test_svd MKL36Z4 - # test_svd MKL43Z4 - # test_svd MKL46Z4 - test_svd MKM14ZA5 - # test_svd MKM33ZA5 - # test_svd MKM34Z7 - test_svd MKM34ZA5 - # test_svd MKW01Z4 - # test_svd SKEAZ1284 - test_svd SKEAZN642 - # test_svd SKEAZN84 - ;; - - Fujitsu) - # OK - test_svd MB9AF10xN - test_svd MB9AF10xR - test_svd MB9AF11xK - test_svd MB9AF11xL - test_svd MB9AF11xM - test_svd MB9AF11xN - test_svd MB9AF12xK - test_svd MB9AF12xL - test_svd MB9AF13xK - test_svd MB9AF13xL - test_svd MB9AF13xM - test_svd MB9AF13xN - test_svd MB9AF14xL - test_svd MB9AF14xM - test_svd MB9AF14xN - test_svd MB9AF15xM - test_svd MB9AF15xN - test_svd MB9AF15xR - test_svd MB9AF1AxL - test_svd MB9AF1AxM - test_svd MB9AF1AxN - test_svd MB9AF31xK - test_svd MB9AF31xL - test_svd MB9AF31xM - test_svd MB9AF31xN - test_svd MB9AF34xL - test_svd MB9AF34xM - test_svd MB9AF34xN - test_svd MB9AF42xK - test_svd MB9AF42xL - test_svd MB9AFA3xL - test_svd MB9AFA3xM - test_svd MB9AFA3xN - test_svd MB9AFA4xL - test_svd MB9AFA4xM - test_svd MB9AFA4xN - test_svd MB9AFAAxL - test_svd MB9AFAAxM - test_svd MB9AFAAxN - test_svd MB9AFB4xL - test_svd MB9AFB4xM - test_svd MB9AFB4xN - test_svd MB9B160L - test_svd MB9B160R - test_svd MB9B360L - test_svd MB9B360R - test_svd MB9B460L - test_svd MB9B460R - test_svd MB9B560L - test_svd MB9B560R - test_svd MB9BF10xN - test_svd MB9BF10xR - test_svd MB9BF11xN - test_svd MB9BF11xR - test_svd MB9BF11xS - test_svd MB9BF11xT - test_svd MB9BF12xJ - test_svd MB9BF12xK - test_svd MB9BF12xL - test_svd MB9BF12xM - test_svd MB9BF12xS - test_svd MB9BF12xT - test_svd MB9BF21xS - test_svd MB9BF21xT - test_svd MB9BF30xN - test_svd MB9BF30xR - test_svd MB9BF31xN - test_svd MB9BF31xR - test_svd MB9BF31xS - test_svd MB9BF31xT - test_svd MB9BF32xK - test_svd MB9BF32xL - test_svd MB9BF32xM - test_svd MB9BF32xS - test_svd MB9BF32xT - test_svd MB9BF40xN - test_svd MB9BF40xR - test_svd MB9BF41xN - test_svd MB9BF41xR - test_svd MB9BF41xS - test_svd MB9BF41xT - test_svd MB9BF42xS - test_svd MB9BF42xT - test_svd MB9BF50xN - test_svd MB9BF50xR - test_svd MB9BF51xN - test_svd MB9BF51xR - test_svd MB9BF51xS - test_svd MB9BF51xT - test_svd MB9BF52xK - test_svd MB9BF52xL - test_svd MB9BF52xM - test_svd MB9BF52xS - test_svd MB9BF52xT - test_svd MB9BF61xS - test_svd MB9BF61xT - test_svd MB9BFD1xS - test_svd MB9BFD1xT - test_svd S6E1A1 - test_svd S6E2CC - ;; - - GD32) - #test_svd_for_target cortex-m https://q.geek.nz/files/gd32f130.svd.patched - ;; - - Holtek) - # OK - test_svd ht32f125x - test_svd ht32f175x - test_svd ht32f275x - ;; - - Microchip) - echo '[dependencies.bare-metal]' >> $td/Cargo.toml - echo 'version = "1.0.0"' >> $td/Cargo.toml - - echo '[dependencies.mips-mcu]' >> $td/Cargo.toml - echo 'version = "0.1.0"' >> $td/Cargo.toml - - test_svd_for_target mips https://raw.githubusercontent.com/kiffie/pic32-pac/master/pic32mx1xxfxxxb/PIC32MX170F256B.svd.patched - test_svd_for_target mips https://raw.githubusercontent.com/kiffie/pic32-pac/master/pic32mx2xxfxxxb/PIC32MX270F256B.svd.patched - ;; - - Nordic) - # BAD-SVD two enumeratedValues have the same value - # test_svd nrf52 - - # OK - test_svd nrf51 - ;; - - Nuvoton) - # OK - test_svd M051_Series - test_svd NUC100_Series - ;; - - NXP) - test_svd MK22F25612 - test_svd MKW41Z4 - - # BAD-SVD two enumeratedValues have the same name - # test_svd LPC11Exx_v5 - # test_svd LPC11Uxx_v7 - # test_svd LPC11xx_v6a - # test_svd LPC11xx_v6 - # test_svd LPC13Uxx_v1 - # test_svd LPC15xx_v0.7 - # test_svd LPC800_v0.3 - # test_svd LPC11E6x_v0.8 - # test_svd LPC176x5x_v0.2 - # test_svd LPC11Cxx_v9 - - # BAD-SVD missing resetValue - # test_svd LPC178x_7x - # test_svd LPC178x_7x_v0.8 - # test_svd LPC408x_7x_v0.7 - # test_svd LPC11Axxv0.6 - - - # BAD-SVD bad identifier: contains a '.' - # test_svd LPC11D14_svd_v4 - # test_svd LPC13xx_svd_v1 - - # BAD-SVD bad identifier: contains a '/' - # test_svd LPC18xx_svd_v18 - # test_svd LPC43xx_svd_v5 - - # BAD-SVD uses the identifier '_' to name a reserved bitfield value - # test_svd LPC1102_4_v4 - - # FIXME(???) "duplicate definitions for `write`" - # #99 regression test - # test_svd LPC5410x_v0.4 - ;; - - # test other targets (architectures) - OTHER) - echo '[dependencies.msp430]' >> $td/Cargo.toml - echo 'version = "0.3.0"' >> $td/Cargo.toml - - # Test MSP430 - test_svd_for_target msp430 https://raw.githubusercontent.com/pftbest/msp430g2553/v0.3.0-svd/msp430g2553.svd - ;; - - # Community-provided RISC-V SVDs - RISC-V) - echo '[dependencies.bare-metal]' >> $td/Cargo.toml - echo 'version = "1.0.0"' >> $td/Cargo.toml - - echo '[dependencies.riscv]' >> $td/Cargo.toml - echo 'version = "0.6.0"' >> $td/Cargo.toml - - echo '[dependencies.riscv-rt]' >> $td/Cargo.toml - echo 'version = "0.8.0"' >> $td/Cargo.toml - - test_svd_for_target riscv https://raw.githubusercontent.com/riscv-rust/e310x/master/e310x.svd - test_svd_for_target riscv https://raw.githubusercontent.com/riscv-rust/k210-pac/master/k210.svd - test_svd_for_target riscv https://raw.githubusercontent.com/riscv-rust/fu540-pac/master/fu540.svd - ;; - - SiliconLabs) - # #99 regression tests - test_svd_for_target cortex-m https://raw.githubusercontent.com/posborne/cmsis-svd/master/data/SiliconLabs/SiM3_NRND/SIM3C1x4.svd - test_svd_for_target cortex-m https://raw.githubusercontent.com/posborne/cmsis-svd/master/data/SiliconLabs/SiM3_NRND/SIM3C1x6.svd - test_svd_for_target cortex-m https://raw.githubusercontent.com/posborne/cmsis-svd/master/data/SiliconLabs/SiM3_NRND/SIM3C1x7.svd - test_svd_for_target cortex-m https://raw.githubusercontent.com/posborne/cmsis-svd/master/data/SiliconLabs/SiM3_NRND/SIM3L1x4.svd - test_svd_for_target cortex-m https://raw.githubusercontent.com/posborne/cmsis-svd/master/data/SiliconLabs/SiM3_NRND/SIM3L1x6.svd - test_svd_for_target cortex-m https://raw.githubusercontent.com/posborne/cmsis-svd/master/data/SiliconLabs/SiM3_NRND/SIM3L1x7.svd - test_svd_for_target cortex-m https://raw.githubusercontent.com/posborne/cmsis-svd/master/data/SiliconLabs/SiM3_NRND/SIM3U1x4.svd - test_svd_for_target cortex-m https://raw.githubusercontent.com/posborne/cmsis-svd/master/data/SiliconLabs/SiM3_NRND/SIM3U1x6.svd - test_svd_for_target cortex-m https://raw.githubusercontent.com/posborne/cmsis-svd/master/data/SiliconLabs/SiM3_NRND/SIM3U1x7.svd - - # FIXME(???) panicked at "c.text.clone()" - # test_svd SIM3L1x8_SVD - ;; - - Spansion) - # OK - # See Fujitsu for other chips - test_svd MB9BF36xx - test_svd MB9BF46xx - test_svd MB9BF56xx - ;; - - STMicro) - # OK - test_svd STM32F030 - test_svd STM32F031x - test_svd STM32F042x - test_svd STM32F072x - test_svd STM32F091x - test_svd STM32F0xx - test_svd STM32F100xx - test_svd STM32F103xx - test_svd STM32F107xx - test_svd STM32F20x - test_svd STM32F21x - test_svd STM32F301 - test_svd STM32F303 - test_svd STM32F401 - test_svd STM32F407 - test_svd STM32F410 - test_svd STM32F413 - test_svd STM32F427 - test_svd STM32F429 - test_svd STM32F446 - test_svd STM32F469 - test_svd STM32L100 - test_svd STM32L15xC - test_svd STM32L15xxE - test_svd STM32L15xxxA - test_svd STM32L1xx - test_svd STM32L4x6 - test_svd STM32W108 - - # FIXME(#91) "field is never used: `register`" - # test_svd STM32L051x - # test_svd STM32L052x - # test_svd STM32L053x - # test_svd STM32L062x - # test_svd STM32L063x - ;; - - Toshiba) - # BAD-SVD resetValue is bigger than the register size - # test_svd M365 - # test_svd M367 - # test_svd M368 - # test_svd M369 - # test_svd M36B - - # OK - test_svd M061 - ;; - - Espressif) - echo '[dependencies.bare-metal]' >> $td/Cargo.toml - echo 'version = "1.0.0"' >> $td/Cargo.toml - - echo '[dependencies.riscv]' >> $td/Cargo.toml - echo 'version = "0.6.0"' >> $td/Cargo.toml - - echo '[dependencies.riscv-rt]' >> $td/Cargo.toml - echo 'version = "0.8.0"' >> $td/Cargo.toml - - echo '[dependencies.xtensa-lx]' >> $td/Cargo.toml - echo 'version = "0.6.0"' >> $td/Cargo.toml - echo 'features = ["esp32"]' >> $td/Cargo.toml - - echo '[dependencies.xtensa-lx-rt]' >> $td/Cargo.toml - echo 'version = "0.9.0"' >> $td/Cargo.toml - echo 'features = ["esp32"]' >> $td/Cargo.toml - - test_svd_for_target riscv https://raw.githubusercontent.com/espressif/svd/main/svd/esp32c3.svd - - test_svd_for_target xtensa-lx https://raw.githubusercontent.com/espressif/svd/main/svd/esp32.svd - test_svd_for_target xtensa-lx https://raw.githubusercontent.com/espressif/svd/main/svd/esp32s2.svd - test_svd_for_target xtensa-lx https://raw.githubusercontent.com/espressif/svd/main/svd/esp32s3.svd - ;; - - esac - - rm -rf $td -} - -if [ -z ${TRAVIS_TAG-} ]; then - main -fi diff --git a/ci/svd2rust-regress/Cargo.toml b/ci/svd2rust-regress/Cargo.toml deleted file mode 100644 index a396cce0..00000000 --- a/ci/svd2rust-regress/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -edition = "2018" -name = "svd2rust-regress" -version = "0.1.0" -authors = ["James Munns ", "The svd2rust developers"] - -[dependencies] -reqwest = { version = "0.10", features= ["blocking"] } -rayon = "1.4" -structopt = "0.2" -error-chain = "0.12" -inflections = "1.1" diff --git a/ci/svd2rust-regress/README.md b/ci/svd2rust-regress/README.md deleted file mode 100644 index 5c623cc7..00000000 --- a/ci/svd2rust-regress/README.md +++ /dev/null @@ -1,107 +0,0 @@ -# `svd2rust` Regression Tester - -`svd2rust-regress` is a helper program for regression testing changes against `svd2rust`. It uses `rayon` to parallelize testing of multiple chips simultaneously. - -## What it does - -`svd2rust-regress` will do the following things for each svd/chip tested: - -1. Create a new crate for that chip in `output/`, populated with the architecture specific dependencies -2. Download the `.svd` file for that chip -3. Run `svd2rust` to generate `output//src/lib.rs` -4. Run `cargo check` to ensure the project still builds - -## Usage - -### Preconditions - -By default, `svd2rust-regress` assumes you have already built `svd2rust` in the root of this repository in `--release` mode. -If this is not possible, it is possible to specify the path to an `svd2rust` binary (see **Options** below). - -You'll also need to have `rustfmt` version > v0.4.0 to use the `--format` flag, you can install `rustfmt` with `rustup component add rustfmt-preview`. - -### Output - -For each test case, `svd2rust-regress` will output the result. - -Pass results look like this: - -```text -Passed: spansion_mb9af12x_k - 23 seconds -``` - -Fail results look like this: - -```text -Failed: si_five_e310x - 0 seconds. Error: Process Failed - cargo check -``` - -If all test cases passed, the return code will be `0`. If any test cases failed, the return code will be `1`. - -### Options - -Here are the options for running `svd2rust-regress`: - - -```text -svd2rust-regress 0.1.0 -James Munns :The svd2rust developers - -USAGE: - svd2rust-regress [FLAGS] [OPTIONS] - -FLAGS: - -b, --bad-tests Include tests expected to fail (will cause a non-zero return code) - -f, --format Enable formatting with `rustfmt` - -h, --help Prints help information - -l, --long-test Run a long test (it's very long) - -V, --version Prints version information - -v, --verbose Use verbose output - -OPTIONS: - -a, --architecture - Filter by architecture, case sensitive, may be combined with other filters Options are: "CortexM", "RiscV", "Msp430", "Mips" and "XtensaLX" - -p, --svd2rust-path - Path to an `svd2rust` binary, relative or absolute. Defaults to `target/release/svd2rust[.exe]` of this - repository (which must be already built) - -c, --chip Filter by chip name, case sensitive, may be combined with other filters - -m, --manufacturer - Filter by manufacturer, case sensitive, may be combined with other filters - - --rustfmt_bin_path - Path to an `rustfmt` binary, relative or absolute. Defaults to `$(rustup which rustfmt)` -``` - -### Filters - -`svd2rust-regress` allows you to filter which tests will be run. These filters can be combined (but not repeated). - -For example, to run all `RiscV` tests: - -```bash -# in the ci/svd2rust-regress folder -cargo run --release -- -a RiscV - Finished release [optimized] target(s) in 0.0 secs - Running `target/release/svd2rust-regress -a RiscV` -Passed: si_five_e310x - 7 seconds -``` - -To run against any chip named `MB9AF12xK`: - -```bash -cargo run --release -- --long-test -c MB9AF12xK - Finished release [optimized] target(s) in 0.0 secs - Running `target/release/svd2rust-regress --long-test -c MB9AF12xK` -Passed: spansion_mb9af12x_k - 23 seconds -Passed: fujitsu_mb9af12x_k - 25 seconds -``` - -To run against specifically the `Fujitsu` `MB9AF12xK`: -```bash -cargo run --release -- --long-test -c MB9AF12xK -m Fujitsu - Finished release [optimized] target(s) in 0.0 secs - Running `target/release/svd2rust-regress --long-test -c MB9AF12xK -m Fujitsu` -Passed: fujitsu_mb9af12x_k - 19 seconds -``` - -Note that you may have to pass `--long-test` to enable some chips as they are known to take a long time to compile. diff --git a/ci/svd2rust-regress/src/errors.rs b/ci/svd2rust-regress/src/errors.rs deleted file mode 100644 index cf598bf5..00000000 --- a/ci/svd2rust-regress/src/errors.rs +++ /dev/null @@ -1,9 +0,0 @@ -use std::path::PathBuf; -error_chain! { - errors { - ProcessFailed(command: String, stderr: Option, stdout: Option, previous_processes_stderr: Vec) { - description("Process Failed") - display("Process Failed - {}", command) - } - } -} diff --git a/ci/svd2rust-regress/src/main.rs b/ci/svd2rust-regress/src/main.rs deleted file mode 100644 index 2ae4ec90..00000000 --- a/ci/svd2rust-regress/src/main.rs +++ /dev/null @@ -1,304 +0,0 @@ -#[macro_use] -extern crate error_chain; - -mod errors; -mod svd_test; -mod tests; - -use error_chain::ChainedError; -use rayon::prelude::*; -use std::fs::File; -use std::io::Read; -use std::path::PathBuf; -use std::process::{exit, Command}; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::time::Instant; -use structopt::StructOpt; - -#[derive(StructOpt, Debug)] -#[structopt(name = "svd2rust-regress")] -struct Opt { - /// Run a long test (it's very long) - #[structopt(short = "l", long = "long-test")] - long_test: bool, - - /// Path to an `svd2rust` binary, relative or absolute. - /// Defaults to `target/release/svd2rust[.exe]` of this repository - /// (which must be already built) - #[structopt(short = "p", long = "svd2rust-path", parse(from_os_str))] - bin_path: Option, - - // TODO: Consider using the same strategy cargo uses for passing args to rustc via `--` - /// Run svd2rust with `--nightly` - #[structopt(long = "nightly")] - nightly: bool, - - /// Filter by chip name, case sensitive, may be combined with other filters - #[structopt(short = "c", long = "chip", raw(validator = "validate_chips"))] - chip: Vec, - - /// Filter by manufacturer, case sensitive, may be combined with other filters - #[structopt( - short = "m", - long = "manufacturer", - raw(validator = "validate_manufacturer") - )] - mfgr: Option, - - /// Filter by architecture, case sensitive, may be combined with other filters - /// Options are: "CortexM", "RiscV", "Msp430", "Mips" and "XtensaLX" - #[structopt( - short = "a", - long = "architecture", - raw(validator = "validate_architecture") - )] - arch: Option, - - /// Include tests expected to fail (will cause a non-zero return code) - #[structopt(short = "b", long = "bad-tests")] - bad_tests: bool, - - /// Enable formatting with `rustfmt` - #[structopt(short = "f", long = "format")] - format: bool, - - /// Print all available test using the specified filters - #[structopt(long = "list")] - list: bool, - - /// Path to an `rustfmt` binary, relative or absolute. - /// Defaults to `$(rustup which rustfmt)` - #[structopt(long = "rustfmt_bin_path", parse(from_os_str))] - rustfmt_bin_path: Option, - - /// Specify what rustup toolchain to use when compiling chip(s) - #[structopt(long = "toolchain", env = "RUSTUP_TOOLCHAIN")] - rustup_toolchain: Option, - - /// Use verbose output - #[structopt(long = "verbose", short = "v", parse(from_occurrences))] - verbose: u8, - // TODO: Specify smaller subset of tests? Maybe with tags? - // TODO: Compile svd2rust? -} - -fn validate_chips(s: String) -> Result<(), String> { - if tests::TESTS.iter().any(|t| t.chip == s) { - Ok(()) - } else { - Err(format!("Chip `{}` is not a valid value", s)) - } -} - -fn validate_architecture(s: String) -> Result<(), String> { - if tests::TESTS.iter().any(|t| format!("{:?}", t.arch) == s) { - Ok(()) - } else { - Err(format!("Architecture `{s}` is not a valid value")) - } -} - -fn validate_manufacturer(s: String) -> Result<(), String> { - if tests::TESTS.iter().any(|t| format!("{:?}", t.mfgr) == s) { - Ok(()) - } else { - Err(format!("Manufacturer `{s}` is not a valid value")) - } -} - -/// Validate any assumptions made by this program -fn validate_tests(tests: &[&tests::TestCase]) { - use std::collections::HashSet; - - let mut fail = false; - - // CONDITION 1: All mfgr+chip names must be unique - let mut uniq = HashSet::new(); - for t in tests { - let name = t.name(); - if !uniq.insert(name.clone()) { - fail = true; - eprintln!("{} is not unique!", name); - } - } - - if fail { - panic!("Tests failed validation"); - } -} - -fn read_file(path: &PathBuf, buf: &mut String) { - if buf.is_empty() { - buf.push_str(&format!("{}\n", path.display())); - } else { - buf.push_str(&format!("\n{}\n", path.display())); - } - File::open(path) - .expect("Couldn't open file") - .read_to_string(buf) - .expect("Couldn't read file to string"); -} - -fn main() { - let opt = Opt::from_args(); - - // Validate all test pre-conditions - validate_tests(tests::TESTS); - - // Determine default svd2rust path - let default_svd2rust_iter = ["..", "..", "..", "..", "target", "release"]; - - let default_svd2rust = if cfg!(windows) { - default_svd2rust_iter.iter().chain(["svd2rust.exe"].iter()) - } else { - default_svd2rust_iter.iter().chain(["svd2rust"].iter()) - } - .collect(); - - let bin_path = match opt.bin_path { - Some(ref bp) => bp, - None => &default_svd2rust, - }; - - let default_rustfmt: Option = if let Some((v, true)) = Command::new("rustup") - .args(&["which", "rustfmt"]) - .output() - .ok() - .map(|o| (o.stdout, o.status.success())) - { - Some(String::from_utf8_lossy(&v).into_owned().trim().into()) - } else { - if opt.format && opt.rustfmt_bin_path.is_none() { - panic!("rustfmt binary not found, is rustup and rustfmt-preview installed?"); - } - None - }; - - let rustfmt_bin_path = match (&opt.rustfmt_bin_path, opt.format) { - (_, false) => None, - (&Some(ref path), true) => Some(path), - (&None, true) => { - // FIXME: Use Option::filter instead when stable, rust-lang/rust#45860 - if default_rustfmt.iter().find(|p| p.is_file()).is_none() { - panic!("No rustfmt found"); - } - default_rustfmt.as_ref() - } - }; - - // Set RUSTUP_TOOLCHAIN if needed - if let Some(toolchain) = &opt.rustup_toolchain { - std::env::set_var("RUSTUP_TOOLCHAIN", toolchain); - } - - // collect enabled tests - let tests = tests::TESTS - .iter() - // Short test? - .filter(|t| t.should_run(!opt.long_test)) - // selected architecture? - .filter(|t| { - if let Some(ref arch) = opt.arch { - arch == &format!("{:?}", t.arch) - } else { - true - } - }) - // selected manufacturer? - .filter(|t| { - if let Some(ref mfgr) = opt.mfgr { - mfgr == &format!("{:?}", t.mfgr) - } else { - true - } - }) - // Specify chip - note: may match multiple - .filter(|t| { - if !opt.chip.is_empty() { - opt.chip.iter().any(|c| c == t.chip) - } else { - true - } - }) - // Run failable tests? - .filter(|t| opt.bad_tests || t.should_pass) - .collect::>(); - - if opt.list { - // FIXME: Prettier output - eprintln!("{:?}", tests.iter().map(|t| t.name()).collect::>()); - exit(0); - } - if tests.is_empty() { - eprintln!("No tests run, you might want to use `--bad-tests` and/or `--long-test`"); - } - - let any_fails = AtomicBool::new(false); - - // TODO: It would be more efficient to reuse directories, so we don't - // have to rebuild all the deps crates - tests.par_iter().for_each(|t| { - let start = Instant::now(); - - match svd_test::test(t, &bin_path, rustfmt_bin_path, opt.nightly, opt.verbose) { - Ok(s) => { - if let Some(stderrs) = s { - let mut buf = String::new(); - for stderr in stderrs { - read_file(&stderr, &mut buf); - } - eprintln!( - "Passed: {} - {} seconds\n{}", - t.name(), - start.elapsed().as_secs(), - buf - ); - } else { - eprintln!( - "Passed: {} - {} seconds", - t.name(), - start.elapsed().as_secs() - ); - } - } - Err(e) => { - any_fails.store(true, Ordering::Release); - let additional_info = if opt.verbose > 0 { - match *e.kind() { - errors::ErrorKind::ProcessFailed( - _, - _, - Some(ref stderr), - ref previous_processes_stderr, - ) => { - let mut buf = String::new(); - if opt.verbose > 1 { - for stderr in previous_processes_stderr { - read_file(&stderr, &mut buf); - } - } - read_file(&stderr, &mut buf); - buf - } - _ => "".into(), - } - } else { - "".into() - }; - eprintln!( - "Failed: {} - {} seconds. {}{}", - t.name(), - start.elapsed().as_secs(), - e.display_chain().to_string().trim_end(), - additional_info, - ); - } - } - }); - - if any_fails.load(Ordering::Acquire) { - exit(1); - } else { - exit(0); - } -} diff --git a/ci/svd2rust-regress/src/svd_test.rs b/ci/svd2rust-regress/src/svd_test.rs deleted file mode 100644 index f20d4b75..00000000 --- a/ci/svd2rust-regress/src/svd_test.rs +++ /dev/null @@ -1,248 +0,0 @@ -use crate::errors::*; -use crate::tests::TestCase; -use std::fs::{self, File, OpenOptions}; -use std::io::prelude::*; -use std::path::PathBuf; -use std::process::{Command, Output}; - -const CRATES_ALL: &[&str] = &["critical-section = \"1.0\"", "vcell = \"0.1.2\""]; -const CRATES_MSP430: &[&str] = &["msp430 = \"0.2.2\"", "msp430-rt = \"0.2.0\""]; -const CRATES_MSP430_NIGHTLY: &[&str] = &["msp430-atomic = \"0.1.2\""]; -const CRATES_CORTEX_M: &[&str] = &["cortex-m = \"0.7.6\"", "cortex-m-rt = \"0.6.13\""]; -const CRATES_RISCV: &[&str] = &["riscv = \"0.9.0\"", "riscv-rt = \"0.9.0\""]; -const CRATES_XTENSALX: &[&str] = &["xtensa-lx-rt = \"0.9.0\"", "xtensa-lx = \"0.6.0\""]; -const CRATES_MIPS: &[&str] = &["mips-mcu = \"0.1.0\""]; -const PROFILE_ALL: &[&str] = &["[profile.dev]", "incremental = false"]; -const FEATURES_ALL: &[&str] = &["[features]"]; -const FEATURES_XTENSALX: &[&str] = &["default = [\"xtensa-lx/esp32\", \"xtensa-lx-rt/esp32\"]"]; - -fn path_helper(input: &[&str]) -> PathBuf { - input.iter().collect() -} - -fn path_helper_base(base: &PathBuf, input: &[&str]) -> PathBuf { - let mut path = base.clone(); - input.iter().for_each(|p| path.push(p)); - path -} - -/// Create and write to file -fn file_helper(payload: &str, path: &PathBuf) -> Result<()> { - let mut f = File::create(path).chain_err(|| format!("Failed to create {path:?}"))?; - - f.write_all(payload.as_bytes()) - .chain_err(|| format!("Failed to write to {path:?}"))?; - - Ok(()) -} - -trait CommandHelper { - fn capture_outputs( - &self, - cant_fail: bool, - name: &str, - stdout: Option<&PathBuf>, - stderr: Option<&PathBuf>, - previous_processes_stderr: &[PathBuf], - ) -> Result<()>; -} - -impl CommandHelper for Output { - fn capture_outputs( - &self, - cant_fail: bool, - name: &str, - stdout: Option<&PathBuf>, - stderr: Option<&PathBuf>, - previous_processes_stderr: &[PathBuf], - ) -> Result<()> { - if let Some(out) = stdout { - let out_payload = String::from_utf8_lossy(&self.stdout); - file_helper(&out_payload, out)?; - }; - - if let Some(err) = stderr { - let err_payload = String::from_utf8_lossy(&self.stderr); - file_helper(&err_payload, err)?; - }; - - if cant_fail && !self.status.success() { - return Err(ErrorKind::ProcessFailed( - name.into(), - stdout.cloned(), - stderr.cloned(), - previous_processes_stderr.to_vec(), - ) - .into()); - } - - Ok(()) - } -} - -pub fn test( - t: &TestCase, - bin_path: &PathBuf, - rustfmt_bin_path: Option<&PathBuf>, - nightly: bool, - verbosity: u8, -) -> Result>> { - let user = match std::env::var("USER") { - Ok(val) => val, - Err(_) => "rusttester".into(), - }; - - // Remove the existing chip directory, if it exists - let chip_dir = path_helper(&["output", &t.name()]); - if let Err(err) = fs::remove_dir_all(&chip_dir) { - match err.kind() { - std::io::ErrorKind::NotFound => (), - _ => Err(err).chain_err(|| "While removing chip directory")?, - } - } - - // Used to build the output from stderr for -v and -vv* - let mut process_stderr_paths: Vec = vec![]; - - // Create a new cargo project. It is necesary to set the user, otherwise - // cargo init will not work (when running in a container with no user set) - Command::new("cargo") - .env("USER", user) - .arg("init") - .arg("--name") - .arg(&t.name()) - .arg("--vcs") - .arg("none") - .arg(&chip_dir) - .output() - .chain_err(|| "Failed to cargo init")? - .capture_outputs(true, "cargo init", None, None, &[])?; - - // Add some crates to the Cargo.toml of our new project - let svd_toml = path_helper_base(&chip_dir, &["Cargo.toml"]); - let mut file = OpenOptions::new() - .write(true) - .append(true) - .open(svd_toml) - .chain_err(|| "Failed to open Cargo.toml for appending")?; - - use crate::tests::Architecture::*; - let crates = CRATES_ALL - .iter() - .chain(match &t.arch { - CortexM => CRATES_CORTEX_M.iter(), - RiscV => CRATES_RISCV.iter(), - Mips => CRATES_MIPS.iter(), - Msp430 => CRATES_MSP430.iter(), - XtensaLX => CRATES_XTENSALX.iter(), - }) - .chain(if nightly { - match &t.arch { - Msp430 => CRATES_MSP430_NIGHTLY.iter(), - _ => [].iter(), - } - } else { - [].iter() - }) - .chain(PROFILE_ALL.iter()) - .chain(FEATURES_ALL.iter()) - .chain(match &t.arch { - XtensaLX => FEATURES_XTENSALX.iter(), - _ => [].iter(), - }); - - for c in crates { - writeln!(file, "{}", c).chain_err(|| "Failed to append to file!")?; - } - - // Download the SVD as specified in the URL - // TODO: Check for existing svd files? `--no-cache` flag? - let svd = reqwest::blocking::get(&t.svd_url()) - .chain_err(|| "Failed to get svd URL")? - .text() - .chain_err(|| "SVD is bad text")?; - - // Write SVD contents to file - let chip_svd = format!("{}.svd", &t.chip); - let svd_file = path_helper_base(&chip_dir, &[&chip_svd]); - file_helper(&svd, &svd_file)?; - - // Generate the lib.rs from the SVD file using the specified `svd2rust` binary - // If the architecture is cortex-m or msp430 we move the generated lib.rs file to src/ - let lib_rs_file = path_helper_base(&chip_dir, &["src", "lib.rs"]); - let svd2rust_err_file = path_helper_base(&chip_dir, &["svd2rust.err.log"]); - let target = match t.arch { - CortexM => "cortex-m", - Msp430 => "msp430", - Mips => "mips", - RiscV => "riscv", - XtensaLX => "xtensa-lx", - }; - let mut svd2rust_bin = Command::new(bin_path); - if nightly { - svd2rust_bin.arg("--nightly"); - } - - let output = svd2rust_bin - .args(&["-i", &chip_svd]) - .args(&["--target", &target]) - .current_dir(&chip_dir) - .output() - .chain_err(|| "failed to execute process")?; - output.capture_outputs( - true, - "svd2rust", - Some(&lib_rs_file) - .filter(|_| (t.arch != CortexM) && (t.arch != Msp430) && (t.arch != XtensaLX)), - Some(&svd2rust_err_file), - &[], - )?; - process_stderr_paths.push(svd2rust_err_file); - - match t.arch { - CortexM | Mips | Msp430 | XtensaLX => { - // TODO: Give error the path to stderr - fs::rename(path_helper_base(&chip_dir, &["lib.rs"]), &lib_rs_file) - .chain_err(|| "While moving lib.rs file")? - } - _ => {} - } - - let rustfmt_err_file = path_helper_base(&chip_dir, &["rustfmt.err.log"]); - if let Some(rustfmt_bin_path) = rustfmt_bin_path { - // Run `cargo fmt`, capturing stderr to a log file - - let output = Command::new(rustfmt_bin_path) - .arg(lib_rs_file) - .output() - .chain_err(|| "failed to format")?; - output.capture_outputs( - false, - "rustfmt", - None, - Some(&rustfmt_err_file), - &process_stderr_paths, - )?; - process_stderr_paths.push(rustfmt_err_file); - } - // Run `cargo check`, capturing stderr to a log file - let cargo_check_err_file = path_helper_base(&chip_dir, &["cargo-check.err.log"]); - let output = Command::new("cargo") - .arg("check") - .current_dir(&chip_dir) - .output() - .chain_err(|| "failed to check")?; - output.capture_outputs( - true, - "cargo check", - None, - Some(&cargo_check_err_file), - &process_stderr_paths, - )?; - process_stderr_paths.push(cargo_check_err_file); - Ok(if verbosity > 1 { - Some(process_stderr_paths) - } else { - None - }) -} diff --git a/ci/svd2rust-regress/src/tests.rs b/ci/svd2rust-regress/src/tests.rs deleted file mode 100644 index 58fce01a..00000000 --- a/ci/svd2rust-regress/src/tests.rs +++ /dev/null @@ -1,4263 +0,0 @@ -use inflections::Inflect; -use std::borrow::Cow; - -#[derive(Debug, PartialEq)] -pub enum Architecture { - // TODO: Coming soon! - // Avr, - CortexM, - Mips, - Msp430, - RiscV, - XtensaLX, -} - -#[derive(Debug)] -pub enum Manufacturer { - Atmel, - Freescale, - Fujitsu, - Holtek, - Microchip, - Nordic, - Nuvoton, - NXP, - SiliconLabs, - Spansion, - STMicro, - Toshiba, - SiFive, - TexasInstruments, - Espressif, -} - -#[derive(Debug)] -pub enum RunWhen { - Always, - NotShort, - - // TODO: Never doesn't really do anything right now - Never, -} - -pub struct TestCase { - pub arch: Architecture, - pub mfgr: Manufacturer, - pub chip: &'static str, - svd_url: Option<&'static str>, - pub should_pass: bool, - run_when: RunWhen, -} - -impl TestCase { - pub fn svd_url(&self) -> String { - match self.svd_url { - Some(u) => u.to_owned(), - None => format!("https://raw.githubusercontent.com/posborne/cmsis-svd/master/data/{vendor:?}/{chip}.svd", - vendor = self.mfgr, - chip = self.chip - ) - } - } - - pub fn should_run(&self, short_test: bool) -> bool { - match (&self.run_when, short_test) { - (&Always, _) => true, - (&NotShort, true) => false, - (_, _) => true, - } - } - - pub fn name(&self) -> String { - format!("{:?}-{}", self.mfgr, self.chip.replace(".", "_")) - .to_sanitized_snake_case() - .into() - } -} - -use self::Architecture::*; -use self::Manufacturer::*; -use self::RunWhen::*; - -/// List of chars that some vendors use in their peripheral/field names but -/// that are not valid in Rust ident -const BLACKLIST_CHARS: &[char] = &['(', ')', '[', ']']; - -/// Lovingly stolen from `svd2rust` -pub trait ToSanitizedCase { - fn to_sanitized_not_keyword_snake_case(&self) -> Cow; - fn to_sanitized_snake_case(&self) -> Cow { - let s = self.to_sanitized_not_keyword_snake_case(); - sanitize_keyword(s) - } -} - -impl ToSanitizedCase for str { - fn to_sanitized_not_keyword_snake_case(&self) -> Cow { - const INTERNALS: [&str; 4] = ["set_bit", "clear_bit", "bit", "bits"]; - - let s = self.replace(BLACKLIST_CHARS, ""); - match s.chars().next().unwrap_or('\0') { - '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => { - format!("_{}", s.to_snake_case()).into() - } - _ => { - let s = Cow::from(s.to_snake_case()); - if INTERNALS.contains(&s.as_ref()) { - s + "_" - } else { - s - } - } - } - } -} - -pub fn sanitize_keyword(sc: Cow) -> Cow { - const KEYWORDS: [&str; 55] = [ - "abstract", "alignof", "as", "async", "await", "become", "box", "break", "const", - "continue", "crate", "do", "dyn", "else", "enum", "extern", "false", "final", "fn", "for", - "if", "impl", "in", "let", "loop", "macro", "match", "mod", "move", "mut", "offsetof", - "override", "priv", "proc", "pub", "pure", "ref", "return", "self", "sizeof", "static", - "struct", "super", "trait", "true", "try", "type", "typeof", "unsafe", "unsized", "use", - "virtual", "where", "while", "yield", - ]; - if KEYWORDS.contains(&sc.as_ref()) { - sc + "_" - } else { - sc - } -} - -// NOTE: All chip names must be unique! -pub const TESTS: &[&TestCase] = &[ - // BAD-SVD missing resetValue - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "AT91SAM9CN11", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "AT91SAM9CN12", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "AT91SAM9G10", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "AT91SAM9G15", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "AT91SAM9G20", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "AT91SAM9G25", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "AT91SAM9G35", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "AT91SAM9M10", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "AT91SAM9M11", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "AT91SAM9N12", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "AT91SAM9X25", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "AT91SAM9X35", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3A4C", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3A8C", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3N00A", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3N00B", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3N0A", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3N0B", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3N0C", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3N1A", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3N1B", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3N1C", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3N2A", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3N2B", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3N2C", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3N4A", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3N4B", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3N4C", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3S1A", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3S1B", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3S1C", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3S2A", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3S2B", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3S2C", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3S4A", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3S4B", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3S4C", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3S8B", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3S8C", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3SD8B", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3SD8C", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3U1C", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3U1E", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3U2C", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3U2E", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3U4C", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3U4E", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3X4C", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3X4E", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3X8C", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM3X8E", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM4S16B", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM4S16C", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM4S8B", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM4S8C", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM4SD32B", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAM4SD32C", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAMA5D31", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAMA5D33", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAMA5D34", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAMA5D35", - svd_url: None, - should_pass: false, - run_when: Never, - }, - // FIXME(#107) "failed to resolve. Use of undeclared type or module `sercom0`" - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAMD21E15A", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAMD21E16A", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAMD21E17A", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAMD21E18A", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAMD21G16A", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAMD21G17A", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAMD21G18A", - svd_url: Some( - "https://raw.githubusercontent.com/wez/atsamd21-rs/master/svd/ATSAMD21G18A.svd", - ), - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAMD21J16A", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAMD21J17A", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAMD21J18A", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAMR21E16A", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAMR21E17A", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAMR21E18A", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAMR21G16A", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAMR21G17A", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Atmel, - chip: "ATSAMR21G18A", - svd_url: None, - should_pass: false, - run_when: Never, - }, - // BAD-SVD bad enumeratedValue value - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKV56F20", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKV56F22", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKV56F24", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKV58F20", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKV58F22", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKV58F24", - svd_url: None, - should_pass: false, - run_when: Never, - }, - // BAD-SVD field names are equivalent when case is ignored - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK61F15", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK61F15WS", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK70F12", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK70F15", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK70F15WS", - svd_url: None, - should_pass: false, - run_when: Never, - }, - // Test 1/3 of these to save time - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK02F12810", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK10D10", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK10D5", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK10D7", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK10DZ10", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK10F12", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK11D5", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK11D5WS", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK11DA5", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK12D5", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK20D10", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK20D5", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK20D7", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK20DZ10", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK20F12", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK21D5", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK21D5WS", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK21DA5", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK21F12", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK21FA12", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK22D5", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK22F12", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK22F12810", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK22F25612", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK22F51212", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK22FA12", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK24F12", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK24F25612", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK26F18", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK30D10", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK30D7", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK30DZ10", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK40D10", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK40D7", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK40DZ10", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK50D10", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK50D7", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK50DZ10", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK51D10", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK51D7", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK51DZ10", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK52D10", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK52DZ10", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK53D10", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK53DZ10", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK60D10", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK60DZ10", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK60F15", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK63F12", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK64F12", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK65F18", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK66F18", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK80F25615", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK81F25615", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MK82F25615", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKE14F16", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKE14Z7", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKE15Z7", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKE16F16", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKE18F16", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKL28T7_CORE0", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKL28T7_CORE1", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKL28Z7", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKL81Z7", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKL82Z7", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKS22F12", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKV10Z1287", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKV10Z7", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKV11Z7", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKV30F12810", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKV31F12810", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKV31F25612", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKV31F51212", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKV40F15", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKV42F16", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKV43F15", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKV44F15", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKV44F16", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKV45F15", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKV46F15", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKV46F16", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKW20Z4", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKW21D5", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKW21Z4", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKW22D5", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKW24D5", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKW30Z4", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKW31Z4", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKW40Z4", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKW41Z4", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKE02Z4", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKE04Z1284", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKE04Z4", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKE06Z4", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKE14D7", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKE15D7", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKL02Z4", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKL03Z4", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKL04Z4", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKL05Z4", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKL13Z644", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKL14Z4", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKL15Z4", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKL16Z4", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKL17Z4", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKL17Z644", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKL24Z4", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKL25Z4", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKL26Z4", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKL27Z4", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKL27Z644", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKL33Z4", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKL33Z644", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKL34Z4", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKL36Z4", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKL43Z4", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKL46Z4", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKM14ZA5", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKM33ZA5", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKM34Z7", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKM34ZA5", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "MKW01Z4", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "SKEAZ1284", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "SKEAZN642", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Freescale, - chip: "SKEAZN84", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AF10xN", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AF10xR", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AF11xK", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AF11xL", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AF11xM", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AF11xN", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AF12xK", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AF12xL", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AF13xK", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AF13xL", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AF13xM", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AF13xN", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AF14xL", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AF14xM", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AF14xN", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AF15xM", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AF15xN", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AF15xR", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AF1AxL", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AF1AxM", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AF1AxN", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AF31xK", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AF31xL", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AF31xM", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AF31xN", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AF34xL", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AF34xM", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AF34xN", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AF42xK", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AF42xL", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AFA3xL", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AFA3xM", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AFA3xN", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AFA4xL", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AFA4xM", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AFA4xN", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AFAAxL", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AFAAxM", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AFAAxN", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AFB4xL", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AFB4xM", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9AFB4xN", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9B160L", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9B160R", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9B360L", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9B360R", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9B460L", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9B460R", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9B560L", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9B560R", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF10xN", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF10xR", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF11xN", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF11xR", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF11xS", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF11xT", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF12xJ", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF12xK", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF12xL", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF12xM", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF12xS", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF12xT", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF21xS", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF21xT", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF30xN", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF30xR", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF31xN", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF31xR", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF31xS", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF31xT", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF32xK", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF32xL", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF32xM", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF32xS", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF32xT", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF40xN", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF40xR", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF41xN", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF41xR", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF41xS", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF41xT", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF42xS", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF42xT", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF50xN", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF50xR", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF51xN", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF51xR", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF51xS", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF51xT", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF52xK", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF52xL", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF52xM", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF52xS", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF52xT", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF61xS", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BF61xT", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BFD1xS", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "MB9BFD1xT", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "S6E1A1", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Fujitsu, - chip: "S6E2CC", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Holtek, - chip: "ht32f125x", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Holtek, - chip: "ht32f175x", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Holtek, - chip: "ht32f275x", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Nordic, - chip: "nrf51", - svd_url: None, - should_pass: true, - run_when: Always, - }, - // BAD-SVD two enumeratedValues have the same value - &TestCase { - arch: CortexM, - mfgr: Nordic, - chip: "nrf52", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Nuvoton, - chip: "M051_Series", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Nuvoton, - chip: "NUC100_Series", - svd_url: None, - should_pass: false, - run_when: Never, - }, - // BAD-SVD two enumeratedValues have the same name - &TestCase { - arch: CortexM, - mfgr: NXP, - chip: "LPC11Exx_v5", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: NXP, - chip: "LPC11Uxx_v7", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: NXP, - chip: "LPC11xx_v6a", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: NXP, - chip: "LPC11xx_v6", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: NXP, - chip: "LPC13Uxx_v1", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: NXP, - chip: "LPC15xx_v0.7", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: NXP, - chip: "LPC800_v0.3", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: NXP, - chip: "LPC11E6x_v0.8", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: NXP, - chip: "LPC176x5x_v0.2", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: NXP, - chip: "LPC11Cxx_v9", - svd_url: None, - should_pass: false, - run_when: Never, - }, - // BAD-SVD missing resetValue - &TestCase { - arch: CortexM, - mfgr: NXP, - chip: "LPC178x_7x", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: NXP, - chip: "LPC178x_7x_v0.8", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: NXP, - chip: "LPC408x_7x_v0.7", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: NXP, - chip: "LPC11Axxv0.6", - svd_url: None, - should_pass: false, - run_when: Never, - }, - // BAD-SVD bad identifier: contains a '.' - &TestCase { - arch: CortexM, - mfgr: NXP, - chip: "LPC11D14_svd_v4", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: NXP, - chip: "LPC13xx_svd_v1", - svd_url: None, - should_pass: false, - run_when: Never, - }, - // BAD-SVD bad identifier: contains a '/' - &TestCase { - arch: CortexM, - mfgr: NXP, - chip: "LPC18xx_svd_v18", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: NXP, - chip: "LPC43xx_43Sxx", - svd_url: None, - should_pass: false, - run_when: Never, - }, - // BAD-SVD uses the identifier '_' to name a reserved bitfield value - &TestCase { - arch: CortexM, - mfgr: NXP, - chip: "LPC1102_4_v4", - svd_url: None, - should_pass: false, - run_when: Never, - }, - // FIXME(???) "duplicate definitions for `write`" - // #99 regression test - &TestCase { - arch: CortexM, - mfgr: NXP, - chip: "LPC5410x_v0.4", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: NXP, - chip: "MK22F25612", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: NXP, - chip: "MK22F51212", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: NXP, - chip: "MKW41Z4", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: SiliconLabs, - chip: "SIM3C1x4_SVD", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: SiliconLabs, - chip: "SIM3C1x6_SVD", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: SiliconLabs, - chip: "SIM3C1x7_SVD", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: SiliconLabs, - chip: "SIM3L1x4_SVD", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: SiliconLabs, - chip: "SIM3L1x6_SVD", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: SiliconLabs, - chip: "SIM3L1x7_SVD", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: SiliconLabs, - chip: "SIM3U1x4_SVD", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: SiliconLabs, - chip: "SIM3U1x6_SVD", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: SiliconLabs, - chip: "SIM3U1x7_SVD", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - // FIXME(???) panicked at "c.text.clone()" - &TestCase { - arch: CortexM, - mfgr: SiliconLabs, - chip: "SIM3L1x8_SVD", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AF12xK", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AF12xL", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AF42xK", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AF42xL", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF12xJ", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF12xS", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF12xT", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF16xx", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF32xS", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF32xT", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF36xx", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF42xS", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF42xT", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF46xx", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF52xS", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF52xT", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF56xx", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - // Test half of these for the sake of time - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AF10xN", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AF10xR", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AF11xK", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AF11xL", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AF11xM", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AF11xN", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AF13xK", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AF13xL", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AF13xM", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AF13xN", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AF14xL", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AF14xM", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AF14xN", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AF15xM", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AF15xN", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AF15xR", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AF31xK", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AF31xL", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AF31xM", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AF31xN", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AF34xL", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AF34xM", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AF34xN", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AFA3xL", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AFA3xM", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AFA3xN", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AFA4xL", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AFA4xM", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AFA4xN", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AFB4xL", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AFB4xM", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9AFB4xN", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF10xN", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF10xR", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF11xN", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF11xR", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF11xS", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF11xT", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF12xK", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF12xL", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF12xM", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF21xS", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF21xT", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF30xN", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF30xR", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF31xN", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF31xR", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF31xS", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF31xT", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF32xK", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF32xL", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF32xM", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF40xN", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF40xR", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF41xN", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF41xR", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF41xS", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF41xT", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF50xN", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF50xR", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF51xN", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF51xR", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF51xS", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF51xT", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF52xK", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF52xL", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF52xM", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF61xS", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BF61xT", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BFD1xS", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: Spansion, - chip: "MB9BFD1xT", - svd_url: None, - should_pass: true, - run_when: NotShort, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F030", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F031x", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F042x", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F072x", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F091x", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F0xx", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F100xx", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F101xx", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F102xx", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F103xx", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F105xx", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F107xx", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F20x", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F21x", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F301", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F302", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F303", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F3x4", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F373", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F401", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F405", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F407", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F410", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F411", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F412", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F413", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F427", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F429", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F446", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F469", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F7x", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F7x2", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F7x3", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F7x5", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F7x6", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F7x7", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32F7x9", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32G07x", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32G431xx", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32G441xx", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32G471xx", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32G474xx", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32G483xx", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32G484xx", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32L100", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32L15xC", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32L15xxE", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32L15xxxA", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32L1xx", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32L4x6", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32W108", - svd_url: None, - should_pass: true, - run_when: Always, - }, - // FIXME(#91) "field is never used: `register`" - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32L051x", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32L052x", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32L053x", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32L062x", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: STMicro, - chip: "STM32L063x", - svd_url: None, - should_pass: false, - run_when: Never, - }, - // BAD-SVD resetValue is bigger than the register size - &TestCase { - arch: CortexM, - mfgr: Toshiba, - chip: "M365", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Toshiba, - chip: "M367", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Toshiba, - chip: "M368", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Toshiba, - chip: "M369", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Toshiba, - chip: "M36B", - svd_url: None, - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: CortexM, - mfgr: Toshiba, - chip: "M061", - svd_url: None, - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: RiscV, - mfgr: SiFive, - chip: "E310x", - svd_url: Some("https://raw.githubusercontent.com/riscv-rust/e310x/master/e310x.svd"), - should_pass: false, - run_when: Never, - }, - &TestCase { - arch: Msp430, - mfgr: TexasInstruments, - chip: "msp430g2553", - svd_url: Some("https://github.com/pftbest/msp430g2553/raw/v0.1.3-svd/msp430g2553.svd"), - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: Msp430, - mfgr: TexasInstruments, - chip: "msp430fr2355", - svd_url: Some( - "https://raw.githubusercontent.com/YuhanLiin/msp430fr2355/rt-up/msp430fr2355.svd", - ), - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: XtensaLX, - mfgr: Espressif, - chip: "esp32", - svd_url: Some( - "https://raw.githubusercontent.com/espressif/svd/main/svd/esp32.svd", - ), - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: XtensaLX, - mfgr: Espressif, - chip: "esp32s2", - svd_url: Some( - "https://raw.githubusercontent.com/espressif/svd/main/svd/esp32s2.svd", - ), - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: XtensaLX, - mfgr: Espressif, - chip: "esp32s3", - svd_url: Some( - "https://raw.githubusercontent.com/espressif/svd/main/svd/esp32s3.svd", - ), - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: RiscV, - mfgr: Espressif, - chip: "esp32c3", - svd_url: Some( - "https://raw.githubusercontent.com/espressif/svd/main/svd/esp32c3.svd", - ), - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: Mips, - mfgr: Microchip, - chip: "pic32mx170f256b", - svd_url: Some( - "https://raw.githubusercontent.com/kiffie/pic32-pac/master/pic32mx1xxfxxxb/PIC32MX170F256B.svd.patched", - ), - should_pass: true, - run_when: Always, - }, - &TestCase { - arch: Mips, - mfgr: Microchip, - chip: "pic32mx270f256b", - svd_url: Some( - "https://raw.githubusercontent.com/kiffie/pic32-pac/master/pic32mx2xxfxxxb/PIC32MX270F256B.svd.patched", - ), - should_pass: true, - run_when: Always, - }, -]; diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 00000000..024d8b78 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,392 @@ +use anyhow::{bail, Result}; +use proc_macro2::{Span, TokenStream}; +use std::{ + collections::HashMap, + ops::{Deref, DerefMut}, + path::{Path, PathBuf}, + str::FromStr, +}; +use syn::{punctuated::Punctuated, Ident}; + +use crate::util::path_segment; + +#[cfg_attr(feature = "serde", derive(serde::Deserialize), serde(default))] +#[derive(Clone, PartialEq, Eq, Debug, Default)] +#[non_exhaustive] +pub struct Config { + pub target: Target, + pub atomics: bool, + pub atomics_feature: Option, + pub generic_mod: bool, + pub make_mod: bool, + pub skip_crate_attributes: bool, + pub ignore_groups: bool, + pub keep_list: bool, + pub strict: bool, + pub feature_group: bool, + pub feature_peripheral: bool, + pub max_cluster_size: bool, + pub impl_debug: bool, + pub impl_debug_feature: Option, + pub impl_defmt: Option, + pub output_dir: Option, + pub input: Option, + pub source_type: SourceType, + pub log_level: Option, + pub interrupt_link_section: Option, + pub reexport_core_peripherals: bool, + pub reexport_interrupt: bool, + pub ident_formats: IdentFormats, + pub ident_formats_theme: Option, + pub field_names_for_enums: bool, + pub base_address_shift: u64, + /// Path to YAML file with chip-specific settings + pub settings_file: Option, + /// Chip-specific settings + pub settings: Settings, +} + +impl Config { + pub fn extra_build(&self) -> Option { + self.settings.extra_build() + } +} + +#[allow(clippy::upper_case_acronyms)] +#[allow(non_camel_case_types)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +pub enum Target { + #[cfg_attr(feature = "serde", serde(rename = "cortex-m"))] + #[default] + CortexM, + #[cfg_attr(feature = "serde", serde(rename = "msp430"))] + Msp430, + #[cfg_attr(feature = "serde", serde(rename = "riscv"))] + RISCV, + #[cfg_attr(feature = "serde", serde(rename = "xtensa-lx"))] + XtensaLX, + #[cfg_attr(feature = "serde", serde(rename = "mips"))] + Mips, + #[cfg_attr(feature = "serde", serde(rename = "none"))] + None, +} + +impl std::fmt::Display for Target { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + Target::CortexM => "cortex-m", + Target::Msp430 => "msp430", + Target::RISCV => "riscv", + Target::XtensaLX => "xtensa-lx", + Target::Mips => "mips", + Target::None => "none", + }) + } +} + +impl Target { + pub fn parse(s: &str) -> Result { + Ok(match s { + "cortex-m" => Target::CortexM, + "msp430" => Target::Msp430, + "riscv" => Target::RISCV, + "xtensa-lx" => Target::XtensaLX, + "mips" => Target::Mips, + "none" => Target::None, + _ => bail!("unknown target {}", s), + }) + } + + pub const fn all() -> &'static [Target] { + use self::Target::*; + &[CortexM, Msp430, RISCV, XtensaLX, Mips] + } +} + +#[cfg_attr( + feature = "serde", + derive(serde::Deserialize), + serde(rename_all = "lowercase") +)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +pub enum SourceType { + #[default] + Xml, + #[cfg(feature = "yaml")] + Yaml, + #[cfg(feature = "json")] + Json, +} + +impl SourceType { + /// Make a new [`SourceType`] from a given extension. + pub fn from_extension(s: &str) -> Option { + match s { + "svd" | "xml" => Some(Self::Xml), + #[cfg(feature = "yaml")] + "yml" | "yaml" => Some(Self::Yaml), + #[cfg(feature = "json")] + "json" => Some(Self::Json), + _ => None, + } + } + pub fn from_path(path: &Path) -> Self { + path.extension() + .and_then(|e| e.to_str()) + .and_then(Self::from_extension) + .unwrap_or_default() + } +} + +#[cfg_attr( + feature = "serde", + derive(serde::Deserialize), + serde(rename_all = "lowercase") +)] +#[derive(Clone, Debug, PartialEq, Eq, Default)] +#[non_exhaustive] +pub enum Case { + #[default] + Constant, + Pascal, + Snake, +} + +impl Case { + pub fn parse(c: &str) -> Result, IdentFormatError> { + Ok(match c { + "" | "unchanged" | "svd" => None, + "p" | "pascal" | "type" => Some(Case::Pascal), + "s" | "snake" | "lower" => Some(Case::Snake), + "c" | "constant" | "upper" => Some(Case::Constant), + _ => { + return Err(IdentFormatError::UnknownCase(c.into())); + } + }) + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum IdentFormatError { + UnknownCase(String), + Other, +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize), serde(default))] +pub struct IdentFormat { + // Ident case. `None` means don't change + pub case: Option, + pub prefix: String, + pub suffix: String, +} + +impl IdentFormat { + pub fn case(mut self, case: Case) -> Self { + self.case = Some(case); + self + } + pub fn constant_case(mut self) -> Self { + self.case = Some(Case::Constant); + self + } + pub fn pascal_case(mut self) -> Self { + self.case = Some(Case::Pascal); + self + } + pub fn snake_case(mut self) -> Self { + self.case = Some(Case::Snake); + self + } + pub fn prefix(mut self, prefix: &str) -> Self { + self.prefix = prefix.into(); + self + } + pub fn suffix(mut self, suffix: &str) -> Self { + self.suffix = suffix.into(); + self + } + pub fn parse(s: &str) -> Result { + let mut f = s.split(':'); + match (f.next(), f.next(), f.next()) { + (Some(p), Some(c), Some(s)) => { + let case = Case::parse(c)?; + Ok(Self { + case, + prefix: p.into(), + suffix: s.into(), + }) + } + (Some(p), Some(c), None) => { + let case = Case::parse(c)?; + Ok(Self { + case, + prefix: p.into(), + suffix: "".into(), + }) + } + (Some(c), None, None) => { + let case = Case::parse(c)?; + Ok(Self { + case, + prefix: "".into(), + suffix: "".into(), + }) + } + _ => Err(IdentFormatError::Other), + } + } +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize), serde(default))] +pub struct IdentFormats(HashMap); + +impl IdentFormats { + fn common() -> Self { + let snake = IdentFormat::default().snake_case(); + Self(HashMap::from([ + ("field_accessor".into(), snake.clone()), + ("register_accessor".into(), snake.clone()), + ("enum_value_accessor".into(), snake.clone()), + ("cluster_accessor".into(), snake.clone()), + ("register_mod".into(), snake.clone()), + ("cluster_mod".into(), snake.clone()), + ("peripheral_mod".into(), snake.clone()), + ("peripheral_feature".into(), snake), + ])) + } + + pub fn default_theme() -> Self { + let mut map = Self::common(); + + let pascal = IdentFormat::default().pascal_case(); + map.extend([ + ("field_reader".into(), pascal.clone().suffix("R")), + ("field_writer".into(), pascal.clone().suffix("W")), + ("enum_name".into(), pascal.clone()), + ("enum_read_name".into(), pascal.clone()), + ("enum_write_name".into(), pascal.clone().suffix("WO")), + ("enum_value".into(), pascal.clone()), + ("interrupt".into(), IdentFormat::default()), + ("register".into(), pascal.clone()), + ("cluster".into(), pascal.clone()), + ("register_spec".into(), pascal.clone().suffix("Spec")), + ("peripheral".into(), pascal), + ( + "peripheral_singleton".into(), + IdentFormat::default().snake_case(), + ), + ]); + + map + } + pub fn legacy_theme() -> Self { + let mut map = Self::common(); + + let constant = IdentFormat::default().constant_case(); + map.extend([ + ("field_reader".into(), constant.clone().suffix("_R")), + ("field_writer".into(), constant.clone().suffix("_W")), + ("enum_name".into(), constant.clone().suffix("_A")), + ("enum_read_name".into(), constant.clone().suffix("_A")), + ("enum_write_name".into(), constant.clone().suffix("_AW")), + ("enum_value".into(), constant.clone()), + ("interrupt".into(), constant.clone()), + ("cluster".into(), constant.clone()), + ("register".into(), constant.clone()), + ("register_spec".into(), constant.clone().suffix("_SPEC")), + ("peripheral".into(), constant.clone()), + ("peripheral_singleton".into(), constant), + ]); + + map + } +} + +impl Deref for IdentFormats { + type Target = HashMap; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl DerefMut for IdentFormats { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +#[cfg_attr( + feature = "serde", + derive(serde::Deserialize), + serde(rename_all = "lowercase") +)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum IdentFormatsTheme { + Legacy, +} + +#[cfg_attr(feature = "serde", derive(serde::Deserialize), serde(default))] +#[derive(Clone, PartialEq, Eq, Debug, Default)] +#[non_exhaustive] +/// Chip-specific settings +pub struct Settings { + /// Path to chip HTML generated by svdtools + pub html_url: Option, + pub crate_path: Option, + /// RISC-V specific settings + pub riscv_config: Option, +} + +impl Settings { + pub fn update_from(&mut self, source: Self) { + if source.html_url.is_some() { + self.html_url = source.html_url; + } + if source.crate_path.is_some() { + self.crate_path = source.crate_path; + } + if source.riscv_config.is_some() { + self.riscv_config = source.riscv_config; + } + } + + pub fn extra_build(&self) -> Option { + self.riscv_config.as_ref().and_then(|cfg| cfg.extra_build()) + } +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct CratePath(pub syn::Path); + +impl Default for CratePath { + fn default() -> Self { + let mut segments = Punctuated::new(); + segments.push(path_segment(Ident::new("crate", Span::call_site()))); + Self(syn::Path { + leading_colon: None, + segments, + }) + } +} + +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for CratePath { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + Ok(Self::from_str(&s).unwrap()) + } +} + +impl FromStr for CratePath { + type Err = syn::Error; + fn from_str(s: &str) -> std::result::Result { + syn::parse_str(s).map(Self) + } +} + +pub mod riscv; diff --git a/src/config/riscv.rs b/src/config/riscv.rs new file mode 100644 index 00000000..90100762 --- /dev/null +++ b/src/config/riscv.rs @@ -0,0 +1,67 @@ +use proc_macro2::TokenStream; +use quote::quote; + +#[cfg_attr(feature = "serde", derive(serde::Deserialize), serde(default))] +#[derive(Clone, PartialEq, Eq, Debug, Default)] +#[non_exhaustive] +pub struct RiscvConfig { + pub core_interrupts: Vec, + pub exceptions: Vec, + pub priorities: Vec, + pub harts: Vec, + pub clint: Option, + pub plic: Option, + pub mtvec_align: Option, +} + +impl RiscvConfig { + pub fn extra_build(&self) -> Option { + self.mtvec_align.map(|align| { + quote! { + // set environment variable RISCV_MTVEC_ALIGN enfoce correct byte alignment of interrupt vector. + println!( + "cargo:rustc-env=RISCV_MTVEC_ALIGN={}", + #align + ); + println!("cargo:rerun-if-env-changed=RISCV_MTVEC_ALIGN"); + } + }) + } +} + +#[cfg_attr(feature = "serde", derive(serde::Deserialize), serde(default))] +#[derive(Clone, PartialEq, Eq, Debug, Default)] +#[non_exhaustive] +pub struct RiscvEnumItem { + pub name: String, + pub value: usize, + pub description: Option, +} + +impl RiscvEnumItem { + pub fn description(&self) -> String { + let description = match &self.description { + Some(d) => d, + None => &self.name, + }; + format!("{} - {}", self.value, description) + } +} + +#[cfg_attr(feature = "serde", derive(serde::Deserialize), serde(default))] +#[derive(Clone, PartialEq, Eq, Debug, Default)] +#[non_exhaustive] +pub struct RiscvClintConfig { + pub name: String, + pub freq: Option, + pub async_delay: bool, +} + +#[cfg_attr(feature = "serde", derive(serde::Deserialize), serde(default))] +#[derive(Clone, PartialEq, Eq, Debug, Default)] +#[non_exhaustive] +pub struct RiscvPlicConfig { + pub name: String, + pub core_interrupt: Option, + pub hart_id: Option, +} diff --git a/src/generate/array_proxy.rs b/src/generate/array_proxy.rs deleted file mode 100644 index b47074ba..00000000 --- a/src/generate/array_proxy.rs +++ /dev/null @@ -1,47 +0,0 @@ -/// Access an array of `COUNT` items of type `T` with the items `STRIDE` bytes -/// apart. This is a zero-sized-type. No objects of this type are ever -/// actually created, it is only a convenience for wrapping pointer arithmetic. -/// -/// There is no safe way to produce items of this type. Unsafe code can produce -/// references by pointer casting. It is up to the unsafe code doing that, to -/// ensure that the memory really is backed by appropriate content. -/// -/// Typically, this is used for accessing hardware registers. -pub struct ArrayProxy { - /// As well as providing a PhantomData, this field is non-public, and - /// therefore ensures that code outside of this module can never create - /// an ArrayProxy. - _array: marker::PhantomData, -} -#[allow(clippy::len_without_is_empty)] -impl ArrayProxy { - /// Get a reference from an [ArrayProxy] with no bounds checking. - pub unsafe fn get_ref(&self, index: usize) -> &T { - let base = self as *const Self as usize; - let address = base + S * index; - &*(address as *const T) - } - /// Get a reference from an [ArrayProxy], or return `None` if the index - /// is out of bounds. - pub fn get(&self, index: usize) -> Option<&T> { - if index < C { - Some(unsafe { self.get_ref(index) }) - } else { - None - } - } - /// Return the number of items. - pub fn len(&self) -> usize { - C - } -} - -impl core::ops::Index for ArrayProxy { - type Output = T; - fn index(&self, index: usize) -> &T { - // Do a real array dereference for the bounds check. - #[allow(clippy::no_effect)] - [(); C][index]; - unsafe { self.get_ref(index) } - } -} diff --git a/src/generate/device.rs b/src/generate/device.rs index 4065f4b9..96e1d332 100644 --- a/src/generate/device.rs +++ b/src/generate/device.rs @@ -1,17 +1,17 @@ use crate::svd::{array::names, Device, Peripheral}; -use proc_macro2::{Ident, Span, TokenStream}; +use proc_macro2::{Span, TokenStream}; use quote::{quote, ToTokens}; -use log::debug; -use std::borrow::Cow; +use log::{debug, warn}; use std::fs::File; use std::io::Write; +use std::path::Path; -use crate::util::{self, Config, ToSanitizedCase}; -use crate::Target; +use crate::config::{Config, Target}; +use crate::util::{self, ident}; use anyhow::{Context, Result}; -use crate::generate::{interrupt, peripheral}; +use crate::generate::{interrupt, peripheral, riscv}; /// Whole device generation pub fn render(d: &Device, config: &Config, device_x: &mut String) -> Result { @@ -28,56 +28,41 @@ pub fn render(d: &Device, config: &Config, device_x: &mut String) -> Result Result { + if config.reexport_core_peripherals { + let fpu = fpu_present.then(|| quote!(FPU,)); + out.extend(quote! { + pub use cortex_m::peripheral::Peripherals as CorePeripherals; + pub use cortex_m::peripheral::{ + CBP, CPUID, DCB, DWT, FPB, #fpu ITM, MPU, NVIC, SCB, SYST, TPIU, + }; + }); + } - if fpu_present { - out.extend(quote! { - pub use cortex_m::peripheral::{ - CBP, CPUID, DCB, DWT, FPB, FPU, ITM, MPU, NVIC, SCB, SYST, TPIU, - }; - }); - } else { - out.extend(quote! { - pub use cortex_m::peripheral::{ - CBP, CPUID, DCB, DWT, FPB, ITM, MPU, NVIC, SCB, SYST, TPIU, - }; - }); + if config.reexport_interrupt { + out.extend(quote! { + #[cfg(feature = "rt")] + pub use cortex_m_rt::interrupt; + #[cfg(feature = "rt")] + pub use self::Interrupt as interrupt; + }); + } } - } - if config.target == Target::Msp430 { - out.extend(quote! { + Target::Msp430 => { // XXX: Are there any core peripherals, really? Requires bump of msp430 crate. // pub use msp430::peripheral::Peripherals as CorePeripherals; - #[cfg(feature = "rt")] - pub use msp430_rt::interrupt; - #[cfg(feature = "rt")] - pub use self::Interrupt as interrupt; - }); - } + if config.reexport_interrupt { + out.extend(quote! { + #[cfg(feature = "rt")] + pub use msp430_rt::interrupt; + #[cfg(feature = "rt")] + pub use self::Interrupt as interrupt; + }); + } + } - if config.target == Target::Mips { - out.extend(quote! { - #[cfg(feature = "rt")] - pub use mips_rt::interrupt; - }); + Target::Mips => { + if config.reexport_interrupt { + out.extend(quote! { + #[cfg(feature = "rt")] + pub use mips_rt::interrupt; + }); + } + } + + _ => {} } - let generic_file = std::str::from_utf8(include_bytes!("generic.rs"))?; + let generic_file = include_str!("generic.rs"); + let generic_reg_file = include_str!("generic_reg_vcell.rs"); + let generic_atomic_file = include_str!("generic_atomic.rs"); if config.generic_mod { - let mut file = File::create(config.output_dir.join("generic.rs"))?; - writeln!(file, "{}", generic_file)?; - if config.target == Target::Msp430 && config.nightly { - let msp430_atomic_file = - std::str::from_utf8(include_bytes!("generic_msp430_atomic.rs"))?; - writeln!(file, "\n{}", msp430_atomic_file)?; - } - if config.const_generic { - let array_proxy = std::str::from_utf8(include_bytes!("array_proxy.rs"))?; - writeln!(file, "{}", array_proxy)?; + let mut file = File::create( + config + .output_dir + .as_deref() + .unwrap_or(Path::new(".")) + .join("generic.rs"), + )?; + writeln!(file, "{generic_file}")?; + writeln!(file, "{generic_reg_file}")?; + if config.atomics { + if let Some(atomics_feature) = config.atomics_feature.as_ref() { + writeln!(file, "#[cfg(feature = \"{atomics_feature}\")]")?; + } + writeln!(file, "\n{generic_atomic_file}")?; } if !config.make_mod { @@ -170,16 +166,12 @@ pub fn render(d: &Device, config: &Config, device_x: &mut String) -> Result Result { + if config.settings.riscv_config.is_none() { + warn!("No settings file provided for RISC-V target. Using legacy interrupts rendering"); + warn!("Please, consider migrating your PAC to riscv 0.12.0 or later"); + out.extend(interrupt::render( + config.target, + &d.peripherals, + device_x, + config, + )?); + } else { + debug!("Rendering RISC-V specific code"); + out.extend(riscv::render(&d.peripherals, device_x, config)?); + } + } + _ => { + debug!("Rendering interrupts"); + out.extend(interrupt::render( + config.target, + &d.peripherals, + device_x, + config, + )?); + } + } + let feature_format = config.ident_formats.get("peripheral_feature").unwrap(); for p in &d.peripherals { if config.target == Target::CortexM && core_peripherals.contains(&p.name.to_uppercase().as_ref()) @@ -207,25 +219,23 @@ pub fn render(d: &Device, config: &Config, device_x: &mut String) -> Result out.extend(periph), - Err(e) => { - let descrip = p.description.as_deref().unwrap_or("No description"); - let group_name = p.group_name.as_deref().unwrap_or("No group name"); - let mut context_string = format!( - "Rendering error at peripheral\nName: {}\nDescription: {descrip}\nGroup: {group_name}", - p.name - ); - if let Some(dname) = p.derived_from.as_ref() { - context_string = format!("{context_string}\nDerived from: {dname}"); - } - let mut e = Err(e); - e = e.with_context(|| context_string); - return e; + let periph = peripheral::render(p, &index, config).with_context(|| { + let group_name = p.group_name.as_deref().unwrap_or("No group name"); + let mut context_string = + format!("can't render peripheral '{}', group '{group_name}'", p.name); + if let Some(dname) = p.derived_from.as_ref() { + context_string += &format!(", derived from: '{dname}'"); } - }; + context_string + })?; + + out.extend(periph); if p.registers .as_ref() @@ -240,52 +250,42 @@ pub fn render(d: &Device, config: &Config, device_x: &mut String) -> Result { let p_name = util::name_of(p, config.ignore_groups); - let p_snake = p_name.to_sanitized_snake_case(); - let p = p_name.to_sanitized_constant_case(); - let id = Ident::new(&p, Span::call_site()); + let p_feature = feature_format.apply(&p_name); + let p_ty = ident(&p_name, config, "peripheral", span); + let p_singleton = ident(&p_name, config, "peripheral_singleton", span); if config.feature_peripheral { - feature_attribute.extend(quote! { #[cfg(feature = #p_snake)] }) + feature_attribute.extend(quote! { #[cfg(feature = #p_feature)] }) }; fields.extend(quote! { - #[doc = #p] + #[doc = #p_name] #feature_attribute - pub #id: #id, + pub #p_singleton: #p_ty, }); - exprs.extend(quote!(#feature_attribute #id: #id { _marker: PhantomData },)); + exprs.extend(quote!(#feature_attribute #p_singleton: #p_ty::steal(),)); } - Peripheral::Array(_p, dim_element) => { - let p_names: Vec> = names(p, dim_element).map(|n| n.into()).collect(); - let p = p_names.iter().map(|p| p.to_sanitized_constant_case()); - let ids_f = p.clone().map(|p| Ident::new(&p, Span::call_site())); - let ids_e = ids_f.clone(); - let feature_attribute = p_names - .iter() - .map(|p_name| { - let p_snake = p_name.to_sanitized_snake_case(); - let mut feature_attribute = feature_attribute.clone(); - if config.feature_peripheral { - feature_attribute.extend(quote! { #[cfg(feature = #p_snake)] }) - }; - feature_attribute - }) - .collect::>(); - fields.extend(quote! { - #( - #[doc = #p] + Peripheral::Array(p, dim_element) => { + for p_name in names(p, dim_element) { + let p_feature = feature_format.apply(&p_name); + let p_ty = ident(&p_name, config, "peripheral", span); + let p_singleton = ident(&p_name, config, "peripheral_singleton", span); + if config.feature_peripheral { + feature_attribute.extend(quote! { #[cfg(feature = #p_feature)] }) + }; + fields.extend(quote! { + #[doc = #p_name] #feature_attribute - pub #ids_f: #ids_f, - )* - }); - exprs.extend( - quote!(#(#feature_attribute #ids_e: #ids_e { _marker: PhantomData },)*), - ); + pub #p_singleton: #p_ty, + }); + exprs.extend(quote!(#feature_attribute #p_singleton: #p_ty::steal(),)); + } } } } diff --git a/src/generate/generic.rs b/src/generate/generic.rs index 4ed05dc4..e527a4a8 100644 --- a/src/generate/generic.rs +++ b/src/generate/generic.rs @@ -1,9 +1,54 @@ use core::marker; +/// Generic peripheral accessor +pub struct Periph { + _marker: marker::PhantomData, +} + +unsafe impl Send for Periph {} + +impl Periph { + ///Pointer to the register block + pub const PTR: *const RB = A as *const _; + + ///Return the pointer to the register block + #[inline(always)] + pub const fn ptr() -> *const RB { + Self::PTR + } + + /// Steal an instance of this peripheral + /// + /// # Safety + /// + /// Ensure that the new instance of the peripheral cannot be used in a way + /// that may race with any existing instances, for example by only + /// accessing read-only or write-only registers, or by consuming the + /// original peripheral and using critical sections to coordinate + /// access between multiple new instances. + /// + /// Additionally, other software such as HALs may rely on only one + /// peripheral instance existing to ensure memory safety; ensure + /// no stolen instances are passed to such software. + pub unsafe fn steal() -> Self { + Self { + _marker: marker::PhantomData, + } + } +} + +impl core::ops::Deref for Periph { + type Target = RB; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + unsafe { &*Self::PTR } + } +} + /// Raw register type (`u8`, `u16`, `u32`, ...) pub trait RawReg: Copy - + Default + From + core::ops::BitOr + core::ops::BitAnd @@ -14,8 +59,10 @@ pub trait RawReg: { /// Mask for bits of width `WI` fn mask() -> Self; - /// Mask for bits of width 1 - fn one() -> Self; + /// `0` + const ZERO: Self; + /// `1` + const ONE: Self; } macro_rules! raw_reg { @@ -25,14 +72,15 @@ macro_rules! raw_reg { fn mask() -> Self { $mask::() } - #[inline(always)] - fn one() -> Self { - 1 - } + const ZERO: Self = 0; + const ONE: Self = 1; } const fn $mask() -> $U { <$U>::MAX >> ($size - WI) } + impl FieldSpec for $U { + type Ux = $U; + } }; } @@ -47,28 +95,34 @@ pub trait RegisterSpec { type Ux: RawReg; } +/// Raw field type +pub trait FieldSpec: Sized { + /// Raw field type (`u8`, `u16`, `u32`, ...). + type Ux: Copy + core::fmt::Debug + PartialEq + From; +} + +/// Marker for fields with fixed values +pub trait IsEnum: FieldSpec {} + /// Trait implemented by readable registers to enable the `read` method. /// -/// Registers marked with `Writable` can be also `modify`'ed. -pub trait Readable: RegisterSpec { - /// Result from a call to `read` and argument to `modify`. - type Reader: From> + core::ops::Deref>; -} +/// Registers marked with `Writable` can be also be `modify`'ed. +pub trait Readable: RegisterSpec {} /// Trait implemented by writeable registers. /// /// This enables the `write`, `write_with_zero` and `reset` methods. /// -/// Registers marked with `Readable` can be also `modify`'ed. +/// Registers marked with `Readable` can be also be `modify`'ed. pub trait Writable: RegisterSpec { - /// Writer type argument to `write`, et al. - type Writer: From> + core::ops::DerefMut>; + /// Is it safe to write any bits to register + type Safety; /// Specifies the register bits that are not changed if you pass `1` and are changed if you pass `0` - const ZERO_TO_MODIFY_FIELDS_BITMAP: Self::Ux; + const ZERO_TO_MODIFY_FIELDS_BITMAP: Self::Ux = Self::Ux::ZERO; /// Specifies the register bits that are not changed if you pass `0` and are changed if you pass `1` - const ONE_TO_MODIFY_FIELDS_BITMAP: Self::Ux; + const ONE_TO_MODIFY_FIELDS_BITMAP: Self::Ux = Self::Ux::ZERO; } /// Reset value of the register. @@ -77,7 +131,7 @@ pub trait Writable: RegisterSpec { /// register by using the `reset` method. pub trait Resettable: RegisterSpec { /// Reset value of the register. - const RESET_VALUE: Self::Ux; + const RESET_VALUE: Self::Ux = Self::Ux::ZERO; /// Reset value of the register. #[inline(always)] @@ -86,166 +140,112 @@ pub trait Resettable: RegisterSpec { } } -/// This structure provides volatile access to registers. -#[repr(transparent)] -pub struct Reg { - register: vcell::VolatileCell, - _marker: marker::PhantomData, -} +#[doc(hidden)] +pub mod raw { + use super::{marker, BitM, FieldSpec, RegisterSpec, Unsafe, Writable}; -unsafe impl Send for Reg where REG::Ux: Send {} + pub struct R { + pub(crate) bits: REG::Ux, + pub(super) _reg: marker::PhantomData, + } -impl Reg { - /// Returns the underlying memory address of register. - /// - /// ```ignore - /// let reg_ptr = periph.reg.as_ptr(); - /// ``` - #[inline(always)] - pub fn as_ptr(&self) -> *mut REG::Ux { - self.register.as_ptr() + pub struct W { + ///Writable bits + pub(crate) bits: REG::Ux, + pub(super) _reg: marker::PhantomData, } -} -impl Reg { - /// Reads the contents of a `Readable` register. - /// - /// You can read the raw contents of a register by using `bits`: - /// ```ignore - /// let bits = periph.reg.read().bits(); - /// ``` - /// or get the content of a particular field of a register: - /// ```ignore - /// let reader = periph.reg.read(); - /// let bits = reader.field1().bits(); - /// let flag = reader.field2().bit_is_set(); - /// ``` - #[inline(always)] - pub fn read(&self) -> REG::Reader { - REG::Reader::from(R { - bits: self.register.get(), - _reg: marker::PhantomData, - }) + pub struct FieldReader + where + FI: FieldSpec, + { + pub(crate) bits: FI::Ux, + _reg: marker::PhantomData, } -} -impl Reg { - /// Writes the reset value to `Writable` register. - /// - /// Resets the register to its initial state. - #[inline(always)] - pub fn reset(&self) { - self.register.set(REG::RESET_VALUE) + impl FieldReader { + /// Creates a new instance of the reader. + #[allow(unused)] + #[inline(always)] + pub(crate) const fn new(bits: FI::Ux) -> Self { + Self { + bits, + _reg: marker::PhantomData, + } + } } - /// Writes bits to a `Writable` register. - /// - /// You can write raw bits into a register: - /// ```ignore - /// periph.reg.write(|w| unsafe { w.bits(rawbits) }); - /// ``` - /// or write only the fields you need: - /// ```ignore - /// periph.reg.write(|w| w - /// .field1().bits(newfield1bits) - /// .field2().set_bit() - /// .field3().variant(VARIANT) - /// ); - /// ``` - /// or an alternative way of saying the same: - /// ```ignore - /// periph.reg.write(|w| { - /// w.field1().bits(newfield1bits); - /// w.field2().set_bit(); - /// w.field3().variant(VARIANT) - /// }); - /// ``` - /// In the latter case, other fields will be set to their reset value. - #[inline(always)] - pub fn write(&self, f: F) + pub struct BitReader { + pub(crate) bits: bool, + _reg: marker::PhantomData, + } + + impl BitReader { + /// Creates a new instance of the reader. + #[allow(unused)] + #[inline(always)] + pub(crate) const fn new(bits: bool) -> Self { + Self { + bits, + _reg: marker::PhantomData, + } + } + } + + #[must_use = "after creating `FieldWriter` you need to call field value setting method"] + pub struct FieldWriter<'a, REG, const WI: u8, FI = u8, Safety = Unsafe> where - F: FnOnce(&mut REG::Writer) -> &mut W, + REG: Writable + RegisterSpec, + FI: FieldSpec, { - self.register.set( - f(&mut REG::Writer::from(W { - bits: REG::RESET_VALUE & !REG::ONE_TO_MODIFY_FIELDS_BITMAP - | REG::ZERO_TO_MODIFY_FIELDS_BITMAP, - _reg: marker::PhantomData, - })) - .bits, - ); + pub(crate) w: &'a mut W, + pub(crate) o: u8, + _field: marker::PhantomData<(FI, Safety)>, } -} -impl Reg { - /// Writes 0 to a `Writable` register. - /// - /// Similar to `write`, but unused bits will contain 0. - /// - /// # Safety - /// - /// Unsafe to use with registers which don't allow to write 0. - #[inline(always)] - pub unsafe fn write_with_zero(&self, f: F) + impl<'a, REG, const WI: u8, FI, Safety> FieldWriter<'a, REG, WI, FI, Safety> where - F: FnOnce(&mut REG::Writer) -> &mut W, + REG: Writable + RegisterSpec, + FI: FieldSpec, { - self.register.set( - f(&mut REG::Writer::from(W { - bits: REG::Ux::default(), - _reg: marker::PhantomData, - })) - .bits, - ); + /// Creates a new instance of the writer + #[allow(unused)] + #[inline(always)] + pub(crate) fn new(w: &'a mut W, o: u8) -> Self { + Self { + w, + o, + _field: marker::PhantomData, + } + } } -} -impl Reg { - /// Modifies the contents of the register by reading and then writing it. - /// - /// E.g. to do a read-modify-write sequence to change parts of a register: - /// ```ignore - /// periph.reg.modify(|r, w| unsafe { w.bits( - /// r.bits() | 3 - /// ) }); - /// ``` - /// or - /// ```ignore - /// periph.reg.modify(|_, w| w - /// .field1().bits(newfield1bits) - /// .field2().set_bit() - /// .field3().variant(VARIANT) - /// ); - /// ``` - /// or an alternative way of saying the same: - /// ```ignore - /// periph.reg.modify(|_, w| { - /// w.field1().bits(newfield1bits); - /// w.field2().set_bit(); - /// w.field3().variant(VARIANT) - /// }); - /// ``` - /// Other fields will have the value they had before the call to `modify`. - #[inline(always)] - pub fn modify(&self, f: F) + #[must_use = "after creating `BitWriter` you need to call bit setting method"] + pub struct BitWriter<'a, REG, FI = bool, M = BitM> where - for<'w> F: FnOnce(®::Reader, &'w mut REG::Writer) -> &'w mut W, + REG: Writable + RegisterSpec, + bool: From, { - let bits = self.register.get(); - self.register.set( - f( - ®::Reader::from(R { - bits, - _reg: marker::PhantomData, - }), - &mut REG::Writer::from(W { - bits: bits & !REG::ONE_TO_MODIFY_FIELDS_BITMAP - | REG::ZERO_TO_MODIFY_FIELDS_BITMAP, - _reg: marker::PhantomData, - }), - ) - .bits, - ); + pub(crate) w: &'a mut W, + pub(crate) o: u8, + _field: marker::PhantomData<(FI, M)>, + } + + impl<'a, REG, FI, M> BitWriter<'a, REG, FI, M> + where + REG: Writable + RegisterSpec, + bool: From, + { + /// Creates a new instance of the writer + #[allow(unused)] + #[inline(always)] + pub(crate) fn new(w: &'a mut W, o: u8) -> Self { + Self { + w, + o, + _field: marker::PhantomData, + } + } } } @@ -253,15 +253,12 @@ impl Reg { /// /// Result of the `read` methods of registers. Also used as a closure argument in the `modify` /// method. -pub struct R { - pub(crate) bits: REG::Ux, - _reg: marker::PhantomData, -} +pub type R = raw::R; impl R { /// Reads raw bits from register. #[inline(always)] - pub fn bits(&self) -> REG::Ux { + pub const fn bits(&self) -> REG::Ux { self.bits } } @@ -281,92 +278,61 @@ where /// Register writer. /// /// Used as an argument to the closures in the `write` and `modify` methods of the register. -pub struct W { - ///Writable bits - pub(crate) bits: REG::Ux, - _reg: marker::PhantomData, -} +pub type W = raw::W; -impl W { +impl W { /// Writes raw bits to the register. /// /// # Safety /// - /// Read datasheet or reference manual to find what values are allowed to pass. + /// Passing incorrect value can cause undefined behaviour. See reference manual #[inline(always)] pub unsafe fn bits(&mut self, bits: REG::Ux) -> &mut Self { self.bits = bits; self } } - -#[doc(hidden)] -pub struct FieldReaderRaw { - pub(crate) bits: U, - _reg: marker::PhantomData, -} - -impl FieldReaderRaw +impl W where - U: Copy, + REG: Writable, { - /// Creates a new instance of the reader. - #[allow(unused)] - #[inline(always)] - pub(crate) fn new(bits: U) -> Self { - Self { - bits, - _reg: marker::PhantomData, - } - } -} - -#[doc(hidden)] -pub struct BitReaderRaw { - pub(crate) bits: bool, - _reg: marker::PhantomData, -} - -impl BitReaderRaw { - /// Creates a new instance of the reader. - #[allow(unused)] + /// Writes raw bits to the register. #[inline(always)] - pub(crate) fn new(bits: bool) -> Self { - Self { - bits, - _reg: marker::PhantomData, - } + pub fn set(&mut self, bits: REG::Ux) -> &mut Self { + self.bits = bits; + self } } /// Field reader. /// /// Result of the `read` methods of fields. -pub type FieldReader = FieldReaderRaw; +pub type FieldReader = raw::FieldReader; /// Bit-wise field reader -pub type BitReader = BitReaderRaw; +pub type BitReader = raw::BitReader; -impl FieldReader -where - U: Copy, -{ +impl FieldReader { /// Reads raw bits from field. #[inline(always)] - pub fn bits(&self) -> U { + pub const fn bits(&self) -> FI::Ux { self.bits } } -impl PartialEq for FieldReader +impl core::fmt::Debug for FieldReader { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + core::fmt::Debug::fmt(&self.bits, f) + } +} + +impl PartialEq for FieldReader where - U: PartialEq, - FI: Copy, - U: From, + FI: FieldSpec + Copy, { #[inline(always)] fn eq(&self, other: &FI) -> bool { - self.bits.eq(&U::from(*other)) + self.bits.eq(&FI::Ux::from(*other)) } } @@ -384,102 +350,161 @@ where impl BitReader { /// Value of the field as raw bits. #[inline(always)] - pub fn bit(&self) -> bool { + pub const fn bit(&self) -> bool { self.bits } /// Returns `true` if the bit is clear (0). #[inline(always)] - pub fn bit_is_clear(&self) -> bool { + pub const fn bit_is_clear(&self) -> bool { !self.bit() } /// Returns `true` if the bit is set (1). #[inline(always)] - pub fn bit_is_set(&self) -> bool { + pub const fn bit_is_set(&self) -> bool { self.bit() } } -#[doc(hidden)] +impl core::fmt::Debug for BitReader { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + core::fmt::Debug::fmt(&self.bits, f) + } +} + +/// Marker for register/field writers which can take any value of specified width pub struct Safe; -#[doc(hidden)] +/// You should check that value is allowed to pass to register/field writer marked with this pub struct Unsafe; - -#[doc(hidden)] -pub struct FieldWriterRaw<'a, U, REG, N, FI, Safety, const WI: u8, const O: u8> +/// Marker for field writers are safe to write in specified inclusive range +pub struct Range; +/// Marker for field writers are safe to write in specified inclusive range +pub struct RangeFrom; +/// Marker for field writers are safe to write in specified inclusive range +pub struct RangeTo; + +/// Write field Proxy +pub type FieldWriter<'a, REG, const WI: u8, FI = u8, Safety = Unsafe> = + raw::FieldWriter<'a, REG, WI, FI, Safety>; + +impl FieldWriter<'_, REG, WI, FI, Safety> where - REG: Writable + RegisterSpec, - N: From, + REG: Writable + RegisterSpec, + FI: FieldSpec, { - pub(crate) w: &'a mut REG::Writer, - _field: marker::PhantomData<(N, FI, Safety)>, + /// Field width + pub const WIDTH: u8 = WI; + + /// Field width + #[inline(always)] + pub const fn width(&self) -> u8 { + WI + } + + /// Field offset + #[inline(always)] + pub const fn offset(&self) -> u8 { + self.o + } } -impl<'a, U, REG, N, FI, Safety, const WI: u8, const O: u8> - FieldWriterRaw<'a, U, REG, N, FI, Safety, WI, O> +impl<'a, REG, const WI: u8, FI, Safety> FieldWriter<'a, REG, WI, FI, Safety> where - REG: Writable + RegisterSpec, - N: From, + REG: Writable + RegisterSpec, + FI: FieldSpec, + REG::Ux: From, { - /// Creates a new instance of the writer - #[allow(unused)] + /// Writes raw bits to the field + /// + /// # Safety + /// + /// Passing incorrect value can cause undefined behaviour. See reference manual #[inline(always)] - pub(crate) fn new(w: &'a mut REG::Writer) -> Self { - Self { - w, - _field: marker::PhantomData, - } + pub unsafe fn bits(self, value: FI::Ux) -> &'a mut W { + self.w.bits &= !(REG::Ux::mask::() << self.o); + self.w.bits |= (REG::Ux::from(value) & REG::Ux::mask::()) << self.o; + self.w } } -#[doc(hidden)] -pub struct BitWriterRaw<'a, U, REG, FI, M, const O: u8> +impl<'a, REG, const WI: u8, FI> FieldWriter<'a, REG, WI, FI, Safe> where - REG: Writable + RegisterSpec, - bool: From, + REG: Writable + RegisterSpec, + FI: FieldSpec, + REG::Ux: From, { - pub(crate) w: &'a mut REG::Writer, - _field: marker::PhantomData<(FI, M)>, + /// Writes raw bits to the field + #[inline(always)] + pub fn set(self, value: FI::Ux) -> &'a mut W { + unsafe { self.bits(value) } + } } -impl<'a, U, REG, FI, M, const O: u8> BitWriterRaw<'a, U, REG, FI, M, O> +impl<'a, REG, const WI: u8, FI, const MIN: u64, const MAX: u64> + FieldWriter<'a, REG, WI, FI, Range> where - REG: Writable + RegisterSpec, - bool: From, + REG: Writable + RegisterSpec, + FI: FieldSpec, + REG::Ux: From, + u64: From, { - /// Creates a new instance of the writer - #[allow(unused)] + /// Writes raw bits to the field #[inline(always)] - pub(crate) fn new(w: &'a mut REG::Writer) -> Self { - Self { - w, - _field: marker::PhantomData, + pub fn set(self, value: FI::Ux) -> &'a mut W { + { + let value = u64::from(value); + assert!(value >= MIN && value <= MAX); } + unsafe { self.bits(value) } } } -/// Write field Proxy with unsafe `bits` -pub type FieldWriter<'a, U, REG, N, FI, const WI: u8, const O: u8> = - FieldWriterRaw<'a, U, REG, N, FI, Unsafe, WI, O>; -/// Write field Proxy with safe `bits` -pub type FieldWriterSafe<'a, U, REG, N, FI, const WI: u8, const O: u8> = - FieldWriterRaw<'a, U, REG, N, FI, Safe, WI, O>; +impl<'a, REG, const WI: u8, FI, const MIN: u64> FieldWriter<'a, REG, WI, FI, RangeFrom> +where + REG: Writable + RegisterSpec, + FI: FieldSpec, + REG::Ux: From, + u64: From, +{ + /// Writes raw bits to the field + #[inline(always)] + pub fn set(self, value: FI::Ux) -> &'a mut W { + { + let value = u64::from(value); + assert!(value >= MIN); + } + unsafe { self.bits(value) } + } +} -impl<'a, U, REG, N, FI, const WI: u8, const OF: u8> FieldWriter<'a, U, REG, N, FI, WI, OF> +impl<'a, REG, const WI: u8, FI, const MAX: u64> FieldWriter<'a, REG, WI, FI, RangeTo> where - REG: Writable + RegisterSpec, - N: From, + REG: Writable + RegisterSpec, + FI: FieldSpec, + REG::Ux: From, + u64: From, { - /// Field width - pub const WIDTH: u8 = WI; + /// Writes raw bits to the field + #[inline(always)] + pub fn set(self, value: FI::Ux) -> &'a mut W { + { + let value = u64::from(value); + assert!(value <= MAX); + } + unsafe { self.bits(value) } + } } -impl<'a, U, REG, N, FI, const WI: u8, const OF: u8> FieldWriterSafe<'a, U, REG, N, FI, WI, OF> +impl<'a, REG, const WI: u8, FI, Safety> FieldWriter<'a, REG, WI, FI, Safety> where - REG: Writable + RegisterSpec, - N: From, + REG: Writable + RegisterSpec, + FI: IsEnum, + REG::Ux: From, { - /// Field width - pub const WIDTH: u8 = WI; + /// Writes `variant` to the field + #[inline(always)] + pub fn variant(self, variant: FI) -> &'a mut W { + unsafe { self.bits(FI::Ux::from(variant)) } + } } macro_rules! bit_proxy { @@ -488,37 +513,38 @@ macro_rules! bit_proxy { pub struct $mwv; /// Bit-wise write field proxy - pub type $writer<'a, U, REG, FI, const O: u8> = BitWriterRaw<'a, U, REG, FI, $mwv, O>; + pub type $writer<'a, REG, FI = bool> = raw::BitWriter<'a, REG, FI, $mwv>; - impl<'a, U, REG, FI, const OF: u8> $writer<'a, U, REG, FI, OF> + impl<'a, REG, FI> $writer<'a, REG, FI> where - REG: Writable + RegisterSpec, + REG: Writable + RegisterSpec, bool: From, { /// Field width pub const WIDTH: u8 = 1; - } - }; -} -macro_rules! impl_bit_proxy { - ($writer:ident) => { - impl<'a, U, REG, FI, const OF: u8> $writer<'a, U, REG, FI, OF> - where - REG: Writable + RegisterSpec, - U: RawReg, - bool: From, - { + /// Field width + #[inline(always)] + pub const fn width(&self) -> u8 { + Self::WIDTH + } + + /// Field offset + #[inline(always)] + pub const fn offset(&self) -> u8 { + self.o + } + /// Writes bit to the field #[inline(always)] - pub fn bit(self, value: bool) -> &'a mut REG::Writer { - self.w.bits &= !(U::one() << OF); - self.w.bits |= (U::from(value) & U::one()) << OF; + pub fn bit(self, value: bool) -> &'a mut W { + self.w.bits &= !(REG::Ux::ONE << self.o); + self.w.bits |= (REG::Ux::from(value) & REG::Ux::ONE) << self.o; self.w } /// Writes `variant` to the field #[inline(always)] - pub fn variant(self, variant: FI) -> &'a mut REG::Writer { + pub fn variant(self, variant: FI) -> &'a mut W { self.bit(bool::from(variant)) } } @@ -533,157 +559,99 @@ bit_proxy!(BitWriter0S, Bit0S); bit_proxy!(BitWriter1T, Bit1T); bit_proxy!(BitWriter0T, Bit0T); -impl<'a, U, REG, N, FI, const WI: u8, const OF: u8> FieldWriter<'a, U, REG, N, FI, WI, OF> -where - REG: Writable + RegisterSpec, - U: RawReg + From, - N: From, -{ - /// Writes raw bits to the field - /// - /// # Safety - /// - /// Passing incorrect value can cause undefined behaviour. See reference manual - #[inline(always)] - pub unsafe fn bits(self, value: N) -> &'a mut REG::Writer { - self.w.bits &= !(U::mask::() << OF); - self.w.bits |= (U::from(value) & U::mask::()) << OF; - self.w - } - /// Writes `variant` to the field - #[inline(always)] - pub fn variant(self, variant: FI) -> &'a mut REG::Writer { - unsafe { self.bits(N::from(variant)) } - } -} -impl<'a, U, REG, N, FI, const WI: u8, const OF: u8> FieldWriterSafe<'a, U, REG, N, FI, WI, OF> -where - REG: Writable + RegisterSpec, - U: RawReg + From, - N: From, -{ - /// Writes raw bits to the field - #[inline(always)] - pub fn bits(self, value: N) -> &'a mut REG::Writer { - self.w.bits &= !(U::mask::() << OF); - self.w.bits |= (U::from(value) & U::mask::()) << OF; - self.w - } - /// Writes `variant` to the field - #[inline(always)] - pub fn variant(self, variant: FI) -> &'a mut REG::Writer { - self.bits(N::from(variant)) - } -} - -impl_bit_proxy!(BitWriter); -impl_bit_proxy!(BitWriter1S); -impl_bit_proxy!(BitWriter0C); -impl_bit_proxy!(BitWriter1C); -impl_bit_proxy!(BitWriter0S); -impl_bit_proxy!(BitWriter1T); -impl_bit_proxy!(BitWriter0T); - -impl<'a, U, REG, FI, const OF: u8> BitWriter<'a, U, REG, FI, OF> +impl<'a, REG, FI> BitWriter<'a, REG, FI> where - REG: Writable + RegisterSpec, - U: RawReg, + REG: Writable + RegisterSpec, bool: From, { /// Sets the field bit #[inline(always)] - pub fn set_bit(self) -> &'a mut REG::Writer { - self.w.bits |= U::one() << OF; + pub fn set_bit(self) -> &'a mut W { + self.w.bits |= REG::Ux::ONE << self.o; self.w } /// Clears the field bit #[inline(always)] - pub fn clear_bit(self) -> &'a mut REG::Writer { - self.w.bits &= !(U::one() << OF); + pub fn clear_bit(self) -> &'a mut W { + self.w.bits &= !(REG::Ux::ONE << self.o); self.w } } -impl<'a, U, REG, FI, const OF: u8> BitWriter1S<'a, U, REG, FI, OF> +impl<'a, REG, FI> BitWriter1S<'a, REG, FI> where - REG: Writable + RegisterSpec, - U: RawReg, + REG: Writable + RegisterSpec, bool: From, { /// Sets the field bit #[inline(always)] - pub fn set_bit(self) -> &'a mut REG::Writer { - self.w.bits |= U::one() << OF; + pub fn set_bit(self) -> &'a mut W { + self.w.bits |= REG::Ux::ONE << self.o; self.w } } -impl<'a, U, REG, FI, const OF: u8> BitWriter0C<'a, U, REG, FI, OF> +impl<'a, REG, FI> BitWriter0C<'a, REG, FI> where - REG: Writable + RegisterSpec, - U: RawReg, + REG: Writable + RegisterSpec, bool: From, { /// Clears the field bit #[inline(always)] - pub fn clear_bit(self) -> &'a mut REG::Writer { - self.w.bits &= !(U::one() << OF); + pub fn clear_bit(self) -> &'a mut W { + self.w.bits &= !(REG::Ux::ONE << self.o); self.w } } -impl<'a, U, REG, FI, const OF: u8> BitWriter1C<'a, U, REG, FI, OF> +impl<'a, REG, FI> BitWriter1C<'a, REG, FI> where - REG: Writable + RegisterSpec, - U: RawReg, + REG: Writable + RegisterSpec, bool: From, { ///Clears the field bit by passing one #[inline(always)] - pub fn clear_bit_by_one(self) -> &'a mut REG::Writer { - self.w.bits |= U::one() << OF; + pub fn clear_bit_by_one(self) -> &'a mut W { + self.w.bits |= REG::Ux::ONE << self.o; self.w } } -impl<'a, U, REG, FI, const OF: u8> BitWriter0S<'a, U, REG, FI, OF> +impl<'a, REG, FI> BitWriter0S<'a, REG, FI> where - REG: Writable + RegisterSpec, - U: RawReg, + REG: Writable + RegisterSpec, bool: From, { ///Sets the field bit by passing zero #[inline(always)] - pub fn set_bit_by_zero(self) -> &'a mut REG::Writer { - self.w.bits &= !(U::one() << OF); + pub fn set_bit_by_zero(self) -> &'a mut W { + self.w.bits &= !(REG::Ux::ONE << self.o); self.w } } -impl<'a, U, REG, FI, const OF: u8> BitWriter1T<'a, U, REG, FI, OF> +impl<'a, REG, FI> BitWriter1T<'a, REG, FI> where - REG: Writable + RegisterSpec, - U: RawReg, + REG: Writable + RegisterSpec, bool: From, { ///Toggle the field bit by passing one #[inline(always)] - pub fn toggle_bit(self) -> &'a mut REG::Writer { - self.w.bits |= U::one() << OF; + pub fn toggle_bit(self) -> &'a mut W { + self.w.bits |= REG::Ux::ONE << self.o; self.w } } -impl<'a, U, REG, FI, const OF: u8> BitWriter0T<'a, U, REG, FI, OF> +impl<'a, REG, FI> BitWriter0T<'a, REG, FI> where - REG: Writable + RegisterSpec, - U: RawReg, + REG: Writable + RegisterSpec, bool: From, { ///Toggle the field bit by passing zero #[inline(always)] - pub fn toggle_bit(self) -> &'a mut REG::Writer { - self.w.bits &= !(U::one() << OF); + pub fn toggle_bit(self) -> &'a mut W { + self.w.bits &= !(REG::Ux::ONE << self.o); self.w } } diff --git a/src/generate/generic_atomic.rs b/src/generate/generic_atomic.rs new file mode 100644 index 00000000..55250961 --- /dev/null +++ b/src/generate/generic_atomic.rs @@ -0,0 +1,101 @@ +mod atomic { + use super::*; + use portable_atomic::Ordering; + + pub trait AtomicOperations { + unsafe fn atomic_or(ptr: *mut Self, val: Self); + unsafe fn atomic_and(ptr: *mut Self, val: Self); + unsafe fn atomic_xor(ptr: *mut Self, val: Self); + } + + macro_rules! impl_atomics { + ($U:ty, $Atomic:ty) => { + impl AtomicOperations for $U { + unsafe fn atomic_or(ptr: *mut Self, val: Self) { + (*(ptr as *const $Atomic)).or(val, Ordering::SeqCst); + } + + unsafe fn atomic_and(ptr: *mut Self, val: Self) { + (*(ptr as *const $Atomic)).and(val, Ordering::SeqCst); + } + + unsafe fn atomic_xor(ptr: *mut Self, val: Self) { + (*(ptr as *const $Atomic)).xor(val, Ordering::SeqCst); + } + } + }; + } + + impl_atomics!(u8, portable_atomic::AtomicU8); + impl_atomics!(u16, portable_atomic::AtomicU16); + + // Exclude 16-bit archs from 32-bit atomics + #[cfg(not(target_pointer_width = "16"))] + impl_atomics!(u32, portable_atomic::AtomicU32); + + // Enable 64-bit atomics for 64-bit RISCV + #[cfg(any(target_pointer_width = "64", target_has_atomic = "64"))] + impl_atomics!(u64, portable_atomic::AtomicU64); + + impl Reg + where + REG::Ux: AtomicOperations, + { + /// Set high every bit in the register that was set in the write proxy. Leave other bits + /// untouched. The write is done in a single atomic instruction. + /// + /// # Safety + /// + /// The resultant bit pattern may not be valid for the register. + #[inline(always)] + pub unsafe fn set_bits(&self, f: F) + where + F: FnOnce(&mut W) -> &mut W, + { + let bits = f(&mut W { + bits: REG::Ux::ZERO, + _reg: marker::PhantomData, + }) + .bits; + REG::Ux::atomic_or(self.register.as_ptr(), bits); + } + + /// Clear every bit in the register that was cleared in the write proxy. Leave other bits + /// untouched. The write is done in a single atomic instruction. + /// + /// # Safety + /// + /// The resultant bit pattern may not be valid for the register. + #[inline(always)] + pub unsafe fn clear_bits(&self, f: F) + where + F: FnOnce(&mut W) -> &mut W, + { + let bits = f(&mut W { + bits: !REG::Ux::ZERO, + _reg: marker::PhantomData, + }) + .bits; + REG::Ux::atomic_and(self.register.as_ptr(), bits); + } + + /// Toggle every bit in the register that was set in the write proxy. Leave other bits + /// untouched. The write is done in a single atomic instruction. + /// + /// # Safety + /// + /// The resultant bit pattern may not be valid for the register. + #[inline(always)] + pub unsafe fn toggle_bits(&self, f: F) + where + F: FnOnce(&mut W) -> &mut W, + { + let bits = f(&mut W { + bits: REG::Ux::ZERO, + _reg: marker::PhantomData, + }) + .bits; + REG::Ux::atomic_xor(self.register.as_ptr(), bits); + } + } +} diff --git a/src/generate/generic_msp430_atomic.rs b/src/generate/generic_msp430_atomic.rs deleted file mode 100644 index cf2b6874..00000000 --- a/src/generate/generic_msp430_atomic.rs +++ /dev/null @@ -1,52 +0,0 @@ -use msp430_atomic::AtomicOperations; - -impl Reg -where - Self: Readable + Writable, - REG::Ux: AtomicOperations + Default + core::ops::Not, -{ - /// Set high every bit in the register that was set in the write proxy. Leave other bits - /// untouched. The write is done in a single atomic instruction. - #[inline(always)] - pub unsafe fn set_bits(&self, f: F) - where - F: FnOnce(&mut W) -> &mut W, - { - let bits = f(&mut W { - bits: Default::default(), - _reg: marker::PhantomData, - }) - .bits; - REG::Ux::atomic_or(self.register.as_ptr(), bits); - } - - /// Clear every bit in the register that was cleared in the write proxy. Leave other bits - /// untouched. The write is done in a single atomic instruction. - #[inline(always)] - pub unsafe fn clear_bits(&self, f: F) - where - F: FnOnce(&mut W) -> &mut W, - { - let bits = f(&mut W { - bits: !REG::Ux::default(), - _reg: marker::PhantomData, - }) - .bits; - REG::Ux::atomic_and(self.register.as_ptr(), bits); - } - - /// Toggle every bit in the register that was set in the write proxy. Leave other bits - /// untouched. The write is done in a single atomic instruction. - #[inline(always)] - pub unsafe fn toggle_bits(&self, f: F) - where - F: FnOnce(&mut W) -> &mut W, - { - let bits = f(&mut W { - bits: Default::default(), - _reg: marker::PhantomData, - }) - .bits; - REG::Ux::atomic_xor(self.register.as_ptr(), bits); - } -} diff --git a/src/generate/generic_reg_vcell.rs b/src/generate/generic_reg_vcell.rs new file mode 100644 index 00000000..9e436334 --- /dev/null +++ b/src/generate/generic_reg_vcell.rs @@ -0,0 +1,295 @@ +/// This structure provides volatile access to registers. +#[repr(transparent)] +pub struct Reg { + register: vcell::VolatileCell, + _marker: marker::PhantomData, +} + +unsafe impl Send for Reg where REG::Ux: Send {} + +impl Reg { + /// Returns the underlying memory address of register. + /// + /// ```ignore + /// let reg_ptr = periph.reg.as_ptr(); + /// ``` + #[inline(always)] + pub fn as_ptr(&self) -> *mut REG::Ux { + self.register.as_ptr() + } +} + +impl Reg { + /// Reads the contents of a `Readable` register. + /// + /// You can read the raw contents of a register by using `bits`: + /// ```ignore + /// let bits = periph.reg.read().bits(); + /// ``` + /// or get the content of a particular field of a register: + /// ```ignore + /// let reader = periph.reg.read(); + /// let bits = reader.field1().bits(); + /// let flag = reader.field2().bit_is_set(); + /// ``` + #[inline(always)] + pub fn read(&self) -> R { + R { + bits: self.register.get(), + _reg: marker::PhantomData, + } + } +} + +impl Reg { + /// Writes the reset value to `Writable` register. + /// + /// Resets the register to its initial state. + #[inline(always)] + pub fn reset(&self) { + self.register.set(REG::RESET_VALUE) + } + + /// Writes bits to a `Writable` register. + /// + /// You can write raw bits into a register: + /// ```ignore + /// periph.reg.write(|w| unsafe { w.bits(rawbits) }); + /// ``` + /// or write only the fields you need: + /// ```ignore + /// periph.reg.write(|w| w + /// .field1().bits(newfield1bits) + /// .field2().set_bit() + /// .field3().variant(VARIANT) + /// ); + /// ``` + /// or an alternative way of saying the same: + /// ```ignore + /// periph.reg.write(|w| { + /// w.field1().bits(newfield1bits); + /// w.field2().set_bit(); + /// w.field3().variant(VARIANT) + /// }); + /// ``` + /// In the latter case, other fields will be set to their reset value. + #[inline(always)] + pub fn write(&self, f: F) -> REG::Ux + where + F: FnOnce(&mut W) -> &mut W, + { + let value = f(&mut W { + bits: REG::RESET_VALUE & !REG::ONE_TO_MODIFY_FIELDS_BITMAP + | REG::ZERO_TO_MODIFY_FIELDS_BITMAP, + _reg: marker::PhantomData, + }) + .bits; + self.register.set(value); + value + } + + /// Writes bits to a `Writable` register and produce a value. + /// + /// You can write raw bits into a register: + /// ```ignore + /// periph.reg.write_and(|w| unsafe { w.bits(rawbits); }); + /// ``` + /// or write only the fields you need: + /// ```ignore + /// periph.reg.write_and(|w| { + /// w.field1().bits(newfield1bits) + /// .field2().set_bit() + /// .field3().variant(VARIANT); + /// }); + /// ``` + /// or an alternative way of saying the same: + /// ```ignore + /// periph.reg.write_and(|w| { + /// w.field1().bits(newfield1bits); + /// w.field2().set_bit(); + /// w.field3().variant(VARIANT); + /// }); + /// ``` + /// In the latter case, other fields will be set to their reset value. + /// + /// Values can be returned from the closure: + /// ```ignore + /// let state = periph.reg.write_and(|w| State::set(w.field1())); + /// ``` + #[inline(always)] + pub fn from_write(&self, f: F) -> T + where + F: FnOnce(&mut W) -> T, + { + let mut writer = W { + bits: REG::RESET_VALUE & !REG::ONE_TO_MODIFY_FIELDS_BITMAP + | REG::ZERO_TO_MODIFY_FIELDS_BITMAP, + _reg: marker::PhantomData, + }; + let result = f(&mut writer); + + self.register.set(writer.bits); + + result + } +} + +impl Reg { + /// Writes 0 to a `Writable` register. + /// + /// Similar to `write`, but unused bits will contain 0. + /// + /// # Safety + /// + /// Unsafe to use with registers which don't allow to write 0. + #[inline(always)] + pub unsafe fn write_with_zero(&self, f: F) -> REG::Ux + where + F: FnOnce(&mut W) -> &mut W, + { + let value = f(&mut W { + bits: REG::Ux::ZERO, + _reg: marker::PhantomData, + }) + .bits; + self.register.set(value); + value + } + + /// Writes 0 to a `Writable` register and produces a value. + /// + /// Similar to `write`, but unused bits will contain 0. + /// + /// # Safety + /// + /// Unsafe to use with registers which don't allow to write 0. + #[inline(always)] + pub unsafe fn from_write_with_zero(&self, f: F) -> T + where + F: FnOnce(&mut W) -> T, + { + let mut writer = W { + bits: REG::Ux::ZERO, + _reg: marker::PhantomData, + }; + + let result = f(&mut writer); + + self.register.set(writer.bits); + + result + } +} + +impl Reg { + /// Modifies the contents of the register by reading and then writing it. + /// + /// E.g. to do a read-modify-write sequence to change parts of a register: + /// ```ignore + /// periph.reg.modify(|r, w| unsafe { w.bits( + /// r.bits() | 3 + /// ) }); + /// ``` + /// or + /// ```ignore + /// periph.reg.modify(|_, w| w + /// .field1().bits(newfield1bits) + /// .field2().set_bit() + /// .field3().variant(VARIANT) + /// ); + /// ``` + /// or an alternative way of saying the same: + /// ```ignore + /// periph.reg.modify(|_, w| { + /// w.field1().bits(newfield1bits); + /// w.field2().set_bit(); + /// w.field3().variant(VARIANT) + /// }); + /// ``` + /// Other fields will have the value they had before the call to `modify`. + #[inline(always)] + pub fn modify(&self, f: F) -> REG::Ux + where + for<'w> F: FnOnce(&R, &'w mut W) -> &'w mut W, + { + let bits = self.register.get(); + let value = f( + &R { + bits, + _reg: marker::PhantomData, + }, + &mut W { + bits: bits & !REG::ONE_TO_MODIFY_FIELDS_BITMAP | REG::ZERO_TO_MODIFY_FIELDS_BITMAP, + _reg: marker::PhantomData, + }, + ) + .bits; + self.register.set(value); + value + } + + /// Modifies the contents of the register by reading and then writing it + /// and produces a value. + /// + /// E.g. to do a read-modify-write sequence to change parts of a register: + /// ```ignore + /// let bits = periph.reg.modify(|r, w| { + /// let new_bits = r.bits() | 3; + /// unsafe { + /// w.bits(new_bits); + /// } + /// + /// new_bits + /// }); + /// ``` + /// or + /// ```ignore + /// periph.reg.modify(|_, w| { + /// w.field1().bits(newfield1bits) + /// .field2().set_bit() + /// .field3().variant(VARIANT); + /// }); + /// ``` + /// or an alternative way of saying the same: + /// ```ignore + /// periph.reg.modify(|_, w| { + /// w.field1().bits(newfield1bits); + /// w.field2().set_bit(); + /// w.field3().variant(VARIANT); + /// }); + /// ``` + /// Other fields will have the value they had before the call to `modify`. + #[inline(always)] + pub fn from_modify(&self, f: F) -> T + where + for<'w> F: FnOnce(&R, &'w mut W) -> T, + { + let bits = self.register.get(); + + let mut writer = W { + bits: bits & !REG::ONE_TO_MODIFY_FIELDS_BITMAP | REG::ZERO_TO_MODIFY_FIELDS_BITMAP, + _reg: marker::PhantomData, + }; + + let result = f( + &R { + bits, + _reg: marker::PhantomData, + }, + &mut writer, + ); + + self.register.set(writer.bits); + + result + } +} + +impl core::fmt::Debug for crate::generic::Reg +where + R: core::fmt::Debug, +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + core::fmt::Debug::fmt(&self.read(), f) + } +} diff --git a/src/generate/interrupt.rs b/src/generate/interrupt.rs index c0d536f3..498bdece 100644 --- a/src/generate/interrupt.rs +++ b/src/generate/interrupt.rs @@ -5,7 +5,7 @@ use crate::svd::Peripheral; use proc_macro2::{Span, TokenStream}; use quote::quote; -use crate::util::{self, ToSanitizedCase}; +use crate::util::{self, ident}; use crate::{Config, Target}; use anyhow::Result; @@ -33,7 +33,7 @@ pub fn render( .map(|i| (i.0.value, (i.0, i.1, i.2))) .collect::>(); - let mut interrupts = interrupts.into_iter().map(|(_, v)| v).collect::>(); + let mut interrupts = interrupts.into_values().collect::>(); interrupts.sort_by_key(|i| i.0.value); let mut root = TokenStream::new(); @@ -46,6 +46,8 @@ pub fn render( // Current position in the vector table let mut pos = 0; let mut mod_items = TokenStream::new(); + let span = Span::call_site(); + let feature_format = config.ident_formats.get("peripheral_feature").unwrap(); for interrupt in &interrupts { while pos < interrupt.0.value { elements.extend(quote!(Vector { _reserved: 0 },)); @@ -53,33 +55,33 @@ pub fn render( } pos += 1; - let name_constant_case = interrupt.0.name.to_constant_case_ident(Span::call_site()); + let i_ty = ident(&interrupt.0.name, config, "interrupt", span); let description = format!( "{} - {}", interrupt.0.value, interrupt .0 .description - .as_ref() - .map(|s| util::respace(s)) - .as_ref() - .map(|s| util::escape_brackets(s)) - .unwrap_or_else(|| interrupt.0.name.clone()) + .as_deref() + .map(util::respace) + .as_deref() + .map(util::escape_special_chars) + .unwrap_or_else(|| interrupt.0.name.as_str().into()) ); - let value = util::unsuffixed(interrupt.0.value.into()); + let value = util::unsuffixed(interrupt.0.value); let mut feature_attribute_flag = false; let mut feature_attribute = TokenStream::new(); let mut not_feature_attribute = TokenStream::new(); if config.feature_group && interrupt.1.is_some() { - let feature_name = interrupt.1.as_ref().unwrap().to_sanitized_snake_case(); + let feature_name = feature_format.apply(interrupt.1.as_ref().unwrap()); feature_attribute_flag = true; feature_attribute.extend(quote! { #[cfg(feature = #feature_name)] }); not_feature_attribute.extend(quote! { feature = #feature_name, }); } if config.feature_peripheral { - let feature_name = interrupt.2.to_sanitized_snake_case(); + let feature_name = feature_format.apply(&interrupt.2); feature_attribute_flag = true; feature_attribute.extend(quote! { #[cfg(feature = #feature_name)] }); not_feature_attribute.extend(quote! { feature = #feature_name, }); @@ -89,12 +91,12 @@ pub fn render( variants.extend(quote! { #[doc = #description] #feature_attribute - #name_constant_case = #value, + #i_ty = #value, }); from_arms.extend(quote! { #feature_attribute - #value => Ok(Interrupt::#name_constant_case), + #value => Ok(Interrupt::#i_ty), }); if feature_attribute_flag { @@ -102,22 +104,30 @@ pub fn render( #not_feature_attribute Vector { _reserved: 0 }, #feature_attribute - Vector { _handler: #name_constant_case }, + Vector { _handler: #i_ty }, }); } else { - elements.extend(quote!(Vector { _handler: #name_constant_case },)); + elements.extend(quote!(Vector { _handler: #i_ty },)); } - names.push(name_constant_case); + names.push(i_ty); names_cfg_attr.push(feature_attribute); } - let n = util::unsuffixed(pos.into()); + let n = util::unsuffixed(pos); match target { Target::CortexM => { for name in &names { - writeln!(device_x, "PROVIDE({} = DefaultHandler);", name)?; + writeln!(device_x, "PROVIDE({name} = DefaultHandler);")?; } + let link_section_name = config + .interrupt_link_section + .as_deref() + .unwrap_or(".vector_table.interrupts"); + let link_section_attr = quote! { + #[link_section = #link_section_name] + }; + root.extend(quote! { #[cfg(feature = "rt")] extern "C" { @@ -125,6 +135,7 @@ pub fn render( } #[doc(hidden)] + #[repr(C)] pub union Vector { _handler: unsafe extern "C" fn(), _reserved: u32, @@ -132,7 +143,7 @@ pub fn render( #[cfg(feature = "rt")] #[doc(hidden)] - #[link_section = ".vector_table.interrupts"] + #link_section_attr #[no_mangle] pub static __INTERRUPTS: [Vector; #n] = [ #elements @@ -141,9 +152,17 @@ pub fn render( } Target::Msp430 => { for name in &names { - writeln!(device_x, "PROVIDE({} = DefaultHandler);", name).unwrap(); + writeln!(device_x, "PROVIDE({name} = DefaultHandler);").unwrap(); } + let link_section_name = config + .interrupt_link_section + .as_deref() + .unwrap_or(".vector_table.interrupts"); + let link_section_attr = quote! { + #[link_section = #link_section_name] + }; + root.extend(quote! { #[cfg(feature = "rt")] extern "msp430-interrupt" { @@ -151,6 +170,7 @@ pub fn render( } #[doc(hidden)] + #[repr(C)] pub union Vector { _handler: unsafe extern "msp430-interrupt" fn(), _reserved: u16, @@ -158,7 +178,7 @@ pub fn render( #[cfg(feature = "rt")] #[doc(hidden)] - #[link_section = ".vector_table.interrupts"] + #link_section_attr #[no_mangle] #[used] pub static __INTERRUPTS: @@ -169,9 +189,15 @@ pub fn render( } Target::RISCV => { for name in &names { - writeln!(device_x, "PROVIDE({} = DefaultHandler);", name)?; + writeln!(device_x, "PROVIDE({name} = DefaultHandler);")?; } + let link_section_attr = config.interrupt_link_section.as_ref().map(|section| { + quote! { + #[link_section = #section] + } + }); + root.extend(quote! { #[cfg(feature = "rt")] extern "C" { @@ -179,6 +205,7 @@ pub fn render( } #[doc(hidden)] + #[repr(C)] pub union Vector { pub _handler: unsafe extern "C" fn(), pub _reserved: usize, @@ -186,6 +213,7 @@ pub fn render( #[cfg(feature = "rt")] #[doc(hidden)] + #link_section_attr #[no_mangle] pub static __EXTERNAL_INTERRUPTS: [Vector; #n] = [ #elements @@ -194,9 +222,15 @@ pub fn render( } Target::XtensaLX => { for name in &names { - writeln!(device_x, "PROVIDE({} = DefaultHandler);", name)?; + writeln!(device_x, "PROVIDE({name} = DefaultHandler);")?; } + let link_section_attr = config.interrupt_link_section.as_ref().map(|section| { + quote! { + #[link_section = #section] + } + }); + root.extend(quote! { #[cfg(feature = "rt")] extern "C" { @@ -204,12 +238,14 @@ pub fn render( } #[doc(hidden)] + #[repr(C)] pub union Vector { pub _handler: unsafe extern "C" fn(), _reserved: u32, } #[cfg(feature = "rt")] + #link_section_attr #[doc(hidden)] pub static __INTERRUPTS: [Vector; #n] = [ #elements @@ -227,9 +263,15 @@ pub fn render( (quote!(#[repr(u16)]), quote!(#self_token as u16)) }; + let defmt = config + .impl_defmt + .as_ref() + .map(|feature| quote!(#[cfg_attr(feature = #feature, derive(defmt::Format))])); + if target == Target::Msp430 { let interrupt_enum = quote! { ///Enumeration of all the interrupts. This enum is seldom used in application or library crates. It is present primarily for documenting the device's implemented interrupts. + #defmt #[derive(Copy, Clone, Debug, PartialEq, Eq)] #enum_repr pub enum Interrupt { @@ -241,6 +283,7 @@ pub fn render( } else { let interrupt_enum = quote! { ///Enumeration of all the interrupts. + #defmt #[derive(Copy, Clone, Debug, PartialEq, Eq)] #enum_repr pub enum Interrupt { @@ -265,14 +308,8 @@ pub fn render( root.extend(quote! { #interrupt_enum - unsafe impl xtensa_lx::interrupt::InterruptNumber for Interrupt { - #[inline(always)] - fn number(#self_token) -> u16 { - #nr_expr - } - } - /// TryFromInterruptError + #defmt #[derive(Debug, Copy, Clone)] pub struct TryFromInterruptError(()); @@ -294,6 +331,7 @@ pub fn render( #interrupt_enum /// TryFromInterruptError + #defmt #[derive(Debug, Copy, Clone)] pub struct TryFromInterruptError(()); diff --git a/src/generate/mod.rs b/src/generate/mod.rs index 54216bfb..16d5bb75 100644 --- a/src/generate/mod.rs +++ b/src/generate/mod.rs @@ -2,3 +2,4 @@ pub mod device; pub mod interrupt; pub mod peripheral; pub mod register; +pub mod riscv; diff --git a/src/generate/peripheral.rs b/src/generate/peripheral.rs index 318d36f1..618952eb 100644 --- a/src/generate/peripheral.rs +++ b/src/generate/peripheral.rs @@ -1,21 +1,32 @@ +use regex::Regex; use std::borrow::Cow; use std::cmp::Ordering; -use svd_parser::expand::{derive_cluster, derive_peripheral, derive_register, BlockPath, Index}; +use std::fmt; +use svd_parser::expand::{ + derive_cluster, derive_peripheral, derive_register, BlockPath, Index, RegisterPath, +}; +use syn::LitInt; -use crate::svd::{array::names, Cluster, ClusterInfo, Peripheral, Register, RegisterCluster}; +use crate::config::Config; +use crate::svd::{ + self, Cluster, ClusterInfo, MaybeArray, Peripheral, Register, RegisterCluster, RegisterInfo, +}; use log::{debug, trace, warn}; -use proc_macro2::{Ident, Punct, Spacing, Span, TokenStream}; +use proc_macro2::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream}; use quote::{quote, ToTokens}; use syn::{punctuated::Punctuated, Token}; use crate::util::{ - self, array_proxy_type, name_to_ty, new_syn_u32, path_segment, type_path, unsuffixed, Config, - FullName, ToSanitizedCase, BITS_PER_BYTE, + self, ident, name_to_ty, path_segment, type_path, unsuffixed, zst_type, DimSuffix, FullName, + BITS_PER_BYTE, }; use anyhow::{anyhow, bail, Context, Result}; use crate::generate::register; +mod accessor; +use accessor::*; + pub fn render(p_original: &Peripheral, index: &Index, config: &Config) -> Result { let mut out = TokenStream::new(); @@ -28,159 +39,121 @@ pub fn render(p_original: &Peripheral, index: &Index, config: &Config) -> Result let name = util::name_of(&p, config.ignore_groups); let span = Span::call_site(); - let name_str = name.to_sanitized_constant_case(); - let name_constant_case = Ident::new(&name_str, span); - let address = util::hex(p.base_address as u64); - let description = util::respace(p.description.as_ref().unwrap_or(&p.name)); + let p_ty = ident(&name, config, "peripheral", span); + let name_str = p_ty.to_string(); + let address = util::hex(p.base_address + config.base_address_shift); + let doc = util::respace(p.description.as_ref().unwrap_or(&name)); + let doc = util::escape_special_chars(&doc); - let name_snake_case = name.to_snake_case_ident(span); + let mod_ty = ident(&name, config, "peripheral_mod", span); let (derive_regs, base, path) = if let Some(path) = path { - (true, path.peripheral.to_snake_case_ident(span), path) + ( + true, + ident(&path.peripheral, config, "peripheral_mod", span), + path, + ) } else { - (false, name_snake_case.clone(), BlockPath::new(&p.name)) + (false, mod_ty.clone(), BlockPath::new(&p.name)) }; let mut feature_attribute = TokenStream::new(); + let feature_format = config.ident_formats.get("peripheral_feature").unwrap(); if config.feature_group && p.group_name.is_some() { - let feature_name = p.group_name.as_ref().unwrap().to_sanitized_snake_case(); + let feature_name = feature_format.apply(p.group_name.as_ref().unwrap()); feature_attribute.extend(quote! { #[cfg(feature = #feature_name)] }); }; + let phtml = config.settings.html_url.as_ref().map(|url| { + let doc = format!("See peripheral [structure]({url}#{})", &path.peripheral); + quote!(#[doc = ""] #[doc = #doc]) + }); + + let per_to_tokens = |out: &mut TokenStream, + feature_attribute: &TokenStream, + doc: &str, + p_ty: &Ident, + doc_alias: Option, + address: LitInt| { + out.extend(quote! { + #[doc = #doc] + #phtml + #doc_alias + #feature_attribute + pub type #p_ty = crate::Periph<#base::RegisterBlock, #address>; + + #feature_attribute + impl core::fmt::Debug for #p_ty { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + f.debug_struct(#name_str).finish() + } + } + }); + }; + match &p { Peripheral::Array(p, dim) => { - let names: Vec> = names(p, dim).map(|n| n.into()).collect(); - let names_str = names.iter().map(|n| n.to_sanitized_constant_case()); - let names_constant_case = names_str.clone().map(|n| Ident::new(&n, span)); - let addresses = - (0..=dim.dim).map(|i| util::hex(p.base_address + (i * dim.dim_increment) as u64)); - let snake_names = names - .iter() - .map(|p_name| p_name.to_sanitized_snake_case()) - .collect::>(); - let feature_attribute_n = snake_names.iter().map(|p_snake| { - let mut feature_attribute = feature_attribute.clone(); + let mut feature_names = Vec::with_capacity(dim.dim as _); + for pi in svd::peripheral::expand(p, dim) { + let name = &pi.name; + let doc = util::respace(pi.description.as_ref().unwrap_or(&pi.name)); + let doc = util::escape_special_chars(&doc); + let p_ty = ident(name, config, "peripheral", span); + let name_str = p_ty.to_string(); + let doc_alias = (&name_str != name).then(|| quote!(#[doc(alias = #name)])); + let address = util::hex(pi.base_address + config.base_address_shift); + let p_feature = feature_format.apply(name); + feature_names.push(p_feature.to_string()); + let mut feature_attribute_n = feature_attribute.clone(); if config.feature_peripheral { - feature_attribute.extend(quote! { #[cfg(feature = #p_snake)] }) + feature_attribute_n.extend(quote! { #[cfg(feature = #p_feature)] }) }; - feature_attribute - }); - // Insert the peripherals structure - out.extend(quote! { - #( - #[doc = #description] - #feature_attribute_n - pub struct #names_constant_case { _marker: PhantomData<*const ()> } - - #feature_attribute_n - unsafe impl Send for #names_constant_case {} - - #feature_attribute_n - impl #names_constant_case { - ///Pointer to the register block - pub const PTR: *const #base::RegisterBlock = #addresses as *const _; - - ///Return the pointer to the register block - #[inline(always)] - pub const fn ptr() -> *const #base::RegisterBlock { - Self::PTR - } - } - - #feature_attribute_n - impl Deref for #names_constant_case { - type Target = #base::RegisterBlock; - - #[inline(always)] - fn deref(&self) -> &Self::Target { - unsafe { &*Self::PTR } - } - } - - #feature_attribute_n - impl core::fmt::Debug for #names_constant_case { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - f.debug_struct(#names_str).finish() - } - } - )* - }); + // Insert the peripherals structure + per_to_tokens( + &mut out, + &feature_attribute_n, + &doc, + &p_ty, + doc_alias, + address, + ); + } - let feature_any_attribute = quote! {#[cfg(any(#(feature = #snake_names),*))]}; + let feature_any_attribute = quote! {#[cfg(any(#(feature = #feature_names),*))]}; // Derived peripherals may not require re-implementation, and will instead // use a single definition of the non-derived version. if derive_regs { // re-export the base module to allow deriveFrom this one out.extend(quote! { - #[doc = #description] + #[doc = #doc] #feature_any_attribute - pub use self::#base as #name_snake_case; + pub use self::#base as #mod_ty; }); return Ok(out); } } Peripheral::Single(_) => { - let p_snake = name.to_sanitized_snake_case(); + let p_feature = feature_format.apply(&name); if config.feature_peripheral { - feature_attribute.extend(quote! { #[cfg(feature = #p_snake)] }) + feature_attribute.extend(quote! { #[cfg(feature = #p_feature)] }) }; // Insert the peripheral structure - out.extend(quote! { - #[doc = #description] - #feature_attribute - pub struct #name_constant_case { _marker: PhantomData<*const ()> } - - #feature_attribute - unsafe impl Send for #name_constant_case {} - - #feature_attribute - impl #name_constant_case { - ///Pointer to the register block - pub const PTR: *const #base::RegisterBlock = #address as *const _; - - ///Return the pointer to the register block - #[inline(always)] - pub const fn ptr() -> *const #base::RegisterBlock { - Self::PTR - } - } - - #feature_attribute - impl Deref for #name_constant_case { - type Target = #base::RegisterBlock; - - #[inline(always)] - fn deref(&self) -> &Self::Target { - unsafe { &*Self::PTR } - } - } - - #feature_attribute - impl core::fmt::Debug for #name_constant_case { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - f.debug_struct(#name_str).finish() - } - } - }); + per_to_tokens(&mut out, &feature_attribute, &doc, &p_ty, None, address); // Derived peripherals may not require re-implementation, and will instead // use a single definition of the non-derived version. if derive_regs { // re-export the base module to allow deriveFrom this one out.extend(quote! { - #[doc = #description] + #[doc = #doc] #feature_attribute - pub use self::#base as #name_snake_case; + pub use self::#base as #mod_ty; }); return Ok(out); } } } - let description = util::escape_brackets( - util::respace(p.description.as_ref().unwrap_or(&name.as_ref().to_owned())).as_ref(), - ); - // Build up an alternate erc list by expanding any derived registers/clusters // erc: *E*ither *R*egister or *C*luster let mut ercs = p.registers.take().unwrap_or_default(); @@ -191,71 +164,81 @@ pub fn render(p_original: &Peripheral, index: &Index, config: &Config) -> Result return Ok(TokenStream::new()); } + debug!("Checking derivation information"); + let derive_infos = check_erc_derive_infos(&mut ercs, &path, index, config)?; + let zipped = ercs.iter_mut().zip(derive_infos.iter()); + for (mut erc, derive_info) in zipped { + if let RegisterCluster::Register(register) = &mut erc { + if let DeriveInfo::Implicit(rpath) = derive_info { + debug!( + "register {} implicitly derives from {}", + register.name, rpath.name + ); + } + } + } + debug!("Pushing cluster & register information into output"); // Push all cluster & register related information into the peripheral module - let mod_items = render_ercs(&mut ercs, &path, index, config)?; + let mod_items = render_ercs(&mut ercs, &derive_infos, &path, index, config)?; // Push any register or cluster blocks into the output debug!( "Pushing {} register or cluster blocks into output", ercs.len() ); - let reg_block = register_or_cluster_block(&ercs, None, None, config)?; - - let open = Punct::new('{', Spacing::Alone); - let close = Punct::new('}', Spacing::Alone); + let reg_block = register_or_cluster_block( + &ercs, + &BlockPath::new(&p.name), + &derive_infos, + None, + "Register block", + None, + config, + )?; out.extend(quote! { - #[doc = #description] + #[doc = #doc] #feature_attribute - pub mod #name_snake_case #open + pub mod #mod_ty }); - out.extend(reg_block); - out.extend(mod_items); + let mut out_items = TokenStream::new(); + out_items.extend(reg_block); + out_items.extend(mod_items); - close.to_tokens(&mut out); + let out_group = Group::new(Delimiter::Brace, out_items); + out.extend(quote! { #out_group }); p.registers = Some(ercs); Ok(out) } -#[derive(Clone, Debug)] -pub struct ArrayAccessor { - pub doc: String, - pub name: Ident, - pub ty: syn::Type, - pub basename: Ident, - pub i: syn::LitInt, +/// An enum describing the derivation status of an erc, which allows for disjoint arrays to be +/// implicitly derived from a common type. +#[derive(Default, Debug, PartialEq)] +enum DeriveInfo { + #[default] + Root, + Explicit(RegisterPath), + Implicit(RegisterPath), + Cluster, // don't do anything different for clusters } -impl ArrayAccessor { - pub fn to_tokens(&self, method: bool) -> TokenStream { - let parens = method.then(|| quote! {()}); - let doc = &self.doc; - let name = &self.name; - let ty = &self.ty; - let basename = &self.basename; - let i = &self.i; - quote! { - #[doc = #doc] - #[inline(always)] - pub fn #name(&self) -> &#ty { - &self.#basename #parens[#i] - } - } +impl fmt::Display for DeriveInfo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{self:?}") } } #[derive(Clone, Debug)] struct RegisterBlockField { syn_field: syn::Field, - description: String, offset: u32, size: u32, - accessors: Vec, + accessors: Vec, } #[derive(Clone, Debug)] @@ -268,11 +251,14 @@ struct Region { } impl Region { + fn size(&self) -> u32 { + self.end - self.offset + } fn shortest_ident(&self) -> Option { let mut idents: Vec<_> = self .rbfs .iter() - .filter_map(|f| f.syn_field.ident.as_ref().map(|ident| ident.to_string())) + .filter_map(|f| f.syn_field.ident.as_ref().map(ToString::to_string)) .collect(); if idents.is_empty() { return None; @@ -310,7 +296,7 @@ impl Region { let idents: Vec<_> = self .rbfs .iter() - .filter_map(|f| f.syn_field.ident.as_ref().map(|ident| ident.to_string())) + .filter_map(|f| f.syn_field.ident.as_ref().map(ToString::to_string)) .collect(); if idents.is_empty() { @@ -378,7 +364,7 @@ impl FieldRegions { let mut indices = Vec::new(); let rbf_start = rbf.offset; - let rbf_end = rbf_start + (rbf.size + BITS_PER_BYTE - 1) / BITS_PER_BYTE; + let rbf_end = rbf_start + rbf.size.div_ceil(BITS_PER_BYTE); // The region that we're going to insert let mut new_region = Region { @@ -429,12 +415,17 @@ impl FieldRegions { .binary_search_by_key(&new_region.offset, |r| r.offset); match idx { Ok(idx) => { - bail!( - "we shouldn't exist in the vec, but are at idx {} {:#?}\n{:#?}", - idx, - new_region, - self.regions - ); + if new_region.size() == 0 { + // add ArrayProxy + self.regions.insert(idx, new_region); + } else { + bail!( + "we shouldn't exist in the vec, but are at idx {} {:#?}\n{:#?}", + idx, + new_region, + self.regions + ); + } } Err(idx) => self.regions.insert(idx, new_region), }; @@ -473,7 +464,8 @@ impl FieldRegions { } fn make_comment(size: u32, offset: u32, description: &str) -> String { - let desc = util::escape_brackets(&util::respace(description)); + let desc = util::respace(description); + let desc = util::escape_special_chars(&desc); if size > 32 { let end = offset + size / 8; format!("0x{offset:02x}..0x{end:02x} - {desc}") @@ -484,15 +476,18 @@ fn make_comment(size: u32, offset: u32, description: &str) -> String { fn register_or_cluster_block( ercs: &[RegisterCluster], + path: &BlockPath, + derive_infos: &[DeriveInfo], name: Option<&str>, + doc: &str, size: Option, config: &Config, ) -> Result { let mut rbfs = TokenStream::new(); let mut accessors = TokenStream::new(); - let ercs_expanded = - expand(ercs, config).with_context(|| "Could not expand register or cluster block")?; + let ercs_expanded = expand(ercs, path, derive_infos, config) + .with_context(|| "Could not expand register or cluster block")?; // Locate conflicting regions; we'll need to use unions to represent them. let mut regions = FieldRegions::default(); @@ -522,39 +517,19 @@ fn register_or_cluster_block( let is_region_a_union = region.is_union(); for reg_block_field in ®ion.rbfs { - let comment = make_comment( - reg_block_field.size, - reg_block_field.offset, - ®_block_field.description, - ); - if is_region_a_union { - let name = ®_block_field.syn_field.ident; - let ty = ®_block_field.syn_field.ty; - let offset = reg_block_field.offset as usize; - accessors.extend(quote! { - #[doc = #comment] - #[inline(always)] - pub fn #name(&self) -> &#ty { - unsafe { - &*(((self as *const Self) as *const u8).add(#offset) as *const #ty) - } - } - }); + reg_block_field.accessors[0] + .clone() + .raw() + .to_tokens(&mut accessors); } else { - region_rbfs.extend(quote! { - #[doc = #comment] - }); - reg_block_field.syn_field.to_tokens(&mut region_rbfs); Punct::new(',', Spacing::Alone).to_tokens(&mut region_rbfs); + reg_block_field.accessors[0].to_tokens(&mut accessors); + } + for a in ®_block_field.accessors[1..] { + a.to_tokens(&mut accessors); } - accessors.extend( - reg_block_field - .accessors - .iter() - .map(|a| a.to_tokens(is_region_a_union)), - ); } if !is_region_a_union { @@ -601,27 +576,39 @@ fn register_or_cluster_block( }); } } + let derive_debug = config.impl_debug.then(|| { + if let Some(feature_name) = &config.impl_debug_feature { + quote!(#[cfg_attr(feature = #feature_name, derive(Debug))]) + } else { + quote!(#[derive(Debug)]) + } + }); - let name = if let Some(name) = name { - name.to_constant_case_ident(span) + let mut doc_alias = None; + let block_ty = if let Some(name) = name { + let ty = ident(name, config, "cluster", span); + if ty != name { + doc_alias = Some(quote!(#[doc(alias = #name)])); + } + ty } else { Ident::new("RegisterBlock", span) }; - let accessors = if !accessors.is_empty() { + let accessors = (!accessors.is_empty()).then(|| { quote! { - impl #name { + impl #block_ty { #accessors } } - } else { - quote! {} - }; + }); Ok(quote! { - ///Register block #[repr(C)] - pub struct #name { + #derive_debug + #[doc = #doc] + #doc_alias + pub struct #block_ty { #rbfs } @@ -631,27 +618,29 @@ fn register_or_cluster_block( /// Expand a list of parsed `Register`s or `Cluster`s, and render them to /// `RegisterBlockField`s containing `Field`s. -fn expand(ercs: &[RegisterCluster], config: &Config) -> Result> { +fn expand( + ercs: &[RegisterCluster], + path: &BlockPath, + derive_infos: &[DeriveInfo], + config: &Config, +) -> Result> { let mut ercs_expanded = vec![]; - debug!("Expanding registers or clusters into Register Block Fields"); - for erc in ercs { + let zipped = ercs.iter().zip(derive_infos.iter()); + + for (erc, derive_info) in zipped { match &erc { RegisterCluster::Register(register) => { let reg_name = ®ister.name; - let expanded_reg = expand_register(register, config).with_context(|| { - let descrip = register.description.as_deref().unwrap_or("No description"); - format!("Error expanding register\nName: {reg_name}\nDescription: {descrip}") - })?; + let expanded_reg = expand_register(register, path, derive_info, config) + .with_context(|| format!("can't expand register '{reg_name}'"))?; trace!("Register: {reg_name}"); ercs_expanded.extend(expanded_reg); } RegisterCluster::Cluster(cluster) => { let cluster_name = &cluster.name; - let expanded_cluster = expand_cluster(cluster, config).with_context(|| { - let descrip = cluster.description.as_deref().unwrap_or("No description"); - format!("Error expanding cluster\nName: {cluster_name}\nDescription: {descrip}") - })?; + let expanded_cluster = expand_cluster(cluster, path, config) + .with_context(|| format!("can't expand cluster '{cluster_name}'"))?; trace!("Cluster: {cluster_name}"); ercs_expanded.extend(expanded_cluster); } @@ -663,12 +652,239 @@ fn expand(ercs: &[RegisterCluster], config: &Config) -> Result Result> { + let mut ercs_type_info: Vec<(String, Option, &RegisterCluster, &mut DeriveInfo)> = + vec![]; + let mut derive_infos: Vec = vec![]; + // fill the array so we can slice it (without implementing clone) + for _i in 0..ercs.len() { + derive_infos.push(DeriveInfo::default()); + } + let derive_infos_slice = &mut derive_infos[0..ercs.len()]; + let zipped = ercs.iter_mut().zip(derive_infos_slice.iter_mut()); + for (mut erc, derive_info) in zipped { + match &mut erc { + RegisterCluster::Register(register) => { + let info_name = register.fullname(config.ignore_groups).to_string(); + let explicit_rpath = match &mut register.derived_from.clone() { + Some(dpath) => { + let (_, root) = find_root(dpath, path, index)?; + Some(root) + } + None => None, + }; + match register { + Register::Single(_) => { + let ty_name = info_name.clone(); + *derive_info = match explicit_rpath { + None => { + match compare_this_against_prev( + register, + &ty_name, + path, + index, + &ercs_type_info, + )? { + Some(root) => { + // make sure the matched register isn't already deriving from this register + if ty_name == root.name { + DeriveInfo::Root + } else { + DeriveInfo::Implicit(root) + } + } + None => DeriveInfo::Root, + } + } + Some(rpath) => DeriveInfo::Explicit(rpath), + }; + ercs_type_info.push((ty_name, None, erc, derive_info)); + } + + Register::Array(..) => { + // Only match integer indeces when searching for disjoint arrays + let re_string = info_name.expand_dim("([0-9]+|%s)"); + let re = Regex::new(format!("^{re_string}$").as_str()).map_err(|_| { + anyhow!("Error creating regex for register {}", register.name) + })?; + let ty_name = info_name.clone(); // keep suffix for regex matching + *derive_info = match explicit_rpath { + None => { + match compare_this_against_prev( + register, + &ty_name, + path, + index, + &ercs_type_info, + )? { + Some(root) => DeriveInfo::Implicit(root), + None => compare_prev_against_this( + register, + &ty_name, + &re, + path, + index, + &mut ercs_type_info, + )?, + } + } + Some(rpath) => DeriveInfo::Explicit(rpath), + }; + ercs_type_info.push((ty_name, Some(re), erc, derive_info)); + } + }; + } + RegisterCluster::Cluster(cluster) => { + *derive_info = DeriveInfo::Cluster; + ercs_type_info.push((cluster.name.clone(), None, erc, derive_info)); + } + }; + } + Ok(derive_infos) +} + +fn find_root( + dpath: &str, + path: &BlockPath, + index: &Index, +) -> Result<(MaybeArray, RegisterPath)> { + let (dblock, dname) = RegisterPath::parse_str(dpath); + let rdpath; + let reg_path; + let d = (if let Some(dblock) = dblock { + reg_path = dblock.new_register(dname); + rdpath = dblock; + index.registers.get(®_path) + } else { + reg_path = path.new_register(dname); + rdpath = path.clone(); + index.registers.get(®_path) + }) + .ok_or_else(|| anyhow!("register {} not found", dpath))?; + match d.derived_from.as_ref() { + Some(dp) => find_root(dp, &rdpath, index), + None => Ok(((*d).clone(), reg_path)), + } +} + +/// Compare the given type name against previous regexs, then inspect fields +fn compare_this_against_prev( + reg: &MaybeArray, + ty_name: &str, + path: &BlockPath, + index: &Index, + ercs_type_info: &Vec<(String, Option, &RegisterCluster, &mut DeriveInfo)>, +) -> Result> { + for prev in ercs_type_info { + let (prev_name, prev_regex, prev_erc, _prev_derive_info) = prev; + if let RegisterCluster::Register(_) = prev_erc { + if let Some(prev_re) = prev_regex { + if prev_re.is_match(ty_name) { + let (source_reg, rpath) = find_root(prev_name, path, index)?; + if is_derivable(&source_reg, reg) { + return Ok(Some(rpath)); + } + } + } + } + } + Ok(None) +} + +/// Compare previous type names against the given regex, then inspect fields +fn compare_prev_against_this( + reg: &MaybeArray, + ty_name: &str, + re: ®ex::Regex, + path: &BlockPath, + index: &Index, + ercs_type_info: &mut Vec<(String, Option, &RegisterCluster, &mut DeriveInfo)>, +) -> Result { + let mut my_derive_info = DeriveInfo::Root; + // Check this type regex against previous names + for prev in ercs_type_info { + let (prev_name, _prev_regex, prev_erc, prev_derive_info) = prev; + if let RegisterCluster::Register(prev_reg) = prev_erc { + if let Register::Array(..) = prev_reg { + // Arrays are covered with compare_this_against_prev + continue; + } + if re.is_match(prev_name) { + let loop_derive_info = match prev_derive_info { + DeriveInfo::Root => { + // Get the RegisterPath for reg + let (_, implicit_rpath) = find_root(ty_name, path, index)?; + if is_derivable(prev_reg, reg) { + **prev_derive_info = DeriveInfo::Implicit(implicit_rpath); + } + DeriveInfo::Root + } + DeriveInfo::Explicit(rpath) => { + let (source_reg, implicit_rpath) = find_root(&rpath.name, path, index)?; + if is_derivable(&source_reg, reg) { + DeriveInfo::Implicit(implicit_rpath) + } else { + DeriveInfo::Root + } + } + DeriveInfo::Implicit(rpath) => { + let (source_reg, _) = find_root(&rpath.name, path, index)?; + if is_derivable(&source_reg, reg) { + DeriveInfo::Implicit(rpath.clone()) + } else { + DeriveInfo::Root + } + } + DeriveInfo::Cluster => { + return Err(anyhow!("register {} represented as cluster", prev_reg.name)) + } + }; + if let DeriveInfo::Root = my_derive_info { + if my_derive_info != loop_derive_info { + my_derive_info = loop_derive_info; + } + } + } + if let DeriveInfo::Implicit(my_rpath) = &my_derive_info { + match prev_derive_info { + DeriveInfo::Implicit(their_rpath) => { + if their_rpath.name == ty_name { + (_, *their_rpath) = find_root(&my_rpath.name, path, index)?; + } + } + DeriveInfo::Explicit(their_rpath) => { + if their_rpath.name == ty_name { + (_, *their_rpath) = find_root(&my_rpath.name, path, index)?; + } + } + _ => {} + } + } + } + } + Ok(my_derive_info) +} + +fn is_derivable( + source_reg: &MaybeArray, + target_reg: &MaybeArray, +) -> bool { + (source_reg.properties == target_reg.properties) && (source_reg.fields == target_reg.fields) +} + /// Calculate the size of a Cluster. If it is an array, then the dimensions /// tell us the size of the array. Otherwise, inspect the contents using /// [cluster_info_size_in_bits]. -fn cluster_size_in_bits(cluster: &Cluster, config: &Config) -> Result { +fn cluster_size_in_bits(cluster: &Cluster, path: &BlockPath, config: &Config) -> Result { match cluster { - Cluster::Single(info) => cluster_info_size_in_bits(info, config), + Cluster::Single(info) => cluster_info_size_in_bits(info, path, config), // If the contained array cluster has a mismatch between the // dimIncrement and the size of the array items, then the array // will get expanded in expand_cluster below. The overall size @@ -678,7 +894,7 @@ fn cluster_size_in_bits(cluster: &Cluster, config: &Config) -> Result { return Ok(0); // Special case! } let last_offset = (dim.dim - 1) * dim.dim_increment * BITS_PER_BYTE; - let last_size = cluster_info_size_in_bits(info, config); + let last_size = cluster_info_size_in_bits(info, path, config); Ok(last_offset + last_size?) } } @@ -686,13 +902,13 @@ fn cluster_size_in_bits(cluster: &Cluster, config: &Config) -> Result { /// Recursively calculate the size of a ClusterInfo. A cluster's size is the /// maximum end position of its recursive children. -fn cluster_info_size_in_bits(info: &ClusterInfo, config: &Config) -> Result { +fn cluster_info_size_in_bits(info: &ClusterInfo, path: &BlockPath, config: &Config) -> Result { let mut size = 0; for c in &info.children { let end = match c { RegisterCluster::Register(reg) => { - let reg_size: u32 = expand_register(reg, config)? + let reg_size: u32 = expand_register(reg, path, &DeriveInfo::Root, config)? .iter() .map(|rbf| rbf.size) .sum(); @@ -700,7 +916,7 @@ fn cluster_info_size_in_bits(info: &ClusterInfo, config: &Config) -> Result (reg.address_offset * BITS_PER_BYTE) + reg_size } RegisterCluster::Cluster(clust) => { - (clust.address_offset * BITS_PER_BYTE) + cluster_size_in_bits(clust, config)? + (clust.address_offset * BITS_PER_BYTE) + cluster_size_in_bits(clust, path, config)? } }; @@ -710,10 +926,14 @@ fn cluster_info_size_in_bits(info: &ClusterInfo, config: &Config) -> Result } /// Render a given cluster (and any children) into `RegisterBlockField`s -fn expand_cluster(cluster: &Cluster, config: &Config) -> Result> { +fn expand_cluster( + cluster: &Cluster, + path: &BlockPath, + config: &Config, +) -> Result> { let mut cluster_expanded = vec![]; - let cluster_size = cluster_info_size_in_bits(cluster, config) + let cluster_size = cluster_info_size_in_bits(cluster, path, config) .with_context(|| format!("Can't calculate cluster {} size", cluster.name))?; let description = cluster .description @@ -722,24 +942,34 @@ fn expand_cluster(cluster: &Cluster, config: &Config) -> Result { - let syn_field = new_syn_field(info.name.to_snake_case_ident(Span::call_site()), ty); + let doc = make_comment(cluster_size, info.address_offset, &description); + let name: Ident = ident(&info.name, config, "cluster_accessor", span); + let syn_field = new_syn_field(name.clone(), ty.clone()); + let accessor = Accessor::Reg(RegAccessor { + doc, + name, + ty, + offset: info.address_offset, + }) + .raw_if(false); cluster_expanded.push(RegisterBlockField { syn_field, - description, offset: info.address_offset, size: cluster_size, - accessors: Vec::new(), + accessors: vec![accessor], }) } Cluster::Array(info, array_info) => { + let ends_with_index = info.name.ends_with("[%s]") || info.name.ends_with("%s"); let increment_bits = array_info.dim_increment * BITS_PER_BYTE; if cluster_size > increment_bits { let cname = &cluster.name; @@ -759,77 +989,103 @@ fn expand_cluster(cluster: &Cluster, config: &Config) -> Result match &array_info.dim_name { - Some(dim_name) => dim_name.contains("[%s]"), - None => info.name.contains("[%s]"), - }, + true => info.name.contains("[%s]"), false => true, }; let array_convertible = sequential_addresses && convert_list; - if array_convertible { - let accessors = if sequential_indexes_from0 { - Vec::new() - } else { - let span = Span::call_site(); - let mut accessors = Vec::new(); - let nb_name_cs = ty_name.to_snake_case_ident(span); - for (i, idx) in array_info.indexes().enumerate() { - let idx_name = - util::replace_suffix(&info.name, &idx).to_snake_case_ident(span); - let comment = make_comment( + if convert_list { + let accessor_name = ident( + if let Some(dim_name) = array_info.dim_name.as_ref() { + dim_name + } else { + &ty_name + }, + config, + "cluster_accessor", + span, + ); + let doc = make_comment( + cluster_size * array_info.dim, + info.address_offset, + &description, + ); + let mut accessors = Vec::with_capacity((array_info.dim + 1) as _); + let first_name = svd::array::names(info, array_info).next().unwrap(); + let note = (array_info.indexes().next().unwrap() != "0").then(|| + format!("
`n` is the index of {0} in the array. `n == 0` corresponds to `{first_name}` {0}.
", "cluster") + ); + accessors.push( + Accessor::Array(ArrayAccessor { + doc, + name: accessor_name.clone(), + ty: ty.clone(), + offset: info.address_offset, + dim: array_info.dim, + increment: array_info.dim_increment, + note, + }) + .raw_if(!array_convertible), + ); + if !sequential_indexes_from0 || !ends_with_index { + for (i, ci) in svd::cluster::expand(info, array_info).enumerate() { + let idx_name = ident(&ci.name, config, "cluster_accessor", span); + let doc = make_comment( cluster_size, - info.address_offset + (i as u32) * cluster_size / 8, - &description, + ci.address_offset, + ci.description.as_deref().unwrap_or(&ci.name), + ); + accessors.push( + Accessor::ArrayElem(ArrayElemAccessor { + doc, + name: idx_name, + ty: ty.clone(), + basename: accessor_name.clone(), + i, + }) + .raw_if(false), ); - let i = unsuffixed(i as _); - accessors.push(ArrayAccessor { - doc: comment, - name: idx_name, - ty: ty.clone(), - basename: nb_name_cs.clone(), - i, - }); } - accessors + } + let array_ty = if array_convertible { + new_syn_array(ty, array_info.dim) + } else { + zst_type() }; - let array_ty = new_syn_array(ty, array_info.dim); - cluster_expanded.push(RegisterBlockField { - syn_field: new_syn_field( - ty_name.to_snake_case_ident(Span::call_site()), - array_ty, - ), - description, - offset: info.address_offset, - size: cluster_size * array_info.dim, - accessors, - }); - } else if sequential_indexes_from0 && config.const_generic { - // Include a ZST ArrayProxy giving indexed access to the - // elements. - let ap_path = array_proxy_type(ty, array_info); - let syn_field = - new_syn_field(ty_name.to_snake_case_ident(Span::call_site()), ap_path); + let syn_field = new_syn_field(accessor_name, array_ty); cluster_expanded.push(RegisterBlockField { syn_field, - description: info.description.as_ref().unwrap_or(&info.name).into(), offset: info.address_offset, - size: 0, - accessors: Vec::new(), + size: if array_convertible { + cluster_size * array_info.dim + } else { + 0 + }, + accessors, }); } else { - for (field_num, idx) in array_info.indexes().enumerate() { - let nb_name = util::replace_suffix(&info.name, &idx); - let syn_field = - new_syn_field(nb_name.to_snake_case_ident(Span::call_site()), ty.clone()); - + for ci in svd::cluster::expand(info, array_info) { + let doc = make_comment( + cluster_size, + ci.address_offset, + ci.description.as_deref().unwrap_or(&ci.name), + ); + let name = ident(&ci.name, config, "cluster_accessor", span); + let syn_field = new_syn_field(name.clone(), ty.clone()); + + let accessor = Accessor::Reg(RegAccessor { + doc, + name, + ty: ty.clone(), + offset: info.address_offset, + }) + .raw_if(false); cluster_expanded.push(RegisterBlockField { syn_field, - description: description.clone(), - offset: info.address_offset + field_num as u32 * array_info.dim_increment, + offset: ci.address_offset, size: cluster_size, - accessors: Vec::new(), + accessors: vec![accessor], }); } } @@ -840,103 +1096,188 @@ fn expand_cluster(cluster: &Cluster, config: &Config) -> Result Result> { +/// numeral indexes, or not containing all elements from 0 to size) they will be expanded. +/// A `DeriveInfo::Implicit(_)` will also cause an array to be expanded. +fn expand_register( + register: &Register, + path: &BlockPath, + derive_info: &DeriveInfo, + config: &Config, +) -> Result> { let mut register_expanded = vec![]; let register_size = register .properties .size .ok_or_else(|| anyhow!("Register {} has no `size` field", register.name))?; - let description = register.description.clone().unwrap_or_default(); + let description = register.description.as_deref().unwrap_or_default(); let info_name = register.fullname(config.ignore_groups); - let ty_name = if register.is_single() { - info_name.to_string() + let mut ty_name = if register.is_single() { + info_name.clone() } else { - util::replace_suffix(&info_name, "") + info_name.remove_dim() }; - let ty = name_to_ty(&ty_name); + let mut ty_str = ty_name.clone(); match register { Register::Single(info) => { - let syn_field = new_syn_field(ty_name.to_snake_case_ident(Span::call_site()), ty); + let doc = make_comment(register_size, info.address_offset, description); + let span = Span::call_site(); + let ty = name_to_ty(ident(&ty_str, config, "register", span)); + let name: Ident = ident(&ty_name, config, "register_accessor", span); + let syn_field = new_syn_field(name.clone(), ty.clone()); + let accessor = Accessor::Reg(RegAccessor { + doc, + name, + ty, + offset: info.address_offset, + }) + .raw_if(false); register_expanded.push(RegisterBlockField { syn_field, - description, offset: info.address_offset, size: register_size, - accessors: Vec::new(), + accessors: vec![accessor], }) } Register::Array(info, array_info) => { + let ends_with_index = info.name.ends_with("[%s]") || info.name.ends_with("%s"); let sequential_addresses = (array_info.dim == 1) || (register_size == array_info.dim_increment * BITS_PER_BYTE); + let disjoint_sequential_addresses = (array_info.dim == 1) + || (register_size <= array_info.dim_increment * BITS_PER_BYTE); let convert_list = match config.keep_list { - true => match &array_info.dim_name { - Some(dim_name) => dim_name.contains("[%s]"), - None => info.name.contains("[%s]"), - }, + true => info_name.contains("[%s]"), false => true, }; - let array_convertible = sequential_addresses && convert_list; - - if array_convertible { - // if dimIndex exists, test if it is a sequence of numbers from 0 to dim - let sequential_indexes_from0 = array_info - .indexes_as_range() - .filter(|r| *r.start() == 0) - .is_some(); + // if dimIndex exists, test if it is a sequence of numbers from 0 to dim + let sequential_indexes_from0 = array_info + .indexes_as_range() + .filter(|r| *r.start() == 0) + .is_some(); - let accessors = if sequential_indexes_from0 { - Vec::new() + // force expansion and rename if we're deriving an array that doesnt start at 0 so we don't get name collisions + let index: Cow = if let Some(dim_index) = &array_info.dim_index { + dim_index[0].as_str().into() + } else if sequential_indexes_from0 { + "0".into() + } else { + "".into() + }; + let ac = match derive_info { + DeriveInfo::Implicit(di) | DeriveInfo::Explicit(di) + if path == &di.block && !info_name.contains("[%s]") => + { + ty_name = info_name.expand_dim(&index); + ty_str = ty_name.clone(); + false + } + _ => convert_list, + }; + let array_convertible = ac && sequential_addresses; + let array_proxy_convertible = ac && disjoint_sequential_addresses; + let span = Span::call_site(); + let ty = name_to_ty(ident(&ty_str, config, "register", span)); + + if array_convertible || array_proxy_convertible { + let accessor_name = if let Some(dim_name) = array_info.dim_name.as_ref() { + ident( + &util::fullname(dim_name, &info.alternate_group, config.ignore_groups), + config, + "register_accessor", + span, + ) } else { - let span = Span::call_site(); - let mut accessors = Vec::new(); - let nb_name_cs = ty_name.to_snake_case_ident(span); - for (i, idx) in array_info.indexes().enumerate() { - let idx_name = - util::replace_suffix(&info_name, &idx).to_snake_case_ident(span); - let comment = make_comment( + ident(&ty_name, config, "register_accessor", span) + }; + let doc = make_comment( + register_size * array_info.dim, + info.address_offset, + description, + ); + let mut accessors = Vec::with_capacity((array_info.dim + 1) as _); + let first_name = svd::array::names(info, array_info).next().unwrap(); + let note = (array_info.indexes().next().unwrap() != "0").then(|| + format!("
`n` is the index of {0} in the array. `n == 0` corresponds to `{first_name}` {0}.
", "register") + ); + accessors.push( + Accessor::Array(ArrayAccessor { + doc, + name: accessor_name.clone(), + ty: ty.clone(), + offset: info.address_offset, + dim: array_info.dim, + increment: array_info.dim_increment, + note, + }) + .raw_if(!array_convertible), + ); + if !sequential_indexes_from0 || !ends_with_index { + for (i, ri) in svd::register::expand(info, array_info).enumerate() { + let idx_name = ident( + &util::fullname(&ri.name, &info.alternate_group, config.ignore_groups), + config, + "register_accessor", + span, + ); + let doc = make_comment( register_size, - info.address_offset + (i as u32) * register_size / 8, - &description, + ri.address_offset, + ri.description.as_deref().unwrap_or(&ri.name), + ); + accessors.push( + Accessor::ArrayElem(ArrayElemAccessor { + doc, + name: idx_name, + ty: ty.clone(), + basename: accessor_name.clone(), + i, + }) + .raw_if(false), ); - let i = unsuffixed(i as _); - accessors.push(ArrayAccessor { - doc: comment, - name: idx_name, - ty: ty.clone(), - basename: nb_name_cs.clone(), - i, - }); } - accessors }; - let array_ty = new_syn_array(ty, array_info.dim); - let syn_field = - new_syn_field(ty_name.to_snake_case_ident(Span::call_site()), array_ty); + let array_ty = if array_convertible { + new_syn_array(ty, array_info.dim) + } else { + zst_type() + }; + let syn_field = new_syn_field(accessor_name, array_ty); register_expanded.push(RegisterBlockField { syn_field, - description, offset: info.address_offset, - size: register_size * array_info.dim, + size: if array_convertible { + register_size * array_info.dim + } else { + 0 + }, accessors, }); } else { - for (field_num, idx) in array_info.indexes().enumerate() { - let nb_name = util::replace_suffix(&info_name, &idx); - let syn_field = - new_syn_field(nb_name.to_snake_case_ident(Span::call_site()), ty.clone()); - + for ri in svd::register::expand(info, array_info) { + let doc = make_comment( + register_size, + info.address_offset, + ri.description.as_deref().unwrap_or(&ri.name), + ); + let name = ident(&ri.name, config, "register_accessor", span); + let syn_field = new_syn_field(name.clone(), ty.clone()); + + let accessor = Accessor::Reg(RegAccessor { + doc, + name, + ty: ty.clone(), + offset: info.address_offset, + }) + .raw_if(false); register_expanded.push(RegisterBlockField { syn_field, - description: description.clone(), - offset: info.address_offset + field_num as u32 * array_info.dim_increment, + offset: ri.address_offset, size: register_size, - accessors: Vec::new(), + accessors: vec![accessor], }); } } @@ -948,13 +1289,15 @@ fn expand_register(register: &Register, config: &Config) -> Result Result { let mut mod_items = TokenStream::new(); - for erc in ercs { + let zipped = ercs.iter_mut().zip(derive_infos.iter()); + for (erc, derive_info) in zipped { match erc { // Generate the sub-cluster blocks. RegisterCluster::Cluster(c) => { @@ -969,21 +1312,19 @@ fn render_ercs( // Generate definition for each of the registers. RegisterCluster::Register(reg) => { - trace!("Register: {}", reg.name); + trace!("Register: {}, DeriveInfo: {}", reg.name, derive_info); let mut rpath = None; - let dpath = reg.derived_from.take(); - if let Some(dpath) = dpath { - rpath = derive_register(reg, &dpath, path, index)?; + if let DeriveInfo::Implicit(rp) = derive_info { + rpath = Some(rp.clone()); + } else { + let dpath = reg.derived_from.take(); + if let Some(dpath) = dpath { + rpath = derive_register(reg, &dpath, path, index)?; + } } let reg_name = ®.name; - - let rendered_reg = - register::render(reg, path, rpath, index, config).with_context(|| { - let descrip = reg.description.as_deref().unwrap_or("No description"); - format!( - "Error rendering register\nName: {reg_name}\nDescription: {descrip}" - ) - })?; + let rendered_reg = register::render(reg, path, rpath, index, config) + .with_context(|| format!("can't render register '{reg_name}'"))?; mod_items.extend(rendered_reg) } } @@ -999,41 +1340,43 @@ fn cluster_block( index: &Index, config: &Config, ) -> Result { - let description = - util::escape_brackets(&util::respace(c.description.as_ref().unwrap_or(&c.name))); - let mod_name = util::replace_suffix(&c.name, ""); + let doc = c.description.as_ref().unwrap_or(&c.name); + let doc = util::respace(doc); + let doc = util::escape_special_chars(&doc); + let mod_name = c.name.remove_dim().to_string(); // name_snake_case needs to take into account array type. let span = Span::call_site(); - let name_snake_case = mod_name.to_snake_case_ident(span); - let name_constant_case = mod_name.to_constant_case_ident(span); + let mod_ty = ident(&mod_name, config, "cluster_mod", span); + let block_ty = ident(&mod_name, config, "cluster", span); if let Some(dpath) = dpath { let dparent = dpath.parent().unwrap(); let mut derived = if &dparent == path { type_path(Punctuated::new()) } else { - util::block_path_to_ty(&dparent, span) + util::block_path_to_ty(&dparent, config, span) }; - let dname = util::replace_suffix(&index.clusters.get(&dpath).unwrap().name, ""); + let dname = index.clusters.get(&dpath).unwrap().name.remove_dim(); let mut mod_derived = derived.clone(); derived .path .segments - .push(path_segment(dname.to_constant_case_ident(span))); + .push(path_segment(ident(&dname, config, "cluster", span))); mod_derived .path .segments - .push(path_segment(dname.to_snake_case_ident(span))); + .push(path_segment(ident(&dname, config, "cluster_mod", span))); Ok(quote! { - #[doc = #description] - pub use self::#derived as #name_constant_case; - pub use self::#mod_derived as #name_snake_case; + #[doc = #doc] + pub use #derived as #block_ty; + pub use #mod_derived as #mod_ty; }) } else { let cpath = path.new_cluster(&c.name); - let mod_items = render_ercs(&mut c.children, &cpath, index, config)?; + let mod_derive_infos = check_erc_derive_infos(&mut c.children, &cpath, index, config)?; + let mod_items = render_ercs(&mut c.children, &mod_derive_infos, &cpath, index, config)?; // Generate the register block. let cluster_size = match c { @@ -1042,8 +1385,15 @@ fn cluster_block( } _ => None, }; - let reg_block = - register_or_cluster_block(&c.children, Some(&mod_name), cluster_size, config)?; + let reg_block = register_or_cluster_block( + &c.children, + &path.new_cluster(&c.name), + &mod_derive_infos, + Some(&mod_name), + &doc, + cluster_size, + config, + )?; let mod_items = quote! { #reg_block @@ -1052,12 +1402,12 @@ fn cluster_block( }; Ok(quote! { - #[doc = #description] - pub use self::#name_snake_case::#name_constant_case; + #[doc = #doc] + pub use self::#mod_ty::#block_ty; ///Cluster - #[doc = #description] - pub mod #name_snake_case { + #[doc = #doc] + pub mod #mod_ty { #mod_items } }) @@ -1068,21 +1418,16 @@ fn new_syn_field(ident: Ident, ty: syn::Type) -> syn::Field { let span = Span::call_site(); syn::Field { ident: Some(ident), - vis: syn::Visibility::Public(syn::VisPublic { - pub_token: Token![pub](span), - }), + vis: syn::Visibility::Inherited, attrs: vec![], colon_token: Some(Token![:](span)), ty, + mutability: syn::FieldMutability::None, } } fn new_syn_array(ty: syn::Type, len: u32) -> syn::Type { let span = Span::call_site(); - syn::Type::Array(syn::TypeArray { - bracket_token: syn::token::Bracket { span }, - elem: ty.into(), - semi_token: Token![;](span), - len: new_syn_u32(len, span), - }) + let len = unsuffixed(len); + syn::parse_quote_spanned!( span => [#ty; #len] ) } diff --git a/src/generate/peripheral/accessor.rs b/src/generate/peripheral/accessor.rs new file mode 100644 index 00000000..5bb91718 --- /dev/null +++ b/src/generate/peripheral/accessor.rs @@ -0,0 +1,169 @@ +use proc_macro2::{Ident, Span, TokenStream}; +use quote::{quote, ToTokens}; + +use crate::util::unsuffixed; + +#[derive(Clone, Debug)] +pub enum Accessor { + Reg(RegAccessor), + Array(ArrayAccessor), + ArrayElem(ArrayElemAccessor), +} + +#[derive(Clone, Debug)] +pub enum AccessType { + Ref(Accessor), + RawRef(Accessor), +} + +impl Accessor { + pub fn raw_if(self, flag: bool) -> AccessType { + if flag { + AccessType::RawRef(self) + } else { + AccessType::Ref(self) + } + } +} + +impl AccessType { + pub fn raw(self) -> Self { + match self { + Self::RawRef(_) => self, + Self::Ref(a) => Self::RawRef(a), + } + } +} + +impl ToTokens for AccessType { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + Self::Ref(Accessor::Reg(RegAccessor { doc, name, ty, .. })) => { + quote! { + #[doc = #doc] + #[inline(always)] + pub const fn #name(&self) -> &#ty { + &self.#name + } + } + } + Self::RawRef(Accessor::Reg(RegAccessor { + doc, + name, + ty, + offset, + })) => { + let offset = (*offset != 0).then(|| unsuffixed(*offset)).map(|o| quote!(.add(#o))); + quote! { + #[doc = #doc] + #[inline(always)] + pub const fn #name(&self) -> &#ty { + unsafe { &*core::ptr::from_ref(self).cast::() #offset .cast() } + } + } + } + Self::Ref(Accessor::Array(ArrayAccessor { doc, name, ty, note, .. })) => { + let name_iter = Ident::new(&format!("{name}_iter"), Span::call_site()); + let note = note.as_ref().map(|note| quote! { + #[doc = ""] + #[doc = #note] + }); + quote! { + #[doc = #doc] + #note + #[inline(always)] + pub const fn #name(&self, n: usize) -> &#ty { + &self.#name[n] + } + #[doc = "Iterator for array of:"] + #[doc = #doc] + #[inline(always)] + pub fn #name_iter(&self) -> impl Iterator { + self.#name.iter() + } + } + } + Self::RawRef(Accessor::Array(ArrayAccessor { + doc, + name, + ty, + offset, + dim, + increment, + note, + })) => { + let name_iter = Ident::new(&format!("{name}_iter"), Span::call_site()); + let offset = (*offset != 0).then(|| unsuffixed(*offset)).map(|o| quote!(.add(#o))); + let dim = unsuffixed(*dim); + let increment = (*increment != 1).then(|| unsuffixed(*increment)).map(|i| quote!(#i *)); + let note = note.as_ref().map(|note| quote! { + #[doc = ""] + #[doc = #note] + }); + let cast = quote! { unsafe { &*core::ptr::from_ref(self).cast::() #offset .add(#increment n).cast() } }; + quote! { + #[doc = #doc] + #note + #[inline(always)] + pub const fn #name(&self, n: usize) -> &#ty { + #[allow(clippy::no_effect)] + [(); #dim][n]; + #cast + } + #[doc = "Iterator for array of:"] + #[doc = #doc] + #[inline(always)] + pub fn #name_iter(&self) -> impl Iterator { + (0..#dim).map(move |n| #cast) + } + } + } + Self::RawRef(Accessor::ArrayElem(elem)) | Self::Ref(Accessor::ArrayElem(elem)) => { + let ArrayElemAccessor { + doc, + name, + ty, + basename, + i, + } = elem; + let i = unsuffixed(*i as u64); + quote! { + #[doc = #doc] + #[inline(always)] + pub const fn #name(&self) -> &#ty { + self.#basename(#i) + } + } + } + } + .to_tokens(tokens); + } +} + +#[derive(Clone, Debug)] +pub struct RegAccessor { + pub doc: String, + pub name: Ident, + pub ty: syn::Type, + pub offset: u32, +} + +#[derive(Clone, Debug)] +pub struct ArrayAccessor { + pub doc: String, + pub name: Ident, + pub ty: syn::Type, + pub offset: u32, + pub dim: u32, + pub increment: u32, + pub note: Option, +} + +#[derive(Clone, Debug)] +pub struct ArrayElemAccessor { + pub doc: String, + pub name: Ident, + pub ty: syn::Type, + pub basename: Ident, + pub i: usize, +} diff --git a/src/generate/register.rs b/src/generate/register.rs index fefb3dcc..7622ba71 100644 --- a/src/generate/register.rs +++ b/src/generate/register.rs @@ -1,20 +1,46 @@ use crate::svd::{ - Access, BitRange, EnumeratedValues, Field, ModifiedWriteValues, ReadAction, Register, - RegisterProperties, Usage, WriteConstraint, + self, Access, BitRange, DimElement, EnumeratedValue, EnumeratedValues, Field, MaybeArray, + ModifiedWriteValues, ReadAction, Register, RegisterProperties, Usage, WriteConstraint, + WriteConstraintRange, }; -use core::u64; use log::warn; -use proc_macro2::{Ident, Punct, Spacing, Span, TokenStream}; -use quote::{quote, ToTokens}; +use proc_macro2::{Delimiter, Group, Ident, Span, TokenStream}; +use quote::quote; use std::collections::HashSet; +use std::fmt::Write; +use std::{borrow::Cow, collections::BTreeMap}; use svd_parser::expand::{ derive_enumerated_values, derive_field, BlockPath, EnumPath, FieldPath, Index, RegisterPath, }; -use crate::util::{self, ident_to_path, path_segment, type_path, Config, ToSanitizedCase, U32Ext}; +use crate::config::Config; +use crate::util::{ + self, ident, ident_to_path, path_segment, type_path, unsuffixed, DimSuffix, FullName, U32Ext, +}; use anyhow::{anyhow, Result}; use syn::punctuated::Punctuated; +fn regspec(name: &str, config: &Config, span: Span) -> Ident { + ident(name, config, "register_spec", span) +} + +fn field_accessor(name: &str, config: &Config, span: Span) -> Ident { + const INTERNALS: [&str; 2] = ["bits", "set"]; + let sc = config + .ident_formats + .get("field_accessor") + .unwrap() + .sanitize(name); + Ident::new( + &(if INTERNALS.contains(&sc.as_ref()) { + sc + "_" + } else { + sc + }), + span, + ) +} + pub fn render( register: &Register, path: &BlockPath, @@ -22,41 +48,54 @@ pub fn render( index: &Index, config: &Config, ) -> Result { - let name = util::name_of(register, config.ignore_groups); + let mut name = util::name_of(register, config.ignore_groups); + // Rename if this is a derived array + if let Some(dpath) = dpath.as_ref() { + if let MaybeArray::Array(info, array_info) = register { + if let Some(dim_index) = &array_info.dim_index { + if path == &dpath.block { + let index: Cow = dim_index.first().unwrap().into(); + name = info + .fullname(config.ignore_groups) + .expand_dim(&index) + .into() + } + } + } + } let span = Span::call_site(); - let name_constant_case = name.to_constant_case_ident(span); - let name_snake_case = name.to_snake_case_ident(span); - let description = util::escape_brackets( - util::respace(®ister.description.clone().unwrap_or_else(|| { - warn!("Missing description for register {}", register.name); - Default::default() - })) - .as_ref(), - ); + let reg_ty = ident(&name, config, "register", span); + let doc_alias = (reg_ty.to_string().as_str() != name).then(|| quote!(#[doc(alias = #name)])); + let mod_ty = ident(&name, config, "register_mod", span); + let description = util::respace(register.description.as_deref().unwrap_or_else(|| { + warn!("Missing description for register {}", register.name); + "" + })); + let description = util::escape_special_chars(&description); if let Some(dpath) = dpath.as_ref() { let mut derived = if &dpath.block == path { type_path(Punctuated::new()) } else { - util::block_path_to_ty(&dpath.block, span) + util::block_path_to_ty(&dpath.block, config, span) }; let dname = util::name_of(index.registers.get(dpath).unwrap(), config.ignore_groups); let mut mod_derived = derived.clone(); derived .path .segments - .push(path_segment(dname.to_constant_case_ident(span))); + .push(path_segment(ident(&dname, config, "register", span))); mod_derived .path .segments - .push(path_segment(dname.to_snake_case_ident(span))); + .push(path_segment(ident(&dname, config, "register_mod", span))); Ok(quote! { - pub use #derived as #name_constant_case; - pub use #mod_derived as #name_snake_case; + pub use #derived as #reg_ty; + pub use #mod_derived as #mod_ty; }) } else { - let name_constant_case_spec = format!("{name}_SPEC").to_constant_case_ident(span); + let regspec_ty = regspec(&name, config, span); let access = util::access_of(®ister.properties, register.fields.as_deref()); let accs = if access.can_read() && access.can_write() { "rw" @@ -67,25 +106,36 @@ pub fn render( } else { return Err(anyhow!("Incorrect access of register {}", register.name)); }; - let alias_doc = format!( - "{name} ({accs}) register accessor: an alias for `Reg<{name_constant_case_spec}>`" + + let rpath = path.new_register(®ister.name); + let mut alias_doc = format!( + "{name} ({accs}) register accessor: {description}{}{}", + api_docs( + access.can_read(), + access.can_write(), + register.properties.reset_value.is_some(), + &mod_ty, + false, + register, + &rpath, + config, + )?, + read_action_docs(access.can_read(), register.read_action), ); + alias_doc += + format!("\n\nFor information about available fields see [`mod@{mod_ty}`] module") + .as_str(); let mut out = TokenStream::new(); out.extend(quote! { #[doc = #alias_doc] - pub type #name_constant_case = crate::Reg<#name_snake_case::#name_constant_case_spec>; + #doc_alias + pub type #reg_ty = crate::Reg<#mod_ty::#regspec_ty>; }); - let mod_items = render_register_mod( - register, - access, - &path.new_register(®ister.name), - index, - config, - )?; + let mod_items = render_register_mod(register, access, &rpath, index, config)?; out.extend(quote! { #[doc = #description] - pub mod #name_snake_case { + pub mod #mod_ty { #mod_items } }); @@ -94,21 +144,115 @@ pub fn render( } } +fn read_action_docs(can_read: bool, read_action: Option) -> String { + let mut doc = String::new(); + if can_read { + if let Some(action) = read_action { + doc.push_str("\n\n
"); + doc.push_str(match action { + ReadAction::Clear => "The register is cleared (set to zero) following a read operation.", + ReadAction::Set => "The register is set (set to ones) following a read operation.", + ReadAction::Modify => "The register is modified in some way after a read operation.", + ReadAction::ModifyExternal => "One or more dependent resources other than the current register are immediately affected by a read operation.", + }); + doc.push_str("
"); + } + } + doc +} + +#[allow(clippy::too_many_arguments)] +fn api_docs( + can_read: bool, + can_write: bool, + can_reset: bool, + module: &Ident, + inmodule: bool, + register: &Register, + rpath: &RegisterPath, + config: &Config, +) -> Result { + fn method(s: &str) -> String { + format!("[`{s}`](crate::Reg::{s})") + } + + let mut doc = String::from("\n\n"); + + if can_read { + write!( + doc, + "You can {} this register and get [`{module}::R`]{}. ", + method("read"), + if inmodule { "(R)" } else { "" }, + )?; + } + + if can_write { + let mut methods = Vec::new(); + if can_reset { + methods.push("reset"); + methods.push("write"); + } + methods.push("write_with_zero"); + write!( + doc, + "You can {} this register using [`{module}::W`]{}. ", + methods + .iter() + .map(|m| method(m)) + .collect::>() + .join(", "), + if inmodule { "(W)" } else { "" }, + )?; + } + + if can_read && can_write { + write!(doc, "You can also {} this register. ", method("modify"))?; + } + + doc.push_str("See [API](https://docs.rs/svd2rust/#read--modify--write-api)."); + + if let Some(url) = config.settings.html_url.as_ref() { + let first_idx = if let Register::Array(_, dim) = ®ister { + dim.indexes().next() + } else { + None + }; + let rname = if let Some(idx) = first_idx { + let idx = format!("[{idx}]"); + rpath.name.replace("[%s]", &idx).replace("%s", &idx) + } else { + rpath.name.clone() + }; + // TODO: support html_urls for registers in cluster + if rpath.block.path.is_empty() { + doc.push_str(&format!( + "\n\nSee register [structure]({url}#{}:{})", + rpath.peripheral(), + rname + )); + } + } + + Ok(doc) +} + pub fn render_register_mod( register: &Register, access: Access, - path: &RegisterPath, + rpath: &RegisterPath, index: &Index, config: &Config, ) -> Result { let properties = ®ister.properties; let name = util::name_of(register, config.ignore_groups); + let rname = ®ister.name; let span = Span::call_site(); - let name_constant_case_spec = format!("{name}_SPEC").to_constant_case_ident(span); - let name_snake_case = name.to_snake_case_ident(span); + let regspec_ty = regspec(&name, config, span); + let mod_ty = ident(&name, config, "register_mod", span); let rsize = properties .size - .ok_or_else(|| anyhow!("Register {} has no `size` field", register.name))?; + .ok_or_else(|| anyhow!("Register {rname} has no `size` field"))?; let rsize = if rsize < 8 { 8 } else if rsize.is_power_of_two() { @@ -117,110 +261,44 @@ pub fn render_register_mod( rsize.next_power_of_two() }; let rty = rsize.to_ty()?; - let description = util::escape_brackets( - util::respace(®ister.description.clone().unwrap_or_else(|| { - warn!("Missing description for register {}", register.name); - Default::default() - })) - .as_ref(), - ); + let description = util::respace(register.description.as_deref().unwrap_or_else(|| { + warn!("Missing description for register {rname}"); + "" + })); + let description = util::escape_special_chars(&description); let mut mod_items = TokenStream::new(); - let mut r_impl_items = TokenStream::new(); - let mut w_impl_items = TokenStream::new(); - let mut methods = vec![]; let can_read = access.can_read(); let can_write = access.can_write(); let can_reset = properties.reset_value.is_some(); if can_read { - let desc = format!("Register `{}` reader", register.name); - let derive = if config.derive_more { - Some(quote! { #[derive(derive_more::Deref, derive_more::From)] }) - } else { - None - }; + let desc = format!("Register `{rname}` reader"); mod_items.extend(quote! { #[doc = #desc] - #derive - pub struct R(crate::R<#name_constant_case_spec>); + pub type R = crate::R<#regspec_ty>; }); - - if !config.derive_more { - mod_items.extend(quote! { - impl core::ops::Deref for R { - type Target = crate::R<#name_constant_case_spec>; - - #[inline(always)] - fn deref(&self) -> &Self::Target { - &self.0 - } - } - - impl From> for R { - #[inline(always)] - fn from(reader: crate::R<#name_constant_case_spec>) -> Self { - R(reader) - } - } - }); - } - methods.push("read"); } if can_write { - let desc = format!("Register `{}` writer", register.name); - let derive = if config.derive_more { - Some(quote! { #[derive(derive_more::Deref, derive_more::DerefMut, derive_more::From)] }) - } else { - None - }; + let desc = format!("Register `{rname}` writer"); mod_items.extend(quote! { #[doc = #desc] - #derive - pub struct W(crate::W<#name_constant_case_spec>); + pub type W = crate::W<#regspec_ty>; }); - - if !config.derive_more { - mod_items.extend(quote! { - impl core::ops::Deref for W { - type Target = crate::W<#name_constant_case_spec>; - - #[inline(always)] - fn deref(&self) -> &Self::Target { - &self.0 - } - } - - impl core::ops::DerefMut for W { - #[inline(always)] - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } - } - - impl From> for W { - #[inline(always)] - fn from(writer: crate::W<#name_constant_case_spec>) -> Self { - W(writer) - } - } - }); - } - methods.push("write_with_zero"); - if can_reset { - methods.push("reset"); - methods.push("write"); - } } - if can_read && can_write { - methods.push("modify"); - } + let mut r_impl_items = TokenStream::new(); + let mut r_debug_impl = TokenStream::new(); + let mut w_impl_items = TokenStream::new(); + let mut zero_to_modify_fields_bitmap = 0; + let mut one_to_modify_fields_bitmap = 0; - let mut zero_to_modify_fields_bitmap: u64 = 0; - let mut one_to_modify_fields_bitmap: u64 = 0; + let debug_feature = config + .impl_debug_feature + .as_ref() + .map(|feature| quote!(#[cfg(feature=#feature)])); if let Some(cur_fields) = register.fields.as_ref() { // filter out all reserved fields, as we should not generate code for @@ -231,46 +309,94 @@ pub fn render_register_mod( .collect(); if !cur_fields.is_empty() { - fields( + if config.impl_debug { + r_debug_impl.extend(render_register_mod_debug( + register, + &access, + &cur_fields, + config, + )) + } + + ( + r_impl_items, + w_impl_items, + zero_to_modify_fields_bitmap, + one_to_modify_fields_bitmap, + ) = fields( cur_fields, - register, - path, - index, - &name_constant_case_spec, - &rty, + ®spec_ty, + register.modified_write_values, access, properties, &mut mod_items, - &mut r_impl_items, - &mut w_impl_items, - &mut zero_to_modify_fields_bitmap, - &mut one_to_modify_fields_bitmap, + rpath, + index, config, )?; } + } else if !access.can_read() || register.read_action.is_some() { + r_debug_impl.extend(quote! { + #debug_feature + impl core::fmt::Debug for crate::generic::Reg<#regspec_ty> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "(not readable)") + } + } + }); + } else { + // no register fields are defined so implement Debug to get entire register value + r_debug_impl.extend(quote! { + #debug_feature + impl core::fmt::Debug for R { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, "{}", self.bits()) + } + } + }); } - let open = Punct::new('{', Spacing::Alone); - let close = Punct::new('}', Spacing::Alone); - if can_read && !r_impl_items.is_empty() { - mod_items.extend(quote! { - impl R #open #r_impl_items #close - }); + mod_items.extend(quote! { impl R { #r_impl_items }}); + } + if !r_debug_impl.is_empty() { + mod_items.extend(quote! { #r_debug_impl }); } if can_write { mod_items.extend(quote! { - impl W #open + impl W { #w_impl_items } }); + } - mod_items.extend(w_impl_items); + let doc = format!( + "{description}{}{}", + api_docs(can_read, can_write, can_reset, &mod_ty, true, register, rpath, config)?, + read_action_docs(access.can_read(), register.read_action), + ); + + mod_items.extend(quote! { + #[doc = #doc] + pub struct #regspec_ty; + impl crate::RegisterSpec for #regspec_ty { + type Ux = #rty; + } + }); + + if can_read { + let doc = format!("`read()` method returns [`{mod_ty}::R`](R) reader structure",); + mod_items.extend(quote! { + #[doc = #doc] + impl crate::Readable for #regspec_ty {} + }); + } + if can_write { // the writer can be safe if: // * there is a single field that covers the entire register // * that field can represent all values // * the write constraints of the register allow full range of values - let can_write_safe = !unsafety( + let safe_ty = if let Safety::Safe = Safety::get( register .fields .as_ref() @@ -278,118 +404,226 @@ pub fn render_register_mod( .and_then(|field| field.write_constraint) .as_ref(), rsize, - ) || !unsafety(register.write_constraint.as_ref(), rsize); - - if can_write_safe { - mod_items.extend(quote! { - #[doc = "Writes raw bits to the register."] - #[inline(always)] - pub fn bits(&mut self, bits: #rty) -> &mut Self { - unsafe { self.0.bits(bits) }; - self - } - }); + ) { + Safety::Safe + } else if let Safety::Safe = Safety::get(register.write_constraint.as_ref(), rsize) { + Safety::Safe } else { - mod_items.extend(quote! { - #[doc = "Writes raw bits to the register."] - #[inline(always)] - pub unsafe fn bits(&mut self, bits: #rty) -> &mut Self { - self.0.bits(bits); - self - } - }); - } + Safety::Unsafe + }; + let safe_ty = safe_ty.ident(rsize); - close.to_tokens(&mut mod_items); - } + let doc = format!("`write(|w| ..)` method takes [`{mod_ty}::W`](W) writer structure",); - let methods = methods - .iter() - .map(|s| format!("[`{0}`](crate::generic::Reg::{0})", s)) - .collect::>(); - let mut doc = format!("{description}\n\nThis register you can {}. See [API](https://docs.rs/svd2rust/#read--modify--write-api).", methods.join(", ")); + let zero_to_modify_fields_bitmap = util::hex_nonzero(zero_to_modify_fields_bitmap) + .map(|bm| quote!(const ZERO_TO_MODIFY_FIELDS_BITMAP: #rty = #bm;)); + let one_to_modify_fields_bitmap = util::hex_nonzero(one_to_modify_fields_bitmap) + .map(|bm| quote!(const ONE_TO_MODIFY_FIELDS_BITMAP: #rty = #bm;)); - if name_snake_case != "cfg" { - doc += format!( - "\n\nFor information about available fields see [{name_snake_case}](index.html) module" - ) - .as_str(); + mod_items.extend(quote! { + #[doc = #doc] + impl crate::Writable for #regspec_ty { + type Safety = crate::#safe_ty; + #zero_to_modify_fields_bitmap + #one_to_modify_fields_bitmap + } + }); } + if let Some(rv) = properties.reset_value.map(util::hex_nonzero) { + let doc = if let Some(rv) = &rv { + format!("`reset()` method sets {} to value {rv}", register.name) + } else { + format!("`reset()` method sets {} to value 0", register.name) + }; + let rv = rv.map(|rv| quote!(const RESET_VALUE: #rty = #rv;)); + mod_items.extend(quote! { + #[doc = #doc] + impl crate::Resettable for #regspec_ty { + #rv + } + }); + } + Ok(mod_items) +} - if can_read { - if let Some(action) = register.read_action { - doc += match action { - ReadAction::Clear => "\n\nThe register is **cleared** (set to zero) following a read operation.", - ReadAction::Set => "\n\nThe register is **set** (set to ones) following a read operation.", - ReadAction::Modify => "\n\nThe register is **modified** in some way after a read operation.", - ReadAction::ModifyExternal => "\n\nOne or more dependent resources other than the current register are immediately affected by a read operation.", +fn render_register_mod_debug( + register: &Register, + access: &Access, + cur_fields: &[&Field], + config: &Config, +) -> Result { + let name = util::name_of(register, config.ignore_groups); + let span = Span::call_site(); + let regspec_ty = regspec(&name, config, span); + let mut r_debug_impl = TokenStream::new(); + let debug_feature = config + .impl_debug_feature + .as_ref() + .map(|feature| quote!(#[cfg(feature=#feature)])); + + // implement Debug for register readable fields that have no read side effects + if access.can_read() && register.read_action.is_none() { + r_debug_impl.extend(quote! { + #debug_feature + impl core::fmt::Debug for R + }); + let mut fmt_outer_impl = TokenStream::new(); + fmt_outer_impl.extend(quote! { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result + }); + let mut fmt_inner_impl = TokenStream::new(); + fmt_inner_impl.extend(quote! { + f.debug_struct(#name) + }); + for &f in cur_fields.iter() { + let field_access = match &f.access { + Some(a) => a, + None => access, }; + log::debug!("register={} field={}", name, f.name); + if field_access.can_read() && f.read_action.is_none() { + if let Field::Array(_, de) = &f { + for suffix in de.indexes() { + let f_name_n = field_accessor(&f.name.expand_dim(&suffix), config, span); + let f_name_n_s = format!("{f_name_n}"); + fmt_inner_impl.extend(quote! { + .field(#f_name_n_s, &self.#f_name_n()) + }); + } + } else { + let f_name = f.name.remove_dim(); + let f_name = field_accessor(&f_name, config, span); + let f_name_s = format!("{f_name}"); + fmt_inner_impl.extend(quote! { + .field(#f_name_s, &self.#f_name()) + }); + } + } } + fmt_inner_impl.extend(quote! { + .finish() + }); + let fmt_inner_group = Group::new(Delimiter::Brace, fmt_inner_impl); + fmt_outer_impl.extend(quote! { #fmt_inner_group }); + let fmt_outer_group = Group::new(Delimiter::Brace, fmt_outer_impl); + r_debug_impl.extend(quote! { #fmt_outer_group }); + } else if !access.can_read() || register.read_action.is_some() { + r_debug_impl.extend(quote! { + #debug_feature + impl core::fmt::Debug for crate::generic::Reg<#regspec_ty> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "(not readable)") + } + } + }); + } else { + warn!("not implementing debug for {name}"); } + Ok(r_debug_impl) +} - mod_items.extend(quote! { - #[doc = #doc] - pub struct #name_constant_case_spec; +#[derive(Clone, Copy, Debug)] +pub enum EV<'a> { + New(&'a EnumeratedValues), + Derived(&'a EnumeratedValues, &'a EnumPath), +} - impl crate::RegisterSpec for #name_constant_case_spec { - type Ux = #rty; +impl EV<'_> { + fn values(&self) -> &EnumeratedValues { + match self { + Self::New(e) | Self::Derived(e, _) => e, } - }); + } +} - if can_read { - let doc = format!("`read()` method returns [{name_snake_case}::R](R) reader structure",); - mod_items.extend(quote! { - #[doc = #doc] - impl crate::Readable for #name_constant_case_spec { - type Reader = R; - } - }); +impl<'a> From<&'a (EnumeratedValues, Option)> for EV<'a> { + fn from(value: &'a (EnumeratedValues, Option)) -> Self { + match value.1.as_ref() { + Some(base) => Self::Derived(&value.0, base), + None => Self::New(&value.0), + } } - if can_write { - let doc = - format!("`write(|w| ..)` method takes [{name_snake_case}::W](W) writer structure",); +} - let zero_to_modify_fields_bitmap = util::hex(zero_to_modify_fields_bitmap); - let one_to_modify_fields_bitmap = util::hex(one_to_modify_fields_bitmap); +#[derive(Clone, Copy, Debug)] +pub enum RWEnum<'a> { + ReadWriteCommon(EV<'a>), + ReadWrite(ReadEnum<'a>, WriteEnum<'a>), + Read(ReadEnum<'a>), + Write(WriteEnum<'a>), +} - mod_items.extend(quote! { - #[doc = #doc] - impl crate::Writable for #name_constant_case_spec { - type Writer = W; - const ZERO_TO_MODIFY_FIELDS_BITMAP: Self::Ux = #zero_to_modify_fields_bitmap; - const ONE_TO_MODIFY_FIELDS_BITMAP: Self::Ux = #one_to_modify_fields_bitmap; - } - }); +#[derive(Clone, Copy, Debug)] +pub enum ReadEnum<'a> { + Enum(EV<'a>), + Raw, +} + +#[derive(Clone, Copy, Debug)] +pub enum WriteEnum<'a> { + Enum(EV<'a>), + Raw, +} + +impl<'a> RWEnum<'a> { + pub fn different_enums(&self) -> bool { + matches!(self, Self::ReadWrite(ReadEnum::Enum(_), WriteEnum::Enum(_))) } - if let Some(rv) = properties.reset_value.map(util::hex) { - let doc = format!("`reset()` method sets {} to value {rv}", register.name); - mod_items.extend(quote! { - #[doc = #doc] - impl crate::Resettable for #name_constant_case_spec { - const RESET_VALUE: Self::Ux = #rv; - } - }); + pub fn read_write(&self) -> bool { + matches!(self, Self::ReadWriteCommon(_) | Self::ReadWrite(_, _)) + } + pub fn read_only(&self) -> bool { + matches!(self, Self::Read(_)) + } + pub fn can_read(&self) -> bool { + self.read_write() || self.read_only() + } + pub fn write_only(&self) -> bool { + matches!(self, Self::Write(_)) + } + pub fn can_write(&self) -> bool { + self.read_write() || self.write_only() + } + pub fn read_enum(&self) -> Option> { + match self { + Self::ReadWriteCommon(e) + | Self::ReadWrite(ReadEnum::Enum(e), _) + | Self::Read(ReadEnum::Enum(e)) => Some(*e), + _ => None, + } + } + pub fn write_enum(&self) -> Option> { + match self { + Self::ReadWriteCommon(e) + | Self::ReadWrite(_, WriteEnum::Enum(e)) + | Self::Write(WriteEnum::Enum(e)) => Some(*e), + _ => None, + } + } + pub fn generate_write_enum(&self) -> bool { + matches!( + self, + Self::ReadWrite(_, WriteEnum::Enum(_)) | Self::Write(WriteEnum::Enum(_)) + ) } - Ok(mod_items) } #[allow(clippy::too_many_arguments)] pub fn fields( mut fields: Vec<&Field>, - register: &Register, - rpath: &RegisterPath, - index: &Index, - name_constant_case_spec: &Ident, - rty: &Ident, + regspec_ty: &Ident, + rmwv: Option, access: Access, properties: &RegisterProperties, mod_items: &mut TokenStream, - r_impl_items: &mut TokenStream, - w_impl_items: &mut TokenStream, - zero_to_modify_fields_bitmap: &mut u64, - one_to_modify_fields_bitmap: &mut u64, + rpath: &RegisterPath, + index: &Index, config: &Config, -) -> Result<()> { +) -> Result<(TokenStream, TokenStream, u64, u64)> { + let mut r_impl_items = TokenStream::new(); + let mut w_impl_items = TokenStream::new(); + let mut zero_to_modify_fields_bitmap = 0u64; + let mut one_to_modify_fields_bitmap = 0u64; let span = Span::call_site(); let can_read = access.can_read(); let can_write = access.can_write(); @@ -398,20 +632,20 @@ pub fn fields( // Hack for #625 let mut enum_derives = HashSet::new(); + let mut read_enum_derives = HashSet::new(); + let mut write_enum_derives = HashSet::new(); let mut reader_derives = HashSet::new(); - let mut writer_enum_derives = HashSet::new(); let mut writer_derives = HashSet::new(); // TODO enumeratedValues let inline = quote! { #[inline(always)] }; for &f in fields.iter() { let mut f = f.clone(); - let mut fpath = None; - let dpath = f.derived_from.take(); - if let Some(dpath) = dpath { - fpath = derive_field(&mut f, &dpath, rpath, index)?; + let mut fdpath = None; + if let Some(dpath) = f.derived_from.take() { + fdpath = derive_field(&mut f, &dpath, rpath, index)?; } - let fpath = fpath.unwrap_or_else(|| rpath.new_field(&f.name)); + let fpath = rpath.new_field(&f.name); // TODO(AJM) - do we need to do anything with this range type? let BitRange { offset, width, .. } = f.bit_range; @@ -419,11 +653,25 @@ pub fn fields( return Err(anyhow!("incorrect field {}", f.name)); } - let name = util::replace_suffix(&f.name, ""); - let name_snake_case = name.to_snake_case_ident(span); - let name_constant_case = name.to_sanitized_constant_case(); + let name = f.name.remove_dim(); + let name_snake_case = field_accessor( + if let Field::Array( + _, + DimElement { + dim_name: Some(dim_name), + .. + }, + ) = &f + { + dim_name + } else { + &name + }, + config, + span, + ); let description_raw = f.description.as_deref().unwrap_or(""); // raw description, if absent using empty string - let description = util::respace(&util::escape_brackets(description_raw)); + let description = util::respace(&util::escape_special_chars(description_raw)); let can_read = can_read && (f.access != Some(Access::WriteOnly)) @@ -436,15 +684,11 @@ pub fn fields( let rv = properties.reset_value.map(|rv| (rv >> offset) & mask); let fty = width.to_ty()?; - let use_mask; - let use_cast; - if let Some(size) = properties.size { + let (use_cast, use_mask) = if let Some(size) = properties.size { let size = size.to_ty_width()?; - use_cast = size != width.to_ty_width()?; - use_mask = size != width; + (size != width.to_ty_width()?, size != width) } else { - use_cast = true; - use_mask = true; + (true, true) }; let mut lookup_results = Vec::new(); @@ -453,22 +697,55 @@ pub fn fields( let dpath = ev.derived_from.take(); if let Some(dpath) = dpath { epath = Some(derive_enumerated_values(&mut ev, &dpath, &fpath, index)?); - } - // TODO: remove this hack - if let Some(epath) = epath.as_ref() { - ev = (*index.evs.get(epath).unwrap()).clone(); + // TODO: remove this hack + if let Some(epath) = epath.as_ref() { + ev = (*index.evs.get(epath).unwrap()).clone(); + } + } else if let Some(path) = fdpath.as_ref() { + epath = Some( + path.new_enum( + ev.name + .clone() + .unwrap_or_else(|| path.name.remove_dim().into()), + ), + ); } lookup_results.push((ev, epath)); } - let mut evs_r = None; + let rwenum = match ( + can_read, + lookup_filter(&lookup_results, Usage::Read), + can_write, + lookup_filter(&lookup_results, Usage::Write), + ) { + (true, Some(e1), true, Some(e2)) if e1.0 == e2.0 => RWEnum::ReadWriteCommon(e1.into()), + (true, Some(e1), true, Some(e2)) => { + RWEnum::ReadWrite(ReadEnum::Enum(e1.into()), WriteEnum::Enum(e2.into())) + } + (true, Some(e), true, None) => { + RWEnum::ReadWrite(ReadEnum::Enum(e.into()), WriteEnum::Raw) + } + (true, None, true, Some(e)) => { + RWEnum::ReadWrite(ReadEnum::Raw, WriteEnum::Enum(e.into())) + } + (true, Some(e), false, _) => RWEnum::Read(ReadEnum::Enum(e.into())), + (true, None, false, _) => RWEnum::Read(ReadEnum::Raw), + (false, _, true, Some(e)) => RWEnum::Write(WriteEnum::Enum(e.into())), + (false, _, true, None) => RWEnum::Write(WriteEnum::Raw), + (true, None, true, None) => RWEnum::ReadWrite(ReadEnum::Raw, WriteEnum::Raw), + (false, _, false, _) => { + return Err(anyhow!("Field {fpath} is not writtable or readable")) + } + }; let brief_suffix = if let Field::Array(_, de) = &f { if let Some(range) = de.indexes_as_range() { - format!("[{}-{}]", *range.start(), *range.end()) + let (start, end) = range.into_inner(); + format!("({start}-{end})") } else { let suffixes: Vec<_> = de.indexes().collect(); - format!("[{}]", suffixes.join(",")) + format!("({})", suffixes.join(",")) } } else { String::new() @@ -476,270 +753,314 @@ pub fn fields( // If this field can be read, generate read proxy structure and value structure. if can_read { - let cast = if width == 1 { - quote! { != 0 } - } else { - quote! { as #fty } - }; - let value = if offset != 0 { - let offset = &util::unsuffixed(offset); - quote! { - ((self.bits >> #offset) & #hexmask) #cast - } - } else if use_cast { - quote! { - (self.bits & #hexmask) #cast - } - } else if use_mask { - quote! { - self.bits & #hexmask - } - } else { - quote! { - self.bits - } - }; - - // get a brief description for this field - // the suffix string from field name is removed in brief description. - let field_reader_brief = format!("Field `{name}{brief_suffix}` reader - {description}"); - - // get the type of value structure. It can be generated from either name field - // in enumeratedValues if it's an enumeration, or from field name directly if it's not. - let value_read_ty = if let Some((evs, _)) = lookup_filter(&lookup_results, Usage::Read) - { - if let Some(enum_name) = &evs.name { - format!("{enum_name}_A").to_constant_case_ident(span) - } else { - // derived_field_value_read_ty - Ident::new(&format!("{name_constant_case}_A"), span) - } - } else { - // raw_field_value_read_ty - fty.clone() - }; - - // name of read proxy type - let reader_ty = Ident::new(&(name_constant_case.clone() + "_R"), span); - - // if it's enumeratedValues and it's derived from base, don't derive the read proxy - // as the base has already dealt with this; - // if it's enumeratedValues but not derived from base, derive the reader from - // information in enumeratedValues; - // if it's not enumeratedValues, always derive the read proxy as we do not need to re-export - // it again from BitReader or FieldReader. - let should_derive_reader = match lookup_filter(&lookup_results, Usage::Read) { - Some((_evs, Some(_base))) => false, - Some((_evs, None)) => true, - None => true, - }; - - // derive the read proxy structure if necessary. - if should_derive_reader { - let reader = if width == 1 { - quote! { crate::BitReader<#value_read_ty> } - } else { - quote! { crate::FieldReader<#fty, #value_read_ty> } - }; - let mut readerdoc = field_reader_brief.clone(); - if let Some(action) = f.read_action { - readerdoc += match action { - ReadAction::Clear => "\n\nThe field is **cleared** (set to zero) following a read operation.", - ReadAction::Set => "\n\nThe field is **set** (set to ones) following a read operation.", - ReadAction::Modify => "\n\nThe field is **modified** in some way after a read operation.", - ReadAction::ModifyExternal => "\n\nOne or more dependent resources other than the current field are immediately affected by a read operation.", - }; - } - mod_items.extend(quote! { - #[doc = #readerdoc] - pub type #reader_ty = #reader; - }); - } - // collect information on items in enumeration to generate it later. let mut enum_items = TokenStream::new(); // if this is an enumeratedValues not derived from base, generate the enum structure // and implement functions for each value in enumeration. - if let Some((evs, None)) = lookup_filter(&lookup_results, Usage::Read) { - // we have enumeration for read, record this. If the enumeration for write operation - // later on is the same as the read enumeration, we reuse and do not generate again. - evs_r = Some(evs); - - // do we have finite definition of this enumeration in svd? If not, the later code would - // return an Option when the value read from field does not match any defined values. - let has_reserved_variant = evs.values.len() != (1 << width); - // parse enum variants from enumeratedValues svd record - let variants = Variant::from_enumerated_values(evs, config.pascal_enum_values)?; - - // if there's no variant defined in enumeratedValues, generate enumeratedValues with new-type - // wrapper struct, and generate From conversation only. - // else, generate enumeratedValues into a Rust enum with functions for each variant. - if variants.is_empty() { - // generate struct VALUE_READ_TY_A(fty) and From for VALUE_READ_TY_A. - add_with_no_variants(mod_items, &value_read_ty, &fty, &description, rv); + let value_read_ty = if let Some(ev) = rwenum.read_enum() { + let derives; + let fmt; + if rwenum.different_enums() { + derives = &mut read_enum_derives; + fmt = "enum_read_name"; } else { - // generate enum VALUE_READ_TY_A { ... each variants ... } and and From for VALUE_READ_TY_A. - add_from_variants(mod_items, &variants, &value_read_ty, &fty, &description, rv); - - // prepare code for each match arm. If we have reserved variant, the match operation would - // return an Option, thus we wrap the return value with Some. - let mut arms = TokenStream::new(); - for v in variants.iter().map(|v| { - let i = util::unsuffixed_or_bool(v.value, width); - let pc = &v.pc; - - if has_reserved_variant { - quote! { #i => Some(#value_read_ty::#pc), } - } else { - quote! { #i => #value_read_ty::#pc, } + derives = &mut enum_derives; + fmt = "enum_name"; + }; + // get the type of value structure. It can be generated from either name field + // in enumeratedValues if it's an enumeration, or from field name directly if it's not. + let value_read_ty = ident( + if config.field_names_for_enums { + &name + } else { + ev.values().name.as_deref().unwrap_or(&name) + }, + config, + fmt, + span, + ); + + match ev { + EV::New(evs) => { + // parse enum variants from enumeratedValues svd record + let mut variants = Variant::from_enumerated_values(evs, config)?; + + let map = enums_to_map(evs); + let mut def = evs + .default_value() + .and_then(|def| { + minimal_hole(&map, width) + .map(|v| Variant::from_value(v, def, config)) + }) + .transpose()?; + if variants.len() == 1 << width { + def = None; + } else if variants.len() == (1 << width) - 1 { + if let Some(def) = def.take() { + variants.push(def); + } } - }) { - arms.extend(v); - } - // if we have reserved variant, for all values other than defined we return None. - // if svd suggests it only would return defined variants but FieldReader has - // other values, it's regarded as unreachable and we enter unreachable! macro. - // This situation is rare and only exists if unsafe code casts any illegal value - // into a FieldReader structure. - if has_reserved_variant { - arms.extend(quote! { - _ => None, - }); - } else if 1 << width.to_ty_width()? != variants.len() { - arms.extend(quote! { - _ => unreachable!(), - }); - } + // if there's no variant defined in enumeratedValues, generate enumeratedValues with new-type + // wrapper struct, and generate From conversation only. + // else, generate enumeratedValues into a Rust enum with functions for each variant. + if variants.is_empty() && def.is_none() { + // generate struct VALUE_READ_TY_A(fty) and From for VALUE_READ_TY_A. + add_with_no_variants( + mod_items, + &value_read_ty, + &fty, + &description, + rv, + config, + ); + } else { + // do we have finite definition of this enumeration in svd? If not, the later code would + // return an Option when the value read from field does not match any defined values. + let has_reserved_variant; + + // generate enum VALUE_READ_TY_A { ... each variants ... } and and From for VALUE_READ_TY_A. + if let Some(def) = def.as_ref() { + add_from_variants( + mod_items, + variants.iter().chain(std::iter::once(def)), + &value_read_ty, + &fty, + &description, + rv, + config, + ); + has_reserved_variant = false; + } else { + add_from_variants( + mod_items, + variants.iter(), + &value_read_ty, + &fty, + &description, + rv, + config, + ); + has_reserved_variant = evs.values.len() != (1 << width); + } - // prepare the `variant` function. This function would return field value in - // Rust structure; if we have reserved variant we return by Option. - if has_reserved_variant { - enum_items.extend(quote! { - #[doc = "Get enumerated values variant"] - #inline - pub fn variant(&self) -> Option<#value_read_ty> { - match self.bits { - #arms + // prepare code for each match arm. If we have reserved variant, the match operation would + // return an Option, thus we wrap the return value with Some. + let mut arms = TokenStream::new(); + for v in variants.iter().map(|v| { + let i = util::unsuffixed_or_bool(v.value, width); + let pc = &v.pc; + + if has_reserved_variant { + quote! { #i => Some(#value_read_ty::#pc), } + } else { + quote! { #i => #value_read_ty::#pc, } } + }) { + arms.extend(v); } - }); - } else { - enum_items.extend(quote! { - #[doc = "Get enumerated values variant"] - #inline - pub fn variant(&self) -> #value_read_ty { - match self.bits { - #arms - } - }}); - } - // for each variant defined, we generate an `is_variant` function. - for v in &variants { - let pc = &v.pc; - let sc = &v.nksc; + // if we have reserved variant, for all values other than defined we return None. + // if svd suggests it only would return defined variants but FieldReader has + // other values, it's regarded as unreachable and we enter unreachable! macro. + // This situation is rare and only exists if unsafe code casts any illegal value + // into a FieldReader structure. + if has_reserved_variant { + arms.extend(quote! { + _ => None, + }); + } else if let Some(v) = def.as_ref() { + let pc = &v.pc; + arms.extend(quote! { + _ => #value_read_ty::#pc, + }); + } else if 1 << width.to_ty_width()? != variants.len() { + arms.extend(quote! { + _ => unreachable!(), + }); + } - let is_variant = Ident::new( - &if sc.to_string().starts_with('_') { - format!("is{sc}") + // prepare the `variant` function. This function would return field value in + // Rust structure; if we have reserved variant we return by Option. + let ret_ty = if has_reserved_variant { + quote!(Option<#value_read_ty>) } else { - format!("is_{sc}") - }, - span, - ); - - let doc = format!("Checks if the value of the field is `{pc}`"); - enum_items.extend(quote! { - #[doc = #doc] - #inline - pub fn #is_variant(&self) -> bool { - *self == #value_read_ty::#pc + quote!(#value_read_ty) + }; + enum_items.extend(quote! { + #[doc = "Get enumerated values variant"] + #inline + pub const fn variant(&self) -> #ret_ty { + match self.bits { + #arms + } + } + }); + + // for each variant defined, we generate an `is_variant` function. + for v in &variants { + let pc = &v.pc; + let is_variant = &v.is_sc; + + let doc = util::respace(&v.doc); + let doc = util::escape_special_chars(&doc); + enum_items.extend(quote! { + #[doc = #doc] + #inline + pub fn #is_variant(&self) -> bool { + *self == #value_read_ty::#pc + } + }); } - }); + if let Some(v) = def.as_ref() { + let pc = &v.pc; + let is_variant = &v.is_sc; + + let doc = util::respace(&v.doc); + let doc = util::escape_special_chars(&doc); + enum_items.extend(quote! { + #[doc = #doc] + #inline + pub fn #is_variant(&self) -> bool { + matches!(self.variant(), #value_read_ty::#pc) + } + }); + } + } + } + EV::Derived(_, base) => { + let base_ident = if config.field_names_for_enums { + ident(&base.field().name.remove_dim(), config, fmt, span) + } else { + ident(&base.name, config, fmt, span) + }; + if !derives.contains(&value_read_ty) { + let base_path = base_syn_path(base, &fpath, &base_ident, config)?; + mod_items.extend(quote! { + #[doc = #description] + pub use #base_path as #value_read_ty; + }); + } } } - } + derives.insert(value_read_ty.clone()); + value_read_ty + } else { + // raw_field_value_read_ty + fty.clone() + }; + + // get a brief description for this field + // the suffix string from field name is removed in brief description. + let field_reader_brief = format!("Field `{name}{brief_suffix}` reader - {description}"); - // if this value is derived from a base, generate `pub use` code for each read proxy and value - // if necessary. - if let Some((evs, Some(base))) = lookup_filter(&lookup_results, Usage::Read) { - // preserve value; if read type equals write type, writer would not generate value type again - evs_r = Some(evs); - // generate pub use field_1 reader as field_2 reader - let base_field = util::replace_suffix(&base.field.name, ""); - let base_r = (base_field + "_R").to_constant_case_ident(span); - if !reader_derives.contains(&reader_ty) { - derive_from_base( - mod_items, - base, - &fpath, - &reader_ty, - &base_r, - &field_reader_brief, - )?; - reader_derives.insert(reader_ty.clone()); + // name of read proxy type + let reader_ty = ident(&name, config, "field_reader", span); + + match rwenum.read_enum() { + Some(EV::New(_)) | None => { + // Generate the read proxy structure if necessary. + + let reader = if width == 1 { + if value_read_ty == "bool" { + quote! { crate::BitReader } + } else { + quote! { crate::BitReader<#value_read_ty> } + } + } else if value_read_ty == "u8" { + quote! { crate::FieldReader } + } else { + quote! { crate::FieldReader<#value_read_ty> } + }; + let mut readerdoc = field_reader_brief.clone(); + if let Some(action) = f.read_action { + readerdoc.push_str("\n\n
"); + readerdoc.push_str(match action { + ReadAction::Clear => "The field is cleared (set to zero) following a read operation.", + ReadAction::Set => "The field is set (set to ones) following a read operation.", + ReadAction::Modify => "The field is modified in some way after a read operation.", + ReadAction::ModifyExternal => "One or more dependent resources other than the current field are immediately affected by a read operation.", + }); + readerdoc.push_str("
"); + } + mod_items.extend(quote! { + #[doc = #readerdoc] + pub type #reader_ty = #reader; + }); } - // only pub use enum when base.register != None. if base.register == None, it emits - // pub use enum from same module which is not expected - if base.register() != fpath.register() { - // use the same enum structure name - if !enum_derives.contains(&value_read_ty) { - derive_from_base( - mod_items, - base, - &fpath, - &value_read_ty, - &value_read_ty, - &description, - )?; - enum_derives.insert(value_read_ty.clone()); + Some(EV::Derived(_, base)) => { + // if this value is derived from a base, generate `pub use` code for each read proxy + // and value if necessary. + + // generate pub use field_1 reader as field_2 reader + let base_field = base.field.name.remove_dim(); + let base_r = ident(&base_field, config, "field_reader", span); + if !reader_derives.contains(&reader_ty) { + let base_path = base_syn_path(base, &fpath, &base_r, config)?; + mod_items.extend(quote! { + #[doc = #field_reader_brief] + pub use #base_path as #reader_ty; + }); + reader_derives.insert(reader_ty.clone()); } } } - if let Field::Array(_, de) = &f { + // Generate field reader accessors + let cast = if width == 1 { + quote! { != 0 } + } else { + quote! { as #fty } + }; + + if let Field::Array(f, de) = &f { let increment = de.dim_increment; - let doc = &util::replace_suffix(&description, &brief_suffix); - if let Some(range) = de.indexes_as_range() { - let first = *range.start(); + let doc = description.expand_dim(&brief_suffix); + let first_name = svd::array::names(f, de).next().unwrap(); + let note = format!("
`n` is number of field in register. `n == 0` corresponds to `{first_name}` field.
"); + let offset_calc = calculate_offset(increment, offset, true); + let value = quote! { ((self.bits >> #offset_calc) & #hexmask) #cast }; + let dim = unsuffixed(de.dim); + let name_snake_case_iter = Ident::new(&format!("{name_snake_case}_iter"), span); + r_impl_items.extend(quote! { + #[doc = #doc] + #[doc = ""] + #[doc = #note] + #inline + pub fn #name_snake_case(&self, n: u8) -> #reader_ty { + #[allow(clippy::no_effect)] + [(); #dim][n as usize]; + #reader_ty::new ( #value ) + } + #[doc = "Iterator for array of:"] + #[doc = #doc] + #inline + pub fn #name_snake_case_iter(&self) -> impl Iterator + '_ { + (0..#dim).map(move |n| #reader_ty::new ( #value )) + } + }); - let offset_calc = calculate_offset(first, increment, offset, true); - let value = quote! { ((self.bits >> #offset_calc) & #hexmask) #cast }; - r_impl_items.extend(quote! { - #[doc = #doc] - #inline - pub unsafe fn #name_snake_case(&self, n: u8) -> #reader_ty { - #reader_ty::new ( #value ) - } - }); - } - for (i, suffix) in de.indexes().enumerate() { - let sub_offset = offset + (i as u64) * (increment as u64); + for fi in svd::field::expand(f, de) { + let sub_offset = fi.bit_offset() as u64; let value = if sub_offset != 0 { - let sub_offset = &util::unsuffixed(sub_offset); - quote! { - ((self.bits >> #sub_offset) & #hexmask) #cast - } - } else if use_cast { - quote! { - (self.bits & #hexmask) #cast - } + let sub_offset = &unsuffixed(sub_offset); + quote! { (self.bits >> #sub_offset) } + } else { + quote! { self.bits } + }; + let value = if use_mask && use_cast { + quote! { (#value & #hexmask) #cast } } else if use_mask { - quote! { - self.bits & #hexmask - } + quote! { #value & #hexmask } } else { - quote! { - self.bits - } + value }; - let name_snake_case_n = util::replace_suffix(&f.name, &suffix) - .to_snake_case_ident(Span::call_site()); - let doc = util::replace_suffix( - &description_with_bits(description_raw, sub_offset, width), - &suffix, + let name_snake_case_n = field_accessor(&fi.name, config, span); + let doc = description_with_bits( + fi.description.as_deref().unwrap_or(&fi.name), + sub_offset, + width, ); r_impl_items.extend(quote! { #[doc = #doc] @@ -750,6 +1071,20 @@ pub fn fields( }); } } else { + let value = if offset != 0 { + let offset = &unsuffixed(offset); + quote! { (self.bits >> #offset) } + } else { + quote! { self.bits } + }; + let value = if use_mask && use_cast { + quote! { (#value & #hexmask) #cast } + } else if use_mask { + quote! { #value & #hexmask } + } else { + value + }; + let doc = description_with_bits(description_raw, offset, width); r_impl_items.extend(quote! { #[doc = #doc] @@ -773,294 +1108,379 @@ pub fn fields( // If this field can be written, generate write proxy. Generate write value if it differs from // the read value, or else we reuse read value. if can_write { - let mwv = f - .modified_write_values - .or(register.modified_write_values) - .unwrap_or_default(); - // gets a brief of write proxy - let field_writer_brief = format!("Field `{name}{brief_suffix}` writer - {description}"); + let mut proxy_items = TokenStream::new(); + let mut safety = Safety::get(f.write_constraint.as_ref(), width); - let value_write_ty = - if let Some((evs, _)) = lookup_filter(&lookup_results, Usage::Write) { - let writer_reader_different_enum = evs_r != Some(evs); - let ty_suffix = if writer_reader_different_enum { - "AW" - } else { - "A" - }; - if let Some(enum_name) = &evs.name { - format!("{enum_name}_{ty_suffix}").to_constant_case_ident(span) - } else { - // derived_field_value_write_ty - Ident::new(&format!("{name_constant_case}_{ty_suffix}"), span) - } + // if we writes to enumeratedValues, generate its structure if it differs from read structure. + let value_write_ty = if let Some(ev) = rwenum.write_enum() { + let derives; + let fmt; + if rwenum.different_enums() { + derives = &mut write_enum_derives; + fmt = "enum_write_name"; } else { - // raw_field_value_write_ty - fty.clone() + derives = &mut enum_derives; + fmt = "enum_name"; }; + let value_write_ty = ident( + if config.field_names_for_enums { + &name + } else { + ev.values().name.as_deref().unwrap_or(&name) + }, + config, + fmt, + span, + ); + + match ev { + EV::New(evs) => { + // parse variants from enumeratedValues svd record + let mut variants = Variant::from_enumerated_values(evs, config)?; + let map = enums_to_map(evs); + let mut def = evs + .default_value() + .and_then(|def| { + minimal_hole(&map, width) + .map(|v| Variant::from_value(v, def, config)) + }) + .transpose()?; + // if the write structure is finite, it can be safely written. + if variants.len() == 1 << width { + safety = Safety::Safe; + } else if let Some(def) = def.take() { + variants.push(def); + safety = Safety::Safe; + } - // name of write proxy type - let writer_ty = Ident::new(&(name_constant_case.clone() + "_W"), span); - - let mut proxy_items = TokenStream::new(); - let mut unsafety = unsafety(f.write_constraint.as_ref(), width); - - // if we writes to enumeratedValues, generate its structure if it differs from read structure. - if let Some((evs, None)) = lookup_filter(&lookup_results, Usage::Write) { - // parse variants from enumeratedValues svd record - let variants = Variant::from_enumerated_values(evs, config.pascal_enum_values)?; + // generate write value structure and From conversation if we can't reuse read value structure. + if rwenum.generate_write_enum() { + if variants.is_empty() { + add_with_no_variants( + mod_items, + &value_write_ty, + &fty, + &description, + rv, + config, + ); + } else { + add_from_variants( + mod_items, + variants.iter(), + &value_write_ty, + &fty, + &description, + rv, + config, + ); + } + } - // if the write structure is finite, it can be safely written. - if variants.len() == 1 << width { - unsafety = false; + // for each variant defined, generate a write function to this field. + for v in &variants { + let pc = &v.pc; + let sc = &v.sc; + let doc = util::respace(&v.doc); + let doc = util::escape_special_chars(&doc); + proxy_items.extend(quote! { + #[doc = #doc] + #inline + pub fn #sc(self) -> &'a mut crate::W { + self.variant(#value_write_ty::#pc) + } + }); + } + } + EV::Derived(_, base) => { + let base_ident = if config.field_names_for_enums { + ident(&base.field().name.remove_dim(), config, fmt, span) + } else { + ident(&base.name, config, fmt, span) + }; + if rwenum.generate_write_enum() && !derives.contains(&value_write_ty) { + let base_path = base_syn_path(base, &fpath, &base_ident, config)?; + mod_items.extend(quote! { + #[doc = #description] + pub use #base_path as #value_write_ty; + }); + } + } } + derives.insert(value_write_ty.clone()); + value_write_ty + } else { + // raw_field_value_write_ty + fty.clone() + }; - // does the read and the write value has the same name? If we have the same, - // we can reuse read value type other than generating a new one. - let writer_reader_different_enum = evs_r != Some(evs); + let mwv = f.modified_write_values.or(rmwv).unwrap_or_default(); - // generate write value structure and From conversation if we can't reuse read value structure. - if writer_reader_different_enum { - if variants.is_empty() { - add_with_no_variants(mod_items, &value_write_ty, &fty, &description, rv); - } else { - add_from_variants( - mod_items, - &variants, - &value_write_ty, - &fty, - &description, - rv, - ); - } - } + // gets a brief of write proxy + let field_writer_brief = format!("Field `{name}{brief_suffix}` writer - {description}"); - // for each variant defined, generate a write function to this field. - for v in &variants { - let pc = &v.pc; - let sc = &v.sc; - let doc = util::escape_brackets(&util::respace(&v.doc)); - proxy_items.extend(quote! { - #[doc = #doc] - #inline - pub fn #sc(self) -> &'a mut W { - self.variant(#value_write_ty::#pc) + // name of write proxy type + let writer_ty = ident(&name, config, "field_writer", span); + + // Generate writer structure by type alias to generic write proxy structure. + match rwenum.write_enum() { + Some(EV::New(_)) | None => { + let proxy = if width == 1 { + use ModifiedWriteValues::*; + let wproxy = Ident::new( + match mwv { + Modify | Set | Clear => "BitWriter", + OneToSet => "BitWriter1S", + ZeroToClear => "BitWriter0C", + OneToClear => "BitWriter1C", + ZeroToSet => "BitWriter0S", + OneToToggle => "BitWriter1T", + ZeroToToggle => "BitWriter0T", + }, + span, + ); + if value_write_ty == "bool" { + quote! { crate::#wproxy<'a, REG> } + } else { + quote! { crate::#wproxy<'a, REG, #value_write_ty> } + } + } else { + let wproxy = Ident::new("FieldWriter", span); + let uwidth = &unsuffixed(width); + if value_write_ty == "u8" && safety != Safety::Safe { + quote! { crate::#wproxy<'a, REG, #uwidth> } + } else if safety != Safety::Safe { + quote! { crate::#wproxy<'a, REG, #uwidth, #value_write_ty> } + } else { + let safe_ty = safety.ident(width); + quote! { crate::#wproxy<'a, REG, #uwidth, #value_write_ty, crate::#safe_ty> } } + }; + mod_items.extend(quote! { + #[doc = #field_writer_brief] + pub type #writer_ty<'a, REG> = #proxy; }); } - } - - // derive writer. We derive writer if the write proxy is in current register module, - // or writer in different register have different _SPEC structures - let should_derive_writer = match lookup_filter(&lookup_results, Usage::Write) { - Some((_evs, Some(base))) => base.register() != fpath.register(), - Some((_evs, None)) => true, - None => true, - }; - - // derive writer structure by type alias to generic write proxy structure. - if should_derive_writer { - let proxy = if width == 1 { - use ModifiedWriteValues::*; - let wproxy = Ident::new( - match mwv { - Modify | Set | Clear => "BitWriter", - OneToSet => "BitWriter1S", - ZeroToClear => "BitWriter0C", - OneToClear => "BitWriter1C", - ZeroToSet => "BitWriter0C", - OneToToggle => "BitWriter1T", - ZeroToToggle => "BitWriter0T", - }, - span, - ); - quote! { crate::#wproxy<'a, #rty, #name_constant_case_spec, #value_write_ty, O> } - } else { - let wproxy = Ident::new( - if unsafety { - "FieldWriter" - } else { - "FieldWriterSafe" - }, - span, - ); - let width = &util::unsuffixed(width as _); - quote! { crate::#wproxy<'a, #rty, #name_constant_case_spec, #fty, #value_write_ty, #width, O> } - }; - mod_items.extend(quote! { - #[doc = #field_writer_brief] - pub type #writer_ty<'a, const O: u8> = #proxy; - }); + Some(EV::Derived(_, base)) => { + // generate pub use field_1 writer as field_2 writer + let base_field = base.field.name.remove_dim(); + let base_w = ident(&base_field, config, "field_writer", span); + if !writer_derives.contains(&writer_ty) { + let base_path = base_syn_path(base, &fpath, &base_w, config)?; + mod_items.extend(quote! { + #[doc = #field_writer_brief] + pub use #base_path as #writer_ty; + }); + writer_derives.insert(writer_ty.clone()); + } + } } // generate proxy items from collected information if !proxy_items.is_empty() { - mod_items.extend(quote! { - impl<'a, const O: u8> #writer_ty<'a, O> { - #proxy_items - } - }); - } - - if let Some((evs, Some(base))) = lookup_filter(&lookup_results, Usage::Write) { - // if base.register == None, it emits pub use structure from same module. - if base.register() != fpath.register() { - let writer_reader_different_enum = evs_r != Some(evs); - if writer_reader_different_enum { - // use the same enum structure name - if !writer_enum_derives.contains(&value_write_ty) { - derive_from_base( - mod_items, - base, - &fpath, - &value_write_ty, - &value_write_ty, - &description, - )?; - writer_enum_derives.insert(value_write_ty.clone()); + mod_items.extend(if width == 1 { + quote! { + impl<'a, REG> #writer_ty<'a, REG> + where + REG: crate::Writable + crate::RegisterSpec, + { + #proxy_items } } } else { - // if base.register == None, derive write from the same module. This is allowed because both - // the generated and source write proxy are in the same module. - // we never reuse writer for writer in different module does not have the same _SPEC strcuture, - // thus we cannot write to current register using re-exported write proxy. - - // generate pub use field_1 writer as field_2 writer - let base_field = util::replace_suffix(&base.field.name, ""); - let base_w = (base_field + "_W").to_constant_case_ident(span); - if !writer_derives.contains(&writer_ty) { - derive_from_base( - mod_items, - base, - &fpath, - &writer_ty, - &base_w, - &field_writer_brief, - )?; - writer_derives.insert(writer_ty.clone()); + quote! { + impl<'a, REG> #writer_ty<'a, REG> + where + REG: crate::Writable + crate::RegisterSpec, + REG::Ux: From<#fty> + { + #proxy_items + } } - } + }); } - if let Field::Array(_, de) = &f { + // Generate field writer accessors + if let Field::Array(f, de) = &f { let increment = de.dim_increment; - let doc = &util::replace_suffix(&description, &brief_suffix); + let offset_calc = calculate_offset(increment, offset, false); + let doc = &description.expand_dim(&brief_suffix); + let first_name = svd::array::names(f, de).next().unwrap(); + let note = format!("
`n` is number of field in register. `n == 0` corresponds to `{first_name}` field.
"); + let dim = unsuffixed(de.dim); w_impl_items.extend(quote! { #[doc = #doc] + #[doc = ""] + #[doc = #note] #inline - #[must_use] - pub unsafe fn #name_snake_case(&mut self) -> #writer_ty { - #writer_ty::new(self) + pub fn #name_snake_case(&mut self, n: u8) -> #writer_ty<#regspec_ty> { + #[allow(clippy::no_effect)] + [(); #dim][n as usize]; + #writer_ty::new(self, #offset_calc) } }); - for (i, suffix) in de.indexes().enumerate() { - let sub_offset = offset + (i as u64) * (increment as u64); - let name_snake_case_n = &util::replace_suffix(&f.name, &suffix) - .to_snake_case_ident(Span::call_site()); - let doc = util::replace_suffix( - &description_with_bits(description_raw, sub_offset, width), - &suffix, + for fi in svd::field::expand(f, de) { + let sub_offset = fi.bit_offset() as u64; + let name_snake_case_n = field_accessor(&fi.name, config, span); + let doc = description_with_bits( + fi.description.as_deref().unwrap_or(&fi.name), + sub_offset, + width, ); - let sub_offset = util::unsuffixed(sub_offset as u64); + let sub_offset = unsuffixed(sub_offset); w_impl_items.extend(quote! { #[doc = #doc] #inline - #[must_use] - pub fn #name_snake_case_n(&mut self) -> #writer_ty<#sub_offset> { - #writer_ty::new(self) + pub fn #name_snake_case_n(&mut self) -> #writer_ty<#regspec_ty> { + #writer_ty::new(self, #sub_offset) } }); } } else { let doc = description_with_bits(description_raw, offset, width); - let offset = util::unsuffixed(offset as u64); + let offset = unsuffixed(offset); w_impl_items.extend(quote! { #[doc = #doc] #inline - #[must_use] - pub fn #name_snake_case(&mut self) -> #writer_ty<#offset> { - #writer_ty::new(self) + pub fn #name_snake_case(&mut self) -> #writer_ty<#regspec_ty> { + #writer_ty::new(self, #offset) } }); } - let bitmask = (u64::MAX >> (64 - width)) << offset; + + let bitmask = f.bitmask(); use ModifiedWriteValues::*; match mwv { Modify | Set | Clear => {} OneToSet | OneToClear | OneToToggle => { - *one_to_modify_fields_bitmap |= bitmask; + one_to_modify_fields_bitmap |= bitmask; } ZeroToClear | ZeroToSet | ZeroToToggle => { - *zero_to_modify_fields_bitmap |= bitmask; + zero_to_modify_fields_bitmap |= bitmask; } } } } - Ok(()) + Ok(( + r_impl_items, + w_impl_items, + zero_to_modify_fields_bitmap, + one_to_modify_fields_bitmap, + )) +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum Safety { + Unsafe, + Range(WriteConstraintRange), + Safe, } -fn unsafety(write_constraint: Option<&WriteConstraint>, width: u32) -> bool { - match &write_constraint { - Some(&WriteConstraint::Range(range)) - if range.min == 0 && range.max == u64::MAX >> (64 - width) => - { - // the SVD has acknowledged that it's safe to write - // any value that can fit in the field - false +impl Safety { + fn get(write_constraint: Option<&WriteConstraint>, width: u32) -> Self { + match &write_constraint { + Some(&WriteConstraint::Range(range)) + if range.min == 0 && range.max == u64::MAX >> (64 - width) => + { + // the SVD has acknowledged that it's safe to write + // any value that can fit in the field + Self::Safe + } + None if width == 1 => { + // the field is one bit wide, so we assume it's legal to write + // either value into it or it wouldn't exist; despite that + // if a writeConstraint exists then respect it + Self::Safe + } + Some(&WriteConstraint::Range(range)) => Self::Range(range), + _ => Self::Unsafe, } - None if width == 1 => { - // the field is one bit wide, so we assume it's legal to write - // either value into it or it wouldn't exist; despite that - // if a writeConstraint exists then respect it - false + } + fn ident(&self, width: u32) -> TokenStream { + match self { + Self::Safe => quote!(Safe), + Self::Unsafe => quote!(Unsafe), + Self::Range(range) => { + let min = unsuffixed(range.min); + let max = unsuffixed(range.max); + if range.min == 0 { + quote!(RangeTo<#max>) + } else if range.max == u64::MAX >> (64 - width) { + quote!(RangeFrom<#min>) + } else { + quote!(Range<#min, #max>) + } + } } - _ => true, } } struct Variant { doc: String, pc: Ident, - nksc: Ident, + is_sc: Ident, sc: Ident, value: u64, } impl Variant { - fn from_enumerated_values(evs: &EnumeratedValues, pc: bool) -> Result> { - let span = Span::call_site(); + fn from_enumerated_values(evs: &EnumeratedValues, config: &Config) -> Result> { evs.values .iter() // filter out all reserved variants, as we should not // generate code for them - .filter(|field| field.name.to_lowercase() != "reserved" && field.is_default == None) + .filter(|ev| ev.name.to_lowercase() != "reserved" && !ev.is_default()) .map(|ev| { let value = ev .value - .ok_or_else(|| anyhow!("EnumeratedValue {} has no `` field", ev.name))?; - - let nksc = ev.name.to_sanitized_not_keyword_snake_case(); - let sc = util::sanitize_keyword(nksc.clone()); - Ok(Variant { - doc: ev - .description - .clone() - .unwrap_or_else(|| format!("`{value:b}`")), - pc: if pc { - ev.name.to_pascal_case_ident(span) - } else { - ev.name.to_constant_case_ident(span) - }, - nksc: Ident::new(&nksc, span), - sc: Ident::new(&sc, span), - value, - }) + .ok_or_else(|| anyhow!("EnumeratedValue {} has no `` entry", ev.name))?; + Self::from_value(value, ev, config) }) - .collect::>>() + .collect() + } + fn from_value(value: u64, ev: &EnumeratedValue, config: &Config) -> Result { + let span = Span::call_site(); + let case = config.ident_formats.get("enum_value_accessor").unwrap(); + let nksc = case.apply(&ev.name); + let is_sc = Ident::new( + &if nksc.to_string().starts_with('_') { + format!("is{nksc}") + } else { + format!("is_{nksc}") + }, + span, + ); + let sc = case.sanitize(&ev.name); + const INTERNALS: [&str; 7] = [ + "bit", + "bits", + "clear_bit", + "set", + "set_bit", + "variant", + "offset", + ]; + let sc = Ident::new( + &(if INTERNALS.contains(&sc.as_ref()) { + sc + "_" + } else { + sc + }), + span, + ); + Ok(Variant { + doc: ev + .description + .clone() + .unwrap_or_else(|| format!("`{value:b}`")), + pc: ident(&ev.name, config, "enum_value", span), + is_sc, + sc, + value, + }) } } @@ -1070,7 +1490,13 @@ fn add_with_no_variants( fty: &Ident, desc: &str, reset_value: Option, + config: &Config, ) { + let defmt = config + .impl_defmt + .as_ref() + .map(|feature| quote!(#[cfg_attr(feature = #feature, derive(defmt::Format))])); + let cast = if fty == "bool" { quote! { val.0 as u8 != 0 } } else { @@ -1085,6 +1511,7 @@ fn add_with_no_variants( mod_items.extend(quote! { #[doc = #desc] + #defmt #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct #pc(#fty); impl From<#pc> for #fty { @@ -1094,16 +1521,29 @@ fn add_with_no_variants( } } }); + if fty != "bool" { + mod_items.extend(quote! { + impl crate::FieldSpec for #pc { + type Ux = #fty; + } + }); + } } -fn add_from_variants( +fn add_from_variants<'a>( mod_items: &mut TokenStream, - variants: &[Variant], + variants: impl Iterator, pc: &Ident, fty: &Ident, desc: &str, reset_value: Option, + config: &Config, ) { + let defmt = config + .impl_defmt + .as_ref() + .map(|feature| quote!(#[cfg_attr(feature = #feature, derive(defmt::Format))])); + let (repr, cast) = if fty == "bool" { (quote! {}, quote! { variant as u8 != 0 }) } else { @@ -1111,10 +1551,11 @@ fn add_from_variants( }; let mut vars = TokenStream::new(); - for v in variants.iter().map(|v| { - let desc = util::escape_brackets(&util::respace(&format!("{}: {}", v.value, v.doc))); + for v in variants.map(|v| { + let desc = util::respace(&format!("{}: {}", v.value, v.doc)); + let desc = util::escape_special_chars(&desc); let pcv = &v.pc; - let pcval = &util::unsuffixed(v.value); + let pcval = &unsuffixed(v.value); quote! { #[doc = #desc] #pcv = #pcval, @@ -1131,6 +1572,7 @@ fn add_from_variants( mod_items.extend(quote! { #[doc = #desc] + #defmt #[derive(Clone, Copy, Debug, PartialEq, Eq)] #repr pub enum #pc { @@ -1143,33 +1585,27 @@ fn add_from_variants( } } }); + if fty != "bool" { + mod_items.extend(quote! { + impl crate::FieldSpec for #pc { + type Ux = #fty; + } + impl crate::IsEnum for #pc {} + }); + } } -fn calculate_offset( - first: u32, - increment: u32, - offset: u64, - with_parentheses: bool, -) -> TokenStream { - let mut res = if first != 0 { - let first = util::unsuffixed(first as u64); - quote! { n - #first } - } else { - quote! { n } - }; +fn calculate_offset(increment: u32, offset: u64, with_parentheses: bool) -> TokenStream { + let mut res = quote! { n }; if increment != 1 { - let increment = util::unsuffixed(increment as u64); - res = if first != 0 { - quote! { (#res) * #increment } - } else { - quote! { #res * #increment } - }; + let increment = unsuffixed(increment); + res = quote! { #res * #increment }; } if offset != 0 { - let offset = &util::unsuffixed(offset); + let offset = &unsuffixed(offset); res = quote! { #res + #offset }; } - let single_ident = (first == 0) && (increment == 1) && (offset == 0); + let single_ident = (increment == 1) && (offset == 0); if with_parentheses && !single_ident { quote! { (#res) } } else { @@ -1185,38 +1621,37 @@ fn description_with_bits(description: &str, offset: u64, width: u32) -> String { }; if !description.is_empty() { res.push_str(" - "); - res.push_str(&util::respace(&util::escape_brackets(description))); + res.push_str(&util::respace(&util::escape_special_chars(description))); } res } -fn derive_from_base( - mod_items: &mut TokenStream, +fn base_syn_path( base: &EnumPath, - field: &FieldPath, - pc: &Ident, - base_pc: &Ident, - desc: &str, -) -> Result<(), syn::Error> { + fpath: &FieldPath, + base_ident: &Ident, + config: &Config, +) -> Result { let span = Span::call_site(); - let path = if base.register() == field.register() { - ident_to_path(base_pc.clone()) - } else if base.register().block == field.register().block { + let path = if base.register() == fpath.register() { + ident_to_path(base_ident.clone()) + } else if base.register().block == fpath.register().block { let mut segments = Punctuated::new(); segments.push(path_segment(Ident::new("super", span))); - segments.push(path_segment(base.register().name.to_snake_case_ident(span))); - segments.push(path_segment(base_pc.clone())); + segments.push(path_segment(ident( + &base.register().name.remove_dim(), + config, + "register_mod", + span, + ))); + segments.push(path_segment(base_ident.clone())); type_path(segments) } else { - let mut rmod_ = crate::util::register_path_to_ty(base.register(), span); - rmod_.path.segments.push(path_segment(base_pc.clone())); + let mut rmod_ = crate::util::register_path_to_ty(base.register(), config, span); + rmod_.path.segments.push(path_segment(base_ident.clone())); rmod_ }; - mod_items.extend(quote! { - #[doc = #desc] - pub use #path as #pc; - }); - Ok(()) + Ok(path) } fn lookup_filter( @@ -1227,3 +1662,17 @@ fn lookup_filter( .find(|evsbase| evsbase.0.usage == Some(usage)) .or_else(|| evs.first()) } + +fn enums_to_map(evs: &EnumeratedValues) -> BTreeMap { + let mut map = BTreeMap::new(); + for ev in &evs.values { + if let Some(v) = ev.value { + map.insert(v, ev); + } + } + map +} + +fn minimal_hole(map: &BTreeMap, width: u32) -> Option { + (0..(1u64 << width)).find(|&v| !map.contains_key(&v)) +} diff --git a/src/generate/riscv.rs b/src/generate/riscv.rs new file mode 100644 index 00000000..fff9e970 --- /dev/null +++ b/src/generate/riscv.rs @@ -0,0 +1,316 @@ +use crate::{svd::Peripheral, util, Config, Settings}; +use anyhow::Result; +use log::debug; +use proc_macro2::TokenStream; +use quote::quote; +use std::{collections::HashMap, fmt::Write, str::FromStr}; + +pub fn is_riscv_peripheral(p: &Peripheral, s: &Settings) -> bool { + // TODO cleaner implementation of this + match &s.riscv_config { + Some(c) => { + c.clint.as_ref().is_some_and(|clint| clint.name == p.name) + || c.plic.as_ref().is_some_and(|plic| plic.name == p.name) + } + _ => false, + } +} + +/// Whole RISC-V generation +pub fn render( + peripherals: &[Peripheral], + device_x: &mut String, + config: &Config, +) -> Result { + let mut mod_items = TokenStream::new(); + + let defmt = config + .impl_defmt + .as_ref() + .map(|feature| quote!(#[cfg_attr(feature = #feature, derive(defmt::Format))])); + + if let Some(c) = config.settings.riscv_config.as_ref() { + if !c.core_interrupts.is_empty() { + debug!("Rendering target-specific core interrupts"); + writeln!(device_x, "/* Core interrupt sources and trap handlers */")?; + let mut interrupts = vec![]; + for interrupt in c.core_interrupts.iter() { + let name = TokenStream::from_str(&interrupt.name).unwrap(); + let value = TokenStream::from_str(&format!("{}", interrupt.value)).unwrap(); + let description = interrupt.description(); + + writeln!(device_x, "PROVIDE({name} = DefaultHandler);")?; + writeln!( + device_x, + "PROVIDE(_start_{name}_trap = _start_DefaultHandler_trap);" + )?; + + interrupts.push(quote! { + #[doc = #description] + #name = #value, + }); + } + mod_items.extend(quote! { + /// Core interrupts. These interrupts are handled by the core itself. + #[riscv::pac_enum(unsafe CoreInterruptNumber)] + #defmt + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum CoreInterrupt { + #(#interrupts)* + } + }); + } else { + // when no interrupts are defined, we re-export the standard riscv interrupts + mod_items.extend(quote! {pub use riscv::interrupt::Interrupt as CoreInterrupt;}); + } + + if !c.exceptions.is_empty() { + debug!("Rendering target-specific exceptions"); + writeln!(device_x, "/* Exception sources */")?; + let mut exceptions = vec![]; + for exception in c.exceptions.iter() { + let name = TokenStream::from_str(&exception.name).unwrap(); + let value = TokenStream::from_str(&format!("{}", exception.value)).unwrap(); + let description = exception.description(); + + writeln!(device_x, "PROVIDE({name} = ExceptionHandler);")?; + + exceptions.push(quote! { + #[doc = #description] + #name = #value, + }); + } + mod_items.extend(quote! { + /// Exception sources in the device. + #[riscv::pac_enum(unsafe ExceptionNumber)] + #defmt + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum Exception { + #(#exceptions)* + } + }); + } else { + // when no exceptions are defined, we re-export the standard riscv exceptions + mod_items.extend(quote! { pub use riscv::interrupt::Exception; }); + } + + if !c.priorities.is_empty() { + debug!("Rendering target-specific priority levels"); + let priorities = c.priorities.iter().map(|priority| { + let name = TokenStream::from_str(&priority.name).unwrap(); + let value = TokenStream::from_str(&format!("{}", priority.value)).unwrap(); + let description = priority.description(); + + quote! { + #[doc = #description] + #name = #value, + } + }); + mod_items.extend(quote! { + /// Priority levels in the device + #[riscv::pac_enum(unsafe PriorityNumber)] + #defmt + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum Priority { + #(#priorities)* + } + }); + } + + if !c.harts.is_empty() { + debug!("Rendering target-specific HART IDs"); + let harts = c.harts.iter().map(|hart| { + let name = TokenStream::from_str(&hart.name).unwrap(); + let value = TokenStream::from_str(&format!("{}", hart.value)).unwrap(); + let description = hart.description(); + + quote! { + #[doc = #description] + #name = #value, + } + }); + mod_items.extend(quote! { + /// HARTs in the device + #[riscv::pac_enum(unsafe HartIdNumber)] + #defmt + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum Hart { + #(#harts)* + } + }); + } + } else { + // when no riscv block is defined, we re-export the standard riscv interrupt and exception enums + mod_items.extend(quote! { + pub use riscv::interrupt::{Interrupt as CoreInterrupt, Exception}; + }); + } + + mod_items.extend(quote! { + pub use riscv::{ + InterruptNumber, ExceptionNumber, PriorityNumber, HartIdNumber, + interrupt::{enable, disable, free, nested} + }; + + pub type Trap = riscv::interrupt::Trap; + + /// Retrieves the cause of a trap in the current hart. + /// + /// If the raw cause is not a valid interrupt or exception for the target, it returns an error. + #[inline] + pub fn try_cause() -> riscv::result::Result { + riscv::interrupt::try_cause() + } + + /// Retrieves the cause of a trap in the current hart (machine mode). + /// + /// If the raw cause is not a valid interrupt or exception for the target, it panics. + #[inline] + pub fn cause() -> Trap { + try_cause().unwrap() + } + }); + + let external_interrupts = peripherals + .iter() + .flat_map(|p| p.interrupt.iter()) + .map(|i| (i.value, i)) + .collect::>(); + let mut external_interrupts = external_interrupts.into_values().collect::>(); + external_interrupts.sort_by_key(|i| i.value); + if !external_interrupts.is_empty() { + debug!("Rendering target-specific external interrupts"); + writeln!(device_x, "/* External interrupt sources */")?; + let mut interrupts = vec![]; + for i in external_interrupts.iter() { + let name = TokenStream::from_str(&i.name).unwrap(); + let value = TokenStream::from_str(&format!("{}", i.value)).unwrap(); + let description = format!( + "{} - {}", + i.value, + i.description + .as_ref() + .map(|s| util::respace(s)) + .as_ref() + .map(|s| util::escape_special_chars(s)) + .unwrap_or_else(|| i.name.as_str().into()) + ); + + writeln!(device_x, "PROVIDE({name} = DefaultHandler);")?; + + interrupts.push(quote! { + #[doc = #description] + #name = #value, + }) + } + mod_items.extend(quote! { + /// External interrupts. These interrupts are handled by the external peripherals. + #[riscv::pac_enum(unsafe ExternalInterruptNumber)] + #defmt + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum ExternalInterrupt { + #(#interrupts)* + } + }); + } + + let mut riscv_peripherals = TokenStream::new(); + if let Some(c) = config.settings.riscv_config.as_ref() { + let harts = match c.harts.is_empty() { + true => vec![], + false => c + .harts + .iter() + .map(|h| (TokenStream::from_str(&h.name).unwrap(), h.value)) + .collect::>(), + }; + if let Some(clint) = &c.clint { + let p = peripherals.iter().find(|&p| p.name == clint.name).unwrap(); + let base = TokenStream::from_str(&format!("base 0x{:X},", p.base_address)).unwrap(); + let freq = match clint.freq { + Some(clk) => match clint.async_delay { + true => TokenStream::from_str(&format!("freq {clk}, async_delay,")).unwrap(), + false => TokenStream::from_str(&format!("freq {clk},")).unwrap(), + }, + None => quote! {}, + }; + let mtimecmps = harts + .iter() + .map(|(name, value)| { + let mtimecmp_name = TokenStream::from_str(&format!("mtimecmp{value}")).unwrap(); + let doc = format!("[{value}](crate::interrupt::Hart::{name})"); + quote! {#mtimecmp_name = (crate::interrupt::Hart::#name, #doc)} + }) + .collect::>(); + let mtimecmps = match mtimecmps.len() { + 0 => quote! {}, + _ => quote! {mtimecmps [ #(#mtimecmps),* ],}, + }; + let msips = harts + .iter() + .map(|(name, value)| { + let msip_name = TokenStream::from_str(&format!("msip{value}")).unwrap(); + let doc = format!("[{value}](crate::interrupt::Hart::{name})"); + quote! {#msip_name = (crate::interrupt::Hart::#name, #doc)} + }) + .collect::>(); + let msips = match msips.len() { + 0 => quote! {}, + _ => quote! {msips [ #(#msips),* ],}, + }; + + riscv_peripherals.extend(quote! { + riscv_peripheral::clint_codegen!(#base #freq #mtimecmps #msips); + }); + } + if let Some(plic) = &c.plic { + let p = peripherals.iter().find(|&p| p.name == plic.name).unwrap(); + let base = TokenStream::from_str(&format!("base 0x{:X},", p.base_address)).unwrap(); + let ctxs = harts + .iter() + .map(|(name, value)| { + let ctx_name = TokenStream::from_str(&format!("ctx{value}")).unwrap(); + let doc = format!("[{value}](crate::interrupt::Hart::{name})"); + quote! {#ctx_name = (crate::interrupt::Hart::#name, #doc)} + }) + .collect::>(); + let ctxs = match ctxs.len() { + 0 => quote! {}, + _ => quote! {ctxs [ #(#ctxs),* ],}, + }; + + riscv_peripherals.extend(quote! { + riscv_peripheral::plic_codegen!(#base #ctxs); + }); + + if let Some(core_interrupt) = &plic.core_interrupt { + let core_interrupt = TokenStream::from_str(core_interrupt).unwrap(); + let ctx = match &plic.hart_id { + Some(hart_id) => { + TokenStream::from_str(&format!("ctx(Hart::{hart_id})")).unwrap() + } + None => quote! { ctx_mhartid() }, + }; + mod_items.extend(quote! { + #[cfg(feature = "rt")] + #[riscv_rt::core_interrupt(CoreInterrupt::#core_interrupt)] + fn plic_handler() { + let claim = crate::PLIC::#ctx.claim(); + if let Some(s) = claim.claim::() { + unsafe { _dispatch_external_interrupt(s.number()) } + claim.complete(s); + } + } + }); + } + } + } + + Ok(quote! { + /// Interrupt numbers, priority levels, and HART IDs. + pub mod interrupt { + #mod_items + } + #riscv_peripherals + }) +} diff --git a/src/lib.rs b/src/lib.rs index ca309307..a1379ef6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ //! Peripheral API generator from [CMSIS-SVD] files //! -//! [CMSIS-SVD]: http://www.keil.com/pack/doc/CMSIS/SVD/html/index.html +//! [CMSIS-SVD]: https://www.keil.com/pack/doc/CMSIS/SVD/html/index.html //! //! An SVD file is an XML file that describes the hardware features of a //! microcontroller. In particular, it lists all the peripherals available to the @@ -37,7 +37,7 @@ //! //! - `build.rs`, build script that places `device.x` somewhere the linker can find. //! - `device.x`, linker script that weakly aliases all the interrupt handlers to the default -//! exception handler (`DefaultHandler`). +//! exception handler (`DefaultHandler`). //! - `lib.rs`, the generated code. //! //! All these files must be included in the same device crate. The `lib.rs` file contains several @@ -77,6 +77,11 @@ //! rt = ["cortex-m-rt/device"] //! ``` //! +//! ## the `--reexport-core-peripherals` flag +//! +//! With `--reexport-core-peripherals` flag enabled peripherals from `cortex-m` crate +//! be reexported by peripheral access crate. +//! //! ## target = msp430 //! //! MSP430 does not natively use the SVD format. However, SVD files can be generated using the @@ -90,7 +95,7 @@ //! //! - `build.rs`, build script that places `device.x` somewhere the linker can find. //! - `device.x`, linker script that weakly aliases all the interrupt handlers to the default -//! exception handler (`DefaultHandler`). +//! exception handler (`DefaultHandler`). //! - `lib.rs`, the generated code. //! //! All these files must be included in the same device crate. The `lib.rs` file contains several @@ -122,21 +127,18 @@ //! - [`msp430-rt`](https://crates.io/crates/msp430-rt) v0.4.x //! - [`vcell`](https://crates.io/crates/vcell) v0.1.x //! -//! If the `--nightly` flag is provided to `svd2rust`, then `msp430-atomic` v0.1.4 is also needed. -//! Furthermore the "device" feature of `msp430-rt` must be enabled when the `rt` feature is +//! The "device" feature of `msp430-rt` must be enabled when the `rt` feature is //! enabled. The `Cargo.toml` of the device crate will look like this: //! //! ``` toml //! [dependencies] //! critical-section = { version = "1.0", optional = true } //! msp430 = "0.4.0" -//! msp430-atomic = "0.1.4" # Only when using the --nightly flag //! msp430-rt = { version = "0.4.0", optional = true } //! vcell = "0.1.0" //! //! [features] //! rt = ["msp430-rt/device"] -//! unstable = ["msp430-atomic"] //! ``` //! //! ## Other targets @@ -147,22 +149,43 @@ //! The resulting crate must provide an opt-in `rt` feature and depend on these crates: //! //! - [`critical-section`](https://crates.io/crates/critical-section) v1.x -//! - [`riscv`](https://crates.io/crates/riscv) v0.9.x (if target is RISC-V) -//! - [`riscv-rt`](https://crates.io/crates/riscv-rt) v0.9.x (if target is RISC-V) +//! - [`riscv`](https://crates.io/crates/riscv) v0.12.x (if target is RISC-V) +//! - [`riscv-peripheral`](https://crates.io/crates/riscv-peripheral) v0.2.x (if target is RISC-V and has standard peripherals) +//! - [`riscv-rt`](https://crates.io/crates/riscv-rt) v0.13.x (if target is RISC-V) //! - [`vcell`](https://crates.io/crates/vcell) v0.1.x //! -//! The `*-rt` dependencies must be optional only enabled when the `rt` feature is enabled. The -//! `Cargo.toml` of the device crate will look like this for a RISC-V target: +//! The `*-rt` dependencies must be optional only enabled when the `rt` feature is enabled. +//! If target is RISC-V and supports vectored mode, you must include a feature `v-trap` to activate `riscv-rt/v-trap`. +//! The `Cargo.toml` of the device crate will look like this for a RISC-V target: //! //! ``` toml //! [dependencies] //! critical-section = { version = "1.0", optional = true } -//! riscv = "0.9.0" -//! riscv-rt = { version = "0.9.0", optional = true } +//! riscv = "0.12.1" +//! riscv-peripheral = "0.2.0" +//! riscv-rt = { version = "0.13.0", optional = true } //! vcell = "0.1.0" //! //! [features] //! rt = ["riscv-rt"] +//! v-trap = ["rt", "riscv-rt/v-trap"] +//! ``` +//! +//! ## Generating documentation +//! +//! You can generate the documentation for the generated library using the following command: +//! +//! ```sh +//! RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc --all-features --open +//! ``` +//! +//! It is recommended to add the following block in the `Cargo.toml` of the generated library +//! if you plan to host the library documentaion on `docs.rs` +//! +//! ```toml +//! [package.metadata.docs.rs] +//! all-features = true +//! rustdoc-args = ["--generate-link-to-definition"] //! ``` //! //! # Peripheral API @@ -170,11 +193,11 @@ //! To use a peripheral first you must get an *instance* of the peripheral. All the device //! peripherals are modeled as singletons (there can only ever be, at most, one instance of any //! one of them) and the only way to get an instance of them is through the `Peripherals::take` -//! method. +//! method, enabled via the `critical-section` feature on the generated crate. //! //! ```ignore //! let mut peripherals = stm32f30x::Peripherals::take().unwrap(); -//! peripherals.GPIOA.odr.write(|w| w.bits(1)); +//! peripherals.GPIOA.odr().write(|w| w.bits(1)); //! ``` //! //! This method can only be successfully called *once* -- that's why the method returns an `Option`. @@ -189,16 +212,16 @@ //! use the implementation provided by the target crate like `cortex-m`, `riscv` and `*-hal` crates. //! See more details in the [`critical-section`](https://crates.io/crates/critical-section) crate documentation. //! -//! The singleton property can be *unsafely* bypassed using the `ptr` static method which is -//! available on all the peripheral types. This method is useful for implementing safe higher -//! level abstractions. +//! The singleton property can be *unsafely* bypassed using the `ptr` or `steal` static methods +//! which are available on all the peripheral types. This method is useful for implementing safe +//! higher level abstractions. //! //! ```ignore //! struct PA0 { _0: () } //! impl PA0 { //! fn is_high(&self) -> bool { //! // NOTE(unsafe) actually safe because this is an atomic read with no side effects -//! unsafe { (*GPIOA::ptr()).idr.read().bits() & 1 != 0 } +//! unsafe { (*GPIOA::ptr()).idr().read().bits() & 1 != 0 } //! } //! //! fn is_low(&self) -> bool { @@ -226,29 +249,40 @@ //! /// Inter-integrated circuit //! pub mod i2c1 { //! /// Register block +//! #[repr(C)] //! pub struct RegisterBlock { +//! cr1: CR1, +//! cr2: CR2, +//! oar1: OAR1, +//! oar2: OAR2, +//! dr: DR, +//! } +//! impl RegisterBlock { //! /// 0x00 - Control register 1 -//! pub cr1: CR1, +//! #[inline(always)] +//! pub const fn cr1(&self) -> &CR1 { +//! &self.cr1 +//! } //! /// 0x04 - Control register 2 -//! pub cr2: CR2, +//! #[inline(always)] +//! pub const fn cr2(&self) -> &CR2 { +//! &self.cr2 +//! } //! /// 0x08 - Own address register 1 -//! pub oar1: OAR1, -//! /// 0x0c - Own address register 2 -//! pub oar2: OAR2, -//! /// 0x10 - Timing register -//! pub timingr: TIMINGR, -//! /// Status register 1 -//! pub timeoutr: TIMEOUTR, -//! /// Interrupt and Status register -//! pub isr: ISR, -//! /// 0x1c - Interrupt clear register -//! pub icr: ICR, -//! /// 0x20 - PEC register -//! pub pecr: PECR, -//! /// 0x24 - Receive data register -//! pub rxdr: RXDR, -//! /// 0x28 - Transmit data register -//! pub txdr: TXDR, +//! #[inline(always)] +//! pub const fn oar1(&self) -> &OAR1 { +//! &self.oar1 +//! } +//! #[doc = "0x0c - Own address register 2"] +//! #[inline(always)] +//! pub const fn oar2(&self) -> &OAR2 { +//! &self.oar2 +//! } +//! #[doc = "0x10 - Data register"] +//! #[inline(always)] +//! pub const fn dr(&self) -> &DR { +//! &self.dr +//! } //! } //! } //! ``` @@ -318,7 +352,7 @@ //! //! ```ignore //! // is the SADD0 bit of the CR2 register set? -//! if i2c1.c2r.read().sadd0().bit() { +//! if i2c1.c2r().read().sadd0().bit() { //! // yes //! } else { //! // no @@ -333,7 +367,7 @@ //! Usage looks like this: //! //! ```ignore -//! if i2c1.c2r.write().reset() +//! if i2c1.c2r().reset() //! ``` //! //! ## `write` @@ -363,7 +397,7 @@ //! // Starting from the reset value, `0x0000_0000`, change the bitfields SADD0 //! // and SADD1 to `1` and `0b0011110` respectively and write that to the //! // register CR2. -//! i2c1.cr2.write(|w| unsafe { w.sadd0().bit(true).sadd1().bits(0b0011110) }); +//! i2c1.cr2().write(|w| unsafe { w.sadd0().bit(true).sadd1().bits(0b0011110) }); //! // NOTE ^ unsafe because you could be writing a reserved bit pattern into //! // the register. In this case, the SVD doesn't provide enough information to //! // check whether that's the case. @@ -386,10 +420,10 @@ //! //! ```ignore //! // Set the START bit to 1 while KEEPING the state of the other bits intact -//! i2c1.cr2.modify(|_, w| unsafe { w.start().bit(true) }); +//! i2c1.cr2().modify(|_, w| unsafe { w.start().bit(true) }); //! //! // TOGGLE the STOP bit, all the other bits will remain untouched -//! i2c1.cr2.modify(|r, w| w.stop().bit(!r.stop().bit())); +//! i2c1.cr2().modify(|r, w| w.stop().bit(!r.stop().bit())); //! ``` //! //! # enumeratedValues @@ -402,7 +436,7 @@ //! The new `read` API returns an enum that you can match: //! //! ```ignore -//! match gpioa.dir.read().pin0().variant() { +//! match gpioa.dir().read().pin0().variant() { //! gpioa::dir::PIN0_A::Input => { .. }, //! gpioa::dir::PIN0_A::Output => { .. }, //! } @@ -411,7 +445,7 @@ //! or test for equality //! //! ```ignore -//! if gpioa.dir.read().pin0().variant() == gpio::dir::PIN0_A::Input { +//! if gpioa.dir().read().pin0().variant() == gpio::dir::PIN0_A::Input { //! .. //! } //! ``` @@ -420,11 +454,11 @@ //! having to import the enum: //! //! ```ignore -//! if gpioa.dir.read().pin0().is_input() { +//! if gpioa.dir().read().pin0().is_input() { //! .. //! } //! -//! if gpioa.dir.read().pin0().is_output() { +//! if gpioa.dir().read().pin0().is_output() { //! .. //! } //! ``` @@ -432,7 +466,7 @@ //! The original `bits` method is available as well: //! //! ```ignore -//! if gpioa.dir.read().pin0().bits() == 0 { +//! if gpioa.dir().read().pin0().bits() == 0 { //! .. //! } //! ``` @@ -442,14 +476,14 @@ //! //! ```ignore //! // enum PIN0_A { Input, Output } -//! gpioa.dir.write(|w| w.pin0().variant(gpio::dir::PIN0_A::Output)); +//! gpioa.dir().write(|w| w.pin0().variant(gpio::dir::PIN0_A::Output)); //! ``` //! //! There are convenience methods to pick one of the variants without having to //! import the enum: //! //! ```ignore -//! gpioa.dir.write(|w| w.pin0().output()); +//! gpioa.dir().write(|w| w.pin0().output()); //! ``` //! //! The `bits` (or `bit`) method is still available but will become safe if it's @@ -457,7 +491,7 @@ //! //! ```ignore //! // safe because there are only two options: `0` or `1` -//! gpioa.dir.write(|w| w.pin0().bit(true)); +//! gpioa.dir().write(|w| w.pin0().bit(true)); //! ``` //! //! # Interrupt API @@ -487,35 +521,100 @@ //! //! [`interrupt`]: https://docs.rs/cortex-m-rt-macros/0.1/cortex_m_rt_macros/attr.interrupt.html //! -//! ## the `--nightly` flag +//! ## the `--reexport-interrupt` flag (deprecated) +//! +//! With `--reexport-interrupt` flag passed `interrupt` macro from `cortex-m-rt` or `riscv-rt` +//! be reexported by peripheral access crate. +//! +//! ## the `--atomics` flag //! -//! The `--nightly` flag can be passed to `svd2rust` to enable features in the generated api that are only available to a nightly -//! compiler. The following features are gated by the `--nightly` flag: +//! The `--atomics` flag can be passed to `svd2rust` to extends the register API with operations to +//! atomically set, clear, and toggle specific bits. The atomic operations allow limited +//! modification of register bits without read-modify-write sequences. As such, they can be +//! concurrently called on different bits in the same register without data races. This flag won't +//! work for RISCV chips without the atomic extension. //! -//! ### MSP430 +//! The `--atomics-feature` flag can also be specified to include atomics implementations conditionally +//! behind the supplied feature name. //! -//! Extends the register API with operations to atomically set, clear, and toggle specific bits. -//! The atomic operations allow limited modification of register bits without read-modify-write -//! sequences. As such, they can be concurrently called on different bits in the same register -//! without data races. +//! `portable-atomic` v0.3.16 must be added to the dependencies, with default features off to +//! disable the `fallback` feature. +//! +//! ## the `--impl-debug` flag +//! +//! The `--impl_debug` option will cause svd2rust to generate `core::fmt::Debug` implementations for +//! all registers and blocks. If a register is readable and has fields defined then each field value +//! will be printed - if no fields are defined then the value of the register will be printed. Any +//! register that has read actions will not be read and printed as `(not read/has read action!)`. +//! Registers that are not readable will have `(write only register)` printed as the value. +//! +//! The `--impl-debug-feature` flag can also be specified to include debug implementations conditionally +//! behind the supplied feature name. //! //! Usage examples: //! //! ```ignore //! // These can be called from different contexts even though they are modifying the same register -//! P1.p1out.set_bits(|w| unsafe { w.bits(1 << 1) }); -//! P1.p1out.clear(|w| unsafe { w.bits(!(1 << 2)) }); -//! P1.p1out.toggle(|w| unsafe { w.bits(1 << 4) }); +//! P1.p1out().set_bits(|w| unsafe { w.bits(1 << 1) }); +//! P1.p1out().clear_bits(|w| unsafe { w.bits(!(1 << 2)) }); +//! P1.p1out().toggle_bits(|w| unsafe { w.bits(1 << 4) }); +//! // if impl_debug was used one can print Registers or RegisterBlocks +//! // print single register +//! println!("RTC_CNT {:#?}", unsafe { &*esp32s3::RTC_CNTL::ptr() }.options0); +//! // print all registers for peripheral +//! println!("RTC_CNT {:#?}", unsafe { &*esp32s3::RTC_CNTL::ptr() }); +//! ``` +//! +//! ## the `--impl-defmt` flag +//! +//! The `--impl-defmt` flag can also be specified to include `defmt::Format` implementations conditionally +//! behind the supplied feature name. +//! +//! ## the `--ident-format` and `--ident-formats-theme` flags +//! +//! The `--ident-format type:prefix:case:suffix` (`-f`) flag can also be specified if you want to change +//! default behavior of formatting rust structure and enum names, register access methods, etc. +//! Passingle multiple flags is supported. +//! Supported cases are `snake_case` (pass `snake` or `s`), `PascalCase` (pass `pascal` or `p`), +//! `CONSTANT_CASE` (pass `constant` or `c`) and `leave_CASE_as_in_SVD` (pass `unchanged` or ``). +//! +//! There are identificator formats by default in the table. +//! Since `svd2rust` 0.32 defaults have been changed. +//! +//! | IdentifierType | Prefix | Case | Case 0.31 | Suffix | Suffix 0.31 | +//! |--------------------------------------------------------------------------------|:------:|:---------:|:---------:|:------:|:-----------:| +//! | field_reader | | pascal | constant | R | _R | +//! | field_writer | | pascal | constant | W | _W | +//! | enum_name
enum_read_name | | pascal | constant | | _A | +//! | enum_write_name | | pascal | constant | WO | _AW | +//! | enum_value | | pascal | constant | | | +//! | interrupt | | unchanged | constant | | | +//! | peripheral_singleton | | snake | constant | | | +//! | peripheral
register
cluster | | pascal | constant | | | +//! | register_spec | | pascal | constant | Spec | _SPEC | +//! | cluster_accessor
register_accessor
field_accessor
enum_value_accessor | | snake | snake | | | +//! | cluster_mod
register_mod
peripheral_mod
peripheral_feature | | snake | snake | | | +//! +//! To revert old behavior for `field_reader` you need to pass flag `-f field_reader::c:_R`. +//! +//! Also you can do the same in config file: +//! ```toml +//! [ident_formats.field_reader] +//! case = constant +//! suffix = "_R" //! ``` +//! +//! To revert old behavior for all identifiers you may pass `--ident-formats-theme legacy`. #![recursion_limit = "128"] use quote::quote; use svd_parser::svd; +pub mod config; pub mod generate; pub mod util; -pub use crate::util::{Config, Target}; +pub use config::{Config, Settings, Target}; #[non_exhaustive] pub struct Generation { @@ -531,22 +630,48 @@ pub struct DeviceSpecific { use anyhow::{Context, Result}; +use crate::config::{IdentFormats, IdentFormatsTheme}; + #[derive(Debug, thiserror::Error)] pub enum SvdError { #[error("Cannot format crate")] Fmt, #[error("Cannot render SVD device")] - Render, + Render(#[from] anyhow::Error), } /// Generates rust code for the specified svd content. pub fn generate(input: &str, config: &Config) -> Result { use std::fmt::Write; - let device = load_from(input, config)?; + let mut config = config.clone(); + + match config.settings_file.as_ref() { + #[cfg(feature = "yaml")] + Some(settings) => { + let file = std::fs::read_to_string(settings).context("could not read settings file")?; + config + .settings + .update_from(serde_yaml::from_str(&file).context("could not parse settings file")?) + } + #[cfg(not(feature = "yaml"))] + Some(_) => { + return Err(anyhow::anyhow!("Support for yaml config files is not available because svd2rust was compiled without the yaml feature")); + } + None => {} + }; + + let mut ident_formats = match config.ident_formats_theme { + Some(IdentFormatsTheme::Legacy) => IdentFormats::legacy_theme(), + _ => IdentFormats::default_theme(), + }; + ident_formats.extend(config.ident_formats.drain()); + config.ident_formats = ident_formats; + + let device = load_from(input, &config)?; let mut device_x = String::new(); let items = - generate::device::render(&device, config, &mut device_x).or(Err(SvdError::Render))?; + generate::device::render(&device, &config, &mut device_x).map_err(SvdError::Render)?; let mut lib_rs = String::new(); writeln!( @@ -563,7 +688,7 @@ pub fn generate(input: &str, config: &Config) -> Result { } else { Some(DeviceSpecific { device_x, - build_rs: util::build_rs().to_string(), + build_rs: util::build_rs(&config).to_string(), }) }; @@ -573,29 +698,39 @@ pub fn generate(input: &str, config: &Config) -> Result { }) } -/// Load a [Device] from a string slice with given [config](crate::util::Config). -pub fn load_from(input: &str, config: &crate::util::Config) -> Result { - use self::util::SourceType; +/// Load a [Device](svd::Device) from a string slice with given [config](crate::config::Config). +pub fn load_from(input: &str, config: &Config) -> Result { + use config::SourceType; use svd_parser::ValidateLevel; + let validate_level = if config.strict { + ValidateLevel::Strict + } else { + ValidateLevel::Weak + }; + let mut device = match config.source_type { SourceType::Xml => { let mut parser_config = svd_parser::Config::default(); - parser_config.validate_level = if config.strict { - ValidateLevel::Strict - } else { - ValidateLevel::Weak - }; + parser_config.validate_level = validate_level; svd_parser::parse_with_config(input, &parser_config) .with_context(|| "Error parsing SVD XML file".to_string())? } #[cfg(feature = "yaml")] - SourceType::Yaml => serde_yaml::from_str(input) - .with_context(|| "Error parsing SVD YAML file".to_string())?, + SourceType::Yaml => { + let device: svd::Device = serde_yaml::from_str(input) + .with_context(|| "Error parsing SVD YAML file".to_string())?; + device.validate_all(validate_level)?; + device + } #[cfg(feature = "json")] - SourceType::Json => serde_json::from_str(input) - .with_context(|| "Error parsing SVD JSON file".to_string())?, + SourceType::Json => { + let device: svd::Device = serde_json::from_str(input) + .with_context(|| "Error parsing SVD JSON file".to_string())?; + device.validate_all(validate_level)?; + device + } }; svd_parser::expand_properties(&mut device); Ok(device) diff --git a/src/main.rs b/src/main.rs index 6dc2b74e..d3efbeba 100755 --- a/src/main.rs +++ b/src/main.rs @@ -1,34 +1,74 @@ #![recursion_limit = "128"] -use log::{debug, error, info}; +use log::{debug, error, info, warn}; +use svd2rust::config::{IdentFormatError, IdentFormats, IdentFormatsTheme}; +use svd2rust::util::IdentFormat; -use std::fs::File; use std::io::Write; use std::process; +use std::{fs::File, path::Path}; use anyhow::{Context, Result}; use clap::{Arg, ArgAction, Command}; use svd2rust::{ + config::{Config, SourceType, Target}, generate, load_from, - util::{self, build_rs, Config, SourceType, Target}, + util::{self, build_rs}, }; fn parse_configs(app: Command) -> Result { use irx_config::parsers::{cmd, toml}; use irx_config::ConfigBuilder; + let ident_formats = app.clone().get_matches(); let irxconfig = ConfigBuilder::default() - .append_parser(cmd::ParserBuilder::new(app).build()?) + .append_parser(cmd::ParserBuilder::new(app).exit_on_error(true).build()?) .append_parser( toml::ParserBuilder::default() .default_path("svd2rust.toml") .path_option("config") .ignore_missing_file(true) .build()?, - ) - .load()?; + ); + + let irxconfig = irxconfig.load()?; + + let mut config: Config = irxconfig.get()?; + + let mut idf = match config.ident_formats_theme { + Some(IdentFormatsTheme::Legacy) => IdentFormats::legacy_theme(), + _ => IdentFormats::default_theme(), + }; + idf.extend(config.ident_formats.drain()); + config.ident_formats = idf; + + if let Some(ident_formats) = ident_formats.get_many::("ident_format") { + for fs in ident_formats { + if let Some((n, fmt)) = fs.split_once(':') { + if let std::collections::hash_map::Entry::Occupied(mut e) = + config.ident_formats.entry(n.into()) + { + match IdentFormat::parse(fmt) { + Ok(ident_format) => { + e.insert(ident_format); + } + Err(IdentFormatError::UnknownCase(c)) => { + warn!("Ident case `{c}` is unknown") + } + Err(IdentFormatError::Other) => { + warn!("Can't parse identifier format string `{fmt}`") + } + } + } else { + warn!("Ident format name `{n}` is unknown"); + } + } else { + warn!("Can't parse identifier format string `{fs}`"); + } + } + } - irxconfig.get().map_err(Into::into) + Ok(config) } fn run() -> Result<()> { @@ -46,6 +86,7 @@ fn run() -> Result<()> { .arg( Arg::new("output_dir") .long("output-dir") + .alias("output_dir") .help("Directory to place generated files") .short('o') .action(ArgAction::Set) @@ -59,6 +100,13 @@ fn run() -> Result<()> { .action(ArgAction::Set) .value_name("TOML_FILE"), ) + .arg( + Arg::new("settings_file") + .long("settings") + .help("Target-specific settings YAML file") + .action(ArgAction::Set) + .value_name("YAML_FILE"), + ) .arg( Arg::new("target") .long("target") @@ -67,64 +115,129 @@ fn run() -> Result<()> { .value_name("ARCH"), ) .arg( - Arg::new("nightly") - .long("nightly") + Arg::new("atomics") + .long("atomics") .action(ArgAction::SetTrue) - .help("Enable features only available to nightly rustc"), + .help("Generate atomic register modification API"), ) .arg( - Arg::new("const_generic") - .long("const_generic") - .action(ArgAction::SetTrue) - .help( - "Use const generics to generate writers for same fields with different offsets", - ), + Arg::new("atomics_feature") + .long("atomics-feature") + .alias("atomics_feature") + .help("add feature gating for atomic register modification API") + .action(ArgAction::Set) + .value_name("FEATURE"), ) .arg( Arg::new("ignore_groups") - .long("ignore_groups") + .long("ignore-groups") + .alias("ignore_groups") .action(ArgAction::SetTrue) .help("Don't add alternateGroup name as prefix to register name"), ) .arg( Arg::new("keep_list") - .long("keep_list") + .long("keep-list") + .alias("keep_list") .action(ArgAction::SetTrue) .help( "Keep lists when generating code of dimElement, instead of trying to generate arrays", )) .arg( Arg::new("generic_mod") - .long("generic_mod") + .long("generic-mod") + .alias("generic_mod") .short('g') .action(ArgAction::SetTrue) .help("Push generic mod in separate file"), ) .arg( Arg::new("feature_group") - .long("feature_group") + .long("feature-group") + .alias("feature_group") .action(ArgAction::SetTrue) .help("Use group_name of peripherals as feature"), ) .arg( Arg::new("feature_peripheral") - .long("feature_peripheral") + .long("feature-peripheral") + .alias("feature_peripheral") .action(ArgAction::SetTrue) .help("Use independent cfg feature flags for each peripheral"), ) + .arg( + Arg::new("ident_format") + .long("ident-format") + .short('f') + .alias("ident_format") + .action(ArgAction::Append) + .long_help( +format!("Specify `-f type:prefix:case:suffix` to change default ident formatting. +Allowed values of `type` are {:?}. +Allowed cases are `unchanged` (''), `pascal` ('p'), `constant` ('c') and `snake` ('s'). +", IdentFormats::default_theme().keys().collect::>()) +), + ) + .arg( + Arg::new("ident_formats_theme") + .long("ident-formats-theme") + .alias("ident_formats_theme") + .help("A set of `ident_format` settings. `new` or `legacy`") + .action(ArgAction::Set) + .value_name("THEME"), + ) + .arg( + Arg::new("field_names_for_enums") + .long("field-names-for-enums") + .alias("field_names_for_enums") + .action(ArgAction::SetTrue) + .help("Use field name for enumerations even when enumeratedValues has a name"), + ) .arg( Arg::new("max_cluster_size") - .long("max_cluster_size") + .long("max-cluster-size") + .alias("max_cluster_size") .action(ArgAction::SetTrue) .help("Use array increment for cluster size"), ) + .arg( + Arg::new("impl_debug") + .long("impl-debug") + .alias("impl_debug") + .action(ArgAction::SetTrue) + .help("implement Debug for readable blocks and registers"), + ) + .arg( + Arg::new("impl_debug_feature") + .long("impl-debug-feature") + .alias("impl_debug_feature") + .help("Add feature gating for block and register debug implementation") + .action(ArgAction::Set) + .value_name("FEATURE"), + ) + .arg( + Arg::new("impl_defmt") + .long("impl-defmt") + .alias("impl_defmt") + .help("Add automatic defmt implementation for enumerated values") + .action(ArgAction::Set) + .value_name("FEATURE"), + ) .arg( Arg::new("make_mod") - .long("make_mod") + .long("make-mod") + .alias("make_mod") .short('m') .action(ArgAction::SetTrue) .help("Create mod.rs instead of lib.rs, without inner attributes"), ) + .arg( + Arg::new("skip_crate_attributes") + .long("skip-crate-attributes") + .alias("skip_crate_attributes") + .action(ArgAction::SetTrue) + .help("Do not generate crate attributes"), + ) .arg( Arg::new("strict") .long("strict") @@ -133,21 +246,35 @@ fn run() -> Result<()> { .help("Make advanced checks due to parsing SVD"), ) .arg( - Arg::new("pascal_enum_values") - .long("pascal_enum_values") + Arg::new("source_type") + .long("source-type") + .alias("source_type") + .help("Specify file/stream format"), + ) + .arg( + Arg::new("reexport_core_peripherals") + .long("reexport-core-peripherals") + .alias("reexport_core_peripherals") .action(ArgAction::SetTrue) - .help("Use PascalCase in stead of UPPER_CASE for enumerated values"), + .help("For Cortex-M target reexport peripherals from cortex-m crate"), ) .arg( - Arg::new("derive_more") - .long("derive_more") + Arg::new("reexport_interrupt") + .long("reexport-interrupt") + .alias("reexport_interrupt") .action(ArgAction::SetTrue) - .help("Use derive_more procedural macros to implement Deref and From"), + .help("Reexport interrupt macro from cortex-m-rt like crates"), ) .arg( - Arg::new("source_type") - .long("source_type") - .help("Specify file/stream format"), + Arg::new("base_address_shift") + .short('b') + .long("base-address-shift") + .alias("base_address_shift") + .action(ArgAction::Set) + .help("Add offset to all base addresses on all peripherals in the SVD file.") + .long_help("Add offset to all base addresses on all peripherals in the SVD file. +Useful for soft-cores where the peripheral address range isn't necessarily fixed. +Ignore this option if you are not building your own FPGA based soft-cores."), ) .arg( Arg::new("log_level") @@ -195,10 +322,25 @@ fn run() -> Result<()> { } } + match config.settings_file.as_ref() { + #[cfg(feature = "yaml")] + Some(settings) => { + let file = std::fs::read_to_string(settings).context("could not read settings file")?; + config + .settings + .update_from(serde_yaml::from_str(&file).context("could not parse settings file")?) + } + #[cfg(not(feature = "yaml"))] + Some(_) => { + return Err(anyhow::anyhow!("Support for yaml config files is not available because svd2rust was compiled without the yaml feature")); + } + None => {} + }; + if let Some(file) = config.input.as_ref() { config.source_type = SourceType::from_path(file) } - let path = &config.output_dir; + let path = config.output_dir.as_deref().unwrap_or(Path::new(".")); info!("Parsing device from SVD file"); let device = load_from(input, &config)?; @@ -211,7 +353,7 @@ fn run() -> Result<()> { let filename = if config.make_mod { "mod.rs" } else { "lib.rs" }; let mut file = File::create(path.join(filename)).expect("Couldn't create output file"); - let data = items.to_string().replace("] ", "]\n"); + let data = items.to_string().replace(" # [", "\n#["); file.write_all(data.as_ref()) .expect("Could not write code to lib.rs"); @@ -223,19 +365,24 @@ fn run() -> Result<()> { ] .contains(&config.target) { - writeln!(File::create(path.join("device.x"))?, "{}", device_x)?; - writeln!(File::create(path.join("build.rs"))?, "{}", build_rs())?; + writeln!(File::create(path.join("device.x"))?, "{device_x}")?; + writeln!( + File::create(path.join("build.rs"))?, + "{}", + build_rs(&config) + )?; } if config.feature_group || config.feature_peripheral { + let feature_format = config.ident_formats.get("peripheral_feature").unwrap(); let mut features = Vec::new(); if config.feature_group { features.extend( - util::group_names(&device) + util::group_names(&device, feature_format) .iter() .map(|s| format!("{s} = []\n")), ); - let add_groups: Vec<_> = util::group_names(&device) + let add_groups: Vec<_> = util::group_names(&device, feature_format) .iter() .map(|s| format!("\"{s}\"")) .collect(); @@ -243,11 +390,11 @@ fn run() -> Result<()> { } if config.feature_peripheral { features.extend( - util::peripheral_names(&device) + util::peripheral_names(&device, feature_format) .iter() .map(|s| format!("{s} = []\n")), ); - let add_peripherals: Vec<_> = util::peripheral_names(&device) + let add_peripherals: Vec<_> = util::peripheral_names(&device, feature_format) .iter() .map(|s| format!("\"{s}\"")) .collect(); diff --git a/src/util.rs b/src/util.rs index 76047352..92e420c6 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,19 +1,21 @@ use std::borrow::Cow; -use crate::svd::{Access, Device, DimElement, Field, RegisterInfo, RegisterProperties}; +pub use crate::config::{Case, IdentFormat}; +use crate::{ + svd::{Access, Device, Field, RegisterInfo, RegisterProperties}, + Config, +}; use inflections::Inflect; use proc_macro2::{Ident, Span, TokenStream}; use quote::quote; use std::collections::HashSet; -use std::path::{Path, PathBuf}; use svd_rs::{MaybeArray, Peripheral, PeripheralInfo}; use syn::{ - punctuated::Punctuated, token::Colon2, AngleBracketedGenericArguments, GenericArgument, Lit, - LitInt, PathArguments, PathSegment, Token, Type, TypePath, + punctuated::Punctuated, token::PathSep, Lit, LitInt, PathArguments, PathSegment, Type, TypePath, }; -use anyhow::{anyhow, bail, Result}; +use anyhow::{anyhow, Result}; pub const BITS_PER_BYTE: u32 = 8; @@ -21,234 +23,122 @@ pub const BITS_PER_BYTE: u32 = 8; /// that are not valid in Rust ident const BLACKLIST_CHARS: &[char] = &['(', ')', '[', ']', '/', ' ', '-']; -#[cfg_attr(feature = "serde", derive(serde::Deserialize))] -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct Config { - #[cfg_attr(feature = "serde", serde(default))] - pub target: Target, - #[cfg_attr(feature = "serde", serde(default))] - pub nightly: bool, - #[cfg_attr(feature = "serde", serde(default))] - pub generic_mod: bool, - #[cfg_attr(feature = "serde", serde(default))] - pub make_mod: bool, - #[cfg_attr(feature = "serde", serde(default))] - pub const_generic: bool, - #[cfg_attr(feature = "serde", serde(default))] - pub ignore_groups: bool, - #[cfg_attr(feature = "serde", serde(default))] - pub keep_list: bool, - #[cfg_attr(feature = "serde", serde(default))] - pub strict: bool, - #[cfg_attr(feature = "serde", serde(default))] - pub pascal_enum_values: bool, - #[cfg_attr(feature = "serde", serde(default))] - pub derive_more: bool, - #[cfg_attr(feature = "serde", serde(default))] - pub feature_group: bool, - #[cfg_attr(feature = "serde", serde(default))] - pub feature_peripheral: bool, - #[cfg_attr(feature = "serde", serde(default))] - pub max_cluster_size: bool, - #[cfg_attr(feature = "serde", serde(default = "current_dir"))] - pub output_dir: PathBuf, - #[cfg_attr(feature = "serde", serde(default))] - pub input: Option, - #[cfg_attr(feature = "serde", serde(default))] - pub source_type: SourceType, - #[cfg_attr(feature = "serde", serde(default))] - pub log_level: Option, -} - -fn current_dir() -> PathBuf { - PathBuf::from(".") -} - -impl Default for Config { - fn default() -> Self { - Self { - target: Target::default(), - nightly: false, - generic_mod: false, - make_mod: false, - const_generic: false, - ignore_groups: false, - keep_list: false, - strict: false, - pascal_enum_values: false, - derive_more: false, - feature_group: false, - feature_peripheral: false, - max_cluster_size: false, - output_dir: current_dir(), - input: None, - source_type: SourceType::default(), - log_level: None, +fn to_pascal_case(s: &str) -> String { + if !s.contains('_') { + s.to_pascal_case() + } else { + let mut string = String::new(); + let mut parts = s.split('_').peekable(); + if let Some(&"") = parts.peek() { + string.push('_'); } + while let Some(p) = parts.next() { + if p.is_empty() { + continue; + } + string.push_str(&p.to_pascal_case()); + match parts.peek() { + Some(nxt) + if p.ends_with(|s: char| s.is_numeric()) + && nxt.starts_with(|s: char| s.is_numeric()) => + { + string.push('_'); + } + Some(&"") => { + string.push('_'); + } + _ => {} + } + } + string } } -#[allow(clippy::upper_case_acronyms)] -#[allow(non_camel_case_types)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize))] -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub enum Target { - #[cfg_attr(feature = "serde", serde(rename = "cortex-m"))] - CortexM, - #[cfg_attr(feature = "serde", serde(rename = "msp430"))] - Msp430, - #[cfg_attr(feature = "serde", serde(rename = "riscv"))] - RISCV, - #[cfg_attr(feature = "serde", serde(rename = "xtensa-lx"))] - XtensaLX, - #[cfg_attr(feature = "serde", serde(rename = "mips"))] - Mips, - #[cfg_attr(feature = "serde", serde(rename = "none"))] - None, -} - -impl Target { - pub fn parse(s: &str) -> Result { - Ok(match s { - "cortex-m" => Target::CortexM, - "msp430" => Target::Msp430, - "riscv" => Target::RISCV, - "xtensa-lx" => Target::XtensaLX, - "mips" => Target::Mips, - "none" => Target::None, - _ => bail!("unknown target {}", s), - }) - } -} - -impl Default for Target { - fn default() -> Self { - Self::CortexM +impl Case { + pub fn cow_to_case<'a>(&self, cow: Cow<'a, str>) -> Cow<'a, str> { + match self { + Self::Constant => match cow { + Cow::Borrowed(s) if s.is_constant_case() => cow, + _ => cow.to_constant_case().into(), + }, + Self::Pascal => match cow { + Cow::Borrowed(s) if s.is_pascal_case() => cow, + _ => to_pascal_case(&cow).into(), + }, + Self::Snake => match cow { + Cow::Borrowed(s) if s.is_snake_case() => cow, + _ => cow.to_snake_case().into(), + }, + } } -} - -#[cfg_attr( - feature = "serde", - derive(serde::Deserialize), - serde(rename_all = "lowercase") -)] -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub enum SourceType { - Xml, - #[cfg(feature = "yaml")] - Yaml, - #[cfg(feature = "json")] - Json, -} -impl Default for SourceType { - fn default() -> Self { - Self::Xml + pub fn sanitize<'a>(&self, s: &'a str) -> Cow<'a, str> { + let s = sanitize(s); + self.cow_to_case(s) } } -impl SourceType { - /// Make a new [`Source`] from a given extension. - pub fn from_extension(s: &str) -> Option { - match s { - "svd" | "xml" => Some(Self::Xml), - #[cfg(feature = "yaml")] - "yml" | "yaml" => Some(Self::Yaml), - #[cfg(feature = "json")] - "json" => Some(Self::Json), - _ => None, - } - } - pub fn from_path(path: &Path) -> Self { - path.extension() - .and_then(|e| e.to_str()) - .and_then(Self::from_extension) - .unwrap_or_default() +fn sanitize(s: &str) -> Cow<'_, str> { + if s.contains(BLACKLIST_CHARS) { + Cow::Owned(s.replace(BLACKLIST_CHARS, "")) + } else { + s.into() } } -/// Convert self string into specific case without overlapping to svd2rust internal names -pub trait ToSanitizedCase { - /// Convert self into PascalCase. - /// - /// Use on name of enumeration values. - fn to_sanitized_pascal_case(&self) -> Cow; - fn to_pascal_case_ident(&self, span: Span) -> Ident { - Ident::new(&self.to_sanitized_pascal_case(), span) - } - /// Convert self into CONSTANT_CASE. - /// - /// Use on name of reader structs, writer structs and enumerations. - fn to_sanitized_constant_case(&self) -> Cow; - fn to_constant_case_ident(&self, span: Span) -> Ident { - Ident::new(&self.to_sanitized_constant_case(), span) - } - /// Convert self into snake_case, must use only if the target is used with extra prefix or suffix. - fn to_sanitized_not_keyword_snake_case(&self) -> Cow; // snake_case - /// Convert self into snake_case target and ensure target is not a Rust keyword. - /// - /// If the sanitized target is a Rust keyword, this function adds an underline `_` - /// to it. - /// - /// Use on name of peripheral modules, register modules and field modules. - fn to_sanitized_snake_case(&self) -> Cow { - let s = self.to_sanitized_not_keyword_snake_case(); - sanitize_keyword(s) - } - fn to_snake_case_ident(&self, span: Span) -> Ident { - Ident::new(&self.to_sanitized_snake_case(), span) - } +pub fn ident(name: &str, config: &Config, fmt: &str, span: Span) -> Ident { + Ident::new( + &config + .ident_formats + .get(fmt) + .expect("Missing {fmt} entry") + .sanitize(name), + span, + ) } -impl ToSanitizedCase for str { - fn to_sanitized_pascal_case(&self) -> Cow { - let s = self.replace(BLACKLIST_CHARS, ""); - - match s.chars().next().unwrap_or('\0') { - '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => { - Cow::from(format!("_{}", s.to_pascal_case())) - } - _ => Cow::from(s.to_pascal_case()), +impl IdentFormat { + pub fn apply<'a>(&self, name: &'a str) -> Cow<'a, str> { + let name = match &self.case { + Some(case) => case.sanitize(name), + _ => sanitize(name), + }; + if self.prefix.is_empty() && self.suffix.is_empty() { + name + } else { + format!("{}{}{}", self.prefix, name, self.suffix).into() } } - fn to_sanitized_constant_case(&self) -> Cow { - let s = self.replace(BLACKLIST_CHARS, ""); - - match s.chars().next().unwrap_or('\0') { - '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => { - Cow::from(format!("_{}", s.to_constant_case())) - } - _ => Cow::from(s.to_constant_case()), + pub fn sanitize<'a>(&self, name: &'a str) -> Cow<'a, str> { + let s = self.apply(name); + let s = if s.as_bytes().first().unwrap_or(&0).is_ascii_digit() { + Cow::from(format!("_{}", s)) + } else { + s + }; + match self.case { + Some(Case::Snake) | None => sanitize_keyword(s), + _ => s, } } - fn to_sanitized_not_keyword_snake_case(&self) -> Cow { - const INTERNALS: [&str; 4] = ["set_bit", "clear_bit", "bit", "bits"]; +} - let s = self.replace(BLACKLIST_CHARS, ""); - match s.chars().next().unwrap_or('\0') { - '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => { - format!("_{}", s.to_snake_case()).into() - } - _ => { - let s = Cow::from(s.to_snake_case()); - if INTERNALS.contains(&s.as_ref()) { - s + "_" - } else { - s - } - } - } - } +pub fn ident_str(name: &str, fmt: &IdentFormat) -> String { + let name = match &fmt.case { + Some(case) => case.sanitize(name), + _ => sanitize(name), + }; + format!("{}{}{}", fmt.prefix, name, fmt.suffix) } pub fn sanitize_keyword(sc: Cow) -> Cow { - const KEYWORDS: [&str; 55] = [ + const KEYWORDS: [&str; 56] = [ "abstract", "alignof", "as", "async", "await", "become", "box", "break", "const", "continue", "crate", "do", "dyn", "else", "enum", "extern", "false", "final", "fn", "for", - "if", "impl", "in", "let", "loop", "macro", "match", "mod", "move", "mut", "offsetof", - "override", "priv", "proc", "pub", "pure", "ref", "return", "self", "sizeof", "static", - "struct", "super", "trait", "true", "try", "type", "typeof", "unsafe", "unsized", "use", - "virtual", "where", "while", "yield", + "gen", "if", "impl", "in", "let", "loop", "macro", "match", "mod", "move", "mut", + "offsetof", "override", "priv", "proc", "pub", "pure", "ref", "return", "self", "sizeof", + "static", "struct", "super", "trait", "true", "try", "type", "typeof", "unsafe", "unsized", + "use", "virtual", "where", "while", "yield", ]; if KEYWORDS.contains(&sc.as_ref()) { sc + "_" @@ -266,7 +156,7 @@ pub fn respace(s: &str) -> String { pub fn escape_brackets(s: &str) -> String { s.split('[') - .fold("".to_string(), |acc, x| { + .fold(String::new(), |acc, x| { if acc.is_empty() { x.to_string() } else if acc.ends_with('\\') { @@ -276,7 +166,7 @@ pub fn escape_brackets(s: &str) -> String { } }) .split(']') - .fold("".to_string(), |acc, x| { + .fold(String::new(), |acc, x| { if acc.is_empty() { x.to_string() } else if acc.ends_with('\\') { @@ -287,18 +177,21 @@ pub fn escape_brackets(s: &str) -> String { }) } -pub fn name_of(maybe_array: &MaybeArray, ignore_group: bool) -> Cow { - match maybe_array { - MaybeArray::Single(info) => info.fullname(ignore_group), - MaybeArray::Array(info, _) => replace_suffix(&info.fullname(ignore_group), "").into(), +/// Escape basic html tags and brackets +pub fn escape_special_chars(s: &str) -> Cow<'_, str> { + if s.contains('[') { + escape_brackets(s).into() + } else { + s.into() } } -pub fn replace_suffix(name: &str, suffix: &str) -> String { - if name.contains("[%s]") { - name.replace("[%s]", suffix) +pub fn name_of(maybe_array: &MaybeArray, ignore_group: bool) -> String { + let fullname = maybe_array.fullname(ignore_group); + if maybe_array.is_array() { + fullname.remove_dim().into() } else { - name.replace("%s", suffix) + fullname.into() } } @@ -362,9 +255,14 @@ pub fn hex(n: u64) -> LitInt { ) } +/// Turns non-zero `n` into an unsuffixed separated hex token +pub fn hex_nonzero(n: u64) -> Option { + (n != 0).then(|| hex(n)) +} + /// Turns `n` into an unsuffixed token -pub fn unsuffixed(n: u64) -> LitInt { - LitInt::new(&n.to_string(), Span::call_site()) +pub fn unsuffixed(n: impl Into) -> LitInt { + LitInt::new(&n.into().to_string(), Span::call_site()) } pub fn unsuffixed_or_bool(n: u64, width: u32) -> Lit { @@ -382,54 +280,54 @@ pub fn new_syn_u32(len: u32, span: Span) -> syn::Expr { }) } -pub fn array_proxy_type(ty: Type, array_info: &DimElement) -> Type { - let span = Span::call_site(); - let inner_path = GenericArgument::Type(ty); - let mut args = Punctuated::new(); - args.push(inner_path); - args.push(GenericArgument::Const(new_syn_u32(array_info.dim, span))); - args.push(GenericArgument::Const(syn::Expr::Lit(syn::ExprLit { - attrs: Vec::new(), - lit: syn::Lit::Int(hex(array_info.dim_increment as u64)), - }))); - let arguments = PathArguments::AngleBracketed(AngleBracketedGenericArguments { - colon2_token: None, - lt_token: Token![<](span), - args, - gt_token: Token![>](span), - }); - - let mut segments = Punctuated::new(); - segments.push(path_segment(Ident::new("crate", span))); - segments.push(PathSegment { - ident: Ident::new("ArrayProxy", span), - arguments, - }); - Type::Path(type_path(segments)) +pub fn zst_type() -> Type { + Type::Tuple(syn::TypeTuple { + paren_token: syn::token::Paren::default(), + elems: Punctuated::new(), + }) } -pub fn name_to_ty(name: &str) -> Type { - let span = Span::call_site(); +pub fn name_to_ty(name: Ident) -> Type { let mut segments = Punctuated::new(); - segments.push(path_segment(name.to_constant_case_ident(span))); + segments.push(path_segment(name)); syn::Type::Path(type_path(segments)) } -pub fn block_path_to_ty(bpath: &svd_parser::expand::BlockPath, span: Span) -> TypePath { - let mut segments = Punctuated::new(); - segments.push(path_segment(Ident::new("crate", span))); - segments.push(path_segment(bpath.peripheral.to_snake_case_ident(span))); +pub fn block_path_to_ty( + bpath: &svd_parser::expand::BlockPath, + config: &Config, + span: Span, +) -> TypePath { + let mut path = config.settings.crate_path.clone().unwrap_or_default().0; + path.segments.push(path_segment(ident( + &bpath.peripheral.remove_dim(), + config, + "peripheral_mod", + span, + ))); for ps in &bpath.path { - segments.push(path_segment(ps.to_snake_case_ident(span))); + path.segments.push(path_segment(ident( + &ps.remove_dim(), + config, + "cluster_mod", + span, + ))); } - type_path(segments) -} - -pub fn register_path_to_ty(rpath: &svd_parser::expand::RegisterPath, span: Span) -> TypePath { - let mut p = block_path_to_ty(&rpath.block, span); - p.path - .segments - .push(path_segment(rpath.name.to_snake_case_ident(span))); + TypePath { qself: None, path } +} + +pub fn register_path_to_ty( + rpath: &svd_parser::expand::RegisterPath, + config: &Config, + span: Span, +) -> TypePath { + let mut p = block_path_to_ty(&rpath.block, config, span); + p.path.segments.push(path_segment(ident( + &rpath.name.remove_dim(), + config, + "register_mod", + span, + ))); p } @@ -439,7 +337,7 @@ pub fn ident_to_path(ident: Ident) -> TypePath { type_path(segments) } -pub fn type_path(segments: Punctuated) -> TypePath { +pub fn type_path(segments: Punctuated) -> TypePath { TypePath { qself: None, path: syn::Path { @@ -469,12 +367,7 @@ impl U32Ext for u32 { 16 => "u16", 32 => "u32", 64 => "u64", - _ => { - return Err(anyhow!( - "can't convert {} bits into register size type", - *self - )) - } + _ => return Err(anyhow!("can't convert {self} bits into register size type")), }) } fn to_ty(&self) -> Result { @@ -487,8 +380,7 @@ impl U32Ext for u32 { 33..=64 => "u64", _ => { return Err(anyhow!( - "can't convert {} bits into a Rust integral type", - *self + "can't convert {self} bits into a Rust integral type" )) } }, @@ -505,16 +397,19 @@ impl U32Ext for u32 { 33..=64 => 64, _ => { return Err(anyhow!( - "can't convert {} bits into a Rust integral type width", - *self + "can't convert {self} bits into a Rust integral type width" )) } }) } } -pub fn build_rs() -> TokenStream { +pub fn build_rs(config: &Config) -> TokenStream { + let extra_build = config.extra_build(); + quote! { + //! Builder file for Peripheral access crate generated by svd2rust tool + use std::env; use std::fs::File; use std::io::Write; @@ -531,6 +426,8 @@ pub fn build_rs() -> TokenStream { println!("cargo:rustc-link-search={}", out.display()); println!("cargo:rerun-if-changed=device.x"); + + #extra_build } println!("cargo:rerun-if-changed=build.rs"); @@ -538,16 +435,38 @@ pub fn build_rs() -> TokenStream { } } +pub trait DimSuffix { + fn expand_dim(&self, suffix: &str) -> Cow; + fn remove_dim(&self) -> Cow { + self.expand_dim("") + } +} + +impl DimSuffix for str { + fn expand_dim(&self, suffix: &str) -> Cow { + if self.contains("%s") { + self.replace(if self.contains("[%s]") { "[%s]" } else { "%s" }, suffix) + .into() + } else { + self.into() + } + } +} + pub trait FullName { fn fullname(&self, ignore_group: bool) -> Cow; } impl FullName for RegisterInfo { fn fullname(&self, ignore_group: bool) -> Cow { - match &self.alternate_group { - Some(group) if !ignore_group => format!("{group}_{}", self.name).into(), - _ => self.name.as_str().into(), - } + fullname(&self.name, &self.alternate_group, ignore_group) + } +} + +pub fn fullname<'a>(name: &'a str, group: &Option, ignore_group: bool) -> Cow<'a, str> { + match &group { + Some(group) if !ignore_group => format!("{group}_{}", name).into(), + _ => name.into(), } } @@ -557,30 +476,40 @@ impl FullName for PeripheralInfo { } } -pub fn group_names(d: &Device) -> Vec> { +pub fn group_names<'a>(d: &'a Device, feature_format: &'a IdentFormat) -> Vec> { let set: HashSet<_> = d .peripherals .iter() .filter_map(|p| p.group_name.as_ref()) - .map(|name| name.to_sanitized_snake_case()) + .map(|name| feature_format.apply(name)) .collect(); let mut v: Vec<_> = set.into_iter().collect(); v.sort(); v } -pub fn peripheral_names(d: &Device) -> Vec { +pub fn peripheral_names(d: &Device, feature_format: &IdentFormat) -> Vec { let mut v = Vec::new(); for p in &d.peripherals { match p { Peripheral::Single(info) => { - v.push(replace_suffix(&info.name.to_sanitized_snake_case(), "")) + v.push(feature_format.apply(&info.name).remove_dim().into()); + } + Peripheral::Array(info, dim) => { + v.extend(svd_rs::array::names(info, dim).map(|n| feature_format.apply(&n).into())); } - Peripheral::Array(info, dim) => v.extend( - svd_rs::array::names(info, dim).map(|n| n.to_sanitized_snake_case().into()), - ), } } v.sort(); v } + +#[test] +fn pascalcase() { + assert_eq!(to_pascal_case("_reserved"), "_Reserved"); + assert_eq!(to_pascal_case("_FOO_BAR_"), "_FooBar_"); + assert_eq!(to_pascal_case("FOO_BAR1"), "FooBar1"); + assert_eq!(to_pascal_case("FOO_BAR_1"), "FooBar1"); + assert_eq!(to_pascal_case("FOO_BAR_1_2"), "FooBar1_2"); + assert_eq!(to_pascal_case("FOO_BAR_1_2_"), "FooBar1_2_"); +} diff --git a/ci/svd2rust-regress/.gitignore b/svd2rust-regress/.gitignore similarity index 100% rename from ci/svd2rust-regress/.gitignore rename to svd2rust-regress/.gitignore diff --git a/svd2rust-regress/Cargo.toml b/svd2rust-regress/Cargo.toml new file mode 100644 index 00000000..9b3fc80e --- /dev/null +++ b/svd2rust-regress/Cargo.toml @@ -0,0 +1,24 @@ +[package] +edition = "2021" +name = "svd2rust-regress" +version = "0.1.0" +authors = ["James Munns ", "The svd2rust developers"] +rust-version = "1.82.0" + +[dependencies] +clap = { version = "4.1", features = ["color", "derive", "string", "env"] } +svd2rust = { path = "../" } +reqwest = { version = "0.12", features = ["blocking", "native-tls-vendored"] } +rayon = "1.4" +anyhow = "1" +thiserror = "1" +serde = "1" +serde_json = "1" +serde_yaml = "0.9" +prettyplease = "0.2" +syn = "2" +wildmatch = "2.1.1" +which = "5.0.0" +tracing = "0.1.40" +tracing-subscriber = { version = "0.3.18", features = ["env-filter", "fmt"] } +shell-words = "1.1" diff --git a/svd2rust-regress/README.md b/svd2rust-regress/README.md new file mode 100644 index 00000000..e4f8da17 --- /dev/null +++ b/svd2rust-regress/README.md @@ -0,0 +1,70 @@ +# `svd2rust` Regression Tester + +`svd2rust-regress` is a helper program for regression testing changes against `svd2rust`. It uses `rayon` to parallelize testing of multiple chips simultaneously. + +## What it does + +`svd2rust-regress` will do the following things for each svd/chip tested: + +1. Create a new crate for that chip in `output/`, populated with the architecture specific dependencies +2. Download the `.svd` file for that chip +3. Run `svd2rust` to generate `output//src/lib.rs` +4. Run `cargo check` to ensure the project still builds + +## Usage + +### Preconditions + +By default, `svd2rust-regress` assumes you have already built `svd2rust` in the root of this repository in `--release` mode. +If this is not possible, it is possible to specify the path to an `svd2rust` binary (see **Options** below). + +You'll also need to have `rustfmt` version > v0.4.0 to use the `--format` flag, you can install `rustfmt` with `rustup component add rustfmt-preview`. + +### Output + +For each test case, `svd2rust-regress` will output the result. + +Pass results look like this: + +```text +Passed: spansion_mb9af12x_k - 23 seconds +``` + +Fail results look like this: + +```text +Failed: si_five_e310x - 0 seconds. Error: Process Failed - cargo check +``` + +If all test cases passed, the return code will be `0`. If any test cases failed, the return code will be `1`. + +### Options + +You can display options for `svd2rust-regress` by running: + +```text +# in the ci/svd2rust-regress folder +cargo regress help +``` + +### Filters + +`svd2rust-regress` allows you to filter which tests will be run. These filters can be combined (but not repeated). + +For example, to run all `RiscV` tests: + +```bash +# in the ci/svd2rust-regress folder +cargo regress tests --architecture riscv +``` + +To run against any chip named `MB9AF12xK`: + +```bash +cargo regress test -c MB9AF12xK +``` + +To run against specifically the `Fujitsu` `MB9AF12xK`: +```bash +cargo regress test -c MB9AF12xK -m Fujitsu +``` diff --git a/svd2rust-regress/all-tests.yml b/svd2rust-regress/all-tests.yml new file mode 100644 index 00000000..06501c71 --- /dev/null +++ b/svd2rust-regress/all-tests.yml @@ -0,0 +1,2139 @@ +- arch: cortex-m + mfgr: Atmel + chip: AT91SAM9CN11 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: AT91SAM9CN12 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: AT91SAM9G09 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: AT91SAM9G15 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: AT91SAM9G20 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: AT91SAM9G25 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: AT91SAM9G35 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: AT91SAM9M10 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: AT91SAM9M11 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: AT91SAM9N12 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: AT91SAM9X25 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: AT91SAM9X35 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3A4C + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3A8C + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3N00A + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3N00B + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3N0A + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3N0B + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3N0C + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3N1A + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3N1B + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3N1C + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3N2A + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3N2B + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3N2C + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3N4A + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3N4B + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3N4C + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3S1A + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3S1B + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3S1C + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3S2A + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3S2B + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3S2C + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3S4A + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3S4B + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3S4C + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3S8B + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3S8C + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3SD8B + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3SD8C + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3U1C + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3U1E + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3U2C + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3U2E + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3U4C + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3U4E + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3X4C + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3X4E + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3X8C + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM3X8E + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM4S16B + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM4S16C + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM4S8B + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM4S8C + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM4SD32B + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAM4SD32C + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAMA5D31 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAMA5D33 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAMA5D34 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAMA5D35 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAMD21E15A + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAMD21E16A + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAMD21E17A + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAMD21E18A + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAMD21G16A + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAMD21G17A + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAMD21G18A + svd_url: https://raw.githubusercontent.com/wez/atsamd21-rs/master/svd/ATSAMD21G18A.svd + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Atmel + chip: ATSAMD21J16A + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAMD21J17A + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAMD21J18A + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAMR21E16A + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAMR21E17A + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAMR21E18A + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAMR21G16A + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAMR21G17A + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Atmel + chip: ATSAMR21G18A + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Freescale + chip: MKV56F20 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Freescale + chip: MKV56F22 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Freescale + chip: MKV56F24 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Freescale + chip: MKV58F20 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Freescale + chip: MKV58F22 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Freescale + chip: MKV58F24 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Freescale + chip: MK61F15 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Freescale + chip: MK61F15WS + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Freescale + chip: MK70F12 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Freescale + chip: MK70F15 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Freescale + chip: MK70F15WS + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Freescale + chip: MK02F12810 +- arch: cortex-m + mfgr: Freescale + chip: MK10D10 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK10D5 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK10D7 +- arch: cortex-m + mfgr: Freescale + chip: MK10DZ10 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK10F12 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK11D5 +- arch: cortex-m + mfgr: Freescale + chip: MK11D5WS + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK11DA5 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK12D5 +- arch: cortex-m + mfgr: Freescale + chip: MK20D10 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK20D5 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK20D7 +- arch: cortex-m + mfgr: Freescale + chip: MK20DZ10 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK20F12 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK21D5 +- arch: cortex-m + mfgr: Freescale + chip: MK21D5WS + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK21DA5 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK21F12 +- arch: cortex-m + mfgr: Freescale + chip: MK21FA12 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK22D5 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK22F12 +- arch: cortex-m + mfgr: Freescale + chip: MK22F12810 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK22F25612 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK22F51212 +- arch: cortex-m + mfgr: Freescale + chip: MK22FA12 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK24F12 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK24F25612 +- arch: cortex-m + mfgr: Freescale + chip: MK26F18 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK30D10 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK30D7 +- arch: cortex-m + mfgr: Freescale + chip: MK30DZ10 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK40D10 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK40D7 +- arch: cortex-m + mfgr: Freescale + chip: MK40DZ10 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK50D10 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK50D7 +- arch: cortex-m + mfgr: Freescale + chip: MK50DZ10 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK51D10 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK51D7 +- arch: cortex-m + mfgr: Freescale + chip: MK51DZ10 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK52D10 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK52DZ10 +- arch: cortex-m + mfgr: Freescale + chip: MK53D10 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK53DZ10 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK60D10 +- arch: cortex-m + mfgr: Freescale + chip: MK60DZ10 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK60F15 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK63F12 +- arch: cortex-m + mfgr: Freescale + chip: MK64F12 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK65F18 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK66F18 +- arch: cortex-m + mfgr: Freescale + chip: MK80F25615 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK81F25615 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MK82F25615 +- arch: cortex-m + mfgr: Freescale + chip: MKE14F16 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKE14Z7 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKE15Z7 +- arch: cortex-m + mfgr: Freescale + chip: MKE16F16 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKE18F16 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKL28T7_CORE0 +- arch: cortex-m + mfgr: Freescale + chip: MKL28T7_CORE1 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKL28Z7 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKL81Z7 +- arch: cortex-m + mfgr: Freescale + chip: MKL82Z7 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKS22F12 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKV10Z1287 +- arch: cortex-m + mfgr: Freescale + chip: MKV10Z7 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKV11Z7 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKV30F12810 +- arch: cortex-m + mfgr: Freescale + chip: MKV31F12810 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKV31F25612 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKV31F51212 +- arch: cortex-m + mfgr: Freescale + chip: MKV40F15 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKV42F16 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKV43F15 +- arch: cortex-m + mfgr: Freescale + chip: MKV44F15 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKV44F16 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKV45F15 +- arch: cortex-m + mfgr: Freescale + chip: MKV46F15 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKV46F16 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKW20Z4 +- arch: cortex-m + mfgr: Freescale + chip: MKW21D5 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKW21Z4 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKW22D5 +- arch: cortex-m + mfgr: Freescale + chip: MKW24D5 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKW30Z4 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKW31Z4 +- arch: cortex-m + mfgr: Freescale + chip: MKW40Z4 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKW41Z4 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKE02Z4 +- arch: cortex-m + mfgr: Freescale + chip: MKE04Z1284 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKE04Z4 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKE06Z4 +- arch: cortex-m + mfgr: Freescale + chip: MKE14D7 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKE15D7 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKL02Z4 +- arch: cortex-m + mfgr: Freescale + chip: MKL03Z4 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKL04Z4 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKL05Z4 +- arch: cortex-m + mfgr: Freescale + chip: MKL13Z644 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKL14Z4 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKL15Z4 +- arch: cortex-m + mfgr: Freescale + chip: MKL16Z4 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKL17Z4 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKL17Z644 +- arch: cortex-m + mfgr: Freescale + chip: MKL24Z4 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKL25Z4 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKL26Z4 +- arch: cortex-m + mfgr: Freescale + chip: MKL27Z4 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKL27Z644 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKL33Z4 +- arch: cortex-m + mfgr: Freescale + chip: MKL33Z644 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKL34Z4 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKL36Z4 +- arch: cortex-m + mfgr: Freescale + chip: MKL43Z4 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKL46Z4 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKM14ZA5 +- arch: cortex-m + mfgr: Freescale + chip: MKM33ZA5 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKM34Z7 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: MKM34ZA5 +- arch: cortex-m + mfgr: Freescale + chip: MKW01Z4 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: SKEAZ1284 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Freescale + chip: SKEAZN642 +- arch: cortex-m + mfgr: Freescale + chip: SKEAZN84 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF10xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF10xR +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF11xK +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF11xL +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF11xM +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF11xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF12xK +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF12xL +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF13xK +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF13xL +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF13xM +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF13xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF14xL +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF14xM +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF14xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF15xM +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF15xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF15xR +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF1AxL +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF1AxM +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF1AxN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF31xK +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF31xL +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF31xM +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF31xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF34xL +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF34xM +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF34xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF42xK +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF42xL +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AFA3xL +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AFA3xM +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AFA3xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AFA4xL +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AFA4xM +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AFA4xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AFAAxL +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AFAAxM +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AFAAxN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AFB4xL +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AFB4xM +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AFB4xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9B160L +- arch: cortex-m + mfgr: Fujitsu + chip: MB9B160R +- arch: cortex-m + mfgr: Fujitsu + chip: MB9B360L +- arch: cortex-m + mfgr: Fujitsu + chip: MB9B360R +- arch: cortex-m + mfgr: Fujitsu + chip: MB9B460L +- arch: cortex-m + mfgr: Fujitsu + chip: MB9B460R +- arch: cortex-m + mfgr: Fujitsu + chip: MB9B560L +- arch: cortex-m + mfgr: Fujitsu + chip: MB9B560R +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF10xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF10xR +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF11xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF11xR +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF11xS +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF11xT +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF12xJ +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF12xK +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF12xL +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF12xM +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF12xS +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF12xT +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF21xS +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF21xT +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF30xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF30xR +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF31xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF31xR +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF31xS +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF31xT +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF32xK +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF32xL +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF32xM +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF32xS +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF32xT +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF40xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF40xR +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF41xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF41xR +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF41xS +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF41xT +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF42xS +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF42xT +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF50xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF50xR +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF51xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF51xR +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF51xS +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF51xT +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF52xK +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF52xL +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF52xM +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF52xS +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF52xT +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF61xS +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF61xT +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BFD1xS +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BFD1xT +- arch: cortex-m + mfgr: Fujitsu + chip: S6E1A1 +- arch: cortex-m + mfgr: Fujitsu + chip: S6E2CC +- arch: cortex-m + mfgr: Holtek + chip: ht32f125x +- arch: cortex-m + mfgr: Holtek + chip: ht32f175x +- arch: cortex-m + mfgr: Holtek + chip: ht32f275x +- arch: cortex-m + mfgr: Nordic + chip: nrf51 +- arch: cortex-m + mfgr: Nordic + chip: nrf52 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Nuvoton + chip: M051_Series + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Nuvoton + chip: NUC100_Series + should_pass: false + run_when: never +- arch: cortex-m + mfgr: NXP + chip: LPC11Exx_v5 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: NXP + chip: LPC11Uxx_v7 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: NXP + chip: LPC11xx_v6a + should_pass: false + run_when: never +- arch: cortex-m + mfgr: NXP + chip: LPC11xx_v6 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: NXP + chip: LPC13Uxx_v1 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: NXP + chip: LPC15xx_v0.7 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: NXP + chip: LPC800_v0.3 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: NXP + chip: LPC11E6x_v0.8 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: NXP + chip: LPC176x5x_v0.2 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: NXP + chip: LPC11Cxx_v9 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: NXP + chip: LPC178x_7x + should_pass: false + run_when: never +- arch: cortex-m + mfgr: NXP + chip: LPC178x_7x_v0.8 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: NXP + chip: LPC408x_7x_v0.7 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: NXP + chip: LPC11Axxv0.6 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: NXP + chip: LPC11D14_svd_v4 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: NXP + chip: LPC13xx_svd_v1 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: NXP + chip: LPC18xx_svd_v18 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: NXP + chip: LPC43xx_43Sxx + should_pass: false + run_when: never +- arch: cortex-m + mfgr: NXP + chip: LPC1102_4_v4 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: NXP + chip: LPC5410x_v0.4 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: NXP + chip: MK22F25612 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: NXP + chip: MK22F51212 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: NXP + chip: MKW41Z4 + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: SiliconLabs + chip: SIM3C1x4_SVD + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: SiliconLabs + chip: SIM3C1x6_SVD + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: SiliconLabs + chip: SIM3C1x7_SVD + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: SiliconLabs + chip: SIM3L1x4_SVD + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: SiliconLabs + chip: SIM3L1x6_SVD + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: SiliconLabs + chip: SIM3L1x7_SVD + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: SiliconLabs + chip: SIM3U1x4_SVD + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: SiliconLabs + chip: SIM3U1x6_SVD + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: SiliconLabs + chip: SIM3U1x7_SVD + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: SiliconLabs + chip: SIM3L1x8_SVD + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Spansion + chip: MB9AF12xK + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9AF12xL + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9AF42xK + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9AF42xL + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9BF12xJ + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9BF12xS + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9BF12xT + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9BF16xx + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9BF32xS + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9BF32xT + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9BF36xx + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9BF42xS + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9BF42xT + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9BF46xx + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9BF52xS + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9BF52xT + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9BF56xx + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9AF10xN + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9AF10xR +- arch: cortex-m + mfgr: Spansion + chip: MB9AF11xK + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9AF11xL +- arch: cortex-m + mfgr: Spansion + chip: MB9AF11xM + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9AF11xN +- arch: cortex-m + mfgr: Spansion + chip: MB9AF13xK + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9AF13xL +- arch: cortex-m + mfgr: Spansion + chip: MB9AF13xM + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9AF13xN +- arch: cortex-m + mfgr: Spansion + chip: MB9AF14xL + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9AF14xM +- arch: cortex-m + mfgr: Spansion + chip: MB9AF14xN + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9AF15xM +- arch: cortex-m + mfgr: Spansion + chip: MB9AF15xN + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9AF15xR +- arch: cortex-m + mfgr: Spansion + chip: MB9AF31xK + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9AF31xL +- arch: cortex-m + mfgr: Spansion + chip: MB9AF31xM + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9AF31xN +- arch: cortex-m + mfgr: Spansion + chip: MB9AF34xL + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9AF34xM +- arch: cortex-m + mfgr: Spansion + chip: MB9AF34xN + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9AFA3xL +- arch: cortex-m + mfgr: Spansion + chip: MB9AFA3xM + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9AFA3xN +- arch: cortex-m + mfgr: Spansion + chip: MB9AFA4xL + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9AFA4xM +- arch: cortex-m + mfgr: Spansion + chip: MB9AFA4xN + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9AFB4xL +- arch: cortex-m + mfgr: Spansion + chip: MB9AFB4xM + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9AFB4xN +- arch: cortex-m + mfgr: Spansion + chip: MB9BF10xN + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9BF10xR +- arch: cortex-m + mfgr: Spansion + chip: MB9BF11xN + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9BF11xR +- arch: cortex-m + mfgr: Spansion + chip: MB9BF11xS + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9BF11xT +- arch: cortex-m + mfgr: Spansion + chip: MB9BF12xK + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9BF12xL +- arch: cortex-m + mfgr: Spansion + chip: MB9BF12xM + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9BF21xS +- arch: cortex-m + mfgr: Spansion + chip: MB9BF21xT + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9BF30xN +- arch: cortex-m + mfgr: Spansion + chip: MB9BF30xR + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9BF31xN +- arch: cortex-m + mfgr: Spansion + chip: MB9BF31xR + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9BF31xS +- arch: cortex-m + mfgr: Spansion + chip: MB9BF31xT + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9BF32xK +- arch: cortex-m + mfgr: Spansion + chip: MB9BF32xL + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9BF32xM +- arch: cortex-m + mfgr: Spansion + chip: MB9BF40xN + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9BF40xR +- arch: cortex-m + mfgr: Spansion + chip: MB9BF41xN + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9BF41xR +- arch: cortex-m + mfgr: Spansion + chip: MB9BF41xS + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9BF41xT +- arch: cortex-m + mfgr: Spansion + chip: MB9BF50xN + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9BF50xR +- arch: cortex-m + mfgr: Spansion + chip: MB9BF51xN + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9BF51xR +- arch: cortex-m + mfgr: Spansion + chip: MB9BF51xS + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9BF51xT +- arch: cortex-m + mfgr: Spansion + chip: MB9BF52xK + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9BF52xL +- arch: cortex-m + mfgr: Spansion + chip: MB9BF52xM + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9BF61xS +- arch: cortex-m + mfgr: Spansion + chip: MB9BF61xT + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: Spansion + chip: MB9BFD1xS +- arch: cortex-m + mfgr: Spansion + chip: MB9BFD1xT + should_pass: true + run_when: not-short +- arch: cortex-m + mfgr: STMicro + chip: STM32F030 +- arch: cortex-m + mfgr: STMicro + chip: STM32F0x2 + svd_url: https://stm32-rs.github.io/stm32-rs/stm32f0x2.svd.patched +- arch: cortex-m + mfgr: STMicro + chip: STM32F103 + svd_url: https://stm32-rs.github.io/stm32-rs/stm32f103.svd.patched +- arch: cortex-m + mfgr: STMicro + chip: STM32F411 + svd_url: https://stm32-rs.github.io/stm32-rs/stm32f411.svd.patched +- arch: cortex-m + mfgr: STMicro + chip: STM32F469 + svd_url: https://stm32-rs.github.io/stm32-rs/stm32f469.svd.patched +- arch: cortex-m + mfgr: STMicro + chip: STM32F723 + svd_url: https://stm32-rs.github.io/stm32-rs/stm32f723.svd.patched +- arch: cortex-m + mfgr: STMicro + chip: STM32G070 + svd_url: https://stm32-rs.github.io/stm32-rs/stm32g070.svd.patched +- arch: cortex-m + mfgr: STMicro + chip: STM32G473 + svd_url: https://stm32-rs.github.io/stm32-rs/stm32g473.svd.patched +- arch: cortex-m + mfgr: STMicro + chip: STM32H753 + svd_url: https://stm32-rs.github.io/stm32-rs/stm32h753.svd.patched +- arch: cortex-m + mfgr: STMicro + chip: STM32L0x3 + svd_url: https://stm32-rs.github.io/stm32-rs/stm32l0x3.svd.patched +- arch: cortex-m + mfgr: STMicro + chip: STM32L162 + svd_url: https://stm32-rs.github.io/stm32-rs/stm32l162.svd.patched +- arch: cortex-m + mfgr: STMicro + chip: STM32L4x6 + svd_url: https://stm32-rs.github.io/stm32-rs/stm32l4x6.svd.patched +- arch: cortex-m + mfgr: STMicro + chip: STM32L562 + svd_url: https://stm32-rs.github.io/stm32-rs/stm32l562.svd.patched +- arch: cortex-m + mfgr: STMicro + chip: STM32MP157 + svd_url: https://stm32-rs.github.io/stm32-rs/stm32mp157.svd.patched +- arch: cortex-m + mfgr: STMicro + chip: STM32WB55 + svd_url: https://stm32-rs.github.io/stm32-rs/stm32wb55.svd.patched +- arch: cortex-m + mfgr: STMicro + chip: STM32WLE5 + svd_url: https://stm32-rs.github.io/stm32-rs/stm32wle5.svd.patched +- arch: cortex-m + mfgr: STMicro + chip: STM32C011 + svd_url: https://stm32-rs.github.io/stm32-rs/stm32c011.svd.patched +- arch: cortex-m + mfgr: STMicro + chip: STM32F031x +- arch: cortex-m + mfgr: STMicro + chip: STM32F042x +- arch: cortex-m + mfgr: STMicro + chip: STM32F072x +- arch: cortex-m + mfgr: STMicro + chip: STM32F091x +- arch: cortex-m + mfgr: STMicro + chip: STM32F0xx +- arch: cortex-m + mfgr: STMicro + chip: STM32F100xx +- arch: cortex-m + mfgr: STMicro + chip: STM32F101xx +- arch: cortex-m + mfgr: STMicro + chip: STM32F102xx +- arch: cortex-m + mfgr: STMicro + chip: STM32F105xx +- arch: cortex-m + mfgr: STMicro + chip: STM32F107xx +- arch: cortex-m + mfgr: STMicro + chip: STM32F20x +- arch: cortex-m + mfgr: STMicro + chip: STM32F21x +- arch: cortex-m + mfgr: STMicro + chip: STM32F301 +- arch: cortex-m + mfgr: STMicro + chip: STM32F302 +- arch: cortex-m + mfgr: STMicro + chip: STM32F303 +- arch: cortex-m + mfgr: STMicro + chip: STM32F3x4 +- arch: cortex-m + mfgr: STMicro + chip: STM32F373 +- arch: cortex-m + mfgr: STMicro + chip: STM32F401 +- arch: cortex-m + mfgr: STMicro + chip: STM32F405 +- arch: cortex-m + mfgr: STMicro + chip: STM32F407 +- arch: cortex-m + mfgr: STMicro + chip: STM32F410 +- arch: cortex-m + mfgr: STMicro + chip: STM32F412 +- arch: cortex-m + mfgr: STMicro + chip: STM32F413 +- arch: cortex-m + mfgr: STMicro + chip: STM32F427 +- arch: cortex-m + mfgr: STMicro + chip: STM32F429 +- arch: cortex-m + mfgr: STMicro + chip: STM32F446 +- arch: cortex-m + mfgr: STMicro + chip: STM32F7x +- arch: cortex-m + mfgr: STMicro + chip: STM32F7x2 +- arch: cortex-m + mfgr: STMicro + chip: STM32F7x5 +- arch: cortex-m + mfgr: STMicro + chip: STM32F7x6 +- arch: cortex-m + mfgr: STMicro + chip: STM32F7x7 +- arch: cortex-m + mfgr: STMicro + chip: STM32F7x9 +- arch: cortex-m + mfgr: STMicro + chip: STM32G07x + should_pass: false + run_when: never +- arch: cortex-m + mfgr: STMicro + chip: STM32G431xx +- arch: cortex-m + mfgr: STMicro + chip: STM32G441xx +- arch: cortex-m + mfgr: STMicro + chip: STM32G471xx +- arch: cortex-m + mfgr: STMicro + chip: STM32G474xx +- arch: cortex-m + mfgr: STMicro + chip: STM32G483xx +- arch: cortex-m + mfgr: STMicro + chip: STM32G484xx +- arch: cortex-m + mfgr: STMicro + chip: STM32L100 +- arch: cortex-m + mfgr: STMicro + chip: STM32L15xC +- arch: cortex-m + mfgr: STMicro + chip: STM32L15xxE +- arch: cortex-m + mfgr: STMicro + chip: STM32L15xxxA +- arch: cortex-m + mfgr: STMicro + chip: STM32L1xx +- arch: cortex-m + mfgr: STMicro + chip: STM32W108 +- arch: cortex-m + mfgr: STMicro + chip: STM32L051x + should_pass: false + run_when: never +- arch: cortex-m + mfgr: STMicro + chip: STM32L052x + should_pass: false + run_when: never +- arch: cortex-m + mfgr: STMicro + chip: STM32L053x + should_pass: false + run_when: never +- arch: cortex-m + mfgr: STMicro + chip: STM32L062x + should_pass: false + run_when: never +- arch: cortex-m + mfgr: STMicro + chip: STM32L063x + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Toshiba + chip: M365 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Toshiba + chip: M367 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Toshiba + chip: M368 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Toshiba + chip: M369 + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Toshiba + chip: M36B + should_pass: false + run_when: never +- arch: cortex-m + mfgr: Toshiba + chip: M061 +- arch: riscv + mfgr: SiFive + chip: E310x + svd_url: https://raw.githubusercontent.com/riscv-rust/e310x/master/e310x/e310x.svd + should_pass: false + run_when: never +- arch: msp430 + mfgr: TexasInstruments + chip: msp430g2553 + svd_url: https://github.com/pftbest/msp430g2553/raw/v0.1.3-svd/msp430g2553.svd +- arch: msp430 + mfgr: TexasInstruments + chip: msp430fr2355 + svd_url: https://raw.githubusercontent.com/YuhanLiin/msp430fr2355/master/msp430fr2355.svd +- arch: xtensa-lx + mfgr: Espressif + chip: esp32 + svd_url: https://raw.githubusercontent.com/espressif/svd/main/svd/esp32.svd +- arch: xtensa-lx + mfgr: Espressif + chip: esp32s2 + svd_url: https://raw.githubusercontent.com/espressif/svd/main/svd/esp32s2.svd +- arch: xtensa-lx + mfgr: Espressif + chip: esp32s3 + svd_url: https://raw.githubusercontent.com/espressif/svd/main/svd/esp32s3.svd +- arch: riscv + mfgr: Espressif + chip: esp32c3 + svd_url: https://raw.githubusercontent.com/espressif/svd/main/svd/esp32c3.svd +- arch: mips + mfgr: Microchip + chip: pic32mx170f256b + svd_url: https://raw.githubusercontent.com/kiffie/pic32-pac/master/pic32mx1xxfxxxb/PIC32MX170F256B.svd.patched +- arch: mips + mfgr: Microchip + chip: pic32mx270f256b + svd_url: https://raw.githubusercontent.com/kiffie/pic32-pac/master/pic32mx2xxfxxxb/PIC32MX270F256B.svd.patched diff --git a/svd2rust-regress/src/ci.rs b/svd2rust-regress/src/ci.rs new file mode 100644 index 00000000..4e4494a0 --- /dev/null +++ b/svd2rust-regress/src/ci.rs @@ -0,0 +1,43 @@ +use crate::Opts; + +#[derive(clap::Parser, Debug)] +#[clap(name = "continuous-integration")] +pub struct Ci { + #[clap(long)] + pub format: bool, + /// Enable splitting `lib.rs` with `form` + #[clap(long)] + pub form_lib: bool, + #[clap(env = "GITHUB_COMMENT")] + pub comment: String, + #[clap(env = "GITHUB_COMMENT_PR")] + pub comment_pr: usize, +} + +#[derive(serde::Serialize)] +struct Diff { + command: String, + needs_semver_checks: bool, + pr: usize, +} + +impl Ci { + pub fn run(&self, _opts: &Opts) -> Result<(), anyhow::Error> { + let mut diffs = vec![]; + for line in self.comment.lines() { + let Some(command) = line.strip_prefix("/ci diff ") else { + continue; + }; + + diffs.push(Diff { + needs_semver_checks: command.contains("semver"), + command: command.to_owned(), + pr: self.comment_pr, + }); + } + let json = serde_json::to_string(&diffs)?; + crate::gha_print(&json); + crate::gha_output("diffs", &json)?; + Ok(()) + } +} diff --git a/svd2rust-regress/src/command.rs b/svd2rust-regress/src/command.rs new file mode 100644 index 00000000..2786166c --- /dev/null +++ b/svd2rust-regress/src/command.rs @@ -0,0 +1,74 @@ +use std::process::Command; + +use anyhow::Context; + +pub trait CommandExt { + #[track_caller] + fn run(&mut self, hide: bool) -> Result<(), anyhow::Error>; + + #[track_caller] + fn run_and_get_output(&mut self, can_fail: bool) + -> Result; + + #[track_caller] + fn get_output_string(&mut self) -> Result; + + fn display(&self) -> String; +} + +impl CommandExt for Command { + #[track_caller] + fn run(&mut self, hide: bool) -> Result<(), anyhow::Error> { + if hide { + self.stdout(std::process::Stdio::null()) + .stdin(std::process::Stdio::null()); + } + let status = self + .status() + .with_context(|| format!("fail! {}", self.display()))?; + if status.success() { + Ok(()) + } else { + anyhow::bail!("command `{}` failed", self.display()) + } + } + + #[track_caller] + fn run_and_get_output( + &mut self, + can_fail: bool, + ) -> Result { + let output = self + .output() + .with_context(|| format!("command `{}` couldn't be run", self.display()))?; + if output.status.success() || can_fail { + Ok(output) + } else { + anyhow::bail!( + "command `{}` failed: stdout: {}\nstderr: {}", + self.display(), + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr), + ) + } + } + + #[track_caller] + fn get_output_string(&mut self) -> Result { + String::from_utf8(self.run_and_get_output(true)?.stdout).map_err(Into::into) + } + + fn display(&self) -> String { + format!( + "{}{} {}", + self.get_current_dir() + .map(|d| format!("{} ", d.display())) + .unwrap_or_default(), + self.get_program().to_string_lossy(), + self.get_args() + .map(|s| s.to_string_lossy()) + .collect::>() + .join(" ") + ) + } +} diff --git a/svd2rust-regress/src/diff.rs b/svd2rust-regress/src/diff.rs new file mode 100644 index 00000000..de4d0d31 --- /dev/null +++ b/svd2rust-regress/src/diff.rs @@ -0,0 +1,370 @@ +use std::path::PathBuf; + +use anyhow::Context; + +use crate::github; +use crate::Opts; + +#[derive(clap::Parser, Debug)] +#[clap(name = "diff")] +pub struct Diffing { + /// The base version of svd2rust to use and the command input, defaults to latest master build: `@master` + /// + /// Change the base version by starting with `@` followed by the source. + /// + /// supports `@pr` for current pr, `@master` for latest master build, or a version tag like `@v0.30.0` + #[clap(global = true, long = "baseline", alias = "base")] + pub baseline: Option, + + /// The head version of svd2rust to use and the command input, defaults to current pr: `@pr` + #[clap(global = true, long = "current", alias = "head")] + pub current: Option, + + /// Enable formatting with `rustfmt` + #[clap(global = true, short = 'f', long)] + pub format: bool, + + /// Enable splitting `lib.rs` with `form` + #[clap(global = true, long)] + pub form_split: bool, + + #[clap(global = true, long, short = 'c')] + pub chip: Vec, + + /// Filter by manufacturer, case sensitive, may be combined with other filters + #[clap( + global = true, + short = 'm', + long = "manufacturer", + ignore_case = true, + value_parser = crate::manufacturers(), + )] + pub mfgr: Option, + + /// Filter by architecture, case sensitive, may be combined with other filters + #[clap( + global = true, + short = 'a', + long = "architecture", + ignore_case = true, + value_parser = crate::architectures(), + )] + pub arch: Option, + + #[clap(global = true, long)] + pub diff_folder: Option, + + /// The pr number to use for `@pr`. If not set will try to get the current pr with the command `gh pr` + #[clap(env = "GITHUB_PR", global = true, long)] + pub pr: Option, + + #[clap(env = "GIT_PAGER", global = true, long)] + pub pager: Option, + + /// if set, will use pager directly instead of `git -c core.pager` + #[clap(global = true, long, short = 'P')] + pub use_pager_directly: bool, + + /// URL for SVD to download + #[clap(global = true, long)] + pub url: Option, + + #[clap(subcommand)] + pub sub: Option, + + #[clap(last = true)] + pub last_args: Option, +} + +#[derive(clap::Parser, Debug, Clone)] +pub enum DiffingMode { + Semver { + #[clap(last = true)] + last_args: Option, + }, + Diff { + #[clap(last = true)] + last_args: Option, + }, + Pr { + #[clap(last = true)] + last_args: Option, + }, +} + +impl DiffingMode { + /// Returns `true` if the diffing mode is [`Pr`]. + /// + /// [`Pr`]: DiffingMode::Pr + #[must_use] + pub fn is_pr(&self) -> bool { + matches!(self, Self::Pr { .. }) + } +} + +type Source<'s> = Option<&'s str>; +type Command<'s> = Option<&'s str>; + +impl Diffing { + pub fn run(&self, opts: &Opts) -> Result<(), anyhow::Error> { + let [baseline, current] = self + .make_case(opts) + .with_context(|| "couldn't setup test case")?; + match self.sub.as_ref() { + None | Some(DiffingMode::Diff { .. } | DiffingMode::Pr { .. }) => { + let mut command; + if let Some(pager) = &self.pager { + if self.use_pager_directly { + let mut pager = pager.split_whitespace(); + command = std::process::Command::new(pager.next().unwrap()); + command.args(pager); + } else { + command = std::process::Command::new("git"); + command.env("GIT_PAGER", pager); + } + } else { + command = std::process::Command::new("git"); + command.arg("--no-pager"); + } + if !self.use_pager_directly { + command.args(["diff", "--no-index", "--minimal"]); + } + command + .args([&*baseline.0, &*current.0]) + .status() + .with_context(|| "couldn't run diff") + .map(|_| ()) + } + Some(DiffingMode::Semver { .. }) => std::process::Command::new("cargo") + .args(["semver-checks", "check-release"]) + .arg("--baseline-root") + .arg(baseline.0) + .arg("--manifest-path") + .arg(current.0.join("Cargo.toml")) + .status() + .with_context(|| "couldn't run semver-checks") + .map(|_| ()), + } + } + + pub fn make_case(&self, opts: &Opts) -> Result<[(PathBuf, Vec); 2], anyhow::Error> { + let [(baseline_bin, baseline_cmd), (current_bin, current_cmd)] = self + .svd2rust_setup(opts) + .with_context(|| "couldn't setup svd2rust")?; + let tests = crate::tests::tests(Some(opts.test_cases.as_ref())) + .with_context(|| "no tests found")?; + + let tests = tests + .iter() + .filter(|t| { + if let Some(ref arch) = self.arch { + arch.to_ascii_lowercase() + .eq_ignore_ascii_case(&t.arch.to_string()) + } else { + true + } + }) + // selected manufacturer? + .filter(|t| { + if let Some(ref mfgr) = self.mfgr { + mfgr.to_ascii_lowercase() + .eq_ignore_ascii_case(&t.mfgr.to_string().to_ascii_lowercase()) + } else { + true + } + }) + .filter(|t| { + if self.chip.is_empty() { + true + } else { + self.chip.iter().any(|c| { + wildmatch::WildMatch::new(&c.to_ascii_lowercase()) + .matches(&t.chip.to_ascii_lowercase()) + }) + } + }) + .collect::>(); + + let test = match (tests.len(), self.sub.as_ref(), self.url.as_ref()) { + (1, _, None) => tests[0].clone(), + (_, Some(DiffingMode::Pr { .. } | DiffingMode::Semver { .. }), None) => tests + .iter() + .find(|t| t.chip == "STM32F103") + .map(|t| (*t).clone()) + .unwrap_or_else(|| tests[0].clone()), + (_, _, Some(url)) => crate::tests::TestCase { + arch: self + .arch + .clone() + .map(|s| svd2rust::Target::parse(&s)) + .transpose()? + .unwrap_or_default(), + mfgr: crate::tests::Manufacturer::Unknown, + chip: url + .rsplit('/') + .next() + .and_then(|file| file.split('.').next()) + .ok_or_else(|| anyhow::anyhow!("couldn't get chip name from url"))? + .to_owned(), + svd_url: Some(url.to_owned()), + should_pass: true, + skip_check: false, + suffix: Default::default(), + opts: Default::default(), + run_when: crate::tests::RunWhen::Always, + }, + _ => { + let error = anyhow::anyhow!("diff requires exactly one test case"); + let len = tests.len(); + return Err(match len { + 0 => error.context("matched no tests"), + 10.. => error.context(format!("matched multiple ({len}) tests")), + _ => error.context(format!( + "matched multiple ({len}) tests\n{:?}", + tests.iter().map(|t| t.name()).collect::>() + )), + }); + } + }; + + let last_args = self.last_args.as_deref().or(match &self.sub { + Some( + DiffingMode::Diff { last_args } + | DiffingMode::Pr { last_args } + | DiffingMode::Semver { last_args }, + ) => last_args.as_deref(), + None => None, + }); + let join = |opt1: Option<&str>, opt2: Option<&str>| -> Option> { + match (opt1, opt2) { + (Some(str1), Some(str2)) => vec![str1.to_owned(), str2.to_owned()].into(), + (Some(str), None) | (None, Some(str)) => Some(vec![str.to_owned()]), + (None, None) => None, + } + }; + let baseline = test + .setup_case( + &opts.output_dir.join("baseline"), + &baseline_bin, + &join(baseline_cmd, last_args), + ) + .with_context(|| "couldn't create head")?; + let current = test + .setup_case( + &opts.output_dir.join("current"), + ¤t_bin, + &join(current_cmd, last_args), + ) + .with_context(|| "couldn't create base")?; + + Ok([baseline, current]) + } + + fn get_source_and_command(&self) -> [Option<(Source, Command)>; 2] { + fn split(s: &str) -> (Source, Command) { + if let Some(s) = s.strip_prefix('@') { + if let Some((source, cmd)) = s.split_once(' ') { + (Some(source), Some(cmd.trim())) + } else { + (Some(s), None) + } + } else { + (None, Some(s.trim())) + } + } + + let baseline = self.baseline.as_deref().map(split); + + let current = self.current.as_deref().map(split); + [baseline, current] + } + + pub fn svd2rust_setup(&self, opts: &Opts) -> Result<[(PathBuf, Command); 2], anyhow::Error> { + // FIXME: refactor this to be less ugly + let [baseline_sc, current_sc] = self.get_source_and_command(); + let baseline = match baseline_sc.and_then(|(source, _)| source) { + reference @ (None | Some("" | "master")) => { + github::get_release_binary_artifact(reference.unwrap_or("master"), &opts.output_dir) + .with_context(|| "couldn't get svd2rust latest unreleased artifact")? + } + Some("pr") if self.pr.is_none() => { + let (number, sha) = + github::get_current_pr().with_context(|| "couldn't get current pr")?; + github::get_pr_binary_artifact(number, &sha, &opts.output_dir) + .with_context(|| "couldn't get pr artifact")? + } + Some("pr") => { + let (number, sha) = + github::get_pr(self.pr.unwrap()).with_context(|| "couldn't get current pr")?; + github::get_pr_binary_artifact(number, &sha, &opts.output_dir) + .with_context(|| "couldn't get pr artifact")? + } + Some("debug") => crate::get_cargo_metadata() + .target_directory + .join(format!("debug/svd2rust{}", std::env::consts::EXE_SUFFIX)), + Some("release") => crate::get_cargo_metadata() + .target_directory + .join(format!("release/svd2rust{}", std::env::consts::EXE_SUFFIX)), + Some(reference) => github::get_release_binary_artifact(reference, &opts.output_dir) + .with_context(|| format!("could not get svd2rust for {reference}"))?, + }; + + let current = match current_sc.and_then(|(source, _)| source) { + None | Some("" | "pr") if self.pr.is_none() => { + let (number, sha) = + github::get_current_pr().with_context(|| "couldn't get current pr")?; + github::get_pr_binary_artifact(number, &sha, &opts.output_dir) + .with_context(|| "couldn't get pr artifact")? + } + None | Some("" | "pr") => { + let (number, sha) = + github::get_pr(self.pr.unwrap()).with_context(|| "couldn't get current pr")?; + github::get_pr_binary_artifact(number, &sha, &opts.output_dir) + .with_context(|| "couldn't get pr artifact")? + } + Some("debug") => crate::get_cargo_metadata() + .target_directory + .join(format!("debug/svd2rust{}", std::env::consts::EXE_SUFFIX)), + Some("release") => crate::get_cargo_metadata() + .target_directory + .join(format!("release/svd2rust{}", std::env::consts::EXE_SUFFIX)), + Some(reference) => github::get_release_binary_artifact(reference, &opts.output_dir) + .with_context(|| format!("could not get svd2rust for {reference}"))?, + }; + + Ok([ + ( + baseline.canonicalize()?, + baseline_sc.and_then(|(_, cmd)| cmd), + ), + (current.canonicalize()?, current_sc.and_then(|(_, cmd)| cmd)), + ]) + } +} + +#[cfg(test)] +#[test] +pub fn diffing_cli_works() { + use clap::Parser; + + Diffing::parse_from(["diff", "pr"]); + Diffing::parse_from(["diff", "--base", "", "--head", "\"--atomics\""]); + Diffing::parse_from(["diff", "--base", "\"@master\"", "--head", "\"@pr\""]); + Diffing::parse_from([ + "diff", + "--base", + "\"@master\"", + "--head", + "\"@pr\"", + "--chip", + "STM32F401", + ]); + Diffing::parse_from([ + "diff", + "--base", + "\"@master\"", + "--head", + "\"@pr --atomics\"", + ]); + Diffing::parse_from(["diff", "--head", "\"--atomics\""]); +} diff --git a/svd2rust-regress/src/github.rs b/svd2rust-regress/src/github.rs new file mode 100644 index 00000000..5a9bbf2b --- /dev/null +++ b/svd2rust-regress/src/github.rs @@ -0,0 +1,230 @@ +use std::process::Command; +use std::{ffi::OsStr, path::Path}; +use std::{iter::IntoIterator, path::PathBuf}; + +use anyhow::Context; + +use crate::command::CommandExt; + +pub fn run_gh(args: I) -> Command +where + I: IntoIterator, + S: AsRef, +{ + let mut command = Command::new("gh"); + command.args(args); + command +} + +pub fn get_current_pr() -> Result<(usize, String), anyhow::Error> { + #[derive(serde::Deserialize)] + struct Pr { + number: usize, + #[serde(rename = "headRefOid")] + head_ref_oid: String, + } + let pr = run_gh(["pr", "view", "--json", "headRefOid,number"]).get_output_string()?; + let Pr { + number, + head_ref_oid, + } = serde_json::from_str(&pr)?; + + Ok((number, head_ref_oid)) +} + +pub fn get_pr(pr: usize) -> Result<(usize, String), anyhow::Error> { + #[derive(serde::Deserialize)] + struct Pr { + number: usize, + #[serde(rename = "headRefOid")] + head_ref_oid: String, + } + let pr = run_gh(["pr", "view", &pr.to_string(), "--json", "headRefOid,number"]) + .get_output_string()?; + let Pr { + number, + head_ref_oid, + } = serde_json::from_str(&pr)?; + + Ok((number, head_ref_oid)) +} + +pub fn get_sha_run_id(sha: &str) -> Result { + let run_id = run_gh([ + "api", + &format!("repos/:owner/:repo/actions/runs?event=pull_request&head_sha={sha}"), + "--jq", + r#"[.workflow_runs[] | select(.name == "Continuous integration")][0] | .id"#, + ]) + .get_output_string()?; + if run_id.trim().is_empty() { + anyhow::bail!("no run id found for sha `{}`", sha); + } + run_id + .trim() + .parse() + .with_context(|| anyhow::anyhow!("couldn't parse api output: {run_id}")) +} + +pub fn get_release_run_id(event: &str) -> Result { + let query = match event { + "master" => "branch=master".to_owned(), + _ => anyhow::bail!("unknown event"), + }; + let run_id = run_gh([ + "api", + &format!("repos/:owner/:repo/actions/runs?{query}"), + "--jq", + r#"[.workflow_runs[] | select(.name == "release")][0] | .id"#, + ]) + .get_output_string()?; + run_id.trim().parse().map_err(Into::into) +} + +fn find_executable(dir: &Path, begins: &str) -> Result, anyhow::Error> { + let find = |entry, begins: &str| -> Result, std::io::Error> { + let entry: std::fs::DirEntry = entry?; + let filename = entry.file_name(); + let filename = filename.to_string_lossy(); + if entry.metadata()?.is_file() + && filename.starts_with(begins) + && (entry.path().extension().is_none() + || entry + .path() + .extension() + .is_some_and(|s| s == std::env::consts::EXE_EXTENSION)) + && entry.path().extension().is_none_or(|s| s != "gz") + { + Ok(Some(entry.path())) + } else { + Ok(None) + } + }; + let mut read_dir = std::fs::read_dir(dir)?; + read_dir + .find_map(|entry| find(entry, begins).transpose()) + .transpose() + .map_err(Into::into) +} + +pub fn get_release_binary_artifact( + reference: &str, + output_dir: &Path, +) -> Result { + let output_dir = output_dir.join(".binary").join(reference); + + match reference { + reference if reference.starts_with('v') || matches!(reference, "master" | "latest") => { + let tag = if reference == "master" { + Some("Unreleased") + } else if reference == "latest" { + None + } else { + Some(reference) + }; + + let artifact = if cfg!(target_os = "linux") && cfg!(target_arch = "x86_64") { + "svd2rust-x86_64-unknown-linux-gnu.gz" + } else if cfg!(target_os = "linux") && cfg!(target_arch = "aarch64") { + "svd2rust-aarch64-unknown-linux-gnu.gz" + } else if cfg!(windows) { + "svd2rust-x86_64-pc-windows-msvc.exe" + } else if cfg!(target_os = "macos") && cfg!(target_arch = "x86_64") { + "svd2rust-x86_64-apple-darwin.gz" + } else if cfg!(target_os = "macos") && cfg!(target_arch = "aarch64") { + "svd2rust-aarch64-apple-darwin.gz" + } else { + anyhow::bail!("regress with release artifact doesn't support current platform") + }; + + std::fs::remove_dir_all(&output_dir).ok(); + + run_gh(["release", "download", "--pattern", artifact, "--dir"]) + .arg(&output_dir) + .args(tag) + .run(true)?; + + if cfg!(target_os = "linux") || cfg!(target_os = "macos") { + Command::new("gzip") + .arg("-d") + .arg(output_dir.join(artifact)) + .run_and_get_output(false)?; + } + } + _ => { + let run_id = + get_release_run_id(reference).with_context(|| "couldn't get release run id")?; + run_gh([ + "run", + "download", + &run_id.to_string(), + "-n", + "svd2rust-x86_64-unknown-linux-gnu", + "--dir", + ]) + .arg(&output_dir) + .run(true)?; + } + } + let binary = + find_executable(&output_dir, "svd2rust").with_context(|| "couldn't find svd2rust")?; + let binary = binary.ok_or_else(|| anyhow::anyhow!("no binary found"))?; + + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + std::fs::set_permissions(&binary, std::fs::Permissions::from_mode(0o755))?; + } + + Ok(binary) +} + +pub fn get_pr_binary_artifact( + pr: usize, + sha: &str, + output_dir: &Path, +) -> Result { + let output_dir = output_dir.join(".binary").join(pr.to_string()).join(sha); + + if let Some(binary) = find_executable(&output_dir, "svd2rust").unwrap_or_default() { + return Ok(binary); + } + + let target = if cfg!(target_os = "linux") && cfg!(target_arch = "x86_64") { + "x86_64-unknown-linux-gnu" + } else if cfg!(target_os = "linux") && cfg!(target_arch = "aarch64") { + "aarch64-unknown-linux-gnu" + } else if cfg!(windows) { + "x86_64-pc-windows-msvc" + } else if cfg!(target_os = "macos") && cfg!(target_arch = "x86_64") { + "x86_64-apple-darwin" + } else if cfg!(target_os = "macos") && cfg!(target_arch = "aarch64") { + "aarch64-apple-darwin" + } else { + anyhow::bail!("regress with pr artifact doesn't support current platform"); + }; + + let run_id = get_sha_run_id(sha).context("when getting run id")?; + run_gh([ + "run", + "download", + &run_id.to_string(), + "-n", + &format!("artifact-svd2rust-{}", target), + "--dir", + ]) + .arg(&output_dir) + .run(true)?; + + let binary = + find_executable(&output_dir, "svd2rust").with_context(|| "couldn't find svd2rust")?; + let binary = binary.ok_or_else(|| anyhow::anyhow!("no binary found"))?; + + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + std::fs::set_permissions(&binary, std::fs::Permissions::from_mode(0o755))?; + } + + Ok(binary) +} diff --git a/svd2rust-regress/src/main.rs b/svd2rust-regress/src/main.rs new file mode 100644 index 00000000..f1a4ae6a --- /dev/null +++ b/svd2rust-regress/src/main.rs @@ -0,0 +1,597 @@ +pub mod ci; +pub mod command; +pub mod diff; +pub mod github; +mod svd_test; +mod tests; + +use anyhow::Context; +use ci::Ci; +use diff::Diffing; + +use clap::Parser; +use rayon::prelude::*; +use std::fs::File; +use std::io::Read; +use std::path::PathBuf; +use std::process::{exit, Command}; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::time::Instant; +use svd_test::WorkspaceTomlGuard; +use wildmatch::WildMatch; + +#[derive(Debug, serde::Deserialize)] +pub struct CargoMetadata { + workspace_root: PathBuf, + target_directory: PathBuf, +} + +static RUSTFMT: std::sync::OnceLock = std::sync::OnceLock::new(); +static FORM: std::sync::OnceLock = std::sync::OnceLock::new(); + +/// Returns the cargo metadata +pub fn get_cargo_metadata() -> &'static CargoMetadata { + static WORKSPACE: std::sync::OnceLock = std::sync::OnceLock::new(); + WORKSPACE.get_or_init(|| { + std::process::Command::new("cargo") + .args(["metadata", "--format-version", "1"]) + .output() + .map(|v| String::from_utf8(v.stdout)) + .unwrap() + .map_err(anyhow::Error::from) + .and_then(|s: String| serde_json::from_str::(&s).map_err(Into::into)) + .unwrap() + }) +} + +/// Returns the cargo workspace for the manifest +#[must_use] +pub fn get_cargo_workspace() -> &'static std::path::Path { + &get_cargo_metadata().workspace_root +} + +#[derive(clap::Parser, Debug)] +pub struct TestAll { + /// Run a long test (it's very long) + #[clap(short = 'l', long)] + pub long_test: bool, + + /// Filter by chip name, case sensitive, may be combined with other filters + #[clap(short = 'c', long)] + pub chip: Vec, + + /// Filter by manufacturer, may be combined with other filters + #[clap( + short = 'm', + long = "manufacturer", + ignore_case = true, + value_parser = manufacturers(), + )] + pub mfgr: Option, + + /// Filter by architecture, may be combined with other filters + #[clap( + short = 'a', + long = "architecture", + ignore_case = true, + value_parser = architectures(), + )] + pub arch: Option, + + /// Include tests expected to fail (will cause a non-zero return code) + #[clap(short = 'b', long)] + pub bad_tests: bool, + + /// Enable formatting with `rustfmt` + #[clap(short = 'f', long)] + pub format: bool, + + #[clap(long)] + /// Enable splitting `lib.rs` with `form` + pub form_lib: bool, + + /// Check generated crates with clippy. + #[clap(long)] + pub clippy: bool, + + /// Check documentation build with stable. + #[clap(long)] + pub docs_stable: bool, + + /// Check documentation build with nightly settings (docs.rs equivalent). + #[clap(long)] + pub docs_nightly: bool, + + /// Print all available test using the specified filters + #[clap(long)] + pub list: bool, + + /// Path to an `svd2rust` binary, relative or absolute. + /// Defaults to `target/release/svd2rust[.exe]` of this repository + /// (which must be already built) + #[clap(short = 'p', long = "svd2rust-path", default_value = default_svd2rust())] + pub current_bin_path: PathBuf, + #[clap(last = true)] + pub passthrough_opts: Option>, + // TODO: Specify smaller subset of tests? Maybe with tags? + // TODO: Compile svd2rust? +} + +#[derive(clap::Parser, Debug)] +// TODO: Replace with https://github.com/clap-rs/clap/issues/2621 when available +#[group(id = "svd_source", required = true)] +pub struct Test { + /// Enable formatting with `rustfmt` + #[arg(short = 'f', long)] + pub format: bool, + + #[arg(long)] + /// Enable splitting `lib.rs` with `form` + pub form_lib: bool, + + #[arg( + short = 'm', + long = "manufacturer", + ignore_case = true, + value_parser = manufacturers(), + )] + /// Manufacturer + pub mfgr: Option, + #[arg( + short = 'a', + long = "architecture", + ignore_case = true, + value_parser = architectures(), + )] + /// Architecture + pub arch: Option, + #[arg(long, group = "svd_source", conflicts_with_all = ["svd_file"], requires = "arch")] + /// URL to SVD file to test + pub url: Option, + #[arg(long = "svd", group = "svd_source")] + /// Path to SVD file to test + pub svd_file: Option, + #[arg(short = 'c', long, group = "svd_source")] + /// Chip to use, use `--url` or `--svd-file` for another way to specify svd + pub chip: Option, + + /// Check generated crate with clippy. + #[arg(long)] + pub clippy: bool, + + /// Check documentation build with stable. + #[clap(long)] + pub docs_stable: bool, + + /// Check documentation build with nightly settings (docs.rs equivalent). + #[clap(long)] + pub docs_nightly: bool, + + /// Path to an `svd2rust` binary, relative or absolute. + /// Defaults to `target/release/svd2rust[.exe]` of this repository + /// (which must be already built) + #[clap(short = 'p', long = "svd2rust-path", default_value = default_svd2rust())] + pub current_bin_path: PathBuf, + #[clap(last = true)] + pub passthrough_opts: Option>, +} + +impl Test { + fn run(&self, opts: &Opts) -> Result<(), anyhow::Error> { + match self { + Self { url: Some(_), .. } => {} + Self { + svd_file: Some(_), .. + } => {} + Self { chip: Some(_), .. } => {} + _ => unreachable!("clap should not allow this"), + } + let _toml_guard = WorkspaceTomlGuard::new()?; + let test = if let (Some(url), Some(arch)) = (&self.url, &self.arch) { + tests::TestCase { + arch: svd2rust::Target::parse(arch)?, + mfgr: tests::Manufacturer::Unknown, + chip: self + .chip + .as_deref() + .or_else(|| url.rsplit('/').next().and_then(|s| s.strip_suffix(".svd"))) + .ok_or_else(|| { + anyhow::anyhow!( + "could not figure out chip name, specify with `--chip `", + ) + })? + .to_owned(), + svd_url: Some(url.clone()), + should_pass: true, + skip_check: false, + suffix: Default::default(), + opts: Default::default(), + run_when: tests::RunWhen::default(), + } + } else { + tests::tests(Some(&opts.test_cases))? + .iter() + .find(|t| self.chip.iter().any(|c| WildMatch::new(c).matches(&t.chip))) + .ok_or_else(|| anyhow::anyhow!("no test found for chip"))? + .to_owned() + }; + test.test( + opts, + &self.current_bin_path, + self.clippy, + self.docs_stable, + self.docs_nightly, + &self.passthrough_opts, + )?; + Ok(()) + } +} + +impl TestAll { + fn run(&self, opt: &Opts) -> Result<(), anyhow::Error> { + let tests = tests::tests(Some(&opt.test_cases))? + .iter() + // Short test? + .filter(|t| t.should_run(!self.long_test)) + // selected architecture? + .filter(|t| { + if let Some(ref arch) = self.arch { + arch.to_ascii_lowercase() + .eq_ignore_ascii_case(&t.arch.to_string()) + } else { + true + } + }) + // selected manufacturer? + .filter(|t| { + if let Some(ref mfgr) = self.mfgr { + mfgr.to_ascii_lowercase() + .eq_ignore_ascii_case(&t.mfgr.to_string().to_ascii_lowercase()) + } else { + true + } + }) + // Specify chip - note: may match multiple + .filter(|t| { + if self.chip.is_empty() { + // Don't run failable tests unless wanted + self.bad_tests || t.should_pass + } else { + self.chip.iter().any(|c| WildMatch::new(c).matches(&t.chip)) + } + }) + .collect::>(); + if self.list { + // FIXME: Prettier output + println!("{:?}", tests.iter().map(|t| t.name()).collect::>()); + exit(0); + } + if tests.is_empty() { + tracing::error!( + "No tests run, you might want to use `--bad-tests` and/or `--long-test`" + ); + } + + let toml_guard = WorkspaceTomlGuard::new()?; + + let any_fails = AtomicBool::new(false); + tests.par_iter().for_each(|t| { + let start = Instant::now(); + + match t.test( + opt, + &self.current_bin_path, + self.clippy, + self.docs_stable, + self.docs_nightly, + &self.passthrough_opts, + ) { + Ok(s) => { + if let Some(stderrs) = s { + let mut buf = String::new(); + for stderr in stderrs { + read_file(&stderr, &mut buf); + } + tracing::info!( + "Passed: {} - {} seconds\n{}", + t.name(), + start.elapsed().as_secs(), + buf + ); + } else { + tracing::info!( + "Passed: {} - {} seconds", + t.name(), + start.elapsed().as_secs() + ); + } + } + Err(e) => { + any_fails.store(true, Ordering::Release); + let additional_info = if opt.verbose > 0 { + match &e { + svd_test::TestError::Process(svd_test::ProcessFailed { + stderr: Some(ref stderr), + previous_processes_stderr, + .. + }) => { + let mut buf = String::new(); + if opt.verbose > 1 { + for stderr in previous_processes_stderr { + read_file(stderr, &mut buf); + } + } + read_file(stderr, &mut buf); + buf + } + _ => String::new(), + } + } else { + String::new() + }; + tracing::error!( + "Failed: {} - {} seconds. {:?}{}", + t.name(), + start.elapsed().as_secs(), + anyhow::Error::new(e), + additional_info, + ); + } + } + }); + drop(toml_guard); + + if any_fails.load(Ordering::Acquire) { + exit(1); + } else { + exit(0); + } + } +} + +#[derive(clap::Subcommand, Debug)] +pub enum Subcommand { + Diff(Diffing), + Tests(TestAll), + Test(Test), + Ci(Ci), +} + +#[derive(Parser, Debug)] +#[command(name = "svd2rust-regress")] +pub struct Opts { + /// Use verbose output + #[clap(global = true, long, short = 'v', action = clap::ArgAction::Count)] + pub verbose: u8, + + /// Path to an `rustfmt` binary, relative or absolute. + /// Defaults to `$(rustup which rustfmt)` + #[clap(global = true, long)] + pub rustfmt_bin_path: Option, + + /// Path to a `form` binary, relative or absolute. + /// Defaults to `form` + #[clap(global = true, long)] + pub form_bin_path: Option, + + /// Specify what rustup toolchain to use when compiling chip(s) + #[clap(global = true, long = "toolchain")] // , env = "RUSTUP_TOOLCHAIN" + pub rustup_toolchain: Option, + + /// Test cases to run + #[clap(global = true, long, default_value = default_test_cases())] + pub test_cases: std::path::PathBuf, + + #[clap(global = true, long, short, default_value = "output")] + pub output_dir: std::path::PathBuf, + + #[clap(subcommand)] + subcommand: Subcommand, +} + +impl Opts { + const fn use_rustfmt(&self) -> bool { + match self.subcommand { + Subcommand::Tests(TestAll { format, .. }) + | Subcommand::Test(Test { format, .. }) + | Subcommand::Diff(Diffing { format, .. }) + | Subcommand::Ci(Ci { format, .. }) => format, + } + } + + const fn use_form(&self) -> bool { + match self.subcommand { + Subcommand::Tests(TestAll { form_lib, .. }) + | Subcommand::Test(Test { form_lib, .. }) + | Subcommand::Diff(Diffing { + form_split: form_lib, + .. + }) + | Subcommand::Ci(Ci { form_lib, .. }) => form_lib, + } + } +} + +/// Hack to use svd2rust-regress/tests.yml as default value when running as `cargo run` +fn default_test_cases() -> std::ffi::OsString { + std::env::var_os("CARGO_MANIFEST_DIR").map_or_else( + || std::ffi::OsString::from("tests.yml".to_owned()), + |path| { + let path = std::path::PathBuf::from(path); + let path = path.join("tests.yml"); + path.to_owned().into_os_string() + }, + ) +} + +fn default_svd2rust() -> std::ffi::OsString { + get_cargo_workspace() + .join(format!( + "target/release/svd2rust{}", + std::env::consts::EXE_SUFFIX, + )) + .into_os_string() +} + +fn architectures() -> Vec { + svd2rust::Target::all() + .iter() + .map(|arch| clap::builder::PossibleValue::new(arch.to_string())) + .collect() +} + +fn manufacturers() -> Vec { + tests::Manufacturer::all() + .iter() + .map(|mfgr| clap::builder::PossibleValue::new(mfgr.to_string())) + .collect() +} + +/// Validate any assumptions made by this program +fn validate_tests(tests: &[tests::TestCase]) { + use std::collections::HashSet; + + let mut fail = false; + + // CONDITION 1: All mfgr+chip names must be unique + let mut uniq = HashSet::new(); + for t in tests { + let name = t.name(); + if !uniq.insert(name.clone()) { + fail = true; + tracing::info!("{} is not unique!", name); + } + } + + assert!(!fail, "Tests failed validation"); +} + +fn read_file(path: &PathBuf, buf: &mut String) { + if buf.is_empty() { + buf.push_str(&format!("{}\n", path.display())); + } else { + buf.push_str(&format!("\n{}\n", path.display())); + } + File::open(path) + .expect("Couldn't open file") + .read_to_string(buf) + .expect("Couldn't read file to string"); +} + +fn main() -> Result<(), anyhow::Error> { + let opt = Opts::parse(); + tracing_subscriber::fmt() + .pretty() + .with_target(false) + .with_writer(std::io::stderr) + .with_env_filter( + tracing_subscriber::EnvFilter::builder() + .with_default_directive(tracing::level_filters::LevelFilter::INFO.into()) + .from_env_lossy(), + ) + .init(); + + // Validate all test pre-conditions + validate_tests(tests::tests(Some(&opt.test_cases))?); + + let default_rustfmt: Option = if let Some((v, true)) = Command::new("rustup") + .args(["which", "rustfmt"]) + .output() + .ok() + .map(|o| (o.stdout, o.status.success())) + { + Some(String::from_utf8_lossy(&v).into_owned().trim().into()) + } else { + None + }; + + match (&opt.rustfmt_bin_path, opt.use_rustfmt()) { + (_, false) => {} + (Some(path), true) => { + RUSTFMT.get_or_init(|| path.clone()); + } + (&None, true) => { + // FIXME: Use Option::filter instead when stable, rust-lang/rust#45860 + assert!( + default_rustfmt.iter().any(|p| p.is_file()), + "No rustfmt found" + ); + if let Some(default_rustfmt) = default_rustfmt { + RUSTFMT.get_or_init(|| default_rustfmt); + } + } + }; + match (&opt.form_bin_path, opt.use_form()) { + (_, false) => {} + (Some(path), true) => { + FORM.get_or_init(|| path.clone()); + } + (&None, true) => { + if let Ok(form) = which::which("form") { + FORM.get_or_init(|| form); + } + } + } + + // Set RUSTUP_TOOLCHAIN if needed + if let Some(toolchain) = &opt.rustup_toolchain { + std::env::set_var("RUSTUP_TOOLCHAIN", toolchain); + } + + match &opt.subcommand { + Subcommand::Tests(test_opts) => { + anyhow::ensure!( + test_opts.current_bin_path.exists(), + "svd2rust binary does not exist" + ); + + test_opts.run(&opt).with_context(|| "failed to run tests") + } + Subcommand::Diff(diff) => diff.run(&opt).with_context(|| "failed to run diff"), + Subcommand::Ci(ci) => ci.run(&opt).with_context(|| "failed to run ci"), + Subcommand::Test(test) => { + anyhow::ensure!( + test.current_bin_path.exists(), + "svd2rust binary does not exist" + ); + test.run(&opt).with_context(|| "failed to run test") + } + } +} + +macro_rules! gha_output { + ($fmt:literal$(, $args:expr)* $(,)?) => { + #[cfg(not(test))] + println!($fmt $(, $args)*); + #[cfg(test)] + eprintln!($fmt $(,$args)*); + }; +} + +pub fn gha_print(content: &str) { + gha_output!("{}", content); +} + +pub fn gha_error(content: &str) { + gha_output!("::error {}", content); +} + +#[track_caller] +pub fn gha_output(tag: &str, content: &str) -> anyhow::Result<()> { + if content.contains('\n') { + // https://github.com/actions/toolkit/issues/403 + anyhow::bail!("output `{tag}` contains newlines, consider serializing with json and deserializing in gha with fromJSON()"); + } + write_to_gha_env_file("GITHUB_OUTPUT", &format!("{tag}={content}"))?; + Ok(()) +} + +// https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#environment-files +pub fn write_to_gha_env_file(env_name: &str, contents: &str) -> anyhow::Result<()> { + use std::io::Write; + let path = std::env::var(env_name)?; + let path = std::path::Path::new(&path); + let mut file = std::fs::OpenOptions::new().append(true).open(path)?; + writeln!(file, "{}", contents)?; + Ok(()) +} diff --git a/svd2rust-regress/src/svd_test.rs b/svd2rust-regress/src/svd_test.rs new file mode 100644 index 00000000..534202c8 --- /dev/null +++ b/svd2rust-regress/src/svd_test.rs @@ -0,0 +1,552 @@ +use anyhow::{anyhow, Context, Result}; +use svd2rust::{util::Case, Target}; + +use crate::{command::CommandExt, tests::TestCase, Opts}; +use std::io::prelude::*; +use std::path::PathBuf; +use std::process::Command; +use std::{ + fmt::Write as _, + fs::{self, File, OpenOptions}, + path::Path, +}; + +const CRATES_ALL: &[&str] = &[ + "critical-section = {version = \"1.0\", optional = true}", + "vcell = \"0.1.2\"", +]; +const CRATES_MSP430: &[&str] = &["msp430 = \"0.4.0\"", "msp430-rt = \"0.4.0\""]; +const CRATES_ATOMICS: &[&str] = + &["portable-atomic = { version = \"1\", default-features = false }"]; +const CRATES_CORTEX_M: &[&str] = &["cortex-m = \"0.7.6\"", "cortex-m-rt = \"0.7\""]; +const CRATES_RISCV: &[&str] = &["riscv = \"0.12.1\"", "riscv-rt = \"0.13.0\""]; +const CRATES_MIPS: &[&str] = &["mips-mcu = \"0.1.0\""]; +const PROFILE_ALL: &[&str] = &["[profile.dev]", "incremental = false"]; +const FEATURES_ALL: &[&str] = &["[features]"]; +const FEATURES_CORTEX_M: &[&str] = &["rt = [\"cortex-m-rt/device\"]"]; +const FEATURES_XTENSA_LX: &[&str] = &["rt = []"]; +const WORKSPACE_EXCLUDE: &[&str] = &["[workspace]"]; + +fn path_helper_base(base: &Path, input: &[&str]) -> PathBuf { + input + .iter() + .fold(base.to_owned(), |b: PathBuf, p| b.join(p)) +} + +/// Create and write to file +fn file_helper(payload: &str, path: &Path) -> Result<()> { + let mut f = OpenOptions::new() + .create(true) + .append(true) + .open(path) + .with_context(|| format!("Failed to create {path:?}"))?; + + f.write_all(payload.as_bytes()) + .with_context(|| format!("Failed to write to {path:?}"))?; + + Ok(()) +} + +#[derive(thiserror::Error)] +#[error("Process failed - {command}")] +pub struct ProcessFailed { + pub command: String, + pub stderr: Option, + pub stdout: Option, + pub previous_processes_stderr: Vec, +} + +#[derive(Debug, thiserror::Error)] +pub enum TestError { + #[error("test case failed")] + Process(#[from] ProcessFailed), + #[error("Failed to run test")] + Other(#[from] anyhow::Error), +} + +impl std::fmt::Debug for ProcessFailed { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("Process failed") + } +} + +trait CommandHelper { + fn run_and_capture_outputs( + &mut self, + cant_fail: bool, + name: &str, + stdout: Option<&PathBuf>, + stderr: Option<&PathBuf>, + previous_processes_stderr: &[PathBuf], + ) -> Result<(), TestError>; + + fn run_and_capture_stderr( + &mut self, + cant_fail: bool, + name: &str, + stderr: &PathBuf, + previous_processes_stderr: &[PathBuf], + ) -> Result<(), TestError> { + self.run_and_capture_outputs( + cant_fail, + name, + None, + Some(stderr), + previous_processes_stderr, + ) + } +} + +impl CommandHelper for Command { + #[tracing::instrument(skip_all, fields(stdout = tracing::field::Empty, stderr = tracing::field::Empty))] + fn run_and_capture_outputs( + &mut self, + cant_fail: bool, + name: &str, + stdout: Option<&PathBuf>, + stderr: Option<&PathBuf>, + previous_processes_stderr: &[PathBuf], + ) -> Result<(), TestError> { + let output = self.run_and_get_output(true)?; + let out_payload = String::from_utf8_lossy(&output.stdout); + if let Some(out) = stdout { + file_helper(&out_payload, out)?; + }; + + let err_payload = String::from_utf8_lossy(&output.stderr); + if let Some(err) = stderr { + file_helper(&err_payload, err)?; + }; + if cant_fail && !output.status.success() { + let span = tracing::Span::current(); + let mut message = format!("Process failed: {}", self.display()); + if !out_payload.trim().is_empty() { + span.record( + "stdout", + tracing::field::display( + stdout.map(|p| p.display().to_string()).unwrap_or_default(), + ), + ); + write!(message, "\nstdout: \n{}", out_payload).unwrap(); + } + if !err_payload.trim().is_empty() { + span.record( + "stderr", + tracing::field::display( + stderr.map(|p| p.display().to_string()).unwrap_or_default(), + ), + ); + write!(message, "\nstderr: \n{}", err_payload).unwrap(); + } + tracing::error!(message=%message); + return Err(ProcessFailed { + command: name.into(), + stdout: stdout.cloned(), + stderr: stderr.cloned(), + previous_processes_stderr: previous_processes_stderr.to_vec(), + } + .into()); + } + + Ok(()) + } +} + +impl TestCase { + #[tracing::instrument(skip(self, opts), fields(name = %self.name(), svd=%self.svd_url()))] + pub fn test( + &self, + opts: &Opts, + bin_path: &Path, + run_clippy: bool, + run_docs_stable: bool, + run_docs_nightly: bool, + cli_passthrough_opts: &Option>, + ) -> Result>, TestError> { + let (chip_dir, mut process_stderr_paths) = self + .setup_case(&opts.output_dir, bin_path, cli_passthrough_opts) + .with_context(|| anyhow!("when setting up case for {}", self.name()))?; + // Run `cargo check`, capturing stderr to a log file + if !self.skip_check { + let cargo_check_err_file = path_helper_base(&chip_dir, &["cargo-check.err.log"]); + Command::new("cargo") + .arg("check") + .current_dir(&chip_dir) + .run_and_capture_stderr( + true, + "cargo check", + &cargo_check_err_file, + &process_stderr_paths, + ) + .with_context(|| "failed to check with cargo check")?; + process_stderr_paths.push(cargo_check_err_file); + } + if run_docs_nightly { + tracing::info!("Checking docs build with nightly"); + let cargo_docs_err_file = path_helper_base(&chip_dir, &["cargo-docs-nightly.err.log"]); + // Docs are built like docs.rs would build them. Additionally, build with all features. + + // Set the RUSTDOCFLAGS environment variable + let rustdocflags = "--cfg docsrs --generate-link-to-definition -Z unstable-options"; + Command::new("cargo") + .arg("+nightly") + .arg("doc") + .arg("--all-features") + .env("RUSTDOCFLAGS", rustdocflags) // Set the environment variable + .current_dir(&chip_dir) + .run_and_capture_stderr( + true, + "cargo docs nightly", + &cargo_docs_err_file, + &process_stderr_paths, + ) + .with_context(|| "failed to generate docs with cargo docs")?; + } + if run_docs_stable { + tracing::info!("Checking docs build with stable"); + let cargo_docs_err_file = path_helper_base(&chip_dir, &["cargo-docs-stable.err.log"]); + // Docs are built like docs.rs would build them. Additionally, build with all features. + Command::new("cargo") + .arg("+stable") + .arg("doc") + .arg("--all-features") + .current_dir(&chip_dir) + .run_and_capture_stderr( + true, + "cargo docs stable", + &cargo_docs_err_file, + &process_stderr_paths, + ) + .with_context(|| "failed to generate docs with cargo docs")?; + } + if run_clippy { + tracing::info!("Checking with clippy"); + let cargo_clippy_err_file = path_helper_base(&chip_dir, &["cargo-clippy.err.log"]); + Command::new("cargo") + .arg("clippy") + .arg("--") + .arg("-D") + .arg("warnings") + .current_dir(&chip_dir) + .run_and_capture_stderr( + true, + "cargo clippy", + &cargo_clippy_err_file, + &process_stderr_paths, + ) + .with_context(|| "failed to check with cargo clippy")?; + } + Ok(if opts.verbose > 1 { + Some(process_stderr_paths) + } else { + None + }) + } + + #[tracing::instrument(skip(self, output_dir, passthrough_opts), fields(name = %self.name(), chip_dir = tracing::field::Empty))] + + pub fn setup_case( + &self, + output_dir: &Path, + svd2rust_bin_path: &Path, + passthrough_opts: &Option>, + ) -> Result<(PathBuf, Vec), TestError> { + let user = match std::env::var("USER") { + Ok(val) => val, + Err(_) => "rusttester".into(), + }; + let mut chip_name = self.name(); + chip_name = Case::Snake.cow_to_case(chip_name.into()).to_string(); + let chip_dir = output_dir.join(&chip_name); + tracing::span::Span::current() + .record("chip_dir", tracing::field::display(chip_dir.display())); + if let Err(err) = fs::remove_dir_all(&chip_dir) { + match err.kind() { + std::io::ErrorKind::NotFound => (), + _ => Err(err).with_context(|| "While removing chip directory")?, + } + } + let mut process_stderr_paths: Vec = vec![]; + tracing::info!( + "Initializing cargo package for `{}` in {}", + self.name(), + chip_dir.display() + ); + println!("chip_dir: {}", chip_dir.display()); + Command::new("cargo") + .env("USER", user) + .arg("init") + .args(["--edition", "2021"]) + .arg("--name") + .arg(chip_name) + .arg("--vcs") + .arg("none") + .arg("--lib") + .arg(&chip_dir) + .run_and_capture_outputs(true, "cargo init", None, None, &[]) + .with_context(|| "Failed to cargo init")?; + + self.prepare_chip_test_toml(&chip_dir, passthrough_opts)?; + let chip_svd = self.prepare_svd_file(&chip_dir)?; + self.prepare_rust_toolchain_file(&chip_dir)?; + + let lib_rs_file = path_helper_base(&chip_dir, &["src", "lib.rs"]); + let src_dir = path_helper_base(&chip_dir, &["src"]); + let svd2rust_err_file = path_helper_base(&chip_dir, &["svd2rust.err.log"]); + self.run_svd2rust( + svd2rust_bin_path, + &chip_svd, + &chip_dir, + &lib_rs_file, + &svd2rust_err_file, + passthrough_opts, + )?; + process_stderr_paths.push(svd2rust_err_file); + match self.arch { + Target::CortexM | Target::Mips | Target::Msp430 | Target::XtensaLX => { + // TODO: Give error the path to stderr + fs::rename(path_helper_base(&chip_dir, &["lib.rs"]), &lib_rs_file) + .with_context(|| "While moving lib.rs file")?; + } + _ => {} + } + let lib_rs = + fs::read_to_string(&lib_rs_file).with_context(|| "Failed to read lib.rs file")?; + let file = syn::parse_file(&lib_rs) + .with_context(|| format!("couldn't parse {}", lib_rs_file.display()))?; + File::options() + .write(true) + .truncate(true) + .open(&lib_rs_file) + .with_context(|| format!("couldn't open {}", lib_rs_file.display()))? + .write(prettyplease::unparse(&file).as_bytes()) + .with_context(|| format!("couldn't write {}", lib_rs_file.display()))?; + let rustfmt_err_file = path_helper_base(&chip_dir, &["rustfmt.err.log"]); + let form_err_file = path_helper_base(&chip_dir, &["form.err.log"]); + if let Some(form_bin_path) = crate::FORM.get() { + tracing::info!("Running form"); + + // move the lib.rs file to src, then split with form. + let new_lib_rs_file = path_helper_base(&chip_dir, &["lib.rs"]); + std::fs::rename(lib_rs_file, &new_lib_rs_file) + .with_context(|| "While moving lib.rs file")?; + Command::new(form_bin_path) + .arg("--input") + .arg(&new_lib_rs_file) + .arg("--outdir") + .arg(&src_dir) + .run_and_capture_outputs( + true, + "form", + None, + Some(&form_err_file), + &process_stderr_paths, + ) + .with_context(|| "failed to form")?; + std::fs::remove_file(&new_lib_rs_file) + .with_context(|| "While removing lib.rs file after form")?; + } + if let Some(rustfmt_bin_path) = crate::RUSTFMT.get() { + tracing::info!("Running rustfmt"); + // Run `rusfmt`, capturing stderr to a log file + + // find all .rs files in src_dir and it's subdirectories + let mut src_files = vec![]; + visit_dirs(&src_dir, &mut |e: &fs::DirEntry| { + if e.path().extension().unwrap_or_default() == "rs" { + src_files.push(e.path()); + } + }) + .context("couldn't visit")?; + src_files.sort(); + + for entry in src_files { + Command::new(rustfmt_bin_path) + .arg(entry) + .args(["--edition", "2021"]) + .run_and_capture_outputs( + false, + "rustfmt", + None, + Some(&rustfmt_err_file), + &process_stderr_paths, + ) + .with_context(|| "failed to format")?; + } + + process_stderr_paths.push(rustfmt_err_file); + } + Ok((chip_dir, process_stderr_paths)) + } + + fn prepare_rust_toolchain_file(&self, chip_dir: &Path) -> Result<(), TestError> { + // Could be extended to let cargo install and use a specific toolchain. + let channel: Option = None; + + if let Some(channel) = channel { + let toolchain_file = path_helper_base(chip_dir, &["rust-toolchain.toml"]); + let mut file = OpenOptions::new() + .create(true) + .truncate(true) + .write(true) + .open(&toolchain_file) + .with_context(|| "failed to create toolchain file")?; + + writeln!(file, "[toolchain]\nchannel = \"{}\"", channel) + .with_context(|| "writing toolchain file failed")?; + } + + Ok(()) + } + + fn prepare_chip_test_toml( + &self, + chip_dir: &Path, + opts: &Option>, + ) -> Result<(), TestError> { + let svd_toml = path_helper_base(chip_dir, &["Cargo.toml"]); + let mut file = OpenOptions::new() + .append(true) + .open(svd_toml) + .with_context(|| "Failed to open Cargo.toml for appending")?; + + let cargo_toml_fragments = CRATES_ALL + .iter() + .chain(match &self.arch { + Target::CortexM => CRATES_CORTEX_M.iter(), + Target::RISCV => CRATES_RISCV.iter(), + Target::Mips => CRATES_MIPS.iter(), + Target::Msp430 => CRATES_MSP430.iter(), + Target::XtensaLX => [].iter(), + Target::None => unreachable!(), + }) + .chain(if let Some(opts) = opts { + if opts.iter().any(|v| v.contains("atomics")) { + CRATES_ATOMICS.iter() + } else { + [].iter() + } + } else { + [].iter() + }) + .chain(PROFILE_ALL.iter()) + .chain(FEATURES_ALL.iter()) + .chain(match &self.arch { + Target::CortexM => FEATURES_CORTEX_M.iter(), + Target::XtensaLX => FEATURES_XTENSA_LX.iter(), + _ => [].iter(), + }) + .chain(WORKSPACE_EXCLUDE.iter()); + for fragments in cargo_toml_fragments { + writeln!(file, "{}", fragments).with_context(|| "Failed to append to file!")?; + } + Ok(()) + } + + fn prepare_svd_file(&self, chip_dir: &Path) -> Result { + tracing::info!("Downloading SVD"); + // FIXME: Avoid downloading multiple times, especially if we're using the diff command + let svd_url = &self.svd_url(); + let svd = reqwest::blocking::get(svd_url) + .with_context(|| format!("Failed to get svd URL: {svd_url}"))? + .error_for_status() + .with_context(|| anyhow!("Response is not ok for svd url"))? + .text() + .with_context(|| "SVD is bad text")?; + + let chip_svd = format!("{}.svd", &self.chip); + let svd_file = path_helper_base(chip_dir, &[&chip_svd]); + file_helper(&svd, &svd_file)?; + Ok(chip_svd) + } + + fn run_svd2rust( + &self, + svd2rust_bin_path: &Path, + chip_svd: &str, + chip_dir: &Path, + lib_rs_file: &PathBuf, + svd2rust_err_file: &PathBuf, + cli_opts: &Option>, + ) -> Result<(), TestError> { + tracing::info!("Running svd2rust"); + + let target = match self.arch { + Target::CortexM => "cortex-m", + Target::Msp430 => "msp430", + Target::Mips => "mips", + Target::RISCV => "riscv", + Target::XtensaLX => "xtensa-lx", + Target::None => unreachable!(), + }; + let mut svd2rust_bin = Command::new(svd2rust_bin_path); + + let base_cmd = svd2rust_bin + .args(["-i", chip_svd]) + .args(["--target", target]); + if let Some(cli_opts) = cli_opts { + base_cmd.args(cli_opts); + } + if let Some(opts) = self.opts.as_ref() { + base_cmd.args(opts); + } + base_cmd.current_dir(chip_dir).run_and_capture_outputs( + true, + "svd2rust", + Some(lib_rs_file).filter(|_| { + !matches!( + self.arch, + Target::CortexM | Target::Msp430 | Target::XtensaLX + ) + }), + Some(svd2rust_err_file), + &[], + )?; + Ok(()) + } +} + +fn visit_dirs(dir: &Path, cb: &mut dyn FnMut(&fs::DirEntry)) -> std::io::Result<()> { + if dir.is_dir() { + for entry in fs::read_dir(dir)? { + let entry = entry?; + let path = entry.path(); + if path.is_dir() { + visit_dirs(&path, cb)?; + } else { + cb(&entry); + } + } + } + Ok(()) +} + +pub struct WorkspaceTomlGuard { + pub unmodified_toml: Vec, + pub manifest_path: PathBuf, +} + +impl WorkspaceTomlGuard { + pub fn new() -> Result { + // XXX: Workaround for + // https://github.com/rust-lang/cargo/issues/6009#issuecomment-1925445245 + // Calling cargo init will add the chip test directory to the workspace members, regardless + // of the exclude list. The unmodified manifest path is stored here and written + // back after cargo init. + let manifest_path = crate::get_cargo_workspace().join("Cargo.toml"); + let unmodified_toml = + fs::read(&manifest_path).with_context(|| "error reading manifest file")?; + Ok(Self { + unmodified_toml, + manifest_path, + }) + } +} + +impl Drop for WorkspaceTomlGuard { + fn drop(&mut self) { + if let Err(e) = std::fs::write(self.manifest_path.clone(), &self.unmodified_toml) { + tracing::error!("Failed to write back to manifest Cargo.toml: {}", e); + } + } +} diff --git a/svd2rust-regress/src/tests.rs b/svd2rust-regress/src/tests.rs new file mode 100644 index 00000000..f6c2f73d --- /dev/null +++ b/svd2rust-regress/src/tests.rs @@ -0,0 +1,153 @@ +use self::RunWhen::*; +use anyhow::Context; +use serde::Serialize as _; +pub use svd2rust::Target; + +#[allow(clippy::upper_case_acronyms)] +#[derive( + Debug, serde::Serialize, serde::Deserialize, PartialOrd, Ord, PartialEq, Eq, Clone, Copy, +)] +pub enum Manufacturer { + Atmel, + Freescale, + Fujitsu, + Holtek, + Microchip, + Nordic, + Nuvoton, + NXP, + SiliconLabs, + Spansion, + STMicro, + Toshiba, + SiFive, + TexasInstruments, + Vorago, + Espressif, + RaspberryPi, + Renesas, + Unknown, +} + +impl Manufacturer { + pub const fn all() -> &'static [Self] { + use self::Manufacturer::*; + &[ + Atmel, + Freescale, + Fujitsu, + Holtek, + Microchip, + Nordic, + Nuvoton, + NXP, + SiliconLabs, + Spansion, + Vorago, + STMicro, + Toshiba, + SiFive, + RaspberryPi, + Renesas, + TexasInstruments, + Espressif, + ] + } +} + +impl std::fmt::Display for Manufacturer { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.serialize(f) + } +} + +#[derive(Debug, serde::Serialize, serde::Deserialize, Default, Clone, Copy)] +#[serde(rename_all = "kebab-case")] +pub enum RunWhen { + #[default] + Always, + NotShort, + + // TODO: Never doesn't really do anything right now + Never, +} + +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] +pub struct TestCase { + pub arch: Target, + pub mfgr: Manufacturer, + pub chip: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub suffix: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub opts: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub svd_url: Option, + #[serde(default = "true_")] + pub should_pass: bool, + #[serde(default)] + pub skip_check: bool, + #[serde(default)] + pub run_when: RunWhen, +} + +fn true_() -> bool { + true +} + +impl TestCase { + pub fn svd_url(&self) -> String { + match &self.svd_url { + Some(u) => u.to_owned(), + None => format!("https://raw.githubusercontent.com/cmsis-svd/cmsis-svd-data/main/data/{vendor:?}/{chip}.svd", + vendor = self.mfgr, + chip = self.chip + ) + } + } + + pub const fn should_run(&self, short_test: bool) -> bool { + match (&self.run_when, short_test) { + (&Always, _) => true, + (&NotShort, true) => false, + (_, _) => true, + } + } + + pub fn name(&self) -> String { + let mut base_name = format!("{:?}-{}", self.mfgr, self.chip.replace('.', "_")); + if let Some(suffix) = &self.suffix { + base_name.push('-'); + base_name.push_str(suffix); + } + base_name + } +} + +pub fn tests(test_cases: Option<&std::path::Path>) -> Result<&'static [TestCase], anyhow::Error> { + pub static TESTS: std::sync::OnceLock> = std::sync::OnceLock::new(); + + if let Some(cases) = TESTS.get() { + Ok(cases) + } else { + let path = test_cases.ok_or_else(|| anyhow::format_err!("no test cases specified"))?; + let cases: Vec = if path.extension() != Some(std::ffi::OsStr::new("yml")) { + serde_json::from_reader( + std::fs::OpenOptions::new() + .read(true) + .open(path) + .with_context(|| format!("couldn't open file {}", path.display()))?, + )? + } else if path.extension() != Some(std::ffi::OsStr::new("json")) { + serde_yaml::from_reader( + std::fs::OpenOptions::new() + .read(true) + .open(path) + .with_context(|| format!("couldn't open file {}", path.display()))?, + )? + } else { + anyhow::bail!("unknown file extension for {}", path.display()); + }; + Ok(TESTS.get_or_init(|| cases)) + } +} diff --git a/svd2rust-regress/tests.yml b/svd2rust-regress/tests.yml new file mode 100644 index 00000000..bf17e088 --- /dev/null +++ b/svd2rust-regress/tests.yml @@ -0,0 +1,711 @@ +# Atmel - BAD-SVD missing resetValue + +# Freescale +- arch: cortex-m + mfgr: Freescale + chip: MK02F12810 +- arch: cortex-m + mfgr: Freescale + chip: MK10D7 +- arch: cortex-m + mfgr: Freescale + chip: MK12D5 +- arch: cortex-m + mfgr: Freescale + chip: MK21D5 +- arch: cortex-m + mfgr: Freescale + chip: MK21F12 +- arch: cortex-m + mfgr: Freescale + chip: MK30D7 +- arch: cortex-m + mfgr: Freescale + chip: MK40D7 +- arch: cortex-m + mfgr: Freescale + chip: MK52DZ10 +- arch: cortex-m + mfgr: Freescale + chip: MK66F18 +- arch: cortex-m + mfgr: Freescale + chip: MK82F25615 +- arch: cortex-m + mfgr: Freescale + chip: MKE15Z7 +- arch: cortex-m + mfgr: Freescale + chip: MKL28T7_CORE0 +- arch: cortex-m + mfgr: Freescale + chip: MKL81Z7 +- arch: cortex-m + mfgr: Freescale + chip: MKV10Z1287 +- arch: cortex-m + mfgr: Freescale + chip: MKV31F51212 +- arch: cortex-m + mfgr: Freescale + chip: MKV45F15 +- arch: cortex-m + mfgr: Freescale + chip: MKW22D5 +- arch: cortex-m + mfgr: Freescale + chip: MKE02Z4 +- arch: cortex-m + mfgr: Freescale + chip: MKE06Z4 +- arch: cortex-m + mfgr: Freescale + chip: MKL05Z4 +- arch: cortex-m + mfgr: Freescale + chip: MKL17Z644 +- arch: cortex-m + mfgr: Freescale + chip: MKL36Z4 +- arch: cortex-m + mfgr: Freescale + chip: MKM14ZA5 +- arch: cortex-m + mfgr: Freescale + chip: MKM34ZA5 +- arch: cortex-m + mfgr: Freescale + chip: SKEAZN642 + +# Fujitsu +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF10xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF10xR +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF11xK +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF11xL +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF11xM +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF11xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF12xK +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF12xL +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF13xK +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF13xL +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF13xM +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF13xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF14xL +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF14xM +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF15xM +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF15xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF15xR +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF1AxL +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF1AxM +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF1AxN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF31xK +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF31xL +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF31xM +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF31xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF34xL +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF34xM +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF34xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF42xK +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AF42xL +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AFA3xL +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AFA3xM +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AFA3xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AFA4xL +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AFA4xM +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AFA4xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AFAAxL +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AFAAxM +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AFAAxN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AFB4xL +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AFB4xM +- arch: cortex-m + mfgr: Fujitsu + chip: MB9AFB4xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9B160L +- arch: cortex-m + mfgr: Fujitsu + chip: MB9B160R +- arch: cortex-m + mfgr: Fujitsu + chip: MB9B360L +- arch: cortex-m + mfgr: Fujitsu + chip: MB9B360R +- arch: cortex-m + mfgr: Fujitsu + chip: MB9B460L +- arch: cortex-m + mfgr: Fujitsu + chip: MB9B460R +- arch: cortex-m + mfgr: Fujitsu + chip: MB9B560L +- arch: cortex-m + mfgr: Fujitsu + chip: MB9B560R +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF10xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF10xR +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF11xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF11xR +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF11xS +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF11xT +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF12xJ +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF12xK +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF12xL +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF12xM +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF12xS +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF12xT +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF21xS +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF21xT +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF30xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF30xR +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF31xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF31xR +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF31xS +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF31xT +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF32xK +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF32xL +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF32xM +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF32xS +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF32xT +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF40xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF40xR +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF41xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF41xR +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF41xS +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF41xT +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF42xS +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF42xT +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF50xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF50xR +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF51xN +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF51xR +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF51xS +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF51xT +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF52xK +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF52xL +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF52xM +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF52xS +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF52xT +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF61xS +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BF61xT +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BFD1xS +- arch: cortex-m + mfgr: Fujitsu + chip: MB9BFD1xT +- arch: cortex-m + mfgr: Fujitsu + chip: S6E1A1 + +# GD32 + +# Holtek +- arch: cortex-m + mfgr: Holtek + chip: ht32f125x +- arch: cortex-m + mfgr: Holtek + chip: ht32f175x +- arch: cortex-m + mfgr: Holtek + chip: ht32f275x + +# Microchip +- arch: mips + mfgr: Microchip + chip: pic32mx170f256b + svd_url: https://raw.githubusercontent.com/kiffie/pic32-pac/master/pic32mx1xxfxxxb/PIC32MX170F256B.svd.patched +- arch: mips + mfgr: Microchip + chip: pic32mx270f256b + svd_url: https://raw.githubusercontent.com/kiffie/pic32-pac/master/pic32mx2xxfxxxb/PIC32MX270F256B.svd.patched + +# Nordic +- arch: cortex-m + mfgr: Nordic + chip: nrf51 +- arch: cortex-m + mfgr: Nordic + chip: nrf52 + should_pass: false + run_when: never + +# Nuvoton +- arch: cortex-m + mfgr: Nuvoton + chip: NUC100_Series +- arch: cortex-m + mfgr: Nuvoton + chip: M051_Series + +# NXP +- arch: cortex-m + mfgr: NXP + chip: MK22F25612 +- arch: cortex-m + mfgr: NXP + chip: MKW41Z4 + +# MSP430 +- arch: msp430 + mfgr: TexasInstruments + chip: msp430g2553 + skip_check: true + svd_url: https://github.com/pftbest/msp430g2553/raw/v0.3.0-svd/msp430g2553.svd +- arch: msp430 + mfgr: TexasInstruments + chip: msp430fr2355 + skip_check: true + svd_url: https://raw.githubusercontent.com/YuhanLiin/msp430fr2355/master/msp430fr2355.svd + +# RISC-V +- arch: riscv + mfgr: SiFive + chip: e310x + svd_url: https://raw.githubusercontent.com/riscv-rust/e310x/master/e310x/e310x.svd +- arch: riscv + mfgr: SiFive + chip: fu540 + svd_url: https://raw.githubusercontent.com/riscv-rust/fu540-pac/master/fu540.svd + +# SiliconLabs +- arch: cortex-m + mfgr: SiliconLabs + chip: SIM3C1x4_SVD + svd_url: https://raw.githubusercontent.com/cmsis-svd/cmsis-svd-data/main/data/SiliconLabs/SiM3_NRND/SIM3C1x4.svd +- arch: cortex-m + mfgr: SiliconLabs + chip: SIM3C1x6_SVD + svd_url: https://raw.githubusercontent.com/cmsis-svd/cmsis-svd-data/main/data/SiliconLabs/SiM3_NRND/SIM3C1x6.svd +- arch: cortex-m + mfgr: SiliconLabs + chip: SIM3C1x7_SVD + svd_url: https://raw.githubusercontent.com/cmsis-svd/cmsis-svd-data/main/data/SiliconLabs/SiM3_NRND/SIM3C1x7.svd +- arch: cortex-m + mfgr: SiliconLabs + chip: SIM3L1x4_SVD + svd_url: https://raw.githubusercontent.com/cmsis-svd/cmsis-svd-data/main/data/SiliconLabs/SiM3_NRND/SIM3L1x4.svd +- arch: cortex-m + mfgr: SiliconLabs + chip: SIM3L1x6_SVD + svd_url: https://raw.githubusercontent.com/cmsis-svd/cmsis-svd-data/main/data/SiliconLabs/SiM3_NRND/SIM3L1x6.svd +- arch: cortex-m + mfgr: SiliconLabs + chip: SIM3L1x7_SVD + svd_url: https://raw.githubusercontent.com/cmsis-svd/cmsis-svd-data/main/data/SiliconLabs/SiM3_NRND/SIM3L1x7.svd +- arch: cortex-m + mfgr: SiliconLabs + chip: SIM3U1x4_SVD + svd_url: https://raw.githubusercontent.com/cmsis-svd/cmsis-svd-data/main/data/SiliconLabs/SiM3_NRND/SIM3U1x4.svd +- arch: cortex-m + mfgr: SiliconLabs + chip: SIM3U1x6_SVD + svd_url: https://raw.githubusercontent.com/cmsis-svd/cmsis-svd-data/main/data/SiliconLabs/SiM3_NRND/SIM3U1x6.svd +- arch: cortex-m + mfgr: SiliconLabs + chip: SIM3U1x7_SVD + svd_url: https://raw.githubusercontent.com/cmsis-svd/cmsis-svd-data/main/data/SiliconLabs/SiM3_NRND/SIM3U1x7.svd +- arch: cortex-m + mfgr: SiliconLabs + chip: SIM3L1x8_SVD + svd_url: https://raw.githubusercontent.com/cmsis-svd/cmsis-svd-data/main/data/SiliconLabs/SiM3_NRND/SIM3L1x8.svd + +# Spansion +- arch: cortex-m + mfgr: Spansion + chip: MB9BF36xx +- arch: cortex-m + mfgr: Spansion + chip: MB9BF46xx +- arch: cortex-m + mfgr: Spansion + chip: MB9BF56xx + +# STMicro +- arch: cortex-m + mfgr: STMicro + chip: STM32F030 +- arch: cortex-m + mfgr: STMicro + chip: STM32F031x +- arch: cortex-m + mfgr: STMicro + chip: STM32F042x +- arch: cortex-m + mfgr: STMicro + chip: STM32F072x +- arch: cortex-m + mfgr: STMicro + chip: STM32F091x +- arch: cortex-m + mfgr: STMicro + chip: STM32F0xx +- arch: cortex-m + mfgr: STMicro + chip: STM32F100xx +- arch: cortex-m + mfgr: STMicro + chip: STM32F103xx +- arch: cortex-m + mfgr: STMicro + chip: STM32F107xx +- arch: cortex-m + mfgr: STMicro + chip: STM32F20x +- arch: cortex-m + mfgr: STMicro + chip: STM32F21x +- arch: cortex-m + mfgr: STMicro + chip: STM32F301 +- arch: cortex-m + mfgr: STMicro + chip: STM32F303 +- arch: cortex-m + mfgr: STMicro + chip: STM32F401 +- arch: cortex-m + mfgr: STMicro + chip: STM32F407 +- arch: cortex-m + mfgr: STMicro + chip: STM32F410 +- arch: cortex-m + mfgr: STMicro + chip: STM32F413 +- arch: cortex-m + mfgr: STMicro + chip: STM32F427 +- arch: cortex-m + mfgr: STMicro + chip: STM32F429 +- arch: cortex-m + mfgr: STMicro + chip: STM32F446 +- arch: cortex-m + mfgr: STMicro + chip: STM32L100 +- arch: cortex-m + mfgr: STMicro + chip: STM32L15xC +- arch: cortex-m + mfgr: STMicro + chip: STM32L15xxE +- arch: cortex-m + mfgr: STMicro + chip: STM32L15xxxA +- arch: cortex-m + mfgr: STMicro + chip: STM32L1xx +- arch: cortex-m + mfgr: STMicro + chip: STM32W108 +# Patched +- arch: cortex-m + mfgr: STMicro + chip: STM32F0x2 + suffix: patched + svd_url: https://stm32-rs.github.io/stm32-rs/stm32f0x2.svd.patched +- arch: cortex-m + mfgr: STMicro + chip: STM32F103 + suffix: patched + svd_url: https://stm32-rs.github.io/stm32-rs/stm32f103.svd.patched +- arch: cortex-m + mfgr: STMicro + chip: STM32F411 + suffix: patched + svd_url: https://stm32-rs.github.io/stm32-rs/stm32f411.svd.patched +- arch: cortex-m + mfgr: STMicro + chip: STM32F469 + suffix: patched + svd_url: https://stm32-rs.github.io/stm32-rs/stm32f469.svd.patched +- arch: cortex-m + mfgr: STMicro + chip: STM32F723 + suffix: patched + svd_url: https://stm32-rs.github.io/stm32-rs/stm32f723.svd.patched +- arch: cortex-m + mfgr: STMicro + chip: STM32G070 + suffix: patched + svd_url: https://stm32-rs.github.io/stm32-rs/stm32g070.svd.patched +- arch: cortex-m + mfgr: STMicro + chip: STM32G473 + suffix: patched + svd_url: https://stm32-rs.github.io/stm32-rs/stm32g473.svd.patched +- arch: cortex-m + mfgr: STMicro + chip: STM32H743 + suffix: patched + svd_url: https://stm32-rs.github.io/stm32-rs/stm32h743.svd.patched +- arch: cortex-m + mfgr: STMicro + chip: STM32L0x3 + suffix: patched + svd_url: https://stm32-rs.github.io/stm32-rs/stm32l0x3.svd.patched +- arch: cortex-m + mfgr: STMicro + chip: STM32L162 + suffix: patched + svd_url: https://stm32-rs.github.io/stm32-rs/stm32l162.svd.patched +- arch: cortex-m + mfgr: STMicro + chip: STM32L4x6 + suffix: patched + svd_url: https://stm32-rs.github.io/stm32-rs/stm32l4x6.svd.patched +- arch: cortex-m + mfgr: STMicro + chip: STM32L562 + suffix: patched + svd_url: https://stm32-rs.github.io/stm32-rs/stm32l562.svd.patched +- arch: cortex-m + mfgr: STMicro + chip: STM32MP157 + svd_url: https://stm32-rs.github.io/stm32-rs/stm32mp157.svd.patched +- arch: cortex-m + mfgr: STMicro + chip: STM32WB55 + suffix: patched + svd_url: https://stm32-rs.github.io/stm32-rs/stm32wb55.svd.patched +- arch: cortex-m + mfgr: STMicro + chip: STM32WLE5 + suffix: patched + svd_url: https://stm32-rs.github.io/stm32-rs/stm32wle5.svd.patched +- arch: cortex-m + mfgr: STMicro + chip: STM32C011 + suffix: patched + svd_url: https://stm32-rs.github.io/stm32-rs/stm32c011.svd.patched + +# Toschiba +- arch: cortex-m + mfgr: Toshiba + chip: M061 + +# Espressif +# Xtensa requires special LLVM fork and nightly compiler, so skip the checks for now. +- arch: xtensa-lx + mfgr: Espressif + chip: esp32 + opts: + - --ident-formats-theme + - legacy + svd_url: https://raw.githubusercontent.com/espressif/svd/main/svd/esp32.svd +- arch: xtensa-lx + mfgr: Espressif + chip: esp32s2 + opts: + - --ident-formats-theme + - legacy + svd_url: https://raw.githubusercontent.com/espressif/svd/main/svd/esp32s2.svd +- arch: xtensa-lx + mfgr: Espressif + chip: esp32s3 + opts: + - --ident-formats-theme + - legacy + svd_url: https://raw.githubusercontent.com/espressif/svd/main/svd/esp32s3.svd +- arch: riscv + mfgr: Espressif + chip: esp32c3 + svd_url: https://raw.githubusercontent.com/espressif/svd/main/svd/esp32c3.svd + +# Vorago +- arch: cortex-m + mfgr: Vorago + chip: va108xx + svd_url: https://raw.githubusercontent.com/us-irs/va108xx-rs/refs/heads/main/va108xx/svd/va108xx.svd.patched + +# Renesas +- arch: cortex-m + mfgr: Renesas + chip: r7fa4m1ab + svd_url: https://raw.githubusercontent.com/ra-rs/ra/refs/heads/main/svd/vendor/R7FA4M1AB.svd + +# Raspberry Pi +- arch: cortex-m + mfgr: RaspberryPi + chip: rp2040 +- arch: cortex-m + mfgr: RaspberryPi + chip: rp2350