aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelipe Balbi <[email protected]>2025-11-07 11:00:15 -0800
committerGitHub <[email protected]>2025-11-07 11:00:15 -0800
commit5632acec18cc5906b1625a8facf530db56c73300 (patch)
treeabf2897f4b2f9814069c64611896be2d42cf2ce8
parent47e383545f4aac3bfaec0563429cc721540e665a (diff)
parent9590d94ee9ba016f65a13100c429fc56ffe58e40 (diff)
Merge pull request #1 from bogdan-petru/import/mcxa276-initial
feat(mcxa276): initial HAL import
-rw-r--r--.cargo/config.toml19
-rw-r--r--.github/DOCS.md23
-rw-r--r--.github/codecov.yml21
-rw-r--r--.github/dependabot.yml19
-rw-r--r--.github/workflows/check.yml106
-rw-r--r--.github/workflows/nostd.yml17
-rw-r--r--.github/workflows/rolling.yml68
-rw-r--r--.gitignore28
-rw-r--r--.vscode/settings.json14
-rw-r--r--CODEOWNERS1
-rw-r--r--CODE_OF_CONDUCT.md2
-rw-r--r--CONTRIBUTING.md27
-rw-r--r--Cargo.toml115
-rw-r--r--Embed.toml28
-rw-r--r--LICENSE5
-rw-r--r--README.md387
-rw-r--r--SECURITY.md2
-rw-r--r--SW-Content-Register.txt78
-rw-r--r--build.rs20
-rw-r--r--defmt.x6
-rw-r--r--deny.toml8
-rw-r--r--examples/adc_interrupt.rs89
-rw-r--r--examples/adc_polling.rs79
-rw-r--r--examples/blink.rs84
-rw-r--r--examples/common/mod.rs45
-rw-r--r--examples/hello.rs114
-rw-r--r--examples/lpuart_buffered.rs77
-rw-r--r--examples/lpuart_polling.rs53
-rw-r--r--examples/ostimer_alarm.rs111
-rw-r--r--examples/ostimer_async.rs57
-rw-r--r--examples/ostimer_counter.rs128
-rw-r--r--examples/ostimer_race_test.rs396
-rw-r--r--examples/rtc_alarm.rs87
-rw-r--r--examples/uart_interrupt.rs69
-rw-r--r--memory.x10
-rw-r--r--ram.ld109
-rw-r--r--run.sh93
-rw-r--r--rust-toolchain.toml2
-rw-r--r--rustfmt.toml2
-rw-r--r--src/adc.rs388
-rw-r--r--src/baremetal/mod.rs6
-rw-r--r--src/board.rs14
-rw-r--r--src/clocks.rs134
-rw-r--r--src/config.rs20
-rw-r--r--src/gpio.rs244
-rw-r--r--src/interrupt.rs349
-rw-r--r--src/lib.rs142
-rw-r--r--src/lpuart/buffered.rs675
-rw-r--r--src/lpuart/mod.rs1201
-rw-r--r--src/main.rs18
-rw-r--r--src/ostimer.rs678
-rw-r--r--src/pins.rs123
-rw-r--r--src/reset.rs112
-rw-r--r--src/rtc.rs284
-rw-r--r--src/uart.rs306
-rw-r--r--supply-chain/README.md230
-rw-r--r--supply-chain/audits.toml126
-rw-r--r--supply-chain/config.toml184
-rw-r--r--supply-chain/imports.lock468
-rw-r--r--tools/run_and_attach_rtt.sh24
-rw-r--r--tools/run_jlink_noblock.sh76
61 files changed, 8157 insertions, 244 deletions
diff --git a/.cargo/config.toml b/.cargo/config.toml
new file mode 100644
index 000000000..3286be01c
--- /dev/null
+++ b/.cargo/config.toml
@@ -0,0 +1,19 @@
1[build]
2target = "thumbv8m.main-none-eabihf"
3
4[target.thumbv8m.main-none-eabihf]
5runner = "./run.sh"
6# Use our custom ram.ld for RAM-execution
7rustflags = ["-C", "link-arg=-Tdefmt.x", "-C", "link-arg=-Tram.ld"]
8
9[alias]
10# Build examples with defmt+RTT and LPUART2 enabled
11build-defmt = ["build", "--features", "defmt defmt-rtt lpuart2", "--examples"]
12# Blocking run (uses run.sh). Good for one-terminal flash+run.
13run-defmt = ["run", "--features", "defmt defmt-rtt lpuart2", "--example", "hello"]
14# Non-blocking flash to RAM via J-Link; frees probe for RTT attach
15flash-nb = ["!./tools/run_jlink_noblock.sh", "target/thumbv8m.main-none-eabihf/debug/examples/hello", "1366:0101:000600110607", "MCXA276", "500"]
16# Attach-only viewer that decodes defmt over RTT using J-Link
17# Requires PROBE_RS_PROBE exported (or it will prompt)
18defmt-attach = ["embed", "--features", "defmt defmt-rtt lpuart2", "--example", "hello"]
19
diff --git a/.github/DOCS.md b/.github/DOCS.md
new file mode 100644
index 000000000..e932784c7
--- /dev/null
+++ b/.github/DOCS.md
@@ -0,0 +1,23 @@
1# Github config and workflows
2
3In this folder there is configuration for codecoverage, dependabot, and ci
4workflows that check the library more deeply than the default configurations.
5
6This folder can be or was merged using a --allow-unrelated-histories merge
7strategy from <https://github.com/jonhoo/rust-ci-conf/> which provides a
8reasonably sensible base for writing your own ci on. By using this strategy
9the history of the CI repo is included in your repo, and future updates to
10the CI can be merged later.
11
12To perform this merge run:
13
14```shell
15git remote add ci https://github.com/jonhoo/rust-ci-conf.git
16git fetch ci
17git merge --allow-unrelated-histories ci/main
18```
19
20An overview of the files in this project is available at:
21<https://www.youtube.com/watch?v=xUH-4y92jPg&t=491s>, which contains some
22rationale for decisions and runs through an example of solving minimal version
23and OpenSSL issues.
diff --git a/.github/codecov.yml b/.github/codecov.yml
new file mode 100644
index 000000000..cd5ce8fc1
--- /dev/null
+++ b/.github/codecov.yml
@@ -0,0 +1,21 @@
1# ref: https://docs.codecov.com/docs/codecovyml-reference
2coverage:
3 # Hold ourselves to a high bar
4 range: 85..100
5 round: down
6 precision: 1
7 status:
8 # ref: https://docs.codecov.com/docs/commit-status
9 project:
10 default:
11 # Avoid false negatives
12 threshold: 1%
13
14# Test files aren't important for coverage
15ignore:
16 - "tests"
17
18# Make comments less noisy
19comment:
20 layout: "files"
21 require_changes: true
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 000000000..d0f091e7b
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,19 @@
1version: 2
2updates:
3 - package-ecosystem: github-actions
4 directory: /
5 schedule:
6 interval: daily
7 - package-ecosystem: cargo
8 directory: /
9 schedule:
10 interval: daily
11 ignore:
12 - dependency-name: "*"
13 # patch and minor updates don't matter for libraries as consumers of this library build
14 # with their own lockfile, rather than the version specified in this library's lockfile
15 # remove this ignore rule if your package has binaries to ensure that the binaries are
16 # built with the exact set of dependencies and those are up to date.
17 update-types:
18 - "version-update:semver-patch"
19 - "version-update:semver-minor"
diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml
index 9bf402d61..1a09a1492 100644
--- a/.github/workflows/check.yml
+++ b/.github/workflows/check.yml
@@ -1,6 +1,5 @@
1# This workflow runs whenever a PR is opened or updated, or a commit is pushed to main. It runs 1# This workflow runs whenever a PR is opened or updated, or a commit is pushed to main. It runs
2# several checks: 2# several checks:
3# - commit_list: produces a list of commits to be checked
4# - fmt: checks that the code is formatted according to rustfmt 3# - fmt: checks that the code is formatted according to rustfmt
5# - clippy: checks that the code does not contain any clippy warnings 4# - clippy: checks that the code does not contain any clippy warnings
6# - doc: checks that the code can be documented without errors 5# - doc: checks that the code can be documented without errors
@@ -8,66 +7,84 @@
8# - msrv: check that the msrv specified in the crate is correct 7# - msrv: check that the msrv specified in the crate is correct
9permissions: 8permissions:
10 contents: read 9 contents: read
10
11# This configuration allows maintainers of this repo to create a branch and pull request based on 11# This configuration allows maintainers of this repo to create a branch and pull request based on
12# the new branch. Restricting the push trigger to the main branch ensures that the PR only gets 12# the new branch. Restricting the push trigger to the main branch ensures that the PR only gets
13# built once. 13# built once.
14on: 14on:
15
15 push: 16 push:
16 branches: [main] 17 branches: [main, main-nextgen]
17 pull_request: 18 pull_request:
19
18# If new code is pushed to a PR branch, then cancel in progress workflows for that PR. Ensures that 20# If new code is pushed to a PR branch, then cancel in progress workflows for that PR. Ensures that
19# we don't waste CI time, and returns results quicker https://github.com/jonhoo/rust-ci-conf/pull/5 21# we don't waste CI time, and returns results quicker https://github.com/jonhoo/rust-ci-conf/pull/5
20concurrency: 22concurrency:
21 group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 23 group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
22 cancel-in-progress: true 24 cancel-in-progress: true
25
23name: check 26name: check
27
24jobs: 28jobs:
29
25 fmt: 30 fmt:
26 runs-on: ubuntu-latest 31 runs-on: ubuntu-latest
27 name: stable / fmt 32 name: nightly / fmt
33
34 strategy:
35 fail-fast: false
36 matrix:
37 workdir: [ ".", "examples/rt633", "examples/rt685s-evk",]
38
28 steps: 39 steps:
29 - uses: actions/checkout@v4 40 - uses: actions/checkout@v4
30 with: 41 with:
31 submodules: true 42 submodules: true
32 - name: Install stable 43
33 uses: dtolnay/rust-toolchain@stable 44 - name: Install nightly
45 uses: dtolnay/rust-toolchain@nightly
34 with: 46 with:
35 components: rustfmt 47 components: rustfmt
48
36 - name: cargo fmt --check 49 - name: cargo fmt --check
37 run: cargo fmt --check 50 run: cargo fmt --check
51 working-directory: ${{ matrix.workdir }}
38 52
39 clippy: 53 clippy-examples:
40 runs-on: ubuntu-latest 54 runs-on: ubuntu-latest
41 name: ${{ matrix.toolchain }} / clippy 55 name: ${{ matrix.toolchain }} / clippy
56
42 permissions: 57 permissions:
43 contents: read 58 contents: read
44 checks: write 59 checks: write
60
45 strategy: 61 strategy:
46 fail-fast: false 62 fail-fast: false
47 matrix: 63 matrix:
48 # Get early warning of new lints which are regularly introduced in beta channels. 64 # Get early warning of new lints which are regularly introduced in beta channels.
49 toolchain: [stable, beta] 65 toolchain: [stable]
66 workdir: ["examples"]
67
50 steps: 68 steps:
51 - uses: actions/checkout@v4 69 - uses: actions/checkout@v4
52 with: 70 with:
53 submodules: true 71 submodules: true
72
54 - name: Install ${{ matrix.toolchain }} 73 - name: Install ${{ matrix.toolchain }}
55 uses: dtolnay/rust-toolchain@master 74 uses: dtolnay/rust-toolchain@master
56 with: 75 with:
57 toolchain: ${{ matrix.toolchain }} 76 toolchain: ${{ matrix.toolchain }}
58 components: clippy 77 components: clippy
78
59 - name: cargo clippy 79 - name: cargo clippy
60 uses: giraffate/clippy-action@v1 80 working-directory: ${{ matrix.workdir }}
61 with: 81 run: |
62 reporter: 'github-pr-check' 82 cargo clippy --locked -- -Dwarnings -D clippy::suspicious -D clippy::correctness -D clippy::perf -D clippy::style
63 github_token: ${{ secrets.GITHUB_TOKEN }}
64 83
65 # Enable once we have a released crate 84 # Enable once we have a released crate
66 # semver: 85 # semver:
67 # runs-on: ubuntu-latest 86 # runs-on: ubuntu-latest
68 # name: semver 87 # name: semver
69 # strategy:
70 # fail-fast: false
71 # steps: 88 # steps:
72 # - uses: actions/checkout@v4 89 # - uses: actions/checkout@v4
73 # with: 90 # with:
@@ -85,14 +102,18 @@ jobs:
85 # API be documented as only available in some specific platforms. 102 # API be documented as only available in some specific platforms.
86 runs-on: ubuntu-latest 103 runs-on: ubuntu-latest
87 name: nightly / doc 104 name: nightly / doc
105
88 steps: 106 steps:
89 - uses: actions/checkout@v4 107 - uses: actions/checkout@v4
90 with: 108 with:
91 submodules: true 109 submodules: true
110
92 - name: Install nightly 111 - name: Install nightly
93 uses: dtolnay/rust-toolchain@nightly 112 uses: dtolnay/rust-toolchain@nightly
113
94 - name: cargo doc 114 - name: cargo doc
95 run: cargo doc --no-deps --all-features 115 run: |
116 cargo doc --no-deps --all-features --locked
96 env: 117 env:
97 RUSTDOCFLAGS: --cfg docsrs 118 RUSTDOCFLAGS: --cfg docsrs
98 119
@@ -101,16 +122,24 @@ jobs:
101 # which is required for feature unification 122 # which is required for feature unification
102 runs-on: ubuntu-latest 123 runs-on: ubuntu-latest
103 name: ubuntu / stable / features 124 name: ubuntu / stable / features
125
126 strategy:
127 fail-fast: false
128
104 steps: 129 steps:
105 - uses: actions/checkout@v4 130 - uses: actions/checkout@v4
106 with: 131 with:
107 submodules: true 132 submodules: true
133
108 - name: Install stable 134 - name: Install stable
109 uses: dtolnay/rust-toolchain@stable 135 uses: dtolnay/rust-toolchain@stable
110 - name: cargo install cargo-hack 136 with:
111 uses: taiki-e/install-action@cargo-hack 137 toolchain: stable
112 # intentionally no target specifier; see https://github.com/jonhoo/rust-ci-conf/pull/4 138 components: clippy
113 # --feature-powerset runs for every combination of features 139
140 - name: rustup target add thumbv8m.main-none-eabihf
141 run: rustup target add thumbv8m.main-none-eabihf
142
114 - name: cargo hack 143 - name: cargo hack
115 run: cargo hack --feature-powerset check 144 run: cargo hack --feature-powerset check
116 145
@@ -119,33 +148,22 @@ jobs:
119 # our dependencies. 148 # our dependencies.
120 runs-on: ubuntu-latest 149 runs-on: ubuntu-latest
121 name: ubuntu / stable / deny 150 name: ubuntu / stable / deny
151
122 steps: 152 steps:
123 - uses: actions/checkout@v4 153 - uses: actions/checkout@v4
124 with: 154 with:
125 submodules: true 155 submodules: true
156
126 - name: Install stable 157 - name: Install stable
127 uses: dtolnay/rust-toolchain@stable 158 uses: dtolnay/rust-toolchain@stable
159
128 - name: cargo install cargo-deny 160 - name: cargo install cargo-deny
129 uses: EmbarkStudios/cargo-deny-action@v2 161 uses: EmbarkStudios/cargo-deny-action@v2
130 with: 162 with:
131 log-level: warn 163 log-level: warn
132 manifest-path: ./Cargo.toml 164 manifest-path: ./Cargo.toml
133 command: check 165 command: check
134 arguments: --all-features 166 arguments: --all-features --locked
135
136 test:
137 runs-on: ubuntu-latest
138 name: ubuntu / stable / test
139 steps:
140 - uses: actions/checkout@v4
141 with:
142 submodules: true
143 - name: Install stable
144 uses: dtolnay/rust-toolchain@stable
145 - name: cargo install cargo-hack
146 uses: taiki-e/install-action@cargo-hack
147 - name: cargo test
148 run: cargo hack --feature-powerset test
149 167
150 msrv: 168 msrv:
151 # check that we can build using the minimal rust version that is specified by this crate 169 # check that we can build using the minimal rust version that is specified by this crate
@@ -155,15 +173,33 @@ jobs:
155 strategy: 173 strategy:
156 fail-fast: false 174 fail-fast: false
157 matrix: 175 matrix:
158 msrv: ["1.85"] 176 msrv: ["1.90"] # We're relying on namespaced-features, which
177 # was released in 1.60
178 #
179 # We also depend on `fixed' which requires rust
180 # 1.71
181 #
182 # Additionally, we depend on embedded-hal-async
183 # which requires 1.75
184 #
185 # embassy-time requires 1.79 due to
186 # collapse_debuginfo
187 #
188 # embassy upstream switched to rust 1.85
189 #
190 # unsigned_is_multiple_of requires 1.90, else we get clippy warnings
191
159 name: ubuntu / ${{ matrix.msrv }} 192 name: ubuntu / ${{ matrix.msrv }}
160 steps: 193 steps:
161 - uses: actions/checkout@v4 194 - uses: actions/checkout@v4
162 with: 195 with:
163 submodules: true 196 submodules: true
197
164 - name: Install ${{ matrix.msrv }} 198 - name: Install ${{ matrix.msrv }}
165 uses: dtolnay/rust-toolchain@master 199 uses: dtolnay/rust-toolchain@master
166 with: 200 with:
167 toolchain: ${{ matrix.msrv }} 201 toolchain: ${{ matrix.msrv }}
202
168 - name: cargo +${{ matrix.msrv }} check 203 - name: cargo +${{ matrix.msrv }} check
169 run: cargo check 204 run: |
205 cargo check --all-features --locked
diff --git a/.github/workflows/nostd.yml b/.github/workflows/nostd.yml
index 532235851..92460bd0f 100644
--- a/.github/workflows/nostd.yml
+++ b/.github/workflows/nostd.yml
@@ -3,28 +3,41 @@
3# information about how the concurrency cancellation and workflow triggering works 3# information about how the concurrency cancellation and workflow triggering works
4permissions: 4permissions:
5 contents: read 5 contents: read
6
6on: 7on:
7 push: 8 push:
8 branches: [main] 9 branches: [main, main-nextgen]
9 pull_request: 10 pull_request:
11
10concurrency: 12concurrency:
11 group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 13 group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
12 cancel-in-progress: true 14 cancel-in-progress: true
15
13name: no-std 16name: no-std
17
14jobs: 18jobs:
15 nostd: 19 nostd:
16 runs-on: ubuntu-latest 20 runs-on: ubuntu-latest
17 name: ${{ matrix.target }} 21 name: ${{ matrix.target }}
22
18 strategy: 23 strategy:
19 matrix: 24 matrix:
20 target: [thumbv8m.main-none-eabihf] 25 target: [thumbv8m.main-none-eabihf]
26
21 steps: 27 steps:
22 - uses: actions/checkout@v4 28 - uses: actions/checkout@v4
23 with: 29 with:
24 submodules: true 30 submodules: true
31
25 - name: Install stable 32 - name: Install stable
26 uses: dtolnay/rust-toolchain@stable 33 uses: dtolnay/rust-toolchain@stable
34
27 - name: rustup target add ${{ matrix.target }} 35 - name: rustup target add ${{ matrix.target }}
28 run: rustup target add ${{ matrix.target }} 36 run: rustup target add ${{ matrix.target }}
37
38 - name: Show variable
39 run: echo ${{ env.TOKEN }}
40
29 - name: cargo check 41 - name: cargo check
30 run: cargo check --target ${{ matrix.target }} 42 run: |
43 cargo check --target ${{ matrix.target }} --all-features --locked
diff --git a/.github/workflows/rolling.yml b/.github/workflows/rolling.yml
new file mode 100644
index 000000000..f572954f9
--- /dev/null
+++ b/.github/workflows/rolling.yml
@@ -0,0 +1,68 @@
1# This workflow runs every morning at midnight. It will run cargo hack
2# and a build with msrv. If any dependency breaks our crate, we will
3# know ASAP.
4#
5# - check: build with all features
6# - msrv: check that the msrv specified in the crate is correct
7permissions:
8 contents: read
9
10on:
11 schedule:
12 - cron: '0 0 * * *'
13
14name: rolling
15jobs:
16
17 check:
18 runs-on: ubuntu-latest
19 name: ubuntu / stable / features
20 strategy:
21 fail-fast: false
22 steps:
23 - uses: actions/checkout@v4
24 with:
25 submodules: true
26 - name: Install stable
27 uses: dtolnay/rust-toolchain@stable
28 - name: cargo install cargo-hack
29 uses: taiki-e/install-action@cargo-hack
30 - name: cargo check
31 run: |
32 cargo update
33 cargo check --all-features check
34
35 msrv:
36 runs-on: ubuntu-latest
37 strategy:
38 fail-fast: false
39 matrix:
40 msrv: ["1.85"] # We're relying on namespaced-features, which
41 # was released in 1.60
42 #
43 # We also depend on `fixed' which requires rust
44 # 1.71
45 #
46 # Additionally, we depend on embedded-hal-async
47 # which requires 1.75
48 #
49 # embassy-time requires 1.79 due to
50 # collapse_debuginfo
51 #
52 # embassy upstream switched to rust 1.83
53 #
54 # embedded-services (storage bus) dependency
55 # requires 1.85
56 name: ubuntu / ${{ matrix.msrv }} (${{ matrix.commit }})
57 steps:
58 - uses: actions/checkout@v4
59 with:
60 submodules: true
61 - name: Install ${{ matrix.msrv }}
62 uses: dtolnay/rust-toolchain@master
63 with:
64 toolchain: ${{ matrix.msrv }}
65 - name: cargo +${{ matrix.msrv }} check
66 run: |
67 cargo update
68 cargo check --all-features check
diff --git a/.gitignore b/.gitignore
index 6985cf1bd..a23881a76 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,14 +1,20 @@
1# Generated by Cargo 1# Rust
2# will have compiled files and executables 2/target/
3debug/
4target/
5
6# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
7# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
8Cargo.lock 3Cargo.lock
9 4
10# These are backup files generated by rustfmt 5# IDE
11**/*.rs.bk 6.vscode/
7.idea/
8
9# OS
10.DS_Store
11Thumbs.db
12
13# Embedded
14*.bin
15*.hex
16*.elf
17*.map
12 18
13# MSVC Windows builds of rustc generate these, which store debugging information 19# Debug
14*.pdb 20*.log
diff --git a/.vscode/settings.json b/.vscode/settings.json
deleted file mode 100644
index ace00d468..000000000
--- a/.vscode/settings.json
+++ /dev/null
@@ -1,14 +0,0 @@
1{
2 "editor.formatOnSave": true,
3 "rust-analyzer.checkOnSave": true,
4 "rust-analyzer.check.allTargets": false,
5 "rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf",
6 "rust-analyzer.cargo.features": "all",
7 "rust-analyzer.check.command": "clippy",
8 "[toml]": {
9 "editor.defaultFormatter": "tamasfe.even-better-toml"
10 },
11 "[rust]": {
12 "editor.defaultFormatter": "rust-lang.rust-analyzer"
13 }
14}
diff --git a/CODEOWNERS b/CODEOWNERS
deleted file mode 100644
index e2a52ae39..000000000
--- a/CODEOWNERS
+++ /dev/null
@@ -1 +0,0 @@
1* @felipebalbi @jerrysxie @tullom @RobertZ2011 @dymk
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 8603671fb..54a673e04 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -129,4 +129,4 @@ For answers to common questions about this code of conduct, see the FAQ at
129[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 129[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
130[Mozilla CoC]: https://github.com/mozilla/diversity 130[Mozilla CoC]: https://github.com/mozilla/diversity
131[FAQ]: https://www.contributor-covenant.org/faq 131[FAQ]: https://www.contributor-covenant.org/faq
132[translations]: https://www.contributor-covenant.org/translations \ No newline at end of file 132[translations]: https://www.contributor-covenant.org/translations
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index c45bc8e43..7c8289a58 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -14,22 +14,35 @@ you have the right to submit those contributions under those terms.
14If you wish to contribute code or documentation authored by others, or using the terms of any other license, please indicate that clearly in your 14If you wish to contribute code or documentation authored by others, or using the terms of any other license, please indicate that clearly in your
15pull request so that the project team can discuss the situation with you. 15pull request so that the project team can discuss the situation with you.
16 16
17## Commit Message 17# Contribution Guideline
18 18
19* For any new HAL driver added, please add corresponding test in the examples
20* Format the code with `cargo fmt`. Or better yet, enable format on save in your IDE for rust source files.
19* Use meaningful commit messages. See [this blogpost](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) 21* Use meaningful commit messages. See [this blogpost](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
20 22
21## PR Etiquette 23# PR Etiquette
22 24
23* Create a draft PR first 25* Create a draft PR first
24* Make sure that your branch has `.github` folder and all the code linting/sanity check workflows are passing in your draft PR before sending it out to code reviewers. 26* Make sure that your branch has `.github` folder and all the code linting/sanity check workflows are passing in your draft PR before sending it out to code reviewers.
25 27
26## Clean Commit History 28# Careful Use of `Unsafe`
27 29
28We disabled squashing of commit and would like to maintain a clean commit history. So please reorganize your commits with the following items: 30Working with embedded, using of `unsafe` is a necessity. However, please wrap unsafe code with safe interfaces to prevent `unsafe` keyword being sprinkled everywhere.
31
32# RFC Draft PR
33
34If you want feedback on your design or HAL driver early, please create a draft PR with title prefix `RFC:`.
35
36# Branch Naming Scheme
29 37
30* Each commit builds successfully without warning 38For now, we're not using forks. Eventually a personal fork will be required for any PRs to limit the amount of people with merge access to the main branch. Until that happens, please use meaningful branch names like this `user_alias/feature` and avoid sending PRs from branches containing prefixes such as "wip", "test", etc. Prior to sending a PR, please rename the branch.
31* Miscellaneous commits to fix typos + formatting are squashed 39
40# Clean Commit History
41
42We disabled squashing of commit and would like to maintain a clean commit history. So please reorganize your commits with the following items:
43 * Each commit builds successfully without warning from `rustc` or `clippy`
44 * Miscellaneous commits to fix typos + formatting are squashed
32 45
33## Regressions 46# Regressions
34 47
35When reporting a regression, please ensure that you use `git bisect` to find the first offending commit, as that will help us finding the culprit a lot faster. 48When reporting a regression, please ensure that you use `git bisect` to find the first offending commit, as that will help us finding the culprit a lot faster.
diff --git a/Cargo.toml b/Cargo.toml
index 71c4806fe..8ec547494 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,19 +1,110 @@
1[package] 1[package]
2name = "embedded-rust-template" 2name = "embassy-mcxa276"
3version = "0.1.0" 3version = "0.1.0"
4edition = "2021" 4edition = "2021"
5license = "MIT" 5license = "MIT OR Apache-2.0"
6repository = "https://github.com/OpenDevicePartnership/embedded-rust-template" 6description = "Embassy Hardware Abstraction Layer (HAL) for NXP MCXA276"
7rust-version = "1.85" 7keywords = ["embedded", "hal", "nxp", "mcxa276", "embassy"]
8categories = ["embedded", "hardware-support", "no-std"]
9
10[lib]
11name = "embassy_mcxa276"
8 12
9[dependencies] 13[dependencies]
10# dependencies for all targets 14cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
15cortex-m-rt = { version = "0.7", features = ["device"] }
16critical-section = "1.2.0"
17defmt = { version = "1.0", optional = true }
18embassy-embedded-hal = "0.5.0"
19embassy-executor = { version = "0.9.0", features = ["arch-cortex-m", "executor-interrupt", "executor-thread"], default-features = false }
20embassy-hal-internal = { version = "0.3.0", features = ["cortex-m", "prio-bits-3"] }
21embassy-sync = "0.7.2"
22embassy-time = "0.5.0"
23embassy-time-driver = "0.2.1"
24embedded-io = "0.6"
25heapless = "0.8"
26mcxa-pac = { git = "https://github.com/OpenDevicePartnership/mcxa-pac", features = ["rt"], rev = "f0281344c605ab24c38979553b41a1655c50625c", version = "0.1.0" }
27paste = "1.0.15"
28
29embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [
30 "unproven",
31] }
32embedded-hal = { package = "embedded-hal", version = "1.0" }
33embedded-hal-async = { version = "1.0" }
34embedded-hal-nb = { version = "1.0" }
35embedded-io-async = { version = "0.6.1" }
36nb = "1.1.0"
37
38[dev-dependencies]
39defmt-rtt = "1.0"
40panic-probe = { version = "1.0", features = ["print-defmt"] }
41
42[features]
43default = []
44
45# Base defmt feature enables core + panic handler
46# Use with one logger feature: defmt-rtt (preferred) or defmt-uart (fallback)
47defmt = ["dep:defmt"]
48
49rt = []
50
51unstable-pac = []
52
53[[example]]
54name = "hello"
55required-features = ["lpuart2"]
56
57[[example]]
58name = "blink"
59required-features = ["gpio", "ostimer0"]
60
61[[example]]
62name = "uart_interrupt"
63required-features = ["lpuart2", "ostimer0"]
64
65[[example]]
66name = "ostimer_alarm"
67required-features = ["lpuart2", "ostimer0"]
68
69[[example]]
70name = "ostimer_async"
71required-features = ["lpuart2", "ostimer0"]
72
73[[example]]
74name = "ostimer_counter"
75required-features = ["lpuart2", "ostimer0"]
76
77[[example]]
78name = "ostimer_race_test"
79required-features = ["lpuart2", "ostimer0"]
80
81[[example]]
82name = "lpuart_polling"
83required-features = ["lpuart2", "ostimer0"]
84
85[[example]]
86name = "lpuart_buffered"
87required-features = ["lpuart2", "ostimer0"]
88
89[[example]]
90name = "rtc_alarm"
91required-features = ["lpuart2", "rtc0"]
92
93[[example]]
94name = "adc_polling"
95required-features = ["adc1", "lpuart2"]
96
97[[example]]
98name = "adc_interrupt"
99required-features = ["adc1", "lpuart2"]
100
101[profile.release]
102debug = 2
103lto = true
104opt-level = "s"
105
106[profile.dev]
107debug = 2
108opt-level = 1
11 109
12[target.'cfg(target_os = "none")'.dependencies]
13# dependencies for no-std targets
14 110
15[lints.clippy]
16suspicious = "forbid"
17correctness = "forbid"
18perf = "forbid"
19style = "forbid"
diff --git a/Embed.toml b/Embed.toml
new file mode 100644
index 000000000..40218c8bf
--- /dev/null
+++ b/Embed.toml
@@ -0,0 +1,28 @@
1[default.probe]
2# Use the first available probe
3protocol = "Swd"
4speed = 1000
5
6[default.flashing]
7# Attach-only: don't flash (we already loaded to RAM)
8enabled = false
9
10[default.reset]
11# Don't reset; keep running app started by run_jlink_noblock.sh
12enabled = false
13
14[default.general]
15# The chip name of the target
16chip = "MCXA276"
17
18[default.gdb]
19# Whether or not a GDB server should be opened after loading
20enabled = false
21
22[default.rtt]
23# Enable RTT for debugging output
24enabled = true
25
26# Increase timeout for RTT CB discovery
27up_channels = [ { channel = 0, mode = "BlockIfFull" } ]
28timeout = 5000
diff --git a/LICENSE b/LICENSE
index 75092dc47..479657c89 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
1MIT License 1MIT License
2 2
3Copyright (c) 2025 Open Device Partnership 3Copyright (c) 2025 OEMWCSE
4 4
5Permission is hereby granted, free of charge, to any person obtaining a copy 5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the "Software"), to deal 6of this software and associated documentation files (the "Software"), to deal
@@ -18,4 +18,5 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21SOFTWARE. \ No newline at end of file 21SOFTWARE.
22
diff --git a/README.md b/README.md
index 60b09c0a6..8a93b5f4a 100644
--- a/README.md
+++ b/README.md
@@ -1,63 +1,362 @@
1# embedded-rust-template 1# Embassy MCXA276 HAL
2Template repository for Embedded Rust development
3 2
4## Customizing This Template 3A Hardware Abstraction Layer (HAL) for the NXP MCXA276 microcontroller
4using the Embassy async framework. This HAL provides safe, idiomatic
5Rust interfaces for GPIO, UART, and OSTIMER peripherals.
5 6
6### Changing the Target Architecture 7## Prerequisites
7 8
8This template is configured for `thumbv8m.main-none-eabihf`, by default, but you can modify it for other targets (i.e. `aarch64-unknown-none`): 9### Ubuntu/Debian Setup
9 10
101. **VSCode Settings**: Update the target in `.vscode/settings.json`: 11```bash
11 ```json 12# Install Rust toolchain
12 "rust-analyzer.cargo.target": "your-target-architecture" 13curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
13 ``` 14source ~/.cargo/env
14 15
16# Add target for MCXA276 (ARM Cortex-M33)
17rustup target add thumbv8m.main-none-eabihf
15 18
16This configuration ensures that: 19# Install required tools
17- Only the specified target architecture is analyzed, not the host platform 20sudo apt update
18- Code is checked against the no_std environment 21sudo apt install -y gdb-multiarch curl wget
19 22
20To temporarily analyze code for the host platform instead, you can remove the `rust-analyzer.cargo.target` setting. 23# Install probe-rs for running and debugging
24cargo install probe-rs --features cli
25```
26
27### Windows Setup
28
29- Install Rust via https://rustup.rs (default options are fine)
30- Add the MCXA276 target:
31 ```powershell
32 rustup target add thumbv8m.main-none-eabihf
33 ```
34- Install probe-rs CLI (we will use it directly; no GDB required):
35 ```powershell
36 cargo install probe-rs --features cli
37 ```
38- Install a serial terminal (e.g., Tera Term): https://ttssh2.osdn.jp/
39- USB drivers: Windows 10/11 usually picks up the board as a USB CDC device automatically (COM port)
40
41### Hardware Requirements
42
43- NXP FRDM-MCXA276 development board
44- Debug probe (CMSIS-DAP compatible)
45- USB cable for power and programming
46
47## Examples
48
49This HAL includes several examples demonstrating different peripherals:
50
51### GPIO Examples
52
53#### `blink`
54Blinks an LED connected to GPIO pin. Demonstrates basic GPIO output operations.
55
56### UART Examples
57
58#### `hello`
59Interactive UART2 demo: prints a banner and supports `help`, `echo <text>`, `hex <byte>`.
60
61### OSTIMER Examples
62
63#### `ostimer_alarm`
64
65Demonstrates setting and waiting for OSTIMER alarms.
66
67#### `ostimer_async`
68Shows asynchronous OSTIMER operations with Embassy's async runtime.
69
70#### `ostimer_counter`
71Demonstrates OSTIMER counter functionality.
72
73#### `ostimer_race_test`
74Advanced example testing OSTIMER race conditions and synchronization.
75
76### RTC Example
77
78#### `rtc_alarm`
79Demonstrates how to enable and use the RTC to generate an interrupt after 10seconds.
80
81## Build and Run
82
83### Using probe-rs
84
85All examples require specifying your debug probe. First, find your probe ID:
21 86
222. **GitHub Workflows**: Modify the target in two workflow files: 87```bash
23 - `.github/workflows/nostd.yml`: Update the targets in the matrix: 88probe-rs list
24 ```yaml 89```
25 matrix: 90
26 target: [your-target-architecture] 91Then run examples with your probe ID (replace `1fc9:0143:H3AYDQVQMTROB` with your actual probe):
27 ``` 92
28 - `.github/workflows/check.yml`: If there are any target-specific checks, update them accordingly. 93```bash
94# GPIO blink example
95PROBE=1fc9:0143:H3AYDQVQMTROB cargo run --features "gpio ostimer0" --example blink
96
97
98
99# UART hello example
100PROBE=1fc9:0143:H3AYDQVQMTROB cargo run --features "lpuart2 ostimer0" --example hello
101
102# OSTIMER examples
103PROBE=1fc9:0143:H3AYDQVQMTROB cargo run --features "lpuart2 ostimer0" --example ostimer_alarm
104PROBE=1fc9:0143:H3AYDQVQMTROB cargo run --features "lpuart2 ostimer0" --example ostimer_async
105PROBE=1fc9:0143:H3AYDQVQMTROB cargo run --features "lpuart2 ostimer0" --example ostimer_counter
106PROBE=1fc9:0143:H3AYDQVQMTROB cargo run --features "lpuart2 ostimer0" --example ostimer_race_test
107
108# RTC example
109PROBE=1fc9:0143:H3AYDQVQMTROB cargo run --features "lpuart2 rtc0" --example rtc_alarm
110```
111
112**Note:** All examples run from RAM, not flash memory. They are loaded directly into RAM for faster development iteration.
113
114**Important:** After pressing the RESET button on the board, the first `cargo run` attempt may fail with a connection error. This is expected - simply run the command again and it will work. The run.sh script now properly sets the Vector Table Offset Register (VTOR) to point to the RAM-based vector table, ensuring the correct stack pointer and reset vector are used.
29 115
303. **Cargo Configuration**: If needed, you can add target-specific configuration in a `.cargo/config.toml` file. 116```console
117smw016108@smw016108:~/Downloads/nxp/rust/uart/embassy-mcxa276$ PROBE=1fc9:0143:H3AYDQVQMTROB cargo run --release --features "gpio ostimer0" --example blink
118 Finished `release` profile [optimized + debuginfo] target(s) in 0.07s
119 Running `/home/smw016108/Downloads/nxp/rust/uart/embassy-mcxa276/./run.sh target/thumbv8m.main-none-eabihf/release/examples/blink`
120probe-rs gdb server failed to connect to target. Log:
121----- probe-rs gdb log -----
122 Error: Connecting to the chip was unsuccessful.
31 123
32### Converting from Binary to Library 124 Caused by:
125 0: An ARM specific error occurred.
126 1: Error using access port FullyQualifiedApAddress { dp: Default, ap: V1(0) }.
127 2: Failed to read register DRW at address 0xd0c
128 3: An error occurred in the communication with an access port or debug port.
129 4: Target device responded with a FAULT response to the request.
130smw016108@smw016108:~/Downloads/nxp/rust/uart/embassy-mcxa276$ PROBE=1fc9:0143:H3AYDQVQMTROB cargo run --release --features "gpio ostimer0" --example blink
131 Finished `release` profile [optimized + debuginfo] target(s) in 0.02s
132 Running `/home/smw016108/Downloads/nxp/rust/uart/embassy-mcxa276/./run.sh target/thumbv8m.main-none-eabihf/release/examples/blink`
133```
134
135### Additional UART Examples
136
137#### `uart_interrupt`
138Interrupt-driven UART2 echo. Type in the serial terminal; each byte is echoed back from the IRQ handler path.
33 139
34To convert this project from a binary to a library: 140#### `lpuart_polling`
141Blocking TX/RX echo over UART2 using the simple polling driver.
35 142
361. **Cargo.toml**: Update your project structure: 143#### `lpuart_buffered`
37 ```toml 144Async buffered driver with separate TX/RX tasks; echoes typed characters in chunks.
38 [lib]
39 name = "your_library_name"
40 ```
41 145
422. **Directory Structure**: 146Pins: UART2 TX=P2_2, RX=P2_3 (ALT3), 115200 8N1.
43 - For a library, ensure you have a `src/lib.rs` file instead of `src/main.rs`
44 - Move your code from `main.rs` to `lib.rs` and adjust as needed
45 147
463. **No-std Configuration**: If you're creating a no-std library, ensure you have: 148### ADC Examples
47 ```rust
48 // In lib.rs
49 #![cfg_attr(target_os = "none", no_std)]
50 // Add other attributes as needed
51 ```
52 149
53### Project Dependencies 150#### `adc_polling`
151Configures ADC1 channel A8 (pin P1_10) and prints conversion values to UART2 periodically.
54 152
55Update the dependencies in `Cargo.toml` based on your target platform: 153#### `adc_interrupt`
154Triggers a conversion and signals completion via ADC1 interrupt, printing a notification on UART2.
56 155
57```toml 156```console
58[dependencies] 1570x20002040 in ?? ()
59# Common dependencies for all targets 158Supported Commands:
60 159
61[target.'cfg(target_os = "none")'.dependencies] 160 info - print session information
62# Dependencies for no-std targets 161 reset - reset target
162 reset halt - reset target and halt afterwards
163
164Loading section .vector_table, size 0x224 lma 0x20000000
165Loading section .text, size 0x97e lma 0x20000224
166Loading section .Reset, size 0x58 lma 0x20000ba4
167Loading section .rodata, size 0x28 lma 0x20000bfc
168Start address 0x20000ba4, load size 3106
169Transfer rate: 13 KB/sec, 776 bytes/write.
63``` 170```
171
172then I see the LED blinking. I press CTRL+C to exit. It will show me ^C
173
174```console
175Program received signal SIGINT, Interrupt.
1760x20000880 in embassy_executor::arch::thread::Executor::run<blink::__cortex_m_rt_main::{closure_env#0}> (self=0x200027e8, init=...) at /home/smw016108/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/embassy-executor-0.9.1/src/arch/cortex_m.rs:106
177106 asm!("wfe");
178[Inferior 1 (process 1) detached]
179Program loaded and started (no reset)
180smw016108@smw016108:~/Downloads/nxp/rust/uart/embassy-mcxa276$ \
181
182Then I press RESET again and I want to run another example, like ostimer_alarm. I open the console using sudo picocom -b 115200 /dev/ttyACM0 and I start running the example:
183
184smw016108@smw016108:~/Downloads/nxp/rust/uart/embassy-mcxa276$ PROBE=1fc9:0143:H3AYDQVQMTROB cargo run --features "lpuart2 ostimer0" --example ostimer_alarm
185 Finished `dev` profile [optimized + debuginfo] target(s) in 0.02s
186 Running `/home/smw016108/Downloads/nxp/rust/uart/embassy-mcxa276/./run.sh target/thumbv8m.main-none-eabihf/debug/examples/ostimer_alarm`
187probe-rs gdb server failed to connect to target. Log:
188----- probe-rs gdb log -----
189 Error: Connecting to the chip was unsuccessful.
190
191 Caused by:
192 0: An ARM specific error occurred.
193 1: Error using access port FullyQualifiedApAddress { dp: Default, ap: V1(0) }.
194 2: Failed to read register DRW at address 0xd0c
195 3: An error occurred in the communication with an access port or debug port.
196 4: Target device responded with a FAULT response to the request.
197smw016108@smw016108:~/Downloads/nxp/rust/uart/embassy-mcxa276$ PROBE=1fc9:0143:H3AYDQVQMTROB cargo run --features "lpuart2 ostimer0" --example ostimer_alarm
198 Finished `dev` profile [optimized + debuginfo] target(s) in 0.02s
199 Running `/home/smw016108/Downloads/nxp/rust/uart/embassy-mcxa276/./run.sh target/thumbv8m.main-none-eabihf/debug/examples/ostimer_alarm`
2000x20002040 in core::panicking::panic_const::panic_const_mul_overflow () at library/core/src/panicking.rs:175
201warning: 175 library/core/src/panicking.rs: No such file or directory
202Supported Commands:
203
204 info - print session information
205 reset - reset target
206 reset halt - reset target and halt afterwards
207
208Loading section .vector_table, size 0x224 lma 0x20000000
209Loading section .text, size 0x2226 lma 0x20000224
210Loading section .Reset, size 0x58 lma 0x2000244c
211Loading section .rodata, size 0x6dc lma 0x200024a4
212Start address 0x2000244c, load size 11134
213Transfer rate: 16 KB/sec, 1855 bytes/write.
214```
215
216I can see in the console
217
218```console
219OSTIMER Alarm Example
220Scheduling alarm for 2 seconds...
221Alarm scheduled successfully
222Alarm expired! Callback executed.
223Scheduling another alarm for 3 seconds...
224Alarm scheduled. Waiting 1 second then canceling...
225Alarm canceled
226Alarm was successfully canceled
227Example complete
228```
229
230then I press CTRL+C to stop running
231
232```console
233^C
234Program received signal SIGINT, Interrupt.
2350x20000e64 in embassy_executor::arch::thread::Executor::run<ostimer_alarm::__cortex_m_rt_main::{closure_env#0}> (self=0x200027e8, init=...) at /home/smw016108/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/embassy-executor-0.9.1/src/arch/cortex_m.rs:106
236106 asm!("wfe");
237[Inferior 1 (process 1) detached]
238Program loaded and started (no reset)
239smw016108@smw016108:~/Downloads/nxp/rust/uart/embassy-mcxa276$
240```
241
242### Windows: Running examples (RAM, no RTT/defmt)
243
244Important: On Windows, do not use `cargo run` because `.cargo/config.toml` sets a Linux-only runner (`./run.sh`). Instead, use `probe-rs run` directly.
245
2461) Find your probe and COM port
247- List probes:
248
249 ```console
250 probe-rs list
251 ```
252- If multiple probes are attached, set the specific one (replace with your ID):
253
254 ```console
255 $env:PROBE_RS_PROBE = "1366:0101:000600110607"
256 ```
257
258- Check Windows Device Manager → Ports (COM & LPT) for the board’s COM port.
259
2602) Build the example
261
262```console
263cargo build --example hello --features "lpuart2"
264```
265
2663) Run from RAM with probe-rs
267
268```console
269probe-rs run --chip MCXA276 --protocol swd --speed 1000 target/thumbv8m.main-none-eabihf/debug/examples/hello
270```
271You will see a short probe-rs warning like "unknown variant, try to set watch point"; it’s harmless.
272
2734) View output in Tera Term
274- Open Tera Term, select the board’s COMx port, 115200 8N1
275- Expected behavior per example:
276 - hello: prints a banner; simple UART output
277 - lpuart_polling / lpuart_buffered / uart_interrupt: echo typed characters
278 - adc_polling: prints ADC values periodically (ADC1 channel A8 on P1_10)
279 - adc_interrupt: prints "*** ADC interrupt TRIGGERED! ***" upon conversion completion
280 - blink: LED on PIO3_18 blinks "SOS" pattern
281 - rtc_alarm: schedules, cancels and reports alarm events on UART
282
283Notes
284- All examples run from RAM (not flashed). Reset clears the program.
285- If the first attempt after a reset fails to connect, just run the command again.
286- UART2 pins: TX=P2_2, RX=P2_3 (ALT3), 115200 8N1.
287
288Quick commands for other examples:
289
290```console
291# Build
292cargo build --example blink --features "gpio ostimer0"
293cargo build --example lpuart_polling --features "lpuart2 ostimer0"
294cargo build --example lpuart_buffered --features "lpuart2 ostimer0"
295cargo build --example uart_interrupt --features "lpuart2 ostimer0"
296cargo build --example rtc_alarm --features "lpuart2 rtc0"
297cargo build --example adc_polling --features "adc1 lpuart2"
298cargo build --example adc_interrupt --features "adc1 lpuart2"
299
300# Run (RAM)
301probe-rs run --chip MCXA276 --protocol swd --speed 1000 target/thumbv8m.main-none-eabihf/debug/examples/blink
302probe-rs run --chip MCXA276 --protocol swd --speed 1000 target/thumbv8m.main-none-eabihf/debug/examples/lpuart_polling
303probe-rs run --chip MCXA276 --protocol swd --speed 1000 target/thumbv8m.main-none-eabihf/debug/examples/lpuart_buffered
304probe-rs run --chip MCXA276 --protocol swd --speed 1000 target/thumbv8m.main-none-eabihf/debug/examples/uart_interrupt
305probe-rs run --chip MCXA276 --protocol swd --speed 1000 target/thumbv8m.main-none-eabihf/debug/examples/rtc_alarm
306probe-rs run --chip MCXA276 --protocol swd --speed 1000 target/thumbv8m.main-none-eabihf/debug/examples/adc_polling
307probe-rs run --chip MCXA276 --protocol swd --speed 1000 target/thumbv8m.main-none-eabihf/debug/examples/adc_interrupt
308probe-rs run --chip MCXA276 --protocol swd --speed 1000 target/thumbv8m.main-none-eabihf/debug/examples/ostimer_alarm
309probe-rs run --chip MCXA276 --protocol swd --speed 1000 target/thumbv8m.main-none-eabihf/debug/examples/ostimer_async
310probe-rs run --chip MCXA276 --protocol swd --speed 1000 target/thumbv8m.main-none-eabihf/debug/examples/ostimer_counter
311probe-rs run --chip MCXA276 --protocol swd --speed 1000 target/thumbv8m.main-none-eabihf/debug/examples/ostimer_race_test
312```
313
314How I tested on Windows
315- Windows 11; Rust stable; probe-rs 0.29.x
316- Built each example as above; ran with `probe-rs run` (RAM execution)
317- Observed UART output in Tera Term at 115200 8N1; all examples behaved as expected
318- No RTT/defmt used; purely UART or LED observation
319
320### Build Only
321
322To build without running:
323
324```console
325cargo build --features "gpio ostimer0" --example blink
326cargo build --features "lpuart2 ostimer0" --example hello
327cargo build --features "lpuart2 ostimer0" --example ostimer_alarm
328cargo build --features "lpuart2 rtc0" --example rtc_alarm
329# etc.
330```
331
332## Development Notes
333
334### Critical Fix: MCXA276 Interrupt Vector Table
335
336
337Update (SVD 25.06.00, mcxa-pac a9dd33): No manual PAC edits are required anymore. OS_EVENT and WAKETIMER0 are present and the vector table is correct. The section below is kept for historical context.
338**Problem:** The OSTIMER examples crashed during interrupt handling with a hardfault (SP=0x00000000). Investigation revealed the OS_EVENT interrupt vector was NULL in the vector table, causing the CPU to jump to address 0 when OSTIMER interrupts fired.
339
340**Root Cause:** The `mcxa276-pac/src/lib.rs` file (generated from the SVD file) was missing the `WAKETIMER0` interrupt handler declaration. This caused the `__INTERRUPTS` array to have an off-by-one error, placing OS_EVENT at IRQ 58 instead of the correct IRQ 57 position.
341
342**Solution:** Manually edited `mcxa276-pac/src/lib.rs` to add the missing WAKETIMER0 interrupt:
343
3441. Added `fn WAKETIMER0()` to the `extern "C"` block
3452. Fixed the `__INTERRUPTS: [Vector; 122]` array sequence:
346 - Changed from: `LPTMR0, _reserved, _reserved, OS_EVENT, _reserved, UTICK0, ...`
347 - Changed to: `LPTMR0, _reserved, OS_EVENT, WAKETIMER0, UTICK0, WWDT0, _reserved, ADC0, ...`
3483. Added `WAKETIMER0 = 58` to the `Interrupt` enum
349
350**Verification:** Binary analysis confirmed OS_EVENT is now at the correct position:
351- IRQ 57 = word 73 = offset 0x124 in vector table
352- OS_EVENT handler: 0x20000BB1 (verified with `arm-none-eabi-objdump`)
353
354**Note:** This is likely an issue with the NXP MCXA276.svd file or svd2rust generation. The WAKETIMER0 peripheral exists in the PAC but the interrupt handler was missing. Future regeneration of the PAC from SVD may require reapplying this fix.
355
356### Warning: Avoid `#[inline(always)]` in Performance-Critical Code
357
358Using `#[inline(always)]` can cause the Rust compiler to generate incorrect assembly, leading to register corruption or unexpected behavior. For example, in tight polling loops like those in the OSTIMER driver, this attribute may result in invalid instructions that zero registers (e.g., `movs r1, r0` causing r1=0), triggering hardfaults.
359
360## License
361
362This project is licensed under MIT OR Apache-2.0.
diff --git a/SECURITY.md b/SECURITY.md
index 6ed358156..5357b8824 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -63,4 +63,4 @@ An embargo can be called for in various cases:
63If we determine that an issue you report requires an embargo, we will discuss 63If we determine that an issue you report requires an embargo, we will discuss
64this with you and try to find a reasonable expiry date (aka “embargo 64this with you and try to find a reasonable expiry date (aka “embargo
65completion date”), as well as who should be included in the list of 65completion date”), as well as who should be included in the list of
66need-to-know people. \ No newline at end of file 66need-to-know people.
diff --git a/SW-Content-Register.txt b/SW-Content-Register.txt
new file mode 100644
index 000000000..09f879c2f
--- /dev/null
+++ b/SW-Content-Register.txt
@@ -0,0 +1,78 @@
1Release Name: Embassy MCXA276 HAL
2Release Version: 0.1.0
3Package License: MIT (see ./License.txt)
4Note: The crate is dual-licensed “MIT OR Apache-2.0” per Cargo.toml; choosing MIT satisfies the dual-license terms.
5
6Scope of this Register
7This file documents software content that is part of this repository and, for transparency, lists major non-vendored Rust dependencies that are resolved from crates.io during builds. Components that are not present in the repository (e.g., external crates) are listed under “Non‑vendored Rust dependencies”.
8
9Repository Components (included in this release)
10
11embassy_mcxa276_hal Name: Embassy MCXA276 HAL (library + examples)
12 Version: 0.1.0
13 Outgoing License: MIT
14 License File: ./License.txt
15 Format: Rust source code, examples, scripts, linker scripts
16 Description: Hardware Abstraction Layer for NXP MCXA276 using the Embassy async framework. Implements drivers and helpers for:
17 - LPUART2 (UART), OSTIMER0, RTC0, ADC1, GPIO
18 - Embassy integration (executors, time)
19 Location: ./src, ./examples, ./build.rs, ./memory.x, ./ram.ld, ./defmt.x, ./.cargo/config.toml, ./run.sh, ./tools/
20 Origin: OEMWCSE (MIT)
21 URL: https://bitbucket.sw.nxp.com/scm/oemwcse/mcxa_rust.git
22
23mcxa276_pac Name: MCXA276 Peripheral Access Crate (PAC)
24 Version: 0.1.0
25 Outgoing License: MIT OR Apache-2.0
26 License File: (see crate metadata)
27 Format: Rust source code (auto-generated PAC)
28 Description: Auto-generated register mappings for MCXA276 peripherals (svd2rust). Includes device.x and interrupt vectors.
29 Location: External (git): https://github.com/bogdan-petru/mcxa-pac (pinned rev a9dd3301)
30 Origin: Generated by svdtools + svd2rust from NXP SVD 25.06.00
31 URL: N/A (generated from NXP SVD)
32
33examples Name: Example applications
34 Version: N/A
35 Outgoing License: MIT
36 License File: ./License.txt
37 Format: Rust source code (examples)
38 Description: Demonstrations for HAL peripherals and features:
39 - hello, blink
40 - lpuart_polling, lpuart_buffered, uart_interrupt
41 - rtc_alarm
42 - adc_polling, adc_interrupt
43 - ostimer_alarm, ostimer_async, ostimer_counter, ostimer_race_test
44 Location: ./examples
45 Origin: OEMWCSE (MIT)
46
47Non‑vendored Rust dependencies (resolved from crates.io at build time)
48The following crates are not vendored in this repository. They are fetched during builds via Cargo. See Cargo.lock for exact versions. License summaries below are for convenience; consult each crate for authoritative terms.
49
50cortex-m License: MIT OR Apache-2.0 Purpose: Cortex-M core support
51cortex-m-rt License: MIT OR Apache-2.0 Purpose: Cortex-M runtime (vectors, reset)
52embassy-executor License: MIT OR Apache-2.0 Purpose: Async executor for Cortex-M
53embassy-time (+ driver) License: MIT OR Apache-2.0 Purpose: Time abstraction and drivers
54embassy-sync License: MIT OR Apache-2.0 Purpose: No-std synchronization primitives
55embassy-embedded-hal License: MIT OR Apache-2.0 Purpose: Traits/adapters for embedded-hal
56embassy-hal-internal License: MIT OR Apache-2.0 Purpose: HAL building blocks (peripherals!)
57embedded-hal (1.0) License: MIT OR Apache-2.0 Purpose: Core embedded hardware traits
58embedded-hal (0.2.6) License: MIT OR Apache-2.0 Purpose: Legacy HAL traits ("unproven")
59embedded-hal-async License: MIT OR Apache-2.0 Purpose: Async HAL traits
60embedded-hal-nb License: MIT OR Apache-2.0 Purpose: Non-blocking HAL traits
61embedded-io / embedded-io-async License: MIT OR Apache-2.0 Purpose: IO traits
62critical-section License: MIT OR Apache-2.0 Purpose: Critical-section abstraction
63heapless License: MIT OR Apache-2.0 Purpose: Fixed-capacity data structures
64paste License: MIT OR Apache-2.0 Purpose: Macro hygiene utility
65nb License: MIT OR Apache-2.0 Purpose: Non-blocking result type
66vcell License: MIT OR Apache-2.0 Purpose: Volatile cell (PAC dependency)
67
68defmt, defmt-rtt, rtt-target, panic-probe License: MIT OR Apache-2.0 Purpose: Optional (feature-gated) logging/panic crates
69
70Attribution notes
71- MCXA276 PAC is sourced as an external dependency (see Cargo.toml). SVD 25.06.00 already includes OS_EVENT and WAKETIMER0 interrupts (no local fixes).
72- Examples run from RAM using custom linker setup; see README.txt for platform-specific instructions.
73
74Compliance
75- This repository’s distributed source is covered by MIT (see License.txt). Where crates are pulled from crates.io, those crates retain their own licenses; the dual-license compatibility (MIT OR Apache-2.0) is standard in the Rust embedded ecosystem and is compatible with this project’s selected MIT distribution.
76
77
78
diff --git a/build.rs b/build.rs
new file mode 100644
index 000000000..645843590
--- /dev/null
+++ b/build.rs
@@ -0,0 +1,20 @@
1use std::env;
2use std::fs::File;
3use std::io::Write;
4use std::path::PathBuf;
5
6fn main() {
7 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
8
9 // Generate memory.x - put "FLASH" at start of RAM, RAM after "FLASH"
10 // cortex-m-rt expects FLASH for code, RAM for data/bss/stack
11 // Both are in RAM, but separated to satisfy cortex-m-rt's expectations
12 // MCXA276 has 128KB RAM total
13 File::create(out.join("memory.x"))
14 .unwrap()
15 .write_all(b"/* MCXA276 RAM-execution: FLASH region holds code, RAM region for data/stack */\nMEMORY { FLASH : ORIGIN = 0x20000000, LENGTH = 64K\n RAM : ORIGIN = 0x20010000, LENGTH = 64K }\n")
16 .unwrap();
17
18 println!("cargo:rustc-link-search={}", out.display());
19 println!("cargo:rerun-if-changed=memory.x");
20}
diff --git a/defmt.x b/defmt.x
new file mode 100644
index 000000000..dbd6d0850
--- /dev/null
+++ b/defmt.x
@@ -0,0 +1,6 @@
1/*
2 Dummy defmt.x to satisfy -Tdefmt.x when building without the `defmt` feature.
3 When `defmt` is enabled, the real defmt.x provided by the defmt crate is used via the linker search path.
4 This file intentionally contains no SECTIONS so it won't override ram.ld.
5*/
6
diff --git a/deny.toml b/deny.toml
index 9ddd12fca..7097f2f55 100644
--- a/deny.toml
+++ b/deny.toml
@@ -74,8 +74,9 @@ ignore = [
74 #{ id = "RUSTSEC-0000-0000", reason = "you can specify a reason the advisory is ignored" }, 74 #{ id = "RUSTSEC-0000-0000", reason = "you can specify a reason the advisory is ignored" },
75 #"[email protected]", # you can also ignore yanked crate versions if you wish 75 #"[email protected]", # you can also ignore yanked crate versions if you wish
76 #{ crate = "[email protected]", reason = "you can specify why you are ignoring the yanked crate" }, 76 #{ crate = "[email protected]", reason = "you can specify why you are ignoring the yanked crate" },
77 { id = "RUSTSEC-2024-0370", reason = "proc-macro-error is unmaintained, no safe upgrade available, need upstream dependencies to migrate away from it." }, 77 # { id = "RUSTSEC-2024-0370", reason = "proc-macro-error is unmaintained, no safe upgrade available, need upstream dependencies to migrate away from it." },
78 { id = "RUSTSEC-2024-0436", reason = "there are no suitable replacements for paste right now; paste has been archived as read-only. It only affects compile time concatenation in macros. We will allow it for now" }, 78 { id = "RUSTSEC-2024-0436", reason = "there are no suitable replacements for paste right now; paste has been archived as read-only. It only affects compile time concatenation in macros. We will allow it for now" },
79 # { id = "RUSTSEC-2023-0089", reason = "this is a deprecation warning for a dependency of a dependency. https://github.com/jamesmunns/postcard/issues/223 tracks fixing the dependency; until that's resolved, we can accept the deprecated code as it has no known vulnerabilities."}
79] 80]
80# If this is true, then cargo deny will use the git executable to fetch advisory database. 81# If this is true, then cargo deny will use the git executable to fetch advisory database.
81# If this is false, then it uses a built-in git library. 82# If this is false, then it uses a built-in git library.
@@ -93,8 +94,9 @@ ignore = [
93allow = [ 94allow = [
94 "MIT", 95 "MIT",
95 "Apache-2.0", 96 "Apache-2.0",
97
98 # unicode-ident 1.0.14 switched from Unicode-DFS-2016 to Unicode-3.0 license.
96 "Unicode-3.0", 99 "Unicode-3.0",
97 "BSD-3-Clause",
98 #"Apache-2.0 WITH LLVM-exception", 100 #"Apache-2.0 WITH LLVM-exception",
99] 101]
100# The confidence threshold for detecting a license from license text. 102# The confidence threshold for detecting a license from license text.
@@ -232,7 +234,7 @@ allow-git = []
232 234
233[sources.allow-org] 235[sources.allow-org]
234# github.com organizations to allow git sources for 236# github.com organizations to allow git sources for
235github = ["embassy-rs"] 237github = ["OpenDevicePartnership"]
236# gitlab.com organizations to allow git sources for 238# gitlab.com organizations to allow git sources for
237gitlab = [] 239gitlab = []
238# bitbucket.org organizations to allow git sources for 240# bitbucket.org organizations to allow git sources for
diff --git a/examples/adc_interrupt.rs b/examples/adc_interrupt.rs
new file mode 100644
index 000000000..f0df3196c
--- /dev/null
+++ b/examples/adc_interrupt.rs
@@ -0,0 +1,89 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use hal::adc::{LpadcConfig, TriggerPriorityPolicy};
6use hal::pac::adc1::cfg::{Pwrsel, Refsel};
7use hal::pac::adc1::cmdl1::{Adch, Mode};
8use hal::pac::adc1::ctrl::CalAvgs;
9use hal::pac::adc1::tctrl::Tcmd;
10use hal::uart;
11use {cortex_m, embassy_mcxa276 as hal};
12mod common;
13
14use hal::{bind_interrupts, InterruptExt};
15use {defmt_rtt as _, panic_probe as _};
16
17bind_interrupts!(struct Irqs {
18 ADC1 => hal::adc::AdcHandler;
19});
20
21#[used]
22#[no_mangle]
23static KEEP_ADC: unsafe extern "C" fn() = ADC1;
24
25#[embassy_executor::main]
26async fn main(_spawner: Spawner) {
27 let p = hal::init(hal::config::Config::default());
28
29 unsafe {
30 common::init_uart2(hal::pac());
31 }
32
33 let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) };
34 let uart = uart::Uart::<uart::Lpuart2>::new(p.LPUART2, uart::Config::new(src));
35
36 uart.write_str_blocking("\r\n=== ADC interrupt Example ===\r\n");
37
38 unsafe {
39 common::init_adc(hal::pac());
40 }
41
42 let adc_config = LpadcConfig {
43 enable_in_doze_mode: true,
44 conversion_average_mode: CalAvgs::Average128,
45 enable_analog_preliminary: true,
46 power_up_delay: 0x80,
47 reference_voltage_source: Refsel::Option3,
48 power_level_mode: Pwrsel::Lowest,
49 trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed,
50 enable_conv_pause: false,
51 conv_pause_delay: 0,
52 fifo_watermark: 0,
53 };
54 let adc = hal::adc::Adc::<hal::adc::Adc1>::new(p.ADC1, adc_config);
55
56 adc.do_offset_calibration();
57 adc.do_auto_calibration();
58
59 let mut conv_command_config = adc.get_default_conv_command_config();
60 conv_command_config.channel_number = Adch::SelectCorrespondingChannel8;
61 conv_command_config.conversion_resolution_mode = Mode::Data16Bits;
62 adc.set_conv_command_config(1, &conv_command_config);
63
64 let mut conv_trigger_config = adc.get_default_conv_trigger_config();
65 conv_trigger_config.target_command_id = Tcmd::ExecuteCmd1;
66 conv_trigger_config.enable_hardware_trigger = false;
67 adc.set_conv_trigger_config(0, &conv_trigger_config);
68
69 uart.write_str_blocking("\r\n=== ADC configuration done... ===\r\n");
70
71 adc.enable_interrupt(0x1);
72
73 unsafe {
74 hal::interrupt::ADC1.enable();
75 }
76
77 unsafe {
78 cortex_m::interrupt::enable();
79 }
80
81 loop {
82 adc.do_software_trigger(1);
83 while !adc.is_interrupt_triggered() {
84 // Wait until the interrupt is triggered
85 }
86 uart.write_str_blocking("\r\n*** ADC interrupt TRIGGERED! ***\r\n");
87 //TBD need to print the value
88 }
89}
diff --git a/examples/adc_polling.rs b/examples/adc_polling.rs
new file mode 100644
index 000000000..561500d2d
--- /dev/null
+++ b/examples/adc_polling.rs
@@ -0,0 +1,79 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa276 as hal;
6use hal::adc::{ConvResult, LpadcConfig, TriggerPriorityPolicy};
7use hal::pac::adc1::cfg::{Pwrsel, Refsel};
8use hal::pac::adc1::cmdl1::{Adch, Mode};
9use hal::pac::adc1::ctrl::CalAvgs;
10use hal::pac::adc1::tctrl::Tcmd;
11use hal::uart;
12
13mod common;
14
15use core::fmt::Write;
16
17use heapless::String;
18use {defmt_rtt as _, panic_probe as _};
19
20const G_LPADC_RESULT_SHIFT: u32 = 0;
21
22#[embassy_executor::main]
23async fn main(_spawner: Spawner) {
24 let p = hal::init(hal::config::Config::default());
25
26 unsafe {
27 common::init_uart2(hal::pac());
28 }
29
30 let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) };
31 let uart = uart::Uart::<uart::Lpuart2>::new(p.LPUART2, uart::Config::new(src));
32
33 uart.write_str_blocking("\r\n=== ADC polling Example ===\r\n");
34
35 unsafe {
36 common::init_adc(hal::pac());
37 }
38
39 let adc_config = LpadcConfig {
40 enable_in_doze_mode: true,
41 conversion_average_mode: CalAvgs::Average128,
42 enable_analog_preliminary: true,
43 power_up_delay: 0x80,
44 reference_voltage_source: Refsel::Option3,
45 power_level_mode: Pwrsel::Lowest,
46 trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed,
47 enable_conv_pause: false,
48 conv_pause_delay: 0,
49 fifo_watermark: 0,
50 };
51 let adc = hal::adc::Adc::<hal::adc::Adc1>::new(p.ADC1, adc_config);
52
53 adc.do_offset_calibration();
54 adc.do_auto_calibration();
55
56 let mut conv_command_config = adc.get_default_conv_command_config();
57 conv_command_config.channel_number = Adch::SelectCorrespondingChannel8;
58 conv_command_config.conversion_resolution_mode = Mode::Data16Bits;
59 adc.set_conv_command_config(1, &conv_command_config);
60
61 let mut conv_trigger_config = adc.get_default_conv_trigger_config();
62 conv_trigger_config.target_command_id = Tcmd::ExecuteCmd1;
63 conv_trigger_config.enable_hardware_trigger = false;
64 adc.set_conv_trigger_config(0, &conv_trigger_config);
65
66 uart.write_str_blocking("\r\n=== ADC configuration done... ===\r\n");
67
68 loop {
69 adc.do_software_trigger(1);
70 let mut result: Option<ConvResult> = None;
71 while result.is_none() {
72 result = hal::adc::get_conv_result();
73 }
74 let value = result.unwrap().conv_value >> G_LPADC_RESULT_SHIFT;
75 let mut buf: String<16> = String::new(); // adjust size as needed
76 write!(buf, "\r\nvalue: {}\r\n", value).unwrap();
77 uart.write_str_blocking(&buf);
78 }
79}
diff --git a/examples/blink.rs b/examples/blink.rs
new file mode 100644
index 000000000..564353d5c
--- /dev/null
+++ b/examples/blink.rs
@@ -0,0 +1,84 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa276 as hal;
6use embassy_time::{Duration, Timer};
7use hal::gpio::pins::PIO3_18;
8use hal::gpio::{Level, Output};
9
10mod common;
11
12use embassy_mcxa276::bind_interrupts;
13
14// Bind only OS_EVENT for timer interrupts
15bind_interrupts!(struct Irqs {
16 OS_EVENT => hal::ostimer::time_driver::OsEventHandler;
17});
18
19#[used]
20#[no_mangle]
21static KEEP_OS_EVENT: unsafe extern "C" fn() = OS_EVENT;
22
23#[embassy_executor::main]
24async fn main(_spawner: Spawner) {
25 let _p = hal::init(hal::config::Config::default());
26
27 // Board-style init: enable LED GPIO/PORT clocks used by blink
28 unsafe {
29 common::init_led(hal::pac());
30 }
31 // Initialize OSTIMER for async timing
32 unsafe {
33 common::init_ostimer0(hal::pac());
34 }
35
36 // Initialize embassy-time global driver backed by OSTIMER0
37 hal::ostimer::time_driver::init(hal::config::Config::default().time_interrupt_priority, 1_000_000);
38
39 // Configure LED pin for GPIO mode
40 PIO3_18::set_mux_gpio();
41
42 let mut led = Output::new(PIO3_18::degrade(), Level::High);
43
44 // Complex blinking pattern: SOS in Morse code
45 // S: ... (3 short)
46 // O: --- (3 long)
47 // S: ... (3 short)
48 // With pauses between letters and words
49
50 loop {
51 // S: three short blinks
52 for _ in 0..3 {
53 led.set_low();
54 Timer::after(Duration::from_millis(150)).await;
55 led.set_high();
56 Timer::after(Duration::from_millis(150)).await;
57 }
58
59 // Pause between letters
60 Timer::after(Duration::from_millis(300)).await;
61
62 // O: three long blinks
63 for _ in 0..3 {
64 led.set_low();
65 Timer::after(Duration::from_millis(450)).await;
66 led.set_high();
67 Timer::after(Duration::from_millis(150)).await;
68 }
69
70 // Pause between letters
71 Timer::after(Duration::from_millis(300)).await;
72
73 // S: three short blinks
74 for _ in 0..3 {
75 led.set_low();
76 Timer::after(Duration::from_millis(150)).await;
77 led.set_high();
78 Timer::after(Duration::from_millis(150)).await;
79 }
80
81 // Long pause between words (SOS repeats)
82 Timer::after(Duration::from_millis(1000)).await;
83 }
84}
diff --git a/examples/common/mod.rs b/examples/common/mod.rs
new file mode 100644
index 000000000..7ada4c456
--- /dev/null
+++ b/examples/common/mod.rs
@@ -0,0 +1,45 @@
1//! Shared board-specific helpers for the FRDM-MCXA276 examples.
2//! These live with the examples so the HAL stays generic.
3
4use embassy_mcxa276 as hal;
5use hal::{clocks, pins, reset};
6
7/// Initialize clocks and pin muxing for UART2 debug console.
8/// Safe to call multiple times; writes are idempotent for our use.
9#[allow(dead_code)]
10pub unsafe fn init_uart2(p: &hal::pac::Peripherals) {
11 clocks::ensure_frolf_running(p);
12 clocks::enable_uart2_port2(p);
13 reset::release_reset_port2(p);
14 reset::release_reset_lpuart2(p);
15 pins::configure_uart2_pins_port2();
16 clocks::select_uart2_clock(p);
17}
18
19/// Initialize clocks for the LED GPIO/PORT used by the blink example.
20#[allow(dead_code)]
21pub unsafe fn init_led(p: &hal::pac::Peripherals) {
22 clocks::enable_led_port(p);
23 reset::release_reset_gpio3(p);
24 reset::release_reset_port3(p);
25}
26
27/// Initialize clocks for OSTIMER0 (1 MHz source).
28#[allow(dead_code)]
29pub unsafe fn init_ostimer0(p: &hal::pac::Peripherals) {
30 clocks::ensure_frolf_running(p);
31 clocks::enable_ostimer0(p);
32 reset::release_reset_ostimer0(p);
33 clocks::select_ostimer0_clock_1m(p);
34}
35
36/// Initialize clocks and pin muxing for ADC.
37#[allow(dead_code)]
38pub unsafe fn init_adc(p: &hal::pac::Peripherals) {
39 clocks::ensure_frolf_running(p);
40 clocks::enable_adc(p);
41 reset::release_reset_port1(p);
42 reset::release_reset_adc1(p);
43 pins::configure_adc_pins();
44 clocks::select_adc_clock(p);
45}
diff --git a/examples/hello.rs b/examples/hello.rs
new file mode 100644
index 000000000..e39adaced
--- /dev/null
+++ b/examples/hello.rs
@@ -0,0 +1,114 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa276 as hal;
6use hal::uart;
7
8mod common;
9
10use {defmt_rtt as _, panic_probe as _};
11
12/// Simple helper to write a byte as hex to UART
13fn write_hex_byte(uart: &hal::uart::Uart<hal::uart::Lpuart2>, byte: u8) {
14 const HEX_DIGITS: &[u8] = b"0123456789ABCDEF";
15 uart.write_byte(HEX_DIGITS[(byte >> 4) as usize]);
16 uart.write_byte(HEX_DIGITS[(byte & 0xF) as usize]);
17}
18
19#[embassy_executor::main]
20async fn main(_spawner: Spawner) {
21 let p = hal::init(hal::config::Config::default());
22
23 defmt::info!("boot");
24
25 // Board-level init for UART2 clocks and pins.
26 unsafe {
27 common::init_uart2(hal::pac());
28 }
29
30 // Get UART source frequency from clock configuration
31 // Using hardcoded frequency for now - dynamic detection may have issues
32 let src = 12_000_000; // FRO_LF_DIV at 12MHz with DIV=0
33 let uart = uart::Uart::<uart::Lpuart2>::new(p.LPUART2, uart::Config::new(src));
34
35 // Print welcome message before any async delays to guarantee early console output
36 uart.write_str_blocking("\r\n=== MCXA276 UART Echo Demo ===\r\n");
37 uart.write_str_blocking("Available commands:\r\n");
38 uart.write_str_blocking(" help - Show this help\r\n");
39 uart.write_str_blocking(" echo <text> - Echo back the text\r\n");
40 uart.write_str_blocking(" hex <byte> - Display byte in hex (0-255)\r\n");
41 uart.write_str_blocking("Type a command: ");
42
43 let mut buffer = [0u8; 64];
44 let mut buf_idx = 0;
45
46 loop {
47 // Read a byte from UART
48 let byte = uart.read_byte_blocking();
49
50 // Echo the character back
51 if byte == b'\r' || byte == b'\n' {
52 // Enter pressed - process command
53 uart.write_str_blocking("\r\n");
54
55 if buf_idx > 0 {
56 let command = &buffer[0..buf_idx];
57
58 if command == b"help" {
59 uart.write_str_blocking("Available commands:\r\n");
60 uart.write_str_blocking(" help - Show this help\r\n");
61 uart.write_str_blocking(" echo <text> - Echo back the text\r\n");
62 uart.write_str_blocking(" hex <byte> - Display byte in hex (0-255)\r\n");
63 } else if command.starts_with(b"echo ") && command.len() > 5 {
64 uart.write_str_blocking("Echo: ");
65 uart.write_str_blocking(core::str::from_utf8(&command[5..]).unwrap_or(""));
66 uart.write_str_blocking("\r\n");
67 } else if command.starts_with(b"hex ") && command.len() > 4 {
68 // Parse the byte value
69 let num_str = &command[4..];
70 if let Ok(num) = parse_u8(num_str) {
71 uart.write_str_blocking("Hex: 0x");
72 write_hex_byte(&uart, num);
73 uart.write_str_blocking("\r\n");
74 } else {
75 uart.write_str_blocking("Invalid number for hex command\r\n");
76 }
77 } else if command.len() > 0 {
78 uart.write_str_blocking("Unknown command: ");
79 uart.write_str_blocking(core::str::from_utf8(command).unwrap_or(""));
80 uart.write_str_blocking("\r\n");
81 }
82 }
83
84 // Reset buffer and prompt
85 buf_idx = 0;
86 uart.write_str_blocking("Type a command: ");
87 } else if byte == 8 || byte == 127 {
88 // Backspace
89 if buf_idx > 0 {
90 buf_idx -= 1;
91 uart.write_str_blocking("\x08 \x08"); // Erase character
92 }
93 } else if buf_idx < buffer.len() - 1 {
94 // Regular character
95 buffer[buf_idx] = byte;
96 buf_idx += 1;
97 uart.write_byte(byte);
98 }
99 }
100}
101
102/// Simple parser for u8 from ASCII bytes
103fn parse_u8(bytes: &[u8]) -> Result<u8, ()> {
104 let mut result = 0u8;
105 for &b in bytes {
106 if b >= b'0' && b <= b'9' {
107 result = result.checked_mul(10).ok_or(())?;
108 result = result.checked_add(b - b'0').ok_or(())?;
109 } else {
110 return Err(());
111 }
112 }
113 Ok(result)
114}
diff --git a/examples/lpuart_buffered.rs b/examples/lpuart_buffered.rs
new file mode 100644
index 000000000..30ba3f333
--- /dev/null
+++ b/examples/lpuart_buffered.rs
@@ -0,0 +1,77 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa276 as hal;
6use embassy_mcxa276::interrupt::typelevel::Handler;
7use embassy_mcxa276::lpuart::buffered::BufferedLpuart;
8use embassy_mcxa276::{bind_interrupts, lpuart};
9use embedded_io_async::{Read, Write};
10
11mod common;
12
13// Bind OS_EVENT for timers plus LPUART2 IRQ for the buffered driver
14bind_interrupts!(struct Irqs {
15 LPUART2 => lpuart::buffered::BufferedInterruptHandler::<lpuart::lib::peripherals::LPUART2>;
16});
17
18// Wrapper function for the interrupt handler
19unsafe extern "C" fn lpuart2_handler() {
20 lpuart::buffered::BufferedInterruptHandler::<lpuart::lib::peripherals::LPUART2>::on_interrupt();
21}
22
23#[embassy_executor::main]
24async fn main(_spawner: Spawner) {
25 let _p = hal::init(hal::config::Config::default());
26 let p2 = lpuart::lib::init();
27
28 unsafe {
29 hal::interrupt::install_irq_handler(mcxa276_pac::Interrupt::LPUART2, lpuart2_handler);
30 }
31
32 // Configure NVIC for LPUART2
33 hal::interrupt::LPUART2.configure_for_uart(hal::interrupt::Priority::P3);
34
35 unsafe {
36 common::init_uart2(hal::pac());
37 common::init_ostimer0(hal::pac());
38 }
39
40 // UART configuration (enable both TX and RX)
41 let config = lpuart::Config {
42 baudrate_bps: 115_200,
43 enable_tx: true,
44 enable_rx: true,
45 rx_fifo_watermark: 0,
46 tx_fifo_watermark: 0,
47 ..Default::default()
48 };
49
50 let mut tx_buf = [0u8; 256];
51 let mut rx_buf = [0u8; 256];
52
53 // Create a buffered LPUART2 instance with both TX and RX
54 let mut uart = BufferedLpuart::new(
55 p2.LPUART2,
56 p2.PIO2_2, // TX pin
57 p2.PIO2_3, // RX pin
58 Irqs,
59 &mut tx_buf,
60 &mut rx_buf,
61 config,
62 )
63 .unwrap();
64
65 // Split into TX and RX parts
66 let (tx, rx) = uart.split_ref();
67
68 tx.write(b"Hello buffered LPUART.\r\n").await.unwrap();
69 tx.write(b"Type characters to echo them back.\r\n").await.unwrap();
70
71 // Echo loop
72 let mut buf = [0u8; 4];
73 loop {
74 rx.read_exact(&mut buf[..]).await.unwrap();
75 tx.write_all(&buf[..]).await.unwrap();
76 }
77}
diff --git a/examples/lpuart_polling.rs b/examples/lpuart_polling.rs
new file mode 100644
index 000000000..067c7eb53
--- /dev/null
+++ b/examples/lpuart_polling.rs
@@ -0,0 +1,53 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use {defmt_rtt as _, embassy_mcxa276 as hal, panic_probe as _};
6
7use crate::hal::lpuart::{lib, Config, Lpuart};
8
9mod common;
10
11#[embassy_executor::main]
12async fn main(_spawner: Spawner) {
13 let _p = hal::init(hal::config::Config::default());
14 let p2 = lib::init();
15
16 defmt::info!("boot");
17
18 // Board-level init for UART2 clocks and pins.
19 unsafe {
20 common::init_uart2(hal::pac());
21 }
22
23 // Create UART configuration
24 let config = Config {
25 baudrate_bps: 115_200,
26 enable_tx: true,
27 enable_rx: true,
28 ..Default::default()
29 };
30
31 // Create UART instance using LPUART2 with PIO2_2 as TX and PIO2_3 as RX
32 let lpuart = Lpuart::new_blocking(
33 p2.LPUART2, // Peripheral
34 p2.PIO2_2, // TX pin
35 p2.PIO2_3, // RX pin
36 config,
37 )
38 .unwrap();
39
40 // Split into separate TX and RX parts
41 let (mut tx, mut rx) = lpuart.split();
42
43 // Write hello messages
44 tx.blocking_write(b"Hello world.\r\n").unwrap();
45 tx.blocking_write(b"Echoing. Type characters...\r\n").unwrap();
46
47 // Echo loop
48 loop {
49 let mut buf = [0u8; 1];
50 rx.blocking_read(&mut buf).unwrap();
51 tx.blocking_write(&buf).unwrap();
52 }
53}
diff --git a/examples/ostimer_alarm.rs b/examples/ostimer_alarm.rs
new file mode 100644
index 000000000..78ca4bbc5
--- /dev/null
+++ b/examples/ostimer_alarm.rs
@@ -0,0 +1,111 @@
1#![no_std]
2#![no_main]
3
4use core::sync::atomic::{AtomicBool, Ordering};
5
6use embassy_executor::Spawner;
7use hal::uart;
8use {cortex_m, embassy_mcxa276 as hal};
9
10mod common;
11
12use embassy_mcxa276::bind_interrupts;
13use {defmt_rtt as _, panic_probe as _};
14
15// Bind only OS_EVENT, and retain the symbol explicitly so it can't be GC'ed.
16bind_interrupts!(struct Irqs {
17 OS_EVENT => hal::ostimer::time_driver::OsEventHandler;
18});
19
20#[used]
21#[no_mangle]
22static KEEP_OS_EVENT: unsafe extern "C" fn() = OS_EVENT;
23
24// Global flag for alarm callback
25static ALARM_FLAG: AtomicBool = AtomicBool::new(false);
26
27// Alarm callback function
28fn alarm_callback() {
29 ALARM_FLAG.store(true, Ordering::Release);
30}
31
32#[embassy_executor::main]
33async fn main(_spawner: Spawner) {
34 let p = hal::init(hal::config::Config::default());
35
36 // Enable/clock OSTIMER0 and UART2 before touching their registers
37 unsafe {
38 common::init_ostimer0(hal::pac());
39 }
40 unsafe {
41 common::init_uart2(hal::pac());
42 }
43 let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) };
44 let uart = uart::Uart::<uart::Lpuart2>::new(p.LPUART2, uart::Config::new(src));
45 uart.write_str_blocking("OSTIMER Alarm Example\n");
46
47 // Initialize embassy-time global driver backed by OSTIMER0
48 hal::ostimer::time_driver::init(hal::config::Config::default().time_interrupt_priority, 1_000_000);
49
50 // Create OSTIMER instance
51 let config = hal::ostimer::Config {
52 init_match_max: true,
53 clock_frequency_hz: 1_000_000, // 1MHz
54 };
55 let ostimer = hal::ostimer::Ostimer::<hal::ostimer::Ostimer0>::new(p.OSTIMER0, config, hal::pac());
56
57 // Create alarm with callback
58 let alarm = hal::ostimer::Alarm::new()
59 .with_callback(alarm_callback)
60 .with_flag(&ALARM_FLAG);
61
62 uart.write_str_blocking("Scheduling alarm for 2 seconds...\n");
63
64 // Schedule alarm to expire in 2 seconds (2,000,000 microseconds)
65 let scheduled = ostimer.schedule_alarm_delay(&alarm, 2_000_000);
66 if scheduled {
67 uart.write_str_blocking("Alarm scheduled successfully\n");
68 } else {
69 uart.write_str_blocking("Failed to schedule alarm (would exceed timer capacity)\n");
70 return;
71 }
72
73 // Wait for alarm to expire
74 loop {
75 // Check if alarm has expired
76 if ALARM_FLAG.load(Ordering::Acquire) {
77 uart.write_str_blocking("Alarm expired! Callback executed.\n");
78 break;
79 }
80
81 // Busy wait - don't use Timer::after_millis as it interferes with alarm MATCH
82 for _ in 0..100000 {
83 cortex_m::asm::nop();
84 }
85 }
86
87 // Demonstrate canceling an alarm
88 uart.write_str_blocking("Scheduling another alarm for 3 seconds...\n");
89 ALARM_FLAG.store(false, Ordering::Release); // Reset flag
90
91 let scheduled = ostimer.schedule_alarm_delay(&alarm, 3_000_000);
92 if scheduled {
93 uart.write_str_blocking("Alarm scheduled. Waiting 1 second then canceling...\n");
94
95 // Wait 1 second
96 embassy_time::Timer::after_millis(1000).await;
97
98 // Cancel the alarm
99 ostimer.cancel_alarm(&alarm);
100 uart.write_str_blocking("Alarm canceled\n");
101
102 // Check immediately if alarm flag is set
103 if !ALARM_FLAG.load(Ordering::Acquire) {
104 uart.write_str_blocking("Alarm was successfully canceled\n");
105 } else {
106 uart.write_str_blocking("Alarm fired despite cancellation\n");
107 }
108 }
109
110 uart.write_str_blocking("Example complete\n");
111}
diff --git a/examples/ostimer_async.rs b/examples/ostimer_async.rs
new file mode 100644
index 000000000..27e14e022
--- /dev/null
+++ b/examples/ostimer_async.rs
@@ -0,0 +1,57 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa276 as hal;
6use hal::uart;
7
8mod common;
9
10use embassy_mcxa276::bind_interrupts;
11use embassy_time::{Duration, Timer};
12use {defmt_rtt as _, panic_probe as _};
13
14// Bind only OS_EVENT, and retain the symbol explicitly so it can’t be GC’ed.
15bind_interrupts!(struct Irqs {
16 OS_EVENT => hal::ostimer::time_driver::OsEventHandler;
17});
18
19#[used]
20#[no_mangle]
21static KEEP_OS_EVENT: unsafe extern "C" fn() = OS_EVENT;
22
23#[embassy_executor::main]
24async fn main(_spawner: Spawner) {
25 let _p = hal::init(hal::config::Config::default());
26
27 // Enable/clock OSTIMER0 and UART2 before touching their registers
28 unsafe {
29 common::init_ostimer0(hal::pac());
30 }
31 unsafe {
32 common::init_uart2(hal::pac());
33 }
34 let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) };
35 let uart = uart::Uart::<uart::Lpuart2>::new(_p.LPUART2, uart::Config::new(src));
36 uart.write_str_blocking("boot\n");
37
38 // Avoid mass NVIC writes here; DefaultHandler now safely returns.
39
40 // Initialize embassy-time global driver backed by OSTIMER0 (re-enables OS_EVENT with priority)
41 // The bind_interrupts! macro handles handler binding automatically
42
43 // Initialize OSTIMER with default 1MHz frequency
44 // Adjust this value to match your actual OSTIMER clock frequency
45 hal::ostimer::time_driver::init(hal::config::Config::default().time_interrupt_priority, 1_000_000);
46
47 // Removed force-pend; rely on real hardware match to trigger OS_EVENT.
48
49 // Log using defmt if enabled
50 defmt::info!("OSTIMER async example starting...");
51
52 loop {
53 defmt::info!("tick");
54 uart.write_str_blocking("tick\n");
55 Timer::after(Duration::from_millis(1000)).await;
56 }
57}
diff --git a/examples/ostimer_counter.rs b/examples/ostimer_counter.rs
new file mode 100644
index 000000000..e95140a88
--- /dev/null
+++ b/examples/ostimer_counter.rs
@@ -0,0 +1,128 @@
1//! # OSTIMER Counter Reading and Reset Example
2//!
3//! This example demonstrates the new timer counter reading and reset functionality
4//! of the OSTIMER driver.
5
6#![no_std]
7#![no_main]
8
9use embassy_executor::Spawner;
10use embassy_time::{Duration, Timer};
11use hal::bind_interrupts;
12use {defmt_rtt as _, embassy_mcxa276 as hal, panic_probe as _};
13
14mod common;
15
16bind_interrupts!(struct Irqs {
17 OS_EVENT => hal::ostimer::time_driver::OsEventHandler;
18});
19
20#[embassy_executor::main]
21async fn main(_spawner: Spawner) {
22 let p = hal::init(Default::default());
23
24 // Enable/clock OSTIMER0 and UART2 before touching their registers
25 unsafe {
26 common::init_ostimer0(hal::pac());
27 }
28 unsafe {
29 common::init_uart2(hal::pac());
30 }
31 let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) };
32 let mut uart = hal::uart::Uart::<hal::uart::Lpuart2>::new(p.LPUART2, hal::uart::Config::new(src));
33
34 uart.write_str_blocking("OSTIMER Counter Reading and Reset Example\n");
35
36 // Initialize the OSTIMER time driver
37 hal::ostimer::time_driver::init(
38 hal::interrupt::Priority::from(3),
39 1_000_000, // 1MHz clock
40 );
41
42 // Create OSTIMER instance
43 let ostimer = hal::ostimer::Ostimer::<hal::ostimer::Ostimer0>::new(
44 p.OSTIMER0,
45 hal::ostimer::Config {
46 init_match_max: true,
47 clock_frequency_hz: 1_000_000,
48 },
49 hal::pac(),
50 );
51
52 // Read initial counter value
53 let initial_counter = ostimer.now();
54 uart.write_str_blocking("Initial counter value: ");
55 write_u64(&mut uart, initial_counter);
56 uart.write_str_blocking("\n");
57
58 // Wait a bit to let counter increment
59 Timer::after(Duration::from_millis(100)).await;
60
61 // Read counter again
62 let counter_after_wait = ostimer.now();
63 uart.write_str_blocking("Counter after 100ms wait: ");
64 write_u64(&mut uart, counter_after_wait);
65 uart.write_str_blocking("\n");
66 uart.write_str_blocking("Difference: ");
67 write_u64(&mut uart, counter_after_wait - initial_counter);
68 uart.write_str_blocking(" ticks\n");
69
70 // Reset the timer
71 uart.write_str_blocking("Resetting timer...\n");
72 ostimer.reset(hal::pac());
73
74 // Read counter after reset
75 let counter_after_reset = ostimer.now();
76 uart.write_str_blocking("Counter after reset: ");
77 write_u64(&mut uart, counter_after_reset);
78 uart.write_str_blocking("\n");
79
80 // Wait again to verify timer is working
81 Timer::after(Duration::from_millis(50)).await;
82
83 let final_counter = ostimer.now();
84 uart.write_str_blocking("Counter after another 50ms: ");
85 write_u64(&mut uart, final_counter);
86 uart.write_str_blocking("\n");
87 uart.write_str_blocking("Difference after reset: ");
88 write_u64(&mut uart, final_counter - counter_after_reset);
89 uart.write_str_blocking(" ticks\n");
90
91 uart.write_str_blocking("Example complete\n");
92}
93
94// Helper function to write a u64 value as decimal string
95fn write_u64(uart: &mut hal::uart::Uart<hal::uart::Lpuart2>, value: u64) {
96 if value == 0 {
97 uart.write_str_blocking("0");
98 return;
99 }
100
101 let mut buffer = [0u8; 20]; // Enough for max u64
102 let mut i = 0;
103 let mut v = value;
104
105 while v > 0 {
106 buffer[i] = b'0' + (v % 10) as u8;
107 v /= 10;
108 i += 1;
109 }
110
111 // Write digits in reverse order
112 while i > 0 {
113 i -= 1;
114 match buffer[i] {
115 b'0' => uart.write_str_blocking("0"),
116 b'1' => uart.write_str_blocking("1"),
117 b'2' => uart.write_str_blocking("2"),
118 b'3' => uart.write_str_blocking("3"),
119 b'4' => uart.write_str_blocking("4"),
120 b'5' => uart.write_str_blocking("5"),
121 b'6' => uart.write_str_blocking("6"),
122 b'7' => uart.write_str_blocking("7"),
123 b'8' => uart.write_str_blocking("8"),
124 b'9' => uart.write_str_blocking("9"),
125 _ => uart.write_str_blocking("?"),
126 }
127 }
128}
diff --git a/examples/ostimer_race_test.rs b/examples/ostimer_race_test.rs
new file mode 100644
index 000000000..a637b6353
--- /dev/null
+++ b/examples/ostimer_race_test.rs
@@ -0,0 +1,396 @@
1//! # OSTIMER Race Condition Test
2//!
3//! This example tests for race conditions in the OSTIMER driver by:
4//! - Scheduling alarms sequentially (hardware limitation: only one at a time)
5//! - Reading the counter during interrupt-heavy periods
6//! - Testing concurrent timer operations
7//! - Stress testing interrupt handling
8
9#![no_std]
10#![no_main]
11
12use core::sync::atomic::{AtomicU32, Ordering};
13
14use embassy_executor::Spawner;
15use embassy_time::{Duration, Timer};
16use hal::bind_interrupts;
17use {defmt_rtt as _, embassy_mcxa276 as hal, panic_probe as _};
18
19mod common;
20
21bind_interrupts!(struct Irqs {
22 OS_EVENT => hal::ostimer::time_driver::OsEventHandler;
23});
24
25#[used]
26#[no_mangle]
27static KEEP_OS_EVENT: unsafe extern "C" fn() = OS_EVENT;
28
29// Global counters for race condition detection
30static ALARM_CALLBACK_COUNT: AtomicU32 = AtomicU32::new(0);
31static INTERRUPT_COUNT: AtomicU32 = AtomicU32::new(0);
32static RACE_DETECTED: AtomicU32 = AtomicU32::new(0);
33
34// Alarm callback function
35fn alarm_callback() {
36 let _count = ALARM_CALLBACK_COUNT.fetch_add(1, Ordering::SeqCst);
37 INTERRUPT_COUNT.fetch_add(1, Ordering::SeqCst);
38
39 // Simulate some work in the callback to increase chance of races
40 for _ in 0..10 {
41 cortex_m::asm::nop();
42 }
43}
44
45fn report_default_handler(uart: &mut hal::uart::Uart<hal::uart::Lpuart2>) {
46 let snapshot = hal::interrupt::default_handler_snapshot();
47 if snapshot.count == 0 {
48 return;
49 }
50
51 uart.write_str_blocking("WARNING: DefaultHandler executed ");
52 write_u32(uart, snapshot.count);
53 uart.write_str_blocking(" time(s). Vector=");
54 write_u32(uart, snapshot.vector as u32);
55 uart.write_str_blocking(" CFSR=0x");
56 write_hex32(uart, snapshot.cfsr);
57 uart.write_str_blocking(" HFSR=0x");
58 write_hex32(uart, snapshot.hfsr);
59 uart.write_str_blocking(" PC=0x");
60 write_hex32(uart, snapshot.stacked_pc);
61 uart.write_str_blocking(" LR=0x");
62 write_hex32(uart, snapshot.stacked_lr);
63 uart.write_str_blocking(" SP=0x");
64 write_hex32(uart, snapshot.stacked_sp);
65 uart.write_str_blocking("\n");
66
67 hal::interrupt::clear_default_handler_snapshot();
68}
69
70#[embassy_executor::main]
71async fn main(_spawner: Spawner) {
72 let p = hal::init(Default::default());
73
74 // Enable/clock OSTIMER0 and UART2 before touching their registers
75 unsafe {
76 common::init_ostimer0(hal::pac());
77 }
78 unsafe {
79 common::init_uart2(hal::pac());
80 }
81 let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) };
82 let mut uart = hal::uart::Uart::<hal::uart::Lpuart2>::new(p.LPUART2, hal::uart::Config::new(src));
83
84 uart.write_str_blocking("OSTIMER Race Condition Test Starting...\n");
85
86 // The bind_interrupts! macro handles handler binding automatically
87
88 // Initialize the OSTIMER time driver FIRST
89 hal::ostimer::time_driver::init(
90 hal::interrupt::Priority::from(3),
91 1_000_000, // 1MHz clock
92 );
93
94 uart.write_str_blocking("Time driver initialized\n");
95
96 // Create OSTIMER instance
97 let ostimer = hal::ostimer::Ostimer::<hal::ostimer::Ostimer0>::new(
98 p.OSTIMER0,
99 hal::ostimer::Config {
100 init_match_max: true,
101 clock_frequency_hz: 1_000_000,
102 },
103 hal::pac(),
104 );
105
106 uart.write_str_blocking("OSTIMER instance created\n");
107
108 // Test 1: Sequential alarm scheduling (OSTIMER only supports one alarm at a time)
109 uart.write_str_blocking("Test 1: Sequential alarm scheduling...\n");
110 test_rapid_alarms(&ostimer, &mut uart).await;
111 report_default_handler(&mut uart);
112
113 // Test 2: Counter reading during interrupts
114 uart.write_str_blocking("Test 2: Counter reading during interrupts...\n");
115 test_counter_reading_during_interrupts(&ostimer, &mut uart).await;
116 report_default_handler(&mut uart);
117
118 // Test 3: Concurrent timer operations
119 uart.write_str_blocking("Test 3: Concurrent timer operations...\n");
120 test_concurrent_operations(&ostimer, &mut uart).await;
121 report_default_handler(&mut uart);
122
123 // Test 4: Timer reset during operation
124 uart.write_str_blocking("Test 4: Timer reset during operation...\n");
125 test_reset_during_operation(&ostimer, &mut uart, hal::pac()).await;
126 report_default_handler(&mut uart);
127
128 // Report results
129 uart.write_str_blocking("Race condition test complete\n");
130 uart.write_str_blocking("Callback count: ");
131 write_u32(&mut uart, ALARM_CALLBACK_COUNT.load(Ordering::SeqCst));
132 uart.write_str_blocking("\nInterrupt count: ");
133 write_u32(&mut uart, INTERRUPT_COUNT.load(Ordering::SeqCst));
134 uart.write_str_blocking("\nRaces detected: ");
135 write_u32(&mut uart, RACE_DETECTED.load(Ordering::SeqCst));
136 uart.write_str_blocking("\n");
137}
138
139// Test rapid alarm scheduling to stress interrupt handling
140async fn test_rapid_alarms(
141 ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>,
142 uart: &mut hal::uart::Uart<hal::uart::Lpuart2>,
143) {
144 let initial_count = ALARM_CALLBACK_COUNT.load(Ordering::SeqCst);
145
146 // Schedule 10 alarms sequentially (OSTIMER only supports one alarm at a time)
147 for _i in 0..10 {
148 let alarm = hal::ostimer::Alarm::new().with_callback(alarm_callback);
149 let delay_us = 1000; // 1ms delay for each alarm
150 if ostimer.schedule_alarm_delay(&alarm, delay_us) {
151 // Wait for this alarm to complete before scheduling the next
152 Timer::after(Duration::from_micros(delay_us + 100)).await;
153 report_default_handler(uart);
154 } else {
155 RACE_DETECTED.fetch_add(1, Ordering::SeqCst);
156 uart.write_str_blocking("ERROR: Failed to program OSTIMER alarm (match not ready)\n");
157 }
158 }
159
160 // All alarms should have completed by now
161 let final_count = ALARM_CALLBACK_COUNT.load(Ordering::SeqCst);
162 let expected_count = initial_count + 10;
163
164 if final_count != expected_count {
165 RACE_DETECTED.fetch_add(1, Ordering::SeqCst);
166 uart.write_str_blocking("ERROR: Expected ");
167 write_u32(uart, expected_count);
168 uart.write_str_blocking(" callbacks, got ");
169 write_u32(uart, final_count);
170 uart.write_str_blocking("\n");
171 } else {
172 uart.write_str_blocking("PASS: All rapid alarms executed\n");
173 }
174}
175
176// Test reading counter while interrupts are firing
177async fn test_counter_reading_during_interrupts(
178 ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>,
179 uart: &mut hal::uart::Uart<hal::uart::Lpuart2>,
180) {
181 let initial_interrupt_count = INTERRUPT_COUNT.load(Ordering::SeqCst);
182
183 // Schedule an alarm that will fire soon
184 let alarm = hal::ostimer::Alarm::new().with_callback(alarm_callback);
185 if !ostimer.schedule_alarm_delay(&alarm, 500) {
186 RACE_DETECTED.fetch_add(1, Ordering::SeqCst);
187 uart.write_str_blocking("ERROR: Failed to program OSTIMER alarm before counter stress\n");
188 }
189
190 // While alarm is pending, read the counter many times rapidly
191 // This tests if counter reading is atomic and doesn't get corrupted by interrupts
192 let mut last_counter = ostimer.now();
193 let mut consistent_reads = 0;
194 let mut total_reads = 0;
195
196 for _ in 0..1000 {
197 let current_counter = ostimer.now();
198 total_reads += 1;
199
200 // Check if counter is monotonically increasing (basic sanity check)
201 if current_counter >= last_counter {
202 consistent_reads += 1;
203 }
204 last_counter = current_counter;
205
206 // Small delay between reads
207 for _ in 0..10 {
208 cortex_m::asm::nop();
209 }
210
211 report_default_handler(uart);
212 }
213
214 // Wait for alarm to complete
215 Timer::after(Duration::from_millis(1)).await;
216
217 let final_interrupt_count = INTERRUPT_COUNT.load(Ordering::SeqCst);
218
219 if consistent_reads == total_reads {
220 uart.write_str_blocking("PASS: Counter reading consistent during interrupts\n");
221 } else {
222 RACE_DETECTED.fetch_add(1, Ordering::SeqCst);
223 uart.write_str_blocking("ERROR: Counter reading inconsistent: ");
224 write_u32(uart, consistent_reads);
225 uart.write_str_blocking("/");
226 write_u32(uart, total_reads);
227 uart.write_str_blocking(" consistent\n");
228 }
229
230 if final_interrupt_count > initial_interrupt_count {
231 uart.write_str_blocking("PASS: Interrupt fired during counter reading test\n");
232 } else {
233 uart.write_str_blocking("WARNING: No interrupt fired during counter reading test\n");
234 }
235}
236
237// Test concurrent timer operations (embassy-time + alarms)
238async fn test_concurrent_operations(
239 ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>,
240 uart: &mut hal::uart::Uart<hal::uart::Lpuart2>,
241) {
242 let initial_interrupt_count = INTERRUPT_COUNT.load(Ordering::SeqCst);
243
244 // Start an embassy-time timer
245 let timer_future = Timer::after(Duration::from_millis(2));
246
247 // Schedule an alarm that should fire before the timer
248 let alarm = hal::ostimer::Alarm::new().with_callback(alarm_callback);
249 if !ostimer.schedule_alarm_delay(&alarm, 1000) {
250 RACE_DETECTED.fetch_add(1, Ordering::SeqCst);
251 uart.write_str_blocking("ERROR: Failed to program OSTIMER alarm before concurrent operations\n");
252 }
253
254 // Wait for both to complete
255 timer_future.await;
256
257 let final_interrupt_count = INTERRUPT_COUNT.load(Ordering::SeqCst);
258
259 if final_interrupt_count > initial_interrupt_count {
260 uart.write_str_blocking("PASS: Concurrent operations completed\n");
261 } else {
262 uart.write_str_blocking("WARNING: No interrupts during concurrent operations\n");
263 }
264}
265
266// Test timer reset during active operations
267async fn test_reset_during_operation(
268 ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>,
269 uart: &mut hal::uart::Uart<hal::uart::Lpuart2>,
270 peripherals: &hal::pac::Peripherals,
271) {
272 let initial_counter = ostimer.now();
273
274 // Schedule an alarm
275 let alarm = hal::ostimer::Alarm::new().with_callback(alarm_callback);
276 if !ostimer.schedule_alarm_delay(&alarm, 2000) {
277 RACE_DETECTED.fetch_add(1, Ordering::SeqCst);
278 uart.write_str_blocking("ERROR: Failed to program OSTIMER alarm before reset test\n");
279 }
280
281 // Wait a bit then reset the timer
282 Timer::after(Duration::from_millis(1)).await;
283 ostimer.reset(peripherals);
284
285 // Check counter after reset
286 let counter_after_reset = ostimer.now();
287
288 // Wait to see if the alarm still fires (it shouldn't after reset)
289 Timer::after(Duration::from_millis(2)).await;
290
291 let final_counter = ostimer.now();
292
293 if counter_after_reset < initial_counter {
294 uart.write_str_blocking("PASS: Timer reset successful\n");
295 } else {
296 RACE_DETECTED.fetch_add(1, Ordering::SeqCst);
297 uart.write_str_blocking("ERROR: Timer reset may have failed\n");
298 }
299
300 uart.write_str_blocking("Counter progression after reset: ");
301 write_u64(uart, initial_counter);
302 uart.write_str_blocking(" -> ");
303 write_u64(uart, counter_after_reset);
304 uart.write_str_blocking(" -> ");
305 write_u64(uart, final_counter);
306 uart.write_str_blocking("\n");
307}
308
309// Helper function to write a u32 value as decimal string
310fn write_u32(uart: &mut hal::uart::Uart<hal::uart::Lpuart2>, value: u32) {
311 if value == 0 {
312 uart.write_str_blocking("0");
313 return;
314 }
315
316 let mut buffer = [0u8; 10]; // Enough for max u32
317 let mut i = 0;
318 let mut v = value;
319
320 while v > 0 {
321 buffer[i] = b'0' + (v % 10) as u8;
322 v /= 10;
323 i += 1;
324 }
325
326 // Write digits in reverse order
327 while i > 0 {
328 i -= 1;
329 match buffer[i] {
330 b'0' => uart.write_str_blocking("0"),
331 b'1' => uart.write_str_blocking("1"),
332 b'2' => uart.write_str_blocking("2"),
333 b'3' => uart.write_str_blocking("3"),
334 b'4' => uart.write_str_blocking("4"),
335 b'5' => uart.write_str_blocking("5"),
336 b'6' => uart.write_str_blocking("6"),
337 b'7' => uart.write_str_blocking("7"),
338 b'8' => uart.write_str_blocking("8"),
339 b'9' => uart.write_str_blocking("9"),
340 _ => uart.write_str_blocking("?"),
341 }
342 }
343}
344
345fn write_hex32(uart: &mut hal::uart::Uart<hal::uart::Lpuart2>, value: u32) {
346 let mut buf = [b'0'; 8];
347 let mut tmp = value;
348 for i in (0..8).rev() {
349 let digit = (tmp & 0xF) as u8;
350 buf[i] = match digit {
351 0..=9 => b'0' + digit,
352 10..=15 => b'A' + (digit - 10),
353 _ => b'?',
354 };
355 tmp >>= 4;
356 }
357 for b in &buf {
358 uart.write_byte(*b);
359 }
360}
361
362// Helper function to write a u64 value as decimal string
363fn write_u64(uart: &mut hal::uart::Uart<hal::uart::Lpuart2>, value: u64) {
364 if value == 0 {
365 uart.write_str_blocking("0");
366 return;
367 }
368
369 let mut buffer = [0u8; 20]; // Enough for max u64
370 let mut i = 0;
371 let mut v = value;
372
373 while v > 0 {
374 buffer[i] = b'0' + (v % 10) as u8;
375 v /= 10;
376 i += 1;
377 }
378
379 // Write digits in reverse order
380 while i > 0 {
381 i -= 1;
382 match buffer[i] {
383 b'0' => uart.write_str_blocking("0"),
384 b'1' => uart.write_str_blocking("1"),
385 b'2' => uart.write_str_blocking("2"),
386 b'3' => uart.write_str_blocking("3"),
387 b'4' => uart.write_str_blocking("4"),
388 b'5' => uart.write_str_blocking("5"),
389 b'6' => uart.write_str_blocking("6"),
390 b'7' => uart.write_str_blocking("7"),
391 b'8' => uart.write_str_blocking("8"),
392 b'9' => uart.write_str_blocking("9"),
393 _ => uart.write_str_blocking("?"),
394 }
395 }
396}
diff --git a/examples/rtc_alarm.rs b/examples/rtc_alarm.rs
new file mode 100644
index 000000000..c27fd4c55
--- /dev/null
+++ b/examples/rtc_alarm.rs
@@ -0,0 +1,87 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use hal::rtc::{RtcDateTime, RtcInterruptEnable};
6use hal::{uart, InterruptExt};
7use {cortex_m, embassy_mcxa276 as hal};
8
9mod common;
10
11type MyRtc = hal::rtc::Rtc<hal::rtc::Rtc0>;
12
13use embassy_mcxa276::bind_interrupts;
14use {defmt_rtt as _, panic_probe as _};
15
16bind_interrupts!(struct Irqs {
17 RTC => hal::rtc::RtcHandler;
18});
19
20#[used]
21#[no_mangle]
22static KEEP_RTC: unsafe extern "C" fn() = RTC;
23
24#[embassy_executor::main]
25async fn main(_spawner: Spawner) {
26 let p = hal::init(hal::config::Config::default());
27
28 unsafe {
29 common::init_uart2(hal::pac());
30 }
31
32 let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) };
33 let uart = uart::Uart::<uart::Lpuart2>::new(p.LPUART2, uart::Config::new(src));
34
35 uart.write_str_blocking("\r\n=== RTC Alarm Example ===\r\n");
36
37 unsafe { hal::clocks::init_fro16k(hal::pac()) };
38
39 let rtc_config = hal::rtc::get_default_config();
40
41 let rtc = MyRtc::new(p.RTC0, rtc_config);
42
43 let now = RtcDateTime {
44 year: 2025,
45 month: 10,
46 day: 15,
47 hour: 14,
48 minute: 30,
49 second: 0,
50 };
51
52 rtc.stop();
53
54 uart.write_str_blocking("Time set to: 2025-10-15 14:30:00\r\n");
55 rtc.set_datetime(now);
56
57 let mut alarm = now;
58 alarm.second += 10;
59
60 rtc.set_alarm(alarm);
61 uart.write_str_blocking("Alarm set for: 2025-10-15 14:30:10 (+10 seconds)\r\n");
62
63 rtc.set_interrupt(RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE);
64
65 unsafe {
66 hal::interrupt::RTC.enable();
67 }
68
69 unsafe {
70 cortex_m::interrupt::enable();
71 }
72
73 rtc.start();
74
75 uart.write_str_blocking("RTC started, waiting for alarm...\r\n");
76
77 loop {
78 if rtc.is_alarm_triggered() {
79 uart.write_str_blocking("\r\n*** ALARM TRIGGERED! ***\r\n");
80 break;
81 }
82 }
83
84 uart.write_str_blocking("Example complete - Test PASSED!\r\n");
85
86 loop {}
87}
diff --git a/examples/uart_interrupt.rs b/examples/uart_interrupt.rs
new file mode 100644
index 000000000..bd734f859
--- /dev/null
+++ b/examples/uart_interrupt.rs
@@ -0,0 +1,69 @@
1#![no_std]
2#![no_main]
3
4use embassy_executor::Spawner;
5use embassy_mcxa276 as hal;
6use hal::interrupt::typelevel::Handler;
7use hal::uart;
8
9mod common;
10
11use embassy_mcxa276::bind_interrupts;
12use {defmt_rtt as _, panic_probe as _};
13
14// Bind LPUART2 interrupt to our handler
15bind_interrupts!(struct Irqs {
16 LPUART2 => hal::uart::UartInterruptHandler;
17});
18
19#[used]
20#[no_mangle]
21static KEEP_LPUART2: unsafe extern "C" fn() = LPUART2;
22
23// Wrapper function for the interrupt handler
24unsafe extern "C" fn lpuart2_handler() {
25 hal::uart::UartInterruptHandler::on_interrupt();
26}
27
28#[embassy_executor::main]
29async fn main(_spawner: Spawner) {
30 let _p = hal::init(hal::config::Config::default());
31
32 // Enable/clock UART2 before touching its registers
33 unsafe {
34 common::init_uart2(hal::pac());
35 }
36 let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) };
37 let uart = uart::Uart::<uart::Lpuart2>::new(_p.LPUART2, uart::Config::new(src));
38
39 // Configure LPUART2 interrupt for UART operation BEFORE any UART usage
40 hal::interrupt::LPUART2.configure_for_uart(hal::interrupt::Priority::from(3));
41
42 // Manually install the interrupt handler and enable RX IRQs in the peripheral
43 unsafe {
44 hal::interrupt::LPUART2.install_handler(lpuart2_handler);
45 // Enable RX interrupts so the handler actually fires on incoming bytes
46 uart.enable_rx_interrupts();
47 }
48
49 // Print welcome message
50 uart.write_str_blocking("UART interrupt echo demo starting...\r\n");
51 uart.write_str_blocking("Type characters to echo them back.\r\n");
52
53 // Log using defmt if enabled
54 defmt::info!("UART interrupt echo demo starting...");
55
56 loop {
57 // Check if we have received any data
58 if uart.rx_data_available() {
59 if let Some(byte) = uart.try_read_byte() {
60 // Echo it back
61 uart.write_byte(byte);
62 uart.write_str_blocking(" (received)\r\n");
63 }
64 } else {
65 // No data available, wait a bit before checking again
66 cortex_m::asm::delay(12_000_000); // ~1 second at 12MHz
67 }
68 }
69}
diff --git a/memory.x b/memory.x
new file mode 100644
index 000000000..b47534f18
--- /dev/null
+++ b/memory.x
@@ -0,0 +1,10 @@
1/* Memory layout for MCXA276 - RAM execution with cortex-m-rt */
2MEMORY
3{
4 /* FLASH and RAM overlap for RAM-execution experiments. */
5 FLASH : ORIGIN = 0x20000000, LENGTH = 128K
6 /* RAM overlaps FLASH */
7 RAM : ORIGIN = 0x20000000, LENGTH = 128K
8}
9
10/* Leave symbol and section boundary definitions to cortex-m-rt's link.x. */
diff --git a/ram.ld b/ram.ld
new file mode 100644
index 000000000..816ab6819
--- /dev/null
+++ b/ram.ld
@@ -0,0 +1,109 @@
1/* Simple RAM execution linker script for MCXA276 */
2MEMORY
3{
4 RAM : ORIGIN = 0x20000000, LENGTH = 128K
5}
6
7/* Pull in device default interrupt symbol aliases (e.g., CMC = DefaultHandler) */
8INCLUDE device.x
9
10
11/* Provide core exception weak aliases if not supplied by cortex-m-rt's link.x */
12PROVIDE(NonMaskableInt = DefaultHandler);
13PROVIDE(HardFault = DefaultHandler);
14PROVIDE(MemoryManagement = DefaultHandler);
15PROVIDE(BusFault = DefaultHandler);
16PROVIDE(UsageFault = DefaultHandler);
17PROVIDE(SecureFault = DefaultHandler);
18PROVIDE(SVCall = DefaultHandler);
19PROVIDE(DebugMonitor = DefaultHandler);
20PROVIDE(PendSV = DefaultHandler);
21PROVIDE(SysTick = DefaultHandler);
22
23/* In RAM-run we have no FLASH sidata; copy from sdata */
24__sidata = __sdata;
25
26/* Ensure the PAC interrupt table is kept */
27EXTERN(__INTERRUPTS);
28
29
30/* Pull in defmt's linker script to generate the defmt table that host decoders expect */
31INCLUDE defmt.x
32
33ENTRY(Reset)
34EXTERN(VECTOR_TABLE)
35EXTERN(Reset)
36EXTERN(main)
37
38/* Define _stack_start at end of RAM BEFORE it's used in vector table */
39_stack_start = ORIGIN(RAM) + LENGTH(RAM);
40
41SECTIONS
42{
43 .vector_table ORIGIN(RAM) :
44 {
45 /* Slot 0: Initial stack pointer - use our explicitly set _stack_start */
46 LONG(_stack_start);
47 /* Slot 1: Reset vector - address of Reset function with Thumb bit set */
48 LONG(Reset | 1);
49 /* Cortex-M33 core exceptions (slots 2-14) */
50 KEEP(*(.vector_table.exceptions));
51 /* Peripheral interrupt vectors provided by PAC (slots 16+) */
52 KEEP(*(.vector_table.interrupts));
53 } > RAM
54
55 .text :
56 {
57 KEEP(*(.text.Reset));
58 KEEP(*(.text.main));
59 *(.text .text.*);
60 } > RAM
61
62 /* Keep defmt table and fragments so host decoders can find metadata */
63 .defmt :
64 {
65 KEEP(*(.defmt));
66 KEEP(*(.defmt.*));
67 } > RAM
68
69 .rodata :
70 {
71 *(.rodata .rodata.*);
72 } > RAM
73
74 .data :
75 {
76 . = ALIGN(4);
77 __sdata = .;
78 *(.data .data.*);
79 . = ALIGN(4);
80 __edata = .;
81 } > RAM
82
83 /* Ensure RTT control block with "SEGGER RTT" signature is loaded to RAM */
84 .rtt :
85 {
86 KEEP(*(.rtt));
87 } > RAM
88
89 /* Place uninitialized buffers (like defmt-rtt) in RAM; load is fine for RAM-run */
90 .uninit :
91 {
92 *(.uninit .uninit.*);
93 } > RAM
94
95 .bss :
96 {
97 . = ALIGN(4);
98 __sbss = .;
99 *(.bss .bss.*);
100 . = ALIGN(4);
101 __ebss = .;
102 } > RAM
103
104 /* Discard exception unwinding info */
105 /DISCARD/ :
106 {
107 *(.ARM.exidx .ARM.exidx.*);
108 }
109}
diff --git a/run.sh b/run.sh
new file mode 100644
index 000000000..418dc8a24
--- /dev/null
+++ b/run.sh
@@ -0,0 +1,93 @@
1#!/usr/bin/env bash
2set -euo pipefail
3
4ELF="${1:-}"
5if [[ -z "${ELF}" ]]; then
6 echo "Usage: $0 <elf_file>"
7 exit 1
8fi
9if [[ ! -f "${ELF}" ]]; then
10 echo "ELF not found: ${ELF}"
11 exit 1
12fi
13
14# Configurable via env
15CHIP="${CHIP:-MCXA276}"
16SPEED="${PROBE_SPEED:-1000}" # kHz
17# Default to J-Link if PROBE not provided
18PROBE_OPT=(--probe "${PROBE:-1366:0101:000600110607}")
19PORT="${PROBE_RS_GDB_PORT:-1337}"
20
21cleanup() {
22 if [[ -n "${GDB_SERVER_PID:-}" ]]; then kill "${GDB_SERVER_PID}" 2>/dev/null || true; fi
23 [[ -n "${GDB_SCRIPT:-}" ]] && rm -f "${GDB_SCRIPT}" || true
24 [[ -n "${SERVER_LOG:-}" ]] && rm -f "${SERVER_LOG}" || true
25}
26trap cleanup EXIT
27
28if ! command -v probe-rs >/dev/null 2>&1; then
29 echo "probe-rs not found (cargo install probe-rs --features cli)"
30 exit 1
31fi
32if ! command -v gdb-multiarch >/dev/null 2>&1; then
33 echo "gdb-multiarch not found; install it (e.g., sudo apt install gdb-multiarch)."
34 exit 1
35fi
36
37# Start probe-rs GDB server and capture its output to a log (do not hide errors)
38SERVER_LOG=$(mktemp)
39set +e
40probe-rs gdb --chip "${CHIP}" --protocol swd --speed "${SPEED}" --non-interactive "${ELF}" "${PROBE_OPT[@]}" \
41 >"${SERVER_LOG}" 2>&1 &
42GDB_SERVER_PID=$!
43set -e
44
45# Wait for server readiness without touching the TCP port to avoid corrupting the GDB protocol
46ready=""
47for _ in {1..50}; do
48 if grep -q "Firing up GDB stub" "${SERVER_LOG}"; then ready=1; break; fi
49 if grep -q "Connecting to the chip was unsuccessful" "${SERVER_LOG}"; then
50 echo "probe-rs gdb server failed to connect to target. Log:" >&2
51 echo "----- probe-rs gdb log -----" >&2
52 sed -e 's/^/ /' "${SERVER_LOG}" >&2 || true
53 exit 1
54 fi
55 sleep 0.1
56done
57if [[ -z "${ready}" ]]; then
58 echo "probe-rs gdb server did not report readiness. Log:" >&2
59 echo "----- probe-rs gdb log -----" >&2
60 sed -e 's/^/ /' "${SERVER_LOG}" >&2 || true
61 exit 1
62fi
63
64# GDB script: load to RAM and run, no reset
65GDB_SCRIPT=$(mktemp)
66cat >"${GDB_SCRIPT}" <<EOF
67set pagination off
68set confirm off
69set mem inaccessible-by-default off
70
71# Connect and load without reset
72target remote :${PORT}
73monitor halt
74load
75# Set VTOR to point to our RAM vector table at 0x20000000
76# This ensures the CPU uses the correct initial SP and Reset vector
77set *0xE000ED08 = 0x20000000
78# Now read SP and PC from our vector table and set them
79set \$sp = *(unsigned int*)0x20000000
80set \$pc = *(unsigned int*)0x20000004
81# Run target (blocks here until you Ctrl+C, like before)
82continue
83EOF
84
85# Run gdb against the server
86if ! gdb-multiarch -q -batch -x "${GDB_SCRIPT}" "${ELF}"; then
87 echo "GDB failed to load/run. probe-rs gdb server log:" >&2
88 echo "----- probe-rs gdb log -----" >&2
89 sed -e 's/^/ /' "${SERVER_LOG}" >&2 || true
90 exit 1
91fi
92
93echo "Program loaded and started (no reset)"
diff --git a/rust-toolchain.toml b/rust-toolchain.toml
deleted file mode 100644
index 367cc1fc3..000000000
--- a/rust-toolchain.toml
+++ /dev/null
@@ -1,2 +0,0 @@
1[toolchain]
2components = ["rustfmt", "clippy"]
diff --git a/rustfmt.toml b/rustfmt.toml
index 753065179..9eb3c3b4f 100644
--- a/rustfmt.toml
+++ b/rustfmt.toml
@@ -1 +1,3 @@
1group_imports = "StdExternalCrate"
2imports_granularity = "Module"
1max_width = 120 3max_width = 120
diff --git a/src/adc.rs b/src/adc.rs
new file mode 100644
index 000000000..d456971f7
--- /dev/null
+++ b/src/adc.rs
@@ -0,0 +1,388 @@
1//! ADC driver
2use core::sync::atomic::{AtomicBool, Ordering};
3
4use crate::pac;
5use crate::pac::adc1::cfg::{HptExdi, Pwrsel, Refsel, Tcmdres, Tprictrl, Tres};
6use crate::pac::adc1::cmdh1::{Avgs, Cmpen, Next, Sts};
7use crate::pac::adc1::cmdl1::{Adch, Ctype, Mode};
8use crate::pac::adc1::ctrl::CalAvgs;
9use crate::pac::adc1::tctrl::{Tcmd, Tpri};
10
11type Regs = pac::adc1::RegisterBlock;
12
13static INTERRUPT_TRIGGERED: AtomicBool = AtomicBool::new(false);
14// Token-based instance pattern like embassy-imxrt
15pub trait Instance {
16 fn ptr() -> *const Regs;
17}
18
19/// Token for ADC1
20pub type Adc1 = crate::peripherals::ADC1;
21impl Instance for crate::peripherals::ADC1 {
22 #[inline(always)]
23 fn ptr() -> *const Regs {
24 pac::Adc1::ptr()
25 }
26}
27
28// Also implement Instance for the Peri wrapper type
29impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::ADC1> {
30 #[inline(always)]
31 fn ptr() -> *const Regs {
32 pac::Adc1::ptr()
33 }
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37#[repr(u8)]
38pub enum TriggerPriorityPolicy {
39 ConvPreemptImmediatelyNotAutoResumed = 0,
40 ConvPreemptSoftlyNotAutoResumed = 1,
41 ConvPreemptImmediatelyAutoRestarted = 4,
42 ConvPreemptSoftlyAutoRestarted = 5,
43 ConvPreemptImmediatelyAutoResumed = 12,
44 ConvPreemptSoftlyAutoResumed = 13,
45 ConvPreemptSubsequentlyNotAutoResumed = 2,
46 ConvPreemptSubsequentlyAutoRestarted = 6,
47 ConvPreemptSubsequentlyAutoResumed = 14,
48 TriggerPriorityExceptionDisabled = 16,
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub struct LpadcConfig {
53 pub enable_in_doze_mode: bool,
54 pub conversion_average_mode: CalAvgs,
55 pub enable_analog_preliminary: bool,
56 pub power_up_delay: u8,
57 pub reference_voltage_source: Refsel,
58 pub power_level_mode: Pwrsel,
59 pub trigger_priority_policy: TriggerPriorityPolicy,
60 pub enable_conv_pause: bool,
61 pub conv_pause_delay: u16,
62 pub fifo_watermark: u8,
63}
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66pub struct ConvCommandConfig {
67 pub sample_channel_mode: Ctype,
68 pub channel_number: Adch,
69 pub chained_next_command_number: Next,
70 pub enable_auto_channel_increment: bool,
71 pub loop_count: u8,
72 pub hardware_average_mode: Avgs,
73 pub sample_time_mode: Sts,
74 pub hardware_compare_mode: Cmpen,
75 pub hardware_compare_value_high: u32,
76 pub hardware_compare_value_low: u32,
77 pub conversion_resolution_mode: Mode,
78 pub enable_wait_trigger: bool,
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq)]
82pub struct ConvTriggerConfig {
83 pub target_command_id: Tcmd,
84 pub delay_power: u8,
85 pub priority: Tpri,
86 pub enable_hardware_trigger: bool,
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
90pub struct ConvResult {
91 pub command_id_source: u32,
92 pub loop_count_index: u32,
93 pub trigger_id_source: u32,
94 pub conv_value: u16,
95}
96
97pub struct Adc<I: Instance> {
98 _inst: core::marker::PhantomData<I>,
99}
100
101impl<I: Instance> Adc<I> {
102 /// initialize ADC
103 pub fn new(_inst: impl Instance, config: LpadcConfig) -> Self {
104 let adc = unsafe { &*I::ptr() };
105
106 /* Reset the module. */
107 adc.ctrl().modify(|_, w| w.rst().held_in_reset());
108 adc.ctrl().modify(|_, w| w.rst().released_from_reset());
109
110 adc.ctrl().modify(|_, w| w.rstfifo0().trigger_reset());
111
112 /* Disable the module before setting configuration. */
113 adc.ctrl().modify(|_, w| w.adcen().disabled());
114
115 /* Configure the module generally. */
116 if config.enable_in_doze_mode {
117 adc.ctrl().modify(|_, w| w.dozen().enabled());
118 } else {
119 adc.ctrl().modify(|_, w| w.dozen().disabled());
120 }
121
122 /* Set calibration average mode. */
123 adc.ctrl()
124 .modify(|_, w| w.cal_avgs().variant(config.conversion_average_mode));
125
126 adc.cfg().write(|w| unsafe {
127 let w = if config.enable_analog_preliminary {
128 w.pwren().pre_enabled()
129 } else {
130 w
131 };
132
133 w.pudly()
134 .bits(config.power_up_delay)
135 .refsel()
136 .variant(config.reference_voltage_source)
137 .pwrsel()
138 .variant(config.power_level_mode)
139 .tprictrl()
140 .variant(match config.trigger_priority_policy {
141 TriggerPriorityPolicy::ConvPreemptSoftlyNotAutoResumed
142 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoRestarted
143 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed => Tprictrl::FinishCurrentOnPriority,
144 TriggerPriorityPolicy::ConvPreemptSubsequentlyNotAutoResumed
145 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoRestarted
146 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed => Tprictrl::FinishSequenceOnPriority,
147 _ => Tprictrl::AbortCurrentOnPriority,
148 })
149 .tres()
150 .variant(match config.trigger_priority_policy {
151 TriggerPriorityPolicy::ConvPreemptImmediatelyAutoRestarted
152 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoRestarted
153 | TriggerPriorityPolicy::ConvPreemptImmediatelyAutoResumed
154 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed
155 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoRestarted
156 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed => Tres::Enabled,
157 _ => Tres::Disabled,
158 })
159 .tcmdres()
160 .variant(match config.trigger_priority_policy {
161 TriggerPriorityPolicy::ConvPreemptImmediatelyAutoResumed
162 | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed
163 | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed
164 | TriggerPriorityPolicy::TriggerPriorityExceptionDisabled => Tcmdres::Enabled,
165 _ => Tcmdres::Disabled,
166 })
167 .hpt_exdi()
168 .variant(match config.trigger_priority_policy {
169 TriggerPriorityPolicy::TriggerPriorityExceptionDisabled => HptExdi::Disabled,
170 _ => HptExdi::Enabled,
171 })
172 });
173
174 if config.enable_conv_pause {
175 adc.pause()
176 .modify(|_, w| unsafe { w.pauseen().enabled().pausedly().bits(config.conv_pause_delay) });
177 } else {
178 adc.pause().write(|w| unsafe { w.bits(0) });
179 }
180
181 adc.fctrl0()
182 .write(|w| unsafe { w.fwmark().bits(config.fifo_watermark) });
183
184 // Enable ADC
185 adc.ctrl().modify(|_, w| w.adcen().enabled());
186
187 Self {
188 _inst: core::marker::PhantomData,
189 }
190 }
191
192 pub fn deinit(&self) {
193 let adc = unsafe { &*I::ptr() };
194 adc.ctrl().modify(|_, w| w.adcen().disabled());
195 }
196
197 pub fn get_default_config() -> LpadcConfig {
198 LpadcConfig {
199 enable_in_doze_mode: true,
200 conversion_average_mode: CalAvgs::NoAverage,
201 enable_analog_preliminary: false,
202 power_up_delay: 0x80,
203 reference_voltage_source: Refsel::Option1,
204 power_level_mode: Pwrsel::Lowest,
205 trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed,
206 enable_conv_pause: false,
207 conv_pause_delay: 0,
208 fifo_watermark: 0,
209 }
210 }
211
212 pub fn do_offset_calibration(&self) {
213 let adc = unsafe { &*I::ptr() };
214 // Enable calibration mode
215 adc.ctrl()
216 .modify(|_, w| w.calofs().offset_calibration_request_pending());
217
218 // Wait for calibration to complete (polling status register)
219 while adc.stat().read().cal_rdy().is_not_set() {}
220 }
221
222 pub fn get_gain_conv_result(&self, mut gain_adjustment: f32) -> u32 {
223 let mut gcra_array = [0u32; 17];
224 let mut gcalr: u32 = 0;
225
226 for i in (1..=17).rev() {
227 let shift = 16 - (i - 1);
228 let step = 1.0 / (1u32 << shift) as f32;
229 let tmp = (gain_adjustment / step) as u32;
230 gcra_array[i - 1] = tmp;
231 gain_adjustment = gain_adjustment - tmp as f32 * step;
232 }
233
234 for i in (1..=17).rev() {
235 gcalr += gcra_array[i - 1] << (i - 1);
236 }
237 gcalr
238 }
239
240 pub fn do_auto_calibration(&self) {
241 let adc = unsafe { &*I::ptr() };
242 adc.ctrl().modify(|_, w| w.cal_req().calibration_request_pending());
243
244 while adc.gcc0().read().rdy().is_gain_cal_not_valid() {}
245
246 let mut gcca = adc.gcc0().read().gain_cal().bits() as u32;
247 if gcca & (((0xFFFF >> 0) + 1) >> 1) != 0 {
248 gcca |= !0xFFFF;
249 }
250
251 let gcra = 131072.0 / (131072.0 - gcca as f32);
252
253 // Write to GCR0
254 adc.gcr0().write(|w| unsafe { w.bits(self.get_gain_conv_result(gcra)) });
255
256 adc.gcr0().modify(|_, w| w.rdy().set_bit());
257
258 // Wait for calibration to complete (polling status register)
259 while adc.stat().read().cal_rdy().is_not_set() {}
260 }
261
262 pub fn do_software_trigger(&self, trigger_id_mask: u32) {
263 let adc = unsafe { &*I::ptr() };
264 adc.swtrig().write(|w| unsafe { w.bits(trigger_id_mask) });
265 }
266
267 pub fn get_default_conv_command_config(&self) -> ConvCommandConfig {
268 ConvCommandConfig {
269 sample_channel_mode: Ctype::SingleEndedASideChannel,
270 channel_number: Adch::SelectCh0,
271 chained_next_command_number: Next::NoNextCmdTerminateOnFinish,
272 enable_auto_channel_increment: false,
273 loop_count: 0,
274 hardware_average_mode: Avgs::NoAverage,
275 sample_time_mode: Sts::Sample3p5,
276 hardware_compare_mode: Cmpen::DisabledAlwaysStoreResult,
277 hardware_compare_value_high: 0,
278 hardware_compare_value_low: 0,
279 conversion_resolution_mode: Mode::Data12Bits,
280 enable_wait_trigger: false,
281 }
282 }
283
284 //TBD Need to add cmdlx and cmdhx with x {2..7}
285 pub fn set_conv_command_config(&self, index: u32, config: &ConvCommandConfig) {
286 let adc = unsafe { &*I::ptr() };
287
288 match index {
289 1 => {
290 adc.cmdl1().write(|w| {
291 w.adch()
292 .variant(config.channel_number)
293 .mode()
294 .variant(config.conversion_resolution_mode)
295 });
296 adc.cmdh1().write(|w| unsafe {
297 w.next()
298 .variant(config.chained_next_command_number)
299 .loop_()
300 .bits(config.loop_count)
301 .avgs()
302 .variant(config.hardware_average_mode)
303 .sts()
304 .variant(config.sample_time_mode)
305 .cmpen()
306 .variant(config.hardware_compare_mode);
307 if config.enable_wait_trigger {
308 w.wait_trig().enabled();
309 }
310 if config.enable_auto_channel_increment {
311 w.lwi().enabled();
312 }
313 w
314 });
315 }
316 _ => panic!("Invalid command index: must be between 1 and 7"),
317 }
318 }
319
320 pub fn get_default_conv_trigger_config(&self) -> ConvTriggerConfig {
321 ConvTriggerConfig {
322 target_command_id: Tcmd::NotValid,
323 delay_power: 0,
324 priority: Tpri::HighestPriority,
325 enable_hardware_trigger: false,
326 }
327 }
328
329 pub fn set_conv_trigger_config(&self, trigger_id: usize, config: &ConvTriggerConfig) {
330 let adc = unsafe { &*I::ptr() };
331 let tctrl = &adc.tctrl(trigger_id);
332
333 tctrl.write(|w| unsafe {
334 let w = w.tcmd().variant(config.target_command_id);
335 let w = w.tdly().bits(config.delay_power);
336 w.tpri().variant(config.priority);
337 if config.enable_hardware_trigger {
338 w.hten().enabled()
339 } else {
340 w
341 }
342 });
343 }
344
345 pub fn do_reset_fifo(&self) {
346 let adc = unsafe { &*I::ptr() };
347 adc.ctrl().modify(|_, w| w.rstfifo0().trigger_reset());
348 }
349
350 pub fn enable_interrupt(&self, mask: u32) {
351 let adc = unsafe { &*I::ptr() };
352 adc.ie().modify(|r, w| unsafe { w.bits(r.bits() | mask) });
353 INTERRUPT_TRIGGERED.store(false, Ordering::SeqCst);
354 }
355
356 pub fn is_interrupt_triggered(&self) -> bool {
357 INTERRUPT_TRIGGERED.load(Ordering::Relaxed)
358 }
359}
360
361pub fn get_conv_result() -> Option<ConvResult> {
362 let adc = unsafe { &*pac::Adc1::ptr() };
363 let fifo = adc.resfifo0().read().bits();
364 const VALID_MASK: u32 = 1 << 31;
365 if fifo & VALID_MASK == 0 {
366 return None;
367 }
368
369 Some(ConvResult {
370 command_id_source: (fifo >> 24) & 0x0F,
371 loop_count_index: (fifo >> 20) & 0x0F,
372 trigger_id_source: (fifo >> 16) & 0x0F,
373 conv_value: (fifo & 0xFFFF) as u16,
374 })
375}
376
377pub fn on_interrupt() {
378 if get_conv_result().is_some() {
379 INTERRUPT_TRIGGERED.store(true, Ordering::SeqCst);
380 }
381}
382
383pub struct AdcHandler;
384impl crate::interrupt::typelevel::Handler<crate::interrupt::typelevel::ADC1> for AdcHandler {
385 unsafe fn on_interrupt() {
386 on_interrupt();
387 }
388}
diff --git a/src/baremetal/mod.rs b/src/baremetal/mod.rs
deleted file mode 100644
index c03b9538b..000000000
--- a/src/baremetal/mod.rs
+++ /dev/null
@@ -1,6 +0,0 @@
1use core::panic::PanicInfo;
2
3#[panic_handler]
4fn panic(_info: &PanicInfo) -> ! {
5 loop {}
6}
diff --git a/src/board.rs b/src/board.rs
new file mode 100644
index 000000000..fa679e82c
--- /dev/null
+++ b/src/board.rs
@@ -0,0 +1,14 @@
1use crate::{clocks, pac, pins};
2
3/// Initialize clocks and pin muxing for UART2 debug console.
4pub unsafe fn init_uart2(p: &pac::Peripherals) {
5 clocks::ensure_frolf_running(p);
6 clocks::enable_uart2_port2(p);
7 pins::configure_uart2_pins_port2();
8 clocks::select_uart2_clock(p);
9}
10
11/// Initialize clocks for the LED GPIO/PORT used by the blink example.
12pub unsafe fn init_led(p: &pac::Peripherals) {
13 clocks::enable_led_port(p);
14}
diff --git a/src/clocks.rs b/src/clocks.rs
new file mode 100644
index 000000000..65a17cef6
--- /dev/null
+++ b/src/clocks.rs
@@ -0,0 +1,134 @@
1//! Clock control helpers (no magic numbers, PAC field access only).
2//! Provides reusable gate abstractions for peripherals used by the examples.
3use crate::pac;
4
5/// Trait describing an AHB clock gate that can be toggled through MRCC.
6pub trait Gate {
7 /// Enable the clock gate.
8 unsafe fn enable(mrcc: &pac::mrcc0::RegisterBlock);
9
10 /// Return whether the clock gate is currently enabled.
11 fn is_enabled(mrcc: &pac::mrcc0::RegisterBlock) -> bool;
12}
13
14/// Enable a clock gate for the given peripheral set.
15#[inline]
16pub unsafe fn enable<G: Gate>(peripherals: &pac::Peripherals) {
17 let mrcc = &peripherals.mrcc0;
18 G::enable(mrcc);
19 while !G::is_enabled(mrcc) {}
20 core::arch::asm!("dsb sy; isb sy", options(nomem, nostack, preserves_flags));
21}
22
23/// Check whether a gate is currently enabled.
24#[inline]
25pub fn is_enabled<G: Gate>(peripherals: &pac::Peripherals) -> bool {
26 G::is_enabled(&peripherals.mrcc0)
27}
28
29macro_rules! impl_cc_gate {
30 ($name:ident, $reg:ident, $field:ident) => {
31 pub struct $name;
32
33 impl Gate for $name {
34 #[inline]
35 unsafe fn enable(mrcc: &pac::mrcc0::RegisterBlock) {
36 mrcc.$reg().modify(|_, w| w.$field().enabled());
37 }
38
39 #[inline]
40 fn is_enabled(mrcc: &pac::mrcc0::RegisterBlock) -> bool {
41 mrcc.$reg().read().$field().is_enabled()
42 }
43 }
44 };
45}
46
47pub mod gate {
48 use super::*;
49
50 impl_cc_gate!(Port2, mrcc_glb_cc1, port2);
51 impl_cc_gate!(Port3, mrcc_glb_cc1, port3);
52 impl_cc_gate!(Ostimer0, mrcc_glb_cc1, ostimer0);
53 impl_cc_gate!(Lpuart2, mrcc_glb_cc0, lpuart2);
54 impl_cc_gate!(Gpio3, mrcc_glb_cc2, gpio3);
55 impl_cc_gate!(Port1, mrcc_glb_cc1, port1);
56 impl_cc_gate!(Adc1, mrcc_glb_cc1, adc1);
57}
58
59/// Convenience helper enabling the PORT2 and LPUART2 gates required for the debug UART.
60pub unsafe fn enable_uart2_port2(peripherals: &pac::Peripherals) {
61 enable::<gate::Port2>(peripherals);
62 enable::<gate::Lpuart2>(peripherals);
63}
64
65/// Convenience helper enabling the PORT3 and GPIO3 gates used by the LED in the examples.
66pub unsafe fn enable_led_port(peripherals: &pac::Peripherals) {
67 enable::<gate::Port3>(peripherals);
68 enable::<gate::Gpio3>(peripherals);
69}
70
71/// Convenience helper enabling the OSTIMER0 clock gate.
72pub unsafe fn enable_ostimer0(peripherals: &pac::Peripherals) {
73 enable::<gate::Ostimer0>(peripherals);
74}
75
76pub unsafe fn select_uart2_clock(peripherals: &pac::Peripherals) {
77 // Use FRO_LF_DIV (already running) MUX=0 DIV=0
78 let mrcc = &peripherals.mrcc0;
79 mrcc.mrcc_lpuart2_clksel().write(|w| w.mux().clkroot_func_0());
80 mrcc.mrcc_lpuart2_clkdiv().write(|w| unsafe { w.bits(0) });
81}
82
83pub unsafe fn ensure_frolf_running(peripherals: &pac::Peripherals) {
84 // Ensure FRO_LF divider clock is running (reset default HALT=1 stops it)
85 let sys = &peripherals.syscon;
86 sys.frolfdiv().modify(|_, w| {
87 // DIV defaults to 0; keep it explicit and clear HALT
88 unsafe { w.div().bits(0) }.halt().run()
89 });
90}
91
92/// Compute the FRO_LF_DIV output frequency currently selected for LPUART2.
93/// Assumes select_uart2_clock() has chosen MUX=0 (FRO_LF_DIV) and DIV is set in SYSCON.FRO_LF_DIV.
94pub unsafe fn uart2_src_hz(peripherals: &pac::Peripherals) -> u32 {
95 // SYSCON.FRO_LF_DIV: DIV field is simple divider: freq_out = 12_000_000 / (DIV+1) for many NXP parts.
96 // On MCXA276 FRO_LF base is 12 MHz; our init keeps DIV=0, so result=12_000_000.
97 // Read it anyway for future generality.
98 let div = peripherals.syscon.frolfdiv().read().div().bits() as u32;
99 let base = 12_000_000u32;
100 base / (div + 1)
101}
102
103/// Enable clock gate and release reset for OSTIMER0.
104/// Select OSTIMER0 clock source = 1 MHz root (working bring-up configuration).
105pub unsafe fn select_ostimer0_clock_1m(peripherals: &pac::Peripherals) {
106 let mrcc = &peripherals.mrcc0;
107 mrcc.mrcc_ostimer0_clksel().write(|w| w.mux().clkroot_1m());
108}
109
110pub unsafe fn init_fro16k(peripherals: &pac::Peripherals) {
111 let vbat = &peripherals.vbat0;
112 // Enable FRO16K oscillator
113 vbat.froctla().modify(|_, w| w.fro_en().set_bit());
114
115 // Lock the control register
116 vbat.frolcka().modify(|_, w| w.lock().set_bit());
117
118 // Enable clock outputs to both VSYS and VDD_CORE domains
119 // Bit 0: clk_16k0 to VSYS domain
120 // Bit 1: clk_16k1 to VDD_CORE domain
121 vbat.froclke().modify(|_, w| unsafe { w.clke().bits(0x3) });
122}
123
124pub unsafe fn enable_adc(peripherals: &pac::Peripherals) {
125 enable::<gate::Port1>(peripherals);
126 enable::<gate::Adc1>(peripherals);
127}
128
129pub unsafe fn select_adc_clock(peripherals: &pac::Peripherals) {
130 // Use FRO_LF_DIV (already running) MUX=0 DIV=0
131 let mrcc = &peripherals.mrcc0;
132 mrcc.mrcc_adc_clksel().write(|w| w.mux().clkroot_func_0());
133 mrcc.mrcc_adc_clkdiv().write(|w| unsafe { w.bits(0) });
134}
diff --git a/src/config.rs b/src/config.rs
new file mode 100644
index 000000000..93aed5a99
--- /dev/null
+++ b/src/config.rs
@@ -0,0 +1,20 @@
1// HAL configuration (minimal), mirroring embassy-imxrt style
2
3use crate::interrupt::Priority;
4
5#[non_exhaustive]
6pub struct Config {
7 pub time_interrupt_priority: Priority,
8 pub rtc_interrupt_priority: Priority,
9 pub adc_interrupt_priority: Priority,
10}
11
12impl Default for Config {
13 fn default() -> Self {
14 Self {
15 time_interrupt_priority: Priority::from(0),
16 rtc_interrupt_priority: Priority::from(0),
17 adc_interrupt_priority: Priority::from(0),
18 }
19 }
20}
diff --git a/src/gpio.rs b/src/gpio.rs
new file mode 100644
index 000000000..1e7214b28
--- /dev/null
+++ b/src/gpio.rs
@@ -0,0 +1,244 @@
1//! GPIO driver built around a type-erased `Flex` pin, similar to other Embassy HALs.
2//! The exported `Output`/`Input` drivers own a `Flex` so they no longer depend on the
3//! concrete pin type.
4
5use core::marker::PhantomData;
6
7use crate::{pac, pins as pin_config};
8
9/// Logical level for GPIO pins.
10#[derive(Copy, Clone, Eq, PartialEq, Debug)]
11pub enum Level {
12 Low,
13 High,
14}
15
16pub type Gpio = crate::peripherals::GPIO;
17
18/// Type-erased representation of a GPIO pin.
19#[derive(Copy, Clone)]
20pub struct AnyPin {
21 port: u8,
22 pin: u8,
23 gpio: *const pac::gpio0::RegisterBlock,
24}
25
26impl AnyPin {
27 /// Create an `AnyPin` from raw components.
28 pub fn new(port: u8, pin: u8, gpio: *const pac::gpio0::RegisterBlock) -> Self {
29 Self { port, pin, gpio }
30 }
31
32 #[inline(always)]
33 fn mask(&self) -> u32 {
34 1u32 << self.pin
35 }
36
37 #[inline(always)]
38 fn gpio(&self) -> &'static pac::gpio0::RegisterBlock {
39 unsafe { &*self.gpio }
40 }
41
42 #[inline(always)]
43 pub fn port_index(&self) -> u8 {
44 self.port
45 }
46
47 #[inline(always)]
48 pub fn pin_index(&self) -> u8 {
49 self.pin
50 }
51}
52
53/// Type-level trait implemented by concrete pin ZSTs.
54pub trait PinId {
55 fn port_index() -> u8;
56 fn pin_index() -> u8;
57 fn gpio_ptr() -> *const pac::gpio0::RegisterBlock;
58
59 fn set_mux_gpio() {
60 unsafe { pin_config::set_pin_mux_gpio(Self::port_index(), Self::pin_index()) }
61 }
62
63 fn degrade() -> AnyPin {
64 AnyPin::new(Self::port_index(), Self::pin_index(), Self::gpio_ptr())
65 }
66}
67
68pub mod pins {
69 use super::{pac, AnyPin, PinId};
70
71 macro_rules! define_pin {
72 ($Name:ident, $port:literal, $pin:literal, $GpioBlk:ident) => {
73 pub struct $Name;
74 impl super::PinId for $Name {
75 #[inline(always)]
76 fn port_index() -> u8 {
77 $port
78 }
79 #[inline(always)]
80 fn pin_index() -> u8 {
81 $pin
82 }
83 #[inline(always)]
84 fn gpio_ptr() -> *const pac::gpio0::RegisterBlock {
85 pac::$GpioBlk::ptr()
86 }
87 }
88
89 impl $Name {
90 /// Convenience helper to obtain a type-erased handle to this pin.
91 pub fn degrade() -> AnyPin {
92 <Self as PinId>::degrade()
93 }
94
95 pub fn set_mux_gpio() {
96 <Self as PinId>::set_mux_gpio()
97 }
98 }
99 };
100 }
101
102 // Extend this list as more pins are needed.
103 define_pin!(PIO3_18, 3, 18, Gpio3);
104}
105
106/// A flexible pin that can be configured as input or output.
107pub struct Flex<'d> {
108 pin: AnyPin,
109 _marker: PhantomData<&'d mut ()>,
110}
111
112impl<'d> Flex<'d> {
113 pub fn new(pin: AnyPin) -> Self {
114 Self {
115 pin,
116 _marker: PhantomData,
117 }
118 }
119
120 #[inline(always)]
121 fn gpio(&self) -> &'static pac::gpio0::RegisterBlock {
122 self.pin.gpio()
123 }
124
125 #[inline(always)]
126 fn mask(&self) -> u32 {
127 self.pin.mask()
128 }
129
130 pub fn set_as_input(&mut self) {
131 let mask = self.mask();
132 let gpio = self.gpio();
133 gpio.pddr().modify(|r, w| unsafe { w.bits(r.bits() & !mask) });
134 }
135
136 pub fn set_as_output(&mut self) {
137 let mask = self.mask();
138 let gpio = self.gpio();
139 gpio.pddr().modify(|r, w| unsafe { w.bits(r.bits() | mask) });
140 }
141
142 pub fn set_high(&mut self) {
143 self.gpio().psor().write(|w| unsafe { w.bits(self.mask()) });
144 }
145
146 pub fn set_low(&mut self) {
147 self.gpio().pcor().write(|w| unsafe { w.bits(self.mask()) });
148 }
149
150 pub fn set_level(&mut self, level: Level) {
151 match level {
152 Level::High => self.set_high(),
153 Level::Low => self.set_low(),
154 }
155 }
156
157 pub fn toggle(&mut self) {
158 self.gpio().ptor().write(|w| unsafe { w.bits(self.mask()) });
159 }
160
161 pub fn is_high(&self) -> bool {
162 (self.gpio().pdir().read().bits() & self.mask()) != 0
163 }
164
165 pub fn is_low(&self) -> bool {
166 !self.is_high()
167 }
168}
169
170/// GPIO output driver that owns a `Flex` pin.
171pub struct Output<'d> {
172 flex: Flex<'d>,
173}
174
175impl<'d> Output<'d> {
176 pub fn new(pin: AnyPin, initial: Level) -> Self {
177 let mut flex = Flex::new(pin);
178 flex.set_level(initial);
179 flex.set_as_output();
180 Self { flex }
181 }
182
183 #[inline]
184 pub fn set_high(&mut self) {
185 self.flex.set_high();
186 }
187
188 #[inline]
189 pub fn set_low(&mut self) {
190 self.flex.set_low();
191 }
192
193 #[inline]
194 pub fn set_level(&mut self, level: Level) {
195 self.flex.set_level(level);
196 }
197
198 #[inline]
199 pub fn toggle(&mut self) {
200 self.flex.toggle();
201 }
202
203 #[inline]
204 pub fn is_set_high(&self) -> bool {
205 self.flex.is_high()
206 }
207
208 #[inline]
209 pub fn is_set_low(&self) -> bool {
210 !self.is_set_high()
211 }
212
213 /// Expose the inner `Flex` if callers need to reconfigure the pin.
214 pub fn into_flex(self) -> Flex<'d> {
215 self.flex
216 }
217}
218
219/// GPIO input driver that owns a `Flex` pin.
220pub struct Input<'d> {
221 flex: Flex<'d>,
222}
223
224impl<'d> Input<'d> {
225 pub fn new(pin: AnyPin) -> Self {
226 let mut flex = Flex::new(pin);
227 flex.set_as_input();
228 Self { flex }
229 }
230
231 #[inline]
232 pub fn is_high(&self) -> bool {
233 self.flex.is_high()
234 }
235
236 #[inline]
237 pub fn is_low(&self) -> bool {
238 self.flex.is_low()
239 }
240
241 pub fn into_flex(self) -> Flex<'d> {
242 self.flex
243 }
244}
diff --git a/src/interrupt.rs b/src/interrupt.rs
new file mode 100644
index 000000000..09d7acbef
--- /dev/null
+++ b/src/interrupt.rs
@@ -0,0 +1,349 @@
1//! Minimal interrupt helpers mirroring embassy-imxrt style for OS_EVENT and LPUART2.
2//! Type-level interrupt traits and bindings are provided by the
3//! `embassy_hal_internal::interrupt_mod!` macro via the generated module below.
4
5mod generated {
6 embassy_hal_internal::interrupt_mod!(OS_EVENT, LPUART2, RTC, ADC1);
7}
8
9use core::sync::atomic::{AtomicU16, AtomicU32, Ordering};
10
11pub use generated::interrupt::{typelevel, Priority};
12
13use crate::pac::Interrupt;
14
15/// Trait for configuring and controlling interrupts.
16///
17/// This trait provides a consistent interface for interrupt management across
18/// different interrupt sources, similar to embassy-imxrt's InterruptExt.
19pub trait InterruptExt {
20 /// Clear any pending interrupt in NVIC.
21 fn unpend(&self);
22
23 /// Set NVIC priority for this interrupt.
24 fn set_priority(&self, priority: Priority);
25
26 /// Enable this interrupt in NVIC.
27 ///
28 /// # Safety
29 /// This function is unsafe because it can enable interrupts that may not be
30 /// properly configured, potentially leading to undefined behavior.
31 unsafe fn enable(&self);
32
33 /// Disable this interrupt in NVIC.
34 ///
35 /// # Safety
36 /// This function is unsafe because disabling interrupts may leave the system
37 /// in an inconsistent state if the interrupt was expected to fire.
38 unsafe fn disable(&self);
39
40 /// Check if the interrupt is pending in NVIC.
41 fn is_pending(&self) -> bool;
42}
43
44#[derive(Clone, Copy, Debug, Default)]
45pub struct DefaultHandlerSnapshot {
46 pub vector: u16,
47 pub count: u32,
48 pub cfsr: u32,
49 pub hfsr: u32,
50 pub stacked_pc: u32,
51 pub stacked_lr: u32,
52 pub stacked_sp: u32,
53}
54
55static LAST_DEFAULT_VECTOR: AtomicU16 = AtomicU16::new(0);
56static LAST_DEFAULT_COUNT: AtomicU32 = AtomicU32::new(0);
57static LAST_DEFAULT_CFSR: AtomicU32 = AtomicU32::new(0);
58static LAST_DEFAULT_HFSR: AtomicU32 = AtomicU32::new(0);
59static LAST_DEFAULT_PC: AtomicU32 = AtomicU32::new(0);
60static LAST_DEFAULT_LR: AtomicU32 = AtomicU32::new(0);
61static LAST_DEFAULT_SP: AtomicU32 = AtomicU32::new(0);
62
63#[inline]
64pub fn default_handler_snapshot() -> DefaultHandlerSnapshot {
65 DefaultHandlerSnapshot {
66 vector: LAST_DEFAULT_VECTOR.load(Ordering::Relaxed),
67 count: LAST_DEFAULT_COUNT.load(Ordering::Relaxed),
68 cfsr: LAST_DEFAULT_CFSR.load(Ordering::Relaxed),
69 hfsr: LAST_DEFAULT_HFSR.load(Ordering::Relaxed),
70 stacked_pc: LAST_DEFAULT_PC.load(Ordering::Relaxed),
71 stacked_lr: LAST_DEFAULT_LR.load(Ordering::Relaxed),
72 stacked_sp: LAST_DEFAULT_SP.load(Ordering::Relaxed),
73 }
74}
75
76#[inline]
77pub fn clear_default_handler_snapshot() {
78 LAST_DEFAULT_VECTOR.store(0, Ordering::Relaxed);
79 LAST_DEFAULT_COUNT.store(0, Ordering::Relaxed);
80 LAST_DEFAULT_CFSR.store(0, Ordering::Relaxed);
81 LAST_DEFAULT_HFSR.store(0, Ordering::Relaxed);
82 LAST_DEFAULT_PC.store(0, Ordering::Relaxed);
83 LAST_DEFAULT_LR.store(0, Ordering::Relaxed);
84 LAST_DEFAULT_SP.store(0, Ordering::Relaxed);
85}
86
87/// OS_EVENT interrupt helper with methods similar to embassy-imxrt's InterruptExt.
88pub struct OsEvent;
89pub const OS_EVENT: OsEvent = OsEvent;
90
91impl InterruptExt for OsEvent {
92 /// Clear any pending OS_EVENT in NVIC.
93 #[inline]
94 fn unpend(&self) {
95 cortex_m::peripheral::NVIC::unpend(Interrupt::OS_EVENT);
96 }
97
98 /// Set NVIC priority for OS_EVENT.
99 #[inline]
100 fn set_priority(&self, priority: Priority) {
101 unsafe {
102 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
103 nvic.set_priority(Interrupt::OS_EVENT, u8::from(priority));
104 }
105 }
106
107 /// Enable OS_EVENT in NVIC.
108 #[inline]
109 unsafe fn enable(&self) {
110 cortex_m::peripheral::NVIC::unmask(Interrupt::OS_EVENT);
111 }
112
113 /// Disable OS_EVENT in NVIC.
114 #[inline]
115 unsafe fn disable(&self) {
116 cortex_m::peripheral::NVIC::mask(Interrupt::OS_EVENT);
117 }
118
119 /// Check if OS_EVENT is pending in NVIC.
120 #[inline]
121 fn is_pending(&self) -> bool {
122 cortex_m::peripheral::NVIC::is_pending(Interrupt::OS_EVENT)
123 }
124}
125
126impl OsEvent {
127 /// Configure OS_EVENT interrupt for timer operation.
128 /// This sets up the NVIC priority, enables the interrupt, and ensures global interrupts are enabled.
129 /// Also performs a software event to wake any pending WFE.
130 pub fn configure_for_timer(&self, priority: Priority) {
131 // Configure NVIC
132 self.unpend();
133 self.set_priority(priority);
134 unsafe {
135 self.enable();
136 }
137
138 // Ensure global interrupts are enabled in no-reset scenarios (e.g., cargo run)
139 // Debuggers typically perform a reset which leaves PRIMASK=0; cargo run may not.
140 unsafe {
141 cortex_m::interrupt::enable();
142 }
143
144 // Wake any executor WFE that might be sleeping when we armed the first deadline
145 cortex_m::asm::sev();
146 }
147}
148
149/// LPUART2 interrupt helper with methods similar to embassy-imxrt's InterruptExt.
150pub struct Lpuart2;
151pub const LPUART2: Lpuart2 = Lpuart2;
152
153impl InterruptExt for Lpuart2 {
154 /// Clear any pending LPUART2 in NVIC.
155 #[inline]
156 fn unpend(&self) {
157 cortex_m::peripheral::NVIC::unpend(Interrupt::LPUART2);
158 }
159
160 /// Set NVIC priority for LPUART2.
161 #[inline]
162 fn set_priority(&self, priority: Priority) {
163 unsafe {
164 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
165 nvic.set_priority(Interrupt::LPUART2, u8::from(priority));
166 }
167 }
168
169 /// Enable LPUART2 in NVIC.
170 #[inline]
171 unsafe fn enable(&self) {
172 cortex_m::peripheral::NVIC::unmask(Interrupt::LPUART2);
173 }
174
175 /// Disable LPUART2 in NVIC.
176 #[inline]
177 unsafe fn disable(&self) {
178 cortex_m::peripheral::NVIC::mask(Interrupt::LPUART2);
179 }
180
181 /// Check if LPUART2 is pending in NVIC.
182 #[inline]
183 fn is_pending(&self) -> bool {
184 cortex_m::peripheral::NVIC::is_pending(Interrupt::LPUART2)
185 }
186}
187
188impl Lpuart2 {
189 /// Configure LPUART2 interrupt for UART operation.
190 /// This sets up the NVIC priority, enables the interrupt, and ensures global interrupts are enabled.
191 pub fn configure_for_uart(&self, priority: Priority) {
192 // Configure NVIC
193 self.unpend();
194 self.set_priority(priority);
195 unsafe {
196 self.enable();
197 }
198
199 // Ensure global interrupts are enabled in no-reset scenarios (e.g., cargo run)
200 // Debuggers typically perform a reset which leaves PRIMASK=0; cargo run may not.
201 unsafe {
202 cortex_m::interrupt::enable();
203 }
204 }
205
206 /// Install LPUART2 handler into the RAM vector table.
207 /// Safety: See `install_irq_handler`.
208 pub unsafe fn install_handler(&self, handler: unsafe extern "C" fn()) {
209 install_irq_handler(Interrupt::LPUART2, handler);
210 }
211}
212
213pub struct Rtc;
214pub const RTC: Rtc = Rtc;
215
216impl InterruptExt for Rtc {
217 /// Clear any pending RTC in NVIC.
218 #[inline]
219 fn unpend(&self) {
220 cortex_m::peripheral::NVIC::unpend(Interrupt::RTC);
221 }
222
223 /// Set NVIC priority for RTC.
224 #[inline]
225 fn set_priority(&self, priority: Priority) {
226 unsafe {
227 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
228 nvic.set_priority(Interrupt::RTC, u8::from(priority));
229 }
230 }
231
232 /// Enable RTC in NVIC.
233 #[inline]
234 unsafe fn enable(&self) {
235 cortex_m::peripheral::NVIC::unmask(Interrupt::RTC);
236 }
237
238 /// Disable RTC in NVIC.
239 #[inline]
240 unsafe fn disable(&self) {
241 cortex_m::peripheral::NVIC::mask(Interrupt::RTC);
242 }
243
244 /// Check if RTC is pending in NVIC.
245 #[inline]
246 fn is_pending(&self) -> bool {
247 cortex_m::peripheral::NVIC::is_pending(Interrupt::RTC)
248 }
249}
250
251pub struct Adc;
252pub const ADC1: Adc = Adc;
253
254impl InterruptExt for Adc {
255 /// Clear any pending ADC1 in NVIC.
256 #[inline]
257 fn unpend(&self) {
258 cortex_m::peripheral::NVIC::unpend(Interrupt::ADC1);
259 }
260
261 /// Set NVIC priority for ADC1.
262 #[inline]
263 fn set_priority(&self, priority: Priority) {
264 unsafe {
265 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
266 nvic.set_priority(Interrupt::ADC1, u8::from(priority));
267 }
268 }
269
270 /// Enable ADC1 in NVIC.
271 #[inline]
272 unsafe fn enable(&self) {
273 cortex_m::peripheral::NVIC::unmask(Interrupt::ADC1);
274 }
275
276 /// Disable ADC1 in NVIC.
277 #[inline]
278 unsafe fn disable(&self) {
279 cortex_m::peripheral::NVIC::mask(Interrupt::ADC1);
280 }
281
282 /// Check if ADC1 is pending in NVIC.
283 #[inline]
284 fn is_pending(&self) -> bool {
285 cortex_m::peripheral::NVIC::is_pending(Interrupt::ADC1)
286 }
287}
288
289/// Set VTOR (Vector Table Offset) to a RAM-based vector table.
290/// Pass a pointer to the first word in the RAM table (stack pointer slot 0).
291/// Safety: Caller must ensure the RAM table is valid and aligned as required by the core.
292pub unsafe fn vtor_set_ram_vector_base(base: *const u32) {
293 core::ptr::write_volatile(0xE000_ED08 as *mut u32, base as u32);
294}
295
296/// Install an interrupt handler into the current VTOR-based vector table.
297/// This writes the function pointer at index 16 + irq number.
298/// Safety: Caller must ensure VTOR points at a writable RAM table and that `handler`
299/// has the correct ABI and lifetime.
300pub unsafe fn install_irq_handler(irq: Interrupt, handler: unsafe extern "C" fn()) {
301 let vtor_base = core::ptr::read_volatile(0xE000_ED08 as *const u32) as *mut u32;
302 let idx = 16 + (irq as isize as usize);
303 core::ptr::write_volatile(vtor_base.add(idx), handler as usize as u32);
304}
305
306impl OsEvent {
307 /// Convenience to install the OS_EVENT handler into the RAM vector table.
308 /// Safety: See `install_irq_handler`.
309 pub unsafe fn install_handler(&self, handler: extern "C" fn()) {
310 install_irq_handler(Interrupt::OS_EVENT, handler);
311 }
312}
313
314/// Install OS_EVENT handler by raw address. Useful to avoid fn pointer type mismatches.
315/// Safety: Caller must ensure the address is a valid `extern "C" fn()` handler.
316pub unsafe fn os_event_install_handler_raw(handler_addr: usize) {
317 let vtor_base = core::ptr::read_volatile(0xE000_ED08 as *const u32) as *mut u32;
318 let idx = 16 + (Interrupt::OS_EVENT as isize as usize);
319 core::ptr::write_volatile(vtor_base.add(idx), handler_addr as u32);
320}
321
322/// Provide a conservative default IRQ handler that avoids wedging the system.
323/// It clears all NVIC pending bits and returns, so spurious or reserved IRQs
324/// don’t trap the core in an infinite WFI loop during bring-up.
325#[no_mangle]
326pub unsafe extern "C" fn DefaultHandler() {
327 let active = core::ptr::read_volatile(0xE000_ED04 as *const u32) & 0x1FF;
328 let cfsr = core::ptr::read_volatile(0xE000_ED28 as *const u32);
329 let hfsr = core::ptr::read_volatile(0xE000_ED2C as *const u32);
330
331 let sp = cortex_m::register::msp::read();
332 let stacked = sp as *const u32;
333 // Stacked registers follow ARMv8-M procedure call standard order
334 let stacked_pc = unsafe { stacked.add(6).read() };
335 let stacked_lr = unsafe { stacked.add(5).read() };
336
337 LAST_DEFAULT_VECTOR.store(active as u16, Ordering::Relaxed);
338 LAST_DEFAULT_CFSR.store(cfsr, Ordering::Relaxed);
339 LAST_DEFAULT_HFSR.store(hfsr, Ordering::Relaxed);
340 LAST_DEFAULT_COUNT.fetch_add(1, Ordering::Relaxed);
341 LAST_DEFAULT_PC.store(stacked_pc, Ordering::Relaxed);
342 LAST_DEFAULT_LR.store(stacked_lr, Ordering::Relaxed);
343 LAST_DEFAULT_SP.store(sp, Ordering::Relaxed);
344
345 // Do nothing here: on some MCUs/TrustZone setups, writing NVIC from a spurious
346 // handler can fault if targeting the Secure bank. Just return.
347 cortex_m::asm::dsb();
348 cortex_m::asm::isb();
349}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 000000000..fe27aadba
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,142 @@
1#![no_std]
2
3pub mod clocks; // still provide clock helpers
4pub mod gpio;
5pub mod pins; // pin mux helpers
6pub mod reset; // reset control helpers
7
8pub mod adc;
9pub mod config;
10pub mod interrupt;
11pub mod lpuart;
12pub mod ostimer;
13pub mod rtc;
14pub mod uart;
15
16embassy_hal_internal::peripherals!(LPUART2, OSTIMER0, GPIO, RTC0, ADC1,);
17
18/// Get access to the PAC Peripherals for low-level register access.
19/// This is a lazy-initialized singleton that can be called after init().
20#[allow(static_mut_refs)]
21pub fn pac() -> &'static pac::Peripherals {
22 // SAFETY: We only call this after init(), and the PAC is a singleton.
23 // The embassy peripheral tokens ensure we don't have multiple mutable accesses.
24 unsafe {
25 static mut PAC_INSTANCE: Option<pac::Peripherals> = None;
26 if PAC_INSTANCE.is_none() {
27 PAC_INSTANCE = Some(pac::Peripherals::steal());
28 }
29 PAC_INSTANCE.as_ref().unwrap()
30 }
31}
32
33// Use cortex-m-rt's #[interrupt] attribute directly; PAC does not re-export it.
34
35// Re-export interrupt traits and types
36pub use adc::Adc1 as Adc1Token;
37pub use gpio::pins::*;
38pub use gpio::{AnyPin, Flex, Gpio as GpioToken, Input, Level, Output};
39pub use interrupt::InterruptExt;
40#[cfg(feature = "unstable-pac")]
41pub use mcxa_pac as pac;
42#[cfg(not(feature = "unstable-pac"))]
43pub(crate) use mcxa_pac as pac;
44pub use ostimer::Ostimer0 as Ostimer0Token;
45pub use rtc::Rtc0 as Rtc0Token;
46pub use uart::Lpuart2 as Uart2Token;
47
48/// Initialize HAL with configuration (mirrors embassy-imxrt style). Minimal: just take peripherals.
49/// Also applies configurable NVIC priority for the OSTIMER OS_EVENT interrupt (no enabling).
50#[allow(unused_variables)]
51pub fn init(cfg: crate::config::Config) -> Peripherals {
52 let peripherals = Peripherals::take();
53 // Apply user-configured priority early; enabling is left to examples/apps
54 crate::interrupt::OS_EVENT.set_priority(cfg.time_interrupt_priority);
55 // Apply user-configured priority early; enabling is left to examples/apps
56 crate::interrupt::RTC.set_priority(cfg.rtc_interrupt_priority);
57 // Apply user-configured priority early; enabling is left to examples/apps
58 crate::interrupt::ADC1.set_priority(cfg.adc_interrupt_priority);
59 peripherals
60}
61
62/// Optional hook called by cortex-m-rt before RAM init.
63/// We proactively mask and clear all NVIC IRQs to avoid wedges from stale state
64/// left by soft resets/debug sessions.
65///
66/// NOTE: Manual VTOR setup is required for RAM execution. The cortex-m-rt 'set-vtor'
67/// feature is incompatible with our setup because it expects __vector_table to be
68/// defined differently than how our RAM-based linker script arranges it.
69#[no_mangle]
70pub unsafe extern "C" fn __pre_init() {
71 // Set the VTOR to point to the interrupt vector table in RAM
72 // This is required since code runs from RAM on this MCU
73 crate::interrupt::vtor_set_ram_vector_base(0x2000_0000 as *const u32);
74
75 // Mask and clear pending for all NVIC lines (0..127) to avoid stale state across runs.
76 let nvic = &*cortex_m::peripheral::NVIC::PTR;
77 for i in 0..4 {
78 // 4 words x 32 = 128 IRQs
79 nvic.icer[i].write(0xFFFF_FFFF);
80 nvic.icpr[i].write(0xFFFF_FFFF);
81 }
82 // Do NOT touch peripheral registers here: clocks may be off and accesses can fault.
83 crate::interrupt::clear_default_handler_snapshot();
84}
85
86/// Internal helper to dispatch a type-level interrupt handler.
87#[inline(always)]
88#[doc(hidden)]
89pub unsafe fn __handle_interrupt<T, H>()
90where
91 T: crate::interrupt::typelevel::Interrupt,
92 H: crate::interrupt::typelevel::Handler<T>,
93{
94 H::on_interrupt();
95}
96
97/// Macro to bind interrupts to handlers, similar to embassy-imxrt.
98///
99/// Example:
100/// - Bind OS_EVENT to the OSTIMER time-driver handler
101/// bind_interrupts!(struct Irqs { OS_EVENT => crate::ostimer::time_driver::OsEventHandler; });
102#[macro_export]
103macro_rules! bind_interrupts {
104 ($(#[$attr:meta])* $vis:vis struct $name:ident {
105 $(
106 $(#[cfg($cond_irq:meta)])?
107 $irq:ident => $(
108 $(#[cfg($cond_handler:meta)])?
109 $handler:ty
110 ),*;
111 )*
112 }) => {
113 #[derive(Copy, Clone)]
114 $(#[$attr])*
115 $vis struct $name;
116
117 $(
118 #[allow(non_snake_case)]
119 #[no_mangle]
120 $(#[cfg($cond_irq)])?
121 unsafe extern "C" fn $irq() {
122 unsafe {
123 $(
124 $(#[cfg($cond_handler)])?
125 <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt();
126 )*
127 }
128 }
129
130 $(#[cfg($cond_irq)])?
131 $crate::bind_interrupts!(@inner
132 $(
133 $(#[cfg($cond_handler)])?
134 unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {}
135 )*
136 );
137 )*
138 };
139 (@inner $($t:tt)*) => {
140 $($t)*
141 }
142}
diff --git a/src/lpuart/buffered.rs b/src/lpuart/buffered.rs
new file mode 100644
index 000000000..0413fed8e
--- /dev/null
+++ b/src/lpuart/buffered.rs
@@ -0,0 +1,675 @@
1use core::future::poll_fn;
2use core::marker::PhantomData;
3use core::sync::atomic::{AtomicBool, Ordering};
4use core::task::Poll;
5
6use embassy_hal_internal::atomic_ring_buffer::RingBuffer;
7use embassy_hal_internal::Peri;
8use embassy_sync::waitqueue::AtomicWaker;
9
10use super::*;
11use crate::interrupt;
12
13// ============================================================================
14// STATIC STATE MANAGEMENT
15// ============================================================================
16
17/// State for buffered LPUART operations
18pub struct State {
19 rx_waker: AtomicWaker,
20 rx_buf: RingBuffer,
21 tx_waker: AtomicWaker,
22 tx_buf: RingBuffer,
23 tx_done: AtomicBool,
24 initialized: AtomicBool,
25}
26
27impl State {
28 /// Create a new state instance
29 pub const fn new() -> Self {
30 Self {
31 rx_waker: AtomicWaker::new(),
32 rx_buf: RingBuffer::new(),
33 tx_waker: AtomicWaker::new(),
34 tx_buf: RingBuffer::new(),
35 tx_done: AtomicBool::new(true),
36 initialized: AtomicBool::new(false),
37 }
38 }
39}
40// ============================================================================
41// BUFFERED DRIVER STRUCTURES
42// ============================================================================
43
44/// Buffered LPUART driver
45pub struct BufferedLpuart<'a> {
46 tx: BufferedLpuartTx<'a>,
47 rx: BufferedLpuartRx<'a>,
48}
49
50/// Buffered LPUART TX driver
51pub struct BufferedLpuartTx<'a> {
52 info: Info,
53 state: &'static State,
54 _tx_pin: Peri<'a, AnyPin>,
55}
56
57/// Buffered LPUART RX driver
58pub struct BufferedLpuartRx<'a> {
59 info: Info,
60 state: &'static State,
61 _rx_pin: Peri<'a, AnyPin>,
62}
63
64// ============================================================================
65// BUFFERED LPUART IMPLEMENTATION
66// ============================================================================
67
68impl<'a> BufferedLpuart<'a> {
69 /// Create a new buffered LPUART instance
70 pub fn new<T: Instance>(
71 _inner: Peri<'a, T>,
72 tx_pin: Peri<'a, impl TxPin<T>>,
73 rx_pin: Peri<'a, impl RxPin<T>>,
74 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
75 tx_buffer: &'a mut [u8],
76 rx_buffer: &'a mut [u8],
77 config: Config,
78 ) -> Result<Self> {
79 // Configure pins
80 tx_pin.as_tx();
81 rx_pin.as_rx();
82
83 // Convert pins to AnyPin
84 let tx_pin: Peri<'a, AnyPin> = tx_pin.into();
85 let rx_pin: Peri<'a, AnyPin> = rx_pin.into();
86
87 let state = T::buffered_state();
88
89 // Initialize the peripheral
90 Self::init::<T>(Some(&tx_pin), Some(&rx_pin), None, None, tx_buffer, rx_buffer, config)?;
91
92 Ok(Self {
93 tx: BufferedLpuartTx {
94 info: T::info(),
95 state,
96 _tx_pin: tx_pin,
97 },
98 rx: BufferedLpuartRx {
99 info: T::info(),
100 state,
101 _rx_pin: rx_pin,
102 },
103 })
104 }
105
106 /// Create a new buffered LPUART with flexible pin configuration
107 pub fn new_with_pins<T: Instance>(
108 _inner: Peri<'a, T>,
109 tx_pin: Option<Peri<'a, impl TxPin<T>>>,
110 rx_pin: Option<Peri<'a, impl RxPin<T>>>,
111 rts_pin: Option<Peri<'a, impl RtsPin<T>>>,
112 cts_pin: Option<Peri<'a, impl CtsPin<T>>>,
113 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
114 tx_buffer: &'a mut [u8],
115 rx_buffer: &'a mut [u8],
116 config: Config,
117 ) -> Result<Self> {
118 // Configure pins if provided
119 let tx_pin = tx_pin.map(|pin| {
120 pin.as_tx();
121 let converted: Peri<'a, AnyPin> = pin.into();
122 converted
123 });
124
125 let rx_pin = rx_pin.map(|pin| {
126 pin.as_rx();
127 let converted: Peri<'a, AnyPin> = pin.into();
128 converted
129 });
130
131 let rts_pin = rts_pin.map(|pin| {
132 pin.as_rts();
133 let converted: Peri<'a, AnyPin> = pin.into();
134 converted
135 });
136
137 let cts_pin = cts_pin.map(|pin| {
138 pin.as_cts();
139 let converted: Peri<'a, AnyPin> = pin.into();
140 converted
141 });
142
143 let state = T::buffered_state();
144
145 // Initialize the peripheral
146 Self::init::<T>(
147 tx_pin.as_ref(),
148 rx_pin.as_ref(),
149 rts_pin.as_ref(),
150 cts_pin.as_ref(),
151 tx_buffer,
152 rx_buffer,
153 config,
154 )?;
155
156 // Create TX and RX instances
157 let (tx, rx) = if let (Some(tx_pin), Some(rx_pin)) = (tx_pin, rx_pin) {
158 (
159 BufferedLpuartTx {
160 info: T::info(),
161 state,
162 _tx_pin: tx_pin,
163 },
164 BufferedLpuartRx {
165 info: T::info(),
166 state,
167 _rx_pin: rx_pin,
168 },
169 )
170 } else {
171 return Err(Error::InvalidArgument);
172 };
173
174 Ok(Self { tx, rx })
175 }
176
177 fn init<T: Instance>(
178 _tx: Option<&Peri<'a, AnyPin>>,
179 _rx: Option<&Peri<'a, AnyPin>>,
180 _rts: Option<&Peri<'a, AnyPin>>,
181 _cts: Option<&Peri<'a, AnyPin>>,
182 tx_buffer: &'a mut [u8],
183 rx_buffer: &'a mut [u8],
184 mut config: Config,
185 ) -> Result<()> {
186 let regs = T::info().regs;
187 let state = T::buffered_state();
188
189 // Check if already initialized
190 if state.initialized.load(Ordering::Relaxed) {
191 return Err(Error::InvalidArgument);
192 }
193
194 // Initialize ring buffers
195 assert!(!tx_buffer.is_empty());
196 unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), tx_buffer.len()) }
197
198 assert!(!rx_buffer.is_empty());
199 unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), rx_buffer.len()) }
200
201 // Mark as initialized
202 state.initialized.store(true, Ordering::Relaxed);
203
204 // Enable TX and RX for buffered operation
205 config.enable_tx = true;
206 config.enable_rx = true;
207
208 // Perform standard initialization
209 perform_software_reset(regs);
210 disable_transceiver(regs);
211 configure_baudrate(regs, config.baudrate_bps, config.clock)?;
212 configure_frame_format(regs, &config);
213 configure_control_settings(regs, &config);
214 configure_fifo(regs, &config);
215 clear_all_status_flags(regs);
216 configure_flow_control(regs, &config);
217 configure_bit_order(regs, config.msb_firs);
218
219 // Enable interrupts for buffered operation
220 cortex_m::interrupt::free(|_| {
221 regs.ctrl().modify(|_, w| {
222 w.rie()
223 .enabled() // RX interrupt
224 .orie()
225 .enabled() // Overrun interrupt
226 .peie()
227 .enabled() // Parity error interrupt
228 .feie()
229 .enabled() // Framing error interrupt
230 .neie()
231 .enabled() // Noise error interrupt
232 });
233 });
234
235 // Enable the transceiver
236 enable_transceiver(regs, config.enable_tx, config.enable_rx);
237
238 // Enable the interrupt
239 // unsafe {
240 // // TODO: Used the propper interrupt enable method for the specific LPUART instance
241 // // T::Interrupt::enable();
242 // }
243
244 Ok(())
245 }
246
247 /// Split the buffered LPUART into separate TX and RX parts
248 pub fn split(self) -> (BufferedLpuartTx<'a>, BufferedLpuartRx<'a>) {
249 (self.tx, self.rx)
250 }
251
252 /// Get mutable references to TX and RX parts
253 pub fn split_ref(&mut self) -> (&mut BufferedLpuartTx<'a>, &mut BufferedLpuartRx<'a>) {
254 (&mut self.tx, &mut self.rx)
255 }
256}
257
258// ============================================================================
259// BUFFERED TX IMPLEMENTATION
260// ============================================================================
261
262impl<'a> BufferedLpuartTx<'a> {
263 /// Create a new TX-only buffered LPUART
264 pub fn new<T: Instance>(
265 _inner: Peri<'a, T>,
266 tx_pin: Peri<'a, impl TxPin<T>>,
267 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
268 tx_buffer: &'a mut [u8],
269 config: Config,
270 ) -> Result<Self> {
271 tx_pin.as_tx();
272 let tx_pin: Peri<'a, AnyPin> = tx_pin.into();
273
274 let info = T::info();
275 let state = T::buffered_state();
276
277 // Check if already initialized
278 if state.initialized.load(Ordering::Relaxed) {
279 return Err(Error::InvalidArgument);
280 }
281
282 // Initialize TX ring buffer only
283 unsafe {
284 let tx_buf = &state.tx_buf as *const _ as *mut RingBuffer;
285 (*tx_buf).init(tx_buffer.as_mut_ptr(), tx_buffer.len());
286 }
287
288 state.initialized.store(true, Ordering::Relaxed);
289
290 // Initialize with TX only
291 BufferedLpuart::init::<T>(
292 Some(&tx_pin),
293 None,
294 None,
295 None,
296 tx_buffer,
297 &mut [], // Empty RX buffer
298 config,
299 )?;
300
301 Ok(Self {
302 info,
303 state,
304 _tx_pin: tx_pin,
305 })
306 }
307
308 /// Write data asynchronously
309 pub async fn write(&mut self, buf: &[u8]) -> Result<usize> {
310 let mut written = 0;
311
312 for &byte in buf {
313 // Wait for space in the buffer
314 poll_fn(|cx| {
315 self.state.tx_waker.register(cx.waker());
316
317 let mut writer = unsafe { self.state.tx_buf.writer() };
318 if writer.push_one(byte) {
319 // Enable TX interrupt to start transmission
320 cortex_m::interrupt::free(|_| {
321 self.info.regs.ctrl().modify(|_, w| w.tie().enabled());
322 });
323 Poll::Ready(Ok(()))
324 } else {
325 Poll::Pending
326 }
327 })
328 .await?;
329
330 written += 1;
331 }
332
333 Ok(written)
334 }
335
336 /// Flush the TX buffer and wait for transmission to complete
337 pub async fn flush(&mut self) -> Result<()> {
338 // Wait for TX buffer to empty and transmission to complete
339 poll_fn(|cx| {
340 self.state.tx_waker.register(cx.waker());
341
342 let tx_empty = self.state.tx_buf.is_empty();
343 let fifo_empty = self.info.regs.water().read().txcount().bits() == 0;
344 let tc_complete = self.info.regs.stat().read().tc().is_complete();
345
346 if tx_empty && fifo_empty && tc_complete {
347 Poll::Ready(Ok(()))
348 } else {
349 // Enable appropriate interrupt
350 cortex_m::interrupt::free(|_| {
351 if !tx_empty {
352 self.info.regs.ctrl().modify(|_, w| w.tie().enabled());
353 } else {
354 self.info.regs.ctrl().modify(|_, w| w.tcie().enabled());
355 }
356 });
357 Poll::Pending
358 }
359 })
360 .await
361 }
362
363 /// Try to write without blocking
364 pub fn try_write(&mut self, buf: &[u8]) -> Result<usize> {
365 let mut writer = unsafe { self.state.tx_buf.writer() };
366 let mut written = 0;
367
368 for &byte in buf {
369 if writer.push_one(byte) {
370 written += 1;
371 } else {
372 break;
373 }
374 }
375
376 if written > 0 {
377 // Enable TX interrupt to start transmission
378 cortex_m::interrupt::free(|_| {
379 self.info.regs.ctrl().modify(|_, w| w.tie().enabled());
380 });
381 }
382
383 Ok(written)
384 }
385}
386
387// ============================================================================
388// BUFFERED RX IMPLEMENTATION
389// ============================================================================
390
391impl<'a> BufferedLpuartRx<'a> {
392 /// Create a new RX-only buffered LPUART
393 pub fn new<T: Instance>(
394 _inner: Peri<'a, T>,
395 rx_pin: Peri<'a, impl RxPin<T>>,
396 _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,
397 rx_buffer: &'a mut [u8],
398 config: Config,
399 ) -> Result<Self> {
400 rx_pin.as_rx();
401 let rx_pin: Peri<'a, AnyPin> = rx_pin.into();
402
403 let info = T::info();
404 let state = T::buffered_state();
405
406 // Check if already initialized
407 if state.initialized.load(Ordering::Relaxed) {
408 return Err(Error::InvalidArgument);
409 }
410
411 // Initialize RX ring buffer only
412 unsafe {
413 let rx_buf = &state.rx_buf as *const _ as *mut RingBuffer;
414 (*rx_buf).init(rx_buffer.as_mut_ptr(), rx_buffer.len());
415 }
416
417 state.initialized.store(true, Ordering::Relaxed);
418
419 // Initialize with RX only
420 BufferedLpuart::init::<T>(
421 None,
422 Some(&rx_pin),
423 None,
424 None,
425 &mut [], // Empty TX buffer
426 rx_buffer,
427 config,
428 )?;
429
430 Ok(Self {
431 info,
432 state,
433 _rx_pin: rx_pin,
434 })
435 }
436
437 /// Read data asynchronously
438 pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
439 if buf.is_empty() {
440 return Ok(0);
441 }
442
443 let mut read = 0;
444
445 // Try to read available data
446 poll_fn(|cx| {
447 self.state.rx_waker.register(cx.waker());
448
449 // Disable RX interrupt while reading from buffer
450 cortex_m::interrupt::free(|_| {
451 self.info.regs.ctrl().modify(|_, w| w.rie().disabled());
452 });
453
454 let mut reader = unsafe { self.state.rx_buf.reader() };
455 let available = reader.pop(|data| {
456 let to_copy = core::cmp::min(data.len(), buf.len() - read);
457 if to_copy > 0 {
458 buf[read..read + to_copy].copy_from_slice(&data[..to_copy]);
459 read += to_copy;
460 }
461 to_copy
462 });
463
464 // Re-enable RX interrupt
465 cortex_m::interrupt::free(|_| {
466 self.info.regs.ctrl().modify(|_, w| w.rie().enabled());
467 });
468
469 if read > 0 {
470 Poll::Ready(Ok(read))
471 } else if available == 0 {
472 Poll::Pending
473 } else {
474 Poll::Ready(Ok(0))
475 }
476 })
477 .await
478 }
479
480 /// Try to read without blocking
481 pub fn try_read(&mut self, buf: &mut [u8]) -> Result<usize> {
482 if buf.is_empty() {
483 return Ok(0);
484 }
485
486 // Disable RX interrupt while reading from buffer
487 cortex_m::interrupt::free(|_| {
488 self.info.regs.ctrl().modify(|_, w| w.rie().disabled());
489 });
490
491 let mut reader = unsafe { self.state.rx_buf.reader() };
492 let read = reader.pop(|data| {
493 let to_copy = core::cmp::min(data.len(), buf.len());
494 if to_copy > 0 {
495 buf[..to_copy].copy_from_slice(&data[..to_copy]);
496 }
497 to_copy
498 });
499
500 // Re-enable RX interrupt
501 cortex_m::interrupt::free(|_| {
502 self.info.regs.ctrl().modify(|_, w| w.rie().enabled());
503 });
504
505 Ok(read)
506 }
507}
508
509// ============================================================================
510// INTERRUPT HANDLER
511// ============================================================================
512
513/// Buffered UART interrupt handler
514pub struct BufferedInterruptHandler<T: Instance> {
515 _phantom: PhantomData<T>,
516}
517
518impl<T: Instance> crate::interrupt::typelevel::Handler<T::Interrupt> for BufferedInterruptHandler<T> {
519 unsafe fn on_interrupt() {
520 let regs = T::info().regs;
521 let state = T::buffered_state();
522
523 // Check if this instance is initialized
524 if !state.initialized.load(Ordering::Relaxed) {
525 return;
526 }
527
528 let ctrl = regs.ctrl().read();
529 let stat = regs.stat().read();
530 let has_fifo = regs.param().read().rxfifo().bits() > 0;
531
532 // Handle overrun error
533 if stat.or().is_overrun() {
534 regs.stat().write(|w| w.or().clear_bit_by_one());
535 state.rx_waker.wake();
536 return;
537 }
538
539 // Clear other error flags
540 if stat.pf().is_parity() {
541 regs.stat().write(|w| w.pf().clear_bit_by_one());
542 }
543 if stat.fe().is_error() {
544 regs.stat().write(|w| w.fe().clear_bit_by_one());
545 }
546 if stat.nf().is_noise() {
547 regs.stat().write(|w| w.nf().clear_bit_by_one());
548 }
549
550 // Handle RX data
551 if ctrl.rie().is_enabled() && (has_data(regs) || stat.idle().is_idle()) {
552 let mut pushed_any = false;
553 let mut writer = state.rx_buf.writer();
554
555 if has_fifo {
556 // Read from FIFO
557 while regs.water().read().rxcount().bits() > 0 {
558 let byte = (regs.data().read().bits() & 0xFF) as u8;
559 if writer.push_one(byte) {
560 pushed_any = true;
561 } else {
562 // Buffer full, stop reading
563 break;
564 }
565 }
566 } else {
567 // Read single byte
568 if regs.stat().read().rdrf().is_rxdata() {
569 let byte = (regs.data().read().bits() & 0xFF) as u8;
570 if writer.push_one(byte) {
571 pushed_any = true;
572 }
573 }
574 }
575
576 if pushed_any {
577 state.rx_waker.wake();
578 }
579
580 // Clear idle flag if set
581 if stat.idle().is_idle() {
582 regs.stat().write(|w| w.idle().clear_bit_by_one());
583 }
584 }
585
586 // Handle TX data
587 if ctrl.tie().is_enabled() {
588 let mut sent_any = false;
589 let mut reader = state.tx_buf.reader();
590
591 // Send data while TX buffer is ready and we have data
592 while regs.stat().read().tdre().is_no_txdata() {
593 if let Some(byte) = reader.pop_one() {
594 regs.data().write(|w| w.bits(u32::from(byte)));
595 sent_any = true;
596 } else {
597 // No more data to send
598 break;
599 }
600 }
601
602 if sent_any {
603 state.tx_waker.wake();
604 }
605
606 // If buffer is empty, switch to TC interrupt or disable
607 if state.tx_buf.is_empty() {
608 cortex_m::interrupt::free(|_| {
609 regs.ctrl().modify(|_, w| w.tie().disabled().tcie().enabled());
610 });
611 }
612 }
613
614 // Handle transmission complete
615 if ctrl.tcie().is_enabled() {
616 if regs.stat().read().tc().is_complete() {
617 state.tx_done.store(true, Ordering::Release);
618 state.tx_waker.wake();
619
620 // Disable TC interrupt
621 cortex_m::interrupt::free(|_| {
622 regs.ctrl().modify(|_, w| w.tcie().disabled());
623 });
624 }
625 }
626 }
627}
628
629// ============================================================================
630// EMBEDDED-IO ASYNC TRAIT IMPLEMENTATIONS
631// ============================================================================
632
633impl embedded_io_async::ErrorType for BufferedLpuartTx<'_> {
634 type Error = Error;
635}
636
637impl embedded_io_async::ErrorType for BufferedLpuartRx<'_> {
638 type Error = Error;
639}
640
641impl embedded_io_async::ErrorType for BufferedLpuart<'_> {
642 type Error = Error;
643}
644
645impl embedded_io_async::Write for BufferedLpuartTx<'_> {
646 async fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> {
647 self.write(buf).await
648 }
649
650 async fn flush(&mut self) -> core::result::Result<(), Self::Error> {
651 self.flush().await
652 }
653}
654
655impl embedded_io_async::Read for BufferedLpuartRx<'_> {
656 async fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
657 self.read(buf).await
658 }
659}
660
661impl embedded_io_async::Write for BufferedLpuart<'_> {
662 async fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> {
663 self.tx.write(buf).await
664 }
665
666 async fn flush(&mut self) -> core::result::Result<(), Self::Error> {
667 self.tx.flush().await
668 }
669}
670
671impl embedded_io_async::Read for BufferedLpuart<'_> {
672 async fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
673 self.rx.read(buf).await
674 }
675}
diff --git a/src/lpuart/mod.rs b/src/lpuart/mod.rs
new file mode 100644
index 000000000..bed10bdb0
--- /dev/null
+++ b/src/lpuart/mod.rs
@@ -0,0 +1,1201 @@
1use core::marker::PhantomData;
2
3use embassy_hal_internal::{Peri, PeripheralType};
4use paste::paste;
5
6use crate::pac::lpuart0::baud::Sbns as StopBits;
7use crate::pac::lpuart0::ctrl::{Idlecfg as IdleConfig, Ilt as IdleType, Pt as Parity, M as DataBits};
8use crate::pac::lpuart0::modir::{Txctsc as TxCtsConfig, Txctssrc as TxCtsSource};
9use crate::pac::lpuart0::stat::Msbf as MsbFirst;
10use crate::{interrupt, pac};
11
12pub mod buffered;
13
14// ============================================================================
15// STUB IMPLEMENTATION
16// ============================================================================
17
18// Stub implementation for LIB (Peripherals), GPIO, DMA and CLOCK until stable API
19// Pin and Clock initialization is currently done at the examples level.
20
21// --- START LIB ---
22
23// Use our own instance of Peripherals, until we align `lib.rs` with the EMBASSY-IMXRT approach
24// Inlined peripherals_definition! to bypass the `Peripherals::take_with_cs()` check
25// SHOULD NOT BE USED IN THE FINAL VERSION
26pub mod lib {
27 // embassy_hal_internal::peripherals!(LPUART2, PIO2_2, PIO2_3)
28
29 embassy_hal_internal::peripherals_definition!(LPUART2, PIO2_2, PIO2_3,);
30 #[doc = r" Struct containing all the peripheral singletons."]
31 #[doc = r""]
32 #[doc = r" To obtain the peripherals, you must initialize the HAL, by calling [`crate::init`]."]
33 #[allow(non_snake_case)]
34 pub struct Peripherals {
35 #[doc = concat!(stringify!(LPUART2)," peripheral")]
36 pub LPUART2: embassy_hal_internal::Peri<'static, peripherals::LPUART2>,
37 #[doc = concat!(stringify!(PIO2_2)," peripheral")]
38 pub PIO2_2: embassy_hal_internal::Peri<'static, peripherals::PIO2_2>,
39 #[doc = concat!(stringify!(PIO2_3)," peripheral")]
40 pub PIO2_3: embassy_hal_internal::Peri<'static, peripherals::PIO2_3>,
41 }
42 impl Peripherals {
43 #[doc = r"Returns all the peripherals *once*"]
44 #[inline]
45 pub(crate) fn take() -> Self {
46 critical_section::with(Self::take_with_cs)
47 }
48 #[doc = r"Returns all the peripherals *once*"]
49 #[inline]
50 pub(crate) fn take_with_cs(_cs: critical_section::CriticalSection) -> Self {
51 #[no_mangle]
52 static mut _EMBASSY_DEVICE_PERIPHERALS2: bool = false; // ALIGN: Temporary fix to use stub Peripherals
53 unsafe {
54 if _EMBASSY_DEVICE_PERIPHERALS2 {
55 panic!("init called more than once!")
56 }
57 _EMBASSY_DEVICE_PERIPHERALS2 = true;
58 Self::steal()
59 }
60 }
61 }
62 impl Peripherals {
63 #[doc = r" Unsafely create an instance of this peripheral out of thin air."]
64 #[doc = r""]
65 #[doc = r" # Safety"]
66 #[doc = r""]
67 #[doc = r" You must ensure that you're only using one instance of this type at a time."]
68 #[inline]
69 pub unsafe fn steal() -> Self {
70 Self {
71 LPUART2: peripherals::LPUART2::steal(),
72 PIO2_2: peripherals::PIO2_2::steal(),
73 PIO2_3: peripherals::PIO2_3::steal(),
74 }
75 }
76 }
77
78 /// Initialize HAL
79 pub fn init() -> Peripherals {
80 Peripherals::take()
81 }
82}
83
84// --- END LIB ---
85
86// --- START GPIO ---
87
88mod gpio {
89 use embassy_hal_internal::PeripheralType;
90 trait SealedPin {}
91
92 #[allow(private_bounds)]
93 pub trait GpioPin: SealedPin + Sized + PeripheralType + Into<AnyPin> + 'static {
94 /// Type-erase the pin.
95 fn degrade(self) -> AnyPin {
96 todo!()
97 }
98 }
99
100 // Add this macro to implement GpioPin for all pins
101 macro_rules! impl_gpio_pin {
102 ($($pin:ident),*) => {
103 $(
104 impl SealedPin for super::lib::peripherals::$pin {}
105
106 impl GpioPin for super::lib::peripherals::$pin {}
107
108 impl Into<AnyPin> for super::lib::peripherals::$pin {
109 fn into(self) -> AnyPin {
110 AnyPin
111 }
112 }
113 )*
114 };
115 }
116
117 // Implement GpioPin for all pins from lib.rs
118 impl_gpio_pin!(PIO2_2, PIO2_3);
119
120 #[derive(Debug, Clone, Copy)]
121 pub struct AnyPin;
122
123 impl PeripheralType for AnyPin {}
124
125 pub enum Alt {
126 ALT3,
127 }
128}
129
130use gpio::{AnyPin, GpioPin as Pin};
131
132// --- END GPIO ---
133
134// --- START DMA ---
135mod dma {
136 pub struct Channel<'d> {
137 pub(super) _lifetime: core::marker::PhantomData<&'d ()>,
138 }
139}
140
141use dma::Channel;
142
143// --- END DMA ---
144
145// --- START CLOCK ---
146mod clock {
147 #[derive(Debug, Clone, Copy)]
148 pub enum Clock {
149 FroLf, // Low-Frequency Free-Running Oscillator
150 }
151}
152
153use clock::Clock;
154
155// --- END CLOCK ---
156
157// ============================================================================
158// MISC
159// ============================================================================
160
161mod sealed {
162 /// Simply seal a trait to prevent external implementations
163 pub trait Sealed {}
164}
165
166// ============================================================================
167// INSTANCE TRAIT
168// ============================================================================
169
170pub type Regs = &'static crate::pac::lpuart0::RegisterBlock;
171
172pub trait SealedInstance {
173 fn info() -> Info;
174 fn index() -> usize;
175 fn buffered_state() -> &'static buffered::State;
176}
177
178pub struct Info {
179 pub regs: Regs,
180}
181
182/// Trait for LPUART peripheral instances
183#[allow(private_bounds)]
184pub trait Instance: SealedInstance + PeripheralType + 'static + Send {
185 type Interrupt: interrupt::typelevel::Interrupt;
186}
187
188macro_rules! impl_instance {
189 ($($n:expr),*) => {
190 $(
191 paste!{
192 impl SealedInstance for lib::peripherals::[<LPUART $n>] {
193 fn info() -> Info {
194 Info {
195 regs: unsafe { &*pac::[<Lpuart $n>]::ptr() },
196 }
197 }
198
199 #[inline]
200 fn index() -> usize {
201 $n
202 }
203
204 fn buffered_state() -> &'static buffered::State {
205 static BUFFERED_STATE: buffered::State = buffered::State::new();
206 &BUFFERED_STATE
207 }
208 }
209
210 impl Instance for lib::peripherals::[<LPUART $n>] {
211 type Interrupt = crate::interrupt::typelevel::[<LPUART $n>];
212 }
213 }
214 )*
215 };
216}
217
218// impl_instance!(0, 1, 2, 3, 4);
219impl_instance!(2);
220
221// ============================================================================
222// INSTANCE HELPER FUNCTIONS
223// ============================================================================
224
225/// Perform software reset on the LPUART peripheral
226pub fn perform_software_reset(regs: Regs) {
227 // Software reset - set and clear RST bit (Global register)
228 regs.global().write(|w| w.rst().reset());
229 regs.global().write(|w| w.rst().no_effect());
230}
231
232/// Disable both transmitter and receiver
233pub fn disable_transceiver(regs: Regs) {
234 regs.ctrl().modify(|_, w| w.te().disabled().re().disabled());
235}
236
237/// Calculate and configure baudrate settings
238pub fn configure_baudrate(regs: Regs, baudrate_bps: u32, clock: Clock) -> Result<()> {
239 let clock_freq = get_fc_freq(clock)?;
240 let (osr, sbr) = calculate_baudrate(baudrate_bps, clock_freq)?;
241
242 // Configure BAUD register
243 regs.baud().modify(|_, w| unsafe {
244 // Clear and set OSR
245 w.osr().bits((osr - 1) as u8);
246 // Clear and set SBR
247 w.sbr().bits(sbr);
248 // Set BOTHEDGE if OSR is between 4 and 7
249 if osr > 3 && osr < 8 {
250 w.bothedge().enabled()
251 } else {
252 w.bothedge().disabled()
253 }
254 });
255
256 Ok(())
257}
258
259/// Configure frame format (stop bits, data bits)
260pub fn configure_frame_format(regs: Regs, config: &Config) {
261 // Configure stop bits
262 regs.baud().modify(|_, w| w.sbns().variant(config.stop_bits_count));
263
264 // Clear M10 for now (10-bit mode)
265 regs.baud().modify(|_, w| w.m10().disabled());
266}
267
268/// Configure control settings (parity, data bits, idle config, pin swap)
269pub fn configure_control_settings(regs: Regs, config: &Config) {
270 regs.ctrl().modify(|_, w| {
271 // Parity configuration
272 let mut w = if let Some(parity) = config.parity_mode {
273 w.pe().enabled().pt().variant(parity)
274 } else {
275 w.pe().disabled()
276 };
277
278 // Data bits configuration
279 w = match config.data_bits_count {
280 DataBits::Data8 => {
281 if config.parity_mode.is_some() {
282 w.m().data9() // 8 data + 1 parity = 9 bits
283 } else {
284 w.m().data8() // 8 data bits only
285 }
286 }
287 DataBits::Data9 => w.m().data9(),
288 };
289
290 // Idle configuration
291 w = w.idlecfg().variant(config.rx_idle_config);
292 w = w.ilt().variant(config.rx_idle_type);
293
294 // Swap TXD/RXD if configured
295 if config.swap_txd_rxd {
296 w.swap().swap()
297 } else {
298 w.swap().standard()
299 }
300 });
301}
302
303/// Configure FIFO settings and watermarks
304pub fn configure_fifo(regs: Regs, config: &Config) {
305 // Configure WATER register for FIFO watermarks
306 regs.water().write(|w| unsafe {
307 w.rxwater()
308 .bits(config.rx_fifo_watermark as u8)
309 .txwater()
310 .bits(config.tx_fifo_watermark as u8)
311 });
312
313 // Enable TX/RX FIFOs
314 regs.fifo().modify(|_, w| w.txfe().enabled().rxfe().enabled());
315
316 // Flush FIFOs
317 regs.fifo()
318 .modify(|_, w| w.txflush().txfifo_rst().rxflush().rxfifo_rst());
319}
320
321/// Clear all status flags
322pub fn clear_all_status_flags(regs: Regs) {
323 regs.stat().reset();
324}
325
326/// Configure hardware flow control if enabled
327pub fn configure_flow_control(regs: Regs, config: &Config) {
328 if config.enable_rx_rts || config.enable_tx_cts {
329 regs.modir().modify(|_, w| {
330 let mut w = w;
331
332 // Configure TX CTS
333 w = w.txctsc().variant(config.tx_cts_config);
334 w = w.txctssrc().variant(config.tx_cts_source);
335
336 if config.enable_rx_rts {
337 w = w.rxrtse().enabled();
338 } else {
339 w = w.rxrtse().disabled();
340 }
341
342 if config.enable_tx_cts {
343 w = w.txctse().enabled();
344 } else {
345 w = w.txctse().disabled();
346 }
347
348 w
349 });
350 }
351}
352
353/// Configure bit order (MSB first or LSB first)
354pub fn configure_bit_order(regs: Regs, msb_first: MsbFirst) {
355 regs.stat().modify(|_, w| w.msbf().variant(msb_first));
356}
357
358/// Enable transmitter and/or receiver based on configuration
359pub fn enable_transceiver(regs: Regs, enable_tx: bool, enable_rx: bool) {
360 regs.ctrl().modify(|_, w| {
361 let mut w = w;
362 if enable_tx {
363 w = w.te().enabled();
364 }
365 if enable_rx {
366 w = w.re().enabled();
367 }
368 w
369 });
370}
371
372pub fn calculate_baudrate(baudrate: u32, src_clock_hz: u32) -> Result<(u8, u16)> {
373 let mut baud_diff = baudrate;
374 let mut osr = 0u8;
375 let mut sbr = 0u16;
376
377 // Try OSR values from 4 to 32
378 for osr_temp in 4u8..=32u8 {
379 // Calculate SBR: (srcClock_Hz * 2 / (baudRate * osr) + 1) / 2
380 let sbr_calc = ((src_clock_hz * 2) / (baudrate * osr_temp as u32) + 1) / 2;
381
382 let sbr_temp = if sbr_calc == 0 {
383 1
384 } else if sbr_calc > 0x1FFF {
385 0x1FFF
386 } else {
387 sbr_calc as u16
388 };
389
390 // Calculate actual baud rate
391 let calculated_baud = src_clock_hz / (osr_temp as u32 * sbr_temp as u32);
392
393 let temp_diff = if calculated_baud > baudrate {
394 calculated_baud - baudrate
395 } else {
396 baudrate - calculated_baud
397 };
398
399 if temp_diff <= baud_diff {
400 baud_diff = temp_diff;
401 osr = osr_temp;
402 sbr = sbr_temp;
403 }
404 }
405
406 // Check if baud rate difference is within 3%
407 if baud_diff > (baudrate / 100) * 3 {
408 return Err(Error::UnsupportedBaudrate);
409 }
410
411 Ok((osr, sbr))
412}
413
414pub fn get_fc_freq(clock: Clock) -> Result<u32> {
415 // This is a placeholder - actual implementation would query the clock system
416 // In real implementation, this would get the LPUART clock frequency
417 match clock {
418 Clock::FroLf => Ok(12_000_000), // Low frequency oscillator
419 #[allow(unreachable_patterns)]
420 _ => Err(Error::InvalidArgument),
421 }
422}
423
424/// Wait for all transmit operations to complete
425pub fn wait_for_tx_complete(regs: Regs) {
426 // Wait for TX FIFO to empty
427 while regs.water().read().txcount().bits() != 0 {
428 // Wait for TX FIFO to drain
429 }
430
431 // Wait for last character to shift out (TC = Transmission Complete)
432 while regs.stat().read().tc().is_active() {
433 // Wait for transmission to complete
434 }
435}
436
437pub fn check_and_clear_rx_errors(regs: Regs) -> Result<()> {
438 let stat = regs.stat().read();
439 let mut status = Ok(());
440
441 // Check for overrun first - other error flags are prevented when OR is set
442 if stat.or().is_overrun() {
443 regs.stat().write(|w| w.or().clear_bit_by_one());
444
445 return Err(Error::Overrun);
446 }
447
448 if stat.pf().is_parity() {
449 regs.stat().write(|w| w.pf().clear_bit_by_one());
450 status = Err(Error::Parity);
451 }
452
453 if stat.fe().is_error() {
454 regs.stat().write(|w| w.fe().clear_bit_by_one());
455 status = Err(Error::Framing);
456 }
457
458 if stat.nf().is_noise() {
459 regs.stat().write(|w| w.nf().clear_bit_by_one());
460 status = Err(Error::Noise);
461 }
462
463 status
464}
465
466pub fn has_data(regs: Regs) -> bool {
467 if regs.param().read().rxfifo().bits() > 0 {
468 // FIFO is available - check RXCOUNT in WATER register
469 regs.water().read().rxcount().bits() > 0
470 } else {
471 // No FIFO - check RDRF flag in STAT register
472 regs.stat().read().rdrf().is_rxdata()
473 }
474}
475
476// ============================================================================
477// PIN TRAITS FOR LPUART FUNCTIONALITY
478// ============================================================================
479
480impl<T: Pin> sealed::Sealed for T {}
481
482/// io configuration trait for Lpuart Tx configuration
483pub trait TxPin<T: Instance>: Pin + sealed::Sealed + PeripheralType {
484 /// convert the pin to appropriate function for Lpuart Tx usage
485 fn as_tx(&self);
486}
487
488/// io configuration trait for Lpuart Rx configuration
489pub trait RxPin<T: Instance>: Pin + sealed::Sealed + PeripheralType {
490 /// convert the pin to appropriate function for Lpuart Rx usage
491 fn as_rx(&self);
492}
493
494/// io configuration trait for Lpuart Cts
495pub trait CtsPin<T: Instance>: Pin + sealed::Sealed + PeripheralType {
496 /// convert the pin to appropriate function for Lpuart Cts usage
497 fn as_cts(&self);
498}
499
500/// io configuration trait for Lpuart Rts
501pub trait RtsPin<T: Instance>: Pin + sealed::Sealed + PeripheralType {
502 /// convert the pin to appropriate function for Lpuart Rts usage
503 fn as_rts(&self);
504}
505
506macro_rules! impl_pin_trait {
507 ($fcn:ident, $mode:ident, $($pin:ident, $alt:ident),*) => {
508 paste! {
509 $(
510 impl [<$mode:camel Pin>]<lib::peripherals::$fcn> for lib::peripherals::$pin {
511 fn [<as_ $mode>](&self) {
512 let _alt = gpio::Alt::$alt;
513 // todo!("Configure pin for LPUART function")
514 }
515 }
516 )*
517 }
518 };
519}
520
521// Document identifier: MCXA343/344 Rev. 1DraftB ReleaseCandidate, 2025-07-10 - 6.1 MCX A173, A174 Signal Multiplexing and Pin Assignments
522// impl_pin_trait!(LPUART0, rx, PIO2_0, ALT2, PIO0_2, ALT2, PIO0_20, ALT3);
523// impl_pin_trait!(LPUART0, tx, PIO2_1, ALT2, PIO0_3, ALT2, PIO0_21, ALT3);
524// impl_pin_trait!(LPUART0, rts, PIO2_2, ALT2, PIO0_0, ALT2, PIO0_22, ALT3);
525// impl_pin_trait!(LPUART0, cts, PIO2_3, ALT2, PIO0_1, ALT2, PIO0_23, ALT3);
526impl_pin_trait!(LPUART2, rx, PIO2_3, ALT3);
527impl_pin_trait!(LPUART2, tx, PIO2_2, ALT3);
528
529// ============================================================================
530// ERROR TYPES AND RESULTS
531// ============================================================================
532
533/// LPUART error types
534#[derive(Debug, Copy, Clone, Eq, PartialEq)]
535#[cfg_attr(feature = "defmt", derive(defmt::Format))]
536pub enum Error {
537 /// Read error
538 Read,
539 /// Buffer overflow
540 Overrun,
541 /// Noise error
542 Noise,
543 /// Framing error
544 Framing,
545 /// Parity error
546 Parity,
547 /// Failure
548 Fail,
549 /// Invalid argument
550 InvalidArgument,
551 /// Lpuart baud rate cannot be supported with the given clock
552 UnsupportedBaudrate,
553 /// RX FIFO Empty
554 RxFifoEmpty,
555 /// TX FIFO Full
556 TxFifoFull,
557 /// TX Busy
558 TxBusy,
559}
560
561/// A specialized Result type for LPUART operations
562pub type Result<T> = core::result::Result<T, Error>;
563
564// ============================================================================
565// CONFIGURATION STRUCTURES
566// ============================================================================
567
568/// Lpuart config
569#[derive(Debug, Clone, Copy)]
570pub struct Config {
571 /// Baud rate in bits per second
572 pub baudrate_bps: u32,
573 /// Clock
574 pub clock: Clock,
575 /// Parity configuration
576 pub parity_mode: Option<Parity>,
577 /// Number of data bits
578 pub data_bits_count: DataBits,
579 /// MSB First or LSB First configuration
580 pub msb_firs: MsbFirst,
581 /// Number of stop bits
582 pub stop_bits_count: StopBits,
583 /// TX FIFO watermark
584 pub tx_fifo_watermark: u8,
585 /// RX FIFO watermark
586 pub rx_fifo_watermark: u8,
587 /// RX RTS enable
588 pub enable_rx_rts: bool,
589 /// TX CTS enable
590 pub enable_tx_cts: bool,
591 /// TX CTS source
592 pub tx_cts_source: TxCtsSource,
593 /// TX CTS configure
594 pub tx_cts_config: TxCtsConfig,
595 /// RX IDLE type
596 pub rx_idle_type: IdleType,
597 /// RX IDLE configuration
598 pub rx_idle_config: IdleConfig,
599 /// Enable transmitter
600 pub enable_tx: bool,
601 /// Enable receiver
602 pub enable_rx: bool,
603 /// Swap TXD and RXD pins
604 pub swap_txd_rxd: bool,
605}
606
607impl Default for Config {
608 fn default() -> Self {
609 Self {
610 baudrate_bps: 115_200u32,
611 clock: Clock::FroLf,
612 parity_mode: None,
613 data_bits_count: DataBits::Data8,
614 msb_firs: MsbFirst::LsbFirst,
615 stop_bits_count: StopBits::One,
616 tx_fifo_watermark: 0,
617 rx_fifo_watermark: 1,
618 enable_rx_rts: false,
619 enable_tx_cts: false,
620 tx_cts_source: TxCtsSource::Cts,
621 tx_cts_config: TxCtsConfig::Start,
622 rx_idle_type: IdleType::FromStart,
623 rx_idle_config: IdleConfig::Idle1,
624 enable_tx: false,
625 enable_rx: false,
626 swap_txd_rxd: false,
627 }
628 }
629}
630
631/// LPUART status flags
632#[derive(Debug, Clone, Copy)]
633#[cfg_attr(feature = "defmt", derive(defmt::Format))]
634pub struct Status {
635 /// Transmit data register empty
636 pub tx_empty: bool,
637 /// Transmission complete
638 pub tx_complete: bool,
639 /// Receive data register full
640 pub rx_full: bool,
641 /// Idle line detected
642 pub idle: bool,
643 /// Receiver overrun
644 pub overrun: bool,
645 /// Noise error
646 pub noise: bool,
647 /// Framing error
648 pub framing: bool,
649 /// Parity error
650 pub parity: bool,
651}
652
653// ============================================================================
654// MODE TRAITS (BLOCKING/ASYNC)
655// ============================================================================
656
657/// Driver move trait.
658#[allow(private_bounds)]
659pub trait Mode: sealed::Sealed {}
660
661/// Blocking mode.
662pub struct Blocking;
663impl sealed::Sealed for Blocking {}
664impl Mode for Blocking {}
665
666/// Async mode.
667pub struct Async;
668impl sealed::Sealed for Async {}
669impl Mode for Async {}
670
671// ============================================================================
672// CORE DRIVER STRUCTURES
673// ============================================================================
674
675/// Lpuart driver.
676pub struct Lpuart<'a, M: Mode> {
677 info: Info,
678 tx: LpuartTx<'a, M>,
679 rx: LpuartRx<'a, M>,
680}
681
682/// Lpuart TX driver.
683pub struct LpuartTx<'a, M: Mode> {
684 info: Info,
685 _tx_pin: Peri<'a, AnyPin>,
686 _tx_dma: Option<Channel<'a>>,
687 mode: PhantomData<(&'a (), M)>,
688}
689
690/// Lpuart Rx driver.
691pub struct LpuartRx<'a, M: Mode> {
692 info: Info,
693 _rx_pin: Peri<'a, AnyPin>,
694 _rx_dma: Option<Channel<'a>>,
695 mode: PhantomData<(&'a (), M)>,
696}
697
698// ============================================================================
699// LPUART CORE IMPLEMENTATION
700// ============================================================================
701
702impl<'a, M: Mode> Lpuart<'a, M> {
703 fn init<T: Instance>(
704 _tx: Option<&Peri<'a, AnyPin>>,
705 _rx: Option<&Peri<'a, AnyPin>>,
706 _rts: Option<&Peri<'a, AnyPin>>,
707 _cts: Option<&Peri<'a, AnyPin>>,
708 config: Config,
709 ) -> Result<()> {
710 let regs = T::info().regs;
711
712 // Perform initialization sequence
713 perform_software_reset(regs);
714 disable_transceiver(regs);
715 configure_baudrate(regs, config.baudrate_bps, config.clock)?;
716 configure_frame_format(regs, &config);
717 configure_control_settings(regs, &config);
718 configure_fifo(regs, &config);
719 clear_all_status_flags(regs);
720 configure_flow_control(regs, &config);
721 configure_bit_order(regs, config.msb_firs);
722 enable_transceiver(regs, config.enable_tx, config.enable_rx);
723
724 Ok(())
725 }
726
727 /// Deinitialize the LPUART peripheral
728 pub fn deinit(&self) -> Result<()> {
729 let regs = self.info.regs;
730
731 // Wait for TX operations to complete
732 wait_for_tx_complete(regs);
733
734 // Clear all status flags
735 clear_all_status_flags(regs);
736
737 // Disable the module - clear all CTRL register bits
738 regs.ctrl().reset();
739
740 Ok(())
741 }
742
743 /// Split the Lpuart into a transmitter and receiver
744 pub fn split(self) -> (LpuartTx<'a, M>, LpuartRx<'a, M>) {
745 (self.tx, self.rx)
746 }
747
748 /// Split the Lpuart into a transmitter and receiver by mutable reference
749 pub fn split_ref(&mut self) -> (&mut LpuartTx<'a, M>, &mut LpuartRx<'a, M>) {
750 (&mut self.tx, &mut self.rx)
751 }
752}
753
754// ============================================================================
755// BLOCKING MODE IMPLEMENTATIONS
756// ============================================================================
757
758impl<'a> Lpuart<'a, Blocking> {
759 /// Create a new blocking LPUART instance with TX and RX pins.
760 pub fn new_blocking<T: Instance>(
761 _inner: Peri<'a, T>,
762 tx_pin: Peri<'a, impl TxPin<T>>,
763 rx_pin: Peri<'a, impl RxPin<T>>,
764 config: Config,
765 ) -> Result<Self> {
766 // Configure the pins for LPUART usage
767 tx_pin.as_tx();
768 rx_pin.as_rx();
769
770 // Convert pins to AnyPin
771 let tx_pin: Peri<'a, AnyPin> = tx_pin.into();
772 let rx_pin: Peri<'a, AnyPin> = rx_pin.into();
773
774 // Initialize the peripheral
775 Self::init::<T>(Some(&tx_pin), Some(&rx_pin), None, None, config)?;
776
777 Ok(Self {
778 info: T::info(),
779 tx: LpuartTx::new_inner(T::info(), tx_pin, None),
780 rx: LpuartRx::new_inner(T::info(), rx_pin, None),
781 })
782 }
783}
784
785// ----------------------------------------------------------------------------
786// Blocking TX Implementation
787// ----------------------------------------------------------------------------
788
789impl<'a, M: Mode> LpuartTx<'a, M> {
790 fn new_inner(info: Info, tx_pin: Peri<'a, AnyPin>, tx_dma: Option<Channel<'a>>) -> Self {
791 Self {
792 info,
793 _tx_pin: tx_pin,
794 _tx_dma: tx_dma,
795 mode: PhantomData,
796 }
797 }
798}
799
800impl<'a> LpuartTx<'a, Blocking> {
801 /// Create a new blocking LPUART which can only send data.
802 pub fn new_blocking<T: Instance>(
803 _inner: Peri<'a, T>,
804 tx_pin: Peri<'a, impl TxPin<T>>,
805 config: Config,
806 ) -> Result<Self> {
807 tx_pin.as_tx();
808
809 let tx_pin: Peri<'a, AnyPin> = tx_pin.into();
810
811 Lpuart::<Blocking>::init::<T>(Some(&tx_pin), None, None, None, config)?;
812
813 Ok(Self::new_inner(T::info(), tx_pin, None))
814 }
815
816 fn write_byte_internal(&mut self, byte: u8) -> Result<()> {
817 self.info.regs.data().modify(|_, w| unsafe { w.bits(u32::from(byte)) });
818
819 Ok(())
820 }
821
822 fn blocking_write_byte(&mut self, byte: u8) -> Result<()> {
823 while self.info.regs.stat().read().tdre().is_txdata() {}
824 self.write_byte_internal(byte)
825 }
826
827 fn write_byte(&mut self, byte: u8) -> Result<()> {
828 if self.info.regs.stat().read().tdre().is_txdata() {
829 Err(Error::TxFifoFull)
830 } else {
831 self.write_byte_internal(byte)
832 }
833 }
834
835 /// Write data to LPUART TX blocking execution until all data is sent.
836 pub fn blocking_write(&mut self, buf: &[u8]) -> Result<()> {
837 for x in buf {
838 self.blocking_write_byte(*x)?;
839 }
840
841 Ok(())
842 }
843
844 /// Write data to LPUART TX without blocking.
845 pub fn write(&mut self, buf: &[u8]) -> Result<()> {
846 for x in buf {
847 self.write_byte(*x)?;
848 }
849
850 Ok(())
851 }
852
853 /// Flush LPUART TX blocking execution until all data has been transmitted.
854 pub fn blocking_flush(&mut self) -> Result<()> {
855 while self.info.regs.water().read().txcount().bits() != 0 {
856 // Wait for TX FIFO to drain
857 }
858
859 // Wait for last character to shift out
860 while self.info.regs.stat().read().tc().is_active() {
861 // Wait for transmission to complete
862 }
863
864 Ok(())
865 }
866
867 /// Flush LPUART TX.
868 pub fn flush(&mut self) -> Result<()> {
869 // Check if TX FIFO is empty
870 if self.info.regs.water().read().txcount().bits() != 0 {
871 return Err(Error::TxBusy);
872 }
873
874 // Check if transmission is complete
875 if self.info.regs.stat().read().tc().is_active() {
876 return Err(Error::TxBusy);
877 }
878
879 Ok(())
880 }
881}
882
883// ----------------------------------------------------------------------------
884// Blocking RX Implementation
885// ----------------------------------------------------------------------------
886
887impl<'a, M: Mode> LpuartRx<'a, M> {
888 fn new_inner(info: Info, rx_pin: Peri<'a, AnyPin>, rx_dma: Option<Channel<'a>>) -> Self {
889 Self {
890 info,
891 _rx_pin: rx_pin,
892 _rx_dma: rx_dma,
893 mode: PhantomData,
894 }
895 }
896}
897
898impl<'a> LpuartRx<'a, Blocking> {
899 /// Create a new blocking LPUART which can only receive data.
900 pub fn new_blocking<T: Instance>(
901 _inner: Peri<'a, T>,
902 rx_pin: Peri<'a, impl RxPin<T>>,
903 config: Config,
904 ) -> Result<Self> {
905 rx_pin.as_rx();
906
907 let rx_pin: Peri<'a, AnyPin> = rx_pin.into();
908
909 Lpuart::<Blocking>::init::<T>(None, Some(&rx_pin), None, None, config)?;
910
911 Ok(Self::new_inner(T::info(), rx_pin, None))
912 }
913
914 fn read_byte_internal(&mut self) -> Result<u8> {
915 let data = self.info.regs.data().read();
916
917 Ok((data.bits() & 0xFF) as u8)
918 }
919
920 fn read_byte(&mut self) -> Result<u8> {
921 check_and_clear_rx_errors(self.info.regs)?;
922
923 if !has_data(self.info.regs) {
924 return Err(Error::RxFifoEmpty);
925 }
926
927 self.read_byte_internal()
928 }
929
930 fn blocking_read_byte(&mut self) -> Result<u8> {
931 loop {
932 if has_data(self.info.regs) {
933 return self.read_byte_internal();
934 }
935
936 check_and_clear_rx_errors(self.info.regs)?;
937 }
938 }
939
940 /// Read data from LPUART RX without blocking.
941 pub fn read(&mut self, buf: &mut [u8]) -> Result<()> {
942 for byte in buf.iter_mut() {
943 *byte = self.read_byte()?;
944 }
945 Ok(())
946 }
947
948 /// Read data from LPUART RX blocking execution until the buffer is filled.
949 pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<()> {
950 for byte in buf.iter_mut() {
951 *byte = self.blocking_read_byte()?;
952 }
953 Ok(())
954 }
955}
956
957impl<'a> Lpuart<'a, Blocking> {
958 /// Read data from LPUART RX blocking execution until the buffer is filled
959 pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<()> {
960 self.rx.blocking_read(buf)
961 }
962
963 /// Read data from LPUART RX without blocking
964 pub fn read(&mut self, buf: &mut [u8]) -> Result<()> {
965 self.rx.read(buf)
966 }
967
968 /// Write data to LPUART TX blocking execution until all data is sent
969 pub fn blocking_write(&mut self, buf: &[u8]) -> Result<()> {
970 self.tx.blocking_write(buf)
971 }
972
973 /// Write data to LPUART TX without blocking
974 pub fn write(&mut self, buf: &[u8]) -> Result<()> {
975 self.tx.write(buf)
976 }
977
978 /// Flush LPUART TX blocking execution until all data has been transmitted
979 pub fn blocking_flush(&mut self) -> Result<()> {
980 self.tx.blocking_flush()
981 }
982
983 /// Flush LPUART TX without blocking
984 pub fn flush(&mut self) -> Result<()> {
985 self.tx.flush()
986 }
987}
988
989// ============================================================================
990// ASYNC MODE IMPLEMENTATIONS
991// ============================================================================
992
993// TODO: Implement async mode for LPUART
994
995// ============================================================================
996// EMBEDDED-HAL 0.2 TRAIT IMPLEMENTATIONS
997// ============================================================================
998
999impl embedded_hal_02::serial::Read<u8> for LpuartRx<'_, Blocking> {
1000 type Error = Error;
1001
1002 fn read(&mut self) -> core::result::Result<u8, nb::Error<Self::Error>> {
1003 let mut buf = [0; 1];
1004 match self.read(&mut buf) {
1005 Ok(_) => Ok(buf[0]),
1006 Err(Error::RxFifoEmpty) => Err(nb::Error::WouldBlock),
1007 Err(e) => Err(nb::Error::Other(e)),
1008 }
1009 }
1010}
1011
1012impl embedded_hal_02::serial::Write<u8> for LpuartTx<'_, Blocking> {
1013 type Error = Error;
1014
1015 fn write(&mut self, word: u8) -> core::result::Result<(), nb::Error<Self::Error>> {
1016 match self.write(&[word]) {
1017 Ok(_) => Ok(()),
1018 Err(Error::TxFifoFull) => Err(nb::Error::WouldBlock),
1019 Err(e) => Err(nb::Error::Other(e)),
1020 }
1021 }
1022
1023 fn flush(&mut self) -> core::result::Result<(), nb::Error<Self::Error>> {
1024 match self.flush() {
1025 Ok(_) => Ok(()),
1026 Err(Error::TxBusy) => Err(nb::Error::WouldBlock),
1027 Err(e) => Err(nb::Error::Other(e)),
1028 }
1029 }
1030}
1031
1032impl embedded_hal_02::blocking::serial::Write<u8> for LpuartTx<'_, Blocking> {
1033 type Error = Error;
1034
1035 fn bwrite_all(&mut self, buffer: &[u8]) -> core::result::Result<(), Self::Error> {
1036 self.blocking_write(buffer)
1037 }
1038
1039 fn bflush(&mut self) -> core::result::Result<(), Self::Error> {
1040 self.blocking_flush()
1041 }
1042}
1043
1044impl embedded_hal_02::serial::Read<u8> for Lpuart<'_, Blocking> {
1045 type Error = Error;
1046
1047 fn read(&mut self) -> core::result::Result<u8, nb::Error<Self::Error>> {
1048 embedded_hal_02::serial::Read::read(&mut self.rx)
1049 }
1050}
1051
1052impl embedded_hal_02::serial::Write<u8> for Lpuart<'_, Blocking> {
1053 type Error = Error;
1054
1055 fn write(&mut self, word: u8) -> core::result::Result<(), nb::Error<Self::Error>> {
1056 embedded_hal_02::serial::Write::write(&mut self.tx, word)
1057 }
1058
1059 fn flush(&mut self) -> core::result::Result<(), nb::Error<Self::Error>> {
1060 embedded_hal_02::serial::Write::flush(&mut self.tx)
1061 }
1062}
1063
1064impl embedded_hal_02::blocking::serial::Write<u8> for Lpuart<'_, Blocking> {
1065 type Error = Error;
1066
1067 fn bwrite_all(&mut self, buffer: &[u8]) -> core::result::Result<(), Self::Error> {
1068 self.blocking_write(buffer)
1069 }
1070
1071 fn bflush(&mut self) -> core::result::Result<(), Self::Error> {
1072 self.blocking_flush()
1073 }
1074}
1075
1076// ============================================================================
1077// EMBEDDED-HAL-NB TRAIT IMPLEMENTATIONS
1078// ============================================================================
1079
1080impl embedded_hal_nb::serial::Error for Error {
1081 fn kind(&self) -> embedded_hal_nb::serial::ErrorKind {
1082 match *self {
1083 Self::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat,
1084 Self::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun,
1085 Self::Parity => embedded_hal_nb::serial::ErrorKind::Parity,
1086 Self::Noise => embedded_hal_nb::serial::ErrorKind::Noise,
1087 _ => embedded_hal_nb::serial::ErrorKind::Other,
1088 }
1089 }
1090}
1091
1092impl embedded_hal_nb::serial::ErrorType for LpuartRx<'_, Blocking> {
1093 type Error = Error;
1094}
1095
1096impl embedded_hal_nb::serial::ErrorType for LpuartTx<'_, Blocking> {
1097 type Error = Error;
1098}
1099
1100impl embedded_hal_nb::serial::ErrorType for Lpuart<'_, Blocking> {
1101 type Error = Error;
1102}
1103
1104impl embedded_hal_nb::serial::Read for LpuartRx<'_, Blocking> {
1105 fn read(&mut self) -> nb::Result<u8, Self::Error> {
1106 let mut buf = [0; 1];
1107 match self.read(&mut buf) {
1108 Ok(_) => Ok(buf[0]),
1109 Err(Error::RxFifoEmpty) => Err(nb::Error::WouldBlock),
1110 Err(e) => Err(nb::Error::Other(e)),
1111 }
1112 }
1113}
1114
1115impl embedded_hal_nb::serial::Write for LpuartTx<'_, Blocking> {
1116 fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
1117 match self.write(&[word]) {
1118 Ok(_) => Ok(()),
1119 Err(Error::TxFifoFull) => Err(nb::Error::WouldBlock),
1120 Err(e) => Err(nb::Error::Other(e)),
1121 }
1122 }
1123
1124 fn flush(&mut self) -> nb::Result<(), Self::Error> {
1125 match self.flush() {
1126 Ok(_) => Ok(()),
1127 Err(Error::TxBusy) => Err(nb::Error::WouldBlock),
1128 Err(e) => Err(nb::Error::Other(e)),
1129 }
1130 }
1131}
1132
1133impl embedded_hal_nb::serial::Read for Lpuart<'_, Blocking> {
1134 fn read(&mut self) -> nb::Result<u8, Self::Error> {
1135 embedded_hal_nb::serial::Read::read(&mut self.rx)
1136 }
1137}
1138
1139impl embedded_hal_nb::serial::Write for Lpuart<'_, Blocking> {
1140 fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
1141 embedded_hal_nb::serial::Write::write(&mut self.tx, char)
1142 }
1143
1144 fn flush(&mut self) -> nb::Result<(), Self::Error> {
1145 embedded_hal_nb::serial::Write::flush(&mut self.tx)
1146 }
1147}
1148
1149// ============================================================================
1150// EMBEDDED-IO TRAIT IMPLEMENTATIONS
1151// ============================================================================
1152
1153impl embedded_io::Error for Error {
1154 fn kind(&self) -> embedded_io::ErrorKind {
1155 embedded_io::ErrorKind::Other
1156 }
1157}
1158
1159impl embedded_io::ErrorType for LpuartRx<'_, Blocking> {
1160 type Error = Error;
1161}
1162
1163impl embedded_io::ErrorType for LpuartTx<'_, Blocking> {
1164 type Error = Error;
1165}
1166
1167impl embedded_io::ErrorType for Lpuart<'_, Blocking> {
1168 type Error = Error;
1169}
1170
1171impl embedded_io::Read for LpuartRx<'_, Blocking> {
1172 fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
1173 self.blocking_read(buf).map(|_| buf.len())
1174 }
1175}
1176
1177impl embedded_io::Write for LpuartTx<'_, Blocking> {
1178 fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> {
1179 self.blocking_write(buf).map(|_| buf.len())
1180 }
1181
1182 fn flush(&mut self) -> core::result::Result<(), Self::Error> {
1183 self.blocking_flush()
1184 }
1185}
1186
1187impl embedded_io::Read for Lpuart<'_, Blocking> {
1188 fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
1189 embedded_io::Read::read(&mut self.rx, buf)
1190 }
1191}
1192
1193impl embedded_io::Write for Lpuart<'_, Blocking> {
1194 fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> {
1195 embedded_io::Write::write(&mut self.tx, buf)
1196 }
1197
1198 fn flush(&mut self) -> core::result::Result<(), Self::Error> {
1199 embedded_io::Write::flush(&mut self.tx)
1200 }
1201}
diff --git a/src/main.rs b/src/main.rs
deleted file mode 100644
index 7111536ff..000000000
--- a/src/main.rs
+++ /dev/null
@@ -1,18 +0,0 @@
1#![cfg_attr(target_os = "none", no_std)]
2#![cfg_attr(target_os = "none", no_main)]
3
4#[cfg(target_os = "none")]
5mod baremetal;
6
7#[cfg(not(target_os = "none"))]
8fn main() {
9 println!("Hello, world!");
10}
11
12#[cfg(test)]
13mod tests {
14 #[test]
15 fn it_works() {
16 assert_eq!(2 + 2, 4);
17 }
18}
diff --git a/src/ostimer.rs b/src/ostimer.rs
new file mode 100644
index 000000000..a4cab6970
--- /dev/null
+++ b/src/ostimer.rs
@@ -0,0 +1,678 @@
1//! # OSTIMER Driver with Robustness Features
2//!
3//! This module provides an async timer driver for the NXP MCXA276 OSTIMER peripheral
4//! with protection against race conditions and timer rollover issues.
5//!
6//! ## Features
7//!
8//! - Async timing with embassy-time integration
9//! - Gray code counter handling (42-bit counter)
10//! - Interrupt-driven wakeups
11//! - Configurable interrupt priority
12//! - **Race condition protection**: Critical sections and atomic operations
13//! - **Timer rollover handling**: Bounds checking and rollover prevention
14//!
15//! ## Clock Frequency Configuration
16//!
17//! The OSTIMER frequency depends on your system's clock configuration. You must provide
18//! the actual frequency when calling `time_driver::init()`.
19//!
20//! ## Race Condition Protection
21//! - Critical sections in interrupt handlers prevent concurrent access
22//! - Atomic register operations with memory barriers
23//! - Proper interrupt flag clearing and validation
24//!
25//! ## Timer Rollover Handling
26//! - Bounds checking prevents scheduling beyond timer capacity
27//! - Immediate wake for timestamps that would cause rollover issues
28#![allow(dead_code)]
29
30use core::sync::atomic::{AtomicBool, Ordering};
31
32use crate::interrupt::InterruptExt;
33use crate::pac;
34
35// PAC defines the shared RegisterBlock under `ostimer0`.
36type Regs = pac::ostimer0::RegisterBlock;
37
38// OSTIMER EVTIMER register layout constants
39/// Total width of the EVTIMER counter in bits (42 bits total)
40const EVTIMER_TOTAL_BITS: u32 = 42;
41/// Width of the low part of EVTIMER (bits 31:0)
42const EVTIMER_LO_BITS: u32 = 32;
43/// Width of the high part of EVTIMER (bits 41:32)
44const EVTIMER_HI_BITS: u32 = 10;
45/// Bit position where high part starts in the combined 64-bit value
46const EVTIMER_HI_SHIFT: u32 = 32;
47
48/// Bit mask for the high part of EVTIMER
49const EVTIMER_HI_MASK: u16 = (1 << EVTIMER_HI_BITS) - 1;
50
51/// Maximum value for MATCH_L register (32-bit)
52const MATCH_L_MAX: u32 = u32::MAX;
53/// Maximum value for MATCH_H register (10-bit)
54const MATCH_H_MAX: u16 = EVTIMER_HI_MASK;
55
56/// Bit mask for extracting the low 32 bits from a 64-bit value
57const LOW_32_BIT_MASK: u64 = u32::MAX as u64;
58
59/// Gray code conversion bit shifts (most significant to least)
60const GRAY_CONVERSION_SHIFTS: [u32; 6] = [32, 16, 8, 4, 2, 1];
61
62/// Maximum timer value before rollover (2^42 - 1 ticks)
63/// Actual rollover time depends on the configured clock frequency
64const TIMER_MAX_VALUE: u64 = (1u64 << EVTIMER_TOTAL_BITS) - 1;
65
66/// Threshold for detecting timer rollover in comparisons (1 second at 1MHz)
67const TIMER_ROLLOVER_THRESHOLD: u64 = 1_000_000;
68
69/// Common default interrupt priority for OSTIMER
70const DEFAULT_INTERRUPT_PRIORITY: u8 = 3;
71
72// Global alarm state for interrupt handling
73static ALARM_ACTIVE: AtomicBool = AtomicBool::new(false);
74static mut ALARM_CALLBACK: Option<fn()> = None;
75static mut ALARM_FLAG: Option<*const AtomicBool> = None;
76static mut ALARM_TARGET_TIME: u64 = 0;
77
78/// Number of tight spin iterations between elapsed time checks while waiting for MATCH writes to return to the idle (0) state.
79const MATCH_WRITE_READY_SPINS: usize = 512;
80/// Maximum time (in OSTIMER ticks) to wait for MATCH registers to become writable (~5 ms at 1 MHz).
81const MATCH_WRITE_READY_TIMEOUT_TICKS: u64 = 5_000;
82/// Short stabilization delay executed after toggling the MRCC reset line to let the OSTIMER bus interface settle.
83const RESET_STABILIZE_SPINS: usize = 512;
84
85pub(super) fn wait_for_match_write_ready(r: &Regs) -> bool {
86 let start = now_ticks_read();
87 let mut spin_budget = 0usize;
88
89 loop {
90 if r.osevent_ctrl().read().match_wr_rdy().bit_is_clear() {
91 return true;
92 }
93
94 cortex_m::asm::nop();
95 spin_budget += 1;
96
97 if spin_budget >= MATCH_WRITE_READY_SPINS {
98 spin_budget = 0;
99
100 let elapsed = now_ticks_read().wrapping_sub(start);
101 if elapsed >= MATCH_WRITE_READY_TIMEOUT_TICKS {
102 return false;
103 }
104 }
105 }
106}
107
108pub(super) fn wait_for_match_write_complete(r: &Regs) -> bool {
109 let start = now_ticks_read();
110 let mut spin_budget = 0usize;
111
112 loop {
113 if r.osevent_ctrl().read().match_wr_rdy().bit_is_clear() {
114 return true;
115 }
116
117 cortex_m::asm::nop();
118 spin_budget += 1;
119
120 if spin_budget >= MATCH_WRITE_READY_SPINS {
121 spin_budget = 0;
122
123 let elapsed = now_ticks_read().wrapping_sub(start);
124 if elapsed >= MATCH_WRITE_READY_TIMEOUT_TICKS {
125 return false;
126 }
127 }
128 }
129}
130
131fn prime_match_registers(r: &Regs) {
132 // Disable the interrupt, clear any pending flag, then wait until the MATCH registers are writable.
133 r.osevent_ctrl()
134 .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit());
135
136 if wait_for_match_write_ready(r) {
137 r.match_l().write(|w| unsafe { w.match_value().bits(MATCH_L_MAX) });
138 r.match_h().write(|w| unsafe { w.match_value().bits(MATCH_H_MAX) });
139 let _ = wait_for_match_write_complete(r);
140 }
141}
142
143/// Single-shot alarm functionality for OSTIMER
144pub struct Alarm<'d> {
145 /// Whether the alarm is currently active
146 active: AtomicBool,
147 /// Callback to execute when alarm expires (optional)
148 callback: Option<fn()>,
149 /// Flag that gets set when alarm expires (optional)
150 flag: Option<&'d AtomicBool>,
151 _phantom: core::marker::PhantomData<&'d mut ()>,
152}
153
154impl<'d> Alarm<'d> {
155 /// Create a new alarm instance
156 pub fn new() -> Self {
157 Self {
158 active: AtomicBool::new(false),
159 callback: None,
160 flag: None,
161 _phantom: core::marker::PhantomData,
162 }
163 }
164
165 /// Set a callback that will be executed when the alarm expires
166 /// Note: Due to interrupt handler constraints, callbacks must be static function pointers
167 pub fn with_callback(mut self, callback: fn()) -> Self {
168 self.callback = Some(callback);
169 self
170 }
171
172 /// Set a flag that will be set to true when the alarm expires
173 pub fn with_flag(mut self, flag: &'d AtomicBool) -> Self {
174 self.flag = Some(flag);
175 self
176 }
177
178 /// Check if the alarm is currently active
179 pub fn is_active(&self) -> bool {
180 self.active.load(Ordering::Acquire)
181 }
182
183 /// Cancel the alarm if it's active
184 pub fn cancel(&self) {
185 self.active.store(false, Ordering::Release);
186 }
187}
188
189/// Configuration for Ostimer::new()
190#[derive(Copy, Clone)]
191pub struct Config {
192 /// Initialize MATCH registers to their max values and mask/clear the interrupt flag.
193 pub init_match_max: bool,
194 /// OSTIMER clock frequency in Hz (must match the actual hardware clock)
195 pub clock_frequency_hz: u64,
196}
197
198impl Default for Config {
199 fn default() -> Self {
200 Self {
201 init_match_max: true,
202 // Default to 1MHz - user should override this with actual frequency
203 clock_frequency_hz: 1_000_000,
204 }
205 }
206}
207
208/// OSTIMER peripheral instance
209pub struct Ostimer<'d, I: Instance> {
210 _inst: core::marker::PhantomData<I>,
211 clock_frequency_hz: u64,
212 _phantom: core::marker::PhantomData<&'d mut ()>,
213}
214
215impl<'d, I: Instance> Ostimer<'d, I> {
216 /// Construct OSTIMER handle.
217 /// Requires clocks for the instance to be enabled by the board before calling.
218 /// Does not enable NVIC or INTENA; use time_driver::init() for async operation.
219 pub fn new(_inst: impl Instance, cfg: Config, _p: &'d crate::pac::Peripherals) -> Self {
220 assert!(cfg.clock_frequency_hz > 0, "OSTIMER frequency must be greater than 0");
221
222 if cfg.init_match_max {
223 let r: &Regs = unsafe { &*I::ptr() };
224 // Mask INTENA, clear pending flag, and set MATCH to max so no spurious IRQ fires.
225 prime_match_registers(r);
226 }
227
228 Self {
229 _inst: core::marker::PhantomData,
230 clock_frequency_hz: cfg.clock_frequency_hz,
231 _phantom: core::marker::PhantomData,
232 }
233 }
234
235 /// Get the configured clock frequency in Hz
236 pub fn clock_frequency_hz(&self) -> u64 {
237 self.clock_frequency_hz
238 }
239
240 /// Read the current timer counter value in timer ticks
241 ///
242 /// # Returns
243 /// Current timer counter value as a 64-bit unsigned integer
244 pub fn now(&self) -> u64 {
245 now_ticks_read()
246 }
247
248 /// Reset the timer counter to zero
249 ///
250 /// This performs a hardware reset of the OSTIMER peripheral, which will reset
251 /// the counter to zero and clear any pending interrupts. Note that this will
252 /// affect all timer operations including embassy-time.
253 ///
254 /// # Safety
255 /// This operation will reset the entire OSTIMER peripheral. Any active alarms
256 /// or time_driver operations will be disrupted. Use with caution.
257 pub fn reset(&self, peripherals: &crate::pac::Peripherals) {
258 critical_section::with(|_| {
259 let r: &Regs = unsafe { &*I::ptr() };
260
261 // Mask the peripheral interrupt flag before we toggle the reset line so that
262 // no new NVIC activity races with the reset sequence.
263 r.osevent_ctrl()
264 .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit());
265
266 unsafe {
267 crate::reset::assert::<crate::reset::line::Ostimer0>(peripherals);
268 }
269
270 for _ in 0..RESET_STABILIZE_SPINS {
271 cortex_m::asm::nop();
272 }
273
274 unsafe {
275 crate::reset::release::<crate::reset::line::Ostimer0>(peripherals);
276 }
277
278 while !<crate::reset::line::Ostimer0 as crate::reset::ResetLine>::is_released(&peripherals.mrcc0) {
279 cortex_m::asm::nop();
280 }
281
282 for _ in 0..RESET_STABILIZE_SPINS {
283 cortex_m::asm::nop();
284 }
285
286 // Clear alarm bookkeeping before re-arming MATCH registers.
287 ALARM_ACTIVE.store(false, Ordering::Release);
288 unsafe {
289 ALARM_TARGET_TIME = 0;
290 ALARM_CALLBACK = None;
291 ALARM_FLAG = None;
292 }
293
294 prime_match_registers(r);
295 });
296
297 // Ensure no stale OS_EVENT request remains pending after the reset sequence.
298 crate::interrupt::OS_EVENT.unpend();
299 }
300
301 /// Schedule a single-shot alarm to expire after the specified delay in microseconds
302 ///
303 /// # Parameters
304 /// * `alarm` - The alarm instance to schedule
305 /// * `delay_us` - Delay in microseconds from now
306 ///
307 /// # Returns
308 /// `true` if the alarm was scheduled successfully, `false` if it would exceed timer capacity
309 pub fn schedule_alarm_delay(&self, alarm: &Alarm, delay_us: u64) -> bool {
310 let delay_ticks = (delay_us * self.clock_frequency_hz) / 1_000_000;
311 let target_time = now_ticks_read() + delay_ticks;
312 self.schedule_alarm_at(alarm, target_time)
313 }
314
315 /// Schedule a single-shot alarm to expire at the specified absolute time in timer ticks
316 ///
317 /// # Parameters
318 /// * `alarm` - The alarm instance to schedule
319 /// * `target_ticks` - Absolute time in timer ticks when the alarm should expire
320 ///
321 /// # Returns
322 /// `true` if the alarm was scheduled successfully, `false` if it would exceed timer capacity
323 pub fn schedule_alarm_at(&self, alarm: &Alarm, target_ticks: u64) -> bool {
324 let now = now_ticks_read();
325
326 // Check if target time is in the past
327 if target_ticks <= now {
328 // Execute callback immediately if alarm was supposed to be active
329 if alarm.active.load(Ordering::Acquire) {
330 alarm.active.store(false, Ordering::Release);
331 if let Some(callback) = alarm.callback {
332 callback();
333 }
334 if let Some(flag) = &alarm.flag {
335 flag.store(true, Ordering::Release);
336 }
337 }
338 return true;
339 }
340
341 // Check for timer rollover
342 let max_future = now + TIMER_MAX_VALUE;
343 if target_ticks > max_future {
344 return false; // Would exceed timer capacity
345 }
346
347 // Program the timer
348 let r: &Regs = unsafe { &*I::ptr() };
349
350 critical_section::with(|_| {
351 // Disable interrupt and clear flag
352 r.osevent_ctrl()
353 .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit());
354
355 if !wait_for_match_write_ready(r) {
356 prime_match_registers(r);
357
358 if !wait_for_match_write_ready(r) {
359 alarm.active.store(false, Ordering::Release);
360 ALARM_ACTIVE.store(false, Ordering::Release);
361 unsafe {
362 ALARM_TARGET_TIME = 0;
363 ALARM_CALLBACK = None;
364 ALARM_FLAG = None;
365 }
366 return false;
367 }
368 }
369
370 // Mark alarm as active now that we know the MATCH registers are writable
371 alarm.active.store(true, Ordering::Release);
372
373 // Set global alarm state for interrupt handler
374 ALARM_ACTIVE.store(true, Ordering::Release);
375 unsafe {
376 ALARM_TARGET_TIME = target_ticks;
377 ALARM_CALLBACK = alarm.callback;
378 ALARM_FLAG = alarm.flag.map(|f| f as *const AtomicBool);
379 }
380
381 // Program MATCH registers (Gray-coded)
382 let gray = bin_to_gray(target_ticks);
383 let l = (gray & LOW_32_BIT_MASK) as u32;
384 let h = (((gray >> EVTIMER_HI_SHIFT) as u16) & EVTIMER_HI_MASK) as u16;
385
386 r.match_l().write(|w| unsafe { w.match_value().bits(l) });
387 r.match_h().write(|w| unsafe { w.match_value().bits(h) });
388
389 if !wait_for_match_write_complete(r) {
390 alarm.active.store(false, Ordering::Release);
391 ALARM_ACTIVE.store(false, Ordering::Release);
392 unsafe {
393 ALARM_TARGET_TIME = 0;
394 ALARM_CALLBACK = None;
395 ALARM_FLAG = None;
396 }
397 return false;
398 }
399
400 let now_after_program = now_ticks_read();
401 let intrflag_set = r.osevent_ctrl().read().ostimer_intrflag().bit_is_set();
402 if now_after_program >= target_ticks && !intrflag_set {
403 alarm.active.store(false, Ordering::Release);
404 ALARM_ACTIVE.store(false, Ordering::Release);
405 unsafe {
406 ALARM_TARGET_TIME = 0;
407 ALARM_CALLBACK = None;
408 ALARM_FLAG = None;
409 }
410 return false;
411 }
412
413 // Enable interrupt
414 r.osevent_ctrl().write(|w| w.ostimer_intena().set_bit());
415
416 true
417 })
418 }
419
420 /// Cancel any active alarm
421 pub fn cancel_alarm(&self, alarm: &Alarm) {
422 critical_section::with(|_| {
423 alarm.cancel();
424
425 // Clear global alarm state
426 ALARM_ACTIVE.store(false, Ordering::Release);
427 unsafe { ALARM_TARGET_TIME = 0 };
428
429 // Reset MATCH registers to maximum values to prevent spurious interrupts
430 let r: &Regs = unsafe { &*I::ptr() };
431 prime_match_registers(r);
432 });
433 }
434
435 /// Check if an alarm has expired (call this from your interrupt handler)
436 /// Returns true if the alarm was active and has now expired
437 pub fn check_alarm_expired(&self, alarm: &Alarm) -> bool {
438 if alarm.active.load(Ordering::Acquire) {
439 alarm.active.store(false, Ordering::Release);
440
441 // Execute callback
442 if let Some(callback) = alarm.callback {
443 callback();
444 }
445
446 // Set flag
447 if let Some(flag) = &alarm.flag {
448 flag.store(true, Ordering::Release);
449 }
450
451 true
452 } else {
453 false
454 }
455 }
456}
457
458/// Read current EVTIMER (Gray-coded) and convert to binary ticks.
459#[inline(always)]
460fn now_ticks_read() -> u64 {
461 let r: &Regs = unsafe { &*pac::Ostimer0::ptr() };
462
463 // Read high then low to minimize incoherent snapshots
464 let hi = (r.evtimerh().read().evtimer_count_value().bits() as u64) & (EVTIMER_HI_MASK as u64);
465 let lo = r.evtimerl().read().evtimer_count_value().bits() as u64;
466
467 // Combine and convert from Gray code to binary
468 let gray = lo | (hi << EVTIMER_HI_SHIFT);
469 gray_to_bin(gray)
470}
471
472// Instance trait like other drivers, providing a PAC pointer for this OSTIMER instance
473pub trait Instance {
474 fn ptr() -> *const Regs;
475}
476
477// Token for OSTIMER0 provided by embassy-hal-internal peripherals macro.
478pub type Ostimer0 = crate::peripherals::OSTIMER0;
479
480impl Instance for crate::peripherals::OSTIMER0 {
481 #[inline(always)]
482 fn ptr() -> *const Regs {
483 pac::Ostimer0::ptr()
484 }
485}
486
487// Also implement Instance for the Peri wrapper type
488impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::OSTIMER0> {
489 #[inline(always)]
490 fn ptr() -> *const Regs {
491 pac::Ostimer0::ptr()
492 }
493}
494
495#[inline(always)]
496fn bin_to_gray(x: u64) -> u64 {
497 x ^ (x >> 1)
498}
499
500#[inline(always)]
501fn gray_to_bin(gray: u64) -> u64 {
502 // More efficient iterative conversion using predefined shifts
503 let mut bin = gray;
504 for &shift in &GRAY_CONVERSION_SHIFTS {
505 bin ^= bin >> shift;
506 }
507 bin
508}
509
510pub mod time_driver {
511 use core::sync::atomic::Ordering;
512 use core::task::Waker;
513
514 use embassy_sync::waitqueue::AtomicWaker;
515 use embassy_time_driver as etd;
516
517 use super::{
518 bin_to_gray, now_ticks_read, Regs, ALARM_ACTIVE, ALARM_CALLBACK, ALARM_FLAG, ALARM_TARGET_TIME,
519 EVTIMER_HI_MASK, EVTIMER_HI_SHIFT, LOW_32_BIT_MASK,
520 };
521 use crate::pac;
522 pub struct Driver;
523 static TIMER_WAKER: AtomicWaker = AtomicWaker::new();
524
525 impl etd::Driver for Driver {
526 fn now(&self) -> u64 {
527 // Use the hardware counter (frequency configured in init)
528 super::now_ticks_read()
529 }
530
531 fn schedule_wake(&self, timestamp: u64, waker: &Waker) {
532 let now = self.now();
533
534 // If timestamp is in the past or very close to now, wake immediately
535 if timestamp <= now {
536 waker.wake_by_ref();
537 return;
538 }
539
540 // Prevent scheduling too far in the future (beyond timer rollover)
541 // This prevents wraparound issues
542 let max_future = now + super::TIMER_MAX_VALUE;
543 if timestamp > max_future {
544 // For very long timeouts, wake immediately to avoid rollover issues
545 waker.wake_by_ref();
546 return;
547 }
548
549 // Register the waker first so any immediate wake below is observed by the executor.
550 TIMER_WAKER.register(waker);
551
552 let r: &Regs = unsafe { &*pac::Ostimer0::ptr() };
553
554 critical_section::with(|_| {
555 // Mask INTENA and clear flag
556 r.osevent_ctrl()
557 .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit());
558
559 // Read back to ensure W1C took effect on hardware
560 let _ = r.osevent_ctrl().read().ostimer_intrflag().bit();
561
562 if !super::wait_for_match_write_ready(r) {
563 super::prime_match_registers(r);
564
565 if !super::wait_for_match_write_ready(r) {
566 // If we can't safely program MATCH, wake immediately and leave INTENA masked.
567 waker.wake_by_ref();
568 return;
569 }
570 }
571
572 // Program MATCH (Gray-coded). Write low then high, then fence.
573 let gray = bin_to_gray(timestamp);
574 let l = (gray & LOW_32_BIT_MASK) as u32;
575
576 let h = (((gray >> EVTIMER_HI_SHIFT) as u16) & EVTIMER_HI_MASK) as u16;
577
578 r.match_l().write(|w| unsafe { w.match_value().bits(l) });
579 r.match_h().write(|w| unsafe { w.match_value().bits(h) });
580
581 if !super::wait_for_match_write_complete(r) {
582 waker.wake_by_ref();
583 return;
584 }
585
586 let now_after_program = super::now_ticks_read();
587 let intrflag_set = r.osevent_ctrl().read().ostimer_intrflag().bit_is_set();
588 if now_after_program >= timestamp && !intrflag_set {
589 waker.wake_by_ref();
590 return;
591 }
592
593 // Enable peripheral interrupt
594 r.osevent_ctrl().write(|w| w.ostimer_intena().set_bit());
595 });
596 }
597 }
598
599 /// Install the global embassy-time driver and configure NVIC priority for OS_EVENT.
600 ///
601 /// # Parameters
602 /// * `priority` - Interrupt priority for the OSTIMER interrupt
603 /// * `frequency_hz` - Actual OSTIMER clock frequency in Hz (stored for future use)
604 ///
605 /// Note: The frequency parameter is currently accepted for API compatibility.
606 /// The embassy_time_driver macro handles driver registration automatically.
607 pub fn init(priority: crate::interrupt::Priority, frequency_hz: u64) {
608 // Mask/clear at peripheral and set default MATCH
609 let r: &Regs = unsafe { &*pac::Ostimer0::ptr() };
610 super::prime_match_registers(r);
611
612 // Configure NVIC for timer operation
613 crate::interrupt::OS_EVENT.configure_for_timer(priority);
614
615 // Note: The embassy_time_driver macro automatically registers the driver
616 // The frequency parameter is accepted for future compatibility
617 let _ = frequency_hz; // Suppress unused parameter warning
618 }
619
620 // Export the global time driver expected by embassy-time
621 embassy_time_driver::time_driver_impl!(static DRIVER: Driver = Driver);
622
623 /// To be called from the OS_EVENT IRQ.
624 pub fn on_interrupt() {
625 let r: &Regs = unsafe { &*pac::Ostimer0::ptr() };
626
627 // Critical section to prevent races with schedule_wake
628 critical_section::with(|_| {
629 // Check if interrupt is actually pending and handle it atomically
630 if r.osevent_ctrl().read().ostimer_intrflag().bit_is_set() {
631 // Clear flag and disable interrupt atomically
632 r.osevent_ctrl().write(|w| {
633 w.ostimer_intrflag()
634 .clear_bit_by_one() // Write-1-to-clear using safe helper
635 .ostimer_intena()
636 .clear_bit()
637 });
638
639 // Wake the waiting task
640 TIMER_WAKER.wake();
641
642 // Handle alarm callback if active and this interrupt is for the alarm
643 if ALARM_ACTIVE.load(Ordering::SeqCst) {
644 let current_time = now_ticks_read();
645 let target_time = unsafe { ALARM_TARGET_TIME };
646
647 // Check if current time is close to alarm target time (within 1000 ticks for timing variations)
648 if current_time >= target_time && current_time <= target_time + 1000 {
649 ALARM_ACTIVE.store(false, Ordering::SeqCst);
650 unsafe { ALARM_TARGET_TIME = 0 };
651
652 // Execute callback if set
653 unsafe {
654 if let Some(callback) = ALARM_CALLBACK {
655 callback();
656 }
657 }
658
659 // Set flag if provided
660 unsafe {
661 if let Some(flag) = ALARM_FLAG {
662 (*flag).store(true, Ordering::SeqCst);
663 }
664 }
665 }
666 }
667 }
668 });
669 }
670
671 /// Type-level handler to be used with bind_interrupts! for OS_EVENT.
672 pub struct OsEventHandler;
673 impl crate::interrupt::typelevel::Handler<crate::interrupt::typelevel::OS_EVENT> for OsEventHandler {
674 unsafe fn on_interrupt() {
675 on_interrupt();
676 }
677 }
678}
diff --git a/src/pins.rs b/src/pins.rs
new file mode 100644
index 000000000..f802568f3
--- /dev/null
+++ b/src/pins.rs
@@ -0,0 +1,123 @@
1//! Pin configuration helpers (separate from peripheral drivers).
2use crate::pac;
3
4pub unsafe fn configure_uart2_pins_port2() {
5 // P2_2 = LPUART2_TX ALT3, P2_3 = LPUART2_RX ALT3 with pull-up, input enable, high drive, slow slew.
6 let port2 = &*pac::Port2::ptr();
7 port2.pcr2().write(|w| {
8 w.ps()
9 .ps1()
10 .pe()
11 .pe1()
12 .sre()
13 .sre1()
14 .dse()
15 .dse1()
16 .mux()
17 .mux11()
18 .ibe()
19 .ibe1()
20 });
21 port2.pcr3().write(|w| {
22 w.ps()
23 .ps1()
24 .pe()
25 .pe1()
26 .sre()
27 .sre1()
28 .dse()
29 .dse1()
30 .mux()
31 .mux11()
32 .ibe()
33 .ibe1()
34 });
35 core::arch::asm!("dsb sy; isb sy");
36}
37
38pub unsafe fn configure_adc_pins() {
39 // P1_10 = ADC1_A8
40 let port1 = &*pac::Port1::ptr();
41 port1.pcr10().write(|w| {
42 w.ps()
43 .ps0()
44 .pe()
45 .pe0()
46 .sre()
47 .sre0()
48 .ode()
49 .ode0()
50 .dse()
51 .dse0()
52 .mux()
53 .mux00()
54 .ibe()
55 .ibe0()
56 .inv()
57 .inv0()
58 .lk()
59 .lk0()
60 });
61 core::arch::asm!("dsb sy; isb sy");
62}
63
64/// Configure a pin for a specific mux alternative.
65///
66/// # Arguments
67/// * `port` - Port number (0-4)
68/// * `pin` - Pin number (varies by port: PORT0=0-7, PORT1=0-19, PORT2=0-26, PORT3=0-31, PORT4=0-7)
69/// * `mux` - Mux alternative (0-15, where 0 = GPIO, 1-15 = other functions)
70pub unsafe fn set_pin_mux(port: u8, pin: u8, mux: u8) {
71 // Validate mux value (0-15)
72 if mux > 15 {
73 panic!("Invalid mux value: {}, must be 0-15", mux);
74 }
75
76 // Validate pin number based on port
77 let max_pin = match port {
78 0 => 7, // PORT0: pins 0-7
79 1 => 19, // PORT1: pins 0-19
80 2 => 26, // PORT2: pins 0-26
81 3 => 31, // PORT3: pins 0-31
82 4 => 7, // PORT4: pins 0-7
83 _ => panic!("Unsupported GPIO port: {}", port),
84 };
85
86 if pin > max_pin {
87 panic!("Invalid pin {} for PORT{}, max pin is {}", pin, port, max_pin);
88 }
89
90 // Get the base address for the port
91 let port_base: *mut u32 = match port {
92 0 => pac::Port0::ptr() as *mut u32,
93 1 => pac::Port1::ptr() as *mut u32,
94 2 => pac::Port2::ptr() as *mut u32,
95 3 => pac::Port3::ptr() as *mut u32,
96 4 => pac::Port4::ptr() as *mut u32,
97 _ => panic!("Unsupported GPIO port: {}", port),
98 };
99
100 // PCR registers are 4 bytes apart, starting at offset 0 for PCR0
101 let pcr_addr = port_base.add(pin as usize);
102
103 // Read current PCR value
104 let current_val = pcr_addr.read_volatile();
105
106 // Clear mux bits (bits 8-11) and set new mux value
107 let new_val = (current_val & !(0xF << 8)) | ((mux as u32) << 8);
108
109 // Write back the new value
110 pcr_addr.write_volatile(new_val);
111
112 core::arch::asm!("dsb sy; isb sy");
113}
114
115/// Configure a pin for GPIO mode (ALT0).
116/// This is a convenience function that calls set_pin_mux with mux=0.
117///
118/// # Arguments
119/// * `port` - Port number (0-4)
120/// * `pin` - Pin number (varies by port: PORT0=0-7, PORT1=0-19, PORT2=0-26, PORT3=0-31, PORT4=0-7)
121pub unsafe fn set_pin_mux_gpio(port: u8, pin: u8) {
122 set_pin_mux(port, pin, 0);
123}
diff --git a/src/reset.rs b/src/reset.rs
new file mode 100644
index 000000000..1c131d1cc
--- /dev/null
+++ b/src/reset.rs
@@ -0,0 +1,112 @@
1//! Reset control helpers built on PAC field writers.
2use crate::pac;
3
4/// Trait describing a reset line that can be asserted/deasserted.
5pub trait ResetLine {
6 /// Drive the peripheral out of reset.
7 unsafe fn release(mrcc: &pac::mrcc0::RegisterBlock);
8
9 /// Drive the peripheral into reset.
10 unsafe fn assert(mrcc: &pac::mrcc0::RegisterBlock);
11
12 /// Check whether the peripheral is currently released.
13 fn is_released(mrcc: &pac::mrcc0::RegisterBlock) -> bool;
14}
15
16/// Release a reset line for the given peripheral set.
17#[inline]
18pub unsafe fn release<R: ResetLine>(peripherals: &pac::Peripherals) {
19 R::release(&peripherals.mrcc0);
20}
21
22/// Assert a reset line for the given peripheral set.
23#[inline]
24pub unsafe fn assert<R: ResetLine>(peripherals: &pac::Peripherals) {
25 R::assert(&peripherals.mrcc0);
26}
27
28/// Pulse a reset line (assert then release) with a short delay.
29#[inline]
30pub unsafe fn pulse<R: ResetLine>(peripherals: &pac::Peripherals) {
31 let mrcc = &peripherals.mrcc0;
32 R::assert(mrcc);
33 cortex_m::asm::nop();
34 cortex_m::asm::nop();
35 R::release(mrcc);
36}
37
38macro_rules! impl_reset_line {
39 ($name:ident, $reg:ident, $field:ident) => {
40 pub struct $name;
41
42 impl ResetLine for $name {
43 #[inline]
44 unsafe fn release(mrcc: &pac::mrcc0::RegisterBlock) {
45 mrcc.$reg().modify(|_, w| w.$field().enabled());
46 }
47
48 #[inline]
49 unsafe fn assert(mrcc: &pac::mrcc0::RegisterBlock) {
50 mrcc.$reg().modify(|_, w| w.$field().disabled());
51 }
52
53 #[inline]
54 fn is_released(mrcc: &pac::mrcc0::RegisterBlock) -> bool {
55 mrcc.$reg().read().$field().is_enabled()
56 }
57 }
58 };
59}
60
61pub mod line {
62 use super::*;
63
64 impl_reset_line!(Port2, mrcc_glb_rst1, port2);
65 impl_reset_line!(Port3, mrcc_glb_rst1, port3);
66 impl_reset_line!(Gpio3, mrcc_glb_rst2, gpio3);
67 impl_reset_line!(Lpuart2, mrcc_glb_rst0, lpuart2);
68 impl_reset_line!(Ostimer0, mrcc_glb_rst1, ostimer0);
69 impl_reset_line!(Port1, mrcc_glb_rst1, port1);
70 impl_reset_line!(Adc1, mrcc_glb_rst1, adc1);
71}
72
73#[inline]
74pub unsafe fn release_reset_port2(peripherals: &pac::Peripherals) {
75 release::<line::Port2>(peripherals);
76}
77
78#[inline]
79pub unsafe fn release_reset_port3(peripherals: &pac::Peripherals) {
80 release::<line::Port3>(peripherals);
81}
82
83#[inline]
84pub unsafe fn release_reset_gpio3(peripherals: &pac::Peripherals) {
85 release::<line::Gpio3>(peripherals);
86}
87
88#[inline]
89pub unsafe fn release_reset_lpuart2(peripherals: &pac::Peripherals) {
90 release::<line::Lpuart2>(peripherals);
91}
92
93#[inline]
94pub unsafe fn release_reset_ostimer0(peripherals: &pac::Peripherals) {
95 release::<line::Ostimer0>(peripherals);
96}
97
98/// Convenience shim retained for existing call sites.
99#[inline]
100pub unsafe fn reset_ostimer0(peripherals: &pac::Peripherals) {
101 pulse::<line::Ostimer0>(peripherals);
102}
103
104#[inline]
105pub unsafe fn release_reset_port1(peripherals: &pac::Peripherals) {
106 release::<line::Port1>(peripherals);
107}
108
109#[inline]
110pub unsafe fn release_reset_adc1(peripherals: &pac::Peripherals) {
111 release::<line::Adc1>(peripherals);
112}
diff --git a/src/rtc.rs b/src/rtc.rs
new file mode 100644
index 000000000..d62da1f0a
--- /dev/null
+++ b/src/rtc.rs
@@ -0,0 +1,284 @@
1//! RTC DateTime driver.
2use core::sync::atomic::{AtomicBool, Ordering};
3
4use crate::pac;
5use crate::pac::rtc0::cr::Um;
6
7type Regs = pac::rtc0::RegisterBlock;
8
9static ALARM_TRIGGERED: AtomicBool = AtomicBool::new(false);
10
11// Token-based instance pattern like embassy-imxrt
12pub trait Instance {
13 fn ptr() -> *const Regs;
14}
15
16/// Token for RTC0
17pub type Rtc0 = crate::peripherals::RTC0;
18impl Instance for crate::peripherals::RTC0 {
19 #[inline(always)]
20 fn ptr() -> *const Regs {
21 pac::Rtc0::ptr()
22 }
23}
24
25// Also implement Instance for the Peri wrapper type
26impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::RTC0> {
27 #[inline(always)]
28 fn ptr() -> *const Regs {
29 pac::Rtc0::ptr()
30 }
31}
32
33const DAYS_IN_A_YEAR: u32 = 365;
34const SECONDS_IN_A_DAY: u32 = 86400;
35const SECONDS_IN_A_HOUR: u32 = 3600;
36const SECONDS_IN_A_MINUTE: u32 = 60;
37const YEAR_RANGE_START: u16 = 1970;
38
39#[derive(Debug, Clone, Copy)]
40pub struct RtcDateTime {
41 pub year: u16,
42 pub month: u8,
43 pub day: u8,
44 pub hour: u8,
45 pub minute: u8,
46 pub second: u8,
47}
48#[derive(Copy, Clone)]
49pub struct RtcConfig {
50 #[allow(dead_code)]
51 wakeup_select: bool,
52 update_mode: Um,
53 #[allow(dead_code)]
54 supervisor_access: bool,
55 compensation_interval: u8,
56 compensation_time: u8,
57}
58
59#[derive(Copy, Clone)]
60pub struct RtcInterruptEnable;
61impl RtcInterruptEnable {
62 pub const RTC_TIME_INVALID_INTERRUPT_ENABLE: u32 = 1 << 0;
63 pub const RTC_TIME_OVERFLOW_INTERRUPT_ENABLE: u32 = 1 << 1;
64 pub const RTC_ALARM_INTERRUPT_ENABLE: u32 = 1 << 2;
65 pub const RTC_SECONDS_INTERRUPT_ENABLE: u32 = 1 << 4;
66}
67
68pub fn convert_datetime_to_seconds(datetime: &RtcDateTime) -> u32 {
69 let month_days: [u16; 13] = [0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
70
71 let mut seconds = (datetime.year as u32 - 1970) * DAYS_IN_A_YEAR;
72 seconds += (datetime.year as u32 / 4) - (1970 / 4);
73 seconds += month_days[datetime.month as usize] as u32;
74 seconds += datetime.day as u32 - 1;
75
76 if (datetime.year & 3 == 0) && (datetime.month <= 2) {
77 seconds -= 1;
78 }
79
80 seconds = seconds * SECONDS_IN_A_DAY
81 + (datetime.hour as u32 * SECONDS_IN_A_HOUR)
82 + (datetime.minute as u32 * SECONDS_IN_A_MINUTE)
83 + datetime.second as u32;
84
85 seconds
86}
87
88pub fn convert_seconds_to_datetime(seconds: u32) -> RtcDateTime {
89 let mut seconds_remaining = seconds;
90 let mut days = seconds_remaining / SECONDS_IN_A_DAY + 1;
91 seconds_remaining %= SECONDS_IN_A_DAY;
92
93 let hour = (seconds_remaining / SECONDS_IN_A_HOUR) as u8;
94 seconds_remaining %= SECONDS_IN_A_HOUR;
95 let minute = (seconds_remaining / SECONDS_IN_A_MINUTE) as u8;
96 let second = (seconds_remaining % SECONDS_IN_A_MINUTE) as u8;
97
98 let mut year = YEAR_RANGE_START;
99 let mut days_in_year = DAYS_IN_A_YEAR;
100
101 while days > days_in_year {
102 days -= days_in_year;
103 year += 1;
104
105 days_in_year = if year % 4 == 0 {
106 DAYS_IN_A_YEAR + 1
107 } else {
108 DAYS_IN_A_YEAR
109 };
110 }
111
112 let mut days_per_month = [0u8, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
113 if year % 4 == 0 {
114 days_per_month[2] = 29;
115 }
116
117 let mut month = 1;
118 for m in 1..=12 {
119 if days <= days_per_month[m] as u32 {
120 month = m;
121 break;
122 } else {
123 days -= days_per_month[m] as u32;
124 }
125 }
126
127 let day = days as u8;
128
129 RtcDateTime {
130 year,
131 month: month as u8,
132 day,
133 hour,
134 minute,
135 second,
136 }
137}
138
139pub fn get_default_config() -> RtcConfig {
140 RtcConfig {
141 wakeup_select: false,
142 update_mode: Um::Um0,
143 supervisor_access: false,
144 compensation_interval: 0,
145 compensation_time: 0,
146 }
147}
148/// Minimal RTC handle for a specific instance I (store the zero-sized token like embassy)
149pub struct Rtc<I: Instance> {
150 _inst: core::marker::PhantomData<I>,
151}
152
153impl<I: Instance> Rtc<I> {
154 /// initialize RTC
155 pub fn new(_inst: impl Instance, config: RtcConfig) -> Self {
156 let rtc = unsafe { &*I::ptr() };
157
158 /* RTC reset */
159 rtc.cr().modify(|_, w| w.swr().set_bit());
160 rtc.cr().modify(|_, w| w.swr().clear_bit());
161 rtc.tsr().write(|w| unsafe { w.bits(1) });
162
163 rtc.cr().modify(|_, w| w.um().variant(config.update_mode));
164
165 rtc.tcr().modify(|_, w| unsafe {
166 w.cir()
167 .bits(config.compensation_interval)
168 .tcr()
169 .bits(config.compensation_time)
170 });
171
172 Self {
173 _inst: core::marker::PhantomData,
174 }
175 }
176
177 pub fn set_datetime(&self, datetime: RtcDateTime) {
178 let rtc = unsafe { &*I::ptr() };
179 let seconds = convert_datetime_to_seconds(&datetime);
180 rtc.tsr().write(|w| unsafe { w.bits(seconds) });
181 }
182
183 pub fn get_datetime(&self) -> RtcDateTime {
184 let rtc = unsafe { &*I::ptr() };
185 let seconds = rtc.tsr().read().bits();
186 convert_seconds_to_datetime(seconds)
187 }
188
189 pub fn set_alarm(&self, alarm: RtcDateTime) {
190 let rtc = unsafe { &*I::ptr() };
191 let seconds = convert_datetime_to_seconds(&alarm);
192
193 rtc.tar().write(|w| unsafe { w.bits(0) });
194 let mut timeout = 10000;
195 while rtc.tar().read().bits() != 0 && timeout > 0 {
196 timeout -= 1;
197 }
198
199 rtc.tar().write(|w| unsafe { w.bits(seconds) });
200
201 let mut timeout = 10000;
202 while rtc.tar().read().bits() != seconds && timeout > 0 {
203 timeout -= 1;
204 }
205 }
206
207 pub fn get_alarm(&self) -> RtcDateTime {
208 let rtc = unsafe { &*I::ptr() };
209 let alarm_seconds = rtc.tar().read().bits();
210 convert_seconds_to_datetime(alarm_seconds)
211 }
212
213 pub fn start(&self) {
214 let rtc = unsafe { &*I::ptr() };
215 rtc.sr().modify(|_, w| w.tce().set_bit());
216 }
217
218 pub fn stop(&self) {
219 let rtc = unsafe { &*I::ptr() };
220 rtc.sr().modify(|_, w| w.tce().clear_bit());
221 }
222
223 pub fn set_interrupt(&self, mask: u32) {
224 let rtc = unsafe { &*I::ptr() };
225
226 if (RtcInterruptEnable::RTC_TIME_INVALID_INTERRUPT_ENABLE & mask) != 0 {
227 rtc.ier().modify(|_, w| w.tiie().tiie_1());
228 }
229 if (RtcInterruptEnable::RTC_TIME_OVERFLOW_INTERRUPT_ENABLE & mask) != 0 {
230 rtc.ier().modify(|_, w| w.toie().toie_1());
231 }
232 if (RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE & mask) != 0 {
233 rtc.ier().modify(|_, w| w.taie().taie_1());
234 }
235 if (RtcInterruptEnable::RTC_SECONDS_INTERRUPT_ENABLE & mask) != 0 {
236 rtc.ier().modify(|_, w| w.tsie().tsie_1());
237 }
238
239 ALARM_TRIGGERED.store(false, Ordering::SeqCst);
240 }
241
242 pub fn disable_interrupt(&self, mask: u32) {
243 let rtc = unsafe { &*I::ptr() };
244
245 if (RtcInterruptEnable::RTC_TIME_INVALID_INTERRUPT_ENABLE & mask) != 0 {
246 rtc.ier().modify(|_, w| w.tiie().tiie_0());
247 }
248 if (RtcInterruptEnable::RTC_TIME_OVERFLOW_INTERRUPT_ENABLE & mask) != 0 {
249 rtc.ier().modify(|_, w| w.toie().toie_0());
250 }
251 if (RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE & mask) != 0 {
252 rtc.ier().modify(|_, w| w.taie().taie_0());
253 }
254 if (RtcInterruptEnable::RTC_SECONDS_INTERRUPT_ENABLE & mask) != 0 {
255 rtc.ier().modify(|_, w| w.tsie().tsie_0());
256 }
257 }
258
259 pub fn clear_alarm_flag(&self) {
260 let rtc = unsafe { &*I::ptr() };
261 rtc.ier().modify(|_, w| w.taie().clear_bit());
262 }
263
264 pub fn is_alarm_triggered(&self) -> bool {
265 ALARM_TRIGGERED.load(Ordering::Relaxed)
266 }
267}
268
269pub fn on_interrupt() {
270 let rtc = unsafe { &*pac::Rtc0::ptr() };
271 // Check if this is actually a time alarm interrupt
272 let sr = rtc.sr().read();
273 if sr.taf().bit_is_set() {
274 rtc.ier().modify(|_, w| w.taie().clear_bit());
275 ALARM_TRIGGERED.store(true, Ordering::SeqCst);
276 }
277}
278
279pub struct RtcHandler;
280impl crate::interrupt::typelevel::Handler<crate::interrupt::typelevel::RTC> for RtcHandler {
281 unsafe fn on_interrupt() {
282 on_interrupt();
283 }
284}
diff --git a/src/uart.rs b/src/uart.rs
new file mode 100644
index 000000000..3209a318d
--- /dev/null
+++ b/src/uart.rs
@@ -0,0 +1,306 @@
1//! Minimal polling UART2 bring-up replicating MCUXpresso hello_world ordering.
2//! WARNING: This is a narrow implementation only for debug console (115200 8N1).
3
4use core::cell::RefCell;
5
6use cortex_m::interrupt::Mutex;
7use embassy_sync::signal::Signal;
8
9use crate::pac;
10
11// svd2rust defines the shared LPUART RegisterBlock under lpuart0; all instances reuse it.
12type Regs = pac::lpuart0::RegisterBlock;
13
14// Token-based instance pattern like embassy-imxrt
15pub trait Instance {
16 fn ptr() -> *const Regs;
17}
18
19/// Token for LPUART2 provided by embassy-hal-internal peripherals macro.
20pub type Lpuart2 = crate::peripherals::LPUART2;
21impl Instance for crate::peripherals::LPUART2 {
22 #[inline(always)]
23 fn ptr() -> *const Regs {
24 pac::Lpuart2::ptr()
25 }
26}
27
28// Also implement Instance for the Peri wrapper type
29impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::LPUART2> {
30 #[inline(always)]
31 fn ptr() -> *const Regs {
32 pac::Lpuart2::ptr()
33 }
34}
35
36/// UART configuration (explicit src_hz; no hardcoded frequencies)
37#[derive(Copy, Clone)]
38pub struct Config {
39 pub src_hz: u32,
40 pub baud: u32,
41 pub parity: Parity,
42 pub stop_bits: StopBits,
43}
44
45#[derive(Copy, Clone)]
46pub enum Parity {
47 None,
48 Even,
49 Odd,
50}
51#[derive(Copy, Clone)]
52pub enum StopBits {
53 One,
54 Two,
55}
56
57impl Config {
58 pub fn new(src_hz: u32) -> Self {
59 Self {
60 src_hz,
61 baud: 115_200,
62 parity: Parity::None,
63 stop_bits: StopBits::One,
64 }
65 }
66}
67
68/// Compute a valid (OSR, SBR) tuple for given source clock and baud.
69/// Uses a functional fold approach to find the best OSR/SBR combination
70/// with minimal baud rate error.
71fn compute_osr_sbr(src_hz: u32, baud: u32) -> (u8, u16) {
72 let (best_osr, best_sbr, _best_err) = (8u32..=32).fold(
73 (16u8, 4u16, u32::MAX), // (best_osr, best_sbr, best_err)
74 |(best_osr, best_sbr, best_err), osr| {
75 let denom = baud.saturating_mul(osr);
76 if denom == 0 {
77 return (best_osr, best_sbr, best_err);
78 }
79
80 let sbr = (src_hz + denom / 2) / denom; // round
81 if sbr == 0 || sbr > 0x1FFF {
82 return (best_osr, best_sbr, best_err);
83 }
84
85 let actual = src_hz / (osr * sbr);
86 let err = actual.abs_diff(baud);
87
88 // Update best if this is better, or same error but higher OSR
89 if err < best_err || (err == best_err && osr as u8 > best_osr) {
90 (osr as u8, sbr as u16, err)
91 } else {
92 (best_osr, best_sbr, best_err)
93 }
94 },
95 );
96 (best_osr, best_sbr)
97}
98
99/// Minimal UART handle for a specific instance I (store the zero-sized token like embassy)
100pub struct Uart<I: Instance> {
101 _inst: core::marker::PhantomData<I>,
102}
103
104impl<I: Instance> Uart<I> {
105 /// Create and initialize LPUART (reset + config). Clocks and pins must be prepared by the caller.
106 pub fn new(_inst: impl Instance, cfg: Config) -> Self {
107 let l = unsafe { &*I::ptr() };
108 // 1) software reset pulse
109 l.global().write(|w| w.rst().reset());
110 cortex_m::asm::delay(3); // Short delay for reset to take effect
111 l.global().write(|w| w.rst().no_effect());
112 cortex_m::asm::delay(10); // Allow peripheral to stabilize after reset
113 // 2) BAUD
114 let (osr, sbr) = compute_osr_sbr(cfg.src_hz, cfg.baud);
115 l.baud().modify(|_, w| {
116 let w = match cfg.stop_bits {
117 StopBits::One => w.sbns().one(),
118 StopBits::Two => w.sbns().two(),
119 };
120 // OSR field encodes (osr-1); use raw bits to avoid a long match on all variants
121 let raw_osr = osr.saturating_sub(1) as u8;
122 unsafe { w.osr().bits(raw_osr).sbr().bits(sbr) }
123 });
124 // 3) CTRL baseline and parity
125 l.ctrl().write(|w| {
126 let w = w.ilt().from_stop().idlecfg().idle_2();
127 let w = match cfg.parity {
128 Parity::None => w.pe().disabled(),
129 Parity::Even => w.pe().enabled().pt().even(),
130 Parity::Odd => w.pe().enabled().pt().odd(),
131 };
132 w.re().enabled().te().enabled().rie().disabled()
133 });
134 // 4) FIFOs and WATER: keep it simple for polling; disable FIFOs and set RX watermark to 0
135 l.fifo().modify(|_, w| {
136 w.txfe()
137 .disabled()
138 .rxfe()
139 .disabled()
140 .txflush()
141 .txfifo_rst()
142 .rxflush()
143 .rxfifo_rst()
144 });
145 l.water()
146 .modify(|_, w| unsafe { w.txwater().bits(0).rxwater().bits(0) });
147 Self {
148 _inst: core::marker::PhantomData,
149 }
150 }
151
152 /// Enable RX interrupts. The caller must ensure an appropriate IRQ handler is installed.
153 pub unsafe fn enable_rx_interrupts(&self) {
154 let l = &*I::ptr();
155 l.ctrl().modify(|_, w| w.rie().enabled());
156 }
157
158 #[inline(never)]
159 pub fn write_byte(&self, b: u8) {
160 let l = unsafe { &*I::ptr() };
161 // Timeout after ~10ms at 12MHz (assuming 115200 baud, should be plenty)
162 const DATA_OFFSET: usize = 0x1C; // DATA register offset inside LPUART block
163 let data_ptr = unsafe { (I::ptr() as *mut u8).add(DATA_OFFSET) };
164 for _ in 0..120000 {
165 if l.water().read().txcount().bits() == 0 {
166 unsafe { core::ptr::write_volatile(data_ptr, b) };
167 return;
168 }
169 }
170 // If timeout, skip the write to avoid hanging
171 }
172
173 #[inline(never)]
174 pub fn write_str_blocking(&self, s: &str) {
175 for &b in s.as_bytes() {
176 if b == b'\n' {
177 self.write_byte(b'\r');
178 }
179 self.write_byte(b);
180 }
181 }
182 pub fn read_byte_blocking(&self) -> u8 {
183 let l = unsafe { &*I::ptr() };
184 while !l.stat().read().rdrf().is_rxdata() {}
185 (l.data().read().bits() & 0xFF) as u8
186 }
187}
188
189// Simple ring buffer for UART RX data
190const RX_BUFFER_SIZE: usize = 256;
191pub struct RingBuffer {
192 buffer: [u8; RX_BUFFER_SIZE],
193 read_idx: usize,
194 write_idx: usize,
195 count: usize,
196}
197
198impl RingBuffer {
199 pub const fn new() -> Self {
200 Self {
201 buffer: [0; RX_BUFFER_SIZE],
202 read_idx: 0,
203 write_idx: 0,
204 count: 0,
205 }
206 }
207
208 pub fn push(&mut self, data: u8) -> bool {
209 if self.count >= RX_BUFFER_SIZE {
210 return false; // Buffer full
211 }
212 self.buffer[self.write_idx] = data;
213 self.write_idx = (self.write_idx + 1) % RX_BUFFER_SIZE;
214 self.count += 1;
215 true
216 }
217
218 pub fn pop(&mut self) -> Option<u8> {
219 if self.count == 0 {
220 return None;
221 }
222 let data = self.buffer[self.read_idx];
223 self.read_idx = (self.read_idx + 1) % RX_BUFFER_SIZE;
224 self.count -= 1;
225 Some(data)
226 }
227
228 pub fn is_empty(&self) -> bool {
229 self.count == 0
230 }
231
232 pub fn len(&self) -> usize {
233 self.count
234 }
235}
236
237// Global RX buffer shared between interrupt handler and UART instance
238static RX_BUFFER: Mutex<RefCell<RingBuffer>> = Mutex::new(RefCell::new(RingBuffer::new()));
239static RX_SIGNAL: Signal<embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex, ()> = Signal::new();
240
241// Debug counter for interrupt handler calls
242static mut INTERRUPT_COUNT: u32 = 0;
243
244impl<I: Instance> Uart<I> {
245 /// Read a byte asynchronously using interrupts
246 pub async fn read_byte_async(&self) -> u8 {
247 loop {
248 // Check if we have data in the buffer
249 let byte = cortex_m::interrupt::free(|cs| {
250 let mut buffer = RX_BUFFER.borrow(cs).borrow_mut();
251 buffer.pop()
252 });
253
254 if let Some(byte) = byte {
255 return byte;
256 }
257
258 // Wait for the interrupt signal
259 RX_SIGNAL.wait().await;
260 }
261 }
262
263 /// Check if there's data available in the RX buffer
264 pub fn rx_data_available(&self) -> bool {
265 cortex_m::interrupt::free(|cs| {
266 let buffer = RX_BUFFER.borrow(cs).borrow();
267 !buffer.is_empty()
268 })
269 }
270
271 /// Try to read a byte from RX buffer (non-blocking)
272 pub fn try_read_byte(&self) -> Option<u8> {
273 cortex_m::interrupt::free(|cs| {
274 let mut buffer = RX_BUFFER.borrow(cs).borrow_mut();
275 buffer.pop()
276 })
277 }
278}
279
280/// Type-level handler for LPUART2 interrupts, compatible with bind_interrupts!.
281pub struct UartInterruptHandler;
282
283impl crate::interrupt::typelevel::Handler<crate::interrupt::typelevel::LPUART2> for UartInterruptHandler {
284 unsafe fn on_interrupt() {
285 INTERRUPT_COUNT += 1;
286
287 let lpuart = &*pac::Lpuart2::ptr();
288
289 // Check if we have RX data
290 if lpuart.stat().read().rdrf().is_rxdata() {
291 // Read the data byte
292 let data = (lpuart.data().read().bits() & 0xFF) as u8;
293
294 // Store in ring buffer
295 cortex_m::interrupt::free(|cs| {
296 let mut buffer = RX_BUFFER.borrow(cs).borrow_mut();
297 if buffer.push(data) {
298 // Data added successfully, signal waiting tasks
299 RX_SIGNAL.signal(());
300 }
301 });
302 }
303 // Always clear any error flags that might cause spurious interrupts
304 let _ = lpuart.stat().read();
305 }
306}
diff --git a/supply-chain/README.md b/supply-chain/README.md
index d43d50485..12f8777b0 100644
--- a/supply-chain/README.md
+++ b/supply-chain/README.md
@@ -1,83 +1,149 @@
1# Working with cargo vet 1# Working with cargo vet
2 2
3## Introduction 3## Introduction
4 4
5`cargo vet` is a tool to help ensure that third-party Rust dependencies have been audited by a trusted entity. 5`cargo vet` is a tool to help ensure that third-party Rust dependencies have been audited by a trusted entity.
6It matches all dependencies against a set of audits conducted by the authors of the project or entities they trust. 6It matches all dependencies against a set of audits conducted by the authors of the project or entities they trust.
7To learn more, visit [mozilla/cargo-vet](https://github.com/mozilla/cargo-vet) 7To learn more, visit [mozilla/cargo-vet](https://github.com/mozilla/cargo-vet)
8 8
9--- 9---
10 10
11## Adding a new dependency 11## Adding a new dependency
12 12
13When updating or adding a new dependency, we need to ensure it's audited before being merged into main. 13When updating or adding a new dependency, we need to ensure it's audited before being merged into main.
14For our repositories, we have designated experts who are responsible for vetting any new dependencies being added to their repository. 14For our repositories, we have designated experts who are responsible for vetting any new dependencies being added to their repository.
15_It is the shared responsibility of the developer creating the PR and the auditors to conduct a successful audit._ 15_It is the shared responsibility of the developer creating the PR and the auditors to conduct a successful audit._
16Follow the process below to ensure compliance: 16Follow the process below to ensure compliance:
17 17
18### For Developers 18### For Developers
191. **Respond to `cargo vet` failures**: 191. **Respond to `cargo vet` failures**:
20 - If your PR fails the `cargo vet` step, the cargo-vet workflow will add a comment to the PR with a template questionnaire 20 - If your PR fails the `cargo vet` step, the cargo-vet workflow will add a comment to the PR with a template questionnaire
21 - Copy the questionnaire, fill it out and paste it as a new comment on the PR. This greatly helps the auditors get some context of the changes requiring the new dependencies 21 - Copy the questionnaire, fill it out and paste it as a new comment on the PR. This greatly helps the auditors get some context of the changes requiring the new dependencies
22 22
232. **Engage with auditors**: 232. **Engage with auditors**:
24 - Respond to any questions that the auditors might have regarding the need of any new dependencies 24 - Respond to any questions that the auditors might have regarding the need of any new dependencies
25 25
263. **Rebase and verify**: 263. **Rebase and verify**:
27 - At their discretion, auditors will check in their audits into either [rust-crate-audits](https://github.com/OpenDevicePartnership/rust-crate-audits) or into the same repository 27 - At their discretion, auditors will check in their audits into either [rust-crate-audits](https://github.com/OpenDevicePartnership/rust-crate-audits) or into the same repository
28 - Once the new audits have been merged, rebase your branch on main and verify it passes `cargo vet` 28 - Once the new audits have been merged, rebase your branch on main and verify it passes `cargo vet`
29 ```bash 29 ```bash
30 git fetch upstream 30 git fetch upstream
31 git rebase upstream/main 31 git rebase upstream/main
32 cargo vet 32 cargo vet
33 ``` 33 ```
34 34
354. **Update PR**: 354. **Update PR**:
36 - If the audits were checked into rust-crate-audits, they will show up in _imports.lock_ on running `cargo vet`. In this case add the updated _imports.lock_ to your PR 36 - If the audits were checked into rust-crate-audits, they will show up in _imports.lock_ on running `cargo vet`. In this case add the updated _imports.lock_ to your PR
37 - If the audits were checked into the same repository, they will be present in _audits.toml_ after rebase and you can simply force push to your PR after rebase 37 - If the audits were checked into the same repository, they will be present in _audits.toml_ after rebase and you can simply force push to your PR after rebase
38 ```bash 38 ```bash
39 git push -f 39 git push -f
40 ``` 40 ```
41 41
425. **Check PR status**: 425. **Check PR status**:
43 - The existing PR comment from the previous failure will be updated with a success message once the check passes 43 - The existing PR comment from the previous failure will be updated with a success message once the check passes
44 44
45### For Auditors 45### For Auditors
46 46
471. **Review the questionnaire**: 471. **Review the questionnaire**:
48 - Check the filled questionnaire on the PR once the developer responds to the `cargo vet` failure 48 - Check the filled questionnaire on the PR once the developer responds to the `cargo vet` failure
49 - Respond to the developer comment in case more information is needed 49 - Respond to the developer comment in case more information is needed
50 50
512. **Audit new dependencies**: 512. **Audit new dependencies**:
52 - Inspect the `cargo vet` failures using your preferred method 52 - Inspect the `cargo vet` failures using your preferred method
53 - Use [gh pr checkout](https://cli.github.com/manual/gh_pr_checkout) to checkout the PR and run `cargo vet --locked` 53 - Use [gh pr checkout](https://cli.github.com/manual/gh_pr_checkout) to checkout the PR and run `cargo vet --locked`
54 - Use [Github Pull Requests for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-pull-request-github) to checkout the PR and run `cargo vet --locked` 54 - Use [Github Pull Requests for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-pull-request-github) to checkout the PR and run `cargo vet --locked`
55 - For more suggestions: [Checking out pull requests locally](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/checking-out-pull-requests-locally) 55 - For more suggestions: [Checking out pull requests locally](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/checking-out-pull-requests-locally)
56 56
573. **Follow `cargo vet` recommendations**: 573. **Follow `cargo vet` recommendations**:
58 - Follow the recommendations of the `cargo vet` command output, either `cargo vet diff` for version update or `cargo vet inspect` for new dependencies 58 - Follow the recommendations of the `cargo vet` command output, either `cargo vet diff` for version update or `cargo vet inspect` for new dependencies
59 59
604. **Record audits**: 604. **Record audits**:
61 - Use `cargo vet certify` to add new audits to _audits.toml_ 61 - Use `cargo vet certify` to add new audits to _audits.toml_
62 - Verify all dependencies pass using `cargo vet` 62 - Verify all dependencies pass using `cargo vet`
63 63
645. **Decide audit location**: 645. **Decide audit location**:
65 - **Shared audits**: New audits should ideally be shared across ODP repositories to reduce the overhead of multiple audits for the same dependencies. To facilitate this, it's recommended to cut and paste the new audits and submit as a separate PR to the _audits.toml_ in [rust-crate-audits](https://github.com/OpenDevicePartnership/rust-crate-audits) 65 - **Shared audits**: New audits should ideally be shared across ODP repositories to reduce the overhead of multiple audits for the same dependencies. To facilitate this, it's recommended to cut and paste the new audits and submit as a separate PR to the _audits.toml_ in [rust-crate-audits](https://github.com/OpenDevicePartnership/rust-crate-audits)
66 - If due to business reasons, the audits are not to be shared across repositories, copy the updated _audits.toml_ to a new branch off main in the same repository and submit the PR to update the audits 66 - If due to business reasons, the audits are not to be shared across repositories, copy the updated _audits.toml_ to a new branch off main in the same repository and submit the PR to update the audits
67 67
686. **Communicate successful audit**: 686. **Communicate successful audit**:
69 - Communicate to the PR developer via a PR comment so they can update the PR and get `cargo vet` to pass 69 - Communicate to the PR developer via a PR comment so they can update the PR and get `cargo vet` to pass
70 70
71--- 71---
72 72
73## Tips for using `cargo vet`: 73## Audit criteria
74 74`cargo vet` comes pre-equipped with two built-in criteria but supports adding new criteria to suit our needs.
75- **Update _imports.lock_**: 75As defined [here](https://mozilla.github.io/cargo-vet/built-in-criteria.html), the default criteria are:
76 - Import trusted third party audits to reduce the number of new audits to be performed. Running `cargo vet` without `--locked` fetches new imports and updates _imports.lock_ with any audits that are helpful for our project. 76
77 77- **safe-to-run**
78- **Add exemptions**: 78 This crate can be compiled, run, and tested on a local workstation or in
79 - If an audit cannot be performed for some dependency due to time sensitivity or business justified reasons, use `cargo vet add-exemption <PACKAGE> <VERSION>` to add the dependency to exemptions in _config.toml_ 79 controlled automation without surprising consequences, such as:
80 - To add all remaining audits to exemptions at once, use `cargo vet regenerate exemptions` 80 * Reading or writing data from sensitive or unrelated parts of the filesystem.
81 81 * Installing software or reconfiguring the device.
82- **Prune unnecessary entries**: 82 * Connecting to untrusted network endpoints.
83 * Misuse of system resources (e.g. cryptocurrency mining).
84
85- **safe-to-deploy**
86 This crate will not introduce a serious security vulnerability to production
87 software exposed to untrusted input.
88
89 Auditors are not required to perform a full logic review of the entire crate.
90 Rather, they must review enough to fully reason about the behavior of all unsafe
91 blocks and usage of powerful imports. For any reasonable usage of the crate in
92 real-world software, an attacker must not be able to manipulate the runtime
93 behavior of these sections in an exploitable or surprising way.
94
95 Ideally, all unsafe code is fully sound, and ambient capabilities (e.g.
96 filesystem access) are hardened against manipulation and consistent with the
97 advertised behavior of the crate. However, some discretion is permitted. In such
98 cases, the nature of the discretion should be recorded in the `notes` field of
99 the audit record.
100
101 For crates which generate deployed code (e.g. build dependencies or procedural
102 macros), reasonable usage of the crate should output code which meets the above
103 criteria.
104
105 **Note: `safe-to-deploy` implies `safe-to-run`**
106
107---
108
109## Conducting an audit
110
111When performing an audit for a new or updated dependency, auditors may consider the following criteria to ensure the safety, reliability, and suitability of the crate for use in our projects:
112
113- **Security**:
114 - Review the crate for known vulnerabilities or security advisories.
115 - Check for unsafe code usage and ensure it is justified and well-documented.
116 - Evaluate the crate’s history of security issues and responsiveness to reported problems.
117
118- **Maintenance and Activity**:
119 - Assess the frequency of updates and the responsiveness of maintainers to issues and pull requests.
120 - Prefer crates that are actively maintained and have a healthy contributor base.
121
122- **License Compliance**:
123 - Verify that the crate’s license is compatible with our project’s licensing requirements.
124
125- **Community Trust and Adoption**:
126 - Consider the crate’s adoption in the wider Rust ecosystem.
127 - Prefer crates that are widely used and trusted by the community.
128
129- **Functionality and Suitability**:
130 - Confirm that the crate provides the required functionality without unnecessary features or bloat.
131 - Evaluate whether the crate’s API is stable and unlikely to introduce breaking changes unexpectedly.
132
133- **Audit Trail**:
134 - Record the audit decision, including any concerns, mitigations, or recommendations for future updates.
135 - If exemptions are granted, document the justification and any follow-up actions required.
136
137---
138
139## Tips for using `cargo vet`:
140
141- **Update _imports.lock_**:
142 - Import trusted third party audits to reduce the number of new audits to be performed. Running `cargo vet` without `--locked` fetches new imports and updates _imports.lock_ with any audits that are helpful for our project.
143
144- **Add exemptions**:
145 - If an audit cannot be performed for some dependency due to time sensitivity or business justified reasons, use `cargo vet add-exemption <PACKAGE> <VERSION>` to add the dependency to exemptions in _config.toml_
146 - To add all remaining audits to exemptions at once, use `cargo vet regenerate exemptions`
147
148- **Prune unnecessary entries**:
83 - Remove unnecessary exemptions and imports using `cargo vet prune` \ No newline at end of file 149 - Remove unnecessary exemptions and imports using `cargo vet prune` \ No newline at end of file
diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml
index 2772ccb21..60ebedf9b 100644
--- a/supply-chain/audits.toml
+++ b/supply-chain/audits.toml
@@ -1,4 +1,128 @@
1 1
2# cargo-vet audits file 2# cargo-vet audits file
3 3
4[audits] 4[[audits.autocfg]]
5who = "Felipe Balbi <[email protected]>"
6criteria = "safe-to-deploy"
7version = "1.5.0"
8
9[[audits.cfg-if]]
10who = "Felipe Balbi <[email protected]>"
11criteria = "safe-to-deploy"
12version = "1.0.4"
13
14[[audits.darling]]
15who = "Felipe Balbi <[email protected]>"
16criteria = "safe-to-deploy"
17version = "0.20.11"
18
19[[audits.darling_core]]
20who = "Felipe Balbi <[email protected]>"
21criteria = "safe-to-deploy"
22version = "0.20.11"
23
24[[audits.darling_macro]]
25who = "Felipe Balbi <[email protected]>"
26criteria = "safe-to-deploy"
27version = "0.20.11"
28
29[[audits.defmt-rtt]]
30who = "Felipe Balbi <[email protected]>"
31criteria = "safe-to-deploy"
32version = "1.0.0"
33notes = "defmt-rtt is used for all our logging purposes. Version 1.0.0 merely stabilizes what was already available previously."
34
35[[audits.defmt-rtt]]
36who = "Felipe Balbi <[email protected]>"
37criteria = "safe-to-deploy"
38delta = "1.0.0 -> 1.1.0"
39
40[[audits.document-features]]
41who = "Felipe Balbi <[email protected]>"
42criteria = "safe-to-deploy"
43version = "0.2.12"
44
45[[audits.document-features]]
46who = "Felipe Balbi <[email protected]>"
47criteria = "safe-to-run"
48version = "0.2.12"
49
50[[audits.embassy-executor]]
51who = "Felipe Balbi <[email protected]>"
52criteria = "safe-to-deploy"
53version = "0.9.1"
54
55[[audits.embassy-executor-macros]]
56who = "Felipe Balbi <[email protected]>"
57criteria = "safe-to-deploy"
58version = "0.7.0"
59
60[[audits.embassy-executor-timer-queue]]
61who = "Felipe Balbi <[email protected]>"
62criteria = "safe-to-deploy"
63version = "0.1.0"
64
65[[audits.embassy-executor-timer-queue]]
66who = "Felipe Balbi <[email protected]>"
67criteria = "safe-to-deploy"
68version = "0.1.0"
69
70[[audits.embassy-time-queue-utils]]
71who = "Felipe Balbi <[email protected]>"
72criteria = "safe-to-deploy"
73version = "0.3.0"
74
75[[audits.ident_case]]
76who = "Felipe Balbi <[email protected]>"
77criteria = "safe-to-deploy"
78version = "1.0.1"
79
80[[audits.litrs]]
81who = "Felipe Balbi <[email protected]>"
82criteria = "safe-to-deploy"
83version = "1.0.0"
84
85[[audits.panic-probe]]
86who = "Felipe Balbi <[email protected]>"
87criteria = "safe-to-deploy"
88version = "1.0.0"
89
90[[audits.proc-macro2]]
91who = "Felipe Balbi <[email protected]>"
92criteria = "safe-to-deploy"
93version = "1.0.103"
94
95[[audits.quote]]
96who = "Felipe Balbi <[email protected]>"
97criteria = "safe-to-deploy"
98version = "1.0.42"
99
100[[audits.stable_deref_trait]]
101who = "Felipe Balbi <[email protected]>"
102criteria = "safe-to-deploy"
103version = "1.2.1"
104
105[[audits.static_cell]]
106who = "jerrysxie <[email protected]>"
107criteria = "safe-to-run"
108delta = "2.1.0 -> 2.1.1"
109
110[[audits.syn]]
111who = "Felipe Balbi <[email protected]>"
112criteria = "safe-to-deploy"
113delta = "2.0.100 -> 2.0.109"
114
115[[audits.thiserror]]
116who = "Felipe Balbi <[email protected]>"
117criteria = "safe-to-deploy"
118version = "2.0.17"
119
120[[audits.thiserror-impl]]
121who = "Felipe Balbi <[email protected]>"
122criteria = "safe-to-deploy"
123version = "2.0.17"
124
125[[audits.unicode-ident]]
126who = "Felipe Balbi <[email protected]>"
127criteria = "safe-to-deploy"
128version = "1.0.22"
diff --git a/supply-chain/config.toml b/supply-chain/config.toml
index 55618c2ff..5927b0b61 100644
--- a/supply-chain/config.toml
+++ b/supply-chain/config.toml
@@ -12,3 +12,187 @@ url = "https://raw.githubusercontent.com/google/rust-crate-audits/main/audits.to
12 12
13[imports.mozilla] 13[imports.mozilla]
14url = "https://raw.githubusercontent.com/mozilla/supply-chain/main/audits.toml" 14url = "https://raw.githubusercontent.com/mozilla/supply-chain/main/audits.toml"
15
16[[exemptions.az]]
17version = "1.2.1"
18criteria = "safe-to-deploy"
19
20[[exemptions.bare-metal]]
21version = "0.2.5"
22criteria = "safe-to-deploy"
23
24[[exemptions.bitfield]]
25version = "0.13.2"
26criteria = "safe-to-deploy"
27
28[[exemptions.bitfield]]
29version = "0.15.0"
30criteria = "safe-to-deploy"
31
32[[exemptions.chrono]]
33version = "0.4.40"
34criteria = "safe-to-deploy"
35
36[[exemptions.cortex-m]]
37version = "0.7.7"
38criteria = "safe-to-deploy"
39
40[[exemptions.cortex-m-rt]]
41version = "0.7.5"
42criteria = "safe-to-deploy"
43
44[[exemptions.cortex-m-rt-macros]]
45version = "0.7.5"
46criteria = "safe-to-deploy"
47
48[[exemptions.critical-section]]
49version = "1.2.0"
50criteria = "safe-to-deploy"
51
52[[exemptions.defmt]]
53version = "1.0.1"
54criteria = "safe-to-deploy"
55
56[[exemptions.defmt-macros]]
57version = "1.0.1"
58criteria = "safe-to-deploy"
59
60[[exemptions.defmt-parser]]
61version = "1.0.0"
62criteria = "safe-to-deploy"
63
64[[exemptions.embassy-embedded-hal]]
65version = "0.5.0"
66criteria = "safe-to-deploy"
67
68[[exemptions.embassy-futures]]
69version = "0.1.2"
70criteria = "safe-to-deploy"
71
72[[exemptions.embassy-hal-internal]]
73version = "0.3.0"
74criteria = "safe-to-deploy"
75
76[[exemptions.embassy-sync]]
77version = "0.7.2"
78criteria = "safe-to-deploy"
79
80[[exemptions.embassy-time]]
81version = "0.5.0"
82criteria = "safe-to-deploy"
83
84[[exemptions.embassy-time-driver]]
85version = "0.2.1"
86criteria = "safe-to-deploy"
87
88[[exemptions.embedded-hal]]
89version = "0.2.7"
90criteria = "safe-to-deploy"
91
92[[exemptions.embedded-hal]]
93version = "1.0.0"
94criteria = "safe-to-deploy"
95
96[[exemptions.embedded-hal-async]]
97version = "1.0.0"
98criteria = "safe-to-deploy"
99
100[[exemptions.embedded-hal-nb]]
101version = "1.0.0"
102criteria = "safe-to-deploy"
103
104[[exemptions.embedded-io]]
105version = "0.6.1"
106criteria = "safe-to-deploy"
107
108[[exemptions.embedded-io-async]]
109version = "0.6.1"
110criteria = "safe-to-deploy"
111
112[[exemptions.embedded-storage]]
113version = "0.3.1"
114criteria = "safe-to-deploy"
115
116[[exemptions.embedded-storage-async]]
117version = "0.4.1"
118criteria = "safe-to-deploy"
119
120[[exemptions.fixed]]
121version = "1.29.0"
122criteria = "safe-to-deploy"
123
124[[exemptions.futures-core]]
125version = "0.3.31"
126criteria = "safe-to-deploy"
127
128[[exemptions.futures-sink]]
129version = "0.3.31"
130criteria = "safe-to-deploy"
131
132[[exemptions.hash32]]
133version = "0.3.1"
134criteria = "safe-to-deploy"
135
136[[exemptions.heapless]]
137version = "0.8.0"
138criteria = "safe-to-deploy"
139
140[[exemptions.itertools]]
141version = "0.11.0"
142criteria = "safe-to-deploy"
143
144[[exemptions.log]]
145version = "0.4.27"
146criteria = "safe-to-deploy"
147
148[[exemptions.mimxrt600-fcb]]
149version = "0.2.1"
150criteria = "safe-to-deploy"
151
152[[exemptions.paste]]
153version = "1.0.15"
154criteria = "safe-to-deploy"
155
156[[exemptions.portable-atomic]]
157version = "1.11.0"
158criteria = "safe-to-run"
159
160[[exemptions.proc-macro-error-attr2]]
161version = "2.0.0"
162criteria = "safe-to-deploy"
163
164[[exemptions.proc-macro-error2]]
165version = "2.0.1"
166criteria = "safe-to-deploy"
167
168[[exemptions.rustc_version]]
169version = "0.2.3"
170criteria = "safe-to-deploy"
171
172[[exemptions.semver]]
173version = "0.9.0"
174criteria = "safe-to-deploy"
175
176[[exemptions.semver-parser]]
177version = "0.7.0"
178criteria = "safe-to-deploy"
179
180[[exemptions.static_cell]]
181version = "2.1.0"
182criteria = "safe-to-run"
183
184[[exemptions.syn]]
185version = "2.0.100"
186criteria = "safe-to-deploy"
187
188[[exemptions.typenum]]
189version = "1.18.0"
190criteria = "safe-to-deploy"
191
192[[exemptions.vcell]]
193version = "0.1.3"
194criteria = "safe-to-deploy"
195
196[[exemptions.volatile-register]]
197version = "0.2.2"
198criteria = "safe-to-deploy"
diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock
index 219dba4ee..3f541e59f 100644
--- a/supply-chain/imports.lock
+++ b/supply-chain/imports.lock
@@ -3,6 +3,470 @@
3 3
4[audits.OpenDevicePartnership.audits] 4[audits.OpenDevicePartnership.audits]
5 5
6[audits.google.audits] 6[[audits.google.audits.autocfg]]
7who = "Manish Goregaokar <[email protected]>"
8criteria = "safe-to-deploy"
9version = "1.4.0"
10notes = "Contains no unsafe"
11aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
7 12
8[audits.mozilla.audits] 13[[audits.google.audits.bitflags]]
14who = "Lukasz Anforowicz <[email protected]>"
15criteria = "safe-to-deploy"
16version = "1.3.2"
17notes = """
18Security review of earlier versions of the crate can be found at
19(Google-internal, sorry): go/image-crate-chromium-security-review
20
21The crate exposes a function marked as `unsafe`, but doesn't use any
22`unsafe` blocks (except for tests of the single `unsafe` function). I
23think this justifies marking this crate as `ub-risk-1`.
24
25Additional review comments can be found at https://crrev.com/c/4723145/31
26"""
27aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
28
29[[audits.google.audits.bytemuck]]
30who = "Lukasz Anforowicz <[email protected]>"
31criteria = "safe-to-deploy"
32version = "1.16.3"
33notes = """
34Review notes from the original audit (of 1.14.3) may be found in
35https://crrev.com/c/5362675. Note that this audit has initially missed UB risk
36that was fixed in 1.16.2 - see https://github.com/Lokathor/bytemuck/pull/258.
37Because of this, the original audit has been edited to certify version `1.16.3`
38instead (see also https://crrev.com/c/5771867).
39"""
40aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
41
42[[audits.google.audits.bytemuck]]
43who = "Lukasz Anforowicz <[email protected]>"
44criteria = "safe-to-deploy"
45delta = "1.16.3 -> 1.17.1"
46notes = "Unsafe review comments can be found in https://crrev.com/c/5813463"
47aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
48
49[[audits.google.audits.bytemuck]]
50who = "Adrian Taylor <[email protected]>"
51criteria = "safe-to-deploy"
52delta = "1.17.1 -> 1.18.0"
53notes = "No code changes - just altering feature flag arrangements"
54aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
55
56[[audits.google.audits.bytemuck]]
57who = "Adrian Taylor <[email protected]>"
58criteria = "safe-to-deploy"
59delta = "1.18.0 -> 1.19.0"
60notes = "No code changes - just comment changes and adding the track_caller attribute."
61aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
62
63[[audits.google.audits.bytemuck]]
64who = "Lukasz Anforowicz <[email protected]>"
65criteria = "safe-to-deploy"
66delta = "1.19.0 -> 1.20.0"
67notes = "`unsafe` review can be found at https://crrev.com/c/6096767"
68aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
69
70[[audits.google.audits.bytemuck]]
71who = "Adrian Taylor <[email protected]>"
72criteria = "safe-to-deploy"
73delta = "1.20.0 -> 1.21.0"
74notes = "Unsafe review at https://chromium-review.googlesource.com/c/chromium/src/+/6111154/"
75aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
76
77[[audits.google.audits.bytemuck]]
78who = "Daniel Cheng <[email protected]>"
79criteria = "safe-to-deploy"
80delta = "1.21.0 -> 1.22.0"
81notes = """
82This adds new instances of unsafe, but the uses are justified:
83- BoxBytes is essentially a Box<[u8], which is Send + Sync, so also marking BoxBytes as Send + Sync is justified.
84- core::num::Saturating<T> meets the criteria for Zeroable + Pod, so marking it as such is justified.
85
86See https://crrev.com/c/6321863 for more audit notes.
87"""
88aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
89
90[[audits.google.audits.byteorder]]
91who = "danakj <[email protected]>"
92criteria = "safe-to-deploy"
93version = "1.5.0"
94notes = "Unsafe review in https://crrev.com/c/5838022"
95aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
96
97[[audits.google.audits.cfg-if]]
98who = "George Burgess IV <[email protected]>"
99criteria = "safe-to-deploy"
100version = "1.0.0"
101aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT"
102
103[[audits.google.audits.either]]
104who = "Manish Goregaokar <[email protected]>"
105criteria = "safe-to-deploy"
106version = "1.13.0"
107notes = "Unsafe code pertaining to wrapping Pin APIs. Mostly passes invariants down."
108aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
109
110[[audits.google.audits.either]]
111who = "Daniel Cheng <[email protected]>"
112criteria = "safe-to-deploy"
113delta = "1.13.0 -> 1.14.0"
114notes = """
115Inheriting ub-risk-1 from the baseline review of 1.13.0. While the delta has some diffs in unsafe code, they are either:
116- migrating code to use helper macros
117- migrating match patterns to take advantage of default bindings mode from RFC 2005
118Either way, the result is code that does exactly the same thing and does not change the risk of UB.
119
120See https://crrev.com/c/6323164 for more audit details.
121"""
122aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
123
124[[audits.google.audits.either]]
125who = "Lukasz Anforowicz <[email protected]>"
126criteria = "safe-to-deploy"
127delta = "1.14.0 -> 1.15.0"
128notes = "The delta in `lib.rs` only tweaks doc comments and `#[cfg(feature = \"std\")]`."
129aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
130
131[[audits.google.audits.nb]]
132who = "George Burgess IV <[email protected]>"
133criteria = "safe-to-deploy"
134version = "1.0.0"
135aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT"
136
137[[audits.google.audits.nb]]
138who = "George Burgess IV <[email protected]>"
139criteria = "safe-to-deploy"
140delta = "1.0.0 -> 0.1.3"
141aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT"
142
143[[audits.google.audits.nb]]
144who = "George Burgess IV <[email protected]>"
145criteria = "safe-to-deploy"
146delta = "1.0.0 -> 1.1.0"
147aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT"
148
149[[audits.google.audits.num-traits]]
150who = "Manish Goregaokar <[email protected]>"
151criteria = "safe-to-deploy"
152version = "0.2.19"
153notes = "Contains a single line of float-to-int unsafe with decent safety comments"
154aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
155
156[[audits.google.audits.proc-macro2]]
157who = "Lukasz Anforowicz <[email protected]>"
158criteria = "safe-to-deploy"
159version = "1.0.78"
160notes = """
161Grepped for \"crypt\", \"cipher\", \"fs\", \"net\" - there were no hits
162(except for a benign \"fs\" hit in a doc comment)
163
164Notes from the `unsafe` review can be found in https://crrev.com/c/5385745.
165"""
166aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
167
168[[audits.google.audits.proc-macro2]]
169who = "Adrian Taylor <[email protected]>"
170criteria = "safe-to-deploy"
171delta = "1.0.78 -> 1.0.79"
172aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
173
174[[audits.google.audits.proc-macro2]]
175who = "Adrian Taylor <[email protected]>"
176criteria = "safe-to-deploy"
177delta = "1.0.79 -> 1.0.80"
178aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
179
180[[audits.google.audits.proc-macro2]]
181who = "Dustin J. Mitchell <[email protected]>"
182criteria = "safe-to-deploy"
183delta = "1.0.80 -> 1.0.81"
184notes = "Comment changes only"
185aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
186
187[[audits.google.audits.proc-macro2]]
188who = "danakj <[email protected]>"
189criteria = "safe-to-deploy"
190delta = "1.0.81 -> 1.0.82"
191aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
192
193[[audits.google.audits.proc-macro2]]
194who = "Dustin J. Mitchell <[email protected]>"
195criteria = "safe-to-deploy"
196delta = "1.0.82 -> 1.0.83"
197notes = "Substantive change is replacing String with Box<str>, saving memory."
198aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
199
200[[audits.google.audits.proc-macro2]]
201who = "Lukasz Anforowicz <[email protected]>"
202criteria = "safe-to-deploy"
203delta = "1.0.83 -> 1.0.84"
204notes = "Only doc comment changes in `src/lib.rs`."
205aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
206
207[[audits.google.audits.proc-macro2]]
208who = "[email protected]"
209criteria = "safe-to-deploy"
210delta = "1.0.84 -> 1.0.85"
211notes = "Test-only changes."
212aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
213
214[[audits.google.audits.proc-macro2]]
215who = "Lukasz Anforowicz <[email protected]>"
216criteria = "safe-to-deploy"
217delta = "1.0.85 -> 1.0.86"
218notes = """
219Comment-only changes in `build.rs`.
220Reordering of `Cargo.toml` entries.
221Just bumping up the version number in `lib.rs`.
222Config-related changes in `test_size.rs`.
223"""
224aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
225
226[[audits.google.audits.proc-macro2]]
227who = "danakj <[email protected]>"
228criteria = "safe-to-deploy"
229delta = "1.0.86 -> 1.0.87"
230notes = "No new unsafe interactions."
231aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
232
233[[audits.google.audits.proc-macro2]]
234who = "Liza Burakova <[email protected]"
235criteria = "safe-to-deploy"
236delta = "1.0.87 -> 1.0.89"
237notes = """
238Biggest change is adding error handling in build.rs.
239Some config related changes in wrapper.rs.
240"""
241aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
242
243[[audits.google.audits.proc-macro2]]
244who = "Lukasz Anforowicz <[email protected]>"
245criteria = "safe-to-deploy"
246delta = "1.0.89 -> 1.0.92"
247notes = """
248I looked at the delta and the previous discussion at
249https://chromium-review.googlesource.com/c/chromium/src/+/5385745/3#message-a8e2813129fa3779dab15acede408ee26d67b7f3
250and the changes look okay to me (including the `unsafe fn from_str_unchecked`
251changes in `wrapper.rs`).
252"""
253aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
254
255[[audits.google.audits.proc-macro2]]
256who = "Lukasz Anforowicz <[email protected]>"
257criteria = "safe-to-deploy"
258delta = "1.0.92 -> 1.0.93"
259notes = "No `unsafe`-related changes."
260aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
261
262[[audits.google.audits.proc-macro2]]
263who = "Daniel Cheng <[email protected]>"
264criteria = "safe-to-deploy"
265delta = "1.0.93 -> 1.0.94"
266notes = "Minor doc changes and clippy lint adjustments+fixes."
267aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
268
269[[audits.google.audits.quote]]
270who = "Lukasz Anforowicz <[email protected]>"
271criteria = "safe-to-deploy"
272version = "1.0.35"
273notes = """
274Grepped for \"unsafe\", \"crypt\", \"cipher\", \"fs\", \"net\" - there were no hits
275(except for benign \"net\" hit in tests and \"fs\" hit in README.md)
276"""
277aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
278
279[[audits.google.audits.quote]]
280who = "Adrian Taylor <[email protected]>"
281criteria = "safe-to-deploy"
282delta = "1.0.35 -> 1.0.36"
283aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
284
285[[audits.google.audits.quote]]
286who = "Lukasz Anforowicz <[email protected]>"
287criteria = "safe-to-deploy"
288delta = "1.0.36 -> 1.0.37"
289notes = """
290The delta just 1) inlines/expands `impl ToTokens` that used to be handled via
291`primitive!` macro and 2) adds `impl ToTokens` for `CStr` and `CString`.
292"""
293aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
294
295[[audits.google.audits.quote]]
296who = "Dustin J. Mitchell <[email protected]>"
297criteria = "safe-to-deploy"
298delta = "1.0.37 -> 1.0.38"
299notes = "Still no unsafe"
300aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
301
302[[audits.google.audits.quote]]
303who = "Daniel Cheng <[email protected]>"
304criteria = "safe-to-deploy"
305delta = "1.0.38 -> 1.0.39"
306notes = "Only minor changes for clippy lints and documentation."
307aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
308
309[[audits.google.audits.quote]]
310who = "Lukasz Anforowicz <[email protected]>"
311criteria = "safe-to-deploy"
312delta = "1.0.39 -> 1.0.40"
313notes = """
314The delta is just a simplification of how `tokens.extend(...)` call is made.
315Still no `unsafe` anywhere.
316"""
317aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
318
319[[audits.google.audits.rand_core]]
320who = "Lukasz Anforowicz <[email protected]>"
321criteria = "safe-to-deploy"
322version = "0.6.4"
323notes = """
324For more detailed unsafe review notes please see https://crrev.com/c/6362797
325"""
326aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
327
328[[audits.google.audits.stable_deref_trait]]
329who = "Manish Goregaokar <[email protected]>"
330criteria = "safe-to-deploy"
331version = "1.2.0"
332notes = "Purely a trait, crates using this should be carefully vetted since self-referential stuff can be super tricky around various unsafe rust edges."
333aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
334
335[[audits.google.audits.strsim]]
336who = "[email protected]"
337criteria = "safe-to-deploy"
338version = "0.10.0"
339notes = """
340Reviewed in https://crrev.com/c/5171063
341
342Previously reviewed during security review and the audit is grandparented in.
343"""
344aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
345
346[[audits.google.audits.unicode-ident]]
347who = "Lukasz Anforowicz <[email protected]>"
348criteria = "safe-to-deploy"
349version = "1.0.12"
350notes = '''
351I grepped for \"crypt\", \"cipher\", \"fs\", \"net\" - there were no hits.
352
353All two functions from the public API of this crate use `unsafe` to avoid bound
354checks for an array access. Cross-module analysis shows that the offsets can
355be statically proven to be within array bounds. More details can be found in
356the unsafe review CL at https://crrev.com/c/5350386.
357
358This crate has been added to Chromium in https://crrev.com/c/3891618.
359'''
360aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
361
362[[audits.google.audits.unicode-ident]]
363who = "Dustin J. Mitchell <[email protected]>"
364criteria = "safe-to-deploy"
365delta = "1.0.12 -> 1.0.13"
366notes = "Lots of table updates, and tables are assumed correct with unsafe `.get_unchecked()`, so ub-risk-2 is appropriate"
367aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
368
369[[audits.google.audits.unicode-ident]]
370who = "Lukasz Anforowicz <[email protected]>"
371criteria = "safe-to-deploy"
372delta = "1.0.13 -> 1.0.14"
373notes = "Minimal delta in `.rs` files: new test assertions + doc changes."
374aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
375
376[[audits.google.audits.unicode-ident]]
377who = "Adrian Taylor <[email protected]>"
378criteria = "safe-to-deploy"
379delta = "1.0.14 -> 1.0.15"
380notes = "No changes relevant to any of these criteria."
381aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
382
383[[audits.google.audits.unicode-ident]]
384who = "Liza Burakova <[email protected]>"
385criteria = "safe-to-deploy"
386delta = "1.0.15 -> 1.0.16"
387aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
388
389[[audits.google.audits.unicode-ident]]
390who = "Daniel Cheng <[email protected]>"
391criteria = "safe-to-deploy"
392delta = "1.0.16 -> 1.0.18"
393notes = "Only minor comment and documentation updates."
394aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
395
396[[audits.google.audits.void]]
397who = "George Burgess IV <[email protected]>"
398criteria = "safe-to-deploy"
399version = "1.0.2"
400aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT"
401
402[[audits.mozilla.audits.crunchy]]
403who = "Erich Gubler <[email protected]>"
404criteria = "safe-to-deploy"
405version = "0.2.3"
406aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
407
408[[audits.mozilla.audits.document-features]]
409who = "Erich Gubler <[email protected]>"
410criteria = "safe-to-deploy"
411version = "0.2.8"
412aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
413
414[[audits.mozilla.audits.document-features]]
415who = "Erich Gubler <[email protected]>"
416criteria = "safe-to-deploy"
417delta = "0.2.8 -> 0.2.9"
418aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
419
420[[audits.mozilla.audits.document-features]]
421who = "Erich Gubler <[email protected]>"
422criteria = "safe-to-deploy"
423delta = "0.2.9 -> 0.2.10"
424aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
425
426[[audits.mozilla.audits.document-features]]
427who = "Teodor Tanasoaia <[email protected]>"
428criteria = "safe-to-deploy"
429delta = "0.2.10 -> 0.2.11"
430aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
431
432[[audits.mozilla.audits.fnv]]
433who = "Bobby Holley <[email protected]>"
434criteria = "safe-to-deploy"
435version = "1.0.7"
436notes = "Simple hasher implementation with no unsafe code."
437aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
438
439[[audits.mozilla.audits.half]]
440who = "John M. Schanck <[email protected]>"
441criteria = "safe-to-deploy"
442version = "1.8.2"
443notes = """
444This crate contains unsafe code for bitwise casts to/from binary16 floating-point
445format. I've reviewed these and found no issues. There are no uses of ambient
446capabilities.
447"""
448aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
449
450[[audits.mozilla.audits.half]]
451who = "Erich Gubler <[email protected]>"
452criteria = "safe-to-deploy"
453delta = "1.8.2 -> 1.8.3"
454aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
455
456[[audits.mozilla.audits.half]]
457who = "Erich Gubler <[email protected]>"
458criteria = "safe-to-deploy"
459delta = "1.8.3 -> 2.5.0"
460aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
461
462[[audits.mozilla.audits.litrs]]
463who = "Erich Gubler <[email protected]>"
464criteria = "safe-to-deploy"
465version = "0.4.1"
466aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
467
468[[audits.mozilla.audits.strsim]]
469who = "Ben Dean-Kawamura <[email protected]>"
470criteria = "safe-to-deploy"
471delta = "0.10.0 -> 0.11.1"
472aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
diff --git a/tools/run_and_attach_rtt.sh b/tools/run_and_attach_rtt.sh
new file mode 100644
index 000000000..13041d06b
--- /dev/null
+++ b/tools/run_and_attach_rtt.sh
@@ -0,0 +1,24 @@
1#!/usr/bin/env bash
2set -euo pipefail
3
4ELF="${1:-target/thumbv8m.main-none-eabihf/debug/examples/hello}"
5PROBE_ID="${2:-1fc9:0143:H3AYDQVQMTROB}"
6CHIP="${3:-MCXA276}"
7SPEED="${4:-1000}"
8
9# 1) Flash & run using the existing run.sh (probe is in use only during this step)
10./run.sh "$ELF"
11
12# 2) Give target a short moment to boot and set up RTT CB in RAM
13sleep 0.5
14
15# 3) Attach RTT/defmt using probe-rs (no flashing)
16exec probe-rs attach \
17 --chip "$CHIP" \
18 --probe "$PROBE_ID" \
19 --protocol swd \
20 --speed "$SPEED" \
21 "$ELF" \
22 --rtt-scan-memory \
23 --log-format oneline
24
diff --git a/tools/run_jlink_noblock.sh b/tools/run_jlink_noblock.sh
new file mode 100644
index 000000000..3ea1f2b4b
--- /dev/null
+++ b/tools/run_jlink_noblock.sh
@@ -0,0 +1,76 @@
1#!/usr/bin/env bash
2set -euo pipefail
3
4ELF="${1:-}"
5PROBE_ID="${2:-1366:0101:000600110607}" # default to your J-Link
6CHIP="${3:-MCXA276}"
7SPEED="${4:-1000}"
8PORT="${PROBE_RS_GDB_PORT:-1337}"
9
10if [[ -z "${ELF}" || ! -f "${ELF}" ]]; then
11 echo "Usage: $0 <elf> [probe-id] [chip] [speed-khz]" >&2
12 exit 1
13fi
14
15if ! command -v probe-rs >/dev/null 2>&1; then
16 echo "probe-rs not found (cargo install probe-rs --features cli)" >&2
17 exit 1
18fi
19if ! command -v gdb-multiarch >/dev/null 2>&1; then
20 echo "gdb-multiarch not found; install it (e.g., sudo apt install gdb-multiarch)." >&2
21 exit 1
22fi
23
24# Start probe-rs GDB server
25SERVER_LOG=$(mktemp)
26probe-rs gdb --chip "${CHIP}" --protocol swd --speed "${SPEED}" --non-interactive "${ELF}" --probe "${PROBE_ID}" \
27 >"${SERVER_LOG}" 2>&1 &
28GDB_SERVER_PID=$!
29
30# Wait for readiness
31for _ in {1..50}; do
32 if grep -q "Firing up GDB stub" "${SERVER_LOG}"; then break; fi
33 if grep -q "Connecting to the chip was unsuccessful" "${SERVER_LOG}"; then
34 echo "probe-rs gdb server failed. Log:" >&2
35 sed -e 's/^/ /' "${SERVER_LOG}" >&2 || true
36 kill "${GDB_SERVER_PID}" 2>/dev/null || true
37 exit 1
38 fi
39 sleep 0.1
40done
41
42# GDB script: load, resume, detach
43GDB_SCRIPT=$(mktemp)
44cat >"${GDB_SCRIPT}" <<EOF
45set pagination off
46set confirm off
47set mem inaccessible-by-default off
48
49target remote :${PORT}
50monitor halt
51load
52set language c
53# Start clean from Reset vector in RAM
54set {int}0xE000ED08 = 0x20000000
55set \$xpsr = 0x01000000
56set \$sp = 0x20020000
57set \$pc = Reset
58monitor resume
59detach
60quit
61EOF
62
63# Run GDB to program and resume target, then exit (probe released)
64if ! gdb-multiarch -q -x "${GDB_SCRIPT}" "${ELF}"; then
65 echo "GDB failed; server log:" >&2
66 sed -e 's/^/ /' "${SERVER_LOG}" >&2 || true
67 kill "${GDB_SERVER_PID}" 2>/dev/null || true
68 exit 1
69fi
70
71# Stop server now that we've detached
72kill "${GDB_SERVER_PID}" 2>/dev/null || true
73rm -f "${GDB_SCRIPT}" "${SERVER_LOG}" || true
74
75echo "Flashed, resumed, and detached (probe free)."
76