aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Hanno Braun <hanno@braun-robotics.com> 2017-10-13 07:41:24 +0200
committerGravatar Hanno Braun <hanno@braun-robotics.com> 2017-12-18 15:46:23 +0100
commitd952f0ca5705c83e1c1e9ab0fdd1f7d5e2acd1b0 (patch)
treee76e67594bbc3c8a5f74f0acfa4d2f8d40e412f1 /src
parent03779e5a32e9c5cabd8e86783574ad5a60633bdf (diff)
downloadcortex-m-d952f0ca5705c83e1c1e9ab0fdd1f7d5e2acd1b0.tar.gz
cortex-m-d952f0ca5705c83e1c1e9ab0fdd1f7d5e2acd1b0.tar.zst
cortex-m-d952f0ca5705c83e1c1e9ab0fdd1f7d5e2acd1b0.zip
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.
Diffstat (limited to 'src')
-rw-r--r--src/peripheral/nvic.rs73
1 files changed, 68 insertions, 5 deletions
diff --git a/src/peripheral/nvic.rs b/src/peripheral/nvic.rs
index c8f82e6..74c6625 100644
--- a/src/peripheral/nvic.rs
+++ b/src/peripheral/nvic.rs
@@ -22,8 +22,34 @@ pub struct RegisterBlock {
/// Interrupt Active Bit
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
+ ///
+ /// 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
}
}