feat: add display UI with splash screen, dashboard, and RSSI sparkline
Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
17624 files changed
+4564928
-38
No files matched your search
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"files":{".cargo/config":"b9918d14c386a4eaf93247308d47795f7a82121c56ca7b56c06a321e27e7d53a",".cargo_vcs_info.json":"e0546b740c78846961815412e9cc0b48a5c69a643a32d4205945261c80c18337",".github/CODEOWNERS":"4c941d2793c0360c653073d3532cf1ec80d34133ecc877525b62245fc6c3f340",".github/bors.toml":"99fe396a0f44ba906a1a710ab023c51a401217c424ab10be3ffd503c86efc996",".github/workflows/ci.yml":"1042cf54c967ac770a8f2dabb5215d482a92339434aca5aa15a47afb8c5a8f8a",".github/workflows/clippy.yml":"c2b3b35dc7c48f6b3ad2da9e90c291d0c92505c7940032171c6d3fe991595266",".github/workflows/cron.yml":"ec5187755f7836bd56abfa9c6ef7863e5819be8817c2c42c5faf62e7b7d58658",".github/workflows/rustfmt.yml":"a536c4566cd886d8f2f853d09bbfe65d52b5e6afbaaedc974ca1850dc71be816","CHANGELOG.md":"0746746ed1d76c49bcb397844b176e7bb14511d56df37a66dc38cea98bcff9e6","CODE_OF_CONDUCT.md":"3746a267d008534cec6d7242f09ca6284c5d37e48306531b878461e6c4d33fee","Cargo.toml":"e50cf067b659be75f8f927b342d3446243bdcd86a4b10ea4daff819f6d9ddebd","Cargo.toml.orig":"509c574635f64d40c3797795862188a613ba627a69bc7ae1a5e426eb5023dd00","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"0a633406531735659d570e7903394ec52f7707195a3c4af10279f06eec7acc76","README.md":"2e2e489b55b8506d174782d8587db6708162f82f1fb4c3272746d079e5972837","asm-toolchain":"5479b28f90ce42bbefc9fed083182e0de8eafc1b69f92fbfc5221d766862682f","asm/inline.rs":"6e9605c595d6c8deddcd2bcb1f9f4d270a10492823e82955888be8ce3b553f3b","asm/lib.rs":"8d7eb1aee66e9d8ff8b5ff5d0a8687d6d255d3403b9c238d7b5620a01602d9b5","bin/thumbv6m-none-eabi-lto.a":"4d13ae583b33613b476b8d2dbf435d363ad25a2135388eb5257b132868461535","bin/thumbv6m-none-eabi.a":"b5002ce424d6baf97639ee188f12a9c7152b1dbc90a1b4393556049f8475be54","bin/thumbv7em-none-eabi-lto.a":"a3dd53e4e7a874f9d7d27acb273eaa17ae72222205e939a7dccc4c02ebb7a3ba","bin/thumbv7em-none-eabi.a":"2ae0edab580e3d1c95bedb4bc9a8a646e2b3ea9fa5bf2768d859fb5a30b8741a","bin/thumbv7em-none-eabihf-lto.a":"71d4bd5af31d3933969aee825400ac105f3d3b826abfb968bfa2c8f3658ecd50","bin/thumbv7em-none-eabihf.a":"0ebff8ad140fe35494fb51ed4184ce19ffab41e9ac62af6a5c5c7b0ea26b9e53","bin/thumbv7m-none-eabi-lto.a":"da3ed6ec4b9fc8803336e280cd71524911fee459e2dac88e29d0e82d043b90d7","bin/thumbv7m-none-eabi.a":"3de841b242e33066555fa06e127f01008711ec08728a2f75d525731d04ae442e","bin/thumbv8m.base-none-eabi-lto.a":"73189749f0c42de0990e76ce531621e17370c0dd00940297e6044a622302df38","bin/thumbv8m.base-none-eabi.a":"b2ff0d88db4aa539ead58c1fb37ef3d171d08462b979a8b7c897ef4148119572","bin/thumbv8m.main-none-eabi-lto.a":"12b43c063fcdb33fb57335c5fc2a0c62e2a427a3c6e8727fb2a4437615e2c07d","bin/thumbv8m.main-none-eabi.a":"9217dcbbd3ef909caa39a5ee75684127b4cf4f51447e41027164462e42a33018","bin/thumbv8m.main-none-eabihf-lto.a":"18aae9047b017ad4003e2311d74b0888701b84333ccf4379d09e6890b1799174","bin/thumbv8m.main-none-eabihf.a":"39fdf845895f31e473fc863fbb21f3590237a571dbfb97c66c7190475a615efb","build.rs":"fe2c0bd4cd56adabf5d3912a9810546e6a32708f165d9ad2dcfe0407df092516","src/asm.rs":"079aaf47d696aa1995ddcf5b38f59ed22e8a48967c261d14676d1757308b534a","src/call_asm.rs":"024486575710dc864613c3c2917887ec9527ae2b66cde183b3233f663a9e790b","src/cmse.rs":"9e868ec9ac0a6296b1e9d943478f6343a3f43979c0f068780adf114f52643707","src/critical_section.rs":"2013adbc5289d71c9f5ccb8353fbc47a2df7f7003180be65b6b07b8c948ca684","src/delay.rs":"27f2adeaf9dc924ac16f1fea9da510469e5c4961d9223aab253d95ee3e0af191","src/interrupt.rs":"41d6338784b86d4c4837e3673c5eff26dd35c851962e741c00f7c933a7e8d218","src/itm.rs":"b50d9d6cad2ec4c841d0f3008df93edb378f8038696a9f096763a488d14df53c","src/lib.rs":"079c614ec9e3ba81bb5ab9638f7f465551805555ab41aab04e62f9015d85fe4a","src/macros.rs":"22d8af93ebe59cd9f6e468cf78c8a342c4444d83f263043d3044358811ffec41","src/peripheral/ac.rs":"b6b228637b09f2fa3ac5344a0759cd4fb852782050f252c2c9bd670660b9a768","src/peripheral/cbp.rs":"9a42efc512ac43278a5efd2fb650043a5672be031e7121c748b41ead97ed4e39","src/peripheral/cpuid.rs":"94285da95a2e6a00f09b049f59c9aede6683dab4be4b218d8bd0cd4183e7a746","src/peripheral/dcb.rs":"3c630cc575c93791f1553607565fdc76d91bb990351deedf9de3efa9184eceb0","src/peripheral/dwt.rs":"780a1094958abc2c8c7d8d196c07637eed619c72d010fcf29235b95c0b1abce3","src/peripheral/fpb.rs":"00eba6fae97614a503e9545f95158083f9e02b6b400fab647be1c0e4747293d7","src/peripheral/fpu.rs":"3bb8d5af0cdce5b25a4f9826fa6bddd75c58f4d61745afd9ed49a5d0e849c2f9","src/peripheral/icb.rs":"fcc13f2652ae39035c4b0f04468d19b3e94bfe3b05832eabc60638dcca9842ae","src/peripheral/itm.rs":"f8c8587ffa60f76e4f4fa293322386727ecf491f1b36672258b7e743b6e37a26","src/peripheral/mod.rs":"900f099bbcae804cde74bf8b5bcb9f84e9dde3018f5efe1098768c5b64021b0b","src/peripheral/mpu.rs":"20d36443ae9928cbf9a7d546298b5ce784c3cae3f2f236a8935b227f9fdbdb06","src/peripheral/nvic.rs":"ae9889495edb21d6a4f09ced401b4b21cdfb2c1ac86e593156e462cc42b5794d","src/peripheral/sau.rs":"32fb6816a3f9c81203b23b1803c8f499059a0460e2fa082e44bff3be9936f467","src/peripheral/scb.rs":"fd56eLine truncated
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
[alias]
|
||||
xtask = "run --package xtask --bin xtask --"
|
||||
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"git": {
|
||||
"sha1": "bb4a78208323260a161e68b2498438867f971bc5"
|
||||
},
|
||||
"path_in_vcs": ""
|
||||
}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
* @rust-embedded/cortex-m
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
block_labels = ["needs-decision"]
|
||||
delete_merged_branches = true
|
||||
required_approvals = 1
|
||||
status = [
|
||||
"ci-linux (stable)",
|
||||
"ci-linux (1.38.0)",
|
||||
"rustfmt",
|
||||
"clippy",
|
||||
]
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
on:
|
||||
push:
|
||||
branches: [ staging, trying, master ]
|
||||
pull_request:
|
||||
|
||||
name: CI
|
||||
|
||||
jobs:
|
||||
ci-linux:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: ${{ matrix.experimental || false }}
|
||||
strategy:
|
||||
matrix:
|
||||
# All generated code should be running on stable now
|
||||
rust: [stable]
|
||||
features: ["", "critical-section-single-core"]
|
||||
|
||||
include:
|
||||
# Test MSRV
|
||||
- rust: 1.38.0
|
||||
features: ""
|
||||
|
||||
# Test nightly but don't fail
|
||||
- rust: nightly
|
||||
features: ""
|
||||
experimental: true
|
||||
- rust: nightly
|
||||
features: "critical-section-single-core"
|
||||
experimental: true
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: ${{ matrix.rust }}
|
||||
override: true
|
||||
- name: Run tests
|
||||
run: cargo test --all --features "${{ matrix.features }}"
|
||||
|
||||
# FIXME: test on macOS and Windows
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
on:
|
||||
push:
|
||||
branches: [ staging, trying, master ]
|
||||
pull_request_target:
|
||||
|
||||
name: Clippy check
|
||||
jobs:
|
||||
clippy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
if: github.event_name == 'pull_request_target'
|
||||
with:
|
||||
ref: refs/pull/${{ github.event.number }}/head
|
||||
- uses: actions/checkout@v2
|
||||
if: github.event_name != 'pull_request_target'
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: clippy
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --features critical-section-single-core
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
on:
|
||||
schedule:
|
||||
# Run every week at 8am UTC Saturday.
|
||||
- cron: '0 8 * * SAT'
|
||||
|
||||
name: Cron CI
|
||||
|
||||
jobs:
|
||||
ci-linux:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
- name: Run tests
|
||||
run: cargo test --all
|
||||
- uses: imjohnbo/issue-bot@v2
|
||||
if: failure()
|
||||
with:
|
||||
title: CI Failure
|
||||
labels: ci
|
||||
body: |
|
||||
Scheduled CI run failed. Details:
|
||||
|
||||
https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
on:
|
||||
push:
|
||||
branches: [ staging, trying, master ]
|
||||
pull_request:
|
||||
|
||||
name: Code formatting check
|
||||
|
||||
jobs:
|
||||
fmt:
|
||||
name: rustfmt
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: rustfmt
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
Vendored
+807
@@ -0,0 +1,807 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v0.7.7] - 2023-01-03
|
||||
|
||||
- Add missing documentation for `critical-section-single-core` feature added
|
||||
in v0.7.6.
|
||||
|
||||
## [v0.7.6] - 2022-08-12
|
||||
|
||||
- Added `critical-section-single-core` feature which provides an implementation for the `critical-section` crate for single-core systems, based on disabling all interrupts. (#448)
|
||||
|
||||
## [v0.7.5] - 2022-05-15
|
||||
|
||||
### Deprecated
|
||||
- the `ptr()` function on all peripherals register blocks in favor of
|
||||
the associated constant `PTR` (#386).
|
||||
|
||||
### Changed
|
||||
|
||||
- The `inline-asm` feature no longer requires a nightly Rust compiler, but
|
||||
does require Rust 1.59 or above.
|
||||
|
||||
### Fixed
|
||||
- Fixed `singleton!()` statics sometimes ending up in `.data` instead of `.bss` (#364, #380).
|
||||
(Backported from upcoming 0.8 release).
|
||||
|
||||
## [v0.7.4] - 2021-12-31
|
||||
|
||||
### Added
|
||||
|
||||
- Added support for additional DWT counters (#349)
|
||||
- CPI counter
|
||||
- Exception overhead counter
|
||||
- LSU counter
|
||||
- Folded-instruction counter
|
||||
- Added `DWT.set_cycle_count` (#347).
|
||||
- Added support for the Cortex-M7 TCM and cache access control registers.
|
||||
There is a feature `cm7` to enable access to these (#352).
|
||||
- Add derives for serde, Hash, and PartialOrd to VectActive behind feature
|
||||
gates for host-platform use (#363).
|
||||
- Support host platforms besides x86_64 (#369).
|
||||
- Added `delay::Delay::with_source`, a constructor that lets you specify
|
||||
the SysTick clock source (#374).
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix incorrect AIRCR PRIGROUP mask (#338, #339).
|
||||
- Fix nightly users of inline-asm breaking now that the asm macro is removed
|
||||
from the prelude (#372).
|
||||
|
||||
### Deprecated
|
||||
|
||||
- `DWT::get_cycle_count` has been deprecated in favor of `DWT::cycle_count`.
|
||||
This change was made for consistency with the [C-GETTER] convention. (#349)
|
||||
|
||||
[C-GETTER]: https://rust-lang.github.io/api-guidelines/naming.html#c-getter
|
||||
|
||||
## [v0.7.3] - 2021-07-03
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed compilation for native targets on non-x86 host systems (#336, #337).
|
||||
|
||||
### Added
|
||||
|
||||
- The `Delay` struct now offers direct `delay_us()` and `delay_ms()` methods
|
||||
without having to go through the embedded-hal traits (#344).
|
||||
|
||||
## [v0.7.2] - 2021-03-07
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed a bug where calling `asm::delay()` with an argument of 0 or 1 would
|
||||
underflow, leading to a very long delay.
|
||||
|
||||
## [v0.7.1] - 2021-01-25
|
||||
|
||||
### Added
|
||||
|
||||
- New assembly methods `asm::semihosting_syscall`, `asm::bootstrap`, and
|
||||
`asm::bootload`.
|
||||
|
||||
### Deprecated
|
||||
|
||||
- `msp::write` has been deprecated in favor of `asm::bootstrap`. It was not
|
||||
possible to use `msp::write` without causing Undefined Behavior, so all
|
||||
existing users are encouraged to migrate.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed a bug in `asm::delay` which could lead to incorrect codegen and
|
||||
infinite loops.
|
||||
- Improved timing guarantees of `asm::delay` on multiple-issue CPU cores.
|
||||
- Additional compiler fences added to inline assembly where necessary.
|
||||
- Fixed DWARF debug information in pre-built assembly binaries.
|
||||
|
||||
## [v0.7.0] - 2020-11-09
|
||||
|
||||
### Added
|
||||
|
||||
- New `InterruptNumber` trait is now required on interrupt arguments to the
|
||||
various NVIC functions, replacing the previous use of `Nr` from bare-metal.
|
||||
For backwards compatibility, `InterruptNumber` is implemented for types
|
||||
which are `Nr + Copy`, but this will be removed in a future version.
|
||||
- Associated const `PTR` is introduced to Core Peripherals to
|
||||
eventually replace the existing `ptr()` API.
|
||||
- A delay driver based on SysTick.
|
||||
- You can now use LTO to inline assembly calls, even on stable Rust.
|
||||
See the `asm/lib.rs` documentation for more details.
|
||||
- Initial ARMv8-M MPU support
|
||||
- ICTR and ACTLR registers added
|
||||
- Support for the Security Attribution Unit on ARMv8-M
|
||||
|
||||
### Changed
|
||||
|
||||
- Previously, asm calls without the `inline-asm` feature enabled used pre-built
|
||||
objects which were built by a GCC compiler, while `inline-asm` enabled the
|
||||
use of `llvm_asm!` calls. The asm system has been replaced with a new
|
||||
technique which generates Rust static libs for stable calling, and uses the
|
||||
new `asm!` macro with `inline-asm`. See the `asm/lib.rs` documentation for
|
||||
more details.
|
||||
- Cache enabling now uses an assembly sequence to ensure correctness.
|
||||
- `ptr()` methods are now `const`.
|
||||
|
||||
### Breaking Changes
|
||||
- `SCB::invalidate_dcache` and related methods are now unsafe, see #188
|
||||
- `Peripherals` struct is now non-exhaustive, so fields may be added in future
|
||||
non-breaking changes
|
||||
- Removed `aligned` dependency
|
||||
- Removed const-fn feature
|
||||
- Removed previously deprecated APIs
|
||||
- `NVIC::clear_pending`
|
||||
- `NVIC::disable`
|
||||
- `NVIC::enable`
|
||||
- `NVIC::set_pending`
|
||||
- `SCB::system_reset`
|
||||
- Removed `basepri`, `basepri_max`, and `faultmask` registers from thumbv8m.base
|
||||
|
||||
## [v0.6.7] - 2021-01-26
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed missing `peripheral::itm` reexport.
|
||||
|
||||
## [v0.6.6] - 2021-01-26
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed missing ITM reexport on `thumbv8m.base` targets.
|
||||
|
||||
## [v0.6.5] - 2021-01-24
|
||||
|
||||
### Changed
|
||||
|
||||
- This release is forwards-compatible with cortex-m 0.7, and depends on and
|
||||
re-exports many types from that version. Both 0.6.5 and 0.7 may co-exist
|
||||
in a build.
|
||||
|
||||
## [v0.6.4] - 2020-10-26
|
||||
|
||||
### Changed
|
||||
|
||||
- MSRV bumped to 1.36.0 due to `aligned` dependency.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Drop AT&T syntax from inline asm, which was causing miscompilations with newer versions of the compiler.
|
||||
|
||||
## [v0.6.3] - 2020-07-20
|
||||
|
||||
### Added
|
||||
|
||||
- Initial Cortex-M Security Extension support for armv8m
|
||||
- `UDF` intrinsic
|
||||
- Methods to enable/disable exceptions in SCB
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix bug in `asm::delay` not updating status clobber flags
|
||||
- Swapped to `llvm_asm!` to support inline assembly on new nightlies
|
||||
- Our precompiled assembly routines have additional debug information
|
||||
- ITM `is_fifo_ready` improved to support armv8
|
||||
- Cache enabling moved to pre-built assembly routines to prevent possible
|
||||
undefined behaviour
|
||||
|
||||
## [v0.6.2] - 2020-01-12
|
||||
|
||||
### Added
|
||||
|
||||
- Allow writing to the `CONTROL` register via `register::control::write`
|
||||
- Add `DWT::unlock()` for a safe way to unlock the DWT
|
||||
|
||||
### Deprecation
|
||||
|
||||
- Deprecated incorrectly included registers (`BASPRI`, `BASEPRI_MAX`, `FAULTMASK`) on `thumbv8.base`
|
||||
|
||||
## [v0.6.1] - 2019-08-21
|
||||
|
||||
### Fixed
|
||||
|
||||
- Better `Debug`, `PartialEq` and `Eq` for more types
|
||||
- The `delay` function is fixed for Cortex-M0 MCUs
|
||||
|
||||
### Added
|
||||
|
||||
- Static version of `system_reset` as `system_reset2`
|
||||
- Now uses `links = "cortex-m"` to not link multiple versions of the crate
|
||||
- Masking of the NVIC is added `NVIC::{mask,unmask}`
|
||||
- Now Rust 2018 edition
|
||||
- `{M,P}SPLIM` access is now possible on ARMv8-M
|
||||
|
||||
### Deprecation
|
||||
|
||||
- `system_reset` is deprecated in favor of `sys_reset`
|
||||
|
||||
## [v0.6.0] - 2019-03-12
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix numerous registers which were incorrectly included for thumbv6
|
||||
- `SHCRS` renamed to `SHCSR` in `SCB`
|
||||
|
||||
### Added
|
||||
|
||||
- Support for ARMv8-M (`thumbv8.base` and `thumbv8.main`)
|
||||
|
||||
- `SCB` gained methods to set and clear `SLEEPONEXIT` bit
|
||||
|
||||
- `NVIC` gained `STIR` register and methods to request an interrupt
|
||||
|
||||
- `DCB` gained methods to check if debugger is attached
|
||||
|
||||
## [v0.5.8] - 2018-10-27
|
||||
|
||||
### Added
|
||||
|
||||
- `SCB` gained methods to set, clear and check the pending state of the PendSV
|
||||
exception.
|
||||
|
||||
- `SCB` gained methods to set, clear and check the pending state of the SysTick
|
||||
exception.
|
||||
|
||||
- `SCB` gained methods to set and get the priority of system handlers like
|
||||
SVCall and SysTick.
|
||||
|
||||
- `NVIC` gained *static* methods, `pend` and `unpend`, to set and clear the
|
||||
pending state of interrupts.
|
||||
|
||||
### Changed
|
||||
|
||||
- The `NVIC.{clear,set}_pending` methods have been deprecated in favor of
|
||||
`NVIC::{unpend,pend}`.
|
||||
|
||||
## [v0.5.7] - 2018-09-06
|
||||
|
||||
### Added
|
||||
|
||||
- `DCB::enable_trace()` and `DCB::disable_trace()`
|
||||
|
||||
### Changed
|
||||
|
||||
- `iprintln!` no longer depends on `iprint!`. `cortex_m::iprintln!` will work
|
||||
even if `cortex_m::iprint` has not been imported.
|
||||
|
||||
## [v0.5.6] - 2018-08-27
|
||||
|
||||
### Fixed
|
||||
|
||||
- Removed duplicated symbols from binary blobs
|
||||
|
||||
- The check-blobs.sh script
|
||||
|
||||
## [v0.5.5] - 2018-08-27 - YANKED
|
||||
|
||||
### Changed
|
||||
|
||||
- This crate no longer depends on `arm-none-eabi-gcc`.
|
||||
|
||||
## [v0.5.4] - 2018-08-11
|
||||
|
||||
### Added
|
||||
|
||||
- A method to trigger a system reset. See `SCB.system_reset`.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Made the VTOR register (see peripheral::SCB) available on `thumbv6m-none-eabi`. This register is
|
||||
present on Cortex-M0+, but not on Cortex-M0.
|
||||
|
||||
- Linking with LLD by marking all external assembly functions as `.thumb_func`. See
|
||||
https://bugs.llvm.org/show_bug.cgi?id=38435 for details.
|
||||
|
||||
## [v0.5.3] - 2018-08-02
|
||||
|
||||
### Fixed
|
||||
|
||||
- Don't assemble basepri*.s and faultmask.s for ARMv6-M. This fix the build when using `clang` as
|
||||
the assembler.
|
||||
|
||||
## [v0.5.2] - 2018-05-18
|
||||
|
||||
### Added
|
||||
|
||||
- `SCB` gained a pair of safe methods to set / clear the DEEPSLEEP bit.
|
||||
|
||||
- `asm::delay`, delay loops whose execution time doesn't depend on the optimization level.
|
||||
|
||||
## [v0.5.1] - 2018-05-13
|
||||
|
||||
### Added
|
||||
|
||||
- An opt-in `"const-fn"` feature that makes `Mutex.new` constructor into a `const fn`. This feature
|
||||
requires a nightly toolchain.
|
||||
|
||||
## [v0.5.0] - 2018-05-11
|
||||
|
||||
### Added
|
||||
|
||||
- `DebugMonitor` and `SecureFault` variants to the `Exception` enumeration.
|
||||
|
||||
- An optional `"inline-asm"` feature
|
||||
|
||||
### Changed
|
||||
|
||||
- [breaking-change] This crate now requires `arm-none-eabi-gcc` to be installed and available in
|
||||
`$PATH` when built with the `"inline-asm"` feature disabled (which is disabled by default).
|
||||
|
||||
- [breaking-change] The `register::{apsr,lr,pc}` modules are now behind the `"inline-asm"` feature.
|
||||
|
||||
- [breaking-change] Some variants of the `Exception` enumeration are no longer available on
|
||||
`thumbv6m-none-eabi`. See API docs for details.
|
||||
|
||||
- [breaking-change] Several of the variants of the `Exception` enumeration have been renamed to
|
||||
match the CMSIS specification.
|
||||
|
||||
- [breaking-change] fixed typo in `shcrs` field of `scb::RegisterBlock`; it was previously named
|
||||
`shpcrs`.
|
||||
|
||||
- [breaking-change] removed several fields from `scb::RegisterBlock` on ARMv6-M. These registers are
|
||||
not available on that sub-architecture.
|
||||
|
||||
- [breaking-change] changed the type of `scb::RegisterBlock.shpr` from `RW<u8>` to `RW<u32>` on
|
||||
ARMv6-M. These registers are word accessible only on that sub-architecture.
|
||||
|
||||
- [breaking-change] renamed the `mmar` field of `scb::RegisterBlock` to `mmfar` to match the CMSIS
|
||||
name.
|
||||
|
||||
- [breaking-change] removed the `iabr` field from `scb::RegisterBlock` on ARMv6-M. This register is
|
||||
not available on that sub-architecture.
|
||||
|
||||
- [breaking-change] removed several fields from `cpuid::RegisterBlock` on ARMv6-M. These registers
|
||||
are not available on that sub-architecture.
|
||||
|
||||
- [breaking-change] The `Mutex.new` constructor is not a `const fn` by default. To make it a `const
|
||||
fn` you have to opt into the `"const-fn"` feature, which was added in v0.5.1, and switch to a
|
||||
nightly compiler.
|
||||
|
||||
### Removed
|
||||
|
||||
- [breaking-change] The `exception` module has been removed. A replacement for `Exception::active`
|
||||
can be found in `SCB::vect_active`. A modified version `exception::Exception` can be found in the
|
||||
`peripheral::scb` module.
|
||||
|
||||
## [v0.4.3] - 2018-01-25
|
||||
|
||||
### Changed
|
||||
|
||||
- The initial value of a `singleton!` no longer needs to be evaluable in const context; it can now
|
||||
be a value computed at runtime, or even a capture of some other local variable.
|
||||
|
||||
## [v0.4.2] - 2018-01-17
|
||||
|
||||
### Fixed
|
||||
|
||||
- Added a missing `Send` implementation to all the peripherals.
|
||||
|
||||
## [v0.4.1] - 2018-01-16
|
||||
|
||||
### Changed
|
||||
|
||||
- `peripheral::Peripherals` is now re-exported at the root of the crate.
|
||||
|
||||
## [v0.4.0] - 2018-01-15
|
||||
|
||||
### Added
|
||||
|
||||
- Formatter and Flush Control register (FFCR) accessor to the TPIU register block.
|
||||
|
||||
- A `singleton!` macro that creates mutable reference to a statically allocated variable.
|
||||
|
||||
- A Cargo feature, `cm7-r0p1`, to work around a silicon erratum that affects writes to BASEPRI on
|
||||
Cortex-M7 r0p1 devices.
|
||||
|
||||
### Changed
|
||||
|
||||
- [breaking-change] All peripherals are now exposed as scoped singletons and they need to be `take`n
|
||||
into scope to become accessible.
|
||||
|
||||
- [breaking-change] The signatures of methods exposed by peripheral proxies have changed to
|
||||
better match the new scoped singletons semantics.
|
||||
|
||||
- All the thin wrappers around assembly instructions now panic when executed on non-ARM devices.
|
||||
|
||||
### Removed
|
||||
|
||||
- [breaking-change] APIs specific to ARMv7-M (`peripheral::{cbp, fpb, fpu, itm, tpiu}`, `itm`) when
|
||||
compiling for `thumb6m-none-eabi`.
|
||||
|
||||
## [v0.3.1] - 2017-07-20
|
||||
|
||||
### Changed
|
||||
|
||||
- `{basepri,basepri_max}::write` are now compiler barriers for the same reason
|
||||
that `interrupt::{disable,enable}` are: they are used to create critical
|
||||
sections.
|
||||
|
||||
## [v0.3.0] - 2017-07-07
|
||||
|
||||
### Changed
|
||||
|
||||
- [breaking-change] Renamed `StackedRergisters` to `ExceptionFrame` to better
|
||||
reflect the ARM documentation.
|
||||
|
||||
- [breaking-change] Renamed the variants of `Exception` to better match the
|
||||
ARM documentation.
|
||||
|
||||
- [breaking-change] Renamed `Exception::current` to `Exception::active` and
|
||||
changed the signature to return `None` when no exception is being serviced.
|
||||
|
||||
- Moved bits non specific to the Cortex-M architecture into the [`bare-metal`]
|
||||
crate with the goal of sharing code between this crate and crates tailored for
|
||||
other (microcontroller) architectures.
|
||||
|
||||
[`bare-metal`]: https://crates.io/crates/bare-metal
|
||||
|
||||
### Removed
|
||||
|
||||
- [breaking-change] The `ctxt` module along with the exception "tokens" in the
|
||||
`exception` module. The `cortex-m-rt` crate v0.3.0 provides a more ergonomic
|
||||
mechanism to add state to interrupts / exceptions; replace your uses of
|
||||
`Local` with that.
|
||||
|
||||
- [breaking-change] `default_handler`, `DEFAULT_HANDLERS` and `Handlers` from
|
||||
the `exception` module as well as `Reserved` from the root of the crate.
|
||||
`cortex-m-rt` v0.3.0 provides a mechanism to override exceptions and the
|
||||
default exception handler. Change your use of these `Handlers` and others to
|
||||
that.
|
||||
|
||||
### Fixed
|
||||
|
||||
- `interrupt::{enable,disable}` are now compiler barriers. The compiler should
|
||||
not reorder code around these function calls for memory safety; that is the
|
||||
case now.
|
||||
|
||||
## [v0.2.11] - 2017-06-16
|
||||
|
||||
### Added
|
||||
|
||||
- An API to maintain the different caches (DCache, ICache) on Cortex M7 devices.
|
||||
|
||||
### Fixed
|
||||
|
||||
- the definition of the `ehprint!` macro.
|
||||
- the implementation of the FPU API.
|
||||
|
||||
## [v0.2.10] - 2017-06-05
|
||||
|
||||
### Added
|
||||
|
||||
- Functions for the instructions DMB, ISB and DSB
|
||||
|
||||
### Changed
|
||||
|
||||
- All the functions in the `asm` module are now `inline(always)`
|
||||
|
||||
## [v0.2.9] - 2017-05-30
|
||||
|
||||
### Fixed
|
||||
|
||||
- A bug in `itm::write_all` where it would ignore the length of the buffer and
|
||||
serialize contents that come after the buffer.
|
||||
|
||||
## [v0.2.8] - 2017-05-30 - YANKED
|
||||
|
||||
### Added
|
||||
|
||||
- An `itm::write_aligned` function to write 4 byte aligned buffers to an ITM
|
||||
port. This function is faster than `itm::write_all` for small buffers but
|
||||
requires the buffer to be aligned.
|
||||
|
||||
## [v0.2.7] - 2017-05-23
|
||||
|
||||
### Added
|
||||
|
||||
- `Dwt.enable_cycle_counter`
|
||||
|
||||
## [v0.2.6] - 2017-05-08
|
||||
|
||||
### Fixed
|
||||
|
||||
- [breaking-change]. MEMORY UNSAFETY. `Mutex` could be used as a channel to send
|
||||
interrupt tokens from one interrupt to other thus breaking the context `Local`
|
||||
abstraction. See reproduction case below. This has been fixed by making
|
||||
`Mutex` `Sync` only if the protected data is `Send`.
|
||||
|
||||
``` rust
|
||||
#![feature(const_fn)]
|
||||
#![feature(used)]
|
||||
#![no_std]
|
||||
|
||||
use core::cell::RefCell;
|
||||
|
||||
use cortex_m::ctxt::Local;
|
||||
use cortex_m::interrupt::Mutex;
|
||||
use stm32f30x::interrupt::{self, Exti0, Exti1};
|
||||
|
||||
fn main() {
|
||||
// ..
|
||||
|
||||
// trigger exti0
|
||||
// then trigger exti0 again
|
||||
}
|
||||
|
||||
static CHANNEL: Mutex<RefCell<Option<Exti0>>> = Mutex::new(RefCell::new(None));
|
||||
// Supposedly task *local* data
|
||||
static LOCAL: Local<i32, Exti0> = Local::new(0);
|
||||
|
||||
extern "C" fn exti0(mut ctxt: Exti0) {
|
||||
static FIRST: Local<bool, Exti0> = Local::new(true);
|
||||
|
||||
let first = *FIRST.borrow(&ctxt);
|
||||
|
||||
// toggle
|
||||
if first {
|
||||
*FIRST.borrow_mut(&mut ctxt) = false;
|
||||
}
|
||||
|
||||
if first {
|
||||
cortex_m::interrupt::free(
|
||||
|cs| {
|
||||
let channel = CHANNEL.borrow(cs);
|
||||
|
||||
// BAD: transfer interrupt token to another interrupt
|
||||
*channel.borrow_mut() = Some(ctxt);
|
||||
},
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
let _local = LOCAL.borrow_mut(&mut ctxt);
|
||||
|
||||
// ..
|
||||
|
||||
// trigger exti1 here
|
||||
|
||||
// ..
|
||||
|
||||
// `LOCAL` mutably borrowed up to this point
|
||||
}
|
||||
|
||||
extern "C" fn exti1(_ctxt: Exti1) {
|
||||
cortex_m::interrupt::free(|cs| {
|
||||
let channel = CHANNEL.borrow(cs);
|
||||
let mut channel = channel.borrow_mut();
|
||||
|
||||
if let Some(mut other_task) = channel.take() {
|
||||
// BAD: `exti1` has access to `exti0`'s interrupt token
|
||||
// so it can now mutably access local while `exti0` is also using it
|
||||
let _local = LOCAL.borrow_mut(&mut other_task);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[used]
|
||||
#[link_section = ".rodata.interrupts"]
|
||||
static INTERRUPTS: interrupt::Handlers = interrupt::Handlers {
|
||||
Exti0: exti0,
|
||||
Exti1: exti1,
|
||||
..interrupt::DEFAULT_HANDLERS
|
||||
};
|
||||
```
|
||||
|
||||
## [v0.2.5] - 2017-05-07 - YANKED
|
||||
|
||||
### Added
|
||||
|
||||
- Higher level API for the SysTick and FPU peripherals
|
||||
|
||||
### Fixed
|
||||
|
||||
- [breaking-change]. MEMORY UNSAFETY. `interrupt::enable` was safe to call
|
||||
inside an `interrupt::free` critical section thus breaking the preemption
|
||||
protection. The `interrupt::enable` method is now `unsafe`.
|
||||
|
||||
## [v0.2.4] - 2017-04-20 - YANKED
|
||||
|
||||
### Fixed
|
||||
|
||||
- [breaking-change]. MEMORY UNSAFETY. `interrupt::free` leaked the critical
|
||||
section making it possible to access a `Mutex` when interrupts are enabled
|
||||
(see below). This has been fixed by changing the signature of
|
||||
`interrupt::free`.
|
||||
|
||||
``` rust
|
||||
static FOO: Mutex<bool> = Mutex::new(false);
|
||||
|
||||
fn main() {
|
||||
let cs = cortex_m::interrupt::free(|cs| cs);
|
||||
// interrupts are enabled at this point
|
||||
let foo = FOO.borrow(&cs);
|
||||
}
|
||||
```
|
||||
|
||||
## [v0.2.3] - 2017-04-11 - YANKED
|
||||
|
||||
### Fixed
|
||||
|
||||
- [breaking-change]. MEMORY UNSAFETY. Some concurrency models that use "partial"
|
||||
critical sections (cf. BASEPRI) can be broken by changing the priority of
|
||||
interrupts or by changing BASEPRI in some scenarios. For this reason
|
||||
`NVIC.set_priority` and `register::basepri::write` are now `unsafe`.
|
||||
|
||||
## [v0.2.2] - 2017-04-08 - YANKED
|
||||
|
||||
### Fixed
|
||||
|
||||
- [breaking-change]. MEMORY UNSAFETY. The `Mutex.borrow_mut` method has been
|
||||
removed as it can be used to bypass Rust's borrow checker and get, for
|
||||
example, two mutable references to the same data.
|
||||
|
||||
``` rust
|
||||
static FOO: Mutex<bool> = Mutex::new(false);
|
||||
|
||||
fn main() {
|
||||
cortex_m::interrupt::free(|mut cs1| {
|
||||
cortex_m::interrupt::free(|mut cs2| {
|
||||
let foo: &mut bool = FOO.borrow_mut(&mut cs1);
|
||||
let and_foo: &mut bool = FOO.borrow_mut(&mut cs2);
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## [v0.2.1] - 2017-03-12 - YANKED
|
||||
|
||||
### Changed
|
||||
|
||||
- The default exception handler now identifies the exception that's being
|
||||
serviced.
|
||||
|
||||
## [v0.2.0] - 2017-03-11 - YANKED
|
||||
|
||||
### Added
|
||||
|
||||
- Semihosting functionality in the `semihosting` module.
|
||||
|
||||
- `exception::Handlers` struct that represent the section of the vector table
|
||||
that contains the exception handlers.
|
||||
|
||||
- A default exception handler
|
||||
|
||||
- A high level API for the NVIC peripheral.
|
||||
|
||||
- Context local data.
|
||||
|
||||
- `borrow`/`borrow_mut` methods to `Mutex` that replace `lock`.
|
||||
|
||||
- API and macros to send bytes / (formatted) strings through ITM
|
||||
|
||||
### Changed
|
||||
|
||||
- [breaking-change] `StackFrame` has been renamed to `StackedRegisters` and
|
||||
moved into the `exceptions` module.
|
||||
|
||||
- [breaking-change] Core peripherals can now be modified via a `&-` reference
|
||||
and are no longer `Sync`.
|
||||
|
||||
- [breaking-change] `interrupt::free`'s closure now includes a critical section
|
||||
token, `CriticalSection`.
|
||||
|
||||
- [breaking-change] the core register API has been revamped for type safety.
|
||||
|
||||
- The safety of assembly wrappers like `wfi` and `interrupt::free` has been
|
||||
reviewed. In many cases, the functions are no longer unsafe.
|
||||
|
||||
- [breaking-change] `bkpt!` has been turned into a function. It no longer
|
||||
accepts an immediate value.
|
||||
|
||||
### Removed
|
||||
|
||||
- `vector_table` and its associated `struct`, `VectorTable`. It's not a good
|
||||
idea to give people a simple way to call the exception handlers.
|
||||
|
||||
- `Mutex`'s `lock` method as it's unsound. You could use it to get multiple
|
||||
`&mut -` references to the wrapped data.
|
||||
|
||||
## [v0.1.6] - 2017-01-22
|
||||
|
||||
### Added
|
||||
|
||||
- `Exception` a enumeration of the kind of exceptions the processor can service.
|
||||
There's also a `Exception::current` constructor that returns the `Exception`
|
||||
that's currently being serviced.
|
||||
|
||||
## [v0.1.5]
|
||||
|
||||
### Added
|
||||
|
||||
- `interrupt::Mutex`, a "mutex" based on critical sections.
|
||||
|
||||
### Changed
|
||||
|
||||
- The closure that `interrupt::free` takes can now return a value.
|
||||
|
||||
## [v0.1.4]
|
||||
|
||||
### Added
|
||||
|
||||
- `asm::nop`, a wrapper over the NOP instruction
|
||||
|
||||
## [v0.1.3]
|
||||
|
||||
### Added
|
||||
|
||||
- a StackFrame data structure
|
||||
|
||||
## [v0.1.2] - 2016-10-04
|
||||
|
||||
### Fixed
|
||||
|
||||
- Read/write Operations on registers (lr, cr, msp, etc.) which were reversed.
|
||||
|
||||
## [v0.1.1] - 2016-10-03 - YANKED
|
||||
|
||||
### Changed
|
||||
|
||||
- Small, non user visible change to make this crate compile further for $HOST (e.g. x86_64) with the
|
||||
goal of making it possible to test, on the HOST, downstream crates that depend on this one.
|
||||
|
||||
## v0.1.0 - 2016-09-27 - YANKED
|
||||
|
||||
### Added
|
||||
|
||||
- Functions to access core peripherals like NVIC, SCB and SysTick.
|
||||
- Functions to access core registers like CONTROL, MSP and PSR.
|
||||
- Functions to enable/disable interrupts
|
||||
- Functions to get the vector table
|
||||
- Wrappers over miscellaneous instructions like `bkpt`
|
||||
|
||||
[Unreleased]: https://github.com/rust-embedded/cortex-m/compare/v0.7.7...HEAD
|
||||
[v0.7.7]: https://github.com/rust-embedded/cortex-m/compare/v0.7.6...v0.7.7
|
||||
[v0.7.6]: https://github.com/rust-embedded/cortex-m/compare/v0.7.5...v0.7.6
|
||||
[v0.7.5]: https://github.com/rust-embedded/cortex-m/compare/v0.7.4...v0.7.5
|
||||
[v0.7.4]: https://github.com/rust-embedded/cortex-m/compare/v0.7.3...v0.7.4
|
||||
[v0.7.3]: https://github.com/rust-embedded/cortex-m/compare/v0.7.2...v0.7.3
|
||||
[v0.7.2]: https://github.com/rust-embedded/cortex-m/compare/v0.7.1...v0.7.2
|
||||
[v0.7.1]: https://github.com/rust-embedded/cortex-m/compare/v0.7.0...v0.7.1
|
||||
[v0.7.0]: https://github.com/rust-embedded/cortex-m/compare/v0.6.4...v0.7.0
|
||||
[v0.6.7]: https://github.com/rust-embedded/cortex-m/compare/v0.6.6...v0.6.7
|
||||
[v0.6.6]: https://github.com/rust-embedded/cortex-m/compare/v0.6.5...v0.6.6
|
||||
[v0.6.5]: https://github.com/rust-embedded/cortex-m/compare/v0.6.4...v0.6.5
|
||||
[v0.6.4]: https://github.com/rust-embedded/cortex-m/compare/v0.6.3...v0.6.4
|
||||
[v0.6.3]: https://github.com/rust-embedded/cortex-m/compare/v0.6.2...v0.6.3
|
||||
[v0.6.2]: https://github.com/rust-embedded/cortex-m/compare/v0.6.1...v0.6.2
|
||||
[v0.6.1]: https://github.com/rust-embedded/cortex-m/compare/v0.6.0...v0.6.1
|
||||
[v0.6.0]: https://github.com/rust-embedded/cortex-m/compare/v0.5.8...v0.6.0
|
||||
[v0.5.8]: https://github.com/rust-embedded/cortex-m/compare/v0.5.7...v0.5.8
|
||||
[v0.5.7]: https://github.com/rust-embedded/cortex-m/compare/v0.5.6...v0.5.7
|
||||
[v0.5.6]: https://github.com/rust-embedded/cortex-m/compare/v0.5.5...v0.5.6
|
||||
[v0.5.5]: https://github.com/rust-embedded/cortex-m/compare/v0.5.4...v0.5.5
|
||||
[v0.5.4]: https://github.com/rust-embedded/cortex-m/compare/v0.5.3...v0.5.4
|
||||
[v0.5.3]: https://github.com/rust-embedded/cortex-m/compare/v0.5.2...v0.5.3
|
||||
[v0.5.2]: https://github.com/rust-embedded/cortex-m/compare/v0.5.1...v0.5.2
|
||||
[v0.5.1]: https://github.com/rust-embedded/cortex-m/compare/v0.5.0...v0.5.1
|
||||
[v0.5.0]: https://github.com/rust-embedded/cortex-m/compare/v0.4.3...v0.5.0
|
||||
[v0.4.3]: https://github.com/rust-embedded/cortex-m/compare/v0.4.2...v0.4.3
|
||||
[v0.4.2]: https://github.com/rust-embedded/cortex-m/compare/v0.4.1...v0.4.2
|
||||
[v0.4.1]: https://github.com/rust-embedded/cortex-m/compare/v0.4.0...v0.4.1
|
||||
[v0.4.0]: https://github.com/rust-embedded/cortex-m/compare/v0.3.1...v0.4.0
|
||||
[v0.3.1]: https://github.com/rust-embedded/cortex-m/compare/v0.3.0...v0.3.1
|
||||
[v0.3.0]: https://github.com/rust-embedded/cortex-m/compare/v0.2.11...v0.3.0
|
||||
[v0.2.11]: https://github.com/rust-embedded/cortex-m/compare/v0.2.10...v0.2.11
|
||||
[v0.2.10]: https://github.com/rust-embedded/cortex-m/compare/v0.2.9...v0.2.10
|
||||
[v0.2.9]: https://github.com/rust-embedded/cortex-m/compare/v0.2.8...v0.2.9
|
||||
[v0.2.8]: https://github.com/rust-embedded/cortex-m/compare/v0.2.7...v0.2.8
|
||||
[v0.2.7]: https://github.com/rust-embedded/cortex-m/compare/v0.2.6...v0.2.7
|
||||
[v0.2.6]: https://github.com/rust-embedded/cortex-m/compare/v0.2.5...v0.2.6
|
||||
[v0.2.5]: https://github.com/rust-embedded/cortex-m/compare/v0.2.4...v0.2.5
|
||||
[v0.2.4]: https://github.com/rust-embedded/cortex-m/compare/v0.2.3...v0.2.4
|
||||
[v0.2.3]: https://github.com/rust-embedded/cortex-m/compare/v0.2.2...v0.2.3
|
||||
[v0.2.2]: https://github.com/rust-embedded/cortex-m/compare/v0.2.1...v0.2.2
|
||||
[v0.2.1]: https://github.com/rust-embedded/cortex-m/compare/v0.2.0...v0.2.1
|
||||
[v0.2.0]: https://github.com/rust-embedded/cortex-m/compare/v0.1.6...v0.2.0
|
||||
[v0.1.6]: https://github.com/rust-embedded/cortex-m/compare/v0.1.5...v0.1.6
|
||||
[v0.1.5]: https://github.com/rust-embedded/cortex-m/compare/v0.1.4...v0.1.5
|
||||
[v0.1.4]: https://github.com/rust-embedded/cortex-m/compare/v0.1.3...v0.1.4
|
||||
[v0.1.3]: https://github.com/rust-embedded/cortex-m/compare/v0.1.2...v0.1.3
|
||||
[v0.1.2]: https://github.com/rust-embedded/cortex-m/compare/v0.1.1...v0.1.2
|
||||
[v0.1.1]: https://github.com/rust-embedded/cortex-m/compare/v0.1.0...v0.1.1
|
||||
Vendored
+37
@@ -0,0 +1,37 @@
|
||||
# The Rust Code of Conduct
|
||||
|
||||
## Conduct
|
||||
|
||||
**Contact**: [Cortex-M team](https://github.com/rust-embedded/wg#the-cortex-m-team)
|
||||
|
||||
* We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other similar characteristic.
|
||||
* On IRC, please avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and welcoming environment for all.
|
||||
* Please be kind and courteous. There's no need to be mean or rude.
|
||||
* Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer.
|
||||
* Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works.
|
||||
* We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We interpret the term "harassment" as including the definition in the [Citizen Code of Conduct](http://citizencodeofconduct.org/); if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups.
|
||||
* Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact one of the channel ops or any of the [Cortex-M team][team] immediately. Whether you're a regular contributor or a newcomer, we care about making this community a safe place for you and we've got your back.
|
||||
* Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome.
|
||||
|
||||
## Moderation
|
||||
|
||||
These are the policies for upholding our community's standards of conduct.
|
||||
|
||||
1. Remarks that violate the Rust standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.)
|
||||
2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed.
|
||||
3. Moderators will first respond to such remarks with a warning.
|
||||
4. If the warning is unheeded, the user will be "kicked," i.e., kicked out of the communication channel to cool off.
|
||||
5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded.
|
||||
6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended party a genuine apology.
|
||||
7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a different moderator, **in private**. Complaints about bans in-channel are not allowed.
|
||||
8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate situation, they should expect less leeway than others.
|
||||
|
||||
In the Rust community we strive to go the extra step to look out for each other. Don't just aim to be technically unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly if they're off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can drive people away from the community entirely.
|
||||
|
||||
And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good there was something you could've communicated better — remember that it's your responsibility to make your fellow Rustaceans comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their trust.
|
||||
|
||||
The enforcement policies listed above apply to all official embedded WG venues; including official IRC channels (#rust-embedded); GitHub repositories under rust-embedded; and all forums under rust-embedded.org (forum.rust-embedded.org).
|
||||
|
||||
*Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) as well as the [Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).*
|
||||
|
||||
[team]: https://github.com/rust-embedded/wg#the-cortex-m-team
|
||||
Vendored
+77
@@ -0,0 +1,77 @@
|
||||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you are reading this file be aware that the original Cargo.toml
|
||||
# will likely look very different (and much more reasonable).
|
||||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
name = "cortex-m"
|
||||
version = "0.7.7"
|
||||
authors = [
|
||||
"The Cortex-M Team <cortex-m@teams.rust-embedded.org>",
|
||||
"Jorge Aparicio <jorge@japaric.io>",
|
||||
]
|
||||
links = "cortex-m"
|
||||
description = "Low level access to Cortex-M processors"
|
||||
documentation = "https://docs.rs/cortex-m"
|
||||
readme = "README.md"
|
||||
keywords = [
|
||||
"arm",
|
||||
"cortex-m",
|
||||
"register",
|
||||
"peripheral",
|
||||
]
|
||||
categories = [
|
||||
"embedded",
|
||||
"hardware-support",
|
||||
"no-std",
|
||||
]
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/rust-embedded/cortex-m"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = [
|
||||
"thumbv8m.main-none-eabihf",
|
||||
"thumbv6m-none-eabi",
|
||||
"thumbv7em-none-eabi",
|
||||
"thumbv7em-none-eabihf",
|
||||
"thumbv7m-none-eabi",
|
||||
"thumbv8m.base-none-eabi",
|
||||
"thumbv8m.main-none-eabi",
|
||||
]
|
||||
|
||||
[dependencies.bare-metal]
|
||||
version = "0.2.4"
|
||||
features = ["const-fn"]
|
||||
|
||||
[dependencies.bitfield]
|
||||
version = "0.13.2"
|
||||
|
||||
[dependencies.critical-section]
|
||||
version = "1.0.0"
|
||||
optional = true
|
||||
|
||||
[dependencies.embedded-hal]
|
||||
version = "0.2.4"
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1"
|
||||
features = ["derive"]
|
||||
optional = true
|
||||
|
||||
[dependencies.volatile-register]
|
||||
version = "0.2.0"
|
||||
|
||||
[features]
|
||||
cm7 = []
|
||||
cm7-r0p1 = ["cm7"]
|
||||
critical-section-single-core = ["critical-section/restore-state-bool"]
|
||||
inline-asm = []
|
||||
linker-plugin-lto = []
|
||||
std = []
|
||||
Vendored
+201
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
Copyright (c) 2016 Jorge Aparicio
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
Vendored
+39
@@ -0,0 +1,39 @@
|
||||
[](https://crates.io/crates/cortex-m)
|
||||
[](https://crates.io/crates/cortex-m)
|
||||
|
||||
# `cortex-m`
|
||||
|
||||
> Low level access to Cortex-M processors
|
||||
|
||||
This project is developed and maintained by the [Cortex-M team][team].
|
||||
|
||||
## [Documentation](https://docs.rs/crate/cortex-m)
|
||||
|
||||
## Minimum Supported Rust Version (MSRV)
|
||||
|
||||
This crate is guaranteed to compile on stable Rust 1.38 and up. It might compile with older versions but that may change in any new patch release.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the
|
||||
work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any
|
||||
additional terms or conditions.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
Contribution to this crate is organized under the terms of the [Rust Code of
|
||||
Conduct][CoC], the maintainer of this crate, the [Cortex-M team][team], promises
|
||||
to intervene to uphold that code of conduct.
|
||||
|
||||
[CoC]: CODE_OF_CONDUCT.md
|
||||
[team]: https://github.com/rust-embedded/wg#the-cortex-m-team
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
nightly-2021-12-16
|
||||
Vendored
+448
@@ -0,0 +1,448 @@
|
||||
//! Inline assembly implementing the routines exposed in `cortex_m::asm`.
|
||||
//!
|
||||
//! If the `inline-asm` feature is enabled, these functions will be directly called by the
|
||||
//! `cortex-m` wrappers. Otherwise, `cortex-m` links against them via prebuilt archives.
|
||||
//!
|
||||
//! All of these functions should be blanket-`unsafe`. `cortex-m` provides safe wrappers where
|
||||
//! applicable.
|
||||
|
||||
use core::arch::asm;
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __bkpt() {
|
||||
asm!("bkpt", options(nomem, nostack, preserves_flags));
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __control_r() -> u32 {
|
||||
let r;
|
||||
asm!("mrs {}, CONTROL", out(reg) r, options(nomem, nostack, preserves_flags));
|
||||
r
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __control_w(w: u32) {
|
||||
// ISB is required after writing to CONTROL,
|
||||
// per ARM architectural requirements (see Application Note 321).
|
||||
asm!(
|
||||
"msr CONTROL, {}",
|
||||
"isb",
|
||||
in(reg) w,
|
||||
options(nomem, nostack, preserves_flags),
|
||||
);
|
||||
|
||||
// Ensure memory accesses are not reordered around the CONTROL update.
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __cpsid() {
|
||||
asm!("cpsid i", options(nomem, nostack, preserves_flags));
|
||||
|
||||
// Ensure no subsequent memory accesses are reordered to before interrupts are disabled.
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __cpsie() {
|
||||
// Ensure no preceeding memory accesses are reordered to after interrupts are enabled.
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
asm!("cpsie i", options(nomem, nostack, preserves_flags));
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __delay(cyc: u32) {
|
||||
// The loop will normally take 3 to 4 CPU cycles per iteration, but superscalar cores
|
||||
// (eg. Cortex-M7) can potentially do it in 2, so we use that as the lower bound, since delaying
|
||||
// for more cycles is okay.
|
||||
// Add 1 to prevent an integer underflow which would cause a long freeze
|
||||
let real_cyc = 1 + cyc / 2;
|
||||
asm!(
|
||||
// Use local labels to avoid R_ARM_THM_JUMP8 relocations which fail on thumbv6m.
|
||||
"1:",
|
||||
"subs {}, #1",
|
||||
"bne 1b",
|
||||
inout(reg) real_cyc => _,
|
||||
options(nomem, nostack),
|
||||
);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __dmb() {
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
asm!("dmb", options(nomem, nostack, preserves_flags));
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __dsb() {
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
asm!("dsb", options(nomem, nostack, preserves_flags));
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __isb() {
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
asm!("isb", options(nomem, nostack, preserves_flags));
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __msp_r() -> u32 {
|
||||
let r;
|
||||
asm!("mrs {}, MSP", out(reg) r, options(nomem, nostack, preserves_flags));
|
||||
r
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __msp_w(val: u32) {
|
||||
// Technically is writing to the stack pointer "not pushing any data to the stack"?
|
||||
// In any event, if we don't set `nostack` here, this method is useless as the new
|
||||
// stack value is immediately mutated by returning. Really this is just not a good
|
||||
// method and its higher-level use is marked as deprecated in cortex-m.
|
||||
asm!("msr MSP, {}", in(reg) val, options(nomem, nostack, preserves_flags));
|
||||
}
|
||||
|
||||
// NOTE: No FFI shim, this requires inline asm.
|
||||
#[inline(always)]
|
||||
pub unsafe fn __apsr_r() -> u32 {
|
||||
let r;
|
||||
asm!("mrs {}, APSR", out(reg) r, options(nomem, nostack, preserves_flags));
|
||||
r
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __nop() {
|
||||
// NOTE: This is a `pure` asm block, but applying that option allows the compiler to eliminate
|
||||
// the nop entirely (or to collapse multiple subsequent ones). Since the user probably wants N
|
||||
// nops when they call `nop` N times, let's not add that option.
|
||||
asm!("nop", options(nomem, nostack, preserves_flags));
|
||||
}
|
||||
|
||||
// NOTE: No FFI shim, this requires inline asm.
|
||||
#[inline(always)]
|
||||
pub unsafe fn __pc_r() -> u32 {
|
||||
let r;
|
||||
asm!("mov {}, pc", out(reg) r, options(nomem, nostack, preserves_flags));
|
||||
r
|
||||
}
|
||||
|
||||
// NOTE: No FFI shim, this requires inline asm.
|
||||
#[inline(always)]
|
||||
pub unsafe fn __pc_w(val: u32) {
|
||||
asm!("mov pc, {}", in(reg) val, options(nomem, nostack, preserves_flags));
|
||||
}
|
||||
|
||||
// NOTE: No FFI shim, this requires inline asm.
|
||||
#[inline(always)]
|
||||
pub unsafe fn __lr_r() -> u32 {
|
||||
let r;
|
||||
asm!("mov {}, lr", out(reg) r, options(nomem, nostack, preserves_flags));
|
||||
r
|
||||
}
|
||||
|
||||
// NOTE: No FFI shim, this requires inline asm.
|
||||
#[inline(always)]
|
||||
pub unsafe fn __lr_w(val: u32) {
|
||||
asm!("mov lr, {}", in(reg) val, options(nomem, nostack, preserves_flags));
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __primask_r() -> u32 {
|
||||
let r;
|
||||
asm!("mrs {}, PRIMASK", out(reg) r, options(nomem, nostack, preserves_flags));
|
||||
r
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __psp_r() -> u32 {
|
||||
let r;
|
||||
asm!("mrs {}, PSP", out(reg) r, options(nomem, nostack, preserves_flags));
|
||||
r
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __psp_w(val: u32) {
|
||||
// See comment on __msp_w. Unlike MSP, there are legitimate use-cases for modifying PSP
|
||||
// if MSP is currently being used as the stack pointer.
|
||||
asm!("msr PSP, {}", in(reg) val, options(nomem, nostack, preserves_flags));
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __sev() {
|
||||
asm!("sev", options(nomem, nostack, preserves_flags));
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __udf() -> ! {
|
||||
asm!("udf #0", options(noreturn, nomem, nostack, preserves_flags));
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __wfe() {
|
||||
asm!("wfe", options(nomem, nostack, preserves_flags));
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __wfi() {
|
||||
asm!("wfi", options(nomem, nostack, preserves_flags));
|
||||
}
|
||||
|
||||
/// Semihosting syscall.
|
||||
#[inline(always)]
|
||||
pub unsafe fn __sh_syscall(mut nr: u32, arg: u32) -> u32 {
|
||||
asm!("bkpt #0xab", inout("r0") nr, in("r1") arg, options(nomem, nostack, preserves_flags));
|
||||
nr
|
||||
}
|
||||
|
||||
/// Set CONTROL.SPSEL to 0, write `msp` to MSP, branch to `rv`.
|
||||
#[inline(always)]
|
||||
pub unsafe fn __bootstrap(msp: u32, rv: u32) -> ! {
|
||||
asm!(
|
||||
"mrs {tmp}, CONTROL",
|
||||
"bics {tmp}, {spsel}",
|
||||
"msr CONTROL, {tmp}",
|
||||
"isb",
|
||||
"msr MSP, {msp}",
|
||||
"bx {rv}",
|
||||
// `out(reg) _` is not permitted in a `noreturn` asm! call,
|
||||
// so instead use `in(reg) 0` and don't restore it afterwards.
|
||||
tmp = in(reg) 0,
|
||||
spsel = in(reg) 2,
|
||||
msp = in(reg) msp,
|
||||
rv = in(reg) rv,
|
||||
options(noreturn, nomem, nostack),
|
||||
);
|
||||
}
|
||||
|
||||
// v7m *AND* v8m.main, but *NOT* v8m.base
|
||||
#[cfg(any(armv7m, armv8m_main))]
|
||||
pub use self::v7m::*;
|
||||
#[cfg(any(armv7m, armv8m_main))]
|
||||
mod v7m {
|
||||
use core::arch::asm;
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __basepri_max(val: u8) {
|
||||
asm!("msr BASEPRI_MAX, {}", in(reg) val, options(nomem, nostack, preserves_flags));
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __basepri_r() -> u8 {
|
||||
let r;
|
||||
asm!("mrs {}, BASEPRI", out(reg) r, options(nomem, nostack, preserves_flags));
|
||||
r
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __basepri_w(val: u8) {
|
||||
asm!("msr BASEPRI, {}", in(reg) val, options(nomem, nostack, preserves_flags));
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __faultmask_r() -> u32 {
|
||||
let r;
|
||||
asm!("mrs {}, FAULTMASK", out(reg) r, options(nomem, nostack, preserves_flags));
|
||||
r
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __enable_icache() {
|
||||
asm!(
|
||||
"ldr {0}, =0xE000ED14", // CCR
|
||||
"mrs {2}, PRIMASK", // save critical nesting info
|
||||
"cpsid i", // mask interrupts
|
||||
"ldr {1}, [{0}]", // read CCR
|
||||
"orr.w {1}, {1}, #(1 << 17)", // Set bit 17, IC
|
||||
"str {1}, [{0}]", // write it back
|
||||
"dsb", // ensure store completes
|
||||
"isb", // synchronize pipeline
|
||||
"msr PRIMASK, {2}", // unnest critical section
|
||||
out(reg) _,
|
||||
out(reg) _,
|
||||
out(reg) _,
|
||||
options(nostack),
|
||||
);
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __enable_dcache() {
|
||||
asm!(
|
||||
"ldr {0}, =0xE000ED14", // CCR
|
||||
"mrs {2}, PRIMASK", // save critical nesting info
|
||||
"cpsid i", // mask interrupts
|
||||
"ldr {1}, [{0}]", // read CCR
|
||||
"orr.w {1}, {1}, #(1 << 16)", // Set bit 16, DC
|
||||
"str {1}, [{0}]", // write it back
|
||||
"dsb", // ensure store completes
|
||||
"isb", // synchronize pipeline
|
||||
"msr PRIMASK, {2}", // unnest critical section
|
||||
out(reg) _,
|
||||
out(reg) _,
|
||||
out(reg) _,
|
||||
options(nostack),
|
||||
);
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(armv7em)]
|
||||
pub use self::v7em::*;
|
||||
#[cfg(armv7em)]
|
||||
mod v7em {
|
||||
use core::arch::asm;
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __basepri_max_cm7_r0p1(val: u8) {
|
||||
asm!(
|
||||
"mrs {1}, PRIMASK",
|
||||
"cpsid i",
|
||||
"tst.w {1}, #1",
|
||||
"msr BASEPRI_MAX, {0}",
|
||||
"it ne",
|
||||
"bxne lr",
|
||||
"cpsie i",
|
||||
in(reg) val,
|
||||
out(reg) _,
|
||||
options(nomem, nostack, preserves_flags),
|
||||
);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __basepri_w_cm7_r0p1(val: u8) {
|
||||
asm!(
|
||||
"mrs {1}, PRIMASK",
|
||||
"cpsid i",
|
||||
"tst.w {1}, #1",
|
||||
"msr BASEPRI, {0}",
|
||||
"it ne",
|
||||
"bxne lr",
|
||||
"cpsie i",
|
||||
in(reg) val,
|
||||
out(reg) _,
|
||||
options(nomem, nostack, preserves_flags),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(armv8m)]
|
||||
pub use self::v8m::*;
|
||||
/// Baseline and Mainline.
|
||||
#[cfg(armv8m)]
|
||||
mod v8m {
|
||||
use core::arch::asm;
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __tt(mut target: u32) -> u32 {
|
||||
asm!(
|
||||
"tt {target}, {target}",
|
||||
target = inout(reg) target,
|
||||
options(nomem, nostack, preserves_flags),
|
||||
);
|
||||
target
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __ttt(mut target: u32) -> u32 {
|
||||
asm!(
|
||||
"ttt {target}, {target}",
|
||||
target = inout(reg) target,
|
||||
options(nomem, nostack, preserves_flags),
|
||||
);
|
||||
target
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __tta(mut target: u32) -> u32 {
|
||||
asm!(
|
||||
"tta {target}, {target}",
|
||||
target = inout(reg) target,
|
||||
options(nomem, nostack, preserves_flags),
|
||||
);
|
||||
target
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __ttat(mut target: u32) -> u32 {
|
||||
asm!(
|
||||
"ttat {target}, {target}",
|
||||
target = inout(reg) target,
|
||||
options(nomem, nostack, preserves_flags),
|
||||
);
|
||||
target
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __msp_ns_r() -> u32 {
|
||||
let r;
|
||||
asm!("mrs {}, MSP_NS", out(reg) r, options(nomem, nostack, preserves_flags));
|
||||
r
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __msp_ns_w(val: u32) {
|
||||
asm!("msr MSP_NS, {}", in(reg) val, options(nomem, nostack, preserves_flags));
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __bxns(val: u32) {
|
||||
asm!("BXNS {}", in(reg) val, options(nomem, nostack, preserves_flags));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(armv8m_main)]
|
||||
pub use self::v8m_main::*;
|
||||
/// Mainline only.
|
||||
#[cfg(armv8m_main)]
|
||||
mod v8m_main {
|
||||
use core::arch::asm;
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __msplim_r() -> u32 {
|
||||
let r;
|
||||
asm!("mrs {}, MSPLIM", out(reg) r, options(nomem, nostack, preserves_flags));
|
||||
r
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __msplim_w(val: u32) {
|
||||
asm!("msr MSPLIM, {}", in(reg) val, options(nomem, nostack, preserves_flags));
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __psplim_r() -> u32 {
|
||||
let r;
|
||||
asm!("mrs {}, PSPLIM", out(reg) r, options(nomem, nostack, preserves_flags));
|
||||
r
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __psplim_w(val: u32) {
|
||||
asm!("msr PSPLIM, {}", in(reg) val, options(nomem, nostack, preserves_flags));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(has_fpu)]
|
||||
pub use self::fpu::*;
|
||||
/// All targets with FPU.
|
||||
#[cfg(has_fpu)]
|
||||
mod fpu {
|
||||
use core::arch::asm;
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __fpscr_r() -> u32 {
|
||||
let r;
|
||||
asm!("vmrs {}, fpscr", out(reg) r, options(nomem, nostack, preserves_flags));
|
||||
r
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn __fpscr_w(val: u32) {
|
||||
asm!("vmsr fpscr, {}", in(reg) val, options(nomem, nostack));
|
||||
}
|
||||
}
|
||||
Vendored
+143
@@ -0,0 +1,143 @@
|
||||
//! FFI shim around the inline assembly in `inline.rs`.
|
||||
//!
|
||||
//! We use this file to precompile some assembly stubs into the static libraries you can find in
|
||||
//! `bin`. Apps using the `cortex-m` crate then link against those static libraries and don't need
|
||||
//! to build this file themselves.
|
||||
//!
|
||||
//! Nowadays the assembly stubs are no longer actual assembly files, but actually just this small
|
||||
//! Rust crate that uses unstable inline assembly, coupled with the `xtask` tool to invoke rustc
|
||||
//! and build the files.
|
||||
//!
|
||||
//! Precompiling this to a static lib allows users to call assembly routines from stable Rust, but
|
||||
//! also perform [linker plugin LTO] with the precompiled artifacts to completely inline the
|
||||
//! assembly routines into their code, which brings the "outline assembly" on par with "real" inline
|
||||
//! assembly.
|
||||
//!
|
||||
//! For developers and contributors to `cortex-m`, this setup means that they don't have to install
|
||||
//! any binutils, assembler, or C compiler to hack on the crate. All they need is to run `cargo
|
||||
//! xtask assemble` to rebuild the archives from this file.
|
||||
//!
|
||||
//! Cool, right?
|
||||
//!
|
||||
//! # Rust version management
|
||||
//!
|
||||
//! Since inline assembly is still unstable, and we want to ensure that the created blobs are
|
||||
//! up-to-date in CI, we have to pin the nightly version we use for this. The nightly toolchain is
|
||||
//! stored in `asm-toolchain`.
|
||||
//!
|
||||
//! The `cargo xtask` automation will automatically install the `asm-toolchain` as well as all
|
||||
//! Cortex-M targets needed to generate the blobs.
|
||||
//!
|
||||
//! [linker plugin LTO]: https://doc.rust-lang.org/stable/rustc/linker-plugin-lto.html
|
||||
|
||||
#![feature(asm)]
|
||||
#![no_std]
|
||||
#![crate_type = "staticlib"]
|
||||
#![deny(warnings)]
|
||||
// Don't warn about feature(asm) being stable on Rust >= 1.59.0
|
||||
#![allow(stable_features)]
|
||||
|
||||
mod inline;
|
||||
|
||||
macro_rules! shims {
|
||||
(
|
||||
$( fn $name:ident( $($arg:ident: $argty:ty),* ) $(-> $ret:ty)?; )+
|
||||
) => {
|
||||
$(
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn $name(
|
||||
$($arg: $argty),*
|
||||
) $(-> $ret)? {
|
||||
crate::inline::$name($($arg),*)
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
shims! {
|
||||
fn __bkpt();
|
||||
fn __control_r() -> u32;
|
||||
fn __control_w(w: u32);
|
||||
fn __cpsid();
|
||||
fn __cpsie();
|
||||
fn __delay(cyc: u32);
|
||||
fn __dmb();
|
||||
fn __dsb();
|
||||
fn __isb();
|
||||
fn __msp_r() -> u32;
|
||||
fn __msp_w(val: u32);
|
||||
fn __nop();
|
||||
fn __primask_r() -> u32;
|
||||
fn __psp_r() -> u32;
|
||||
fn __psp_w(val: u32);
|
||||
fn __sev();
|
||||
fn __udf() -> !;
|
||||
fn __wfe();
|
||||
fn __wfi();
|
||||
fn __sh_syscall(nr: u32, arg: u32) -> u32;
|
||||
fn __bootstrap(msp: u32, rv: u32) -> !;
|
||||
}
|
||||
|
||||
// v7m *AND* v8m.main, but *NOT* v8m.base
|
||||
#[cfg(any(armv7m, armv8m_main))]
|
||||
shims! {
|
||||
fn __basepri_max(val: u8);
|
||||
fn __basepri_r() -> u8;
|
||||
fn __basepri_w(val: u8);
|
||||
fn __faultmask_r() -> u32;
|
||||
fn __enable_icache();
|
||||
fn __enable_dcache();
|
||||
}
|
||||
|
||||
#[cfg(armv7em)]
|
||||
shims! {
|
||||
fn __basepri_max_cm7_r0p1(val: u8);
|
||||
fn __basepri_w_cm7_r0p1(val: u8);
|
||||
}
|
||||
|
||||
// Baseline and Mainline.
|
||||
#[cfg(armv8m)]
|
||||
shims! {
|
||||
fn __tt(target: u32) -> u32;
|
||||
fn __ttt(target: u32) -> u32;
|
||||
fn __tta(target: u32) -> u32;
|
||||
fn __ttat(target: u32) -> u32;
|
||||
fn __msp_ns_r() -> u32;
|
||||
fn __msp_ns_w(val: u32);
|
||||
fn __bxns(val: u32);
|
||||
}
|
||||
|
||||
// Mainline only.
|
||||
#[cfg(armv8m_main)]
|
||||
shims! {
|
||||
fn __msplim_r() -> u32;
|
||||
fn __msplim_w(val: u32);
|
||||
fn __psplim_r() -> u32;
|
||||
fn __psplim_w(val: u32);
|
||||
}
|
||||
|
||||
// All targets with FPU.
|
||||
#[cfg(has_fpu)]
|
||||
shims! {
|
||||
fn __fpscr_r() -> u32;
|
||||
fn __fpscr_w(val: u32);
|
||||
}
|
||||
|
||||
/// We *must* define a panic handler here, even though nothing here should ever be able to panic.
|
||||
///
|
||||
/// We prove that nothing will ever panic by calling a function that doesn't exist. If the panic
|
||||
/// handler gets linked in, this causes a linker error. We always build this file with optimizations
|
||||
/// enabled, but even without them the panic handler should never be linked in.
|
||||
#[panic_handler]
|
||||
#[link_section = ".text.asm_panic_handler"]
|
||||
fn panic(_: &core::panic::PanicInfo) -> ! {
|
||||
extern "C" {
|
||||
#[link_name = "cortex-m internal error: panic handler not optimized out, please file an \
|
||||
issue at https://github.com/rust-embedded/cortex-m"]
|
||||
fn __cortex_m_should_not_panic() -> !;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
__cortex_m_should_not_panic();
|
||||
}
|
||||
}
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Vendored
+54
@@ -0,0 +1,54 @@
|
||||
use std::path::PathBuf;
|
||||
use std::{env, fs};
|
||||
|
||||
fn main() {
|
||||
let target = env::var("TARGET").unwrap();
|
||||
let host_triple = env::var("HOST").unwrap();
|
||||
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
let name = env::var("CARGO_PKG_NAME").unwrap();
|
||||
|
||||
if host_triple == target {
|
||||
println!("cargo:rustc-cfg=native");
|
||||
}
|
||||
|
||||
if target.starts_with("thumb") {
|
||||
let suffix = if env::var_os("CARGO_FEATURE_LINKER_PLUGIN_LTO").is_some() {
|
||||
"-lto"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
fs::copy(
|
||||
format!("bin/{}{}.a", target, suffix),
|
||||
out_dir.join(format!("lib{}.a", name)),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
println!("cargo:rustc-link-lib=static={}", name);
|
||||
println!("cargo:rustc-link-search={}", out_dir.display());
|
||||
}
|
||||
|
||||
if target.starts_with("thumbv6m-") {
|
||||
println!("cargo:rustc-cfg=cortex_m");
|
||||
println!("cargo:rustc-cfg=armv6m");
|
||||
} else if target.starts_with("thumbv7m-") {
|
||||
println!("cargo:rustc-cfg=cortex_m");
|
||||
println!("cargo:rustc-cfg=armv7m");
|
||||
} else if target.starts_with("thumbv7em-") {
|
||||
println!("cargo:rustc-cfg=cortex_m");
|
||||
println!("cargo:rustc-cfg=armv7m");
|
||||
println!("cargo:rustc-cfg=armv7em"); // (not currently used)
|
||||
} else if target.starts_with("thumbv8m.base") {
|
||||
println!("cargo:rustc-cfg=cortex_m");
|
||||
println!("cargo:rustc-cfg=armv8m");
|
||||
println!("cargo:rustc-cfg=armv8m_base");
|
||||
} else if target.starts_with("thumbv8m.main") {
|
||||
println!("cargo:rustc-cfg=cortex_m");
|
||||
println!("cargo:rustc-cfg=armv8m");
|
||||
println!("cargo:rustc-cfg=armv8m_main");
|
||||
}
|
||||
|
||||
if target.ends_with("-eabihf") {
|
||||
println!("cargo:rustc-cfg=has_fpu");
|
||||
}
|
||||
}
|
||||
Vendored
+209
@@ -0,0 +1,209 @@
|
||||
//! Miscellaneous assembly instructions
|
||||
|
||||
// When inline assembly is enabled, pull in the assembly routines here. `call_asm!` will invoke
|
||||
// these routines.
|
||||
#[cfg(feature = "inline-asm")]
|
||||
#[path = "../asm/inline.rs"]
|
||||
pub(crate) mod inline;
|
||||
|
||||
/// Puts the processor in Debug state. Debuggers can pick this up as a "breakpoint".
|
||||
///
|
||||
/// **NOTE** calling `bkpt` when the processor is not connected to a debugger will cause an
|
||||
/// exception.
|
||||
#[inline(always)]
|
||||
pub fn bkpt() {
|
||||
call_asm!(__bkpt());
|
||||
}
|
||||
|
||||
/// Blocks the program for *at least* `cycles` CPU cycles.
|
||||
///
|
||||
/// This is implemented in assembly so its execution time is independent of the optimization
|
||||
/// level, however it is dependent on the specific architecture and core configuration.
|
||||
///
|
||||
/// NOTE that the delay can take much longer if interrupts are serviced during its execution
|
||||
/// and the execution time may vary with other factors. This delay is mainly useful for simple
|
||||
/// timer-less initialization of peripherals if and only if accurate timing is not essential. In
|
||||
/// any other case please use a more accurate method to produce a delay.
|
||||
#[inline]
|
||||
pub fn delay(cycles: u32) {
|
||||
call_asm!(__delay(cycles: u32));
|
||||
}
|
||||
|
||||
/// A no-operation. Useful to prevent delay loops from being optimized away.
|
||||
#[inline]
|
||||
pub fn nop() {
|
||||
call_asm!(__nop());
|
||||
}
|
||||
|
||||
/// Generate an Undefined Instruction exception.
|
||||
///
|
||||
/// Can be used as a stable alternative to `core::intrinsics::abort`.
|
||||
#[inline]
|
||||
pub fn udf() -> ! {
|
||||
call_asm!(__udf() -> !)
|
||||
}
|
||||
|
||||
/// Wait For Event
|
||||
#[inline]
|
||||
pub fn wfe() {
|
||||
call_asm!(__wfe())
|
||||
}
|
||||
|
||||
/// Wait For Interrupt
|
||||
#[inline]
|
||||
pub fn wfi() {
|
||||
call_asm!(__wfi())
|
||||
}
|
||||
|
||||
/// Send Event
|
||||
#[inline]
|
||||
pub fn sev() {
|
||||
call_asm!(__sev())
|
||||
}
|
||||
|
||||
/// Instruction Synchronization Barrier
|
||||
///
|
||||
/// Flushes the pipeline in the processor, so that all instructions following the `ISB` are fetched
|
||||
/// from cache or memory, after the instruction has been completed.
|
||||
#[inline]
|
||||
pub fn isb() {
|
||||
call_asm!(__isb())
|
||||
}
|
||||
|
||||
/// Data Synchronization Barrier
|
||||
///
|
||||
/// Acts as a special kind of memory barrier. No instruction in program order after this instruction
|
||||
/// can execute until this instruction completes. This instruction completes only when both:
|
||||
///
|
||||
/// * any explicit memory access made before this instruction is complete
|
||||
/// * all cache and branch predictor maintenance operations before this instruction complete
|
||||
#[inline]
|
||||
pub fn dsb() {
|
||||
call_asm!(__dsb())
|
||||
}
|
||||
|
||||
/// Data Memory Barrier
|
||||
///
|
||||
/// Ensures that all explicit memory accesses that appear in program order before the `DMB`
|
||||
/// instruction are observed before any explicit memory accesses that appear in program order
|
||||
/// after the `DMB` instruction.
|
||||
#[inline]
|
||||
pub fn dmb() {
|
||||
call_asm!(__dmb())
|
||||
}
|
||||
|
||||
/// Test Target
|
||||
///
|
||||
/// Queries the Security state and access permissions of a memory location.
|
||||
/// Returns a Test Target Response Payload (cf section D1.2.215 of
|
||||
/// Armv8-M Architecture Reference Manual).
|
||||
#[inline]
|
||||
#[cfg(armv8m)]
|
||||
// The __tt function does not dereference the pointer received.
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
pub fn tt(addr: *mut u32) -> u32 {
|
||||
let addr = addr as u32;
|
||||
call_asm!(__tt(addr: u32) -> u32)
|
||||
}
|
||||
|
||||
/// Test Target Unprivileged
|
||||
///
|
||||
/// Queries the Security state and access permissions of a memory location for an unprivileged
|
||||
/// access to that location.
|
||||
/// Returns a Test Target Response Payload (cf section D1.2.215 of
|
||||
/// Armv8-M Architecture Reference Manual).
|
||||
#[inline]
|
||||
#[cfg(armv8m)]
|
||||
// The __ttt function does not dereference the pointer received.
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
pub fn ttt(addr: *mut u32) -> u32 {
|
||||
let addr = addr as u32;
|
||||
call_asm!(__ttt(addr: u32) -> u32)
|
||||
}
|
||||
|
||||
/// Test Target Alternate Domain
|
||||
///
|
||||
/// Queries the Security state and access permissions of a memory location for a Non-Secure access
|
||||
/// to that location. This instruction is only valid when executing in Secure state and is
|
||||
/// undefined if used from Non-Secure state.
|
||||
/// Returns a Test Target Response Payload (cf section D1.2.215 of
|
||||
/// Armv8-M Architecture Reference Manual).
|
||||
#[inline]
|
||||
#[cfg(armv8m)]
|
||||
// The __tta function does not dereference the pointer received.
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
pub fn tta(addr: *mut u32) -> u32 {
|
||||
let addr = addr as u32;
|
||||
call_asm!(__tta(addr: u32) -> u32)
|
||||
}
|
||||
|
||||
/// Test Target Alternate Domain Unprivileged
|
||||
///
|
||||
/// Queries the Security state and access permissions of a memory location for a Non-Secure and
|
||||
/// unprivileged access to that location. This instruction is only valid when executing in Secure
|
||||
/// state and is undefined if used from Non-Secure state.
|
||||
/// Returns a Test Target Response Payload (cf section D1.2.215 of
|
||||
/// Armv8-M Architecture Reference Manual).
|
||||
#[inline]
|
||||
#[cfg(armv8m)]
|
||||
// The __ttat function does not dereference the pointer received.
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
pub fn ttat(addr: *mut u32) -> u32 {
|
||||
let addr = addr as u32;
|
||||
call_asm!(__ttat(addr: u32) -> u32)
|
||||
}
|
||||
|
||||
/// Branch and Exchange Non-secure
|
||||
///
|
||||
/// See section C2.4.26 of Armv8-M Architecture Reference Manual for details.
|
||||
/// Undefined if executed in Non-Secure state.
|
||||
#[inline]
|
||||
#[cfg(armv8m)]
|
||||
pub unsafe fn bx_ns(addr: u32) {
|
||||
call_asm!(__bxns(addr: u32));
|
||||
}
|
||||
|
||||
/// Semihosting syscall.
|
||||
///
|
||||
/// This method is used by cortex-m-semihosting to provide semihosting syscalls.
|
||||
#[inline]
|
||||
pub unsafe fn semihosting_syscall(nr: u32, arg: u32) -> u32 {
|
||||
call_asm!(__sh_syscall(nr: u32, arg: u32) -> u32)
|
||||
}
|
||||
|
||||
/// Bootstrap.
|
||||
///
|
||||
/// Clears CONTROL.SPSEL (setting the main stack to be the active stack),
|
||||
/// updates the main stack pointer to the address in `msp`, then jumps
|
||||
/// to the address in `rv`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `msp` and `rv` must point to valid stack memory and executable code,
|
||||
/// respectively.
|
||||
#[inline]
|
||||
pub unsafe fn bootstrap(msp: *const u32, rv: *const u32) -> ! {
|
||||
// Ensure thumb mode is set.
|
||||
let rv = (rv as u32) | 1;
|
||||
let msp = msp as u32;
|
||||
call_asm!(__bootstrap(msp: u32, rv: u32) -> !);
|
||||
}
|
||||
|
||||
/// Bootload.
|
||||
///
|
||||
/// Reads the initial stack pointer value and reset vector from
|
||||
/// the provided vector table address, sets the active stack to
|
||||
/// the main stack, sets the main stack pointer to the new initial
|
||||
/// stack pointer, then jumps to the reset vector.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The provided `vector_table` must point to a valid vector
|
||||
/// table, with a valid stack pointer as the first word and
|
||||
/// a valid reset vector as the second word.
|
||||
#[inline]
|
||||
pub unsafe fn bootload(vector_table: *const u32) -> ! {
|
||||
let msp = core::ptr::read_volatile(vector_table);
|
||||
let rv = core::ptr::read_volatile(vector_table.offset(1));
|
||||
bootstrap(msp as *const u32, rv as *const u32);
|
||||
}
|
||||
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
/// An internal macro to invoke an assembly routine.
|
||||
///
|
||||
/// Depending on whether the unstable `inline-asm` feature is enabled, this will either call into
|
||||
/// the inline assembly implementation directly, or through the FFI shim (see `asm/lib.rs`).
|
||||
macro_rules! call_asm {
|
||||
( $func:ident ( $($args:ident: $tys:ty),* ) $(-> $ret:ty)? ) => {{
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
match () {
|
||||
#[cfg(feature = "inline-asm")]
|
||||
() => crate::asm::inline::$func($($args),*),
|
||||
|
||||
#[cfg(not(feature = "inline-asm"))]
|
||||
() => {
|
||||
extern "C" {
|
||||
fn $func($($args: $tys),*) $(-> $ret)?;
|
||||
}
|
||||
|
||||
$func($($args),*)
|
||||
},
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
Vendored
+238
@@ -0,0 +1,238 @@
|
||||
//! Cortex-M Security Extensions
|
||||
//!
|
||||
//! This module provides several helper functions to support Armv8-M and Armv8.1-M Security
|
||||
//! Extensions.
|
||||
//! Most of this implementation is directly inspired by the "Armv8-M Security Extensions:
|
||||
//! Requirements on Development Tools" document available here:
|
||||
//! https://developer.arm.com/docs/ecm0359818/latest
|
||||
//!
|
||||
//! Please note that the TT instructions support as described part 4 of the document linked above is
|
||||
//! not part of CMSE but is still present in this module. The TT instructions return the
|
||||
//! configuration of the Memory Protection Unit at an address.
|
||||
//!
|
||||
//! # Notes
|
||||
//!
|
||||
//! * Non-Secure Unprivileged code will always read zeroes from TestTarget and should not use it.
|
||||
//! * Non-Secure Privileged code can check current (AccessType::Current) and Non-Secure Unprivileged
|
||||
//! accesses (AccessType::Unprivileged).
|
||||
//! * Secure Unprivileged code can check Non-Secure Unprivileged accesses (AccessType::NonSecure).
|
||||
//! * Secure Privileged code can check all access types.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```
|
||||
//! use cortex_m::cmse::{TestTarget, AccessType};
|
||||
//!
|
||||
//! // suspect_address was given by Non-Secure to a Secure function to write at it.
|
||||
//! // But is it allowed to?
|
||||
//! let suspect_address_test = TestTarget::check(0xDEADBEEF as *mut u32,
|
||||
//! AccessType::NonSecureUnprivileged);
|
||||
//! if suspect_address_test.ns_read_and_writable() {
|
||||
//! // Non-Secure can not read or write this address!
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use crate::asm::{tt, tta, ttat, ttt};
|
||||
use bitfield::bitfield;
|
||||
|
||||
/// Memory access behaviour: determine which privilege execution mode is used and which Memory
|
||||
/// Protection Unit (MPU) is used.
|
||||
#[derive(PartialEq, Copy, Clone, Debug)]
|
||||
pub enum AccessType {
|
||||
/// Access using current privilege level and reading from current security state MPU.
|
||||
/// Uses the TT instruction.
|
||||
Current,
|
||||
/// Unprivileged access reading from current security state MPU. Uses the TTT instruction.
|
||||
Unprivileged,
|
||||
/// Access using current privilege level reading from Non-Secure MPU. Uses the TTA instruction.
|
||||
/// Undefined if used from Non-Secure state.
|
||||
NonSecure,
|
||||
/// Unprivilege access reading from Non-Secure MPU. Uses the TTAT instruction.
|
||||
/// Undefined if used from Non-Secure state.
|
||||
NonSecureUnprivileged,
|
||||
}
|
||||
|
||||
/// Abstraction of TT instructions and helper functions to determine the security and privilege
|
||||
/// attribute of a target address, accessed in different ways.
|
||||
#[derive(PartialEq, Copy, Clone, Debug)]
|
||||
pub struct TestTarget {
|
||||
tt_resp: TtResp,
|
||||
access_type: AccessType,
|
||||
}
|
||||
|
||||
bitfield! {
|
||||
/// Test Target Response Payload
|
||||
///
|
||||
/// Provides the response payload from a TT, TTA, TTT or TTAT instruction.
|
||||
#[derive(PartialEq, Copy, Clone)]
|
||||
struct TtResp(u32);
|
||||
impl Debug;
|
||||
mregion, _: 7, 0;
|
||||
sregion, _: 15, 8;
|
||||
mrvalid, _: 16;
|
||||
srvalid, _: 17;
|
||||
r, _: 18;
|
||||
rw, _: 19;
|
||||
nsr, _: 20;
|
||||
nsrw, _: 21;
|
||||
s, _: 22;
|
||||
irvalid, _: 23;
|
||||
iregion, _: 31, 24;
|
||||
}
|
||||
|
||||
impl TestTarget {
|
||||
/// Creates a Test Target Response Payload by testing addr using access_type.
|
||||
#[inline]
|
||||
pub fn check(addr: *mut u32, access_type: AccessType) -> Self {
|
||||
let tt_resp = match access_type {
|
||||
AccessType::Current => TtResp(tt(addr)),
|
||||
AccessType::Unprivileged => TtResp(ttt(addr)),
|
||||
AccessType::NonSecure => TtResp(tta(addr)),
|
||||
AccessType::NonSecureUnprivileged => TtResp(ttat(addr)),
|
||||
};
|
||||
|
||||
TestTarget {
|
||||
tt_resp,
|
||||
access_type,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a Test Target Response Payload by testing the zone from addr to addr + size - 1
|
||||
/// using access_type.
|
||||
/// Returns None if:
|
||||
/// * the address zone overlaps SAU, IDAU or MPU region boundaries
|
||||
/// * size is 0
|
||||
/// * addr + size - 1 overflows
|
||||
#[inline]
|
||||
pub fn check_range(addr: *mut u32, size: usize, access_type: AccessType) -> Option<Self> {
|
||||
let begin: usize = addr as usize;
|
||||
// Last address of the range (addr + size - 1). This also checks if size is 0.
|
||||
let end: usize = begin.checked_add(size.checked_sub(1)?)?;
|
||||
|
||||
// Regions are aligned at 32-byte boundaries. If the address range fits in one 32-byte
|
||||
// address line, a single TT instruction suffices. This is the case when the following
|
||||
// constraint holds.
|
||||
let single_check: bool = (begin % 32).checked_add(size)? <= 32usize;
|
||||
|
||||
let test_start = TestTarget::check(addr, access_type);
|
||||
|
||||
if single_check {
|
||||
Some(test_start)
|
||||
} else {
|
||||
let test_end = TestTarget::check(end as *mut u32, access_type);
|
||||
// Check that the range does not cross SAU, IDAU or MPU region boundaries.
|
||||
if test_start != test_end {
|
||||
None
|
||||
} else {
|
||||
Some(test_start)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Access type that was used for this test target.
|
||||
#[inline]
|
||||
pub fn access_type(self) -> AccessType {
|
||||
self.access_type
|
||||
}
|
||||
|
||||
/// Get the raw u32 value returned by the TT instruction used.
|
||||
#[inline]
|
||||
pub fn as_u32(self) -> u32 {
|
||||
self.tt_resp.0
|
||||
}
|
||||
|
||||
/// Read accessibility of the target address. Only returns the MPU settings without checking
|
||||
/// the Security state of the target.
|
||||
/// For Unprivileged and NonSecureUnprivileged access types, returns the permissions for
|
||||
/// unprivileged access, regardless of whether the current mode is privileged or unprivileged.
|
||||
/// Returns false if the TT instruction was executed from an unprivileged mode
|
||||
/// and the NonSecure access type was not specified.
|
||||
/// Returns false if the address matches multiple MPU regions.
|
||||
#[inline]
|
||||
pub fn readable(self) -> bool {
|
||||
self.tt_resp.r()
|
||||
}
|
||||
|
||||
/// Read and write accessibility of the target address. Only returns the MPU settings without
|
||||
/// checking the Security state of the target.
|
||||
/// For Unprivileged and NonSecureUnprivileged access types, returns the permissions for
|
||||
/// unprivileged access, regardless of whether the current mode is privileged or unprivileged.
|
||||
/// Returns false if the TT instruction was executed from an unprivileged mode
|
||||
/// and the NonSecure access type was not specified.
|
||||
/// Returns false if the address matches multiple MPU regions.
|
||||
#[inline]
|
||||
pub fn read_and_writable(self) -> bool {
|
||||
self.tt_resp.rw()
|
||||
}
|
||||
|
||||
/// Indicate the MPU region number containing the target address.
|
||||
/// Returns None if the value is not valid:
|
||||
/// * the MPU is not implemented or MPU_CTRL.ENABLE is set to zero
|
||||
/// * the register argument specified by the MREGION field does not match any enabled MPU regions
|
||||
/// * the address matched multiple MPU regions
|
||||
/// * the address specified by the SREGION field is exempt from the secure memory attribution
|
||||
/// * the TT instruction was executed from an unprivileged mode and the A flag was not specified.
|
||||
#[inline]
|
||||
pub fn mpu_region(self) -> Option<u8> {
|
||||
if self.tt_resp.srvalid() {
|
||||
// Cast is safe as SREGION field is defined on 8 bits.
|
||||
Some(self.tt_resp.sregion() as u8)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Indicates the Security attribute of the target address. Independent of AccessType.
|
||||
/// Always zero when the test target is done in the Non-Secure state.
|
||||
#[inline]
|
||||
pub fn secure(self) -> bool {
|
||||
self.tt_resp.s()
|
||||
}
|
||||
|
||||
/// Non-Secure Read accessibility of the target address.
|
||||
/// Same as readable() && !secure()
|
||||
#[inline]
|
||||
pub fn ns_readable(self) -> bool {
|
||||
self.tt_resp.nsr()
|
||||
}
|
||||
|
||||
/// Non-Secure Read and Write accessibility of the target address.
|
||||
/// Same as read_and_writable() && !secure()
|
||||
#[inline]
|
||||
pub fn ns_read_and_writable(self) -> bool {
|
||||
self.tt_resp.nsrw()
|
||||
}
|
||||
|
||||
/// Indicate the IDAU region number containing the target address. Independent of AccessType.
|
||||
/// Returns None if the value is not valid:
|
||||
/// * the IDAU cannot provide a region number
|
||||
/// * the address is exempt from security attribution
|
||||
/// * the test target is done from Non-Secure state
|
||||
#[inline]
|
||||
pub fn idau_region(self) -> Option<u8> {
|
||||
if self.tt_resp.irvalid() {
|
||||
// Cast is safe as IREGION field is defined on 8 bits.
|
||||
Some(self.tt_resp.iregion() as u8)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Indicate the SAU region number containing the target address. Independent of AccessType.
|
||||
/// Returns None if the value is not valid:
|
||||
/// * SAU_CTRL.ENABLE is set to zero
|
||||
/// * the register argument specified in the SREGION field does not match any enabled SAU regions
|
||||
/// * the address specified matches multiple enabled SAU regions
|
||||
/// * the address specified by the SREGION field is exempt from the secure memory attribution
|
||||
/// * the TT instruction was executed from the Non-secure state or the Security Extension is not
|
||||
/// implemented
|
||||
#[inline]
|
||||
pub fn sau_region(self) -> Option<u8> {
|
||||
if self.tt_resp.srvalid() {
|
||||
// Cast is safe as SREGION field is defined on 8 bits.
|
||||
Some(self.tt_resp.sregion() as u8)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
#[cfg(all(cortex_m, feature = "critical-section-single-core"))]
|
||||
mod single_core_critical_section {
|
||||
use critical_section::{set_impl, Impl, RawRestoreState};
|
||||
|
||||
use crate::interrupt;
|
||||
use crate::register::primask;
|
||||
|
||||
struct SingleCoreCriticalSection;
|
||||
set_impl!(SingleCoreCriticalSection);
|
||||
|
||||
unsafe impl Impl for SingleCoreCriticalSection {
|
||||
unsafe fn acquire() -> RawRestoreState {
|
||||
let was_active = primask::read().is_active();
|
||||
interrupt::disable();
|
||||
was_active
|
||||
}
|
||||
|
||||
unsafe fn release(was_active: RawRestoreState) {
|
||||
// Only re-enable interrupts if they were enabled before the critical section.
|
||||
if was_active {
|
||||
interrupt::enable()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+136
@@ -0,0 +1,136 @@
|
||||
//! A delay driver based on SysTick.
|
||||
|
||||
use crate::peripheral::{syst::SystClkSource, SYST};
|
||||
use embedded_hal::blocking::delay::{DelayMs, DelayUs};
|
||||
|
||||
/// System timer (SysTick) as a delay provider.
|
||||
pub struct Delay {
|
||||
syst: SYST,
|
||||
frequency: u32,
|
||||
}
|
||||
|
||||
impl Delay {
|
||||
/// Configures the system timer (SysTick) as a delay provider.
|
||||
///
|
||||
/// `ahb_frequency` is a frequency of the AHB bus in Hz.
|
||||
#[inline]
|
||||
pub fn new(syst: SYST, ahb_frequency: u32) -> Self {
|
||||
Self::with_source(syst, ahb_frequency, SystClkSource::Core)
|
||||
}
|
||||
|
||||
/// Configures the system timer (SysTick) as a delay provider
|
||||
/// with a clock source.
|
||||
///
|
||||
/// `frequency` is the frequency of your `clock_source` in Hz.
|
||||
#[inline]
|
||||
pub fn with_source(mut syst: SYST, frequency: u32, clock_source: SystClkSource) -> Self {
|
||||
syst.set_clock_source(clock_source);
|
||||
|
||||
Delay { syst, frequency }
|
||||
}
|
||||
|
||||
/// Releases the system timer (SysTick) resource.
|
||||
#[inline]
|
||||
pub fn free(self) -> SYST {
|
||||
self.syst
|
||||
}
|
||||
|
||||
/// Delay using the Cortex-M systick for a certain duration, in µs.
|
||||
#[allow(clippy::missing_inline_in_public_items)]
|
||||
pub fn delay_us(&mut self, us: u32) {
|
||||
let ticks = (u64::from(us)) * (u64::from(self.frequency)) / 1_000_000;
|
||||
|
||||
let full_cycles = ticks >> 24;
|
||||
if full_cycles > 0 {
|
||||
self.syst.set_reload(0xffffff);
|
||||
self.syst.clear_current();
|
||||
self.syst.enable_counter();
|
||||
|
||||
for _ in 0..full_cycles {
|
||||
while !self.syst.has_wrapped() {}
|
||||
}
|
||||
}
|
||||
|
||||
let ticks = (ticks & 0xffffff) as u32;
|
||||
if ticks > 1 {
|
||||
self.syst.set_reload(ticks - 1);
|
||||
self.syst.clear_current();
|
||||
self.syst.enable_counter();
|
||||
|
||||
while !self.syst.has_wrapped() {}
|
||||
}
|
||||
|
||||
self.syst.disable_counter();
|
||||
}
|
||||
|
||||
/// Delay using the Cortex-M systick for a certain duration, in ms.
|
||||
#[inline]
|
||||
pub fn delay_ms(&mut self, mut ms: u32) {
|
||||
// 4294967 is the highest u32 value which you can multiply by 1000 without overflow
|
||||
while ms > 4294967 {
|
||||
self.delay_us(4294967000u32);
|
||||
ms -= 4294967;
|
||||
}
|
||||
self.delay_us(ms * 1_000);
|
||||
}
|
||||
}
|
||||
|
||||
impl DelayMs<u32> for Delay {
|
||||
#[inline]
|
||||
fn delay_ms(&mut self, ms: u32) {
|
||||
Delay::delay_ms(self, ms);
|
||||
}
|
||||
}
|
||||
|
||||
// This is a workaround to allow `delay_ms(42)` construction without specifying a type.
|
||||
impl DelayMs<i32> for Delay {
|
||||
#[inline(always)]
|
||||
fn delay_ms(&mut self, ms: i32) {
|
||||
assert!(ms >= 0);
|
||||
Delay::delay_ms(self, ms as u32);
|
||||
}
|
||||
}
|
||||
|
||||
impl DelayMs<u16> for Delay {
|
||||
#[inline(always)]
|
||||
fn delay_ms(&mut self, ms: u16) {
|
||||
Delay::delay_ms(self, u32::from(ms));
|
||||
}
|
||||
}
|
||||
|
||||
impl DelayMs<u8> for Delay {
|
||||
#[inline(always)]
|
||||
fn delay_ms(&mut self, ms: u8) {
|
||||
Delay::delay_ms(self, u32::from(ms));
|
||||
}
|
||||
}
|
||||
|
||||
impl DelayUs<u32> for Delay {
|
||||
#[inline]
|
||||
fn delay_us(&mut self, us: u32) {
|
||||
Delay::delay_us(self, us);
|
||||
}
|
||||
}
|
||||
|
||||
// This is a workaround to allow `delay_us(42)` construction without specifying a type.
|
||||
impl DelayUs<i32> for Delay {
|
||||
#[inline(always)]
|
||||
fn delay_us(&mut self, us: i32) {
|
||||
assert!(us >= 0);
|
||||
Delay::delay_us(self, us as u32);
|
||||
}
|
||||
}
|
||||
|
||||
impl DelayUs<u16> for Delay {
|
||||
#[inline(always)]
|
||||
fn delay_us(&mut self, us: u16) {
|
||||
Delay::delay_us(self, u32::from(us))
|
||||
}
|
||||
}
|
||||
|
||||
impl DelayUs<u8> for Delay {
|
||||
#[inline(always)]
|
||||
fn delay_us(&mut self, us: u8) {
|
||||
Delay::delay_us(self, u32::from(us))
|
||||
}
|
||||
}
|
||||
Vendored
+73
@@ -0,0 +1,73 @@
|
||||
//! Interrupts
|
||||
|
||||
pub use bare_metal::{CriticalSection, Mutex, Nr};
|
||||
|
||||
/// Trait for enums of external interrupt numbers.
|
||||
///
|
||||
/// This trait should be implemented by a peripheral access crate (PAC)
|
||||
/// on its enum of available external interrupts for a specific device.
|
||||
/// Each variant must convert to a u16 of its interrupt number,
|
||||
/// which is its exception number - 16.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This trait must only be implemented on enums of device interrupts. Each
|
||||
/// enum variant must represent a distinct value (no duplicates are permitted),
|
||||
/// and must always return the same value (do not change at runtime).
|
||||
///
|
||||
/// These requirements ensure safe nesting of critical sections.
|
||||
pub unsafe trait InterruptNumber: Copy {
|
||||
/// Return the interrupt number associated with this variant.
|
||||
///
|
||||
/// See trait documentation for safety requirements.
|
||||
fn number(self) -> u16;
|
||||
}
|
||||
|
||||
/// Implement InterruptNumber for the old bare_metal::Nr trait.
|
||||
/// This implementation is for backwards compatibility only and will be removed in cortex-m 0.8.
|
||||
unsafe impl<T: Nr + Copy> InterruptNumber for T {
|
||||
#[inline]
|
||||
fn number(self) -> u16 {
|
||||
self.nr() as u16
|
||||
}
|
||||
}
|
||||
|
||||
/// Disables all interrupts
|
||||
#[inline]
|
||||
pub fn disable() {
|
||||
call_asm!(__cpsid());
|
||||
}
|
||||
|
||||
/// Enables all the interrupts
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - Do not call this function inside an `interrupt::free` critical section
|
||||
#[inline]
|
||||
pub unsafe fn enable() {
|
||||
call_asm!(__cpsie());
|
||||
}
|
||||
|
||||
/// Execute closure `f` in an interrupt-free context.
|
||||
///
|
||||
/// This as also known as a "critical section".
|
||||
#[inline]
|
||||
pub fn free<F, R>(f: F) -> R
|
||||
where
|
||||
F: FnOnce(&CriticalSection) -> R,
|
||||
{
|
||||
let primask = crate::register::primask::read();
|
||||
|
||||
// disable interrupts
|
||||
disable();
|
||||
|
||||
let r = f(unsafe { &CriticalSection::new() });
|
||||
|
||||
// If the interrupts were active before our `disable` call, then re-enable
|
||||
// them. Otherwise, keep them disabled
|
||||
if primask.is_active() {
|
||||
unsafe { enable() }
|
||||
}
|
||||
|
||||
r
|
||||
}
|
||||
Vendored
+158
@@ -0,0 +1,158 @@
|
||||
//! Instrumentation Trace Macrocell
|
||||
//!
|
||||
//! **NOTE** This module is only available on ARMv7-M and newer.
|
||||
|
||||
use core::{fmt, ptr, slice};
|
||||
|
||||
use crate::peripheral::itm::Stim;
|
||||
|
||||
// NOTE assumes that `bytes` is 32-bit aligned
|
||||
unsafe fn write_words(stim: &mut Stim, bytes: &[u32]) {
|
||||
let mut p = bytes.as_ptr();
|
||||
for _ in 0..bytes.len() {
|
||||
while !stim.is_fifo_ready() {}
|
||||
stim.write_u32(ptr::read(p));
|
||||
p = p.offset(1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes an aligned byte slice to the ITM.
|
||||
///
|
||||
/// `buffer` must be 4-byte aligned.
|
||||
unsafe fn write_aligned_impl(port: &mut Stim, buffer: &[u8]) {
|
||||
let len = buffer.len();
|
||||
|
||||
if len == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let split = len & !0b11;
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
write_words(
|
||||
port,
|
||||
slice::from_raw_parts(buffer.as_ptr() as *const u32, split >> 2),
|
||||
);
|
||||
|
||||
// 3 bytes or less left
|
||||
let mut left = len & 0b11;
|
||||
let mut ptr = buffer.as_ptr().add(split);
|
||||
|
||||
// at least 2 bytes left
|
||||
if left > 1 {
|
||||
while !port.is_fifo_ready() {}
|
||||
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
port.write_u16(ptr::read(ptr as *const u16));
|
||||
|
||||
ptr = ptr.offset(2);
|
||||
left -= 2;
|
||||
}
|
||||
|
||||
// final byte
|
||||
if left == 1 {
|
||||
while !port.is_fifo_ready() {}
|
||||
port.write_u8(*ptr);
|
||||
}
|
||||
}
|
||||
|
||||
struct Port<'p>(&'p mut Stim);
|
||||
|
||||
impl<'p> fmt::Write for Port<'p> {
|
||||
#[inline]
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
write_all(self.0, s.as_bytes());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper type that aligns its contents on a 4-Byte boundary.
|
||||
///
|
||||
/// ITM transfers are most efficient when the data is 4-Byte-aligned. This type provides an easy
|
||||
/// way to accomplish and enforce such an alignment.
|
||||
#[repr(align(4))]
|
||||
pub struct Aligned<T: ?Sized>(pub T);
|
||||
|
||||
/// Writes `buffer` to an ITM port.
|
||||
#[allow(clippy::missing_inline_in_public_items)]
|
||||
pub fn write_all(port: &mut Stim, buffer: &[u8]) {
|
||||
unsafe {
|
||||
let mut len = buffer.len();
|
||||
let mut ptr = buffer.as_ptr();
|
||||
|
||||
if len == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
// 0x01 OR 0x03
|
||||
if ptr as usize % 2 == 1 {
|
||||
while !port.is_fifo_ready() {}
|
||||
port.write_u8(*ptr);
|
||||
|
||||
// 0x02 OR 0x04
|
||||
ptr = ptr.offset(1);
|
||||
len -= 1;
|
||||
}
|
||||
|
||||
// 0x02
|
||||
if ptr as usize % 4 == 2 {
|
||||
if len > 1 {
|
||||
// at least 2 bytes
|
||||
while !port.is_fifo_ready() {}
|
||||
|
||||
// We checked the alignment above, so this is safe
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
port.write_u16(ptr::read(ptr as *const u16));
|
||||
|
||||
// 0x04
|
||||
ptr = ptr.offset(2);
|
||||
len -= 2;
|
||||
} else {
|
||||
if len == 1 {
|
||||
// last byte
|
||||
while !port.is_fifo_ready() {}
|
||||
port.write_u8(*ptr);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// The remaining data is 4-byte aligned, but might not be a multiple of 4 bytes
|
||||
write_aligned_impl(port, slice::from_raw_parts(ptr, len));
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes a 4-byte aligned `buffer` to an ITM port.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use cortex_m::{itm::{self, Aligned}, peripheral::ITM};
|
||||
/// # let port = unsafe { &mut (*ITM::PTR).stim[0] };
|
||||
/// let mut buffer = Aligned([0; 14]);
|
||||
///
|
||||
/// buffer.0.copy_from_slice(b"Hello, world!\n");
|
||||
///
|
||||
/// itm::write_aligned(port, &buffer);
|
||||
///
|
||||
/// // Or equivalently
|
||||
/// itm::write_aligned(port, &Aligned(*b"Hello, world!\n"));
|
||||
/// ```
|
||||
#[allow(clippy::missing_inline_in_public_items)]
|
||||
pub fn write_aligned(port: &mut Stim, buffer: &Aligned<[u8]>) {
|
||||
unsafe { write_aligned_impl(port, &buffer.0) }
|
||||
}
|
||||
|
||||
/// Writes `fmt::Arguments` to the ITM `port`
|
||||
#[inline]
|
||||
pub fn write_fmt(port: &mut Stim, args: fmt::Arguments) {
|
||||
use core::fmt::Write;
|
||||
|
||||
Port(port).write_fmt(args).ok();
|
||||
}
|
||||
|
||||
/// Writes a string to the ITM `port`
|
||||
#[inline]
|
||||
pub fn write_str(port: &mut Stim, string: &str) {
|
||||
write_all(port, string.as_bytes())
|
||||
}
|
||||
Vendored
+112
@@ -0,0 +1,112 @@
|
||||
//! Low level access to Cortex-M processors
|
||||
//!
|
||||
//! This crate provides:
|
||||
//!
|
||||
//! - Access to core peripherals like NVIC, SCB and SysTick.
|
||||
//! - Access to core registers like CONTROL, MSP and PSR.
|
||||
//! - Interrupt manipulation mechanisms
|
||||
//! - Safe wrappers around Cortex-M specific instructions like `bkpt`
|
||||
//!
|
||||
//! # Optional features
|
||||
//!
|
||||
//! ## `inline-asm`
|
||||
//!
|
||||
//! When this feature is enabled the implementation of all the functions inside the `asm` and
|
||||
//! `register` modules use inline assembly (`asm!`) instead of external assembly (FFI into separate
|
||||
//! assembly files pre-compiled using `arm-none-eabi-gcc`). The advantages of enabling `inline-asm`
|
||||
//! are:
|
||||
//!
|
||||
//! - Reduced overhead. FFI eliminates the possibility of inlining so all operations include a
|
||||
//! function call overhead when `inline-asm` is not enabled.
|
||||
//!
|
||||
//! - Some of the `register` API only becomes available only when `inline-asm` is enabled. Check the
|
||||
//! API docs for details.
|
||||
//!
|
||||
//! The disadvantage is that `inline-asm` requires a Rust version at least 1.59 to use the `asm!()`
|
||||
//! macro. In the future 0.8 and above versions of `cortex-m`, this feature will always be enabled.
|
||||
//!
|
||||
//! ## `critical-section-single-core`
|
||||
//!
|
||||
//! This feature enables a [`critical-section`](https://github.com/rust-embedded/critical-section)
|
||||
//! implementation suitable for single-core targets, based on disabling interrupts globally.
|
||||
//!
|
||||
//! It is **unsound** to enable it on multi-core targets or for code running in unprivileged mode,
|
||||
//! and may cause functional problems in systems where some interrupts must be not be disabled
|
||||
//! or critical sections are managed as part of an RTOS. In these cases, you should use
|
||||
//! a target-specific implementation instead, typically provided by a HAL or RTOS crate.
|
||||
//!
|
||||
//! ## `cm7-r0p1`
|
||||
//!
|
||||
//! This feature enables workarounds for errata found on Cortex-M7 chips with revision r0p1. Some
|
||||
//! functions in this crate only work correctly on those chips if this Cargo feature is enabled
|
||||
//! (the functions are documented accordingly).
|
||||
//!
|
||||
//! ## `linker-plugin-lto`
|
||||
//!
|
||||
//! This feature links against prebuilt assembly blobs that are compatible with [Linker-Plugin LTO].
|
||||
//! This allows inlining assembly routines into the caller, even without the `inline-asm` feature,
|
||||
//! and works on stable Rust (but note the drawbacks below!).
|
||||
//!
|
||||
//! If you want to use this feature, you need to be aware of a few things:
|
||||
//!
|
||||
//! - You need to make sure that `-Clinker-plugin-lto` is passed to rustc. Please refer to the
|
||||
//! [Linker-Plugin LTO] documentation for details.
|
||||
//!
|
||||
//! - You have to use a Rust version whose LLVM version is compatible with the toolchain in
|
||||
//! `asm-toolchain`.
|
||||
//!
|
||||
//! - Due to a [Rust bug][rust-lang/rust#75940] in compiler versions **before 1.49**, this option
|
||||
//! does not work with optimization levels `s` and `z`.
|
||||
//!
|
||||
//! [Linker-Plugin LTO]: https://doc.rust-lang.org/stable/rustc/linker-plugin-lto.html
|
||||
//! [rust-lang/rust#75940]: https://github.com/rust-lang/rust/issues/75940
|
||||
//!
|
||||
//! # Minimum Supported Rust Version (MSRV)
|
||||
//!
|
||||
//! This crate is guaranteed to compile on stable Rust 1.38 and up. It *might*
|
||||
//! compile with older versions but that may change in any new patch release.
|
||||
|
||||
#![deny(missing_docs)]
|
||||
#![no_std]
|
||||
#![allow(clippy::identity_op)]
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
// Prevent clippy from complaining about empty match expression that are used for cfg gating.
|
||||
#![allow(clippy::match_single_binding)]
|
||||
// This makes clippy warn about public functions which are not #[inline].
|
||||
//
|
||||
// Almost all functions in this crate result in trivial or even no assembly.
|
||||
// These functions should be #[inline].
|
||||
//
|
||||
// If you do add a function that's not supposed to be #[inline], you can add
|
||||
// #[allow(clippy::missing_inline_in_public_items)] in front of it to add an
|
||||
// exception to clippy's rules.
|
||||
//
|
||||
// This should be done in case of:
|
||||
// - A function containing non-trivial logic (such as itm::write_all); or
|
||||
// - A generated #[derive(Debug)] function (in which case the attribute needs
|
||||
// to be applied to the struct).
|
||||
#![deny(clippy::missing_inline_in_public_items)]
|
||||
// Don't warn about feature(asm) being stable on Rust >= 1.59.0
|
||||
#![allow(stable_features)]
|
||||
|
||||
extern crate bare_metal;
|
||||
extern crate volatile_register;
|
||||
|
||||
#[macro_use]
|
||||
mod call_asm;
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
pub mod asm;
|
||||
#[cfg(armv8m)]
|
||||
pub mod cmse;
|
||||
mod critical_section;
|
||||
pub mod delay;
|
||||
pub mod interrupt;
|
||||
#[cfg(all(not(armv6m), not(armv8m_base)))]
|
||||
pub mod itm;
|
||||
pub mod peripheral;
|
||||
pub mod prelude;
|
||||
pub mod register;
|
||||
|
||||
pub use crate::peripheral::Peripherals;
|
||||
Vendored
+114
@@ -0,0 +1,114 @@
|
||||
/// Macro for sending a formatted string through an ITM channel
|
||||
#[macro_export]
|
||||
macro_rules! iprint {
|
||||
($channel:expr, $s:expr) => {
|
||||
$crate::itm::write_str($channel, $s);
|
||||
};
|
||||
($channel:expr, $($arg:tt)*) => {
|
||||
$crate::itm::write_fmt($channel, format_args!($($arg)*));
|
||||
};
|
||||
}
|
||||
|
||||
/// Macro for sending a formatted string through an ITM channel, with a newline.
|
||||
#[macro_export]
|
||||
macro_rules! iprintln {
|
||||
($channel:expr) => {
|
||||
$crate::itm::write_str($channel, "\n");
|
||||
};
|
||||
($channel:expr, $fmt:expr) => {
|
||||
$crate::itm::write_str($channel, concat!($fmt, "\n"));
|
||||
};
|
||||
($channel:expr, $fmt:expr, $($arg:tt)*) => {
|
||||
$crate::itm::write_fmt($channel, format_args!(concat!($fmt, "\n"), $($arg)*));
|
||||
};
|
||||
}
|
||||
|
||||
/// Macro to create a mutable reference to a statically allocated value
|
||||
///
|
||||
/// This macro returns a value with type `Option<&'static mut $ty>`. `Some($expr)` will be returned
|
||||
/// the first time the macro is executed; further calls will return `None`. To avoid `unwrap`ping a
|
||||
/// `None` variant the caller must ensure that the macro is called from a function that's executed
|
||||
/// at most once in the whole lifetime of the program.
|
||||
///
|
||||
/// # Notes
|
||||
/// This macro is unsound on multi core systems.
|
||||
///
|
||||
/// For debuggability, you can set an explicit name for a singleton. This name only shows up the
|
||||
/// the debugger and is not referencable from other code. See example below.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ``` no_run
|
||||
/// use cortex_m::singleton;
|
||||
///
|
||||
/// fn main() {
|
||||
/// // OK if `main` is executed only once
|
||||
/// let x: &'static mut bool = singleton!(: bool = false).unwrap();
|
||||
///
|
||||
/// let y = alias();
|
||||
/// // BAD this second call to `alias` will definitively `panic!`
|
||||
/// let y_alias = alias();
|
||||
/// }
|
||||
///
|
||||
/// fn alias() -> &'static mut bool {
|
||||
/// singleton!(: bool = false).unwrap()
|
||||
/// }
|
||||
///
|
||||
/// fn singleton_with_name() {
|
||||
/// // A name only for debugging purposes
|
||||
/// singleton!(FOO_BUFFER: [u8; 1024] = [0u8; 1024]);
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! singleton {
|
||||
($name:ident: $ty:ty = $expr:expr) => {
|
||||
$crate::interrupt::free(|_| {
|
||||
// this is a tuple of a MaybeUninit and a bool because using an Option here is
|
||||
// problematic: Due to niche-optimization, an Option could end up producing a non-zero
|
||||
// initializer value which would move the entire static from `.bss` into `.data`...
|
||||
static mut $name: (::core::mem::MaybeUninit<$ty>, bool) =
|
||||
(::core::mem::MaybeUninit::uninit(), false);
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
let used = unsafe { $name.1 };
|
||||
if used {
|
||||
None
|
||||
} else {
|
||||
let expr = $expr;
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe {
|
||||
$name.1 = true;
|
||||
$name.0 = ::core::mem::MaybeUninit::new(expr);
|
||||
Some(&mut *$name.0.as_mut_ptr())
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
(: $ty:ty = $expr:expr) => {
|
||||
$crate::singleton!(VAR: $ty = $expr)
|
||||
};
|
||||
}
|
||||
|
||||
/// ``` compile_fail
|
||||
/// use cortex_m::singleton;
|
||||
///
|
||||
/// fn foo() {
|
||||
/// // check that the call to `uninitialized` requires unsafe
|
||||
/// singleton!(: u8 = std::mem::uninitialized());
|
||||
/// }
|
||||
/// ```
|
||||
#[allow(dead_code)]
|
||||
const CFAIL: () = ();
|
||||
|
||||
/// ```
|
||||
/// #![deny(unsafe_code)]
|
||||
/// use cortex_m::singleton;
|
||||
///
|
||||
/// fn foo() {
|
||||
/// // check that calls to `singleton!` don't trip the `unsafe_code` lint
|
||||
/// singleton!(: u8 = 0);
|
||||
/// }
|
||||
/// ```
|
||||
#[allow(dead_code)]
|
||||
const CPASS: () = ();
|
||||
Vendored
+93
@@ -0,0 +1,93 @@
|
||||
//! Cortex-M7 TCM and Cache access control.
|
||||
|
||||
use volatile_register::RW;
|
||||
|
||||
/// Register block
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
/// Instruction Tightly-Coupled Memory Control Register
|
||||
pub itcmcr: RW<u32>,
|
||||
/// Data Tightly-Coupled Memory Control Register
|
||||
pub dtcmcr: RW<u32>,
|
||||
/// AHBP Control Register
|
||||
pub ahbpcr: RW<u32>,
|
||||
/// L1 Cache Control Register
|
||||
pub cacr: RW<u32>,
|
||||
/// AHB Slave Control Register
|
||||
pub ahbscr: RW<u32>,
|
||||
reserved0: u32,
|
||||
/// Auxilary Bus Fault Status Register
|
||||
pub abfsr: RW<u32>,
|
||||
}
|
||||
|
||||
/// ITCMCR and DTCMCR TCM enable bit.
|
||||
pub const TCM_EN: u32 = 1;
|
||||
|
||||
/// ITCMCR and DTCMCR TCM read-modify-write bit.
|
||||
pub const TCM_RMW: u32 = 2;
|
||||
|
||||
/// ITCMCR and DTCMCR TCM rety phase enable bit.
|
||||
pub const TCM_RETEN: u32 = 4;
|
||||
|
||||
/// ITCMCR and DTCMCR TCM size mask.
|
||||
pub const TCM_SZ_MASK: u32 = 0x78;
|
||||
|
||||
/// ITCMCR and DTCMCR TCM shift.
|
||||
pub const TCM_SZ_SHIFT: usize = 3;
|
||||
|
||||
/// AHBPCR AHBP enable bit.
|
||||
pub const AHBPCR_EN: u32 = 1;
|
||||
|
||||
/// AHBPCR AHBP size mask.
|
||||
pub const AHBPCR_SZ_MASK: u32 = 0x0e;
|
||||
|
||||
/// AHBPCR AHBP size shit.
|
||||
pub const AHBPCR_SZ_SHIFT: usize = 1;
|
||||
|
||||
/// CACR Shared cachedable-is-WT for data cache.
|
||||
pub const CACR_SIWT: u32 = 1;
|
||||
|
||||
/// CACR ECC in the instruction and data cache (disable).
|
||||
pub const CACR_ECCDIS: u32 = 2;
|
||||
|
||||
/// CACR Force Write-Through in the data cache.
|
||||
pub const CACR_FORCEWT: u32 = 4;
|
||||
|
||||
/// AHBSCR AHBS prioritization control mask.
|
||||
pub const AHBSCR_CTL_MASK: u32 = 0x03;
|
||||
|
||||
/// AHBSCR AHBS prioritization control shift.
|
||||
pub const AHBSCR_CTL_SHIFT: usize = 0;
|
||||
|
||||
/// AHBSCR Threshold execution prioity for AHBS traffic demotion, mask.
|
||||
pub const AHBSCR_TPRI_MASK: u32 = 0x7fc;
|
||||
|
||||
/// AHBSCR Threshold execution prioity for AHBS traffic demotion, shift.
|
||||
pub const AHBSCR_TPRI_SHIFT: usize = 2;
|
||||
|
||||
/// AHBSCR Failness counter initialization value, mask.
|
||||
pub const AHBSCR_INITCOUNT_MASK: u32 = 0xf800;
|
||||
|
||||
/// AHBSCR Failness counter initialization value, shift.
|
||||
pub const AHBSCR_INITCOUNT_SHIFT: usize = 11;
|
||||
|
||||
/// ABFSR Async fault on ITCM interface.
|
||||
pub const ABFSR_ITCM: u32 = 1;
|
||||
|
||||
/// ABFSR Async fault on DTCM interface.
|
||||
pub const ABFSR_DTCM: u32 = 2;
|
||||
|
||||
/// ABFSR Async fault on AHBP interface.
|
||||
pub const ABFSR_AHBP: u32 = 4;
|
||||
|
||||
/// ABFSR Async fault on AXIM interface.
|
||||
pub const ABFSR_AXIM: u32 = 8;
|
||||
|
||||
/// ABFSR Async fault on EPPB interface.
|
||||
pub const ABFSR_EPPB: u32 = 16;
|
||||
|
||||
/// ABFSR Indicates the type of fault on the AXIM interface, mask.
|
||||
pub const ABFSR_AXIMTYPE_MASK: u32 = 0x300;
|
||||
|
||||
/// ABFSR Indicates the type of fault on the AXIM interface, shift.
|
||||
pub const ABFSR_AXIMTYPE_SHIFT: usize = 8;
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
//! Cache and branch predictor maintenance operations
|
||||
//!
|
||||
//! *NOTE* Not available on Armv6-M.
|
||||
|
||||
use volatile_register::WO;
|
||||
|
||||
use crate::peripheral::CBP;
|
||||
|
||||
/// Register block
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
/// I-cache invalidate all to PoU
|
||||
pub iciallu: WO<u32>,
|
||||
reserved0: u32,
|
||||
/// I-cache invalidate by MVA to PoU
|
||||
pub icimvau: WO<u32>,
|
||||
/// D-cache invalidate by MVA to PoC
|
||||
pub dcimvac: WO<u32>,
|
||||
/// D-cache invalidate by set-way
|
||||
pub dcisw: WO<u32>,
|
||||
/// D-cache clean by MVA to PoU
|
||||
pub dccmvau: WO<u32>,
|
||||
/// D-cache clean by MVA to PoC
|
||||
pub dccmvac: WO<u32>,
|
||||
/// D-cache clean by set-way
|
||||
pub dccsw: WO<u32>,
|
||||
/// D-cache clean and invalidate by MVA to PoC
|
||||
pub dccimvac: WO<u32>,
|
||||
/// D-cache clean and invalidate by set-way
|
||||
pub dccisw: WO<u32>,
|
||||
/// Branch predictor invalidate all
|
||||
pub bpiall: WO<u32>,
|
||||
}
|
||||
|
||||
const CBP_SW_WAY_POS: u32 = 30;
|
||||
const CBP_SW_WAY_MASK: u32 = 0x3 << CBP_SW_WAY_POS;
|
||||
const CBP_SW_SET_POS: u32 = 5;
|
||||
const CBP_SW_SET_MASK: u32 = 0x1FF << CBP_SW_SET_POS;
|
||||
|
||||
impl CBP {
|
||||
/// I-cache invalidate all to PoU
|
||||
#[inline(always)]
|
||||
pub fn iciallu(&mut self) {
|
||||
unsafe { self.iciallu.write(0) };
|
||||
}
|
||||
|
||||
/// I-cache invalidate by MVA to PoU
|
||||
#[inline(always)]
|
||||
pub fn icimvau(&mut self, mva: u32) {
|
||||
unsafe { self.icimvau.write(mva) };
|
||||
}
|
||||
|
||||
/// D-cache invalidate by MVA to PoC
|
||||
#[inline(always)]
|
||||
pub unsafe fn dcimvac(&mut self, mva: u32) {
|
||||
self.dcimvac.write(mva);
|
||||
}
|
||||
|
||||
/// D-cache invalidate by set-way
|
||||
///
|
||||
/// `set` is masked to be between 0 and 3, and `way` between 0 and 511.
|
||||
#[inline(always)]
|
||||
pub unsafe fn dcisw(&mut self, set: u16, way: u16) {
|
||||
// The ARMv7-M Architecture Reference Manual, as of Revision E.b, says these set/way
|
||||
// operations have a register data format which depends on the implementation's
|
||||
// associativity and number of sets. Specifically the 'way' and 'set' fields have
|
||||
// offsets 32-log2(ASSOCIATIVITY) and log2(LINELEN) respectively.
|
||||
//
|
||||
// However, in Cortex-M7 devices, these offsets are fixed at 30 and 5, as per the Cortex-M7
|
||||
// Generic User Guide section 4.8.3. Since no other ARMv7-M implementations except the
|
||||
// Cortex-M7 have a DCACHE or ICACHE at all, it seems safe to do the same thing as the
|
||||
// CMSIS-Core implementation and use fixed values.
|
||||
self.dcisw.write(
|
||||
((u32::from(way) & (CBP_SW_WAY_MASK >> CBP_SW_WAY_POS)) << CBP_SW_WAY_POS)
|
||||
| ((u32::from(set) & (CBP_SW_SET_MASK >> CBP_SW_SET_POS)) << CBP_SW_SET_POS),
|
||||
);
|
||||
}
|
||||
|
||||
/// D-cache clean by MVA to PoU
|
||||
#[inline(always)]
|
||||
pub fn dccmvau(&mut self, mva: u32) {
|
||||
unsafe {
|
||||
self.dccmvau.write(mva);
|
||||
}
|
||||
}
|
||||
|
||||
/// D-cache clean by MVA to PoC
|
||||
#[inline(always)]
|
||||
pub fn dccmvac(&mut self, mva: u32) {
|
||||
unsafe {
|
||||
self.dccmvac.write(mva);
|
||||
}
|
||||
}
|
||||
|
||||
/// D-cache clean by set-way
|
||||
///
|
||||
/// `set` is masked to be between 0 and 3, and `way` between 0 and 511.
|
||||
#[inline(always)]
|
||||
pub fn dccsw(&mut self, set: u16, way: u16) {
|
||||
// See comment for dcisw() about the format here
|
||||
unsafe {
|
||||
self.dccsw.write(
|
||||
((u32::from(way) & (CBP_SW_WAY_MASK >> CBP_SW_WAY_POS)) << CBP_SW_WAY_POS)
|
||||
| ((u32::from(set) & (CBP_SW_SET_MASK >> CBP_SW_SET_POS)) << CBP_SW_SET_POS),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// D-cache clean and invalidate by MVA to PoC
|
||||
#[inline(always)]
|
||||
pub fn dccimvac(&mut self, mva: u32) {
|
||||
unsafe {
|
||||
self.dccimvac.write(mva);
|
||||
}
|
||||
}
|
||||
|
||||
/// D-cache clean and invalidate by set-way
|
||||
///
|
||||
/// `set` is masked to be between 0 and 3, and `way` between 0 and 511.
|
||||
#[inline(always)]
|
||||
pub fn dccisw(&mut self, set: u16, way: u16) {
|
||||
// See comment for dcisw() about the format here
|
||||
unsafe {
|
||||
self.dccisw.write(
|
||||
((u32::from(way) & (CBP_SW_WAY_MASK >> CBP_SW_WAY_POS)) << CBP_SW_WAY_POS)
|
||||
| ((u32::from(set) & (CBP_SW_SET_MASK >> CBP_SW_SET_POS)) << CBP_SW_SET_POS),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Branch predictor invalidate all
|
||||
#[inline(always)]
|
||||
pub fn bpiall(&mut self) {
|
||||
unsafe {
|
||||
self.bpiall.write(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
//! CPUID
|
||||
|
||||
use volatile_register::RO;
|
||||
#[cfg(not(armv6m))]
|
||||
use volatile_register::RW;
|
||||
|
||||
#[cfg(not(armv6m))]
|
||||
use crate::peripheral::CPUID;
|
||||
|
||||
/// Register block
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
/// CPUID base
|
||||
pub base: RO<u32>,
|
||||
|
||||
_reserved0: [u32; 15],
|
||||
|
||||
/// Processor Feature (not present on Cortex-M0 variants)
|
||||
#[cfg(not(armv6m))]
|
||||
pub pfr: [RO<u32>; 2],
|
||||
#[cfg(armv6m)]
|
||||
_reserved1: [u32; 2],
|
||||
|
||||
/// Debug Feature (not present on Cortex-M0 variants)
|
||||
#[cfg(not(armv6m))]
|
||||
pub dfr: RO<u32>,
|
||||
#[cfg(armv6m)]
|
||||
_reserved2: u32,
|
||||
|
||||
/// Auxiliary Feature (not present on Cortex-M0 variants)
|
||||
#[cfg(not(armv6m))]
|
||||
pub afr: RO<u32>,
|
||||
#[cfg(armv6m)]
|
||||
_reserved3: u32,
|
||||
|
||||
/// Memory Model Feature (not present on Cortex-M0 variants)
|
||||
#[cfg(not(armv6m))]
|
||||
pub mmfr: [RO<u32>; 4],
|
||||
#[cfg(armv6m)]
|
||||
_reserved4: [u32; 4],
|
||||
|
||||
/// Instruction Set Attribute (not present on Cortex-M0 variants)
|
||||
#[cfg(not(armv6m))]
|
||||
pub isar: [RO<u32>; 5],
|
||||
#[cfg(armv6m)]
|
||||
_reserved5: [u32; 5],
|
||||
|
||||
_reserved6: u32,
|
||||
|
||||
/// Cache Level ID (only present on Cortex-M7)
|
||||
#[cfg(not(armv6m))]
|
||||
pub clidr: RO<u32>,
|
||||
|
||||
/// Cache Type (only present on Cortex-M7)
|
||||
#[cfg(not(armv6m))]
|
||||
pub ctr: RO<u32>,
|
||||
|
||||
/// Cache Size ID (only present on Cortex-M7)
|
||||
#[cfg(not(armv6m))]
|
||||
pub ccsidr: RO<u32>,
|
||||
|
||||
/// Cache Size Selection (only present on Cortex-M7)
|
||||
#[cfg(not(armv6m))]
|
||||
pub csselr: RW<u32>,
|
||||
}
|
||||
|
||||
/// Type of cache to select on CSSELR writes.
|
||||
#[cfg(not(armv6m))]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum CsselrCacheType {
|
||||
/// Select DCache or unified cache
|
||||
DataOrUnified = 0,
|
||||
/// Select ICache
|
||||
Instruction = 1,
|
||||
}
|
||||
|
||||
#[cfg(not(armv6m))]
|
||||
impl CPUID {
|
||||
/// Selects the current CCSIDR
|
||||
///
|
||||
/// * `level`: the required cache level minus 1, e.g. 0 for L1, 1 for L2
|
||||
/// * `ind`: select instruction cache or data/unified cache
|
||||
///
|
||||
/// `level` is masked to be between 0 and 7.
|
||||
#[inline]
|
||||
pub fn select_cache(&mut self, level: u8, ind: CsselrCacheType) {
|
||||
const CSSELR_IND_POS: u32 = 0;
|
||||
const CSSELR_IND_MASK: u32 = 1 << CSSELR_IND_POS;
|
||||
const CSSELR_LEVEL_POS: u32 = 1;
|
||||
const CSSELR_LEVEL_MASK: u32 = 0x7 << CSSELR_LEVEL_POS;
|
||||
|
||||
unsafe {
|
||||
self.csselr.write(
|
||||
((u32::from(level) << CSSELR_LEVEL_POS) & CSSELR_LEVEL_MASK)
|
||||
| (((ind as u32) << CSSELR_IND_POS) & CSSELR_IND_MASK),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of sets and ways in the selected cache
|
||||
#[inline]
|
||||
pub fn cache_num_sets_ways(&mut self, level: u8, ind: CsselrCacheType) -> (u16, u16) {
|
||||
const CCSIDR_NUMSETS_POS: u32 = 13;
|
||||
const CCSIDR_NUMSETS_MASK: u32 = 0x7FFF << CCSIDR_NUMSETS_POS;
|
||||
const CCSIDR_ASSOCIATIVITY_POS: u32 = 3;
|
||||
const CCSIDR_ASSOCIATIVITY_MASK: u32 = 0x3FF << CCSIDR_ASSOCIATIVITY_POS;
|
||||
|
||||
self.select_cache(level, ind);
|
||||
crate::asm::dsb();
|
||||
let ccsidr = self.ccsidr.read();
|
||||
(
|
||||
(1 + ((ccsidr & CCSIDR_NUMSETS_MASK) >> CCSIDR_NUMSETS_POS)) as u16,
|
||||
(1 + ((ccsidr & CCSIDR_ASSOCIATIVITY_MASK) >> CCSIDR_ASSOCIATIVITY_POS)) as u16,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns log2 of the number of words in the smallest cache line of all the data cache and
|
||||
/// unified caches that are controlled by the processor.
|
||||
///
|
||||
/// This is the `DminLine` field of the CTR register.
|
||||
#[inline(always)]
|
||||
pub fn cache_dminline() -> u32 {
|
||||
const CTR_DMINLINE_POS: u32 = 16;
|
||||
const CTR_DMINLINE_MASK: u32 = 0xF << CTR_DMINLINE_POS;
|
||||
let ctr = unsafe { (*Self::PTR).ctr.read() };
|
||||
(ctr & CTR_DMINLINE_MASK) >> CTR_DMINLINE_POS
|
||||
}
|
||||
|
||||
/// Returns log2 of the number of words in the smallest cache line of all the instruction
|
||||
/// caches that are controlled by the processor.
|
||||
///
|
||||
/// This is the `IminLine` field of the CTR register.
|
||||
#[inline(always)]
|
||||
pub fn cache_iminline() -> u32 {
|
||||
const CTR_IMINLINE_POS: u32 = 0;
|
||||
const CTR_IMINLINE_MASK: u32 = 0xF << CTR_IMINLINE_POS;
|
||||
let ctr = unsafe { (*Self::PTR).ctr.read() };
|
||||
(ctr & CTR_IMINLINE_MASK) >> CTR_IMINLINE_POS
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
//! Debug Control Block
|
||||
|
||||
use volatile_register::{RW, WO};
|
||||
|
||||
use crate::peripheral::DCB;
|
||||
use core::ptr;
|
||||
|
||||
const DCB_DEMCR_TRCENA: u32 = 1 << 24;
|
||||
|
||||
/// Register block
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
/// Debug Halting Control and Status
|
||||
pub dhcsr: RW<u32>,
|
||||
/// Debug Core Register Selector
|
||||
pub dcrsr: WO<u32>,
|
||||
/// Debug Core Register Data
|
||||
pub dcrdr: RW<u32>,
|
||||
/// Debug Exception and Monitor Control
|
||||
pub demcr: RW<u32>,
|
||||
}
|
||||
|
||||
impl DCB {
|
||||
/// Enables TRACE. This is for example required by the
|
||||
/// `peripheral::DWT` cycle counter to work properly.
|
||||
/// As by STM documentation, this flag is not reset on
|
||||
/// soft-reset, only on power reset.
|
||||
#[inline]
|
||||
pub fn enable_trace(&mut self) {
|
||||
// set bit 24 / TRCENA
|
||||
unsafe {
|
||||
self.demcr.modify(|w| w | DCB_DEMCR_TRCENA);
|
||||
}
|
||||
}
|
||||
|
||||
/// Disables TRACE. See `DCB::enable_trace()` for more details
|
||||
#[inline]
|
||||
pub fn disable_trace(&mut self) {
|
||||
// unset bit 24 / TRCENA
|
||||
unsafe {
|
||||
self.demcr.modify(|w| w & !DCB_DEMCR_TRCENA);
|
||||
}
|
||||
}
|
||||
|
||||
/// Is there a debugger attached? (see note)
|
||||
///
|
||||
/// Note: This function is [reported not to
|
||||
/// work](http://web.archive.org/web/20180821191012/https://community.nxp.com/thread/424925#comment-782843)
|
||||
/// on Cortex-M0 devices. Per the ARM v6-M Architecture Reference Manual, "Access to the DHCSR
|
||||
/// from software running on the processor is IMPLEMENTATION DEFINED". Indeed, from the
|
||||
/// [Cortex-M0+ r0p1 Technical Reference Manual](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0484c/BABJHEIG.html), "Note Software cannot access the debug registers."
|
||||
#[inline]
|
||||
pub fn is_debugger_attached() -> bool {
|
||||
unsafe {
|
||||
// do an 8-bit read of the 32-bit DHCSR register, and get the LSB
|
||||
let value = ptr::read_volatile(Self::PTR as *const u8);
|
||||
value & 0x1 == 1
|
||||
}
|
||||
}
|
||||
}
|
||||
+268
@@ -0,0 +1,268 @@
|
||||
//! Data Watchpoint and Trace unit
|
||||
|
||||
#[cfg(not(armv6m))]
|
||||
use volatile_register::WO;
|
||||
use volatile_register::{RO, RW};
|
||||
|
||||
use crate::peripheral::DWT;
|
||||
|
||||
/// Register block
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
/// Control
|
||||
pub ctrl: RW<u32>,
|
||||
/// Cycle Count
|
||||
#[cfg(not(armv6m))]
|
||||
pub cyccnt: RW<u32>,
|
||||
/// CPI Count
|
||||
#[cfg(not(armv6m))]
|
||||
pub cpicnt: RW<u32>,
|
||||
/// Exception Overhead Count
|
||||
#[cfg(not(armv6m))]
|
||||
pub exccnt: RW<u32>,
|
||||
/// Sleep Count
|
||||
#[cfg(not(armv6m))]
|
||||
pub sleepcnt: RW<u32>,
|
||||
/// LSU Count
|
||||
#[cfg(not(armv6m))]
|
||||
pub lsucnt: RW<u32>,
|
||||
/// Folded-instruction Count
|
||||
#[cfg(not(armv6m))]
|
||||
pub foldcnt: RW<u32>,
|
||||
/// Cortex-M0(+) does not have these parts
|
||||
#[cfg(armv6m)]
|
||||
reserved: [u32; 6],
|
||||
/// Program Counter Sample
|
||||
pub pcsr: RO<u32>,
|
||||
/// Comparators
|
||||
#[cfg(armv6m)]
|
||||
pub c: [Comparator; 2],
|
||||
#[cfg(not(armv6m))]
|
||||
/// Comparators
|
||||
pub c: [Comparator; 16],
|
||||
#[cfg(not(armv6m))]
|
||||
reserved: [u32; 932],
|
||||
/// Lock Access
|
||||
#[cfg(not(armv6m))]
|
||||
pub lar: WO<u32>,
|
||||
/// Lock Status
|
||||
#[cfg(not(armv6m))]
|
||||
pub lsr: RO<u32>,
|
||||
}
|
||||
|
||||
/// Comparator
|
||||
#[repr(C)]
|
||||
pub struct Comparator {
|
||||
/// Comparator
|
||||
pub comp: RW<u32>,
|
||||
/// Comparator Mask
|
||||
pub mask: RW<u32>,
|
||||
/// Comparator Function
|
||||
pub function: RW<u32>,
|
||||
reserved: u32,
|
||||
}
|
||||
|
||||
// DWT CTRL register fields
|
||||
const NUMCOMP_OFFSET: u32 = 28;
|
||||
const NOTRCPKT: u32 = 1 << 27;
|
||||
const NOEXTTRIG: u32 = 1 << 26;
|
||||
const NOCYCCNT: u32 = 1 << 25;
|
||||
const NOPRFCNT: u32 = 1 << 24;
|
||||
const CYCCNTENA: u32 = 1 << 0;
|
||||
|
||||
impl DWT {
|
||||
/// Number of comparators implemented
|
||||
///
|
||||
/// A value of zero indicates no comparator support.
|
||||
#[inline]
|
||||
pub fn num_comp() -> u8 {
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
unsafe { ((*Self::PTR).ctrl.read() >> NUMCOMP_OFFSET) as u8 }
|
||||
}
|
||||
|
||||
/// Returns `true` if the the implementation supports sampling and exception tracing
|
||||
#[cfg(not(armv6m))]
|
||||
#[inline]
|
||||
pub fn has_exception_trace() -> bool {
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
unsafe { (*Self::PTR).ctrl.read() & NOTRCPKT == 0 }
|
||||
}
|
||||
|
||||
/// Returns `true` if the implementation includes external match signals
|
||||
#[cfg(not(armv6m))]
|
||||
#[inline]
|
||||
pub fn has_external_match() -> bool {
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
unsafe { (*Self::PTR).ctrl.read() & NOEXTTRIG == 0 }
|
||||
}
|
||||
|
||||
/// Returns `true` if the implementation supports a cycle counter
|
||||
#[cfg(not(armv6m))]
|
||||
#[inline]
|
||||
pub fn has_cycle_counter() -> bool {
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
unsafe { (*Self::PTR).ctrl.read() & NOCYCCNT == 0 }
|
||||
}
|
||||
|
||||
/// Returns `true` if the implementation the profiling counters
|
||||
#[cfg(not(armv6m))]
|
||||
#[inline]
|
||||
pub fn has_profiling_counter() -> bool {
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
unsafe { (*Self::PTR).ctrl.read() & NOPRFCNT == 0 }
|
||||
}
|
||||
|
||||
/// Enables the cycle counter
|
||||
///
|
||||
/// The global trace enable ([`DCB::enable_trace`]) should be set before
|
||||
/// enabling the cycle counter, the processor may ignore writes to the
|
||||
/// cycle counter enable if the global trace is disabled
|
||||
/// (implementation defined behaviour).
|
||||
///
|
||||
/// [`DCB::enable_trace`]: crate::peripheral::DCB::enable_trace
|
||||
#[cfg(not(armv6m))]
|
||||
#[inline]
|
||||
pub fn enable_cycle_counter(&mut self) {
|
||||
unsafe { self.ctrl.modify(|r| r | CYCCNTENA) }
|
||||
}
|
||||
|
||||
/// Disables the cycle counter
|
||||
#[cfg(not(armv6m))]
|
||||
#[inline]
|
||||
pub fn disable_cycle_counter(&mut self) {
|
||||
unsafe { self.ctrl.modify(|r| r & !CYCCNTENA) }
|
||||
}
|
||||
|
||||
/// Returns `true` if the cycle counter is enabled
|
||||
#[cfg(not(armv6m))]
|
||||
#[inline]
|
||||
pub fn cycle_counter_enabled() -> bool {
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
unsafe { (*Self::PTR).ctrl.read() & CYCCNTENA != 0 }
|
||||
}
|
||||
|
||||
/// Returns the current clock cycle count
|
||||
#[cfg(not(armv6m))]
|
||||
#[inline]
|
||||
#[deprecated(
|
||||
since = "0.7.4",
|
||||
note = "Use `cycle_count` which follows the C-GETTER convention"
|
||||
)]
|
||||
pub fn get_cycle_count() -> u32 {
|
||||
Self::cycle_count()
|
||||
}
|
||||
|
||||
/// Returns the current clock cycle count
|
||||
#[cfg(not(armv6m))]
|
||||
#[inline]
|
||||
pub fn cycle_count() -> u32 {
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
unsafe { (*Self::PTR).cyccnt.read() }
|
||||
}
|
||||
|
||||
/// Set the cycle count
|
||||
#[cfg(not(armv6m))]
|
||||
#[inline]
|
||||
pub fn set_cycle_count(&mut self, count: u32) {
|
||||
unsafe { self.cyccnt.write(count) }
|
||||
}
|
||||
|
||||
/// Removes the software lock on the DWT
|
||||
///
|
||||
/// Some devices, like the STM32F7, software lock the DWT after a power cycle.
|
||||
#[cfg(not(armv6m))]
|
||||
#[inline]
|
||||
pub fn unlock() {
|
||||
// NOTE(unsafe) atomic write to a stateless, write-only register
|
||||
unsafe { (*Self::PTR).lar.write(0xC5AC_CE55) }
|
||||
}
|
||||
|
||||
/// Get the CPI count
|
||||
///
|
||||
/// Counts additional cycles required to execute multi-cycle instructions,
|
||||
/// except those recorded by [`lsu_count`], and counts any instruction fetch
|
||||
/// stalls.
|
||||
///
|
||||
/// [`lsu_count`]: DWT::lsu_count
|
||||
#[cfg(not(armv6m))]
|
||||
#[inline]
|
||||
pub fn cpi_count() -> u8 {
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
unsafe { (*Self::PTR).cpicnt.read() as u8 }
|
||||
}
|
||||
|
||||
/// Set the CPI count
|
||||
#[cfg(not(armv6m))]
|
||||
#[inline]
|
||||
pub fn set_cpi_count(&mut self, count: u8) {
|
||||
unsafe { self.cpicnt.write(count as u32) }
|
||||
}
|
||||
|
||||
/// Get the total cycles spent in exception processing
|
||||
#[cfg(not(armv6m))]
|
||||
#[inline]
|
||||
pub fn exception_count() -> u8 {
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
unsafe { (*Self::PTR).exccnt.read() as u8 }
|
||||
}
|
||||
|
||||
/// Set the exception count
|
||||
#[cfg(not(armv6m))]
|
||||
#[inline]
|
||||
pub fn set_exception_count(&mut self, count: u8) {
|
||||
unsafe { self.exccnt.write(count as u32) }
|
||||
}
|
||||
|
||||
/// Get the total number of cycles that the processor is sleeping
|
||||
///
|
||||
/// ARM recommends that this counter counts all cycles when the processor is sleeping,
|
||||
/// regardless of whether a WFI or WFE instruction, or the sleep-on-exit functionality,
|
||||
/// caused the entry to sleep mode.
|
||||
/// However, all sleep features are implementation defined and therefore when
|
||||
/// this counter counts is implementation defined.
|
||||
#[cfg(not(armv6m))]
|
||||
#[inline]
|
||||
pub fn sleep_count() -> u8 {
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
unsafe { (*Self::PTR).sleepcnt.read() as u8 }
|
||||
}
|
||||
|
||||
/// Set the sleep count
|
||||
#[cfg(not(armv6m))]
|
||||
#[inline]
|
||||
pub fn set_sleep_count(&mut self, count: u8) {
|
||||
unsafe { self.sleepcnt.write(count as u32) }
|
||||
}
|
||||
|
||||
/// Get the additional cycles required to execute all load or store instructions
|
||||
#[cfg(not(armv6m))]
|
||||
#[inline]
|
||||
pub fn lsu_count() -> u8 {
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
unsafe { (*Self::PTR).lsucnt.read() as u8 }
|
||||
}
|
||||
|
||||
/// Set the lsu count
|
||||
#[cfg(not(armv6m))]
|
||||
#[inline]
|
||||
pub fn set_lsu_count(&mut self, count: u8) {
|
||||
unsafe { self.lsucnt.write(count as u32) }
|
||||
}
|
||||
|
||||
/// Get the folded instruction count
|
||||
///
|
||||
/// Increments on each instruction that takes 0 cycles.
|
||||
#[cfg(not(armv6m))]
|
||||
#[inline]
|
||||
pub fn fold_count() -> u8 {
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
unsafe { (*Self::PTR).foldcnt.read() as u8 }
|
||||
}
|
||||
|
||||
/// Set the folded instruction count
|
||||
#[cfg(not(armv6m))]
|
||||
#[inline]
|
||||
pub fn set_fold_count(&mut self, count: u8) {
|
||||
unsafe { self.foldcnt.write(count as u32) }
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
//! Flash Patch and Breakpoint unit
|
||||
//!
|
||||
//! *NOTE* Not available on Armv6-M.
|
||||
|
||||
use volatile_register::{RO, RW, WO};
|
||||
|
||||
/// Register block
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
/// Control
|
||||
pub ctrl: RW<u32>,
|
||||
/// Remap
|
||||
pub remap: RW<u32>,
|
||||
/// Comparator
|
||||
pub comp: [RW<u32>; 127],
|
||||
reserved: [u32; 875],
|
||||
/// Lock Access
|
||||
pub lar: WO<u32>,
|
||||
/// Lock Status
|
||||
pub lsr: RO<u32>,
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
//! Floating Point Unit
|
||||
//!
|
||||
//! *NOTE* Available only on targets with a Floating Point Unit (FPU) extension.
|
||||
|
||||
use volatile_register::{RO, RW};
|
||||
|
||||
/// Register block
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
reserved: u32,
|
||||
/// Floating Point Context Control
|
||||
pub fpccr: RW<u32>,
|
||||
/// Floating Point Context Address
|
||||
pub fpcar: RW<u32>,
|
||||
/// Floating Point Default Status Control
|
||||
pub fpdscr: RW<u32>,
|
||||
/// Media and FP Feature
|
||||
pub mvfr: [RO<u32>; 3],
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
//! Implementation Control Block
|
||||
|
||||
#[cfg(any(armv7m, armv8m, native))]
|
||||
use volatile_register::RO;
|
||||
use volatile_register::RW;
|
||||
|
||||
/// Register block
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
/// Interrupt Controller Type Register
|
||||
///
|
||||
/// The bottom four bits of this register give the number of implemented
|
||||
/// interrupt lines, divided by 32. So a value of `0b0010` indicates 64
|
||||
/// interrupts.
|
||||
#[cfg(any(armv7m, armv8m, native))]
|
||||
pub ictr: RO<u32>,
|
||||
|
||||
/// The ICTR is not defined in the ARMv6-M Architecture Reference manual, so
|
||||
/// we replace it with this.
|
||||
#[cfg(not(any(armv7m, armv8m, native)))]
|
||||
_reserved: u32,
|
||||
|
||||
/// Auxiliary Control Register
|
||||
///
|
||||
/// This register is entirely implementation defined -- the standard gives
|
||||
/// it an address, but does not define its role or contents.
|
||||
pub actlr: RW<u32>,
|
||||
|
||||
/// Coprocessor Power Control Register
|
||||
#[cfg(armv8m)]
|
||||
pub cppwr: RW<u32>,
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
//! Instrumentation Trace Macrocell
|
||||
//!
|
||||
//! *NOTE* Not available on Armv6-M and Armv8-M Baseline.
|
||||
|
||||
use core::cell::UnsafeCell;
|
||||
use core::ptr;
|
||||
|
||||
use volatile_register::{RO, RW, WO};
|
||||
|
||||
/// Register block
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
/// Stimulus Port
|
||||
pub stim: [Stim; 256],
|
||||
reserved0: [u32; 640],
|
||||
/// Trace Enable
|
||||
pub ter: [RW<u32>; 8],
|
||||
reserved1: [u32; 8],
|
||||
/// Trace Privilege
|
||||
pub tpr: RW<u32>,
|
||||
reserved2: [u32; 15],
|
||||
/// Trace Control
|
||||
pub tcr: RW<u32>,
|
||||
reserved3: [u32; 75],
|
||||
/// Lock Access
|
||||
pub lar: WO<u32>,
|
||||
/// Lock Status
|
||||
pub lsr: RO<u32>,
|
||||
}
|
||||
|
||||
/// Stimulus Port
|
||||
pub struct Stim {
|
||||
register: UnsafeCell<u32>,
|
||||
}
|
||||
|
||||
impl Stim {
|
||||
/// Writes an `u8` payload into the stimulus port
|
||||
#[inline]
|
||||
pub fn write_u8(&mut self, value: u8) {
|
||||
unsafe { ptr::write_volatile(self.register.get() as *mut u8, value) }
|
||||
}
|
||||
|
||||
/// Writes an `u16` payload into the stimulus port
|
||||
#[inline]
|
||||
pub fn write_u16(&mut self, value: u16) {
|
||||
unsafe { ptr::write_volatile(self.register.get() as *mut u16, value) }
|
||||
}
|
||||
|
||||
/// Writes an `u32` payload into the stimulus port
|
||||
#[inline]
|
||||
pub fn write_u32(&mut self, value: u32) {
|
||||
unsafe { ptr::write_volatile(self.register.get(), value) }
|
||||
}
|
||||
|
||||
/// Returns `true` if the stimulus port is ready to accept more data
|
||||
#[cfg(not(armv8m))]
|
||||
#[inline]
|
||||
pub fn is_fifo_ready(&self) -> bool {
|
||||
unsafe { ptr::read_volatile(self.register.get()) & 0b1 == 1 }
|
||||
}
|
||||
|
||||
/// Returns `true` if the stimulus port is ready to accept more data
|
||||
#[cfg(armv8m)]
|
||||
#[inline]
|
||||
pub fn is_fifo_ready(&self) -> bool {
|
||||
// ARMv8-M adds a disabled bit; we indicate that we are ready to
|
||||
// proceed with a stimulus write if the port is either ready (bit 0) or
|
||||
// disabled (bit 1).
|
||||
unsafe { ptr::read_volatile(self.register.get()) & 0b11 != 0 }
|
||||
}
|
||||
}
|
||||
+685
@@ -0,0 +1,685 @@
|
||||
//! Core peripherals.
|
||||
//!
|
||||
//! # API
|
||||
//!
|
||||
//! To use (most of) the peripheral API first you must get an *instance* of the peripheral. All the
|
||||
//! core peripherals are modeled as singletons (there can only ever be, at most, one instance of any
|
||||
//! one of them at any given point in time) and the only way to get an instance of them is through
|
||||
//! the [`Peripherals::take`](struct.Peripherals.html#method.take) method.
|
||||
//!
|
||||
//! ``` no_run
|
||||
//! # use cortex_m::peripheral::Peripherals;
|
||||
//! let mut peripherals = Peripherals::take().unwrap();
|
||||
//! peripherals.DCB.enable_trace();
|
||||
//! ```
|
||||
//!
|
||||
//! This method can only be successfully called *once* -- this is why the method returns an
|
||||
//! `Option`. Subsequent calls to the method will result in a `None` value being returned.
|
||||
//!
|
||||
//! ``` no_run, should_panic
|
||||
//! # use cortex_m::peripheral::Peripherals;
|
||||
//! let ok = Peripherals::take().unwrap();
|
||||
//! let panics = Peripherals::take().unwrap();
|
||||
//! ```
|
||||
//! A part of the peripheral API doesn't require access to a peripheral instance. This part of the
|
||||
//! API is provided as static methods on the peripheral types. One example is the
|
||||
//! [`DWT::cycle_count`](struct.DWT.html#method.cycle_count) method.
|
||||
//!
|
||||
//! ``` no_run
|
||||
//! # use cortex_m::peripheral::{DWT, Peripherals};
|
||||
//! {
|
||||
//! let mut peripherals = Peripherals::take().unwrap();
|
||||
//! peripherals.DCB.enable_trace();
|
||||
//! peripherals.DWT.enable_cycle_counter();
|
||||
//! } // all the peripheral singletons are destroyed here
|
||||
//!
|
||||
//! // but this method can be called without a DWT instance
|
||||
//! let cyccnt = DWT::cycle_count();
|
||||
//! ```
|
||||
//!
|
||||
//! The singleton property can be *unsafely* bypassed using the `ptr` static method which is
|
||||
//! available on all the peripheral types. This method is a useful building block for implementing
|
||||
//! safe higher level abstractions.
|
||||
//!
|
||||
//! ``` no_run
|
||||
//! # use cortex_m::peripheral::{DWT, Peripherals};
|
||||
//! {
|
||||
//! let mut peripherals = Peripherals::take().unwrap();
|
||||
//! peripherals.DCB.enable_trace();
|
||||
//! peripherals.DWT.enable_cycle_counter();
|
||||
//! } // all the peripheral singletons are destroyed here
|
||||
//!
|
||||
//! // actually safe because this is an atomic read with no side effects
|
||||
//! let cyccnt = unsafe { (*DWT::PTR).cyccnt.read() };
|
||||
//! ```
|
||||
//!
|
||||
//! # References
|
||||
//!
|
||||
//! - ARMv7-M Architecture Reference Manual (Issue E.b) - Chapter B3
|
||||
|
||||
use core::marker::PhantomData;
|
||||
use core::ops;
|
||||
|
||||
use crate::interrupt;
|
||||
|
||||
#[cfg(cm7)]
|
||||
pub mod ac;
|
||||
#[cfg(not(armv6m))]
|
||||
pub mod cbp;
|
||||
pub mod cpuid;
|
||||
pub mod dcb;
|
||||
pub mod dwt;
|
||||
#[cfg(not(armv6m))]
|
||||
pub mod fpb;
|
||||
// NOTE(native) is for documentation purposes
|
||||
#[cfg(any(has_fpu, native))]
|
||||
pub mod fpu;
|
||||
pub mod icb;
|
||||
#[cfg(all(not(armv6m), not(armv8m_base)))]
|
||||
pub mod itm;
|
||||
pub mod mpu;
|
||||
pub mod nvic;
|
||||
#[cfg(armv8m)]
|
||||
pub mod sau;
|
||||
pub mod scb;
|
||||
pub mod syst;
|
||||
#[cfg(not(armv6m))]
|
||||
pub mod tpiu;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
||||
// NOTE the `PhantomData` used in the peripherals proxy is to make them `Send` but *not* `Sync`
|
||||
|
||||
/// Core peripherals
|
||||
#[allow(non_snake_case)]
|
||||
#[allow(clippy::manual_non_exhaustive)]
|
||||
pub struct Peripherals {
|
||||
/// Cortex-M7 TCM and cache access control.
|
||||
#[cfg(cm7)]
|
||||
pub AC: AC,
|
||||
|
||||
/// Cache and branch predictor maintenance operations.
|
||||
/// Not available on Armv6-M.
|
||||
pub CBP: CBP,
|
||||
|
||||
/// CPUID
|
||||
pub CPUID: CPUID,
|
||||
|
||||
/// Debug Control Block
|
||||
pub DCB: DCB,
|
||||
|
||||
/// Data Watchpoint and Trace unit
|
||||
pub DWT: DWT,
|
||||
|
||||
/// Flash Patch and Breakpoint unit.
|
||||
/// Not available on Armv6-M.
|
||||
pub FPB: FPB,
|
||||
|
||||
/// Floating Point Unit.
|
||||
pub FPU: FPU,
|
||||
|
||||
/// Implementation Control Block.
|
||||
///
|
||||
/// The name is from the v8-M spec, but the block existed in earlier
|
||||
/// revisions, without a name.
|
||||
pub ICB: ICB,
|
||||
|
||||
/// Instrumentation Trace Macrocell.
|
||||
/// Not available on Armv6-M and Armv8-M Baseline.
|
||||
pub ITM: ITM,
|
||||
|
||||
/// Memory Protection Unit
|
||||
pub MPU: MPU,
|
||||
|
||||
/// Nested Vector Interrupt Controller
|
||||
pub NVIC: NVIC,
|
||||
|
||||
/// Security Attribution Unit
|
||||
pub SAU: SAU,
|
||||
|
||||
/// System Control Block
|
||||
pub SCB: SCB,
|
||||
|
||||
/// SysTick: System Timer
|
||||
pub SYST: SYST,
|
||||
|
||||
/// Trace Port Interface Unit.
|
||||
/// Not available on Armv6-M.
|
||||
pub TPIU: TPIU,
|
||||
|
||||
// Private field making `Peripherals` non-exhaustive. We don't use `#[non_exhaustive]` so we
|
||||
// can support older Rust versions.
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
// NOTE `no_mangle` is used here to prevent linking different minor versions of this crate as that
|
||||
// would let you `take` the core peripherals more than once (one per minor version)
|
||||
#[no_mangle]
|
||||
static CORE_PERIPHERALS: () = ();
|
||||
|
||||
/// Set to `true` when `take` or `steal` was called to make `Peripherals` a singleton.
|
||||
static mut TAKEN: bool = false;
|
||||
|
||||
impl Peripherals {
|
||||
/// Returns all the core peripherals *once*
|
||||
#[inline]
|
||||
pub fn take() -> Option<Self> {
|
||||
interrupt::free(|_| {
|
||||
if unsafe { TAKEN } {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { Peripherals::steal() })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Unchecked version of `Peripherals::take`
|
||||
#[inline]
|
||||
pub unsafe fn steal() -> Self {
|
||||
TAKEN = true;
|
||||
|
||||
Peripherals {
|
||||
#[cfg(cm7)]
|
||||
AC: AC {
|
||||
_marker: PhantomData,
|
||||
},
|
||||
CBP: CBP {
|
||||
_marker: PhantomData,
|
||||
},
|
||||
CPUID: CPUID {
|
||||
_marker: PhantomData,
|
||||
},
|
||||
DCB: DCB {
|
||||
_marker: PhantomData,
|
||||
},
|
||||
DWT: DWT {
|
||||
_marker: PhantomData,
|
||||
},
|
||||
FPB: FPB {
|
||||
_marker: PhantomData,
|
||||
},
|
||||
FPU: FPU {
|
||||
_marker: PhantomData,
|
||||
},
|
||||
ICB: ICB {
|
||||
_marker: PhantomData,
|
||||
},
|
||||
ITM: ITM {
|
||||
_marker: PhantomData,
|
||||
},
|
||||
MPU: MPU {
|
||||
_marker: PhantomData,
|
||||
},
|
||||
NVIC: NVIC {
|
||||
_marker: PhantomData,
|
||||
},
|
||||
SAU: SAU {
|
||||
_marker: PhantomData,
|
||||
},
|
||||
SCB: SCB {
|
||||
_marker: PhantomData,
|
||||
},
|
||||
SYST: SYST {
|
||||
_marker: PhantomData,
|
||||
},
|
||||
TPIU: TPIU {
|
||||
_marker: PhantomData,
|
||||
},
|
||||
_priv: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Access control
|
||||
#[cfg(cm7)]
|
||||
pub struct AC {
|
||||
_marker: PhantomData<*const ()>,
|
||||
}
|
||||
|
||||
#[cfg(cm7)]
|
||||
unsafe impl Send for AC {}
|
||||
|
||||
#[cfg(cm7)]
|
||||
impl AC {
|
||||
/// Pointer to the register block
|
||||
pub const PTR: *const self::ac::RegisterBlock = 0xE000_EF90 as *const _;
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")]
|
||||
pub const fn ptr() -> *const self::ac::RegisterBlock {
|
||||
Self::PTR
|
||||
}
|
||||
}
|
||||
|
||||
/// Cache and branch predictor maintenance operations
|
||||
pub struct CBP {
|
||||
_marker: PhantomData<*const ()>,
|
||||
}
|
||||
|
||||
unsafe impl Send for CBP {}
|
||||
|
||||
#[cfg(not(armv6m))]
|
||||
impl CBP {
|
||||
#[inline(always)]
|
||||
pub(crate) const unsafe fn new() -> Self {
|
||||
CBP {
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Pointer to the register block
|
||||
pub const PTR: *const self::cbp::RegisterBlock = 0xE000_EF50 as *const _;
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")]
|
||||
pub const fn ptr() -> *const self::cbp::RegisterBlock {
|
||||
Self::PTR
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(armv6m))]
|
||||
impl ops::Deref for CBP {
|
||||
type Target = self::cbp::RegisterBlock;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*Self::PTR }
|
||||
}
|
||||
}
|
||||
|
||||
/// CPUID
|
||||
pub struct CPUID {
|
||||
_marker: PhantomData<*const ()>,
|
||||
}
|
||||
|
||||
unsafe impl Send for CPUID {}
|
||||
|
||||
impl CPUID {
|
||||
/// Pointer to the register block
|
||||
pub const PTR: *const self::cpuid::RegisterBlock = 0xE000_ED00 as *const _;
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")]
|
||||
pub const fn ptr() -> *const self::cpuid::RegisterBlock {
|
||||
Self::PTR
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Deref for CPUID {
|
||||
type Target = self::cpuid::RegisterBlock;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*Self::PTR }
|
||||
}
|
||||
}
|
||||
|
||||
/// Debug Control Block
|
||||
pub struct DCB {
|
||||
_marker: PhantomData<*const ()>,
|
||||
}
|
||||
|
||||
unsafe impl Send for DCB {}
|
||||
|
||||
impl DCB {
|
||||
/// Pointer to the register block
|
||||
pub const PTR: *const dcb::RegisterBlock = 0xE000_EDF0 as *const _;
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")]
|
||||
pub const fn ptr() -> *const dcb::RegisterBlock {
|
||||
Self::PTR
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Deref for DCB {
|
||||
type Target = self::dcb::RegisterBlock;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*DCB::PTR }
|
||||
}
|
||||
}
|
||||
|
||||
/// Data Watchpoint and Trace unit
|
||||
pub struct DWT {
|
||||
_marker: PhantomData<*const ()>,
|
||||
}
|
||||
|
||||
unsafe impl Send for DWT {}
|
||||
|
||||
impl DWT {
|
||||
/// Pointer to the register block
|
||||
pub const PTR: *const dwt::RegisterBlock = 0xE000_1000 as *const _;
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")]
|
||||
pub const fn ptr() -> *const dwt::RegisterBlock {
|
||||
Self::PTR
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Deref for DWT {
|
||||
type Target = self::dwt::RegisterBlock;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*Self::PTR }
|
||||
}
|
||||
}
|
||||
|
||||
/// Flash Patch and Breakpoint unit
|
||||
pub struct FPB {
|
||||
_marker: PhantomData<*const ()>,
|
||||
}
|
||||
|
||||
unsafe impl Send for FPB {}
|
||||
|
||||
#[cfg(not(armv6m))]
|
||||
impl FPB {
|
||||
/// Pointer to the register block
|
||||
pub const PTR: *const fpb::RegisterBlock = 0xE000_2000 as *const _;
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")]
|
||||
pub const fn ptr() -> *const fpb::RegisterBlock {
|
||||
Self::PTR
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(armv6m))]
|
||||
impl ops::Deref for FPB {
|
||||
type Target = self::fpb::RegisterBlock;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*Self::PTR }
|
||||
}
|
||||
}
|
||||
|
||||
/// Floating Point Unit
|
||||
pub struct FPU {
|
||||
_marker: PhantomData<*const ()>,
|
||||
}
|
||||
|
||||
unsafe impl Send for FPU {}
|
||||
|
||||
#[cfg(any(has_fpu, native))]
|
||||
impl FPU {
|
||||
/// Pointer to the register block
|
||||
pub const PTR: *const fpu::RegisterBlock = 0xE000_EF30 as *const _;
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")]
|
||||
pub const fn ptr() -> *const fpu::RegisterBlock {
|
||||
Self::PTR
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(has_fpu, native))]
|
||||
impl ops::Deref for FPU {
|
||||
type Target = self::fpu::RegisterBlock;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*Self::PTR }
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation Control Block.
|
||||
///
|
||||
/// This block contains implementation-defined registers like `ictr` and
|
||||
/// `actlr`. It's called the "implementation control block" in the ARMv8-M
|
||||
/// standard, but earlier standards contained the registers, just without a
|
||||
/// name.
|
||||
pub struct ICB {
|
||||
_marker: PhantomData<*const ()>,
|
||||
}
|
||||
|
||||
unsafe impl Send for ICB {}
|
||||
|
||||
impl ICB {
|
||||
/// Pointer to the register block
|
||||
pub const PTR: *mut icb::RegisterBlock = 0xE000_E004 as *mut _;
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")]
|
||||
pub const fn ptr() -> *mut icb::RegisterBlock {
|
||||
Self::PTR
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Deref for ICB {
|
||||
type Target = self::icb::RegisterBlock;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*Self::PTR }
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::DerefMut for ICB {
|
||||
#[inline(always)]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { &mut *Self::PTR }
|
||||
}
|
||||
}
|
||||
|
||||
/// Instrumentation Trace Macrocell
|
||||
pub struct ITM {
|
||||
_marker: PhantomData<*const ()>,
|
||||
}
|
||||
|
||||
unsafe impl Send for ITM {}
|
||||
|
||||
#[cfg(all(not(armv6m), not(armv8m_base)))]
|
||||
impl ITM {
|
||||
/// Pointer to the register block
|
||||
pub const PTR: *mut itm::RegisterBlock = 0xE000_0000 as *mut _;
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")]
|
||||
pub const fn ptr() -> *mut itm::RegisterBlock {
|
||||
Self::PTR
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(not(armv6m), not(armv8m_base)))]
|
||||
impl ops::Deref for ITM {
|
||||
type Target = self::itm::RegisterBlock;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*Self::PTR }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(not(armv6m), not(armv8m_base)))]
|
||||
impl ops::DerefMut for ITM {
|
||||
#[inline(always)]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { &mut *Self::PTR }
|
||||
}
|
||||
}
|
||||
|
||||
/// Memory Protection Unit
|
||||
pub struct MPU {
|
||||
_marker: PhantomData<*const ()>,
|
||||
}
|
||||
|
||||
unsafe impl Send for MPU {}
|
||||
|
||||
impl MPU {
|
||||
/// Pointer to the register block
|
||||
pub const PTR: *const mpu::RegisterBlock = 0xE000_ED90 as *const _;
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")]
|
||||
pub const fn ptr() -> *const mpu::RegisterBlock {
|
||||
Self::PTR
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Deref for MPU {
|
||||
type Target = self::mpu::RegisterBlock;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*Self::PTR }
|
||||
}
|
||||
}
|
||||
|
||||
/// Nested Vector Interrupt Controller
|
||||
pub struct NVIC {
|
||||
_marker: PhantomData<*const ()>,
|
||||
}
|
||||
|
||||
unsafe impl Send for NVIC {}
|
||||
|
||||
impl NVIC {
|
||||
/// Pointer to the register block
|
||||
pub const PTR: *const nvic::RegisterBlock = 0xE000_E100 as *const _;
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")]
|
||||
pub const fn ptr() -> *const nvic::RegisterBlock {
|
||||
Self::PTR
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Deref for NVIC {
|
||||
type Target = self::nvic::RegisterBlock;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*Self::PTR }
|
||||
}
|
||||
}
|
||||
|
||||
/// Security Attribution Unit
|
||||
pub struct SAU {
|
||||
_marker: PhantomData<*const ()>,
|
||||
}
|
||||
|
||||
unsafe impl Send for SAU {}
|
||||
|
||||
#[cfg(armv8m)]
|
||||
impl SAU {
|
||||
/// Pointer to the register block
|
||||
pub const PTR: *const sau::RegisterBlock = 0xE000_EDD0 as *const _;
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")]
|
||||
pub const fn ptr() -> *const sau::RegisterBlock {
|
||||
Self::PTR
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(armv8m)]
|
||||
impl ops::Deref for SAU {
|
||||
type Target = self::sau::RegisterBlock;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*Self::PTR }
|
||||
}
|
||||
}
|
||||
|
||||
/// System Control Block
|
||||
pub struct SCB {
|
||||
_marker: PhantomData<*const ()>,
|
||||
}
|
||||
|
||||
unsafe impl Send for SCB {}
|
||||
|
||||
impl SCB {
|
||||
/// Pointer to the register block
|
||||
pub const PTR: *const scb::RegisterBlock = 0xE000_ED04 as *const _;
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")]
|
||||
pub const fn ptr() -> *const scb::RegisterBlock {
|
||||
Self::PTR
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Deref for SCB {
|
||||
type Target = self::scb::RegisterBlock;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*Self::PTR }
|
||||
}
|
||||
}
|
||||
|
||||
/// SysTick: System Timer
|
||||
pub struct SYST {
|
||||
_marker: PhantomData<*const ()>,
|
||||
}
|
||||
|
||||
unsafe impl Send for SYST {}
|
||||
|
||||
impl SYST {
|
||||
/// Pointer to the register block
|
||||
pub const PTR: *const syst::RegisterBlock = 0xE000_E010 as *const _;
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")]
|
||||
pub const fn ptr() -> *const syst::RegisterBlock {
|
||||
Self::PTR
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Deref for SYST {
|
||||
type Target = self::syst::RegisterBlock;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*Self::PTR }
|
||||
}
|
||||
}
|
||||
|
||||
/// Trace Port Interface Unit
|
||||
pub struct TPIU {
|
||||
_marker: PhantomData<*const ()>,
|
||||
}
|
||||
|
||||
unsafe impl Send for TPIU {}
|
||||
|
||||
#[cfg(not(armv6m))]
|
||||
impl TPIU {
|
||||
/// Pointer to the register block
|
||||
pub const PTR: *const tpiu::RegisterBlock = 0xE004_0000 as *const _;
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")]
|
||||
pub const fn ptr() -> *const tpiu::RegisterBlock {
|
||||
Self::PTR
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(armv6m))]
|
||||
impl ops::Deref for TPIU {
|
||||
type Target = self::tpiu::RegisterBlock;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*Self::PTR }
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
//! Memory Protection Unit
|
||||
|
||||
use volatile_register::{RO, RW};
|
||||
|
||||
/// Register block for ARMv7-M
|
||||
#[cfg(not(armv8m))]
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
/// Type
|
||||
pub _type: RO<u32>,
|
||||
/// Control
|
||||
pub ctrl: RW<u32>,
|
||||
/// Region Number
|
||||
pub rnr: RW<u32>,
|
||||
/// Region Base Address
|
||||
pub rbar: RW<u32>,
|
||||
/// Region Attribute and Size
|
||||
pub rasr: RW<u32>,
|
||||
/// Alias 1 of RBAR
|
||||
pub rbar_a1: RW<u32>,
|
||||
/// Alias 1 of RASR
|
||||
pub rasr_a1: RW<u32>,
|
||||
/// Alias 2 of RBAR
|
||||
pub rbar_a2: RW<u32>,
|
||||
/// Alias 2 of RASR
|
||||
pub rasr_a2: RW<u32>,
|
||||
/// Alias 3 of RBAR
|
||||
pub rbar_a3: RW<u32>,
|
||||
/// Alias 3 of RASR
|
||||
pub rasr_a3: RW<u32>,
|
||||
}
|
||||
|
||||
/// Register block for ARMv8-M
|
||||
#[cfg(armv8m)]
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
/// Type
|
||||
pub _type: RO<u32>,
|
||||
/// Control
|
||||
pub ctrl: RW<u32>,
|
||||
/// Region Number
|
||||
pub rnr: RW<u32>,
|
||||
/// Region Base Address
|
||||
pub rbar: RW<u32>,
|
||||
/// Region Limit Address
|
||||
pub rlar: RW<u32>,
|
||||
/// Alias 1 of RBAR
|
||||
pub rbar_a1: RW<u32>,
|
||||
/// Alias 1 of RLAR
|
||||
pub rlar_a1: RW<u32>,
|
||||
/// Alias 2 of RBAR
|
||||
pub rbar_a2: RW<u32>,
|
||||
/// Alias 2 of RLAR
|
||||
pub rlar_a2: RW<u32>,
|
||||
/// Alias 3 of RBAR
|
||||
pub rbar_a3: RW<u32>,
|
||||
/// Alias 3 of RLAR
|
||||
pub rlar_a3: RW<u32>,
|
||||
|
||||
// Reserved word at offset 0xBC
|
||||
_reserved: u32,
|
||||
|
||||
/// Memory Attribute Indirection register 0 and 1
|
||||
pub mair: [RW<u32>; 2],
|
||||
}
|
||||
+265
@@ -0,0 +1,265 @@
|
||||
//! Nested Vector Interrupt Controller
|
||||
|
||||
use volatile_register::RW;
|
||||
#[cfg(not(armv6m))]
|
||||
use volatile_register::{RO, WO};
|
||||
|
||||
use crate::interrupt::InterruptNumber;
|
||||
use crate::peripheral::NVIC;
|
||||
|
||||
/// Register block
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
/// Interrupt Set-Enable
|
||||
pub iser: [RW<u32>; 16],
|
||||
|
||||
_reserved0: [u32; 16],
|
||||
|
||||
/// Interrupt Clear-Enable
|
||||
pub icer: [RW<u32>; 16],
|
||||
|
||||
_reserved1: [u32; 16],
|
||||
|
||||
/// Interrupt Set-Pending
|
||||
pub ispr: [RW<u32>; 16],
|
||||
|
||||
_reserved2: [u32; 16],
|
||||
|
||||
/// Interrupt Clear-Pending
|
||||
pub icpr: [RW<u32>; 16],
|
||||
|
||||
_reserved3: [u32; 16],
|
||||
|
||||
/// Interrupt Active Bit (not present on Cortex-M0 variants)
|
||||
#[cfg(not(armv6m))]
|
||||
pub iabr: [RO<u32>; 16],
|
||||
#[cfg(armv6m)]
|
||||
_reserved4: [u32; 16],
|
||||
|
||||
_reserved5: [u32; 48],
|
||||
|
||||
/// Interrupt Priority
|
||||
///
|
||||
/// On ARMv7-M, 124 word-sized registers are available. Each of those
|
||||
/// contains of 4 interrupt priorities of 8 byte each.The architecture
|
||||
/// specifically allows accessing those along byte boundaries, so they are
|
||||
/// represented as 496 byte-sized registers, for convenience, and to allow
|
||||
/// atomic priority updates.
|
||||
///
|
||||
/// On ARMv6-M, the registers must only be accessed along word boundaries,
|
||||
/// so convenient byte-sized representation wouldn't work on that
|
||||
/// architecture.
|
||||
#[cfg(not(armv6m))]
|
||||
pub ipr: [RW<u8>; 496],
|
||||
|
||||
/// Interrupt Priority
|
||||
///
|
||||
/// On ARMv7-M, 124 word-sized registers are available. Each of those
|
||||
/// contains of 4 interrupt priorities of 8 byte each.The architecture
|
||||
/// specifically allows accessing those along byte boundaries, so they are
|
||||
/// represented as 496 byte-sized registers, for convenience, and to allow
|
||||
/// atomic priority updates.
|
||||
///
|
||||
/// On ARMv6-M, the registers must only be accessed along word boundaries,
|
||||
/// so convenient byte-sized representation wouldn't work on that
|
||||
/// architecture.
|
||||
#[cfg(armv6m)]
|
||||
pub ipr: [RW<u32>; 8],
|
||||
|
||||
#[cfg(not(armv6m))]
|
||||
_reserved6: [u32; 580],
|
||||
|
||||
/// Software Trigger Interrupt
|
||||
#[cfg(not(armv6m))]
|
||||
pub stir: WO<u32>,
|
||||
}
|
||||
|
||||
impl NVIC {
|
||||
/// Request an IRQ in software
|
||||
///
|
||||
/// Writing a value to the INTID field is the same as manually pending an interrupt by setting
|
||||
/// the corresponding interrupt bit in an Interrupt Set Pending Register. This is similar to
|
||||
/// [`NVIC::pend`].
|
||||
///
|
||||
/// This method is not available on ARMv6-M chips.
|
||||
///
|
||||
/// [`NVIC::pend`]: #method.pend
|
||||
#[cfg(not(armv6m))]
|
||||
#[inline]
|
||||
pub fn request<I>(&mut self, interrupt: I)
|
||||
where
|
||||
I: InterruptNumber,
|
||||
{
|
||||
let nr = interrupt.number();
|
||||
|
||||
unsafe {
|
||||
self.stir.write(u32::from(nr));
|
||||
}
|
||||
}
|
||||
|
||||
/// Disables `interrupt`
|
||||
#[inline]
|
||||
pub fn mask<I>(interrupt: I)
|
||||
where
|
||||
I: InterruptNumber,
|
||||
{
|
||||
let nr = interrupt.number();
|
||||
// NOTE(unsafe) this is a write to a stateless register
|
||||
unsafe { (*Self::PTR).icer[usize::from(nr / 32)].write(1 << (nr % 32)) }
|
||||
}
|
||||
|
||||
/// Enables `interrupt`
|
||||
///
|
||||
/// This function is `unsafe` because it can break mask-based critical sections
|
||||
#[inline]
|
||||
pub unsafe fn unmask<I>(interrupt: I)
|
||||
where
|
||||
I: InterruptNumber,
|
||||
{
|
||||
let nr = interrupt.number();
|
||||
// NOTE(ptr) this is a write to a stateless register
|
||||
(*Self::PTR).iser[usize::from(nr / 32)].write(1 << (nr % 32))
|
||||
}
|
||||
|
||||
/// Returns the NVIC priority of `interrupt`
|
||||
///
|
||||
/// *NOTE* NVIC encodes priority in the highest bits of a byte so values like `1` and `2` map
|
||||
/// to the same priority. Also for NVIC priorities, a lower value (e.g. `16`) has higher
|
||||
/// priority (urgency) than a larger value (e.g. `32`).
|
||||
#[inline]
|
||||
pub fn get_priority<I>(interrupt: I) -> u8
|
||||
where
|
||||
I: InterruptNumber,
|
||||
{
|
||||
#[cfg(not(armv6m))]
|
||||
{
|
||||
let nr = interrupt.number();
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
unsafe { (*Self::PTR).ipr[usize::from(nr)].read() }
|
||||
}
|
||||
|
||||
#[cfg(armv6m)]
|
||||
{
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
let ipr_n = unsafe { (*Self::PTR).ipr[Self::ipr_index(interrupt)].read() };
|
||||
let prio = (ipr_n >> Self::ipr_shift(interrupt)) & 0x0000_00ff;
|
||||
prio as u8
|
||||
}
|
||||
}
|
||||
|
||||
/// Is `interrupt` active or pre-empted and stacked
|
||||
#[cfg(not(armv6m))]
|
||||
#[inline]
|
||||
pub fn is_active<I>(interrupt: I) -> bool
|
||||
where
|
||||
I: InterruptNumber,
|
||||
{
|
||||
let nr = interrupt.number();
|
||||
let mask = 1 << (nr % 32);
|
||||
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
unsafe { ((*Self::PTR).iabr[usize::from(nr / 32)].read() & mask) == mask }
|
||||
}
|
||||
|
||||
/// Checks if `interrupt` is enabled
|
||||
#[inline]
|
||||
pub fn is_enabled<I>(interrupt: I) -> bool
|
||||
where
|
||||
I: InterruptNumber,
|
||||
{
|
||||
let nr = interrupt.number();
|
||||
let mask = 1 << (nr % 32);
|
||||
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
unsafe { ((*Self::PTR).iser[usize::from(nr / 32)].read() & mask) == mask }
|
||||
}
|
||||
|
||||
/// Checks if `interrupt` is pending
|
||||
#[inline]
|
||||
pub fn is_pending<I>(interrupt: I) -> bool
|
||||
where
|
||||
I: InterruptNumber,
|
||||
{
|
||||
let nr = interrupt.number();
|
||||
let mask = 1 << (nr % 32);
|
||||
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
unsafe { ((*Self::PTR).ispr[usize::from(nr / 32)].read() & mask) == mask }
|
||||
}
|
||||
|
||||
/// Forces `interrupt` into pending state
|
||||
#[inline]
|
||||
pub fn pend<I>(interrupt: I)
|
||||
where
|
||||
I: InterruptNumber,
|
||||
{
|
||||
let nr = interrupt.number();
|
||||
|
||||
// NOTE(unsafe) atomic stateless write; ICPR doesn't store any state
|
||||
unsafe { (*Self::PTR).ispr[usize::from(nr / 32)].write(1 << (nr % 32)) }
|
||||
}
|
||||
|
||||
/// Sets the "priority" of `interrupt` to `prio`
|
||||
///
|
||||
/// *NOTE* See [`get_priority`](struct.NVIC.html#method.get_priority) method for an explanation
|
||||
/// of how NVIC priorities work.
|
||||
///
|
||||
/// On ARMv6-M, updating an interrupt priority requires a read-modify-write operation. On
|
||||
/// ARMv7-M, the operation is performed in a single atomic write operation.
|
||||
///
|
||||
/// # Unsafety
|
||||
///
|
||||
/// Changing priority levels can break priority-based critical sections (see
|
||||
/// [`register::basepri`](crate::register::basepri)) and compromise memory safety.
|
||||
#[inline]
|
||||
pub unsafe fn set_priority<I>(&mut self, interrupt: I, prio: u8)
|
||||
where
|
||||
I: InterruptNumber,
|
||||
{
|
||||
#[cfg(not(armv6m))]
|
||||
{
|
||||
let nr = interrupt.number();
|
||||
self.ipr[usize::from(nr)].write(prio)
|
||||
}
|
||||
|
||||
#[cfg(armv6m)]
|
||||
{
|
||||
self.ipr[Self::ipr_index(interrupt)].modify(|value| {
|
||||
let mask = 0x0000_00ff << Self::ipr_shift(interrupt);
|
||||
let prio = u32::from(prio) << Self::ipr_shift(interrupt);
|
||||
|
||||
(value & !mask) | prio
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Clears `interrupt`'s pending state
|
||||
#[inline]
|
||||
pub fn unpend<I>(interrupt: I)
|
||||
where
|
||||
I: InterruptNumber,
|
||||
{
|
||||
let nr = interrupt.number();
|
||||
|
||||
// NOTE(unsafe) atomic stateless write; ICPR doesn't store any state
|
||||
unsafe { (*Self::PTR).icpr[usize::from(nr / 32)].write(1 << (nr % 32)) }
|
||||
}
|
||||
|
||||
#[cfg(armv6m)]
|
||||
#[inline]
|
||||
fn ipr_index<I>(interrupt: I) -> usize
|
||||
where
|
||||
I: InterruptNumber,
|
||||
{
|
||||
usize::from(interrupt.number()) / 4
|
||||
}
|
||||
|
||||
#[cfg(armv6m)]
|
||||
#[inline]
|
||||
fn ipr_shift<I>(interrupt: I) -> usize
|
||||
where
|
||||
I: InterruptNumber,
|
||||
{
|
||||
(usize::from(interrupt.number()) % 4) * 8
|
||||
}
|
||||
}
|
||||
+243
@@ -0,0 +1,243 @@
|
||||
//! Security Attribution Unit
|
||||
//!
|
||||
//! *NOTE* Available only on Armv8-M and Armv8.1-M, for the following Rust target triples:
|
||||
//! * `thumbv8m.base-none-eabi`
|
||||
//! * `thumbv8m.main-none-eabi`
|
||||
//! * `thumbv8m.main-none-eabihf`
|
||||
//!
|
||||
//! For reference please check the section B8.3 of the Armv8-M Architecture Reference Manual.
|
||||
|
||||
use crate::interrupt;
|
||||
use crate::peripheral::SAU;
|
||||
use bitfield::bitfield;
|
||||
use volatile_register::{RO, RW};
|
||||
|
||||
/// Register block
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
/// Control Register
|
||||
pub ctrl: RW<Ctrl>,
|
||||
/// Type Register
|
||||
pub _type: RO<Type>,
|
||||
/// Region Number Register
|
||||
pub rnr: RW<Rnr>,
|
||||
/// Region Base Address Register
|
||||
pub rbar: RW<Rbar>,
|
||||
/// Region Limit Address Register
|
||||
pub rlar: RW<Rlar>,
|
||||
/// Secure Fault Status Register
|
||||
pub sfsr: RO<Sfsr>,
|
||||
/// Secure Fault Address Register
|
||||
pub sfar: RO<Sfar>,
|
||||
}
|
||||
|
||||
bitfield! {
|
||||
/// Control Register description
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Ctrl(u32);
|
||||
get_enable, set_enable: 0;
|
||||
get_allns, set_allns: 1;
|
||||
}
|
||||
|
||||
bitfield! {
|
||||
/// Type Register description
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Type(u32);
|
||||
u8;
|
||||
sregion, _: 7, 0;
|
||||
}
|
||||
|
||||
bitfield! {
|
||||
/// Region Number Register description
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Rnr(u32);
|
||||
u8;
|
||||
get_region, set_region: 7, 0;
|
||||
}
|
||||
|
||||
bitfield! {
|
||||
/// Region Base Address Register description
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Rbar(u32);
|
||||
u32;
|
||||
get_baddr, set_baddr: 31, 5;
|
||||
}
|
||||
|
||||
bitfield! {
|
||||
/// Region Limit Address Register description
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Rlar(u32);
|
||||
u32;
|
||||
get_laddr, set_laddr: 31, 5;
|
||||
get_nsc, set_nsc: 1;
|
||||
get_enable, set_enable: 0;
|
||||
}
|
||||
|
||||
bitfield! {
|
||||
/// Secure Fault Status Register description
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Sfsr(u32);
|
||||
invep, _: 0;
|
||||
invis, _: 1;
|
||||
inver, _: 2;
|
||||
auviol, _: 3;
|
||||
invtran, _: 4;
|
||||
lsperr, _: 5;
|
||||
sfarvalid, _: 6;
|
||||
lserr, _: 7;
|
||||
}
|
||||
|
||||
bitfield! {
|
||||
/// Secure Fault Address Register description
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Sfar(u32);
|
||||
u32;
|
||||
address, _: 31, 0;
|
||||
}
|
||||
|
||||
/// Possible attribute of a SAU region.
|
||||
#[derive(Debug)]
|
||||
pub enum SauRegionAttribute {
|
||||
/// SAU region is Secure
|
||||
Secure,
|
||||
/// SAU region is Non-Secure Callable
|
||||
NonSecureCallable,
|
||||
/// SAU region is Non-Secure
|
||||
NonSecure,
|
||||
}
|
||||
|
||||
/// Description of a SAU region.
|
||||
#[derive(Debug)]
|
||||
pub struct SauRegion {
|
||||
/// First address of the region, its 5 least significant bits must be set to zero.
|
||||
pub base_address: u32,
|
||||
/// Last address of the region, its 5 least significant bits must be set to one.
|
||||
pub limit_address: u32,
|
||||
/// Attribute of the region.
|
||||
pub attribute: SauRegionAttribute,
|
||||
}
|
||||
|
||||
/// Possible error values returned by the SAU methods.
|
||||
#[derive(Debug)]
|
||||
pub enum SauError {
|
||||
/// The region number parameter to set or get a region must be between 0 and
|
||||
/// region_numbers() - 1.
|
||||
RegionNumberTooBig,
|
||||
/// Bits 0 to 4 of the base address of a SAU region must be set to zero.
|
||||
WrongBaseAddress,
|
||||
/// Bits 0 to 4 of the limit address of a SAU region must be set to one.
|
||||
WrongLimitAddress,
|
||||
}
|
||||
|
||||
impl SAU {
|
||||
/// Get the number of implemented SAU regions.
|
||||
#[inline]
|
||||
pub fn region_numbers(&self) -> u8 {
|
||||
self._type.read().sregion()
|
||||
}
|
||||
|
||||
/// Enable the SAU.
|
||||
#[inline]
|
||||
pub fn enable(&mut self) {
|
||||
unsafe {
|
||||
self.ctrl.modify(|mut ctrl| {
|
||||
ctrl.set_enable(true);
|
||||
ctrl
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Set a SAU region to a region number.
|
||||
/// SAU regions must be 32 bytes aligned and their sizes must be a multiple of 32 bytes. It
|
||||
/// means that the 5 least significant bits of the base address of a SAU region must be set to
|
||||
/// zero and the 5 least significant bits of the limit address must be set to one.
|
||||
/// The region number must be valid.
|
||||
/// This function is executed under a critical section to prevent having inconsistent results.
|
||||
#[inline]
|
||||
pub fn set_region(&mut self, region_number: u8, region: SauRegion) -> Result<(), SauError> {
|
||||
interrupt::free(|_| {
|
||||
let base_address = region.base_address;
|
||||
let limit_address = region.limit_address;
|
||||
let attribute = region.attribute;
|
||||
|
||||
if region_number >= self.region_numbers() {
|
||||
Err(SauError::RegionNumberTooBig)
|
||||
} else if base_address & 0x1F != 0 {
|
||||
Err(SauError::WrongBaseAddress)
|
||||
} else if limit_address & 0x1F != 0x1F {
|
||||
Err(SauError::WrongLimitAddress)
|
||||
} else {
|
||||
// All fields of these registers are going to be modified so we don't need to read them
|
||||
// before.
|
||||
let mut rnr = Rnr(0);
|
||||
let mut rbar = Rbar(0);
|
||||
let mut rlar = Rlar(0);
|
||||
|
||||
rnr.set_region(region_number);
|
||||
rbar.set_baddr(base_address >> 5);
|
||||
rlar.set_laddr(limit_address >> 5);
|
||||
|
||||
match attribute {
|
||||
SauRegionAttribute::Secure => {
|
||||
rlar.set_nsc(false);
|
||||
rlar.set_enable(false);
|
||||
}
|
||||
SauRegionAttribute::NonSecureCallable => {
|
||||
rlar.set_nsc(true);
|
||||
rlar.set_enable(true);
|
||||
}
|
||||
SauRegionAttribute::NonSecure => {
|
||||
rlar.set_nsc(false);
|
||||
rlar.set_enable(true);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
self.rnr.write(rnr);
|
||||
self.rbar.write(rbar);
|
||||
self.rlar.write(rlar);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Get a region from the SAU.
|
||||
/// The region number must be valid.
|
||||
/// This function is executed under a critical section to prevent having inconsistent results.
|
||||
#[inline]
|
||||
pub fn get_region(&mut self, region_number: u8) -> Result<SauRegion, SauError> {
|
||||
interrupt::free(|_| {
|
||||
if region_number >= self.region_numbers() {
|
||||
Err(SauError::RegionNumberTooBig)
|
||||
} else {
|
||||
unsafe {
|
||||
self.rnr.write(Rnr(region_number.into()));
|
||||
}
|
||||
|
||||
let rbar = self.rbar.read();
|
||||
let rlar = self.rlar.read();
|
||||
|
||||
let attribute = match (rlar.get_enable(), rlar.get_nsc()) {
|
||||
(false, _) => SauRegionAttribute::Secure,
|
||||
(true, false) => SauRegionAttribute::NonSecure,
|
||||
(true, true) => SauRegionAttribute::NonSecureCallable,
|
||||
};
|
||||
|
||||
Ok(SauRegion {
|
||||
base_address: rbar.get_baddr() << 5,
|
||||
limit_address: (rlar.get_laddr() << 5) | 0x1F,
|
||||
attribute,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+1109
File diff suppressed because it is too large.
Load diff
+185
@@ -0,0 +1,185 @@
|
||||
//! SysTick: System Timer
|
||||
|
||||
use volatile_register::{RO, RW};
|
||||
|
||||
use crate::peripheral::SYST;
|
||||
|
||||
/// Register block
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
/// Control and Status
|
||||
pub csr: RW<u32>,
|
||||
/// Reload Value
|
||||
pub rvr: RW<u32>,
|
||||
/// Current Value
|
||||
pub cvr: RW<u32>,
|
||||
/// Calibration Value
|
||||
pub calib: RO<u32>,
|
||||
}
|
||||
|
||||
/// SysTick clock source
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum SystClkSource {
|
||||
/// Core-provided clock
|
||||
Core,
|
||||
/// External reference clock
|
||||
External,
|
||||
}
|
||||
|
||||
const SYST_COUNTER_MASK: u32 = 0x00ff_ffff;
|
||||
|
||||
const SYST_CSR_ENABLE: u32 = 1 << 0;
|
||||
const SYST_CSR_TICKINT: u32 = 1 << 1;
|
||||
const SYST_CSR_CLKSOURCE: u32 = 1 << 2;
|
||||
const SYST_CSR_COUNTFLAG: u32 = 1 << 16;
|
||||
|
||||
const SYST_CALIB_SKEW: u32 = 1 << 30;
|
||||
const SYST_CALIB_NOREF: u32 = 1 << 31;
|
||||
|
||||
impl SYST {
|
||||
/// Clears current value to 0
|
||||
///
|
||||
/// After calling `clear_current()`, the next call to `has_wrapped()` will return `false`.
|
||||
#[inline]
|
||||
pub fn clear_current(&mut self) {
|
||||
unsafe { self.cvr.write(0) }
|
||||
}
|
||||
|
||||
/// Disables counter
|
||||
#[inline]
|
||||
pub fn disable_counter(&mut self) {
|
||||
unsafe { self.csr.modify(|v| v & !SYST_CSR_ENABLE) }
|
||||
}
|
||||
|
||||
/// Disables SysTick interrupt
|
||||
#[inline]
|
||||
pub fn disable_interrupt(&mut self) {
|
||||
unsafe { self.csr.modify(|v| v & !SYST_CSR_TICKINT) }
|
||||
}
|
||||
|
||||
/// Enables counter
|
||||
///
|
||||
/// *NOTE* The reference manual indicates that:
|
||||
///
|
||||
/// "The SysTick counter reload and current value are undefined at reset, the correct
|
||||
/// initialization sequence for the SysTick counter is:
|
||||
///
|
||||
/// - Program reload value
|
||||
/// - Clear current value
|
||||
/// - Program Control and Status register"
|
||||
///
|
||||
/// The sequence translates to `self.set_reload(x); self.clear_current(); self.enable_counter()`
|
||||
#[inline]
|
||||
pub fn enable_counter(&mut self) {
|
||||
unsafe { self.csr.modify(|v| v | SYST_CSR_ENABLE) }
|
||||
}
|
||||
|
||||
/// Enables SysTick interrupt
|
||||
#[inline]
|
||||
pub fn enable_interrupt(&mut self) {
|
||||
unsafe { self.csr.modify(|v| v | SYST_CSR_TICKINT) }
|
||||
}
|
||||
|
||||
/// Gets clock source
|
||||
///
|
||||
/// *NOTE* This takes `&mut self` because the read operation is side effectful and can clear the
|
||||
/// bit that indicates that the timer has wrapped (cf. `SYST.has_wrapped`)
|
||||
#[inline]
|
||||
pub fn get_clock_source(&mut self) -> SystClkSource {
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
if self.csr.read() & SYST_CSR_CLKSOURCE != 0 {
|
||||
SystClkSource::Core
|
||||
} else {
|
||||
SystClkSource::External
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets current value
|
||||
#[inline]
|
||||
pub fn get_current() -> u32 {
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
unsafe { (*Self::PTR).cvr.read() }
|
||||
}
|
||||
|
||||
/// Gets reload value
|
||||
#[inline]
|
||||
pub fn get_reload() -> u32 {
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
unsafe { (*Self::PTR).rvr.read() }
|
||||
}
|
||||
|
||||
/// Returns the reload value with which the counter would wrap once per 10
|
||||
/// ms
|
||||
///
|
||||
/// Returns `0` if the value is not known (e.g. because the clock can
|
||||
/// change dynamically).
|
||||
#[inline]
|
||||
pub fn get_ticks_per_10ms() -> u32 {
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
unsafe { (*Self::PTR).calib.read() & SYST_COUNTER_MASK }
|
||||
}
|
||||
|
||||
/// Checks if an external reference clock is available
|
||||
#[inline]
|
||||
pub fn has_reference_clock() -> bool {
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
unsafe { (*Self::PTR).calib.read() & SYST_CALIB_NOREF == 0 }
|
||||
}
|
||||
|
||||
/// Checks if the counter wrapped (underflowed) since the last check
|
||||
///
|
||||
/// *NOTE* This takes `&mut self` because the read operation is side effectful and will clear
|
||||
/// the bit of the read register.
|
||||
#[inline]
|
||||
pub fn has_wrapped(&mut self) -> bool {
|
||||
self.csr.read() & SYST_CSR_COUNTFLAG != 0
|
||||
}
|
||||
|
||||
/// Checks if counter is enabled
|
||||
///
|
||||
/// *NOTE* This takes `&mut self` because the read operation is side effectful and can clear the
|
||||
/// bit that indicates that the timer has wrapped (cf. `SYST.has_wrapped`)
|
||||
#[inline]
|
||||
pub fn is_counter_enabled(&mut self) -> bool {
|
||||
self.csr.read() & SYST_CSR_ENABLE != 0
|
||||
}
|
||||
|
||||
/// Checks if SysTick interrupt is enabled
|
||||
///
|
||||
/// *NOTE* This takes `&mut self` because the read operation is side effectful and can clear the
|
||||
/// bit that indicates that the timer has wrapped (cf. `SYST.has_wrapped`)
|
||||
#[inline]
|
||||
pub fn is_interrupt_enabled(&mut self) -> bool {
|
||||
self.csr.read() & SYST_CSR_TICKINT != 0
|
||||
}
|
||||
|
||||
/// Checks if the calibration value is precise
|
||||
///
|
||||
/// Returns `false` if using the reload value returned by
|
||||
/// `get_ticks_per_10ms()` may result in a period significantly deviating
|
||||
/// from 10 ms.
|
||||
#[inline]
|
||||
pub fn is_precise() -> bool {
|
||||
// NOTE(unsafe) atomic read with no side effects
|
||||
unsafe { (*Self::PTR).calib.read() & SYST_CALIB_SKEW == 0 }
|
||||
}
|
||||
|
||||
/// Sets clock source
|
||||
#[inline]
|
||||
pub fn set_clock_source(&mut self, clk_source: SystClkSource) {
|
||||
match clk_source {
|
||||
SystClkSource::External => unsafe { self.csr.modify(|v| v & !SYST_CSR_CLKSOURCE) },
|
||||
SystClkSource::Core => unsafe { self.csr.modify(|v| v | SYST_CSR_CLKSOURCE) },
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets reload value
|
||||
///
|
||||
/// Valid values are between `1` and `0x00ffffff`.
|
||||
///
|
||||
/// *NOTE* To make the timer wrap every `N` ticks set the reload value to `N - 1`
|
||||
#[inline]
|
||||
pub fn set_reload(&mut self, value: u32) {
|
||||
unsafe { self.rvr.write(value) }
|
||||
}
|
||||
}
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
#[test]
|
||||
fn cpuid() {
|
||||
let cpuid = unsafe { &*crate::peripheral::CPUID::PTR };
|
||||
|
||||
assert_eq!(address(&cpuid.base), 0xE000_ED00);
|
||||
assert_eq!(address(&cpuid.pfr), 0xE000_ED40);
|
||||
assert_eq!(address(&cpuid.dfr), 0xE000_ED48);
|
||||
assert_eq!(address(&cpuid.afr), 0xE000_ED4C);
|
||||
assert_eq!(address(&cpuid.mmfr), 0xE000_ED50);
|
||||
assert_eq!(address(&cpuid.isar), 0xE000_ED60);
|
||||
assert_eq!(address(&cpuid.clidr), 0xE000_ED78);
|
||||
assert_eq!(address(&cpuid.ctr), 0xE000_ED7C);
|
||||
assert_eq!(address(&cpuid.ccsidr), 0xE000_ED80);
|
||||
assert_eq!(address(&cpuid.csselr), 0xE000_ED84);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dcb() {
|
||||
let dcb = unsafe { &*crate::peripheral::DCB::PTR };
|
||||
|
||||
assert_eq!(address(&dcb.dhcsr), 0xE000_EDF0);
|
||||
assert_eq!(address(&dcb.dcrsr), 0xE000_EDF4);
|
||||
assert_eq!(address(&dcb.dcrdr), 0xE000_EDF8);
|
||||
assert_eq!(address(&dcb.demcr), 0xE000_EDFC);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dwt() {
|
||||
let dwt = unsafe { &*crate::peripheral::DWT::PTR };
|
||||
|
||||
assert_eq!(address(&dwt.ctrl), 0xE000_1000);
|
||||
#[cfg(not(armv6m))]
|
||||
assert_eq!(address(&dwt.cyccnt), 0xE000_1004);
|
||||
#[cfg(not(armv6m))]
|
||||
assert_eq!(address(&dwt.cpicnt), 0xE000_1008);
|
||||
#[cfg(not(armv6m))]
|
||||
assert_eq!(address(&dwt.exccnt), 0xE000_100C);
|
||||
#[cfg(not(armv6m))]
|
||||
assert_eq!(address(&dwt.sleepcnt), 0xE000_1010);
|
||||
#[cfg(not(armv6m))]
|
||||
assert_eq!(address(&dwt.lsucnt), 0xE000_1014);
|
||||
#[cfg(not(armv6m))]
|
||||
assert_eq!(address(&dwt.foldcnt), 0xE000_1018);
|
||||
assert_eq!(address(&dwt.pcsr), 0xE000_101C);
|
||||
assert_eq!(address(&dwt.c[0].comp), 0xE000_1020);
|
||||
assert_eq!(address(&dwt.c[0].mask), 0xE000_1024);
|
||||
assert_eq!(address(&dwt.c[0].function), 0xE000_1028);
|
||||
assert_eq!(address(&dwt.c[1].comp), 0xE000_1030);
|
||||
assert_eq!(address(&dwt.c[1].mask), 0xE000_1034);
|
||||
assert_eq!(address(&dwt.c[1].function), 0xE000_1038);
|
||||
#[cfg(not(armv6m))]
|
||||
assert_eq!(address(&dwt.lar), 0xE000_1FB0);
|
||||
#[cfg(not(armv6m))]
|
||||
assert_eq!(address(&dwt.lsr), 0xE000_1FB4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fpb() {
|
||||
let fpb = unsafe { &*crate::peripheral::FPB::PTR };
|
||||
|
||||
assert_eq!(address(&fpb.ctrl), 0xE000_2000);
|
||||
assert_eq!(address(&fpb.remap), 0xE000_2004);
|
||||
assert_eq!(address(&fpb.comp), 0xE000_2008);
|
||||
assert_eq!(address(&fpb.comp[1]), 0xE000_200C);
|
||||
assert_eq!(address(&fpb.lar), 0xE000_2FB0);
|
||||
assert_eq!(address(&fpb.lsr), 0xE000_2FB4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fpu() {
|
||||
let fpu = unsafe { &*crate::peripheral::FPU::PTR };
|
||||
|
||||
assert_eq!(address(&fpu.fpccr), 0xE000_EF34);
|
||||
assert_eq!(address(&fpu.fpcar), 0xE000_EF38);
|
||||
assert_eq!(address(&fpu.fpdscr), 0xE000_EF3C);
|
||||
assert_eq!(address(&fpu.mvfr), 0xE000_EF40);
|
||||
assert_eq!(address(&fpu.mvfr[1]), 0xE000_EF44);
|
||||
assert_eq!(address(&fpu.mvfr[2]), 0xE000_EF48);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn itm() {
|
||||
let itm = unsafe { &*crate::peripheral::ITM::PTR };
|
||||
|
||||
assert_eq!(address(&itm.stim), 0xE000_0000);
|
||||
assert_eq!(address(&itm.ter), 0xE000_0E00);
|
||||
assert_eq!(address(&itm.tpr), 0xE000_0E40);
|
||||
assert_eq!(address(&itm.tcr), 0xE000_0E80);
|
||||
assert_eq!(address(&itm.lar), 0xE000_0FB0);
|
||||
assert_eq!(address(&itm.lsr), 0xE000_0FB4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mpu() {
|
||||
let mpu = unsafe { &*crate::peripheral::MPU::PTR };
|
||||
|
||||
assert_eq!(address(&mpu._type), 0xE000ED90);
|
||||
assert_eq!(address(&mpu.ctrl), 0xE000ED94);
|
||||
assert_eq!(address(&mpu.rnr), 0xE000ED98);
|
||||
assert_eq!(address(&mpu.rbar), 0xE000ED9C);
|
||||
assert_eq!(address(&mpu.rasr), 0xE000EDA0);
|
||||
assert_eq!(address(&mpu.rbar_a1), 0xE000EDA4);
|
||||
assert_eq!(address(&mpu.rasr_a1), 0xE000EDA8);
|
||||
assert_eq!(address(&mpu.rbar_a2), 0xE000EDAC);
|
||||
assert_eq!(address(&mpu.rasr_a2), 0xE000EDB0);
|
||||
assert_eq!(address(&mpu.rbar_a3), 0xE000EDB4);
|
||||
assert_eq!(address(&mpu.rasr_a3), 0xE000EDB8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nvic() {
|
||||
let nvic = unsafe { &*crate::peripheral::NVIC::PTR };
|
||||
|
||||
assert_eq!(address(&nvic.iser), 0xE000E100);
|
||||
assert_eq!(address(&nvic.icer), 0xE000E180);
|
||||
assert_eq!(address(&nvic.ispr), 0xE000E200);
|
||||
assert_eq!(address(&nvic.icpr), 0xE000E280);
|
||||
assert_eq!(address(&nvic.iabr), 0xE000E300);
|
||||
assert_eq!(address(&nvic.ipr), 0xE000E400);
|
||||
#[cfg(not(armv6m))]
|
||||
assert_eq!(address(&nvic.stir), 0xE000EF00);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scb() {
|
||||
let scb = unsafe { &*crate::peripheral::SCB::PTR };
|
||||
|
||||
assert_eq!(address(&scb.icsr), 0xE000_ED04);
|
||||
assert_eq!(address(&scb.vtor), 0xE000_ED08);
|
||||
assert_eq!(address(&scb.aircr), 0xE000_ED0C);
|
||||
assert_eq!(address(&scb.scr), 0xE000_ED10);
|
||||
assert_eq!(address(&scb.ccr), 0xE000_ED14);
|
||||
assert_eq!(address(&scb.shpr), 0xE000_ED18);
|
||||
assert_eq!(address(&scb.shcsr), 0xE000_ED24);
|
||||
assert_eq!(address(&scb.cfsr), 0xE000_ED28);
|
||||
assert_eq!(address(&scb.hfsr), 0xE000_ED2C);
|
||||
assert_eq!(address(&scb.dfsr), 0xE000_ED30);
|
||||
assert_eq!(address(&scb.mmfar), 0xE000_ED34);
|
||||
assert_eq!(address(&scb.bfar), 0xE000_ED38);
|
||||
assert_eq!(address(&scb.afsr), 0xE000_ED3C);
|
||||
assert_eq!(address(&scb.cpacr), 0xE000_ED88);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn syst() {
|
||||
let syst = unsafe { &*crate::peripheral::SYST::PTR };
|
||||
|
||||
assert_eq!(address(&syst.csr), 0xE000_E010);
|
||||
assert_eq!(address(&syst.rvr), 0xE000_E014);
|
||||
assert_eq!(address(&syst.cvr), 0xE000_E018);
|
||||
assert_eq!(address(&syst.calib), 0xE000_E01C);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tpiu() {
|
||||
let tpiu = unsafe { &*crate::peripheral::TPIU::PTR };
|
||||
|
||||
assert_eq!(address(&tpiu.sspsr), 0xE004_0000);
|
||||
assert_eq!(address(&tpiu.cspsr), 0xE004_0004);
|
||||
assert_eq!(address(&tpiu.acpr), 0xE004_0010);
|
||||
assert_eq!(address(&tpiu.sppr), 0xE004_00F0);
|
||||
assert_eq!(address(&tpiu.ffcr), 0xE004_0304);
|
||||
assert_eq!(address(&tpiu.lar), 0xE004_0FB0);
|
||||
assert_eq!(address(&tpiu.lsr), 0xE004_0FB4);
|
||||
assert_eq!(address(&tpiu._type), 0xE004_0FC8);
|
||||
}
|
||||
|
||||
fn address<T>(r: *const T) -> usize {
|
||||
r as usize
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
//! Trace Port Interface Unit;
|
||||
//!
|
||||
//! *NOTE* Not available on Armv6-M.
|
||||
|
||||
use volatile_register::{RO, RW, WO};
|
||||
|
||||
/// Register block
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
/// Supported Parallel Port Sizes
|
||||
pub sspsr: RO<u32>,
|
||||
/// Current Parallel Port Size
|
||||
pub cspsr: RW<u32>,
|
||||
reserved0: [u32; 2],
|
||||
/// Asynchronous Clock Prescaler
|
||||
pub acpr: RW<u32>,
|
||||
reserved1: [u32; 55],
|
||||
/// Selected Pin Control
|
||||
pub sppr: RW<u32>,
|
||||
reserved2: [u32; 132],
|
||||
/// Formatter and Flush Control
|
||||
pub ffcr: RW<u32>,
|
||||
reserved3: [u32; 810],
|
||||
/// Lock Access
|
||||
pub lar: WO<u32>,
|
||||
/// Lock Status
|
||||
pub lsr: RO<u32>,
|
||||
reserved4: [u32; 4],
|
||||
/// TPIU Type
|
||||
pub _type: RO<u32>,
|
||||
}
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
//! Prelude
|
||||
|
||||
pub use embedded_hal::prelude::*;
|
||||
Vendored
+54
@@ -0,0 +1,54 @@
|
||||
//! Application Program Status Register
|
||||
|
||||
/// Application Program Status Register
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Apsr {
|
||||
bits: u32,
|
||||
}
|
||||
|
||||
impl Apsr {
|
||||
/// Returns the contents of the register as raw bits
|
||||
#[inline]
|
||||
pub fn bits(self) -> u32 {
|
||||
self.bits
|
||||
}
|
||||
|
||||
/// DSP overflow and saturation flag
|
||||
#[inline]
|
||||
pub fn q(self) -> bool {
|
||||
self.bits & (1 << 27) == (1 << 27)
|
||||
}
|
||||
|
||||
/// Overflow flag
|
||||
#[inline]
|
||||
pub fn v(self) -> bool {
|
||||
self.bits & (1 << 28) == (1 << 28)
|
||||
}
|
||||
|
||||
/// Carry or borrow flag
|
||||
#[inline]
|
||||
pub fn c(self) -> bool {
|
||||
self.bits & (1 << 29) == (1 << 29)
|
||||
}
|
||||
|
||||
/// Zero flag
|
||||
#[inline]
|
||||
pub fn z(self) -> bool {
|
||||
self.bits & (1 << 30) == (1 << 30)
|
||||
}
|
||||
|
||||
/// Negative flag
|
||||
#[inline]
|
||||
pub fn n(self) -> bool {
|
||||
self.bits & (1 << 31) == (1 << 31)
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads the CPU register
|
||||
///
|
||||
/// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature.
|
||||
#[inline]
|
||||
pub fn read() -> Apsr {
|
||||
let bits: u32 = call_asm!(__apsr_r() -> u32);
|
||||
Apsr { bits }
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
//! Base Priority Mask Register
|
||||
|
||||
/// Reads the CPU register
|
||||
#[inline]
|
||||
pub fn read() -> u8 {
|
||||
call_asm!(__basepri_r() -> u8)
|
||||
}
|
||||
|
||||
/// Writes to the CPU register
|
||||
///
|
||||
/// **IMPORTANT** If you are using a Cortex-M7 device with revision r0p1 you MUST enable the
|
||||
/// `cm7-r0p1` Cargo feature or this function WILL misbehave.
|
||||
#[inline]
|
||||
pub unsafe fn write(basepri: u8) {
|
||||
#[cfg(feature = "cm7-r0p1")]
|
||||
{
|
||||
call_asm!(__basepri_w_cm7_r0p1(basepri: u8));
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "cm7-r0p1"))]
|
||||
{
|
||||
call_asm!(__basepri_w(basepri: u8));
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
//! Base Priority Mask Register (conditional write)
|
||||
|
||||
/// Writes to BASEPRI *if*
|
||||
///
|
||||
/// - `basepri != 0` AND `basepri::read() == 0`, OR
|
||||
/// - `basepri != 0` AND `basepri < basepri::read()`
|
||||
///
|
||||
/// **IMPORTANT** If you are using a Cortex-M7 device with revision r0p1 you MUST enable the
|
||||
/// `cm7-r0p1` Cargo feature or this function WILL misbehave.
|
||||
#[inline]
|
||||
pub fn write(basepri: u8) {
|
||||
#[cfg(feature = "cm7-r0p1")]
|
||||
{
|
||||
call_asm!(__basepri_max_cm7_r0p1(basepri: u8));
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "cm7-r0p1"))]
|
||||
{
|
||||
call_asm!(__basepri_max(basepri: u8));
|
||||
}
|
||||
}
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
//! Control register
|
||||
|
||||
/// Control register
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Control {
|
||||
bits: u32,
|
||||
}
|
||||
|
||||
impl Control {
|
||||
/// Creates a `Control` value from raw bits.
|
||||
#[inline]
|
||||
pub fn from_bits(bits: u32) -> Self {
|
||||
Self { bits }
|
||||
}
|
||||
|
||||
/// Returns the contents of the register as raw bits
|
||||
#[inline]
|
||||
pub fn bits(self) -> u32 {
|
||||
self.bits
|
||||
}
|
||||
|
||||
/// Thread mode privilege level
|
||||
#[inline]
|
||||
pub fn npriv(self) -> Npriv {
|
||||
if self.bits & (1 << 0) == (1 << 0) {
|
||||
Npriv::Unprivileged
|
||||
} else {
|
||||
Npriv::Privileged
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the thread mode privilege level value (nPRIV).
|
||||
#[inline]
|
||||
pub fn set_npriv(&mut self, npriv: Npriv) {
|
||||
let mask = 1 << 0;
|
||||
match npriv {
|
||||
Npriv::Unprivileged => self.bits |= mask,
|
||||
Npriv::Privileged => self.bits &= !mask,
|
||||
}
|
||||
}
|
||||
|
||||
/// Currently active stack pointer
|
||||
#[inline]
|
||||
pub fn spsel(self) -> Spsel {
|
||||
if self.bits & (1 << 1) == (1 << 1) {
|
||||
Spsel::Psp
|
||||
} else {
|
||||
Spsel::Msp
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the SPSEL value.
|
||||
#[inline]
|
||||
pub fn set_spsel(&mut self, spsel: Spsel) {
|
||||
let mask = 1 << 1;
|
||||
match spsel {
|
||||
Spsel::Psp => self.bits |= mask,
|
||||
Spsel::Msp => self.bits &= !mask,
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether context floating-point is currently active
|
||||
#[inline]
|
||||
pub fn fpca(self) -> Fpca {
|
||||
if self.bits & (1 << 2) == (1 << 2) {
|
||||
Fpca::Active
|
||||
} else {
|
||||
Fpca::NotActive
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the FPCA value.
|
||||
#[inline]
|
||||
pub fn set_fpca(&mut self, fpca: Fpca) {
|
||||
let mask = 1 << 2;
|
||||
match fpca {
|
||||
Fpca::Active => self.bits |= mask,
|
||||
Fpca::NotActive => self.bits &= !mask,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Thread mode privilege level
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Npriv {
|
||||
/// Privileged
|
||||
Privileged,
|
||||
/// Unprivileged
|
||||
Unprivileged,
|
||||
}
|
||||
|
||||
impl Npriv {
|
||||
/// Is in privileged thread mode?
|
||||
#[inline]
|
||||
pub fn is_privileged(self) -> bool {
|
||||
self == Npriv::Privileged
|
||||
}
|
||||
|
||||
/// Is in unprivileged thread mode?
|
||||
#[inline]
|
||||
pub fn is_unprivileged(self) -> bool {
|
||||
self == Npriv::Unprivileged
|
||||
}
|
||||
}
|
||||
|
||||
/// Currently active stack pointer
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Spsel {
|
||||
/// MSP is the current stack pointer
|
||||
Msp,
|
||||
/// PSP is the current stack pointer
|
||||
Psp,
|
||||
}
|
||||
|
||||
impl Spsel {
|
||||
/// Is MSP the current stack pointer?
|
||||
#[inline]
|
||||
pub fn is_msp(self) -> bool {
|
||||
self == Spsel::Msp
|
||||
}
|
||||
|
||||
/// Is PSP the current stack pointer?
|
||||
#[inline]
|
||||
pub fn is_psp(self) -> bool {
|
||||
self == Spsel::Psp
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether context floating-point is currently active
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Fpca {
|
||||
/// Floating-point context active.
|
||||
Active,
|
||||
/// No floating-point context active
|
||||
NotActive,
|
||||
}
|
||||
|
||||
impl Fpca {
|
||||
/// Is a floating-point context active?
|
||||
#[inline]
|
||||
pub fn is_active(self) -> bool {
|
||||
self == Fpca::Active
|
||||
}
|
||||
|
||||
/// Is a floating-point context not active?
|
||||
#[inline]
|
||||
pub fn is_not_active(self) -> bool {
|
||||
self == Fpca::NotActive
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads the CPU register
|
||||
#[inline]
|
||||
pub fn read() -> Control {
|
||||
let bits: u32 = call_asm!(__control_r() -> u32);
|
||||
Control { bits }
|
||||
}
|
||||
|
||||
/// Writes to the CPU register.
|
||||
#[inline]
|
||||
pub unsafe fn write(control: Control) {
|
||||
let control = control.bits();
|
||||
call_asm!(__control_w(control: u32));
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
//! Fault Mask Register
|
||||
|
||||
/// All exceptions are ...
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Faultmask {
|
||||
/// Active
|
||||
Active,
|
||||
/// Inactive, expect for NMI
|
||||
Inactive,
|
||||
}
|
||||
|
||||
impl Faultmask {
|
||||
/// All exceptions are active
|
||||
#[inline]
|
||||
pub fn is_active(self) -> bool {
|
||||
self == Faultmask::Active
|
||||
}
|
||||
|
||||
/// All exceptions, except for NMI, are inactive
|
||||
#[inline]
|
||||
pub fn is_inactive(self) -> bool {
|
||||
self == Faultmask::Inactive
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads the CPU register
|
||||
#[inline]
|
||||
pub fn read() -> Faultmask {
|
||||
let r: u32 = call_asm!(__faultmask_r() -> u32);
|
||||
if r & (1 << 0) == (1 << 0) {
|
||||
Faultmask::Inactive
|
||||
} else {
|
||||
Faultmask::Active
|
||||
}
|
||||
}
|
||||
+305
@@ -0,0 +1,305 @@
|
||||
//! Floating-point Status Control Register
|
||||
|
||||
/// Floating-point Status Control Register
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Fpscr {
|
||||
bits: u32,
|
||||
}
|
||||
|
||||
impl Fpscr {
|
||||
/// Creates a `Fspcr` value from raw bits.
|
||||
#[inline]
|
||||
pub fn from_bits(bits: u32) -> Self {
|
||||
Self { bits }
|
||||
}
|
||||
|
||||
/// Returns the contents of the register as raw bits
|
||||
#[inline]
|
||||
pub fn bits(self) -> u32 {
|
||||
self.bits
|
||||
}
|
||||
|
||||
/// Read the Negative condition code flag
|
||||
#[inline]
|
||||
pub fn n(self) -> bool {
|
||||
self.bits & (1 << 31) != 0
|
||||
}
|
||||
|
||||
/// Sets the Negative condition code flag
|
||||
#[inline]
|
||||
pub fn set_n(&mut self, n: bool) {
|
||||
let mask = 1 << 31;
|
||||
match n {
|
||||
true => self.bits |= mask,
|
||||
false => self.bits &= !mask,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the Zero condition code flag
|
||||
#[inline]
|
||||
pub fn z(self) -> bool {
|
||||
self.bits & (1 << 30) != 0
|
||||
}
|
||||
|
||||
/// Sets the Zero condition code flag
|
||||
#[inline]
|
||||
pub fn set_z(&mut self, z: bool) {
|
||||
let mask = 1 << 30;
|
||||
match z {
|
||||
true => self.bits |= mask,
|
||||
false => self.bits &= !mask,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the Carry condition code flag
|
||||
#[inline]
|
||||
pub fn c(self) -> bool {
|
||||
self.bits & (1 << 29) != 0
|
||||
}
|
||||
|
||||
/// Sets the Carry condition code flag
|
||||
#[inline]
|
||||
pub fn set_c(&mut self, c: bool) {
|
||||
let mask = 1 << 29;
|
||||
match c {
|
||||
true => self.bits |= mask,
|
||||
false => self.bits &= !mask,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the Overflow condition code flag
|
||||
#[inline]
|
||||
pub fn v(self) -> bool {
|
||||
self.bits & (1 << 28) != 0
|
||||
}
|
||||
|
||||
/// Sets the Zero condition code flag
|
||||
#[inline]
|
||||
pub fn set_v(&mut self, v: bool) {
|
||||
let mask = 1 << 28;
|
||||
match v {
|
||||
true => self.bits |= mask,
|
||||
false => self.bits &= !mask,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the Alternative Half Precision bit
|
||||
#[inline]
|
||||
pub fn ahp(self) -> bool {
|
||||
self.bits & (1 << 26) != 0
|
||||
}
|
||||
|
||||
/// Sets the Alternative Half Precision bit
|
||||
#[inline]
|
||||
pub fn set_ahp(&mut self, ahp: bool) {
|
||||
let mask = 1 << 26;
|
||||
match ahp {
|
||||
true => self.bits |= mask,
|
||||
false => self.bits &= !mask,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the Default NaN mode bit
|
||||
#[inline]
|
||||
pub fn dn(self) -> bool {
|
||||
self.bits & (1 << 25) != 0
|
||||
}
|
||||
|
||||
/// Sets the Default NaN mode bit
|
||||
#[inline]
|
||||
pub fn set_dn(&mut self, dn: bool) {
|
||||
let mask = 1 << 25;
|
||||
match dn {
|
||||
true => self.bits |= mask,
|
||||
false => self.bits &= !mask,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the Flush to Zero mode bit
|
||||
#[inline]
|
||||
pub fn fz(self) -> bool {
|
||||
self.bits & (1 << 24) != 0
|
||||
}
|
||||
|
||||
/// Sets the Flush to Zero mode bit
|
||||
#[inline]
|
||||
pub fn set_fz(&mut self, fz: bool) {
|
||||
let mask = 1 << 24;
|
||||
match fz {
|
||||
true => self.bits |= mask,
|
||||
false => self.bits &= !mask,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the Rounding Mode control field
|
||||
#[inline]
|
||||
pub fn rmode(self) -> RMode {
|
||||
match (self.bits & (3 << 22)) >> 22 {
|
||||
0 => RMode::Nearest,
|
||||
1 => RMode::PlusInfinity,
|
||||
2 => RMode::MinusInfinity,
|
||||
_ => RMode::Zero,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the Rounding Mode control field
|
||||
#[inline]
|
||||
pub fn set_rmode(&mut self, rmode: RMode) {
|
||||
let mask = 3 << 22;
|
||||
match rmode {
|
||||
RMode::Nearest => self.bits &= !mask,
|
||||
RMode::PlusInfinity => self.bits = (self.bits & !mask) | (1 << 22),
|
||||
RMode::MinusInfinity => self.bits = (self.bits & !mask) | (2 << 22),
|
||||
RMode::Zero => self.bits |= mask,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the Input Denormal cumulative exception bit
|
||||
#[inline]
|
||||
pub fn idc(self) -> bool {
|
||||
self.bits & (1 << 7) != 0
|
||||
}
|
||||
|
||||
/// Sets the Input Denormal cumulative exception bit
|
||||
#[inline]
|
||||
pub fn set_idc(&mut self, idc: bool) {
|
||||
let mask = 1 << 7;
|
||||
match idc {
|
||||
true => self.bits |= mask,
|
||||
false => self.bits &= !mask,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the Inexact cumulative exception bit
|
||||
#[inline]
|
||||
pub fn ixc(self) -> bool {
|
||||
self.bits & (1 << 4) != 0
|
||||
}
|
||||
|
||||
/// Sets the Inexact cumulative exception bit
|
||||
#[inline]
|
||||
pub fn set_ixc(&mut self, ixc: bool) {
|
||||
let mask = 1 << 4;
|
||||
match ixc {
|
||||
true => self.bits |= mask,
|
||||
false => self.bits &= !mask,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the Underflow cumulative exception bit
|
||||
#[inline]
|
||||
pub fn ufc(self) -> bool {
|
||||
self.bits & (1 << 3) != 0
|
||||
}
|
||||
|
||||
/// Sets the Underflow cumulative exception bit
|
||||
#[inline]
|
||||
pub fn set_ufc(&mut self, ufc: bool) {
|
||||
let mask = 1 << 3;
|
||||
match ufc {
|
||||
true => self.bits |= mask,
|
||||
false => self.bits &= !mask,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the Overflow cumulative exception bit
|
||||
#[inline]
|
||||
pub fn ofc(self) -> bool {
|
||||
self.bits & (1 << 2) != 0
|
||||
}
|
||||
|
||||
/// Sets the Overflow cumulative exception bit
|
||||
#[inline]
|
||||
pub fn set_ofc(&mut self, ofc: bool) {
|
||||
let mask = 1 << 2;
|
||||
match ofc {
|
||||
true => self.bits |= mask,
|
||||
false => self.bits &= !mask,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the Division by Zero cumulative exception bit
|
||||
#[inline]
|
||||
pub fn dzc(self) -> bool {
|
||||
self.bits & (1 << 1) != 0
|
||||
}
|
||||
|
||||
/// Sets the Division by Zero cumulative exception bit
|
||||
#[inline]
|
||||
pub fn set_dzc(&mut self, dzc: bool) {
|
||||
let mask = 1 << 1;
|
||||
match dzc {
|
||||
true => self.bits |= mask,
|
||||
false => self.bits &= !mask,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the Invalid Operation cumulative exception bit
|
||||
#[inline]
|
||||
pub fn ioc(self) -> bool {
|
||||
self.bits & (1 << 0) != 0
|
||||
}
|
||||
|
||||
/// Sets the Invalid Operation cumulative exception bit
|
||||
#[inline]
|
||||
pub fn set_ioc(&mut self, ioc: bool) {
|
||||
let mask = 1 << 0;
|
||||
match ioc {
|
||||
true => self.bits |= mask,
|
||||
false => self.bits &= !mask,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Rounding mode
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum RMode {
|
||||
/// Round to Nearest (RN) mode. This is the reset value.
|
||||
Nearest,
|
||||
/// Round towards Plus Infinity (RP) mode.
|
||||
PlusInfinity,
|
||||
/// Round towards Minus Infinity (RM) mode.
|
||||
MinusInfinity,
|
||||
/// Round towards Zero (RZ) mode.
|
||||
Zero,
|
||||
}
|
||||
|
||||
impl RMode {
|
||||
/// Is Nearest the current rounding mode?
|
||||
#[inline]
|
||||
pub fn is_nearest(self) -> bool {
|
||||
self == RMode::Nearest
|
||||
}
|
||||
|
||||
/// Is Plus Infinity the current rounding mode?
|
||||
#[inline]
|
||||
pub fn is_plus_infinity(self) -> bool {
|
||||
self == RMode::PlusInfinity
|
||||
}
|
||||
|
||||
/// Is Minus Infinity the current rounding mode?
|
||||
#[inline]
|
||||
pub fn is_minus_infinity(self) -> bool {
|
||||
self == RMode::MinusInfinity
|
||||
}
|
||||
|
||||
/// Is Zero the current rounding mode?
|
||||
#[inline]
|
||||
pub fn is_zero(self) -> bool {
|
||||
self == RMode::Zero
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the FPSCR register
|
||||
#[inline]
|
||||
pub fn read() -> Fpscr {
|
||||
let r: u32 = call_asm!(__fpscr_r() -> u32);
|
||||
Fpscr::from_bits(r)
|
||||
}
|
||||
|
||||
/// Set the value of the FPSCR register
|
||||
#[inline]
|
||||
pub unsafe fn write(fpscr: Fpscr) {
|
||||
let fpscr = fpscr.bits();
|
||||
call_asm!(__fpscr_w(fpscr: u32));
|
||||
}
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
//! Link register
|
||||
|
||||
/// Reads the CPU register
|
||||
///
|
||||
/// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature.
|
||||
#[inline]
|
||||
pub fn read() -> u32 {
|
||||
call_asm!(__lr_r() -> u32)
|
||||
}
|
||||
|
||||
/// Writes `bits` to the CPU register
|
||||
///
|
||||
/// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature.
|
||||
#[inline]
|
||||
pub unsafe fn write(bits: u32) {
|
||||
call_asm!(__lr_w(bits: u32));
|
||||
}
|
||||
Vendored
+68
@@ -0,0 +1,68 @@
|
||||
//! Processor core registers
|
||||
//!
|
||||
//! The following registers can only be accessed in PRIVILEGED mode:
|
||||
//!
|
||||
//! - BASEPRI
|
||||
//! - CONTROL
|
||||
//! - FAULTMASK
|
||||
//! - MSP
|
||||
//! - PRIMASK
|
||||
//!
|
||||
//! The rest of registers (see list below) can be accessed in either, PRIVILEGED
|
||||
//! or UNPRIVILEGED, mode.
|
||||
//!
|
||||
//! - APSR
|
||||
//! - LR
|
||||
//! - PC
|
||||
//! - PSP
|
||||
//!
|
||||
//! The following registers are NOT available on ARMv6-M devices
|
||||
//! (`thumbv6m-none-eabi`):
|
||||
//!
|
||||
//! - BASEPRI
|
||||
//! - FAULTMASK
|
||||
//!
|
||||
//! The following registers are only available for devices with an FPU:
|
||||
//!
|
||||
//! - FPSCR
|
||||
//!
|
||||
//! # References
|
||||
//!
|
||||
//! - Cortex-M* Devices Generic User Guide - Section 2.1.3 Core registers
|
||||
|
||||
#[cfg(all(not(armv6m), not(armv8m_base)))]
|
||||
pub mod basepri;
|
||||
|
||||
#[cfg(all(not(armv6m), not(armv8m_base)))]
|
||||
pub mod basepri_max;
|
||||
|
||||
pub mod control;
|
||||
|
||||
#[cfg(all(not(armv6m), not(armv8m_base)))]
|
||||
pub mod faultmask;
|
||||
|
||||
#[cfg(has_fpu)]
|
||||
pub mod fpscr;
|
||||
|
||||
pub mod msp;
|
||||
|
||||
pub mod primask;
|
||||
|
||||
pub mod psp;
|
||||
|
||||
#[cfg(armv8m_main)]
|
||||
pub mod msplim;
|
||||
|
||||
#[cfg(armv8m_main)]
|
||||
pub mod psplim;
|
||||
|
||||
// Accessing these registers requires inline assembly because their contents are tied to the current
|
||||
// stack frame
|
||||
#[cfg(feature = "inline-asm")]
|
||||
pub mod apsr;
|
||||
|
||||
#[cfg(feature = "inline-asm")]
|
||||
pub mod lr;
|
||||
|
||||
#[cfg(feature = "inline-asm")]
|
||||
pub mod pc;
|
||||
Vendored
+32
@@ -0,0 +1,32 @@
|
||||
//! Main Stack Pointer
|
||||
|
||||
/// Reads the CPU register
|
||||
#[inline]
|
||||
pub fn read() -> u32 {
|
||||
call_asm!(__msp_r() -> u32)
|
||||
}
|
||||
|
||||
/// Writes `bits` to the CPU register
|
||||
#[inline]
|
||||
#[deprecated = "calling this function invokes Undefined Behavior, consider asm::bootstrap as an alternative"]
|
||||
pub unsafe fn write(bits: u32) {
|
||||
call_asm!(__msp_w(bits: u32));
|
||||
}
|
||||
|
||||
/// Reads the Non-Secure CPU register from Secure state.
|
||||
///
|
||||
/// Executing this function in Non-Secure state will return zeroes.
|
||||
#[cfg(armv8m)]
|
||||
#[inline]
|
||||
pub fn read_ns() -> u32 {
|
||||
call_asm!(__msp_ns_r() -> u32)
|
||||
}
|
||||
|
||||
/// Writes `bits` to the Non-Secure CPU register from Secure state.
|
||||
///
|
||||
/// Executing this function in Non-Secure state will be ignored.
|
||||
#[cfg(armv8m)]
|
||||
#[inline]
|
||||
pub unsafe fn write_ns(bits: u32) {
|
||||
call_asm!(__msp_ns_w(bits: u32));
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
//! Main Stack Pointer Limit Register
|
||||
|
||||
/// Reads the CPU register
|
||||
#[inline]
|
||||
pub fn read() -> u32 {
|
||||
call_asm!(__msplim_r() -> u32)
|
||||
}
|
||||
|
||||
/// Writes `bits` to the CPU register
|
||||
#[inline]
|
||||
pub unsafe fn write(bits: u32) {
|
||||
call_asm!(__msplim_w(bits: u32))
|
||||
}
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
//! Program counter
|
||||
|
||||
/// Reads the CPU register
|
||||
///
|
||||
/// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature.
|
||||
#[inline]
|
||||
pub fn read() -> u32 {
|
||||
call_asm!(__pc_r() -> u32)
|
||||
}
|
||||
|
||||
/// Writes `bits` to the CPU register
|
||||
///
|
||||
/// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature.
|
||||
#[inline]
|
||||
pub unsafe fn write(bits: u32) {
|
||||
call_asm!(__pc_w(bits: u32));
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
//! Priority mask register
|
||||
|
||||
/// All exceptions with configurable priority are ...
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Primask {
|
||||
/// Active
|
||||
Active,
|
||||
/// Inactive
|
||||
Inactive,
|
||||
}
|
||||
|
||||
impl Primask {
|
||||
/// All exceptions with configurable priority are active
|
||||
#[inline]
|
||||
pub fn is_active(self) -> bool {
|
||||
self == Primask::Active
|
||||
}
|
||||
|
||||
/// All exceptions with configurable priority are inactive
|
||||
#[inline]
|
||||
pub fn is_inactive(self) -> bool {
|
||||
self == Primask::Inactive
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads the CPU register
|
||||
#[inline]
|
||||
pub fn read() -> Primask {
|
||||
let r: u32 = call_asm!(__primask_r() -> u32);
|
||||
if r & (1 << 0) == (1 << 0) {
|
||||
Primask::Inactive
|
||||
} else {
|
||||
Primask::Active
|
||||
}
|
||||
}
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
//! Process Stack Pointer
|
||||
|
||||
/// Reads the CPU register
|
||||
#[inline]
|
||||
pub fn read() -> u32 {
|
||||
call_asm!(__psp_r() -> u32)
|
||||
}
|
||||
|
||||
/// Writes `bits` to the CPU register
|
||||
#[inline]
|
||||
pub unsafe fn write(bits: u32) {
|
||||
call_asm!(__psp_w(bits: u32))
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
//! Process Stack Pointer Limit Register
|
||||
|
||||
/// Reads the CPU register
|
||||
#[inline]
|
||||
pub fn read() -> u32 {
|
||||
call_asm!(__psplim_r() -> u32)
|
||||
}
|
||||
|
||||
/// Writes `bits` to the CPU register
|
||||
#[inline]
|
||||
pub unsafe fn write(bits: u32) {
|
||||
call_asm!(__psplim_w(bits: u32))
|
||||
}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
[assign]
|
||||
Reference in new issue
Block a user