Skip to content

Commit

Permalink
Auto merge of #62 - hannobraun:update-nvic, r=japaric
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
homunkulus committed Dec 20, 2017
2 parents 74cb12e + d952f0c commit 4689992
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 22 deletions.
95 changes: 79 additions & 16 deletions src/peripheral/nvic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
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
}
}
6 changes: 0 additions & 6 deletions src/peripheral/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down

0 comments on commit 4689992

Please sign in to comment.