ARM学习(3) 异常模式学习(CortexR5)

笔者简单介绍一下ARM CortexR5异常模式 学习

1、由来

笔者工作中用到了SSD的主控芯片是基于CortexR5系列的,所以研究一下CortexR5系列的一些异常模型。

什么时候会用到这些异常模型呢?比如发生异常程序进入abort 等,需要分析原因,这时候需要异常模型下的一些数据来反映现场的一些信息,比如PC、CPSR以及FAR等。

最典型的应用就是发生异常的时候触发CoreDump,保存现场数据。

2、异常模式

  • 异常会打断正常程序流的顺序执行,同时为了返回正常的程序流处继续执行,在进入异常服务函数之前需要保存返回地址等信息。
  • 异常处理主要有:复位中断AbortSVCundefined断点指令等。

    2.1 异常模式的进入与退出


    初看这张表时,可能不太清楚这张表表达的意思是什么?

  • 第一列代表的是进入哪种异常
  • 第二列返回异常的指令
  • 第三列进入异常时LR与PC的关系,IA笔者理解的是instruction address,就是指令地址
    那么LR是返回的地址,肯定比PC值大,所以根据当时异常LR的地址,可以推断出PC在什么地方发生异常,且LR的值在发生异常时,自动被赋值。

例如触发Data Abort,那么PC = LR -8 可以知道具体位置,从而恢复当时的现场,查出具体原因。

进入异常时,主要有以下过程:

  1. 保存下一条指令到LR,此时指令地址的保存依赖于异常类型以及执行状态(ARM或者Thumb),可能+2、+4或者+8
  2. 复制CPSR到SPSR,依赖于异常类型,可能会修改CPSR中的IT 执行状态,以便于促进程序返回
  3. 依赖于异常类型,修改CPSR的模式位M[4:0],清除IT 指令状态位
  4. 设置系统寄存器的中EE bit为1(进入异常时 该标志位为1
  5. 设置系统寄存器中TE bit 为1 (thumb下使能异常产生)
  6. 强制PC去相应的异常向量中取下一条指令
    处理器也会禁止中断标志位,阻止一些其他不受管理的中断嵌套。
    这里我理解的都是在异常模式下的寄存器进行操作,比如LR,也是异常模型下的LR。

2.2 复位

nRESETm 信号被驱动为低电平时,触发复位,处理器就放弃指令执行。

当nRESETm and nCPUHALTm 再次被驱动为高电平时,处理器有如下行为:

  1. 强制CPSR M[4:0] 进入 SVC模式。CPSR中的A、I、F bit置1,其他位 未知,基于CFGEE 引脚的状态置位。
  2. 强制PC从复位向量地址拿下一条指令执行
  3. 依据TEINIT 引脚的状态,转换成相应的ARM或者Thumb状态,并且重新开始执行。
    复位之后,除PC以及CPSR外,其他寄存器都是未知的

2.3 中断

处理器有两个中断输入,正常中断(nIRQm)和快速中断(nFIQm),每个中断引脚,都会引起处理器采取合适类型的中断异常,CPSR 中的 I bit与 F bit 会屏蔽中断。

有很多措施来提升中断延迟,换句话说 就是减少中断引脚输入到中断服务函数的执行的时间。自ARMv6以来,处理器就引入了低中断延迟的措施。

处理器有端口来链接向量中断控制器,并且支持不可屏蔽中断。

2.3.1 普通中断

  • IRQ异常是一个通用型的中断,由nIRQm输入引脚造成的异常,比FIQ 优先级更低,在进入FIQ序列时,IRQ被屏蔽。
  • nIRQm引脚的输入要一直触发处理器认为这是一个中断请求或者 其来自于VIC接口 或者是软件中断才行。
  • IRQ的返回,SUBS PC ,R14~irq~,#0x4
  • 进入IRQ 异常,CPSR的I bit 位置1 意味着普通中断无法打断普通中断,只能由FIQ打断,
  • 当然你可以清除 I bit,保存相关的寄存器数据,来实现嵌套。

2.3.2 快速中断

  • 快速中断有自己的私有的8个寄存器,减少了寄存器保存的时间,所以响应速度更快。
  • nFIQm 引脚输入造成了快速中断异常
  • FIQ的返回,SUBS PC ,R14~fiq~ ,#0x4
  • 进入FIQ 异常,CPSR的I bit F bit 位置1 意味着相关中断都无法打断
  • 当然你可以清除F I bit,保存相关的寄存器数据,来实现嵌套。
  • NMFI bit 可以阻止屏蔽FIQ中断
  • 这里笔者觉得编译器做的不好的地方是,FIQ没有发挥出其优势,其有独特的R8-R14寄存器的优势,但是就当没有,和IRQ一样了,同样保存了r11 以及 r12这种。
  • 这里的压栈,是使用异常模式下的栈,而不是正常程序运行的栈。

2.3.3 低中断延迟

  • 一些内存访问指令,在中断来临前后,可能会被访问两次,所以,PC会回到LR -4的位置,这是为了加快中断响应,降低延迟,中断来临,来放弃一部分内存访问指令的访问,等待中断执行完成之后再继续访问
  • 一些对内存访问敏感,比如设备内存或者强排序的内存,应该避免使用多自己写入加载的指令,这种访问一定会完成或者不完成,

2.3.4 中断控制器

  • 多个中断请求输入,每个中断源一个,以及一个或者多个合并到处理器的中断请求输出
  • 可以屏蔽中断请求
  • 支持中断源的优先级嵌套

即使带了中断控制器,软件仍然需要:

  • 决定哪个中断源是中断请求服务
  • 决定中断源的服务程序在什么地方被加载
  • 屏蔽和清除中断源,以便于允许其他中断被采取。
  • 与中断控制器握手,获取IRQ中断处理器地址。

2.3.5

  • VE 为IRQ 控制器的中断地址选择,是VIC提供还是默认的中断向量表0x18
  • nFIQ 或者nIRQ 代表 触发源电平 ,这个条件判断有点意思,多种情况覆盖,F 禁止,nIRQ没有电平触发,I 禁止,nFIQ没有电平触发,或者 F与 I 都禁止,或者nFIQ与nIRQ 都没有信号 直接返回,其他情况下,可以继续进行。
  • FIQ 如果在没有禁止的时候,有电平触发,则进入FIQ的流程,(做完一列列操作后(见上文异常模式的进入与退出),执行0x1C)
  • IRQ 如果在没有禁止的时候,有电平触发,则进入IRQ的流程,
  • IRQ 比较特殊的是,VIC可以提供中断地址,由VE bit指示,否则的话是 执行0x18地址
  • V bit 可以指示 中断的地址 是 0x18/0x1C 还是 0xFFFF0018/0xFFFF001C

    由上图可以看出,IRQ FIQ中断向量的地址 是 0x18 0x1C,不过其都没有顺序执行,而是跳到另外的地址执行,(有一种情况是FIQ handler 或者 IRQ Handler 被链接到固定的段上,所以需要跳转过去执行)

2.4 Aborts

当处理器内存系统不能正常的获取内存数据,会产生Abort,Abort发生的时候是有多种原因的:

  • MPU保护的地址会触发
  • 内存总线传输数据的错误响应
  • ECC检测到的数据错误

作用:了解Abort之后,在发生ABort之后,在Abort处理函数里面做相应的处理,记录现场数据,就可以推测出问题的地址,进行排查问题。

指令预取的错误会触发prefetch abort,数据获取错误会触发 data abort,同步abort 是准确的,异步abort 是不准确的。

当abort发生时,处理器会记录abort的类型

Abort的处理函数,会在中断向量表里面指定地址。

start_vector.s 

Import Reset_Handler
Import Abort_Handler 
Import Prefetch_Handler 

Vectors
    LDR PC,Reset_Addr       ;0x0
    LDR PC,Undefined_Addr   ;0x4
    LDR PC,SVC_Addr         ;0x8
    LDR PC,Prefetch_Addr    ;0xC
    LDR PC, Abort_Addr      ;0x10
    LDR PC,IRQ_Addr         ;0x18
    LDR PC,FIQ_Addr         ;0x1C

Reset_Addr DCD Reset_Handler
Abort_Addr DCD Abort_Handler 

Abort_Handler FUNCTION
    push .....

    pop .....
    ENDFUNC

Prefetch_Handler FUNCTION
    push .....

    pop .....
    ENDFUNC

2.4.1 Data Abort

获取内存数据发生错误会造成Data Abort,如果获取内存指令并没有执行或者被打断了,那么不会造成Data Abort。

  • Data abort可能是同步或者异步的,根据造成的错误类型来定
  • 异步Abort 可以被屏蔽,在CPSR中A bit可以控制。
  • DFAR 以及DFSR 记录出错的地址与类型

DFSR 寄存器保存这出错的类型,有同步异常和异步异常

DFAR 记录着出错的地址

 MRC p15,0,r0,c5,c0,0   ;dfsr  读取
 MRC p15,0,r0,c6,c0,0   ;dfar   读取

2.4.2 Prefetch Abort

其abort 必须是同步的,当其发生时,处理器会将预取的指令标记为无效的,但是直到执行到这个指令时,才会触发Prefetch Abort,如果指令没有执行,则异常不会被触发。

  • IFSR 记录着指令异常的类型,可以看到其下面没有异步的异常,而DATA Abort则有

 MRC p15,0,r0,c5,c0,1   ;ifsr   读取
 MRC p15,0,r0,c6,c0,2   ;ifar   读取

2.5 SVC指令

SVC 通常也称为软中断,通过指令可以进入SVC异常,在异常里面可以进行做事。

最常见的就是OS的调度处理,比如等信号量时 直接触发SVC指令,进入异常,然后在异常中进行栈切换,然后进入其他task去执行,如果等到信号量,则 直接触发SVC异常,再进行栈切换,进入到等信号量的task执行。

两级task ,task1 优先级1(高) ,task2 优先级2

抢占式调度(高优先级的任务 如果不释放,会一直执行)

  • task1 任务执行
  • task1 等待信号量,然后在等待信号的函数里面触发SVC异常,保存现场,切换到task2执行
  • task2 任务执行
  • task2 释放信号量,在释放信号量的函数里面触发SVC异常,保存现场,切换到task1执行
  • task1 等待到信号量继续执行,

协程调度(靠主动释放 (yield)去进行栈切换)

  • task1任务执行
  • task1 等待信号量,然后在等待信号的函数里面触发SVC异常,保存现场,切换到task2执行
  • task2 任务执行
  • task2 释放信号量,在释放信号量的函数将task1加入就绪队列,任务继续执行
  • 直到执行到yeiled 或者sleep 等等,然后触发SVC异常,保存现场,切换到task1执行。

SVC指令如下,在thumb 和 arm模式下有区别。

SVC 有相应的opcode,即imm8 可以区分在required sevvice是什么服务函数。

在SVC handler 里面读取请求服务opcode,来决定是什么请求service 来做什么样的处理。

2.6 undefined instruction

2.7 Breakpoint instruction

2.8 Exception vections

版权声明:
作者:ZhangYixi
链接:http://zyixi.xyz/571-2/
来源:一西站点
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
打赏
< <上一篇
下一篇>>
文章目录
关闭
目 录