硬件中断处理是实时系统设计中的关键性问题,设计人员有必要对其作深入研究,以更好地满足开发工作需要。文中以VxWorks操作系统为软件平台,讨论了在实时系统中进行中断应用设计时要注意的一些问题。由于软硬件的相关性,选用广泛应用的X86架构的嵌入式汁算机为硬件平台,对PenriumCPU和计算机主板对硬件中断的管理机制也做了详细介绍 所得出的研究结论在具体的开发项目中均得以验证,可供相关技术人员参考。
硬件中断处理是实时系统设计中最重要、最关键的问题。文中综合软硬件,从工程应用的角度对此问题加以讨论。文中所述内容针对的软件平台是VxWorks实时操作系统,硬件平台选用广泛使用的X86架构的嵌入式计算机,全文按照CPU、主板、操作系统自下而上的顺序展开。
1 Pentium CPU的中断类型
有两类事件可引起Pentium挂起当前的指令流,即中断和异常。中断是由外部事件引发的,在程序执行的任何时刻都可能出现;异常也称异常中断,是由内部事件引发的。中断和异常各有两类触发源:
(1)中断。
可屏蔽中断:CPU的INTR引脚收到有效信号,如果Pentium标志寄存器IF位为1,则允许中断,否则信号在CPU内被屏蔽。
非屏蔽中断:CPU的NMI引脚收到有效信号而引发的中断,这类中断不能被阻止。
(2)异常。
执行异常:CPU试图执行一条指令的过程中出现错误、故障等不正常条件而引发的异常中断。
执行软件中断指令:Pentium指令系统中包括一些如INTO,INT n这类软件中断指令,执行时产生异常中断。
详细分类的话,Pentium可以识别256种中断和异常。每种中断给予一个编号,即0~255,称为中断向量号(interrupt vector number)。其中NMI、异常以及系统保留占用中断向量号0~31,而32~255为用户中断向量号,可供INTR和自定义软件中断(如汇编中的INT指令)使用。
2 Pentium CPU的中断响应过程
中断处理子程序的入口地址信息存于内存中的一个表内,实模式为中断向量表IVT,保护模式为中断描述符表IDT。中断发生时,CPU首先通过某种方式获得中断向量号,再以中断向量号检索此表,即可获取中断服务子程序入口地址,详述如下:
(1)实模式使用中断向量表。
中断向量表IVR的基地址由IDTR(中断描述符寄存器)指定,大小为1kB。中断响应时的查表过程与8086/8088一致,在此不再赘述。
(2)保护模式使用中断描述符表。
中断描述符表(IDT)的基地址也由IDTR指定,大小为2kB。中断描述符表每一表项对应一个中断向量号,但表项称为中断门描述符或陷阱门描述符。这些门描述符为8字节长,对应256个中断向量号。以中断向量号乘以8作为访问IDT的偏移,读取相应的中断门/陷阱门描述符表项。门描述符给出中断服务子程序人口地址(段:偏移),其中32位偏移量装入EIP,16位的段值被装入CS寄存器。但此段值是选择符,CPU会自动查GDT或LDT取得代码段描述符并送到相应的描述符寄存器中。
3 X86架构的计算机对外部中断的管理
在嵌入式应用中,人们感兴趣的主要是指由硬件信号触发的非屏蔽中断与可屏蔽中断。在单CPU的X86计算机中,采用两片8259级联来管理16个可屏蔽外部中断,由于主8259的IRQ2用于级联,所以实际可用的IRQ只有15个,其中又有一些被系统占用,这种逻辑如今已被集成在主板芯片组的南桥中,如图1所示。由于传统的PIC提供的中断资源较少,现代PC开始采用APIC(高级可编程中断控制器)管理外部中断,它的一个显著优点是能够扩充系统可用的IRQ资源。文中问题的讨论基于传统的PIC结构。
图1 外部中断管理逻辑
4 在VxWorks中设计中断应用
Vxworks运行在保护模式下。在Vxworks中,可以采用intConnect关联中断服务程序至某个中断向量。然而intConnect并不是直接将用户设计的ISR与中断门描述符相关联,而是对它加了一层封装,然后将封装代码的内存首地址与中断门描述符相关联,中断响应过程如图2所示。采用intConnect为ISA总线设备关联中断服务程序IRQ—ISR至IRQ10的程序片断如下:
代码
- #define IRQNum 10 /*1*/
- ……
- /* 2,3 */
- if(intConneet((VOIDFUNCPTR)INUM_TO_IVEC(IRQNum+0x20),IRQ_ISR,0)==OK)
- {
- if(sysIntEnablePIC(IRQNum)==OK) /*4*/
- {
- print{("Succeeded.\n");
- }
- }
- void IRQ_ISR()
- {
- int intLockKey;
- intLockKey=intLock();/*5*/
- ……(critical section)
- intUnlock(intLocKey);
- }
图2 中断响应过程
程序要点详述:
① X86架构的计算机中,一些中断资源已经固定地分配给某些外部设备,如系统时钟固定使用IRQ0,所以在选择中断号时首先应参考硬件手册,避免与已用的中断资源冲突。选定中断号后,需要在BIOS中加以设置。避免BIOS在初始化时,把此中断号作为可用资源分配给PCI设备,造成中断冲突。以上是在开发ISA设备时要注意的,若开发PCI设备,一般不做上述考虑,因为BIOS可为PCI设备动态分配中断资源,且多个PCI设备可共享一个中断号,只需从PCI配置头中读取分配到的中断号使用即可。
② VxWorks中使用intConnect挂接中断服务程序,但对于PCI设备,一般采用pciIntConnect挂接中断,它与intConnect的主要不同在于intConnect使用的中断向量是独占的,而pciIntConnect则可使多个外部中断共享一个中断向量。它在内部使用一个链表管理多个ISR,发生中断时,链接在一个链表上的各个ISR被依次调用,pciIntConnect要求每个ISR被调用时,应该首先查询是否为自己的设备产生的中断,不是则应立即返回,以继续调用其它ISR。
③ 在VxWorks中要注意区分以下4个与中断相关的概念:IRQNumber,INumber,IVector,ILevel。
- IRQNumber:外部中断信号由两片8259级联构成的PIC的那个输入管脚引入,主8259的8条中断输入线对应IRQ0~IRQ7,从8259对应IRQ8~IRQ15。
- INumber:Pentium CPU 的中断向量号。INumber=IRQNumber+INT_NUM_IRQ0,INT_NUM_IRQ0,即IRQ0对应的中断向量号,BSP的config.h中定义其为0x20(十进制的32),即Pentium CPU 中断向量号用户定义区的起始编号。
- IVector:中断向量,是指某中断的中断描述符在中断描述符表中的偏移量。INumber与IVector间的关系可简单描述为:IVector=INumber*8。可以用ivi86.h中定义的两个宏(INUM_TO_IVEC,IVEC_TO_INUM)实施转换。
- ILevel:中断优先级。由于主从8259都被初始化为固定优先级,优先级逐渐递减,且两片8259存在级联关系,所以优先级关系为IRQ0 > IRQ1> IRQ2[IRQ8>IRQ9>…>IRQ15]>IRQ3>…>IRQ6>IRQ7。
表1列出了IRQ0~IRQ15对应的中断向量号(INumber)与中断向量(IVector)。
IRQNumber | INumber | IVector |
0 | 32 | 0x100 |
1 | 33 | 0x108 |
2 | 34 | 0x110 |
… | … | … |
15 | 47 | 0x178 |
④ 用intConnect挂中断后,还必须用sysIntEnablePIC使能中断,sysIntEnablePIC的函数原型在i8259Pic.C中定义,实际上就是针对某个IRQ,置8259中断屏蔽寄存器中的相应位为允许。sysIntEnablePIC中的基本操作是利用端口读写函数sysInByte,sysOutByte读写8259的中断屏蔽寄存器。sysInByte,sysOutByte的函数原型在sysALib.s中定义。
⑤ 关键代码段可用关中断intLock和开中断intUnlock加以保护。语句对intLock与intUnlock在不同的CPU体系架构中实现原理不同。在X86架构中,它们是通过操作EFLAGS中的IF位实现的。反汇编后可观察它们的汇编形式代码。
intLock的反汇编代码:
_intLock: PUSHF /*将EFLAGS压栈*/
_intLock: POP EAX
+0x002: AND EAX,0x200 /*只保存感兴趣的位IF*/
+0x007: CLI /*清IF位*/
+0x008: RET /*EAX作为返回值,即lock-out key */
intUnlock的反汇编代码:
_intUnlock: MOV EAX,[ESP+4] /*将lock-out key作为参数传给EAX */
+0x004: AND EAX,0x200 /*判封锁中断前IF位的状态如何*/
+0x009: JE intUnlock0 /*若封锁中断前也是关中断状态,则不做操作*/
+0x011: STI /*若封锁中断前是开中断状态,则置IF位*/
intUnlock0: RET /*返回*/
注意不要在中断闭锁期间调用Vxworks系统函数,否则有可能意外打开中断闭锁,违反临界代码的设计意图。intLock可以在ISR或通常的任务中使用,当在任务中使用时,关中断并不会禁止任务调度,所以,若一个任务关中断后,又发生了任务调度,则新任务的上下文将被恢复,而EFLAGS是任务上下文的一部分,所以IF位可能会发生变化,中断屏蔽可能会被解除。为了在关中断的同时禁止任务调度,可采用如下形式:
if(taskLock()==OK)
{
intLockKey= intLock();
…(critical section)
intUnlock(intLockKey);
taskUnlock();
}
以上介绍了设计中断应用时在软件方面要注意的一些问题,再简单说一下硬件中断信号的提供。VxWorks中8259被初始化为上升沿触发,值得注意的是,外部中断信号 须保持为高电平直至第一个INTA信号的下降沿到来,否则会造成假中断,触发IRQ7的中断服务程序,常用的硬件中断信号形式是利用一个负脉冲的后沿(上升沿)。IRQ7是并口使用的中断号,在调试过程中,可以在VxWorks中裁减掉并口模块,在IRQ7上挂一个测试用的中断服务程序,以观察记录假中断。
5 结束语
文中结合工程实践,以VXWorks与X86架构的嵌入式计算机为软硬件平台,较深入地阐述了在实时系统中设计中断应用时需要考虑的一些问题。由于篇幅所限,一些在其他资料中被广泛提及的设计要点(如中断服务程序不能调用可能会引起调用阻塞的函数)在此不作介绍。
VxWorks中的中断应用设计要点,码迷,mamicode.com