aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar homunkulus <homunkulus@gmx.com> 2017-12-20 12:10:25 +0000
committerGravatar homunkulus <homunkulus@gmx.com> 2017-12-20 12:10:25 +0000
commit4689992eace79c49fecac4a51323f0c0dcc1e812 (patch)
treee76e67594bbc3c8a5f74f0acfa4d2f8d40e412f1
parent74cb12e105346d11d57ef653669748d28d96c05e (diff)
parentd952f0ca5705c83e1c1e9ab0fdd1f7d5e2acd1b0 (diff)
downloadcortex-m-4689992eace79c49fecac4a51323f0c0dcc1e812.tar.gz
cortex-m-4689992eace79c49fecac4a51323f0c0dcc1e812.tar.zst
cortex-m-4689992eace79c49fecac4a51323f0c0dcc1e812.zip
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.
-rw-r--r--src/peripheral/nvic.rs95
-rw-r--r--src/peripheral/test.rs6
2 files changed, 79 insertions, 22 deletions
diff --git a/src/peripheral/nvic.rs b/src/peripheral/nvic.rs
index 1154f38..74c6625 100644
--- a/src/peripheral/nvic.rs
+++ b/src/peripheral/nvic.rs
@@ -8,22 +8,48 @@ use interrupt::Nr;
#[repr(C)]
pub struct RegisterBlock {
/// Interrupt Set-Enable
- pub iser: [RW<u32>; 8],
- reserved0: [u32; 24],
+ pub iser: [RW<u32>; 16],
+ reserved0: [u32; 16],
/// Interrupt Clear-Enable
- pub icer: [RW<u32>; 8],
- reserved1: [u32; 24],
+ pub icer: [RW<u32>; 16],
+ reserved1: [u32; 16],
/// Interrupt Set-Pending
- pub ispr: [RW<u32>; 8],
- reserved2: [u32; 24],
+ pub ispr: [RW<u32>; 16],
+ reserved2: [u32; 16],
/// Interrupt Clear-Pending
- pub icpr: [RW<u32>; 8],
- reserved3: [u32; 24],
+ pub icpr: [RW<u32>; 16],
+ reserved3: [u32; 16],
/// Interrupt Active Bit
- pub iabr: [RO<u32>; 8],
- reserved4: [u32; 56],
+ pub iabr: [RO<u32>; 16],
+ reserved4: [u32; 48],
+
+ #[cfg(not(armv6m))]
+ /// Interrupt Priority
+ ///
+ /// On ARMv7-M, 124 word-sized registers are available. Each of those
+ /// contains of 4 interrupt priorities of 8 byte each.The architecture
+ /// specifically allows accessing those along byte boundaries, so they are
+ /// represented as 496 byte-sized registers, for convenience, and to allow
+ /// atomic priority updates.
+ ///
+ /// On ARMv6-M, the registers must only be accessed along word boundaries,
+ /// so convenient byte-sized representation wouldn't work on that
+ /// architecture.
+ pub ipr: [RW<u8>; 496],
+
+ #[cfg(armv6m)]
/// Interrupt Priority
- pub ipr: [RW<u8>; 240],
+ ///
+ /// On ARMv7-M, 124 word-sized registers are available. Each of those
+ /// contains of 4 interrupt priorities of 8 byte each.The architecture
+ /// specifically allows accessing those along byte boundaries, so they are
+ /// represented as 496 byte-sized registers, for convenience, and to allow
+ /// atomic priority updates.
+ ///
+ /// On ARMv6-M, the registers must only be accessed along word boundaries,
+ /// so convenient byte-sized representation wouldn't work on that
+ /// architecture.
+ pub ipr: [RW<u32>; 8],
}
impl RegisterBlock {
@@ -66,9 +92,18 @@ impl RegisterBlock {
where
I: Nr,
{
- let nr = interrupt.nr();
-
- self.ipr[usize::from(nr)].read()
+ #[cfg(not(armv6m))]
+ {
+ let nr = interrupt.nr();
+ self.ipr[usize::from(nr)].read()
+ }
+
+ #[cfg(armv6m)]
+ {
+ let ipr_n = self.ipr[Self::ipr_index(&interrupt)].read();
+ let prio = (ipr_n >> Self::ipr_shift(&interrupt)) & 0x000000ff;
+ prio as u8
+ }
}
/// Is `interrupt` active or pre-empted and stacked
@@ -118,12 +153,40 @@ impl RegisterBlock {
///
/// NOTE See `get_priority` method for an explanation of how NVIC priorities
/// work.
+ ///
+ /// On ARMv6-M, updating an interrupt priority requires a read-modify-write
+ /// operation, which is not atomic. This is inherently racy, so please
+ /// ensure proper access to this method.
+ ///
+ /// On ARMv7-M, this method is atomic.
pub unsafe fn set_priority<I>(&self, interrupt: I, prio: u8)
where
I: Nr,
{
- let nr = interrupt.nr();
+ #[cfg(not(armv6m))]
+ {
+ let nr = interrupt.nr();
+ self.ipr[usize::from(nr)].write(prio)
+ }
+
+ #[cfg(armv6m)]
+ {
+ self.ipr[Self::ipr_index(&interrupt)].modify(|value| {
+ let mask = 0x000000ff << Self::ipr_shift(&interrupt);
+ let prio = u32::from(prio) << Self::ipr_shift(&interrupt);
+
+ (value & !mask) | prio
+ })
+ }
+ }
+
+ #[cfg(armv6m)]
+ fn ipr_index<I>(interrupt: &I) -> usize where I: Nr {
+ usize::from(interrupt.nr()) / 4
+ }
- self.ipr[usize::from(nr)].write(prio)
+ #[cfg(armv6m)]
+ fn ipr_shift<I>(interrupt: &I) -> usize where I: Nr {
+ (usize::from(interrupt.nr()) % 4) * 8
}
}
diff --git a/src/peripheral/test.rs b/src/peripheral/test.rs
index 4283954..d50ece2 100644
--- a/src/peripheral/test.rs
+++ b/src/peripheral/test.rs
@@ -104,17 +104,11 @@ fn nvic() {
let nvic = unsafe { &*::peripheral::NVIC::ptr() };
assert_eq!(address(&nvic.iser), 0xE000E100);
- assert_eq!(address(&nvic.iser[7]), 0xE000E11C);
assert_eq!(address(&nvic.icer), 0xE000E180);
- assert_eq!(address(&nvic.icer[7]), 0xE000E19C);
assert_eq!(address(&nvic.ispr), 0xE000E200);
- assert_eq!(address(&nvic.ispr[7]), 0xE000E21C);
assert_eq!(address(&nvic.icpr), 0xE000E280);
- assert_eq!(address(&nvic.icpr[7]), 0xE000E29C);
assert_eq!(address(&nvic.iabr), 0xE000E300);
- assert_eq!(address(&nvic.iabr[7]), 0xE000E31C);
assert_eq!(address(&nvic.ipr), 0xE000E400);
- assert_eq!(address(&nvic.ipr[239]), 0xE000E4eF);
}
#[test]