ARM体系结构


【1】ARM相关理论基础介绍

1、ARM相关的概念

指令:就是一条汇编代码,可以完成一个特定的功能
指令就是一堆CMOS组成的可以完成特定功能的电路图
CMOS:栅极 源极 漏极 开关特性
NMOS:栅极为高电平,源极和漏极导通
	 栅极为低电平,源极和漏极截止
PMOS:栅极为低电平,源极和漏极导通
	 栅极为高电平,源极和漏极截止

image

内核(ARM内核) = 运算器 + 控制器 + 存储器(寄存器)
运算器:汇编指令
控制器:控制程序运行
存储器:数据

ARM公司将ARM内核授权给芯片厂家,使用ARM的内核生产对应的芯片
ARM指令集的架构(ARM-V1 到 ARM-V8)
1.此架构支持的是32位ARM指令集,还有64位的ARM指令集
	ARM-V1 ~ ARM-V7架构属于32位ARM指令集
	ARM-V8机构属于64位ARM指令集
2.架构的版本越高,支持的指令个数越多
	指令实现的功能越复杂
总结:指令集->架构->内核->SOC

2、ARM的发展史

1. 1978年,CPU公司 
	Cambridge processing Unit
	
2. 1979年    Acorn

3. 1985年, 32位,8MHz, 
    使用的精简指令集RISC
    芯片的名字ARM 
4.1990年,转折点
iphone 150万英镑 VLSI: 25万英镑
ARM公司12工程师+技术专利:150万英镑
ARM公司-》 Advanced RISC Machine

ARM公司不生产芯片,做技术的授权,
提供解决方案。芯片厂家拿到技术授权之后,
根据需求设计生产自己的SOC。
5. 2016年,日本软银收购

6. 2020年,英伟达(未收购成功)

ARM指令集 
RISC-V指令:完全开源

3、精简指令集和复杂指令集的区别

精简指令集(RISC)ARM架构
1.精简指令集从复杂指令集中提取一些使用频率较高,或者相对简单的指令组合成精简指令集
2.精简指令集的指令周期和指令宽度都是固定的
3.指令的周期:执行一条指令所需要的时间
4.指令的宽度:一条指令编译器成机器码之后,占用内存空间的大小
复杂指令集(CISC)X86架构
1.复杂指令集更加注重指令的功能性和指令的运算能力
2.复杂指令集的指令宽度和指令的周期不固定

4、ARM产品的分布

cortex-A:高端处理器,可以运行linux系统(手机、平板)
cortex-R:主要针对的实时处理(汽车电子,摄像机,照相机)
cortex-M:低端电子产品,运行逻辑程序(物联网设备开发)
		 stm32/stm8(意法半导体公司)
		 可以运行实时的操作系统
arm内核命令的规范:
早期:ARM7 ARM9 ARM10 ARM11
从ARM11之后ARM内核的命名调整为Cortex-A


5、ARM数据的约定

ARM-V7约定:
    char :8bits 
    half word:16bits 
    word:32bits 
    double word :64bits(cortex-a系列才支持)

ARM-V8约定:
    char :8bits 
    half word:16bits 
    word:32bits 
    double word :64bits(cortex-a系列才支持)
    quad word:128bits(ARM-V8架构才支持)

6、ARM处理器的工作模式

ARM处理器的工作模式就是不同的代码执行在不同的工作模式下,完成特定的功能,ARM处理器的7种工作模式
1.ARM7 ARM9 ARM11有7种工作模式
2.Cortex-A系列有8种工作模式,多了monitor(安全监控模式)
用户模式 user 应用层
系统模式 system user的特权模式
普通中断模式 IRQ 外设中断
快速中断模式 FIQ 高速数据
未定义模式 Undef 指令未定义
中止模式 Abort 取指/取数据中止
管理模式 SVC 复位/软中断

7、ARM寄存器组织

8、特殊的寄存器

1.r13 -->别名sp:the stack pointer 栈指针寄存器
	主要存放的是栈顶的地址
2.r14 -->别名lr:the linking register 链接寄存器
	主要用于保存函数返回地址的
3.r15 -->别名pc:the program counter 程序计数寄存器
	存储的是当前取指指令的地址
	取完一条指令之后,PC中的值会自动加4指向下一条指令,继续取指
4.cpsr -->current program status register
	当前程序状态寄存器
	保存的是当前程序的运行状态,如工作模式
5.spsr -->saved program status register
	保存程序状态寄存器
	专门用于保存CPSR中的值到SPSR中  

9、三级流水线

1.取指器:根据PC寄存器中的地址,取对应的指令
2.译码器:翻译指令的功能,交给对应的执行器执行
3.执行器:执行指令,并将指令的执行结果写回到寄存器中
以上三个期间都是独立的硬件,工作互不干扰
三个器件都属于单周期的器件,及一个时钟周期可以完成一件事

【2】ARM汇编指令集

1、汇编代码(.s)中的主要符号

1.指令:编译器将指令编译生成32位机器码,	   执行时可以完成特定的功能2.伪指令:伪指令本身不是指令,编译器可以将伪指令编译生成多条指令,共同完成一条伪指令的功能3.伪操作:伪操作不占用任何的代码段的空间,指导编译器对代码如何进行编译的(.text .global .end)以.开头的都属于伪操作4.注释:单行注释:@   多行注释:/**/

2、map.lds链接脚本文件分析

//1.链接脚本文件:告诉编译器如何对代码进行编译的//2.输出可执行文件为:32位 elf文件 小端对齐OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")/*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*///3.输出文件的架构:arm架构的可执行程序OUTPUT_ARCH(arm)//4.程序的入口是:_startENTRY(_start)//5.可执行程序中各个段的排布方式SECTIONS{	. = 0x00000000;//程序的入口地址	. = ALIGN(4);//对齐2^4:占位	.text      ://代码段	{	//代码段开始的位置必须放start.o的代码		./Objects/start.o(.text)	//其他的.o文件编译器看着办		*(.text)	}	. = ALIGN(4);    .rodata : //只读数据段	{ *(.rodata) }    . = ALIGN(4);    .data : //全局数据段	{ *(.data) }    . = ALIGN(4);	__bss_start = .;     .bss ://未初始化的全局变量     { *(.bss) }	__bss_end__ = .;}

3、指令的基本语法格式

<opcode><cond>{S}  Rd, Rn, oprand2
<opcode>:指令的名字
<cond>:条件码
{s}:状态位,加S,指令的执行结构影响CPSR的NZCV位
Rd:目标寄存器
Rn:第一个操作寄存器
oprand2:第二个操作数
	1>可以是一个普通的寄存器
	2>可以是一个立即数
	3>可以是一个经过移位操作的寄存器
	
注意:
1><opcode><cond>{S} :连到一起写
2>指令和目标寄存器之间使用空格隔开
3>寄存器之间使用“,”隔开
4>一条指令独占一行
5>指令中不区分大小写
	mov r0,#0xff
	mov R0,#0xFF

【3】数据操作指令(最简单、最常用)

1、数据搬移指令mov / mvn

语法格式:opcode <cond> {s} Rd,oprand2
mov r0, #0x000000FF  @ r0 = 0xFF 
mov r1, r0     @ r1 = r0
mvn r2, #0x000000FF  @ r2 = ~0xFF

@ mov r0, #0xFFF   @ error  
@ mov r1, #0xFFFF  @ error
mov r2, #0xFF000000  @ 0xFF >> 8  ==> 4FF
@ mov r3, #0xFFFF0000  @error
mov r4, #0xFFFFFF00 @ mvn r4, #0xFF

@ 伪指令  ldr  
ldr r5, =0x12345678  @// r5 = 0x12345678

image

2、移位操作指令

语法格式:opcode <cond> {s} Rd,Rn,oprand2lsl:逻辑左移(无符号数左移)高位移出,低位补0lsr:逻辑右移(无符号右移)低位移出,高位补0asr:算数右移(有符号数右移)低位移出,高位补符号位ror:循环右移  低位移出,补到高位mov r0,#0xFF00lsl r1,r0 #0x4 @ r1 = r0 << 4lsr r2,r0,#0x4 @ r2 = r0 >> 4asr r3,r0,#0x4 @ r3 = r0 >> 4

3、算数运算指令

语法格式:opcode <cond> {s} Rd,Rn,oprand2add:普通加法指令adc:带进位的加法指令sub:普通的减法指令subc:带借位的减法指令mul:乘法指令注:arm-v7架构之前都没有除法指令    arm-v8架构才支持除法指令 div  
/*假设2个64位的数相加第一个64位的数R0存放低32位,R1存放高32位第二个64位数R2存放低32位,R3存放高32位结果R4存放低32位,R5存放高32位*/mov r0,#0xfffffffemov r1,#0x3mov r2,#0x4mov r3,#0x5adds r4,r0,r2adc r5,r1,r3   @ r5 = r1 + r3 + C
/*假设2个64位的数相减第一个64位的数R0存放低32位,R1存放高32位第二个64位数R2存放低32位,R3存放高32位结果R4存放低32位,R5存放高32位*/mov r0,#0x2mov r1,#0x9mov r2,#0x4mov r3,#0x5subs r4,r0,r2sbc r5,r1,r3  @ r5 = r1 -r3 -!c
mov r0,#3mov r1,#4mul r2,r0,r1

4、位运算指令

位运算指令:and orr eor bic << >> ~语法格式:<opcode><cond>{S}  Rd, Rn, oprand2         Rd  =  Rn # oprand2 ldr r0,=0x12345678@ 1> 将R0寄存器中的,第[3]位置1,保证其他位不变orr r0,r0,#(0x1 << 3)orr r0,r0.#0x8@ 2> 将R0寄存器中的,第[10]位清0,保证其他位不变and r0,r0,#(~(0x1 << 10))@ 3> 将R0寄存器中的,第[7:4]位置1,保证其他位不变orr r0,r0,#(0xf << 4)@ 4> 将R0寄存器中的,第[23:20]位清0,保证其他位不变and r0,r0,#(~(0xf << 20))@ 5> 将R0寄存器中的,第[15:10]位设置为0b101010,保证其他位不变①先清0and r0,r0,#(~(0x3f << 10))②在把相应的位置1orr r0,r0,#(0x2a << 10)@ 6> 将R0寄存器中的,第[31:28]位设置为0b1100,第[11:8]位设置为1011,保证其他位不变and r0,r0,#(~(0xf << 28))orr r0,r0,#(0xc << 28)and r0,r0,#(~(0xf << 8))orr r0,r0,#(0xb << 8)@ 7> bic  位清除指令 给对应的位写1,就可以清0@ 将R0寄存器中的,第[23:20]位清0,保证其他位不变bic r0,r0,#(0xf << 20)        

5、比较指令

比较指令:cmp
用于比较两个数的大小
格式:cmp Rn,oprand2
1.没有目标寄存器
2.比较的结果影响的是CPSR的NZCV位
3.指令后边不需要加S
4.本质:做减法运算
cmp指令常跟条件码一起使用

image

mov r0,#8
mov r1,#15
cmp r0,r1
subhi r0,r0,r1
subcc r1,r1,r0

【4】跳转指令

1、b跳转指令

b:不带保存返回地址的跳转指令
格式:b <cond> Label
	跳转到Label标签下的第一条指令出执行
标签:label
场合:有去无回

2、bl跳转指令

bl:带保存返回地址的跳转指令格式:bl <cond> Label	跳转到Label标签下的第一条指令出执行	同时保存跳转指令的下一条的地址到lr寄存器中标签:label场合:有去有回总结:跳转指令的本质就是修改PC中的值mov r0,#4	mov r1,#5	bl add_func	mov r2,#6	b stopadd_func:	add r0,r0,r1	mov pc,lr
/*实现1~100之间所有数相加之和*/for(i=1;i<=100;i++){	sum = sum + i;}sum = r0i = r1for(1;2;3){	4;}{1;2}{4;3;2}{4;3;2}{4;3;2}
/*求最大公约数*/		mov r0,#0x5	mov r1,#0x8	loop:	cmp r0,r1	beq stop 	subhi r0,r0,r1	subcc r1,r1,r0	b loop

【5】特殊功能寄存器操作指令

1、mrs和msr操作指令

msr cpsr,Rn   @cpsr = Rn  写操作  实际操作是修改cpsr中的值
mrs Rn,cpsr   @Rn = cpsr  读操作  实际操作是将cpsr中的值读出来,不对cpsr中的值进行比较

2、从SVC模式切换到USER模式

 /*第一种方式*/
	msr cpsr,#0xD0
	
/*第二种方式*/
	mrs r0,cpsr @将CPSR中的值读到普通寄存器中
	bic r0,r0,#0x1f @清除模式位
	orr r0,r0,#0x10 @修改模式位对应位为1
	msr cpsr,r0 @在写到cpsr中

【6】load/store内存读写指令

1、单寄存器操作指令

单寄存器操作指令:ldr str
格式:ldr/str<cond> Rn,[Rm]
Rn:Rn中的数据看成一个普通的数据
Rm:Rm中的数据看成内存的地址

ldr:将Rm指向的内存地址中的内容,读到Rn寄存器中
str:将Rn寄存器中的内容,写到Rm指向的地址空间中

ldr r0,=0x40000800
ldr r1,=0x12345678
str r1,[r0]  将r1寄存器中的值,写到r0指向的内存地址中的内容
ldr r2,[r0]  将r0指向的内存地址中的内容,读到r2寄存器中
ldr r0,=0x40000800
ldr r1,=0x11111111
ldr r2,=0x22222222
ldr r3,=0x33333333
@将r1中的值存到r0+4指向的地址中,r0中的值不变
str r1,[r0,#4]
@将r2中的值存到r0指向的地址中,同时r0 = r0 + 4
str r2,[r0],#4
@将r3中的值存到r0+4指向的地址中,同时r0 = r0 + 4
@!:更新地址
str r3,[r0,#4]!

2、测试arm的大小端

问:首先要知道什么是大端存储,什么是小端存储?
答:小端存储低地址中存放低字节内容,高地址存放高字节内容
    大端存储高地址存放低字节内容,低地址存放高字节内容
思考:如何用汇编代码,编写测试大小端,使用字节进行读取数据就可以了ldr r0,=0x40000800ldr r1,=0x12345678str r1,[r0]ldrb r2,[r0]ARM是小端存储方式
@使用ldrh读出地址中4字节的数据@并将读到的数据合并的r9寄存器中ldrh r3,[r0]ldrh r4,[r0,#2]orr r9,r3,r4,lsl #16

3、多寄存器操作指令

多寄存器操作指令:ldm stmstm/ldm  Rm, {寄存器列表}stm:将寄存器列表中的所有寄存器中的内容,存到Rm指向的地址连续空间中ldm:将Rm指向的连续的内存空间中的内容,读到寄存器列表中的所有寄存器中注意:1.如果寄存器列表中的寄存器连续,则使用“-”隔开,不连续使用“,”隔开2.寄存器列表中的寄存器编号顺序,不管怎么写,永远都是地地址对应小编号的寄存器,高地址对应大编号的寄存器
ldr r0,=0x40000800ldr r1,=0x11111111ldr r2,=0x22222222ldr r3,=0x33333333ldr r4,=0x44444444ldr r5,=0x55555555stm r0,{r1-r4,r5}ldm r0,{r6-r10}或者如下写法:stm r0, {r5,r4,r3,r2,r1}ldm r0, {r10,r9,r8,r7,r6}

【7】栈操作指令

1、栈的4种类型(sp栈指针)

满栈:sp指向的栈空间有有效的数据,需先移动栈指针,在存储数据空栈:sp指向的栈空间没有有效的数据,先存储数据,在将栈指针移动到一个空的空	间增栈:栈指针向高地址方向移动减栈:栈指针向低地址方向移动

2、栈的4种操作方式

满增栈:stmfa/ldmfa   full ascending 满减栈:stmfd/ldmfd   full descending 空增栈:stmea/ldmea   empty ascending空减栈:stmed/ldmed   empty descending

3、ARM处理器采用满减栈

语法格式:
stmfd/ldmfd sp!, {寄存器列表}
!:更新栈指针的地址

ldr sp,=0x40000820
ldr r0,=0x11111111
ldr r1,=0x22222222
ldr r2,=0x33333333
ldr r3,=0x44444444
ldr r4,=0x55555555
stmfd sp!, {r0-r4}
ldmfd sp!, {r5-r9}
	ldr sp,=0x40000820	
	mov r0,#3
	mov r1,#4
	bl add_func
	add r2,r0,r1  @r2 = 0x7
	b stop

add_func:
	@保存现场
	stmfd sp!,{r0-r1,lr}
	mov r0,#5
	mov r1,#6
	bl sub_func
	add r3,r0,r1   @ r3 = 0xb
	@恢复现场
	ldmfd sp!,{r0-r1,pc}
	
sub_func:
	stmfd sp!,{r0-r1,lr}
	mov r0,#9
	mov r1,#7
	sub r4,r0,r1  @r4 = r0-r1 = 0x2
	ldmfd sp!,{r0-r1,pc}

【8】混合编程

ATPCS规范(ARM规定)1.参数的传递通过r0-r3传递2.返回值通过r0返回3.参数的个数如果超过4个通过压栈的方法进行传递4.返回值大于4个字节通过r0,r1返回

1、汇编调用c代码

.text   /* .text 代码段 .data 数据段*/.global _start  /* 将_start声明为全局的函数 */ 	_start: /* 标签:理解成C语言的函数名 */	@ 汇编调用C 	@ C代码必须有栈 	ldr sp, =0x40000820 @ 初始化栈		mov r0, #3  @ 指定实参值	mov r1, #4	bl addfunc  @ 调用C函数				@ 返回通过R0返回	stop: /* while(1) {} */		@ 汇编指令		b stop  /* 指令:跳转指令 调用stop函数 */.end /* 结束 */
int addfunc(int a, int b){	return a + b;}

2、c调用汇编

.text   /* .text 代码段 .data 数据段*/
.global _start  /* 将_start声明为全局的函数 */
 	
_start: /* 标签:理解成C语言的函数名 */
@ 启动文件,在汇编的阶段完成栈的初始化

	@ C代码必须有栈 
	ldr sp, =0x40000820 @ 初始化栈
	
	b main

.end /* 结束 */
		
main.c文件
//1.需要使用extern将add_func函数进行声明
extern int add_func(int a,int b,int c,int d);
int sum = 0;
int main()
{
	sum = add_func(1,2,3,4);
	return 0;
}
add_func.s文件
.text 

@ 2. 将汇编的函数声明成一个全局的函数
.global add_func

add_func:
	add r0, r0, r1
	add r0, r0, r2
	add r0, r0, r3
	mov pc, lr
.end 

3、内联汇编

格式:
asm volatile(
"汇编指令\n\t"
:输出列表
:输入列表
:破坏列表
);
//混合编程
int add_func2(int a,int b,int c,int d)
{
	int sum;
	asm volatile(
	"add r0,r0,r1\n\t" //指令列表
	"add r0,r0,r2\n\t"
	"add r0,r0,r3\n\t"
	:"=r"(sum) //输出列表
	:"r"(a),"r"(b),"r"(c),"r"(d) //输入列表
	:   //破坏列表
	);
	return sum;
}


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM