1 armv7 GIC介绍
armv7 32位 gic采用v2版本,参考手册 https://developer.arm.com/documentation/ihi0048/bb/?lang=en
GIC400 就是v2版本的中断控制器 IP 核,当 GIC 接收到外部中断信号以后就会报给 ARM 内核。框架如下:
GIC 架构分为了两个逻辑块:Distributor 和 CPU Interface,也就是分发器端和 CPU 接口端。
分发器用来全局中断使能控制,每一个中断使能开关,中断优先级,外部中断触发方式(边沿触发、电平触发)等。
cpu接收端用来接收中断信号汇报给cpu, 如:应答中断,通知中断处理完成,定义抢占策略,设置优先级掩码,当多个中断到来选择最高优先级的中断号。
例如:I.MX6U给了一个 core_ca7.h定义了GIC的所有寄存器。
/*
* GIC 寄存器描述结构体,
* GIC 分为分发器端和 CPU 接口端
*/
typedef struct {
/* 分发器端寄存器 */
int32_t RESERVED0[1024];
_IOM uint32_t D_CTLR; /* Offset: 0x1000 (R/W) */
_IM uint32_t D_TYPER; /* Offset: 0x1004 (R/ ) */
_IM uint32_t D_IIDR; /* Offset: 0x1008 (R/ ) */
int32_t RESERVED1[29];
_IOM uint32_t D_IGROUPR[16]; /* Offset: 0x1080 - 0x0BC (R/W) */
uint32_t RESERVED2[16];
__IOM uint32_t D_ISENABLER[16];/* Offset: 0x1100 - 0x13C (R/W) */
uint32_t RESERVED3[16];
__IOM uint32_t D_ICENABLER[16];/* Offset: 0x1180 - 0x1BC (R/W) */
uint32_t RESERVED4[16];
__IOM uint32_t D_ISPENDR[16]; /* Offset: 0x1200 - 0x23C (R/W) */
uint32_t RESERVED5[16];
__IOM uint32_t D_ICPENDR[16]; /* Offset: 0x1280 - 0x2BC (R/W) */
uint32_t RESERVED6[16];
__IOM uint32_t D_ISACTIVER[16];/* Offset: 0x1300 - 0x33C (R/W) */
uint32_t RESERVED7[16];
__IOM uint32_t D_ICACTIVER[16];/* Offset: 0x1380 - 0x3BC (R/W) */
uint32_t RESERVED8[16];
__IOM uint8_t D_IPRIORITYR[512];/* Offset: 0x1400 - 0x5FC (R/W) */
uint32_t RESERVED9[128];
__IOM uint8_t D_ITARGETSR[512];/* Offset: 0x1800 - 0x9FC (R/W) */
uint32_t RESERVED10[128];
__IOM uint32_t D_ICFGR[32]; /* Offset: 0x1C00 - 0xC7C (R/W) */
uint32_t RESERVED11[32];
__IM uint32_t D_PPISR; /* Offset: 0x1D00 (R/ ) */
__IM uint32_t D_SPISR[15]; /* Offset: 0x1D04 - 0xD3C (R/ ) */
uint32_t RESERVED12[112];
__OM uint32_t D_SGIR; /* Offset: 0x1F00 ( /W) */
uint32_t RESERVED13[3];
__IOM uint8_t D_CPENDSGIR[16];/* Offset: 0x1F10 - 0xF1C (R/W) */
__IOM uint8_t D_SPENDSGIR[16];/* Offset: 0x1F20 - 0xF2C (R/W) */
uint32_t RESERVED14[40];
__IM uint32_t D_PIDR4; /* Offset: 0x1FD0 (R/ ) */
__IM uint32_t D_PIDR5; /* Offset: 0x1FD4 (R/ ) */
__IM uint32_t D_PIDR6; /* Offset: 0x1FD8 (R/ ) */
__IM uint32_t D_PIDR7; /* Offset: 0x1FDC (R/ ) */
__IM uint32_t D_PIDR0; /* Offset: 0x1FE0 (R/ ) */
__IM uint32_t D_PIDR1; /* Offset: 0x1FE4 (R/ ) */
__IM uint32_t D_PIDR2; /* Offset: 0x1FE8 (R/ ) */
__IM uint32_t D_PIDR3; /* Offset: 0x1FEC (R/ ) */
__IM uint32_t D_CIDR0; /* Offset: 0x1FF0 (R/ ) */
__IM uint32_t D_CIDR1; /* Offset: 0x1FF4 (R/ ) */
__IM uint32_t D_CIDR2; /* Offset: 0x1FF8 (R/ ) */
__IM uint32_t D_CIDR3; /* Offset: 0x1FFC (R/ ) */
/* CPU 接口端寄存器 */
__IOM uint32_t C_CTLR; /* Offset: 0x2000 (R/W) */
__IOM uint32_t C_PMR; /* Offset: 0x2004 (R/W) */
__IOM uint32_t C_BPR; /* Offset: 0x2008 (R/W) */
__IM uint32_t C_IAR; /* Offset: 0x200C (R/ ) */
__OM uint32_t C_EOIR; /* Offset: 0x2010 ( /W) */
__IM uint32_t C_RPR; /* Offset: 0x2014 (R/ ) */
__IM uint32_t C_HPPIR; /* Offset: 0x2018 (R/ ) */
__IOM uint32_t C_ABPR; /* Offset: 0x201C (R/W) */
__IM uint32_t C_AIAR; /* Offset: 0x2020 (R/ ) */
__OM uint32_t C_AEOIR; /* Offset: 0x2024 ( /W) */
__IM uint32_t C_AHPPIR; /* Offset: 0x2028 (R/ ) */
uint32_t RESERVED15[41];
__IOM uint32_t C_APR0; /* Offset: 0x20D0 (R/W) */
uint32_t RESERVED16[3];
__IOM uint32_t C_NSAPR0; /* Offset: 0x20E0 (R/W) */
uint32_t RESERVED17[6];
__IM uint32_t C_IIDR; /* Offset: 0x20FC (R/ ) */
uint32_t RESERVED18[960];
__OM uint32_t C_DIR; /* Offset: 0x3000 ( /W) */
} GIC_Type;
1.1 GIC 类型
GIC 将众多的中断源分为分为三类:
①、SPI(Shared Peripheral Interrupt),共享中断,顾名思义,所有 Core 共享的中断,这个是最常见的,那些外部中断都属于 SPI 中断 。
②、PPI(Private Peripheral Interrupt),私有中断,我们说了 GIC 是支持多核的,每个核肯定有自己独有的中断。这些独有的中断肯定是要指定的核心处理,因此这些中断就叫做私有中断。
③、SGI(Software-generated Interrupt),软件中断,由软件触发引起的中断,通过向寄存器GICD_SGIR 写入数据来触发,系统会使用 SGI 中断来完成多核之间的通信。
1.2 中断 ID
为了区分这些不同的中断源肯定要给他们分配一个唯一 ID,这些 ID 就是中断 ID。每一个 CPU 最多支持 1020 个中断 ID,ID0~ID1019 中断使能和禁止。这 1020 个 ID 包含了 PPI、SPI 和 SGI。
ID0~ID15:这 16 个 ID 分配给 SGI。
ID16~ID31:这 16 个 ID 分配给 PPI。
ID32~ID1019:这 988 个 ID 分配给 SPI。
例如:I.MX6U 的总共使用了 128 个中断 ID,加上前面属于 PPI 和 SGI 的 32 个 ID,I.MX6U 的中断源共有 128+32=160,那么irq为0的中断ID即为32。
个。NXP 官方 SDK中的文件 MCIMX6Y2C.h定义了160个中断ID。
#define NUMBER_OF_INT_VECTORS 160 /* 中断源 160 个,SGI+PPI+SPI*/
typedef enum IRQn {
/* Auxiliary constants */
otAvail_IRQn = -128,
/* Core interrupts */
oftware0_IRQn = 0,
oftware1_IRQn = 1,
Software2_IRQn = 2,
Software3_IRQn = 3,
Software4_IRQn = 4,
Software5_IRQn = 5,
Software6_IRQn = 6,
Software7_IRQn = 7,
Software8_IRQn = 8,
Software9_IRQn = 9,
Software10_IRQn = 10,
Software11_IRQn = 11,
Software12_IRQn = 12,
Software13_IRQn = 13,
Software14_IRQn = 14,
Software15_IRQn = 15,
VirtualMaintenance_IRQn = 25,
HypervisorTimer_IRQn = 26,
VirtualTimer_IRQn = 27,
LegacyFastInt_IRQn = 28,
SecurePhyTimer_IRQn = 29,
NonSecurePhyTimer_IRQn = 30,
LegacyIRQ_IRQn = 31,
/* Device specific interrupts */
IOMUXC_IRQn = 32,
DAP_IRQn = 33,
SDMA_IRQn = 34,
TSC_IRQn = 35,
SNVS_IRQn = 36,
...... ......
} IRQn_Type;
1.3 中断使能
1.3.1 IRQ 和 FIQ 总中断使能
"CPSR程序状态寄存器”已经讲过了,寄存器 CPSR 的 I=1 禁止 IRQ,当 I=0 使
能 IRQ;F=1 禁止 FIQ,F=0 使能 FIQ。我们还有更简单的指令来完成 IRQ 或者 FIQ 的使能和
禁止:
指令 | 描述 |
---|---|
cpsid i | 禁止 IRQ 中断。 |
cpsie i | 使能 IRQ 中断。 |
cpsid f | 禁止 FIQ 中断。 |
cpsie f | 使能 FIQ 中断。 |
1.3.2 ID0~ID1019 中断使能和禁止
前面讲到中断ID有 ID0~ID1019, GIC 寄存器 GICD_ISENABLERn 和 GICD_ ICENABLERn 用来完成外部中断的使能和禁止,对于 Cortex-A7 内核来说中断 ID 只使用了 512 个。
一共16组GICD_ISENABLER和GICD_ISENABLER,其中GICD_ISENABLER0 的 bit[15:0]对应ID15~0 的 SGI 中断,GICD_ISENABLER0 的 bit[31:16]对应 ID31~16 的 PPI 中断。剩下的GICD_ISENABLER1~GICD_ISENABLER15 就是控制 SPI 中断的。
1.3.3 中断优先级数量 GICC_PMR
Cortex-A7 GIC 控制器最多可以支持 256 个优先级,数字越小,优先级越高!Cortex-A7 选择了 32 个优先级,GICC_PMR 寄存器,此寄存器用来决定使用几级优先级,GICC_PMR 寄存器只有低 8 位有效,这 8 位最多可以设置 256 个优先级:
I.MX6U 为例 Cortex-A7内核,所以支持 32 个优先级,因此 GICC_PMR 要设置为 0b11111000。
1.3.4 中断抢占优先级和子优先级位数 GICC_BPR
寄存器 GICC_BPR 只有低 3 位有效,其值不同,抢占优先级和子优先级占用的位数也不同:
比如 I.MX6U 的优先级位数为 5(32 个优先级),所以可以设置 Binary point 为 2,表示 5 个优先级位全部为抢占优先级。
1.3.5 中断priority D_IPRIORITYR
Cortex-A7 使用了 512 个中断 ID,每个中断 ID 配有一个优先级寄存器,所以一共有 512 个 D_IPRIORITYR 寄存器。如果优先级个数为 32 的话,使用寄存器 D_IPRIORITYR 的 bit7:4 来设置优先级,也就是说实际的优先级要左移 3 位。比如要设置ID40 中断的优先级为 5,示例代码如下:
GICD_IPRIORITYR[40] = 5 << 3;
1.4 GIC控制器寄存器介绍
前面讲了GIC 架构分为了两个逻辑块:Distributor 和 CPU Interface,也就是分发器端和 CPU 接口端。
1.4.1 GIC的内存映射
GIC基地址偏移0x1000是分发器 block, 偏移0x2000是CPU 接口端 block。
1.4.1.1 分发器寄存器
1.4.1.2 cpu接口端寄存器
GICC_IAR寄存器描述来自《ARM Generic Interrupt Controller Architecture Specification.pdf》,它用来表示中断ID号。
处理完具体的中断处理函数,需要将GICC_IAR寄存器的值写入GICC_EOIR寄存器中。
For every read of a valid Interrupt ID from the GICC_IAR, the connected processor must perform a matching write to the GICC_EOIR. The value written to the GICC_EOIR must be the interrupt ID read from the GICC_IAR。
2. 中断示例start.s分析
以nxp的IMX6UL为例,SDK中core_ca7.h定了了GIC相关函数:
函数 | 描述 |
---|---|
GIC_Init | 初始化 GIC。 |
GIC_EnableIRQ | 使能指定的外设中断。 |
GIC_DisableIRQ | 关闭指定的外设中断。 |
GIC_AcknowledgeIRQ | 返回中断号。 |
GIC_DeactivateIRQ | 无效化指定中断。 |
GIC_GetRunningPriority | 获取当前正在运行的中断优先级。 |
GIC_SetPriorityGrouping | 设置抢占优先级位数。 |
GIC_GetPriorityGrouping | 获取抢占优先级位数。 |
GIC_SetPriority | 设置指定中断的优先级。 |
GIC_GetPriority | 获取指定中断的优先级。 |
点击查看代码
.global _start /* 全局标号 */
_start:
ldr pc, =Reset_Handler /* 复位中断 */
ldr pc, =Undefined_Handler /* 未定义中断 */
ldr pc, =SVC_Handler /* SVC(Supervisor)中断 */
ldr pc, =PrefAbort_Handler /* 预取终止中断 */
ldr pc, =DataAbort_Handler /* 数据终止中断 */
ldr pc, =NotUsed_Handler /* 未使用中断 */
ldr pc, =IRQ_Handler /* IRQ中断 */
ldr pc, =FIQ_Handler /* FIQ(快速中断)未定义中断 */
/* 复位中断 */
Reset_Handler:
cpsid i /* 关闭全局中断 */
/* 关闭I,DCache和MMU
* 采取读-改-写的方式。
*/
mrc p15, 0, r0, c1, c0, 0 /* 读取CP15的C1寄存器到R0中 */
bic r0, r0, #(0x1 << 12) /* 清除C1寄存器的bit12位(I位),关闭I Cache */
bic r0, r0, #(0x1 << 2) /* 清除C1寄存器的bit2(C位),关闭D Cache */
bic r0, r0, #0x2 /* 清除C1寄存器的bit1(A位),关闭对齐 */
bic r0, r0, #(0x1 << 11) /* 清除C1寄存器的bit11(Z位),关闭分支预测 */
bic r0, r0, #0x1 /* 清除C1寄存器的bit0(M位),关闭MMU */
mcr p15, 0, r0, c1, c0, 0 /* 将r0寄存器中的值写入到CP15的C1寄存器中 */
#if 0
/* 汇编版本设置中断向量表偏移 */
ldr r0, =0X87800000
dsb
isb
mcr p15, 0, r0, c12, c0, 0
dsb
isb
#endif
/* 设置各个模式下的栈指针,
* 注意:IMX6UL的堆栈是向下增长的!
* 堆栈指针地址一定要是4字节地址对齐的!!!
* DDR范围:0X80000000~0X9FFFFFFF
*/
/* 进入IRQ模式 */
mrs r0, cpsr
bic r0, r0, #0x1f /* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 */
orr r0, r0, #0x12 /* r0或上0x13,表示使用IRQ模式 */
msr cpsr, r0 /* 将r0 的数据写入到cpsr_c中 */
ldr sp, =0x80600000 /* 设置IRQ模式下的栈首地址为0X80600000,大小为2MB */
/* 进入SYS模式 */
mrs r0, cpsr
bic r0, r0, #0x1f /* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 */
orr r0, r0, #0x1f /* r0或上0x13,表示使用SYS模式 */
msr cpsr, r0 /* 将r0 的数据写入到cpsr_c中 */
ldr sp, =0x80400000 /* 设置SYS模式下的栈首地址为0X80400000,大小为2MB */
/* 进入SVC模式 */
mrs r0, cpsr
bic r0, r0, #0x1f /* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 */
orr r0, r0, #0x13 /* r0或上0x13,表示使用SVC模式 */
msr cpsr, r0 /* 将r0 的数据写入到cpsr_c中 */
ldr sp, =0X80200000 /* 设置SVC模式下的栈首地址为0X80200000,大小为2MB */
cpsie i /* 打开全局中断 */
#if 0
/* 使能IRQ中断 */
mrs r0, cpsr /* 读取cpsr寄存器值到r0中 */
bic r0, r0, #0x80 /* 将r0寄存器中bit7清零,也就是CPSR中的I位清零,表示允许IRQ中断 */
msr cpsr, r0 /* 将r0重新写入到cpsr中 */
#endif
b main /* 跳转到main函数 */
/* 未定义中断 */
Undefined_Handler:
ldr r0, =Undefined_Handler
bx r0
/* SVC中断 */
SVC_Handler:
ldr r0, =SVC_Handler
bx r0
/* 预取终止中断 */
PrefAbort_Handler:
ldr r0, =PrefAbort_Handler
bx r0
/* 数据终止中断 */
DataAbort_Handler:
ldr r0, =DataAbort_Handler
bx r0
/* 未使用的中断 */
NotUsed_Handler:
ldr r0, =NotUsed_Handler
bx r0
/* IRQ中断!重点!!!!! */
IRQ_Handler:
push {lr} /* 保存lr地址 */
push {r0-r3, r12} /* 保存r0-r3,r12寄存器 */
mrs r0, spsr /* 读取spsr寄存器 */
push {r0} /* 保存spsr寄存器 */
mrc p15, 4, r1, c15, c0, 0 /* 从CP15的C0寄存器内的值到R1寄存器中
* 参考文档ARM Cortex-A(armV7)编程手册V4.0.pdf P49
* Cortex-A7 Technical ReferenceManua.pdf P68 P138
*/
add r1, r1, #0X2000 /* GIC基地址加0X2000,也就是GIC的CPU接口端基地址 */
ldr r0, [r1, #0XC] /* GIC的CPU接口端基地址加0X0C就是GICC_IAR寄存器,
* GICC_IAR寄存器保存这当前发生中断的中断号,我们要根据
* 这个中断号来绝对调用哪个中断服务函数
*/
push {r0, r1} /* 保存r0,r1 */
cps #0x13 /* 进入SVC模式,允许其他中断再次进去 */
push {lr} /* 保存SVC模式的lr寄存器 */
ldr r2, =system_irqhandler /* 加载C语言中断处理函数到r2寄存器中*/
blx r2 /* 运行C语言中断处理函数,带有一个参数,保存在R0寄存器中 */
pop {lr} /* 执行完C语言中断服务函数,lr出栈 */
cps #0x12 /* 进入IRQ模式 */
pop {r0, r1}
str r0, [r1, #0X10] /* 中断执行完成,写EOIR */
pop {r0}
msr spsr_cxsf, r0 /* 恢复spsr */
pop {r0-r3, r12} /* r0-r3,r12出栈 */
pop {lr} /* lr出栈 */
subs pc, lr, #4 /* 将lr-4赋给pc */
/* FIQ中断 */
FIQ_Handler:
ldr r0, =FIQ_Handler
bx r0
2.1 启动流程
1.进入_start,初始化异常向量表。进入复位中断,初始化时钟,关闭看门狗,关闭MMU和ICACHE DCACHE,关闭总中断
2.设置各个模式的SP指针
3.代码段重定位到DDR上并且清bss段
4.开启总中断
5.跳转到C语言main函数执行
这里很多流程如代码重定位,清除bss,关闭看门狗等没有列举出来。
标签:__,10,中断,GIC,armv7,Offset,IRQn,uint32 From: https://www.cnblogs.com/fuzidage/p/17733146.html