diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5d239663..92d9873a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,6 @@ on: push: - branches: master + branches: [master] pull_request: merge_group: @@ -10,7 +10,7 @@ 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 @@ -42,6 +42,9 @@ jobs: with: key: ${{ 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 @@ -51,52 +54,55 @@ jobs: runs-on: ubuntu-latest needs: [check] strategy: + fail-fast: false matrix: - # Options are all, none, strict and const include: - - { rust: stable, vendor: Atmel, options: all } - - { rust: stable, vendor: Atmel, options: "" } - - { rust: stable, vendor: Freescale, options: all } - - { rust: stable, vendor: Freescale, options: "" } - - { rust: stable, vendor: Fujitsu, options: "" } - - { rust: stable, vendor: Fujitsu, options: "--atomics" } - - { rust: stable, vendor: GD32, options: all } - - { rust: stable, vendor: GD32, options: "" } - - { rust: stable, vendor: Holtek, options: all } - - { rust: stable, vendor: Holtek, options: "" } - - { rust: stable, vendor: Microchip, options: "" } - - { rust: stable, vendor: Microchip, options: "--atomics" } - - { rust: stable, vendor: Nordic, options: all } - - { rust: stable, vendor: Nordic, options: "" } - - { rust: stable, vendor: Nuvoton, options: "" } - - { rust: stable, vendor: Nuvoton, options: "--atomics" } - - { rust: stable, vendor: NXP, options: all } - - { rust: stable, vendor: NXP, options: "" } - - { rust: stable, vendor: RISC-V, options: "" } - - { rust: stable, vendor: RISC-V, options: "--atomics" } - - { rust: stable, vendor: SiliconLabs, options: all } - - { rust: stable, vendor: SiliconLabs, options: "" } - - { rust: stable, vendor: Spansion, options: "" } - - { rust: stable, vendor: Spansion, options: "--atomics" } - - { rust: stable, vendor: STMicro, options: "" } - - { rust: stable, vendor: STMicro, options: "--atomics" } - - { rust: stable, vendor: STM32-patched, options: "--strict --pascal-enum-values --max-cluster-size --atomics --atomics-feature atomics --impl-debug --impl-defmt defmt" } - - { rust: stable, vendor: Toshiba, options: all } - - { rust: stable, vendor: Toshiba, options: "" } - # Test MSRV - - { rust: 1.70.0, vendor: Nordic, options: "" } - # Use nightly for architectures which don't support stable - - { rust: nightly, vendor: MSP430, options: "--atomics" } - - { rust: nightly, vendor: MSP430, options: "" } - - { rust: nightly, vendor: Espressif, options: "--atomics" } - - { rust: nightly, vendor: Espressif, options: "" } + - { 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@v4 - - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ matrix.rust }} + - uses: dtolnay/rust-toolchain@stable - name: Cache uses: Swatinem/rust-cache@v2 @@ -105,23 +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 - RUST_TOOLCHAIN: ${{ matrix.rust }} - 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@v4 - - uses: dtolnay/rust-toolchain@master - with: - toolchain: stable + - 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 @@ -130,12 +165,8 @@ jobs: run: | cargo install svd2rust --path . - - name: Run CI script - env: - VENDOR: RISC-V - OPTIONS: "" - 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 @@ -149,11 +180,11 @@ jobs: - 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: | @@ -193,7 +224,7 @@ jobs: runs-on: windows-latest suffix: .exe steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: @@ -209,7 +240,7 @@ jobs: - 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@v3 + uses: actions/upload-artifact@v4 with: name: artifact-svd2rust-${{ matrix.target }} path: svd2rust-${{ matrix.target }}* diff --git a/.github/workflows/diff.yml b/.github/workflows/diff.yml index c64712d9..a055ddec 100644 --- a/.github/workflows/diff.yml +++ b/.github/workflows/diff.yml @@ -5,11 +5,19 @@ on: jobs: generate: + name: | + Generate matrix. runs-on: ubuntu-latest outputs: diffs: ${{ steps.regress-ci.outputs.diffs }} - if: github.event.issue.pull_request && github.event.comment.author_association == 'MEMBER' && (contains(github.event.comment.body, '\n/ci') || startsWith(github.event.comment.body, '/ci')) + 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 @@ -25,7 +33,7 @@ jobs: id: regress-ci env: GITHUB_COMMENT: ${{ github.event.comment.body }} - GITHUB_COMMENT_PR: ${{ github.event.comment.issue_url }} + GITHUB_COMMENT_PR: ${{ github.event.issue.number }} diff: runs-on: ubuntu-latest needs: [generate] @@ -50,16 +58,16 @@ jobs: with: tool: cargo-semver-checks - # if a new line is added here, make sure to update the `summary` job to reference the new step index - 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 --hunk-header-style omit + GIT_PAGER: delta --raw summary: runs-on: ubuntu-latest needs: [diff, generate] @@ -68,9 +76,8 @@ jobs: - uses: actions/checkout@v4 - run: | - PR_ID=$(echo "${{ github.event.comment.issue_url }}" | grep -o '[0-9]\+$') 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:45)"] | join("\n"))' --arg pr_id "$PR_ID" --arg comment "${{ github.event.comment.url }}"| \ - gh pr comment "$PR_ID" --body "$(< /dev/stdin)" + 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 77ebdd1b..c9ce19be 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -52,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 }} @@ -63,7 +63,7 @@ jobs: needs: [build] steps: - uses: actions/checkout@v4 - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: path: artifacts - run: ls -R ./artifacts @@ -76,7 +76,7 @@ jobs: 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/CHANGELOG.md b/CHANGELOG.md index 6649d514..8985ad60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,102 @@ 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) @@ -858,7 +954,19 @@ peripheral.register.write(|w| w.field().set()); - Initial version of the `svd2rust` tool -[Unreleased]: https://github.com/rust-embedded/svd2rust/compare/v0.31.4...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 diff --git a/Cargo.lock b/Cargo.lock index a2acfcfc..f87d1e9c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,138 +4,140 @@ version = 3 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "anstream" -version = "0.6.5" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "once_cell", + "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.76" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59d2a3357dde987206219e78ecfbbb6e8dad06cbb65292758d3270e6254f7355" +checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" [[package]] name = "arrayref" -version = "0.3.7" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" -version = "0.7.4" +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 = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.1.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", + "windows-targets 0.52.6", ] [[package]] name = "base64" -version = "0.21.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" - -[[package]] -name = "bitflags" -version = "1.3.2" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "blake2b_simd" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780" +checksum = "06e903a20b159e944f91ec8499fe1e55651480c541ea0a584f5d967c49ad9d99" dependencies = [ "arrayref", "arrayvec", @@ -144,23 +146,23 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "bytes" -version = "1.5.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.0.83" +version = "1.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" dependencies = [ - "libc", + "shlex", ] [[package]] @@ -171,9 +173,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.4.11" +version = "4.5.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" +checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944" dependencies = [ "clap_builder", "clap_derive", @@ -181,9 +183,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.11" +version = "4.5.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" +checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9" dependencies = [ "anstream", "anstyle", @@ -193,33 +195,33 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.4.7" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.42", + "syn", ] [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "constant_time_eq" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" [[package]] name = "core-foundation" @@ -233,47 +235,40 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "crossbeam-deque" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ - "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.16" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2fe95351b870527a5d09bf563ed3c97c0cffb87cf1c78a591bf48bb218d9aa" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "autocfg", - "cfg-if", "crossbeam-utils", - "memoffset", ] [[package]] name = "crossbeam-utils" -version = "0.8.17" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d96137f14f244c37f989d9fff8f95e6c18b918e71f36638f8c49112e4c78f" -dependencies = [ - "cfg-if", -] +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "darling" -version = "0.14.4" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ "darling_core", "darling_macro", @@ -281,109 +276,130 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.14.4" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 1.0.109", + "syn", ] [[package]] name = "darling_macro" -version = "0.14.4" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 1.0.109", + "syn", ] [[package]] name = "derive_builder" -version = "0.12.0" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" dependencies = [ "derive_builder_macro", ] [[package]] name = "derive_builder_core" -version = "0.12.0" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" dependencies = [ "darling", "proc-macro2", "quote", - "syn 1.0.109", + "syn", ] [[package]] name = "derive_builder_macro" -version = "0.12.0" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 1.0.109", + "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.9.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] [[package]] -name = "env_logger" -version = "0.10.1" +name = "env_filter" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" dependencies = [ - "humantime", - "is-terminal", "log", "regex", - "termcolor", +] + +[[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.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "fastrand" -version = "2.0.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fnv" @@ -417,45 +433,47 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.29" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", + "futures-sink", ] [[package]] name = "futures-core" -version = "0.3.29" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-io" -version = "0.3.29" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-sink" -version = "0.3.29" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.29" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.29" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", "futures-io", + "futures-sink", "futures-task", "memchr", "pin-project-lite", @@ -463,23 +481,46 @@ dependencies = [ "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.28.1" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "h2" -version = "0.3.22" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" +checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" dependencies = [ + "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "futures-util", "http", "indexmap", "slab", @@ -490,45 +531,30 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "hermit-abi" -version = "0.3.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "html-escape" -version = "0.2.13" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "utf8-width", + "windows-sys 0.59.0", ] [[package]] name = "http" -version = "0.2.11" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", @@ -537,68 +563,222 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.6" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", "http", - "pin-project-lite", ] [[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.3" +name = "http-body-util" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] [[package]] -name = "humantime" -version = "2.1.0" +name = "httparse" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hyper" -version = "0.14.28" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ "bytes", "futures-channel", - "futures-core", "futures-util", "h2", "http", "http-body", "httparse", - "httpdate", "itoa", "pin-project-lite", - "socket2", + "smallvec", "tokio", - "tower-service", - "tracing", "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.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +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]] @@ -609,19 +789,30 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.5.0" +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 = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "icu_normalizer", + "icu_properties", ] [[package]] name = "indexmap" -version = "2.1.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" dependencies = [ "equivalent", "hashbrown", @@ -635,17 +826,18 @@ checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a" [[package]] name = "ipnet" -version = "2.9.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "irx-config" -version = "3.3.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0071d0561e65904b6ea900e6e09d84579766c20a2b6b4d628eaf3caf9ec9ee" +checksum = "6266a086f9c5635dffbfd7bd49262b2a40e0a580ab34e85d88dc59ed8133976a" dependencies = [ "blake2b_simd", + "cfg-if", "clap", "derive_builder", "serde", @@ -656,54 +848,86 @@ dependencies = [ ] [[package]] -name = "is-terminal" -version = "0.4.9" +name = "is_terminal_polyfill" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +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 = [ - "hermit-abi", - "rustix", - "windows-sys 0.48.0", + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde", ] [[package]] -name = "itoa" -version = "1.0.10" +name = "jiff-static" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "4cdde31a9d349f1b1f51a0b3714a5940ac022976f4b49485fc04be052b183b4c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "js-sys" -version = "0.3.66" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.151" +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 = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" -version = "0.4.12" +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 = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" [[package]] name = "log" -version = "0.4.20" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "matchers" @@ -716,18 +940,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" - -[[package]] -name = "memoffset" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" -dependencies = [ - "autocfg", -] +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "mime" @@ -737,31 +952,30 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430" dependencies = [ - "adler", + "adler2", ] [[package]] name = "mio" -version = "0.8.10" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", - "wasi", - "windows-sys 0.48.0", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.52.0", ] [[package]] name = "native-tls" -version = "0.2.11" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" dependencies = [ - "lazy_static", "libc", "log", "openssl", @@ -783,38 +997,28 @@ dependencies = [ "winapi", ] -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "object" -version = "0.32.1" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "openssl" -version = "0.10.62" +version = "0.10.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" +checksum = "5e14130c6a98cd258fdcb0fb6d744152343ff729cbfcb28c656a9d12b999fbcd" dependencies = [ - "bitflags 2.4.1", + "bitflags", "cfg-if", "foreign-types", "libc", @@ -831,23 +1035,33 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn", ] [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +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.98" +version = "0.9.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7" +checksum = "8bb61ea9811cc39e3c2069f40b8b8e2e70d8569b361f879786cc7ed48b777cdd" dependencies = [ "cc", "libc", + "openssl-src", "pkg-config", "vcpkg", ] @@ -866,9 +1080,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -878,43 +1092,64 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.28" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" +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.15" +version = "0.2.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" +checksum = "5316f57387668042f561aae71480de936257848f9c43ce528e311d89a07cadeb" dependencies = [ "proc-macro2", - "syn 2.0.42", + "syn", ] [[package]] name = "proc-macro2" -version = "1.0.71" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +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.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -922,33 +1157,24 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", ] -[[package]] -name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "regex" -version = "1.9.6" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebee201405406dbf528b8b672104ae6d6d63e6d118cb10e4d51abbc7b58044ff" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.3.9", - "regex-syntax 0.7.5", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", ] [[package]] @@ -962,13 +1188,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.9" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.5", + "regex-syntax 0.8.5", ] [[package]] @@ -979,26 +1205,30 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.5" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.11.23" +version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" +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", @@ -1007,67 +1237,142 @@ dependencies = [ "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", - "winreg", + "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 = "roxmltree" -version = "0.19.0" +name = "rustls" +version = "0.23.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f" +checksum = "822ee9188ac4ec04a2f0531e55d035fb2de73f18b41a63c70c2712503b6fb13c" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] [[package]] -name = "rustc-demangle" -version = "0.1.23" +name = "rustls-pemfile" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] [[package]] -name = "rustix" -version = "0.38.28" +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 = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" dependencies = [ - "bitflags 2.4.1", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", + "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.16" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "schannel" -version = "0.1.22" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] name = "security-framework" -version = "2.9.2" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 1.3.2", + "bitflags", "core-foundation", "core-foundation-sys", "libc", @@ -1076,9 +1381,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.1" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", @@ -1086,40 +1391,41 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.193" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.193" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn", ] [[package]] name = "serde_json" -version = "1.0.108" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] @@ -1138,9 +1444,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.29" +version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15e0ef66bf939a7c890a0bf6d5a733c70202225f9888a89ed5c62298b019129" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ "indexmap", "itoa", @@ -1158,6 +1464,18 @@ 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" @@ -1169,31 +1487,43 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" [[package]] name = "socket2" -version = "0.5.5" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" dependencies = [ "libc", - "windows-sys 0.48.0", + "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.10.0" +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 = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "svd-parser" -version = "0.14.5" +version = "0.14.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d17a2c2ef5aa450e80d714232a5932e7d8a39cac092e9e9ef8411bc833de3c4" +checksum = "a41fe17e46dee363a7d3b20e878bbf6d6b4e839ae010ff07ef0e05d41d201811" dependencies = [ "anyhow", "roxmltree", @@ -1203,9 +1533,9 @@ dependencies = [ [[package]] name = "svd-rs" -version = "0.14.7" +version = "0.14.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c82f375efa1d0145467e6cc98fa0e4e1a3f3d497cece4bcdda247f4904a77d4" +checksum = "c9b88ee2e82f09623ff76965587dc15a2e7150a3126854899c86e94dab777458" dependencies = [ "once_cell", "regex", @@ -1215,12 +1545,11 @@ dependencies = [ [[package]] name = "svd2rust" -version = "0.31.4" +version = "0.36.1" dependencies = [ "anyhow", "clap", "env_logger", - "html-escape", "inflections", "irx-config", "log", @@ -1232,8 +1561,9 @@ dependencies = [ "serde_yaml", "svd-parser", "svd-rs", - "syn 2.0.42", + "syn", "thiserror", + "url", ] [[package]] @@ -1248,8 +1578,9 @@ dependencies = [ "serde", "serde_json", "serde_yaml", + "shell-words", "svd2rust", - "syn 2.0.42", + "syn", "thiserror", "tracing", "tracing-subscriber", @@ -1259,9 +1590,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.109" +version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", @@ -1269,32 +1600,41 @@ dependencies = [ ] [[package]] -name = "syn" -version = "2.0.42" +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 = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "unicode-ident", + "syn", ] [[package]] name = "system-configuration" -version = "0.5.1" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 1.3.2", + "bitflags", "core-foundation", "system-configuration-sys", ] [[package]] name = "system-configuration-sys" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" dependencies = [ "core-foundation-sys", "libc", @@ -1302,85 +1642,70 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.8.1" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" dependencies = [ - "cfg-if", "fastrand", - "redox_syscall", - "rustix", - "windows-sys 0.48.0", -] - -[[package]] -name = "termcolor" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" -dependencies = [ - "winapi-util", + "getrandom 0.3.2", + "once_cell", + "rustix 1.0.5", + "windows-sys 0.59.0", ] [[package]] name = "thiserror" -version = "1.0.51" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.51" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn", ] [[package]] name = "thread_local" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", ] [[package]] -name = "tinyvec" -version = "1.6.0" +name = "tinystr" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ - "tinyvec_macros", + "displaydoc", + "zerovec", ] -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "tokio" -version = "1.35.1" +version = "1.44.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a" dependencies = [ "backtrace", "bytes", "libc", "mio", - "num_cpus", "pin-project-lite", "socket2", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1393,25 +1718,34 @@ dependencies = [ "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.10" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", - "tracing", ] [[package]] name = "toml" -version = "0.7.8" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" dependencies = [ "serde", "serde_spanned", @@ -1421,18 +1755,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.19.15" +version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ "indexmap", "serde", @@ -1441,17 +1775,38 @@ dependencies = [ "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.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -1460,20 +1815,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn", ] [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -1492,9 +1847,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "matchers", "nu-ansi-term", @@ -1514,61 +1869,59 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "unicode-bidi" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" - [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] -name = "unicode-normalization" -version = "0.1.22" +name = "unsafe-libyaml" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" [[package]] -name = "unsafe-libyaml" -version = "0.2.10" +name = "untrusted" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.0" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] [[package]] -name = "utf8-width" -version = "0.1.7" +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 = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "valuable" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "vcpkg" @@ -1591,48 +1944,59 @@ 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.89" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", + "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.89" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.42", + "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.39" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.89" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1640,28 +2004,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.89" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.89" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "web-sys" -version = "0.3.66" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -1676,15 +2043,15 @@ dependencies = [ "either", "home", "once_cell", - "rustix", + "rustix 0.38.44", "windows-sys 0.48.0", ] [[package]] name = "wildmatch" -version = "2.2.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffa44a4268d649eba546544ed45fd9591059d9653a0e584efe030b56d8172b58" +checksum = "68ce1ab1f8c62655ebe1350f589c61e505cf94d385bc6a12899442d9081e71fd" [[package]] name = "winapi" @@ -1703,19 +2070,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] -name = "winapi-util" -version = "0.1.6" +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 = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" dependencies = [ - "winapi", + "windows-result", + "windows-strings", + "windows-targets 0.53.0", ] [[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +name = "windows-result" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +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" @@ -1732,7 +2125,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "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]] @@ -1752,17 +2154,34 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +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 = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "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]] @@ -1773,9 +2192,15 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +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 = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" [[package]] name = "windows_aarch64_msvc" @@ -1785,9 +2210,15 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +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 = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" [[package]] name = "windows_i686_gnu" @@ -1797,9 +2228,27 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +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 = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" [[package]] name = "windows_i686_msvc" @@ -1809,9 +2258,15 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +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 = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" [[package]] name = "windows_x86_64_gnu" @@ -1821,9 +2276,15 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +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 = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" [[package]] name = "windows_x86_64_gnullvm" @@ -1833,9 +2294,15 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +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" @@ -1845,25 +2312,115 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +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 = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" -version = "0.5.30" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b5c3db89721d50d0e2a673f5043fc4722f76dcc352d7b1ab8b8288bed4ed2c5" +checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" dependencies = [ "memchr", ] [[package]] -name = "winreg" -version = "0.50.0" +name = "wit-bindgen-rt" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "cfg-if", - "windows-sys 0.48.0", + "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 055bdfe5..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.31.4" +version = "0.36.1" readme = "README.md" -rust-version = "1.70" +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.3.0", features = ["cmd", "toml-parser"], optional = true } -env_logger = { version = "0.10", 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,22 +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.9.0" -html-escape = "0.2" +regex = "1.10.0" +url = { version = "2.5", features = ["serde"] } [dependencies.svd-parser] features = ["expand"] -version = "0.14.5" +version = "0.14.9" [dependencies.svd-rs] features = ["serde"] -version = "0.14.7" +version = "0.14.12" [dependencies.syn] version = "2.0" -features = ["full","extra-traits"] +features = ["full", "extra-traits"] [workspace] -members = ["ci/svd2rust-regress"] +members = ["svd2rust-regress"] default-members = ["."] -exclude = ["output"] +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 d2f06d4d..d0bf6fb5 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ ![GitHub top language](https://img.shields.io/github/languages/top/rust-embedded/svd2rust) -![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.70+-blue.svg) +![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) @@ -17,9 +17,9 @@ This project is developed and maintained by the [Tools team][team]. ## Minimum Supported Rust Version (MSRV) -The **generated code** is guaranteed to compile on stable Rust 1.65.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.65.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 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 1e4ab69a..00000000 --- a/ci/script.sh +++ /dev/null @@ -1,638 +0,0 @@ -set -euxo pipefail - -test_svd() { - test_svd_for_target cortex-m https://raw.githubusercontent.com/cmsis-svd/cmsis-svd-data/main/data/$VENDOR/${1}.svd -} - -test_patched_stm32() { - test_svd_for_target cortex-m https://stm32-rs.github.io/stm32-rs/${1}.svd.patched -} - -test_svd_for_target() { - curl -fL --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 $options --target $1 --source-type xml -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) - options="--strict --atomics" - ;; - *) - options=$OPTIONS - ;; - esac - - # test crate - cargo init --lib --name foo $td - echo 'cortex-m = "0.7.7"' >> $td/Cargo.toml - echo 'cortex-m-rt = "0.7.3"' >> $td/Cargo.toml - echo 'vcell = "0.1.3"' >> $td/Cargo.toml - if [[ "$options" == *"--atomics"* ]]; then - echo 'portable-atomic = { version = "1.4", default-features = false }' >> $td/Cargo.toml - fi - if [[ "$options" == *"--impl-defmt"* ]]; then - echo 'defmt = { version = "0.3.5", optional = true }' >> $td/Cargo.toml - fi - echo '[profile.dev]' >> $td/Cargo.toml - echo 'incremental = false' >> $td/Cargo.toml - - echo '[lints.rust]' >> $td/Cargo.toml - echo 'dead_code = "deny"' >> $td/Cargo.toml - echo 'improper_ctypes = "deny"' >> $td/Cargo.toml - echo 'missing_docs = "deny"' >> $td/Cargo.toml - echo 'no_mangle_generic_items = "deny"' >> $td/Cargo.toml - echo 'non_shorthand_field_patterns = "deny"' >> $td/Cargo.toml - echo 'overflowing_literals = "deny"' >> $td/Cargo.toml - echo 'path_statements = "deny"' >> $td/Cargo.toml - echo 'patterns_in_fns_without_body = "deny"' >> $td/Cargo.toml - echo 'unconditional_recursion = "deny"' >> $td/Cargo.toml - echo 'unused_allocation = "deny"' >> $td/Cargo.toml - echo 'unused_comparisons = "deny"' >> $td/Cargo.toml - echo 'unused_parens = "deny"' >> $td/Cargo.toml - echo 'while_true = "deny"' >> $td/Cargo.toml - if [[ "${RUST_TOOLCHAIN:-}" == *"nightly"* ]]; then - echo 'private_bounds = "deny"' >> $td/Cargo.toml - echo 'private_interfaces = "deny"' >> $td/Cargo.toml - fi - - 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 - ;; - - # MSP430 - MSP430) - echo '[dependencies.msp430]' >> $td/Cargo.toml - echo 'version = "0.4.0"' >> $td/Cargo.toml - - # Test MSP430 - test_svd_for_target msp430 https://raw.githubusercontent.com/pftbest/msp430g2553/v0.3.0-svd/msp430g2553.svd - test_svd_for_target msp430 https://raw.githubusercontent.com/YuhanLiin/msp430fr2355/master/msp430fr2355.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/cmsis-svd/cmsis-svd-data/main/data/SiliconLabs/SiM3_NRND/SIM3C1x4.svd - test_svd_for_target cortex-m https://raw.githubusercontent.com/cmsis-svd/cmsis-svd-data/main/data/SiliconLabs/SiM3_NRND/SIM3C1x6.svd - test_svd_for_target cortex-m https://raw.githubusercontent.com/cmsis-svd/cmsis-svd-data/main/data/SiliconLabs/SiM3_NRND/SIM3C1x7.svd - test_svd_for_target cortex-m https://raw.githubusercontent.com/cmsis-svd/cmsis-svd-data/main/data/SiliconLabs/SiM3_NRND/SIM3L1x4.svd - test_svd_for_target cortex-m https://raw.githubusercontent.com/cmsis-svd/cmsis-svd-data/main/data/SiliconLabs/SiM3_NRND/SIM3L1x6.svd - test_svd_for_target cortex-m https://raw.githubusercontent.com/cmsis-svd/cmsis-svd-data/main/data/SiliconLabs/SiM3_NRND/SIM3L1x7.svd - test_svd_for_target cortex-m https://raw.githubusercontent.com/cmsis-svd/cmsis-svd-data/main/data/SiliconLabs/SiM3_NRND/SIM3U1x4.svd - test_svd_for_target cortex-m https://raw.githubusercontent.com/cmsis-svd/cmsis-svd-data/main/data/SiliconLabs/SiM3_NRND/SIM3U1x6.svd - test_svd_for_target cortex-m https://raw.githubusercontent.com/cmsis-svd/cmsis-svd-data/main/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 - ;; - - STM32-patched) - # OK - test_patched_stm32 stm32f0x2 - test_patched_stm32 stm32f103 - test_patched_stm32 stm32f411 - test_patched_stm32 stm32f469 - test_patched_stm32 stm32f7x3 - test_patched_stm32 stm32g070 - test_patched_stm32 stm32g473 - test_patched_stm32 stm32h753 - test_patched_stm32 stm32l0x3 - test_patched_stm32 stm32l162 - test_patched_stm32 stm32l4x6 - test_patched_stm32 stm32l562 - test_patched_stm32 stm32mp157 - test_patched_stm32 stm32wb55 - test_patched_stm32 stm32wle5 - test_patched_stm32 stm32c011 - ;; - - 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/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/src/config.rs b/src/config.rs index 8a359dd1..024d8b78 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,8 +1,18 @@ use anyhow::{bail, Result}; -use std::path::{Path, PathBuf}; +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, @@ -13,7 +23,6 @@ pub struct Config { pub ignore_groups: bool, pub keep_list: bool, pub strict: bool, - pub pascal_enum_values: bool, pub feature_group: bool, pub feature_peripheral: bool, pub max_cluster_size: bool, @@ -28,6 +37,19 @@ pub struct Config { 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)] @@ -131,9 +153,30 @@ pub enum Case { 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, @@ -152,8 +195,8 @@ impl IdentFormat { self.case = Some(Case::Pascal); self } - pub fn scake_case(mut self) -> Self { - self.case = Some(Case::Pascal); + pub fn snake_case(mut self) -> Self { + self.case = Some(Case::Snake); self } pub fn prefix(mut self, prefix: &str) -> Self { @@ -164,36 +207,186 @@ impl IdentFormat { 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, PartialEq, Eq)] +#[derive(Clone, Debug, Default, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize), serde(default))] -pub struct IdentFormats { - pub field_reader: IdentFormat, - pub field_writer: IdentFormat, - pub enum_name: IdentFormat, - pub enum_write_name: IdentFormat, - pub enum_value: IdentFormat, - pub interrupt: IdentFormat, - pub cluster: IdentFormat, - pub register: IdentFormat, - pub register_spec: IdentFormat, - pub peripheral: IdentFormat, -} - -impl Default for IdentFormats { - fn default() -> Self { - Self { - field_reader: IdentFormat::default().constant_case().suffix("_R"), - field_writer: IdentFormat::default().constant_case().suffix("_W"), - enum_name: IdentFormat::default().constant_case().suffix("_A"), - enum_write_name: IdentFormat::default().constant_case().suffix("_AW"), - enum_value: IdentFormat::default().constant_case(), - interrupt: IdentFormat::default().constant_case(), - cluster: IdentFormat::default().constant_case(), - register: IdentFormat::default().constant_case(), - register_spec: IdentFormat::default().constant_case().suffix("_SPEC"), - peripheral: IdentFormat::default().constant_case(), +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/device.rs b/src/generate/device.rs index 5251c7ba..96e1d332 100644 --- a/src/generate/device.rs +++ b/src/generate/device.rs @@ -2,16 +2,16 @@ use crate::svd::{array::names, Device, Peripheral}; use proc_macro2::{Span, TokenStream}; use quote::{quote, ToTokens}; -use log::debug; +use log::{debug, warn}; use std::fs::File; use std::io::Write; use std::path::Path; use crate::config::{Config, Target}; -use crate::util::{self, ident, ToSanitizedCase}; +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,7 +28,8 @@ pub fn render(d: &Device, config: &Config, device_x: &mut String) -> Result Result Result Result 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()) @@ -199,6 +219,10 @@ pub fn render(d: &Device, config: &Config, device_x: &mut String) -> Result Result Result { let p_name = util::name_of(p, config.ignore_groups); - let p_snake = p_name.to_sanitized_snake_case(); - let p_ty = ident(&p_name, &config.ident_formats.peripheral, span); + 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_name] #feature_attribute - pub #p_ty: #p_ty, + pub #p_singleton: #p_ty, }); - exprs.extend(quote!(#feature_attribute #p_ty: #p_ty { _marker: PhantomData },)); + exprs.extend(quote!(#feature_attribute #p_singleton: #p_ty::steal(),)); } Peripheral::Array(p, dim_element) => { for p_name in names(p, dim_element) { - let p_snake = p_name.to_sanitized_snake_case(); - let p_ty = ident(&p_name, &config.ident_formats.peripheral, span); + 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_name] #feature_attribute - pub #p_ty: #p_ty, + pub #p_singleton: #p_ty, }); - exprs.extend(quote!(#feature_attribute #p_ty: #p_ty { _marker: PhantomData },)); + exprs.extend(quote!(#feature_attribute #p_singleton: #p_ty::steal(),)); } } } diff --git a/src/generate/generic.rs b/src/generate/generic.rs index 186d3364..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,10 +72,8 @@ 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) @@ -53,9 +98,12 @@ pub trait RegisterSpec { /// Raw field type pub trait FieldSpec: Sized { /// Raw field type (`u8`, `u16`, `u32`, ...). - type Ux: Copy + PartialEq + From; + 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 be `modify`'ed. @@ -67,11 +115,14 @@ pub trait Readable: RegisterSpec {} /// /// Registers marked with `Readable` can be also be `modify`'ed. pub trait Writable: RegisterSpec { + /// 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. @@ -80,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)] @@ -89,169 +140,6 @@ pub trait Resettable: RegisterSpec { } } -/// 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) - where - F: FnOnce(&mut W) -> &mut W, - { - self.register.set( - f(&mut W { - bits: REG::RESET_VALUE & !REG::ONE_TO_MODIFY_FIELDS_BITMAP - | REG::ZERO_TO_MODIFY_FIELDS_BITMAP, - _reg: marker::PhantomData, - }) - .bits, - ); - } -} - -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) - where - F: FnOnce(&mut W) -> &mut W, - { - self.register.set( - f(&mut W { - bits: REG::Ux::default(), - _reg: marker::PhantomData, - }) - .bits, - ); - } -} - -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) - where - for<'w> F: FnOnce(&R, &'w mut W) -> &'w mut W, - { - let bits = self.register.get(); - self.register.set( - 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, - ); - } -} - #[doc(hidden)] pub mod raw { use super::{marker, BitM, FieldSpec, RegisterSpec, Unsafe, Writable}; @@ -304,6 +192,7 @@ pub mod raw { } } + #[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 REG: Writable + RegisterSpec, @@ -331,6 +220,7 @@ pub mod raw { } } + #[must_use = "after creating `BitWriter` you need to call bit setting method"] pub struct BitWriter<'a, REG, FI = bool, M = BitM> where REG: Writable + RegisterSpec, @@ -390,6 +280,30 @@ where /// Used as an argument to the closures in the `write` and `modify` methods of the register. pub type W = raw::W; +impl W { + /// Writes raw bits to the register. + /// + /// # Safety + /// + /// 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 + } +} +impl W +where + REG: Writable, +{ + /// Writes raw bits to the register. + #[inline(always)] + pub fn set(&mut self, bits: REG::Ux) -> &mut Self { + self.bits = bits; + self + } +} + /// Field reader. /// /// Result of the `read` methods of fields. @@ -406,6 +320,12 @@ impl 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 FI: FieldSpec + Copy, @@ -445,21 +365,31 @@ impl BitReader { } } -#[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; - -/// Write field Proxy with unsafe `bits` -pub type FieldWriter<'a, REG, const WI: u8, FI = u8> = raw::FieldWriter<'a, REG, WI, FI, Unsafe>; -/// Write field Proxy with safe `bits` -pub type FieldWriterSafe<'a, REG, const WI: u8, FI = u8> = raw::FieldWriter<'a, REG, WI, FI, Safe>; - -impl<'a, REG, const WI: u8, FI> FieldWriter<'a, REG, WI, FI> +/// 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, FI: FieldSpec, - REG::Ux: From, { /// Field width pub const WIDTH: u8 = WI; @@ -475,7 +405,14 @@ where pub const fn offset(&self) -> u8 { self.o } +} +impl<'a, REG, const WI: u8, FI, Safety> FieldWriter<'a, REG, WI, FI, Safety> +where + REG: Writable + RegisterSpec, + FI: FieldSpec, + REG::Ux: From, +{ /// Writes raw bits to the field /// /// # Safety @@ -487,45 +424,86 @@ where self.w.bits |= (REG::Ux::from(value) & REG::Ux::mask::()) << self.o; self.w } - /// Writes `variant` to the field +} + +impl<'a, REG, const WI: u8, FI> FieldWriter<'a, REG, WI, FI, Safe> +where + REG: Writable + RegisterSpec, + FI: FieldSpec, + REG::Ux: From, +{ + /// Writes raw bits to the field #[inline(always)] - pub fn variant(self, variant: FI) -> &'a mut W { - unsafe { self.bits(FI::Ux::from(variant)) } + pub fn set(self, value: FI::Ux) -> &'a mut W { + unsafe { self.bits(value) } } } -impl<'a, REG, const WI: u8, FI> FieldWriterSafe<'a, REG, WI, FI> +impl<'a, REG, const WI: u8, FI, const MIN: u64, const MAX: u64> + FieldWriter<'a, REG, WI, FI, Range> where REG: Writable + RegisterSpec, FI: FieldSpec, REG::Ux: From, + u64: From, { - /// Field width - pub const WIDTH: u8 = WI; - - /// Field width + /// Writes raw bits to the field #[inline(always)] - pub const fn width(&self) -> u8 { - WI + pub fn set(self, value: FI::Ux) -> &'a mut W { + { + let value = u64::from(value); + assert!(value >= MIN && value <= MAX); + } + unsafe { self.bits(value) } } +} - /// Field offset +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 const fn offset(&self) -> u8 { - self.o + pub fn set(self, value: FI::Ux) -> &'a mut W { + { + let value = u64::from(value); + assert!(value >= MIN); + } + unsafe { self.bits(value) } } +} +impl<'a, REG, const WI: u8, FI, const MAX: u64> FieldWriter<'a, REG, WI, FI, RangeTo> +where + REG: Writable + RegisterSpec, + FI: FieldSpec, + REG::Ux: From, + u64: From, +{ /// Writes raw bits to the field #[inline(always)] - pub 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 + pub fn set(self, value: FI::Ux) -> &'a mut W { + { + let value = u64::from(value); + assert!(value <= MAX); + } + unsafe { self.bits(value) } } +} + +impl<'a, REG, const WI: u8, FI, Safety> FieldWriter<'a, REG, WI, FI, Safety> +where + REG: Writable + RegisterSpec, + FI: IsEnum, + REG::Ux: From, +{ /// Writes `variant` to the field #[inline(always)] pub fn variant(self, variant: FI) -> &'a mut W { - self.bits(FI::Ux::from(variant)) + unsafe { self.bits(FI::Ux::from(variant)) } } } @@ -550,7 +528,7 @@ macro_rules! bit_proxy { pub const fn width(&self) -> u8 { Self::WIDTH } - + /// Field offset #[inline(always)] pub const fn offset(&self) -> u8 { @@ -560,8 +538,8 @@ macro_rules! bit_proxy { /// Writes bit to the field #[inline(always)] 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.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 @@ -589,13 +567,13 @@ where /// Sets the field bit #[inline(always)] pub fn set_bit(self) -> &'a mut W { - self.w.bits |= REG::Ux::one() << self.o; + self.w.bits |= REG::Ux::ONE << self.o; self.w } /// Clears the field bit #[inline(always)] pub fn clear_bit(self) -> &'a mut W { - self.w.bits &= !(REG::Ux::one() << self.o); + self.w.bits &= !(REG::Ux::ONE << self.o); self.w } } @@ -608,7 +586,7 @@ where /// Sets the field bit #[inline(always)] pub fn set_bit(self) -> &'a mut W { - self.w.bits |= REG::Ux::one() << self.o; + self.w.bits |= REG::Ux::ONE << self.o; self.w } } @@ -621,7 +599,7 @@ where /// Clears the field bit #[inline(always)] pub fn clear_bit(self) -> &'a mut W { - self.w.bits &= !(REG::Ux::one() << self.o); + self.w.bits &= !(REG::Ux::ONE << self.o); self.w } } @@ -634,7 +612,7 @@ where ///Clears the field bit by passing one #[inline(always)] pub fn clear_bit_by_one(self) -> &'a mut W { - self.w.bits |= REG::Ux::one() << self.o; + self.w.bits |= REG::Ux::ONE << self.o; self.w } } @@ -647,7 +625,7 @@ where ///Sets the field bit by passing zero #[inline(always)] pub fn set_bit_by_zero(self) -> &'a mut W { - self.w.bits &= !(REG::Ux::one() << self.o); + self.w.bits &= !(REG::Ux::ONE << self.o); self.w } } @@ -660,7 +638,7 @@ where ///Toggle the field bit by passing one #[inline(always)] pub fn toggle_bit(self) -> &'a mut W { - self.w.bits |= REG::Ux::one() << self.o; + self.w.bits |= REG::Ux::ONE << self.o; self.w } } @@ -673,7 +651,7 @@ where ///Toggle the field bit by passing zero #[inline(always)] pub fn toggle_bit(self) -> &'a mut W { - self.w.bits &= !(REG::Ux::one() << self.o); + self.w.bits &= !(REG::Ux::ONE << self.o); self.w } } diff --git a/src/generate/generic_atomic.rs b/src/generate/generic_atomic.rs index f0c436f6..55250961 100644 --- a/src/generate/generic_atomic.rs +++ b/src/generate/generic_atomic.rs @@ -39,7 +39,7 @@ mod atomic { impl Reg where - REG::Ux: AtomicOperations + Default + core::ops::Not, + 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. @@ -53,7 +53,7 @@ mod atomic { F: FnOnce(&mut W) -> &mut W, { let bits = f(&mut W { - bits: Default::default(), + bits: REG::Ux::ZERO, _reg: marker::PhantomData, }) .bits; @@ -72,7 +72,7 @@ mod atomic { F: FnOnce(&mut W) -> &mut W, { let bits = f(&mut W { - bits: !REG::Ux::default(), + bits: !REG::Ux::ZERO, _reg: marker::PhantomData, }) .bits; @@ -91,7 +91,7 @@ mod atomic { F: FnOnce(&mut W) -> &mut W, { let bits = f(&mut W { - bits: Default::default(), + bits: REG::Ux::ZERO, _reg: marker::PhantomData, }) .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 8c1a2ed6..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, ident, ToSanitizedCase}; +use crate::util::{self, ident}; use crate::{Config, Target}; use anyhow::Result; @@ -47,6 +47,7 @@ pub fn render( 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 },)); @@ -54,18 +55,18 @@ pub fn render( } pos += 1; - let i_ty = ident(&interrupt.0.name, &config.ident_formats.interrupt, span); + 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_special_chars(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); @@ -74,13 +75,13 @@ pub fn render( 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, }); @@ -307,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(()); @@ -336,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 9e50d123..618952eb 100644 --- a/src/generate/peripheral.rs +++ b/src/generate/peripheral.rs @@ -5,19 +5,20 @@ use std::fmt; use svd_parser::expand::{ derive_cluster, derive_peripheral, derive_register, BlockPath, Index, RegisterPath, }; +use syn::LitInt; 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, ident, name_to_ty, path_segment, type_path, unsuffixed, zst_type, 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}; @@ -38,108 +39,93 @@ 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 p_ty = ident(&name, &config.ident_formats.peripheral, span); + let p_ty = ident(&name, config, "peripheral", span); let name_str = p_ty.to_string(); - let address = util::hex(p.base_address); - let description = util::respace(p.description.as_ref().unwrap_or(&p.name)); + 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 mod_ty = 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, 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 steal_fn = quote! { - /// 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: PhantomData } - } + 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 mut snake_names = Vec::with_capacity(dim.dim as _); + let mut feature_names = Vec::with_capacity(dim.dim as _); for pi in svd::peripheral::expand(p, dim) { let name = &pi.name; - let description = pi.description.as_deref().unwrap_or(&p.name); - let p_ty = ident(name, &config.ident_formats.peripheral, span); + 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 address = util::hex(pi.base_address); - let p_snake = name.to_sanitized_snake_case(); - snake_names.push(p_snake.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_n.extend(quote! { #[cfg(feature = #p_snake)] }) + feature_attribute_n.extend(quote! { #[cfg(feature = #p_feature)] }) }; // Insert the peripherals structure - out.extend(quote! { - #[doc = #description] - #feature_attribute_n - pub struct #p_ty { _marker: PhantomData<*const ()> } - - #feature_attribute_n - unsafe impl Send for #p_ty {} - - #feature_attribute_n - impl #p_ty { - ///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 - } - - #steal_fn - } - - #feature_attribute_n - impl Deref for #p_ty { - type Target = #base::RegisterBlock; - - #[inline(always)] - fn deref(&self) -> &Self::Target { - unsafe { &*Self::PTR } - } - } - - #feature_attribute_n - impl core::fmt::Debug for #p_ty { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - f.debug_struct(#name_str).finish() - } - } - }); + 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 #mod_ty; }); @@ -147,57 +133,19 @@ pub fn render(p_original: &Peripheral, index: &Index, config: &Config) -> Result } } 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 #p_ty { _marker: PhantomData<*const ()> } - - #feature_attribute - unsafe impl Send for #p_ty {} - - #feature_attribute - impl #p_ty { - ///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 - } - - #steal_fn - } - - #feature_attribute - impl Deref for #p_ty { - type Target = #base::RegisterBlock; - - #[inline(always)] - fn deref(&self) -> &Self::Target { - unsafe { &*Self::PTR } - } - } - - #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() - } - } - }); + 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 #mod_ty; }); @@ -206,10 +154,6 @@ pub fn render(p_original: &Peripheral, index: &Index, config: &Config) -> Result } } - let description = util::escape_special_chars( - 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(); @@ -244,21 +188,28 @@ pub fn render(p_original: &Peripheral, index: &Index, config: &Config) -> Result "Pushing {} register or cluster blocks into output", ercs.len() ); - let reg_block = register_or_cluster_block(&ercs, &derive_infos, 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 #mod_ty #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); @@ -287,7 +238,7 @@ struct RegisterBlockField { syn_field: syn::Field, offset: u32, size: u32, - accessors: Vec, + accessors: Vec, } #[derive(Clone, Debug)] @@ -307,7 +258,7 @@ impl Region { 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; @@ -345,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() { @@ -413,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 { @@ -513,7 +464,8 @@ impl FieldRegions { } fn make_comment(size: u32, offset: u32, description: &str) -> String { - let desc = util::escape_special_chars(&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}") @@ -524,15 +476,17 @@ 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, derive_infos, config) + 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. @@ -630,8 +584,13 @@ fn register_or_cluster_block( } }); + let mut doc_alias = None; let block_ty = if let Some(name) = name { - ident(name, &config.ident_formats.cluster, span) + let ty = ident(name, config, "cluster", span); + if ty != name { + doc_alias = Some(quote!(#[doc(alias = #name)])); + } + ty } else { Ident::new("RegisterBlock", span) }; @@ -645,9 +604,10 @@ fn register_or_cluster_block( }); Ok(quote! { - ///Register block #[repr(C)] #derive_debug + #[doc = #doc] + #doc_alias pub struct #block_ty { #rbfs } @@ -660,6 +620,7 @@ fn register_or_cluster_block( /// `RegisterBlockField`s containing `Field`s. fn expand( ercs: &[RegisterCluster], + path: &BlockPath, derive_infos: &[DeriveInfo], config: &Config, ) -> Result> { @@ -671,14 +632,14 @@ fn expand( match &erc { RegisterCluster::Register(register) => { let reg_name = ®ister.name; - let expanded_reg = expand_register(register, derive_info, config) + 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) + 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); @@ -721,7 +682,7 @@ fn check_erc_derive_infos( }; match register { Register::Single(_) => { - let ty_name = info_name.to_string(); + let ty_name = info_name.clone(); *derive_info = match explicit_rpath { None => { match compare_this_against_prev( @@ -749,11 +710,11 @@ fn check_erc_derive_infos( Register::Array(..) => { // Only match integer indeces when searching for disjoint arrays - let re_string = util::replace_suffix(&info_name, "([0-9]+|%s)"); + 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.to_string(); // keep suffix for regex matching + let ty_name = info_name.clone(); // keep suffix for regex matching *derive_info = match explicit_rpath { None => { match compare_this_against_prev( @@ -782,7 +743,7 @@ fn check_erc_derive_infos( } RegisterCluster::Cluster(cluster) => { *derive_info = DeriveInfo::Cluster; - ercs_type_info.push((cluster.name.to_string(), None, erc, derive_info)); + ercs_type_info.push((cluster.name.clone(), None, erc, derive_info)); } }; } @@ -921,9 +882,9 @@ fn is_derivable( /// 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 @@ -933,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?) } } @@ -941,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, &DeriveInfo::Root, config)? + let reg_size: u32 = expand_register(reg, path, &DeriveInfo::Root, config)? .iter() .map(|rbf| rbf.size) .sum(); @@ -955,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)? } }; @@ -965,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 @@ -977,28 +942,30 @@ fn expand_cluster(cluster: &Cluster, config: &Config) -> Result { let doc = make_comment(cluster_size, info.address_offset, &description); - let name: Ident = info.name.to_snake_case_ident(Span::call_site()); + 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, offset: info.address_offset, size: cluster_size, - accessors: vec![RegAccessor { - doc, - name, - ty, - offset: unsuffixed(info.address_offset), - } - .into()], + accessors: vec![accessor], }) } Cluster::Array(info, array_info) => { @@ -1029,57 +996,55 @@ fn expand_cluster(cluster: &Cluster, config: &Config) -> Result::with_capacity((array_info.dim + 1) as _); - accessors.push(if array_convertible { - ArrayAccessor { - doc, - name: nb_name_sc.clone(), - ty: ty.clone(), - offset: unsuffixed(info.address_offset), - dim: unsuffixed(array_info.dim), - increment: unsuffixed(array_info.dim_increment), - } - .into() - } else { - RawArrayAccessor { + 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: nb_name_sc.clone(), + name: accessor_name.clone(), ty: ty.clone(), - offset: unsuffixed(info.address_offset), - dim: unsuffixed(array_info.dim), - increment: unsuffixed(array_info.dim_increment), - } - .into() - }); + 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 = ci.name.to_snake_case_ident(span); + let idx_name = ident(&ci.name, config, "cluster_accessor", span); let doc = make_comment( cluster_size, ci.address_offset, ci.description.as_deref().unwrap_or(&ci.name), ); - let i = unsuffixed(i as u64); accessors.push( - ArrayElemAccessor { + Accessor::ArrayElem(ArrayElemAccessor { doc, name: idx_name, ty: ty.clone(), - basename: nb_name_sc.clone(), + basename: accessor_name.clone(), i, - } - .into(), + }) + .raw_if(false), ); } } @@ -1088,7 +1053,7 @@ fn expand_cluster(cluster: &Cluster, config: &Config) -> Result Result Result Result> { @@ -1143,32 +1110,35 @@ fn expand_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 mut ty_name = if register.is_single() { - info_name.to_string() + info_name.clone() } else { - util::replace_suffix(&info_name, "") + info_name.remove_dim() }; + let mut ty_str = ty_name.clone(); match register { Register::Single(info) => { - let doc = make_comment(register_size, info.address_offset, &description); - let ty = name_to_ty(&ty_name); - let name = ty_name.to_snake_case_ident(Span::call_site()); + 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, offset: info.address_offset, size: register_size, - accessors: vec![RegAccessor { - doc, - name, - ty, - offset: unsuffixed(info.address_offset), - } - .into()], + accessors: vec![accessor], }) } Register::Array(info, array_info) => { @@ -1179,7 +1149,7 @@ fn expand_register( || (register_size <= array_info.dim_increment * BITS_PER_BYTE); let convert_list = match config.keep_list { - true => info.name.contains("[%s]"), + true => info_name.contains("[%s]"), false => true, }; @@ -1191,82 +1161,82 @@ fn expand_register( // 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.first().unwrap().into() + dim_index[0].as_str().into() } else if sequential_indexes_from0 { "0".into() } else { "".into() }; let ac = match derive_info { - DeriveInfo::Implicit(_) => { - ty_name = util::replace_suffix(&info_name, &index); - convert_list && sequential_indexes_from0 - } - DeriveInfo::Explicit(_) => { - ty_name = util::replace_suffix(&info_name, &index); - convert_list && sequential_indexes_from0 + 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 ty = name_to_ty(&ty_name); + let span = Span::call_site(); + let ty = name_to_ty(ident(&ty_str, config, "register", span)); if array_convertible || array_proxy_convertible { - let span = Span::call_site(); - let nb_name_sc = if let Some(dim_name) = array_info.dim_name.as_ref() { - util::fullname(dim_name, &info.alternate_group, config.ignore_groups) - .to_snake_case_ident(span) + 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 { - ty_name.to_snake_case_ident(span) + ident(&ty_name, config, "register_accessor", span) }; let doc = make_comment( register_size * array_info.dim, info.address_offset, - &description, + description, ); - let mut accessors = Vec::::with_capacity((array_info.dim + 1) as _); - accessors.push(if array_convertible { - ArrayAccessor { - doc, - name: nb_name_sc.clone(), - ty: ty.clone(), - offset: unsuffixed(info.address_offset), - dim: unsuffixed(array_info.dim), - increment: unsuffixed(array_info.dim_increment), - } - .into() - } else { - RawArrayAccessor { + 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: nb_name_sc.clone(), + name: accessor_name.clone(), ty: ty.clone(), - offset: unsuffixed(info.address_offset), - dim: unsuffixed(array_info.dim), - increment: unsuffixed(array_info.dim_increment), - } - .into() - }); + 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 = - util::fullname(&ri.name, &info.alternate_group, config.ignore_groups) - .to_snake_case_ident(span); + let idx_name = ident( + &util::fullname(&ri.name, &info.alternate_group, config.ignore_groups), + config, + "register_accessor", + span, + ); let doc = make_comment( register_size, ri.address_offset, ri.description.as_deref().unwrap_or(&ri.name), ); - let i = unsuffixed(i as u64); accessors.push( - ArrayElemAccessor { + Accessor::ArrayElem(ArrayElemAccessor { doc, name: idx_name, ty: ty.clone(), - basename: nb_name_sc.clone(), + basename: accessor_name.clone(), i, - } - .into(), + }) + .raw_if(false), ); } }; @@ -1275,7 +1245,7 @@ fn expand_register( } else { zst_type() }; - let syn_field = new_syn_field(nb_name_sc, array_ty); + let syn_field = new_syn_field(accessor_name, array_ty); register_expanded.push(RegisterBlockField { syn_field, offset: info.address_offset, @@ -1293,20 +1263,21 @@ fn expand_register( info.address_offset, ri.description.as_deref().unwrap_or(&ri.name), ); - let name = ri.name.to_snake_case_ident(Span::call_site()); + 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, offset: ri.address_offset, size: register_size, - accessors: vec![RegAccessor { - doc, - name, - ty: ty.clone(), - offset: unsuffixed(info.address_offset), - } - .into()], + accessors: vec![accessor], }); } } @@ -1369,38 +1340,38 @@ fn cluster_block( index: &Index, config: &Config, ) -> Result { - let description = - util::escape_special_chars(&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 mod_ty = mod_name.to_snake_case_ident(span); - let block_ty = ident(&mod_name, &config.ident_formats.cluster, 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(ident( - &dname, - &config.ident_formats.cluster, - span, - ))); + derived + .path + .segments + .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 #block_ty; - pub use self::#mod_derived as #mod_ty; + #[doc = #doc] + pub use #derived as #block_ty; + pub use #mod_derived as #mod_ty; }) } else { let cpath = path.new_cluster(&c.name); @@ -1416,8 +1387,10 @@ fn cluster_block( }; let reg_block = register_or_cluster_block( &c.children, + &path.new_cluster(&c.name), &mod_derive_infos, Some(&mod_name), + &doc, cluster_size, config, )?; @@ -1429,11 +1402,11 @@ fn cluster_block( }; Ok(quote! { - #[doc = #description] + #[doc = #doc] pub use self::#mod_ty::#block_ty; ///Cluster - #[doc = #description] + #[doc = #doc] pub mod #mod_ty { #mod_items } diff --git a/src/generate/peripheral/accessor.rs b/src/generate/peripheral/accessor.rs index 76a17b55..5bb91718 100644 --- a/src/generate/peripheral/accessor.rs +++ b/src/generate/peripheral/accessor.rs @@ -1,124 +1,139 @@ use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote, ToTokens}; +use crate::util::unsuffixed; + #[derive(Clone, Debug)] pub enum Accessor { Reg(RegAccessor), - RawReg(RawRegAccessor), Array(ArrayAccessor), - RawArray(RawArrayAccessor), ArrayElem(ArrayElemAccessor), } +#[derive(Clone, Debug)] +pub enum AccessType { + Ref(Accessor), + RawRef(Accessor), +} + impl Accessor { - pub fn raw(self) -> Self { - match self { - Self::RawReg(_) | Self::RawArray(_) | Self::ArrayElem(_) => self, - Self::Reg(a) => RawRegAccessor { - doc: a.doc, - name: a.name, - ty: a.ty, - offset: a.offset, - } - .into(), - Self::Array(a) => RawArrayAccessor { - doc: a.doc, - name: a.name, - ty: a.ty, - offset: a.offset, - dim: a.dim, - increment: a.increment, - } - .into(), + pub fn raw_if(self, flag: bool) -> AccessType { + if flag { + AccessType::RawRef(self) + } else { + AccessType::Ref(self) } } } -impl ToTokens for Accessor { - fn to_tokens(&self, tokens: &mut TokenStream) { +impl AccessType { + pub fn raw(self) -> Self { match self { - Self::Reg(a) => a.to_tokens(tokens), - Self::RawReg(a) => a.to_tokens(tokens), - Self::Array(a) => a.to_tokens(tokens), - Self::RawArray(a) => a.to_tokens(tokens), - Self::ArrayElem(a) => a.to_tokens(tokens), + Self::RawRef(_) => self, + Self::Ref(a) => Self::RawRef(a), } } } -impl From for Accessor { - fn from(value: RegAccessor) -> Self { - Self::Reg(value) - } -} - -impl From for Accessor { - fn from(value: RawRegAccessor) -> Self { - Self::RawReg(value) - } -} - -impl From for Accessor { - fn from(value: ArrayAccessor) -> Self { - Self::Array(value) - } -} - -impl From for Accessor { - fn from(value: RawArrayAccessor) -> Self { - Self::RawArray(value) - } -} - -impl From for Accessor { - fn from(value: ArrayElemAccessor) -> Self { - Self::ArrayElem(value) - } -} - -#[derive(Clone, Debug)] -pub struct RegAccessor { - pub doc: String, - pub name: Ident, - pub ty: syn::Type, - pub offset: syn::LitInt, -} - -impl ToTokens for RegAccessor { +impl ToTokens for AccessType { fn to_tokens(&self, tokens: &mut TokenStream) { - let Self { doc, name, ty, .. } = self; - quote! { - #[doc = #doc] - #[inline(always)] - pub const fn #name(&self) -> &#ty { - &self.#name + match self { + Self::Ref(Accessor::Reg(RegAccessor { doc, name, ty, .. })) => { + quote! { + #[doc = #doc] + #[inline(always)] + pub const fn #name(&self) -> &#ty { + &self.#name + } + } } - } - .to_tokens(tokens); - } -} - -#[derive(Clone, Debug)] -pub struct RawRegAccessor { - pub doc: String, - pub name: Ident, - pub ty: syn::Type, - pub offset: syn::LitInt, -} - -impl ToTokens for RawRegAccessor { - fn to_tokens(&self, tokens: &mut TokenStream) { - let Self { - doc, - name, - ty, - offset, - } = self; - quote! { - #[doc = #doc] - #[inline(always)] - pub const fn #name(&self) -> &#ty { - unsafe { &*(self as *const Self).cast::().add(#offset).cast() } + 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); @@ -126,75 +141,22 @@ impl ToTokens for RawRegAccessor { } #[derive(Clone, Debug)] -pub struct ArrayAccessor { +pub struct RegAccessor { pub doc: String, pub name: Ident, pub ty: syn::Type, - pub offset: syn::LitInt, - pub dim: syn::LitInt, - pub increment: syn::LitInt, -} - -impl ToTokens for ArrayAccessor { - fn to_tokens(&self, tokens: &mut TokenStream) { - let Self { doc, name, ty, .. } = self; - let name_iter = Ident::new(&format!("{name}_iter"), Span::call_site()); - quote! { - #[doc = #doc] - #[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() - } - } - .to_tokens(tokens); - } + pub offset: u32, } #[derive(Clone, Debug)] -pub struct RawArrayAccessor { +pub struct ArrayAccessor { pub doc: String, pub name: Ident, pub ty: syn::Type, - pub offset: syn::LitInt, - pub dim: syn::LitInt, - pub increment: syn::LitInt, -} - -impl ToTokens for RawArrayAccessor { - fn to_tokens(&self, tokens: &mut TokenStream) { - let Self { - doc, - name, - ty, - offset, - dim, - increment, - } = self; - let name_iter = Ident::new(&format!("{name}_iter"), Span::call_site()); - let cast = quote! { unsafe { &*(self as *const Self).cast::().add(#offset).add(#increment * n).cast() } }; - quote! { - #[doc = #doc] - #[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(|n| #cast) - } - } - .to_tokens(tokens); - } + pub offset: u32, + pub dim: u32, + pub increment: u32, + pub note: Option, } #[derive(Clone, Debug)] @@ -203,25 +165,5 @@ pub struct ArrayElemAccessor { pub name: Ident, pub ty: syn::Type, pub basename: Ident, - pub i: syn::LitInt, -} - -impl ToTokens for ArrayElemAccessor { - fn to_tokens(&self, tokens: &mut TokenStream) { - let Self { - doc, - name, - ty, - basename, - i, - } = &self; - quote! { - #[doc = #doc] - #[inline(always)] - pub const fn #name(&self) -> &#ty { - self.#basename(#i) - } - } - .to_tokens(tokens); - } + pub i: usize, } diff --git a/src/generate/register.rs b/src/generate/register.rs index af48cbdf..7622ba71 100644 --- a/src/generate/register.rs +++ b/src/generate/register.rs @@ -1,11 +1,11 @@ use crate::svd::{ 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}; @@ -15,14 +15,30 @@ use svd_parser::expand::{ use crate::config::Config; use crate::util::{ - self, ident, ident_to_path, path_segment, replace_suffix, type_path, unsuffixed, FullName, - ToSanitizedCase, U32Ext, + 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.ident_formats.register_spec, span) + 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( @@ -34,42 +50,45 @@ pub fn render( ) -> Result { let mut name = util::name_of(register, config.ignore_groups); // Rename if this is a derived array - if dpath.is_some() { + if let Some(dpath) = dpath.as_ref() { if let MaybeArray::Array(info, array_info) = register { if let Some(dim_index) = &array_info.dim_index { - let index: Cow = dim_index.first().unwrap().into(); - name = replace_suffix(&info.fullname(config.ignore_groups), &index).into() + 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 reg_ty = ident(&name, &config.ident_formats.register, span); - let mod_ty = name.to_snake_case_ident(span); - let description = util::escape_special_chars( - 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(ident( - &dname, - &config.ident_formats.register, - span, - ))); + derived + .path + .segments + .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 #reg_ty; @@ -88,34 +107,31 @@ pub fn render( return Err(anyhow!("Incorrect access of register {}", register.name)); }; + let rpath = path.new_register(®ister.name); let mut alias_doc = format!( - "{name} ({accs}) register accessor: {description}\n\n{}", + "{name} ({accs}) register accessor: {description}{}{}", api_docs( access.can_read(), access.can_write(), register.properties.reset_value.is_some(), &mod_ty, false, - register.read_action, - )? + register, + &rpath, + config, + )?, + read_action_docs(access.can_read(), register.read_action), ); - if mod_ty != "cfg" { - alias_doc += - format!("\n\nFor information about available fields see [`mod@{mod_ty}`] module") - .as_str(); - } + 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] + #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] @@ -128,19 +144,39 @@ 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, - read_action: Option, + register: &Register, + rpath: &RegisterPath, + config: &Config, ) -> Result { fn method(s: &str) -> String { - format!("[`{s}`](crate::generic::Reg::{s})") + format!("[`{s}`](crate::Reg::{s})") } - let mut doc = String::new(); + let mut doc = String::from("\n\n"); if can_read { write!( @@ -149,17 +185,6 @@ fn api_docs( method("read"), if inmodule { "(R)" } else { "" }, )?; - - if let Some(action) = read_action { - doc.push_str("WARN: "); - 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(' '); } if can_write { @@ -182,29 +207,52 @@ fn api_docs( } if can_read && can_write { - write!(doc, "You can also {} this register. ", method("modify"),)?; + 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 regspec_ty = regspec(&name, config, span); - let name_snake_case = name.to_snake_case_ident(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() { @@ -213,13 +261,11 @@ pub fn render_register_mod( rsize.next_power_of_two() }; let rty = rsize.to_ty()?; - let description = util::escape_special_chars( - 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(); @@ -228,7 +274,7 @@ pub fn render_register_mod( let can_reset = properties.reset_value.is_some(); if can_read { - let desc = format!("Register `{}` reader", register.name); + let desc = format!("Register `{rname}` reader"); mod_items.extend(quote! { #[doc = #desc] pub type R = crate::R<#regspec_ty>; @@ -236,7 +282,7 @@ pub fn render_register_mod( } if can_write { - let desc = format!("Register `{}` writer", register.name); + let desc = format!("Register `{rname}` writer"); mod_items.extend(quote! { #[doc = #desc] pub type W = crate::W<#regspec_ty>; @@ -249,9 +295,6 @@ pub fn render_register_mod( let mut zero_to_modify_fields_bitmap = 0; let mut one_to_modify_fields_bitmap = 0; - let open = Punct::new('{', Spacing::Alone); - let close = Punct::new('}', Spacing::Alone); - let debug_feature = config .impl_debug_feature .as_ref() @@ -287,7 +330,7 @@ pub fn render_register_mod( access, properties, &mut mod_items, - path, + rpath, index, config, )?; @@ -310,84 +353,26 @@ pub fn render_register_mod( write!(f, "{}", self.bits()) } } - #debug_feature - impl core::fmt::Debug for crate::generic::Reg<#regspec_ty> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - core::fmt::Debug::fmt(&self.read(), f) - } - } }); } 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 - }); + 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); - - // 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( - register - .fields - .as_ref() - .and_then(|fields| fields.first()) - .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 { - self.bits = bits; - self - } - }); - } else { - mod_items.extend(quote! { - /// Writes raw bits to the register. - /// - /// # Safety - /// - /// Passing incorrect value can cause undefined behaviour. See reference manual - #[inline(always)] - pub unsafe fn bits(&mut self, bits: #rty) -> &mut Self { - self.bits = bits; - self - } - }); - } - - close.to_tokens(&mut mod_items); } let doc = format!( - "{description}\n\n{}", - api_docs( - can_read, - can_write, - can_reset, - &name_snake_case, - true, - register.read_action, - )? + "{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! { @@ -400,33 +385,61 @@ pub fn render_register_mod( }); if can_read { - let doc = format!("`read()` method returns [`{name_snake_case}::R`](R) reader structure",); + 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 { - let doc = - format!("`write(|w| ..)` method takes [`{name_snake_case}::W`](W) writer structure",); + // 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 safe_ty = if let Safety::Safe = Safety::get( + register + .fields + .as_ref() + .and_then(|fields| fields.first()) + .and_then(|field| field.write_constraint) + .as_ref(), + rsize, + ) { + Safety::Safe + } else if let Safety::Safe = Safety::get(register.write_constraint.as_ref(), rsize) { + Safety::Safe + } else { + Safety::Unsafe + }; + let safe_ty = safe_ty.ident(rsize); + + let doc = format!("`write(|w| ..)` method takes [`{mod_ty}::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); + 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;)); mod_items.extend(quote! { #[doc = #doc] impl crate::Writable for #regspec_ty { - 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; + 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) { - let doc = format!("`reset()` method sets {} to value {rv}", register.name); + 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 { - const RESET_VALUE: Self::Ux = #rv; + #rv } }); } @@ -442,8 +455,6 @@ fn render_register_mod_debug( let name = util::name_of(register, config.ignore_groups); let span = Span::call_site(); let regspec_ty = regspec(&name, config, span); - let open = Punct::new('{', Spacing::Alone); - let close = Punct::new('}', Spacing::Alone); let mut r_debug_impl = TokenStream::new(); let debug_feature = config .impl_debug_feature @@ -454,8 +465,14 @@ fn render_register_mod_debug( if access.can_read() && register.read_action.is_none() { r_debug_impl.extend(quote! { #debug_feature - impl core::fmt::Debug for R #open - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result #open + 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() { @@ -463,42 +480,33 @@ fn render_register_mod_debug( Some(a) => a, None => access, }; - let bit_or_bits = if f.bit_width() > 1 { "bits" } else { "bit" }; - let bit_or_bits = syn::Ident::new(bit_or_bits, span); 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 = util::replace_suffix(&f.name, &suffix) - .to_snake_case_ident(Span::call_site()); + let f_name_n = field_accessor(&f.name.expand_dim(&suffix), config, span); let f_name_n_s = format!("{f_name_n}"); - r_debug_impl.extend(quote! { - .field(#f_name_n_s, &format_args!("{}", self.#f_name_n().#bit_or_bits())) + fmt_inner_impl.extend(quote! { + .field(#f_name_n_s, &self.#f_name_n()) }); } } else { - let f_name = util::replace_suffix(&f.name, ""); - let f_name = f_name.to_snake_case_ident(span); + let f_name = f.name.remove_dim(); + let f_name = field_accessor(&f_name, config, span); let f_name_s = format!("{f_name}"); - r_debug_impl.extend(quote! { - .field(#f_name_s, &format_args!("{}", self.#f_name().#bit_or_bits())) + fmt_inner_impl.extend(quote! { + .field(#f_name_s, &self.#f_name()) }); } } } - r_debug_impl.extend(quote! { + fmt_inner_impl.extend(quote! { .finish() - #close - #close - }); - 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 { - core::fmt::Debug::fmt(&self.read(), f) - } - } }); + 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 @@ -514,6 +522,92 @@ fn render_register_mod_debug( Ok(r_debug_impl) } +#[derive(Clone, Copy, Debug)] +pub enum EV<'a> { + New(&'a EnumeratedValues), + Derived(&'a EnumeratedValues, &'a EnumPath), +} + +impl EV<'_> { + fn values(&self) -> &EnumeratedValues { + match self { + Self::New(e) | Self::Derived(e, _) => e, + } + } +} + +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), + } + } +} + +#[derive(Clone, Copy, Debug)] +pub enum RWEnum<'a> { + ReadWriteCommon(EV<'a>), + ReadWrite(ReadEnum<'a>, WriteEnum<'a>), + Read(ReadEnum<'a>), + Write(WriteEnum<'a>), +} + +#[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(_))) + } + 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(_)) + ) + } +} + #[allow(clippy::too_many_arguments)] pub fn fields( mut fields: Vec<&Field>, @@ -538,8 +632,9 @@ 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 @@ -558,19 +653,23 @@ pub fn fields( return Err(anyhow!("incorrect field {}", f.name)); } - let name = util::replace_suffix(&f.name, ""); - let name_snake_case = if let Field::Array( - _, - DimElement { - dim_name: Some(dim_name), - .. + 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 }, - ) = &f - { - dim_name.to_snake_case_ident(span) - } else { - name.to_snake_case_ident(span) - }; + config, + span, + ); let description_raw = f.description.as_deref().unwrap_or(""); // raw description, if absent using empty string let description = util::respace(&util::escape_special_chars(description_raw)); @@ -585,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(); @@ -607,16 +702,47 @@ pub fn fields( 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.clone()))); + 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(",")) @@ -627,286 +753,272 @@ 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 = &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 - }; - - // 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) - { - ident( - evs.name.as_deref().unwrap_or(&name), - &config.ident_formats.enum_name, - span, - ) - } else { - // raw_field_value_read_ty - fty.clone() - }; - - // name of read proxy type - let reader_ty = ident(&name, &config.ident_formats.field_reader, 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 = matches!( - lookup_filter(&lookup_results, Usage::Read), - Some((_, None)) | None - ); - - // derive the read proxy structure if necessary. - if should_derive_reader { - 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 += 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); - - // 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); - } - } - - // 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, config); + 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 { - // 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; + 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 { - add_from_variants( - mod_items, - variants.iter(), - &value_read_ty, - &fty, - &description, - rv, - config, - ); - has_reserved_variant = evs.values.len() != (1 << width); - } - - // 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, } + 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 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!(), - }); - } + // 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. - let ret_ty = if has_reserved_variant { - quote!(Option<#value_read_ty>) - } else { - 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 + // 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); } - } - }); - // 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, - ); + 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 + } + } + }); - let doc = util::escape_special_chars(&util::respace(&v.doc)); - enum_items.extend(quote! { - #[doc = #doc] - #inline - pub fn #is_variant(&self) -> bool { - *self == #value_read_ty::#pc + // 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) + } + }); + } + } } - if let Some(v) = def.as_ref() { - let pc = &v.pc; - let sc = &v.nksc; + 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() + }; - let is_variant = Ident::new( - &if sc.to_string().starts_with('_') { - format!("is{sc}") - } else { - format!("is_{sc}") - }, - span, - ); + // 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}"); - let doc = util::escape_special_chars(&util::respace(&v.doc)); - enum_items.extend(quote! { - #[doc = #doc] - #inline - pub fn #is_variant(&self) -> bool { - matches!(self.variant(), #value_read_ty::#pc) - } + // 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("
"); } - } - } - - // 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 = ident(&base_field, &config.ident_formats.field_reader, span); - if !reader_derives.contains(&reader_ty) { - let base_path = base_syn_path(base, &fpath, &base_r)?; mod_items.extend(quote! { - #[doc = #field_reader_brief] - pub use #base_path as #reader_ty; + #[doc = #readerdoc] + pub type #reader_ty = #reader; }); - reader_derives.insert(reader_ty.clone()); } - // 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) { - let base_path = base_syn_path(base, &fpath, &value_read_ty)?; + 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 = #description] - pub use #base_path as #value_read_ty; + #[doc = #field_reader_brief] + pub use #base_path as #reader_ty; }); - enum_derives.insert(value_read_ty.clone()); + reader_derives.insert(reader_ty.clone()); } } } + // 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); + let doc = description.expand_dim(&brief_suffix); let first_name = svd::array::names(f, de).next().unwrap(); - let note = format!("NOTE: `n` is number of field in register. `n == 0` corresponds to `{first_name}` field"); + 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); @@ -944,7 +1056,7 @@ pub fn fields( } else { value }; - let name_snake_case_n = fi.name.to_snake_case_ident(Span::call_site()); + 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, @@ -959,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] @@ -982,143 +1108,172 @@ 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(rmwv).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 fmt = if writer_reader_different_enum { - &config.ident_formats.enum_write_name - } else { - &config.ident_formats.enum_name - }; - ident(evs.name.as_deref().unwrap_or(&name), fmt, 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(&name, &config.ident_formats.field_writer, span); - - let mut proxy_items = TokenStream::new(); - let mut unsafety = unsafety(f.write_constraint.as_ref(), width); + // 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 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 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 { - } else if let Some(def) = def.take() { - variants.push(def); - 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() + }; - // if the write structure is finite, it can be safely written. - if variants.len() == 1 << width { - unsafety = false; - } + let mwv = f.modified_write_values.or(rmwv).unwrap_or_default(); + + // gets a brief of write proxy + let field_writer_brief = format!("Field `{name}{brief_suffix}` writer - {description}"); - // 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); - - // 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, - config, + // 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 { - add_from_variants( - mod_items, - variants.iter(), - &value_write_ty, - &fty, - &description, - rv, - config, - ); - } - } - - // 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_special_chars(&util::respace(&v.doc)); - proxy_items.extend(quote! { - #[doc = #doc] - #inline - pub fn #sc(self) -> &'a mut crate::W { - self.variant(#value_write_ty::#pc) + 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 = matches!( - lookup_filter(&lookup_results, Usage::Write), - Some((_, None)) | None - ); - - // 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, - ); - if value_write_ty == "bool" { - quote! { crate::#wproxy<'a, REG> } - } else { - quote! { crate::#wproxy<'a, REG, #value_write_ty> } - } - } else { - let wproxy = Ident::new( - if unsafety { - "FieldWriter" - } else { - "FieldWriterSafe" - }, - span, - ); - let width = &unsuffixed(width); - if value_write_ty == "u8" { - quote! { crate::#wproxy<'a, REG, #width> } - } else { - quote! { crate::#wproxy<'a, REG, #width, #value_write_ty> } + 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()); } - }; - mod_items.extend(quote! { - #[doc = #field_writer_brief] - pub type #writer_ty<'a, REG> = #proxy; - }); + } } // generate proxy items from collected information @@ -1145,53 +1300,19 @@ pub fn fields( }); } - if let Some((evs, Some(base))) = lookup_filter(&lookup_results, Usage::Write) { - // 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 = ident(&base_field, &config.ident_formats.field_writer, span); - if !writer_derives.contains(&writer_ty) { - let base_path = base_syn_path(base, &fpath, &base_w)?; - mod_items.extend(quote! { - #[doc = #field_writer_brief] - pub use #base_path as #writer_ty; - }); - writer_derives.insert(writer_ty.clone()); - } - // 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) { - let base_path = base_syn_path(base, &fpath, &value_write_ty)?; - mod_items.extend(quote! { - #[doc = #description] - pub use #base_path as #value_write_ty; - }); - writer_enum_derives.insert(value_write_ty.clone()); - } - } - } - } - + // Generate field writer accessors if let Field::Array(f, de) = &f { let increment = de.dim_increment; let offset_calc = calculate_offset(increment, offset, false); - let doc = &util::replace_suffix(&description, &brief_suffix); + let doc = &description.expand_dim(&brief_suffix); let first_name = svd::array::names(f, de).next().unwrap(); - let note = format!("NOTE: `n` is number of field in register. `n == 0` corresponds to `{first_name}` field"); + 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 fn #name_snake_case(&mut self, n: u8) -> #writer_ty<#regspec_ty> { #[allow(clippy::no_effect)] [(); #dim][n as usize]; @@ -1201,7 +1322,7 @@ pub fn fields( for fi in svd::field::expand(f, de) { let sub_offset = fi.bit_offset() as u64; - let name_snake_case_n = &fi.name.to_snake_case_ident(Span::call_site()); + 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, @@ -1212,7 +1333,6 @@ pub fn fields( w_impl_items.extend(quote! { #[doc = #doc] #inline - #[must_use] pub fn #name_snake_case_n(&mut self) -> #writer_ty<#regspec_ty> { #writer_ty::new(self, #sub_offset) } @@ -1224,13 +1344,13 @@ pub fn fields( w_impl_items.extend(quote! { #[doc = #doc] #inline - #[must_use] 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 => {} @@ -1252,29 +1372,56 @@ pub fn fields( )) } -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 +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum Safety { + Unsafe, + Range(WriteConstraintRange), + Safe, +} + +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, } @@ -1292,20 +1439,46 @@ impl Variant { .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 nksc = ev.name.to_sanitized_not_keyword_snake_case(); - let sc = util::sanitize_keyword(nksc.clone()); + 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.ident_formats.enum_value, span), - nksc: Ident::new(&nksc, span), - sc: Ident::new(&sc, span), + pc: ident(&ev.name, config, "enum_value", span), + is_sc, + sc, value, }) } @@ -1379,7 +1552,8 @@ fn add_from_variants<'a>( let mut vars = TokenStream::new(); for v in variants.map(|v| { - let desc = util::escape_special_chars(&util::respace(&format!("{}: {}", v.value, v.doc))); + let desc = util::respace(&format!("{}: {}", v.value, v.doc)); + let desc = util::escape_special_chars(&desc); let pcv = &v.pc; let pcval = &unsuffixed(v.value); quote! { @@ -1416,6 +1590,7 @@ fn add_from_variants<'a>( impl crate::FieldSpec for #pc { type Ux = #fty; } + impl crate::IsEnum for #pc {} }); } } @@ -1455,6 +1630,7 @@ fn base_syn_path( base: &EnumPath, fpath: &FieldPath, base_ident: &Ident, + config: &Config, ) -> Result { let span = Span::call_site(); let path = if base.register() == fpath.register() { @@ -1462,11 +1638,16 @@ fn base_syn_path( } 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(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); + let mut rmod_ = crate::util::register_path_to_ty(base.register(), config, span); rmod_.path.segments.push(path_segment(base_ident.clone())); rmod_ }; 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 251d7e8e..a1379ef6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 @@ -95,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 @@ -149,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 @@ -548,6 +569,42 @@ //! //! 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; @@ -557,7 +614,7 @@ pub mod config; pub mod generate; pub mod util; -pub use config::{Config, Target}; +pub use config::{Config, Settings, Target}; #[non_exhaustive] pub struct Generation { @@ -573,6 +630,8 @@ pub struct DeviceSpecific { use anyhow::{Context, Result}; +use crate::config::{IdentFormats, IdentFormatsTheme}; + #[derive(Debug, thiserror::Error)] pub enum SvdError { #[error("Cannot format crate")] @@ -585,10 +644,34 @@ pub enum SvdError { 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).map_err(SvdError::Render)?; + generate::device::render(&device, &config, &mut device_x).map_err(SvdError::Render)?; let mut lib_rs = String::new(); writeln!( @@ -605,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(), }) }; diff --git a/src/main.rs b/src/main.rs index c9a6f379..d3efbeba 100755 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,8 @@ #![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::io::Write; use std::process; @@ -18,6 +20,7 @@ use svd2rust::{ 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).exit_on_error(true).build()?) .append_parser( @@ -26,10 +29,46 @@ fn parse_configs(app: Command) -> Result { .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<()> { @@ -61,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") @@ -119,6 +165,34 @@ fn run() -> Result<()> { .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") @@ -171,14 +245,6 @@ fn run() -> Result<()> { .action(ArgAction::SetTrue) .help("Make advanced checks due to parsing SVD"), ) - // TODO: deprecate - .arg( - Arg::new("pascal_enum_values") - .long("pascal-enum-values") - .alias("pascal_enum_values") - .action(ArgAction::SetTrue) - .help("Use PascalCase in stead of CONSTANT_CASE for enumerated values"), - ) .arg( Arg::new("source_type") .long("source-type") @@ -199,6 +265,17 @@ fn run() -> Result<()> { .action(ArgAction::SetTrue) .help("Reexport interrupt macro from cortex-m-rt like crates"), ) + .arg( + 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") .long("log") @@ -245,13 +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.as_deref().unwrap_or(Path::new(".")); - if config.pascal_enum_values { - config.ident_formats.enum_value.case = Some(svd2rust::config::Case::Pascal); - } info!("Parsing device from SVD file"); let device = load_from(input, &config)?; @@ -264,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"); @@ -277,18 +366,23 @@ 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("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(); @@ -296,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 fd80df55..92e420c6 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,8 +1,10 @@ use std::borrow::Cow; pub use crate::config::{Case, IdentFormat}; -use crate::svd::{Access, Device, Field, RegisterInfo, RegisterProperties}; -use html_escape::encode_text_minimal; +use crate::{ + svd::{Access, Device, Field, RegisterInfo, RegisterProperties}, + Config, +}; use inflections::Inflect; use proc_macro2::{Ident, Span, TokenStream}; use quote::quote; @@ -21,6 +23,37 @@ pub const BITS_PER_BYTE: u32 = 8; /// that are not valid in Rust ident const BLACKLIST_CHARS: &[char] = &['(', ')', '[', ']', '/', ' ', '-']; +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 + } +} + impl Case { pub fn cow_to_case<'a>(&self, cow: Cow<'a, str>) -> Cow<'a, str> { match self { @@ -30,7 +63,7 @@ impl Case { }, Self::Pascal => match cow { Cow::Borrowed(s) if s.is_pascal_case() => cow, - _ => cow.to_pascal_case().into(), + _ => to_pascal_case(&cow).into(), }, Self::Snake => match cow { Cow::Borrowed(s) if s.is_snake_case() => cow, @@ -38,9 +71,9 @@ impl Case { }, } } + pub fn sanitize<'a>(&self, s: &'a str) -> Cow<'a, str> { let s = sanitize(s); - self.cow_to_case(s) } } @@ -53,88 +86,59 @@ fn sanitize(s: &str) -> Cow<'_, str> { } } -pub fn ident(name: &str, fmt: &IdentFormat, span: Span) -> Ident { - let name = match &fmt.case { - Some(Case::Constant) => name.to_sanitized_constant_case(), - Some(Case::Pascal) => name.to_sanitized_pascal_case(), - Some(Case::Snake) => name.to_sanitized_snake_case(), - _ => sanitize(name), - }; - Ident::new(&format!("{}{}{}", fmt.prefix, name, fmt.suffix), span) -} - -/// 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 = Case::Pascal.sanitize(self); - if s.as_bytes().first().unwrap_or(&0).is_ascii_digit() { - Cow::from(format!("_{}", s)) +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 { - s + format!("{}{}{}", self.prefix, name, self.suffix).into() } } - fn to_sanitized_constant_case(&self) -> Cow { - let s = Case::Constant.sanitize(self); - if s.as_bytes().first().unwrap_or(&0).is_ascii_digit() { + 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 = Case::Snake.sanitize(self); - if s.as_bytes().first().unwrap_or(&0).is_ascii_digit() { - format!("_{}", s).into() - } else 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 + "_" @@ -174,23 +178,20 @@ pub fn escape_brackets(s: &str) -> String { } /// Escape basic html tags and brackets -pub fn escape_special_chars(s: &str) -> String { - let html_escaped = encode_text_minimal(s); - escape_brackets(&html_escaped) -} - -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(), +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() } } @@ -254,6 +255,11 @@ 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: impl Into) -> LitInt { LitInt::new(&n.into().to_string(), Span::call_site()) @@ -281,28 +287,47 @@ pub fn zst_type() -> Type { }) } -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 } @@ -342,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 { @@ -360,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" )) } }, @@ -378,15 +397,16 @@ 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 @@ -406,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"); @@ -413,6 +435,24 @@ 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; } @@ -436,32 +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| n.to_sanitized_snake_case().into()), - ); + v.extend(svd_rs::array::names(info, dim).map(|n| feature_format.apply(&n).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/ci/svd2rust-regress/Cargo.toml b/svd2rust-regress/Cargo.toml similarity index 76% rename from ci/svd2rust-regress/Cargo.toml rename to svd2rust-regress/Cargo.toml index 10709734..9b3fc80e 100644 --- a/ci/svd2rust-regress/Cargo.toml +++ b/svd2rust-regress/Cargo.toml @@ -3,11 +3,12 @@ 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.11", features = ["blocking"] } +svd2rust = { path = "../" } +reqwest = { version = "0.12", features = ["blocking", "native-tls-vendored"] } rayon = "1.4" anyhow = "1" thiserror = "1" @@ -20,3 +21,4 @@ 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/ci/svd2rust-regress/tests.yml b/svd2rust-regress/all-tests.yml similarity index 99% rename from ci/svd2rust-regress/tests.yml rename to svd2rust-regress/all-tests.yml index a1dadb4a..06501c71 100644 --- a/ci/svd2rust-regress/tests.yml +++ b/svd2rust-regress/all-tests.yml @@ -1863,8 +1863,8 @@ svd_url: https://stm32-rs.github.io/stm32-rs/stm32f469.svd.patched - arch: cortex-m mfgr: STMicro - chip: STM32F7x3 - svd_url: https://stm32-rs.github.io/stm32-rs/stm32f7x3.svd.patched + chip: STM32F723 + svd_url: https://stm32-rs.github.io/stm32-rs/stm32f723.svd.patched - arch: cortex-m mfgr: STMicro chip: STM32G070 @@ -2102,7 +2102,7 @@ - arch: riscv mfgr: SiFive chip: E310x - svd_url: https://raw.githubusercontent.com/riscv-rust/e310x/master/e310x.svd + svd_url: https://raw.githubusercontent.com/riscv-rust/e310x/master/e310x/e310x.svd should_pass: false run_when: never - arch: msp430 diff --git a/ci/svd2rust-regress/src/ci.rs b/svd2rust-regress/src/ci.rs similarity index 91% rename from ci/svd2rust-regress/src/ci.rs rename to svd2rust-regress/src/ci.rs index e8c4d2b1..4e4494a0 100644 --- a/ci/svd2rust-regress/src/ci.rs +++ b/svd2rust-regress/src/ci.rs @@ -11,7 +11,7 @@ pub struct Ci { #[clap(env = "GITHUB_COMMENT")] pub comment: String, #[clap(env = "GITHUB_COMMENT_PR")] - pub comment_pr: String, + pub comment_pr: usize, } #[derive(serde::Serialize)] @@ -32,7 +32,7 @@ impl Ci { diffs.push(Diff { needs_semver_checks: command.contains("semver"), command: command.to_owned(), - pr: self.comment_pr.split('/').last().unwrap().parse()?, + pr: self.comment_pr, }); } let json = serde_json::to_string(&diffs)?; diff --git a/ci/svd2rust-regress/src/command.rs b/svd2rust-regress/src/command.rs similarity index 85% rename from ci/svd2rust-regress/src/command.rs rename to svd2rust-regress/src/command.rs index 2c873bb8..2786166c 100644 --- a/ci/svd2rust-regress/src/command.rs +++ b/svd2rust-regress/src/command.rs @@ -7,7 +7,8 @@ pub trait CommandExt { fn run(&mut self, hide: bool) -> Result<(), anyhow::Error>; #[track_caller] - fn get_output(&mut self, can_fail: bool) -> Result; + fn run_and_get_output(&mut self, can_fail: bool) + -> Result; #[track_caller] fn get_output_string(&mut self) -> Result; @@ -33,7 +34,10 @@ impl CommandExt for Command { } #[track_caller] - fn get_output(&mut self, can_fail: bool) -> Result { + 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()))?; @@ -51,7 +55,7 @@ impl CommandExt for Command { #[track_caller] fn get_output_string(&mut self) -> Result { - String::from_utf8(self.get_output(true)?.stdout).map_err(Into::into) + String::from_utf8(self.run_and_get_output(true)?.stdout).map_err(Into::into) } fn display(&self) -> String { diff --git a/ci/svd2rust-regress/src/diff.rs b/svd2rust-regress/src/diff.rs similarity index 92% rename from ci/svd2rust-regress/src/diff.rs rename to svd2rust-regress/src/diff.rs index 4d3d2a81..de4d0d31 100644 --- a/ci/svd2rust-regress/src/diff.rs +++ b/svd2rust-regress/src/diff.rs @@ -8,7 +8,7 @@ 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 + /// 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. /// @@ -16,6 +16,7 @@ pub struct Diffing { #[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, @@ -27,14 +28,12 @@ pub struct Diffing { #[clap(global = true, long)] pub form_split: bool, - #[clap(subcommand)] - pub sub: Option, - #[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, @@ -44,6 +43,7 @@ pub struct Diffing { /// Filter by architecture, case sensitive, may be combined with other filters #[clap( + global = true, short = 'a', long = "architecture", ignore_case = true, @@ -54,20 +54,24 @@ pub struct Diffing { #[clap(global = true, long)] pub diff_folder: Option, - #[clap(hide = true, env = "GITHUB_PR")] + /// 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", long)] + #[clap(env = "GIT_PAGER", global = true, long)] pub pager: Option, /// if set, will use pager directly instead of `git -c core.pager` - #[clap(long, short = 'P')] + #[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, } @@ -183,7 +187,7 @@ impl Diffing { let test = match (tests.len(), self.sub.as_ref(), self.url.as_ref()) { (1, _, None) => tests[0].clone(), - (_, Some(DiffingMode::Pr { .. }), None) => tests + (_, Some(DiffingMode::Pr { .. } | DiffingMode::Semver { .. }), None) => tests .iter() .find(|t| t.chip == "STM32F103") .map(|t| (*t).clone()) @@ -204,6 +208,9 @@ impl Diffing { .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, }, _ => { @@ -228,10 +235,10 @@ impl Diffing { ) => last_args.as_deref(), None => None, }); - let join = |opt1: Option<&str>, opt2: Option<&str>| -> Option { + let join = |opt1: Option<&str>, opt2: Option<&str>| -> Option> { match (opt1, opt2) { - (Some(str1), Some(str2)) => Some(format!("{} {}", str1, str2)), - (Some(str), None) | (None, Some(str)) => Some(str.to_owned()), + (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, } }; @@ -239,22 +246,22 @@ impl Diffing { .setup_case( &opts.output_dir.join("baseline"), &baseline_bin, - join(baseline_cmd, last_args).as_deref(), + &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).as_deref(), + &join(current_cmd, last_args), ) .with_context(|| "couldn't create base")?; Ok([baseline, current]) } - fn get_source_and_command<'s>(&'s self) -> [Option<(Source, Command)>; 2] { - let split = |s: &'s str| -> (Source, Command) { + 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())) @@ -264,7 +271,7 @@ impl Diffing { } else { (None, Some(s.trim())) } - }; + } let baseline = self.baseline.as_deref().map(split); diff --git a/ci/svd2rust-regress/src/github.rs b/svd2rust-regress/src/github.rs similarity index 98% rename from ci/svd2rust-regress/src/github.rs rename to svd2rust-regress/src/github.rs index 1a5b0ecb..5a9bbf2b 100644 --- a/ci/svd2rust-regress/src/github.rs +++ b/svd2rust-regress/src/github.rs @@ -93,7 +93,7 @@ fn find_executable(dir: &Path, begins: &str) -> Result, anyhow:: .path() .extension() .is_some_and(|s| s == std::env::consts::EXE_EXTENSION)) - && !entry.path().extension().is_some_and(|s| s == "gz") + && entry.path().extension().is_none_or(|s| s != "gz") { Ok(Some(entry.path())) } else { @@ -148,7 +148,7 @@ pub fn get_release_binary_artifact( Command::new("gzip") .arg("-d") .arg(output_dir.join(artifact)) - .get_output(false)?; + .run_and_get_output(false)?; } } _ => { diff --git a/ci/svd2rust-regress/src/main.rs b/svd2rust-regress/src/main.rs similarity index 70% rename from ci/svd2rust-regress/src/main.rs rename to svd2rust-regress/src/main.rs index a8c1d246..f1a4ae6a 100644 --- a/ci/svd2rust-regress/src/main.rs +++ b/svd2rust-regress/src/main.rs @@ -17,6 +17,7 @@ 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)] @@ -50,7 +51,7 @@ pub fn get_cargo_workspace() -> &'static std::path::Path { } #[derive(clap::Parser, Debug)] -pub struct TestOpts { +pub struct TestAll { /// Run a long test (it's very long) #[clap(short = 'l', long)] pub long_test: bool, @@ -59,22 +60,22 @@ pub struct TestOpts { #[clap(short = 'c', long)] pub chip: Vec, - /// Filter by manufacturer, case sensitive, may be combined with other filters + /// Filter by manufacturer, may be combined with other filters #[clap( - short = 'm', - long = "manufacturer", - ignore_case = true, - value_parser = manufacturers(), -)] + short = 'm', + long = "manufacturer", + ignore_case = true, + value_parser = manufacturers(), + )] pub mfgr: Option, - /// Filter by architecture, case sensitive, may be combined with other filters + /// Filter by architecture, may be combined with other filters #[clap( - short = 'a', - long = "architecture", - ignore_case = true, - value_parser = architectures(), -)] + 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) @@ -89,6 +90,18 @@ pub struct TestOpts { /// 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, @@ -99,12 +112,122 @@ pub struct TestOpts { #[clap(short = 'p', long = "svd2rust-path", default_value = default_svd2rust())] pub current_bin_path: PathBuf, #[clap(last = true)] - pub command: Option, + pub passthrough_opts: Option>, // TODO: Specify smaller subset of tests? Maybe with tags? // TODO: Compile svd2rust? } -impl TestOpts { +#[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() @@ -148,11 +271,21 @@ impl TestOpts { "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) { + 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(); @@ -206,6 +339,8 @@ impl TestOpts { } } }); + drop(toml_guard); + if any_fails.load(Ordering::Acquire) { exit(1); } else { @@ -217,7 +352,8 @@ impl TestOpts { #[derive(clap::Subcommand, Debug)] pub enum Subcommand { Diff(Diffing), - Tests(TestOpts), + Tests(TestAll), + Test(Test), Ci(Ci), } @@ -256,7 +392,8 @@ pub struct Opts { impl Opts { const fn use_rustfmt(&self) -> bool { match self.subcommand { - Subcommand::Tests(TestOpts { format, .. }) + Subcommand::Tests(TestAll { format, .. }) + | Subcommand::Test(Test { format, .. }) | Subcommand::Diff(Diffing { format, .. }) | Subcommand::Ci(Ci { format, .. }) => format, } @@ -264,7 +401,8 @@ impl Opts { const fn use_form(&self) -> bool { match self.subcommand { - Subcommand::Tests(TestOpts { form_lib, .. }) + Subcommand::Tests(TestAll { form_lib, .. }) + | Subcommand::Test(Test { form_lib, .. }) | Subcommand::Diff(Diffing { form_split: form_lib, .. @@ -274,17 +412,14 @@ impl Opts { } } -/// Hack to use ci/tests.yml as default value when running as `cargo run` +/// 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()), - |mut e| { - e.extend([std::ffi::OsStr::new("/tests.yml")]); - std::path::PathBuf::from(e) - .strip_prefix(std::env::current_dir().unwrap()) - .unwrap() - .to_owned() - .into_os_string() + |path| { + let path = std::path::PathBuf::from(path); + let path = path.join("tests.yml"); + path.to_owned().into_os_string() }, ) } @@ -414,6 +549,13 @@ fn main() -> Result<(), anyhow::Error> { } 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") + } } } diff --git a/ci/svd2rust-regress/src/svd_test.rs b/svd2rust-regress/src/svd_test.rs similarity index 54% rename from ci/svd2rust-regress/src/svd_test.rs rename to svd2rust-regress/src/svd_test.rs index 7f0f290f..534202c8 100644 --- a/ci/svd2rust-regress/src/svd_test.rs +++ b/svd2rust-regress/src/svd_test.rs @@ -1,7 +1,7 @@ use anyhow::{anyhow, Context, Result}; -use svd2rust::{util::ToSanitizedCase, Target}; +use svd2rust::{util::Case, Target}; -use crate::{command::CommandExt, tests::TestCase, Opts, TestOpts}; +use crate::{command::CommandExt, tests::TestCase, Opts}; use std::io::prelude::*; use std::path::PathBuf; use std::process::Command; @@ -11,17 +11,21 @@ use std::{ path::Path, }; -const CRATES_ALL: &[&str] = &["critical-section = \"1.0\"", "vcell = \"0.1.2\""]; +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 = \"0.3.16\", default-features = false }"]; -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\""]; + &["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_XTENSALX: &[&str] = &["default = [\"xtensa-lx/esp32\", \"xtensa-lx-rt/esp32\"]"]; +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 @@ -67,7 +71,7 @@ impl std::fmt::Debug for ProcessFailed { } trait CommandHelper { - fn capture_outputs( + fn run_and_capture_outputs( &mut self, cant_fail: bool, name: &str, @@ -75,11 +79,27 @@ trait CommandHelper { 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 capture_outputs( + fn run_and_capture_outputs( &mut self, cant_fail: bool, name: &str, @@ -87,7 +107,7 @@ impl CommandHelper for Command { stderr: Option<&PathBuf>, previous_processes_stderr: &[PathBuf], ) -> Result<(), TestError> { - let output = self.get_output(true)?; + 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)?; @@ -133,33 +153,89 @@ impl CommandHelper for Command { } impl TestCase { - #[tracing::instrument(skip(self, opts, test_opts), fields(name = %self.name()))] + #[tracing::instrument(skip(self, opts), fields(name = %self.name(), svd=%self.svd_url()))] pub fn test( &self, opts: &Opts, - test_opts: &TestOpts, + 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, - &test_opts.current_bin_path, - test_opts.command.as_deref(), - ) + .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 - let cargo_check_err_file = path_helper_base(&chip_dir, &["cargo-check.err.log"]); - Command::new("cargo") - .arg("check") - .current_dir(&chip_dir) - .capture_outputs( - true, - "cargo check", - None, - Some(&cargo_check_err_file), - &process_stderr_paths, - ) - .with_context(|| "failed to check")?; - process_stderr_paths.push(cargo_check_err_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 { @@ -167,19 +243,21 @@ impl TestCase { }) } - #[tracing::instrument(skip(self, output_dir, command), fields(name = %self.name(), chip_dir = tracing::field::Empty))] + #[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, - command: Option<&str>, + passthrough_opts: &Option>, ) -> Result<(PathBuf, Vec), TestError> { let user = match std::env::var("USER") { Ok(val) => val, Err(_) => "rusttester".into(), }; - let chip_dir = output_dir.join(self.name().to_sanitized_snake_case().as_ref()); + 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) { @@ -194,93 +272,35 @@ impl TestCase { 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(self.name().to_sanitized_snake_case().as_ref()) + .arg(chip_name) .arg("--vcs") .arg("none") + .arg("--lib") .arg(&chip_dir) - .capture_outputs(true, "cargo init", None, None, &[]) + .run_and_capture_outputs(true, "cargo init", None, None, &[]) .with_context(|| "Failed to cargo init")?; - let svd_toml = path_helper_base(&chip_dir, &["Cargo.toml"]); - let mut file = OpenOptions::new() - .write(true) - .append(true) - .open(svd_toml) - .with_context(|| "Failed to open Cargo.toml for appending")?; - let crates = 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 => CRATES_XTENSALX.iter(), - Target::None => unreachable!(), - }) - .chain(if command.unwrap_or_default().contains("--atomics") { - CRATES_ATOMICS.iter() - } else { - [].iter() - }) - .chain(PROFILE_ALL.iter()) - .chain(FEATURES_ALL.iter()) - .chain(match &self.arch { - Target::XtensaLX => FEATURES_XTENSALX.iter(), - _ => [].iter(), - }); - for c in crates { - writeln!(file, "{}", c).with_context(|| "Failed to append to file!")?; - } - 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)?; + 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"]); - 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!(), - }; - tracing::info!("Running svd2rust"); - let mut svd2rust_bin = Command::new(svd2rust_bin_path); - if let Some(command) = command { - if !command.is_empty() { - svd2rust_bin.arg(command); - } - } - svd2rust_bin - .args(["-i", &chip_svd]) - .args(["--target", target]) - .current_dir(&chip_dir) - .capture_outputs( - true, - "svd2rust", - Some(&lib_rs_file).filter(|_| { - !matches!( - self.arch, - Target::CortexM | Target::Msp430 | Target::XtensaLX - ) - }), - Some(&svd2rust_err_file), - &[], - )?; + 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 => { @@ -296,6 +316,7 @@ impl TestCase { .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()) @@ -314,7 +335,7 @@ impl TestCase { .arg(&new_lib_rs_file) .arg("--outdir") .arg(&src_dir) - .capture_outputs( + .run_and_capture_outputs( true, "form", None, @@ -343,7 +364,7 @@ impl TestCase { Command::new(rustfmt_bin_path) .arg(entry) .args(["--edition", "2021"]) - .capture_outputs( + .run_and_capture_outputs( false, "rustfmt", None, @@ -355,9 +376,134 @@ impl TestCase { process_stderr_paths.push(rustfmt_err_file); } - tracing::info!("Done processing"); 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<()> { @@ -374,3 +520,33 @@ fn visit_dirs(dir: &Path, cb: &mut dyn FnMut(&fs::DirEntry)) -> std::io::Result< } 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/ci/svd2rust-regress/src/tests.rs b/svd2rust-regress/src/tests.rs similarity index 84% rename from ci/svd2rust-regress/src/tests.rs rename to svd2rust-regress/src/tests.rs index 040b4c78..f6c2f73d 100644 --- a/ci/svd2rust-regress/src/tests.rs +++ b/svd2rust-regress/src/tests.rs @@ -22,7 +22,10 @@ pub enum Manufacturer { Toshiba, SiFive, TexasInstruments, + Vorago, Espressif, + RaspberryPi, + Renesas, Unknown, } @@ -40,9 +43,12 @@ impl Manufacturer { NXP, SiliconLabs, Spansion, + Vorago, STMicro, Toshiba, SiFive, + RaspberryPi, + Renesas, TexasInstruments, Espressif, ] @@ -66,16 +72,22 @@ pub enum RunWhen { Never, } -#[derive(serde::Serialize, serde::Deserialize, Clone)] +#[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, } @@ -103,7 +115,12 @@ impl TestCase { } pub fn name(&self) -> String { - format!("{:?}-{}", self.mfgr, self.chip.replace('.', "_")) + 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 } } 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