Skip to content

Commit 4689992

Browse files
committed
Auto merge of #62 - hannobraun:update-nvic, r=japaric
Update NVIC This pull request updated the NVIC definition and brings it in line with the ARMv7-M technical reference manual. Since ARMv6-M's NVIC is a subset of ARMv7-M's one, this should work on both platforms. I tried adding some `#[cfg(armv7m]`, to make only the registers available on ARMv6-M visible on that platform, but aborted that plan, as it seemed to add a lot of complexity. What do you think about this, @japaric? I also checked the [ARMv8-M Technical Reference Manual](https://static.docs.arm.com/ddi0553/a/DDI0553A_e_armv8m_arm.pdf). The NVIC is largely identical to ARMv7-M's one and only adds an additional block of registers between IABR and IPR, so it will be straight-forward to add support once that becomes relevant.
2 parents 74cb12e + d952f0c commit 4689992

File tree

2 files changed

+79
-22
lines changed

2 files changed

+79
-22
lines changed

src/peripheral/nvic.rs

+79-16
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,48 @@ use interrupt::Nr;
88
#[repr(C)]
99
pub struct RegisterBlock {
1010
/// Interrupt Set-Enable
11-
pub iser: [RW<u32>; 8],
12-
reserved0: [u32; 24],
11+
pub iser: [RW<u32>; 16],
12+
reserved0: [u32; 16],
1313
/// Interrupt Clear-Enable
14-
pub icer: [RW<u32>; 8],
15-
reserved1: [u32; 24],
14+
pub icer: [RW<u32>; 16],
15+
reserved1: [u32; 16],
1616
/// Interrupt Set-Pending
17-
pub ispr: [RW<u32>; 8],
18-
reserved2: [u32; 24],
17+
pub ispr: [RW<u32>; 16],
18+
reserved2: [u32; 16],
1919
/// Interrupt Clear-Pending
20-
pub icpr: [RW<u32>; 8],
21-
reserved3: [u32; 24],
20+
pub icpr: [RW<u32>; 16],
21+
reserved3: [u32; 16],
2222
/// Interrupt Active Bit
23-
pub iabr: [RO<u32>; 8],
24-
reserved4: [u32; 56],
23+
pub iabr: [RO<u32>; 16],
24+
reserved4: [u32; 48],
25+
26+
#[cfg(not(armv6m))]
27+
/// 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.
38+
pub ipr: [RW<u8>; 496],
39+
40+
#[cfg(armv6m)]
2541
/// Interrupt Priority
26-
pub ipr: [RW<u8>; 240],
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
}

src/peripheral/test.rs

-6
Original file line numberDiff line numberDiff line change
@@ -104,17 +104,11 @@ fn nvic() {
104104
let nvic = unsafe { &*::peripheral::NVIC::ptr() };
105105

106106
assert_eq!(address(&nvic.iser), 0xE000E100);
107-
assert_eq!(address(&nvic.iser[7]), 0xE000E11C);
108107
assert_eq!(address(&nvic.icer), 0xE000E180);
109-
assert_eq!(address(&nvic.icer[7]), 0xE000E19C);
110108
assert_eq!(address(&nvic.ispr), 0xE000E200);
111-
assert_eq!(address(&nvic.ispr[7]), 0xE000E21C);
112109
assert_eq!(address(&nvic.icpr), 0xE000E280);
113-
assert_eq!(address(&nvic.icpr[7]), 0xE000E29C);
114110
assert_eq!(address(&nvic.iabr), 0xE000E300);
115-
assert_eq!(address(&nvic.iabr[7]), 0xE000E31C);
116111
assert_eq!(address(&nvic.ipr), 0xE000E400);
117-
assert_eq!(address(&nvic.ipr[239]), 0xE000E4eF);
118112
}
119113

120114
#[test]

0 commit comments

Comments
 (0)