[del] git subrepo clone git@github.com:metta-systems/tock-registers.git crates/tock-registers
subrepo: subdir: "crates/tock-registers" merged: "4d8202452" upstream: origin: "git@github.com:metta-systems/tock-registers.git" branch: "master" commit: "4d8202452" git-subrepo: version: "0.3.1" origin: "???" commit: "???"
This commit is contained in:
parent
be1873b678
commit
15a38d5689
|
@ -0,0 +1,11 @@
|
|||
; DO NOT EDIT (unless you know what you are doing)
|
||||
;
|
||||
; This subdirectory is a git "subrepo", and this file is maintained by the
|
||||
; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme
|
||||
;
|
||||
[subrepo]
|
||||
remote = git@github.com:metta-systems/tock-registers.git
|
||||
branch = master
|
||||
commit = 4d8202452f5b7f1c056b524eac62445b74ceebce
|
||||
parent = d78ffd283311c6a257f3cc93df8bc4cfe42bb44a
|
||||
cmdver = 0.3.1
|
|
@ -0,0 +1,12 @@
|
|||
# Changelog
|
||||
|
||||
## 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
|
|
@ -0,0 +1,6 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "tock-registers"
|
||||
version = "0.3.0"
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
[package]
|
||||
name = "tock-registers"
|
||||
version = "0.3.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/"
|
||||
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 = "2018"
|
||||
|
||||
[badges]
|
||||
travis-ci = { repository = "tock/tock", branch = "master" }
|
|
@ -0,0 +1,332 @@
|
|||
# Tock Register Interface
|
||||
|
||||
This crate provides an interface for defining and manipulating memory mapped
|
||||
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.
|
||||
|
||||
Defining the registers is similar to the C-style approach, where each register
|
||||
is a field in a packed struct:
|
||||
|
||||
```rust
|
||||
use tock_registers::registers::{ReadOnly, ReadWrite, WriteOnly};
|
||||
|
||||
#[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>,
|
||||
word: ReadWrite<u32>
|
||||
|
||||
// Etc.
|
||||
}
|
||||
```
|
||||
|
||||
## 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,
|
||||
|
||||
// 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(3) [
|
||||
// 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 three types provided by the register interface: `ReadOnly`,
|
||||
`WriteOnly`, and `ReadWrite`. They provide the following functions:
|
||||
|
||||
```rust
|
||||
ReadOnly<T: IntLike, R: RegisterLongName = ()>
|
||||
.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: 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
|
||||
.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
|
||||
|
||||
```
|
||||
|
||||
The first type parameter (the `IntLike` type) is `u8`, `u16`, `u32`, or `u64`.
|
||||
|
||||
## 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);
|
||||
|
||||
// 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::FullDuplex) => { /* ... */ }
|
||||
Some(Status::MODE::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);
|
||||
```
|
||||
|
||||
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 CR and registers.cr are both 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));
|
||||
```
|
||||
|
||||
## 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
|
||||
]
|
||||
]
|
||||
]
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
//! Tock Register Interface
|
||||
//!
|
||||
//!
|
||||
|
||||
#![feature(const_fn)]
|
||||
#![no_std]
|
||||
|
||||
#[macro_use]
|
||||
pub mod macros;
|
||||
|
||||
pub mod registers;
|
|
@ -0,0 +1,138 @@
|
|||
//! Macros for cleanly defining peripheral registers.
|
||||
|
||||
/// 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)),+
|
||||
]
|
||||
} => {
|
||||
$(#[$outer])*
|
||||
$( $crate::register_bitmasks!($valtype, $reg_desc, $(#[$inner])* $field, $offset, 1, []); )*
|
||||
};
|
||||
{
|
||||
// BITFIELD_NAME OFFSET
|
||||
// All fields are 1 bit
|
||||
$(#[$outer:meta])*
|
||||
$valtype:ty, $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:ty, $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:ty, $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:ty, $reg_desc:ident, $(#[$outer:meta])* $field:ident,
|
||||
$offset:expr, $numbits:expr,
|
||||
[$( $(#[$inner:meta])* $valname:ident = $value:expr ),*]
|
||||
} => {
|
||||
#[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);
|
||||
|
||||
#[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)]
|
||||
$(#[$inner])*
|
||||
pub const $valname: FieldValue<$valtype, $reg_desc> =
|
||||
FieldValue::<$valtype, $reg_desc>::new((1<<($numbits-1))+((1<<($numbits-1))-1),
|
||||
$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));
|
||||
|
||||
#[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),
|
||||
$offset, 0);
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[allow(non_camel_case_types)]
|
||||
$(#[$outer])*
|
||||
pub enum Value {
|
||||
$(
|
||||
$(#[$inner])*
|
||||
$valname = $value,
|
||||
)*
|
||||
}
|
||||
|
||||
impl TryFromValue<$valtype> for Value {
|
||||
type EnumType = Value;
|
||||
|
||||
fn try_from(v: $valtype) -> Option<Self::EnumType> {
|
||||
match v {
|
||||
$(
|
||||
$(#[$inner])*
|
||||
x if x == Value::$valname as $valtype => Some(Value::$valname),
|
||||
)*
|
||||
|
||||
_ => Option::None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Define register types and fields.
|
||||
#[macro_export]
|
||||
macro_rules! register_bitfields {
|
||||
{
|
||||
$valtype:ty, $( $(#[$inner:meta])* $reg:ident $fields:tt ),*
|
||||
} => {
|
||||
$(
|
||||
#[allow(non_snake_case)]
|
||||
$(#[$inner])*
|
||||
pub mod $reg {
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Register;
|
||||
impl $crate::registers::RegisterLongName for Register {}
|
||||
|
||||
use $crate::registers::Field;
|
||||
|
||||
$crate::register_bitmasks!( $valtype, Register, $fields );
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
|
@ -0,0 +1,541 @@
|
|||
//! Implementation of registers and bitfields.
|
||||
//!
|
||||
//! Provides efficient mechanisms to express and use type-checked memory mapped
|
||||
//! registers and bitfields.
|
||||
//!
|
||||
//! ```rust
|
||||
//! # #[macro_use]
|
||||
//! # extern crate tock_registers;
|
||||
//! # fn main() {}
|
||||
//!
|
||||
//! use tock_registers::registers::{ReadOnly, ReadWrite};
|
||||
//!
|
||||
//! // 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>
|
||||
|
||||
// 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::fmt;
|
||||
use core::marker::PhantomData;
|
||||
use core::ops::{Add, AddAssign, BitAnd, BitOr, Not, Shl, Shr};
|
||||
|
||||
/// IntLike properties needed to read/write/modify a register.
|
||||
pub trait IntLike:
|
||||
BitAnd<Output = Self>
|
||||
+ BitOr<Output = Self>
|
||||
+ Not<Output = Self>
|
||||
+ Eq
|
||||
+ Shr<usize, Output = Self>
|
||||
+ Shl<usize, Output = Self>
|
||||
+ Copy
|
||||
+ Clone
|
||||
{
|
||||
fn zero() -> Self;
|
||||
}
|
||||
|
||||
impl IntLike for u8 {
|
||||
fn zero() -> Self {
|
||||
0
|
||||
}
|
||||
}
|
||||
impl IntLike for u16 {
|
||||
fn zero() -> Self {
|
||||
0
|
||||
}
|
||||
}
|
||||
impl IntLike for u32 {
|
||||
fn zero() -> Self {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
impl IntLike for u64 {
|
||||
fn zero() -> Self {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
/// Descriptive name for each register.
|
||||
pub trait RegisterLongName {}
|
||||
|
||||
impl RegisterLongName for () {}
|
||||
|
||||
/// 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(v: V) -> Option<Self::EnumType>;
|
||||
}
|
||||
|
||||
/// Read/Write registers.
|
||||
pub struct ReadWrite<T: IntLike, R: RegisterLongName = ()> {
|
||||
value: T,
|
||||
associated_register: PhantomData<R>,
|
||||
}
|
||||
|
||||
/// Read-only registers.
|
||||
pub struct ReadOnly<T: IntLike, R: RegisterLongName = ()> {
|
||||
value: T,
|
||||
associated_register: PhantomData<R>,
|
||||
}
|
||||
|
||||
/// Write-only registers.
|
||||
pub struct WriteOnly<T: IntLike, R: RegisterLongName = ()> {
|
||||
value: T,
|
||||
associated_register: PhantomData<R>,
|
||||
}
|
||||
|
||||
impl<T: IntLike, R: RegisterLongName> ReadWrite<T, R> {
|
||||
pub const fn new(value: T) -> Self {
|
||||
ReadWrite {
|
||||
value: value,
|
||||
associated_register: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get(&self) -> T {
|
||||
unsafe { ::core::ptr::read_volatile(&self.value) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set(&self, value: T) {
|
||||
unsafe { ::core::ptr::write_volatile(&self.value as *const T as *mut T, value) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read(&self, field: Field<T, R>) -> T {
|
||||
(self.get() & (field.mask << field.shift)) >> field.shift
|
||||
}
|
||||
|
||||
#[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)
|
||||
}
|
||||
|
||||
#[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>) {
|
||||
let reg: T = self.get();
|
||||
self.set((reg & !field.mask) | field.value);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn modify_no_read(&self, original: LocalRegisterCopy<T, R>, field: FieldValue<T, R>) {
|
||||
self.set((original.get() & !field.mask) | field.value);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_set(&self, field: Field<T, R>) -> bool {
|
||||
self.read(field) != T::zero()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn matches_any(&self, field: FieldValue<T, R>) -> bool {
|
||||
self.get() & field.mask != T::zero()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn matches_all(&self, field: FieldValue<T, R>) -> bool {
|
||||
self.get() & field.mask == field.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IntLike, R: RegisterLongName> ReadOnly<T, R> {
|
||||
pub const fn new(value: T) -> Self {
|
||||
ReadOnly {
|
||||
value: value,
|
||||
associated_register: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get(&self) -> T {
|
||||
unsafe { ::core::ptr::read_volatile(&self.value) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read(&self, field: Field<T, R>) -> T {
|
||||
(self.get() & (field.mask << field.shift)) >> field.shift
|
||||
}
|
||||
|
||||
#[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)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn extract(&self) -> LocalRegisterCopy<T, R> {
|
||||
LocalRegisterCopy::new(self.get())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_set(&self, field: Field<T, R>) -> bool {
|
||||
self.read(field) != T::zero()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn matches_any(&self, field: FieldValue<T, R>) -> bool {
|
||||
self.get() & field.mask != T::zero()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn matches_all(&self, field: FieldValue<T, R>) -> bool {
|
||||
self.get() & field.mask == field.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IntLike, R: RegisterLongName> WriteOnly<T, R> {
|
||||
pub const fn new(value: T) -> Self {
|
||||
WriteOnly {
|
||||
value: value,
|
||||
associated_register: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set(&self, value: T) {
|
||||
unsafe { ::core::ptr::write_volatile(&self.value as *const T as *mut T, value) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn write(&self, field: FieldValue<T, R>) {
|
||||
self.set(field.value);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// 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.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct LocalRegisterCopy<T: IntLike, R: RegisterLongName = ()> {
|
||||
value: T,
|
||||
associated_register: PhantomData<R>,
|
||||
}
|
||||
|
||||
impl<T: IntLike, R: RegisterLongName> LocalRegisterCopy<T, R> {
|
||||
pub const fn new(value: T) -> Self {
|
||||
LocalRegisterCopy {
|
||||
value: value,
|
||||
associated_register: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get(&self) -> T {
|
||||
self.value
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read(&self, field: Field<T, R>) -> T {
|
||||
(self.value & (field.mask << field.shift)) >> field.shift
|
||||
}
|
||||
|
||||
#[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)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_set(&self, field: Field<T, R>) -> bool {
|
||||
self.read(field) != T::zero()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn matches_any(&self, field: FieldValue<T, R>) -> bool {
|
||||
self.value & field.mask != T::zero()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn matches_all(&self, field: FieldValue<T, R>) -> bool {
|
||||
self.value & field.mask == field.value
|
||||
}
|
||||
|
||||
/// 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: IntLike + fmt::Debug, R: RegisterLongName> fmt::Debug for LocalRegisterCopy<T, R> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}", self.value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: RegisterLongName> From<LocalRegisterCopy<u8, R>> for u8 {
|
||||
fn from(r: LocalRegisterCopy<u8, R>) -> u8 {
|
||||
r.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: RegisterLongName> From<LocalRegisterCopy<u16, R>> for u16 {
|
||||
fn from(r: LocalRegisterCopy<u16, R>) -> u16 {
|
||||
r.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: RegisterLongName> From<LocalRegisterCopy<u32, R>> for u32 {
|
||||
fn from(r: LocalRegisterCopy<u32, R>) -> u32 {
|
||||
r.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: RegisterLongName> From<LocalRegisterCopy<u64, R>> for u64 {
|
||||
fn from(r: LocalRegisterCopy<u64, R>) -> u64 {
|
||||
r.value
|
||||
}
|
||||
}
|
||||
|
||||
/// Specific section of a register.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Field<T: IntLike, R: RegisterLongName> {
|
||||
pub mask: T,
|
||||
pub shift: usize,
|
||||
associated_register: PhantomData<R>,
|
||||
}
|
||||
|
||||
// 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> {
|
||||
Field {
|
||||
mask: mask,
|
||||
shift: shift,
|
||||
associated_register: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn val(&self, value: u8) -> FieldValue<u8, R> {
|
||||
FieldValue::<u8, R>::new(self.mask, self.shift, value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: RegisterLongName> Field<u16, R> {
|
||||
pub const fn new(mask: u16, shift: usize) -> Field<u16, R> {
|
||||
Field {
|
||||
mask: mask,
|
||||
shift: shift,
|
||||
associated_register: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn val(&self, value: u16) -> FieldValue<u16, R> {
|
||||
FieldValue::<u16, R>::new(self.mask, self.shift, value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: RegisterLongName> Field<u32, R> {
|
||||
pub const fn new(mask: u32, shift: usize) -> Field<u32, R> {
|
||||
Field {
|
||||
mask: mask,
|
||||
shift: shift,
|
||||
associated_register: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn val(&self, value: u32) -> FieldValue<u32, R> {
|
||||
FieldValue::<u32, R>::new(self.mask, self.shift, value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: RegisterLongName> Field<u64, R> {
|
||||
pub const fn new(mask: u64, shift: usize) -> Field<u64, R> {
|
||||
Field {
|
||||
mask: mask,
|
||||
shift: shift,
|
||||
associated_register: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn val(&self, value: u64) -> FieldValue<u64, R> {
|
||||
FieldValue::<u64, R>::new(self.mask, self.shift, value)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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: IntLike, R: RegisterLongName> {
|
||||
pub mask: T,
|
||||
pub value: T,
|
||||
associated_register: PhantomData<R>,
|
||||
}
|
||||
|
||||
// Necessary to split the implementation of u8 and u32 out because the bitwise
|
||||
// math isn't treated as const when the type is generic.
|
||||
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),
|
||||
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 {
|
||||
fn from(val: FieldValue<u8, R>) -> u8 {
|
||||
val.value
|
||||
}
|
||||
}
|
||||
|
||||
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),
|
||||
associated_register: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: RegisterLongName> From<FieldValue<u16, R>> for u16 {
|
||||
fn from(val: FieldValue<u16, R>) -> u16 {
|
||||
val.value
|
||||
}
|
||||
}
|
||||
|
||||
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),
|
||||
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 {
|
||||
fn from(val: FieldValue<u32, R>) -> u32 {
|
||||
val.value
|
||||
}
|
||||
}
|
||||
|
||||
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),
|
||||
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 {
|
||||
fn from(val: FieldValue<u64, R>) -> u64 {
|
||||
val.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IntLike, R: RegisterLongName> FieldValue<T, R> {
|
||||
// Modify fields in a register value
|
||||
pub fn modify(self, val: T) -> T {
|
||||
(val & !self.mask) | self.value
|
||||
}
|
||||
}
|
||||
|
||||
// Combine two fields with the addition operator
|
||||
impl<T: IntLike, R: RegisterLongName> Add for FieldValue<T, R> {
|
||||
type Output = Self;
|
||||
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: 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,
|
||||
};
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue