参考:
计算机硬件基础
1. 底层课程导学
1.1 课程回顾
1.2 嵌入式系统分层
操作系统的作用
- 向下管理硬件、向上提供接口(API)
应用开发
- 使用系统提供的接口(API),做上层应用程序的开发
底层开发
- 做操作系统本身的开发
1.3 Linux层次结构
Linux子系统
- 进程管理:管理进程的创建、调度、销毁等
- 内存管理:管理内存的申请、释放、映射等
- 文件系统:管理和访问磁盘中的文件
- 设备管理:硬件设备及驱动的管理
- 网络协议:通过网络协议栈(TCP、IP…)进行通信
2. ARM体系结构与接口技术课程导学
课程内容
学习方法
3. 计算机基础
计算机的进制
- 逻辑1和0
在计算机中数据的存储、运算、传输都是以高低电平的方式所以数字电路中用高、低电平来表示逻辑1和0
计算机的组成
-
输入设备
把其他信号转换成计算机能识别和处理的信号并送入计算机中
如键盘、鼠标、摄像头等 -
输出设备
把运算结果以人或其他设备所能接受的形式送出计算机外
如显示器、音响、打印机等 -
存储器
存储器是用来存储程序和数据的部件,是实现"存储程序控制"的基础
如内存、硬盘等 -
运算器
CPU中负责进行算数运算和逻辑运算的部件,其核心是算术逻辑单元ALU -
控制器
控制器是CPU的指挥中心,其控制着整个CPU执行程序的逻辑过程注:运算器和控制器共同组成了CPU
总线
- 总线
总线是计算机中各个部件之间传送信息的公共通信干线, 在物理上就是一束导线按照其传递信息的类型可以分为数据总线、地址总线、控制总线 - DMA总线
DMA(Direct Memory Access)即直接存储器访问,使用DMA总线可以不通过CPU直接在存储器之间进行数据传递
4. 多级存储结构与地址空间
三级存储结构
- Cache
速度最快、价格最贵、容量最小、断电数据丢失、cpu可直接访问
存储当前正在执行的程序中的活跃部分,以便快速地向CPU提供指令和数据 - 主存储器
速度、价格、容量介于Cache与辅存之间、断电数据丢失、cpu可直接访问
存储当前正在执行的程序和数据 - 辅助存储器
速度最慢、价格最低、容量最大、断电数据不丢失、cpu不可直接访问
存储暂时不运行的程序和数据,需要时再传送到主存
地址空间
- 地址空间
一个处理器能够访问(读写)的存储空间是有限的,我们称这个空间为它的地址空间(寻址空间),一般来说N位地址总线的处理器的地址空间是2的N次方
5. CPU工作原理
一条指令的执行分为三个阶段
-
取址:
CPU将PC寄存器中的地址发送给内存,内存将其地址中对应的指令返回
到CPU中的指令寄存器(IR) -
译码:
译码器对IR中的指令进行识别,将指令(机器码)解析成具体的运算 -
执行:
控制器控制运算器中对应的运算单元进行运算,运算结果写入寄存器每执行一条指令后PC的值会自动增加指向下一条指令
ARM体系结构理论基础
1. ARM概述
公司概述
ARM(Advanced RISC Machines)有三种含义
- 一个公司的名称、一类处理器的通称、一种技术
ARM公司(成立于1990年11月,前身为Acorn计算机公司)
- 主要设计ARM系列RISC处理器内核
- 授权ARM内核给生产和销售半导体的合作伙伴,ARM公司并不生产芯片
- 提供基于ARM架构的开发设计技术软件工具、评估板、调试工具、应用软件
总线架构、外围设备单元等
ARM产品系列
- 早先经典处理器
包括ARM7、ARM9、ARM11家族- Cortex-A系列
针对开放式操作系统的高性能处理器
应用于智能手机、数字电视、智能本等高端运用- Cortex-R系列
针对实时系统、满足实时性的控制需求
应于汽车制动系统、动力系统等- Cortex-M系列
为单片机驱动的系统提供了低成本优化方案
应用于传统的微控制器市场、智能传感器、汽车周边等
RISC处理器(Reduced Instruction Set Computer)
- 只保留常用的的简单指令,硬件结构简单,复杂操作一般通过简单指令的组合实现,一般指令长度固定,且多为单周期指令
- RISC处理器在功耗、体积、价格等方面有很大优势,所以在嵌入式移动终端领域应用极为广泛
CISC处理器
- 不仅包含了常用指令,还包含了很多不常用的特殊指令,硬件结构复杂,指令条数较多,一般指令长度和周期都不固定
- CISC处理器在性能上有很大优势,多用于PC及服务器等领域
SOC(System on Chip)
- 即片上系统,将一个系统中所需要的全部部件集成在一个芯片中在体积、功耗、价格上有很大优势
2. ARM指令集概述
指令集
指令
- 能够指示处理器执行某种运算的命令称为指令(如加、减、乘 …)
- 指令在内存中以机器码(二进制)的方式存在
- 每一条指令都对应一条汇编
- 程序是指令的有序集合
指令集
- 处理器能识别的指令的集合称为指令集
- 不同架构的处理器指令集不同(运算电路不同)
- 指令集是处理器对开发者提供的接口
ARM指令集
大多数ARM处理器都支持两种指令集:
ARM指令集
- 所有指令(机器码)都占用32bit存储空间
- 代码灵活度高、简化了解码复杂度
- 执行ARM指令集时PC值每次自增4
Thumb指令集
- 所有指令(机器码)都占用16bit存储空间
- 代码密度高、节省存储空间
- 执行Thumb指令集时PC值每次自增2
编译原理
机器码(二进制)
是处理器能直接识别的语言,不同的机器码代表不同的运算指令,处理器能够识别哪些机器码是由处理器的硬件设计所决定的,不同的处理器机器码不同,所以机器码不可移植
汇编语言
是机器码的符号化,即汇编就是用一个符号来代替一条机器码,所以不同的处理器汇编也不一样,即汇编语言也不可移植
C语言
在编译时我们可以使用不同的编译器将C源码编译成不同架构处理器的汇编,所以C语言可以移植
3. ARM存储模型
ARM数据类型
字节序(大小端对齐)
ARM指令存储
4. ARM工作模式
ARM工作模式
ARM有8个基本的工作模式
- User
非特权模式,一般在执行上层的应用程序时ARM处于该模式- FIQ(Fast Interrupt Request)
当一个高优先级中断产生后ARM将进入这种模式(当程序执行时,被外部事件打断,进入该模式)- IRQ(Interrupt ReQuest)
当一个低优先级中断产生后ARM将进入这种模式(同上)- SVC
当复位或执行软中断指令后ARM将进入这种模式- Abort
当产生存取异常时ARM将进入这种模式- Undef
当执行未定义的指令时ARM将进入这种模式(ARM不认识该指令)- System
使用和User模式相同寄存器集的特权模式- Monitor
为了安全而扩展出的用于执行安全监控代码的模式
工作模式的理解
-
不同模式拥有不同权限
在飞行模式下,你用不了数据、wifi
在User模式,操作权限低,该模式出现野指针,只会造成当前程序崩溃,对其他地方不构成影响 -
不同模式执行不同代码
-
不同模式完成不同的功能
ARM工作模式分类
按照权限
- User为非特权模式(权限较低),其余模式均为特权模式(权限较高)
按照状态
- FIQ、IRQ、SVC、Abort、Undef属于异常模式,即当处理器遇到异常后
会进入对应的模式
ARM寄存器组织
1. 寄存器
1.1 寄存器
1.2 C语言储存类型
C语言包含4种储存类型,见下表:
特征 | 自动储存类型 | 寄存器储存类型 | 静态储存类型 | 外部储存类型 |
---|---|---|---|---|
关键字 | auto | register | static | extern |
储存于 | 内存 | CPU寄存器 | 内存 | 内存 |
默认初始值 | 垃圾值 | 垃圾值 | 0或空白符 | 0或空白符 |
作用域 | 局限于块 | 局限于块 | 局限于块 | 全局 |
生命周期 | 块内 | 块内 | 存在于函数之间 | 存在于函数之间 |
- 块指的是写在左右花括号:“ { } ” 内的一组语句。局部变量是声明在块内的变量。
首先要来理解一下可执行文件加载进内存后形成的进程在内存中的结构
代码区:
存放CPU执行的机器指令,代码区是可共享,并且是只读的。
数据区:
存放已初始化的全局变量、静态变量(全局和局部)、常量数据。
BBS区:
存放的是未初始化的全局变量和静态变量。
栈区:
由编译器自动分配释放,存放函数的参数值、返回值和局部变量,在程序运行过程中实时分配和释放,栈区由操作系统自动管理,无须程序员手动管理。
堆区:
堆是由malloc()函数分配的内存块,使用free()函数来释放内存,堆的申请释放工作由程序员控制,容易产生内存泄漏。
c语言中的存储类型有auto, extern, register, static 这四种,存储类型说明了该变量要在进程的哪一个段中分配内存空间,可以为变量分配内存存储空间的有数据区、BBS区、栈区、堆区。下面来一一举例看一下这几个存储类型:
- auto存储类型
auto只能用来标识局部变量的存储类型,对于局部变量,auto是默认的存储类型,不需要显示的指定。因此,auto标识的变量存储在栈区中。示例如下:
#include <stdio.h>
int main(void)
{
auto int i=1; //显示指定变量的存储类型
int j=2;
printf("i=%d\tj=%d\n",i,j);
return 0;
}
- extern存储类型
extern用来声明在当前文件中引用在当前项目中的其它文件中定义的全局变量。如果全局变量未被初始化,那么将被存在BBS区中,且在编译时,自动将其值赋值为0,如果已经被初始化,那么就被存在数据区中。全局变量,不管是否被初始化,其生命周期都是整个程序运行过程中,为了节省内存空间,在当前文件中使用extern来声明其它文件中定义的全局变量时,就不会再为其分配内存空间。
示例如下:
#include <stdio.h>
int i=5; //定义全局变量,并初始化
void test(void)
{
printf("in subfunction i=%d\n",i);
}
#include <stdio.h>
extern i; //声明引用全局变量i
int main(void)
{
printf("in main i=%d\n",i);
test();
return 0;
}
$ gcc -o test test.c file.c #编译连接
$ ./test #运行
结果:
in main i=5
in subfunction i=5
- register存储类型
声明为register的变量在由内存调入到CPU寄存器后,则常驻在CPU的寄存器中,因此访问register变量将在很大程度上提高效率,因为省去了变量由内存调入到寄存器过程中的好几个指令周期;
- 不能对register型变量进行取地址操作,它没有地址
- register型变量不能是浮点型的,因为CPU 不支持浮点型
- register型变量只能修饰局部变量
(寄存器数量少,如果把全局变量放进去,它从开始到结束都存在寄存器中可能导致CPU就无法使用寄存器)
如下示例:
#include <stdio.h>
int main(void)
{
register int i,sum=0;
for(i=0;i<10;i++)
sum=sum+1;
printf("%d\n",sum);
return 0;
}
- static存储类型
被声明为静态类型的变量,无论是全局的还是局部的,都存储在数据区中,其生命周期为整个程序,如果是静态局部变量,其作用域为一对{}内,如果是静态全局变量,其作用域为当前文件。静态变量如果没有被初始化,则自动初始化为0。静态变量只能够初始化一次。示例如下:
#include <stdio.h>
int sum(int a)
{
auto int c=0;
static int b=5;
c++;
b++;
printf("a=%d,\tc=%d,\tb=%d\t",a,c,b);
return (a+b+c);
}
int main()
{
int i;
int a=2;
for(i=0;i<5;i++)
printf("sum(a)=%d\n",sum(a));
return 0;
}
$ gcc -o test test.c
$ ./test
a=2, c=1, b=6 sum(a)=9
a=2, c=1, b=7 sum(a)=10
a=2, c=1, b=8 sum(a)=11
a=2, c=1, b=9 sum(a)=12
a=2, c=1, b=10 sum(a)=13
- 字符串常量
字符串常量存储在数据区中,其生存期为整个程序运行时间,但作用域为当前文件,示例如下:
#include <stdio.h>
char *a="hello";
void test()
{
char *c="hello";
if(a==c)
printf("yes,a==c\n");
else
printf("no,a!=c\n");
}
int main()
{
char *b="hello";
char *d="hello2";
if(a==b)
printf("yes,a==b\n");
else
printf("no,a!=b\n");
test();
if(a==d)
printf("yes,a==d\n");
else
printf("no,a!=d\n");
return 0;
}
$ gcc -o test test.c
$ ./test
yes,a==b
yes,a==c
no,a!=d
2. ARM寄存器
- System 和 User 模式在同一列
- 带三角的寄存器是一个模式特有的,不带三角的是很多模式通用的
- ARM 一共有40个寄存器(其他地方可能是37个,因为没有Monitor这个模式)
3. 专用寄存器
R15(PC,Program Counter)
程序计数器,用于存储当前取址指令的地址
- PC的值除了自动增加,也可以通过汇编指令人为修改
(当我们想让CPU执行一条特定指令时,比如,下图中我们下次想执行 ‘+’ 操作,我们就可直接把 PC的值改成 0x00)
R14(LR,Link Register)
链接寄存器,一般有以下两种用途:
- 执行跳转指令(BL/BLX)时,LR会自动保存跳转指令下一条指令的地址
程序需要返回时将LR的值复制到PC即可实现- 产生异常时,对应异常模式下的LR会自动保存被异常打断的指令的下
一条指令的地址,异常处理结束后将LR的值复制到PC可实现程序返回
R13(SP,Stack Pointer)
栈指针,用于存储当前模式下的栈顶地址
4. CPSR寄存器
CPSR(Current Program Status Register),当前程序状态寄存器
-
CPSR寄存器分为四个域,[31:24]为条件域用F表示、[23:16]为状
态域用S表示、[15:8]为预留域用X表示、[8:0]为控制域用C表示
-
Bit[4:0]
[10000]User, [10001]FIQ, [10010]IRQ, [10011]SVC
[10111]Abort, [11011]Undef, [11111]System, [10110]Monitor -
Bit[5]
[0]ARM状态, [1]Thumb状态 -
Bit[6]
[0]开启FIQ, [1]禁止FIQ -
Bit[7]
[0]开启IRQ, [1]禁止IRQ
-
Bit[28]
当运算器中进行加法运算且产生符号位进位时该位自动置1,否则为0
当运算器中进行减法运算且产生符号位借位时该位自动置0,否则为1 -
Bit[29]
当运算器中进行加法运算且产生进位时该位自动置1,否则为0
当运算器中进行减法运算且产生借位时该位自动置0,否则为1 -
Bit[30]
当运算器中产生了0的结果该位自动置1,否则为0 -
Bit[31]
当运算器中产生了负数的结果该位自动置1,否则为0
ARM异常处理
1. 异常
异常是什么?
处理器在正常执行程序的过程中可能会遇到一些不正常的事件发生
这时处理器就要将当前的程序暂停下来转而去处理这个异常的事件
异常事件处理完成之后再返回到被异常打断的点继续执行程序
2. 异常处理机制
异常处理机制是什么?
不同的处理器对异常的处理的流程大体相似,但是不同的处理器在具体实现的机制上有所不同;
比如处理器遇到哪些事件认为是异常事件遇到异常事件之后处理器有哪些动作、处理器如何跳转到异常处理程序如何处理异常、处理完异常之后又如何返回到被打断的程序继续执行等我们将这些细节的实现称为处理器的异常处理机制
3. ARM异常源
什么是ARM异常源?
导致异常产生的事件称为异常源
ARM异常源
FIQ | 快速中断请求引脚有效 | |
IRQ | 外部中断请求引脚有效 | |
Reset | 复位电平有效 | |
Software Interrupt | 执行swi指令 | |
Data Abort | 数据终止 | |
Prefetch Abort | 指令预取终止 | |
Undefined Instruction | 遇到不能处理的指令 |
FIQ、IRQ
一般由硬件产生(比如按键)Reset
会让CPU停止当前的程序,去复位Software Interrupt
CPU执行到某个特定的程序时,CPU会把自己打断Data Abort
CPU去内存里读数据时,出了问题(比如地址不或不允许访问)Prefetch Abort
当CPU去内存里预取指令时出了问题(比如地址不对)
注:模式是ARM的工作模式,异常源是异常源(ARM认为遇到这几件事后,认为这是异常)
4. ARM异常模式
异常模式和异常源
在ARM的基本工作模式中有5个属于异常模式,即ARM遇到异常后会切换成对应的异常模式
5. ARM异常响应
ARM产生异常后的动作(自动完成)
1.拷贝CPSR中的内容到对应异常模式下的SPSR_
<mode>
2.修改CPSR的值
- 修改中断禁止位禁止相应的中断
(正讲课时,一个老师来敲门,就去处理第一个老师的事情,这时第二个老师就不能再打断;
但如果是校长来了(优先级高),就要先去处理校长的事,处理完后,再回来处理第一个老师的事)- 修改模式位进入相应的异常模式
- 修改状态位进入ARM状态 (处理异常时,必须是ARM状态)
3.保存返回地址到对应异常模式下的LR_
<mode>
4.设置PC为相应的异常向量(异常向量表对应的地址)
注: 如果第一次来的中断是IRQ,IRQ就会被禁止(后来的IRQ被无视),但此时FIQ (优先级高) 不受限制;如果第一次来的是FIQ,则后来的 IRQ、FIQ都不处理
6. 异常向量表
异常向量表
- 异常向量表的本质是内存中的一段代码(32Byte)
- 表中为每个异常源分配了四个字节的存储空间
- 遇到异常后处理器自动将PC修改为对应的地址
- 因为异常向量表空间有限一般我们不会再这里写异常处理程序,而是在对应的位置写一条跳转指令使其跳转到指定的异常处理程序的入口
注:ARM的异常向量表的基地址默认在0x00地址,但可以通过配置协处理器来修改其地址
7. 异常返回
ARM异常返回的动作(自己编写)
1.将SPSR_的值复制给CPSR使处理器恢复之前的状态
2.将LR_的值复制给PC使程序跳转回被打断的地址继续执行
8. IRQ异常举例
注:整个过程CPSR保存的永远是当前程序运行状态,SPSR只是异常时对原来的CPSR进行备份
9. 异常优先级
10. FIQ和IRQ
FIQ的响应速度比IRQ快
1.FIQ在异常向量表位于最末
- 可直接把异常处理写在异常向量表之后,省去跳转
2.FIQ模式有5个私有寄存器(R8-R12)
- 执行中断处理程序前无需压栈保存寄存器,可直接处理中断
(IRQ模式和User模式共用寄存器,故IRQ处理中断首先要进行压栈(把User模式下的寄存器值暂时保存在栈里,防止被覆盖),然后处理中断)
3.FIQ的优先级高于IRQ
- 两个中断同时发生时先响应FIQ
- FIQ可以打断RIQ,但RIQ不能打断FIQ
注:不把FIQ的寄存器都设私有,是因为方便不同模式之间通信
11. LR寄存器、BL、IRQ中断
LR寄存器
R14(LR,Link Register)
链接寄存器,一般有以下两种用途:
- 执行跳转指令(BL/BLX)时,LR会自动保存跳转指令下一条指令的地址
程序需要返回时将LR的值复制到PC即可实现- 产生异常时,对应异常模式下的LR会自动保存被异常打断的指令的下
一条指令的地址,异常处理结束后将LR的值复制到PC可实现程序返回
BL
当执行BL指令时,指令执行过程中处理器内部就会将PC寄存器的值拷贝到
LR寄存器,然后再将LR寄存器中的值自减4, 所以LR寄存器中保存的就是
BL指令下一条指令的地址
该时刻PC=N+8 LR=N+4
IRQ中断
当执行一条指令时产生了一个IRQ中断,执行这条指令过程中处理器不会保
存返回地址,而是执行完成后才会保存,但执行完成后PC的值又会自动增4,
所以对于IRQ来说LR中保存的是被中断打断的指令的下下条指令的地址
该时刻PC=N+12 LR=N+8
ARM微架构
1. 流水线
2. 指令流水线
ARM指令流水线
-
ARM7采用3级流水线
-
ARM9采用5级流水线
-
Cortex-A9采用8级流水线
注:虽然流水线级数越来越多,但都是在三级流水线的基础上进行了细分
PC的作用(取指)
- 不管几级流水线,PC指向的永远是当前正在取指的指令,而当前正在执行
的指令的地址为PC-8 (需要理解)
关于流水线机制要知道
指令流水线机制的引入确实能够大大的提升指令执行的速度,但在实际执行程序的过程中很多情况下流水线时是无法形成的
比如芯片刚上电的前两个周期、执行跳转指令后的两个周期等
所以指令流水线的引入以及优化只能使平均指令周期不断的接近1而不可能真正的达到1,且流水线级数越多芯片设计的复程度就越高,芯片的功耗就越高
多核处理器
-
多核处理器
即一个SOC中集成了多个CPU核 -
作用
不同的线程可以运行在不同的核心中
做到真正的并发 -
资源
多核处理器共用外设与接口资源