仲裁单元如何确认 系统中断占用cpu发到哪个cpu

developerWorks 社区
本文对中断系统进行了全面的分析与探讨,主要包括中断控制器、中断分类、中断亲和力、中断线程化与 SMP 中的中断迁徙等。首先对中断工作原理进行了简要分析,接着详细探讨了中断亲和力的实现原理,最后对中断线程化与非线程化中断之间的实现机理进行了对比分析。
, 在读研究生
苏春艳:在读研究生,主要在Linux系统下从事嵌入式开发。
(), 在读研究生
杨小华,目前从事 Linux 内核方面的研究,喜欢捣鼓 Linux 系统,对 Linux 中断系统比较了解。可以通过 与他取得联系。
什么是中断Linux 内核需要对连接到计算机上的所有硬件设备进行管理,毫无疑问这是它的份内事。如果要管理这些设备,首先得和它们互相通信才行,一般有两种方案可实现这种功能:轮询(polling) 让内核定期对设备的状态进行查询,然后做出相应的处理;中断(interrupt) 让硬件在需要的时候向内核发出信号(变内核主动为硬件主动)。第一种方案会让内核做不少的无用功,因为轮询总会周期性的重复执行,大量地耗用 CPU 时间,因此效率及其低下,所以一般都是采用第二种方案 。从物理学的角度看,中断是一种电信号,由硬件设备产生,并直接送入中断控制器(如 8259A)的输入引脚上,然后再由中断控制器向处理器发送相应的信号。处理器一经检测到该信号,便中断自己当前正在处理的工作,转而去处理中断。此后,处理器会通知 OS 已经产生中断。这样,OS 就可以对这个中断进行适当的处理。不同的设备对应的中断不同,而每个中断都通过一个唯一的数字标识,这些值通常被称为中断请求线。APIC vs 8259AX86计算机的 CPU 为中断只提供了两条外接引脚:NMI 和 INTR。其中 NMI 是不可屏蔽中断,它通常用于电源掉电和物理存储器奇偶校验;INTR是可屏蔽中断,可以通过设置中断屏蔽位来进行中断屏蔽,它主要用于接受外部硬件的中断信号,这些信号由中断控制器传递给 CPU。常见的中断控制器有两种:1. 可编程中断控制器8259A传统的 PIC(Programmable Interrupt Controller)是由两片 8259A 风格的外部芯片以“级联”的方式连接在一起。每个芯片可处理多达 8 个不同的 IRQ。因为从 PIC 的 INT 输出线连接到主 PIC 的 IRQ2 引脚,所以可用 IRQ 线的个数达到 15 个,如图 1 所示。图 1:8259A 级联原理图2. 高级可编程中断控制器(APIC)8259A 只适合单 CPU 的情况,为了充分挖掘 SMP 体系结构的并行性,能够把中断传递给系统中的每个 CPU 至关重要。基于此理由,Intel 引入了一种名为 I/O 高级可编程控制器的新组件,来替代老式的 8259A 可编程中断控制器。该组件包含两大组成部分:一是“本地 APIC”,主要负责传递中断信号到指定的处理器;举例来说,一台具有三个处理器的机器,则它必须相对的要有三个本地 APIC。另外一个重要的部分是 I/O APIC,主要是收集来自 I/O
装置的 Interrupt 信号且在当那些装置需要中断时发送信号到本地 APIC,系统中最多可拥有 8 个 I/O APIC。每个本地 APIC 都有 32 位的寄存器,一个内部时钟,一个本地定时设备以及为本地中断保留的两条额外的 IRQ 线 LINT0 和 LINT1。所有本地 APIC 都连接到 I/O APIC,形成一个多级 APIC 系统,如图 2 所示。图 2:多级I/O APIC系统目前大部分单处理器系统都包含一个 I/O APIC 芯片,可以通过以下两种方式来对这种芯片进行配置:1) 作为一种标准的 8259A 工作方式。本地 APIC 被禁止,外部 I/O APIC 连接到 CPU,两条 LINT0 和 LINT1 分别连接到 INTR 和 NMI 引脚。2) 作为一种标准外部 I/O APIC。本地 APIC 被激活,且所有的外部中断都通过 I/O APIC 接收。辨别一个系统是否正在使用 I/O APIC,可以在命令行输入如下命令:# cat /proc/interrupts
IO-APIC-edge
IO-APIC-edge
IO-APIC-edge
IO-APIC-level
IO-APIC-edge
IO-APIC-edge
IO-APIC-edge
IO-APIC-level
IO-APIC-level
...如果输出结果中列出了 IO-APIC,说明您的系统正在使用 APIC。如果看到 XT-PIC,意味着您的系统正在使用 8259A 芯片。中断分类中断可分为同步(synchronous)中断和异步(asynchronous)中断:1. 同步中断是当指令执行时由 CPU 控制单元产生,之所以称为同步,是因为只有在一条指令执行完毕后 CPU 才会发出中断,而不是发生在代码指令执行期间,比如系统调用。2. 异步中断是指由其他硬件设备依照 CPU 时钟信号随机产生,即意味着中断能够在指令之间发生,例如键盘中断。根据 Intel 官方资料,同步中断称为异常(exception),异步中断被称为中断(interrupt)。中断可分为可屏蔽中断(Maskable interrupt)和非屏蔽中断(Nomaskable interrupt)。异常可分为故障(fault)、陷阱(trap)、终止(abort)三类。从广义上讲,中断可分为四类:中断、故障、陷阱、终止。这些类别之间的异同点请参看 表 1。表 1:中断类别及其行为类别原因异步/同步返回行为中断来自I/O设备的信号异步总是返回到下一条指令陷阱有意的异常同步总是返回到下一条指令故障潜在可恢复的错误同步返回到当前指令终止不可恢复的错误同步不会返回X86 体系结构的每个中断都被赋予一个唯一的编号或者向量(8 位无符号整数)。非屏蔽中断和异常向量是固定的,而可屏蔽中断向量可以通过对中断控制器的编程来改变。Linux 2.6 中断处理原理简介中断描述符表(Interrupt Descriptor Table,IDT)是一个系统表,它与每一个中断或异常向量相联系,每一个向量在表中存放的是相应的中断或异常处理程序的入口地址。内核在允许中断发生前,也就是在系统初始化时,必须把 IDT 表的初始化地址装载到 idtr 寄存器中,初始化表中的每一项。当处于实模式下时,IDT 被初始化并由 BIOS 程序所使用。然而,一旦 Linux 开始接管,IDT 就被移到 ARM 的另一个区域,并进行第二次初始化,因为 Linux 不使用任何 BIOS 程序,而使用自己专门的中断服务程序(例程)(interrupt service routine,ISR)。中断和异常处理程序很像常规的 C 函数有三个主要的数据结构包含了与 IRQ 相关的所有信息:hw_interrupt_type、irq_desc_t 和 irqaction,图3 解释了它们之间是如何关联的。图 3:IRQ 结构之间的关系在 X86 系统中,对于 8259A 和 I/O APIC 这两种不同类型的中断控制器,hw_interrupt_type 结构体被赋予不同的值,具体区别参见表 2。表 2:8259A 和 I/O APIC PIC 的区别8259AI/O APIC
static struct hw_interrupt_type i8259A_irq_type = {
startup_8259A_irq,
shutdown_8259A_irq,
enable_8259A_irq,
disable_8259A_irq,
mask_and_ack_8259A,
end_8259A_irq,
static struct hw_interrupt_type ioapic_edge_type = {
.typename = "IO-APIC-edge",
= startup_edge_ioapic,
= shutdown_edge_ioapic,
= enable_edge_ioapic,
= disable_edge_ioapic,
= ack_edge_ioapic,
= end_edge_ioapic,
.set_affinity = set_ioapic_affinity,
static struct hw_interrupt_type ioapic_level_type = {
.typename = "IO-APIC-level",
= startup_level_ioapic,
.shutdown = shutdown_level_ioapic,
= enable_level_ioapic,
= disable_level_ioapic,
= mask_and_ack_level_ioapic,
= end_level_ioapic,
.set_affinity = set_ioapic_affinity,
在中断初始化阶段,调用 hw_interrupt_type 类型的变量初始化 irq_desc_t 结构中的 handle 成员。在早期的系统中使用级联的8259A,所以将用 i8259A_irq_type 来进行初始化,而对于SMP系统来说,要么以 ioapic_edge_type,或以 ioapic_level_type 来初始化 handle 变量。对于每一个外设,要么以静态(声明为 static 类型的全局变量)或动态(调用 request_irq 函数)的方式向 Linux 内核注册中断处理程序。不管以何种方式注册,都会声明或分配一块 irqaction 结构(其中 handler 指向中断服务程序),然后调用 setup_irq() 函数,将 irq_desc_t 和 irqaction 联系起来。当中断发生时,通过中断描述符表 IDT 获取中断服务程序入口地址,对于 32≤ i ≤255(i≠128)
之间的中断向量,将会执行 push $i-256,jmp common_interrupt 指令。随之将调用 do_IRQ() 函数,以中断向量为 irq_desc[] 结构的下标,获取 action 的指针,然后调用 handler 所指向的中断服务程序。从以上描述,我们不难看出整个中断的流程,如图 4 所示:图 4:X86中断流本文作者之一曾经对2.6.10的中断系统进行过情景分析,有兴趣的读者可以和作者取得联系,获取相关资料。中断绑定——中断亲和力(IRQ Affinity)在 SMP 体系结构中,我们可以通过调用系统调用和一组相关的宏来设置 CPU 亲和力(CPU affinity),将一个或多个进程绑定到一个或多个处理器上运行。中断在这方面也毫不示弱,也具有相同的特性。中断亲和力是指将一个或多个中断源绑定到特定的 CPU 上运行。中断亲和力最初由 Ingo Molnar 设计并实现。在 /proc/irq 目录中,对于已经注册中断处理程序的硬件设备,都会在该目录下存在一个以该中断号命名的目录 IRQ# ,IRQ# 目录下有一个 smp_affinity 文件(SMP 体系结构才有该文件),它是一个 CPU 的位掩码,可以用来设置该中断的亲和力, 默认值为 0xffffffff,表明把中断发送到所有的 CPU 上去处理。如果中断控制器不支持 IRQ affinity,不能改变此默认值,同时也不能关闭所有的 CPU 位掩码,即不能设置成 0x0。我们以网卡(eth1,中断号 44 )为例,在具有 8 个 CPU 的服务器上来设置网卡中断的亲和力(以下数据出自内核源码 Documentation\IRQ-affinity.txt):[root@moon 44]# cat smp_affinity
[root@moon 44]# echo 0f & smp_affinity
[root@moon 44]# cat smp_affinity
[root@moon 44]# ping -f h
PING hell (195.4.7.3): 56 data bytes
--- hell ping statistics ---
6029 packets transmitted, 6027 packets received, 0% packet loss
round-trip min/avg/max = 0.1/0.1/0.4 ms
[root@moon 44]# cat /proc/interrupts | grep 44:
IO-APIC-level
[root@moon 44]# echo f0 & smp_affinity
[root@moon 44]# ping -f h
PING hell (195.4.7.3): 56 data bytes
--- hell ping statistics ---
2779 packets transmitted, 2777 packets received, 0% packet loss
round-trip min/avg/max = 0.1/0.5/585.4 ms
[root@moon 44]# cat /proc/interrupts | grep 44:
IO-APIC-level
[root@moon 44]#在上例中,我们首先只允许在 CPU0~3 上处理网卡中断,接着运行 ping 程序,不难发现在 CPU4~7 上并没有对网卡中断进行处理。然后只在 CPU4~7 上对网卡中断进行处理, CPU0~3 不对网卡中断进行任何处理,运行 ping 程序之后,再次查看 /proc/interrupts 文件时,不难发现 CPU4~7 上的中断次数明显增加,而 CPU0~3 上的中断次数没有太大的变化。在探讨中断亲和力的实现原理之前,我们首先来了解 I/O APIC 中的组成。I/O APIC 由一组 24 条 IRQ 线,一张 24 项的中断重定向表(Interrupt Redirection Table),可编程寄存器,以及通过 APIC 总线发送和接收 APIC 信息的一个信息单元组成。其中与中断亲和力息息相关的是中断重定向表,中断重定向表表中的每一项都可以被单独编程以指明中断向量和优先级、目标处理器及选择处理器的方式。通过表 2,不难发现 8259A 和 APIC 中断控制器最大不同点在于 hw_interrupt_type 类型变量的最后一项。对于 8259A 类型,set_affinity 被置为 NULL,而对于 SMP 的 APIC 类型,set_affinity 被赋值为 set_ioapic_affinity。在系统初始化期间,对于 SMP 体系结构,将会调用 setup_IO_APIC_irqs() 函数来初始化 I/O APIC 芯片,芯片中的中断重定向表的 24 项被填充。在系统启动期间,所有的 CPU 都执行 setup_local_APIC() 函数,完成本地的 APIC 初始化。当有中断被触发时,将相应的中断重定向表中的值转换成一条消息,然后,通过 APIC 总线把消息发送给一个或多个本地 APIC 单元,这样,中断就能立即被传递给一个特定的 CPU,或一组 CPU,或所有的 CPU,从而来实现中断亲和力。当我们通过 cat 命令将 CPU 掩码写进 smp_affinity 文件时,此时的调用路线图为:write() -&sys_write() -&vfs_write() -&proc_file_write() -&irq_affinity_write_proc() -&set_affinity() -&set_ioapic_affinity() -&set_ioapic_affinity_irq() -&io_apic_write();其中在调用
set_ioapic_affinity_irq() 函数时,以中断号和 CPU 掩码作为参数,接着继续调用 io_apic_write(),修改相应的中断重定向中的值,来完成中断亲和力的设置。当执行 ping 命令时,网卡中断被触发,产生了一个中断信号,多 APIC 系统根据中断重定向表中的值,依照仲裁机制,选择 CPU0~3 中的某一个 CPU,并将该信号传递给相应的本地 APIC,本地 APIC 又中断它的 CPU,整个事件不通报给其他所有的 CPU。新特性展望——中断线程化(Interrupt Threads)在嵌入式领域,业界对 Linux 实时性的呼声越来越高,对中断进行改造势在必行。在 Linux 中,中断具有最高的优先级。不论在任何时刻,只要产生中断事件,内核将立即执行相应的中断处理程序,等到所有挂起的中断和软中断处理完毕后才能执行正常的任务,因此有可能造成实时任务得不到及时的处理。中断线程化之后,中断将作为内核线程运行而且被赋予不同的实时优先级,实时任务可以有比中断线程更高的优先级。这样,具有最高优先级的实时任务就能得到优先处理,即使在严重负载下仍有实时性保证。目前较新的 Linux 2.6.17 还不支持中断线程化。但由 Ingo Molnar 设计并实现的实时补丁,实现了中断线程化。最新的下载地址为:下面将对中断线程化进行简要分析。在初始化阶段,中断线程化的中断初始化与常规中断初始化大体上相同,在 start_kernel() 函数中都调用了 trap_init() 和 init_IRQ() 两个函数来初始化 irq_desc_t 结构体,不同点主要体现在内核初始化创建 init 线程时,中断线程化的中断在 init() 函数中还将调用 init_hardirqs(kernel/irq/manage.c(已经打过上文提到的补丁)),来为每一个 IRQ 创建一个内核线程,最高实时优先级为 50,依次类推直到 25,因此任何 IRQ 线程的最低实时优先级为 25。void __init init_hardirqs(void)
for (i = 0; i & NR_IRQS; i++) {
irq_desc_t *desc = irq_desc +
if (desc-&action && !(desc-&status & IRQ_NODELAY))
desc-&thread = kthread_create(do_irqd, desc, "IRQ %d", irq);
static int do_irqd(void * __desc)
* Scale irq thread priorities from prio 50 to prio 25
param.sched_priority = curr_irq_
if (param.sched_priority & 25)
curr_irq_prio = param.sched_priority - 1;
}如果某个中断号状态位中的 IRQ_NODELAY 被置位,那么该中断不能被线程化。在中断处理阶段,两者之间的异同点主要体现在:两者相同的部分是当发生中断时,CPU 将调用 do_IRQ() 函数来处理相应的中断,do_IRQ() 在做了必要的相关处理之后调用 __do_IRQ()。两者最大的不同点体现在 __do_IRQ() 函数中,在该函数中,将判断该中断是否已经被线程化(如果中断描述符的状态字段不包含 IRQ_NODELAY 标志,则说明该中断被线程化了),对于没有线程化的中断,将直接调用 handle_IRQ_event() 函数来处理。fastcall notrace unsigned int __do_IRQ(unsigned int irq, struct pt_regs *regs)
if (redirect_hardirq(desc))
goto out_no_
action_ret = handle_IRQ_event(irq, regs, action);
int redirect_hardirq(struct irq_desc *desc)
if (!hardirq_preemption || (desc-&status & IRQ_NODELAY) || !desc-&thread)
if (desc-&thread && desc-&thread-&state != TASK_RUNNING)
wake_up_process(desc-&thread);
}对于已经线程化的情况,调用 wake_up_process() 函数唤醒中断处理线程,并开始运行,内核线程将调用 do_hardirq() 来处理相应的中断,该函数将判断是否有中断需要被处理,如果有就调用 handle_IRQ_event() 来处理。handle_IRQ_event() 将直接调用相应的中断处理函数来完成中断处理。不难看出,不管是线程化还是非线程化的中断,最终都会执行 handle_IRQ_event() 函数来调用相应的中断处理函数,只是线程化的中断处理函数是在内核线程中执行的。并不是所有的中断都可以被线程化,比如时钟中断,主要用来维护系统时间以及定时器等,其中定时器是操作系统的脉搏,一旦被线程化,就有可能被挂起,这样后果将不堪设想,所以不应当被线程化。如果某个中断需要被实时处理,它可以像时钟中断那样,用 SA_NODELAY 标志来声明自己非线程化,例如:static struct irqaction irq0 = {
timer_interrupt, SA_INTERRUPT | SA_NODELAY, CPU_MASK_NONE, "timer", NULL, NULL
};其中,SA_NODELAY 到 IRQ_NODELAY 之间的转换,是在 setup_irq() 函数中完成的。中断负载均衡—SMP体系结构下的中断中断负载均衡的实现主要封装在 arch\ arch\i386\kernel\io-apic.c 文件中。如果在编译内核时配置了 CONFIG_IRQBALANCE 选项,那么 SMP 体系结构中的中断负载均衡将以模块的形式存在于内核中。late_initcall(balanced_irq_init);
#define late_initcall(fn)
module_init(fn)
//include\linux\init.h在 balanced_irq_init() 函数中,将创建一个内核线程来负责中断负载均衡:static int __init balanced_irq_init(void)
printk(KERN_INFO "Starting balanced_irq\n");
if (kernel_thread(balanced_irq, NULL, CLONE_KERNEL) &= 0)
printk(KERN_ERR "balanced_irq_init: failed to spawn balanced_irq");
}在 balanced_irq() 函数中,每隔 5HZ=5s 的时间,将调用一次 do_irq_balance() 函数,进行中断的迁徙。将重负载 CPU 上的中断迁移到较空闲的CPU上进行处理。总结随着中断亲和力和中断线程化的相继实现,Linux 内核在 SMP 和实时性能方面的表现越来越让人满意,完全有理由相信,在不久的将来,中断线程化将被合并到基线版本中。本文对中断线程化的分析只是起一个抛砖引玉的作用,当新特性发布时,不至于让人感到迷茫。注释 1:轮询也不是毫无用处,比如NAPI,就是轮询与中断相结合的经典案例。
参考资料 Rebert Love,《Linux Kernel Development,2rd Edition》,机械工业出版社,2006。Daniel P. Bovet,Marco Cesati,《Understanding the Linux Kernel,3rd Edition》,东南大学出版社,2006。Jonatban Corbet 等,魏永明等译,《Linux设备驱动程序》,中国电力出版社,2006。Gordon Fischer 等,《The Linux Kernel Prime》,机械工业出版社,2006。
developerWorks: 登录
标有星(*)号的字段是必填字段。
保持登录。
单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件。
在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。
所有提交的信息确保安全。
选择您的昵称
当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。昵称长度在 3 至 31 个字符之间。
您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。
标有星(*)号的字段是必填字段。
(昵称长度在 3 至 31 个字符之间)
单击提交则表示您同意developerWorks 的条款和条件。 .
所有提交的信息确保安全。
文章、教程、演示,帮助您构建、部署和管理云应用。
立即加入来自 IBM 的专业 IT 社交网络。
免费下载、试用软件产品,构建应用并提升技能。
static.content.url=/developerworks/js/artrating/SITE_ID=10Zone=LinuxArticleID=228270ArticleTitle=Linux 内核中断内幕publish-date=英飞凌tricore用户手册 第13章 中断系统INT_图文_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
英飞凌tricore用户手册 第13章 中断系统INT
&&英飞凌tricore用户手册
阅读已结束,下载文档到电脑
想免费下载本文?
定制HR最喜欢的简历
下载文档到电脑,方便使用
还剩22页未读,继续阅读
定制HR最喜欢的简历
你可能喜欢● 工程师进阶(7)
--------【SoftWare Exam】(7)
&&&&&&& 因为上篇的知识点总结是在做题过程中临时发现的问题,临时总结的,所以有点偏乱。而且各个知识点都有,所
以针对做过的上午题进行了一些总结,发现有些章节的题是经常容易出错,知识点容易混淆的。这一片总结计算机基
&&&&&&& 运算器
&&&&&&& 是什么?负责计算机的各种算术和逻辑运算。
&&&&&&& 组成部分?
&&&&&&& 1、算术逻辑单元:负责算术和逻辑运算。
&&&&&&& 2、累加寄存器:存放运算结果。
&&&&&&& 3、数据缓冲寄存器:存放内存器指令或数据字,数据传送中转站。
&&&&&&& 4、状态条件寄存器:保存运算结果,建立各种条件码内容。&
&&&&&&& 控制器
&&&&&&& 是什么?控制CPU工作。
&&&&&&& 组成部分?
&&&&&&& 1、指令寄存器:从内存储器到缓冲寄存器。
&&&&&&& 2、程序计数器:指令计数器,顺序+转移执行。
&&&&&&& 3、地址寄存器:保留内存单元地址。
&&&&&&& 4、指令译码器:对指令中的操作码字段分析。
&&&&&&& 中断处理方法
1、多中断信号线法:中断请求信号线向CPU发送请求
2、中断软件查询法:中断服务程序查询谁发出中断信号请求
3、菊花链法:硬件查询法,中断确认信号在I/O模块间传递。
4、总线仲裁法:获得总线控制权,裁定谁可以发出中断请求信号
5、中断向量法:中断向量表保存中断服务程序的入口地址。中断控制器确定其中断号,查找中断向量表,获取中断服务程序入口地址。
是什么?直接内存存取
特点:不许CPU干预。
&&&&&&&&&&
芯片内总线:集成电路芯片内部的连接
元件级总线:电路板内各元器件的连接
外部:通信总线,设备级互连。
常见的外部总线:RS-232C,SCSI,USB,IEEE-1394,IEEE-488。
系统:插件板级互连。构成:CPU、内存、接口等
常见的系统总线:ISA,EISA,PCI。
&&&&&& Flynn分类法
结构:控制部分1;处理器1;主存模块1
代表:单处理器系统
结构:控制部分1;处理器多;主存模块多
特性:异步形式执行同一条命令
代表:并行处理机;阵列处理机;超级向量处理机
结构:都多
特性:不可能,未实现
结构:都多
特性:全面并行
代表:多处理系统;多计算机
&&&&&&& 寻址方式
隐含寻址:隐含着操作数的地址
立即寻址(最快):直接存操作数
直接寻址:存操作数地址
间接寻址:存操作数地址的地址
寄存器寻址方式和寄存器简介寻址方式:操作数放在CPU通用寄存器中,而非内存中
相对寻址方式:程序计数器内容+指令格式的形式地址
基址寻址:基址寄存器的内容+指令格式的形式地址
变址寻址:CPU变址寄存器内容+偏移量
先进度排名:奇偶校验&CRC校验&海明校验
奇偶:判断信息在传输是否产生1位错误,不能判断具体是哪一位
CRC:循环冗余校验,利用除法及余数的原理
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:99714次
积分:8776
积分:8776
排名:第2268名
原创:164篇
评论:3570条
阅读:1663
阅读:5297
阅读:8796
(4)(5)(5)(4)(6)(5)(9)(5)(4)(3)(5)(5)(5)(5)(4)(6)(3)(4)(7)(4)(4)(5)(4)(3)(1)(4)(5)(4)(2)(3)(3)(10)(3)(7)(2)(5)(2)(4)(4)
(window.slotbydup = window.slotbydup || []).push({
id: '4740881',
container: s,
size: '200,200',
display: 'inlay-fix'下次自动登录
现在的位置:
& 综合 & 正文
S3C2440系统中断
S3C2440系统中断
CPU和外设构成了计算机系统,CPU和外设之间通过总线进行连接,用于数据通信和控制,CPU管理监视计算机系统中所有硬件,通常以两种方式来对硬件进行管理监视:
查询方式:CPU不停的去查询每一个硬件的当前状态,根据硬件的状态决定处理与否。好比是工厂里的检查员,不停的检查各个岗位工作状态,发现情况及时处理。这种方式实现起来简单,通常用在只有少量外设硬件的系统中,如果一个计算机系统中有很多硬件,这种方式无疑是耗时,低效的,同时还大量占用CPU资源,并且对多任务系统反应迟钝。
中断方式:当某个硬件产生需要CPU处理的事件时,主动通过一根信号线“告知”CPU,同时设置某个寄存器里对应的位,CPU一旦发现这根信号线上的电平有变化,就会中断当前程序,然后去处理发出该中断请求。这就像是医院重危病房,病房每张病床床头有一个应急按钮,该按钮连接到病房监控室里控制台一盏指示灯,只要该张病床出现紧急情况病人按下按钮,病房监控室里电铃会响起,通知医护人员有紧急情况,医护人员这时查看控制台上的指示灯,找出具体病房,病床号,直接过去处理紧急情况。中断处理方式相对查询方式要复杂的多,并且需要硬件的支持,但是它处理的实时性更高,嵌入式系统里基本上都使用这种方式来处理。
系统中断是嵌入式硬件实时地处理内部或外部事件的一种机制。对于不同CPU而言,中断的处理只是细节不同,大体处理流程都一样,S3C2440A的中断控制器结构如下图所示:
图3-3 S3C2440中断控制器
中断请求由硬件产生,根据中断源类型分别将中断信号送到SUBSRCPND(SubSourcePending)和SRCPND(SourcePending)寄存器,SUBSRCPND是子中断源暂存寄存器,用来保存子中断源信号,SRCPND是中断源暂存寄存器,用来保存中断源信号。中断信号可通过编程方式屏蔽掉,SUBMASK是子中断源屏蔽寄存器,可以屏蔽指定的子中断信号, MASK功能同SUBMASK用来屏蔽中断源信号。中断分为两种模式:一般中断的和快速中断,MODE是中断模式判断寄存器,用来判断当前中断是否为快速中断,如果为快速中断直接将快速中断信号送给ARM内核,如果不是快速中断,还要将中断信号进行仲裁选择。S3C2440A支持多达60种中断,很有可能多个硬件同时产生中断请求,这时要求中断控制器做出裁决,Priority是中断源优先级仲裁选择器,当多个中断产生时,选择出优先级最高的中断源进行处理,INTPND是中断源结果寄存器,里面存放优先级仲裁出的唯一中断源。
中断的产生-中断源
S3C2440A支持60种中断源,基本上满足了开发板内部,外部设备等对中断的需求。其中每一个中断源对应寄存器中的一位,显然要支持60种中断至少需要二个32位寄存器,SUBSRCPND和SRCPND分别保存中断源信号。S3C2440A对60种中断源的管理是按层级分的。如图3-4所示:
图3-4中断源信号复合示意图
S3C2440A将中断源分为两级:中断源和子中断源,中断源里包含单一中断源和复合中断源,复合中断源是子中断源的复合信号。如实时时钟中断,该硬件只会产生一种中断,它是单一中断源,直接将其中断信号线连接到中断源寄存器上。对于复合中断源,以UART串口为例进行说明,S3C2440A可以支持3个UART串口,每个串口对应一个复合中断源信号INT_UARTn,每个串口可以产生三种中断,也就是三个子中断:接收数据中断INT_RXDn,发送数据中断INT_TXDn,数据错误中断INT_ERRn,这三个子中断信号在中断源寄存器复合为一个中断信号,三种中断任何一个产生都会将中断信号传递给对应的中断源INT_UARTn,然后通过中断信号线传递给ARM内核。
图3-5 UART串口中断源信号复合示意图
总中断源详下面表中列出了S3C2440A部分中断源,它分别对应中断源寄存器里某个位:详细中断源请查看S3C2440A硬件手册。
表3-5 部分中断源信号
优先级仲裁分组
数模转换和触摸屏中断
实时时钟中断
UART0中断(包含子中断)
NandFlash控制中断
INT_WDT_AC97
看门狗中断
外部中断8~23(包含外部子中断)
外部中断4~7(包含外部子中断)
中断信号除上述分法之外,还可以按照硬件位置分为:外部中断源和内部中断源。
内部中断源:它是嵌入式系统中常见硬件产生的中断信号,比如:UART串口中断源,时钟Timer中断源,看门狗中断源等
外部中断源:有时嵌入式系统里要在外部接口上挂载一些外部设备,这些设备并不是一个通用嵌入式系统里必备硬件,比如:蓝牙模块,各种传感器,WIFI无线通信模块,这些硬件也要产生中断让CPU来处理数据,因此这些外设硬件通过中断信号线连接到中断控制器上,它们产生的中断叫做外部中断信号。它们有着和内部中断一样的处理机制,只不过,它没有一个固定的中断号与之对应,硬件与嵌入式系统的连接方式与中断处理完全由系统硬件与软件设计者实现。
外设硬件通过输入输出接口I/O Ports挂接到嵌入式系统上,I/O Ports向外设提供外部中断信号线,输出电源,频率时钟和输入输出信号线,外部硬件根据自己需要连接到I/O Ports上,产生中断时向外部中断信号线上送出中断信号,通过外部中断信号线传递到中断控制器。
按键Key可以认为最为简单的一种硬件设备了,如下图所示:
图3-6按键硬件接线原理图
它功能很简单,可以将电路接通,按键K1~K6一端接地为低电平,另外一端接电源正极为高电平,EINT8,EINT11,EINT13,EINT14,EINT15,EINT19六根中断信号线分别和高电平端按键相连,当按键按下时电路接通,整个电路变成低电平,中断信号线上电压产生变化,通过设置中断触发方式,产生外部中断请求,输入到CPU内部,从而实现按键中断控制。
S3C2440A可以支持EINT0~EINT23共24种外部中断,完全可以满足小型嵌入式设备外设硬件的需求。
外部中断源也分为外部中断源和外部子中断源,其处理方式和内部中断源基本一样。
中断优先级
S3C2440A支持60种中断,多个硬件可能同时产生中断请求,由于CPU只能处理一个中断,中断控制器怎么选择出一个最佳的中断,交给ARM内核进行处理呢?中断控制器采用优先级仲裁比较的方式进行选择,找出优先级最高的中断源。中断控制器将60种中断源分成7组,如下图所示,它类似体育赛事里的比赛方式,所有参赛选手在小组赛PK,选择出小组赛最优秀选手,然后进入决赛阶段和其它小组最优先选择再PK,最后优胜者就是总冠军。其中ARBITER0~ARBITER5为“小组赛”阶段,中断源信号在各自小组里进行优先级仲裁,选择出最高优先级中断信号,每小组选出的中断信号送到ARBITER6,也就是决赛阶段,选择出最高优先级中断信号,交给ARM内核。
图3-7 S3C2440优先级仲裁示意图
中断信号在7个分组里PK时的优先级是可编程的,通过PRIORITY寄存器进行优先级设置。如下表(只列出PRIORITY寄存器部分位):
表3-6 中断优先级控制寄存器(PRIORITY)
复位默认值
0x4A00000C
中断优先级控制寄存器
仲裁组6优先级排序方式
00 = REQ 0-1-2-3-4-5
01 = REQ 0-2-3-4-1-5
10 = REQ 0-3-4-1-2-5
11 = REQ 0-4-1-2-3-5
仲裁组5优先级排序
00 = REQ 1-2-3-4
01 = REQ 2-3-4-1
10 = REQ 3-4-1-2
11 = REQ 4-1-2-3
仲裁组6优先级是否轮转:
0 = 不轮转,
仲裁组5优先级是否轮转:
0 = 不轮转,
通过设置仲裁组n优先级排序方式位,设置每个仲裁组内中断信号的优先级顺序,比如:ARB_SEL5分组时包含四个中断信号:REQ1 INT_UART0, REQ2 INT_SPI1, REQ3 INT_RTC, REQ4 INT_ADC,ARB_SEL5位采用默认值:00,当INT_UART0和INT_RTC中断信号同时产生时,INT_UART0会被选出,通过可编程方式改变优先级排序方式来改变中断信号优先级。
ARB_MODE0~ ARB_MODE6为每个仲裁分组的优先级轮转设置位,采用默认值时,当前中断信号被选择处理之后,再次产生中断请求时,它的优先级自动轮转到该组最低,这样可以保证优先级低的中断信号可以被及时处理,不至于出现优先级高且中断请求频繁的中断每次都被优先处理,而优先级低的被“饿死”的情况。显然,这种方式更民主,实时性更佳。
中断控制器相关寄存器
(1)SUBSRCPND子中断源暂存寄存器
表3-7 子中断源暂存寄存器(SUBSRCPND)
复位默认值
0x4A000018
子中断源暂存寄存器,保存中断请求状态:
0:没有中断请求信号
1:中断请求信号产生
对应SRCPND
INT_WDT_AC97
0 = 未产生中断 1 = 产生中断
0 = 未产生中断 1 = 产生中断
该寄存器用来标识保存子中断源信号,当某个子中断信号产生之后,SUBSRCPND对应位被自动置1,该位会一直保持被置位,只到中断处理程序将其清除为止,需要注意一下,清除中断是通过向对应位写入1来清除,而不是写入0,写入0无效。
(2)INTSUBMSK子中断源屏蔽寄存器
表3-8 子中断源屏蔽寄存器(INTSUBMSK)
复位默认值
0x4A00001C
子中断源信号屏蔽存寄存器,设置相应位来屏蔽中断信号:
0:未屏蔽,中断可用
1:屏蔽中断信号
0 = 未屏蔽1 = 屏蔽中断
0 = 未屏蔽1 = 屏蔽中断
该寄存器用来屏蔽子中断源信号,默认值为全部子中断都被屏蔽掉,因此要想处理某个硬件中断,必须要打开中断屏蔽位,通过写入0来取消屏蔽中断。
(3)SRCPND中断源暂存寄存器
表3-9中断源暂存寄存器(SRCPND)
复位默认值
0x4A000000
中断源暂存寄存器,保存中断请求状态:
0:没有中断请求信号
1:中断请求信号产生
0 = 未产生中断 1 = 产生中断
0 = 未产生中断 1 = 产生中断
该寄存器用来保存中断源信号,当某个中断信号产生之后, SRCPND对应位被自动置1,该位会一直保持被置位,只到中断处理程序将其清除为止,需要注意一下,清除中断是通过向对应位写入1来清除,而不是写入0,写入0无效。
(4)INTMSK中断源屏蔽寄存器
表3-10中断源屏蔽寄存器(INTMSK)
复位默认值
0x4A000008
中断源信号屏蔽存寄存器,设置相应位来屏蔽中断信号:
0:未屏蔽,中断可用
1:屏蔽中断信号
0xFFFFFFFF
0 = 未屏蔽1 = 屏蔽中断
0 = 未屏蔽1 = 屏蔽中断
该寄存器用来屏蔽中断源信号,默认值为全部中断都被屏蔽掉,因此要想处理某个硬件中断,必须要打开中断屏蔽位,通过写入0来取消屏蔽中断。
(5)INTPND最高优先级中断暂存寄存器
表3-11最高优先级中断暂存寄存器(INTPND)
复位默认值
0x4A000010
最高优先级中断暂存寄存器里面保存有经过优先级仲裁的结果:
0:没有中断请求信号
1:中断请求信号产生
0 = 未产生中断 1 = 产生中断
0 = 未产生中断 1 = 产生中断
该寄存器保存了经过优先级仲裁出的中断信号位,它是所有当前中断请求里优先级别最高的中断,因此该寄存器里的值最多有一位被置1,通常中断处理程序中会通过读取该寄存器的值来获得当前正在处理的中断请求。中断处理完成之后,通过写入1来清除中断。
(6)INTOFFSET中断号偏移量寄存器
表3-12中断号偏移量寄存器(INTOFFSET)
复位默认值
0x4A000014
中断号偏移量寄存器,用来保存当前处理的中断号
该寄存器里存放的是经过优先级仲裁出的中断信号对应的中断号,是一个0~31之间的整数,其实它就是INTPND里对应的位号,比如:INT_UART0产生了中断,INTPND里第28位置1,INTOFFSET里保存的整数就是28,多出来这个寄存器的目的主要是方便中断处理程序查询中断源,清除中断源:
#define TIMER0_IRQ_OFT
// 时钟0定时中断
#define EINT0_IRQ_OFT
// 开发板K1按键1对应外部中断EINT0
handle_irq()
unsigned long irqOffSet = INTOFFSET;
// 取得中断号
switch(irqOffSet)
case TIMER0_IRQ_OFT:
// 当前中断为定时器0中断
do_timer();
// 跳入定时器0处理程序
case EINT0_IRQ_OFT:
// 当前中断为K1按键触发
do_key1_pressed();
// 处理K1按下事件
SRCPND &= (1&&irqOffSet);
// 清除中断源
INTPND = INTPND;
// 清除最高优先级中断暂存寄存器中断
(7)INTMOD中断模式寄存器
表3-13中断模式寄存器(INTMOD)
复位默认值
0x4A000004
中断模式寄存器,指定对应中断模式:
0 = IRQ一般中断模式
1 = FIQ快速中断模式
0 = IRQ 1 = FIQ
0 = IRQ 1 = FIQ
通过设置INTMOD寄存器对应位,来指定对应中断模式,如果指定为一般中断,那么中断信号会进行优先级仲裁,如果指定为快速中断,那么中断信号直接送给ARM内核产生中断。需要注意的是,快速中断不存在优先级仲裁,只能有一位被设置为FIQ模式。
**********************************************************************************************************************************************************
*************************************************************************************************************************************************************
ARM处理器异常处理
所谓异常就是正常的用户程序被暂时中止,处理器就进入异常模式,例如响应一个来自外设的中断,或者当前程序非法访问内存地址都会进入相应异常模式。
(1)复位异常
当CPU刚上电时或按下reset重启键之后进入该异常,该异常在管理模式下处理。
(2)一般/快速中断请求
CPU和外部设备是分别独立的硬件执行单元,CPU对全部设备进行管理和资源调度处理,CPU要想知道外部设备的运行状态,要么CPU定时的去查看外部设备特定寄存器,要么让外部设备在出现需要CPU干涉处理时“打断”CPU,让它来处理外部设备的请求,毫无疑问第二种方式更合理,可以让CPU“专心”去工作,这里的“打断”操作就叫做中断请求,根据请求的紧急情况,中断请求分一般中断和快速中断,快速中断具有最高中断优先级和最小的中断延迟,通常用于处理高速数据传输及通道的中数据恢复处理,如DMA等,绝大部分外设使用一般中断请求。
(3)预取指令中止异常
该异常发生在CPU流水线取指阶段,如果目标指令地址是非法地址进入该异常,该异常在中止异常模式下处理。
(4)未定义指令异常
该异常发生在流水线技术里的译码阶段,如果当前指令不能被识别为有效指令,产生未定义指令异常,该异常在未定义异常模式下处理。
(5)软件中断指令(swi)异常
该异常是应用程序自己调用时产生的,用于用户程序申请访问硬件资源时,例如:printf()打印函数,要将用户数据打印到显示器上,用户程序要想实现打印必须申请使用显示器,而用户程序又没有外设硬件的使用权,只能通过使用软件中断指令切换到内核态,通过操作系统内核代码来访问外设硬件,内核态是工作在特权模式下,操作系统在特权模式下完成将用户数据打印到显示器上。这样做的目的无非是为了保护操作系统的安全和硬件资源的合理使用,该异常在管理模式下处理。
(6)数据中止访问异常
该异常发生在要访问数据地址不存在或者为非法地址时,该异常在中止异常模式下处理。
异常发生的硬件操作
在异常发生后,ARM内核会自动做以下工作:
保存执行状态:将CPSR复制到发生的异常模式下SPSR中;
模式切换:将CPSR模式位强制设置为与异常类型相对应的值,同时处理器进入到ARM执行模式,禁止所有IRQ中断,当进入FIQ快速中断模式时禁止FIQ中断;
保存返回地址:将下一条指令的地址(被打断程序)保存在LR(异常模式下LR_excep)中。
跳入异常向量表:强制设置PC的值为相应异常向量地址,跳转到异常处理程序中。
(1)保存执行状态
当前程序的执行状态是保存在CPSR里面的,异常发生时,要保存当前的CPSR里的执行状态到异常模式里的SPSR里,将来异常返回时,恢复回CPSR,恢复执行状态。
(2)模式切换
硬件自动根据当前的异常类型,将异常码写入CPSR里的M[4:0]模式位,这样CPU就进入了对应异常模式下。不管是在ARM状态下还是在THUMB状态下发生异常,都会自动切换到ARM状态下进行异常的处理,这是由硬件自动完成的,将CPSR[5] 设置为 0。同时,CPU会关闭中断IRQ(设置CPSR 寄存器I位),防止中断进入,如果当前是快速中断FIQ异常,关闭快速中断(设置CPSR寄存器F位)。
(3)保存返回地址
当前程序被异常打断,切换到异常处理程序里,异常处理完之后,返回当前被打断模式继续执行,因此必须要保存当前执行指令的下一条指令的地址到LR_excep(异常模式下LR,并不存在LR_excep寄存器,为方便读者理解加上_excep,以下道理相同),由于异常模式不同以及ARM内核采用流水线技术,异常处理程序里要根据异常模式计算返回地址。
(4)跳入异常向量表
该操作是CPU硬件自动完成的,当异常发生时,CPU强制将PC的值修改为一个固定内存地址,这个固定地址叫做异常向量(详见3.2.4章节)。
异常返回地址
由1.1.3节可知,一条指令的执行分为:取指,译码,执行三个主要阶段, CPU由于使用流水线技术,造成当前执行指令的地址应该是PC – 8(32位机一条指令四个字节),那么执行指令的下条指令应该是PC – 4。在异常发生时,CPU自动会将将PC – 4 的值保存到LR里,但是该值是否正确还要看异常类型才能决定。
各模式的返回地址说明如下:
(a)一般/快速中断请求:
快速中断请求和一般中断请求返回处理是一样的。通常处理器执行完当前指令后,查询FIQ/IRQ中断引脚,并查看是否允许FIQ/IRQ中断,如果某个中断引脚有效,并且系统允许该中断产生,处理器将产生FIQ/IRQ异常中断,当FIQ/IRQ异常中断产生时,程序计数器pc的值已经更新,它指向当前指令后面第3条指令(对于ARM指令,它指向当前指令地址加12字节的位置;对于Thumb指令,它指向当前指令地址加6字节的位置),当FIQ/IRQ异常中断产生时,处理器将值(pc-4)保存到FIQ/IRQ异常模式下的寄存器lr_irq/lr_irq中,它指向当前指令之后的第2条指令,因此正确返回地址可以通过下面指令算出:
PC,LR_irq,#4
; 一般中断
PC,LR_fiq,#4
; 快速中断
注:LR_irq/LR_fiq分别为一般中断和快速中断异常模式下LR,并不存在LR_xxx寄存器,为方便读者理解加上_xxx
(b)预取指中止异常:
在指令预取时,如果目标地址是非法的,该指令被标记成有问题的指令,这时,流水线上该指令之前的指令继续执行,当执行到该被标记成有问题的指令时,处理器产生指令预取中止异常中断。发生指令预取异常中断时,程序要返回到该有问题的指令处,重新读取并执行该指令,因此指令预取中止异常中断应该返回到产生该指令预取中止异常中断的指令处,而不是当前指令的下一条指令。
指令预取中止异常中断由当前执行的指令在ALU里执行时产生,当指令预取中止异常中断发生时,程序计数器pc的值还未更新,它指向当前指令后面第2条指令(对于ARM指令,它指向当前指令地址加8字节的位置;对于Thumb指令,它指向当前指令地址加4字节的位置)。此时处理器将值(pc-4)保存到lr_abt中,它指向当前指令的下一条指令,所以返回操作可以通过下面指令实现:
PC,LR_abt,#4
注:LR_abt为中止模式下LR,并不存在LR_abt寄存器,为方便读者理解加上_abt
(c)未定义指令异常:
未定义指令异常中断由当前执行的指令在ALU里执行时产生,当未定义指令异常中断产生时,程序计数器pc的值还未更新,它指向当前指令后面第2条指令(对于ARM指令,它指向当前指令地址加8字节的位置;对于Thumb指令,它指向当前指令地址加4字节的位置),当未定义指令异常中断发生时,处理器将值(pc-4)保存到lr_und中,此时(pc-4)指向当前指令的下一条指令,所以从未定义指令异常中断返回可以通过如下指令来实现:
注:LR_und为未定义模式下LR,并不存在LR_und寄存器,为方便读者理解加上_und
(d)软中断指令(SWI)异常:
SWI异常中断和未定义异常中断指令一样,也是由当前执行的指令在ALU里执行时产生,当SWI指令执行时,pc的值还未更新,它指向当前指令后面第2条指令(对于ARM指令,它指向当前指令地址加8字节的位置;对于Thumb指令,它指向当前指令地址加4字节的位置),当未定义指令异常中断发生时,处理器将值(pc-4)保存到lr_svc中,此时(pc-4)指向当前指令的下一条指令,所以从SWI异常中断处理返回的实现方法与从未定义指令异常中断处理返回一样:
注:LR_svc为管理模式下LR,并不存在LR_svc寄存器,为方便读者理解加上_svc
(e)数据中止异常:
发生数据访问异常中断时,程序要返回到该有问题的指令处,重新访问该数据,因此数据访问异常中断应该返回到产生该数据访问中止异常中断的指令处,而不是当前指令的下一条指令。
数据访问异常中断由当前执行的指令在ALU里执行时产生,当数据访问异常中断发生时,程序计数器pc的值已经更新,它指向当前指令后面第3条指令(对于ARM指令,它指向当前指令地址加12字节的位置;对于Thumb指令,它指向当前指令地址加6字节的位置)。此时处理器将值(pc-4)保存到lr_abt中,它指向当前指令后面第2条指令,所以返回操作可以通过下面指令实现:
注:LR_abt为中止模式下LR,并不存在LR_abt寄存器,为方便读者理解加上_abt
上述每一种异常发生时,其返回地址都要根据具体异常类型进行重新修复返回地址,再次强调下,被打断程序的返回地址保存在对应异常模式下的LR_excep里。
异常向量表
异常向量表是一段特定内存地址空间,每种ARM异常对应一个字长空间(4Bytes),正好是一条32位指令长度,当异常发生时,CPU强制将PC的值设置为当前异常对应的固定内存地址。如表3-4所示是S3C2440的异常向量表。
表3-4 异常向量表
异常向量也可以出现在高地址0xFFFF0000处,当今操作系统为了控制内存访问权限,通常会开启虚拟内存,开启了虚拟内存之后,内存的开始空间通常为内核进程空间,和页表空间,异常向量表不能再安装在0地址处了
ARM的例外优先级从高到低依次为Reset→Data abort→FIQ→IRQ→Prefetch abort→Undefined instruction/SWI。
跳入异常向量表操作是异常发生时,硬件自动完成的,剩下的异常处理任务完全交给了程序员。由上表可知,异常向量是一个固定的内存地址,我们可以通过向该地址处写一条跳转指令,让它跳向我们自己定义的异常处理程序的入口,就可以完成异常处理了。
正是由于异常向量表的存在,才让硬件异常处理和程序员自定义处理程序有机联系起来。异常向量表里0x地址处是reset复位异常,之所以它为0地址,是因为CPU在上电时自动从0地址处加载指令,由此可见将复位异常安装在此地址处也是前后接合起来设计的,不得不感叹CPU设计师的伟大,其后面分别是其余7种异常向量,每种异常向量都占有四个字节,正好是一条指令的大小,最后一个异常是快速中断异常,将其安装在此也有它的意义,在0x0000001C地址处可以直接存放快速中断的处理程序,不用设置跳转指令,这样可以节省一个时钟周期,加快快速中断处理时间。
我们可以通过简单的使用下面的指令来安装异常向量表:
;跳入reset处理程序
b HandleUndef
;跳入未定义处理程序
;跳入软中断处理程序
b HandPrefetchAbt
;跳入预取指令处理程序
b HandDataAbt
;跳入数据访问中止处理程序
b HandNoUsed
;跳入未使用程序
b HandleIRQ
;跳入中断处理程序
b HandleFIQ
;跳入快速中断处理程序
通常安装完异常向量表,跳到我们自己定义的处理程序入口,这时我们还没有保存被打断程序的现场,因此在异常处理程序的入口里先要保存打断程序现场。
保存执行现场:
异常处理程序最开始,要保存被打断程序的执行现场,程序的执行现场无非就是保存当前操作寄存器里的数据,可以通过下面的栈操作指令实现保存现场:
SP_excep!,
{R0 – R12,
注:LR_abt,SP_excep分别为对应异常模式下LR和SP,为方便读者理解加上_abt
需要注意的是,在跳转到异常处理程序入口时,已经切换到对应异常模式下了,因此这里的SP是异常模式下的SP_excep了,所以被打断程序现场(寄存器数据)是保存在异常模式下的栈里,上述指令将R0~R12全部都保存到了异常模式栈,最后将修改完的被打断程序返回地址入栈保存,之所以保存该返回地址就是将来可以通过类似:MOV
LR的指令,返回用户程序继续执行。
异常发生后,要针对异常类型进行处理,因此,每种异常都有自己的异常处理程序,异常处理过程通过下节的系统中断处理来进行分析。
异常处理的返回
异常处理完成之后,返回被打断程序继续执行,具体操作如下:
恢复被打断程序运行时寄存器数据
恢复程序运行时状态CPSR
通过进入异常时保存的返回地址,返回到被打断程序继续执行
异常发生后,进入异常处理程序时,将用户程序寄存器R0~R12里的数据保存在了异常模式下栈里面,异常处理完返回时,要将栈里保存的的数据再恢复回原先R0~R12里,毫无疑问在异常处理过程中必须要保证异常处理入口和出口时栈指针SP_excep要一样,否则恢复到R0~R12里的数据不正确,返回被打断程序时执行现场不一致,出现问题,虽然将执行现场恢复了,但是此时还是在异常模式下,CPSR里的状态是异常模式下状态,因此要恢复SPSR_excep里的保存状态到CPSR里,SPSR_excep是被打断程序执行时的状态,在恢复SPSR_excep到CPSR的同时,CPU的模式和状态从异常模式切换回了被打断程序执行时的模式和状态。此刻程序现场恢复了,状态也恢复了,但PC里的值仍然指向异常模式下的地址空间,我们要让CPU继续执行被打断程序,因此要再手动改变PC的值为进入异常时的返回地址,该地址在异常处理入口时已经计算好,直接将PC
= LR_excep即可。
上述操作可以一步一步实现,但是通常我们可以通过一条指令实现上述全部操作:
注:SP_excep为对应异常模式下SP,^符号表示恢复SPSR_excep到CPSR
以上操作可以用下图来描述:
&&&&推荐文章:
【上篇】【下篇】

我要回帖

更多关于 系统中断占用cpu 的文章

 

随机推荐