From 89160b7cb9b3623e0a50f6745296d470fa7ea79d Mon Sep 17 00:00:00 2001 From: Finomnis Date: Wed, 6 Dec 2023 08:49:38 +0100 Subject: [PATCH] Fix nrf monotonics (#852) * Fix nrf::timer * Bootstrap nrf52840-blinky example * More work on nrf blinky example * Fix README * Add asserts for correct timer functionality * Add correctness check to other monotonics as well * Update Changelog * Fix potential timing issues * Fix race condition in nrf::rtc * Add changelog * Add rtc blinky example * Change rtc example to RC lf clock source * Add changelog to rtic-time * Add changelog * Attempt to fix CI * Update teensy4-blinky Cargo.lock --- .github/workflows/build.yml | 31 +- examples/nrf52840_blinky/.cargo/config.toml | 18 + examples/nrf52840_blinky/.gitignore | 1 + .../nrf52840_blinky/.vscode/settings.json | 9 + examples/nrf52840_blinky/Cargo.lock | 612 ++++++++++++++++++ examples/nrf52840_blinky/Cargo.toml | 68 ++ examples/nrf52840_blinky/README.md | 33 + .../nrf52840_blinky/src/bin/blinky_rtc.rs | 69 ++ .../nrf52840_blinky/src/bin/blinky_timer.rs | 66 ++ examples/nrf52840_blinky/src/lib.rs | 37 ++ examples/teensy4_blinky/Cargo.lock | 10 +- rtic-monotonics/CHANGELOG.md | 6 + rtic-monotonics/src/imxrt.rs | 10 +- rtic-monotonics/src/nrf/rtc.rs | 98 ++- rtic-monotonics/src/nrf/timer.rs | 99 +-- rtic-monotonics/src/stm32.rs | 6 +- rtic-time/CHANGELOG.md | 2 + rtic-time/src/half_period_counter.rs | 6 +- 18 files changed, 1080 insertions(+), 101 deletions(-) create mode 100644 examples/nrf52840_blinky/.cargo/config.toml create mode 100644 examples/nrf52840_blinky/.gitignore create mode 100644 examples/nrf52840_blinky/.vscode/settings.json create mode 100644 examples/nrf52840_blinky/Cargo.lock create mode 100644 examples/nrf52840_blinky/Cargo.toml create mode 100644 examples/nrf52840_blinky/README.md create mode 100644 examples/nrf52840_blinky/src/bin/blinky_rtc.rs create mode 100644 examples/nrf52840_blinky/src/bin/blinky_timer.rs create mode 100644 examples/nrf52840_blinky/src/lib.rs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 19a13d7184..58aaf7d046 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -148,7 +148,7 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - + - name: Install rust ${{ matrix.toolchain }} run: | rustup set profile minimal @@ -164,6 +164,9 @@ jobs: - name: Cache Dependencies uses: Swatinem/rust-cache@v2 + - name: Install flip-link + run: cargo install flip-link + - name: Check the examples run: cargo xtask usage-example-build @@ -275,22 +278,22 @@ jobs: cp -r target/doc $td/api echo rtic lychee --offline --format detailed $td/api/rtic/ - + echo rtic_common lychee --offline --format detailed $td/api/rtic_common/ - + echo rtic_macros lychee --offline --format detailed $td/api/rtic_macros/ - + echo rtic_monotonics lychee --offline --format detailed $td/api/rtic_monotonics/ echo rtic_sync lychee --offline --format detailed $td/api/rtic_sync/ - + echo rtic_time lychee --offline --format detailed $td/api/rtic_time/ - + - name: Archive the API docs run: | @@ -336,7 +339,7 @@ jobs: name: apidocs - name: Extract the API docs - run: tar -xf apidocs.tar + run: tar -xf apidocs.tar - name: Check links run: | @@ -463,7 +466,7 @@ jobs: # master branch. # As master moves on to development, the work on the # stable version will happen in release/v"stable_version". - # Thus, no need to push changes + # Thus, no need to push changes # # This needs to run before book is built, as bookbuilding fetches from the branch pushtostablebranch: @@ -503,8 +506,8 @@ jobs: runs-on: ubuntu-22.04 needs: - pushtostablebranch - - docs - - mdbookold + - docs + - mdbookold - mdbook # Only run this when pushing to master branch @@ -521,7 +524,7 @@ jobs: uses: taiki-e/install-action@v2 with: tool: mdbook-mermaid - + - name: mdBook Action uses: peaceiris/actions-mdbook@v1 with: @@ -537,7 +540,7 @@ jobs: - name: Extract the dev-version book and API docs run: | - tar -xf book.tar + tar -xf book.tar - name: Download built old versions of books and API docs uses: actions/download-artifact@v3 @@ -546,7 +549,7 @@ jobs: - name: Extract the old version books and API docs run: | - tar -xf mdbookold.tar + tar -xf mdbookold.tar - name: Prepare books shell: 'script --return --quiet --command "bash {0}"' @@ -631,7 +634,7 @@ jobs: - name: Extract the books run: | - tar -xf bookstodeploy.tar + tar -xf bookstodeploy.tar - name: Deploy to GH-pages uses: peaceiris/actions-gh-pages@v3 diff --git a/examples/nrf52840_blinky/.cargo/config.toml b/examples/nrf52840_blinky/.cargo/config.toml new file mode 100644 index 0000000000..419b43aacf --- /dev/null +++ b/examples/nrf52840_blinky/.cargo/config.toml @@ -0,0 +1,18 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +runner = "probe-rs run --chip nRF52840_xxAA" + +rustflags = [ + "-C", "linker=flip-link", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + # This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x + # See https://github.com/rust-embedded/cortex-m-quickstart/pull/95 + "-C", "link-arg=--nmagic", +] + +[build] +target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) + +[alias] +rb = "run --bin" +rrb = "run --release --bin" diff --git a/examples/nrf52840_blinky/.gitignore b/examples/nrf52840_blinky/.gitignore new file mode 100644 index 0000000000..ea8c4bf7f3 --- /dev/null +++ b/examples/nrf52840_blinky/.gitignore @@ -0,0 +1 @@ +/target diff --git a/examples/nrf52840_blinky/.vscode/settings.json b/examples/nrf52840_blinky/.vscode/settings.json new file mode 100644 index 0000000000..c684c2425b --- /dev/null +++ b/examples/nrf52840_blinky/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + // override the default setting (`cargo check --all-targets`) which produces the following error + // "can't find crate for `test`" when the default compilation target is a no_std target + // with these changes RA will call `cargo check --bins` on save + "rust-analyzer.checkOnSave.allTargets": false, + "rust-analyzer.checkOnSave.extraArgs": [ + "--bins" + ] +} diff --git a/examples/nrf52840_blinky/Cargo.lock b/examples/nrf52840_blinky/Cargo.lock new file mode 100644 index 0000000000..a3d754273d --- /dev/null +++ b/examples/nrf52840_blinky/Cargo.lock @@ -0,0 +1,612 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + +[[package]] +name = "az" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" + +[[package]] +name = "bare-metal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "bare-metal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603" + +[[package]] +name = "bitfield" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bytemuck" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cortex-m" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" +dependencies = [ + "bare-metal 0.2.5", + "bitfield", + "critical-section", + "embedded-hal 0.2.7", + "volatile-register", +] + +[[package]] +name = "cortex-m-rt" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee84e813d593101b1723e13ec38b6ab6abbdbaaa4546553f5395ed274079ddb1" +dependencies = [ + "cortex-m-rt-macros", +] + +[[package]] +name = "cortex-m-rt-macros" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "cortex-m-semihosting" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c23234600452033cc77e4b761e740e02d2c4168e11dbf36ab14a0f58973592b0" +dependencies = [ + "cortex-m", +] + +[[package]] +name = "critical-section" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "defmt" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a2d011b2fee29fb7d659b83c43fce9a2cb4df453e16d441a51448e448f3f98" +dependencies = [ + "bitflags", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54f0216f6c5acb5ae1a47050a6645024e6edafc2ee32d421955eccfef12ef92e" +dependencies = [ + "defmt-parser", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "defmt-parser" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "269924c02afd7f94bc4cecbfa5c379f6ffcf9766b3408fe63d22c728654eccd0" +dependencies = [ + "thiserror", +] + +[[package]] +name = "defmt-rtt" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "609923761264dd99ed9c7d209718cda4631c5fe84668e0f0960124cbb844c49f" +dependencies = [ + "critical-section", + "defmt", +] + +[[package]] +name = "embedded-dma" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "994f7e5b5cb23521c22304927195f236813053eb9c065dd2226a32ba64695446" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "embedded-hal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" +dependencies = [ + "nb 0.1.3", + "void", +] + +[[package]] +name = "embedded-hal" +version = "1.0.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e57ec6ad0bc8eb967cf9c9f144177f5e8f2f6f02dad0b8b683f9f05f6b22def" + +[[package]] +name = "embedded-storage" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "156d7a2fdd98ebbf9ae579cbceca3058cff946e13f8e17b90e3511db0508c723" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "fixed" +version = "1.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02c69ce7e7c0f17aa18fdd9d0de39727adb9c6281f2ad12f57cbe54ae6e76e7d" +dependencies = [ + "az", + "bytemuck", + "half", + "typenum", +] + +[[package]] +name = "fugit" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7" +dependencies = [ + "defmt", + "gcd", +] + +[[package]] +name = "futures-core" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" + +[[package]] +name = "futures-task" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" + +[[package]] +name = "futures-util" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "gcd" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" + +[[package]] +name = "half" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc52e53916c08643f1b56ec082790d1e86a32e58dc5268f897f313fbae7b4872" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "nb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + +[[package]] +name = "nrf-hal-common" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd244c63d588066d75cffdcae8a03299fd5fb272e36bdde4a9f922f3e4bc6e4b" +dependencies = [ + "cast", + "cfg-if", + "cortex-m", + "embedded-dma", + "embedded-hal 0.2.7", + "embedded-storage", + "fixed", + "nb 1.1.0", + "nrf-usbd", + "nrf52840-pac", + "rand_core", + "void", +] + +[[package]] +name = "nrf-usbd" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66b2907c0b3ec4d264981c1fc5a2321d51c463d5a63d386e573f00e84d5495e6" +dependencies = [ + "cortex-m", + "critical-section", + "usb-device", + "vcell", +] + +[[package]] +name = "nrf52840-blinky" +version = "0.1.0" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "cortex-m-semihosting", + "defmt", + "defmt-rtt", + "fugit", + "nrf52840-hal", + "panic-probe", + "rtic", + "rtic-monotonics", +] + +[[package]] +name = "nrf52840-hal" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b54757ec98f38331889d1d6c535edb5dd543c762751abfe507f2d644b30f6d4f" +dependencies = [ + "embedded-hal 0.2.7", + "nrf-hal-common", + "nrf52840-pac", +] + +[[package]] +name = "nrf52840-pac" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30713f36f1be02e5bc9abefa30eae4a1f943d810f199d4923d3ad062d1be1b3d" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "vcell", +] + +[[package]] +name = "panic-probe" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa6fa5645ef5a760cd340eaa92af9c1ce131c8c09e7f8926d8a24b59d26652b9" +dependencies = [ + "cortex-m", + "defmt", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "portable-atomic" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bccab0e7fd7cc19f820a1c8c91720af652d0c88dc9664dd72aef2614f04af3b" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rtic" +version = "2.0.1" +dependencies = [ + "atomic-polyfill", + "bare-metal 1.0.0", + "cortex-m", + "critical-section", + "rtic-core", + "rtic-macros", +] + +[[package]] +name = "rtic-common" +version = "1.0.1" +dependencies = [ + "critical-section", + "portable-atomic", +] + +[[package]] +name = "rtic-core" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9369355b04d06a3780ec0f51ea2d225624db777acbc60abd8ca4832da5c1a42" + +[[package]] +name = "rtic-macros" +version = "2.0.1" +dependencies = [ + "indexmap", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rtic-monotonics" +version = "1.4.0" +dependencies = [ + "atomic-polyfill", + "cfg-if", + "cortex-m", + "critical-section", + "embedded-hal 1.0.0-rc.2", + "fugit", + "nrf52840-pac", + "rtic-time", +] + +[[package]] +name = "rtic-time" +version = "1.1.0" +dependencies = [ + "critical-section", + "futures-util", + "rtic-common", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "usb-device" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f6cc3adc849b5292b4075fc0d5fdcf2f24866e88e336dd27a8943090a520508" + +[[package]] +name = "vcell" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "volatile-register" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc" +dependencies = [ + "vcell", +] diff --git a/examples/nrf52840_blinky/Cargo.toml b/examples/nrf52840_blinky/Cargo.toml new file mode 100644 index 0000000000..8b13649321 --- /dev/null +++ b/examples/nrf52840_blinky/Cargo.toml @@ -0,0 +1,68 @@ +[package] +authors = ["Finomnis "] +name = "nrf52840-blinky" +edition = "2021" +version = "0.1.0" + +[workspace] + +[lib] +harness = false + +[dependencies] +cortex-m = { version = "0.7", features = ["critical-section-single-core"] } +cortex-m-rt = "0.7" +defmt = "0.3" +defmt-rtt = "0.4" +panic-probe = { version = "0.3", features = ["print-defmt"] } +cortex-m-semihosting = "0.5.0" +nrf52840-hal = "0.16.0" +fugit = { version = "0.3.7", features = ["defmt"] } + +[dependencies.rtic] +path = "../../rtic" +version = "2.0.1" +features = ["thumbv7-backend"] + +[dependencies.rtic-monotonics] +path = "../../rtic-monotonics" +version = "1.4.0" +features = ["nrf52840"] + +# cargo build/run +[profile.dev] +codegen-units = 1 +debug = 2 +debug-assertions = true # <- +incremental = false +opt-level = 'z' # <- +overflow-checks = true # <- + +# cargo test +[profile.test] +codegen-units = 1 +debug = 2 +debug-assertions = true # <- +incremental = false +opt-level = 3 # <- +overflow-checks = true # <- + +# cargo build/run --release +[profile.release] +codegen-units = 1 +debug = 2 +debug-assertions = false # <- +incremental = false +lto = 'fat' +opt-level = 3 # <- +overflow-checks = false # <- + +# cargo test --release +[profile.bench] +codegen-units = 1 +debug = 2 +debug-assertions = false # <- +incremental = false +lto = 'fat' +opt-level = 3 # <- +overflow-checks = false # <- diff --git a/examples/nrf52840_blinky/README.md b/examples/nrf52840_blinky/README.md new file mode 100644 index 0000000000..56eac32af5 --- /dev/null +++ b/examples/nrf52840_blinky/README.md @@ -0,0 +1,33 @@ +# `nrf52840_blinky` + +An RTIC blinky example intended for [nrf52840-dongle]. + +[nrf52840-dongle]: https://www.nordicsemi.com/Products/Development-hardware/nrf52840-dongle + +## Dependencies + +#### 1. `flip-link`: + +```console +$ cargo install flip-link +``` + +#### 2. `probe-rs`: + +``` console +$ # make sure to install v0.2.0 or later +$ cargo install probe-rs --features cli +``` + + +## Run + +The [nrf52840-dongle] needs to be connected to the computer via an SWD probe, like a [J-Link EDU Mini]. + +Then, run: + +``` +cargo run --release --bin blinky_timer +``` + +[J-Link EDU Mini]: https://www.segger.com/products/debug-probes/j-link/models/j-link-edu-mini/ diff --git a/examples/nrf52840_blinky/src/bin/blinky_rtc.rs b/examples/nrf52840_blinky/src/bin/blinky_rtc.rs new file mode 100644 index 0000000000..e515659ee3 --- /dev/null +++ b/examples/nrf52840_blinky/src/bin/blinky_rtc.rs @@ -0,0 +1,69 @@ +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] + +use nrf52840_blinky::hal; + +#[rtic::app(device = hal::pac, dispatchers = [SWI0_EGU0])] +mod app { + use super::*; + + use hal::gpio::{Level, Output, Pin, PushPull}; + use hal::prelude::*; + + use rtic_monotonics::nrf::rtc::Rtc0 as Mono; + use rtic_monotonics::nrf::rtc::*; + use rtic_monotonics::Monotonic; + + #[shared] + struct Shared {} + + #[local] + struct Local { + led: Pin>, + } + + #[init] + fn init(cx: init::Context) -> (Shared, Local) { + // Configure low frequency clock + hal::clocks::Clocks::new(cx.device.CLOCK).start_lfclk(); + + // Initialize Monotonic + let token = rtic_monotonics::create_nrf_rtc0_monotonic_token!(); + Mono::start(cx.device.RTC0, token); + + // Setup LED + let port0 = hal::gpio::p0::Parts::new(cx.device.P0); + let led = port0.p0_06.into_push_pull_output(Level::Low).degrade(); + + // Schedule the blinking task + blink::spawn().ok(); + + (Shared {}, Local { led }) + } + + #[task(local = [led])] + async fn blink(cx: blink::Context) { + let blink::LocalResources { led, .. } = cx.local; + + let mut next_tick = Mono::now(); + let mut blink_on = false; + loop { + let now = Mono::now(); + let now_ms: fugit::SecsDurationU64 = now.duration_since_epoch().convert(); + defmt::println!("Timer {} ({})", now_ms, now.ticks()); + + blink_on = !blink_on; + if blink_on { + led.set_high().unwrap(); + } else { + led.set_low().unwrap(); + } + + next_tick += 1000.millis(); + Mono::delay_until(next_tick).await; + } + } +} diff --git a/examples/nrf52840_blinky/src/bin/blinky_timer.rs b/examples/nrf52840_blinky/src/bin/blinky_timer.rs new file mode 100644 index 0000000000..53ccf4e7e6 --- /dev/null +++ b/examples/nrf52840_blinky/src/bin/blinky_timer.rs @@ -0,0 +1,66 @@ +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] + +use nrf52840_blinky::hal; + +#[rtic::app(device = hal::pac, dispatchers = [SWI0_EGU0])] +mod app { + use super::*; + + use hal::gpio::{Level, Output, Pin, PushPull}; + use hal::prelude::*; + + use rtic_monotonics::nrf::timer::Timer0 as Mono; + use rtic_monotonics::nrf::timer::*; + use rtic_monotonics::Monotonic; + + #[shared] + struct Shared {} + + #[local] + struct Local { + led: Pin>, + } + + #[init] + fn init(cx: init::Context) -> (Shared, Local) { + // Initialize Monotonic + let token = rtic_monotonics::create_nrf_timer0_monotonic_token!(); + Mono::start(cx.device.TIMER0, token); + + // Setup LED + let port0 = hal::gpio::p0::Parts::new(cx.device.P0); + let led = port0.p0_06.into_push_pull_output(Level::Low).degrade(); + + // Schedule the blinking task + blink::spawn().ok(); + + (Shared {}, Local { led }) + } + + #[task(local = [led])] + async fn blink(cx: blink::Context) { + let blink::LocalResources { led, .. } = cx.local; + + let mut next_tick = Mono::now(); + let mut blink_on = false; + loop { + let now = Mono::now(); + let now_ms: fugit::SecsDurationU64 = now.duration_since_epoch().convert(); + defmt::println!("Timer {} ({})", now_ms, now.ticks()); + + blink_on = !blink_on; + if blink_on { + led.set_high().unwrap(); + } else { + led.set_low().unwrap(); + } + + next_tick += 1000.millis(); + Mono::delay_until(next_tick).await; + } + } +} diff --git a/examples/nrf52840_blinky/src/lib.rs b/examples/nrf52840_blinky/src/lib.rs new file mode 100644 index 0000000000..7795f2da16 --- /dev/null +++ b/examples/nrf52840_blinky/src/lib.rs @@ -0,0 +1,37 @@ +#![no_main] +#![no_std] + +use cortex_m_semihosting::debug; + +use defmt_rtt as _; // global logger + +pub use nrf52840_hal as hal; // memory layout + +use panic_probe as _; + +// same panicking *behavior* as `panic-probe` but doesn't print a panic message +// this prevents the panic message being printed *twice* when `defmt::panic` is invoked +#[defmt::panic_handler] +fn panic() -> ! { + cortex_m::asm::udf() +} + +/// Terminates the application and makes a semihosting-capable debug tool exit +/// with status code 0. +pub fn exit() -> ! { + loop { + debug::exit(debug::EXIT_SUCCESS); + } +} + +/// Hardfault handler. +/// +/// Terminates the application and makes a semihosting-capable debug tool exit +/// with an error. This seems better than the default, which is to spin in a +/// loop. +#[cortex_m_rt::exception] +unsafe fn HardFault(_frame: &cortex_m_rt::ExceptionFrame) -> ! { + loop { + debug::exit(debug::EXIT_FAILURE); + } +} diff --git a/examples/teensy4_blinky/Cargo.lock b/examples/teensy4_blinky/Cargo.lock index 408bfe7dd4..314eae0629 100644 --- a/examples/teensy4_blinky/Cargo.lock +++ b/examples/teensy4_blinky/Cargo.lock @@ -133,9 +133,9 @@ dependencies = [ [[package]] name = "embedded-hal" -version = "1.0.0-rc.1" +version = "1.0.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2894bc2f0457b8ca3d6b8ab8aad64d9337583672494013457f86c5a9146c0e22" +checksum = "3e57ec6ad0bc8eb967cf9c9f144177f5e8f2f6f02dad0b8b683f9f05f6b22def" [[package]] name = "equivalent" @@ -440,12 +440,12 @@ dependencies = [ [[package]] name = "rtic-monotonics" -version = "1.3.0" +version = "1.4.0" dependencies = [ "atomic-polyfill", "cfg-if", "cortex-m", - "embedded-hal 1.0.0-rc.1", + "embedded-hal 1.0.0-rc.2", "fugit", "imxrt-ral", "rtic-time", @@ -453,7 +453,7 @@ dependencies = [ [[package]] name = "rtic-time" -version = "1.0.0" +version = "1.1.0" dependencies = [ "critical-section", "futures-util", diff --git a/rtic-monotonics/CHANGELOG.md b/rtic-monotonics/CHANGELOG.md index eda2351d45..d6b43e2eea 100644 --- a/rtic-monotonics/CHANGELOG.md +++ b/rtic-monotonics/CHANGELOG.md @@ -7,6 +7,12 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top! ## Unreleased +### Fixed + +- Race condition in `nrf::timer`. +- Race condition in `nrf::rtc`. +- Add internal counter integrity check to all half-period based monotonics. + ## v1.4.0 - 2023-12-04 ### Fixed diff --git a/rtic-monotonics/src/imxrt.rs b/rtic-monotonics/src/imxrt.rs index 5f9fc08873..ecf9129b70 100644 --- a/rtic-monotonics/src/imxrt.rs +++ b/rtic-monotonics/src/imxrt.rs @@ -141,13 +141,15 @@ macro_rules! make_timer { // so it gets combined with rollover interrupt ral::write_reg!(ral::gpt, gpt, OCR[1], 0x0000_0000); + // Initialize timer queue + $tq.initialize(Self {}); + // Enable the timer ral::modify_reg!(ral::gpt, gpt, CR, EN: 1); ral::modify_reg!(ral::gpt, gpt, CR, ENMOD: 0, // Keep state when disabled ); - $tq.initialize(Self {}); // SAFETY: We take full ownership of the peripheral and interrupt vector, // plus we are not using any external shared resources so we won't impact @@ -244,13 +246,15 @@ macro_rules! make_timer { let (rollover, half_rollover) = ral::read_reg!(ral::gpt, gpt, SR, ROV, OF1); if rollover != 0 { - $period.fetch_add(1, Ordering::Relaxed); + let prev = $period.fetch_add(1, Ordering::Relaxed); ral::write_reg!(ral::gpt, gpt, SR, ROV: 1); + assert!(prev % 2 == 1, "Monotonic must have skipped an interrupt!"); } if half_rollover != 0 { - $period.fetch_add(1, Ordering::Relaxed); + let prev = $period.fetch_add(1, Ordering::Relaxed); ral::write_reg!(ral::gpt, gpt, SR, OF1: 1); + assert!(prev % 2 == 0, "Monotonic must have skipped an interrupt!"); } } } diff --git a/rtic-monotonics/src/nrf/rtc.rs b/rtic-monotonics/src/nrf/rtc.rs index 1f4e34f5ac..884b523ada 100644 --- a/rtic-monotonics/src/nrf/rtc.rs +++ b/rtic-monotonics/src/nrf/rtc.rs @@ -44,6 +44,7 @@ use crate::{Monotonic, TimeoutError, TimerQueue}; use atomic_polyfill::{AtomicU32, Ordering}; use core::future::Future; pub use fugit::{self, ExtU64, ExtU64Ceil}; +use rtic_time::half_period_counter::calculate_now; #[doc(hidden)] #[macro_export] @@ -92,6 +93,16 @@ macro_rules! create_nrf_rtc2_monotonic_token { }}; } +struct TimerValueU24(u32); +impl rtic_time::half_period_counter::TimerValue for TimerValueU24 { + const BITS: u32 = 24; +} +impl From for u64 { + fn from(value: TimerValueU24) -> Self { + Self::from(value.0) + } +} + macro_rules! make_rtc { ($mono_name:ident, $rtc:ident, $overflow:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => { /// Monotonic timer queue implementation. @@ -107,13 +118,49 @@ macro_rules! make_rtc { /// Start the timer monotonic. pub fn start(rtc: $rtc, _interrupt_token: impl crate::InterruptToken) { unsafe { rtc.prescaler.write(|w| w.bits(0)) }; - rtc.intenset.write(|w| w.compare0().set().ovrflw().set()); - rtc.evtenset.write(|w| w.compare0().set().ovrflw().set()); - rtc.tasks_clear.write(|w| unsafe { w.bits(1) }); - rtc.tasks_start.write(|w| unsafe { w.bits(1) }); + // Disable interrupts, as preparation + rtc.intenclr.write(|w| w + .compare0().clear() + .compare1().clear() + .ovrflw().clear() + ); - $tq.initialize(Self {}); + // Configure compare registers + rtc.cc[0].write(|w| unsafe { w.bits(0) }); // Dynamic wakeup + rtc.cc[1].write(|w| unsafe { w.bits(0x80_0000) }); // Half-period + + // Timing critical, make sure we don't get interrupted + critical_section::with(|_|{ + // Reset the timer + rtc.tasks_clear.write(|w| unsafe { w.bits(1) }); + rtc.tasks_start.write(|w| unsafe { w.bits(1) }); + + // Clear pending events. + // Should be close enough to the timer reset that we don't miss any events. + rtc.events_ovrflw.write(|w| w); + rtc.events_compare[0].write(|w| w); + rtc.events_compare[1].write(|w| w); + + // Make sure overflow counter is synced with the timer value + $overflow.store(0, Ordering::SeqCst); + + // Initialized the timer queue + $tq.initialize(Self {}); + + // Enable interrupts. + // Should be close enough to the timer reset that we don't miss any events. + rtc.intenset.write(|w| w + .compare0().set() + .compare1().set() + .ovrflw().set() + ); + rtc.evtenset.write(|w| w + .compare0().set() + .compare1().set() + .ovrflw().set() + ); + }); // SAFETY: We take full ownership of the peripheral and interrupt vector, // plus we are not using any external shared resources so we won't impact @@ -130,12 +177,6 @@ macro_rules! make_rtc { &$tq } - #[inline(always)] - fn is_overflow() -> bool { - let rtc = unsafe { &*$rtc::PTR }; - rtc.events_ovrflw.read().bits() == 1 - } - /// Timeout at a specific time. #[inline] pub async fn timeout_at( @@ -181,31 +222,24 @@ macro_rules! make_rtc { type Duration = fugit::TimerDurationU64<32_768>; fn now() -> Self::Instant { - // In a critical section to not get a race between overflow updates and reading it - // and the flag here. - critical_section::with(|_| { - let rtc = unsafe { &*$rtc::PTR }; - let cnt = rtc.counter.read().bits(); - // OVERFLOW HAPPENS HERE race needs to be handled - let ovf = if Self::is_overflow() { - $overflow.load(Ordering::Relaxed) + 1 - } else { - $overflow.load(Ordering::Relaxed) - } as u64; - - // Check and fix if above race happened - let new_cnt = rtc.counter.read().bits(); - let cnt = if new_cnt >= cnt { cnt } else { new_cnt } as u64; - - Self::Instant::from_ticks((ovf << 24) | cnt) - }) + let rtc = unsafe { &*$rtc::PTR }; + Self::Instant::from_ticks(calculate_now( + $overflow.load(Ordering::Relaxed), + || TimerValueU24(rtc.counter.read().bits()) + )) } fn on_interrupt() { let rtc = unsafe { &*$rtc::PTR }; - if Self::is_overflow() { - $overflow.fetch_add(1, Ordering::SeqCst); + if rtc.events_ovrflw.read().bits() == 1 { rtc.events_ovrflw.write(|w| unsafe { w.bits(0) }); + let prev = $overflow.fetch_add(1, Ordering::Relaxed); + assert!(prev % 2 == 1, "Monotonic must have skipped an interrupt!"); + } + if rtc.events_compare[1].read().bits() == 1 { + rtc.events_compare[1].write(|w| unsafe { w.bits(0) }); + let prev = $overflow.fetch_add(1, Ordering::Relaxed); + assert!(prev % 2 == 0, "Monotonic must have skipped an interrupt!"); } } @@ -221,7 +255,7 @@ macro_rules! make_rtc { fn set_compare(instant: Self::Instant) { let rtc = unsafe { &*$rtc::PTR }; - unsafe { rtc.cc[0].write(|w| w.bits(instant.ticks() as u32 & 0xffffff)) }; + unsafe { rtc.cc[0].write(|w| w.bits(instant.ticks() as u32 & 0xff_ffff)) }; } fn clear_compare_flag() { diff --git a/rtic-monotonics/src/nrf/timer.rs b/rtic-monotonics/src/nrf/timer.rs index 0488ca9b22..2f83f40b68 100644 --- a/rtic-monotonics/src/nrf/timer.rs +++ b/rtic-monotonics/src/nrf/timer.rs @@ -30,6 +30,7 @@ use crate::{Monotonic, TimeoutError, TimerQueue}; use atomic_polyfill::{AtomicU32, Ordering}; use core::future::Future; pub use fugit::{self, ExtU64, ExtU64Ceil}; +use rtic_time::half_period_counter::calculate_now; #[cfg(feature = "nrf52810")] use nrf52810_pac::{self as pac, Interrupt, TIMER0, TIMER1, TIMER2}; @@ -139,17 +140,45 @@ macro_rules! make_timer { // 1 MHz timer.prescaler.write(|w| unsafe { w.prescaler().bits(4) }); timer.bitmode.write(|w| w.bitmode()._32bit()); - timer - .intenset - .modify(|_, w| w.compare0().set().compare1().set()); - timer.cc[1].write(|w| unsafe { w.cc().bits(0) }); // Overflow - timer.tasks_clear.write(|w| unsafe { w.bits(1) }); - timer.tasks_start.write(|w| unsafe { w.bits(1) }); - $tq.initialize(Self {}); + // Disable interrupts, as preparation + timer.intenclr.modify(|_, w| w + .compare0().clear() + .compare1().clear() + .compare2().clear() + ); - timer.events_compare[0].write(|w| w); - timer.events_compare[1].write(|w| w); + // Configure compare registers + timer.cc[0].write(|w| unsafe { w.cc().bits(0) }); // Dynamic wakeup + timer.cc[1].write(|w| unsafe { w.cc().bits(0x0000_0000) }); // Overflow + timer.cc[2].write(|w| unsafe { w.cc().bits(0x8000_0000) }); // Half-period + + // Timing critical, make sure we don't get interrupted + critical_section::with(|_|{ + // Reset the timer + timer.tasks_clear.write(|w| unsafe { w.bits(1) }); + timer.tasks_start.write(|w| unsafe { w.bits(1) }); + + // Clear pending events. + // Should be close enough to the timer reset that we don't miss any events. + timer.events_compare[0].write(|w| w); + timer.events_compare[1].write(|w| w); + timer.events_compare[2].write(|w| w); + + // Make sure overflow counter is synced with the timer value + $overflow.store(0, Ordering::SeqCst); + + // Initialized the timer queue + $tq.initialize(Self {}); + + // Enable interrupts. + // Should be close enough to the timer reset that we don't miss any events. + timer.intenset.modify(|_, w| w + .compare0().set() + .compare1().set() + .compare2().set() + ); + }); // SAFETY: We take full ownership of the peripheral and interrupt vector, // plus we are not using any external shared resources so we won't impact @@ -166,12 +195,6 @@ macro_rules! make_timer { &$tq } - #[inline(always)] - fn is_overflow() -> bool { - let timer = unsafe { &*$timer::PTR }; - timer.events_compare[1].read().bits() & 1 != 0 - } - /// Timeout at a specific time. #[inline] pub async fn timeout_at( @@ -216,45 +239,35 @@ macro_rules! make_timer { type Duration = fugit::TimerDurationU64<1_000_000>; fn now() -> Self::Instant { - // In a critical section to not get a race between overflow updates and reading it - // and the flag here. - critical_section::with(|_| { - let timer = unsafe { &*$timer::PTR }; - timer.tasks_capture[2].write(|w| unsafe { w.bits(1) }); - let cnt = timer.cc[2].read().bits(); + let timer = unsafe { &*$timer::PTR }; - let unhandled_overflow = if Self::is_overflow() { - // The overflow has not been handled yet, so add an extra to the read overflow. - 1 - } else { - 0 - }; - - timer.tasks_capture[2].write(|w| unsafe { w.bits(1) }); - let new_cnt = timer.cc[2].read().bits(); - let cnt = if new_cnt >= cnt { cnt } else { new_cnt } as u64; - - Self::Instant::from_ticks( - (unhandled_overflow + $overflow.load(Ordering::Relaxed) as u64) << 32 - | cnt as u64, - ) - }) + Self::Instant::from_ticks(calculate_now( + $overflow.load(Ordering::Relaxed), + || { + timer.tasks_capture[3].write(|w| unsafe { w.bits(1) }); + timer.cc[3].read().bits() + } + )) } fn on_interrupt() { let timer = unsafe { &*$timer::PTR }; // If there is a compare match on channel 1, it is an overflow - if Self::is_overflow() { + if timer.events_compare[1].read().bits() & 1 != 0 { timer.events_compare[1].write(|w| w); - $overflow.fetch_add(1, Ordering::SeqCst); + let prev = $overflow.fetch_add(1, Ordering::Relaxed); + assert!(prev % 2 == 1, "Monotonic must have skipped an interrupt!"); + } + + // If there is a compare match on channel 2, it is a half-period overflow + if timer.events_compare[2].read().bits() & 1 != 0 { + timer.events_compare[2].write(|w| w); + let prev = $overflow.fetch_add(1, Ordering::Relaxed); + assert!(prev % 2 == 0, "Monotonic must have skipped an interrupt!"); } } - fn enable_timer() {} - - fn disable_timer() {} - fn set_compare(instant: Self::Instant) { let timer = unsafe { &*$timer::PTR }; timer.cc[0].write(|w| unsafe { w.cc().bits(instant.ticks() as u32) }); diff --git a/rtic-monotonics/src/stm32.rs b/rtic-monotonics/src/stm32.rs index c86005effd..601196a3d5 100644 --- a/rtic-monotonics/src/stm32.rs +++ b/rtic-monotonics/src/stm32.rs @@ -272,12 +272,14 @@ macro_rules! make_timer { // Full period if $timer.sr().read().uif() { $timer.sr().modify(|r| r.set_uif(false)); - $overflow.fetch_add(1, Ordering::Relaxed); + let prev = $overflow.fetch_add(1, Ordering::Relaxed); + assert!(prev % 2 == 1, "Monotonic must have missed an interrupt!"); } // Half period if $timer.sr().read().ccif(2) { $timer.sr().modify(|r| r.set_ccif(2, false)); - $overflow.fetch_add(1, Ordering::Relaxed); + let prev = $overflow.fetch_add(1, Ordering::Relaxed); + assert!(prev % 2 == 0, "Monotonic must have missed an interrupt!"); } } } diff --git a/rtic-time/CHANGELOG.md b/rtic-time/CHANGELOG.md index 15ebf5b254..2e02a9d024 100644 --- a/rtic-time/CHANGELOG.md +++ b/rtic-time/CHANGELOG.md @@ -11,6 +11,8 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top! ### Changed +- Docs: Add sanity check to `half_period_counter` code example + ### Fixed ## v1.1.0 - 2023-12-04 diff --git a/rtic-time/src/half_period_counter.rs b/rtic-time/src/half_period_counter.rs index 5d3bf7520b..0aaec5e2f8 100644 --- a/rtic-time/src/half_period_counter.rs +++ b/rtic-time/src/half_period_counter.rs @@ -96,11 +96,13 @@ //! fn on_interrupt() { //! if overflow_interrupt_happened() { //! clear_overflow_interrupt(); -//! HALF_PERIOD_COUNTER.fetch_add(1, Ordering::Relaxed); +//! let prev = HALF_PERIOD_COUNTER.fetch_add(1, Ordering::Relaxed); +//! assert!(prev % 2 == 1, "Monotonic must have skipped an interrupt!"); //! } //! if compare_interrupt_happened() { //! clear_compare_interrupt(); -//! HALF_PERIOD_COUNTER.fetch_add(1, Ordering::Relaxed); +//! let prev = HALF_PERIOD_COUNTER.fetch_add(1, Ordering::Relaxed); +//! assert!(prev % 2 == 0, "Monotonic must have skipped an interrupt!"); //! } //! } //!