From 03779e5a32e9c5cabd8e86783574ad5a60633bdf Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 13 Oct 2017 07:52:03 +0200 Subject: [PATCH 1/2] Make all available NVIC registers accessible According to the ARMv7-M Technical Reference Manual[1], there are 124 IPR registers available on ARMv7-M, and 16 of all others. I don't know where the original numbers came from, since on ARMv6-M, there are only 8 IPR registers available, and 1 of each of the others.[2] This commit removes some test cases that were checking the address of the last register. Since the last register has changed, those are no longer applicable. I decided to remove instead of update them, since they only really test the length of each register type, which is obvious enough from the code. [1]: https://silver.arm.com/download/ARM_and_AMBA_Architecture/AR580-DA-70000-r0p0-05rel0/DDI0403E_B_armv7m_arm.pdf [2]: https://silver.arm.com/download/ARM_and_AMBA_Architecture/AR585-DA-70000-r0p0-00rel0/DDI0419C_arm_architecture_v6m_reference_manual.pdf --- src/peripheral/nvic.rs | 22 +++++++++++----------- src/peripheral/test.rs | 6 ------ 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/peripheral/nvic.rs b/src/peripheral/nvic.rs index 1154f388..c8f82e6e 100644 --- a/src/peripheral/nvic.rs +++ b/src/peripheral/nvic.rs @@ -8,22 +8,22 @@ 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], /// Interrupt Priority - pub ipr: [RW; 240], + pub ipr: [RW; 496], } impl RegisterBlock { 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] From d952f0ca5705c83e1c1e9ab0fdd1f7d5e2acd1b0 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 13 Oct 2017 07:41:24 +0200 Subject: [PATCH 2/2] 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. --- src/peripheral/nvic.rs | 73 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 5 deletions(-) diff --git a/src/peripheral/nvic.rs b/src/peripheral/nvic.rs index c8f82e6e..74c6625c 100644 --- a/src/peripheral/nvic.rs +++ b/src/peripheral/nvic.rs @@ -22,8 +22,34 @@ pub struct RegisterBlock { /// Interrupt Active Bit 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 + /// + /// 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 } }