diff options
author | Ross Kettleson <kettro@google.com> | 2023-03-21 14:59:09 -0700 |
---|---|---|
committer | Ross Kettleson <kettro@google.com> | 2023-03-21 14:59:09 -0700 |
commit | 033862392a6f028b15898ddcb70da99f488a9797 (patch) | |
tree | d59bb754e9438dc70bd618f32c2f0f86d35e2358 | |
parent | 3a28018c8903c27c0e817829429e7efc75fa1102 (diff) | |
download | tock-registers-033862392a6f028b15898ddcb70da99f488a9797.tar.gz |
Initial import of tock-registers crate
Change-Id: If72cfd606808e53714098afea9b122b4f39007eb
Signed-off-by: Ross Kettleson <kettro@google.com>
-rw-r--r-- | Android.bp | 22 | ||||
-rw-r--r-- | CHANGELOG.md | 100 | ||||
-rw-r--r-- | Cargo.toml | 41 | ||||
-rw-r--r-- | Cargo.toml.orig | 23 | ||||
-rw-r--r-- | LICENSE | 202 | ||||
-rw-r--r-- | METADATA | 19 | ||||
-rw-r--r-- | MODULE_LICENSE_APACHE2 | 0 | ||||
-rw-r--r-- | NOTICE | 202 | ||||
-rw-r--r-- | OWNERS | 1 | ||||
-rw-r--r-- | README.md | 468 | ||||
-rw-r--r-- | src/fields.rs | 771 | ||||
-rw-r--r-- | src/interfaces.rs | 309 | ||||
-rw-r--r-- | src/lib.rs | 128 | ||||
-rw-r--r-- | src/local_register.rs | 129 | ||||
-rw-r--r-- | src/macros.rs | 370 | ||||
-rw-r--r-- | src/registers.rs | 181 |
16 files changed, 2966 insertions, 0 deletions
diff --git a/Android.bp b/Android.bp new file mode 100644 index 0000000..65cb665 --- /dev/null +++ b/Android.bp @@ -0,0 +1,22 @@ +// This file is generated by cargo2android.py --tests --device --run. +// Do not modify this file as changes will be overridden on upgrade. + + + +rust_library { + name: "libtock_registers", + host_supported: true, + crate_name: "tock_registers", + cargo_env_compat: true, + cargo_pkg_version: "0.8.1", + srcs: ["src/lib.rs"], + edition: "2021", + features: [ + "default", + "register_types", + ], + apex_available: [ + "//apex_available:platform", + "//apex_available:anyapex", + ], +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..fbf1f6b --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,100 @@ +# Changelog + +## master + +## v0.8.1 + +A point release to allow multiple invocations of `register_fields!` in +the same module. See the PR and linked Issue for details. + + - #3230: don't encapsulate test_fields! tests in mod + +## v0.8 + +`tock-registers` now supports stable Rust! + +There is a small breaking change, documented below, required to support +Rust 2021 edition. Most of the remaining changes are improvements to the +internal self-testing infrastructure and documentation. There are also +some additions to `FieldValue` to improve ergonomics. + +### **Breaking Changes** + + - #2842: tock-registers: rename TryFromValue::try_from to try_from_value + - #2838: Update to Rust 2021 edition + +### Other Changes + + - #3126: [trivial] tock-registers: mark two methods as `const` + - #3088: tock_registers/test_fields: respect struct size padding w/ alignment + - #3072: Update Rust nightly version + Expose virtual-function-elimination + - libraries/tock-register-interface: Fixup register_structs documentation + - #2988: Remove const_fn_trait_bound feature and update nightly (Mar 2022) + - #3014: tock-registers: Implement From field enum value type for FieldValue + - #3013: tock-register-interface: Provide none method for FieldValue type + - #2916: tock-register-interface: improve read_as_enum documentation + - #2922: tock-register-interface: replace register tests by const assertions + +## v0.7 + + - #2642: Rename `IntLike` to `UIntLike` to match semantics + - #2618: Reorganize, document, and feature-gate modules and exports + - #2589: Upgrade nightly for `const_fn` -> `const_fn_trait_bound` + - #2517: Use traits for accessing / manipulating registers + - #2512: Fix `Copy` and `Clone` implementation on `Field` + - #2300: Add support for `usize` + - #2220: Remove duplicate code, make local register copy read-write + - #2210: Add `u128` to `IntLike` + - #2197: Accept trailing comma in bitfields and bitmasks + +## v0.6 + + - #2095: Fix syntax errors and inconsistencies in documentation + - #2071: Clarify bit widths in documentation examples + - #2015: Use UnsafeCell in registers (see issue #2005) + - #1939: Make the Field::mask and FieldValue::mask fields private + - #1823: Allow large unsigned values as bitmasks + add bitmask! helper macro + - #1554: Allow lifetime parameters for `register_structs! { Foo<'a> { ..` + - #1661: Add `Aliased` register type for MMIO with differing R/W behavior + +## v0.5 + + - #1510 + - Register visibility granularity: don't automatically make everything + `pub`, rather give creation macro callers visbility control. + + - #1489 + - Make `register_structs!` unit test generation opt-out, so that + `custom-test-frameworks` environments can disable them. + + - #1481 + - Add `#[derive(Copy, Clone)]` to InMemoryRegister. + + - #1428 + - Implement `mask()` for `FieldValue<u16>` which seems to have been + skipped at some point. + - Implement `read()` for `FieldValue` so that individual fields + can be extracted from a register `FieldValue` representation. + + - #1461: Update `register_structs` macro to support flexible visibility of each + struct and each field. Also revert to private structs by default. + +## v0.4.1 + + - #1458: Update struct macro to create `pub` structs + +## v0.4 + + - #1368: Remove `new()` and add `InMemoryRegister` + - #1410: Add new macro for generating structs + +## v0.3 + + - #1243: Update to Rust 2018 (nightly) + - #1250: Doc-only: Fix some rustdoc warnings + +## v0.2 + + - #1161: Add `read_as_enum` to `LocalRegisterCopy`; thanks @andre-richter + +## v0.1 - Initial Release diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..00d5c8d --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,41 @@ +# 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 = "2021" +name = "tock-registers" +version = "0.8.1" +authors = ["Tock Project Developers <tock-dev@googlegroups.com>"] +description = "Memory-Mapped I/O and register interface developed for Tock." +homepage = "https://www.tockos.org/" +readme = "README.md" +keywords = [ + "tock", + "embedded", + "registers", + "mmio", + "bare-metal", +] +categories = [ + "data-structures", + "embedded", + "no-std", +] +license = "MIT/Apache-2.0" +repository = "https://github.com/tock/tock/tree/master/libraries/tock-register-interface" + +[features] +default = ["register_types"] +register_types = [] + +[badges.travis-ci] +branch = "master" +repository = "tock/tock" diff --git a/Cargo.toml.orig b/Cargo.toml.orig new file mode 100644 index 0000000..052ddd8 --- /dev/null +++ b/Cargo.toml.orig @@ -0,0 +1,23 @@ +[package] +name = "tock-registers" +version = "0.8.1" +authors = ["Tock Project Developers <tock-dev@googlegroups.com>"] +description = "Memory-Mapped I/O and register interface developed for Tock." +homepage = "https://www.tockos.org/" +repository = "https://github.com/tock/tock/tree/master/libraries/tock-register-interface" +readme = "README.md" +keywords = ["tock", "embedded", "registers", "mmio", "bare-metal"] +categories = ["data-structures", "embedded", "no-std"] +license = "MIT/Apache-2.0" +edition = "2021" + +[badges] +travis-ci = { repository = "tock/tock", branch = "master" } + +[features] +default = [ "register_types" ] + +# Include actual register types (except LocalRegisterCopy). Disabling +# the feature makes this an interface-only library and removes all +# usage of unsafe code +register_types = [] @@ -0,0 +1,202 @@ + + 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. diff --git a/METADATA b/METADATA new file mode 100644 index 0000000..3107a6b --- /dev/null +++ b/METADATA @@ -0,0 +1,19 @@ +name: "tock-registers" +description: "Memory-Mapped I/O and register interface developed for Tock." +third_party { + url { + type: HOMEPAGE + value: "https://crates.io/crates/tock-registers" + } + url { + type: ARCHIVE + value: "https://static.crates.io/crates/tock-registers/tock-registers-0.8.1.crate" + } + version: "0.8.1" + license_type: NOTICE + last_upgrade_date { + year: 2023 + month: 3 + day: 13 + } +} diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/MODULE_LICENSE_APACHE2 @@ -0,0 +1,202 @@ + + 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. @@ -0,0 +1 @@ +include platform/prebuilts/rust:master:/OWNERS diff --git a/README.md b/README.md new file mode 100644 index 0000000..de9067b --- /dev/null +++ b/README.md @@ -0,0 +1,468 @@ +# Tock Register Interface + +This crate provides an interface and types for defining and +manipulating registers and bitfields. + +## Defining registers + +The crate provides three types for working with memory mapped registers: +`ReadWrite`, `ReadOnly`, and `WriteOnly`, providing read-write, read-only, and +write-only functionality, respectively. These types implement the `Readable`, +`Writeable` and `ReadWriteable` traits. + +Defining the registers is done with the `register_structs` macro, which expects +for each register an offset, a field name, and a type. Registers must be +declared in increasing order of offsets and contiguously. Gaps when defining the +registers must be explicitly annotated with an offset and gap identifier (by +convention using a field named `_reservedN`), but without a type. The macro will +then automatically take care of calculating the gap size and inserting a +suitable filler struct. The end of the struct is marked with its size and the +`@END` keyword, effectively pointing to the offset immediately past the list of +registers. + +```rust +use tock_registers::registers::{ReadOnly, ReadWrite, WriteOnly}; + +register_structs! { + Registers { + // Control register: read-write + // The 'Control' parameter constrains this register to only use fields from + // a certain group (defined below in the bitfields section). + (0x000 => cr: ReadWrite<u8, Control::Register>), + + // Status register: read-only + (0x001 => s: ReadOnly<u8, Status::Register>), + + // Registers can be bytes, halfwords, or words: + // Note that the second type parameter can be omitted, meaning that there + // are no bitfields defined for these registers. + (0x002 => byte0: ReadWrite<u8>), + (0x003 => byte1: ReadWrite<u8>), + (0x004 => short: ReadWrite<u16>), + + // Empty space between registers must be marked with a padding field, + // declared as follows. The length of this padding is automatically + // computed by the macro. + (0x006 => _reserved), + (0x008 => word: ReadWrite<u32>), + + // The type for a register can be anything. Conveniently, you can use an + // array when there are a bunch of similar registers. + (0x00C => array: [ReadWrite<u32>; 4]), + (0x01C => ... ), + + // Etc. + + // The end of the struct is marked as follows. + (0x100 => @END), + } +} +``` + +This generates a C-style struct of the following form. + +```rust +#[repr(C)] +struct Registers { + // Control register: read-write + // The 'Control' parameter constrains this register to only use fields from + // a certain group (defined below in the bitfields section). + cr: ReadWrite<u8, Control::Register>, + + // Status register: read-only + s: ReadOnly<u8, Status::Register> + + // Registers can be bytes, halfwords, or words: + // Note that the second type parameter can be omitted, meaning that there + // are no bitfields defined for these registers. + byte0: ReadWrite<u8>, + byte1: ReadWrite<u8>, + short: ReadWrite<u16>, + + // The padding length was automatically computed as 0x008 - 0x006. + _reserved: [u8; 2], + word: ReadWrite<u32>, + + // Arrays are expanded as-is, like any other type. + array: [ReadWrite<u32>; 4], + + // Etc. +} +``` + +This crate will generate additional, compile time (`const`) assertions +to validate various invariants of the register structs, such as + +- proper start offset of padding fields, +- proper start and end offsets of actual fields, +- invalid alignment of field types, +- the `@END` marker matching the size of the struct. + +For more information on the generated assertions, check out the [`test_fields!` +macro documentation](https://docs.tockos.org/tock_registers/macro.test_fields.html). + +By default, the visibility of the generated structs and fields is private. You +can make them public using the `pub` keyword, just before the struct name or the +field identifier. + +For example, the following call to the macro: + +```rust +register_structs! { + pub Registers { + (0x000 => foo: ReadOnly<u32>), + (0x004 => pub bar: ReadOnly<u32>), + (0x008 => @END), + } +} +``` + +will generate the following struct. + +```rust +#[repr(C)] +pub struct Registers { + foo: ReadOnly<u32>, + pub bar: ReadOnly<u32>, +} +``` + +## Defining bitfields + +Bitfields are defined through the `register_bitfields!` macro: + +```rust +register_bitfields! [ + // First parameter is the register width. Can be u8, u16, u32, or u64. + u32, + + // Each subsequent parameter is a register abbreviation, its descriptive + // name, and its associated bitfields. + // The descriptive name defines this 'group' of bitfields. Only registers + // defined as ReadWrite<_, Control::Register> can use these bitfields. + Control [ + // Bitfields are defined as: + // name OFFSET(shift) NUMBITS(num) [ /* optional values */ ] + + // This is a two-bit field which includes bits 4 and 5 + RANGE OFFSET(4) NUMBITS(2) [ + // Each of these defines a name for a value that the bitfield can be + // written with or matched against. Note that this set is not exclusive-- + // the field can still be written with arbitrary constants. + VeryHigh = 0, + High = 1, + Low = 2 + ], + + // A common case is single-bit bitfields, which usually just mean + // 'enable' or 'disable' something. + EN OFFSET(3) NUMBITS(1) [], + INT OFFSET(2) NUMBITS(1) [] + ], + + // Another example: + // Status register + Status [ + TXCOMPLETE OFFSET(0) NUMBITS(1) [], + TXINTERRUPT OFFSET(1) NUMBITS(1) [], + RXCOMPLETE OFFSET(2) NUMBITS(1) [], + RXINTERRUPT OFFSET(3) NUMBITS(1) [], + MODE OFFSET(4) NUMBITS(3) [ + FullDuplex = 0, + HalfDuplex = 1, + Loopback = 2, + Disabled = 3 + ], + ERRORCOUNT OFFSET(6) NUMBITS(3) [] + ], + + // In a simple case, offset can just be a number, and the number of bits + // is set to 1: + InterruptFlags [ + UNDES 10, + TXEMPTY 9, + NSSR 8, + OVRES 3, + MODF 2, + TDRE 1, + RDRF 0 + ] +] +``` + +## Register Interface Summary + +There are four types provided by the register interface: `ReadOnly`, +`WriteOnly`, `ReadWrite`, and `Aliased`. They expose the following +methods, through the implementations of the `Readable`, `Writeable` +and `ReadWriteable` traits respectively: + +```rust +ReadOnly<T: UIntLike, R: RegisterLongName = ()>: Readable +.get() -> T // Get the raw register value +.read(field: Field<T, R>) -> T // Read the value of the given field +.read_as_enum<E>(field: Field<T, R>) -> Option<E> // Read value of the given field as a enum member +.is_set(field: Field<T, R>) -> bool // Check if one or more bits in a field are set +.matches_any(value: FieldValue<T, R>) -> bool // Check if any specified parts of a field match +.matches_all(value: FieldValue<T, R>) -> bool // Check if all specified parts of a field match +.extract() -> LocalRegisterCopy<T, R> // Make local copy of register + +WriteOnly<T: UIntLike, R: RegisterLongName = ()>: Writeable +.set(value: T) // Set the raw register value +.write(value: FieldValue<T, R>) // Write the value of one or more fields, + // overwriting other fields to zero +ReadWrite<T: UIntLike, R: RegisterLongName = ()>: Readable + Writeable + ReadWriteable +.get() -> T // Get the raw register value +.set(value: T) // Set the raw register value +.read(field: Field<T, R>) -> T // Read the value of the given field +.read_as_enum<E>(field: Field<T, R>) -> Option<E> // Read value of the given field as a enum member +.write(value: FieldValue<T, R>) // Write the value of one or more fields, + // overwriting other fields to zero +.modify(value: FieldValue<T, R>) // Write the value of one or more fields, + // leaving other fields unchanged +.modify_no_read( // Write the value of one or more fields, + original: LocalRegisterCopy<T, R>, // leaving other fields unchanged, but pass in + value: FieldValue<T, R>) // the original value, instead of doing a register read +.is_set(field: Field<T, R>) -> bool // Check if one or more bits in a field are set +.matches_any(value: FieldValue<T, R>) -> bool // Check if any specified parts of a field match +.matches_all(value: FieldValue<T, R>) -> bool // Check if all specified parts of a field match +.extract() -> LocalRegisterCopy<T, R> // Make local copy of register + +Aliased<T: UIntLike, R: RegisterLongName = (), W: RegisterLongName = ()>: Readable + Writeable +.get() -> T // Get the raw register value +.set(value: T) // Set the raw register value +.read(field: Field<T, R>) -> T // Read the value of the given field +.read_as_enum<E>(field: Field<T, R>) -> Option<E> // Read value of the given field as a enum member +.write(value: FieldValue<T, W>) // Write the value of one or more fields, + // overwriting other fields to zero +.is_set(field: Field<T, R>) -> bool // Check if one or more bits in a field are set +.matches_any(value: FieldValue<T, R>) -> bool // Check if any specified parts of a field match +.matches_all(value: FieldValue<T, R>) -> bool // Check if all specified parts of a field match +.extract() -> LocalRegisterCopy<T, R> // Make local copy of register +``` + +The `Aliased` type represents cases where read-only and write-only registers, +with different meanings, are aliased to the same memory location. + +The first type parameter (the `UIntLike` type) is `u8`, `u16`, `u32`, +`u64`, `u128` or `usize`. + +## Example: Using registers and bitfields + +Assuming we have defined a `Registers` struct and the corresponding bitfields as +in the previous two sections. We also have an immutable reference to the +`Registers` struct, named `registers`. + +```rust +// ----------------------------------------------------------------------------- +// RAW ACCESS +// ----------------------------------------------------------------------------- + +// Get or set the raw value of the register directly. Nothing fancy: +registers.cr.set(registers.cr.get() + 1); + + +// ----------------------------------------------------------------------------- +// READ +// ----------------------------------------------------------------------------- + +// `range` will contain the value of the RANGE field, e.g. 0, 1, 2, or 3. +// The type annotation is not necessary, but provided for clarity here. +let range: u8 = registers.cr.read(Control::RANGE); + +// Or one can read `range` as a enum and `match` over it. +let range = registers.cr.read_as_enum(Control::RANGE); +match range { + Some(Control::RANGE::Value::VeryHigh) => { /* ... */ } + Some(Control::RANGE::Value::High) => { /* ... */ } + Some(Control::RANGE::Value::Low) => { /* ... */ } + + None => unreachable!("invalid value") +} + +// `en` will be 0 or 1 +let en: u8 = registers.cr.read(Control::EN); + + +// ----------------------------------------------------------------------------- +// MODIFY +// ----------------------------------------------------------------------------- + +// Write a value to a bitfield without altering the values in other fields: +registers.cr.modify(Control::RANGE.val(2)); // Leaves EN, INT unchanged + +// Named constants can be used instead of the raw values: +registers.cr.modify(Control::RANGE::VeryHigh); + +// Enum values can also be used: +registers.cr.modify(Control::RANGE::Value::VeryHigh.into()) + +// Another example of writing a field with a raw value: +registers.cr.modify(Control::EN.val(0)); // Leaves RANGE, INT unchanged + +// For one-bit fields, the named values SET and CLEAR are automatically +// defined: +registers.cr.modify(Control::EN::SET); + +// Write multiple values at once, without altering other fields: +registers.cr.modify(Control::EN::CLEAR + Control::RANGE::Low); // INT unchanged + +// Any number of non-overlapping fields can be combined: +registers.cr.modify(Control::EN::CLEAR + Control::RANGE::High + CR::INT::SET); + +// In some cases (such as a protected register) .modify() may not be appropriate. +// To enable updating a register without coupling the read and write, use +// modify_no_read(): +let original = registers.cr.extract(); +registers.cr.modify_no_read(original, Control::EN::CLEAR); + + +// ----------------------------------------------------------------------------- +// WRITE +// ----------------------------------------------------------------------------- + +// Same interface as modify, except that all unspecified fields are overwritten to zero. +registers.cr.write(Control::RANGE.val(1)); // implictly sets all other bits to zero + +// ----------------------------------------------------------------------------- +// BITFLAGS +// ----------------------------------------------------------------------------- + +// For one-bit fields, easily check if they are set or clear: +let txcomplete: bool = registers.s.is_set(Status::TXCOMPLETE); + +// ----------------------------------------------------------------------------- +// MATCHING +// ----------------------------------------------------------------------------- + +// You can also query a specific register state easily with `matches_[any|all]`: + +// Doesn't care about the state of any field except TXCOMPLETE and MODE: +let ready: bool = registers.s.matches_all(Status::TXCOMPLETE:SET + + Status::MODE::FullDuplex); + +// This is very useful for awaiting for a specific condition: +while !registers.s.matches_all(Status::TXCOMPLETE::SET + + Status::RXCOMPLETE::SET + + Status::TXINTERRUPT::CLEAR) {} + +// Or for checking whether any interrupts are enabled: +let any_ints = registers.s.matches_any(Status::TXINTERRUPT + Status::RXINTERRUPT); + +// Also you can read a register with set of enumerated values as a enum and `match` over it: +let mode = registers.cr.read_as_enum(Status::MODE); + +match mode { + Some(Status::MODE::Value::FullDuplex) => { /* ... */ } + Some(Status::MODE::Value::HalfDuplex) => { /* ... */ } + + None => unreachable!("invalid value") +} + +// ----------------------------------------------------------------------------- +// LOCAL COPY +// ----------------------------------------------------------------------------- + +// More complex code may want to read a register value once and then keep it in +// a local variable before using the normal register interface functions on the +// local copy. + +// Create a copy of the register value as a local variable. +let local = registers.cr.extract(); + +// Now all the functions for a ReadOnly register work. +let txcomplete: bool = local.is_set(Status::TXCOMPLETE); + +// ----------------------------------------------------------------------------- +// In-Memory Registers +// ----------------------------------------------------------------------------- + +// In some cases, code may want to edit a memory location with all of the +// register features described above, but the actual memory location is not a +// fixed MMIO register but instead an arbitrary location in memory. If this +// location is then shared with the hardware (i.e. via DMA) then the code +// must do volatile reads and writes since the value may change without the +// software knowing. To support this, the library includes an `InMemoryRegister` +// type. + +let control: InMemoryRegister<u32, Control::Register> = InMemoryRegister::new(0) +control.write(Contol::BYTE_COUNT.val(0) + + Contol::ENABLE::Yes + + Contol::LENGTH.val(10)); +``` + +Note that `modify` performs exactly one volatile load and one volatile store, +`write` performs exactly one volatile store, and `read` performs exactly one +volatile load. Thus, you are ensured that a single call will set or query all +fields simultaneously. + +## Performance + +Examining the binaries while testing this interface, everything compiles +down to the optimal inlined bit twiddling instructions--in other words, there is +zero runtime cost, as far as an informal preliminary study has found. + +## Nice type checking + +This interface helps the compiler catch some common types of bugs via type checking. + +If you define the bitfields for e.g. a control register, you can give them a +descriptive group name like `Control`. This group of bitfields will only work +with a register of the type `ReadWrite<_, Control>` (or `ReadOnly/WriteOnly`, +etc). For instance, if we have the bitfields and registers as defined above, + +```rust +// This line compiles, because registers.cr is associated with the Control group +// of bitfields. +registers.cr.modify(Control::RANGE.val(1)); + +// This line will not compile, because registers.s is associated with the Status +// group, not the Control group. +let range = registers.s.read(Control::RANGE); +``` + +## Naming conventions + +There are several related names in the register definitions. Below is a +description of the naming convention for each: + +```rust +use tock_registers::registers::ReadWrite; + +#[repr(C)] +struct Registers { + // The register name in the struct should be a lowercase version of the + // register abbreviation, as written in the datasheet: + cr: ReadWrite<u8, Control::Register>, +} + +register_bitfields! [ + u8, + + // The name should be the long descriptive register name, + // camelcase, without the word 'register'. + Control [ + // The field name should be the capitalized abbreviated + // field name, as given in the datasheet. + RANGE OFFSET(4) NUMBITS(3) [ + // Each of the field values should be camelcase, + // as descriptive of their value as possible. + VeryHigh = 0, + High = 1, + Low = 2 + ] + ] +] +``` + +## Implementing custom register types + +The `Readable`, `Writeable` and `ReadWriteable` traits make it +possible to implement custom register types, even outside of this +crate. A particularly useful application area for this are CPU +registers, such as ARM SPSRs or RISC-V CSRs. It is sufficient to +implement the `Readable::get` and `Writeable::set` methods for the +rest of the API to be automatically implemented by the crate-provided +traits. For more in-depth documentation on how this works, [refer to +the `interfaces` module +documentation](https://docs.tockos.org/tock_registers/index.html). diff --git a/src/fields.rs b/src/fields.rs new file mode 100644 index 0000000..e0bf661 --- /dev/null +++ b/src/fields.rs @@ -0,0 +1,771 @@ +//! Register bitfield types and macros +//! +//! To conveniently access and manipulate fields of a register, this +//! library provides types and macros to describe and access bitfields +//! of a register. This can be especially useful in conjuction with +//! the APIs defined in [`interfaces`](crate::interfaces), which make +//! use of these types and hence allow to access and manipulate +//! bitfields of proper registers directly. +//! +//! A specific section (bitfield) in a register is described by the +//! [`Field`] type, consisting of an unshifted bitmask over the base +//! register [`UIntLike`](crate::UIntLike) type, and a shift +//! parameter. It is further associated with a specific +//! [`RegisterLongName`], which can prevent its use with incompatible +//! registers. +//! +//! A value of a section of a register is described by the +//! [`FieldValue`] type. It stores the information of the respective +//! section in the register, as well as the associated value. A +//! [`FieldValue`] can be created from a [`Field`] through the +//! [`val`](Field::val) method. +//! +//! ## `register_bitfields` macro +//! +//! For defining register layouts with an associated +//! [`RegisterLongName`](crate::RegisterLongName), along with +//! [`Field`]s and matching [`FieldValue`]s, a convenient macro-based +//! interface can be used. +//! +//! The following example demonstrates how two registers can be +//! defined, over a `u32` base type: +//! +//! ```rust +//! # use tock_registers::register_bitfields; +//! # use tock_registers::registers::InMemoryRegister; +//! # use tock_registers::interfaces::{Readable, ReadWriteable}; +//! register_bitfields![u32, +//! Uart [ +//! ENABLE OFFSET(0) NUMBITS(4) [ +//! ON = 8, +//! OFF = 0 +//! ] +//! ], +//! Psel [ +//! PIN OFFSET(0) NUMBITS(6), +//! CONNECT OFFSET(31) NUMBITS(1) +//! ], +//! ]; +//! +//! // In this scope, `Uart` is a module, representing the register and +//! // its fields. `Uart::Register` is a `RegisterLongName` type +//! // identifying this register. `Uart::ENABLE` is a field covering the +//! // first 4 bits of this register. `Uart::ENABLE::ON` is a +//! // `FieldValue` over that field, with the associated value 8. +//! // We can now use the types like so: +//! let reg: InMemoryRegister<u32, Uart::Register> = InMemoryRegister::new(0); +//! assert!(reg.read(Uart::ENABLE) == 0x00000000); +//! reg.modify(Uart::ENABLE::ON); +//! assert!(reg.get() == 0x00000008); +//! ``` + +// The register interface uses `+` in a way that is fine for bitfields, but +// looks unusual (and perhaps problematic) to a linter. We just ignore those +// lints for this file. +#![allow(clippy::suspicious_op_assign_impl)] +#![allow(clippy::suspicious_arithmetic_impl)] + +use core::marker::PhantomData; +use core::ops::{Add, AddAssign}; + +use crate::{RegisterLongName, UIntLike}; + +/// Specific section of a register. +/// +/// For the Field, the mask is unshifted, ie. the LSB should always be set. +pub struct Field<T: UIntLike, R: RegisterLongName> { + pub mask: T, + pub shift: usize, + associated_register: PhantomData<R>, +} + +impl<T: UIntLike, R: RegisterLongName> Field<T, R> { + pub const fn new(mask: T, shift: usize) -> Field<T, R> { + Field { + mask: mask, + shift: shift, + associated_register: PhantomData, + } + } + + #[inline] + pub fn read(self, val: T) -> T { + (val & (self.mask << self.shift)) >> self.shift + } + + #[inline] + /// Check if one or more bits in a field are set + pub fn is_set(self, val: T) -> bool { + val & (self.mask << self.shift) != T::zero() + } + + #[inline] + /// Read value of the field as an enum member + /// + /// This method expects to be passed the unasked and unshifted register + /// value, extracts the field value by calling [`Field::read`] and + /// subsequently passes that value to the [`TryFromValue`] implementation on + /// the passed enum type. + /// + /// The [`register_bitfields!`](crate::register_bitfields) macro will + /// generate an enum containing the various named field variants and + /// implementing the required [`TryFromValue`] trait. It is accessible as + /// `$REGISTER_NAME::$FIELD_NAME::Value`. + /// + /// This method can be useful to symbolically represent read register field + /// states throughout the codebase and to enforce exhaustive matches over + /// all defined valid register field values. + /// + /// ## Usage Example + /// + /// ```rust + /// # use tock_registers::interfaces::Readable; + /// # use tock_registers::registers::InMemoryRegister; + /// # use tock_registers::register_bitfields; + /// register_bitfields![u8, + /// EXAMPLEREG [ + /// TESTFIELD OFFSET(3) NUMBITS(3) [ + /// Foo = 2, + /// Bar = 3, + /// Baz = 6, + /// ], + /// ], + /// ]; + /// + /// match EXAMPLEREG::TESTFIELD.read_as_enum(0x9C) { + /// Some(EXAMPLEREG::TESTFIELD::Value::Bar) => "The value is 3!", + /// _ => panic!("boo!"), + /// }; + /// ``` + pub fn read_as_enum<E: TryFromValue<T, EnumType = E>>(self, val: T) -> Option<E> { + E::try_from_value(self.read(val)) + } +} + +// #[derive(Copy, Clone)] won't work here because it will use +// incorrect bounds, as a result of using a PhantomData over the +// generic R. The PhantomData<R> implements Copy regardless of whether +// R does, but the #[derive(Copy, Clone)] generates +// +// #[automatically_derived] +// #[allow(unused_qualifications)] +// impl<T: ::core::marker::Copy + UIntLike, +// R: ::core::marker::Copy + RegisterLongName> +// ::core::marker::Copy for Field<T, R> {} +// +// , so Field will only implement Copy if R: Copy. +// +// Manually implementing Clone and Copy works around this issue. +// +// Relevant Rust issue: https://github.com/rust-lang/rust/issues/26925 +impl<T: UIntLike, R: RegisterLongName> Clone for Field<T, R> { + fn clone(&self) -> Self { + Field { + mask: self.mask, + shift: self.shift, + associated_register: self.associated_register, + } + } +} +impl<T: UIntLike, R: RegisterLongName> Copy for Field<T, R> {} + +macro_rules! Field_impl_for { + ($type:ty) => { + impl<R: RegisterLongName> Field<$type, R> { + pub const fn val(&self, value: $type) -> FieldValue<$type, R> { + FieldValue::<$type, R>::new(self.mask, self.shift, value) + } + } + }; +} + +Field_impl_for!(u8); +Field_impl_for!(u16); +Field_impl_for!(u32); +Field_impl_for!(u64); +Field_impl_for!(u128); +Field_impl_for!(usize); + +/// Values for the specific register fields. +/// +/// For the FieldValue, the masks and values are shifted into their actual +/// location in the register. +#[derive(Copy, Clone)] +pub struct FieldValue<T: UIntLike, R: RegisterLongName> { + mask: T, + pub value: T, + associated_register: PhantomData<R>, +} + +macro_rules! FieldValue_impl_for { + ($type:ty) => { + // Necessary to split the implementation of new() out because the bitwise + // math isn't treated as const when the type is generic. + // Tracking issue: https://github.com/rust-lang/rfcs/pull/2632 + impl<R: RegisterLongName> FieldValue<$type, R> { + pub const fn new(mask: $type, shift: usize, value: $type) -> Self { + FieldValue { + mask: mask << shift, + value: (value & mask) << shift, + associated_register: PhantomData, + } + } + } + + // Necessary to split the implementation of From<> out because of the orphan rule + // for foreign trait implementation (see [E0210](https://doc.rust-lang.org/error-index.html#E0210)). + impl<R: RegisterLongName> From<FieldValue<$type, R>> for $type { + fn from(val: FieldValue<$type, R>) -> $type { + val.value + } + } + }; +} + +FieldValue_impl_for!(u8); +FieldValue_impl_for!(u16); +FieldValue_impl_for!(u32); +FieldValue_impl_for!(u64); +FieldValue_impl_for!(u128); +FieldValue_impl_for!(usize); + +impl<T: UIntLike, R: RegisterLongName> FieldValue<T, R> { + #[inline] + pub fn none() -> Self { + Self { + mask: T::zero(), + value: T::zero(), + associated_register: PhantomData, + } + } + + /// Get the raw bitmask represented by this FieldValue. + #[inline] + pub const fn mask(&self) -> T { + self.mask as T + } + + #[inline] + pub fn read(&self, field: Field<T, R>) -> T { + field.read(self.value) + } + + /// Modify fields in a register value + #[inline] + pub fn modify(self, val: T) -> T { + (val & !self.mask) | self.value + } + + /// Check if any specified parts of a field match + #[inline] + pub fn matches_any(&self, val: T) -> bool { + val & self.mask != T::zero() + } + + /// Check if all specified parts of a field match + #[inline] + pub fn matches_all(&self, val: T) -> bool { + val & self.mask == self.value + } +} + +// Combine two fields with the addition operator +impl<T: UIntLike, R: RegisterLongName> Add for FieldValue<T, R> { + type Output = Self; + + #[inline] + fn add(self, rhs: Self) -> Self { + FieldValue { + mask: self.mask | rhs.mask, + value: self.value | rhs.value, + associated_register: PhantomData, + } + } +} + +// Combine two fields with the += operator +impl<T: UIntLike, R: RegisterLongName> AddAssign for FieldValue<T, R> { + #[inline] + fn add_assign(&mut self, rhs: FieldValue<T, R>) { + self.mask |= rhs.mask; + self.value |= rhs.value; + } +} + +/// Conversion of raw register value into enumerated values member. +/// Implemented inside register_bitfields! macro for each bit field. +pub trait TryFromValue<V> { + type EnumType; + + fn try_from_value(v: V) -> Option<Self::EnumType>; +} + +/// Helper macro for computing bitmask of variable number of bits +#[macro_export] +macro_rules! bitmask { + ($numbits:expr) => { + (1 << ($numbits - 1)) + ((1 << ($numbits - 1)) - 1) + }; +} + +/// Helper macro for defining register fields. +#[macro_export] +macro_rules! register_bitmasks { + { + // BITFIELD_NAME OFFSET(x) + $(#[$outer:meta])* + $valtype:ident, $reg_desc:ident, [ + $( $(#[$inner:meta])* $field:ident OFFSET($offset:expr)),+ $(,)? + ] + } => { + $(#[$outer])* + $( $crate::register_bitmasks!($valtype, $reg_desc, $(#[$inner])* $field, $offset, 1, []); )* + }; + { + // BITFIELD_NAME OFFSET + // All fields are 1 bit + $(#[$outer:meta])* + $valtype:ident, $reg_desc:ident, [ + $( $(#[$inner:meta])* $field:ident $offset:expr ),+ $(,)? + ] + } => { + $(#[$outer])* + $( $crate::register_bitmasks!($valtype, $reg_desc, $(#[$inner])* $field, $offset, 1, []); )* + }; + + { + // BITFIELD_NAME OFFSET(x) NUMBITS(y) + $(#[$outer:meta])* + $valtype:ident, $reg_desc:ident, [ + $( $(#[$inner:meta])* $field:ident OFFSET($offset:expr) NUMBITS($numbits:expr) ),+ $(,)? + ] + } => { + $(#[$outer])* + $( $crate::register_bitmasks!($valtype, $reg_desc, $(#[$inner])* $field, $offset, $numbits, []); )* + }; + + { + // BITFIELD_NAME OFFSET(x) NUMBITS(y) [] + $(#[$outer:meta])* + $valtype:ident, $reg_desc:ident, [ + $( $(#[$inner:meta])* $field:ident OFFSET($offset:expr) NUMBITS($numbits:expr) + $values:tt ),+ $(,)? + ] + } => { + $(#[$outer])* + $( $crate::register_bitmasks!($valtype, $reg_desc, $(#[$inner])* $field, $offset, $numbits, + $values); )* + }; + { + $valtype:ident, $reg_desc:ident, $(#[$outer:meta])* $field:ident, + $offset:expr, $numbits:expr, + [$( $(#[$inner:meta])* $valname:ident = $value:expr ),+ $(,)?] + } => { // this match arm is duplicated below with an allowance for 0 elements in the valname -> value array, + // to seperately support the case of zero-variant enums not supporting non-default + // representations. + #[allow(non_upper_case_globals)] + #[allow(unused)] + pub const $field: Field<$valtype, $reg_desc> = + Field::<$valtype, $reg_desc>::new($crate::bitmask!($numbits), $offset); + + #[allow(non_snake_case)] + #[allow(unused)] + $(#[$outer])* + pub mod $field { + #[allow(unused_imports)] + use $crate::fields::{TryFromValue, FieldValue}; + use super::$reg_desc; + + $( + #[allow(non_upper_case_globals)] + #[allow(unused)] + $(#[$inner])* + pub const $valname: FieldValue<$valtype, $reg_desc> = + FieldValue::<$valtype, $reg_desc>::new($crate::bitmask!($numbits), + $offset, $value); + )* + + #[allow(non_upper_case_globals)] + #[allow(unused)] + pub const SET: FieldValue<$valtype, $reg_desc> = + FieldValue::<$valtype, $reg_desc>::new($crate::bitmask!($numbits), + $offset, $crate::bitmask!($numbits)); + + #[allow(non_upper_case_globals)] + #[allow(unused)] + pub const CLEAR: FieldValue<$valtype, $reg_desc> = + FieldValue::<$valtype, $reg_desc>::new($crate::bitmask!($numbits), + $offset, 0); + + #[allow(dead_code)] + #[allow(non_camel_case_types)] + #[derive(Copy, Clone, Eq, PartialEq)] + #[repr($valtype)] // so that values larger than isize::MAX can be stored + $(#[$outer])* + pub enum Value { + $( + $(#[$inner])* + $valname = $value, + )* + } + + impl TryFromValue<$valtype> for Value { + type EnumType = Value; + + fn try_from_value(v: $valtype) -> Option<Self::EnumType> { + match v { + $( + $(#[$inner])* + x if x == Value::$valname as $valtype => Some(Value::$valname), + )* + + _ => Option::None + } + } + } + + impl From<Value> for FieldValue<$valtype, $reg_desc> { + fn from(v: Value) -> Self { + Self::new($crate::bitmask!($numbits), $offset, v as $valtype) + } + } + } + }; + { + $valtype:ident, $reg_desc:ident, $(#[$outer:meta])* $field:ident, + $offset:expr, $numbits:expr, + [] + } => { //same pattern as previous match arm, for 0 elements in array. Removes code associated with array. + #[allow(non_upper_case_globals)] + #[allow(unused)] + pub const $field: Field<$valtype, $reg_desc> = + Field::<$valtype, $reg_desc>::new($crate::bitmask!($numbits), $offset); + + #[allow(non_snake_case)] + #[allow(unused)] + $(#[$outer])* + pub mod $field { + #[allow(unused_imports)] + use $crate::fields::{FieldValue, TryFromValue}; + use super::$reg_desc; + + #[allow(non_upper_case_globals)] + #[allow(unused)] + pub const SET: FieldValue<$valtype, $reg_desc> = + FieldValue::<$valtype, $reg_desc>::new($crate::bitmask!($numbits), + $offset, $crate::bitmask!($numbits)); + + #[allow(non_upper_case_globals)] + #[allow(unused)] + pub const CLEAR: FieldValue<$valtype, $reg_desc> = + FieldValue::<$valtype, $reg_desc>::new($crate::bitmask!($numbits), + $offset, 0); + + #[allow(dead_code)] + #[allow(non_camel_case_types)] + $(#[$outer])* + pub enum Value {} + + impl TryFromValue<$valtype> for Value { + type EnumType = Value; + + fn try_from_value(_v: $valtype) -> Option<Self::EnumType> { + Option::None + } + } + } + }; +} + +/// Define register types and fields. +#[macro_export] +macro_rules! register_bitfields { + { + $valtype:ident, $( $(#[$inner:meta])* $vis:vis $reg:ident $fields:tt ),* $(,)? + } => { + $( + #[allow(non_snake_case)] + $(#[$inner])* + $vis mod $reg { + // Visibility note: This is left always `pub` as it is not + // meaningful to restrict access to the `Register` element of + // the register module if the module itself is in scope + // + // (if you can access $reg, you can access $reg::Register) + #[derive(Clone, Copy)] + pub struct Register; + impl $crate::RegisterLongName for Register {} + + use $crate::fields::Field; + + $crate::register_bitmasks!( $valtype, Register, $fields ); + } + )* + } +} + +#[cfg(feature = "std_unit_tests")] +#[cfg(test)] +mod tests { + #[derive(Debug, PartialEq, Eq)] + enum Foo { + Foo0, + Foo1, + Foo2, + Foo3, + Foo4, + Foo5, + Foo6, + Foo7, + } + + impl crate::fields::TryFromValue<u16> for Foo { + type EnumType = Foo; + + fn try_from_value(v: u16) -> Option<Self::EnumType> { + Self::try_from_value(v as u32) + } + } + impl crate::fields::TryFromValue<u32> for Foo { + type EnumType = Foo; + + fn try_from_value(v: u32) -> Option<Self::EnumType> { + match v { + 0 => Some(Foo::Foo0), + 1 => Some(Foo::Foo1), + 2 => Some(Foo::Foo2), + 3 => Some(Foo::Foo3), + 4 => Some(Foo::Foo4), + 5 => Some(Foo::Foo5), + 6 => Some(Foo::Foo6), + 7 => Some(Foo::Foo7), + _ => None, + } + } + } + + mod field { + use super::Foo; + use crate::fields::{Field, TryFromValue}; + + #[test] + fn test_new() { + let field8 = Field::<u8, ()>::new(0x12, 3); + assert_eq!(field8.mask, 0x12_u8); + assert_eq!(field8.shift, 3); + let field16 = Field::<u16, ()>::new(0x1234, 5); + assert_eq!(field16.mask, 0x1234_u16); + assert_eq!(field16.shift, 5); + let field32 = Field::<u32, ()>::new(0x12345678, 9); + assert_eq!(field32.mask, 0x12345678_u32); + assert_eq!(field32.shift, 9); + let field64 = Field::<u64, ()>::new(0x12345678_9abcdef0, 1); + assert_eq!(field64.mask, 0x12345678_9abcdef0_u64); + assert_eq!(field64.shift, 1); + let field128 = Field::<u128, ()>::new(0x12345678_9abcdef0_0fedcba9_87654321, 1); + assert_eq!(field128.mask, 0x12345678_9abcdef0_0fedcba9_87654321_u128); + assert_eq!(field128.shift, 1); + } + + #[test] + fn test_read() { + let field = Field::<u32, ()>::new(0xFF, 4); + assert_eq!(field.read(0x123), 0x12); + let field = Field::<u32, ()>::new(0xF0F, 4); + assert_eq!(field.read(0x1234), 0x103); + } + + #[test] + fn test_is_set() { + let field = Field::<u16, ()>::new(0xFF, 4); + assert_eq!(field.is_set(0), false); + assert_eq!(field.is_set(0xFFFF), true); + assert_eq!(field.is_set(0x0FF0), true); + assert_eq!(field.is_set(0x1000), false); + assert_eq!(field.is_set(0x0100), true); + assert_eq!(field.is_set(0x0010), true); + assert_eq!(field.is_set(0x0001), false); + + for shift in 0..24 { + let field = Field::<u32, ()>::new(0xFF, shift); + for x in 1..=0xFF { + assert_eq!(field.is_set(x << shift), true); + } + assert_eq!(field.is_set(!(0xFF << shift)), false); + } + } + + #[test] + fn test_read_as_enum() { + let field = Field::<u16, ()>::new(0x7, 4); + assert_eq!(field.read_as_enum(0x1234), Some(Foo::Foo3)); + assert_eq!(field.read_as_enum(0x5678), Some(Foo::Foo7)); + assert_eq!(field.read_as_enum(0xFFFF), Some(Foo::Foo7)); + assert_eq!(field.read_as_enum(0x0000), Some(Foo::Foo0)); + assert_eq!(field.read_as_enum(0x0010), Some(Foo::Foo1)); + assert_eq!(field.read_as_enum(0x1204), Some(Foo::Foo0)); + + for shift in 0..29 { + let field = Field::<u32, ()>::new(0x7, shift); + for x in 0..8 { + assert_eq!(field.read_as_enum(x << shift), Foo::try_from_value(x)); + } + } + } + } + + mod field_value { + use crate::fields::Field; + + #[test] + fn test_from() { + let field = Field::<u32, ()>::new(0xFF, 4); + assert_eq!(u32::from(field.val(0)), 0); + assert_eq!(u32::from(field.val(0xFFFFFFFF)), 0xFF0); + assert_eq!(u32::from(field.val(0x12)), 0x120); + assert_eq!(u32::from(field.val(0x123)), 0x230); + + for shift in 0..32 { + let field = Field::<u32, ()>::new(0xFF, shift); + for x in 0..=0xFF { + assert_eq!(u32::from(field.val(x)), x << shift); + } + } + } + + #[test] + fn test_read_same_field() { + let field = Field::<u32, ()>::new(0xFF, 4); + assert_eq!(field.val(0).read(field), 0); + assert_eq!(field.val(0xFFFFFFFF).read(field), 0xFF); + assert_eq!(field.val(0x12).read(field), 0x12); + assert_eq!(field.val(0x123).read(field), 0x23); + + for shift in 0..24 { + let field = Field::<u32, ()>::new(0xFF, shift); + for x in 0..=0xFF { + assert_eq!(field.val(x).read(field), x); + } + } + } + + #[test] + fn test_read_disjoint_fields() { + for shift in 0..24 { + let field1 = Field::<u32, ()>::new(0xF0, shift); + let field2 = Field::<u32, ()>::new(0x0F, shift); + for x in 0..=0xFF { + assert_eq!(field1.val(x).read(field2), 0); + assert_eq!(field2.val(x).read(field1), 0); + } + } + for shift in 0..24 { + let field1 = Field::<u32, ()>::new(0xF, shift); + let field2 = Field::<u32, ()>::new(0xF, shift + 4); + for x in 0..=0xFF { + assert_eq!(field1.val(x).read(field2), 0); + assert_eq!(field2.val(x).read(field1), 0); + } + } + } + + #[test] + fn test_modify() { + let field = Field::<u32, ()>::new(0xFF, 4); + assert_eq!(field.val(0x23).modify(0x0000), 0x0230); + assert_eq!(field.val(0x23).modify(0xFFFF), 0xF23F); + assert_eq!(field.val(0x23).modify(0x1234), 0x1234); + assert_eq!(field.val(0x23).modify(0x5678), 0x5238); + } + + #[test] + fn test_matches_any() { + let field = Field::<u32, ()>::new(0xFF, 4); + assert_eq!(field.val(0x23).matches_any(0x1234), true); + assert_eq!(field.val(0x23).matches_any(0x5678), true); + assert_eq!(field.val(0x23).matches_any(0x5008), false); + + for shift in 0..24 { + let field = Field::<u32, ()>::new(0xFF, shift); + for x in 0..=0xFF { + let field_value = field.val(x); + for y in 1..=0xFF { + assert_eq!(field_value.matches_any(y << shift), true); + } + assert_eq!(field_value.matches_any(0), false); + assert_eq!(field_value.matches_any(!(0xFF << shift)), false); + } + } + } + + #[test] + fn test_matches_all() { + let field = Field::<u32, ()>::new(0xFF, 4); + assert_eq!(field.val(0x23).matches_all(0x1234), true); + assert_eq!(field.val(0x23).matches_all(0x5678), false); + + for shift in 0..24 { + let field = Field::<u32, ()>::new(0xFF, shift); + for x in 0..=0xFF { + assert_eq!(field.val(x).matches_all(x << shift), true); + assert_eq!(field.val(x + 1).matches_all(x << shift), false); + } + } + } + + #[test] + fn test_add_disjoint_fields() { + let field1 = Field::<u32, ()>::new(0xFF, 24); + let field2 = Field::<u32, ()>::new(0xFF, 16); + let field3 = Field::<u32, ()>::new(0xFF, 8); + let field4 = Field::<u32, ()>::new(0xFF, 0); + assert_eq!( + u32::from( + field1.val(0x12) + field2.val(0x34) + field3.val(0x56) + field4.val(0x78) + ), + 0x12345678 + ); + + for shift in 0..24 { + let field1 = Field::<u32, ()>::new(0xF, shift); + let field2 = Field::<u32, ()>::new(0xF, shift + 4); + for x in 0..=0xF { + for y in 0..=0xF { + assert_eq!( + u32::from(field1.val(x) + field2.val(y)), + (x | (y << 4)) << shift + ); + } + } + } + } + + #[test] + fn test_add_assign_disjoint_fields() { + let field1 = Field::<u32, ()>::new(0xFF, 24); + let field2 = Field::<u32, ()>::new(0xFF, 16); + let field3 = Field::<u32, ()>::new(0xFF, 8); + let field4 = Field::<u32, ()>::new(0xFF, 0); + + let mut value = field1.val(0x12); + value += field2.val(0x34); + value += field3.val(0x56); + value += field4.val(0x78); + assert_eq!(u32::from(value), 0x12345678); + + for shift in 0..24 { + let field1 = Field::<u32, ()>::new(0xF, shift); + let field2 = Field::<u32, ()>::new(0xF, shift + 4); + for x in 0..=0xF { + for y in 0..=0xF { + let mut value = field1.val(x); + value += field2.val(y); + assert_eq!(u32::from(value), (x | (y << 4)) << shift); + } + } + } + } + } + + // TODO: More unit tests here. +} diff --git a/src/interfaces.rs b/src/interfaces.rs new file mode 100644 index 0000000..dbd252a --- /dev/null +++ b/src/interfaces.rs @@ -0,0 +1,309 @@ +//! Interfaces (traits) to register types +//! +//! This module contains traits which reflect standardized interfaces +//! to different types of registers. Examples of registers +//! implementing these interfaces are [`ReadWrite`](crate::registers::ReadWrite) or +//! [`InMemoryRegister`](crate::registers::InMemoryRegister). +//! +//! Each trait has two associated type parameters, namely: +//! +//! - `T`: [`UIntLike`](crate::UIntLike), indicating the underlying +//! integer type used to represent the register's raw contents. +//! +//! - `R`: [`RegisterLongName`](crate::RegisterLongName), functioning +//! as a type to identify this register's descriptive name and +//! semantic meaning. It is further used to impose type constraints +//! on values passed through the API, such as +//! [`FieldValue`](crate::fields::FieldValue). +//! +//! Registers can have different access levels, which are mapped to +//! different traits respectively: +//! +//! - [`Readable`]: indicates that the current value of this register +//! can be read. Implementations will need to provide the +//! [`get`](crate::interfaces::Readable::get) method. +//! +//! - [`Writeable`]: indicates that the value of this register can be +//! set. Implementations will need to provide the +//! [`set`](crate::interfaces::Writeable::set) method. +//! +//! - [`ReadWriteable`]: indicates that this register can be +//! _modified_. It is not sufficient for registers to be both read- +//! and writable, they must also have the same semantic meaning when +//! read from and written to. This is not true in general, for +//! example a memory-mapped UART register might transmit when +//! writing and receive when reading. +//! +//! If a type implements both [`Readable`] and [`Writeable`], and +//! the associated [`RegisterLongName`](crate::RegisterLongName) +//! type parameters are identical, it will automatically implement +//! [`ReadWriteable`]. In particular, for +//! [`Aliased`](crate::registers::Aliased) this is -- in general -- +//! not the case, so +//! +//! ```rust +//! # use tock_registers::interfaces::{Readable, Writeable, ReadWriteable}; +//! # use tock_registers::registers::ReadWrite; +//! # use tock_registers::register_bitfields; +//! register_bitfields![u8, +//! A [ +//! DUMMY OFFSET(0) NUMBITS(1) [], +//! ], +//! ]; +//! let read_write_reg: &ReadWrite<u8, A::Register> = unsafe { +//! core::mem::transmute(Box::leak(Box::new(0_u8))) +//! }; +//! ReadWriteable::modify(read_write_reg, A::DUMMY::SET); +//! ``` +//! +//! works, but not +//! +//! ```compile_fail +//! # use tock_registers::interfaces::{Readable, Writeable, ReadWriteable}; +//! # use tock_registers::registers::Aliased; +//! # use tock_registers::register_bitfields; +//! register_bitfields![u8, +//! A [ +//! DUMMY OFFSET(0) NUMBITS(1) [], +//! ], +//! B [ +//! DUMMY OFFSET(0) NUMBITS(1) [], +//! ], +//! ]; +//! let aliased_reg: &Aliased<u8, A::Register, B::Register> = unsafe { +//! core::mem::transmute(Box::leak(Box::new(0_u8))) +//! }; +//! ReadWriteable::modify(aliased_reg, A::DUMMY::SET); +//! ``` +//! +//! ## Example: implementing a custom register type +//! +//! These traits can be used to implement custom register types, which +//! are compatible to the ones shipped in this crate. For example, to +//! define a register which sets a `u8` value using a Cell reference, +//! always reads the bitwise-negated vale and prints every written +//! value to the console: +//! +//! ```rust +//! # use core::cell::Cell; +//! # use core::marker::PhantomData; +//! # +//! # use tock_registers::interfaces::{Readable, Writeable, ReadWriteable}; +//! # use tock_registers::RegisterLongName; +//! # use tock_registers::register_bitfields; +//! # +//! struct DummyRegister<'a, R: RegisterLongName> { +//! cell_ref: &'a Cell<u8>, +//! _register_long_name: PhantomData<R>, +//! } +//! +//! impl<'a, R: RegisterLongName> Readable for DummyRegister<'a, R> { +//! type T = u8; +//! type R = R; +//! +//! fn get(&self) -> u8 { +//! // Return the bitwise-inverse of the current value +//! !self.cell_ref.get() +//! } +//! } +//! +//! impl<'a, R: RegisterLongName> Writeable for DummyRegister<'a, R> { +//! type T = u8; +//! type R = R; +//! +//! fn set(&self, value: u8) { +//! println!("Setting Cell to {:02x?}!", value); +//! self.cell_ref.set(value); +//! } +//! } +//! +//! register_bitfields![u8, +//! DummyReg [ +//! HIGH OFFSET(4) NUMBITS(4) [ +//! A = 0b0001, +//! B = 0b0010, +//! C = 0b0100, +//! D = 0b1000, +//! ], +//! LOW OFFSET(0) NUMBITS(4) [], +//! ], +//! ]; +//! +//! // Create a new DummyRegister over some Cell<u8> +//! let cell = Cell::new(0); +//! let dummy = DummyRegister { +//! cell_ref: &cell, +//! _register_long_name: PhantomData, +//! }; +//! +//! // Set a value and read it back. This demonstrates the raw getters +//! // and setters of Writeable and Readable +//! dummy.set(0xFA); +//! assert!(dummy.get() == 0x05); +//! +//! // Use some of the automatically derived APIs, such as +//! // ReadWriteable::modify and Readable::read +//! dummy.modify(DummyReg::HIGH::C); +//! assert!(dummy.read(DummyReg::HIGH) == 0xb); +//! ``` + +use crate::fields::{Field, FieldValue, TryFromValue}; +use crate::{LocalRegisterCopy, RegisterLongName, UIntLike}; + +/// Readable register +/// +/// Register which at least supports reading the current value. Only +/// [`Readable::get`] must be implemented, as for other methods a +/// default implementation is provided. +/// +/// A register that is both [`Readable`] and [`Writeable`] will also +/// automatically be [`ReadWriteable`], if the [`RegisterLongName`] of +/// [`Readable`] is the same as that of [`Writeable`] (i.e. not for +/// [`Aliased`](crate::registers::Aliased) registers). +pub trait Readable { + type T: UIntLike; + type R: RegisterLongName; + + /// Get the raw register value + fn get(&self) -> Self::T; + + #[inline] + /// Read the value of the given field + fn read(&self, field: Field<Self::T, Self::R>) -> Self::T { + field.read(self.get()) + } + + /// Set the raw register value + /// + /// The [`register_bitfields!`](crate::register_bitfields) macro will + /// generate an enum containing the various named field variants and + /// implementing the required [`TryFromValue`] trait. It is accessible as + /// `$REGISTER_NAME::$FIELD_NAME::Value`. + /// + /// This method can be useful to symbolically represent read register field + /// states throughout the codebase and to enforce exhaustive matches over + /// all defined valid register field values. + /// + /// ## Usage Example + /// + /// ```rust + /// # use tock_registers::interfaces::Readable; + /// # use tock_registers::registers::InMemoryRegister; + /// # use tock_registers::register_bitfields; + /// register_bitfields![u8, + /// EXAMPLEREG [ + /// TESTFIELD OFFSET(0) NUMBITS(2) [ + /// Foo = 0, + /// Bar = 1, + /// Baz = 2, + /// ], + /// ], + /// ]; + /// + /// let reg: InMemoryRegister<u8, EXAMPLEREG::Register> = + /// InMemoryRegister::new(2); + /// + /// match reg.read_as_enum(EXAMPLEREG::TESTFIELD) { + /// Some(EXAMPLEREG::TESTFIELD::Value::Foo) => "Tock", + /// Some(EXAMPLEREG::TESTFIELD::Value::Bar) => "is", + /// Some(EXAMPLEREG::TESTFIELD::Value::Baz) => "awesome!", + /// None => panic!("boo!"), + /// }; + /// ``` + #[inline] + fn read_as_enum<E: TryFromValue<Self::T, EnumType = E>>( + &self, + field: Field<Self::T, Self::R>, + ) -> Option<E> { + field.read_as_enum(self.get()) + } + + #[inline] + /// Make a local copy of the register + fn extract(&self) -> LocalRegisterCopy<Self::T, Self::R> { + LocalRegisterCopy::new(self.get()) + } + + #[inline] + /// Check if one or more bits in a field are set + fn is_set(&self, field: Field<Self::T, Self::R>) -> bool { + field.is_set(self.get()) + } + + #[inline] + /// Check if any specified parts of a field match + fn matches_any(&self, field: FieldValue<Self::T, Self::R>) -> bool { + field.matches_any(self.get()) + } + + #[inline] + /// Check if all specified parts of a field match + fn matches_all(&self, field: FieldValue<Self::T, Self::R>) -> bool { + field.matches_all(self.get()) + } +} + +/// Writeable register +/// +/// Register which at least supports setting a value. Only +/// [`Writeable::set`] must be implemented, as for other methods a +/// default implementation is provided. +/// +/// A register that is both [`Readable`] and [`Writeable`] will also +/// automatically be [`ReadWriteable`], if the [`RegisterLongName`] of +/// [`Readable`] is the same as that of [`Writeable`] (i.e. not for +/// [`Aliased`](crate::registers::Aliased) registers). +pub trait Writeable { + type T: UIntLike; + type R: RegisterLongName; + + /// Set the raw register value + fn set(&self, value: Self::T); + + #[inline] + /// Write the value of one or more fields, overwriting the other fields with zero + fn write(&self, field: FieldValue<Self::T, Self::R>) { + self.set(field.value); + } + + #[inline] + /// Write the value of one or more fields, maintaining the value of unchanged fields via a + /// provided original value, rather than a register read. + fn modify_no_read( + &self, + original: LocalRegisterCopy<Self::T, Self::R>, + field: FieldValue<Self::T, Self::R>, + ) { + self.set(field.modify(original.get())); + } +} + +/// [`Readable`] and [`Writeable`] register, over the same +/// [`RegisterLongName`] +/// +/// Register which supports both reading and setting a value. +/// +/// **This trait does not have to be implemented manually!** It is +/// automatically implemented for every type that is both [`Readable`] +/// and [`Writeable`], as long as [`Readable::R`] == [`Writeable::R`] +/// (i.e. not for [`Aliased`](crate::registers::Aliased) registers). +pub trait ReadWriteable { + type T: UIntLike; + type R: RegisterLongName; + + /// Write the value of one or more fields, leaving the other fields unchanged + fn modify(&self, field: FieldValue<Self::T, Self::R>); +} + +impl<T: UIntLike, R: RegisterLongName, S> ReadWriteable for S +where + S: Readable<T = T, R = R> + Writeable<T = T, R = R>, +{ + type T = T; + type R = R; + + #[inline] + fn modify(&self, field: FieldValue<Self::T, Self::R>) { + self.set(field.modify(self.get())); + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..ecc7a09 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,128 @@ +//! Tock Register Interface +//! +//! Provides efficient mechanisms to express and use type-checked +//! memory mapped registers and bitfields. +//! +//! ```rust +//! # fn main() {} +//! +//! use tock_registers::registers::{ReadOnly, ReadWrite}; +//! use tock_registers::register_bitfields; +//! +//! // Register maps are specified like this: +//! #[repr(C)] +//! struct Registers { +//! // Control register: read-write +//! cr: ReadWrite<u32, Control::Register>, +//! // Status register: read-only +//! s: ReadOnly<u32, Status::Register>, +//! } +//! +//! // Register fields and definitions look like this: +//! register_bitfields![u32, +//! // Simpler bitfields are expressed concisely: +//! Control [ +//! /// Stop the Current Transfer +//! STOP 8, +//! /// Software Reset +//! SWRST 7, +//! /// Master Disable +//! MDIS 1, +//! /// Master Enable +//! MEN 0 +//! ], +//! +//! // More complex registers can express subtypes: +//! Status [ +//! TXCOMPLETE OFFSET(0) NUMBITS(1) [], +//! TXINTERRUPT OFFSET(1) NUMBITS(1) [], +//! RXCOMPLETE OFFSET(2) NUMBITS(1) [], +//! RXINTERRUPT OFFSET(3) NUMBITS(1) [], +//! MODE OFFSET(4) NUMBITS(3) [ +//! FullDuplex = 0, +//! HalfDuplex = 1, +//! Loopback = 2, +//! Disabled = 3 +//! ], +//! ERRORCOUNT OFFSET(6) NUMBITS(3) [] +//! ] +//! ]; +//! ``` +//! +//! Author +//! ------ +//! - Shane Leonard <shanel@stanford.edu> + +#![no_std] +// If we don't build any actual register types, we don't need unsafe +// code in this crate +#![cfg_attr(not(feature = "register_types"), forbid(unsafe_code))] + +pub mod fields; +pub mod interfaces; +pub mod macros; + +#[cfg(feature = "register_types")] +pub mod registers; + +mod local_register; +pub use local_register::LocalRegisterCopy; + +use core::ops::{BitAnd, BitOr, BitOrAssign, Not, Shl, Shr}; + +/// Trait representing the base type of registers. +/// +/// UIntLike defines basic properties of types required to +/// read/write/modify a register through its methods and supertrait +/// requirements. +/// +/// It features a range of default implementations for common unsigned +/// integer types, such as [`u8`], [`u16`], [`u32`], [`u64`], [`u128`], +/// and [`usize`]. +pub trait UIntLike: + BitAnd<Output = Self> + + BitOr<Output = Self> + + BitOrAssign + + Not<Output = Self> + + Eq + + Shr<usize, Output = Self> + + Shl<usize, Output = Self> + + Copy + + Clone +{ + /// Return the representation of the value `0` in the implementing + /// type. + /// + /// This can be used to acquire values of the [`UIntLike`] type, + /// even in generic implementations. For instance, to get the + /// value `1`, one can use `<T as UIntLike>::zero() + 1`. To get + /// the largest representable value, use a bitwise negation: `~(<T + /// as UIntLike>::zero())`. + fn zero() -> Self; +} + +// Helper macro for implementing the UIntLike trait on differrent +// types. +macro_rules! UIntLike_impl_for { + ($type:ty) => { + impl UIntLike for $type { + fn zero() -> Self { + 0 + } + } + }; +} + +UIntLike_impl_for!(u8); +UIntLike_impl_for!(u16); +UIntLike_impl_for!(u32); +UIntLike_impl_for!(u64); +UIntLike_impl_for!(u128); +UIntLike_impl_for!(usize); + +/// Descriptive name for each register. +pub trait RegisterLongName {} + +// Useful implementation for when no RegisterLongName is required +// (e.g. no fields need to be accessed, just the raw register values) +impl RegisterLongName for () {} diff --git a/src/local_register.rs b/src/local_register.rs new file mode 100644 index 0000000..15bf7d4 --- /dev/null +++ b/src/local_register.rs @@ -0,0 +1,129 @@ +//! Module containing the [`LocalRegisterCopy`] type. Please refer to +//! its documentation. + +use core::fmt; +use core::marker::PhantomData; + +use crate::fields::{Field, FieldValue, TryFromValue}; +use crate::{RegisterLongName, UIntLike}; + +/// A read-write copy of register contents. +/// +/// This behaves very similarly to a read-write register, but instead of doing a +/// volatile read to MMIO to get the value for each function call, a copy of the +/// register contents are stored locally in memory. This allows a peripheral +/// to do a single read on a register, and then check which bits are set without +/// having to do a full MMIO read each time. It also allows the value of the +/// register to be "cached" in case the peripheral driver needs to clear the +/// register in hardware yet still be able to check the bits. +/// You can write to a local register, which will modify the stored value, but +/// will not modify any hardware because it operates only on local copy. +/// +/// This type does not implement the +/// [`Readable`](crate::interfaces::Readable) and +/// [`Writeable`](crate::interfaces::Writeable) traits because it +/// requires a mutable reference to modify the contained value. It +/// still mirrors the interface which would be exposed by a type +/// implementing [`Readable`](crate::interfaces::Readable), +/// [`Writeable`](crate::interfaces::Writeable) and +/// [`ReadWriteable`](crate::interfaces::ReadWriteable). +#[derive(Copy, Clone)] +pub struct LocalRegisterCopy<T: UIntLike, R: RegisterLongName = ()> { + value: T, + associated_register: PhantomData<R>, +} + +impl<T: UIntLike, R: RegisterLongName> LocalRegisterCopy<T, R> { + pub const fn new(value: T) -> Self { + LocalRegisterCopy { + value: value, + associated_register: PhantomData, + } + } + + /// Get the raw register value + #[inline] + pub fn get(&self) -> T { + self.value + } + + /// Set the raw register value + #[inline] + pub fn set(&mut self, value: T) { + self.value = value; + } + + /// Read the value of the given field + #[inline] + pub fn read(&self, field: Field<T, R>) -> T { + field.read(self.get()) + } + + /// Read value of the given field as an enum member + #[inline] + pub fn read_as_enum<E: TryFromValue<T, EnumType = E>>(&self, field: Field<T, R>) -> Option<E> { + field.read_as_enum(self.get()) + } + + /// Write the value of one or more fields, overwriting the other fields with zero + #[inline] + pub fn write(&mut self, field: FieldValue<T, R>) { + self.set(field.value); + } + + /// Write the value of one or more fields, leaving the other fields unchanged + #[inline] + pub fn modify(&mut self, field: FieldValue<T, R>) { + self.set(field.modify(self.get())); + } + + /// Check if one or more bits in a field are set + #[inline] + pub fn is_set(&self, field: Field<T, R>) -> bool { + field.is_set(self.get()) + } + + /// Check if any specified parts of a field match + #[inline] + pub fn matches_any(&self, field: FieldValue<T, R>) -> bool { + field.matches_any(self.get()) + } + + /// Check if all specified parts of a field match + #[inline] + pub fn matches_all(&self, field: FieldValue<T, R>) -> bool { + field.matches_all(self.get()) + } + + /// Do a bitwise AND operation of the stored value and the passed in value + /// and return a new LocalRegisterCopy. + #[inline] + pub fn bitand(&self, rhs: T) -> LocalRegisterCopy<T, R> { + LocalRegisterCopy::new(self.value & rhs) + } +} + +impl<T: UIntLike + fmt::Debug, R: RegisterLongName> fmt::Debug for LocalRegisterCopy<T, R> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self.value) + } +} + +// Helper macro to implement From<LocalRegisterCopy<T: UIntLike>, R>> +// for <T: UIntLike> +macro_rules! From_impl_for { + ($type:ty) => { + impl<R: RegisterLongName> From<LocalRegisterCopy<$type, R>> for $type { + fn from(r: LocalRegisterCopy<$type, R>) -> $type { + r.value + } + } + }; +} + +From_impl_for!(u8); +From_impl_for!(u16); +From_impl_for!(u32); +From_impl_for!(u64); +From_impl_for!(u128); +From_impl_for!(usize); diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000..b8e2c89 --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,370 @@ +//! Macros for cleanly defining peripheral registers. + +#[macro_export] +macro_rules! register_fields { + // Macro entry point. + (@root $(#[$attr_struct:meta])* $vis_struct:vis $name:ident $(<$life:lifetime>)? { $($input:tt)* } ) => { + $crate::register_fields!( + @munch ( + $($input)* + ) -> { + $vis_struct struct $(#[$attr_struct])* $name $(<$life>)? + } + ); + }; + + // Print the struct once all fields have been munched. + (@munch + ( + $(#[$attr_end:meta])* + ($offset:expr => @END), + ) + -> {$vis_struct:vis struct $(#[$attr_struct:meta])* $name:ident $(<$life:lifetime>)? $( + $(#[$attr:meta])* + ($vis:vis $id:ident: $ty:ty) + )*} + ) => { + $(#[$attr_struct])* + #[repr(C)] + $vis_struct struct $name $(<$life>)? { + $( + $(#[$attr])* + $vis $id: $ty + ),* + } + }; + + // Munch field. + (@munch + ( + $(#[$attr:meta])* + ($offset_start:expr => $vis:vis $field:ident: $ty:ty), + $($after:tt)* + ) + -> {$($output:tt)*} + ) => { + $crate::register_fields!( + @munch ( + $($after)* + ) -> { + $($output)* + $(#[$attr])* + ($vis $field: $ty) + } + ); + }; + + // Munch padding. + (@munch + ( + $(#[$attr:meta])* + ($offset_start:expr => $padding:ident), + $(#[$attr_next:meta])* + ($offset_end:expr => $($next:tt)*), + $($after:tt)* + ) + -> {$($output:tt)*} + ) => { + $crate::register_fields!( + @munch ( + $(#[$attr_next])* + ($offset_end => $($next)*), + $($after)* + ) -> { + $($output)* + $(#[$attr])* + ($padding: [u8; $offset_end - $offset_start]) + } + ); + }; +} + +// TODO: All of the rustdoc tests below use a `should_fail` attribute instead of +// `should_panic` because a const panic will result in a failure to evaluate a +// constant value, and thus a compiler error. However, this means that these +// examples could break for unrelated reasons, trigger a compiler error, but not +// test the desired assertion any longer. This should be switched to a +// `should_panic`-akin attribute which works for const panics, once that is +// available. +/// Statically validate the size and offsets of the fields defined +/// within the register struct through the `register_structs!()` +/// macro. +/// +/// This macro expands to an expression which contains static +/// assertions about various parameters of the individual fields in +/// the register struct definition. It will test for: +/// +/// - Proper start offset of padding fields. It will fail in cases +/// such as +/// +/// ```should_fail +/// # #[macro_use] +/// # extern crate tock_registers; +/// # use tock_registers::register_structs; +/// # use tock_registers::registers::ReadWrite; +/// register_structs! { +/// UartRegisters { +/// (0x04 => _reserved), +/// (0x08 => foo: ReadWrite<u32>), +/// (0x0C => @END), +/// } +/// } +/// # // This is required for rustdoc to not place this code snipped into an +/// # // fn main() {...} function. +/// # fn main() { } +/// ``` +/// +/// In this example, the start offset of `_reserved` should have been `0x00` +/// instead of `0x04`. +/// +/// - Correct start offset and end offset (start offset of next field) in actual +/// fields. It will fail in cases such as +/// +/// ```should_fail +/// # #[macro_use] +/// # extern crate tock_registers; +/// # use tock_registers::register_structs; +/// # use tock_registers::registers::ReadWrite; +/// register_structs! { +/// UartRegisters { +/// (0x00 => foo: ReadWrite<u32>), +/// (0x05 => bar: ReadWrite<u32>), +/// (0x08 => @END), +/// } +/// } +/// # // This is required for rustdoc to not place this code snipped into an +/// # // fn main() {...} function. +/// # fn main() { } +/// ``` +/// +/// In this example, the start offset of `bar` and thus the end offset of +/// `foo` should have been `0x04` instead of `0x05`. +/// +/// - Invalid alignment of fields. +/// +/// - That the end marker matches the actual generated struct size. This will +/// fail in cases such as +/// +/// ```should_fail +/// # #[macro_use] +/// # extern crate tock_registers; +/// # use tock_registers::register_structs; +/// # use tock_registers::registers::ReadWrite; +/// register_structs! { +/// UartRegisters { +/// (0x00 => foo: ReadWrite<u32>), +/// (0x04 => bar: ReadWrite<u32>), +/// (0x10 => @END), +/// } +/// } +/// # // This is required for rustdoc to not place this code snipped into an +/// # // fn main() {...} function. +/// # fn main() { } +/// ``` +#[macro_export] +macro_rules! test_fields { + // This macro works by iterating over all defined fields, until it hits an + // ($size:expr => @END) field. Each iteration generates an expression which, + // when evaluated, yields the current byte offset in the fields. Thus, when + // reading a field or padding, the field or padding length must be added to + // the returned size. + // + // By feeding this expression recursively into the macro, deeper invocations + // can continue validating fields through knowledge of the current offset + // and the remaining fields. + // + // The nested expression returned by this macro is guaranteed to be + // const-evaluable. + + // Macro entry point. + (@root $struct:ident $(<$life:lifetime>)? { $($input:tt)* } ) => { + // Start recursion at offset 0. + $crate::test_fields!(@munch $struct $(<$life>)? ($($input)*) : (0, 0)); + }; + + // Consume the ($size:expr => @END) field, which MUST be the last field in + // the register struct. + (@munch $struct:ident $(<$life:lifetime>)? + ( + $(#[$attr_end:meta])* + ($size:expr => @END), + ) + : $stmts:expr + ) => { + const _: () = { + // We've reached the end! Normally it is sufficient to compare the + // struct's size to the reported end offet. However, we must + // evaluate the previous iterations' expressions for them to have an + // effect anyways, so we can perform an internal sanity check on + // this value as well. + const SUM_MAX_ALIGN: (usize, usize) = $stmts; + const SUM: usize = SUM_MAX_ALIGN.0; + const MAX_ALIGN: usize = SUM_MAX_ALIGN.1; + + // Internal sanity check. If we have reached this point and + // correctly iterated over the struct's fields, the current offset + // and the claimed end offset MUST be equal. + assert!(SUM == $size); + + const STRUCT_SIZE: usize = core::mem::size_of::<$struct $(<$life>)?>(); + const ALIGNMENT_CORRECTED_SIZE: usize = if $size % MAX_ALIGN != 0 { $size + (MAX_ALIGN - ($size % MAX_ALIGN)) } else { $size }; + + assert!( + STRUCT_SIZE == ALIGNMENT_CORRECTED_SIZE, + "{}", + concat!( + "Invalid size for struct ", + stringify!($struct), + " (expected ", + $size, + ", actual struct size differs)", + ), + ); + }; + }; + + // Consume a proper ($offset:expr => $field:ident: $ty:ty) field. + (@munch $struct:ident $(<$life:lifetime>)? + ( + $(#[$attr:meta])* + ($offset_start:expr => $vis:vis $field:ident: $ty:ty), + $(#[$attr_next:meta])* + ($offset_end:expr => $($next:tt)*), + $($after:tt)* + ) + : $output:expr + ) => { + $crate::test_fields!( + @munch $struct $(<$life>)? ( + $(#[$attr_next])* + ($offset_end => $($next)*), + $($after)* + ) : { + // Evaluate the previous iterations' expression to determine the + // current offset. + const SUM_MAX_ALIGN: (usize, usize) = $output; + const SUM: usize = SUM_MAX_ALIGN.0; + const MAX_ALIGN: usize = SUM_MAX_ALIGN.1; + + // Validate the start offset of the current field. This check is + // mostly relevant for when this is the first field in the + // struct, as any subsequent start offset error will be detected + // by an end offset error of the previous field. + assert!( + SUM == $offset_start, + "{}", + concat!( + "Invalid start offset for field ", + stringify!($field), + " (expected ", + $offset_start, + " but actual value differs)", + ), + ); + + // Validate that the start offset of the current field within + // the struct matches the type's minimum alignment constraint. + const ALIGN: usize = core::mem::align_of::<$ty>(); + // Clippy can tell that (align - 1) is zero for some fields, so + // we allow this lint and further encapsule the assert! as an + // expression, such that the allow attr can apply. + #[allow(clippy::bad_bit_mask)] + { + assert!( + SUM & (ALIGN - 1) == 0, + "{}", + concat!( + "Invalid alignment for field ", + stringify!($field), + " (offset differs from expected)", + ), + ); + } + + // Add the current field's length to the offset and validate the + // end offset of the field based on the next field's claimed + // start offset. + const NEW_SUM: usize = SUM + core::mem::size_of::<$ty>(); + assert!( + NEW_SUM == $offset_end, + "{}", + concat!( + "Invalid end offset for field ", + stringify!($field), + " (expected ", + $offset_end, + " but actual value differs)", + ), + ); + + // Determine the new maximum alignment. core::cmp::max(ALIGN, + // MAX_ALIGN) does not work here, as the function is not const. + const NEW_MAX_ALIGN: usize = if ALIGN > MAX_ALIGN { ALIGN } else { MAX_ALIGN }; + + // Provide the updated offset and alignment to the next + // iteration. + (NEW_SUM, NEW_MAX_ALIGN) + } + ); + }; + + // Consume a padding ($offset:expr => $padding:ident) field. + (@munch $struct:ident $(<$life:lifetime>)? + ( + $(#[$attr:meta])* + ($offset_start:expr => $padding:ident), + $(#[$attr_next:meta])* + ($offset_end:expr => $($next:tt)*), + $($after:tt)* + ) + : $output:expr + ) => { + $crate::test_fields!( + @munch $struct $(<$life>)? ( + $(#[$attr_next])* + ($offset_end => $($next)*), + $($after)* + ) : { + // Evaluate the previous iterations' expression to determine the + // current offset. + const SUM_MAX_ALIGN: (usize, usize) = $output; + const SUM: usize = SUM_MAX_ALIGN.0; + const MAX_ALIGN: usize = SUM_MAX_ALIGN.1; + + // Validate the start offset of the current padding field. This + // check is mostly relevant for when this is the first field in + // the struct, as any subsequent start offset error will be + // detected by an end offset error of the previous field. + assert!( + SUM == $offset_start, + concat!( + "Invalid start offset for padding ", + stringify!($padding), + " (expected ", + $offset_start, + " but actual value differs)", + ), + ); + + // The padding field is automatically sized. Provide the start + // offset of the next field to the next iteration. + ($offset_end, MAX_ALIGN) + } + ); + }; +} + +#[macro_export] +macro_rules! register_structs { + { + $( + $(#[$attr:meta])* + $vis_struct:vis $name:ident $(<$life:lifetime>)? { + $( $fields:tt )* + } + ),* + } => { + $( $crate::register_fields!(@root $(#[$attr])* $vis_struct $name $(<$life>)? { $($fields)* } ); )* + $( $crate::test_fields!(@root $name $(<$life>)? { $($fields)* } ); )* + }; +} diff --git a/src/registers.rs b/src/registers.rs new file mode 100644 index 0000000..c11a979 --- /dev/null +++ b/src/registers.rs @@ -0,0 +1,181 @@ +//! Implementation of included register types. +//! +//! This module provides a standard set of register types, which can +//! describe different access levels: +//! +//! - [`ReadWrite`] for registers which can be read and written to +//! - [`ReadOnly`] for registers which can only be read +//! - [`WriteOnly`] for registers which can only be written to +//! - [`Aliased`] for registers which can be both read and written, +//! but represent different registers depending on the operation +//! - [`InMemoryRegister`] provide a register-type in RAM using +//! volatile operations +//! +//! These types can be disabled by removing the `register_types` crate +//! feature (part of the default features). This is useful if this +//! crate should be used only as an interface library, or if all +//! unsafe code should be disabled. + +use core::cell::UnsafeCell; +use core::marker::PhantomData; + +use crate::interfaces::{Readable, Writeable}; +use crate::{RegisterLongName, UIntLike}; + +/// Read/Write registers. +/// +/// For accessing and manipulating the register contents, the +/// [`Readable`], [`Writeable`] and +/// [`ReadWriteable`](crate::interfaces::ReadWriteable) traits are +/// implemented. +// To successfully alias this structure onto hardware registers in memory, this +// struct must be exactly the size of the `T`. +#[repr(transparent)] +pub struct ReadWrite<T: UIntLike, R: RegisterLongName = ()> { + value: UnsafeCell<T>, + associated_register: PhantomData<R>, +} +impl<T: UIntLike, R: RegisterLongName> Readable for ReadWrite<T, R> { + type T = T; + type R = R; + + #[inline] + fn get(&self) -> Self::T { + unsafe { ::core::ptr::read_volatile(self.value.get()) } + } +} +impl<T: UIntLike, R: RegisterLongName> Writeable for ReadWrite<T, R> { + type T = T; + type R = R; + + #[inline] + fn set(&self, value: T) { + unsafe { ::core::ptr::write_volatile(self.value.get(), value) } + } +} + +/// Read-only registers. +/// +/// For accessing the register contents the [`Readable`] trait is +/// implemented. +// To successfully alias this structure onto hardware registers in memory, this +// struct must be exactly the size of the `T`. +#[repr(transparent)] +pub struct ReadOnly<T: UIntLike, R: RegisterLongName = ()> { + value: T, + associated_register: PhantomData<R>, +} +impl<T: UIntLike, R: RegisterLongName> Readable for ReadOnly<T, R> { + type T = T; + type R = R; + + #[inline] + fn get(&self) -> T { + unsafe { ::core::ptr::read_volatile(&self.value) } + } +} + +/// Write-only registers. +/// +/// For setting the register contents the [`Writeable`] trait is +/// implemented. +// To successfully alias this structure onto hardware registers in memory, this +// struct must be exactly the size of the `T`. +#[repr(transparent)] +pub struct WriteOnly<T: UIntLike, R: RegisterLongName = ()> { + value: UnsafeCell<T>, + associated_register: PhantomData<R>, +} +impl<T: UIntLike, R: RegisterLongName> Writeable for WriteOnly<T, R> { + type T = T; + type R = R; + + #[inline] + fn set(&self, value: T) { + unsafe { ::core::ptr::write_volatile(self.value.get(), value) } + } +} + +/// Read-only and write-only registers aliased to the same address. +/// +/// Unlike the [`ReadWrite`] register, this represents a register +/// which has different meanings based on if it is written or read. +/// This might be found on a device where control and status registers +/// are accessed via the same memory address via writes and reads, +/// respectively. +/// +/// This register implements [`Readable`] and [`Writeable`], but in +/// general does not implement +/// [`ReadWriteable`](crate::interfaces::ReadWriteable) (only if the +/// type parameters `R` and `W` are identical, in which case a +/// [`ReadWrite`] register might be a better choice). +// To successfully alias this structure onto hardware registers in memory, this +// struct must be exactly the size of the `T`. +#[repr(transparent)] +pub struct Aliased<T: UIntLike, R: RegisterLongName = (), W: RegisterLongName = ()> { + value: UnsafeCell<T>, + associated_register: PhantomData<(R, W)>, +} +impl<T: UIntLike, R: RegisterLongName, W: RegisterLongName> Readable for Aliased<T, R, W> { + type T = T; + type R = R; + + #[inline] + fn get(&self) -> Self::T { + unsafe { ::core::ptr::read_volatile(self.value.get()) } + } +} +impl<T: UIntLike, R: RegisterLongName, W: RegisterLongName> Writeable for Aliased<T, R, W> { + type T = T; + type R = W; + + #[inline] + fn set(&self, value: Self::T) { + unsafe { ::core::ptr::write_volatile(self.value.get(), value) } + } +} + +/// In memory volatile register. +/// +/// Like [`ReadWrite`], but can be safely constructed using the +/// [`InMemoryRegister::new`] method. It will always be initialized to +/// the passed in, well-defined initial value. +/// +/// For accessing and manipulating the register contents, the +/// [`Readable`], [`Writeable`] and +/// [`ReadWriteable`](crate::interfaces::ReadWriteable) traits are +/// implemented. +// To successfully alias this structure onto hardware registers in memory, this +// struct must be exactly the size of the `T`. +#[repr(transparent)] +pub struct InMemoryRegister<T: UIntLike, R: RegisterLongName = ()> { + value: UnsafeCell<T>, + associated_register: PhantomData<R>, +} + +impl<T: UIntLike, R: RegisterLongName> InMemoryRegister<T, R> { + pub const fn new(value: T) -> Self { + InMemoryRegister { + value: UnsafeCell::new(value), + associated_register: PhantomData, + } + } +} +impl<T: UIntLike, R: RegisterLongName> Readable for InMemoryRegister<T, R> { + type T = T; + type R = R; + + #[inline] + fn get(&self) -> Self::T { + unsafe { ::core::ptr::read_volatile(self.value.get()) } + } +} +impl<T: UIntLike, R: RegisterLongName> Writeable for InMemoryRegister<T, R> { + type T = T; + type R = R; + + #[inline] + fn set(&self, value: T) { + unsafe { ::core::ptr::write_volatile(self.value.get(), value) } + } +} |