s3c2440 中断相关的知识

【详解】中断相关的知识

版本:v1.1

Crifan Li

摘要

此文详细介绍了中断,陷阱和异常之间的区别和联系。以及中断的各种分类,包括向量中断和非向量中断,内部中断和外部中断,软件中断和硬件中断可屏蔽中断和非可屏蔽中断,并且对向量中断和非向量中断进行了实例解析

[提示] 本文提供多种格式供:
在线阅读 HTML HTMLs PDF CHM TXT RTF WEBHELP
下载(7zip压缩包) HTML HTMLs PDF CHM TXT RTF WEBHELP

HTML版本的在线地址为:

http://www.crifan.com/files/doc/docbook/hardware_basic/release/html/hardware_basic.html

有任何意见,建议,提交bug等,都欢迎去讨论组发帖讨论:

http://www.crifan.com/bbs/categories/hardware_basic/

2012-08-14

修订历史
修订 1.0 2011-11-01 crl
  1. 详细解释了中断,陷阱和异常之间的区别和联系
  2. 详细解释了中断的各种分类
修订 1.1 2012-08-14 crl
  1. 通过Docbook发布

插图清单

2.1.  中断,陷阱和异常的区别

缩略词

NMI ( NMI)

Non-Maskable Interrupt

非可屏蔽中断

正文之前

1. 此文目的

解释中断的基本概念,和其与异常和陷阱之间的区别和联系。

然后再详细解释,从不同方面来看,中断所细分出来的各种分类。

2. 声明

由于笔者水平有限,文中难免有误,欢迎批评指正:admin (at) crifan.com。

版权所有,欢迎转载。

第 1 章 什么是中断

摘要

1.1. 中断出现之前

在中断出现之前,程序的执行,都是CPU一步步按照指令顺序执行下去的,中间即使有程序的跳转,但是CPU的执行的顺序,还是由程序中的代码的逻辑决定的。

首先要了解的背景知识是,CPU指令的执行速度和外设IO之间,前者很多都是纳秒(nm)级别的,后者很多都是毫秒(ms)级别的,所以时间量级上相差很大。

有了这个背景知识后,再去了解程序实际执行过程中,有时候会涉及到CPU要去和各种外设打交道。

比如,CPU发送完毕一个指令后,外设IO开始执行对应的动作,然后CPU就开始去读取对应的外设的状态,由于CPU指令执行很快,然后结果就是CPU执行了N次指令了,结果查询到的结果是,外设还没有执行完对应的动作。

最后的最后,好不容易等到外设执行外对应的动作了,然后才接着执行后面的指令。

此循环地,不断地去等待外设完成对应的动作,叫做轮询(Poll)。

可以发现,CPU在轮询过程中,就是在做无用功,而为了提高CPU的效率,不要让其浪费生命在无谓的轮询上,所以才会引出中断这个概念。

1.2. 为何要有中断

所谓的中断的方式,就是当CPU发送完指令,让外设开始去执行其对应的动作之后,不再像之前一样傻傻等待,不断地轮询,而是发送完指令后,让外设去干其该干的活,然后自己就返回了,继续执行其他的指令了,然后等过了会,外设干完活了,硬件上,会自动发送一个中断给CPU,通知其说,我活已经干完了,你可以过来处理后续该要处理的事情了,比如将对应的数据拷贝到对应的其他内容中等等之类的事情。

这套机制,就是对应的中断的概念,简要描述就是:

CPU对外设等设备做好对应的准备工作后,然后就继续执行其他CPU指令了,然后外设做完自己的事情之后,硬件上,会自动发送对应的中断给CPU的,而CPU发现有中断了,然后才会跳转到对应的中断服务程序,即常说的ISR(Interrupt Service Routine),做接下来的事情,比如将数据拷贝到对应的内容中等等。

关于中断的概念方面,此处不再赘述,因为这里有更加清晰易懂的解释:

【转】ARM9 2410移植之ARM中断原理, 中断嵌套的误区,中断号的怎么来的

第 2 章 中断和异常,陷阱的区别和联系

摘要

说到中断,那么多很多人会想到其他几个相关的词,即异常和陷阱。

这几个概念,很多人都会搞混淆,所以,在此特别地,详细地解释一下。

首先,各自名字所对应的英文单词叫法分别是,中断是interrupt,异常是exception,陷阱是trap。

对于中断,异常和陷阱这几个词来说,其实还有其他一些相关的词:故障/错误fault,终止abort。对于这些名词,其实没有一个完全统一和精确的解释。

因此也出现,不同的厂家,不同的作者,有不同的各自的解释。

不过,相对来说,这么多名词中,最常用的,还是上面这三个:中断,异常和陷阱。

此处,对于其各自的含义,也采用大家所最常用的解释。

只是,如果你在别处看到对这些名词有不同的解释,也别太惊讶就行了。

2.1. 中断Interrupt

此处的中断,一般也成为硬件中断,即外部的硬件发生了某些事件了,然后就通知到了当前CPU,CPU跳转到对应的ISR去执行了。

此处的外部的硬件中断,和当前执行的指令,是没有任何关系的。

而且往往都是外部设备,需要引起CPU的注意,比如某个按键被按下了,定时器超时了等等,才会去打断CPU,CPU才会跳转到ISR执行,然后执行完对应的ISR,再回来继续执行下一条指令。

可见,CPU此处是被动的,也不知道什么时候,属于不可预知的,就会受到外部的硬件中断,而外部的硬件中断,和当前CPU所执行的指令,没有任何关系,即属于异步关系。

2.2. 异常Exception

异常,可以称为自动产生的陷阱,不是我们所期望产生的,而是被动的强制产生的。

我们执行的指令遇到了一些异常情况,比如(除法中出现了)除零,(指令执行时候遇到了)非法指令,内存非法访问(比如向只读的内存中写入数据)等,就会触发异常,CPU发现你程序在做非法的事情了,发现你在干坏事,那么当前要出来处理这些紧急事务了,即跳转到对应的异常处理程序中去,接下来要做的事情,可以有多种做法和选择,比如尝试修正此错误,或者简单地中止程序的运行,或者是打印出相关的错误信息(比如Windows遇到非法地址访问,会跳出对应的对话框提示你,某某地址,常见的是0地址,不能访问)。

可见,异常,对于CPU来说,也是被动的,不知道何时会发生的,但是和当前指令是有关系的,即当前执行指令出现错误的时候,才会出现异常,即是同步的。

2.3. 陷阱Trap

陷阱,也常被叫做软件中断(software interrupt)。80x86系统中,有对应的INT指令,执行对应的INT指令,就会跳转到对应的函数中去执行对应的ISR了。

此处的陷阱,可以看出,是无条件执行的,而且是有意为之,是软件写的代码,故意的,让其产生对应的陷阱的。即只要执行了INT指令后,就会跳转到对应的ISR中去,而不像中断和异常那样需要满足相关的条件。

陷阱主要是用于调试的,比如用来通知调试器,某条执行被执行了,然后触发调试器做出对应的动作。

对于CPU来说,陷阱是由于执行了INT等指令而产生的,所以是(和当前指令的执行是)同步的(synchronous),对于CPU也是预知的,知道是什么时候会发生陷阱的,属于主动的,而不是被动的。

2.4. 中断和异常,陷阱的区别和联系

对于中断,异常和陷阱的各种关系,总结如下:

表 2.1. 中断,异常和陷阱的区别和联系

  对CPU来说是 和当前CPU所执行的指令的关系 CPU接下来的事情 程序员和用户的态度
中断 被动的 异步的 没关系 跳转到对应的ISR 希望有对应的中断,以使得CPU可以响应对应的中断,执行对应的ISR
异常 被动的 同步的 有关系,因为就是当前指令执行出问题,才有的异常 跳转到对应的异常处理 不希望出现异常,如果出现了,那往往是指令执行出现某些错误了
陷阱 主动的 同步的 有关系,就是执行当前软中断指令,才进入的软中断 执行对应的软中断处理函数 对于想要实现调试功能的程序员,有需要此陷阱的必要,其他人不用关心此点

下面,用图表总结了,中断,陷阱和异常的区别:

图 2.1. 中断,陷阱和异常的区别

中断,陷阱和异常的区别

[注意] 注意

无论是中断,还是异常和陷阱,对应的处理函数,一般都可以称其为中断服务程序ISR,都只是一个函数

具体函数里面要做什么事情,是由你写程序的人决定的

比如中断中处理对应的事情,异常中自己决定如何响应出现的异常,陷阱中决定做什么事情。

第 3 章 中断的分类

摘要

中断,从不同的角度来区分的话,可以有不同的分类。

下面就来从不同的方面,不同的角度,不同的侧重点,来对常见的分类,做个详细的解释。

3.1. 内部中断和外部中断

根据中断来源的方向不同,可分为内部中断和外部中断。

首先我们要搞懂,此处所说的内部中断和外部中断中的“内部”和“外部”,都是相对于CPU,或者说相对于CPU所执行的指令,来说的。

另外,广义上说,中断,就是中止,打断,即中止打断正在执行指令的CPU(而让其去执行别的ISR程序)的,都可以叫做中断。

所以,不论是狭义上的普通所说的中断,还是陷阱和异常,对于CPU执行正常的指令这个过程来说,都是会打断正常执行指令的顺序,所以都可以叫做中断。

所以,就很好理解这里的外部和内部的含义了:

针对于CPU执行指令来说,从外界过来干扰自己的,那种普通外设所产生的硬件的中断,就是外部中断;

而CPU本身执行的指令产生错误了或者本身就是执行的是INT等中断指令,那么就会产生异常或陷阱,就属于内部中断。

即对于广义上的中断的概念来说:

普通的硬件中断,属于外部中断;

陷阱和异常,属于内部中断。

3.2. 软件中断和硬件中断

根据中断的来源的性质不同,中断可分为软件中断和硬件中断。

根据前面对于中断,异常和陷阱的详细解释,所以此处也很好理解所谓的软件中断和硬件中断了。

即,从中断的来源来说,来自外部的外设所产生的中断,叫做硬件中断;

而从CPU内部所执行的(类似于80x86中的INT)指令所产生的中断,成为软件中断,是软件写的中断指令,即软件代码,所产生的中断,此处就是指陷阱。

所以,可以简单的理解为:

软件中断就是陷阱;

硬件中断就是普通的外设的中断;

3.3. 向量中断和非向量中断

中断根据如何系统如何处理和响应的方式,可以分为向量中断和非向量中断。

向量者,矢量也,即指方向,门路。

简单的说就是:

向量中断由硬件提供中断服务程序入口地址;

非向量中断由软件提供中断服务程序入口地址;

向量中断,是CPU得到此中断后,直接跳转到,对应的,固定的某个地址,该地址原先已经存放好了该中断所对应的中断服务程序ISR的地址了,即某个ISR函数的地址,所以CPU直接跳转到该位置,也就是直接跳转到对应的ISR,执行对应程序了。

对于非向量中断,是对应着多个函数,共享这个非向量中断的总入口地址,即可以理解为,多个非向量中断,其入口地址都是同一个地址,即普通的IRQ中断的那个地址,然后此地址中存放了通用的ISR函数的地址,该ISR函数中,会去经过一系列的判断,找出是具体真正的哪个外设的中断,然后再在对应的之前设置好的某个中断服务程序表中,找到对应的该中断所对应中断函数,去执行对应的真正的ISR函数。

3.3.1. 向量中断和非向量中断实例解析

以ARM系统为例,物理地址最开始的几个地址,存放的是对应的几个最常见的向量中断,如数据中止异常,软中断,预取指错误异常等,还有一个是其他非向量中断的总入口IRQ。

此部分内容,可以参考Uboot中的ARM的初始化部分start.S中的代码来解释:

其相关代码如下:

_start:	b       reset
	ldr	pc, _undefined_instruction
	ldr	pc, _software_interrupt
	ldr	pc, _prefetch_abort
	ldr	pc, _data_abort
	ldr	pc, _not_used
	ldr	pc, _irq
	ldr	pc, _fiq

_undefined_instruction:	.word undefined_instruction
_software_interrupt:	.word software_interrupt
_prefetch_abort:	.word prefetch_abort
_data_abort:		.word data_abort
_not_used:		.word not_used
_irq:			.word irq
_fiq:			.word fiq
。。。
/*
 * exception handlers
 */
	.align  5
undefined_instruction:
	get_bad_stack
	bad_save_user_regs
	bl 	do_undefined_instruction

	.align	5
software_interrupt:
	get_bad_stack
	bad_save_user_regs
	bl 	do_software_interrupt

	.align	5
prefetch_abort:
	get_bad_stack
	bad_save_user_regs
	bl 	do_prefetch_abort

	.align	5
data_abort:
	get_bad_stack
	bad_save_user_regs
	bl 	do_data_abort

	.align	5
not_used:
	get_bad_stack
	bad_save_user_regs
	bl 	do_not_used
。。。

	.align	5
irq:
	sub	lr, lr, #4			        @ the return address
	ldr	sp, IRQ_STACK_START	        @ the stack for irq
	stmdb	sp!, 	{ r0-r12,lr }	@ save registers
	
	ldr	lr,	=int_return		        @ set the return addr
	ldr	pc, =IRQ_Handle		        @ call the isr
int_return:
	ldmia	sp!, 	{ r0-r12,pc }^	@ return from interrupt

	.align	5
fiq:
	get_fiq_stack
	/* someone ought to write a more effiction fiq_save_user_regs */
	irq_save_user_regs
	bl 	do_fiq
	irq_restore_user_regs
        

可看出,物理内存最开始的存放的内容是:

地址0x0: reset整个系统

地址0x04:放了一个指令,该指令是将_undefined_instruction存入PC,即实现PC跳转到_undefined_instruction的地址中去;

地址0x08:同理,PC跳转到_software_interrupt

地址0x0C:同理,PC跳转到_prefetch_abort

地址0x10:同理,PC跳转到_data_abort

地址0x14:同理,PC跳转到_not_used

地址0x18:同理,PC跳转到_irq

地址0x1C:同理,PC跳转到_fiq

其中,对于_undefined_instruction,很明显,就是我们之前所解释的异常,即指令执行出了对应的问题了,PC会直接跳转到此处的0x04的地址,然后该地址中,就是把PC跳转到对应的_undefined_instructio的位置,去执行对应的异常处理。

其他的_software_interrupt和_data_abort等,都是同样道理,不多解释。

而上述这些异常或_software_interrupt,就都是所谓的中断向量,都是由硬件架构决定的,固定好的了地址,作为软件开发人员,只要把对应的指令写好,到时候发生对应的异常,系统自动会跳转到此处的地址,实现对应的PC的跳转,去做对应的处理。

而对于0x18处的_irq,就是我们所说的所有的非向量中断的总的入口地址,即系统发现有中断了,此处发现是普通的中断,那么就会跳转到0x18的地址这里,然后执行的是:

PC跳转到_irq,而_irq地址所对应的内容是:保存对应的当前的环境,即上下文,然后执行“ldr pc, =IRQ_Handle”,即跳转到IRQ_Handle函数中去。

而以TQ2440的S3C2410为例,其代码为:

interrupts.c (opt\embedsky\u-boot-1.1.6\cpu\arm920t\s3c24x0)

void Isr_Init(void)
{
    int i = 0;
    intregs = S3C24X0_GetBase_INTERRUPT();

    for (i = 0; i < sizeof(isr_handle_array) / sizeof(isr_handle_array[0]); i++ )
    {
        isr_handle_array[i] = Dummy_isr;
    }

    intregs->INTMOD=0x0;                // All=IRQ mode
    intregs->INTMSK=BIT_ALLMSK;         // All interrupt is masked.

    //pISR_URXD0=(unsigned)Uart0_RxInt;
    //rINTMSK=~(BIT_URXD0);             //enable UART0 RX Default value=0xffffffff

    isr_handle_array[ISR_TIMER4_OFT] = IsrTimer4;
    isr_handle_array[ISR_WDT_OFT]  = IsrWatchdog;

#ifdef CONFIG_USB_DEVICE
    isr_handle_array[ISR_USBD_OFT] = IsrUsbd;
    isr_handle_array[ISR_DMA2_OFT] = IsrDma2;
    ClearPending(BIT_DMA2);
    ClearPending(BIT_USBD);
#endif
}

void IRQ_Handle(void)
{
    unsigned long oft = intregs->INTOFFSET;
    S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();

//  printk("IRQ_Handle: %d\n", oft);

    if( oft == 4 ) gpio->EINTPEND = 1<<7;       
    intregs->SRCPND = 1<<oft;
    intregs->INTPND = intregs->INTPND;

    /* run the isr */
    isr_handle_array[oft]();
}

        

可见,其中IRQ_Handle做的事情,就是去读取对应的寄存器,然后经过计算,找到真正的中断源的偏移量,然后再通过偏移量,在中断函数表中,去获得对应该中断的中断服务程序ISR。

而其中的中断函数表isr_handle_array是在程序最开始初始化时候去调用Isr_Init来初始化好的,已经见每个中断多对应的ISR函数存放了对应的位置了。

向量中断的优点是,反应速度快,有了中断,CPU直接跳转到对应的位置,去执行对应的代码了,属于速度快,但是无法扩展,由硬件设计时候觉得的,固定好了,没法改变。

而非向量中断,由于多了一层调用关系,而且在总的普通中断的入口函数中,要去读取寄存器,再去计算到底是哪个中断,所以,速度上,就相对较慢了,属于速度慢,但是扩展性较好。

简单的说就是:

硬件中断,由硬件提供ISR地址,速度较快;

软件中断,由软件计算出中断源,再去找出对应的ISR,速度相对慢。

3.4. 可屏蔽中断和非可屏蔽(NMI)中断

而普通的中断,即前面所说的外部的硬件的中断,根据其中断的性质,是否可以被屏蔽掉,而分为可屏蔽中断和非可屏蔽中断。

对于如果是可屏蔽的中断,是通过设置硬件上对应的标志位flag来实现屏蔽对应的中断的。

一般的中断服务程序中,很多时候,你都会看到,在进入ISR的时候,首先做的事情就是去关闭中断,此时,指的就是设置对应的中断屏蔽寄存器的标志位,去关闭后续的可能发生的中断。

常见的系统中,都有对应的mask 寄存器的,针对每一个中断,设置其是否被屏蔽。

一般来说,向量中断,都是不可屏蔽的,而非向量中断,多数都是可以屏蔽的。

猜你喜欢

转载自blog.csdn.net/qq_26690505/article/details/79306003