Chinaunix首页 | 论坛 | 博客
  • 博客访问: 830865
  • 博文数量: 281
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 2770
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-02 19:45
个人简介

邮箱:zhuimengcanyang@163.com 痴爱嵌入式技术的蜗牛

文章分类
文章存档

2020年(1)

2018年(1)

2017年(56)

2016年(72)

2015年(151)

分类: 嵌入式

2017-01-04 15:58:14

12. ARM的编程模式和7种模式

ARM的基本设定
    ARM 采用的是32位架构.
    ARM 约定:
    Byte :    8 bits
    Halfword :16 bits (2 byte)
    Word :    32 bits (4 byte)

    大部分ARM core 提供:
    ARM 指令集(32-bit)
    Thumb 指令集(16-bit )
    Thumb2指令集(16 & 32bit)
    Jazelle cores 支持 Java bytecode

ARM处理器工作模式:
ARM 有7个基本工作模式:

用户模式:
    User : 非特权模式,大部分任务执行在这种模式

特权模式:
    5种异常模式:

    FIQ :   当一个高优先级(fast) 中断产生时将会进入这种模式
    IRQ :   当一个低优先级(normal) 中断产生时将会进入这种模式
    Supervisor :当复位或软中断指令执行时将会进入这种模式 (管理模式)
    Abort : 当存取异常时将会进入这种模式                 (终止模式)
    Undef : 当执行未定义指令时会进入这种模式             (未定义模式)
    1个系统模式:
        System : 使用和User模式相同寄存器集的特权模式


说明:
    除User(用户模式)是Normal(普通模式)外,其他6种都是Privilege(特权模式)。
    Privilege中除Sys模式外,其余5种为异常模式。
    各种模式的切换,可以是程序员通过代码主动切换(通过写CPSR寄存器);也可以是CPU在某些情况下自动切换。
    各种模式下权限和可以访问的寄存器不同。
    
CPU为什么设计这些模式?
    CPU是硬件,OS是软件,软件的设计要依赖硬件的特性,硬件的设计要考虑软件需要,便于实现软件特性。
    操作系统有安全级别要求,因此CPU设计多种模式是为了方便操作系统的多种角色安全等级需要。
       [注释:设计这么多模式,主要是配合操作系统来用的。在裸机程序中,不会用到这么多模式的。]

13. ARM的37个寄存器详解

总结:
    ARM共有37个寄存器,都是32位长度
    37个寄存器中30个为“通用”型,1个固定用作PC,一个固定用作CPSR,5个固定用作5种异常模式下的SPSR。
    

    
CPSR程序状态寄存器:

    条件位:
    N = Negative result from ALU
    Z = Zero result from ALU
    C = ALU operation Carried out
    V = ALU operation oVerflowed
    Q 位:
    仅ARM 5TE/J架构支持
    指示饱和状态
    J 位
    仅ARM 5TE/J架构支持
    中断禁止位:
    I  = 1: 禁止  IRQ.
    F = 1: 禁止  FIQ.
    T Bit
    仅ARM  xT架构支持
    T = 0: 处理器处于 ARM 状态
    T = 1: 处理器处于 Thumb 状态
    Mode位:
    处理器模式位

总结:
    CPSR中各个bit位表明了CPU的某些状态信息,这些信息非常重要,和后面学到的汇编指令息息相关(譬如BLE指令中的E就和CPSR中的Z标志位有关)
    CPSR中的I、F位和开中断、关中断有关
    CPSR中的mode位(bit4~bit0共5位)决定了CPU的工作模式,在uboot代码中会使用汇编进行设置。
    
PC(r15)程序控制寄存器:
    PC(Program control register)为程序指针,PC指向哪里,CPU就会执行哪条指令(所以程序跳转时就是把目标地址代码放到PC中)
    整个CPU中只有一个PC(CPSR也只有一个,但SPSR有5个)。

14. ARM的异常处理方式简单介绍

什么是异常:
    正常工作之外的流程都叫异常
    异常会打断正在执行的工作,并且一般我们希望异常处理完成后继续回来执行原来的工作
    中断是异常的一种
    
异常向量表:
    所有的CPU都有异常向量表,这是CPU设计时就设定好的,是硬件决定的。
    当异常发生时,CPU会自动动作(PC跳转到异常向量处处理异常,有时伴有一些辅助动作)
    异常向量表是硬件向软件提供的处理异常的支持。
    
ARM的异常处理机制:
    当异常产生时, ARM core:
    拷贝 CPSR 到 SPSR_
    设置适当的 CPSR 位:
    改变处理器状态进入 ARM 态
    改变处理器模式进入相应的异常模式
    设置中断禁止位禁止相应中断 (如果需要)
    保存返回地址到 LR_
    设置 PC 为相应的异常向量
    返回时, 异常处理需要:
    从 SPSR_恢复CPSR
    从LR_恢复PC
    Note:这些操作只能在 ARM 态执行.

总结:
    异常处理中有一些是硬件自动做的,有一些是程序员需要自己做的。需要搞清楚哪些是需要自己做的,才知道如何写代码。
    以上说的是CPU设计时提供的异常向量表,一般成为一级向量表。有些CPU为了支持多个中断,还会提供二级中断向量表,
    处理思路类似于这里说的一级中断向量表。

15. ARM汇编指令集1_2

指令与伪指令:
    (汇编)指令是CPU机器指令的助记符,经过编译后会得到一串10组成的机器码,可以由CPU读取执行。
    (汇编)伪指令本质上不是指令(只是和指令一起写在代码中),它是编译器环境提供的,目的是用来指导编译过程,
    经过编译后伪指令最终不会生成机器码。
    
两种不同风格的ARM指令
    ARM官方的ARM汇编风格:指令一般用大写、Windows中IDE开发环境(如ADS、MDK等)常用。如: LDR R0, [R1]
    GNU风格的ARM汇编:指令一般用小写字母、linux中常用。如:ldr r0, [r1]

ARM汇编特点1:LDR/STR架构
    ARM采用RISC架构,CPU本身不能直接读取内存,而需要先将内存中内容加载入CPU中通用寄存器中才能被CPU处理。
    ldr(load register)指令将内存内容加载入通用寄存器。
    str(store register)指令将寄存器内容存入内存空间中。
    ldr/str组合用来实现 ARM CPU和内存数据交换
    
ARM汇编特点2:8种寻址方式
    寄存器寻址            mov r1, r2
    立即寻址                mov r0, #0xFF00
    寄存器移位寻址        mov r0, r1, lsl #3
    寄存器间接寻址        ldr r1, [r2]
    基址变址寻址            ldr r1, [r2, #4]
    多寄存器寻址            ldmia r1!, {r2-r7, r12}
    堆栈寻址                stmfd sp!, {r2-r7, lr}

    相对寻址             beq flag                  
                                            flag:

ARM汇编特点3:指令后缀

    同一指令经常附带不同后缀,变成不同的指令。经常使用的后缀有:
    B(byte)功能不变,操作长度变为8位
    H(half word)功能不变,长度变为16位
    S(signed)功能不变,操作数变为有符号
    如 ldr ldrb ldrh ldrsb ldrsh
    S(S标志)功能不变,影响CPSR标志位
    如 mov和movs        movs r0, #0
    

ARM汇编特点4:条件执行后缀

    

ARM汇编特点5:多级指令流水线

    为增加处理器指令流的速度,ARM使用多级流水线.(S5PV210使用13级流水线,ARM11为8级)
    允许多个操作同时处理,而非顺序执行。
    PC指向正被取指的指令,而非正在执行的指令
    PC(fetch) = PC(excute) + 8

16. ARM汇编指令集3_数据传输和跳转指令详解

常用ARM指令1:数据处理指令
    数据传输指令    mov mvn
    算术指令        add sub rsb adc sbc rsc
    逻辑指令        and orr eor bic
    比较指令        cmp cmn tst teq
    乘法指令        mvl mla umull umlal smull smlal
    前导零计数        clz

常用ARM指令2:cpsr访问指令
    mrs & msr
    mrs用来读psr,msr用来写psr
    CPSR寄存器比较特殊,需要专门的指令访问,这就是mrs和msr。

常用ARM指令3:跳转(分支)指令
    b & bl & bx

    b 直接跳转(就没打开算返回)
    bl branch and link,跳转前把返回地址放入lr中,以便返回,以便用于函数调用
    bx跳转同时切换到ARM模式,一般用于异常处理的跳转。

    
常用ARM指令4:访存指令
    ldr/str &    ldm/stm & swp

    单个字/半字/字节访问 ldr/str
    多字批量访问  ldm/stm
    swp r1, r2, [r0]
    swp r1, r1, [r0]
    
    
ARM汇编中的立即数
    合法立即数与非法立即数

    ARM指令都是32位,除了指令标记和操作标记外,本身只能附带很少位数的立即数。因此立即数有合法和非法之分。
    [注释:合法立即数表示的是经过任意位数的移位后非零部分可以用8位表示的即为合法立即数]

17. 常用ARM指令5:软中断指令

    swi(software interrupt)

    软中断指令用来实现操作系统中系统调用
    

18. ARM汇编指令集4_协处理器和协处理器指令详解

协处理器cp15操作指令:
    mcr & mrc

    mrc用于读取CP15中的寄存器
    mcr用于写入CP15中的寄存器
    
什么是协处理器
    SoC内部另一处理核心,协助主CPU实现某些功能,被主CPU调用执行一定任务。
    ARM设计上支持多达16个协处理器,但是一般SoC只实现其中的CP15.(cp:coprocessor)
    协处理器和MMU、cache、TLB等处理有关,功能上和操作系统的虚拟地址映射、cache管理等有关。

MRC & MCR的使用方法
    mcr{}   p15, , , , , {}
    opcode_1:对于cp15永远为0
    Rd:ARM的普通寄存器
    Crn:cp15的寄存器,合法值是c0~c15
    Crm:cp15的寄存器,一般均设为c0
    opcode_2:一般省略或为0

举例(来自于uboot)
    mrc p15, 0, r0, c1, c0, 0
    orr    r0, r0, #1
    mcr p15, 0, r0, c1, c0, 0

    其他见uboot源码start.S中相关代码

协处理器学习要点
    不必深究,将uboot中和kernel中起始代码中的一般操作搞明白即可。
    只看一般用法,不详细区分参数细节,否则会陷入很多复杂未知中。
    关键在于理解,而不在于记住。

19. ARM汇编指令集5_ldm/stm与栈的处理

为什么需要多寄存器访问指令
    ldr/str每周期只能访问4字节内存,如果需要批量读取、写入内存时太慢,解决方案是stm/ldm
    ldm(load register mutiple)
    stm(store register mutiple)

举例(uboot start.S 537行)
    stmia    sp, {r0 - r12}
    将r0存入sp指向的内存处(假设为0x30001000);然后地址+4(即指向0x30001004),将r1存入该地址;
    然后地址再+4(指向0x30001008),将r2存入该地址······直到r12内容放入(0x3001030),指令完成。
    一个访存周期同时完成13个寄存器的读写
8种后缀
    ia(increase after)先传输,再地址+4
    ib(increase before)先地址+4,再传输
    da(decrease after)先传输,再地址-4
    db(decrease before)先地址-4,再传输
    fd(full decrease)满递减堆栈
    ed(empty decrease)空递减堆栈
    fa(·······) 满递增堆栈
    ea(·······)空递增堆栈
    
四种栈
    空栈:栈指针指向空位,每次存入时可以直接存入然后栈指针移动一格;而取出时需要先移动一格才能取出
    满栈:栈指针指向栈中最后一格数据,每次存入时需要先移动栈指针一格再存入;取出时可以直接取出,然后再移动栈指针
    增栈:栈指针移动时向地址增加的方向移动的栈
    减栈:栈指针移动时向地址减小的方向移动的栈

    
!的作用
    ldmia    r0, {r2 - r3}
    ldmia    r0!, {r2 - r3}

    感叹号的作用就是r0的值在ldm过程中发生的增加或者减少最后写回到r0去,也就是说ldm时会改变r0的值。

^的作用
    ldmfd    sp!, {r0 - r6, pc}
    ldmfd    sp!, {r0 - r6, pc}^

    ^的作用:在目标寄存器中有pc时,会同时将spsr写入到cpsr,一般用于从异常模式返回。

总结    
    批量读取或写入内存时要用ldm/stm指令
    各种后缀以理解为主,不需记忆,最常见的是stmia和stmfd
    谨记:操作栈时使用相同的后缀就不会出错,不管是满栈还是空栈、增栈还是减栈
        

20. ARM汇编伪指令

伪指令的意义:
    伪指令不是指令,伪指令和指令的根本区别是经过编译后会不会生成机器码。
    伪指令的意义在于指导编译过程。
    伪指令是和具体的编译器相关的,我们使用gnu工具链,因此学习gnu环境下的汇编伪指令。

gnu汇编中的一些符号
    @ 用来做注释。可以在行首也可以在代码后面同一行直接跟,和C语言中//类似
    # 做注释,一般放在行首,表示这一行都是注释而不是代码。
    :以冒号结尾的是标号
    .  点号在gnu汇编中表示当前指令的地址
    # 立即数前面要加#或$,表示这是个立即数

常用gnu伪指令
    .global _start        @ 给_start外部链接属性
    .section .text        @ 指定当前段为代码段
    .ascii .byte .short .long .word
    .quad .float .string @ 定义数据
    .align 4            @ 以16字节对齐
    .balignl 16 0xabcdefgh     @ 16字节对齐填充
    .equ            @ 类似于C中宏定义

偶尔会用到的gnu伪指令
    .end            @标识文件结束
    .include            @ 头文件包含
    .arm / .code32    @声明以下为arm指令
    .thumb / .code16    @声明以下为thubm指令

最重要的几个伪指令
    ldr        大范围的地址加载指令
    adr        小范围的地址加载指令
    adrl    中等范围的地址加载指令
    nop        空操作

    ARM中有一个ldr指令,还有一个ldr伪指令
    一般都使用ldr伪指令而不用ldr指令,
    问题:怎么判断是ldr指令,还是ldr伪指令?
    回答:LDR伪指令只有一个操作数且操作数前有一个“=”前缀符号。故这个前缀符号“=”就是区分LDR伪指令的关键。

adr与ldr [注释:最重要的指令]
    adr编译时会被1条sub或add指令替代,而ldr编译时会被一条mov指令替代或者文字池方式处理;
    adr总是以PC为基准来表示地址,因此指令本身和运行地址有关,可以用来检测程序当前的运行地址在哪里
    ldr加载的地址和链接时给定的地址有关,由链接脚本决定。

阅读(991) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~