Skip to content

Update NVIC #62

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 20, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is not an issue with scoped singletons, at least if we change the signature to &mut self. With scoped singletons if the singleton (peripheral) in owned by an execution context then that context has &mut - access to the peripheral and this is not an issue. If the peripheral is shared between two, or more, execution contexts then some mechanism must be in place to (safely and temporarily) yield a &mut- reference to the register block to one of the contexts -- again, no issue there.

I think it's still a good idea to mention that the operation is an atomic write on ARMv7 and a RMW operation on ARMv6.

/// 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