diff --git a/src/peripheral/nvic.rs b/src/peripheral/nvic.rs index 1154f388..74c6625c 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; 8], - reserved0: [u32; 24], + pub iser: [RW; 16], + reserved0: [u32; 16], /// Interrupt Clear-Enable - pub icer: [RW; 8], - reserved1: [u32; 24], + pub icer: [RW; 16], + reserved1: [u32; 16], /// Interrupt Set-Pending - pub ispr: [RW; 8], - reserved2: [u32; 24], + pub ispr: [RW; 16], + reserved2: [u32; 16], /// Interrupt Clear-Pending - pub icpr: [RW; 8], - reserved3: [u32; 24], + pub icpr: [RW; 16], + reserved3: [u32; 16], /// Interrupt Active Bit - pub iabr: [RO; 8], - reserved4: [u32; 56], + pub iabr: [RO; 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; 496], + + #[cfg(armv6m)] /// Interrupt Priority - pub ipr: [RW; 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; 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(&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(interrupt: &I) -> usize where I: Nr { + usize::from(interrupt.nr()) / 4 + } - self.ipr[usize::from(nr)].write(prio) + #[cfg(armv6m)] + fn ipr_shift(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 4283954e..d50ece28 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]