Skip to content

Commit

Permalink
Fix IPR representation on ARMv6-M
Browse files Browse the repository at this point in the history
On ARMv6-M, anything but world-aligned access to the IPR registers will
lead to unpredictable results.

Fixes #61.
  • Loading branch information
hannobraun committed Dec 18, 2017
1 parent 03779e5 commit d952f0c
Showing 1 changed file with 68 additions and 5 deletions.
73 changes: 68 additions & 5 deletions src/peripheral/nvic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
}

0 comments on commit d952f0c

Please sign in to comment.