中断在教科书里总是一副温文尔雅的模样——“优雅的异步通知”、“高效的事件响应”,但实际它是怎么实现的呢?
上一篇文章真真没啥技术的我学Linux驱动 |多副面孔的CPU和它的寄存器侍卫在Cortex-A7内核架构下,介绍了ARM的运行模式和核心寄存器,并在片尾以
'''
......
一声急报,从远处响起。。。。。。
“报——!北境有烽火骤起!”
......
'''片段,引出本篇内容。
本篇属于第一部分 万事开头难之入门篇
扒一扒中断的底裤
这是一个系列文章,系列引子在
在 i.MX.6ULL 这座城里,GPIO外设要是急眼了,它绝不会客气地敲门。但你以为它能直接冲进皇宫揪住 CPU 皇帝的领子?想多了。 在它和 CPU 之间,横着一个掌握生杀大权的“大内总管”——GIC(通用中断控制器,就跟STM32 Cortex-M中NVIC一样,只不过这个更复杂)。



)。




fromMCIMX6Y2.htypedef enum IRQn {/* Auxiliary constants */NotAvail_IRQn = -128, /**< Not available device specific interrupt *//* Core interrupts */Software0_IRQn = 0, /**< Cortex-A7 Software Generated Interrupt 0 */Software1_IRQn = 1, /**< Cortex-A7 Software Generated Interrupt 1 */Software2_IRQn = 2, /**< Cortex-A7 Software Generated Interrupt 2 */Software3_IRQn = 3, /**< Cortex-A7 Software Generated Interrupt 3 */Software4_IRQn = 4, /**< Cortex-A7 Software Generated Interrupt 4 */Software5_IRQn = 5, /**< Cortex-A7 Software Generated Interrupt 5 */Software6_IRQn = 6, /**< Cortex-A7 Software Generated Interrupt 6 */Software7_IRQn = 7, /**< Cortex-A7 Software Generated Interrupt 7 */Software8_IRQn = 8, /**< Cortex-A7 Software Generated Interrupt 8 */Software9_IRQn = 9, /**< Cortex-A7 Software Generated Interrupt 9 */Software10_IRQn = 10, /**< Cortex-A7 Software Generated Interrupt 10 */Software11_IRQn = 11, /**< Cortex-A7 Software Generated Interrupt 11 */Software12_IRQn = 12, /**< Cortex-A7 Software Generated Interrupt 12 */Software13_IRQn = 13, /**< Cortex-A7 Software Generated Interrupt 13 */Software14_IRQn = 14, /**< Cortex-A7 Software Generated Interrupt 14 */Software15_IRQn = 15, /**< Cortex-A7 Software Generated Interrupt 15 */VirtualMaintenance_IRQn = 25, /**< Cortex-A7 Virtual Maintenance Interrupt */HypervisorTimer_IRQn = 26, /**< Cortex-A7 Hypervisor Timer Interrupt */VirtualTimer_IRQn = 27, /**< Cortex-A7 Virtual Timer Interrupt */LegacyFastInt_IRQn = 28, /**< Cortex-A7 Legacy nFIQ signal Interrupt */SecurePhyTimer_IRQn = 29, /**< Cortex-A7 Secure Physical Timer Interrupt */NonSecurePhyTimer_IRQn = 30, /**< Cortex-A7 Non-secure Physical Timer Interrupt */LegacyIRQ_IRQn = 31, /**< Cortex-A7 Legacy nIRQ Interrupt *//* Device specific interrupts */IOMUXC_IRQn = 32, /**< General Purpose Register 1 from IOMUXC. Used to notify cores on exception condition while boot. */DAP_IRQn = 33, /**< Debug Access Port interrupt request. */SDMA_IRQn = 34, /**< SDMA interrupt request from all channels. */TSC_IRQn = 35, /**< TSC interrupt. */SNVS_IRQn = 36, /**< Logic OR of SNVS_LP and SNVS_HP interrupts. */Reserved37_IRQn = 37, /**< Reserved */.....UART1_IRQn = 58, /**< UART1 interrupt request. */UART2_IRQn = 59, /**< UART2 interrupt request. */......I2C2_IRQn = 69, /**< I2C2 interrupt request. */I2C3_IRQn = 70, /**< I2C3 interrupt request. */......GPIO1_Combined_0_15_IRQn = 98, /**< Combined interrupt indication for GPIO1 signals 0 - 15. */GPIO1_Combined_16_31_IRQn = 99, /**< Combined interrupt indication for GPIO1 signals 16 - 31. */......Reserved157_IRQn = 157, /**< Reserved */Reserved158_IRQn = 158, /**< Reserved */PMU_IRQ2_IRQn = 159 /**< Brown-out event on either core, gpu or soc regulators. */} IRQn_Type;
typedef struct{uint32_t RESERVED0[1024];__IOM uint32_t D_CTLR; /*!< Offset: 0x1000 (R/W) Distributor Control Register */__IM uint32_t D_TYPER; /*!< Offset: 0x1004 (R/ ) Interrupt Controller Type Register */__IM uint32_t D_IIDR; /*!< Offset: 0x1008 (R/ ) Distributor Implementer Identification Register */uint32_t RESERVED1[29];__IOM uint32_t D_IGROUPR[16]; /*!< Offset: 0x1080 - 0x0BC (R/W) Interrupt Group Registers */uint32_t RESERVED2[16];__IOM uint32_t D_ISENABLER[16]; /*!< Offset: 0x1100 - 0x13C (R/W) Interrupt Set-Enable Registers */uint32_t RESERVED3[16];__IOM uint32_t D_ICENABLER[16]; /*!< Offset: 0x1180 - 0x1BC (R/W) Interrupt Clear-Enable Registers */uint32_t RESERVED4[16];__IOM uint32_t D_ISPENDR[16]; /*!< Offset: 0x1200 - 0x23C (R/W) Interrupt Set-Pending Registers */uint32_t RESERVED5[16];__IOM uint32_t D_ICPENDR[16]; /*!< Offset: 0x1280 - 0x2BC (R/W) Interrupt Clear-Pending Registers */uint32_t RESERVED6[16];__IOM uint32_t D_ISACTIVER[16]; /*!< Offset: 0x1300 - 0x33C (R/W) Interrupt Set-Active Registers */uint32_t RESERVED7[16];__IOM uint32_t D_ICACTIVER[16]; /*!< Offset: 0x1380 - 0x3BC (R/W) Interrupt Clear-Active Registers */uint32_t RESERVED8[16];__IOM uint8_t D_IPRIORITYR[512]; /*!< Offset: 0x1400 - 0x5FC (R/W) Interrupt Priority Registers */uint32_t RESERVED9[128];__IOM uint8_t D_ITARGETSR[512]; /*!< Offset: 0x1800 - 0x9FC (R/W) Interrupt Targets Registers */uint32_t RESERVED10[128];__IOM uint32_t D_ICFGR[32]; /*!< Offset: 0x1C00 - 0xC7C (R/W) Interrupt configuration registers */uint32_t RESERVED11[32];__IM uint32_t D_PPISR; /*!< Offset: 0x1D00 (R/ ) Private Peripheral Interrupt Status Register */__IM uint32_t D_SPISR[15]; /*!< Offset: 0x1D04 - 0xD3C (R/ ) Shared Peripheral Interrupt Status Registers */uint32_t RESERVED12[112];__OM uint32_t D_SGIR; /*!< Offset: 0x1F00 ( /W) Software Generated Interrupt Register */uint32_t RESERVED13[3];__IOM uint8_t D_CPENDSGIR[16]; /*!< Offset: 0x1F10 - 0xF1C (R/W) SGI Clear-Pending Registers */__IOM uint8_t D_SPENDSGIR[16]; /*!< Offset: 0x1F20 - 0xF2C (R/W) SGI Set-Pending Registers */uint32_t RESERVED14[40];__IM uint32_t D_PIDR4; /*!< Offset: 0x1FD0 (R/ ) Peripheral ID4 Register */__IM uint32_t D_PIDR5; /*!< Offset: 0x1FD4 (R/ ) Peripheral ID5 Register */__IM uint32_t D_PIDR6; /*!< Offset: 0x1FD8 (R/ ) Peripheral ID6 Register */__IM uint32_t D_PIDR7; /*!< Offset: 0x1FDC (R/ ) Peripheral ID7 Register */__IM uint32_t D_PIDR0; /*!< Offset: 0x1FE0 (R/ ) Peripheral ID0 Register */__IM uint32_t D_PIDR1; /*!< Offset: 0x1FE4 (R/ ) Peripheral ID1 Register */__IM uint32_t D_PIDR2; /*!< Offset: 0x1FE8 (R/ ) Peripheral ID2 Register */__IM uint32_t D_PIDR3; /*!< Offset: 0x1FEC (R/ ) Peripheral ID3 Register */__IM uint32_t D_CIDR0; /*!< Offset: 0x1FF0 (R/ ) Component ID0 Register */__IM uint32_t D_CIDR1; /*!< Offset: 0x1FF4 (R/ ) Component ID1 Register */__IM uint32_t D_CIDR2; /*!< Offset: 0x1FF8 (R/ ) Component ID2 Register */__IM uint32_t D_CIDR3; /*!< Offset: 0x1FFC (R/ ) Component ID3 Register */__IOM uint32_t C_CTLR; /*!< Offset: 0x2000 (R/W) CPU Interface Control Register */__IOM uint32_t C_PMR; /*!< Offset: 0x2004 (R/W) Interrupt Priority Mask Register */__IOM uint32_t C_BPR; /*!< Offset: 0x2008 (R/W) Binary Point Register */__IM uint32_t C_IAR; /*!< Offset: 0x200C (R/ ) Interrupt Acknowledge Register */__OM uint32_t C_EOIR; /*!< Offset: 0x2010 ( /W) End Of Interrupt Register */__IM uint32_t C_RPR; /*!< Offset: 0x2014 (R/ ) Running Priority Register */__IM uint32_t C_HPPIR; /*!< Offset: 0x2018 (R/ ) Highest Priority Pending Interrupt Register */__IOM uint32_t C_ABPR; /*!< Offset: 0x201C (R/W) Aliased Binary Point Register */__IM uint32_t C_AIAR; /*!< Offset: 0x2020 (R/ ) Aliased Interrupt Acknowledge Register */__OM uint32_t C_AEOIR; /*!< Offset: 0x2024 ( /W) Aliased End Of Interrupt Register */__IM uint32_t C_AHPPIR; /*!< Offset: 0x2028 (R/ ) Aliased Highest Priority Pending Interrupt Register */uint32_t RESERVED15[41];__IOM uint32_t C_APR0; /*!< Offset: 0x20D0 (R/W) Active Priority Register */uint32_t RESERVED16[3];__IOM uint32_t C_NSAPR0; /*!< Offset: 0x20E0 (R/W) Non-secure Active Priority Register */uint32_t RESERVED17[6];__IM uint32_t C_IIDR; /*!< Offset: 0x20FC (R/ ) CPU Interface Identification Register */uint32_t RESERVED18[960];__OM uint32_t C_DIR; /*!< Offset: 0x3000 ( /W) Deactivate Interrupt Register */} GIC_Type;
.syntax unified.arch armv7-a.section .isr_vector, "a".align 2.globl __isr_vector__isr_vector:ldr pc, =Reset_Handler /* Reset */ldr pc, =Undefined_Handler /* Undefined instructions */ldr pc, =SVC_Handler /* Supervisor Call */ldr pc, =PrefAbort_Handler /* Prefetch abort */ldr pc, =DataAbort_Handler /* Data abort */.word 0 /* RESERVED */ldr pc, =IRQ_Handler /* IRQ interrupt */ldr pc, =FIQ_Handler /* FIQ interrupt */
IRQ_Handler:push {lr} /* Save return address+4 */push {r0-r3, r12} /* Push caller save registers */MRS r0, spsr /* Save SPRS to allow interrupt reentry */push {r0}MRC P15, 4, r1, C15, C0, 0 /* Get GIC base address */ADD r1, r1, #0x2000 /* r1: GICC base address */LDR r0, [r1, #0xC] /* r0: IAR */push {r0, r1}CPS #0x13 /* Change to Supervisor mode to allow interrupt reentry */push {lr} /* Save Supervisor lr */LDR r2, =SystemIrqHandlerBLX r2 /* Call SystemIrqHandler with param GCC */POP {lr}CPS #0x12 /* Back to IRQ mode */POP {r0, r1}STR r0, [r1, #0x10] /* Now IRQ handler finished: write to EOIR */POP {r0}MSR spsr_cxsf, r0POP {r0-r3, r12}POP {lr}SUBS pc, lr, #4
voidSystemIrqHandler(uint32_t giccIar) __attribute__((weak)){#else#error Not supported compiler type#endifuint32_t intNum = giccIar & 0x3FFUL;/* Spurious interrupt ID or Wrong interrupt number */if ((intNum == 1023) || (intNum >= NUMBER_OF_INT_VECTORS)){return;}irqNesting++;__enable_irq(); /* Support nesting interrupt *//* Now call the real irq handler for intNum */irqTable[intNum].irqHandler(giccIar, irqTable[intNum].userParam);__disable_irq();irqNesting--;}
voidGPIO1_IO18_IRQHandler(void){/* clear the interrupt status */GPIO_ClearPinsInterruptFlags(GPIO1, 18);}
__attribute__( ( always_inline ) ) __STATIC_INLINE void __enable_irq(void){__ASM volatile ("cpsie i" : : : "memory");}__attribute__( ( always_inline ) ) __STATIC_INLINE void __disable_irq(void){__ASM volatile ("cpsid i" : : : "memory");}
此刻,我们知道了程序开头放着异常向量表,如下图所示,
__isr_vector:ldr pc, =Reset_Handler /* Reset */ldr pc, =Undefined_Handler /* Undefined instructions */ldr pc, =SVC_Handler /* Supervisor Call */ldr pc, =PrefAbort_Handler /* Prefetch abort */ldr pc, =DataAbort_Handler /* Data abort */.word 0 /* RESERVED */ldr pc, =IRQ_Handler /* IRQ interrupt */ldr pc, =FIQ_Handler /* FIQ interrupt
但在真真没啥技术的我学Linux驱动 | ROM Code的“接头暗号”铸造“信物”的程序中编译出来的程序,一开始连续好几个18 F0 9F E5是否引起你的好奇?

为什么在当我们在代码中写下不同的“指令咒语”却产生了相同的魔法数字?
带着这样的疑问,让我们走进CPU能听懂的语言-指令集。
参考资料《ARM® Generic Interrupt Controller Architecture version 2.0 Architecture Specification》《i.MX 6ULL Applications Processor Reference Manual》《ARM® Cortex™-A Series Version: 4.0 Programmer’s Guide》《Cortex™-A7 MPCore™ Revision: r0p5 Technical Reference Manual》《ARM® Architecture Reference Manual ARMv7-A and ARMv7-R edition》