嵌入式开发交流群280352802,欢迎加入!
在针对ARM体系结构的编程中,一般很难直接使用C语言产生操作协处理器的相关代码,因此使用汇编语言来实现就成为了唯一的选择。但如果完全通过汇编代码实现,又会过于复杂、难以调试。因此,C语言内嵌汇编的方式倒是一个不错的选择。然而,使用内联汇编的一个主要问题是,内联汇编的语法格式与使用的编译器直接相关,也就是说,使用不同的C编译器内联汇编代码时,它们的写法是各不相同的。下面介绍在ARM体系结构下GCC的内联汇编。GCC内联汇编的一般格式:
在C代码中嵌入汇编需要使用asm关键字,在asm的修饰下,代码列表、输出运算符列表、输入运算符列表和被更改的资源列表这4个部分被3个“:”分隔。下面,我们看一个例子:
注:换行符和制表符的使用可以使得指令列表看起来变得美观。你第一次看起来可能有点怪异,但是当C编译器编译C语句的是候,它就是按照上面(换行和制表)生成汇编的。
函数test中内嵌了一条汇编指令实现将立即数1赋值给寄存器R1的操作。由于没有任何形式的输出和输入,因此输出和输入列表的位置上什么都没有填写。但是,在汇编代码执行过程中R1寄存器会被修改,因此为了通知编译器,在被更改资源列表中,需要写上寄存器R1。
寄存器被修改这种现象发生的频率还是比较高的。例如,在调用某段汇编程序之前,寄存器R1可能已经保存了某个重要数据,当汇编指令被调用之后,R1寄存器被赋予了新的值,原来的值就会被修改,所以,需要将会被修改的寄存器放入到被更改资源列表中,这样编译器会自动帮助我们解决这个问题。也可以说,出现在被更改资源列表中的资源会在调用汇编代码一开始就首先保存起来,然后在汇编代码结束时释放出去。所以,上面的代码与如下代码从语义上来说是等价的。
这段代码中的内联汇编既无输出又无输入,也没有资源被更改,只留下了汇编代码的部分。由于程序在修改R1之前已经将寄存器R1的值压入了堆栈,在使用完之后,又将R1的值从堆栈中弹出,所以,通过被更改资源列表来临时保存R1的值就没什么必要了。
在以上两段代码中,汇编指令都是独立运行的。但更多的时候,C和内联汇编之间会存在一种交互。C程序需要把某些值传递给内联汇编运算,内联汇编也会把运算结果输出给C代码。此时就可以通过将适当的值列在输入运算符列表和输出运算符列表中来实现这一要求。请看下面的例子:
上面的代码中有一条mov指令,该指令将%0赋值给R4。这里,符号%0代表出现在输入运算符列表和输出运算符列表中的第一个值。如果%1存在的话,那么它就代表出现在列表中的第二个值,依此类推。所以,在该段代码中,%0代表的就是“r”(tmp)这个表达式的值了。
那么这个新的表达式又该怎样解释呢?原来,在“r”(tmp)这个表达式中,tmp代表的正是C语言向内联汇编输入的变量,操作符“r”则代表tmp的值会通过某一个寄存器来传递。在GCC4中与之相类似的操作符还包括“m”、“I”,等等,其含义见下表:
与输入运算符列表的应用方法一致,当C语言需要利用内联汇编输出结果时,可以使用输出运算符列表来实现,其格式应该是下面这样的。
在上面的代码中,原本应出现在输入运算符列表中的运算符,现在出现在了输出运算符列表中,同时变量tmp将会存储内联汇编的输出结果。这里有一点可能已经引起大家的注意了,上面的代码中操作符r的前面多了一个“=”。这个等号被称为约束修饰符,其作用是对内联汇编的操作符进行修饰。几种修饰符的含义如下表所示:
当一个操作符没有修饰符对其进行修饰时,代表这个操作符是只读的。当我们需要将内联汇编的结果输出出来,那么至少要保证该操作符是可写的。因此,“=”或者“+”也就必不可少了。
访问CP15寄存器指令的编码格式及语法说明如下:
31 28 |
27 24 |
23 21 |
20 |
19 16 |
15 12 |
11 8 |
7 5 |
4 |
3 0 |
cond |
1 1 1 0 |
opcode_1 |
L |
cr n |
rd |
1 1 1 1 |
opcode_2 |
1 |
crm |
说明:
指 令 |
说 明 |
语法格式 |
mcr |
将ARM处理器的寄存器中的数据写到CP15中的寄存器中 |
mcr{ |
mrc |
将CP15中的寄存器中的数据读到ARM处理器的寄存器中 |
mcr{ |
CP15的寄存器列表如表4-1所示。
表4-1 ARM处理器中CP15协处理器的寄存器
寄存器编号 |
基本作用 |
在MMU中的作用 |
在PU中的作用 |
0 |
ID编码(只读) |
ID编码和cache类型 |
|
1 |
控制位(可读写) |
各种控制位 |
|
2 |
存储保护和控制 |
地址转换表基地址 |
Cachability的控制位 |
3 |
存储保护和控制 |
域访问控制位 |
Bufferablity控制位 |
4 |
存储保护和控制 |
保留 |
保留 |
5 |
存储保护和控制 |
内存失效状态 |
访问权限控制位 |
6 |
存储保护和控制 |
内存失效地址 |
保护区域控制 |
7 |
高速缓存和写缓存 |
高速缓存和写缓存控制 |
|
8 |
存储保护和控制 |
TLB控制 |
保留 |
9 |
高速缓存和写缓存 |
高速缓存锁定 |
|
10 |
存储保护和控制 |
TLB锁定 |
保留 |
11 |
保留 |
|
|
12 |
保留 |
|
|
13 |
进程标识符 |
进程标识符 |
|
14 |
保留 |
|
|
15 |
因不同设计而异 |
因不同设计而异 |
因不同设计而异 |
CP15中寄存器C0对应两个标识符寄存器,由访问CP15中的寄存器指令中的
opcode_2编码 |
对应的标识符号寄存器 |
0b000 |
主标识符寄存器 |
0b001 |
cache类型标识符寄存器 |
其他 |
保留 |
1)主标识符寄存器
访问主标识符寄存器的指令格式如下所示:
mrc p15, 0, r0, c0, c0, 0 ;将主标识符寄存器C0,0的值读到r0中
ARM不同版本体系处理器中主标识符寄存器的编码格式说明如下。
ARM7之后处理器的主标识符寄存器编码格式如下所示:
31 24 23 20 19 16 15 4 3 0 |
||||
由生产商确定 |
产品子编号 |
ARM体系版本号 |
产品主编号 |
处理器版本号 |
位 |
说 明 |
位[3: 0] |
生产商定义的处理器版本号 |
位[15: 4] |
生产商定义的产品主编号,其中最高4位即位[15:12]可能的取值为0~7但不能是0或7 |
位[19: 16] |
ARM体系的版本号,可能的取值如下: 0x1 ARM体系版本4 0x2 ARM体系版本4T 0x3 ARM体系版本5 0x4 ARM体系版本5T 0x5 ARM体系版本5TE 其他 由ARM公司保留将来使用 |
位[23: 20] |
生产商定义的产品子编号,当产品主编号相同时,使用子编号来区分不同的产品子类,如产品中不同的高速缓存的大小等 |
位[31: 24] |
生产厂商的编号,现在已经定义的有以下值: 0x41 =A ARM公司 0x44 =D Digital Equipment公司 0x69 =I intel公司 |
ARM7处理器的主标识符寄存器编码格式如下所示:
31 24 23 22 16 15 4 3 0 |
||||
由生产商确定 |
A |
产品子编号 |
产品主编号 |
处理器版本号 |
位 |
说 明 |
位[3: 0] |
生产商定义的处理器版本号 |
位[15: 4] |
生产商定义的产品主编号,其中最高4位即位[15:12]的值为0x7 |
位[22: 16] |
生产商定义的产品子编号,当产品主编号相同时,使用子编号来区分不同的产品子类,如产品中不同的高速缓存的大小等 |
续表
位 |
说 明 |
位[23] |
ARM7支持下面两种ARM体系的版本号: 0x0 ARM体系版本3 0x1 ARM体系版本4T |
位[31: 24] |
生产厂商的编号,现在已经定义的有以下值: 0x41 =A ARM公司 0x44 =D Digital Equipment公司 0x69 =I Intel公司 |
ARM7之前处理器的主标识符寄存器编码格式如下所示:
31 24 23 22 16 15 4 3 0 |
||||
由生产商确定 |
A |
产品子编号 |
产品主编号 |
处理器版本号 |
位 |
说 明 |
位[3: 0] |
生产商定义的处理器版本号 |
位[15: 4] |
生产商定义的产品主编号,其中最高4位即为[15:12]的值为0x7 |
位[22: 16] |
生产商定义的产品子编号,当产品主编号相同时,使用子编号来区分不同的产品子类,如产品中不同的高速缓存的大小等 |
位[23] |
ARM7支持下面两种ARM体系的版本号: 0x0 ARM体系版本3 0x1 ARM体系版本4T |
位[31: 24] |
生产厂商的编号,现在已经定义的有以下值: 0x41 =A ARM公司 0x44 =D Digital Equipment公司 0x69 =I intel公司 |
2)cache类型标识符寄存器
访问cache类型标识符寄存器的指令格式如下所示:
mrc p15, 0, r0, c0, c0, 1 ;将cache类型标识符寄存器C0,1的值读到r0中
ARM处理器中cache类型标识符寄存器的编码格式如下所示:
31 29 28 25 24 23 12 11 0 |
||||
0 0 0 |
属性字段 |
S |
数据cache相关属性 |
指令cache相关属性 |
位 |
说明 |
位[28: 25] |
指定控制字段位[24: 0]指定的属性之外的cache的其他属性,详见表4-2 |
位[24] |
定义系统中的数据cache和指令cache是分开的还是统一的: 0 系统的数据cache和指令cache是统一的; 1 系统的数据cache和指令cache是分开的 |
位[23: 12] |
定义数据cache的相关属性,如果位[24]为0,本字段定义整个cache的属性 |
位[31: 24] |
定义指令cache的相关属性,如果位[24]为0,本字段定义整个cache的属性 |
其中控制字段位[28:25]的含义说明如下:
表4-2 cache类型标识符寄存器的控制字段位[28:25]
编 码 |
cache类型 |
cache内容清除方法 |
cache内容锁定方法 |
0b0000 |
写通类型 |
不需要内容清除 |
不支持内容锁定 |
0b0001 |
写回类型 |
数据块读取 |
不支持内容锁定 |
0b0010 |
写回类型 |
由寄存器C7定义 |
不支持内容锁定 |
0b0110 |
写回类型 |
由寄存器C7定义 |
支持格式A |
0b0111 |
写回类型 |
由寄存器C7定义 |
支持格式B |
控制字段位[23:12]和控制字段位[11:0]的编码格式相同,含义如下所示:
11 9 8 6 5 3 2 1 0 |
||||
0 0 0 |
cache容量 |
cache相联特性 |
M |
块大小 |
cache容量字段bits[8: 6]的含义如下所示:
编 码 |
M=0时含义(单位KB) |
M=1时含义(单位KB) |
0b000 |
0.5 |
0.75 |
0b001 |
1 |
1.5 |
0b010 |
2 |
3 |
0b011 |
4 |
6 |
0b100 |
8 |
12 |
0b101 |
16 |
24 |
0b110 |
32 |
48 |
0b111 |
64 |
96 |
cache相联特性字段bits[5: 3]的含义如下所示:
编 码 |
M=0时含义 |
M=1时含义 |
0b000 |
1路相联(直接映射) |
没有cache |
0b001 |
2路相联 |
3路相联 |
0b010 |
4路相联 |
6路相联 |
0b011 |
8路相联 |
12路相联 |
0b100 |
16路相联 |
24路相联 |
0b101 |
32路相联 |
48路相联 |
0b110 |
64路相联 |
96路相联 |
0b111 |
128路相联 |
192路相联 |
cache块大小字段bits[1: 0]的含义如下所示:
编 码 |
cache块大小 |
0b00 |
2个字(8字节) |
0b01 |
4个字(16字节) |
0b10 |
8个字(32字节) |
0b11 |
16个字(64字节) |
访问主标识符寄存器的指令格式如下所示:
mrc p15, 0, r0, c1, c0{, 0} ;将CP15的寄存器C1的值读到r0中
mcr p15, 0, r0, c1, c0{, 0} ;将r0的值写到CP15的寄存器C1中
CP15中的寄存器C1的编码格式及含义说明如下:
31 16 |
15 |
14 |
13 |
12 |
11 |
10 |
9 |
8 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
附加 |
L4 |
RR |
V |
I |
Z |
F |
R |
S |
B |
L |
D |
P |
W |
C |
A |
M |
位 |
说 明 |
M |
0:禁止MMU或者PU;1:使能MMU或者PU |
A |
0:禁止地址对齐检查;1:使能地址对齐检查 |
C |
0:禁止数据/整个cache;1:使能数据/整个cache |
W |
0:禁止写缓冲;1:使能写缓冲 |
P |
0:异常中断处理程序进入32位地址模式;1:异常中断处理程序进入26位地址模式 |
D |
0:禁止26位地址异常检查;1:使能26位地址异常检查 |
L |
0:选择早期中止模型;1:选择后期中止模型 |
B |
0:little endian;1:big endian |
S |
在基于MMU的存储系统中,本位用作系统保护 |
R |
在基于MMU的存储系统中,本位用作ROM保护 |
F |
0:由生产商定义 |
Z |
0:禁止跳转预测功能;1:使能跳转预测指令 |
I |
0:禁止指令cache;1:使能指令cache |
V |
0:选择低端异常中断向量0x0~0x1c;1:选择高端异常中断向量0xffff0000~ 0xffff001c |
RR |
0:常规的cache淘汰算法,如随机淘汰;1:预测性淘汰算法,如round-robin淘汰算法 |
L4 |
0:保持ARMv5以上版本的正常功能;1:将ARMv5以上版本与以前版本处理器兼容,不根据跳转地址的bit[0]进行ARM指令和Thumb状态切换:bit[0]等于0表示ARM指令,等于1表示Thumb指令 |
附加: |
|
CP15中的寄存器C2保存的是页表的基地址,即一级映射描述符表的基地址。其编码格如下所示:
31 0 |
一级映射描述符表的基地址(物理地址) |
CP15中的寄存器C3定义了ARM处理器的16个域的访问权限。
31 0 |
|||||||||||||||
D15 |
D14 |
D13 |
D12 |
D11 |
D10 |
D9 |
D8 |
D7 |
D6 |
D5 |
D4 |
D3 |
D2 |
D1 |
D0 |
CP15中的寄存器C5是失效状态寄存器,编码格式如下所示:
31 9 8 7 4 3 0 |
|||
UNP/SBZP |
0 |
域标识 |
状态标识 |
其中,域标识bit[7:4]表示存放引起存储访问失效的存储访问所属的域。
状态标识bit[3:0]表示放引起存储访问失效的存储访问类型,该字段含义如表4-3所示(优先级由上到下递减)。
表4-3 状态标识字段含义
引起访问失效的原因 |
状态标识 |
域标识 |
C6 |
终端异常(Terminal Exception) |
0b0010 |
无效 |
生产商定义 |
中断向量访问异常(Vector Exception) |
0b0000 |
无效 |
有效 |
地址对齐 |
0b00x1 |
无效 |
有效 |
一级页表访问失效 |
0b1100 |
无效 |
有效 |
二级页表访问失效 |
0b1110 |
有效 |
有效 |
基于段的地址变换失效 |
0b0101 |
无效 |
有效 |
基于页的地址变换失效 |
0b0111 |
有效 |
有效 |
基于段的存储访问中域控制失效 |
0b1001 |
有效 |
有效 |
基于页的存储访问中域控制失效 |
0b1101 |
有效 |
有效 |
基于段的存储访问中访问权限控制失效 |
0b1111 |
有效 |
有效 |
基于页的存储访问中访问权限控制失效 |
0b0100 |
有效 |
有效 |
基于段的cache预取时外部存储系统失效 |
0b0110 |
有效 |
有效 |
基于页的cache预取时外部存储系统失效 |
0b1000 |
有效 |
有效 |
基于段的非cache预取时外部存储系统失效 |
0b1010 |
有效 |
有效 |
CP15中的寄存器C5是失效地址寄存器,编码格式如下所示:
31 0 |
失效地址(虚拟地址) |
CP15的C7寄存器用来控制cache和写缓存,它是一个只写寄存器,读操作将产生不可预知的后果。
访问CP15的C7寄存器的指令格式如下所示:
mcr p15, 0,
CP15的C8寄存器用来控制清除TLB的内容,是只写寄存器,读操作将产生不可预知的后果。
访问CP15的C8寄存器的指令格式如下所示:
mcr p15, 0,
CP15的C9寄存器用于控制cache内容锁定。
访问CP15的C9寄存器的指令格式如下所示:
mcr p15, 0,
mrc p15, 0,
如果系统中包含独立的指令cache和数据cache,那么对应于数据cache和指令cache分别有一个独立的cache内容锁定寄存器,
CP15的C9寄存器有A、B两种编码格式。编码格式A如下所示:
31 32-W 31-W 0 |
|
cache组内块序号index |
0 |
其中index表示当下一次发生cache未命中时,将预取的存储块存入cache中该块对应的组中序号为index的cache块中。此时序号为0~index-1的cache块被锁定,当发生cache替换时,从序号为index到ASSOCIATIVITY的块中选择被替换的块。
编码格式B如下所示:
31 30 W W-1 0 |
||
L |
0 |
cache组内块序号index |
位 |
说 明 |
L=0 |
当发生cache未命中时,将预取的存储块存入cache中该块对应的组中序号为index的cache块中 |
续表
位 |
说 明 |
L=1 |
如果本次写操作之前L=0,并且index值小于本次写入的index,本次写操作执行的结果不可预知;否则,这时被锁定的cache块包括序号为0~index-1的块,当发生cache替换时,从序号为index到ASSOCIATIVITY的块中选择被替换的块 |
CP15的C10寄存器用于控制TLB内容锁定。
访问CP15的C10寄存器的指令格式如下所示:
mcr p15, 0,
mrc p15, 0,
如果系统中包含独立的指令TLB和数据TLB,那么对应于数据TLB和指令TLB分别有一个独立的TLB内容锁定寄存器,
C10寄存器的编码格式如下:
31 30 32-W 31-W 32-2W 31-2W 1 0 |
|||
可被替换的条目起始地址的base |
下一个将被替换的条目地址victim |
0 |
P |
位 |
说 明 |
victim |
指定下一次TLB没有命中(所需的地址变换条目没有包含在TLB中)时,从内存页表中读取所需的地址变换条目,并把该地址变换条目保存在TLB中地址victim处 |
base |
指定TLB替换时,所使用的地址范围,从(base)到(TLB中条目数-1);字段victim的值应该包含在该范围内 |
P |
1:写入TLB的地址变换条目不会受使整个TLB无效操作的影响,一直保持有效;0:写入TLB的地址变换条目将会受到使整个TLB无效操作的影响 |
C13寄存器用于快速上下文切换FCSE。
访问CP15的C13寄存器的指令格式如下所示:
mcr p15, 0,
mrc p15, 0,
C13寄存器的编码格式如下所示:
31 25 24 0 |
|
PID |
0 |
其中,PID表示当前进程的所在的进程空间块的编号,即当前进程的进程标识符,取值为0~127。
0:MVA(变换后的虚拟地址)= VA(虚拟地址),禁止FCSE(快速上下文切换技术),系统复位后PID=0;
非0:使能FCSE。
MCR指令将ARM处理器的寄存器中的数据传送到协处理器的寄存器中。如果协处理器不能成功地执行该操作,将产生未定义的指令异常中断。
指令的语法格式:
MCR{
MCR2 p15, 0,
其中,
MRC指令将协处理器的寄存器中数值传送到ARM处理器的寄存器中。如果协处理器不能成功地执行该操作,将产生未定义的指令异常中断。
指令的语法格式:
MRC{
MRC2 p15, 0,
MVN指令
MVN指令的格式为:
MVN{条件}{S} 目的寄存器,源操作数
MVN指令可完成从另一个寄存器、被移位的寄存器、或将一个立即数加载到目的寄存器。与MOV指令不同之处是在传送之前按位被取反了,即把一个被取反的值传送到目的寄存器中。其中S决定指令的操作是否影响CPSR中条件标志位的值,当没有S时指令不更新CPSR中条件标志位的值。
4(00000100b) 取反为 11111011,这个是数是-5的补码,所以r0=-5
1111011高位为1,为负数,故减一取反为5,加上符号就为-5
BIC 位清0指令
Rd, Rn, Oprand2
BIC(位清除)指令对 Rn 中的值 和 Operand2 值的反码按位进行逻辑“与”运算。 (注意:ARM官方网站有误, 写的是补码)
BIC 是 逻辑”与非” 指令, 实现的 Bit Clear的功能
举例:
BIC R0, R0 , #0xF0000000
ORR 逻辑或指令
n AND位与指令
AND R0,R1,R2; R0=R1 & R2
AND R0,R1,#0xFF ;R0=R1 & 0xFF
n ORR位或指令
ORR R0,R1,R2; R0=R1 | R2
ORR R0,R1,#0xFF ;R0=R1 | 0xFF
n TST测试某一位是否为1,并把结果写入CPSR,供下一句使用
TST R1,#0xffe; 等同于if(R1 & 0xffe)
TST R1,#%1;测试最低位是否为1,%表示二进制
n BIC清位操作
BIC R0,R0,#0xF ; 等同于 R0 &=~(0xF)
BIC R0,R0,#%1011 ; 该指令清除 R0 中的位 0 1 3,其余的位保持; %表示是二进制,0x表示十六进制
n CMP比较两个操作数,并把结果存入CPSR供下一句语句使用