第六章:体系结构
1.引言
体系结构:程序员所见到的计算机->组成
- 指令集(汇编语言)
- 操作空间(寄存器和存储器)
- 例子:ARM,x86,MIPS,SPARC,PowerPC(他们有不同的语言及寄存器形式)
指令
-
定义
- 计算机语言的单词叫指令
-
两种形式
- 机器码:计算机只能理解0和1,指令需要被编码为二进制,(32位处理器,指令为32bit)
- 汇编语言:人类能理解的形式;使用符号来表示指令
- 汇编语言=>机器码
-
包括
- 要完成的操作(+ - 分支 跳转)
- 操作数(寄存器、存储器、立即数(指令自身))
计算机语言的词汇集叫做指令集:计算机硬件能够实现的操作;不同体系结构的指令集像各地方言,而不是不同的语言,都是定义了基础指令,操作数也都来源于寄存器,存储器,指令本身
不同体系结构并未定义底层硬件实现,同一体系结构,可以根据(价格,性能,低功耗)等要求调整硬件实现形式
寄存器、存储器、ALU和其他模块组织成微处理器的方式称为微结构
ARM体系结构设计的4个准则:
-
规整性支持简单设计
- 指令包含固定数目的操作数
-
加快常见功能
-
多条汇编指令执行复杂操作(RISV精简指令集)
- ARM
- MIPS
-
只包含简单和常用指令
-
简化硬件(指令操作和指令译码),指令编码中区分不同指令操作的位数较少
-
复杂指令集(CISV)硬件复杂,指令编码凌乱,复杂指令操作快,但是增加了简单指令的开销
-
-
越小的设计越快
- 仅有16个寄存器
-
好的设计需要好的折中方法
- 机器语言的少量格式允许指令之间的规整性,因此允许更简单的译码器硬件,同时还满足不同的指令需求(三种主要格式:数据处理,存储器。分支)变长太复杂,所以少量类别
2.汇编指令
介绍
- 以人类能看懂的方式表示机器语言
- 每条指令包括:操作、操作数
- 操作数:寄存器、存储器、立即数
指令
-
加法
-
减法
操作数
-
来源
- 寄存器
- 指令自身
- 存储器
-
详细介绍三种来源
-
寄存器
-
SARM阵列构造(静态RAM,锁存器)
-
访问操作数快,指令执行的快
-
ARM体系结构有16个寄存器,x86有8个寄存器
-
指令仅在寄存器上运行,先将内存的数加载到寄存器
-
举例
- a代表空间别名 -
寄存器集
- R0|参数值/返回值/临时变量- R0-R4函数参数,超过4个存栈帧里,R0函数返回值 - R12临时变量 - R13(SP)栈指针(进栈减小出栈增大) - R14(LR)链接寄存器(跳转发生,保护现场,存储跳转指令下一指令的程序PC地址) - R15(PC)程序计数器(要执行的下一条指令,但是由于历史原因,读回的PC值是PC+8后的值)
-
-
常数/立即数
-
从指令中获取
-
例子
-
-
存储器
-
从memory读取数据到register
- LDR
-
把reg的数据存储到memory
- STR
-
ARM字节可寻址,一个字包括4字节,故每个字的地址均为4的倍数,高有效自己在左侧,低有效字节在右侧,地址从下向上生长增大(小端)
-
字节可寻址存储器的组织方式有大端和小端两种格式,子地址相同,只是字节地址顺序不同,大端从高有效字节开始,小端从低有效字节开始
-
寻址方式
-
扩展索引
-
LDR R3, [R0, R1, LSL #2]
- R1左移两位+R0=>R0+(R1X4)
-
-
偏移量
-
预索引
-
后索引
-
-
存储器存储字节和字符
-
LDRB(加载字符)(补0扩展到32位)
-
STRB(存储字节)(忽略高字节,只保存低字节)
-
LDRSB(存储有符号字节)(补符号位扩展到32位)
-
存储Hello!(0x48 65 6C 6C 6F 21 00)
-
-
-
3.编程
数据处理指令(逻辑和算术指令)
-
逻辑指令
- AND
- ORR(OR)
- EOR(XOR)
- BIC(位清除) R1 AND(NOT R2)
- MVN(按位取反)
-
移位指令
-
LSL(逻辑左移)左移N位=>乘2^N
-
LSR(逻辑右移)(逻辑移位可以提取组合位域)
-
ASR(算术右移)右移N位=>除2^N
-
ROR(右旋)(没有ROL,因可以右旋互补量完成左旋)
-
例子
-
-
乘法指令
- 32位*32位=64位
- 结果小于32位的MUL R1, R2, R3
- 无符号长整型UMULL R1, R2, R3, R4
- 有符号长整型SMULL
- MLA
- SMLAL
- UMLAL
条件标志
-
程序每次以相同循序运行会很无聊
-
根据CPSR寄存器前四位来有条件的执行后续指令
- N
- Z
- C
- V
-
指令的cond字段为条件字段:4bits
-
一般由CMP指令设置状态位,并根据状态位设置条件标志:
-
分支(跳转)
-
无条件分支
-
有条件分支(与条件标志结合)BEQ
-
B(简单分支)
-
BL(分支并链接,用于函数)
条件语句(汇编代码检测条件与高级语言相反)
-
if
-
条件指令
-
-
if/else
-
条件指令
-
-
switch/case
循环
-
while
-
for(访问内存非常有用)
-
访问数组
-
后索引for循环
-
函数
-
调用函数
- 调用函数将参数存在R0~R3中
- 将返回地址保存在LR寄存器中
- BL指令跳转到被调用函数
- R0~R3如果需要保护,必须由调用函数入栈
-
被调函数
-
输入参数
- 保存在R0~R3
-
输出返回值
- 保存在R0
-
将需要保护的寄存器入栈(如果用到R4~R11,LR,必须先入栈才能用)以及SP以上的栈空间不能被改变
-
-
函数调用与返回
-
基本指令
- BL SIMPLE (将BL指令的下一个指令地址存入LR寄存器(BL地址+4),并将PC寄存器值改为跳转标号处的指令地址)指令中的立即数为相对于PC+8的指令地址的个数
- MOV PC LR(返回执行指令的地址)
-
简单例子
- LR(0x00008024=PC-4)跳转到0x000902C执行这条指令(PC+8+立即数*4=>LSL 2=0x000902C)
- 流水线机制导致PC值+8,三级流水线,指令在执行时,PC已经将两个周期后的指令取出来了,故是相对于PC+8
-
-
带输入参数和返回值
-
参数依次存入R0~R3,存不下的放入栈中
-
返回值存入R0
-
例子
-
-
栈
-
存储局部变量的存储单元
-
每个函数有自己的栈帧
-
函数执行完毕,栈要回收
-
后进先出
-
栈是向下生长的,SP(R13)要减去需要的存储空间地址个数,相当于开辟栈空间,再将变量入栈
-
保存受保护寄存器步骤
- 创建栈空间来存储一个或多个寄存器的值
- 将寄存器的值存储在栈中
- 使用寄存器执行函数
- 从栈中恢复寄存器的原始值
- 回收栈空间
-
例子
-
代码
- -
保存和恢复多个寄存器
-
LDM与STM分为4种形式
- 满的降序(FD)=PUSH=STMFD SP! {regs}
- 空的降序(ED)=POP=LDMFD SP! {regs}
- 满的升序(FA)
- 空的升序(EA)
-
-
受保护和不受保护寄存器的
- 受保护寄存器(被调用者保存的)
- 不受保护寄存器(调用者保存的)
-
非叶子函数调用
-
不用调用其他函数的函数称为叶子函数
-
调用其他函数的函数称为非叶子函数
-
调用函数规则
- 必须保存调用后所需的所需的任何不受保护的寄存器(R0~R3,R12),调用后必须恢复这些寄存器
-
被调用函数规则
- 保存受保护的寄存器(R4~R11和LR)
-
例子
-
-
-
-
递归函数
-
自己调用自己
-
例子
-
栈情况
-
-
-
-
附加参数和局部变量
- 调用函数开辟栈帧保存多于四个的其他参数
- 被调用函数此时可以方位调用函数的栈帧
- 栈帧中可以保存寄存器R0~R12,LR,局部变量和数组
4.机器语言
简介
-
主要的三种指令格式
-
数据处理
-
第一源:源寄存器
-
第二源:
-
立即数
-
寄存器
- 移位的
-
-
目的寄存器
-
-
存储器
-
三操作数
- 基址寄存器
- 立即数或可选移位寄存器存储的偏移量
- LDR的目标操作数,STR的源操作数寄存器
-
-
分支
- 24位立即数分支偏移量
-
数据处理命令
-
六个字段
-
cond(条件编码,无条件默认1110)
-
op(操作码)
- 数据处理00
- 存储器01
- 分支10
-
funct(功能码)
-
I
- Src2是否是立即数
-
cmd
- 特定的数据处理指令
-
S
- 指令是否设置条件标志,是1
-
-
Rn(第一源寄存器)
-
Rd(目标寄存器)
-
Src2(第二源寄存器)
-
三种变体
- 立即数
- 可选移常数位的寄存器Rm
- Rs寄存器指定移位数的Rm寄存器
-
-
-
例子
-
汇编=>机器语言
-
先看属于什么类型确定op,是否条件指令确定cond与S,什么指令确定cmd,根据操作数类型确定其他位I,Src2等
-
循环移位rot*2位产生立即数
-
-
几个例子
存储器指令
-
六个字段
-
cond(条件编码,无条件默认1110)
-
op(操作码)
- 数据处理00
- 存储器01
- 分支10
-
funct(功能码)
-
I'
- Src2是否是立即数
-
U
- 加减指令
-
P
- 预索引
-
W
- 写回
-
B
- 字节
-
L
- 加载
-
-
Rn(基址寄存器)
-
Rd(LDR目标寄存器,STR源寄存器)
-
Src2(偏移量)
-
两种变体
- 立即数
- 可选移常数位的寄存器Rm
-
-
字段说明图
-
例子
-
分支指令
-
四个字段
-
cond
-
op=10
-
1L
- BL=1
- B=0
-
立即数24位
- 24位二进制补码用于指定相对于PC+8的指令地址
-
字段说明图
-
例子
- 0x80B4-0x80A8=3故IMM24=3
-
寻址模式
-
寄存器
- 仅寄存器
- 立即数移位的寄存器
- 寄存器移位的寄存器
-
立即数
-
基址
- 立即数偏移量
- 寄存器偏移量
- 立即数移位的寄存器偏移量
-
PC相对寻址
- 相对PC+8+偏移量的地址寻址
解释机器语言代码
- 先看27:26找到op,再看funct字段
程序存储
-
机器语言编写的程序是一个32位数序列,这些指令存放在存储器中,这就是程序存储
-
程序提供通用计算能力,而不是特定的硬件。
-
程序存储中的指令从存储器中找到或取出,然后由处理器执行,
-
处理器从存储器中顺序地读出指令->用数字电路硬件译码和执行,当前指令地址存在PC(R15)寄存器中
-
微处理器的体系结构状态
- 寄存器文件
- 状态寄存器
5.编译、汇编与加载
流程
内存映射
-
地址空间2^3字节=4GB,字地址是4的倍数为0~0xFFFFFFFC
-
ARM地址空间划分为5个部分
-
文本段(只读段)
- 代码
- 文字常量
- 只读数据
-
全局数据段(读写段)
-
存放全局变量
- 静态基址寄存器访问全局变量,通常使用R9作为静态基指针
-
-
动态数据段
- 栈顶部向下生长
- 堆底部向上生长
-
异常处理程序
- 最底部存储异常向量表
- 异常处理程序
-
操作系统OS和输入输出段
- 最高部分
-
示意图
-
编译、汇编
-
GCC
-
例子
-
汇编
-
链接
-
例子
加载
-
例子
6.其他主题
加载文字常量例如 int a=0x2B9056F
-
两种形式的加载
- LDR Rd,=文字常量
- LDR Rd, =label标签
-
例子
- 0x44=0x815C-(0x8110+8)
NOP
- 伪指令=no op
- 汇编程序将其转换为=>MOV R0,R0(0xE1A00000)
- 实现延迟或对齐指令是有用的
异常
-
异常就是,没有经过调用,就自动调用了
-
硬件
- 硬件键盘IO异常叫做中断
- 重置 reset或读取不存在的内存
-
软件(陷阱)
- 错误条件
- 未定义的指令
- 重要的形式:系统调用=调用高权限级别程序OS中的函数
-
-
执行模式和权限级别
-
特权级别
-
PL0
-
用户模式
- 不能访问存储器受保护部分
- 例如:操作系统代码
-
-
PL1
- 监视
- 终止
- 未定义
- 中断(IRQ)
- 快速中断(FIQ)
-
-
-
异常向量表
- 发生异常->处理器根据异常原因跳转到异常向量表->执行处理异常的代码->退出返回用户的代码
-
备份寄存器
- 异常更改PC寄存器之前需要将返回地址保存在LR寄存器
- 每个执行模式都需要LR寄存器,故需要一组寄存器充当每个模式下的LR寄存器
- 同理需要一组状态寄存器SPSR保存CPSR副本
- 每个执行模式有自己的堆栈及SP指针存储副本寄存器
- 在启动之前时需要初始化堆栈和库存版本
-
异常处理
-
第一件事将寄存器入堆栈
-
异常期间流程
- 1.将CPSR存储到被保存的一组SPSR
- 2.根据异常类型设置执行模式和权限级别
- 3.在CPSR中设置中断屏蔽位,以便不会中断异常处理程序
- 4.将返回地址存储到保存的LR
- 5.根据异常类型跳转到异常向量表
-
异常向量表存的是异常处理程序的入口地址
-
处理程序:
-
寄存器入堆栈
-
处理异常
-
出栈
-
返回
-
MOVS PC LR(MOVS执行)
- 1.将存储的SPSR恢复到CPSR
- 2.复制存储的LR(可能有例外)到PC来返回发生异常的程序
- 3.恢复执行模式和权限级别(CPSR4:0)
-
-
-
-
异常相关指令
-
程序在低级别运行
-
操作系统在高级别运行
-
操作系统和高级别PL1执行模式
-
可使用
- MRS(从特殊寄存器移动到寄存器)
- MSR(从寄存器移动到特殊寄存器)
-
例子
- 引导时->操作系统可使用这些指令初始化异常处理程序的堆栈
-
-
-
启动
-
管理员模式->执行引导和加载程序代码
-
配置内存系统
-
初始化堆栈指针
-
从磁盘读取操作系统
-
二级启动
-
加载程序(更改为非特权用户模式)
- 跳转到程序的开头
-
-
-
-
7.ARM体系结构的演变
发展
- 26位总线ARMv2
- 32位ARMv4现代处理器核心
- ARMv4T加入Thumb 16位指令集提高代码密度
- ARMv6添加了多媒体指令并增强了Thumb指令集
- ARMv7改进了浮点和多媒体指令
- ARMv8引入了全新64位体系结构
Thumb指令集
-
16位,指令功能较弱-同功能需要更多条指令
-
约为ARM指令总长的0.65
-
由ISETSTATE寄存器的T位指示处理器模式:0正常,1:Thumb
-
指令更复杂不规则
-
Cortex-M系列处理器仅在Thumb状态下运行
-
例子
DSP指令
-
增加了许多指令乘法累加(MAC)...
-
数据类型
-
指令一般在短16位数据上操作
-
乘法累加指令
- -
各种数据类型的乘法和MAC代码
- -
浮点指令
-
节能和安全指令
- ARMv6支持睡眠模式
- ARMv7增强了异常处理方式以支持虚拟化和安全性
- 虚拟模式多个操作系统并行
- 管理程序以PL2特权级别在操作系统之间切换
- 有一个安全状态->有限的进入方式和对存储器安全部份的受限访问
- 即使攻击者危及操作系统,安全内核也可能抵制篡改
-
SIMD(单指令多数据)指令(压缩算术)
-
V开头
-
类型
- 同样为浮点指令定义的基本算数函数
- 多个元素的加载和存储,包括去交错和交错
- 按位逻辑运算
- 比较
- 使用和不使用包和运算的各种种类的移位、加法和减法
- 各种乘法和MAC
- 杂项指令
-
例子
- VADD.I8 D2,D1,D0 D64位,8*8打包
- VADD.I32 Q2,Q1,Q0 Q128位,32*4打包
-
64位体系结构
- ARMv8指令仍然是32位长,指令集非常像ARMv7,消除了某些问题
- 寄存器文件扩展为31个64位寄存器(X0~X30)
- PC和SP不再是通用寄存器的一部分。链接寄存器LR=X30,没有X31寄存器它被称为零寄存器(ZR)硬连线至0
- 数据处理指令可以在32位和64位值上运行,而加载和存储始终使用64位地址
- 大多数指令删除了条件字段,为了腾出空间来指定源目寄存器
- 分支指令仍然是有条件的
- ARMv8还简化了异常处理,使高级SIMD寄存器的数量增加了一倍,并添加了AES和SHA加密指令。指令编码相当复杂
- 复位时,ARMv8处理器以64位模式启动,通过在系统寄存器中置位一个位并调用异常,处理器可以进入32位模式。异常返回时,他返回64位模式
8.另一个视角:x86体系结构
x86体系结构
-
个人PC都是X86
-
CISC复杂指令集
-
变长指令集
-
译码硬件复杂
-
主要差异
x86寄存器
-
8个16位寄存器
- 大部分为通用寄存器
-
一些寄存器可独立存取高8位和低8位字节
-
32位产生后寄存器也被扩展为32位
-
EAX
- 通用寄存器
-
EBX
- 通用寄存器
-
ECX
- 通用寄存器
-
EDX
- 通用寄存器
-
ESP
- 栈指针
-
EBP
- 基址指针
-
ESI
- 源寄存器
-
EDI
- 目的寄存器
-
EIP
- 程序计数器
-
EFLAGS
- 状态标志寄存器
-
x86操作数
-
立即数
-
寄存器
-
内存
-
只有两个操作数
-
操作数位置组合(除了内存到内存随意组合)
-
-
寻址方式
-
可对8位16位数据操作
状态标志
-
使用状态标志(条件吗)判断分支跳转以及保存进位和算术溢出标志
-
使用EFLAGS32位寄存器存储状态标志
-
一些位给出,另一些位用于操作系统
x86指令集
-
x86指令集比ARM指令集大
--
条件跳转指令
-
x86指令编码
- 复杂
x86的其他特征性
-
将内存分段
- 每段不大于64KB
-
各种前缀:
- 0x66用于选择16位或32位操作数
- 锁定总线
- 预测分支是否执行
- 字符串移动中重复执行指令
-
有些指令比简单指令组合慢要避免使用:例如整个字符串操作
整体情况
- x86更倾向于更加短小的程序代码
- 大杂烩
9.总结
要指挥一台计算机必须要说计算机的语言
计算机体系结构定义了如何指挥一个处理器
了解一个新的体系结构需要了解
- 数据的字长是多少位
- 寄存器如何组织
- 内存时怎样组织的
- 指令是怎样的