diff options
author | 2017-12-20 12:10:25 +0000 | |
---|---|---|
committer | 2017-12-20 12:10:25 +0000 | |
commit | 4689992eace79c49fecac4a51323f0c0dcc1e812 (patch) | |
tree | e76e67594bbc3c8a5f74f0acfa4d2f8d40e412f1 | |
parent | 74cb12e105346d11d57ef653669748d28d96c05e (diff) | |
parent | d952f0ca5705c83e1c1e9ab0fdd1f7d5e2acd1b0 (diff) | |
download | cortex-m-4689992eace79c49fecac4a51323f0c0dcc1e812.tar.gz cortex-m-4689992eace79c49fecac4a51323f0c0dcc1e812.tar.zst cortex-m-4689992eace79c49fecac4a51323f0c0dcc1e812.zip |
Auto merge of #62 - hannobraun:update-nvic, r=japaric
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.
-rw-r--r-- | src/peripheral/nvic.rs | 95 | ||||
-rw-r--r-- | src/peripheral/test.rs | 6 |
2 files changed, 79 insertions, 22 deletions
diff --git a/src/peripheral/nvic.rs b/src/peripheral/nvic.rs index 1154f38..74c6625 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<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 { @@ -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<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 } } diff --git a/src/peripheral/test.rs b/src/peripheral/test.rs index 4283954..d50ece2 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] |