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

来源:互联网 发布:php 获取图片拍摄地点 编辑:程序博客网 时间:2024/05/22 03:48
回顾:
面试题:谈谈对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按键的中断处理函数,然后
  一注册即可,将来就等着被调用!