arm体系结构:研究核core,如何工作 内部原理以及工作工程
ARM核:CPU中最核心最重要的部分,ARM核由ALU,存储器,逻辑控制器,移位寄存器等组成
ARM指令:随着cpu的发展,指令也在发生变化
指令:cpu执行的就是指令
指令的生成: 与计算机语言的发展相反
x.c ----> x.s ---> x.o
C语言---->汇编(助记符)---->二进制指令
每条汇编就对应一条二进制指令,二进制指令和汇编一一对应
指令执行过程:
flash存放指令,控制器负责从flash中 取指 交给运算器执行,执行过程中,
有临时数据存放在寄存器中,一般建议,指令在执行之前拷贝到RAM(内存)中
Flash(Rom):读写速度慢,断电不丢失数据,永久存储
flash淘汰了Rom---固态硬盘/SD/存储内存--并行存储
ARM的工作模式:
ARM和人也有相似的地方,人在生活中会有很多种模式,如睡觉、工作、学习、打游戏...
在不同模式下,所占有的资源和权限是不一样
cpu:在不同模式下,功耗不同
模式切换:
作用:方便获取该模式下的权限和资源
从一种模式切换到另一种模式,需要保存当前状态/位置,方便以后返回
USR模式, 该模式权限最低, 大部分程序都要在该模式下执行 90% ---安全
SVC模式: super 管理员模式, 权限最高, 负责执行高优先级的程序 6%
IRQ模式: 外部中断模式, 权限很高, 负责处理中断 3%
*FIQ模式: fast IRQ模式, 权限比IRQ高, 处理高优先级中断,linux并没有该模式
#### undefine 模式: 当cpu发现指令异常, 进入该模式处理
#### abort模式: cpu发现内存违法,进入该模式
#### system模式, 和usr一样
不同模式下, 权限和资源不一样
寄存器,用来存放临时数据的,
寄存器 r0 ~ r7 只有一份, 各种模式都共享他们
*r8 ~ r12 有两份 FIQ模式 自占一份
r13 r14各个模式都有一份 r15(PC)一份
CPSR只有一份, SPSR多份-
总结:不管如何分配,每个模式都有r0-r15可以使用, 还有cpsr
但是: 有几个特殊的寄存器, 他们有专门的用途
r15:又称pc ,pragram counter--程序计数器
pc记录了指令执行的位置:
控制器每次都会从pc 记录的位置取出一条指令,pc自动+4
然后控制器循环取指....
pc记录的是 程序指令地址不是指令本身, pc指向的指令是将要被执行的
r14: 又称LR寄存器, linker register
pc 的备胎backup, 当程序跳转的时候,记录pc的位置 (LR =pc),
那么程序返回的时候 pc = LR
r13: 又称sp, stack point 栈指针
CPSR: current process state register,当前处理器状态寄存器
比如cpu的当前模式,其他东西
[4:0] 记录当前模式, 读取获取当时的模式,写入改变模式
[7] 读取获取当前IRQ 状态, 写入改变IRQ状态
SPSR:save process state register ,保存处理器状态寄存器
模式切换 ,会从模式A 切换到模式B, CPSR 会被改变
那么当返回的时候,CPSR 要恢复到以前的状态
ARM指令集编码基本格式:
条件码 指定码 目的寄存器 操作数1寄存器 操作数2
31-28 27-20 19-16 15-13 12-0
标志位 (EQ NE )
<opcode> <Rd> <Rn> <operand2>
操作码 目标寄存器 操作数1寄存器 第二个操作数
add r1, r0,#3 @@ r1=r0+3
add R3, R2, R1,LSR #2 #R3 = R2+R1 /4 @@r1逻辑右移两位+4
######## 汇编指令 ####################
@@@@@@@@ 搬移指令 mov
mov r0, #1 @@ r0 =1
mov r1, r0 @@ r1=r0
mov r2, r1
@@@切换CPU模式--->usr模式
@@@ mov cpsr ,#ox10 非法,因为 cpsr只有两条可以修改 msr mrs
@@@@@@@@ 算数指令 ############
add r1, r0,#3 @@ r1=r0+3
add r2,r1,r0 @@ 用法 :add 目的寄存器 源寄存器 寄存器/数值
sub r2, r1 ,#3 @@ r2=r1-3
mul r2,r1,r2 @@ r2 = r1*r2 mul 目的寄存器,源1寄存器,源2寄存器
@@没有除法
@@@@@@@@@ 逻辑运算 and--& orr-- | bic--清零 bit clear
与指令
and Rd,Rn,operand
and指令将Rn 的值与操作数operand2按位逻辑 与 ,结果存放到目的寄存器Rd 中。
或指令
orr Rd,Rn,operand
orr指令将Rn 的值与操作数operand2按位逻辑 或 ,结果存放到目的寄存器Rd 中。
异或指令
eor Rd,Rn, operand
eor 指令将Rn 的值与操作数operand2按位逻辑”异或”,相同为0,不同为1,
结果存放到目的寄存器Rd 中。
清除指令
bic Rd,Rn,operand
bic指令将Rn 的值与操作数operand2 的反码按位逻辑”与”,结果存放到目的寄存器Rd 中
bic R0,R0,#0x1F; @@将R0最低5位清零,其余位不变
@@@@@@@@@@@@@ 比较指令 条件执行 #########
mov r1,#5
cmp r1,#8
@@@ 内部原理: r1-8 得到一个结果: 负值 正值 0
@@ 结果放在 cpsr.N cpsr.Z cmp每次执行都会改变这些bit
@@ cpsr.N negative 负数 =1 表示有负数产生 =0表示没有负数产生
@@@ cpsr.Z zero =1 b表示结果为零 =0 表示结果非零
@@@@ 条件执行 大于great 小于 -little 等于-equeue 不等于--not equeue
movgt r3,#9 @@ 大于执行 如果加上gt那么该指令会首先读取 cpsr.n/z,然后决定是否执行
movlt r4,#8
moveq r5,#5
movne r6,#7
cmp r2,r1
@@@@@@@@@@ 跳转指令 #######
修改PC值
mov r0,#1
mov r1,#2
mov r2,#3
mov r3,#4
b main @@@强制修改PC =main所在的地址, pc=18
mov r4,#5
mov r5,#6
main: @@标号仅仅表示当前指令存在的地址,标号在编译之后不存在 不占用空间
mov r9,#9
mov r10,#10
@@@@@@@@@@@@@@@@@ b 基本操作 --break
bl --- link register
bl 首先把 当前指令的地址存放在 lr中,然后再跳转
bl main
mov pc,lr
##### 深入指令 ###########
指令,32bit 基本上和汇编一一对应
指令生成: gcc x.c -o x.out
如何从汇编翻译为机器码/指令:
总结:每条指令都是独立的,不依赖于其他指令
指令执行过程:
最基本的三个过程: 取指 译码 (找到运算器 输入源 输出源) 执行
我们都是使用 指令流水 完成---流水线---一条指令一个周期
51单片机: 三级流水
STM32 : 五级流水
x86 : 13级流水
流水 :提高效率,不用等待, 每个模块只要关注一件事情
####### 立即数问题 ############
放入指令中的常数
指令有 12bit 用来存放数据 0xFFF 1111 1111 1111
理论上 0-4095,但是这样就没有办法表示一个 很大很大的数
实际上:
12bit 分为 高8位 低四位
高8bit用来存放一个 8bit数据, 0~255, 假设为 x
低4bit表示 X 左移的位数*2 假设 低4bit值为 y
那么最终结果是 :x<<(2*y)
定义:立即数即一个bit常数循环左移偶数位得到的值
判断一个数是不是立即数??
如果它可以由一个 8bit常数 经过偶数位循环 左移得到
0x01880 0001 1000 1000 0000 可以左移六位得到
ox01F40 0001 1111 0100 0000 可以左移六位得到
0x1234 不是
问题:我们C语言 从没有考虑这个为问题 ,如何把0x12345678 放入r0呢?
mov r0,0x12345678 不行 非法
如何做:
1.拆分大常数, 比如 0x1234 --> 0x1200 and 0x34,分别放入 r0,r1 ,然后orr r3,r0,r1
*2.使用伪指令 : 假指令
ldr r0, =0x12345678
原理:编译器 遇到伪指令,会帮你替换为真实的指令,完成你要求的功能
####### 内存访问指令 ######
寄存器的存储是有限的 r0-r15
ldr str
arm 规定,内存是不能直接拷贝的(从一片区域拷贝到另一片)
ldr :load register,将数据 从此内存拷贝到 寄存器
str :store register ,将寄存器值 存入内存中
LDR用于将内存的内容加载到寄存器中,STR用于存储寄存器的内容到 存储器地址
@@@i++如何实现的??
ldr r0,=val_i @@将val_i的内存地址写入寄存器r0
ldr r1,[r0] @@ r1=*r0 @@取出值
add r1,r1,#1
str r1,[r0] //将r1 中的值存储到r0中的内存地址中
mov r0,#buf @@r0 = buf的地址
ldr r0,= buf
ldr r1= [r0] @@ r1=*r0 r0存储了数据的地址, *r0 取出内容
ldr r0,=dbuf
str r1,[r0] @@ *r0 = r1
.date @@@ 表示后面是 数据段, 可以修改的
val_i:
.byte 0x1,0x3,0x5,0x7
buf: @@ char buf[] = {0x01,0x02,0x03,0x04}
.byte 0x01,0x02,0x03,0x04
@@小端模式 :高字节放高位
dbuf: @@ char *buf = malloc(8)
.space 8
@@@@@@@@@@@ 异常 #############@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
正常--状态:学习 吃饭 睡觉
异常 :打断正常节奏的事件, 称之为异常
CPU :在执行过程中,会遇到 中断 指令错误 内存错误...
异常:需要处理,而且需要 及时处理
|
|
|
| 突然发生异常
-----------> cpu立马放下手中的任务,转而去处理异常
LR=pc,spsr=cpsr |
| pc和 cpsr被改变
|
------------
| 返回正常程序
| pc=LR,cpsr=spsr
|
中断,是异常的一种,异常包括很多种,中断异常 软中断异常
中断是其中最重要的一种
中断好处:提高系统的响应速度(鼠标跟踪)--实时性
异常特点: 一般随机发生的,但是也可以主动让某个异常发生
复位异常/RESET: 程序正在执行,突然系统复位..---优先级最高
IRQ异常: 中断异常,当中断产生的时候
@ FIQ异常: 快速中断发生
swi软中断异常: 唯一一个 可以主动发生的异常
只要执行一个指令,就可以产生异常 swi
@未定义异常: 当程序发现 未定义指令的时候......
@数据访问异常: 程序发现 内存非法(内存被释放)
不同的异常, 处理的时候需要不同的资源和权限(模式)
不同的异常发生, cpu会自动进入不同的模式,从而获得不同的权限和资源,执行不同的代码
不同的异常--跳到异常向量表的0位置-->跳转指令,在跳转到代码执行区
SVC --supervisor
RESET/SWI ---SVC模式
IRQ ---IRQ模式
当发生不同异常,PC指针应该跳转到不同的位置,执行不同的代码段处理该异常
那么问题:程序的位置是不同的
异常向量表 vctor Table: 他存放在flash的0地址 0x00
每4B为一个表项, 当cpu 发生不同异常的时候,pc指针跳转到对应的表项
RESET --->0x00
swi软中断异常--->0x08
IRQ异常
@@跳转指令
b reset
reset:
//初始化栈指针
ldr sp, =end_stack @@r13 一般写为sp ,我们一般使用减栈
stmfd sp!,(r0-r12,lr) @@将r0-r12,r14统统放入sp指向的栈中,!sp指针自动调整
@@出栈
ldmfd sp!,(r0-r12,pc) @注: lr直接给了pc
@@ mov pc,lr
.date
stack_base:
.space 512
end_stack:
@@@@ 伪指令
假指令 ,真是不存在的指令, 作用是 告诉编译器 该如何去干活
.text 告诉编译器,后面都是指令
.end , 结束了
.byte buf: .byte 0x1,0x2,0x3 ...告诉编译器,buf后面的空间 按照 字节为单位 进行访问,依次存入
.word buf: .word 0x12345678--一个字 0x23355589 ...
告诉编译器,buf后面的空间都按照 字为单位进行访问
int buf[] = {0x1234,0x2345678}
ldr ldr r3,=0x1234666 告诉编译器,把这个数放入寄存器r3
.global 标签 .global _start 声明 _start 为全局标签,即允许标签 被外部使用
.equ PI 3.1415926 @@@ #define PI 3.1415926