笔者来介绍arc的编程模型的中断流程和异常流程
1、中断介绍
主要介绍一下中断进入的流程,包括需要配置的寄存器等信息。
- 中断号:16-255,总共240个中断。
- 触发类型:脉冲或者电平触发
- 中断优先级:16个,0最大,15最小。
- 中断向量表地址:地址0x400对齐,因为中断+异常的地址总共大小占用0x400的字节
- 中断向量大小:32位
1.1 中断配置流程
- 配置全局优先级阈值,STATUS.E[3:0],
- 关闭外设中断
- 关闭全局中断,即STATU32.IE 为0,
- 选择需要配置的中断,IRQ_Select 寄存器,
- 配置中断优先级,IRQ_Priority
- 配置触发方式,IRQ_Trigger
- 清除脉冲,IRQ_PULSE_CANCEL
- 使能该中断,IRQ_ENABLE
- 开启全局中断,即STATU32.IE 为1
- 最后在开启外设中断
注意:首先肯定得开启外设使用,包括总线时钟使能等。
2、异常介绍
主要介绍一些arc-v2版本的异常向量以及异常的处理流程。也分为同步异常和异步的异常:
- 同步异常:准确的异常,知道准确出错误的数据地址,指令地址等,cpu在发生异常时,立即进入异常处理函数,
- 异步异常:不准确的异常,在出错之后才发生的异常,发生异常时,不是第一现场
接下里介绍一下arc-v2的一些异常向量,以及处理异常的一些流程。
2.1 异常向量
异常向量共有16个,向量号0x-0x15,占用64字节,每个异常向量就是一个异常处理函数地址,异常发生后,会从该处取地址,然后跳到对应的处理函数。
每个异常可能都有多种原因,为了区分多种异常原因,arc这边提供了辅助寄存器来指示异常的原因,有点类似于ARM架构下面的fsr ,详情看ARM学习(3) 异常模式学习(CortexR5),寄存器为ESR,上文中已经介绍过,ARC学习(2)基本编程模型认识(二)。
- Vector Number,向量号(16-23Bit):指明哪个异常发生
- Cause 异常原因(8-15Bit):该异常时有什么原因触发,
- Parameter 参数(0-7Bit):原因的参数是什么,也类似于一种原因类型,进一步细分
- P:指示异常发生在中断里面
异常的地址由异常地址寄存器指明:EFA,32位,可能是pc地址,可能是数据地址,或者是cache 地址等等。
再来看一下异常向量具体会有哪些原因触发:
- 复位:Reset,外部信号造成的硬件复位,core寄存器不会被初始化,需要手动初始化。
- 内存错误:
- 包括总线指令内存错误、
- 总线数据内存错误,
- 不存在的memory获取指令
- 跨多个指令memory类型的指令获取,
- 跨多个数据memory类型的数据获取,
- 跨多个mpu region的指令获取,
内存类型有如下几种:
- 指令错误,无效的指令或者指令序列
- 机制检查异常
- 异常里面发生异常,两次异常
- 指令地址翻译错误,或者一个地址的多次翻译匹配
- 致命的cache错误
- 内部memory的指令获取异常
- 内部memory的数据获取异常
- 无效的MPU区域重叠
- 不可屏蔽的异步异常发生或者两次发生
- 获取向量时,无法纠正的ECC 或者奇偶校验发生,
- 指令页表缓存未命中(ITLB,Instruction Translation Lookaside Buffer)
- 数据页表缓存未命中(DTLB,Data Translation Lookaside Buffer),包括读写没有命中
- 保护机制违反异常
- 代码保护机制,栈保护机制,MPU和MMU保护下的内存读 违反机制
- 代码保护机制,栈保护机制,MPU、MMU和NVM保护下的内存写 违反机制
- 代码保护机制,栈保护机制,MPU、MMU和NVM保护下的读修改写的违反机制
- 读主要有:LD,POP,中断离开等
- 写主要有:ST,PUSH,中断进入等
- 特权违反异常机制
- 特权违反,比如只允许特权模式操作的指令,比如BRK、SWI和FLAG等
- 禁止拓展的寄存器访问,比如32以外的寄存器
10. 软件中断:SWI 或者SWI_S指令会触发该向量处理函数,
- 陷阱异常:TRAP_S指令触发该向量异常处理函数
- 拓展指令异常:例如浮点等指令
- 除0异常:除数为0的异常,STATUS32 的DZ=1,
- 数据cahce 一致异常,不是cache 内存的操作指令 执行了一个data cache的地址。
- 数据未对齐访问异常
- status32 的AD位置0,则运行对齐检查,未对齐的数据访问,进入该异常,比如32位的数据,处于奇地址。
- EFA会抓到访问的异常地址。
- 未对齐的向量内存访问异常
- 保留
异常的优先级和处理顺序:处理器每次都只能处理一个异常,所以当多个异常发生时,要排队处理,涉及到优先处理的异常。
注意:异常的优先级不按异常向量来决定,而是根据异常的原因来进行排序。
2.2 异常流程
异常进入:exception entry
- 所有的异常指令被丢弃
- ERET 寄存器被赋值PC地址,PC地址为那错误指令的地址,如果是TRAP异常,ERET寄存器保存的是下一条指令。
- ESTATUS被赋予STATUS32寄存器的值
- ERBTA被赋予异常返回目标分支地址,即BTA的寄存器值
- ECR记录了异常向量,异常原因和参数等数据
- EFA记录异常的地址,比如当内存访问异常,则EFA记录触发异常的数据地址,如果是指令触发异常,则记录PC的地址,
- STATUS32的状态位开始修改,CPU转为内核模式,STATUS32的U bit 置0,中断被禁止,STATUS32的IE bit 置1,处于异常模式,STATUS32的AE bit 置1,STATUS32的EI bit 置0,禁止栈溢出检查,STATUS32的SC bit 置0,禁止除0异常,STATUS32的DZ bit 置0,0赋值循环禁止,STATUS32的L bit 置1,禁止延迟分支阻塞,STATUS32的DE bit 置0,
- PC 开始从异常向量表取异常向量,开始跳转到异常处理函数执行。
- 异常处理函数需要保存所有用到的寄存器,并且退出时恢复所有的寄存器,
流程图绘制:
异常退出:exception exit:
主要是影响:ERET、ESTATUS和ERBTA,恢复到原来的状态,PC,STATUS32和BTA,以及恢复寄存器等等,相对比较简单。
3、实际例子说明
3.1 数据总线异常
下面例子代码会造成异常,0x3FFFFFFF是memory总线不存在的例子,
ls_u32_t *ptr=0x3FFFFFFF;
printf("0x%x 0x%x\r\n", ptr, *ptr);
实际异常后,打印出如下寄存器信息:
[18999]reg0=0x0001 [18999]reg1=0x0003 [18999]reg2=0x1fd30 [18999]reg3=0x30000000 [18999]reg4=0x0000 [18999]reg5=0x3fffffff [18999]reg6=0x0000 [18999]reg7=0x0000 [18999]reg8=0x10004f68 [18999]reg9=0x0001 [18999]reg10=0x1a17a [18999]reg11=0x0000 [18999]reg12=0x10004ef7 [18999]reg13=0x0000 [18999]reg14=0x0000 [18999]reg15=0x100006a0 [18999]
[18999]reg16=0x103f4 [18999]reg17=0x0000 [18999]reg18=0x0000 [18999]reg19=0x0020 [18999]reg20=0x0001 [18999]reg21=0x0080 [18999]reg22=0x0000 [18999]reg23=0x0000 [18999]reg24=0x0000 [18999]reg25=0x0000 [18999]reg26=0x0000 [18999]reg27=0x10005000 [18999]reg28=0x10004f8c [18999]reg29=0x172ee [18999]reg30=0x10004f68 [18999]pc=0x4000959c lr=0x1fd2c sp=0x10004f8c status32=0x80081624
[18999]ECR=0x11000 ERET=0x4000ca54 EFA=0x3fffffff ERSTATUS=0x84004
提取出如下异常寄存器如下:
ECR=0x11000
ERET=0x4000ca54
EFA=0x3fffffff
ERSTATUS=0x84004
如ECR寄存器的值,我们可以找到如下说明,看到是访问data memory异常,符合预期异常。
然后EFA=0x3FFFFFFF,就是异常访问的地址信息,确实是异常的地址,
最后ERET,就是异常的返回地址,可以通过addr2line 然后定位源代码位置。
3.2 地址总线异常
再看一一个异常例子。
ECR=0x1000
ERET=0x3f8eec48
EFA=0x3f8eec48
ERSTATUS=0x80084804
如ECR寄存器的值,我们可以找到如下说明,看到是访问指令 memory异常
看到ERET和EFA,均是0x3f8eec48,怀疑是CPU跑飞到一块异常地址,该地址的值均是0x3f8eec48,所以ERET和EFA相等。