Chinaunix首页 | 论坛 | 博客
  • 博客访问: 83853
  • 博文数量: 113
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 10
  • 用 户 组: 普通用户
  • 注册时间: 2016-09-19 20:07
文章分类

全部博文(113)

文章存档

2016年(113)

我的朋友

分类: LINUX

2016-09-19 20:50:24

原文地址:裸奔之中断 作者:草根老师

一、中断的介绍

简单的来说,中断就是处理突发事件的一种机制。

例如:

生活角度:
你一直在工作,突然你的手机响了,此时你就去接电话了,接完电话,你又回来干活。

从程序运行的角度来说:
当我们程序正常执行时,如果有中断产生,那么程序的正常执行流程就被打断,转而执行处理中断的程序。

二、ARM的中断

说ARM中断之前,我们先来看看ARM的别的知识

A.ARM的异常

所谓异常,指的就是中止了程序正常执行的过程而不得不去完成的一些特殊工作,如芯片复位、取址失败、指令未定义,等等,具体的异常类型如下表:

 
我们通常所说的中断其实也是一种异常,这里的中断包括外部硬件产生的外部中断和由芯片内部硬件产生的内部中断。由中断产生的异常和其他异常,从处理方法的角度来看并没有任何区别,所以我们可以把这些异常统一起来研究。

B.ARM的模式

ARM一共有7种处理器模式,分别是中止模式(ABT)、中断模式(IRQ)、快速中断模式(FIQ)、管理模式(SVC)、系统模式(SYS)、未定义模式(UND)以及用户模式(USR)。其中,除用户模式和系统模式之外的5种处理器模式又称为异常模式,同时,把除用户模式之外的其他6种处理器模式称为特权模式。

注意:处理器之所以被设计出支持多种模式是为了能够更好地处理各种异常。

正常情况下,一个普通程序可能会运行在用户模式或系统模式下,而当中断发生时,ARM就会自动切换到中断模式去处理中断,处理完成后,又会回到用户模式或系统模式下继续之前的工作。因为每一种模式都包含有相应的私有资源,因此可以保证在处理中断时,原来的程序环境不会被新的环境破坏,从而保证了系统的正常运行。

每一种模式下的可以使用的寄存器资源:

 
从上面可以知道,R0-R12,R15,CPSR(FIQ模式除外)这些寄存器都是公用的即只有一个,而R13,R14,SPSR每一种模式下都有私有的。这一点很重要,比如cpu处于SVC模式,访问的R13是自己的,也就是说此时我们对R13修改不会影响到其他模式下的R13,在比如我们修改R0,那就会有影响了。因为R0,只有一个是共享的。

这里面比较特殊是处理器处于FIQ模式,我们可以发现在这种模式下相比其他模式拥有更多的私有寄存器资源。这样设计的目的是给快速中断模式去迅速地执行中断处理程序的。

通过前面的描述我们知道,共有寄存器是在各个模式下都允许访问的,这在模式切换时会带来一些问题,比如,程序原本运行在系统模式下,寄存器R0-R12的值都保存了系统模式下的数据,此时突然发生了中断,程序跳转到中断模式下工作,能够使用的私有寄存器只有R13和R14以及SPSR_irq。所以在中断模式下,如果需要使用某些共有寄存器,那就必须先将这些寄存器的值保存到中断模式的堆栈中。要知道把寄存器压入堆栈是需要花时间的。如果运行在中断模式下的程序需要使用从R0~R12的所寄存器,就需要将这些寄存器全部都压入堆栈。而如果是在快速中断模式下处理中断,又会怎样呢?如果是在快速中断模式下,最多只需要将寄存器R0~R7的内容压入堆栈,从而提高了中断处理的速度。

C.异常发生时的处理器动作
 
 
当一种异常发生时,硬件就会自动执行如下动作:
(1) 将CPSR保存到相应异常模式下的SPSR中
(2)把PC寄存器保存到相应异常模式下的LR中
(3)将CPSR设置成相应的异常模式
(4)设置PC寄存器的值为相应异常向量表的入口地址

注意一点哦,从上面图可以知道pc指向"正在提取的指令",呵呵,流水线,ARM920T的核支持三级流水线,关于流水线的知识这里就不介绍,读者可以查阅相关资料了解。一点要知道的是当异常产生的时候,正在执行的指令时不能被打断的,必须等执行完再去响应异常。

提一个小问题,当异常处理完之后,是不是要回来呀,应该回到那里呢?从上面的介绍我们可以知道,异常发生时,硬件会自动把PC值保存到相应异常模式下的LR中。从这里我们可以肯定,从异常返回的时候,我们需要借助异常模式下的LR返回。

这样返回对吗?

此处省略很多...

MOV  PC,LR

???不对,应该是:

SUB LR,LR,#4
MOV PC,LR

呵呵,返回的应该是上次执行完的下一条指令.
这个就到这里吧,不明白的看完流水线我想就明白了吧,


三、异常向量表

ARM一种有7种异常,但是只有5种异常模式,按道理,每一种异常模式都应该有一个唯一的入口地址。这些入口地址彼此相邻,我们一般称之为异常向量表。

通常情况下,异常向量表示从物理地址0x0处开始的。但这并不是绝对的,我们也可以通过配置CP15协处理,将异常向量表映射到地址的最末尾,即0xFFFFF000处。

 
通常我们写成这样
 
 
四、中断处理的过程
 
 
关于图上面的很多寄存器我就不详细介绍了,数据手册上有详细说明。

这里需要知道的是MASK,它的意思是屏蔽中断,就是说如果我们不想产生某种中断时,把这个寄存器的相应位设置一下就可以了。


五、实验核心代码


IRQ中断处理部分
 
 
INTERRUPT.C

  1. #include "s3c2410.h"
  2. #include "irq.h"


  3. /*如果内联汇编语句的“更改资源列表部分”存在
  4.  * “memory”,那么GCC会保证在此内联汇编之前,如果某个内存的内容
  5.  * 被装入了寄存器,那么在这个内联汇编之后,如果需要使用这个内
  6.  *存处的内容,就会直接到这个内存处重新读取,而不是使用被存放
  7.  *在寄存器中的拷贝。因为这个时候寄存器中的拷贝已经很可能和内存
  8.  *处的内容不一致了.
  9.  */
  10. void enable_interrupts(void)
  11. {
  12.     unsigned long temp;

  13.     __asm__ __volatile__(
  14.             "mrs %0,cpsr\n"
  15.             "bic %0,%0,#0x80\n"
  16.             "msr cpsr_c,%0"
  17.             : "=r"(temp) //输出资源列表
  18.             :             //输入资源列表
  19.             : "memory" //更改资源列表
  20.             );

  21.     return;
  22. }

  23. /*
  24.  *disable IRQ/FIQ interrupts
  25.  *returns true if interrupts had been enabled before we disabled them
  26.  */
  27. int disable_interrupts(void)
  28. {
  29.     unsigned long old,temp;

  30.     __asm__ __volatile__(
  31.             "mrs %0,cpsr\n"
  32.             "orr %1,%0,#0xc0\n"
  33.             :"=r"(old),"=r"(temp)
  34.             :
  35.             :"memory"
  36.             );

  37.     return (old & 0x80) == 0;
  38. }

  39. /*初始化中断*/
  40. int init_interrupt()
  41. {    
  42.     //使能irq中断
  43.     enable_interrupts();    
  44.     //ALL=IRQ MODE
  45.     INTMOD = 0x0;
  46.     //ALL Interrupted is masked
  47.     INTMSK = 0xFFFFFFFF;
  48.     //ALL sub-Interrupt is masked
  49.     INTSUBMSK = 0xFFFFFFFF;

  50.     return 0;
  51. }

  52. /*屏蔽中断*/
  53. void irq_unmask(int irq_no)
  54. {
  55.     if(irq_no >= IRQ_MIN && irq_no <= IRQ_MAX)
  56.         INTMSK &= ~(1 << (irq_no - 1));

  57.     return;
  58. }

  59. /*不屏蔽中断*/
  60. void irq_mask(int irq_no)
  61. {
  62.     if(irq_no >= IRQ_MIN && irq_no <= IRQ_MAX)
  63.         INTMSK |= (1 << (irq_no - 1));

  64.     return;
  65. }

  66. /*清除中断,写1清0*/
  67. void irq_clear(int irq_no)
  68. {
  69.     if(irq_no >= IRQ_MIN && irq_no <= IRQ_MAX)
  70.     {
  71.         SRCPND = (1 << (irq_no - 1));
  72.         INTPND = (1 << (irq_no - 1));
  73.     }

  74.     return;
  75. }

  76. /********************************************************************
  77. // Function name    : Isr_Request
  78. // Description     : 注册中断函数
  79. // Return type        : void
  80. // Argument : int irq_no
  81. // #define IRQ_EINT0 1
  82. // #define IRQ_EINT1 2
  83. // #define IRQ_EINT2 3
  84. // #define IRQ_EINT3 4
  85. // #define IRQ_EINT4_7 5
  86. // #define IRQ_EINT8_23 6
  87. // #define IRQ_NOTUSED6 7
  88. // #define IRQ_BAT_FLT 8
  89. // #define IRQ_TICK 9
  90. // #define IRQ_WDT 10
  91. // #define IRQ_TIMER0 11
  92. // #define IRQ_TIMER1 12
  93. // #define IRQ_TIMER2 13
  94. // #define IRQ_TIMER3 14
  95. // #define IRQ_TIMER4 15
  96. // #define IRQ_UART2 16
  97. // #define IRQ_LCD 17
  98. // #define IRQ_DMA0 18
  99. // #define IRQ_DMA1 19
  100. // #define IRQ_DMA2 20
  101. // #define IRQ_DMA3 21
  102. // #define IRQ_SDI 22
  103. // #define IRQ_SPI0 23
  104. // #define IRQ_UART1 24
  105. // #define IRQ_NOTUSED24 25
  106. // #define IRQ_USBD 26
  107. // #define IRQ_USBH 27
  108. // #define IRQ_IIC 28
  109. // #define IRQ_UART0 29
  110. // #define IRQ_SPI1 30
  111. // #define IRQ_RTC 31
  112. // #define IRQ_ADC 32
  113. // Argument : void* irq_routine
  114. *********************************************************************/
  115. void irq_routine_register(int irq_no, void* irq_routine)
  116. {
  117.     if(irq_no >= IRQ_MIN && irq_no <= IRQ_MAX)
  118.     {
  119.         *((unsigned int *)((irq_no - 1) * sizeof(int) + (unsigned int)_ISR_STARTADDRESS_))\
  120.             =(unsigned int)irq_routine;
  121.     }
  122.     
  123.     return;
  124. }

最后附上整个实验的代码:
 
 
阅读(236) | 评论(0) | 转发(0) |
0

上一篇:裸奔之IIC

下一篇:裸奔之MMU

给主人留下些什么吧!~~