Module drone_core::reg [−][src]
The Memory-Mapped Registers module.
NOTE A Drone platform crate may re-export this module with its own additions under the same name, in which case it should be used instead.
A memory-mapped register is a special location in memory. Reads and writes
from/to this location produce side-effects. For example writing 1
or 0
to such location may set the related GPIO output pin to the high or low
logical level. In the same way reading from such location may return 1
or
0
depending on the input level of the related GPIO input pin. The most
basic way to deal with this memory is to use core::ptr::read_volatile
and core::ptr::write_volatile
. Here is an example:
use core::ptr::{read_volatile, write_volatile}; // The address of GPIOA_CRL register. const GPIOA_CRL: usize = 0x4001_0800; // The offset for MODE2 field of GPIOA_CRL register. const GPIOA_CRL_MODE2_OFFSET: usize = 8; // The mask for MODE2 field of GPIOA_CRL register. const GPIOA_CRL_MODE2_MASK: u32 = 0x0000_0300; // Read the state of GPIOA_CRL register. The function is unsafe because it // can read from any location in memory. let mut gpioa_crl = unsafe { read_volatile(GPIOA_CRL as *const u32) }; // Do bit arithmetic to get the value of MODE2 field. let mut gpioa_crl_mode2 = (gpioa_crl & GPIOA_CRL_MODE2_MASK) >> GPIOA_CRL_MODE2_OFFSET; // Toggle some bits. gpioa_crl_mode2 ^= 0b10; // Do bit arithmetic to update the register value with the new field value. gpioa_crl = gpioa_crl & !GPIOA_CRL_MODE2_MASK | gpioa_crl_mode2 << GPIOA_CRL_MODE2_OFFSET; // Update the state of GPIOA_CRL register. The function is also unsafe // because it can write to any location in memory. unsafe { write_volatile(GPIOA_CRL as *mut u32, gpioa_crl) };
This way has numerous disadvantages: it’s obscure, verbose, error-prone,
and requires unsafe
blocks. Also it has less obvious problems like lack of
thread-safety. This module provides safe and zero-cost abstractions to
this problem. As result the above example can be written like this:
gpioa_crl.modify(|r| r.write_mode2(r.mode2() ^ 0b10));
We abstract this type of memory with zero-sized
token
s. (Familiarity with token
module
is required.) Only the code that have the token instance for a particular
memory-mapped register can manipulate it safely. At the very base there is
Register Field Token (like MODE2
in the above example.) Register Field
Tokens for a particular register grouped in Register Token (like
GPIO_CRL
in the above example.) And all available Register Tokens are
grouped in one Register Tokens Index.
API
The memory-mapped registers API is scattered across numerous traits.
Therefore it is recommended to use reg::prelude
:
use drone_core::reg::prelude::*;
Field Token
Field Width | Field Mode | Register Mode | |
---|---|---|---|
into_unsync | |||
into_sync | |||
into_copy | |||
as_sync | |||
load_val | read | read | |
default_val | write | write-only | |
store_val | write | write-only | |
store | write | write-only | |
read | one-bit | read | read |
read_bit | one-bit | read | read |
set | one-bit | write | write |
clear | one-bit | write | write |
toggle | one-bit | write | write |
set_bit | one-bit | write | write-only |
clear_bit | one-bit | write | write-only |
toggle_bit | one-bit | write | write-only |
read | multi-bit | read | read |
read_bits | multi-bit | read | read |
write | multi-bit | write | write |
write_bits | multi-bit | write | write-only |
Register Token
Mode | Tag | |
---|---|---|
into_unsync | ||
into_sync | ||
into_copy | ||
as_sync | ||
default_val | ||
default | ||
hold | ||
load | read | |
load_val | read | |
load_bits | read | |
as_ptr | read | |
as_mut_ptr | write | |
store | write | Urt |
store | write | Srt, Crt |
store_reg | write | Urt |
store_reg | write | Srt, Crt |
store_val | write | Urt |
store_val | write | Srt, Crt |
store_bits | write | Urt |
store_bits | write | Srt, Crt |
reset | write | Urt |
reset | write | Srt, Crt |
modify | read-write | Urt |
modify_reg | read-write | Urt |
Register Value
Autogenerated field methods for RegHold
(foo
as an example field
name):
Field Width | Mode | |
---|---|---|
foo() (read ) | one-bit | read |
foo() (read ) | multi-bit | read |
set_foo() (set ) | one-bit | write |
clear_foo() (clear ) | one-bit | write |
toggle_foo() (toggle ) | one-bit | write |
write_foo(bits) (write ) | multi-bit | write |
Tags
Each register or field token can have one of three flavors. They are encoded
by tag
s in their types. For example Reg<Urt>
, or RegField<Srt>
.
Here are available tags and their properties:
Atomic | Affine | |
---|---|---|
Urt (Unsynchronized) | - | + |
Srt (Synchronized) | + | + |
Crt (Copyable) | + | - |
Atomic means the token uses more costly atomic operations, but could be shared between threads.
Non-atomic means the token uses less costly non-atomic operations, but couldn’t be shared between threads.
Affine means the token can’t be cloned or copied and uses move-semantics.
Non-affine means the token could be freely copied.
Tokens of some tags can be converted to the same tokens of other tags using
.into_unsync()
, .into_sync()
, .into_copy()
. Here is the conversion
matrix for register tokens:
from \ to | Urt | Srt | Crt |
---|---|---|---|
Urt | + | + | + |
Srt | + | + | + |
Crt | - | - | + |
And here is the conversion matrix for field tokens:
from \ to | Urt | Srt | Crt |
---|---|---|---|
Urt | + | - | - |
Srt | - | + | + |
Crt | - | - | + |
Mappings
We define concrete register mappings in platform crates. Usually the user doesn’t need to map registers themselves. But lets have a look to an example of how it could be organized for STM32 platform:
use core::mem::size_of_val; use drone_core::{reg::prelude::*, token::Token}; use drone_core::reg; // ----- this is drone_cortex_m crate ----- // Registers belong to blocks. Here we declare CTRL register in STK block. reg! { // This macro will expand to a module: `pub mod stk_ctrl { ... }`. /// SysTick control and status register. pub STK CTRL => { address => 0xE000_E010; // the register address in memory size => 0x20; // size of the register in bits reset => 0x0000_0000; // reset value of the register // Traits to implement for the register token. The most common sets are: // RReg RoReg - read-only register // RReg WReg - read-write register // WReg WoReg - write-only register traits => { RReg WReg }; // Register fields. fields => { /// Counter enable. ENABLE => { offset => 0; // offset of the field width => 1; // width of the field // Traits to implement for the field token. The most common sets are: // RRRegField RoRRegField - read-only field // RRRegField WWRegField - read-write field // WWRegField WoWRegField - read-write field traits => { RRRegField WWRegField }; }; }; }; } // Here we define the register tokens index. Actually the result of this macro // is another macro, which can be used to define the final register token index // or to extend with another registers in downstream crates. It will become // clearer below. reg::tokens! { // The result of this macro is // `macro_rules! cortex_m_reg_tokens { ... }`. /// Defines an index of core ARM Cortex-M register tokens. pub macro cortex_m_reg_tokens; // Path prefix to reach registers. crate; // Absolute path to the current module. crate; // Here we declare all register blocks. This produces `pub mod stk { ... }` /// SysTick timer. pub mod STK { // Declare all registers for this block. This produces: // pub mod stk { // pub use crate::stk_ctrl as ctrl; // } CTRL; } } // ----- this is drone_stm32 crate ----- // This crate parses SVD files provided by the manufacturer and generates more // registers. // Same as above, except it will reuse the upstream macro, resulting in a // combined register tokens index. Note `use macro cortex_m_reg_tokens`. reg::tokens! { /// Defines an index of STM32F103 register tokens. pub macro stm32_reg_tokens; use macro cortex_m_reg_tokens; crate; crate; } // ----- this is an application crate ----- // This macro defines the concrete register tokens index for STM32 MCU. The // index is a sum of `drone_cortex_m` and `drone_stm32` registers. The result // of this macro is `pub struct Regs { ... }`. stm32_reg_tokens! { /// Register tokens. index => pub Regs; } // Your entry point. fn main() { // It's unsafe because we can accidentally create more than one instance // of the index. let reg = unsafe { Regs::take() }; // The index doesn't really exist in memory. assert_eq!(size_of_val(®), 0); assert_eq!(size_of_val(®.stk_ctrl), 0); assert_eq!(size_of_val(®.stk_ctrl.enable), 0); // Pass the index to your safe entry point. trunk(reg); } fn trunk(reg: Regs) {}
Modules
field | Memory-mapped register fields module. |
marker | Marker traits representing properties of memory-mapped registers. |
prelude | The Memory-Mapped Registers prelude. |
tag | Register token tags and their traits. |
Macros
assert_taken | Assert exclusive ownership of the register. |
tokens | A macro to define a macro to define a set of register tokens. |
Traits
RReg | Readable register. |
Reg | The base trait for a memory-mapped register token. |
RegHold | Exposed storage for register values. |
RegRef | |
RoReg | Read-only register. |
RwRegUnsync | Non-atomic operations for read-write register. |
WReg | Writable register. |
WRegAtomic | Atomic operations for writable register. |
WRegUnsync | Non-atomic operations for writable register. |
WoReg | Write-only register. |