中断和异常 指在当前执行程序的某处出现一个事件,该事件需要处理器进行处理。通常,这种事情会导致从当前运行程序转移到被称为中断处理或异常处理的程序中。
一.异常向量表
系统入口 ARM(AArch32)架构中断向量表
1.Reset : 处理器在工作时, 突然按下重启键, 就会触发该异常; 2.Undefined instructions : 处理器执行的指令是有规范的, 如果尝试执行不符合要求的指令, 就会进入到该异常; 3.Software interrupt : 软中断, 软件中需要去打断处理器工作, 可以使用软中断来执行; 4.Prefetch Abort : ARM 在执行指令的过程中, 要先去预取指令准备执行, 如果预取指令失败, 就会产生该异常; 5.Data : 读取数据失败; 6.IRQ: 普通中断; 7.FIQ : 快速中断, 快速中断要比普通中断响应速度要快一些;
(AArch32)异常向量表通常在0x00000000,也可以在0xffff0000 在哪个地址有cp15控制
ARM64(AArch64)架构异常向量表
异常向量表结构: 实际上有四张表,每张表有四个异常入口,分别对应同步异常,IRQ,FIQ 和 出错异常。 每一个异常入口不再仅仅占用 4bytes 的空间,而是占用 0x80 bytes空间,也就是说,每一个异常入口可以放置多条指令,而不仅仅是一条跳转指令
在ARM32中,每个向量表对应四个字节,每四个字节对应一条指令。ARM32入口对应0,每个指令对应四个字节,可计算偏移量。 在ARM64中,向量表中所有指令都设置好了地址。
MIPS32架构异常向量表 异常向量表设置到了入口地址
向量表结构,进入分支,加调试信息,知道向量表入口。
二.中断处理流程
串口中断,定时器中断都是IRQ中断
产生IRQ中断后,pc指针从上面的汇编跳到这个函数archIntEntry
这个函数就是中断入口archIntEntry(在base中) 不同模式对应不同的栈位置 第一步把cpu相关寄存器存入栈里,第二部调用系统定义好的中断进入函数。(API_InterEnter)
中断入口函数API_InterEnter中先中断嵌套值CPU_ulInterNesting++,然后调用archIntCtxSaveReg
archIntCtxSaveReg里面做一次中断保存寄存器 这个函数里面做了一次拷贝(archTaskCtCopy函数),前面中断入口archIntEntry里也做了拷贝 这里的拷贝再把中断存储到任务控制块TCB中
当异常进入(上面汇编的中断入口),把任务先存入IRQ对应的栈位置(临时保存)。 第二次保存时,存储到对应上下文的任务控制块TCB中(archTaskCtCopy函数)。 这两次保存才是完整的任务保存流程。
判断是不是第一次进入中断
第一次进入中断,获取CPU中断堆栈(不是第一次产生中断,则产生中断嵌套)
总结:1.保存到临时的IRQ对应的栈位置。2.上下文保存到TCB中。3.保存完后重新设置中断处理中栈处理的位置。4.后面是真正处理中断流程。
bspIntHandle处理中断的函数(bspLib.c中的bspIntHandle函数)
这个函数中获取中断号并调用archIntHandle
archIntHandle函数又回到BASE中的处理 先判断中断号是否合法;然后判断中断号是否可抢占;如果可抢占会发生嵌套,不可抢占进入API_InterVectorIsr中断总服务函数。
进入API_InterVectorIsr中断总服务函数 这个函数中会根据中断号调用驱动中定义的中断服务程序,获得中断返回值。
最后执行中断退出API_InterExit函数
中断嵌套值CPU_ulInterNesting–(可根据这个值来判断有些函数是否在中断中调用)
执行中断调度,中断退出之前,在调度表找出最应该调度的任务。 执行中断返回,调度那个最应该调度的任务,返回上下文。
进入中断前要保存上下文,中断返回后恢复上下文(恢复的上下文不一定和之前保存的上下文一样,因为中断退出函数中会执行中断调度,找出最应该执行的任务然后返回)。
中断处理流程总结: 1.archIntEntry 向量表入口跳转的位置。将正文保存到 IRQ 模式栈中。 2.API_InterEnter 将中断嵌套计数增加。将 IRQ 模式栈中的寄存器信息保存到 TCB 上下文中。 3.bspIntHandle 从中断控制器中获取中断号。调用注册好的中断服务程序 4.API_InterExit 将中断嵌套计数减少。调度。恢复上下文
API_InterStackBaseGet 中断的处理流程在指定的栈区域; 可统计中断堆栈的使用量(flag)
bspIntHandle(需要实现的函数,不同中断处理器流程不同) 不同中断控制器处理流程不同 调用 archIntHandle,是否开嵌套
API_InterExit 中断中的调度接口 中断上下文的恢复
中断嵌套流程: 先禁用当前中断的中断号(避免当前中断号再产生中断),然后接收其它中断号。其他中断进入时,会判断是不是第一次中断(根据中断嵌套值),然后处理中断流程与上面类似。当中断退出时流程与正常中断不同,嵌套中断不会回到调度的任务而是回到上一个中断。 中断嵌套(优先级高的中断会抢占优先级低的中断) 1.中断嵌套开启的时机 archIntHandle ——> API_InterVectorIsr 2.中断嵌套栈使用的变化 继续使用当前IRQ模式栈 3.中断嵌套的返回 不会调度,不会从异常加载上下文,而是回到上一次异常位置
核间中断:有自己的处理分支_SmpProclpi(多核) 普通中断:根据中断号,找到对应中断服务程序 链式中断:根据中断号,遍历中断服务程序,判断返回值 设置链式中断的方法: API_InterVectorSetFlag(LW_IRQ_4, LW_IRQ_FLAG_QUEUE);
三.中断控制器的实现
bspIntInit 中断控制器的初始化 bspIntVectorEnable/bspIntVectorDisable 中断使能/中断禁能(对应MASK寄存器) bspIntVectorSetPriority/bspIntVectorGetPriority 中断优先级设置/中断优先级获取 bspIntVectorSetTarget/bspIntVectorGetTarget 中断绑核设置/中断绑核获取 bspMpInt 核间中断触发
S3C2440的中断控制器 单核,结构简单 ARM GIC V1,V2 Distributor+CPU Interface ARM GIC V3,V4
关中断 KN_INT_DISABLE/KN_INT_ENABLE
Disable IRQ Interrupts 多核处理器,每个核都有自己的I位 关中断只是关闭本核的中断
2440中断接口实现
VOID bspIntInit (VOID)
{
API_InterVectorSetFlag(LW_IRQ_4, LW_IRQ_FLAG_QUEUE);
API_InterVectorSetFlag(LW_IRQ_5, LW_IRQ_FLAG_QUEUE);
API_InterVectorSetFlag(LW_IRQ_0, LW_IRQ_FLAG_SAMPLE_RAND);
}
VOID bspIntHandle (VOID)
{
REGISTER UINT32 uiIrqVic = rINTOFFSET;
archIntHandle((ULONG)uiIrqVic, LW_FALSE);
}
VOID bspIntVectorEnable (ULONG ulVector)
{
INTER_CLR_MSK((1u << ulVector));
}
VOID bspIntVectorDisable (ULONG ulVector)
{
INTER_SET_MSK((1u << ulVector));
}
BOOL bspIntVectorIsEnable (ULONG ulVector)
{
return (INTER_GET_MSK((1u << ulVector)) ? LW_FALSE : LW_TRUE);
}
#if LW_CFG_INTER_PRIO > 0
ULONG bspIntVectorSetPriority (ULONG ulVector, UINT uiPrio)
{
return (ERROR_NONE);
}
ULONG bspIntVectorGetPriority (ULONG ulVector, UINT *puiPrio)
{
*puiPrio = 0;
return (ERROR_NONE);
}
#endif
#if LW_CFG_INTER_TARGET > 0
ULONG bspIntVectorSetTarget (ULONG ulVector, size_t stSize, const PLW_CLASS_CPUSET pcpuset)
{
return (ERROR_NONE);
}
ULONG bspIntVectorGetTarget (ULONG ulVector, size_t stSize, PLW_CLASS_CPUSET pcpuset)
{
LW_CPU_ZERO(pcpuset);
LW_CPU_SET(0, pcpuset);
return (ERROR_NONE);
}
#endif
四.其他异常的处理流程
1.进入异常时的上下文保存。可返回类型的异常的返回 2.未定义异常与FPU的关系 3.异常产生时的信息分析
未定义异常与FPU
|