Skip to content

Commit d952f0c

Browse files
committed
Fix IPR representation on ARMv6-M
On ARMv6-M, anything but world-aligned access to the IPR registers will lead to unpredictable results. Fixes #61.
1 parent 03779e5 commit d952f0c

File tree

1 file changed

+68
-5
lines changed

1 file changed

+68
-5
lines changed

src/peripheral/nvic.rs

+68-5
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,34 @@ pub struct RegisterBlock {
2222
/// Interrupt Active Bit
2323
pub iabr: [RO<u32>; 16],
2424
reserved4: [u32; 48],
25+
26+
#[cfg(not(armv6m))]
2527
/// Interrupt Priority
28+
///
29+
/// On ARMv7-M, 124 word-sized registers are available. Each of those
30+
/// contains of 4 interrupt priorities of 8 byte each.The architecture
31+
/// specifically allows accessing those along byte boundaries, so they are
32+
/// represented as 496 byte-sized registers, for convenience, and to allow
33+
/// atomic priority updates.
34+
///
35+
/// On ARMv6-M, the registers must only be accessed along word boundaries,
36+
/// so convenient byte-sized representation wouldn't work on that
37+
/// architecture.
2638
pub ipr: [RW<u8>; 496],
39+
40+
#[cfg(armv6m)]
41+
/// Interrupt Priority
42+
///
43+
/// On ARMv7-M, 124 word-sized registers are available. Each of those
44+
/// contains of 4 interrupt priorities of 8 byte each.The architecture
45+
/// specifically allows accessing those along byte boundaries, so they are
46+
/// represented as 496 byte-sized registers, for convenience, and to allow
47+
/// atomic priority updates.
48+
///
49+
/// On ARMv6-M, the registers must only be accessed along word boundaries,
50+
/// so convenient byte-sized representation wouldn't work on that
51+
/// architecture.
52+
pub ipr: [RW<u32>; 8],
2753
}
2854

2955
impl RegisterBlock {
@@ -66,9 +92,18 @@ impl RegisterBlock {
6692
where
6793
I: Nr,
6894
{
69-
let nr = interrupt.nr();
70-
71-
self.ipr[usize::from(nr)].read()
95+
#[cfg(not(armv6m))]
96+
{
97+
let nr = interrupt.nr();
98+
self.ipr[usize::from(nr)].read()
99+
}
100+
101+
#[cfg(armv6m)]
102+
{
103+
let ipr_n = self.ipr[Self::ipr_index(&interrupt)].read();
104+
let prio = (ipr_n >> Self::ipr_shift(&interrupt)) & 0x000000ff;
105+
prio as u8
106+
}
72107
}
73108

74109
/// Is `interrupt` active or pre-empted and stacked
@@ -118,12 +153,40 @@ impl RegisterBlock {
118153
///
119154
/// NOTE See `get_priority` method for an explanation of how NVIC priorities
120155
/// work.
156+
///
157+
/// On ARMv6-M, updating an interrupt priority requires a read-modify-write
158+
/// operation, which is not atomic. This is inherently racy, so please
159+
/// ensure proper access to this method.
160+
///
161+
/// On ARMv7-M, this method is atomic.
121162
pub unsafe fn set_priority<I>(&self, interrupt: I, prio: u8)
122163
where
123164
I: Nr,
124165
{
125-
let nr = interrupt.nr();
166+
#[cfg(not(armv6m))]
167+
{
168+
let nr = interrupt.nr();
169+
self.ipr[usize::from(nr)].write(prio)
170+
}
171+
172+
#[cfg(armv6m)]
173+
{
174+
self.ipr[Self::ipr_index(&interrupt)].modify(|value| {
175+
let mask = 0x000000ff << Self::ipr_shift(&interrupt);
176+
let prio = u32::from(prio) << Self::ipr_shift(&interrupt);
177+
178+
(value & !mask) | prio
179+
})
180+
}
181+
}
182+
183+
#[cfg(armv6m)]
184+
fn ipr_index<I>(interrupt: &I) -> usize where I: Nr {
185+
usize::from(interrupt.nr()) / 4
186+
}
126187

127-
self.ipr[usize::from(nr)].write(prio)
188+
#[cfg(armv6m)]
189+
fn ipr_shift<I>(interrupt: &I) -> usize where I: Nr {
190+
(usize::from(interrupt.nr()) % 4) * 8
128191
}
129192
}

0 commit comments

Comments
 (0)