Skip to content

Commit 01e39c0

Browse files
committed
rewrite NVIC to use derive-mmio
1 parent d232aaf commit 01e39c0

4 files changed

Lines changed: 128 additions & 77 deletions

File tree

cortex-m/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ rust-version = "1.85"
1515
[dependencies]
1616
bare-metal = { version = "0.2.4", features = ["const-fn"] }
1717
critical-section = "1.0.0"
18+
derive-mmio = "0.6"
1819
volatile-register = "0.2.2"
1920
bitfield = "0.13.2"
2021
eh0 = { package = "embedded-hal", version = "0.2.4" }

cortex-m/src/peripheral/mod.rs

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -206,9 +206,7 @@ impl Peripherals {
206206
MPU: MPU {
207207
_marker: PhantomData,
208208
},
209-
NVIC: NVIC {
210-
_marker: PhantomData,
211-
},
209+
NVIC: NVIC::steal(),
212210
SAU: SAU {
213211
_marker: PhantomData,
214212
},
@@ -536,30 +534,36 @@ impl ops::Deref for MPU {
536534
}
537535

538536
/// Nested Vector Interrupt Controller
539-
pub struct NVIC {
540-
_marker: PhantomData<*const ()>,
541-
}
537+
pub struct NVIC(nvic::MmioRegisterBlock<'static>);
542538

543539
unsafe impl Send for NVIC {}
544540

545541
impl NVIC {
546-
/// Pointer to the register block
547-
pub const PTR: *const nvic::RegisterBlock = 0xE000_E100 as *const _;
548-
549-
/// Returns a pointer to the register block
550-
#[inline(always)]
551-
#[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")]
552-
pub const fn ptr() -> *const nvic::RegisterBlock {
553-
Self::PTR
542+
/// Unsafely steal an instance of the NVIC.
543+
///
544+
/// # Safety
545+
///
546+
/// This potentially allows to create multiple instances of the NVIC register block, which
547+
/// might only be valid in certain multi-core environments.
548+
#[inline]
549+
pub unsafe fn steal() -> Self {
550+
NVIC(unsafe { nvic::RegisterBlock::new_mmio_fixed() })
554551
}
555552
}
556553

557554
impl ops::Deref for NVIC {
558-
type Target = self::nvic::RegisterBlock;
555+
type Target = nvic::MmioRegisterBlock<'static>;
559556

560557
#[inline(always)]
561558
fn deref(&self) -> &Self::Target {
562-
unsafe { &*Self::PTR }
559+
&self.0
560+
}
561+
}
562+
563+
impl ops::DerefMut for NVIC {
564+
#[inline(always)]
565+
fn deref_mut(&mut self) -> &mut Self::Target {
566+
&mut self.0
563567
}
564568
}
565569

cortex-m/src/peripheral/nvic.rs

Lines changed: 98 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,45 @@
11
//! Nested Vector Interrupt Controller
2-
3-
use volatile_register::RW;
4-
#[cfg(not(armv6m))]
5-
use volatile_register::{RO, WO};
6-
72
use crate::interrupt::InterruptNumber;
83
use crate::peripheral::NVIC;
94

10-
/// Register block
5+
/// NVIC base address.
6+
pub const BASE_ADDRESS: usize = 0xE000_E100;
7+
8+
/// NVIC register block.
9+
#[derive(derive_mmio::Mmio)]
1110
#[repr(C)]
1211
pub struct RegisterBlock {
1312
/// Interrupt Set-Enable
14-
pub iser: [RW<u32>; 16],
13+
iser: [u32; 16],
1514

1615
_reserved0: [u32; 16],
1716

1817
/// Interrupt Clear-Enable
19-
pub icer: [RW<u32>; 16],
18+
icer: [u32; 16],
2019

2120
_reserved1: [u32; 16],
2221

2322
/// Interrupt Set-Pending
24-
pub ispr: [RW<u32>; 16],
23+
ispr: [u32; 16],
2524

2625
_reserved2: [u32; 16],
2726

2827
/// Interrupt Clear-Pending
29-
pub icpr: [RW<u32>; 16],
28+
icpr: [u32; 16],
3029

3130
_reserved3: [u32; 16],
3231

3332
/// Interrupt Active Bit (not present on Cortex-M0 variants)
3433
#[cfg(not(armv6m))]
35-
pub iabr: [RO<u32>; 16],
34+
iabr: [u32; 16],
3635
#[cfg(armv6m)]
3736
_reserved4: [u32; 16],
3837

3938
_reserved5: [u32; 16],
4039

4140
#[cfg(armv8m)]
4241
/// Interrupt Target Non-secure (only present on Arm v8-M)
43-
pub itns: [RW<u32>; 16],
42+
itns: [u32; 16],
4443
#[cfg(not(armv8m))]
4544
_reserved6: [u32; 16],
4645

@@ -58,7 +57,7 @@ pub struct RegisterBlock {
5857
/// so convenient byte-sized representation wouldn't work on that
5958
/// architecture.
6059
#[cfg(not(armv6m))]
61-
pub ipr: [RW<u8>; 496],
60+
ipr: [u32; 124],
6261

6362
/// Interrupt Priority
6463
///
@@ -72,14 +71,27 @@ pub struct RegisterBlock {
7271
/// so convenient byte-sized representation wouldn't work on that
7372
/// architecture.
7473
#[cfg(armv6m)]
75-
pub ipr: [RW<u32>; 8],
74+
ipr: [u32; 8],
7675

7776
#[cfg(not(armv6m))]
7877
_reserved8: [u32; 580],
7978

8079
/// Software Trigger Interrupt
8180
#[cfg(not(armv6m))]
82-
pub stir: WO<u32>,
81+
stir: u32,
82+
}
83+
84+
impl RegisterBlock {
85+
/// Creates a new instance of the NVIC register block.
86+
///
87+
/// # Safety
88+
///
89+
/// This potentially allows to create multiple instances of the NVIC register block, which
90+
/// might only be valid in certain multi-core environments.
91+
#[inline]
92+
pub const unsafe fn new_mmio_fixed() -> MmioRegisterBlock<'static> {
93+
unsafe { RegisterBlock::new_mmio_at(BASE_ADDRESS) }
94+
}
8395
}
8496

8597
impl NVIC {
@@ -100,9 +112,7 @@ impl NVIC {
100112
{
101113
let nr = interrupt.number();
102114

103-
unsafe {
104-
self.stir.write(u32::from(nr));
105-
}
115+
self.write_stir(u32::from(nr));
106116
}
107117

108118
/// Disables `interrupt`
@@ -113,7 +123,9 @@ impl NVIC {
113123
{
114124
let nr = interrupt.number();
115125
// NOTE(unsafe) this is a write to a stateless register
116-
unsafe { (*Self::PTR).icer[usize::from(nr / 32)].write(1 << (nr % 32)) }
126+
let mut nvic = unsafe { Self::steal() };
127+
nvic.write_icer(usize::from(nr / 32), 1 << (nr % 32))
128+
.expect("invalid interrupt number");
117129
}
118130

119131
/// Enables `interrupt`
@@ -124,11 +136,11 @@ impl NVIC {
124136
where
125137
I: InterruptNumber,
126138
{
127-
unsafe {
128-
let nr = interrupt.number();
129-
// NOTE(ptr) this is a write to a stateless register
130-
(*Self::PTR).iser[usize::from(nr / 32)].write(1 << (nr % 32))
131-
}
139+
let nr = interrupt.number();
140+
// NOTE(ptr) this is a write to a stateless register
141+
let mut nvic = unsafe { Self::steal() };
142+
nvic.write_iser(usize::from(nr / 32), 1 << (nr % 32))
143+
.expect("invalid interrupt number");
132144
}
133145

134146
/// Returns the NVIC priority of `interrupt`
@@ -144,16 +156,25 @@ impl NVIC {
144156
#[cfg(not(armv6m))]
145157
{
146158
let nr = interrupt.number();
147-
// NOTE(unsafe) atomic read with no side effects
148-
unsafe { (*Self::PTR).ipr[usize::from(nr)].read() }
159+
// note(unsafe) atomic read with no side effects
160+
let nvic = unsafe { Self::steal() };
161+
let ipr_ptr = nvic.pointer_to_ipr_start() as *const u8;
162+
// This should never happen for correct `InterruptNumber` implementations.
163+
if nr >= 124 {
164+
panic!("unexpected interrupt number");
165+
}
166+
// note(unsafe) atomic read with no side effects
167+
unsafe { core::ptr::read_volatile(ipr_ptr.offset(nr as isize)) }
149168
}
150169

151170
#[cfg(armv6m)]
152171
{
153-
// NOTE(unsafe) atomic read with no side effects
154-
let ipr_n = unsafe { (*Self::PTR).ipr[Self::ipr_index(interrupt)].read() };
155-
let prio = (ipr_n >> Self::ipr_shift(interrupt)) & 0x0000_00ff;
156-
prio as u8
172+
// note(unsafe) atomic read with no side effects
173+
let nvic = unsafe { Self::steal() };
174+
let ipr_n = nvic
175+
.read_ipr(Self::ipr_index(interrupt))
176+
.expect("unexpected interrupt number");
177+
((ipr_n >> Self::ipr_shift(interrupt)) & 0x0000_00ff) as u8
157178
}
158179
}
159180

@@ -167,8 +188,12 @@ impl NVIC {
167188
let nr = interrupt.number();
168189
let mask = 1 << (nr % 32);
169190

170-
// NOTE(unsafe) atomic read with no side effects
171-
unsafe { ((*Self::PTR).iabr[usize::from(nr / 32)].read() & mask) == mask }
191+
// note(unsafe) atomic read with no side effects
192+
let nvic = unsafe { Self::steal() };
193+
nvic.read_iabr(usize::from(nr / 32))
194+
.expect("unexpected interrupt number")
195+
& mask
196+
== mask
172197
}
173198

174199
/// Checks if `interrupt` is enabled
@@ -180,8 +205,12 @@ impl NVIC {
180205
let nr = interrupt.number();
181206
let mask = 1 << (nr % 32);
182207

183-
// NOTE(unsafe) atomic read with no side effects
184-
unsafe { ((*Self::PTR).iser[usize::from(nr / 32)].read() & mask) == mask }
208+
// note(unsafe) atomic read with no side effects
209+
let nvic = unsafe { Self::steal() };
210+
nvic.read_iser(usize::from(nr / 32))
211+
.expect("unexpected interrupt number")
212+
& mask
213+
== mask
185214
}
186215

187216
/// Checks if `interrupt` is pending
@@ -193,8 +222,12 @@ impl NVIC {
193222
let nr = interrupt.number();
194223
let mask = 1 << (nr % 32);
195224

196-
// NOTE(unsafe) atomic read with no side effects
197-
unsafe { ((*Self::PTR).ispr[usize::from(nr / 32)].read() & mask) == mask }
225+
// note(unsafe) atomic read with no side effects
226+
let nvic = unsafe { Self::steal() };
227+
nvic.read_ispr(usize::from(nr / 32))
228+
.expect("unexpected interrupt number")
229+
& mask
230+
== mask
198231
}
199232

200233
/// Forces `interrupt` into pending state
@@ -206,7 +239,9 @@ impl NVIC {
206239
let nr = interrupt.number();
207240

208241
// NOTE(unsafe) atomic stateless write; ICPR doesn't store any state
209-
unsafe { (*Self::PTR).ispr[usize::from(nr / 32)].write(1 << (nr % 32)) }
242+
let mut nvic = unsafe { Self::steal() };
243+
nvic.write_ispr(usize::from(nr / 32), 1 << (nr % 32))
244+
.expect("unexpected interrupt number")
210245
}
211246

212247
/// Sets the "priority" of `interrupt` to `prio`
@@ -226,22 +261,31 @@ impl NVIC {
226261
where
227262
I: InterruptNumber,
228263
{
229-
unsafe {
230-
#[cfg(not(armv6m))]
231-
{
232-
let nr = interrupt.number();
233-
self.ipr[usize::from(nr)].write(prio)
264+
#[cfg(not(armv6m))]
265+
{
266+
let nr = interrupt.number();
267+
// NOTE(unsafe) atomic stateless write; IPR doesn't store any state
268+
let nvic = unsafe { Self::steal() };
269+
let ipr_ptr = nvic.pointer_to_ipr_start() as *mut u8;
270+
// This should never happen for correct `InterruptNumber` implementations.
271+
if nr >= 124 {
272+
panic!("unexpected interrupt number");
234273
}
274+
// NOTE(unsafe) atomic stateless write; IPR doesn't store any state
275+
unsafe { core::ptr::write_volatile(ipr_ptr.offset(nr as isize), prio) }
276+
}
235277

236-
#[cfg(armv6m)]
237-
{
238-
self.ipr[Self::ipr_index(interrupt)].modify(|value| {
239-
let mask = 0x0000_00ff << Self::ipr_shift(interrupt);
240-
let prio = u32::from(prio) << Self::ipr_shift(interrupt);
241-
242-
(value & !mask) | prio
243-
})
244-
}
278+
#[cfg(armv6m)]
279+
{
280+
// NOTE(unsafe) atomic stateless write; IPR doesn't store any state
281+
let mut nvic = unsafe { Self::steal() };
282+
nvic.modify_ipr(Self::ipr_index(interrupt), |mut value| {
283+
let mask = 0x0000_00ff << Self::ipr_shift(interrupt);
284+
let prio = u32::from(prio) << Self::ipr_shift(interrupt);
285+
286+
(value & !mask) | prio
287+
})
288+
.expect("unexpected interrupt number");
245289
}
246290
}
247291

@@ -254,7 +298,9 @@ impl NVIC {
254298
let nr = interrupt.number();
255299

256300
// NOTE(unsafe) atomic stateless write; ICPR doesn't store any state
257-
unsafe { (*Self::PTR).icpr[usize::from(nr / 32)].write(1 << (nr % 32)) }
301+
let mut nvic = unsafe { Self::steal() };
302+
nvic.write_icpr(usize::from(nr / 32), 1 << (nr % 32))
303+
.expect("unexpected interrupt number")
258304
}
259305

260306
#[cfg(armv6m)]

cortex-m/src/peripheral/test.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -109,16 +109,16 @@ fn mpu() {
109109

110110
#[test]
111111
fn nvic() {
112-
let nvic = unsafe { &*crate::peripheral::NVIC::PTR };
113-
114-
assert_eq!(address(&nvic.iser), 0xE000E100);
115-
assert_eq!(address(&nvic.icer), 0xE000E180);
116-
assert_eq!(address(&nvic.ispr), 0xE000E200);
117-
assert_eq!(address(&nvic.icpr), 0xE000E280);
118-
assert_eq!(address(&nvic.iabr), 0xE000E300);
119-
assert_eq!(address(&nvic.ipr), 0xE000E400);
112+
let nvic = unsafe { crate::peripheral::NVIC::steal() };
113+
114+
assert_eq!(address(nvic.pointer_to_iser_start()), 0xE000E100);
115+
assert_eq!(address(nvic.pointer_to_icer_start()), 0xE000E180);
116+
assert_eq!(address(nvic.pointer_to_ispr_start()), 0xE000E200);
117+
assert_eq!(address(nvic.pointer_to_icpr_start()), 0xE000E280);
118+
assert_eq!(address(nvic.pointer_to_iabr_start()), 0xE000E300);
119+
assert_eq!(address(nvic.pointer_to_ipr_start()), 0xE000E400);
120120
#[cfg(not(armv6m))]
121-
assert_eq!(address(&nvic.stir), 0xE000EF00);
121+
assert_eq!(address(nvic.pointer_to_stir()), 0xE000EF00);
122122
}
123123

124124
#[test]

0 commit comments

Comments
 (0)