(六)ARM体系结构与寻址方式

本专栏总结王利涛《C语言嵌入式Linux高级编程》第二期课程

一、ARM体系结构与汇编语言

1)汇编语言在嵌入式开发的需求

  • 嵌入式软件优化(c语言钟内嵌汇编) ,目的是榨干硬件的最后一点性能,同时,汇编也能用于编写启动代码。

2)学习汇编有什么好处?

  • 理解CPU体系结构、内部工作原理。
  • 从底层深刻理解C语言:函数变量引用、参数传递、中断。
  • 嵌入式软件、内核调式的基础(如OOPS信息)。

3)如何学习?

  • 推荐:看wik,看博客了解大概,然后看芯片用户手册,接着看ARM指令集手册(可当作字典)。
  • ARM资料下载:infocenter.arm.com
  • 英语的要求:①计算机行业英语;②IC行业英语;③电子行业英语。

二、ARM体系结构

1)精简指令计算机结构(RISC)

  • load/store体系结构,只能处理寄存器的数据。比如,要处理内存某一地址的处理,首先将内存数据load到cpu的寄存器,处理完后再store到内存里;
  • 它是固定指令长度、单周期指令;
  • 采用流水线方式;
  • 倾向于使用更多的寄存器来存储数据。

2)ARM指令集跟RISC的差异

  • 桶形移位寄存器:单周期内完成数据的各种移位操作;
  • 并不是所有ARM指令都是单周期;
  • Thumb指令集(16位):32位ARM指令集的压缩形式,提高了代码密度。(比如原本代码要100行,现在只需执行50行,CPU的功耗将会大大减少);
  • 条件执行:减少了分支指令数目,提高代码密度;
  • 增加DSP指令(嵌入式工程师不必理会)。

三、流水线

在这里插入图片描述

怎么理解ARM的三级流水线?

假如,把三级流水线分别看作三个人。小A负责取指工作,小B负责译码工作,小C负责执行指令的工作。一条指令要经过三个阶段,取指-译码-执行。
T1时,小A取到MOV的指令;T2时,小A取到ADD指令;T3时,小A取到SUB指令;小A就是专门负责取指令,之后的事就别找他啦。
T1时,小B还能等待小A的指令;T2时,小B对MOV指令进行译码;T3时,小B对ADD指令进行译码;T4时,小B对SUB指令进行译码;所以,小B就是用译码的,别的事她都不会。
T1、T2时,小C闲着没事做; T3时,执行MOV指令;T4时,执行ADD指令;T5时,执行SUB指令。

四、ARM工作模式

在这里插入图片描述
详细内容,可查看文章https://www.cnblogs.com/yygsj/p/4869240.html.

五、ARM寄存器

1)通用寄存器

  • R0-R12:其中R0-R0 用来传递函数参数。我们都知道,函数传参一般通过堆栈方式传参,而ARM为了提高效率,前四个寄存器用作函数的参数,如果传入的函数大于4,多出的参数以然采用堆栈的方式,所以,只传入4个或以下参数个数,运行时最快的。
  • R4-R11:用来保存子程序的局部变量。
  • R12:常用作调用运行过程中的临时寄存器。
  • 程序计数器R15(pc): 不能用于其它用途。它是取指令地址的指针,其值等于当前内存指令地址+8 (三级流水线,当cpu执行指令的同时,也在取下一条指令,相差两个字,即8个字节);

2)状态寄存器

  • CPSR: 用于保存当前处理器的状态,比如,ARM的工作模式将要发生切换时,则会把当前CPU状态保存在CPSR。

3)各模式下独立的寄存器

  • 堆栈寄存器:R13(SP), 在子程序中R13不能用作其它用途;
  • 链接寄存器:R14(LR),保存子程序的返回地址;
  • FIQ工作模式下,有自己的R8~R12寄存器;
  • SPSR:用于保存在COSR的工作状态,每个模式下都有一个寄存器。
    在这里插入图片描述

六、ARM寻址方式

1)寄存器寻址

  • MOV R1 , R2 ; 将R2的值搬入R1
  • SUB R0, R1, R2; R1-R2结果保存到R0,操作数的值在寄存器中,指令直接取值。

2)立即寻址

  • SUBS R0, R0 ,#1
  • MOV R0, #0XFF00
  • 立即数以# 为前缀,0x表示16进制。

3)寄存器偏移寻址

  • MOV R0, R2, LSL, #3; R2左移3位,保存到R0
  • 逻辑位移与算术位移:逻辑位移,空缺处补0;算术位移,保证符号位不变,右移空缺处用符号位填补,左移补0。
  • 常用的移位操作
    • LSL: 逻辑左移
    • LSR: 逻辑右移
    • ASR/ASL:算术右移/左移
    • ROR/ROL:循环左移/右移
    • RRX:带扩展的循环右移

4)寄存器间接寻址

  • 寄存器为操作数的地址指针;
  • C指针操作:寄存器间接寻址;
  • LDR R1, [R2] ; 将R2中的值作为内存地址,取出该内存地址中的数据送到R1;
  • SWP R1, R1, [R2]; 将R2中的值作为内存地址,取出此地址中的数值与R1进行交换,保存到R1。

5)基础寻址

  • 基础寻址

    • (1)寄存器 中的值与给出的偏移量相加,得到实际地址;
    • (2)然后取出此地址中的值;
    • (3)常用于查表、数组操作、功能部件寄存器访问;
    • (4)分为前索引和后索引基地址寻址;
  • STR R1, [R0, #-2] : 先将R0中的值减2作为内存地址,再把R1写入此地址;

  • STR R1, [R0], #2 : 先将R0中的值作为内存地址,把R1中的值写入此地址,然后R0加2;

  • LDR R2, [R3, #0X03] : 将R3中的数值加0x03作为内存地址,取出此地址中的数值,送入R2。

6)多寄存器寻址

  • 一次可以传送多个寄存器的值
  • LDMIA R1!, {R2 - R7, R12}:将R1地址单元中的数据读出到R2-R7和R12, 然后R1自动加1;
  • STMIA R0!, {R3-R6, R19}:将R3-R6中的数据保存到R0指向地址,然后R0自动加1。
  • 规则
    • (1)寄存器到列表中顺序不重要,最终编号小的寄存器会与内存低地址相对应;
    • (2)连续的寄存器可用横杆表示连续,寄存器之间可以用逗号隔开;
    • (3)LDM/STM可以接AI, IB, DA, DB, 分别表示increase after, increase before, decrease after, decrease before。

7)堆栈寻址

  • ARM中栈的操作

    • (1)子程序局部变量存储,参数传递要通过堆栈完成;
    • (2)ARM指令集中没有出栈和入栈的专门指令;
    • (3)ARM中栈的操作是通过STM/LDM和栈指针sp配合操作完成;
    • (4)ARM采用栈的类型是 满递减堆栈
  • 栈的分类

    • (1)递增堆栈A: 向上生长,向高地址方向增长;
    • (2)减低堆栈D:向下生长,向低地址方向增长;
    • (3)满堆栈 F :sp栈指针指向栈顶元素;
    • (4)空堆栈E :sp栈指针指向下一次要入栈元素的地址。
  • 举例

    • (1)STMFD SP!, {R1-R7, LR}; 将R1-R7,LR入栈。满递减入栈。
    • (2)LDMFD SP!, {R1-R7,LR}; 数据出栈,弹出到R1-R7和LR,满递减出栈。
    • (3)STMFD 和 LDMFD 可以配对使用,完成堆栈的入栈和出栈操作,方便!

8)相对地址

  • 寻址方式
    • (1)由PC作为基地址,指令中的地址码段作为偏移量;
    • (2)两者相加后得到的地址即为操作数的有效地址;
    • (3)常见指令:B, BL, adr
  • 举例
	 B LOOP
	 ...
LOOP MOV R0, #1
	 MOV R1, R0
	 ...

此时,B LOOP指令等价于 ADD PC,PC #0FFSET,其中OFFSET为B LOOP指令地址与LOOP标号地址之间的偏移,B的前后跳转范围:[0,32M]

  • 采用相对寻址的程序,无论代码在哪个地方,通通都能运行,因为它与内存无关,它只需计算一下代码的相对位置,缺点是每次都需要花时间去计算。在gcc-fPIC 生成与位置无关的代码,编译器就会翻译成类似上面的代码。


猜你喜欢

转载自blog.csdn.net/weixin_38956024/article/details/107406090