S3C2440 中断体系结构
ARM的7种工作模式
用户模式(usr):ARM处理器正常的程序执行状态
快速中断模式(fiq):用于高速数据传输或通道处理
中断模式(irq):用于通用的中断处理
管理模式(svc):操作系统使用的保护模式
数据访问终止模式(abt):当数据或指令预取终止时进入该模式,可用于虚拟存储及存储保护
系统模式(sys):运行具有特权的操作系统任务
未定义指令终止模式(und):当未定义的指令执行时进入该模式,可用于支持硬件协处理器的软件仿真
除了用户模式之外,其他的6种工作模式都属于特权模式。大多数程序运行于用户模式,进入特权模式是为了处理中断、异常或者访问被保护的系统资源。
当发生各类中断、异常时CPU自动进入相应的模式。当在特权模式时,可以通过软件来进行模式切换。
ARM的CPU有两种工作状态:ARM状态和Thumb状态。ARM状态下CPU执行32位的字对齐的ARM指令;Thumb状态下CPU执行16位的半字对齐的Thumb指令。CPU上电复位后处于ARM状态。
目前我们只关心ARM状态下的操作。在ARM状态下,ARM920T有31个通用寄存器和6个程序状态寄存器,这些寄存器都是32位的。这37个寄存器分为7组,如下图所示:
图中的Banked register,即备份寄存器,专属于相应的工作模式。不同的工作模式下都有自己的副本,当切换到另一个工作模式时,那个工作模式的寄存器副本就将被使用。
ARM状态下,每种工作模式都有16个通用寄存器和1~2个程序状态寄存器。除了R15寄存器之外,R0~R14都是通用寄存器,这些寄存器既可以用于保存数据,也可以用于保存地址。R15又叫PC,就是我们所熟知的程序计数器。需要指出,通常R13~R15都有特殊用途。其中,R13又被称为栈指针寄存器,通常被用于保存栈指针,简记为sp。R14又被称为程序连接寄存器(link register),当执行BL指令调用子程序时,R14中得到跳转前要执行的下一条指令的地址。当发生中断或异常时,对应的相应模式R14中保存返回值。
快速中断模式中有7个备份寄存器R8~R14,这使得进入快速中断模式执行时,不需要保存R8~R14寄存器。其他特权模式只有两个备份寄存器R13、R14。
CPSR(Current Program Status Register,程序状态寄存器)是一个很重要的寄存器,具体如下图所示:
各比特位的含义如下所示:
第0到第4比特位 M[4:0]:表明CPU当前处于什么工作模式,设置后使CPU进入指定的工作模式。除了sys模式之外,其他特权模式下都有SPSR寄存器(Saved Program Status Registers,程序状态保存寄存器)。当切换到这些工作模式时,在SPSR中保存前一个工作模式的CPSR值。当返回到之前的工作模式时,可以将SPSR的值恢复到CPSR中。
第5比特位T位:置1时表明CPU处于Thumb状态,置0时表示处于ARM状态。
第6比特位F位、第7比特位I位:这两比特位都属于中断禁止位。当置1时,对应的FIQ中断和IRQ中断分别被禁止。
发生异常或中断
当发生异常或中断时,ARM920T CPU核将自动完成以下工作:
在该特权模式的lr寄存器中保存之前工作模式的将要执行的下一条指令的地址。对于ARM状态,这个值可能是PC值+4或者+8。具体情况如下表所示:
将CPSR的值复制到对应的特权模式的SPSR中;
将CPSR的工作模式位设置为新的特权模式对应的值;
令PC等于该异常或中断对应的异常向量表中的地址。异常向量表和对应的工作模式如下图所示:
退出异常工作模式
在特权模式下,将lr减去一个适当的值,之后赋给PC
将SPSR的值赋值给CPSR
S3C2440中断控制器
CPU在运行过程中,对于如何检测到各类外设发生的随机事件,主要有以下两种方法:
查询方式: 程序循环地查询各设备的状态。这样做效率太低,不适合并发。
中断方式:当某事件发生时,硬件会设置某个寄存器。CPU在每执行完一条指令后,都会区检测这个寄存器,如果发生了事件,则CPU中断当前流程,跳转到一个固定的地址去处理该事件,之后返回原来被中断的程序继续执行。
无论是何种CPU,中断的处理过程是相似的。
首先由中断控制器汇集 各类外设发出的中断信号,之后通知CPU;
CPU保存当前程序的运行上下文,然后调用中断服务程序ISR(Interrupt Service Routine)来处理这些中断;
在ISR中通过读取中断控制器、外设相关寄存器来识别中断并进行相应的处理;
清除中断
恢复被中断程序的运行上下文,然后继续执行。
我们通过S3C2440的用户手册可以得到其中断控制器的结构如下图所示:
如上图所示,SUBSRCPND和SRCPND寄存器表明有哪些中断被触发了,正在等待处理。而SUBMASK和MASK寄存器则起到开关的作用,用于屏蔽某些中断。
当Request sources(without sub-register)中的中断源被触发之后,SUBSRCPND寄存器中相应位被置1,如果此中断没有被SUBMASK寄存器屏蔽的话,它在SRCPND寄存器中的相应位也会被置1,之后处理过程和2一样;
当Request sources(without sub-register)中的中断源被触发后,SRCPND寄存器中相应位被置1,如果此中断没有被INTMASK寄存器屏蔽的话,或者此中断是FIQ,那么它将被路由至下一步。
如果被触发的中断是FIQ,那么CPU进入快速中断处理模式;如果是一般中断IRQ,那么可能同时有几个中断被触发,未被屏蔽的中断会全部进入到优先级比较器,优先级最高的中断胜出,并将INTPND寄存器中相应的位置1,然后CPU进入IRQ中断模式进行处理。中断服务程序会结合INTOFFSET和INTPND寄存器来确定中断源。
中断源和子中断源
对于某些中断来说,比如INT_UART0中断源,其需要细分为三个子中断源:INT_ERR0、INT_TXD0、INT_RXD0,用以分别表示UART0的出错中断、发送中断和接收中断。
S3C2440的中断源和子中断源如下图所示:
中断源:
子中断源:
相关寄存器
与中断控制器相关的寄存器有8个。其中,SUBSRCPND和INTSUBMSK这两个寄存器中相同的位对应相同的中断;SRCPND、INTMSK、INTMOD、INTPND这四个寄存器相同的位对应相同的中断。
SUBSRCPND寄存器
SUBSRCPND用来标志INT_RXD0、INT_TXD0等中断是否已经发生。在S3C2440中,这类子中断共有15个。每位对应一个中断。当中断发生时,对应位被置1,之后如果INTSUBMSK中对应的位没有被置1屏蔽,那么它们中的若干位将汇集到SRCPND寄存器中的某一位上。如SUBSRCPND寄存器中的INT_ERR0、INT_TXD0和INT_RXD0这3个中断,只要有一个子中断发生且没有被屏蔽,那么SRCPND寄存器中的INT_UART0位将被置1。SUBSRCPND寄存器中具体哪几位对应SRCPND寄存器中的一位,如下图所示:
对于SUBSRCPND寄存器,往其中某一位写1,则即可将该为清零,写0无效果,寄存器中原数据保持不变。
INTSUBMSK寄存器
INTSUBMSK寄存器被用来屏蔽SUBSRCPND寄存器中所标志的中断。置1表示屏蔽对应位上的中断。
SRCPND寄存器
SRCPND寄存器中每一位被用来表示一个或者一类中断是否已经发生。“一类中断”自然指的是那些对应有SUBSRCPND的中断。
和SUBSRCPND类似,想要清除SRCPND寄存器中的某一位,往此位写1即可。
INTMSK寄存器
INTMSK寄存器被用来屏蔽SRCPND寄存器中所标志的中断。置1屏蔽。需要说明的是,INTMSK寄存器只能屏蔽被设置为IRQ的中断,而不能屏蔽被设置为FIQ的中断。怎么将中断设置为FIQ,详见下面的INTMOD寄存器。
INTMOD寄存器
当INTMOD寄存器中的某一位被设置为1时,它对应的中断被设置为FIQ。之后,若此发生此中断,那么CPU将进入快速中断模式,这通常用来处理特别紧急的中断。
需要说明的是,在同一时刻,INTMOD寄存器中只能有一位被设置为1。
PRIORITY寄存器
优先级寄存器我们接下来用一节专门讲解。这里强调一点,优先级比较只针对于IRQ中断。因为有之前的框图也可知,FIQ直接到达CPU了。我们把IRQ中断叫做普通中断,简称中断。
INTPND寄存器
经过中断优先级仲裁器选出的优先级最高的中断,将被设置进INTPND寄存器。即INTPND寄存器中对应的位被置1。此后CPU将进入中断模式处理它。显然,同一时刻,INTPND寄存器只能有一位被置1。在ISR中,我们可以根据这一位来断定是哪一个中断发生了。
同样,写1清零。
INTOFFSET寄存器
INTOFFSET寄存器被用来表示INTPND寄存器中哪一位被置1了,即当INTPND寄存器中位[x]被置1了,那么此时INTOFFSET寄存器的值就是x。x的取值范围是闭区间[0, 31]。
当清除SRCPND、INTPND寄存器时,INTOFFSET寄存器被自动清零。
优先级比较器
S3C2440将不同的中断分为几组,每一组内先进行比较,之后每一组的胜出者再进行比较,最终决出优先级最高的一个中断,如下图所示:
关于优先级设置,我们先看一下优先级设置寄存器:
每一个仲裁器都含有6个输入引脚,PRIORITY寄存器使用三位来控制其行为,一位是ARB_MODE,用于选择工作模式;另外两位是ARB_SEL,被用于控制输入信号的优先级。
由于ARB_SEL是两位,所以其优先级设置有四种方式:
我们可以看到,无论ARB_SEL设置为任何值,REQ0的优先级始终是最高的,REQ5的优先级始终是最低的。
当我们将工作模式位ARB_MODE设置为0时,对应的ARB_SEL位是不会自动变化的,也就是说该仲裁器的6个输入引脚的优先级是固定不变的。当ARB_MODE位被置1时,ARB_SEL会随着已经被服务过的REQ自动变化,服务过谁,那么就设置为谁优先级最低,如下图所示:
异常实验
如上图所示,我们先实现异常向量表,并进行reset、und和swi异常的实验。当系统复位或上电时,我们打印出booting;当执行未定义机器指令时,打印出Undefiend instruction;当进行软中断调用时,我们打印出调用号。
我们知道,SVC、Und、Abt、IRQ、FIQ模式都有自己的堆栈,而Usr和Sys模式共用一个堆栈。所以我们在它们各自的处理过程中首先就要初始化栈指针。由之前的博客介绍可以知道,JZ2440开发板外接了两片EM63A165TS-6G,每片大小为32MB,所以JZ2440外接内存大小一共为64MB。由于ARM的堆栈是满减栈,所以我们将各个模式的堆栈指针依次设置在内存最高地址处。SDRAM接在BANK6,对应的基址是0x30000000,故最高内存地址为0x34000000。设置每个栈大小为4KB。所以:
sp_svc = 0x34000000
sp_und = 0x33FFF000
sp_abt = 0x33FFE000
sp_irq = 0x33FFD000
sp_fiq = 0x33FFC000
sp_usr_sys = 0x33FFB000
如下所示,我们先构造中断向量表:
.text
.global _start
_start:
b reset //SVC
b undEXP //Und
b swiEXP //SVC
b AbortPrefetch //Abort
b AbortData //Abort
b reset
b IRQINT //IRQ
b FIQINT //FIQ
其中reset标号对应的就是以前正常启动的代码,而其他标号则对应各个异常的处理程序。需要特别注意的是,这里的跳转指令需要使用位置无关跳转,同时不能使用bl指令,因为它会修改lr寄存器,使得异常处理之后需要返回的地址被覆盖掉。
下面我们分别来介绍各个异常处理过程。
未定义指令异常
我们将und模式下的栈指针设置为0x33FFF000。
当pc指向的地址中存储的是一条非法指令时,cpu将其取指到指令寄存器中,执行时就会产生未定义指令异常。由上面的异常退出表可知,此时lr寄存器中保存的就是待返回的用户程序指令地址。所以我们直接将r0~r12寄存器,还有lr_und保存到栈中。之后直接调用C函数进行相应的异常处理即可。
由于此时lr寄存器中保存的是用户程序中的下一条指令的地址。所以导致未定义指令异常的指令就是之前地址的内容。故lr_und - 4就是未定义指令的地址。我们将该地址作为实参传递给异常处理函数,方便其进行异常处理。
在C处理函数返回之后,我们弹栈并恢复CPSR寄存器即可。代码如下所示:
undEXP:
ldr sp, =0x33FFF000
/*因为lr是异常处理后的返回地址,所以不用修正lr */
/* 保存r0-r12以及lr*/
//使用满减栈
stmdb sp!, {r0-r12, lr}
//lr减4,即为导致异常的指令地址
sub r0, lr, #4
bl undExpHandler
/* 恢复现场 */
ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */
void undExpHandler(unsigned int* undAddr) {
printf("Undefined Instruction(0x%08x) at address(0x%08x) is found!nr",
*undAddr, undAddr);
}
软件中断异常
由于swi模式对应的是SVC管理员模式,所以我们将swi模式下的栈指针设置为0x34000000。
同样在开头需要将r0~r12以及lr寄存器压栈。
当我们调用swi xx的时候,则会进入管理员模式,此时通常是为了调用一个特殊的管理员模式下的功能函数。由于此时lr保存的也是程序要返回的地址,所以lr_svc - 4就是swi指令所在的地址。我们取出swi指令的二进制值,同时结合swi指令格式:
我们可知swi指令中的数字序号就存放在低24位中,故我们可以取出该软件中断号,并传给C处理函数。
在C处理函数返回之后,我们弹栈并恢复CPSR寄存器即可。代码如下所示:
swiEXP:
ldr sp, =0x34000000
/*因为lr是异常处理后的返回地址,所以不用修正lr */
/* 保存r0-r12以及lr*/
//使用满减栈
stmdb sp!, {r0-r12,lr}
//lr减4,即为导致异常的指令地址
ldr r0, [lr, #-4]
//只保留低24位的软件号
bic r0, r0, #0xFF000000
bl swiExpHandler
/* 恢复现场 */
ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */
void swiExpHandler(unsigned int swiNum) {
printf("Software Exception is Checked with Number %d(0x%x)nr", swiNum, swiNum);
}
指令预取异常和数据预取异常
这两个异常对应的模式都是abt模式,所以我们设置其栈指针为0x33FFE000。和前两个处理流程几乎一样,需要特别注意的是,数据预取异常需要在开头修正lr,令lr = lr - 8;指令预取异常修正lr = lr - 4。其他的没有什么区别。这两个异常的处理代码如下所示:
AbortPrefetch:
ldr sp, =0x33FFE000
subs lr, lr, #4
//使用满减栈
stmdb sp!, {r0-r12, lr}
sub r0, lr, #4
bl abortPrefetchExpHandler
/* 恢复现场 */
ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */
AbortData:
ldr sp, =0x33FFE000
subs lr, lr, #8
//使用满减栈
stmdb sp!, {r0-r12, lr}
mov r0, lr
bl abortDataExpHandler
/* 恢复现场 */
ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */
void abortPrefetchExpHandler(unsigned int* abtPrefetchAddr) {
printf("Fetch instruction from address(0x%08x) failed.nr", abtPrefetchAddr);
}
void abortDataExpHandler(unsigned int* abtDataAddr) {
printf("Fetch data failed at address(0x%08x).nr",
abtDataAddr);
}
IRQ中断和FIQ中断
按照我们之前的设定,我们将IRQ的栈指针初始化为0x33FFD000,我们将FIQ的栈指针初始化为0x33FFC000。同时还要将二者的lr寄存器进行修正, lr = lr - 4才是程序真正应该返回的地址。在本文中,对于irq普通中断和fiq快速中断,我们调用C处理函数,并处理中断返回工作。在C函数中只先简单打印一行提示。具体任务将在下一篇文章中详述,下一篇文章将进行按键中断点灯和按键fiq快速中断点灯,同时采用串口中断进行收发数据。
IRQINT:
ldr sp, =0x33FFD000
subs lr, lr, #4
//使用满减栈
stmdb sp!, {r0-r12, lr}
bl irqExpHandler
/* 恢复现场 */
ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */
FIQINT:
ldr sp, =0x33FFC000
subs lr, lr, #4
//使用满减栈
stmdb sp!, {r0-r12, lr}
bl fiqExpHandler
/* 恢复现场 */
ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */
void irqExpHandler(void) {
printf("IRQ is Enter!nr");
}
void fiqExpHandler(void) {
printf("FIQ is Enter!nr");
}
添加触发代码
为了在实验中触发未定义指令异常和软件中断异常,我们在代码拷贝到内存之后添加未定义指令0xdeadC0de和swi软件中断调用,如下所示:
ldr r0, =bootMsg
bl uart0_puts
swi 0x2020
.word 0xdeadC0de
ldr lr, =halt
ldr pc, =main //这才是真正跳转到内存中开始运行
实验结果如下图所示:
上一篇:11.S3C2440 中断实验(五)定时器中断实验
下一篇:S3C2440 开发板实战(4):外部中断
推荐阅读
史海拾趣
由于篇幅限制,无法在此处直接提供5个完整的、每篇至少500字的Eclipse Magnetics公司电子行业发展的相关故事。不过,我可以概括地描述几个可能的故事线索,这些线索可以基于Eclipse Magnetics公司的历史、技术发展和市场应用来构建。
- 品牌初创与磁铁技术的突破
在1930年代,Eclipse Magnetics的名字首次出现,标志着这个品牌的诞生。随着技术的不断发展,公司在1940年代成为独立公司,并开始专注于磁铁技术的研发。到了1950年代,Eclipse Magnetics在技术上取得了重大突破,他们将磁铁涂上红色,并设计了与公司颜色相匹配的马蹄形标志,这一设计不仅提升了品牌形象,也成为了国际通用标记。这一时期的Eclipse Magnetics,以其独特的技术和产品,开始在电子行业中崭露头角。
- 航空航天领域的应用与拓展
随着航空航天技术的不断发展,Eclipse Magnetics看到了磁铁技术在该领域的巨大潜力。公司开始投入大量资源,研发适用于航空航天领域的磁性解决方案。通过不断的技术创新和产品优化,Eclipse Magnetics成功地为航空航天领域提供了高性能、高可靠性的磁铁产品,并在这一领域树立了良好的口碑。
- 与制药工业的深入合作
除了航空航天领域,Eclipse Magnetics还与制药工业建立了深入的合作关系。在制药过程中,磁铁技术可以用于磁选机,有效地去除原料中的杂质,提高药品的纯度。Eclipse Magnetics根据制药工业的特殊需求,定制了多款磁选机产品,并提供了全面的技术支持和售后服务。通过与制药工业的深入合作,Eclipse Magnetics不仅拓展了市场,也提升了自身的技术实力。
- 在汽车制造行业的应用
随着汽车制造业的快速发展,Eclipse Magnetics也看到了磁铁技术在该领域的广阔前景。公司针对汽车制造中的各个环节,研发了多款适用于不同场景的磁性解决方案。例如,在齿轮箱轴制造过程中,Eclipse Magnetics的磁性过滤器可以保持冷却液的清洁,提高生产效率和产品质量。这些解决方案不仅满足了汽车制造业的需求,也进一步巩固了Eclipse Magnetics在电子行业中的地位。
- 创新研发与可持续发展
作为一家专注于磁性技术的公司,Eclipse Magnetics始终将创新作为发展的核心动力。公司不断投入研发资源,推动磁铁技术的不断进步。同时,Eclipse Magnetics也关注可持续发展问题,致力于研发环保、高效的磁性产品。通过创新研发和可持续发展战略的实施,Eclipse Magnetics在电子行业中保持了领先地位,并为未来的发展奠定了坚实的基础。
请注意,以上内容仅为故事线索的概括描述,具体的故事细节和数据需要根据Eclipse Magnetics公司的实际情况进行补充和完善。
随着全球市场的不断变化和发展,ELECTRONICON积极实施国际化战略,拓展海外市场。公司在全球范围内建立了完善的销售和服务网络,为客户提供及时、高效的服务。未来,ELECTRONICON将继续秉承“质量为本、客户至上”的经营理念,不断创新和发展,为电子行业的繁荣做出更大的贡献。
Advanced Detector Corp公司成立于上世纪80年代,由一群热衷于探测器技术研发的科学家和工程师创立。在创立初期,ADC便专注于开发高精度、高灵敏度的探测器技术,以满足当时日益增长的电子测量需求。公司通过持续的技术创新,逐渐在探测器领域取得了突破性的进展,并成功推出了一系列具有竞争力的产品。
随着全球市场的不断开放和经济的全球化,ADC开始将业务拓展至全球范围。公司在全球各地设立了分支机构,并建立了完善的销售和服务网络。通过全球化布局,ADC成功打开了新的市场,提升了品牌影响力,实现了业务的快速增长。
Crane Co.的创立可以追溯到1855年,当时R. T. 克瑞黄铜与铸钟厂在芝加哥正式成立。公司起初主要生产阀门、配件和特种铸件,这些产品为当时的工业发展提供了关键支持。随着美国工业的蓬勃发展,Crane Co.凭借其精湛的铸造技术和优质的产品质量,逐渐在阀门制造领域崭露头角。
随着电子行业的快速发展,艾迈斯(AMASS)公司意识到只有不断创新才能保持领先地位。为此,公司加大了对研发的投入,引进了一批高素质的研发人才,并建立了完善的研发体系。经过多年的努力,艾迈斯成功研发出了多款具有创新性的连接器产品,其中包括高性能、高安全性的智能设备动力连接器。这些产品的推出不仅提升了公司的技术实力和市场竞争力,也为整个行业的发展带来了积极的影响。