u-boot中断功能初步分析之---------按键中断

发布时间:2023-06-26  

以前一直有个疑问,在U-boot下到底能不能使用中断,为了验证这个问题,于是乎,昨天晚上我在自己的 TQ2440开发板上进行了uboot环境下的按键中断实验,这次使用的我刚移植的最新版Uboot,版本是 u-boot-2014-04,验证的结论是:

U-boot完全能够支持中断

下面就以u-boot-2014-04为例,介绍一下按键中断的实现。

这里分为几部分介绍:

1、异常向量表            ------  由u-boot完成

2、通用中断处理函数       ------  由u-boot完成

3、u-boot自己完成的中断初始化部分  ----- 由u-boot完成

4、用户按键中断中断初始化  ------  由用户完成

5、用户自定义中断处理函数  ------  由用户完成

这里有两篇我在网络上搜集的关于S3C2440中断的文章:

https://files.cnblogs.com/pengdonglin137/S3C2440%E7%B3%BB%E7%BB%9F%E4%B8%AD%E6%96%AD.rar

https://files.cnblogs.com/pengdonglin137/S3C2440%E5%A4%96%E9%83%A8%E4%B8%AD%E6%96%AD%E6%93%8D%E4%BD%9C.pdf

其中介绍了如何使用S3C2440的中断功能以及ARM处理器异常处理。

先简单介绍一下几个知识点:

  • ARM状态下的寄存器组织

image

在系统上电时,也就是RESET后,处于SVC特权模式

  • ARM状态寄存器

image

关于状态寄存器的介绍可以参考:

http://www.cnblogs.com/pengdonglin137/p/3819546.html

Control Bits的含义:

image

Mode bits的含义:

image

  • 异常向量表

异常向量表是一段特定内存地址空间,每种ARM异常对应一个字长空间(4Bytes),正好是一条32位指令长度,当异常发生时,CPU强制将PC的值设置为当前异常对应的固定内存地址。如表3-4所示是S3C2440的异常向量表。

wps_clip_image-15397

我们一般都是用的是IRQ异常。下面的按键产生IRQ异常。

  • 异常发生的硬件操作

在异常发生后,ARM内核会自动做以下工作:
        保存执行状态:将CPSR复制到发生的异常模式下SPSR中;

(以按键中断为例,uboot环境下处于SVC模式,中断后,处于irq模式,所以这步完成的动作是:CPSR ----> SPSR_irq)
        模式切换:将CPSR模式位强制设置为与异常类型相对应的值,同时处理器进入到ARM执行模式,禁止所有IRQ中断,当进入FIQ快速中断模式时禁止FIQ中断;

(以按键中断为例,将CPSR的mode bits设置为0x12,将I位置为1,屏蔽IRQ中断,将T位置为1,进入ARM状态)
        保存返回地址:将下一条指令的地址(被打断程序)保存在LR(异常模式下LR_excep)中。

(一条指令的执行分为:取指,译码,执行三个主要阶段, CPU由于使用流水线技术,造成当前执行指令的地址应该是PC – 8(32位机一条指令四个字节),那么执行指令的下条指令应该是PC – 4。在异常发生时,CPU自动会将将PC – 4 的值保存到LR里,但是该值是否正确还要看异常类型才能决定。

快速中断请求和一般中断请求返回处理是一样的。通常处理器执行完当前指令后,查询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 条指令,因此正确返回地址可以通过下面指令算出:
SUBS    PC,LR_irq,#4        ; 一般中断
SUBS    PC,LR_fiq,#4                   ; 快速中断
注:LR_irq/LR_fiq分别为一般中断和快速中断异常模式下LR,并不存在LR_xxx寄存器,为方便读者理解加上_xxx)
        跳入异常向量表:强制设置PC的值为相应异常向量地址,跳转到异常处理程序中。

(以按键中断为例,将PC强制设置为0x18)

  • 保存执行现场 

异常处理程序最开始,要保存被打断程序的执行现场,程序的执行现场无非就是保存当前操作寄存器里的数据,可以通过下面的栈操作指令实现保存现场:
STMFD  SP_excep!,  {R0 – R12,  LR_excep}
注:LR_abt,SP_excep分别为对应异常模式下LR和SP,为方便读者理解加上_abt
需要注意的是,在跳转到异常处理程序入口时,已经切换到对应异常模式下了,因此这里的SP是异常模式下的SP_excep了,所以被打断程序现场 (寄存器数据)是保存在异常模式下的栈里,上述指令将R0~R12全部都保存到了异常模式栈,最后将修改完的被打断程序返回地址入栈保存,之所以保存该返 回地址就是将来可以通过类似:MOV  PC,  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即可。
上述操作可以一步一步实现,但是通常我们可以通过一条指令实现上述全部操作:
LDMFD  SP_excp!,  {r0-r12,  pc}^
注:SP_excep为对应异常模式下SP,^符号表示恢复SPSR_excep到CPSR

以上操作可以用下图来描述

wps_clip_image-26392

接下来分析u-boot代码。

让u-boot支持中断,首先需要在配置文件中定义几个宏,我在我的板子的配置文件include/configs/smdk2440.h中定义了如下几个宏(少定义了在编译时会报错,可以根据出错信息判断少定义了那些宏):

#define CONFIG_USE_IRQ
   #define CONFIG_STACKSIZE_IRQ  (4*1024)    /* IRQ的栈大小*/
   #define CONFIG_STACKSIZE_FIQ  (4*1024)    /* FIQ的栈大小*/

异常向量表

首先分析一下arch/arm/cpu/arm920t/start.S

   1:  .globl _start                                                     指令链接地址                      指令的运行地址     

   2:  _start:    b    start_code                                         0x33f00000                        0x00000000

   3:      ldr    pc, _undefined_instruction                              

0x33f00004                   0x00000004

   4:      ldr    pc, _software_interrupt                                 

0x33f00008 0x00000008

   5:      ldr    pc, _prefetch_abort                                     

0x33f0000c                    0x0000000c

   6:      ldr    pc, _data_abort                                         

0x33f00010 0x00000010

   7:      ldr    pc, _not_used                                           

0x33f00014  0x00000014

   8:      ldr    pc, _irq                                                

0x33f00018 0x00000018

   9:      ldr    pc, _fiq                                                

0x33f0001c  0x0000001c

  10:   

  11:  _undefined_instruction:    .word undefined_instruction             

0x33f00020 0x00000020

  12:  _software_interrupt:    .word software_interrupt                   

0x33f00024 0x00000024

  13:  _prefetch_abort:    .word prefetch_abort                           

0x33f00028 0x00000028

  14:  _data_abort:        .word data_abort                               

0x33f0002c 0x0000002c

  15:  _not_used:        .word not_used                                   

0x33f00030 0x00000030

  16:  _irq:            .word irq                                         

0x33f00034 0x00000034

  17:  _fiq:            .word fiq                                         

0x33f00038 0x00000038

  18:   

  19:      .balignl 16,0xdeadbeef

上面就是建立异常向量表,其中b start_code指令的地址对应的就是复位异常发生时要赋给PC的值,b 是一条相对跳转指令。其中,我们要关注的是IRQ异常: ldr  pc, _irq  ,这条语句的作用是将_irq中存放的数据放入pc中,可以将_irq看做变量名或者一个*p,而其中存放的是内容就是irq,即中断处理的入口地址(链接地址)。


即当发生按键动作是,pc会指向“ldr pc, _irq”所在的地址,执行这条指令(会被解释成ldr pc, [pc, #offset]),这条指令完成了将irq的地址(链接地址)赋给了pc,从而从异常向量表中直接跳入了中断处理程序(链接时确定的地址处)。


这里需要解释一下,指令的运行地址和链接地址。链接地址是在编译连接时编译器确定的地址,运行地址是实际运行这条指令时,去哪个物理地址去取这条指令,这两个地址一般相同。如果设备支持程序在Flash中运行,那么这两个地址相同,但是对于从NandFlash启动时,他们就不同了,以S3C2440为例,系统会先把NandFlash的前4KB的内容读到SRAM(sram会被映射到物理地址0开始的地方),然后运行这4KB的程序,这段4KB的程序负责把整个程序从NandFlash读到他们的链接地址处(一般在物理内存的末端,S3C2440的物理内存起始地址是0x30000000)。那么对于刚才运行在SRAM中的那4KB程序来说,他们的运行地址(sram中,起始地址0)跟链接地址(内存中,起始地址0x30000000)就不相同了。ARM架构下的异常向量表默认应该存放在0地址处,即要想使用异常,物理地址0处应该存放正确完整的异常向量表。对于从NorFlash启动,自然不是问题,此时NorFlash会被映射到物理地址0开始的地方,NorFlash的中存放的uboot开头便是异常向量表。对于从NandFlash启动时,SRAM被映射到了物理地址0开始的地方,并且前面已经说过,SRAM中的代码来自NandFlash的前4KB,这4KB也就是uboot的前4KB,自然含有异常向量表,也不会出问题,如果你故意在u-boot中通过使用命令mw破坏SRAM中的异常向量表,当发生异常时,u-boot就跑飞了。这里还要提一下被重定向到内存中的u-boot,其中也含有异常向量表,但是异常产生时系统用不到。


通用中断处理函数

通用中断处理函数在u-boot中的实现,还是在start.S中(我做了修改):


   1:      .align    5

   2:  irq:

   3:      sub    lr, lr, #4                @ the return address

   4:      ldr    sp, IRQ_STACK_START        @ the stack for irq

   5:      stmdb    sp!,    { r0-r12,lr }    @ save registers

   6:      

   7:      ldr    lr,    =int_return            @ set the return addr

   8:      ldr    pc,     =do_irq         @ call the isr

   9:  int_return:

  10:      ldmia    sp!,    { r0-r12,pc }^    @ return from interrupt

  11:      

解释:


“sub  lr, lr, #4”的原因在上面已经解释过了。


“ldr sp, IRQ_STACK_START”


这条指令中的sp已经是irq模式下的sp,即r13_irq,意思是将IRQ_STACK_START中存放的数据放入sp,即初始化irq模式下的栈指针。IRQ_STACK_START在什么地方赋值呢?一会儿分析。


“stmdb    sp!,    { r0-r12,lr }”

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>