1 GNU 汇编格式
label:instruction @ comment
label 即标号,表示地址位置,有些指令前面可能会有标号,这样就可以通过这个标号得到指令的地址,标号也可以用来表示数据地址。注意 label 后面的“:”,任何以“:”结尾的标识符都会被识别为一个标号。
instruction 即指令,也就是汇编指令或伪指令。
@符号,表示后面的是注释,就跟 C 语言里面的“/”和“/”一样,其实在 GNU 汇编文件中我们也可以使用“/*”和“*/”来注释。
comment 就是注释内容。
add:
MOVS R0, #0X12 @设置 R0=0X12
注意: ARM 中的指令、伪指令、伪操作、寄存器名等可以全部使用大写,也可以全部使用小写,但是不能大小写混用
1.1伪操作
伪汇编指令都是大小写不敏感的.
1.1.1 .section
来定义一个段,汇编系统预定义了一些段名:
.text 表示代码段。
.data 初始化的数据段。
.bss 未初始化的数据段。
.rodata 只读数据段。
定义一个 testsetcion 段
.section .testsection
同时,还可以指定该段的属性,对应的属性见下表:
a section is allocatable
d section is a GNU_MBIND section
e section is excluded from executable and shared library.
w section is writable
x section is executable
M section is mergeable
S section contains zero terminated strings
G section is a member of a section group
T section is used for thread-local-storage
种属性可以组合, 比如:
.section .foo,"aex"
text...
1.1.2 .global
.global _start
_start:
ldr r0, =0x12 @r0=0x12
.global 是伪操作,表示_start 是一个全局标号,类似 C 语言里面的全局变量一样,常见的伪操作有:
.byte 定义单字节数据,比如.byte 0x12。
.short 定义双字节数据,比如.short 0x1234。
.long 定义一个 4 字节数据,比如.long 0x12345678。
.equ 赋值语句,格式为:.equ 变量名,表达式,比如.equ num, 0x12,表示 num=0x12。
.align 数据字节对齐,比如:.align 4 表示 4 字节对齐。
.end 表示源文件结束。
.global 定义一个全局符号,格式为:.global symbol,比如:.global _start。
1.1.3 .comm
.comm 表示目标文件中的 common symbol,表示公共的符号:
.comm symbol,length
这和 GNU 中的强弱符号机制相关,未初始化的变量表示为弱符号,初始化的变量为强符号,当不同源文件中存在多个同名变量时,强符号会覆盖弱符号而不会报错,这是 gcc 的扩展语法,所以实际上未初始化的全局变量是作为公共符号保存的,当多个文件中的 comm 符号出现冲突时,需要将其以一定规则融合. 实际上,C 语言中未定义的全局变量(也就是 comm 符号)并非是存放到 bss 段中的,而是保存在 COMMON 段.
1.1.4 .byte , .word
伪汇编中有一系列的数据放置指令,表示在当前位置放置某些数据,相对应的有:
.byte : 放置一个字节
.hword:放置半字,在 32 位平台中对应两个字节,64 位对应四字节
.short:放置一个 short 类型数据,两个字节.word:放置一个字,在 32 位平台中对应四个字节,64 位对应八字节
.int : 放置一个 int 类型的数据,数据长度根据平台而定,16位平台为两字节,32位和64位平台为四字节
.long:放置一个 long 类型的数据,数据长度根据平台而定,32位平台为四字节,64位平台为八字节
.float:放置一个 float 类型数据,四字节
.double:放置一个 double 类型数据,八字节
1.1.5 .set, .eqv, .equ, .equiv
.set:设置一个符号的值, C 中的宏。通过设置一个符号的值,在后续的代码中可以重复使用该符号的值。
.set sym_set,0x100
mov r0,#sym_set
1.1.6 .weak
定义一个 weak 类型的符号,当这个符号在相同作用域的地方存在定义,当前符号会被忽略,如果这个符号之前不存在,这个符号就会被使用. 这和 C 语言中的 weak 机制是一样的.
1.2 函数定义
函数名:
函数体
返回语句
GNU 汇编函数返回语句不是必须的,如下代码就是用汇编写的Cortex-A7 中断服务函数:
/* 未定义中断 */
Undefined_Handler:
ldr r0, =Undefined_Handler
bx r0
/* SVC 中断 */
SVC_Handler:
ldr r0, =SVC_Handler
bx r0
/* 预取终止中断 */
PrefAbort_Handler:
ldr r0, =PrefAbort_Handler
bx r0
以函数 Undefined_Handler 为例我们来看一下汇编函数组成,“Undefined_Handler”就是函数名,“ldr r0, =Undefined_Handler”是函数体,“bx r0”是函数返回语句,“bx”指令是返回指令,函数返回语句不是必须的.
2 ARMv7汇编指令
2.1 数据移动指令
数据移动指令都是cpu内部寄存器之间的数据拷贝。
2.1.1 MOV
MOV R0,R1 @将寄存器 R1 中的数据传递给 R0,即 R0=R1
MOV R0, #0X12 @将立即数 0X12 传递给 R0 寄存器,即 R0=0X12
2.1.2 MRS
读取特殊寄存器的数据只能使用 MRS 指令:
MRS R0, CPSR @将 CPSR 里面的数据传递给 R0,即 R0=CPSR
2.1.3 MSR
和 MRS 刚好相反,通用寄存器写入到特殊寄存器
MSR CPSR, R0 @将 R0 中的数据复制到 CPSR 中,即 CPSR=R0
举个例子利用MRS MSR进行清bss (_bss_start, __bss_end定义在链接脚本):
.global _start
.global _bss_start
.global _bss_end
_bss_start:
.word __bss_start
_bss_end:
.word __bss_end
_start:
//disable watchdog, disable icache dcache
//init_clk
//enter svc mode
/*clear bss*/
ldr r0, _bss_start
ldr r1, _bss_end
move r2, 0
clr_bss:
stmia r0!, {r2} //复制一r2中的数据给r0, 并将指针r0增加4
cmp r0, r1
ble clr_bss /*if r0<r1, b clr_bss*/
2.1.4 CPS
特权模式下(除了用户模式,剩余的模式都是特权模式),可以通过CPS指令直接修改CPSR寄存器的M[4:0],让处理器进入不同的模式。
CPS #0x12 /*irq mode*/
CPS #0x13 /*svc mode*/
2.2 数据存取指令(访问存储器RAM)
2.2.1 LDR
数据加载指令,从指定地址读取到cpu寄存器。
LDR R0, =0X0209C004 @将寄存器地址 0X0209C004 加载到 R0 中,即 R0=0X0209C004
LDR R1, [R0] @读取地址 0X0209C004 中的数据到 R1 寄存器中
2.2.2 STR
数据存放指令,从cpu寄存器写入指定地址。
LDR R0, =0X0209C004 @将寄存器地址 0X0209C004 加载到 R0 中,即 R0=0X0209C004
LDR R1, =0X20000002 @R1 保存要写入到寄存器的值,即 R1=0X20000002
STR R1, [R0] @将 R1 中的值写入到 R0 中所保存的地址中
LDR 和 STR 都是按照4 byte进行读取和写入的,也就是操作的 32 位数据,如果要按照字节、半字进行操作的话可以在指令“LDR”后面加上 B 或 H,比如按字节操作的指令就是 LDRB 和STRB,按半字(16位)操作的指令就是 LDRH 和 STRH。
2.2.3 多寄存器加载存储指令LDMIA,STMIA等
1.LDMIA指令、LDMIB指令、LDMDB指令、LDMDA指令
LDM是LDR指令的增强型 , 将连续的数据加载到多组寄存器。
DB (Decrement Before)栈指针先减小再操作、DA(Decrement After)栈指针先操作再减小。
IB(Increment Before)栈指针先增加再操作、IA(Increment After)栈指针先操作再增加。
LDMIA指令,IA表示每次传送后地址加4
LDMIB指令,IB表示每次传送前地址加4
LDMDA指令,DA表示每次传送后地址减4
LDMDB指令,DB表示每次传送前地址减4
LDMIA R14,{R0-R3,R12} /*从R14寄存器指向的地址取出5个32位数据分别存进到R0-R4以及R12*/
//等效于
//R0=*R14
//R1=*(R14+4)
//R2=*(R14+8)
//R3=*(R14+12)
//R12=*(R14+16)
LDMIA R1!,{R4-R11} /*从R1指向的地址取8个32位数据存入R4-R11, 每取一次,让R1指针加4,因此最后R1指针加了32*/
2.STMIA指令、STMIB指令、STMDB指令、STMDA指令
同理,STM是STR指令的增强型 , 将多组寄存器数据保存进连续地址空间。
STMIA R13!,{R0-R1} /*将R0,R1寄存器中的数据存入R13指向的栈空间, r13指向的地址存入R0数据,再地址+4后存入R1的数据*/
2.3 入栈出栈指令
函调调用过程中离不开现场的保护和恢复。保存 R0~R15 寄存器的操作就叫做现场保护,恢复 R0~R15 寄存器的操作就叫做恢复现场。
2.3.1 PUSH
比如要将 R0~R3 和 R12 这 5 个寄存器压栈,当前的 SP (stack pointer)指针指向 0X80000000,我们知道栈空间的地址是向下增长的,堆空间地址向上增长。
PUSH {R0~R3, R12} @将 R0~R3 和 R12 压栈
那么压栈完成以后的堆栈如下:入栈保护现场完这5个寄存器后,SP指向0X7FFFFFEC(每压栈一个寄存器,SP地址减4)
再次保存LR寄存器,进行压栈:
PUSH {LR} @将 LR 进行压栈
2.3.2 POP
POP {LR} @先恢复 LR
POP {R0~R3,R12} @在恢复 R0~R3,R12
可以看出入栈出栈本质都是对SP指针进行加减,入栈减,出栈加,入栈把寄存器依次保存进SP指向的地址去,出栈从SP地址依次取出数据。
2.3.3 STMFD和LDMFD
入栈出栈的另外一种写法是“STMFD SP!”和“LDMFD SP!”。
STMFD SP!,{R0~R3, R12} @R0~R3,R12 入栈
STMFD SP!,{LR} @LR 入栈
bl xxx
LDMFD SP!, {LR} @先恢复 LR
LDMFD SP!, {R0~R3, R12} @再恢复 R0~R3, R12
STMFD 可以分为两部分:STM 和 FD,同理,LDMFD 也可以分为 LDM 和 FD。前面我们讲了 LDR 和 STR,这两个是数据加载和存储指令,但是每次只能读写存储器中的一个数据。STM 和 LDM 就是多存储和多加载,可以连续的读写存储器中的多个连续数据。
FD 是 Full Descending 的缩写,即满递减的意思。根据 ATPCS 规则,ARM 使用的 FD 类型的堆栈,SP 指向最后一个入栈的数值,堆栈是由高地址向下增长的,也就是前面说的向下增长的堆栈,因此最常用的指令就是 STMFD 和 LDMFD。STM 和 LDM 的指令寄存器列表中编号小的对应低地址,编号高的对应高地址.
2.4 跳转指令
2.4.1 B 指令
B 指令会将 PC 寄存器的值设置为跳转目标地址,如果要调用的函数不会再返回到原来的执行处,那就可以用 B 指令.
_start:
ldr sp,=0X80200000 @设置栈指针
b main @跳转到 main 函数
在汇编中初始化 C 运行环境,然后跳转到 C 文件的 main 函数中运行,上述代码只是初始化了 SP 指针,有些处理器还需要做其他的初始化,比如初始化 DDR 等等.
2.4.2 BL 指令
有返回的跳转,跳转之前会在寄存器 LR(R14)中保存当前 PC 寄存器值,所以可以通过将 LR 寄存器中的值重新加载到 PC 中来继续从跳转之前的代码处运行,这是子程序调用一个基本但常用的手段。
比如 Cortex-A 处理器的 irq 中断服务函数都是汇编写的,主要用汇编来实现现场的保护和恢复、获取中断号等。但是具体的中断处理过程都是 C 函数,所以就会存在汇编中调用 C 函数的问题。而且当 C 语言版本的中断处理函数执行完成以后是需要返回到irq 汇编中断服务函数,因为还要处理其他的工作,一般是恢复现场。
push {r0, r1} @保存 r0,r1
cps #0x13 @进入 SVC 模式,允许其他中断再次进去
bl system_irqhandler @加载 C 语言中断处理函数到 r2 寄存器中
cps #0x12 @进入 IRQ 模式
pop {r0, r1}
str r0, [r1, #0X10] @中断执行完成,写 EOIR
跳转指令总结:
有多种跳转操作,比如:
①、直接使用跳转指令 B、BL、BX 等。
②、直接向 PC 寄存器里面写入数据。
2.5 算数运算指令
加减乘除,常用的运算指令用法:
2.6 逻辑运算指令
与或非指令用法:
来看一个例子利用arm汇编进行初始化C语言环境。让arm进入svc模式,才能访问特殊寄存器如cpsr, spsr, sp指针。
.global _start
_start:
/* 进入SVC模式 */
mrs r0, cpsr
bic r0, r0, #0x1f /* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 */
orr r0, r0, #0x13 /* r0或上0x13,表示使用SVC模式 */
msr cpsr, r0 /* 将r0 的数据写入到cpsr_c中 */
ldr sp, =0X80200000 /* 设置栈指针 */
b main /* 跳转到main函数 */
2.7 内存屏障指令
2.7.1 Data Memory Barrier(DMB):数据内存屏障
DMB指令确保在DMB之前的所有显式数据内存传输指令都已经在内存中读取或写入完成,同时确保任何后续的数据内存传输指令都将在DMB执行之后开始执行,否则有些数据传输指令可能会提前执行。保证了两个内存访问能按正确的顺序执行。
应用场景:
(1)DMA
在使用DMA控制器时,需要在CPU内存访问和DMA操作之间插入DMB屏障,以确保CPU当前的内存读写操作在DMA开始之前完成。
(2)多核系统中的信号量
在多核系统中,使用信号量进行核间同步。需要使用DMB来强制指定内存执行顺序,以避免潜在的竞态条件或数据不一致性。当一个核要访问共享资源之前,它会先检查信号量的状态。如果信号量已经被另一个核获取,当前核就必须等待,直到信号量状态变为可用。这个等待过程需要保证在一个核释放信号量之后,其他核能够立即看到信号量状态的变化,而不是因为处理器优化或缓存导致的无效读取而产生错误。
2.7.2 Data Synchronization Barrier(DSB):数据同步屏障
在多线程编程中,两个线程同时对共享的内存进行读写操作,由于读/写操作的重排序,就会导致数据的不一致, DSB指令时,它确保在DSB之前的所有显式数据内存传输指令都已经在内存中读取或写入完成,同时确保任何后续的指令都将在DSB执行之后开始执行。
应用场景:
例如启用或禁用特定的中断、配置时钟、设置系统控制位等。为了确保对SCS的修改在下一条指令执行之前生效,需要使用DSB指令进行数据同步。一些特殊的指令如SVC(Supervisor Call,特权级调用)、WFI(Wait For Interrupt,等待中断)、WFE(Wait For Event,等待事件)等操作,涉及到特权级的转换或者等待系统事件发生,需要使用DSB指令。
2.7.3 Instruction Synchronization Barrier(ISB):指令同步屏障
插入ISB指令,处理器会将流水线中的指令全部刷新,从而确保之前的指令不会影响后续指令的执行,并且后续指令将从正确的上下文开始重新获取。
应用场景:
在进行异常进入之前,处理器会执行ISB操作。这样做的目的是刷新指令流水线,确保异常处理程序的指令是从正确的地址开始执行,避免异常之前的指令对异常处理程序造成干扰。
在进行异常返回之前,处理器同样会执行ISB操作。这样做的目的是刷新指令流水线,确保返回时从正确的地址重新获取指令,避免异常处理程序的指令对正常任务造成干扰。
3 arm-v7 cpu运行模式
以前的 ARM 处理器有 7 中运行模型:User、FIQ、IRQ、Supervisor(SVC)、Abort、Undef和 System,其中 User 是非特权模式,其余 6 中都是特权模式。
到了Cortex-A7 处理器有 9 种处理模式:
模式 | 描述 |
---|---|
User(USR) | 用户模式,非特权模式,大部分程序运行的时候就处于此模式。 |
FIQ | 快速中断模式,进入 FIQ 中断异常 |
IRQ | 一般中断模式。 |
Supervisor(SVC) | 超级管理员模式,特权模式,供操作系统使用。 |
Monitor(MON) | 监视模式?这个模式用于安全扩展模式。 |
Abort(ABT) | 数据访问终止模式,用于虚拟存储以及存储保护。 |
Hyp(HYP) | Hyp(HYP) 超级监视模式?用于虚拟化扩展。 |
Undef(UND) | Undef(UND) 未定义指令终止模式。 |
System(SYS) | System(SYS) 系统模式,用于运行特权级的操作系统任务 |
九种模式所对应的寄存器:
arm920t cpu模式
4 arm-v7 cpu通用和特殊寄存器
ARM 架构提供了 16 个 32 位的通用寄存器(R0~R15)供软件使用,前 15 个(R0~R14)可以用作通用的数据存储,R15 是程序计数器 PC,用来保存将要执行的指令。ARM 还提供了一个当前程序状态寄存器 CPSR 和一个备份程序状态寄存器 SPSR,SPSR 寄存器就是 CPSR 寄存器的备份。
4.1 通用寄存器
R0~R15 就是通用寄存器,通用寄存器可以分为以下三类:
①、未备份寄存器,即 R0~R7。
②、备份寄存器,即 R8~R14。
③、程序计数器 PC,即 R15。
4.1.2 未备份寄存器R0-R7
未备份寄存器指的是 R0~R7 这 8 个寄存器,因为在所有的处理器模式下这 8 个寄存器都是同一个物理寄存器,在不同的模式下,这 8 个寄存器中的数据就会被破坏.
4.1.3 备份寄存器
4.1.3.1 R8~R12
R8~R12 这 5 个寄存器有2种物理寄存器.在快速中断模式下(FIQ)它们对应着 Rx_irq(x=8~12)物理寄存器,其他模式下对应着 Rx(8~12)物理寄存器. FIQ 模式下的 R8~R12 是独立的,因此中断处理程序可以不用执行保存和恢复中断现场的指令,从而加速中断的执行过程。
4.1.3.2 R13 (SP)
R13 一共有 8 个物理寄存器,其中一个是用户模式(User)和系统模式(Sys)共用的,剩下的 7 个分别对应 7 种不同的模式。R13 也叫做 SP,用来做为栈指针。基本上每种模式
都有一个自己的 R13 物理寄存器,应用程序会初始化 R13,使其指向该模式专用的栈地址,这就是常说的初始化 SP 指针.
4.1.3.2 R14 (LR)
R14 一共有 7 个物理寄存器,其中一个是用户模式(User)、系统模式(Sys)和超级监视模式(Hyp)所共有的,剩下的 6 个分别对应 6 种不同的模式.
LR被叫做链接寄存器:
①用来存放子函数的返回地址。
在子函数中,将 R14(LR)中的值赋给 R15(PC)即可完成子函数返回,比如在子程序中可以使用如下代码:
MOV PC, LR
②当异常发生以后,该异常模式对应的 R14寄存器被设置成该异常模式将要返回的地址.
subs pc, lr, #4 /* 将lr-4赋给pc */
比如下面代码示例:
0X2000 MOV R1, R0 ;执行
0X2004 MOV R2, R3 ;译指
0X2008 MOV R4, R5 ;取值 PC
当前正在执行 0X2000地址处的指令“MOV R1, R0”,但是 PC 里面已经保存了 0X2008 地址处的指令“MOV R4, R5”。假设此时发生了中断,中断发生的时候保存在 lr 中的是 pc 的值,也就是地址 0X2008。
4.1.3.2 R15 (PC)
R15 保存着当前执行的指令地址值加 8 个字节,这是因为 ARM的流水线机制导致的。ARM 处理器 3 级流水线:取指->译码->执行,这三级流水线循环执行,比如当前正在执行第一条指令的同时也对第二条指令进行译码,第三条指令也同时被取出存放在 R15(PC)中.
对于arm32位处理器:
R15 (PC)值 = 当前执行的程序位置 + 8 个字节
4.2 特殊寄存器
4.2.1 CPSR
当前程序状态寄存器(current program status register),所有模式共用一个 CPSR 物理寄存器,因此 CPSR 可以在任何模式下被访问。
N(bit31):当两个补码表示的 有符号整数运算的时候,N=1 表示运算对的结果为负数,N=0表示结果为正数。
Z(bit30):Z=1 表示运算结果为零,Z=0 表示运算结果不为零,对于 CMP 指令,Z=1 表示进行比较的两个数大小相等。
C(bit29):在加法指令中,当结果产生了进位,则 C=1,表示无符号数运算发生上溢,其它情况下 C=0。在减法指令中,当运算中发生借位,则 C=0,表示无符号数运算发生下溢,其它情况下 C=1。对于包含移位操作的非加/减法运算指令,C 中包含最后一次溢出的位的数值,对于其它非加/减运算指令,C 位的值通常不受影响。
V(bit28):对于加/减法运算指令,当操作数和运算结果表示为二进制的补码表示的带符号数时,V=1 表示符号位溢出,通常其他位不影响 V 位。
Q(bit27):仅 ARM v5TE_J 架构支持,表示饱和状态,Q=1 表示累积饱和,Q=0 表示累积不饱和。
IT1:0:和 IT7:2一起组成 IT[7:0],作为 IF-THEN 指令执行状态。
J(bit24):仅 ARM_v5TE-J 架构支持,J=1 表示处于 Jazelle 状态,此位通常和 T(bit5)位一起表示当前所使用的指令集:
J | T | 描述 |
---|---|---|
0 | 0 | ARM |
0 | 1 | Thumb |
1 | 1 | ThumbEE |
GE3:0:SIMD 指令有效,大于或等于。
IT7:2:参考 IT[1:0]。
E(bit9):大小端控制位,E=1 表示大端模式,E=0 表示小端模式。
A(bit8):禁止异步中断位,A=1 表示禁止异步中断。
I(bit7):I=1 禁止 IRQ,I=0 使能 IRQ。
F(bit6):F=1 禁止 FIQ,F=0 使能 FIQ。
T(bit5):控制指令执行状态,表明本指令是 ARM 指令还是 Thumb 指令,通常和 J(bit24)一起表明指令类型,参考 J(bit24)位。
M[4:0]:处理器模式控制位
cpsr最常用就是来控制处理器模式
M[4:0] | 处理器模式 |
---|---|
10000 | User 模式 |
10001 | FIQ 模式 |
10010 | IRQ 模式 |
10011 | Supervisor(SVC)模式 |
10110 | Monitor(MON)模式 |
10111 | Abort(ABT)模式 |
11010 | Hyp(HYP)模式 |
11011 | Undef(UND)模式 |
11111 | System(SYS)模式 |
/* 进入SVC模式 */
mrs r0, cpsr
bic r0, r0, #0x1f /* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 */
orr r0, r0, #0x13 /* r0或上0x13,表示使用SVC模式 */
msr cpsr, r0 /* 将r0 的数据写入到cpsr_c中 */
ldr sp, =0X80200000 /* 设置SVC模式下的栈首地址为0X80200000,大小为2MB */
4.2.2 SPSR
除了 User 和 Sys 这两个模式以外,其他 7 个模式每个都配备了一个专用的物理状态寄存器,叫做 SPSR(备份程序状态寄存器),当特定的异常中断发生时,SPSR 寄存器用来保存当前程序状态寄存器(CPSR)的值,当异常退出以后可以用 SPSR 中保存的值来恢复 CPSR。User 和 Sys 这两个模式不是异常模式,所以并没有配备 SPSR,因此不能在 User 和Sys 模式下访问 SPSR。
5 CP15协处理器
CP15 协处理器一般用于存储系统管理,但是在中断中也会使用到,比如进入reset复位异常向量时,需要利用协处理器命令进行ICache DCache的开关。CP15 协处理器一共有16 个 32 位寄存器(C0-C15),MRC和MCR用来访问CP15协处理器。
MRC: 将 CP15 协处理器中的寄存器数据读到 ARM 寄存器中。
MCR: 将 ARM 寄存器的数据写入到 CP15 协处理器寄存器中。
格式如下:
MCR{cond} p15, <opc1>, <Rt>, <CRn>, <CRm>, <opc2>
cond:指令执行的条件码,如果忽略的话就表示无条件执行。
opc1:协处理器要执行的操作码。
Rt:ARM 源寄存器,要写入到 CP15 寄存器的数据就保存在此寄存器中。
CRn:CP15 协处理器的目标寄存器。
CRm:协处理器中附加的目标寄存器或者源操作数寄存器,如果不需要附加信息就将CRm 设置为 C0,否则结果不可预测。
opc2:可选的协处理器特定操作码,当不需要的时候要设置为 0
例如:
MRC p15, 0, r0, c0, c0, 0 //将 CP15 中 C0 寄存器的值读取到 R0 寄存器
5.1 c0寄存器(MIDR)
使用 MRC 或者 MCR 指令访问 c0-c15 寄存器的时候,指令中的 CRn、opc1、CRm 和 opc2 通过不同的搭配,其得到的寄存器含义不同:
当CRn=c0,opc1=0,CRm=c0,opc2=0 的时候就表示此时的 c0 就是 MIDR 寄存器,也就是主 ID 寄存器,这个也是 c0 的基本作用。来看下c0作为MDIR寄存器时的含义:
bit31:24:厂商编号,0X41,ARM。
bit23:20:内核架构的主版本号,ARM 内核版本一般使用 rnpn 来表示,比如 r0p1,其中 r0
后面的 0 就是内核架构主版本号。
bit19:16:架构代码,0XF,ARMv7 架构。
bit15:4:内核版本号,0XC07,Cortex-A7 MPCore 内核。
bit3:0:内核架构的次版本号,rnpn 中的 pn,比如 r0p1 中 p1 后面的 1 就是次版本号。
5.2 c1寄存器(SCTLR)
CRn=c1,opc1=0,CRm=c0,opc2=0 的时候就表示此时的 c1 就是 SCTLR 寄存器,也就是系统控制寄存器,这个是 c1 的基本作用。
SCTLR 寄存器主要是完成控制功能的,比如使能或者禁止 MMU、I/D Cache 等。 SCTLR 寄存器展开如下:
bit13:V , 中断向量表基地址选择位,为 0 的话中断向量表基地址为 0X00000000,软件可以使用 VBAR 来重映射此基地址,也就是中断向量表重定位。为 1 的话中断向量表基地址为0XFFFF0000,此基地址不能被重映射。
bit12:I,I Cache 使能位,为 0 的话关闭 I Cache,为 1 的话使能 I Cache。
bit11:Z,分支预测使能位,如果开启 MMU 的话,此位也会使能。
bit10:SW,SWP 和 SWPB 使能位,当为 0 的话关闭 SWP 和 SWPB 指令,当为 1 的时候就使能 SWP 和 SWPB 指令。
bit9:3:未使用,保留。
bit2:C,D Cache 和缓存一致性使能位,为 0 的时候禁止 D Cache 和缓存一致性,为 1 时使能。
bit1:A,内存对齐检查使能位,为 0 的时候关闭内存对齐检查,为 1 的时候使能内存对齐检查。
bit0:M,MMU 使能位,为 0 的时候禁止 MMU,为 1 的时候使能 MMU。
举个读写SCTLR的例子:
MRC p15, 0, <Rt>, c1, c0, 0 ;读取 SCTLR 寄存器,数据保存到 Rt 中。
MCR p15, 0, <Rt>, c1, c0, 0 ;将 Rt 中的数据写到 SCTLR(c1)寄存器中。
再来一个关闭MMU,ICache,DCache的例子:
mrc p15, 0, r0, c1, c0, 0 /* 读取CP15的C1寄存器到R0中 */
bic r0, r0, #(0x1 << 12) /* 清除C1寄存器的bit12位(I位),关闭I Cache */
bic r0, r0, #(0x1 << 2) /* 清除C1寄存器的bit2(C位),关闭D Cache */
bic r0, r0, #0x2 /* 清除C1寄存器的bit1(A位),关闭对齐 */
bic r0, r0, #(0x1 << 11) /* 清除C1寄存器的bit11(Z位),关闭分支预测 */
bic r0, r0, #0x1 /* 清除C1寄存器的bit0(M位),关闭MMU */
mcr p15, 0, r0, c1, c0, 0 /* 将r0寄存器中的值写入到CP15的C1寄存器中 */
5.3 c12寄存器(VBAR)
CRn=c12,opc1=0,CRm=c0,opc2=0 的时候就表示此时 c12 为 VBAR 寄存器,也就是中断向量表基地址寄存器。
比如代码链接到DDR的某个位置作为起始地址,起始地址为0X87800000,而中断向量表肯定要放到最前面,也就是 0X87800000 这个地址处。所以就需要设置 VBAR 为 0X87800000,设置命令如下:
dsb
isb
ldr r0, =0x87800000 ; r0=0x87800000
MCR p15, 0, r0, c12, c0, 0 ;将 r0 里面的数据写入到 c12 中,即 c12=0X87800000
dsb
isb
5.3 c15寄存器(CBAR)
CBAR寄存器中保存了GIC(Generic Interrupt Controller)的基地址。GIC基地址偏移0x1000是分发器 block, 偏移0x2000是CPU 接口端 block。
MRC p15, 4, r1, c15, c0, 0 ; 获取 GIC 基础地址,基地址保存在 r1 中
6 参考链接
armv7都是32位处理器,典型的有CorTex-A A7 A8 A9 A15 A17。armv8采用64位处理器,典型的比如手机处理器,有Cortex A53 A76 A77都是64位架构。
https://developer.arm.com/documentation/ddi0406/cd?lang=en
https://developer.arm.com/documentation/den0013/d/?lang=en
https://developer.arm.com/documentation/ddi0464/latest/
7 附件1:armv7裸机启动汇编示例:
点击查看代码
.global _start /* 全局标号 */
_start:
ldr pc, =Reset_Handler /* 复位中断 */
ldr pc, =Undefined_Handler /* 未定义中断 */
ldr pc, =SVC_Handler /* SVC(Supervisor)中断 */
ldr pc, =PrefAbort_Handler /* 预取终止中断 */
ldr pc, =DataAbort_Handler /* 数据终止中断 */
ldr pc, =NotUsed_Handler /* 未使用中断 */
ldr pc, =IRQ_Handler /* IRQ中断 */
ldr pc, =FIQ_Handler /* FIQ(快速中断)未定义中断 */
/* 复位中断 */
Reset_Handler:
cpsid i /* 关闭全局中断 */
/* 关闭I,DCache和MMU
* 采取读-改-写的方式。
*/
mrc p15, 0, r0, c1, c0, 0 /* 读取CP15的C1寄存器到R0中 */
bic r0, r0, #(0x1 << 12) /* 清除C1寄存器的bit12位(I位),关闭I Cache */
bic r0, r0, #(0x1 << 2) /* 清除C1寄存器的bit2(C位),关闭D Cache */
bic r0, r0, #0x2 /* 清除C1寄存器的bit1(A位),关闭对齐 */
bic r0, r0, #(0x1 << 11) /* 清除C1寄存器的bit11(Z位),关闭分支预测 */
bic r0, r0, #0x1 /* 清除C1寄存器的bit0(M位),关闭MMU */
mcr p15, 0, r0, c1, c0, 0 /* 将r0寄存器中的值写入到CP15的C1寄存器中 */
#if 0
/* 汇编版本设置中断向量表偏移 */
ldr r0, =0X87800000
dsb
isb
mcr p15, 0, r0, c12, c0, 0
dsb
isb
#endif
/* 设置各个模式下的栈指针,
* 注意:IMX6UL的堆栈是向下增长的!
* 堆栈指针地址一定要是4字节地址对齐的!!!
* DDR范围:0X80000000~0X9FFFFFFF
*/
/* 进入IRQ模式 */
mrs r0, cpsr
bic r0, r0, #0x1f /* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 */
orr r0, r0, #0x12 /* r0或上0x13,表示使用IRQ模式 */
msr cpsr, r0 /* 将r0 的数据写入到cpsr_c中 */
ldr sp, =0x80600000 /* 设置IRQ模式下的栈首地址为0X80600000,大小为2MB */
/* 进入SYS模式 */
mrs r0, cpsr
bic r0, r0, #0x1f /* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 */
orr r0, r0, #0x1f /* r0或上0x13,表示使用SYS模式 */
msr cpsr, r0 /* 将r0 的数据写入到cpsr_c中 */
ldr sp, =0x80400000 /* 设置SYS模式下的栈首地址为0X80400000,大小为2MB */
/* 进入SVC模式 */
mrs r0, cpsr
bic r0, r0, #0x1f /* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 */
orr r0, r0, #0x13 /* r0或上0x13,表示使用SVC模式 */
msr cpsr, r0 /* 将r0 的数据写入到cpsr_c中 */
ldr sp, =0X80200000 /* 设置SVC模式下的栈首地址为0X80200000,大小为2MB */
cpsie i /* 打开全局中断 */
#if 0
/* 使能IRQ中断 */
mrs r0, cpsr /* 读取cpsr寄存器值到r0中 */
bic r0, r0, #0x80 /* 将r0寄存器中bit7清零,也就是CPSR中的I位清零,表示允许IRQ中断 */
msr cpsr, r0 /* 将r0重新写入到cpsr中 */
#endif
b main /* 跳转到main函数 */
/* 未定义中断 */
Undefined_Handler:
ldr r0, =Undefined_Handler
bx r0
/* SVC中断 */
SVC_Handler:
ldr r0, =SVC_Handler
bx r0
/* 预取终止中断 */
PrefAbort_Handler:
ldr r0, =PrefAbort_Handler
bx r0
/* 数据终止中断 */
DataAbort_Handler:
ldr r0, =DataAbort_Handler
bx r0
/* 未使用的中断 */
NotUsed_Handler:
ldr r0, =NotUsed_Handler
bx r0
IRQ_Handler:
push {lr} /* 保存lr地址 */
push {r0-r3, r12} /* 保存r0-r3,r12寄存器 */
mrs r0, spsr /* 读取spsr寄存器 */
push {r0} /* 保存spsr寄存器 */
mrc p15, 4, r1, c15, c0, 0 /* 从CP15的C0寄存器内的值到R1寄存器中
* 参考文档ARM Cortex-A(armV7)编程手册V4.0.pdf P49
* Cortex-A7 Technical ReferenceManua.pdf P68 P138
*/
add r1, r1, #0X2000 /* GIC基地址加0X2000,也就是GIC的CPU接口端基地址 */
ldr r0, [r1, #0XC] /* GIC的CPU接口端基地址加0X0C就是GICC_IAR寄存器,
* GICC_IAR寄存器保存这当前发生中断的中断号,我们要根据
* 这个中断号来绝对调用哪个中断服务函数
*/
push {r0, r1} /* 保存r0,r1 */
cps #0x13 /* 进入SVC模式,允许其他中断再次进去 */
push {lr} /* 保存SVC模式的lr寄存器 */
ldr r2, =system_irqhandler /* 加载C语言中断处理函数到r2寄存器中*/
blx r2 /* 运行C语言中断处理函数,带有一个参数,保存在R0寄存器中 */
pop {lr} /* 执行完C语言中断服务函数,lr出栈 */
cps #0x12 /* 进入IRQ模式 */
pop {r0, r1}
str r0, [r1, #0X10] /* 中断执行完成,写EOIR */
pop {r0}
msr spsr_cxsf, r0 /* 恢复spsr */
pop {r0-r3, r12} /* r0-r3,r12出栈 */
pop {lr} /* lr出栈 */
subs pc, lr, #4 /* 将lr-4赋给pc */
/* FIQ中断 */
FIQ_Handler:
ldr r0, =FIQ_Handler
bx r0
8 附件2:GIC指令和寄存器定义(内联汇编方式)
点击查看代码
#ifndef __CORTEX_CA7_H
#define __CORTEX_CA7_H
/***************************************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名 : core_ca7.h
作者 : 左忠凯
版本 : V1.0
描述 : Cortex-A7内核通用文件。
其他 : 本文件主要实现了对GIC操作函数
论坛 : www.wtmembed.com
日志 : 初版V1.0 2019/1/4 左忠凯创建
***************************************************************/
#include <stdint.h>
#include <string.h>
#define FORCEDINLINE __attribute__((always_inline))
#define __ASM __asm /* GNU C语言内嵌汇编关键字 */
#define __INLINE inline /* GNU内联关键字 */
#define __STATIC_INLINE static inline
#define __IM volatile const /* 只读 */
#define __OM volatile /* 只写 */
#define __IOM volatile /* 读写 */
#define __STRINGIFY(x) #x
/* C语言实现MCR指令 */
#define __MCR(coproc, opcode_1, src, CRn, CRm, opcode_2) \
__ASM volatile ("MCR " __STRINGIFY(p##coproc) ", " __STRINGIFY(opcode_1) ", " \
"%0, " __STRINGIFY(c##CRn) ", " __STRINGIFY(c##CRm) ", " \
__STRINGIFY(opcode_2) \
: : "r" (src) )
/* C语言实现MRC指令 */
#define __MRC(coproc, opcode_1, CRn, CRm, opcode_2) \
({ \
uint32_t __dst; \
__ASM volatile ("MRC " __STRINGIFY(p##coproc) ", " __STRINGIFY(opcode_1) ", " \
"%0, " __STRINGIFY(c##CRn) ", " __STRINGIFY(c##CRm) ", " \
__STRINGIFY(opcode_2) \
: "=r" (__dst) ); \
__dst; \
})
/* 其他一些C语言内嵌汇编 */
__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_APSR(uint32_t apsr)
{
__ASM volatile ("MSR apsr, %0" : : "r" (apsr) : "cc");
}
__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_CPSR(void)
{
uint32_t result;
__ASM volatile ("MRS %0, cpsr" : "=r" (result) );
return(result);
}
__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_CPSR(uint32_t cpsr)
{
__ASM volatile ("MSR cpsr, %0" : : "r" (cpsr) : "cc");
}
__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_FPEXC(void)
{
uint32_t result;
__ASM volatile ("VMRS %0, fpexc" : "=r" (result) );
return result;
}
__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_FPEXC(uint32_t fpexc)
{
__ASM volatile ("VMSR fpexc, %0" : : "r" (fpexc));
}
/*******************************************************************************
* 一些内核寄存器定义和抽象
定义如下几个内核寄存器:
- CPSR
- CP15
******************************************************************************/
/* CPSR寄存器
* 参考资料:ARM Cortex-A(armV7)编程手册V4.0.pdf P46
*/
typedef union
{
struct
{
uint32_t M:5; /*!< bit: 0.. 4 Mode field */
uint32_t T:1; /*!< bit: 5 Thumb execution state bit */
uint32_t F:1; /*!< bit: 6 FIQ mask bit */
uint32_t I:1; /*!< bit: 7 IRQ mask bit */
uint32_t A:1; /*!< bit: 8 Asynchronous abort mask bit */
uint32_t E:1; /*!< bit: 9 Endianness execution state bit */
uint32_t IT1:6; /*!< bit: 10..15 If-Then execution state bits 2-7 */
uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */
uint32_t _reserved0:4; /*!< bit: 20..23 Reserved */
uint32_t J:1; /*!< bit: 24 Jazelle bit */
uint32_t IT0:2; /*!< bit: 25..26 If-Then execution state bits 0-1 */
uint32_t Q:1; /*!< bit: 27 Saturation condition flag */
uint32_t V:1; /*!< bit: 28 Overflow condition code flag */
uint32_t C:1; /*!< bit: 29 Carry condition code flag */
uint32_t Z:1; /*!< bit: 30 Zero condition code flag */
uint32_t N:1; /*!< bit: 31 Negative condition code flag */
} b; /*!< Structure used for bit access */
uint32_t w; /*!< Type used for word access */
} CPSR_Type;
/* CP15的SCTLR寄存器
* 参考资料:Cortex-A7 Technical ReferenceManua.pdf P105
*/
typedef union
{
struct
{
uint32_t M:1; /*!< bit: 0 MMU enable */
uint32_t A:1; /*!< bit: 1 Alignment check enable */
uint32_t C:1; /*!< bit: 2 Cache enable */
uint32_t _reserved0:2; /*!< bit: 3.. 4 Reserved */
uint32_t CP15BEN:1; /*!< bit: 5 CP15 barrier enable */
uint32_t _reserved1:1; /*!< bit: 6 Reserved */
uint32_t B:1; /*!< bit: 7 Endianness model */
uint32_t _reserved2:2; /*!< bit: 8.. 9 Reserved */
uint32_t SW:1; /*!< bit: 10 SWP and SWPB enable */
uint32_t Z:1; /*!< bit: 11 Branch prediction enable */
uint32_t I:1; /*!< bit: 12 Instruction cache enable */
uint32_t V:1; /*!< bit: 13 Vectors bit */
uint32_t RR:1; /*!< bit: 14 Round Robin select */
uint32_t _reserved3:2; /*!< bit:15..16 Reserved */
uint32_t HA:1; /*!< bit: 17 Hardware Access flag enable */
uint32_t _reserved4:1; /*!< bit: 18 Reserved */
uint32_t WXN:1; /*!< bit: 19 Write permission implies XN */
uint32_t UWXN:1; /*!< bit: 20 Unprivileged write permission implies PL1 XN */
uint32_t FI:1; /*!< bit: 21 Fast interrupts configuration enable */
uint32_t U:1; /*!< bit: 22 Alignment model */
uint32_t _reserved5:1; /*!< bit: 23 Reserved */
uint32_t VE:1; /*!< bit: 24 Interrupt Vectors Enable */
uint32_t EE:1; /*!< bit: 25 Exception Endianness */
uint32_t _reserved6:1; /*!< bit: 26 Reserved */
uint32_t NMFI:1; /*!< bit: 27 Non-maskable FIQ (NMFI) support */
uint32_t TRE:1; /*!< bit: 28 TEX remap enable. */
uint32_t AFE:1; /*!< bit: 29 Access flag enable */
uint32_t TE:1; /*!< bit: 30 Thumb Exception enable */
uint32_t _reserved7:1; /*!< bit: 31 Reserved */
} b; /*!< Structure used for bit access */
uint32_t w; /*!< Type used for word access */
} SCTLR_Type;
/* CP15 寄存器SCTLR各个位定义 */
#define SCTLR_TE_Pos 30U /*!< SCTLR: TE Position */
#define SCTLR_TE_Msk (1UL << SCTLR_TE_Pos) /*!< SCTLR: TE Mask */
#define SCTLR_AFE_Pos 29U /*!< SCTLR: AFE Position */
#define SCTLR_AFE_Msk (1UL << SCTLR_AFE_Pos) /*!< SCTLR: AFE Mask */
#define SCTLR_TRE_Pos 28U /*!< SCTLR: TRE Position */
#define SCTLR_TRE_Msk (1UL << SCTLR_TRE_Pos) /*!< SCTLR: TRE Mask */
#define SCTLR_NMFI_Pos 27U /*!< SCTLR: NMFI Position */
#define SCTLR_NMFI_Msk (1UL << SCTLR_NMFI_Pos) /*!< SCTLR: NMFI Mask */
#define SCTLR_EE_Pos 25U /*!< SCTLR: EE Position */
#define SCTLR_EE_Msk (1UL << SCTLR_EE_Pos) /*!< SCTLR: EE Mask */
#define SCTLR_VE_Pos 24U /*!< SCTLR: VE Position */
#define SCTLR_VE_Msk (1UL << SCTLR_VE_Pos) /*!< SCTLR: VE Mask */
#define SCTLR_U_Pos 22U /*!< SCTLR: U Position */
#define SCTLR_U_Msk (1UL << SCTLR_U_Pos) /*!< SCTLR: U Mask */
#define SCTLR_FI_Pos 21U /*!< SCTLR: FI Position */
#define SCTLR_FI_Msk (1UL << SCTLR_FI_Pos) /*!< SCTLR: FI Mask */
#define SCTLR_UWXN_Pos 20U /*!< SCTLR: UWXN Position */
#define SCTLR_UWXN_Msk (1UL << SCTLR_UWXN_Pos) /*!< SCTLR: UWXN Mask */
#define SCTLR_WXN_Pos 19U /*!< SCTLR: WXN Position */
#define SCTLR_WXN_Msk (1UL << SCTLR_WXN_Pos) /*!< SCTLR: WXN Mask */
#define SCTLR_HA_Pos 17U /*!< SCTLR: HA Position */
#define SCTLR_HA_Msk (1UL << SCTLR_HA_Pos) /*!< SCTLR: HA Mask */
#define SCTLR_RR_Pos 14U /*!< SCTLR: RR Position */
#define SCTLR_RR_Msk (1UL << SCTLR_RR_Pos) /*!< SCTLR: RR Mask */
#define SCTLR_V_Pos 13U /*!< SCTLR: V Position */
#define SCTLR_V_Msk (1UL << SCTLR_V_Pos) /*!< SCTLR: V Mask */
#define SCTLR_I_Pos 12U /*!< SCTLR: I Position */
#define SCTLR_I_Msk (1UL << SCTLR_I_Pos) /*!< SCTLR: I Mask */
#define SCTLR_Z_Pos 11U /*!< SCTLR: Z Position */
#define SCTLR_Z_Msk (1UL << SCTLR_Z_Pos) /*!< SCTLR: Z Mask */
#define SCTLR_SW_Pos 10U /*!< SCTLR: SW Position */
#define SCTLR_SW_Msk (1UL << SCTLR_SW_Pos) /*!< SCTLR: SW Mask */
#define SCTLR_B_Pos 7U /*!< SCTLR: B Position */
#define SCTLR_B_Msk (1UL << SCTLR_B_Pos) /*!< SCTLR: B Mask */
#define SCTLR_CP15BEN_Pos 5U /*!< SCTLR: CP15BEN Position */
#define SCTLR_CP15BEN_Msk (1UL << SCTLR_CP15BEN_Pos) /*!< SCTLR: CP15BEN Mask */
#define SCTLR_C_Pos 2U /*!< SCTLR: C Position */
#define SCTLR_C_Msk (1UL << SCTLR_C_Pos) /*!< SCTLR: C Mask */
#define SCTLR_A_Pos 1U /*!< SCTLR: A Position */
#define SCTLR_A_Msk (1UL << SCTLR_A_Pos) /*!< SCTLR: A Mask */
#define SCTLR_M_Pos 0U /*!< SCTLR: M Position */
#define SCTLR_M_Msk (1UL << SCTLR_M_Pos) /*!< SCTLR: M Mask */
/* CP15的ACTLR寄存器
* 参考资料:Cortex-A7 Technical ReferenceManua.pdf P113
*/
typedef union
{
struct
{
uint32_t _reserved0:6; /*!< bit: 0.. 5 Reserved */
uint32_t SMP:1; /*!< bit: 6 Enables coherent requests to the processor */
uint32_t _reserved1:3; /*!< bit: 7.. 9 Reserved */
uint32_t DODMBS:1; /*!< bit: 10 Disable optimized data memory barrier behavior */
uint32_t L2RADIS:1; /*!< bit: 11 L2 Data Cache read-allocate mode disable */
uint32_t L1RADIS:1; /*!< bit: 12 L1 Data Cache read-allocate mode disable */
uint32_t L1PCTL:2; /*!< bit:13..14 L1 Data prefetch control */
uint32_t DDVM:1; /*!< bit: 15 Disable Distributed Virtual Memory (DVM) transactions */
uint32_t _reserved3:12; /*!< bit:16..27 Reserved */
uint32_t DDI:1; /*!< bit: 28 Disable dual issue */
uint32_t _reserved7:3; /*!< bit:29..31 Reserved */
} b; /*!< Structure used for bit access */
uint32_t w; /*!< Type used for word access */
} ACTLR_Type;
#define ACTLR_DDI_Pos 28U /*!< ACTLR: DDI Position */
#define ACTLR_DDI_Msk (1UL << ACTLR_DDI_Pos) /*!< ACTLR: DDI Mask */
#define ACTLR_DDVM_Pos 15U /*!< ACTLR: DDVM Position */
#define ACTLR_DDVM_Msk (1UL << ACTLR_DDVM_Pos) /*!< ACTLR: DDVM Mask */
#define ACTLR_L1PCTL_Pos 13U /*!< ACTLR: L1PCTL Position */
#define ACTLR_L1PCTL_Msk (3UL << ACTLR_L1PCTL_Pos) /*!< ACTLR: L1PCTL Mask */
#define ACTLR_L1RADIS_Pos 12U /*!< ACTLR: L1RADIS Position */
#define ACTLR_L1RADIS_Msk (1UL << ACTLR_L1RADIS_Pos) /*!< ACTLR: L1RADIS Mask */
#define ACTLR_L2RADIS_Pos 11U /*!< ACTLR: L2RADIS Position */
#define ACTLR_L2RADIS_Msk (1UL << ACTLR_L2RADIS_Pos) /*!< ACTLR: L2RADIS Mask */
#define ACTLR_DODMBS_Pos 10U /*!< ACTLR: DODMBS Position */
#define ACTLR_DODMBS_Msk (1UL << ACTLR_DODMBS_Pos) /*!< ACTLR: DODMBS Mask */
#define ACTLR_SMP_Pos 6U /*!< ACTLR: SMP Position */
#define ACTLR_SMP_Msk (1UL << ACTLR_SMP_Pos) /*!< ACTLR: SMP Mask */
/* CP15的CPACR寄存器
* 参考资料:Cortex-A7 Technical ReferenceManua.pdf P115
*/
typedef union
{
struct
{
uint32_t _reserved0:20; /*!< bit: 0..19 Reserved */
uint32_t cp10:2; /*!< bit:20..21 Access rights for coprocessor 10 */
uint32_t cp11:2; /*!< bit:22..23 Access rights for coprocessor 11 */
uint32_t _reserved1:6; /*!< bit:24..29 Reserved */
uint32_t D32DIS:1; /*!< bit: 30 Disable use of registers D16-D31 of the VFP register file */
uint32_t ASEDIS:1; /*!< bit: 31 Disable Advanced SIMD Functionality */
} b; /*!< Structure used for bit access */
uint32_t w; /*!< Type used for word access */
} CPACR_Type;
#define CPACR_ASEDIS_Pos 31U /*!< CPACR: ASEDIS Position */
#define CPACR_ASEDIS_Msk (1UL << CPACR_ASEDIS_Pos) /*!< CPACR: ASEDIS Mask */
#define CPACR_D32DIS_Pos 30U /*!< CPACR: D32DIS Position */
#define CPACR_D32DIS_Msk (1UL << CPACR_D32DIS_Pos) /*!< CPACR: D32DIS Mask */
#define CPACR_cp11_Pos 22U /*!< CPACR: cp11 Position */
#define CPACR_cp11_Msk (3UL << CPACR_cp11_Pos) /*!< CPACR: cp11 Mask */
#define CPACR_cp10_Pos 20U /*!< CPACR: cp10 Position */
#define CPACR_cp10_Msk (3UL << CPACR_cp10_Pos) /*!< CPACR: cp10 Mask */
/* CP15的DFSR寄存器
* 参考资料:Cortex-A7 Technical ReferenceManua.pdf P128
*/
typedef union
{
struct
{
uint32_t FS0:4; /*!< bit: 0.. 3 Fault Status bits bit 0-3 */
uint32_t Domain:4; /*!< bit: 4.. 7 Fault on which domain */
uint32_t _reserved0:2; /*!< bit: 8.. 9 Reserved */
uint32_t FS1:1; /*!< bit: 10 Fault Status bits bit 4 */
uint32_t WnR:1; /*!< bit: 11 Write not Read bit */
uint32_t ExT:1; /*!< bit: 12 External abort type */
uint32_t CM:1; /*!< bit: 13 Cache maintenance fault */
uint32_t _reserved1:18; /*!< bit:14..31 Reserved */
} b; /*!< Structure used for bit access */
uint32_t w; /*!< Type used for word access */
} DFSR_Type;
#define DFSR_CM_Pos 13U /*!< DFSR: CM Position */
#define DFSR_CM_Msk (1UL << DFSR_CM_Pos) /*!< DFSR: CM Mask */
#define DFSR_Ext_Pos 12U /*!< DFSR: Ext Position */
#define DFSR_Ext_Msk (1UL << DFSR_Ext_Pos) /*!< DFSR: Ext Mask */
#define DFSR_WnR_Pos 11U /*!< DFSR: WnR Position */
#define DFSR_WnR_Msk (1UL << DFSR_WnR_Pos) /*!< DFSR: WnR Mask */
#define DFSR_FS1_Pos 10U /*!< DFSR: FS1 Position */
#define DFSR_FS1_Msk (1UL << DFSR_FS1_Pos) /*!< DFSR: FS1 Mask */
#define DFSR_Domain_Pos 4U /*!< DFSR: Domain Position */
#define DFSR_Domain_Msk (0xFUL << DFSR_Domain_Pos) /*!< DFSR: Domain Mask */
#define DFSR_FS0_Pos 0U /*!< DFSR: FS0 Position */
#define DFSR_FS0_Msk (0xFUL << DFSR_FS0_Pos) /*!< DFSR: FS0 Mask */
/* CP15的IFSR寄存器
* 参考资料:Cortex-A7 Technical ReferenceManua.pdf P131
*/
typedef union
{
struct
{
uint32_t FS0:4; /*!< bit: 0.. 3 Fault Status bits bit 0-3 */
uint32_t _reserved0:6; /*!< bit: 4.. 9 Reserved */
uint32_t FS1:1; /*!< bit: 10 Fault Status bits bit 4 */
uint32_t _reserved1:1; /*!< bit: 11 Reserved */
uint32_t ExT:1; /*!< bit: 12 External abort type */
uint32_t _reserved2:19; /*!< bit:13..31 Reserved */
} b; /*!< Structure used for bit access */
uint32_t w; /*!< Type used for word access */
} IFSR_Type;
#define IFSR_ExT_Pos 12U /*!< IFSR: ExT Position */
#define IFSR_ExT_Msk (1UL << IFSR_ExT_Pos) /*!< IFSR: ExT Mask */
#define IFSR_FS1_Pos 10U /*!< IFSR: FS1 Position */
#define IFSR_FS1_Msk (1UL << IFSR_FS1_Pos) /*!< IFSR: FS1 Mask */
#define IFSR_FS0_Pos 0U /*!< IFSR: FS0 Position */
#define IFSR_FS0_Msk (0xFUL << IFSR_FS0_Pos) /*!< IFSR: FS0 Mask */
/* CP15的ISR寄存器
* 参考资料:ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition.pdf P1640
*/
typedef union
{
struct
{
uint32_t _reserved0:6; /*!< bit: 0.. 5 Reserved */
uint32_t F:1; /*!< bit: 6 FIQ pending bit */
uint32_t I:1; /*!< bit: 7 IRQ pending bit */
uint32_t A:1; /*!< bit: 8 External abort pending bit */
uint32_t _reserved1:23; /*!< bit:14..31 Reserved */
} b; /*!< Structure used for bit access */
uint32_t w; /*!< Type used for word access */
} ISR_Type;
#define ISR_A_Pos 13U /*!< ISR: A Position */
#define ISR_A_Msk (1UL << ISR_A_Pos) /*!< ISR: A Mask */
#define ISR_I_Pos 12U /*!< ISR: I Position */
#define ISR_I_Msk (1UL << ISR_I_Pos) /*!< ISR: I Mask */
#define ISR_F_Pos 11U /*!< ISR: F Position */
#define ISR_F_Msk (1UL << ISR_F_Pos) /*!< ISR: F Mask */
/* Mask and shift a bit field value for use in a register bit range. */
#define _VAL2FLD(field, value) ((value << field ## _Pos) & field ## _Msk)
/* Mask and shift a register value to extract a bit filed value. */
#define _FLD2VAL(field, value) ((value & field ## _Msk) >> field ## _Pos)
/*******************************************************************************
* CP15 访问函数
******************************************************************************/
FORCEDINLINE __STATIC_INLINE uint32_t __get_SCTLR(void)
{
return __MRC(15, 0, 1, 0, 0);
}
FORCEDINLINE __STATIC_INLINE void __set_SCTLR(uint32_t sctlr)
{
__MCR(15, 0, sctlr, 1, 0, 0);
}
FORCEDINLINE __STATIC_INLINE uint32_t __get_ACTLR(void)
{
return __MRC(15, 0, 1, 0, 1);
}
FORCEDINLINE __STATIC_INLINE void __set_ACTLR(uint32_t actlr)
{
__MCR(15, 0, actlr, 1, 0, 1);
}
FORCEDINLINE __STATIC_INLINE uint32_t __get_CPACR(void)
{
return __MRC(15, 0, 1, 0, 2);
}
FORCEDINLINE __STATIC_INLINE void __set_CPACR(uint32_t cpacr)
{
__MCR(15, 0, cpacr, 1, 0, 2);
}
FORCEDINLINE __STATIC_INLINE uint32_t __get_TTBR0(void)
{
return __MRC(15, 0, 2, 0, 0);
}
FORCEDINLINE __STATIC_INLINE void __set_TTBR0(uint32_t ttbr0)
{
__MCR(15, 0, ttbr0, 2, 0, 0);
}
FORCEDINLINE __STATIC_INLINE uint32_t __get_TTBR1(void)
{
return __MRC(15, 0, 2, 0, 1);
}
FORCEDINLINE __STATIC_INLINE void __set_TTBR1(uint32_t ttbr1)
{
__MCR(15, 0, ttbr1, 2, 0, 1);
}
FORCEDINLINE __STATIC_INLINE uint32_t __get_TTBCR(void)
{
return __MRC(15, 0, 2, 0, 2);
}
FORCEDINLINE __STATIC_INLINE void __set_TTBCR(uint32_t ttbcr)
{
__MCR(15, 0, ttbcr, 2, 0, 2);
}
FORCEDINLINE __STATIC_INLINE uint32_t __get_DACR(void)
{
return __MRC(15, 0, 3, 0, 0);
}
FORCEDINLINE __STATIC_INLINE void __set_DACR(uint32_t dacr)
{
__MCR(15, 0, dacr, 3, 0, 0);
}
FORCEDINLINE __STATIC_INLINE uint32_t __get_DFSR(void)
{
return __MRC(15, 0, 5, 0, 0);
}
FORCEDINLINE __STATIC_INLINE void __set_DFSR(uint32_t dfsr)
{
__MCR(15, 0, dfsr, 5, 0, 0);
}
FORCEDINLINE __STATIC_INLINE uint32_t __get_IFSR(void)
{
return __MRC(15, 0, 5, 0, 1);
}
FORCEDINLINE __STATIC_INLINE void __set_IFSR(uint32_t ifsr)
{
__MCR(15, 0, ifsr, 5, 0, 1);
}
FORCEDINLINE __STATIC_INLINE uint32_t __get_DFAR(void)
{
return __MRC(15, 0, 6, 0, 0);
}
FORCEDINLINE __STATIC_INLINE void __set_DFAR(uint32_t dfar)
{
__MCR(15, 0, dfar, 6, 0, 0);
}
FORCEDINLINE __STATIC_INLINE uint32_t __get_IFAR(void)
{
return __MRC(15, 0, 6, 0, 2);
}
FORCEDINLINE __STATIC_INLINE void __set_IFAR(uint32_t ifar)
{
__MCR(15, 0, ifar, 6, 0, 2);
}
FORCEDINLINE __STATIC_INLINE uint32_t __get_VBAR(void)
{
return __MRC(15, 0, 12, 0, 0);
}
FORCEDINLINE __STATIC_INLINE void __set_VBAR(uint32_t vbar)
{
__MCR(15, 0, vbar, 12, 0, 0);
}
FORCEDINLINE __STATIC_INLINE uint32_t __get_ISR(void)
{
return __MRC(15, 0, 12, 1, 0);
}
FORCEDINLINE __STATIC_INLINE void __set_ISR(uint32_t isr)
{
__MCR(15, 0, isr, 12, 1, 0);
}
FORCEDINLINE __STATIC_INLINE uint32_t __get_CONTEXTIDR(void)
{
return __MRC(15, 0, 13, 0, 1);
}
FORCEDINLINE __STATIC_INLINE void __set_CONTEXTIDR(uint32_t contextidr)
{
__MCR(15, 0, contextidr, 13, 0, 1);
}
FORCEDINLINE __STATIC_INLINE uint32_t __get_CBAR(void)
{
return __MRC(15, 4, 15, 0, 0);
}
/*******************************************************************************
* GIC相关内容
*有关GIC的内容,参考:ARM Generic Interrupt Controller(ARM GIC控制器)V2.0.pdf
******************************************************************************/
/*
* GIC寄存器描述结构体,
* GIC分为分发器端和CPU接口端
*/
typedef struct
{
uint32_t RESERVED0[1024];
__IOM uint32_t D_CTLR; /*!< Offset: 0x1000 (R/W) Distributor Control Register */
__IM uint32_t D_TYPER; /*!< Offset: 0x1004 (R/ ) Interrupt Controller Type Register */
__IM uint32_t D_IIDR; /*!< Offset: 0x1008 (R/ ) Distributor Implementer Identification Register */
uint32_t RESERVED1[29];
__IOM uint32_t D_IGROUPR[16]; /*!< Offset: 0x1080 - 0x0BC (R/W) Interrupt Group Registers */
uint32_t RESERVED2[16];
__IOM uint32_t D_ISENABLER[16]; /*!< Offset: 0x1100 - 0x13C (R/W) Interrupt Set-Enable Registers */
uint32_t RESERVED3[16];
__IOM uint32_t D_ICENABLER[16]; /*!< Offset: 0x1180 - 0x1BC (R/W) Interrupt Clear-Enable Registers */
uint32_t RESERVED4[16];
__IOM uint32_t D_ISPENDR[16]; /*!< Offset: 0x1200 - 0x23C (R/W) Interrupt Set-Pending Registers */
uint32_t RESERVED5[16];
__IOM uint32_t D_ICPENDR[16]; /*!< Offset: 0x1280 - 0x2BC (R/W) Interrupt Clear-Pending Registers */
uint32_t RESERVED6[16];
__IOM uint32_t D_ISACTIVER[16]; /*!< Offset: 0x1300 - 0x33C (R/W) Interrupt Set-Active Registers */
uint32_t RESERVED7[16];
__IOM uint32_t D_ICACTIVER[16]; /*!< Offset: 0x1380 - 0x3BC (R/W) Interrupt Clear-Active Registers */
uint32_t RESERVED8[16];
__IOM uint8_t D_IPRIORITYR[512]; /*!< Offset: 0x1400 - 0x5FC (R/W) Interrupt Priority Registers */
uint32_t RESERVED9[128];
__IOM uint8_t D_ITARGETSR[512]; /*!< Offset: 0x1800 - 0x9FC (R/W) Interrupt Targets Registers */
uint32_t RESERVED10[128];
__IOM uint32_t D_ICFGR[32]; /*!< Offset: 0x1C00 - 0xC7C (R/W) Interrupt configuration registers */
uint32_t RESERVED11[32];
__IM uint32_t D_PPISR; /*!< Offset: 0x1D00 (R/ ) Private Peripheral Interrupt Status Register */
__IM uint32_t D_SPISR[15]; /*!< Offset: 0x1D04 - 0xD3C (R/ ) Shared Peripheral Interrupt Status Registers */
uint32_t RESERVED12[112];
__OM uint32_t D_SGIR; /*!< Offset: 0x1F00 ( /W) Software Generated Interrupt Register */
uint32_t RESERVED13[3];
__IOM uint8_t D_CPENDSGIR[16]; /*!< Offset: 0x1F10 - 0xF1C (R/W) SGI Clear-Pending Registers */
__IOM uint8_t D_SPENDSGIR[16]; /*!< Offset: 0x1F20 - 0xF2C (R/W) SGI Set-Pending Registers */
uint32_t RESERVED14[40];
__IM uint32_t D_PIDR4; /*!< Offset: 0x1FD0 (R/ ) Peripheral ID4 Register */
__IM uint32_t D_PIDR5; /*!< Offset: 0x1FD4 (R/ ) Peripheral ID5 Register */
__IM uint32_t D_PIDR6; /*!< Offset: 0x1FD8 (R/ ) Peripheral ID6 Register */
__IM uint32_t D_PIDR7; /*!< Offset: 0x1FDC (R/ ) Peripheral ID7 Register */
__IM uint32_t D_PIDR0; /*!< Offset: 0x1FE0 (R/ ) Peripheral ID0 Register */
__IM uint32_t D_PIDR1; /*!< Offset: 0x1FE4 (R/ ) Peripheral ID1 Register */
__IM uint32_t D_PIDR2; /*!< Offset: 0x1FE8 (R/ ) Peripheral ID2 Register */
__IM uint32_t D_PIDR3; /*!< Offset: 0x1FEC (R/ ) Peripheral ID3 Register */
__IM uint32_t D_CIDR0; /*!< Offset: 0x1FF0 (R/ ) Component ID0 Register */
__IM uint32_t D_CIDR1; /*!< Offset: 0x1FF4 (R/ ) Component ID1 Register */
__IM uint32_t D_CIDR2; /*!< Offset: 0x1FF8 (R/ ) Component ID2 Register */
__IM uint32_t D_CIDR3; /*!< Offset: 0x1FFC (R/ ) Component ID3 Register */
__IOM uint32_t C_CTLR; /*!< Offset: 0x2000 (R/W) CPU Interface Control Register */
__IOM uint32_t C_PMR; /*!< Offset: 0x2004 (R/W) Interrupt Priority Mask Register */
__IOM uint32_t C_BPR; /*!< Offset: 0x2008 (R/W) Binary Point Register */
__IM uint32_t C_IAR; /*!< Offset: 0x200C (R/ ) Interrupt Acknowledge Register */
__OM uint32_t C_EOIR; /*!< Offset: 0x2010 ( /W) End Of Interrupt Register */
__IM uint32_t C_RPR; /*!< Offset: 0x2014 (R/ ) Running Priority Register */
__IM uint32_t C_HPPIR; /*!< Offset: 0x2018 (R/ ) Highest Priority Pending Interrupt Register */
__IOM uint32_t C_ABPR; /*!< Offset: 0x201C (R/W) Aliased Binary Point Register */
__IM uint32_t C_AIAR; /*!< Offset: 0x2020 (R/ ) Aliased Interrupt Acknowledge Register */
__OM uint32_t C_AEOIR; /*!< Offset: 0x2024 ( /W) Aliased End Of Interrupt Register */
__IM uint32_t C_AHPPIR; /*!< Offset: 0x2028 (R/ ) Aliased Highest Priority Pending Interrupt Register */
uint32_t RESERVED15[41];
__IOM uint32_t C_APR0; /*!< Offset: 0x20D0 (R/W) Active Priority Register */
uint32_t RESERVED16[3];
__IOM uint32_t C_NSAPR0; /*!< Offset: 0x20E0 (R/W) Non-secure Active Priority Register */
uint32_t RESERVED17[6];
__IM uint32_t C_IIDR; /*!< Offset: 0x20FC (R/ ) CPU Interface Identification Register */
uint32_t RESERVED18[960];
__OM uint32_t C_DIR; /*!< Offset: 0x3000 ( /W) Deactivate Interrupt Register */
} GIC_Type;
/*
* GIC初始化
* 为了简单使用GIC的group0
*/
FORCEDINLINE __STATIC_INLINE void GIC_Init(void)
{
uint32_t i;
uint32_t irqRegs;
GIC_Type *gic = (GIC_Type *)(__get_CBAR() & 0xFFFF0000UL);
irqRegs = (gic->D_TYPER & 0x1FUL) + 1;
/* On POR, all SPI is in group 0, level-sensitive and using 1-N model */
/* Disable all PPI, SGI and SPI */
for (i = 0; i < irqRegs; i++)
gic->D_ICENABLER[i] = 0xFFFFFFFFUL;
/* Make all interrupts have higher priority */
gic->C_PMR = (0xFFUL << (8 - __GIC_PRIO_BITS)) & 0xFFUL;
/* No subpriority, all priority level allows preemption */
gic->C_BPR = 7 - __GIC_PRIO_BITS;
/* Enable group0 distribution */
gic->D_CTLR = 1UL;
/* Enable group0 signaling */
gic->C_CTLR = 1UL;
}
/*
* 使能指定的中断
*/
FORCEDINLINE __STATIC_INLINE void GIC_EnableIRQ(IRQn_Type IRQn)
{
GIC_Type *gic = (GIC_Type *)(__get_CBAR() & 0xFFFF0000UL);
gic->D_ISENABLER[((uint32_t)(int32_t)IRQn) >> 5] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL));
}
/*
* 关闭指定的中断
*/
FORCEDINLINE __STATIC_INLINE void GIC_DisableIRQ(IRQn_Type IRQn)
{
GIC_Type *gic = (GIC_Type *)(__get_CBAR() & 0xFFFF0000UL);
gic->D_ICENABLER[((uint32_t)(int32_t)IRQn) >> 5] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL));
}
/*
* 返回中断号
*/
FORCEDINLINE __STATIC_INLINE uint32_t GIC_AcknowledgeIRQ(void)
{
GIC_Type *gic = (GIC_Type *)(__get_CBAR() & 0xFFFF0000UL);
return gic->C_IAR & 0x1FFFUL;
}
/*
* 向EOIR写入发送中断的中断号来释放中断
*/
FORCEDINLINE __STATIC_INLINE void GIC_DeactivateIRQ(uint32_t value)
{
GIC_Type *gic = (GIC_Type *)(__get_CBAR() & 0xFFFF0000UL);
gic->C_EOIR = value;
}
/*
* 获取运行优先级
*/
FORCEDINLINE __STATIC_INLINE uint32_t GIC_GetRunningPriority(void)
{
GIC_Type *gic = (GIC_Type *)(__get_CBAR() & 0xFFFF0000UL);
return gic->C_RPR & 0xFFUL;
}
/*
* 设置组优先级
*/
FORCEDINLINE __STATIC_INLINE void GIC_SetPriorityGrouping(uint32_t PriorityGroup)
{
GIC_Type *gic = (GIC_Type *)(__get_CBAR() & 0xFFFF0000UL);
gic->C_BPR = PriorityGroup & 0x7UL;
}
/*
* 获取组优先级
*/
FORCEDINLINE __STATIC_INLINE uint32_t GIC_GetPriorityGrouping(void)
{
GIC_Type *gic = (GIC_Type *)(__get_CBAR() & 0xFFFF0000UL);
return gic->C_BPR & 0x7UL;
}
/*
* 设置优先级
*/
FORCEDINLINE __STATIC_INLINE void GIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
{
GIC_Type *gic = (GIC_Type *)(__get_CBAR() & 0xFFFF0000UL);
gic->D_IPRIORITYR[((uint32_t)(int32_t)IRQn)] = (uint8_t)((priority << (8UL - __GIC_PRIO_BITS)) & (uint32_t)0xFFUL);
}
/*
* 获取优先级
*/
FORCEDINLINE __STATIC_INLINE uint32_t GIC_GetPriority(IRQn_Type IRQn)
{
GIC_Type *gic = (GIC_Type *)(__get_CBAR() & 0xFFFF0000UL);
return(((uint32_t)gic->D_IPRIORITYR[((uint32_t)(int32_t)IRQn)] >> (8UL - __GIC_PRIO_BITS)));
}
#endif
8.1 内联汇编原理
先看一个错误的示例:
void func(void)
{
...
asm("mov r1,r0");
__asm__("mov r2,r1");
...
}
这种操作可能产生负面的影响,因为 r0~r2 寄存器很可能正在被程序的其它部分使用而在这里被意外地修改。
这就需要使用到嵌入汇编的另一种表达形式:
asm(code : output operand list : input operand list : clobber list);
这种嵌入汇编的形式一共分为四个部分:
* code
* [attr]output operand list
* [attr]input operand list
* clobber list
code
:汇编的操作代码,一条或者多条指令,如果是多条指令,需要在指令间使用\n\t
隔开。
与通用的汇编代码有一些不同:因为支持 C 变量的操作,所以在操作由第二、三部分提供
的操作数时,使用 %n 来替代操作数。
output operand list
:表示输出的操作数,通常是一个或者多个 C 函数中的变量。
input operand list
:表示输入的操作数,通常是一个或者多个 C 函数中的变量,attr
部分表示操作数的属性,以字符串的形式提供,是必须的参数。
clobber list
:被破坏的列表,这部分我们放到后面讨论。
void func(void)
{
int val1 = 111,val2 = 222;
asm("mov %0,%1"
:"+r"(val1)
:"r"(val2)
:);
printf("val1 = %d\n",val1);
}
func
函数的输出结果为:val1 = 222
.
分析:
输出操作数为 val1,属性为 "=r"。
输入操作数为 val2,属性为 "r"
code 部分为 mov %1,%0, %0 表示输入输出列表中的第一个操作数,
%1 表示操作数列表中提供的第二个操作数,以此类推,这条汇编指
令很明显就是将第二个操作数(val2)赋值给第一个操作数(val1),所以最后的结果为 val1 = 222.
code 中的操作数命名顺序为输出操作书列表递增,输入操作数列表递增,比如增加一个操作数的代码为:
int val1 = 111,val2 = 222,val3=333;
asm("mov %0,%2" :"+r"(val1) :"r"(val2),"r"(val3) :);
8.1.1 操作数属性
在上述的示例中,输入操作数和输出操作数都会使用到 attr
字段,即操作数的属性,下面是其属性对应的列表:
- "=" 表示只写,通常用于所有输出操作数的属性
- "+" 表示读写,只能被列为输出操作数的属性,否则编译会报错。
8.1.2 clobber list
clobber 的意思为破坏,在这里的意思是:这段汇编指令将会破坏哪些寄存器的值。
在目前的 gcc 设计中,编译分为4个过程:预编译、编译、汇编、链接
。 其中,编译就是将 C 代码编译成汇编代码,而通用的汇编代码在这个过程是不会处理的,也就是说,嵌入汇编代码
的解析只涉及到输入输出操作数的替换,对于不包含输入输出操作数的部分不会解析,所以在编译阶段,编译器不会知道嵌入汇编代码中静态地使用到哪些寄存器,而是自顾自地编译 C 代码,从而导致 C 代码和嵌入汇编代码操作到同一个寄存器,而出现错误。例如:
void func(void) {
asm("mov lr,#1");
}
嵌入汇编代码过程
中,将lr
的值修改为 1,当前函数返回的时候也就返回到 1 地址,不出意料: 程序出现段错误:
Segmentation fault (core dumped)
解决办法是:将程序修改一下,用clobber list
这种标准格式:
void func(void) {
asm("mov lr,#1":::"lr");
}
为了进一步追求真相,我们来对比它们的反汇编代码:
不添加 clobber list 的反汇编代码:
000083f4 <func3>:
83f4: b480 push {r7}
83f6: af00 add r7, sp, #0
83f8: f04f 0e01 mov.w lr, #1
83fc: 46bd mov sp, r7
83fe: f85d 7b04 ldr.w r7, [sp], #4
8402: 4770 bx lr
添加 clobber list 的反汇编代码:
void func3(void)
{
83f4: b580 push {r7, lr}
83f6: af00 add r7, sp, #0
83f8: f04f 0e01 mov.w lr, #1
}
83fc: bd80 pop {r7, pc}
83fe: bf00 nop
对比可以看出,对于不将"lr"
添加到 clobber list
中的代码,返回指令为 bx lr
,因为lr
被修改为 1,所以返回一定出错.
第二个代码则不一样,在函数调用之初,就将lr
寄存器使用 push 指令保存到了栈上,在最后返回的时候再将栈上的原 lr
数据 pop 到 pc 指针中,其中lr
的修改没有任何影响。
通常情况下,clobber list
对应着寄存器的修改,所以我们只需要将寄存器的名字作为参数添加到clobber list
中,比如"r0" "lr"
等,有两个特殊的参数需要关注,就是 "cc" 和 "memory"
。
"cc"
对应的并非是普通寄存器,而是 CPU 的状态寄存器
,如果某些指令将状态寄存器修改了,需要在clobber list
中添加"cc"
来声明这个事情。
"memory"
对应内存操作,这从名称也可以看出,当clobber list
中包含"memory"
时,表示嵌入汇编代码会对内存进行一些操作,gcc 生成的代码会将特定的寄存器的值写回到内存中以保证内存中的值是最新的,这样做的原因是 gcc 经常会将内存数据缓存在寄存器中,如果不及时写回,嵌入汇编代码读到的内存就是原来的值。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了