您的位置:首页 > 其它

day11 ARM混合调用案例、ARM核 异常处理流程、软件处理异常

2017-06-16 17:20 1276 查看
回顾:

面试题:谈谈对ARM处理器的认识

1.常见的处理器

2.ARM定义

3.ARM版本

4.ARM流水线

5.ARM工作模式

6.ARM工作状态

7.ARM寄存器

8.ARM异常

9.ARM异常处理流程

10.ARM指令之分支跳转

11.ARM指令之数据处理指令

   数据传送指令

   算数运算指令

   位运算指令

   比较测试指令

   ARM核内部玩

12.ARM指令之加载存储指令:ldr/str

   ARM核和外设数据通信

13.ARM指令之栈操作指令:push/pop(满减栈)

14.ARM指令之状态寄存器操作指令:mrs/msr

15.ARM伪指令:adr/ldr

16.ARM伪操作:各种...

17.ARM混合调用之函数传递参数的两种方式方法

   默认寄存器:r0/r1/r2/r3,返回值用r0

   强制使用栈:asmlinkage

18.ARM混合调用之C调用汇编的变量或者函数

   参考案例:

   mkdir /opt/arm/day11/1.0 -p

   cd /opt/arm/day11/1.0

   vim add.s 添加如下内容

   .text

   .arm

   .global add @声明全局标签add,类似C的一个全局函数

   .global yy  @声明全局标签yy,类似C的全局变量

    

   @类似C语言函数定义

   @调用:ret=add(100,200)对应的汇编:r0=100,r1=200

   @调用时使用的跳转指令为bl add

   @ret=300=r0

   add:

           add r0,r0,r1 @r0=r0+r1=100+200=300

           mov pc, lr @实现返回

           

   @类似C语言变量定义

   yy:

         .word 0x1234

    

   .end

   保存退出

    

   vim main.c 添加如下内容

   extern int add(int, int);//声明

   extern int yy; //声明

   int main(void)

   {

           int ret;

           ret = add(100, 200); //对应的汇编为bl add

                                                    //r0=100,r1=200

                                                    //ret=r0

         ret = yy;

   }

    

   arm-cortex_a9-linux-gnueabi-as -g -o add.o add.s

   arm-cortex_a9-linux-gnueabi-gcc -nostdlib -g -c main.c -o main.o

   arm-cortex_a9-linux-gnueabi-ld -nostdlib  

               -nostartfiles -o main main.o add.o -emain

    

   测试:

   qemu-arm -g 1234 main  

   arm-cortex_a9-linux-gnueabi-gdb main

   (gdb)target remote localhost:1234

   (gdb)l

   (gdb)b  

   (gdb)s

   (gdb)info reg //查看传递的参数100,200

   ...

   (gdb)p ret //查看变量ret的值,默认是10进制显示

   (gdb)p /x ret //查看变量ret的值,按16进制显示

    

   总结:C调用汇编注意事项

   1.汇编的"变量"和"函数"要用.global进行全局化

   2.汇编的函数一定要用mov pc, lr进行返回

   3.C在调用之前,记得用extern进行声明

 

19.ARM混合调用之汇编调用C的变量或者函数  

   参考案例:

   mkdir /opt/arm/day11/2.0

   cd /opt/arm/day11/2.0

   vim add.c 添加如下内容

   int g_data = 10000; //全局变量

   //全局函数

   int add(int a, int b)

   {

        return a+b;

   }

   保存退出

    

   vim main.s 添加如下内容

   .text

   .arm

   .global main @程序入口函数

   .extern add  @声明

   .extern g_data @声明  

    

   main:

           mov r0, #100 @传递的第一个参数,给add函数的a

           mov r1, #200 @传递的第二个参数,给add函数的b

           bl add       @调用add函数

       

      ldr r2, =g_data @将C的全局变量g_data的首地址给r2

                                    @r2=&g_data

      ldr r3, [r2]    @r3=10000

       

      b .

   .end

   保存退出,编译,测试

   总结:汇编调用C的注意事项

   1.C只要保证被调用的变量和函数是全局即可

     千万不能用static修改

   2.汇编调用之前记得要用.extern进行声明

   3.汇编调用C的函数用bl指令

 

20.ARM核异常处理的流程

   20.1.ARM异常的总体流程

        以UART控制器给CPU核发送IRQ中断信号为例

        1.UART控制器接收到数据首先UART控制器

          给中断控制器发送中断电信号

        2.中断控制器对此中断信号进行一番的判断

          最终中断控制器给CPU核发送IRQ中断信号

          当然也可以给CPU核发送FIQ中断信号

        3.CPU核一旦接收到IRQ中断信号,立刻触发了

          IRQ中断异常,CPU核立马要处理IRQ中断异常

        4.CPU核首先硬件上自动做:

          备份CPSR到SPSR_IRQ

          设置CPSR的:

                  MODE=XXXXX

                  T=0

                  F=1

                  I=1  

          保存返回地址LR_IRQ=PC-4

          设置PC=0x18,让CPU核跑到0x18地址去运行

          0x18地址可以放置自己的软件程序

          至此开启了软件处理IRQ异常的流程

        5.软件处理IRQ异常的流程:

            如何编程实现完全由程序员"自行决定"

        6.软件处理IRQ异常以后,最终CPU核要返回到

          原先被打断的地方继续执行,返回需要软件

          实现,只需做两件事:

          CPSR=SPSR_IRQ 原先被打断的程序的状态恢复

          PC=LR_IRQ 返回到原先被打断的地方继续执行

           

        7.总结:对于处理异常,软件只负责实现第5和6两个步骤即可

          第5步做的事就是在异常对应的入口地址放置添加

          自己的软件代码(根据用户需求来定)

          第6步做的是就是CPSR=SPSR_IRQ和PC=LR_IRQ

           

    20.1.ARM核异常返回的软件编程代码实现(针对第6步):

        1.软中断异常返回指令:

            例如:

                ...

                sub r1, r1, #4

                swi  

                add r0, r0, #1

                bic r0, r0, #0xf

                ...

          分析过程:

          1.当CPU执行swi指令时,触发软中断异常

          2.CPU核硬件上做:

            备份

            设置MODE/T/I/F

            lr_swi=pc-4(pc=bic)

                  =add

            设置pc=0x08 //CPU核跑到0x08地址去运行

                                  //正式开启了软件处理软中断

                                  异常的流程

          3.软件处理软中断异常最终返回,CPU核只需返回到

            add指令继续运行,对应的代码:

            movs pc, lr  

                此代码实现两个功能:

                  cpsr=spsr_swi

                  pc=lr_swi=add

       

      2.未定义指令异常

        例如:

            ...

            sub r0, r0, #1

            lisi r0, r1

            add r0, r0, #1

            bic r0, r0, #0xf

            ...

        分析:

        1.当CPU执行lisi指令时,lisi指令CPU不识别

          触发未定义指令异常

        2.ARM硬件上做

          备份

          设置

          lr_undef=pc-4(pc=bic)

                          =add

          设置pc=0x04 //CPU核跑到0x04地址去运行

        3.软件处理未定义指令异常最终返回,只需返回

          到add继续执行即可,对应的软件代码:

              movs pc, lr

      

     3.FIQ/IRQ中断异常

       例如:

               ...

               sub r0, r0, #1

               add r0, r0, #1

               bic r0, r0, #0xf

               cmp r0, r1

               ...

       分析:

       1.当CPU执行sub指令时,突然外设给CPU核

         发送了一个IRQ或者FIQ中断信号,立马

         触发一个FIQ或者IRQ中断异常

       2.CPU核硬件上立马执行:

         备份

         设置

         lr_irq/lr_fiq=pc-4(pc=cmp)   

                                   =bic

         设置pc=0x1c/0x18 //软件继续处理FIQ、irq中断长

       3.软件处理中断返回,返回到add执行继续执行

         对应的代码:

         subs pc, lr, #4

           做:

           cpsr=spsr_irq/spsr_fiq

           pc=lr-4=bic-4=add

            

     4.预取指令异常

       例如:

           ...

           sub r1, r1, #1

           add r0, r0, #1

           bic r0, r0, #0xf

           cmp r0, r1

           ...

       分析:

       1.当CPU进行对sub取指时,压根就没有取到

         当CPU执行时,没有合法的指令,就会触发

         一个预取指令异常

       2.CPU核硬件做:

         备份

         设置

         lr_instr=pc-4(pc=bic)

                          =add

         设置pc=0x0c  

       3.软件处理预期指令异常返回,理论上只需返回到  

         add指令继续执行,但是ARM公司建议取指失败没关系

         建议再来一次,所以还是需要跑到sub指令运行

         返回的代码:

         subs pc, lr, #4

          

     5.数据处理异常

       例如:

       ...

       ldr r1, [r0]

       sub r1, r1, #0

       add r0, r0, #1

       bic r0, r0, #0x0f

       cmp r0, r1

       ...

       分析:

       1.当CPU执行ldr以后,还要进行访存M,此时此刻

         发生了CPU访存失败,就是没有找到要访问

         的外设地址,发生了数据处理异常

       2.CPU核硬件上做:

         备份

         设置

         lr_data=pc-4(pc=bic)

                         =add

         设置pc=0x10 //开启软件处理异常

       3.软件处理异常完毕,理论上返回到sub指令

         去运行,但是ARM公司建议,随着这次访存失败了

         建议再来一次,只需在返回到ldr运行,返回代码:

         subs pc, lr, #8

         

       切记结论:

       1.软中断异常返回代码:movs pc, lr

       2.未定义指令异常返回代码:movs pc, lr

       3.FIQ/IRQ异常返回代码:subs pc, lr, #4

       4.预取指令异常返回代码:subs pc, lr,#4

       5.数据处理异常返回代码:subs pc, lr,#8

 

20.2.软件处理异常的流程

     1.明确:异常发生,异常的整个处理分两部分

       硬件部分(ARM核硬件自动完成)

               备份CPSR

               设置CPSR

               保存返回地址

               设置PC为异常对应的入口地址,至此切换到软件部分

       软件部分(程序员实现)

               编写异常向量表的代码

               编写保护现场的代码

               根据用户需求编写异常的具体处理代码

               编写恢复现场的代码

      

     2.明确相关概念

       异常向量表:本质上就是在异常的入口地址

                   放置添加自己的处理函数,处理

                   代码,参见vetor.bmp

        

       保护现场:保护被打断的进程使用的寄存器数据

                 只需将被打断的进程使用的寄存器中的

                 数据进行压栈处理即可!

        

       恢复现场:异常处理完毕,CPU要返回到原先被打断

                 的进程地方继续执行,执行之前先从

                 栈中把原先保存的数据再进行恢复           

             

     3.实战:以下位机的按键为例,掌握IRQ中断异常的

             整个处理流程,按下按键,下位机通过UART

             给上位机发送一句打印信息即可

       3.1.分析用户需求

                按下按键,下位机给上位机发送打印信息

       3.2.掌握按键的硬件信息

                1.打开底板原理图,以SW6按键为例

                  SW6按键连接到S5P6818的GPIOA28引脚

                  SW6按键不进行按下操作,GPIOA28为高电平

                  SW6按键按下操作,GPIOA28为低电平

                  GPIOA28引脚的状态是外设按键给S5P6818       

            

           2.画出一个简要的硬件连接示意图

            

           3.分析软件操作的方式方法

             由于按键的操作是随机的,软件又要确保

             能够获取到GPIOA28的正确的电平状态,首先

             想到采用轮训方式,CPU不干其他事情,一直

             判断GPIOA28的电平是否变成了低电平,但是这么

             做没问题,问题在于相当耗费CPU资源,大大降低了

             CPU的利用率,对于这种外设,还要想到中断方式

             来判断GPIOA28的电平是否有变化

            

           4.明确:按键中断的处理流程

             4.0.CPU核正在执行某个进程

             4.1.突然按键SW6按下,按键对应的GPIOA28

                 引脚由高电平变成低电平,产生

                 一个下降沿电信号

             4.2.此下降沿电信号跑到中断控制器

                 中断控制器立马硬件上要对此

                 电信号进行一番的判断

             4.3.中断控制器首先判断GPIOA28引脚

                 中断功能是否使能  

                 假如此引脚的中断功能禁止,中断控制器

                 直接将此下降沿电信号丢弃

                 假如此引脚的中断功能使能,中断控制器

                 继续对此电信号进行判断

             4.4.中断控制然后判断此下降沿电信号是否

                 是有效的中断触发信号

                 明确:中断的触发电信号的方式有五种

                        高电平触发

                        低电平触发

                        下降沿触发

                        上升沿触发

                        双边沿触发(上升+下降都可以)、

                 假如中断控制器不识别下降沿中断电信号,

                 中断控制器同样直接丢弃

                 假如中断控制器认为此下降沿中断电信号

                 为有效的中断触发信号,中断控制器继续判断

             4.5.假如下降沿电信号为有效的中断信号,中断控制器

                 继续判断此中断信号的优先级

                 中断控制器判断当前CPU核是否有处理的

                 中断,如果发现CPU核处理的中断优先级高于

                 当前按键SW6的中断优先级,中断控制器同样

                 直接丢弃SW6对应的下降沿中断信号

                 如果发现CPU核没有处理中断或者处理的

                 中断的优先级低于SW6按键的中断优先级

                 中断控制器继续判断

               

              4.6.中断控制器然后判断SW6按键中断信号

                   到底给哪个CPU核发送中断电信号

                   给CPU0单独发?还是给全部的CPU核发送呢?

                    

              4.7.中断控制器最后判断SW6按键中断信号

                 到底以什么样的方式发送给CPU核

                 以IRQ形式呢?还是以FIQ形式呢?

                 只能选择其中一种方式,最终中断控制器

                 会给CPU核发送一个IRQ或者FIQ的中断信号

              4.8.CPU核一旦接收到了IRQ或者FIQ中断

                  信号,立马触发一个IRQ或者FIQ中断异常

                  CPU核立马硬件上做:

                  备份

                  设置

                  保存

                  设置PC=0x18/0x1c

                  软件处理中断

              4.10.软件处理中断

                      先写好异常向量表的代码

                      一旦异常发生,CPU核就去到0x18

                      或者0x1c地址去执行对应的处理代码

                      先保护现场,就是将被打断进程使用的

                      寄存器中的数据进行压栈保护,为了放置不被破坏

                      紧接着向上位机发送字符串

                              uart_puts("hello\n");

                    最后恢复现场,恢复到原先被打断进程的位置继续执行

                    别忘记从栈中将原先保存的进程数据进行恢复到寄存器中

           

           5.打开S5P6818芯片手册P448,掌握中断控制器

             极其内部特殊功能寄存器的硬件操作特性

             1.每一个中断可以配置为GROUP0也可以配置为GROUP1

             2.中断类型:

               SGI:软中断,

                   中断编号:0~15

               PPI:私有中断

                   中断编号:16~31

               SPI:共享中断

                   中断编号:32~1019

                        GPIOA28属于共享中断        

             3.中断使能配置

               GICD_ISENABLER0[15:0]:设置SGI

               GICD_ISENABLER0[31:16]:设置PPI

               从GICD_ISENABLER1这个寄存器开始

               要设置SPI共享中断的使能或者禁止

               并且GPIOA组的中断编号号为53

               请问:GPIOA28对应的使能位是哪位?

               明确:只要把GPIOA组使能,GPIOA28也就使能

               GPIOA28属SGI,GPIOA也属于SGI,编号为53

               而GICD_ISENABLER1对应的编号的个数为32

               显然不够,往后推到GICD_ISENABLER2的bit[21]         

       3.硬件信息掌控完毕,编写ARM裸板程序,完成按键

         中断的软件处理

         ftp://ARM/day11/shell-vm-irq.rar  

         理顺代码的执行流程:

         tftp 48000000 shell.bin

         go 48000000 //CPU核跑到0x48000000运行(F->D->E->M->W)

             -> b     reset

                           1.设置CPU为SVC管理模式

                           2.修改异常向量表的起始入口地址

                             由0x00为0x48000000   

                           3.初始化各个模式下的栈

                             注意:满减栈     

                      -> bl main

                                   xxx_init();//各种硬件初始化

                                   //CPU核陷入一个死循环运行

                                   while(1) {

                                          ...

                                   }

         

        突然有人按下SW6按键,最终千辛万苦中断信号

        跑到了CPU核,CPU核各种处理,CPU核从

        main函数中的while(1)中毫无条件的跑到

        0x480000018地址(之前在go 480000000的时候

        已经进行了修改)去运行

        问:0x480000018对应的代码是:

        答:ldr    pc, _irq  

                        1.保护现场

                        2.根据用户需求打印信息

                        3.恢复现场

                        

总结:实际开发,ARM裸板编程对于中断要完成的工作:

1.明确:一个完整的中断处理编码工作如下:

  1.编写中断控制器的初始化代码

   

  2.编写异常向量表

  b     reset  /*0x48000000*/

    ldr    pc, _undefined_instruction /*0x48000004*/

    ldr    pc, _software_interrupt /*0x48000008*/

    ldr    pc, _prefetch_abort /*0x4800000c*/

    ldr    pc, _data_abort /*0x48000010*/

    ldr    pc, _not_used /*0x48000014*/

    ldr    pc, _irq /*0x48000018*/

    ldr    pc, _fiq /*0x4800001c*/

    

    3.编写保护现场的代码

    push...

    

    4.编写中断处理函数,根据用户需求完成代码

    bl do_irq

    

    5.编写恢复现场的代码

    pop

    

2.但是将来不管是ARM裸板开发还是在linux系统下

  开发,程序员只需完成第4步即可,其余部分都是

  ARM公司和芯片厂家完成!

  很简单只需编写一个SW6按键的中断处理函数,然后

  一注册即可,将来就等着被调用!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: