【物联网】ARM核常用指令(详解):数据传送、计算、位运算、比较、跳转、内存访问、CPSR/SPSR、流水线及伪指令

文章目录


指令格式(重点)

1. 立即数

一个常数,该常数必须对应8位位图,即一个8位的常数通过,循环右移偶数位得到该数,该数
数为合法立即数。

在指令中表示方法:#数字,例如:#100

快速判定是否是合法立即数:

  • 首先将这个数转换为32bit的16进制形式,例如218=0xDA=0x000000DA
  • 除零外,仅有一位数为合法立即数
  • 除零外,仅有二位数,并且相邻(包括首尾,如0x1000000A)的为合法立即数。
  • 除零外,仅有三位数,并且相邻(包括中间有0相间,例如0x10800000,包括首尾相邻
    如:0x14000003),这三位数中,最高位取值仅能为1、2、3,最低位取值仅能为4、8、C
    中间位0x0~0xF。
    这种组合的为合法立即数。

2. 寄存器位移

将寄存器值读取之后,进行移位运算后,作为操作数2参与运算。支持的移位方式如下:

  • LSL(Logical shift Left)逻辑右移
  • LSR(Logical shift Right)逻辑左移
  • ASR(Arithmetic shift Right)算术右移
c 复制代码
r0,lsr #4 表示r0 >>4
r0,lsr r1 表示r0 >>r1
#3,LsL #4 错误,只能是寄存器移位,不能是立即数移位

一、数据传送指令

1. MOV指令

格式:mov 目标寄存器,操作数2

功能:将操作数2的值赋值给目标寄存器

2. MVN指令

格式:mvn 目标寄存器,操作数2

功能:将操作2取反的值给目标寄存器

3. LDR指令

格式: LDR 目标寄存器,= 数据

功能: 完成任意的数据传送到目标寄存器

注意: 数据前面不能加#,因为此时数据不按立即数来处理

二、数据计算指令

1. ADD指令

格式: add 目标寄存器,操作数1操作数2

功能: 将操作数1加上操作数2的结果给目标寄存器

1. SUB指令

格式: sub 目标寄存器,操作数1操作数2

功能: 将操作数1减去操作数2的结果给目标寄存器

1. MUL指令

格式: mul 目标寄存器,操作1操作2

功能: 将操作数1乘以操作数2的结果存放在目标寄存器

注意:操作数1操作2必须都是寄存器,并且操作1的寄存器编号不能和目标寄存器一样

三、位运算指令

1. AND指令

格式: and 目标寄存器,操作数1操作数2

功能: 将操作数1按位与操作数2的结果存放在目标寄存器

2. ORR指令

格式: orr 目标寄存器,操作数1操作数2

功能: 将操作1按位或操作2的结果存放在目标寄存器

3. EOR指令

格式: eor 目标寄存器,操作1操作2

功能: 将操作数1按位异或操作数2的结果存放在目标寄存器

4. BIC指令

格式: bic 目标寄存器,操作1操作2

功能: 将操作数1按位与操作数2取反的结果存放在目标寄存器
目标寄存器 = 操作数1 & ~操作数2

四、比较指令

格式: cmp 寄存器,操作数2

等于寄存器减去操作数2

功能: 将寄存器的值与操作2比较,比较的结果会自动影响CPSR的NZCV

答案

五、跳转指令

1. B/BL指令

格式: B/BL 标签

功能: 跳到一个指定的标签,BL 跳转之前,将跳转前的PC的值保存在LR,跳转范围+/- 32M

NZCV 标志位

标志位 含义
N (Negative) 结果为负数(Rn < Rm)
Z (Zero) 结果为 0(Rn == Rm)
C (Carry) 发生借位(无符号比较时 Rn < Rm)
V (Overflow) 溢出(有符号计算超出范围)

比较指令 + B 条件跳转

指令 条件 说明
BEQ label Z == 1 相等(Rn == Rm)时跳转
BNE label Z == 0 不相等(Rn ≠ Rm)时跳转
BGT label Z == 0 且 N == V 大于(Rn > Rm,有符号)时跳转
BGE label N == V 大于等于(Rn ≥ Rm,有符号)时跳转
BLT label N ≠ V 小于(Rn < Rm,有符号)时跳转
BLE label Z == 1 或 N ≠ V 小于等于(Rn ≤ Rm,有符号)时跳转
BHI label C == 1 且 Z == 0 大于(Rn > Rm,无符号)时跳转
BHS label C == 1 大于等于(Rn ≥ Rm,无符号)时跳转
BLO label C == 0 小于(Rn < Rm,无符号)时跳转
BLS label C == 0 或 Z == 1 小于等于(Rn ≤ Rm,无符号)时跳转

2. ldr指令

格式: ldr pc,= 标签名

功能: 将PC指针指闻标签表示的地址

练习

答案

六、内存访问指令

1. 单内存访问指令

LDR 将内存中的值加载到寄存器(读内存 )
STR 将寄存器的内容写入内存(写内存)

寄存器间接寻址:寄存器的值是一个地址

LDR ro,[r1 ]     //r0 = *r1
STR ro,[ r1 ] //*r1 = ro

基址变址寻址:将基地址寄存器加上指令中给出的偏移量,得到数据存放的地址

  • A. 前索引

    STR r0,[r1,#4] //(r1 + 4)= r0
    LDR r0,[r1,#4] //r0 =
    (r1+ 4)

  • B. 后索引

    STR r0,[r1],#4 //*r1=r0 &&r1=r1 + 4
    LDR r0,[r1],#4 //r0=*r1 &&r1=r1 + 4

  • C. 自动索引

    STR r0,[r1,#4]! //(r1+4)=r0&&r1=r1+4
    LDR r0,[r1,#4]! //r0=
    (r1+4)&&r1 =r1+4

示范:


练习

将1-10数据存放在基地址为0x4000,0000,将0x4000,0000起始地址的值拷贝到0x4000,0100

答案

将0x1234写到0x4000,0000,将0xabcd写到0x4000,0004,然后从这两个地址读取数据做案加,最终结果存放在r0

答案2

2. 多内存访问指令

LDM 将一块内存的数据,加载到多个寄存器中
STM 将多个寄存器的值,存储到一块内存

格式:

LDM{条件}{s}<MODE>基址寄存器{!},{Reglist}^
STM{条件}{s}<MODE>基址寄存器{!},{Reglist}^

mode 说明
IA 后增加地址
IB 先增加地址
DA 后减少地址
DB 先减少地址

基址寄存器

用于放内存的起始地址

!

最后更新基址寄存器的值

Reglist

  • 多个寄存器,从小到大,中间用 , 隔开,如 {r0,r2,r3}{r0-r7,r10}
  • 寄存器号大的对应内存的高地址,寄存器号小的对应内存的低地址

^

  • 它存在,如果 Reglist 没有 pc 的时候,这个时候操作的寄存器是用户模式下的寄存器
  • LDM 指令中,有 PC 的时候,在数据传送的时候,会将 SPSR 的值拷贝到 CPSR,用于异常的返回

流程图:

示例

3. 栈操作指令

A. 进栈

stmfd sp!,{寄存器列表}

B. 出栈

Idmfd sp!,{寄存器列表}

注意

在对栈操作之前,必须先设置sp的值,进栈和出栈的方式一样,ATPCS标准规定满减栈

流程图:

堆栈指针指向最后压入的堆栈的有效数据项,称为满堆栈
堆栈指针指向下一个待压入数据的空位置,称为空堆栈

示例

七、CPSR/SPSR操作指令

A. 读操作

MRS Rn,CPSR/SPSR
将状态寄存器的值,读到通用寄存器中

B. 写操作

MSR CPSR/SPSR,Rn
将通用寄存器的值,写到状态寄存器

练习

A.写一段代码,将CPSR的第I(7)位清0,其他位不变(使能IRQ异常)

B.写一段代码,将CPSR的第I(7)位置1,其他位不变(禁用IRQ异常)

答案

八、ARM指令流水线分析及伪指令

在ARM核中,为增加处理器指令流的速度,ARM7系列使用3级流水线。允许多个操作同时处理,而非顺序执行。不同的ARM核,流水线的级数是不一样的,ARM核版本越高,流水线级数越多。对于软件工程师编程而言,统一按照三级流水线来分析就可以了。

PC指向正被取指的指令,而非正在执行的指令

1. 最佳流水线

该例中用5个时钟周期执行了5条指令,所有的操作都在寄存器中(单周期执行)
指令周期数(CPI)=1

2. 内存访问指令流水线

该例中,用6周期执行了4条指令,指令周期数(CPI)=1.5

3. 分支流水线

4. ARM伪指令、汇编与C混合编程、Volatile关键字

伪指令定义:
为了方便程序员使用,编译器设计的指令,这个指令ARM核无法直接识别,需要编译器对他翻译成ARM核所能识别的指令。

(1)LDR R0,=0x12345678分析

再次强调:PC指向正被取指的指令,而非正在执行的指令

如何看内存中的12345678

正在读取的LDR内存是0x0008 加上 PC所在的地址(因为LDR正在执行 所以pc等于0x0000000C预取的值)

也就是0x0008加上pc的值0x0000000C等于0x00000014

总结

编译器在编译的时候,将Idr r0,=0x12345678翻译成了ldr r0,[pc,#0x0008]这一条读内存的指令。根据PC的值加上偏移量算出0x12345678这个数据在内存的地址,然后使用Idr指令读取这个地址的数据。

(2)LDR R0,=Label 分析

1) 链接地址指定为0x0情况分析

0x00000018等于0x000C加上pc的值0x000C

注意 0x00000018的值是14 这是个值 是编译器算出来的一个值

2) 链接地址指定为0x2000情况分析

修改链接地址

再运行

label的地址也就是0x000c+pc的值0x0000200c=0x00002018

3) 总结

LDR r0,=Label指令表示将Label的值写入r0,Label的值由指定的代码段运行地址(-Ttext=地址值)来决定。

编译器做法:

  • 首先根据指定的代码段开始的地址,算出Label标签对应的地址值
  • 然后将这个表示的地址值存放在一个位置
  • 生成内存访问指令,根据pc +固定偏移量,找到标签对应值存放的位置

注意

当代码编译结束的时候,标签表示的地址值(根据指定的代码段地址)已经编译死存放在程序文件中了。

(3)LDR R0,Label

LDR R0,Label 表示读取Label表示的地址对应数据

不带=的时候 存的是标签里的内容

(4)ADR R0,Label分析

动态方式 根据pc的值+0x00000008

之前是静态的 在编译完的时候 label就已经确定值是什么了

这个是动态

举个例子:如果是用LDR我把这个代码放到A内存和B内存运行

这两块内存的值是一模一样的 因为在编译完的时候 label就已经确定值是什么了

如果是ADR A内存的0x0008 和B内存的0x0008 是不一样的

有点难理解

ADR R0,Label指令表示根据当前的PC的值 +/-偏移量,动态获取当前Label所表示的内存地址

(5)如何判别代码在实际内存中运行的地址?

ADR r0,_start 可以知道,因为他是根据pc的值,动态获取
LDR r0,=_start 无法知道,这条指令不论在哪里运行,r0的值都是固定(取决于指定的链接地址)

相关推荐
7yewh3 小时前
嵌入式知识点总结 ARM体系与架构 专题提升(四)-编程
arm开发·stm32·单片机·嵌入式硬件·mcu·物联网·51单片机
『往事』&白驹过隙;4 小时前
操作系统(Linux Kernel 0.11&Linux Kernel 0.12)解读整理——内核初始化(main & init)之缓冲区的管理
linux·c语言·数据结构·物联网·操作系统
7yewh7 小时前
嵌入式知识点总结 操作系统 专题提升(一)-进程和线程
linux·arm开发·驱动开发·stm32·嵌入式硬件·mcu·物联网
LS·Cui21 小时前
第7章 任务的定义与任务切换的实现--总结
物联网
7yewh1 天前
嵌入式知识点总结 C/C++ 专题提升(七)-位操作
c语言·c++·stm32·单片机·mcu·物联网·位操作
Anna_Tong1 天前
物联网边缘(Beta)离全面落地还有多远?
物联网·阿里云·边缘计算·腾讯云·智能制造
雪兽软件1 天前
零售业革命:改变行业的顶级物联网用例
物联网
XLYcmy1 天前
三篇物联网漏洞挖掘综述
论文阅读·物联网·网络安全·静态分析·漏洞挖掘·动态分析·固件
神一样的老师1 天前
基于马尔可夫链和多属性决策方法的物联网生态系统信任评分预测与管理
物联网