复习提纲:
—处理器原理与结构
—ARM处理器的特点
—ARM指令集
—ARM汇编程序
—ARM程序优化
一、处理器原理与结构
要求:⑴掌握微处理器的基本结构(组成)
⑵掌握嵌入式处理器的分类方式
⑶掌握RISC和CISC指令集的特点
⑷熟悉常用嵌入式处理器的特点
⑸掌握选择嵌入式微处理器的原则
1、处理器分类
1)按指令系统
①CISC(如:Intel x86、AMD K6、TMS320XXX)
②RISC(如:PowerPC、ARM、AVR、MIPS)
2)按总线结构
①冯.诺依曼架构:指令和数据共享同一条总线
②哈佛架构:指令和数据使用两条不同的总线
3)按处理器字长
①8 bits(如8008,89C51)
②16bits(如8086,Ti 54X)
③32bits(如80486,Ti C3X,C6X)
④64bits(如Itanium,PowerPC970)
⑤128bits(?)
2、处理器工作原理
1)指令执行过程:取指(IF)—>译码(ID)—>执行(IE)—>写回(WB)
2)表示CPU性能的方法:
①CPI:执行一条指令所需的平均时钟周期
CPI = 一个程序的CPU时钟周期 / 该程序的指令数
②MIPS:每秒百万条指令
MIPS = 指令数 / (指令的执行时间 x )
= 时钟频率 / (CPI x )
③MFLOPS:每秒百万次浮点运算次数
MFLOPS = 浮点运算次数 / (浮点运算时间 x )
3、处理器的指令系统
1)指令格式
MOV |
AX, |
BX |
操作码 |
目标操作数 |
源操作数 |
操作数 |
2)寻址方式(老师的复习提纲里只有以下几种寻址方式)
①立即数寻址:ADD R4,#5 ; reg(R4) <— reg(R4) + 5
②寄存器寻址:ADD R4,R3 ; reg(R4) <— reg(R4) + reg(R3)
③直接寻址(又称绝对寻址):ADD R1,(1001) ; reg(R1) <— reg(R1) + Mem(1001)
④寄存器间接寻址:ADD R4,(R1) ; reg(R4) <— reg(R4) +Mem(reg(R1))
⑤存储器间接寻址:ADD R1,@(R2) ; reg(R4) <— reg(R4) +Mem[Mem[reg(R2)]]
4、高速缓存Cache
1)作用
内存的随度虽然很快,但还是和CPU不是一个数量级的,这样CPU的效率就浪费掉了。Cache的速度虽然还是比不上CPU的速度,但比内存快很多,可以起到一个缓冲的作用。CPU需要的数据先是Cache从内存中取出,等到CPU要用的时候再传给CPU。这样可以提高不少效率
3)Cache映射方式
①全相联映射方式:主存的任意一块可以映像到Cache中任意一块
②直接相联映像方式:主存中一块只能映象到Cache的一个特定的块中
③组相联映射方式:是对全相联映象和直接映象的一种折中的处理方案。组间全相联映象,组内直接映象。
3)替换方法
①随机法(RAND法):随机地确定替换的存储块。
②先进先出法(FIFO法):选择最先调入的那个块进行替换
③最近最少使用法(LRU法):依据各块使用的情况1,总是选择那个最近最少使用的块被替换
4)写通(WT)和写回(WB)的区别
①写通法是指CPU在执行写操作时,必须把数据同时写入cache和主存
②写回法是指CPU在执行写操作时,被写的数据只写入cache,不写入主存。仅当需要替换时,才把已经修改的cache块写回到主存中
③写通和写回的区别主要体现在执行写操作时数据是否同时写入主存
5)影响命中率的因素
①CPU在访问内存时,首先判断所要访问的内容是否在cache中,如果在,就称为“命中”,此时CPU直接从cache中调用该内容;否则,就称为“不命中”,CPU只好去内存中调用所需的子程序或指令了
②影响cache命中率的因素很多,如cache的容量,块的大小,映射方式,替换策略以及程序执行中地址流的分布情况等。一般来说:
—cache容量越大则命中率越高,当容量达到一定程度后,容量的增加命中率的改变并不大
—cache块容量加大,命中率也明显增加,但增加到一定值之后反而出现命中率下降的现象
—直接映射方式命中率较低,全相联映射方式命中率比较高,在组相联方式中,组数分得越多,则命中率下降
6)程序对Cache命中率的影响
编写程序时,对同一个变量处理过程尽量放置在相近位置,这样可以提高Cache的命中率。
5、流水线
1)作用:使用流水线可以在取下一条指令的同时译码和执行其它指令,从而加快执行速度
2)吞吐率:流水线在特定的时间内可以处理的任务或输出数据结果的数量
3)加速比:采用串行模式之后的工作速度和采用流水线模式后的工作速度的比值
4)效率:处于工作状态的部件和总部件的比值
5)影响加速比的因素
①多个任务在同一时间周期内争用同一个流水段
②数据依赖(比如,A运算必须得到B运算的结果,但是,B运算还没有开始,A运算动作就必须等待,直到B运算完成,两次运算不能同时执行)
6)解决指令相关形式及解决方法
①增加运算部件的数量来使他们不必争用同一个部件
②用指令调度的方法重新安排指令或运算的顺序
7)超级流水线:把每一个流水级分成多个子流水级,而在每一个子流水级中取出的仍只有一条指令
8)超线程、超流水线
超线程(HT, Hyper-Threading)是英特尔研发的一个技术,于2002年发布。英特尔的HT技术是在CPU内部仅复制必要的资源、让CPU模拟成两个线程;也就是一个实体核心,两个逻辑线程,在一单位时间内处理两个线程的工作,模拟双核心、双线程运作。
超流水线:把每一个流水级分成多个子流水级,而在每一个子流水级中取出的仍只有一条指令
6、MMU
1)作用:
它是中央处理器(CPU)中用来管理虚拟存储器、物理存储器的控制线路,同时也负责虚拟地址映射为物理地址,以及提供硬件机制的内存访问授权,多用户多进程操作系统
ARM系列的MMU介绍
(ARM出品的CPU,MMU作为一个协处理器存在。根据不同的系列有不同的搭配。需要查询DATASHEET才可知道是否有MMU。如果有的话,一定是编号为15的协处理器。可以提供32BIT共4G的地址空间
ARM MMU提供的分页机制有1K/4K/64K3种模式。目前操作系统通常使用的是4K模式
涉及的寄存器,全部位于协处理器15
ARM CPU地址转换涉及三种地址:虚拟地址(VA,Virtual Address),变换后的虚拟地址(MVA,Modified Virtual Address),物理地址(PA,Physical Address)
没有启动MMU时,CPU核心、cache、MMU、外设等所有部件使用的都是物理地址。启动MMU后,CPU核心对外发出的是虚拟地址VA,VA被转换为MVA供cache、MMU使用,并再次被转换为PA,最后使用PA读取实际设备
现代操作系统都提供了一种内存管理的抽象,即虚拟内存。进程使用虚拟内存中的地址,由操作系统协助相关硬件,把它“转换”成真正的物理地址,这个“转换”是所有问题讨论的关键。有了这样的抽象,一个程序,就可以使用比真实物理地址大得多的地址空间)
2)物理地址:
是指出现在CPU外部地址总线上的寻址物理内存的地址信号,是地址交换的最终结果地址。如果启用了分页机制,那么线性地址会使用页目录和页表中的项变换成物理地址。如果没有启动分页机制,那么线性地址就直接成为物理地址了
3)虚拟地址:
①虚拟内存是指计算机呈现出要比实际拥有的内存大得多的内存量。因此它允许程序员编制并运行比实际系统拥有的内存大得多的程序。这使得许多大型项目也能够在具有有限内存资源的系统上实现
②整个内存的抽象描述:比如32bitCPU,其虚拟内存空间为4G,虚拟地址即这个4G空间中的地址。例如,一个0x08000000内存地址。CPU启用了MMU,CPU核发出的地址将被MMU截获,从CPU到MMU的地址称为虚拟地址(Virtual Addres,简称VA),而MMU将这个地址翻译成另一个地址发到CPU芯片的外部地址引脚上,也就是将虚拟地址映射成物理地址
5)逻辑地址
一个逻辑地址,是由一个段标识符加上一个指定段内相对地址的偏移量,表示为[段标识符:段内偏移量] 是指由程序产生的与段相关的偏移地址部分
6)地址转换的过程
虚拟地址到物理地址的转化方法是与体系结构相关的。一般来说有分段、分页两种方式。以现在的x86 CPU为例,分段分页都是支持的。MMU负责从虚拟地址到物理地址的转化。逻辑地址是段标识+段内偏移量的形式,MMU通过查询段表,可以把逻辑地址转化为线性地址。如果CPU没有开启分页功能,那么线性地址就是物理地址;如果CPU开启了分页功能,MMU还需要查询页表来将线性地址转化为物理地址:逻辑地址——(段表)——>线性地址——(页表)——>物理地址
不同的逻辑地址可以映射到同一个线性地址上;不同的线性地址也可以映射到用一个物理地址上;所以是多对一的关系。另外,同一个线性地址,在发生换页以后,也可能被重新装载到另一个物理地址上。所以这种多对一的映射关系也会随时间发生变化
7)转换方式
页、段、段页
7、嵌入式处理器
1)特点(与通用处理器相比):①功耗低
②稳定性好
③体积小
④成本低
2)种类:①MCU(传统单片机)
—优点:应用广、开发资源丰富、使用方便、成本低
—缺点:性能低
—趋势:应用集成,结构可编程,混合器件,32位
②X86(老师ppt上是这么写的)
—优点:软件移植方便,开发快捷,软件资源多,功能强大
—缺点:体积大,功耗高,实时性差
—趋势:多核,低功耗
③DSP
—优点:速度快,信号处理能力强
—缺点:IO能力弱,开发平台使用不方便
—趋势:多核,SOC化
④SOC
—优点:功能全面,系统设计方便,系统体积小,功耗低
—缺点:可裁剪性差
—趋势:面向应用,多核,动态可编程
8)选择因素:①性能
②功耗
③体积
④成本
⑤开发工具
…...
二、ARM处理器
要求:⑴ARM处理器的特点
⑵ARM处理器名称中字母的意思
⑶ARM处理器不同模式下寄存器的使用
⑷ARM处理器异常事件及响应过程
1、特点
1)结构
①寄存器的特点
—数量:最多有18个活动寄存器,其中16个数据寄存器,2个状态寄存器
—使用:r0~r12:通用寄存器
r13:通常用作堆栈指针sp,保存当前处理器模式堆栈的栈顶
r14:又称链接寄存器lr,保存调用子程序的返回地址
r15:程序计数器pc,其内容是处理器要取的下一条指令的地址
cpsr:当前程序状态寄存器
spsr:备份的程序状态寄存器
2)功耗:比起x86来说,功耗低
2、版本符号的意思
3、处理器的状态模式
①用户模式(usr):正常程序执行模式
② 快速中断模式(fiq):支持高速数据传送或通道处理
中断模式(irq):用于通用的中断处理
管理模式(svc):操作系统保护模式
中止模式(abt):实现虚拟存储器或者存储器保护
未定义模式(und):支持硬件协处理器的软件仿真
③系统模式(sys):运行特权操作系统任务
4、协处理器的使用
协处理器对当前在流水线译码段的指令进行译码,并检查指令是否为协处理器指令。若当前在译码段的指令是相关的协处理器指令,则协处理器试图执行这些指令,协处理器与主处理器通过CPA CPB握手
三、ARM指令集
要求:⑴掌握ARM和THUMB指令的差别
⑵掌握特殊指令的意思和使用方法
⑶掌握两个伪指令的使用
⑷掌握常用指令操作数的范围和执行结果
1. 指令格式
2、指令类型
1)通用数据操作指令
2)存储器访问指令
3)跳转指令
4)乘法指令
5)协处理器指令
6)其它指令
3、数据处理指令
1)移位
助记符 |
说明 |
移位操作 |
结果 |
Y值 |
LSL |
逻辑左移 |
x LSL y |
x << y |
#0~31 or Rs |
LSR |
逻辑右移 |
x LSR y |
(unsigned)x >> y |
#1~32 or Rs |
ASR |
算术右移 |
x ASR y |
(signed)x >> y |
#1~32 or Rs |
ROR |
循环右移 |
x ROR y |
((unsigned)x >> y) | (x << (32 - y)) |
#1~32 or Rs |
RRX |
扩展的循环右移 |
x RRX |
(c flag << 31) | ((unsigned)x >> 1) |
none |
2)算术运算
语法:<指令>{
ADC |
32位带进位加法 |
Rd = Rn + N + C |
ADD |
32位加法 |
Rd = Rn + N |
RSB |
32位逆向减法 |
Rd = N – Rn |
RSC |
带进位的32位逆向减法 |
Rd = N – Rn - !C |
SBC |
带进位的32位减法 |
Rd = Rn – N - !C |
SUB |
32位减法 |
Rd = Rn – N |
3)逻辑运算
语法:<指令>{
AND |
32位逻辑“与” |
Rd = Rn & N |
ORR |
32位逻辑“或” |
Rd = Rn | N |
EOR |
32位逻辑“异或” |
Rd = Rn ^ N |
BIC |
逻辑位清除(AND NOT) |
Rd = Rn &~ N |
4)数据比较与数据测试(只改变cpsr中的条件标志,不影响参与比较的寄存器内容)
语法:<指令>{
CMN |
取负比较 |
标记根据Rn + N的值设置 |
CMP |
比较 |
标记根据 Rn – N的值设置 |
TEQ |
等值测试 |
标记根据Rn ^ N的值设置 |
TST |
位测试 |
标记根据Rn & N的值设置 |
2、存储器数据读写
1)LDR和STR
语法:
LDR |
把一个字装入一个寄存器 |
Rd <—mem32[address] |
STR |
从一个寄存器保存一个字或一个字节 |
Rd —>mem32[address] |
2)LDM和STM
语法:
LDM |
装载多个寄存器 |
{Rn}*N <—mem32[start address + 4 * N] |
STM |
保存多个寄存器 |
{Rn}*N—> mem32[start address + 4 * N] |
3、寄存器数据装载
1)MOV和MVN
语法:<指令>{cond} {S} Rd,N
MOV |
把一个32位数送到一个寄存器 |
Rd = N |
MVN |
把一个32位数的“非”送到一个寄存器 |
Rd = ~N |
5、跳转
语法:B{
BL{
BX{
BLX{
B |
跳转 |
pc = label |
BL |
带返回的跳转 |
pc = label lr = BL后面的第一条指令地址 |
BX |
跳转并切换状态 |
pc = Rm & 0xfffffffe, T = Rm & 1 |
BLX |
带返回的跳转并切换状态 |
pc = label,T = 1 pc = Rm & 0fffffffe,T = Rm & 1 lr = BLX后面的第一条指令地址 |
6、乘法指令
语法:MLA{
MUL{
<指令>{
MLA |
乘累加 |
Rd = (Rm * Rs) + Rn |
MUL |
乘法 |
Rd = Rm * Rs |
SMLAL |
长整型有符号乘累加 |
[RdHi,RdLo] = [RdHi,RdLo] + (Rm * Rs) |
SMULL |
长整型有符号乘法 |
[RdHi,RdLo] = Rm * Rs |
UMLAL |
长整型无符号乘累加 |
[RdHi,RdLo] = [RdHi,RdLo] + (Rm * Rs) |
UMULL |
长整型无符号乘法 |
[RdHi,RdLo] = Rm * Rs |
7、协处理器访问指令
语法:CDP{
CDP |
协处理器数据处理——在协处理器内部执行一个数据处理操作 |
MRC MCR |
协处理器寄存器传输——把数据送入或取出协处理器寄存器 |
LDC STC |
协处理器内存传输——从协处理器装载/存储一个内存数据块 |
8、软中断
语法:SWI {
SWI |
软件中断 |
lr_svc = SWI指令后面的指令地址 spsr_svc = cpsr pc = vectors + 0x8 cpsr模式 = SVC cpsr I = 1(屏蔽IRQ中断) |
9、状态寄存器访问
语法:MRS{
MSR{
MSR{
注意:在指令语法中可看到一个称为fields域的项),它可以是控制(c)、扩展(x)、状态(s)及标志(f)的任意组合
MRS |
把程序状态寄存器的值送到一个通用寄存器 |
Rd = spr |
MSR |
把通用寄存器的值送到程序状态寄存器 |
psr[field] = Rm |
MSR |
把一个立即数送到程序状态寄存器 |
Psr[field] = immediate |
10、伪指令
语法:LDR Rd,=constant
ADR Rd, label
LDR |
常量装载伪指令 |
Rd = 32位常量 |
ADR |
地址装载伪指令 |
Rd = 32位相对地址 |
11、Thumb指令的特点与ARM指令的区别
1)仅能使用ARM的r0~r7 8个通用寄存器
2)没有条件执行
3)指令自动更新标志,不需加(S)
4)仅有LDMIA(STMIA)
—仅有IA一种形式
—必须加“!”
5)在数据运算指令中不支持第二操作数移位
四、汇编程序
要求:(1)掌握ARM汇编程序的基本语法
(2)能够编写实现简单运算功能的汇编语言
1、汇编语言程序的格式(ADS)
AREA ARMex,CODE,READONLY
ENTRY
……
END
2、变量空间分配
1)命令
①全局
—GBLA,分配算术变量空间,初始化为0
—GBLL,分配逻辑变量空间,初始化为{FALSE}
—GBLS,分配字符串变量,初始化为NULL
②局部
—LCLA,分配算术变量空间,初始化为0
—LCLL,分配逻辑变量空间,初始化为{FALSE}
—LCLS,分配字符串半两空间,初始化为NULL
2)使用方法
①GBLX variable
②LCLX variable
3、变量空间初始化
1)命令
①SETA
②SETL
③SETS
2)使用方法
Variable SETX expression
注:GBLA声明的变量variable不能作为地址使用,只能以常量的方式进行访问
4、内存区域分配
1)SPACE
①语法:[label] SPACE expression
②Example: Data1 SPACE 256
③可用%代替SPACE
2)DCX{U-对齐选项}(加U不需要对齐)
①种类
—DCB-{1byte}
—DCDand DCDU-(4byte)
—DCFDand DCFDU-(double-pression float point)
—DCFSand DCFSU-(single-pression float point)
—DCI-(like DCD and DCDU)
—DCQand DCQU-(8 bytes)
—DCWand DCWU-(2 bytes)
②用法
{label} DCX{U} expression,{expression,……}
5、代码模块的声明
AREA Myprogram,CODE,READONLY
6、数据模块的声明
AREA mydata,DATA,READWRITE
src DCD 1,2,3,4,,5,6,7,8,1,2,3,4,5,6,7,8,1,2,3,4
dst DCD 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
7、装入寄存器常数(见上面伪指令部分)
8、宏定义
①定义格式
②替换形式
9、多寄存器的读写
1)指令:LDM、TM
2)类型:IA,IB,DA,DB
3)指令格式:LDMIA r0!,{r2-r3}
10、条件执行
11、函数声明和调用
n 汇编程序之间
n 同一个文件中
n 不在同一个文件中
答:1)在同一个文件中,调用子程序主要用 BL label 和mov PC, LR 两条指令
Label 为子程序第一条指令标号,bl指令将返回地址放到LR寄存器中,并将子程序的首地址赋予PC。通常在调用子程序的之前保存当前工作寄存器。特别注意的是:当嵌套调用子程序是,还必须保存LR寄存器。将LR寄存器的值赋给PC,实现子程序的返回。
n Example:函数在同一个源文件
AREAsubrout, CODE, READONLY
ENTRY
start
MOV r0, #10
MOV r1, #3
BL doadd
……
doadd
ADD r0, r0, r1
MOV pc, lr
END
2)不在同一个文件中
File 1要调用file2中一段代码,label
需要在file1中 声明import label(引入全局变量) 然后bl label
在file2中声明export label
Example:;file1
AREA Main, CODE, READONLY
IMPORT Testprg; import声明为全局变量
ENTRY
……
Mainprg
BL Testprg
Stop
……
END
;file2
AREA Test, CODE, READONLY
EXPORT Testprg
Testprg
MOV r5, #2
ADD r3, r5, #1
MOV pc,lr
END
n 汇编和C之间
n 汇编调用C的函数
答 :1)
在c程序中,声明好c函数 f(),以备调用,在汇编程序import f,然后 bl f
需要提前对r0 r1 r2 r3 等赋好值
Example:
C文件
int g(int a, int b, int c, int d, int e)
{
return a + b + c + d + e;
}
汇编文件
AREA f, CODE, READONLY
IMPORT g
LDR r4, =data
LDR r0, [r4], #4
LDR r1, [r4], #4
LDR r2, [r4], #4
LDR r3, [r4], #4
LDR r5, [r4]
STR r5, [sp, #-4]!
BL g
ADD sp, sp, #4
END
n C调用汇编得函数
答:1) c程序和汇编程序在不同文件
在asm文件中,写好一段程序标签为label,然后export
在 c文件中,export label,然后就可以在c文件中直接调用,其参数默认存在 r0 r1 r2 r3 中。
Example:
AREA SCopy, CODE, READONLY
EXPORT strcopy
strcopy
LDRB r2, [r1],#1 ; Load byte and update address.
STRBr2, [r0],#1 ;
CMP r2, #0 ; Check for zero terminator.
BNE strcopy ;Keep going if not.
MOV pc,lr ;Return.
END
#include
extern void strcopy(char *d, const char *s); //ASM
int main()
{ const char *srcstr = "First string - source ";
char dststr[] = "Second string - destination ";
strcopy(dststr,srcstr);
printf("After copying:\n");
printf(" %s\n %s\n",srcstr,dststr);
return (0);
}
2) c文件内嵌汇编
需要用__asm(“指令[;指令]”)格式
Example:
#include
Voidmy_strcpy(char *src, const char *dst)
{
Intch;
__asm
{
…..
}
}
Intmain(void)
{
Constchar *a=”hello world”;
Charb[20];
__asm
{
mov r0,a;
mov r1,b;
bl my_strcpy, {r0,r1} //处理器转移到r0 r1
}
printf(“originalstring: %s\n”,a);
printf(“copiedstring: %s\n”,b);
return 0;
}
n 汇编和C++之间
答:1)在c++中为了调用汇编函数,将汇编同c语言一样定义为 extern “C”
Example:
struct S {
S(int s){i=s; }
int i;
};
extern"C" void asmfunc(S *);
int f()
{
S s(2);
asmfunc(&s);
returns.i * 3;
}
汇编程序
AREA Asm, CODE
EXPORT asmfunc
asmfunc ; the definition of the Asm
LDR r1, [r0] ; function to be called from C++
ADD r1, r1, #5
STR r1, [r0]
MOV pc, lr
END
在汇编函数中调用C++
在c++ 中同样使用 extern "C" voidfunction( );
在汇编代码中import function c++函数 然后bl function
n 调用过程中参数的传递和结果的返回
答:1)参数个数不变的子程序参数传输
①<=4,r0-r3,依次
②>4,存入数据栈,最后一个数据先入栈(栈是“后进先出”)
2)参数个数可变的子程序参数传输
①前4个整数通过r0-r3
②其他通过堆栈来传递
3)浮点参数传递(顺序传递)
①FPA
f0-f7(s0-s7,d0-d7,e0-e7)
②VFP(d0-d15,s0-s31)
4)参数返回规则
①结果为32bit整数时,通过r0传递
②结果为64bit整数时,通过r0和r1传递
③结果为浮点数时,通过浮点寄存器返回(f0,d0)
④更多的寄存器通过内存返回
五、程序及优化
要求:(1)掌握异常处理程序的编写和安装的方法
(2)掌握ARM程序函数调用过程中参数的传递和结果返回方式
(3)掌握嵌入式C语言的特点
(4)掌握控制Link过程中内存分配方法
(5)掌握常用的程序优化方法
(6)掌握ARM系统初始化的过程
(7)了解Bootloader的组成
1、如何处理异常?
1)执行完当前指令
①非流水线
②流水线
2)处理异常中断
3)返回发生中断的下一条指令
4)处理器响应异常的过程
2、ARM中断向量表的特点
1)指定异常中断及其中断处理程序之间的对应关系
2)ARM向量表的大小32个字节,每个异常中断占据4个字节
3)每个字存放跳转指令或向PC赋值的数据访问指令
4)通常存放在存储器地址的低端
3、中断向量表
向量地址 |
异常中断类型 |
异常中断模式 |
优先级 |
0x0 |
Reset |
特权(SVC) |
1 |
0x4 |
Undefined Instruction |
未定义指令中止模式 |
6 |
0x8 |
SWI |
特权模式 |
6 |
0xc |
Prefetch Abort |
中止模式 |
5 |
0x10 |
Data Abort |
中止模式 |
2 |
0x14 |
Reserved |
未使用 |
未使用 |
0x18 |
IRQ |
IRQ模式 |
4 |
0x1c |
FIQ |
FIQ模式 |
3 |
4、退出异常处理的过程
1)将lr_mode的内容复制到PC中(执行指令)
2)将spsr_mode中的内容复制到当前的cpsr中
5、 中断服务程序的安装
1) 静态(程序初试化时的安装)
Vector_Init_Block
LDR PC, Reset_Addr
LDR PC, Undefined_Addr
LDR PC, SWI_Addr
LDR PC, Prefetch_Addr
LDR PC, Abort_Addr
NOP ;Reserved vector
LDR PC, IRQ_Addr
LDR PC, FIQ_Addr
Reset_Addr DCD Start_Boot
Undefined_Addr DCD Undefined_Handler
SWI_Addr DCD SWI_Handler
Prefetch_Addr DCD Prefetch_Handler
Abort_Addr DCD Abort_Handler
DCD 0 ;Reserved vector
IRQ_Addr DCD IRQ_Handler
FIQ_Addr DCD FIQ_Handler
将数据从ROM中Copy到RAM中
MOV r8, #0
ADR r9, Vector_Init_Block
LDMIA r9!,{r0-r7} ;Copy the vectors
STMIA r8!,{r0-r7}
LDMIA r9!,{r0-r7} ;Copy the DCD'edaddresses
STMIA r8!,{r0-r7}
2) 动态(程序运行过程中的安装)
A.向量表中使用跳转指令
步骤:
1)读取中断处理程序的地址addr1
2)将addr1减去该中断对应的中断向量的地址vector1
3)addr=Addr1-vector1-8(允许指令预取)
4)addr LSR #2
5)if(addr and0xff000000 ==0)
6)addr or0xea00 0000
7)结果写回中断向量表
unsignedInstall_Handler (unsigned *handlerloc, unsigned *vector)
{ unsigned vec, oldvec;
vec = ((unsigned)handlerloc- (unsigned)vector - 0x8)〉〉2;
if ((vec & 0xFF000000) != 0)
{ exit (1);}
vec = 0xEa000000 | vec;
oldvec = *vector;
*vector = vec;
return (oldvec);
}
unsigned *irqvec= (unsigned *)0x18;
Install_Handler((unsigned)IRQHandler_location, irqvec);
B.向量表中使用数据读写指令 (Load PC)
步骤:
1)读取中断处理程序的地址表中addr1
2)将addr1减去该中断对应的中断向量的地址vector1
3)addr=Addr1-vector1-8(允许指令预取)
4)addr or0xe59f f000
5)结果写回中断向量表
6)把中断服务程序入口地址存入地址表中(Addr1只是中断服务程序入口地址表的地址,不是中断服务程序的入口地址。要实现中断服务,还需要修改地址表。)
unsignedInstall_Handler (unsigned *location, unsigned *vector)
{ unsigned vec, oldvec;
vec =(location - vector -0x8) | 0xe59ff000
oldvec = *vector;
*vector = vec;
*location=IQR_Handler;
return (oldvec);
}
6、 软中断服务程序的安装及功能调用方式
处理过程 (用户模式调用系统模式下的程序)
第一级:从内存中读取SWI指令,获取其中的24位立即数
第二级:根据获取的立即数,进入相应的SWI功能处理程序
第一级
AREA TopLevelSwi,CODE, READONLY
EXPORT SWI_Handler
SWI_Handler
STMFD sp!,{r0-r12,lr}
LDR r0,[lr,#-4]
BIC r0,r0,#0xff000000 ; 参数1
MOV R1, SP ; 参数2,传递堆栈指针
; Use value in r0 to determine which SWI
; routine to execute.
; BL C_SWI_Handler
LDMFD sp!, {r0-r12,pc}^
END
^ 后缀的解释:
1、若在LDM指令用寄存器列表中含有pc时,会附加将SPSR拷贝到CPSR的动作。 可以引起模式切换,通常用于中断返回,这种情况用户模式不能用,因为用户模式没有SPSR
2、若列表中没有PC,则加载/存储的是用户模式寄存器。而不是当前模式。可用于中断中访问用户模式中的重名寄存器
第二级
C
voidC_SWI_handler (unsigned number)
{ switch (number)
{case 0 : /* SWInumber 0 code */
break;
case 1 : /* SWInumber 1 code */
break;
……
default : /* Unknown SWI -report error */
}
}
汇编调用
BL C_SWI_handler
7、嵌入式应用设计
1)生成bin文件的过程
2)如何指定link过程中内存地址的映射
3)Scatter文件编写
4)根据link信息,估计系统存储资源的需求
①Codesize,Rodata,Rwdata,Zidata的意思
è Codesize 代码所占空间;Rodata:只读数据的大小;Rwdata:可读写数据大小;Zidata: 未初始化的可读写数据大小。
②ROM
③RAM
8、C语言
9、绝对地址的访问方式
LDR Rd, =constant 或 MOV Rd,#constant
10、 volatile
用来定义变量,易变的,每次访问都要到存储器取数据
防止编译器对代码进行优化,比如对端口的实时访问必须加这个关键字
1)数据类型
①char,signed char,unsigned char
②short,signed short,unsigned short
③enum
④int,signed int,unsigned int
⑤long,signed long,unsigned long
⑥long long,signed long long
⑦pointer
⑧float
⑨double,long double
⑩signed,unsigned
2)优化
3)程序的位置无关
11、 程序优化(代码举例分析请查看老师ppt相应章节)
1)提高速度
I、处理器相关
⑴变量类型
—计算过程中的局部变量应尽量避免使用char和short数据类型,除非需要使用char和short的数据溢出特性
—存在主存中的数组和全局变量尽可能使用小尺寸的数据类型,以节省存储空间
⑵参数类型
—尽量用int或unsigned int型参数
—对于返回值尽量避免使用char和short类型
—防止编译器做不必要的类型转换
⑶位域
—避免使用位域,而使用#define来定义屏蔽位
—使用逻辑运算对位域进行测试、取反等操作
⑷局部变量与全局变量
—全局变量在存储器中,而函数中的局部变量(数量较少)在寄存器中
—用局部变量替换全局变量,减少程序访问存储器的次数
⑸循环展开
—可以减少程序的跳转,从而降低流水线中断的次数,提高程序执行的效率
⑹去除相关性
—消除数据之间的相关性,可以更有效地利用流水线,提高程序的执行速度
⑺数据Cache
—访问连续的数据区,才能充分发挥数据Cache的优势
⑻片内RAM与片外RAM
—将计算量较大的代码和常用数据放到片内RAM中可以提高程序执行的速度
⑼Float
—对于没有浮点运算指令的处理器,把浮点数转换成整数,用整数运算替代浮点运算,提高程序的执行速度
⑽边界不对齐
—在内存空间没有严格限制的情况下,尽量使数据边界对齐
II、处理器无关
⑴有符号数与无符号数
—对于非负数的计算,采用无符号数类型效率更高
⑵固定次数循环
—尽量采用递减循环
⑶不定次数循环
—do-while比for产生的代码更有效
⑷循环展开
—通过循环展开,可以减少循环开销,提高程序执行速度
⑸调用过程中的参数传递
—尽量限制参数的个数,不要超过4个(因为4个以内是通过寄存器访问,5个及以上是通过压栈/退栈方式访问,调用开销大)
⑹短函数的调用
—把较小的被调用函数和调用函数放在同一个源文件中,并且先定义后调用。编译器就可以优化调用函数或内联较小的函数
⑺指针别名
—建立一个新的局部变量来保存表达式的值
—避免使用局部变量的地址
⑻代码替换
—用运算量小但功能相同的表达式替换原来复杂的表达式,提高程序的速度
⑼减少重复运算
—将不变计算移到循环体外,减少重复计算,提高程序速度
⑽避免结构体深度访问
— 对于二级元素访问,增添临时变量,将后续的二级访问改为一级访问,减少了部分计算
⑾慢速存储器访问
—当访问片外RAM或者Flash中的数据时,当需要多次读取或修改时,应遵循“读—改—写”模式,即首先读取片外RAM或者Flash中的数据,将其保存在片内RAM中,针对本地变量进行计算,计算完毕后再写回到片外RAM或者Flash中,而不是每修改一次就进行回写操作
⑿查找表
—对既消耗时间又消耗资源的运算,应尽量使用查找表的方式,并且将数据表置于程序存储区,但这需要预先计算出表的所有项
—如果表很大,则直接生成所需的表比较困难,此时可以在启动时的初始化函数中先计算,然后在数据存储器中生成所需的表,以后在程序运行直接查表就可以了,减少了程序执行过程中重复计算的工作量
⒀Switch-case
—为了提高速度,设法把具体的情况按照它们发生的相对频率排序。即把最可能发生的情况放在第一位,最不可能的情况放在最后
2)减少空间
I、代码空间
⑴结构体数据安排
—结构元素要按照元素的大小排列,从最小的元素开始,最大的元素安排在最后
—避免使用很大的结构体,可以用层次化的小结构体来代替
⑵ARM与THUMB
—对于支持多种指令长度的处理器,选择短指令长度的指令集可以减少程序指令所占有的空间
⑶运算替换
—用指令直接支持的计算替换需要用库函数实现的运算,可以减少程序的指令数
⑷循环处理
—增加循环次数,减少循环体内指令数,可以减少指令空间
⑸内联函数
—取消内联函数,可以减少代码所占有的空间
II、数据空间
⑴数据类型
—在满足精度要求的前提下,选用小字宽的数据类型
⑵空间复用
—复用已申请空间,减少对总数据空间的需求
⑶数组与指针
—用指针代替数组,动态分配内存空间并及时释放,将更有效地利用存储空间
3)降低功耗
10、不同优化方法的特点
11、系统初始化过程
12、Bootloader
常见ARM Bootloader(Linux)
l U-boot
l RedBoot
l Vivi
1)结构
Uboot 工程结构
l board:平台依赖 存放电路板相关的目录文件
l cpu:平台依赖 存放CPU相关的目录文件
l lib_arm :平台依赖 存放对ARM体系结构通用的文件。
l common :通用的多功能函数实现。
l include:通用头文件和开发板配置文件
l lib_generic:通用库函数的实现
l net:存放网络协议的程序
l drivers:通用的设备驱动程序。