当前位置:   article > 正文

ARM-Cortex A9异常处理流程_cortex-a9 cpsr

cortex-a9 cpsr

第一部分

一. 异常的概念

处理器在正常执行程序的过程中可能会遇到一些不正常的事件发生

    这时处理器就要将当前的程序暂停下来转而去处理这个异常的事件

    异常事件处理完成之后再返回到被异常打断的点继续执行程序

二. 异常处理机制

异常处理机制

    不同的处理器对异常的处理的流程大体相似,但是不同的处理器在具体实现的机制上有所不同;比如处理器遇到哪些事件认为是异常事件遇到异常事件之后处理器有哪些动作、处理器如何跳转到异常处理程序如何处理异常、处理完异常之后又如何返回到被打断的程序继续执行等我们将这些细节的实现称为处理器的异常处理机制

三. ARM异常源

概念

    导致异常产生的事件称为异常源

 ARM异常源

    FIQ                                快速中断请求引脚有效                

    IRQ                                外部中断请求引脚有效

    Reset                        复位电平有效

    Software Interrupt        执行swi指令

    Data Abort                        数据终止

    Prefetch Abort                指令预取终止

    Undefined Instruction        遇到不能处理的指令

四. ARM异常模式

异常模式

    在ARM的基本工作模式中有5个属于异常模式,即ARM遇到异常后会切

换成对应的异常模式

五. ARM异常响应

ARM产生异常后的动作(自动完成)

    1.拷贝CPSR中的内容到对应异常模式下的SPSR_<mode>

    2.修改CPSR的值

2.1.修改中断禁止位禁止相应的中断

2.2.修改模式位进入相应的异常模式     产生异常

            2.3.修改状态位进入ARM状态

    3.保存返回地址到对应异常模式下的LR_<mode>

    4.设置PC为相应的异常向量(异常向量表对应的地址)

六. 异常向量表

异常向量表

    > 异常向量表的本质是内存中的一段代码

    > 表中为每个异常源分配了四个字节的存储空间

    > 遇到异常后处理器自动将PC修改为对应的地址

    > 因为异常向量表空间有限一般我们不会再这里

      写异常处理程序,而是在对应的位置写一条跳

      转指令使其跳转到指定的异常处理程序的入口

    注:ARM的异常向量表的基地址默认在0x00地址

        但可以通过配置协处理器来修改其地址

七. 异常返回

 ARM异常返回的动作(自己编写)

    1.将SPSR_<mode>的值复制给CPSR

      使处理器恢复之前的状态

    2.将LR_<mode>的值复制给PC

      使程序跳转回被打断的地址继续执行

八. IRQ异常举例

注:整个过程CPSR保存的永远是当前程序运行状态 , SPSR只是异常时对原来的CPSR进行备份

CPSR:当前程序状态寄存器



第二部分:中断代码执行流程分析

一. 启动代码的分析

1. 实验思路

2. 程序源码

2. 1启动文件
  1. .text
  2. .global _start
  3. _start:
  4. /*
  5. * Vector table 异常向量表
  6. */
  7. b reset
  8. b .
  9. b .
  10. b .
  11. b .
  12. b .
  13. /*
  14. * 从异常向量表再跳转到IRQ的异常处理程序
  15. */
  16. b irq_handler
  17. b .
  18. reset:
  19. /*
  20. * Set vector address in CP15 VBAR register
  21. */
  22. ldr r0, =_start
  23. mcr p15, 0, r0, c12, c0, 0 @Set VBAR
  24. /*
  25. * Set the cpu to SVC32 mode, Disable FIQ/IRQ
  26. */
  27. mrs r0, cpsr
  28. bic r0, r0, #0x1f
  29. orr r0, r0, #0xd3
  30. msr cpsr ,r0
  31. /*
  32. * Defines access permissions for each coprocessor
  33. */
  34. mov r0, #0xfffffff
  35. mcr p15, 0, r0, c1, c0, 2
  36. /*
  37. * Invalidate L1 I/D
  38. */
  39. mov r0, #0 @Set up for MCR
  40. mcr p15, 0, r0, c8, c7, 0 @Invalidate TLBs
  41. mcr p15, 0, r0, c7, c5, 0 @Invalidate icache
  42. /*
  43. * Set the FPEXC EN bit to enable the FPU
  44. */
  45. mov r3, #0x40000000
  46. fmxr FPEXC, r3
  47. /*
  48. * Disable MMU stuff and caches
  49. */
  50. mrc p15, 0, r0, c1, c0, 0
  51. bic r0, r0, #0x00002000 @Clear bits 13 (--V-)
  52. bic r0, r0, #0x00000007 @Clear bits 2:0 (-CAM)
  53. orr r0, r0, #0x00001000 @Set bit 12 (---I) Icache
  54. orr r0, r0, #0x00000002 @Set bit 1 (--A-) Align
  55. orr r0, r0, #0x00000800 @Set bit 11 (Z---) BTB
  56. mcr p15, 0, r0, c1, c0, 0
  57. /*
  58. * Initialize stacks
  59. */
  60. init_stack:
  61. /*svc mode stack*/
  62. msr cpsr, #0xd3
  63. ldr sp, _stack_svc_end
  64. /*undef mode stack*/
  65. msr cpsr, #0xdb
  66. ldr sp, _stack_und_end
  67. /*abort mode stack*/
  68. msr cpsr,#0xd7
  69. ldr sp,_stack_abt_end
  70. /*irq mode stack*/
  71. msr cpsr,#0xd2
  72. ldr sp, _stack_irq_end
  73. /*fiq mode stack*/
  74. msr cpsr,#0xd1
  75. ldr sp, _stack_fiq_end
  76. /*user mode stack, enable FIQ/IRQ*/
  77. msr cpsr,#0x10
  78. ldr sp, _stack_usr_end
  79. /*Call main*/
  80. b main
  81. /*
  82. * IRQ的异常处理程序
  83. */
  84. irq_handler:
  85. /*
  86. * 因为产生IRQ异常后ARM自动保存到LR中的返回地址是被IRQ打断的指令
  87. * 的下一条再下一条指令的地址,所以我们需要人为的去修正一下
  88. */
  89. sub lr, lr, #4
  90. /*
  91. * 因为IRQ模式下使用的R0-R12寄存器和USER模式下使用的是同一组
  92. * 所以在处理异常之前需要先将之前寄存器中的值压栈保护
  93. */
  94. stmfd sp!, {r0-r12,lr}
  95. /*
  96. * 跳转到do_irq处理异常
  97. */
  98. bl do_irq
  99. /*
  100. * 异常返回
  101. * 1.将R0-R12寄存器中的值出栈,使其恢复到被异常打断之前的值
  102. * 2.将SPSR寄存器中的值恢复到CPSR,使CPU的状态恢复到被异常打断之前
  103. * 3.将栈中保存的LR寄存器的值出栈给PC,使程序跳转回被异常打断的点继续执行
  104. */
  105. ldmfd sp!,{r0-r12,pc}^ // '^'意思是自动将SPSR中的值赋值给
  106. _stack_svc_end:
  107. .word stack_svc + 512
  108. _stack_und_end:
  109. .word stack_und + 512
  110. _stack_abt_end:
  111. .word stack_abt + 512
  112. _stack_irq_end:
  113. .word stack_irq + 512
  114. _stack_fiq_end:
  115. .word stack_fiq + 512
  116. _stack_usr_end:
  117. .word stack_usr + 512
  118. .data
  119. stack_svc:
  120. .space 512
  121. stack_und:
  122. .space 512
  123. stack_abt:
  124. .space 512
  125. stack_irq:
  126. .space 512
  127. stack_fiq:
  128. .space 512
  129. stack_usr:
  130. .space 512
 2.2用户文件
  1. #include "exynos_4412.h"
  2. unsigned int Flags = 0;
  3. void Delay(unsigned int Time)
  4. {
  5. while(Time--);
  6. }
  7. //IRQ异常处理
  8. void do_irq(void)
  9. {
  10. unsigned int IrqNum = 0;
  11. /*从中断控制器中获取当前中断的中断号*/
  12. IrqNum = CPU0.ICCIAR & 0x3FF;
  13. /*根据中断号处理不同的中断*/
  14. switch(IrqNum)
  15. {
  16. case 0:
  17. //0号中断的处理程序
  18. break;
  19. case 1:
  20. //1号中断的处理程序
  21. break;
  22. /*
  23. * ... ...
  24. */
  25. case 58: //[EINT 10]
  26. Delay(1000000);
  27. Flags++;
  28. printf("key3 pressed.\n");
  29. /*清除GPIO控制器中GPX1_2的中断挂起标志位*/
  30. EXT_INT41_PEND = (1 << 2);
  31. /*将当前中断的中断号写回到中断控制器中,以这种方式来告知中断控制器当前的中断已经处理完成,可以发送其它中断*/
  32. CPU0.ICCEOIR = CPU0.ICCEOIR & (~(0x3FF)) | (58);
  33. break;
  34. /*
  35. * ... ...
  36. */
  37. case 159:
  38. //159号中断的处理程序
  39. break;
  40. default:
  41. break;
  42. }
  43. }
  44. void LED2_ON() {
  45. GPX2.DAT = GPX2.DAT | (1 << 7);
  46. }
  47. void LED2_OFF() {
  48. GPX2.DAT = GPX2.DAT & (~(1 << 7));
  49. }
  50. int main()
  51. {
  52. /******************************/
  53. //初始化
  54. /******************************/
  55. /*外设层次 - 让外部的硬件控制器产生一个中断信号发送给中断控制器*/
  56. /*将GPX1_1设置成中断功能*/
  57. GPX1.CON = GPX1.CON | (0xF << 8);
  58. /*设置GPX1_1的中断触发方式为下降沿触发*/
  59. EXT_INT41_CON = EXT_INT41_CON & (~(0x7 << 8)) | (0x2 << 8);
  60. /*使能GPX1_2的中断功能*/
  61. EXT_INT41_MASK = EXT_INT41_MASK & (~(1 << 2));
  62. /******************************/
  63. //中断控制器层次
  64. /******************************/
  65. /*中断控制器层次 - 让中断控制器接收外设产生的中断信号并对其进行管理然后再转发给CPU处理*/
  66. /*全局使能中断控制器使其能接收外设产生的中断信号并转发到CPU接口*/
  67. ICDDCR = ICDDCR | 1;
  68. /*在中断控制器中使能57号中断,使中断控制器接收到57号中断后能将其转发到CPU接口*/
  69. ICDISER.ICDISER1 = ICDISER.ICDISER1 | (1 << 26);
  70. /*选择由CPU0来处理57号中断*/
  71. ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xFF << 16)) | (0X01 << 16);
  72. /*使能中断控制器和CPU0之间的接口,使中断控制器转发的中断信号能够到达CPU0*/
  73. CPU0.ICCICR = CPU0.ICCICR | 1;
  74. /*初始化GPX2_7为输出功能 为LED2做准备*/
  75. GPX2.CON = GPX2.CON & (~(0xF << 28)) | (0x1 << 28);
  76. while(1)
  77. {
  78. switch(Flags % 2) {
  79. case 0:
  80. LED2_ON();
  81. printf("LED2_ON\n");
  82. Delay(1000000);
  83. break;
  84. case 1:
  85. LED2_OFF();
  86. printf("LED2_OFF\n");
  87. Delay(1000000);
  88. break;
  89. }
  90. }
  91. return 0;
  92. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/正经夜光杯/article/detail/973569
推荐阅读
相关标签
  

闽ICP备14008679号