一、中断概念https://m.toutiao.com/is/Jt3oK7Q/ 操作系统中,中断是很重要的组成部分。出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。 有了中断系统才可以不用一直轮询(polling)是否有事件发生,系统效率才得以提高。 一般在系统中,中断控制分为三个部分:「模块、中断控制器和处理器」。 其中模块通常由寄存器控制是否使能中断和中断触发条件等;中断控制器可以管理中断的优先级等,而处理器则由寄存器设置用来响应中断。 二、GIC作为 ARM 系统中通用中断控制器的是 GIC(Generic Interrupt Controller),目前有四个版本,V1~V4(V2最多支持8个ARM core,V3/V4支持更多的ARM core,主要用于ARM64系统结构)。 【注意】对于一些老的ARM处理器,比如ARM11,Cortex-A8,中断控制器一般是VIC(向量中断控制器)。 1. GIC-400下面以GIC-400为例,它更适合嵌入式系统,符合v2版本的GIC architecture specification。GIC-400通过AMBA(Advanced Microcontroller Bus Architecture)片上总线连接到一个或者多个ARM处理器上。 GIC中断控制器全局图 从上图可以看出, GIC 是联系外设中断和 CPU 的桥梁,也是各 CPU 之间中断互联的通道(也带有管理功能),它负责检测、管理、分发中断,可以做到:
ARM CPU 对外的连接只有2 个中断: 「IRQ和FIQ」 ,相对应的处理模式分别是一般中断(IRQ )处理模式和快速中断(FIQ )处理模式。所以GIC 最后要把中断汇集成2 条线,与CPU 对接。 GIC中断控制器结构 分发器:负责各个子中断使能,设置触发方式,优先级排序,分发到哪个 CPU 上; 接口:负责总的中断的使能,状态的维护。 2. 分发器功能分发器的主要的作用是检测各个中断源的状态,控制各个中断源的行为,分发各个中断源产生的中断事件到指定的一个或者多个CPU接口上。虽然分发器可以管理多个中断源,但是它总是把优先级最高的那个中断请求送往CPU接口。分发器对中断的控制包括:
分发器可以管理若干个中断源,这些中断源用ID来标识,我们称之interrupt ID。 3. CPU接口功能CPU接口主要用于和CPU进行接口。 主要功能包括:
key中断管理模块图 以上图为例,该图是按键产生的中断信号要到达cpu所要经过的路径。
三、中断分类1. 中断源硬件中断(Hardware Interrupt)
软件中断(Software Interrupt)软件中断SWI,是一条CPU指令,用以自陷一个中断。由于软中断指令通常要运行一个切换CPU至内核态的子例程,它常被用作实现系统调用(System call)。 外部中断
产生于CPU内部的中断源
2. 中断类型GIC 中断类型有3种:SGI(Software-generated interrupt)、PPI(Private peripheral interrupt )、SPI(Shared peripheral interrupt)。
通常为边沿触发和电平触发。
Note:电平触发是在高或低电平保持的时间内触发, 而边沿触发是由高到低或由低到高这一瞬间触发;在GIC中PPI和SGI类型的中断可以有相同的中断ID。 3. 中断分派模式
举两个例子说明: 1)UART 接收到一包数据,产生了一个中断给GIC,GIC可以将该中断分配给CPU0-7中任何一个处理;假设该中断分配给CPU0处理了,那么在中断处理函数里面会把接收到的数据从UART FIFO读出。可以想象一下,如果CPU0在读数据时,另外一个CPU也在处理该中断,恰巧也在读数据,那么CPU0读到的数据是不全的。这就是1-N model中断,或者说SPI中断。 2)比如CPU0给CPU1-7发送中断,想告知对方自己正在处理某个进程A。这种场景下,CPU1-7都接收到中断,都进入中断处理函数,CPU1-7获取到CPU0的信息后,在进程调度时,就可以绕开进程A,而自己调度其他进程。 注:这个例子只是说明N-N model,实际上进程调度不都全是这样的。 4. 通用中断处理当GIC接收到一个中断请求,将其状态设置为Pending。重新产生一个挂起状态的中断不影响该中断状态。中断处理顺序: ① GIC决定该中断是否使能,若没有被使能对GIC没有影响; ② 对于每个Pending中断,GIC决定目标处理器; ③ 对于每个处理器 ,Distributor根据它拥有的每个中断优先级信息决定最高优先级的挂起中断,将该中断传递给目标CPU Interface; ④ GIC Distributor将一个中断传递给CPU Interface后,该CPU Interface决定该中断是否有足够的优先级将中断请求发给CPU; ⑤ 当CPU开始处理该异常中断,它读取GICC_IAR应答中断。读取的GICC_IAR获取到中断ID,对于SGI,还有源处理器ID。中断ID被用来查找正确的中断处理程序。 GIC识别读过程后,将改变该中断的状态: a) 当中断状态变为active时,如果该中断挂起状态持续存在或者中断再次产生,中断状态将从Pending转化为pending & active b) 否则,中断状态将从pending状态变为active ⑥ 当中断完成中断处理后,它需要通知GIC处理已经完成。这个过程称为 priority drop and interrupt deactivation: a) 总是需要向EOIR寄存器写入一个有效的值(end of interrupt register) b) 也需要接着向GICC_DIR写入值(deactivate interrupt register) 5. 中断优先级软件可以通过给每一个中断源分配优先级值来配置中断优先级。优先级的值是个8位的无符号二进制数,GIC支持最小16和最大256的优先级级别。如果GIC实现的优先级少于256,那么优先级字段的低阶位为RAZ/WI。这就意味着实现的优先级字段个数范围是4~8,如下图所示: Effect of not implementing some priority field bits Note: 1)、如何确定优先级字段所支持的优先级位? 通过软件往可写GICD_IPRIORITYn优先级字段写入0XFF,然后回读出该字段的值便可以确定优先级字段所支持的优先级位(因为有些位没实现是RAZ/WI) 2)、ARM 推荐在检查中断优先级范围之前先: · 对于外设中断,软件先禁用该中断 · 对于SGI,软件先检查该中断确定为inactive 6. 中断抢占在一个active中断处理完之前,CPU interface支持发送更高优先级的挂起中断到目标处理器。这种情况必要条件如下:
7. 中断屏蔽CPU interface的GICC_PMR寄存器定义了目标处理器的优先级阀值,GIC仅上报优先级高于阀值的pending中断给目标处理器。寄存器初始值为0,屏蔽所有的中断。 四、FS4412中断外设-key下面我们来分析FS4412开发板的第一个中断设备按键。 1. 电路图key 由该电路图可得:
以下是key2与soc的连接, key与soc的连接 可以看到key2复用了GPIX1_1这个引脚,同时该引脚还可以作为中断【XEINT9】使用。 顺便看下GPXCON寄存器的配置 GPX1CON 由上图所示,
2. key中断处理中断配置key与soc的关系图如下图所示: 按键中断寄存器配置流程 由上图所示:
清中断CPU处理完中断,需要清除中断,对于按键来说,有3个寄存器需要操作: 清中断 由上图所示:
3. 寄存器汇总前面分析了按键连接的是GPX1_1,现在我们来看下对应的寄存器应该如何配置 【1】、GPIO控制器
将GPX1_1引脚的上拉和下拉禁止 GPX1PUD[3:2]= 0b00;
GPX1CON 将GPX1_1引脚功能设置为中断功能
EXT_INT41CON 配置成成下降沿触发: EXT_INT41CON[6:4] = 0x2
EXT_INT41_MASK 中断使能寄存器
EXT_INT41_PEND 当GPX1_1引脚接收到中断信号,中断发生,中断状态寄存器EXT_INT41_PEND 相应位会自动置1 注意:中断处理完成的时候,需要清除相应状态位。置1清0. EXT_INT41_PEND[1] =0b1 【2】GIC根据外设中断名称EINT9来查看该中断对应的GIC中维护的HW id。【所有的中断源在芯片厂商设计的时候都分配了唯一的一个ID,GIC通过该ID来驱动中断源】 查看芯片手册(datasheet -- 9.2表) GIC中断源表 通过【9.2中断源表】找到和外设中断标示对应的中断控制器中断标识(GPIO有32个可被唤醒寄存器)其对应「EINT[9],中断ID为57」,这是非常重要的,在后面的寄存器设置中起很大作用; 1) ICDISER使能相应中断到分配器 ICDISER ICDISER用于使能相应中断到分配器,一个bit控制一个中断源,一个ICDISER可以控制32个中断源,这里INT[9] 对应的中断ID为57,所以在ICDSER1中进行设置,57/32 =1余25,所以这里在ICDISER1第25位置一。
ICDIPTR ICDIPTR 选择cpu ICDIPTR寄存器每8个bit 控制一个中断源,其中CPU0可以处理160个中断源,所以需要40个寄存器。要选择cpu0第一个bit必须是1。 设置SPI[25]/ID[57]由cpu0处理,57/4=16余1 所以选择寄存器ICDIPTR14的第2个字节[15:8]。 //SPI 25 interrupts are sent to processor 0 //57/4 = 14..1 14号寄存器的[15:8]ICDIPTR.ICDIPTR14 |= 0x01<<8;
ICCPMR CPU0.ICCPMR = 0xFF;//设置cpu0 中断屏蔽优先级为255 最低,所有中断都能响应)
ICCICR EXYNOS 4412一共有4个cpu,用4个寄存器分别来控制4个cpu,每个寄存器的bit[0]用于全局控制对应的cpu。我们选择cpu0处理中断,将bit[0]置1即可。
ICCIAR 当中断发生之后,中断的HW id值会由硬件写入到寄存器ICCIAR[9:0]中;对于SGIs来说,多处理器环境下,CPU的interface值写入到[12:10]中。 读取HW id: int irq_num; irq_num = CPU0.ICCIAR&0x3ff; //获取中断号 五、代码实现要处理中断异常,必须安装异常向量表,异常的处理流程可以参考前面的文章《6. 从0开始学ARM-异常、异常向量表、swi》 1. 异常向量表基址异常向量表地址是可以修改的,比如uboot在启动的时候,会从flash中搬运代码到RAM中,而flash的异常向量表地址和ram的地址肯定不一样,所以搬运完代码后,就必须要修改对应的异常向量表地址。 修改异常向量表的地址的需要借助协处理器指令mcr:
上述命令是将地址0x40008000设置为异常向量表的地址,关于mcr指令,我们没有必要深究,知道即可。 RAM中异常向量表地址我们选用的是0x40008000,以下是exynos4412 地址空间分布。 exynos4412 地址分布 2. 异常向量表安装.text.global _start_start: b reset ldr pc,_undefined_instruction ldr pc,_software_interrupt ldr pc,_prefetch_abort ldr pc,_data_abort ldr pc,_not_used ldr pc,=irq_handler ldr pc,_fiqreset: ldr r0,=0x40008000 mcr p15,0,r0,c12,c0,0 @ Vector Base Address Registerinit_stack://初始化栈……b main //跳转至c的main函数irq_handler: //中断入口函数 sub lr,lr,#4 stmfd sp!,{r0-r12,lr} .weak do_irq bl do_irq ldmfd sp!,{r0-r12,pc}^stacktop: .word stack+4*512//栈顶.datastack: .space 4*512 //栈空间 中断入口函数do_irq()
实现按键中断的初始化函数key_init(): void key_init(void) { GPX1.CON =GPX1.CON & (~(0xf << 4)) |(0xf << 4); //配置引脚功能为外部中断 GPX1.PUD = GPX1.PUD & (~(0x3 << 2)); //关闭上下拉电阻 EXT_INT41_CON = EXT_INT41_CON &(~(0xf << 4))|(0x2 << 4); //外部中断触发方式 EXT_INT41_MASK = EXT_INT41_MASK & (~(0x1 << 1)); //使能中断 ICDDCR = 1; //使能分配器 ICDISER.ICDISER1 = ICDISER.ICDISER1 | (0x1 << 25); //使能相应中断到分配器 ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xff << 8))|(0x1 << 8); //选择CPU接口 CPU0.ICCPMR = 255; //中断屏蔽优先级 CPU0.ICCICR = 1; //使能中断到CPU return ; } 六、轮询方式除了中断方式之外我们还可以通过轮询方式读取按键的信息,原理如下:
GPX1.CON = GPX1.CON &(~(0xf<<4)) ; GPX1.PUD = GPX1.PUD & ~(0x3 << 2);
|
|
来自: 山峰云绕 > 《计算机科学(体系结构原理等)》