Skip to content

Commit 08c1320

Browse files
committed
Auto merge of #69 - japaric:revise-api, r=japaric
revise peripheral API This PR changes the signature of many of the high level methods available on peripherals like `NVIC::get_priority`. Additionally some instance methods have been turned into static methods. The following guidelines have been used to apply the changes: - If the method body contains a single, atomic read operation with *no* side effects (e.g. the read operation clears one of the bits of the register): the signature changed to make the method static, i.e. `&self` was removed from the signature. - If the method involves writing to or a RMW operation on a register: the signature changed to take the singleton by `&mut self` reference. - If the method involves only read operations where at least one of them modifies the value of a register: the signature changed to take the singleton by `&mut self` reference. The rationale for this last guideline is that using `&self`, instead of `&mut self`, lets the user (unintentionally) break abstractions in the presence of generators. Example below: ``` rust let peripherals = Peripherals::take().unwrap(); let syst = &peripherals.SYST; // tasks let mut a = || { loop { // yielding "busy wait" while !a.has_wrapped() { yield; } // do stuff } }; let mut b = || { // .. // *NOTE* the problem is in the line below: this `is_counter_enabled` method reads the CSR // register and that read operation clears the COUNTFLAG bit of the register (if set), which is // the bit the `has_wrapped` method checks for. if syst.is_counter_enabled() { // .. } // .. }; ``` One more guideline was considered but the required conditions didn't apply to any of the existing methods: - If the method involves only non side effectful, non necessarily atomic read operations: the signature of the method should remain as `&self`. The rationale for this guideline is that a static method (no `self` argument) wouldn't be appropriate because that can result in a torn read if the read operation can be preempted by some context that modifies the register. In any case, this last guideline doesn't seem to apply well to the peripherals structs exposed by this crate because they *deref* to a `RegisterBlock` that allows mutation through a `&self` reference. When these two properties (the guideline and `Deref<Target=RegisterBlock>`) are mixed the user can potentially break abstractions using generators (as shown in the `syst` example). cc @hannobraun closes #67
2 parents 34f6621 + 5a19c39 commit 08c1320

File tree

9 files changed

+282
-165
lines changed

9 files changed

+282
-165
lines changed

src/exception.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ pub enum Exception {
2222
/// An interrupt
2323
Interrupt(u8),
2424
// Unreachable variant
25-
#[doc(hidden)]
26-
Reserved,
25+
#[doc(hidden)] Reserved,
2726
}
2827

2928
impl Exception {

src/peripheral/cbp.rs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
33
use volatile_register::WO;
44

5+
use peripheral::CBP;
6+
57
/// Register block
68
#[repr(C)]
79
pub struct RegisterBlock {
@@ -33,26 +35,26 @@ const CBP_SW_WAY_MASK: u32 = 0x3 << CBP_SW_WAY_POS;
3335
const CBP_SW_SET_POS: u32 = 5;
3436
const CBP_SW_SET_MASK: u32 = 0x1FF << CBP_SW_SET_POS;
3537

36-
impl RegisterBlock {
38+
impl CBP {
3739
/// I-cache invalidate all to PoU
3840
#[inline]
39-
pub fn iciallu(&self) {
41+
pub fn iciallu(&mut self) {
4042
unsafe {
4143
self.iciallu.write(0);
4244
}
4345
}
4446

4547
/// I-cache invalidate by MVA to PoU
4648
#[inline]
47-
pub fn icimvau(&self, mva: u32) {
49+
pub fn icimvau(&mut self, mva: u32) {
4850
unsafe {
4951
self.icimvau.write(mva);
5052
}
5153
}
5254

5355
/// D-cache invalidate by MVA to PoC
5456
#[inline]
55-
pub fn dcimvac(&self, mva: u32) {
57+
pub fn dcimvac(&mut self, mva: u32) {
5658
unsafe {
5759
self.dcimvac.write(mva);
5860
}
@@ -62,7 +64,7 @@ impl RegisterBlock {
6264
///
6365
/// `set` is masked to be between 0 and 3, and `way` between 0 and 511.
6466
#[inline]
65-
pub fn dcisw(&self, set: u16, way: u16) {
67+
pub fn dcisw(&mut self, set: u16, way: u16) {
6668
// The ARMv7-M Architecture Reference Manual, as of Revision E.b, says these set/way
6769
// operations have a register data format which depends on the implementation's
6870
// associativity and number of sets. Specifically the 'way' and 'set' fields have
@@ -82,15 +84,15 @@ impl RegisterBlock {
8284

8385
/// D-cache clean by MVA to PoU
8486
#[inline]
85-
pub fn dccmvau(&self, mva: u32) {
87+
pub fn dccmvau(&mut self, mva: u32) {
8688
unsafe {
8789
self.dccmvau.write(mva);
8890
}
8991
}
9092

9193
/// D-cache clean by MVA to PoC
9294
#[inline]
93-
pub fn dccmvac(&self, mva: u32) {
95+
pub fn dccmvac(&mut self, mva: u32) {
9496
unsafe {
9597
self.dccmvac.write(mva);
9698
}
@@ -100,7 +102,7 @@ impl RegisterBlock {
100102
///
101103
/// `set` is masked to be between 0 and 3, and `way` between 0 and 511.
102104
#[inline]
103-
pub fn dccsw(&self, set: u16, way: u16) {
105+
pub fn dccsw(&mut self, set: u16, way: u16) {
104106
// See comment for dcisw() about the format here
105107
unsafe {
106108
self.dccsw.write(
@@ -112,7 +114,7 @@ impl RegisterBlock {
112114

113115
/// D-cache clean and invalidate by MVA to PoC
114116
#[inline]
115-
pub fn dccimvac(&self, mva: u32) {
117+
pub fn dccimvac(&mut self, mva: u32) {
116118
unsafe {
117119
self.dccimvac.write(mva);
118120
}
@@ -122,7 +124,7 @@ impl RegisterBlock {
122124
///
123125
/// `set` is masked to be between 0 and 3, and `way` between 0 and 511.
124126
#[inline]
125-
pub fn dccisw(&self, set: u16, way: u16) {
127+
pub fn dccisw(&mut self, set: u16, way: u16) {
126128
// See comment for dcisw() about the format here
127129
unsafe {
128130
self.dccisw.write(
@@ -134,7 +136,7 @@ impl RegisterBlock {
134136

135137
/// Branch predictor invalidate all
136138
#[inline]
137-
pub fn bpiall(&self) {
139+
pub fn bpiall(&mut self) {
138140
unsafe {
139141
self.bpiall.write(0);
140142
}

src/peripheral/cpuid.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ use volatile_register::RO;
44
#[cfg(any(armv7m, test))]
55
use volatile_register::RW;
66

7+
#[cfg(armv7m)]
8+
use peripheral::CPUID;
9+
710
/// Register block
811
#[repr(C)]
912
pub struct RegisterBlock {
@@ -45,14 +48,14 @@ pub enum CsselrCacheType {
4548
}
4649

4750
#[cfg(armv7m)]
48-
impl RegisterBlock {
51+
impl CPUID {
4952
/// Selects the current CCSIDR
5053
///
5154
/// * `level`: the required cache level minus 1, e.g. 0 for L1, 1 for L2
5255
/// * `ind`: select instruction cache or data/unified cache
5356
///
5457
/// `level` is masked to be between 0 and 7.
55-
pub fn select_cache(&self, level: u8, ind: CsselrCacheType) {
58+
pub fn select_cache(&mut self, level: u8, ind: CsselrCacheType) {
5659
const CSSELR_IND_POS: u32 = 0;
5760
const CSSELR_IND_MASK: u32 = 1 << CSSELR_IND_POS;
5861
const CSSELR_LEVEL_POS: u32 = 1;
@@ -67,7 +70,7 @@ impl RegisterBlock {
6770
}
6871

6972
/// Returns the number of sets and ways in the selected cache
70-
pub fn cache_num_sets_ways(&self, level: u8, ind: CsselrCacheType) -> (u16, u16) {
73+
pub fn cache_num_sets_ways(&mut self, level: u8, ind: CsselrCacheType) -> (u16, u16) {
7174
const CCSIDR_NUMSETS_POS: u32 = 13;
7275
const CCSIDR_NUMSETS_MASK: u32 = 0x7FFF << CCSIDR_NUMSETS_POS;
7376
const CCSIDR_ASSOCIATIVITY_POS: u32 = 3;

src/peripheral/dwt.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
33
use volatile_register::{RO, RW, WO};
44

5+
use peripheral::DWT;
6+
57
/// Register block
68
#[repr(C)]
79
pub struct RegisterBlock {
@@ -30,13 +32,6 @@ pub struct RegisterBlock {
3032
pub lsr: RO<u32>,
3133
}
3234

33-
impl RegisterBlock {
34-
/// Enables the cycle counter
35-
pub fn enable_cycle_counter(&self) {
36-
unsafe { self.ctrl.modify(|r| r | 1) }
37-
}
38-
}
39-
4035
/// Comparator
4136
#[repr(C)]
4237
pub struct Comparator {
@@ -48,3 +43,16 @@ pub struct Comparator {
4843
pub function: RW<u32>,
4944
reserved: u32,
5045
}
46+
47+
impl DWT {
48+
/// Enables the cycle counter
49+
pub fn enable_cycle_counter(&mut self) {
50+
unsafe { self.ctrl.modify(|r| r | 1) }
51+
}
52+
53+
/// Returns the current clock cycle count
54+
pub fn get_cycle_count() -> u32 {
55+
// NOTE(unsafe) atomic read with no side effects
56+
unsafe { (*Self::ptr()).cyccnt.read() }
57+
}
58+
}

src/peripheral/mod.rs

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,66 @@
11
//! Core peripherals
22
//!
3+
//! # API
4+
//!
5+
//! To use (most of) the peripheral API first you must get an *instance* of the peripheral. All the
6+
//! core peripherals are modeled as singletons (there can only ever be, at most, one instance of
7+
//! them at any given point in time) and the only way to get an instance of them is through the
8+
//! [`Peripherals::take`](struct.Peripherals.html#method.take) method.
9+
//!
10+
//! ``` no_run
11+
//! extern crate cortex_m;
12+
//!
13+
//! use cortex_m::peripheral::Peripherals;
14+
//!
15+
//! fn main() {
16+
//! let mut peripherals = Peripherals::take().unwrap();
17+
//! peripherals.DWT.enable_cycle_counter();
18+
//! }
19+
//! ```
20+
//!
21+
//! This method can only be successfully called *once* -- this is why the method returns an
22+
//! `Option`. Subsequent calls to the method will result in a `None` value being returned.
23+
//!
24+
//! A part of the peripheral API doesn't require access to a peripheral instance. This part of the
25+
//! API is provided as static methods on the peripheral types. One example is the
26+
//! [`DWT::cyccnt`](struct.DWT.html#method.cyccnt) method.
27+
//!
28+
//! ``` no_run
29+
//! extern crate cortex_m;
30+
//!
31+
//! use cortex_m::peripheral::{DWT, Peripherals};
32+
//!
33+
//! fn main() {
34+
//! {
35+
//! let mut peripherals = Peripherals::take().unwrap();
36+
//! peripherals.DWT.enable_cycle_counter();
37+
//! } // all the peripheral singletons are destroyed here
38+
//!
39+
//! // but this method can be called without a DWT instance
40+
//! let cyccnt = DWT::get_cycle_count();
41+
//! }
42+
//! ```
43+
//!
44+
//! The singleton property can be *unsafely* bypassed using the `ptr` static method which is
45+
//! available on all the peripheral types. This method is a useful building block for implementing
46+
//! higher level and safe abstractions.
47+
//!
48+
//! ``` no_run
49+
//! extern crate cortex_m;
50+
//!
51+
//! use cortex_m::peripheral::{DWT, Peripherals};
52+
//!
53+
//! fn main() {
54+
//! {
55+
//! let mut peripherals = Peripherals::take().unwrap();
56+
//! peripherals.DWT.enable_cycle_counter();
57+
//! } // all the peripheral singletons are destroyed here
58+
//!
59+
//! // actually safe because this is an atomic read with no side effects
60+
//! let cyccnt = unsafe { (*DWT::ptr()).cyccnt.read() };
61+
//! }
62+
//! ```
63+
//!
364
//! # References
465
//!
566
//! - ARMv7-M Architecture Reference Manual (Issue E.b) - Chapter B3
@@ -80,7 +141,7 @@ impl Peripherals {
80141
})
81142
}
82143

83-
/// Unchecked version of `Peripherals::steal`
144+
/// Unchecked version of `Peripherals::take`
84145
pub unsafe fn steal() -> Self {
85146
debug_assert!(!CORE_PERIPHERALS);
86147

@@ -136,6 +197,12 @@ pub struct CBP {
136197

137198
#[cfg(armv7m)]
138199
impl CBP {
200+
pub(crate) unsafe fn new() -> Self {
201+
CBP {
202+
_marker: PhantomData,
203+
}
204+
}
205+
139206
/// Returns a pointer to the register block
140207
pub fn ptr() -> *const self::cbp::RegisterBlock {
141208
0xE000_EF50 as *const _

0 commit comments

Comments
 (0)