[del] Add local copy of tock-registers with u128 impl of IntLike
This commit is contained in:
parent
b025fb6dd3
commit
b2c99f52c7
|
@ -113,8 +113,6 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "tock-registers"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f521a79accce68c417c9c77ce22108056b626126da1932f7e2e9b5bbffee0cea"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"nucleus"
|
||||
"nucleus",
|
||||
"crates/tock-registers"
|
||||
]
|
||||
|
||||
[profile.dev]
|
||||
|
@ -18,3 +19,6 @@ lto = true
|
|||
[profile.test]
|
||||
opt-level = 's'
|
||||
debug = true
|
||||
|
||||
[patch.crates-io]
|
||||
tock-registers = { path = 'crates/tock-registers' }
|
||||
|
|
|
@ -1,5 +1,48 @@
|
|||
# Changelog
|
||||
|
||||
## master
|
||||
|
||||
## 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)
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "tock-registers"
|
||||
version = "0.3.0"
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "tock-registers"
|
||||
version = "0.3.0"
|
||||
version = "0.6.0"
|
||||
authors = ["Tock Project Developers <tock-dev@googlegroups.com>"]
|
||||
description = "Memory-Mapped I/O and register interface developed for Tock."
|
||||
homepage = "https://www.tockos.org/"
|
||||
|
@ -13,3 +13,6 @@ edition = "2018"
|
|||
|
||||
[badges]
|
||||
travis-ci = { repository = "tock/tock", branch = "master" }
|
||||
|
||||
[features]
|
||||
no_std_unit_tests = []
|
||||
|
|
|
@ -9,12 +9,58 @@ 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.
|
||||
|
||||
Defining the registers is similar to the C-style approach, where each register
|
||||
is a field in a packed struct:
|
||||
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
|
||||
|
@ -31,21 +77,97 @@ struct Registers {
|
|||
byte0: ReadWrite<u8>,
|
||||
byte1: ReadWrite<u8>,
|
||||
short: ReadWrite<u16>,
|
||||
word: ReadWrite<u32>
|
||||
|
||||
// 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.
|
||||
}
|
||||
```
|
||||
|
||||
By default, `std` unit tests for the struct are generated as well (that is,
|
||||
tests attributed with `#[test]`). The unit tests make sure that the offsets and
|
||||
padding are consistent with the actual fields in the struct, and that alignment
|
||||
is correct.
|
||||
|
||||
Since those tests would break compilation in `custom-test-frameworks`
|
||||
environments, it is possible to opt out of the test generation. To do so, add
|
||||
the following cargo feature:
|
||||
|
||||
```toml
|
||||
[dependencies.tock-registers]
|
||||
version = "0.4.x"
|
||||
features = ["no_std_unit_tests"]
|
||||
```
|
||||
|
||||
WARNING: For now, the **unit tests checking offsets and alignments are not yet
|
||||
run** on `make ci-travis`. This means that leaving an unintentional gap between
|
||||
registers will **not** be caught. Instead, the `register_structs` macro will
|
||||
generate a struct with invalid offsets without warning. Please follow the
|
||||
discussion on https://github.com/tock/tock/pull/1393.
|
||||
|
||||
For example, the following call to the macro:
|
||||
|
||||
```rust
|
||||
register_structs! {
|
||||
Registers {
|
||||
(0x000 => foo: ReadOnly<u8>),
|
||||
(0x008 => bar: ReadOnly<u8>),
|
||||
(0x009 => @END),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
will generate the following struct, even though there is an unintentional gap of
|
||||
4 bytes between addresses `0x004` (the end of register `foo`) and `0x008` (the
|
||||
intended beginning of register `bar`).
|
||||
|
||||
```rust
|
||||
#[repr(C)]
|
||||
struct Registers {
|
||||
foo: ReadOnly<u32>,
|
||||
bar: ReadOnly<u32>,
|
||||
}
|
||||
```
|
||||
|
||||
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 for the bitfields. Can be u8, u16,
|
||||
// u32, or u64.
|
||||
u8,
|
||||
// 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.
|
||||
|
@ -56,7 +178,7 @@ register_bitfields! [
|
|||
// name OFFSET(shift) NUMBITS(num) [ /* optional values */ ]
|
||||
|
||||
// This is a two-bit field which includes bits 4 and 5
|
||||
RANGE OFFSET(4) NUMBITS(3) [
|
||||
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.
|
||||
|
@ -103,8 +225,8 @@ register_bitfields! [
|
|||
|
||||
## Register Interface Summary
|
||||
|
||||
There are three types provided by the register interface: `ReadOnly`,
|
||||
`WriteOnly`, and `ReadWrite`. They provide the following functions:
|
||||
There are four types provided by the register interface: `ReadOnly`,
|
||||
`WriteOnly`, `ReadWrite`, and `Aliased`. They provide the following functions:
|
||||
|
||||
```rust
|
||||
ReadOnly<T: IntLike, R: RegisterLongName = ()>
|
||||
|
@ -120,8 +242,6 @@ WriteOnly<T: IntLike, R: RegisterLongName = ()>
|
|||
.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: IntLike, R: RegisterLongName = ()>
|
||||
.get() -> T // Get the raw register value
|
||||
.set(value: T) // Set the raw register value
|
||||
|
@ -139,8 +259,22 @@ ReadWrite<T: IntLike, R: RegisterLongName = ()>
|
|||
.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: IntLike, R: RegisterLongName = (), W: RegisterLongName = ()>
|
||||
.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 `IntLike` type) is `u8`, `u16`, `u32`, or `u64`.
|
||||
|
||||
## Example: Using registers and bitfields
|
||||
|
@ -265,6 +399,23 @@ 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,
|
||||
|
@ -288,13 +439,13 @@ 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 CR and registers.cr are both associated with the
|
||||
// Control group of bitfields.
|
||||
// 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 CR is associated with the Control group,
|
||||
// while registers.s is associated with the Status group.
|
||||
registers.s.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
|
||||
|
|
|
@ -5,7 +5,5 @@
|
|||
#![feature(const_fn)]
|
||||
#![no_std]
|
||||
|
||||
#[macro_use]
|
||||
pub mod macros;
|
||||
|
||||
pub mod registers;
|
||||
|
|
|
@ -1,13 +1,21 @@
|
|||
//! Macros for cleanly defining peripheral registers.
|
||||
|
||||
/// 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:ty, $reg_desc:ident, [
|
||||
$( $(#[$inner:meta])* $field:ident OFFSET($offset:expr)),+
|
||||
$valtype:ident, $reg_desc:ident, [
|
||||
$( $(#[$inner:meta])* $field:ident OFFSET($offset:expr)),+ $(,)?
|
||||
]
|
||||
} => {
|
||||
$(#[$outer])*
|
||||
|
@ -17,8 +25,8 @@ macro_rules! register_bitmasks {
|
|||
// BITFIELD_NAME OFFSET
|
||||
// All fields are 1 bit
|
||||
$(#[$outer:meta])*
|
||||
$valtype:ty, $reg_desc:ident, [
|
||||
$( $(#[$inner:meta])* $field:ident $offset:expr ),+
|
||||
$valtype:ident, $reg_desc:ident, [
|
||||
$( $(#[$inner:meta])* $field:ident $offset:expr ),+ $(,)?
|
||||
]
|
||||
} => {
|
||||
$(#[$outer])*
|
||||
|
@ -28,8 +36,8 @@ macro_rules! register_bitmasks {
|
|||
{
|
||||
// BITFIELD_NAME OFFSET(x) NUMBITS(y)
|
||||
$(#[$outer:meta])*
|
||||
$valtype:ty, $reg_desc:ident, [
|
||||
$( $(#[$inner:meta])* $field:ident OFFSET($offset:expr) NUMBITS($numbits:expr) ),+
|
||||
$valtype:ident, $reg_desc:ident, [
|
||||
$( $(#[$inner:meta])* $field:ident OFFSET($offset:expr) NUMBITS($numbits:expr) ),+ $(,)?
|
||||
]
|
||||
} => {
|
||||
$(#[$outer])*
|
||||
|
@ -39,9 +47,9 @@ macro_rules! register_bitmasks {
|
|||
{
|
||||
// BITFIELD_NAME OFFSET(x) NUMBITS(y) []
|
||||
$(#[$outer:meta])*
|
||||
$valtype:ty, $reg_desc:ident, [
|
||||
$valtype:ident, $reg_desc:ident, [
|
||||
$( $(#[$inner:meta])* $field:ident OFFSET($offset:expr) NUMBITS($numbits:expr)
|
||||
$values:tt ),+
|
||||
$values:tt ),+ $(,)?
|
||||
]
|
||||
} => {
|
||||
$(#[$outer])*
|
||||
|
@ -49,14 +57,16 @@ macro_rules! register_bitmasks {
|
|||
$values); )*
|
||||
};
|
||||
{
|
||||
$valtype:ty, $reg_desc:ident, $(#[$outer:meta])* $field:ident,
|
||||
$valtype:ident, $reg_desc:ident, $(#[$outer:meta])* $field:ident,
|
||||
$offset:expr, $numbits:expr,
|
||||
[$( $(#[$inner:meta])* $valname:ident = $value: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((1<<($numbits-1))+((1<<($numbits-1))-1), $offset);
|
||||
Field::<$valtype, $reg_desc>::new($crate::bitmask!($numbits), $offset);
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[allow(unused)]
|
||||
|
@ -71,24 +81,25 @@ macro_rules! register_bitmasks {
|
|||
#[allow(unused)]
|
||||
$(#[$inner])*
|
||||
pub const $valname: FieldValue<$valtype, $reg_desc> =
|
||||
FieldValue::<$valtype, $reg_desc>::new((1<<($numbits-1))+((1<<($numbits-1))-1),
|
||||
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((1<<($numbits-1))+((1<<($numbits-1))-1),
|
||||
$offset, (1<<($numbits-1))+((1<<($numbits-1))-1));
|
||||
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((1<<($numbits-1))+((1<<($numbits-1))-1),
|
||||
FieldValue::<$valtype, $reg_desc>::new($crate::bitmask!($numbits),
|
||||
$offset, 0);
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[allow(non_camel_case_types)]
|
||||
#[repr($valtype)] // so that values larger than isize::MAX can be stored
|
||||
$(#[$outer])*
|
||||
pub enum Value {
|
||||
$(
|
||||
|
@ -113,18 +124,67 @@ macro_rules! register_bitmasks {
|
|||
}
|
||||
}
|
||||
};
|
||||
{
|
||||
$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::registers::{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(_v: $valtype) -> Option<Self::EnumType> {
|
||||
Option::None
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Define register types and fields.
|
||||
#[macro_export]
|
||||
macro_rules! register_bitfields {
|
||||
{
|
||||
$valtype:ty, $( $(#[$inner:meta])* $reg:ident $fields:tt ),*
|
||||
$valtype:ident, $( $(#[$inner:meta])* $vis:vis $reg:ident $fields:tt ),* $(,)?
|
||||
} => {
|
||||
$(
|
||||
#[allow(non_snake_case)]
|
||||
$(#[$inner])*
|
||||
pub mod $reg {
|
||||
$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::registers::RegisterLongName for Register {}
|
||||
|
@ -136,3 +196,242 @@ macro_rules! register_bitfields {
|
|||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[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])
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! test_fields {
|
||||
// Macro entry point.
|
||||
(@root $struct:ident $(<$life:lifetime>)? { $($input:tt)* } ) => {
|
||||
$crate::test_fields!(@munch $struct $(<$life>)? sum ($($input)*) -> {});
|
||||
};
|
||||
|
||||
// Print the tests once all fields have been munched.
|
||||
// We wrap the tests in a "detail" function that potentially takes a lifetime parameter, so that
|
||||
// the lifetime is declared inside it - therefore all types using the lifetime are well-defined.
|
||||
(@munch $struct:ident $(<$life:lifetime>)? $sum:ident
|
||||
(
|
||||
$(#[$attr_end:meta])*
|
||||
($size:expr => @END),
|
||||
)
|
||||
-> {$($stmts:block)*}
|
||||
) => {
|
||||
{
|
||||
fn detail $(<$life>)? ()
|
||||
{
|
||||
let mut $sum: usize = 0;
|
||||
$($stmts)*
|
||||
let size = core::mem::size_of::<$struct $(<$life>)?>();
|
||||
assert!(
|
||||
size == $size,
|
||||
"Invalid size for struct {} (expected {:#X} but was {:#X})",
|
||||
stringify!($struct),
|
||||
$size,
|
||||
size
|
||||
);
|
||||
}
|
||||
|
||||
detail();
|
||||
}
|
||||
};
|
||||
|
||||
// Munch field.
|
||||
(@munch $struct:ident $(<$life:lifetime>)? $sum:ident
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
($offset_start:expr => $vis:vis $field:ident: $ty:ty),
|
||||
$(#[$attr_next:meta])*
|
||||
($offset_end:expr => $($next:tt)*),
|
||||
$($after:tt)*
|
||||
)
|
||||
-> {$($output:block)*}
|
||||
) => {
|
||||
$crate::test_fields!(
|
||||
@munch $struct $(<$life>)? $sum (
|
||||
$(#[$attr_next])*
|
||||
($offset_end => $($next)*),
|
||||
$($after)*
|
||||
) -> {
|
||||
$($output)*
|
||||
{
|
||||
assert!(
|
||||
$sum == $offset_start,
|
||||
"Invalid start offset for field {} (expected {:#X} but was {:#X})",
|
||||
stringify!($field),
|
||||
$offset_start,
|
||||
$sum
|
||||
);
|
||||
let align = core::mem::align_of::<$ty>();
|
||||
assert!(
|
||||
$sum & (align - 1) == 0,
|
||||
"Invalid alignment for field {} (expected alignment of {:#X} but offset was {:#X})",
|
||||
stringify!($field),
|
||||
align,
|
||||
$sum
|
||||
);
|
||||
$sum += core::mem::size_of::<$ty>();
|
||||
assert!(
|
||||
$sum == $offset_end,
|
||||
"Invalid end offset for field {} (expected {:#X} but was {:#X})",
|
||||
stringify!($field),
|
||||
$offset_end,
|
||||
$sum
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
// Munch padding.
|
||||
(@munch $struct:ident $(<$life:lifetime>)? $sum:ident
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
($offset_start:expr => $padding:ident),
|
||||
$(#[$attr_next:meta])*
|
||||
($offset_end:expr => $($next:tt)*),
|
||||
$($after:tt)*
|
||||
)
|
||||
-> {$($output:block)*}
|
||||
) => {
|
||||
$crate::test_fields!(
|
||||
@munch $struct $(<$life>)? $sum (
|
||||
$(#[$attr_next])*
|
||||
($offset_end => $($next)*),
|
||||
$($after)*
|
||||
) -> {
|
||||
$($output)*
|
||||
{
|
||||
assert!(
|
||||
$sum == $offset_start,
|
||||
"Invalid start offset for padding {} (expected {:#X} but was {:#X})",
|
||||
stringify!($padding),
|
||||
$offset_start,
|
||||
$sum
|
||||
);
|
||||
$sum = $offset_end;
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_std_unit_tests"))]
|
||||
#[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)* } ); )*
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_register_structs {
|
||||
$(
|
||||
#[allow(non_snake_case)]
|
||||
mod $name {
|
||||
use super::super::*;
|
||||
#[test]
|
||||
fn test_offsets() {
|
||||
$crate::test_fields!(@root $name $(<$life>)? { $($fields)* } )
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "no_std_unit_tests")]
|
||||
#[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)* } ); )*
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,11 +4,10 @@
|
|||
//! registers and bitfields.
|
||||
//!
|
||||
//! ```rust
|
||||
//! # #[macro_use]
|
||||
//! # extern crate tock_registers;
|
||||
//! # fn main() {}
|
||||
//!
|
||||
//! use tock_registers::registers::{ReadOnly, ReadWrite};
|
||||
//! use tock_registers::register_bitfields;
|
||||
//!
|
||||
//! // Register maps are specified like this:
|
||||
//! #[repr(C)]
|
||||
|
@ -60,14 +59,16 @@
|
|||
#![allow(clippy::suspicious_op_assign_impl)]
|
||||
#![allow(clippy::suspicious_arithmetic_impl)]
|
||||
|
||||
use core::cell::UnsafeCell;
|
||||
use core::fmt;
|
||||
use core::marker::PhantomData;
|
||||
use core::ops::{Add, AddAssign, BitAnd, BitOr, Not, Shl, Shr};
|
||||
use core::ops::{Add, AddAssign, BitAnd, BitOr, BitOrAssign, Not, Shl, Shr};
|
||||
|
||||
/// IntLike properties needed to read/write/modify a register.
|
||||
pub trait IntLike:
|
||||
BitAnd<Output = Self>
|
||||
+ BitOr<Output = Self>
|
||||
+ BitOrAssign
|
||||
+ Not<Output = Self>
|
||||
+ Eq
|
||||
+ Shr<usize, Output = Self>
|
||||
|
@ -93,12 +94,16 @@ impl IntLike for u32 {
|
|||
0
|
||||
}
|
||||
}
|
||||
|
||||
impl IntLike for u64 {
|
||||
fn zero() -> Self {
|
||||
0
|
||||
}
|
||||
}
|
||||
impl IntLike for u128 {
|
||||
fn zero() -> Self {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
/// Descriptive name for each register.
|
||||
pub trait RegisterLongName {}
|
||||
|
@ -114,155 +119,230 @@ pub trait TryFromValue<V> {
|
|||
}
|
||||
|
||||
/// Read/Write registers.
|
||||
// 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: IntLike, R: RegisterLongName = ()> {
|
||||
value: T,
|
||||
value: UnsafeCell<T>,
|
||||
associated_register: PhantomData<R>,
|
||||
}
|
||||
|
||||
/// Read-only registers.
|
||||
// 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: IntLike, R: RegisterLongName = ()> {
|
||||
value: T,
|
||||
associated_register: PhantomData<R>,
|
||||
}
|
||||
|
||||
/// Write-only registers.
|
||||
// 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: IntLike, R: RegisterLongName = ()> {
|
||||
value: T,
|
||||
value: UnsafeCell<T>,
|
||||
associated_register: PhantomData<R>,
|
||||
}
|
||||
|
||||
/// 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.
|
||||
// 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: IntLike, R: RegisterLongName = (), W: RegisterLongName = ()> {
|
||||
value: UnsafeCell<T>,
|
||||
associated_register: PhantomData<(R, W)>,
|
||||
}
|
||||
|
||||
impl<T: IntLike, R: RegisterLongName> ReadWrite<T, R> {
|
||||
pub const fn new(value: T) -> Self {
|
||||
ReadWrite {
|
||||
value: value,
|
||||
associated_register: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Get the raw register value
|
||||
pub fn get(&self) -> T {
|
||||
unsafe { ::core::ptr::read_volatile(&self.value) }
|
||||
unsafe { ::core::ptr::read_volatile(self.value.get()) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Set the raw register value
|
||||
pub fn set(&self, value: T) {
|
||||
unsafe { ::core::ptr::write_volatile(&self.value as *const T as *mut T, value) }
|
||||
unsafe { ::core::ptr::write_volatile(self.value.get(), value) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Read the value of the given field
|
||||
pub fn read(&self, field: Field<T, R>) -> T {
|
||||
(self.get() & (field.mask << field.shift)) >> field.shift
|
||||
field.read(self.get())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Read value of the given field as an enum member
|
||||
pub fn read_as_enum<E: TryFromValue<T, EnumType = E>>(&self, field: Field<T, R>) -> Option<E> {
|
||||
let val: T = self.read(field);
|
||||
|
||||
E::try_from(val)
|
||||
field.read_as_enum(self.get())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Make a local copy of the register
|
||||
pub fn extract(&self) -> LocalRegisterCopy<T, R> {
|
||||
LocalRegisterCopy::new(self.get())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Write the value of one or more fields, overwriting the other fields with zero
|
||||
pub fn write(&self, field: FieldValue<T, R>) {
|
||||
self.set(field.value);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Write the value of one or more fields, leaving the other fields unchanged
|
||||
pub fn modify(&self, field: FieldValue<T, R>) {
|
||||
let reg: T = self.get();
|
||||
self.set((reg & !field.mask) | field.value);
|
||||
self.set(field.modify(self.get()));
|
||||
}
|
||||
|
||||
#[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.
|
||||
pub fn modify_no_read(&self, original: LocalRegisterCopy<T, R>, field: FieldValue<T, R>) {
|
||||
self.set((original.get() & !field.mask) | field.value);
|
||||
self.set(field.modify(original.get()));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Check if one or more bits in a field are set
|
||||
pub fn is_set(&self, field: Field<T, R>) -> bool {
|
||||
self.read(field) != T::zero()
|
||||
field.is_set(self.get())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Check if any specified parts of a field match
|
||||
pub fn matches_any(&self, field: FieldValue<T, R>) -> bool {
|
||||
self.get() & field.mask != T::zero()
|
||||
field.matches_any(self.get())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Check if all specified parts of a field match
|
||||
pub fn matches_all(&self, field: FieldValue<T, R>) -> bool {
|
||||
self.get() & field.mask == field.value
|
||||
field.matches_all(self.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IntLike, R: RegisterLongName> ReadOnly<T, R> {
|
||||
pub const fn new(value: T) -> Self {
|
||||
ReadOnly {
|
||||
value: value,
|
||||
associated_register: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Get the raw register value
|
||||
pub fn get(&self) -> T {
|
||||
unsafe { ::core::ptr::read_volatile(&self.value) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Read the value of the given field
|
||||
pub fn read(&self, field: Field<T, R>) -> T {
|
||||
(self.get() & (field.mask << field.shift)) >> field.shift
|
||||
field.read(self.get())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Read value of the given field as an enum member
|
||||
pub fn read_as_enum<E: TryFromValue<T, EnumType = E>>(&self, field: Field<T, R>) -> Option<E> {
|
||||
let val: T = self.read(field);
|
||||
|
||||
E::try_from(val)
|
||||
field.read_as_enum(self.get())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Make a local copy of the register
|
||||
pub fn extract(&self) -> LocalRegisterCopy<T, R> {
|
||||
LocalRegisterCopy::new(self.get())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Check if one or more bits in a field are set
|
||||
pub fn is_set(&self, field: Field<T, R>) -> bool {
|
||||
self.read(field) != T::zero()
|
||||
field.is_set(self.get())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Check if any specified parts of a field match
|
||||
pub fn matches_any(&self, field: FieldValue<T, R>) -> bool {
|
||||
self.get() & field.mask != T::zero()
|
||||
field.matches_any(self.get())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Check if all specified parts of a field match
|
||||
pub fn matches_all(&self, field: FieldValue<T, R>) -> bool {
|
||||
self.get() & field.mask == field.value
|
||||
field.matches_all(self.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IntLike, R: RegisterLongName> WriteOnly<T, R> {
|
||||
pub const fn new(value: T) -> Self {
|
||||
WriteOnly {
|
||||
value: value,
|
||||
associated_register: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Set the raw register value
|
||||
pub fn set(&self, value: T) {
|
||||
unsafe { ::core::ptr::write_volatile(&self.value as *const T as *mut T, value) }
|
||||
unsafe { ::core::ptr::write_volatile(self.value.get(), value) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Write the value of one or more fields, overwriting the other fields with zero
|
||||
pub fn write(&self, field: FieldValue<T, R>) {
|
||||
self.set(field.value);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IntLike, R: RegisterLongName, W: RegisterLongName> Aliased<T, R, W> {
|
||||
#[inline]
|
||||
/// Get the raw register value
|
||||
pub fn get(&self) -> T {
|
||||
unsafe { ::core::ptr::read_volatile(self.value.get()) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Set the raw register value
|
||||
pub fn set(&self, value: T) {
|
||||
unsafe { ::core::ptr::write_volatile(self.value.get(), value) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Read the value of the given field
|
||||
pub fn read(&self, field: Field<T, R>) -> T {
|
||||
field.read(self.get())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Read value of the given field as an enum member
|
||||
pub fn read_as_enum<E: TryFromValue<T, EnumType = E>>(&self, field: Field<T, R>) -> Option<E> {
|
||||
field.read_as_enum(self.get())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Make a local copy of the register
|
||||
pub fn extract(&self) -> LocalRegisterCopy<T, R> {
|
||||
LocalRegisterCopy::new(self.get())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Write the value of one or more fields, overwriting the other fields with zero
|
||||
pub fn write(&self, field: FieldValue<T, W>) {
|
||||
self.set(field.value);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Check if one or more bits in a field are set
|
||||
pub fn is_set(&self, field: Field<T, R>) -> bool {
|
||||
field.is_set(self.get())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Check if any specified parts of a field match
|
||||
pub fn matches_any(&self, field: FieldValue<T, R>) -> bool {
|
||||
field.matches_any(self.get())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Check if all specified parts of a field match
|
||||
pub fn matches_all(&self, field: FieldValue<T, R>) -> bool {
|
||||
field.matches_all(self.get())
|
||||
}
|
||||
}
|
||||
|
||||
/// A read-only copy register contents
|
||||
///
|
||||
/// This behaves very similarly to a read-only 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
|
||||
|
@ -291,29 +371,27 @@ impl<T: IntLike, R: RegisterLongName> LocalRegisterCopy<T, R> {
|
|||
|
||||
#[inline]
|
||||
pub fn read(&self, field: Field<T, R>) -> T {
|
||||
(self.value & (field.mask << field.shift)) >> field.shift
|
||||
field.read(self.get())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_as_enum<E: TryFromValue<T, EnumType = E>>(&self, field: Field<T, R>) -> Option<E> {
|
||||
let val: T = self.read(field);
|
||||
|
||||
E::try_from(val)
|
||||
field.read_as_enum(self.get())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_set(&self, field: Field<T, R>) -> bool {
|
||||
self.read(field) != T::zero()
|
||||
field.is_set(self.get())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn matches_any(&self, field: FieldValue<T, R>) -> bool {
|
||||
self.value & field.mask != T::zero()
|
||||
field.matches_any(self.get())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn matches_all(&self, field: FieldValue<T, R>) -> bool {
|
||||
self.value & field.mask == field.value
|
||||
field.matches_all(self.get())
|
||||
}
|
||||
|
||||
/// Do a bitwise AND operation of the stored value and the passed in value
|
||||
|
@ -354,14 +432,106 @@ impl<R: RegisterLongName> From<LocalRegisterCopy<u64, R>> for u64 {
|
|||
}
|
||||
}
|
||||
|
||||
/// In memory volatile register.
|
||||
// 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: IntLike, R: RegisterLongName = ()> {
|
||||
value: UnsafeCell<T>,
|
||||
associated_register: PhantomData<R>,
|
||||
}
|
||||
|
||||
impl<T: IntLike, R: RegisterLongName> InMemoryRegister<T, R> {
|
||||
pub const fn new(value: T) -> Self {
|
||||
InMemoryRegister {
|
||||
value: UnsafeCell::new(value),
|
||||
associated_register: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get(&self) -> T {
|
||||
unsafe { ::core::ptr::read_volatile(self.value.get()) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set(&self, value: T) {
|
||||
unsafe { ::core::ptr::write_volatile(self.value.get(), value) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read(&self, field: Field<T, R>) -> T {
|
||||
field.read(self.get())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_as_enum<E: TryFromValue<T, EnumType = E>>(&self, field: Field<T, R>) -> Option<E> {
|
||||
field.read_as_enum(self.get())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn extract(&self) -> LocalRegisterCopy<T, R> {
|
||||
LocalRegisterCopy::new(self.get())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn write(&self, field: FieldValue<T, R>) {
|
||||
self.set(field.value);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn modify(&self, field: FieldValue<T, R>) {
|
||||
self.set(field.modify(self.get()));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn modify_no_read(&self, original: LocalRegisterCopy<T, R>, field: FieldValue<T, R>) {
|
||||
self.set(field.modify(original.get()));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_set(&self, field: Field<T, R>) -> bool {
|
||||
field.is_set(self.get())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn matches_any(&self, field: FieldValue<T, R>) -> bool {
|
||||
field.matches_any(self.get())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn matches_all(&self, field: FieldValue<T, R>) -> bool {
|
||||
field.matches_all(self.get())
|
||||
}
|
||||
}
|
||||
|
||||
/// Specific section of a register.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Field<T: IntLike, R: RegisterLongName> {
|
||||
pub mask: T,
|
||||
mask: T,
|
||||
pub shift: usize,
|
||||
associated_register: PhantomData<R>,
|
||||
}
|
||||
|
||||
impl<T: IntLike, R: RegisterLongName> Field<T, R> {
|
||||
#[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
|
||||
pub fn read_as_enum<E: TryFromValue<T, EnumType = E>>(self, val: T) -> Option<E> {
|
||||
E::try_from(self.read(val))
|
||||
}
|
||||
}
|
||||
|
||||
// For the Field, the mask is unshifted, ie. the LSB should always be set
|
||||
impl<R: RegisterLongName> Field<u8, R> {
|
||||
pub const fn new(mask: u8, shift: usize) -> Field<u8, R> {
|
||||
|
@ -424,26 +594,22 @@ impl<R: RegisterLongName> Field<u64, R> {
|
|||
// location in the register.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct FieldValue<T: IntLike, R: RegisterLongName> {
|
||||
pub mask: T,
|
||||
mask: T,
|
||||
pub value: T,
|
||||
associated_register: PhantomData<R>,
|
||||
}
|
||||
|
||||
// Necessary to split the implementation of u8 and u32 out because the bitwise
|
||||
// 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<u8, R> {
|
||||
pub const fn new(mask: u8, shift: usize, value: u8) -> Self {
|
||||
FieldValue {
|
||||
mask: mask << shift,
|
||||
value: (value << shift) & (mask << shift),
|
||||
value: (value & mask) << shift,
|
||||
associated_register: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the raw bitmask represented by this FieldValue.
|
||||
pub fn mask(self) -> u8 {
|
||||
self.mask as u8
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: RegisterLongName> From<FieldValue<u8, R>> for u8 {
|
||||
|
@ -456,7 +622,7 @@ impl<R: RegisterLongName> FieldValue<u16, R> {
|
|||
pub const fn new(mask: u16, shift: usize, value: u16) -> Self {
|
||||
FieldValue {
|
||||
mask: mask << shift,
|
||||
value: (value << shift) & (mask << shift),
|
||||
value: (value & mask) << shift,
|
||||
associated_register: PhantomData,
|
||||
}
|
||||
}
|
||||
|
@ -472,15 +638,10 @@ impl<R: RegisterLongName> FieldValue<u32, R> {
|
|||
pub const fn new(mask: u32, shift: usize, value: u32) -> Self {
|
||||
FieldValue {
|
||||
mask: mask << shift,
|
||||
value: (value << shift) & (mask << shift),
|
||||
value: (value & mask) << shift,
|
||||
associated_register: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the raw bitmask represented by this FieldValue.
|
||||
pub fn mask(self) -> u32 {
|
||||
self.mask as u32
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: RegisterLongName> From<FieldValue<u32, R>> for u32 {
|
||||
|
@ -493,15 +654,10 @@ impl<R: RegisterLongName> FieldValue<u64, R> {
|
|||
pub const fn new(mask: u64, shift: usize, value: u64) -> Self {
|
||||
FieldValue {
|
||||
mask: mask << shift,
|
||||
value: (value << shift) & (mask << shift),
|
||||
value: (value & mask) << shift,
|
||||
associated_register: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the raw bitmask represented by this FieldValue.
|
||||
pub fn mask(self) -> u64 {
|
||||
self.mask as u64
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: RegisterLongName> From<FieldValue<u64, R>> for u64 {
|
||||
|
@ -511,10 +667,30 @@ impl<R: RegisterLongName> From<FieldValue<u64, R>> for u64 {
|
|||
}
|
||||
|
||||
impl<T: IntLike, R: RegisterLongName> FieldValue<T, R> {
|
||||
// Modify fields in a register value
|
||||
/// Get the raw bitmask represented by this FieldValue.
|
||||
pub 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
|
||||
pub fn modify(self, val: T) -> T {
|
||||
(val & !self.mask) | self.value
|
||||
}
|
||||
|
||||
/// Check if any specified parts of a field match
|
||||
pub fn matches_any(self, val: T) -> bool {
|
||||
val & self.mask != T::zero()
|
||||
}
|
||||
|
||||
/// Check if all specified parts of a field match
|
||||
pub fn matches_all(self, val: T) -> bool {
|
||||
val & self.mask == self.value
|
||||
}
|
||||
}
|
||||
|
||||
// Combine two fields with the addition operator
|
||||
|
@ -532,10 +708,270 @@ impl<T: IntLike, R: RegisterLongName> Add for FieldValue<T, R> {
|
|||
// Combine two fields with the += operator
|
||||
impl<T: IntLike, R: RegisterLongName> AddAssign for FieldValue<T, R> {
|
||||
fn add_assign(&mut self, rhs: FieldValue<T, R>) {
|
||||
*self = FieldValue {
|
||||
mask: self.mask | rhs.mask,
|
||||
value: self.value | rhs.value,
|
||||
associated_register: PhantomData,
|
||||
};
|
||||
self.mask |= rhs.mask;
|
||||
self.value |= rhs.value;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_std_unit_tests"))]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum Foo {
|
||||
Foo0,
|
||||
Foo1,
|
||||
Foo2,
|
||||
Foo3,
|
||||
Foo4,
|
||||
Foo5,
|
||||
Foo6,
|
||||
Foo7,
|
||||
}
|
||||
|
||||
impl super::TryFromValue<u16> for Foo {
|
||||
type EnumType = Foo;
|
||||
|
||||
fn try_from(v: u16) -> Option<Self::EnumType> {
|
||||
Self::try_from(v as u32)
|
||||
}
|
||||
}
|
||||
impl super::TryFromValue<u32> for Foo {
|
||||
type EnumType = Foo;
|
||||
|
||||
fn try_from(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::super::{Field, TryFromValue};
|
||||
use super::Foo;
|
||||
|
||||
#[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);
|
||||
}
|
||||
|
||||
#[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(x));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod field_value {
|
||||
use super::super::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.
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue