标签 嵌入式 下的文章
ARMv7-A 那些事 - 7.栈回溯浅析在嵌入式开发过程中,经常需要对代码进行调试来解决各种各样的问题,常用的调试手段有:(1)、开发环境搭配硬件仿真器进行在线调试。优点:调试过程中能够清楚的知道各个寄存器的值以及各个变量的值,程序的执行流程也能够一目了然。缺点:板卡需要引出硬件仿真器的连接口,并且需要购买硬件仿真器。(2)、通过调试串口打印信息梳理程序的执行流程,结合代码分析问题产生的原因。优点:足够简单,通过增加较多的打印信息来分析问题出现的位置,再结合代码分析问题产生的原因。缺点:没法准确的定位问题产生的位置和原因。(3)、在应用或者操作系统死机的时候,根据操作系统输出的异常栈信息进行分析,再结合镜像或者应用的反汇编代码进行定位。通常这种方法和方法(2)结合使用。本文主要简单的讲讲栈回溯,对于以后去理解操作系统的异常栈处理打个基础吧。ARM处理器的栈回溯主要有两种方式:一种是基于栈帧寄存器(FP)的栈回溯,另一种是unwind形式的栈回溯。本文主要讲讲基于栈帧寄存器(FP)的栈回溯。栈回溯相关寄存器在栈回溯过程中,主要涉及如下寄存器:R15:又叫程序计数器(Program Counter)PC,PC主要用于存放CPU取指的地址。R14:又叫链接寄存器(Link register)LR,LR主要用于存放函数的返回地址,即当函数返回时,知道自己该回到哪儿去继续运行。R13:又叫堆栈指针寄存器(Stack pointer)SP,SP通常用于保存堆栈地址,在使用入栈和出栈指令时,SP中的堆栈地址会自动的更新。R12:又叫内部过程调用暂存寄存器(Intra-Procedure-call scratch register)IP,主要用于暂存SP。R11:又叫帧指针寄存器(Frame pointer)FP,通常指向一个函数的栈帧底部,表示一个函数栈的开始位置。ARM栈帧结构依据AAPCS (ARM Archtecture Procedure Call Standard)规范,当调用子函数时,子函数一开始的代码总是会执行压栈操作来保留父函数的相关信息,压栈步骤示例如下所示:每个函数都有自己的栈空间,这一部分称为栈帧。栈帧在函数被调用的时候创建,在函数返回后销毁。每个函数的栈帧是由SP寄存器和FP寄存器来界定的,ARM栈帧结构典型示意图如下所示:上图描述的栈帧,main函数和func1函数的示意代码如下:每个函数的栈帧中都会保存调用该函数之前的PC、LR、SP、FP寄存器的值;如果函数具有参数并且函数内部使用了局部变量,那么函数栈帧中也会保存函数的参数和局部变量;如果被调用的子函数参数过多,那么多余的参数会通过父函数的栈进行传递。比如func1函数的参数p5通过main函数的栈帧进行传递的。(注:编译器的版本不同,函数栈帧中参数和局部变量的压栈顺序可能不同,PC,LR,SP和FP这4个寄存器的压栈顺序一般是固定的)函数栈帧中的PC和LR均指向代码段,PC表示执行入栈指令时CPU正在取指的地址,LR表示当前函数返回后继续执行的地址。栈回溯原理在栈回溯的过程中,我们主要利用FP寄存器进行栈回溯。通过FP就可以知道当前函数的栈底,从而可以找到存储在栈帧中的LR寄存器的数据,这个数据就是函数的返回地址。同时也可以找到保存在函数栈帧中的上一级函数FP的数据,这个数据指向了上一级函数的栈底,按照同样的方法可以找出上一级函数栈帧中存储的LR和FP数据,就知道哪个函数调用了上一级函数以及这个函数的栈底地址。这就是栈回溯的流程,整个流程以FP为核心,依次找出每个函数栈帧中存储的LR和FP数据,计算出函数返回地址和上一级函数栈底地址,从而找出每一级函数调用关系。栈回溯编译选项当gcc的编译选项带有-mapcs-frame时,编译出来的代码能够将PC,LR,SP和FP寄存器的值压入函数的栈帧中。默认情况下gcc的编译选项为-mno-apcs-frame,此时编译出来的代码不一定会将PC,LR,SP和FP这四个寄存器的值压入函数的栈帧中,可能只会将LR和FP寄存器的值压入函数的栈帧中。关于-mapcs-frame选项,gcc的手册描述如下:我这里使用的gcc信息如下:虽然gcc手册上说-mapcs-frame选项被废弃了,但是只有添加了该选项,编译出来的代码才会将PC,LR,SP和FP寄存器的值压入函数的栈帧中。我这里编译代码仍然使用-mapcs-frame选项,有知道该选项对应的新的栈帧配置选项的兄弟可以告知我一下。栈回溯示例根据前面的内容,这里简单的写了一个栈回溯的示例,函数调用流程为:main -> test_a -> test_b -> test_c。函数的源代码如下:上述函数的反汇编内容如下:当程序运行到test_c()函数的return c;代码处时,FP的值为0x9FDFFF94,此时内存数据如下:test_c()函数的栈底为0x9FDFFF94,可以得到test_c()函数栈帧中LR为0x800021C8、FP为0x9FDFFFB4,LR是test_c()函数执行完成后的返回地址,与反汇编代码中test_b()函数调用完test_c()之后的下一个执行地址一致:FP为0x9FDFFFB4表示test_b()函数的栈底为0x9FDFFFB4,有了test_b()函数的栈底就可以得到test_b()函数栈帧中LR为0x80002194、FP为0x9FDFFFDC,从而知道test_b()函数执行完成后的返回地址以及test_a()函数的栈底,依次逐级回溯,就可以知道程序的整个运行流程了。在栈回溯的过程中我们可以利用addr2line工具辅助我们对程序执行流程的分析。
ARMv7-A 那些事 - 6.常用汇编指令对于搞嵌入式驱动或者操作系统的人来说,掌握汇编语言的使用还是比较重要的,毕竟有时候在分析定位问题的时候,多多少少都会有汇编的身影。本文主要讲讲ARM指令集格式以及常用的ARM汇编指令(主要包括LDR和STR指令,LDM和STM指令,push和pop指令,MOV指令,CPS指令,MRS和MSR指令,MRC和MCR指令,其余指令暂时没列出来,用到时可以查看ARM手册进行了解)。ARM指令集格式ARMv7架构是一个32位的处理器架构。同时ARM架构是一个加载/存储体系结构,所有的数据处理操作需要在通用寄存器中完成。要学习了解处理器的汇编指令,那么首先可以看看汇编指令的通用表达式,具体的指令也就是使用具体的指令和参数代替通用表达式的参数。ARM指令集的指令表达如下所示:opcode{<cond>}{S} <Rd>, <Rn> {, <Rm>}opcode:指令助记符,比如LDR,STR,MOV等。{}:大括号括起来的内容表示可选。<>:<>括起来的内容是必须的。cond:条件码,比如EQ,NE,CS等,条件码的内容如下图所示:S:可选的后缀,如果指令中添加了S,那么指令的执行结果将会影响到CPSR寄存器的标志位域。Rd:目标寄存器。Rn:第一个操作数寄存器。Rm:第二个操作数寄存器。在了解了ARM指令的表达式之后,下面就讲讲常用的汇编指令。LDR和STR指令LDR指令用于从内存中读取数据存储到通用寄存器中。STR指令用于将通用寄存器中的值存储到内存中。LDR指令的语法如下所示:STR指令的语法如下所示:type:操作的数据宽度,可以是:B(unsigned byte),SB(signed byte),H(unsigned halfword),SH(signed halfword)。cond:条件码。Rt:目标寄存器。Rn:存储内存操作基地址的寄存器。Rm:存储偏移量的寄存器。offset:立即数。!:如果存在,表示最终的地址要写回Rn。T:表示处理器是在用户模式下访问内存地址。加载存储指令有4种寻址方式,LDR的操作描述如下(STR指令的操作类似):寄存器寻址:要寻址的地址存放在寄存器中。前变基寻址:在内存访问之前,将寄存器中的内存地址加上偏移量之后作为新的内存地址进行内存访问。指令形式为:LDR Rt, [Rn, Op2]。偏移量Op2可以是正数或者是负数,可以是一个立即数,可以是另一个寄存器的值,可以是另一个寄存器中的数据进行移位之后的值。带写回的前变基寻址:指令形式为:LDR Rt, [Rn, Op2]!。该寻址模式和前变基寻址一样,只是在访问完内存之后Rn寄存器中的值就更新为运算之后得到的新内存地址的值。带写回的后变基寻址:指令形式为:LDR Rt, [Rn], #offset和LDR Rt, [Rn], +/-Rm。将寄存器Rn中存储的数值作为内存地址,将该内存地址中的数据读出来存储到Rt寄存器中,然后将内存地址加减立即数offset或者Rm寄存器中的数值得到新的内存地址存储到Rn寄存器中。上面说的这些有可能不太好懂,下面简单的列举几个例子吧:STR指令的操作和LDR指令类似,这里就不列举了。LDR伪指令LDR相关的伪指令语法如下所示:下面是LDR伪指令简单的使用:LDM和STM指令LDM指令用于加载指定地址上的数据保存到一个或者多个寄存器中。STM指令用于将一个或者多个寄存器中的数据存储到指定地址上。LDM和STM指令主要用于现场保护和数据复制。LDM指令的语法如下所示:LDM{addr_mode}{cond} Rn{!},reglist{^}STM指令的语法如下所示:STM{addr_mode}{cond} Rn{!},reglist{^}addr_mode:地址模式,用于数据块传输的地址模式,如下所示:也可以使用相应的面向堆栈的寻址模式,如下所示:cond:条件码。Rn:Rn存储了用于传输的初始地址。!:如果存在,表示最终的地址要写回Rn。reglist:用{}括起来的一个寄存器或者多个寄存器组成的列表。它可以是一个寄存器范围。如果{}中的寄存器超过一个,那么寄存器或者寄存器范围之间通过逗号(,)分隔。^:如果在除了USR模式和SYS模式下存在该符号,意味着将发生下述的两个动作:当寄存器列表中不包含PC时,加载/存储的是USR模式的寄存器,而不是当前模式的寄存器。在使用LDM指令时,如果寄存器列表中包含PC时,那么除了正常的多寄存器传送外,会将SPSR 拷贝到CPSR 中,这可用于异常处理返回。上面的内容可能不是很好理解,下面简单的列举写例子:LDMIA和STMIA例子LDMIA例子如下所示:STMIA例子如下所示:LDMIB和STMIB例子LDMIB例子如下所示:STMIB例子如下所示:LDMDA和STMDA例子LDMDA例子如下所示:STMDA例子如下所示:LDMDB和STMDB例子LDMDB例子如下所示:STMDB例子如下所示:现场保护在数据块的传输中:STMDB和LDMIA对应使用,STMIA和LDMDB对应使用。在堆栈操作中:STMFD和LDMFD对应使用,STMFA和LDMFA对应使用。在子程序或者异常处理时,使用LDMFD和STMFD进行现场保护的例子如下:同样的可以使用STMDB和LDMIA指令进行现场保护,因此上述代码可以修改成下述形式:push和pop指令push和pop指令主要用于子程序或者异常的现场保护。push指令用于将寄存器内容压入堆栈。pop指令用于将堆栈中的内容恢复到寄存器中。push指令的语法如下所示:PUSH{cond} reglistpop指令的语法如下所示:POP{cond} reglistcond:条件码。reglist:用{}括起来的一个寄存器或者多个寄存器组成的列表。它可以是一个寄存器范围。如果{}中的寄存器超过一个,那么寄存器或者寄存器范围之间通过逗号(,)分隔。push指令等价于STMDB指令。pop指令等价于LDMIA指令。使用push指令和pop指令保护现场的例子如下所示:MOV指令MOV指令主要用于将数据搬移到寄存器中。MOV指令的语法如下所示:S:可选的后缀,如果指令中添加了S,那么指令的执行结果将会影响到CPSR寄存器的标志位域。cond:条件码。Rn:目标寄存器。Rm:源寄存器。imm:立即数。MOV指令的使用例子如下:CPS指令可以通过CPS(Change Processor State)指令来修改处理器模式。CPS指令也可以用来使能或者禁止异常。CPS指令的语法如下所示:mode是处理器的模式编码,比如在从其他模式下切换到SYS模式,使用下述代码即可:IE使能中断或者终止。ID禁止中断或者终止。iflags由下面的一种或者几种组成:a:表示异步终止(asynchronous abort);i:表示中断(IRQ);f:表示快中断(FIQ);下述代码是CPS指令的一些简单用法:MRS与MSR指令MRS和MSR指令可用于读写程序状态寄存器CPSR,APSR和SPSR。在ARM处理器中,只有MRS指令可以从程序状态寄存器CPSR,APSR和SPSR中读出数据到通用寄存器中。MRS指令操作程序状态寄存器的语法如下:MRS{cond} Rd, psrcond为条件码。Rd为目标寄存器,Rd不允许为R15。psr为程序状态寄存器CPSR,APSR或者SPSR。MRS指令的示例代码如下所示:MSR指令可以用来写程序状态寄存器CPSR,APSR和SPSR的全部或者部分域。MSR指令操作程序状态寄存器的语法如下:cond为条件码。psr为程序状态寄存器CPSR或者SPSR。constant是一个8位立即数。ARM文档对于constant的介绍如下:constant is an 8-bit pattern rotated by an even number of bits within a 32-bit word. (Not available in Thumb.)Rm是源寄存器。fields由下面的一个或者多个组合而成:c:xPSR[7:0],控制位域;x:xPSR[15:8],扩展位域;s:xPSR[23:16],状态位域;f:xPSR[31:24],标志位域;MSR指令的示例代码如下所示:只有在除用户模式外的其他模式下才能够修改状态寄存器。MRC和MCR指令ARMv7-A体系结构的处理器提供了MRC和MCR指令用于对协处理器进行读写操作。MRC指令用于将协处理器中的寄存器数据读取到ARM通用寄存器中。MCR指令用于将ARM通用寄存器中的数据写入到协处理器的寄存器中。MRCMRC指令的语法如下所示:MRC{cond} coproc, opc1, Rt, CRn, CRm{, opc2}cond为条件码。coproc为协处理器名称,CP0~CP15协处理器分别对应名称p0~p15。opc1为协处理器要执行的操作码,取指范围为0~7。Rt为ARM通用寄存器,用于存储读取到的协处理器寄存器数据。CRn为协处理器寄存器,对于CP15协处理器来说,CRn取值范围为c0~c15。CRm为协处理器寄存器,对于CP15协处理器来说,通过CRm和opc2一起来确定CRn对应的具体寄存器。opc2为可选的协处理器执行操作码,取指范围为0~7,当不需要的时候要设置为0。MRC指令使用示例如下:MCRMCR指令的语法如下所示:MCR{cond} coproc, opc1, Rt, CRn, CRm{, opc2}cond为条件码。coproc为协处理器名称,CP0~CP15协处理器分别对应名称p0~p15。opc1为协处理器要执行的操作码,取指范围为0~7。Rt为ARM通用寄存器,用于存储要写入到协处理器寄存器中的数据。CRn为协处理器寄存器,对于CP15协处理器来说,CRn取值范围为c0~c15。CRm为协处理器寄存器,对于CP15协处理器来说,通过CRm和opc2一起来确定CRn对应的具体寄存器。opc2为可选的协处理器执行操作码,取指范围为0~7,当不需要的时候要设置为0。MCR指令使用示例如下:
ARMv7-A 那些事 - 5.CP15协处理器协处理器概述ARM架构通过支持协处理器来扩展处理器的功能。ARM架构的处理器支持最多16个协处理器,通常称为CP0~CP15。下述的协处理器被ARM用于特殊用途:CP15:提供系统控制功能,主要用于配置MMU、TLB和Cache等功能。CP14:主要用于控制系统Debug功能。CP10、CP11:两个协处理器一起提供了对浮点运算和向量操作的支持,这两个协处理器主要用于控制和配置浮点功能和高级SIMD指令扩展。其他协处理器被ARM保留用于将来使用。本文主要说说CP15协处理器。CP15协处理器总览CP15是系统控制协处理器,主要用于对ARM处理器核心支持的许多特性功能进行配置。CP15协处理器支持16个32位主寄存器(primary register),命名为c0~c15。c0~c15主寄存器各自又有多个32位的物理寄存器(physical register)。CP15协处理器的大多数寄存器不能在USR模式下访问,只能在除USR模式外的其他模式下访问。下面列出c0~c15中比较常用的寄存器:primary registerphysical register描述c0MIDR主ID寄存器,用于记录版本信息c0MPIDR多核处理器情况下,提供一种方法来唯一标识集群中的各个核心c1SCTLR系统控制寄存器c1ACTLR辅助控制寄存器c1CPACR协处理器访问控制寄存器,控制访问除了CP14和CP15的协处理器c1SCR安全配置寄存器,被TrustZone使用c2、c3TTBR0一级转换页表基址寄存器0c2、c3TTBR1一级转换页表基址寄存器1c2、c3TTBCR页表转换控制寄存器c5、c6DFSR数据异常(Data Fault)状态寄存器c5、c6IFSR指令异常(Instruction Fault)状态寄存器c5、c6DFAR数据异常(Data Fault)地址寄存器c5、c6IFAR指令异常(Instruction Fault)地址寄存器c7branch predictorcache和分支预测管理功能c7barrier数据和指令屏障操作c8TLBTLB操作c9performance monitors性能监视器c12VBAR提供非监视模式处理异常的异常基地址c12MVBAR提供监视模式处理异常的异常基地址c13CONTEXTIDR上下文ID寄存器c15CBAR配置基址寄存器,为GIC和本地时钟类型外设提供基地址在CP15协处理器中,c0~c15每个主处理器下面有多个物理寄存器,上述表格只列出了部分常用的物理寄存器。协处理器操作指令ARMv7-A体系结构的处理器提供了MRC和MCR指令用于对协处理器进行读写操作。MRC指令用于将CP15协处理器中的寄存器数据读取到ARM通用寄存器中。MCR指令用于将ARM通用寄存器中的数据写入到CP15协处理器的寄存器中。MRCMRC指令的语法如下所示:MRC{cond} coproc, opc1, Rt, CRn, CRm{, opc2}cond为条件码。coproc为协处理器名称,CP0~CP15协处理器分别对应名称p0~p15。opc1为协处理器要执行的操作码,取指范围为0~7。Rt为ARM通用寄存器,用于存储读取到的协处理器寄存器数据。CRn为协处理器寄存器,对于CP15协处理器来说,CRn取值范围为c0~c15。CRm为协处理器寄存器,对于CP15协处理器来说,通过CRm和opc2一起来确定CRn对应的具体寄存器。opc2为可选的协处理器执行操作码,取指范围为0~7,当不需要的时候要设置为0。MRC指令使用示例如下:MCRMCR指令的语法如下所示:MCR{cond} coproc, opc1, Rt, CRn, CRm{, opc2}cond为条件码。coproc为协处理器名称,CP0~CP15协处理器分别对应名称p0~p15。opc1为协处理器要执行的操作码,取指范围为0~7。Rt为ARM通用寄存器,用于存储要写入到协处理器寄存器中的数据。CRn为协处理器寄存器,对于CP15协处理器来说,CRn取值范围为c0~c15。CRm为协处理器寄存器,对于CP15协处理器来说,通过CRm和opc2一起来确定CRn对应的具体寄存器。opc2为可选的协处理器执行操作码,取指范围为0~7,当不需要的时候要设置为0。MCR指令使用示例如下:CP15协处理器主寄存器组成CP15协处理器有c0~c15总共16个主寄存器,在每个主寄存器下面,又有多个物理寄存器。下图总结了CP15协处理器的寄存器组织形式:上图对于MRC和MCR指令所要使用到的一些参数都标明了,对于想要访问CP15协处理器相关寄存器,只需要看图填写好参数就行了。下面具体列一下c0~c15各个主寄存器的组成。CP15协处理器c0寄存器组成主寄存器c0主要提供ID相关的功能,c0寄存器的组成如下图所示:CP15协处理器c1寄存器组成主寄存器c1主要提供系统控制相关的功能,c1寄存器的组成如下图所示:在CP15协处理器的寄存器中,系统控制寄存器SCTLR是被访问的比较多的寄存器。对SCTLR寄存器的访问需要在PL1或者更高的特权等级。SCTLR寄存器的位关系如下图所示:位标志说明30TEThumb异常使能,控制在异常发生时(包括reset),将会进入哪种指令集,0:ARM指令集,1:Thumb指令集27NMFI不可屏蔽的FIQ支持,0:软件可以通过写CPSR.F位来屏蔽FIQ,1:软件不可以通过写CPSR.F位来屏蔽FIQ25EE在进入异常处理时的大小端模式配置,0:小端,1:大端22U表明是否使用对齐模式21FIFIQ配置使能13V选择异常向量表基址,0:0x00000000,1:0xffff000012I指令cache使能11Z分支预测使能2C数据cache使能1A对齐检查使能0MMMU使能CP15协处理器c2 c3寄存器组成主寄存器c2和c3主要提供内存保护和内存控制相关的功能,c2和c3寄存器的组成如下图所示:CP15协处理器c4寄存器组成在任何基于ARMv7实现的处理器中,协处理器CP15的c4寄存器没有被使用。CP15协处理器c5 c6寄存器组成主寄存器c5和c6主要提供内存系统错误上报功能,c5和c6寄存器的组成如下图所示:CP15协处理器c7寄存器组成主寄存器c7主要提供cache维护,地址转换和内存屏障操作相关的功能,c7寄存器的组成如下图所示:CP15协处理器c8寄存器组成主寄存器c8主要提供TLB维护相关的功能,c8寄存器的组成如下图所示:CP15协处理器c9寄存器组成主寄存器c9保留用于分支预测,cache和TCM操作,c9寄存器的组成如下图所示:CP15协处理器c10寄存器组成主寄存器c10主要提供内存重映射和TLB控制相关的功能,c10寄存器的组成如下图所示:CP15协处理器c11寄存器组成主寄存器c11保留用于TCM DMA操作,c11寄存器的组成如下图所示:CP15协处理器c12寄存器组成主寄存器c12提供安全扩展功能,c12寄存器的组成如下图所示:CP15协处理器c13寄存器组成主寄存器c13提供进程ID、上下文ID和线程ID处理功能,c13寄存器的组成如下图所示:CP15协处理器c14寄存器组成主寄存器c14保留用于通用定时器功能,c14寄存器的组成如下图所示:CP15协处理器c15寄存器组成主寄存器c15由处理器实现决定。这里只是简单的将CP15各个主寄存器的组成列出来了,方便在使用MRC和MCR指令配置CP15主寄存器时,查看指令各个参数的设置,以及对照配置的具体寄存器,至于寄存器的具体内容由于篇幅原因就不列出来了,CP15寄存器的细节可以参考ARMv7AR手册的B3.17章节内容。
ARMv7-A 那些事 - 4.处理器模式与特权等级对于现代操作系统,通常情况下用户的应用程序运行在用户态,操作系统内核运行在内核态。用户态的应用对于系统硬件资源的访问是受限的,内核态则能够访问所有的系统硬件资源。操作系统的用户态和内核态是根据处理器的特权等级和运行模式进行硬件隔离的,这也极大的提高了操作系统的安全性。安全扩展和虚拟化扩展ARMv7-A体系结构支持安全扩展和虚拟化扩展。当处理器实现了安全扩展之后,处理器就存在普通世界(Normal world)和安全世界(Secure world)这两个世界,这在硬件层面上就可以将敏感数据和要求在安全环境运行的应用和普通应用完全隔离,如下图所示:当处理器实现了虚拟化扩展之后,处理器就新增了一个hypervisor mode (Hyp),并且也新增了一个特权级模式PL2。支持虚拟化扩展的处理器示意图如下所示:虚拟化扩展允许在Normal world运行多个操作系统,Hypervisor只能运行在Normal world。Secure world可以运行Trusted OS和Trusted services。特权等级处理器的模式,特权等级和安全状态的关系如下图所示:本文仅讨论非安全状态。在非安全状态下,存在3种特权等级(Privilege level):PL0、PL1和PL2,描述如下:PL0:用户模式(User mode)运行的应用程序处于PL0特权等级。运行在用户模式的程序被称为非特权程序。非特权程序对于系统资源的访问是受限的,对应Linux的用户态。PL1:除了用户模式和Hyp模式外,其他模式下的程序执行都处于PL1特权等级。PL1模式是指除了用户模式和Hyp模式之外的其他模式。操作系统运行在PL1特权级。PL2:如果实现了虚拟化扩展,Hyp模式运行的系统管理程序处于PL2特权等级。系统管理程序将控制并启用多个操作系统在同一个处理器系统上共存和执行。处理器模式ARMv7-A体系结构提供了9种处理器模式,如下图所示:从上图可以知道ARMv7-A提供的处理器模式有User、FIQ、IRQ、Supervisor(SVC)、Monitor(MON)、Abort(ABT)、Hyp、Undefined(UND)、System(SYS)模式,各个处理器模式的描述如下:User:用户模式,用户程序运行在User模式下,拥有受限的系统资源访问权限。FIQ:快中断异常处理模式,发生FIQ中断时的处理器模式,相对于中断而言,快中断拥有更高的响应等级和更低的延迟。IRQ:中断异常处理模式,发生IRQ中断时的处理器模式。Supervisor(SVC):管理员模式,操作系统内核通常运行在该模式下,在处理器复位或者应用程序调用svc指令的时候将会进入到该模式,系统调用就是通过svc指令完成的。Abort(ABT):异常终止模式,当发生Data Abort exception或者Prefetch Abort exception异常的时候进入这个模式。Undefined(UND):未定义指令模式,当执行未定义指令时进入这个模式。System(SYS):系统模式,系统模式和用户模式共享寄存器视图,并且目前大多数系统未使用该模式,利用这个特性我们可以在处理器启动时通过设置系统模式的SP寄存器来达到设置用户模式堆栈的目的,要设置用户模式的其他寄存器也可以这样操作。Monitor(MON):监视模式,实现了安全扩展的处理器才有该模式,在该模式下执行Secure和Non-secure处理器状态的切换。Hyp:实现了虚拟化扩展的处理器才有该模式。User模式处于PL0特权等级。FIQ、IRQ、Supervisor(SVC)、Monitor(MON)、Abort(ABT)、Undefined(UND)、System(SYS)这些模式处于PL1特权等级。Hyp模式处于PL2特权等级。本文不讨论支持安全扩展和虚拟化扩展的场景,因此对于Monitor模式和Hyp模式也不做深入探讨。通常情况下,应用程序运行在User模式(PL0),运行在User模式下的应用程序对硬件没有直接访问权,所有的硬件操作都需要通过系统调用向内核进行申请。操作系统内核运行在管理员模式(PL1),对系统调用、中断、异常等系统事件进行响应、处理并返回,以这种硬件隔离的方式保证了操作系统内核的安全。以Linux操作系统为例,虽然ARMv7-A体系结构的处理器有9种模式,但是操作系统只工作在SVC和USR模式,SVC处于内核态,USR处于用户态。至于其他的异常模式,Linux只是简单的略过。比如中断模式irq,Linux只有很短的汇编代码在irq模式运行,主要是保存上下文,然后就立马切换到了SVC模式,由内核进行统一处理。寄存器集ARMv7-A体系结构的处理器在不同处理器模式下,对于通用寄存器的使用情况也有所不同,如下图所示:上图中蓝色背景的寄存器属于bank寄存器,也就是相同的寄存器名对应不同的寄存器实体。从上图可以看出:R0~R7,PC在所有模式下是共享的。系统模式和用户模式共享寄存器视图,系统模式没有bank寄存器。FIQ 模式下,R8~R12、SP、LR 都是该模式专门的寄存器,FIQ比IRQ响应和处理速度更快,也得益于FIQ模式具有比IRQ模式更多的bank寄存器。FIQ、IRQ、ABT、SVC和UND模式,都有他们自己模式下专用的SP和LR,也就是说,在模式切换的时候,不需要针对这两个寄存器进行现场保护和恢复;FIQ、IRQ、ABT、SVC和UND模式,都有他们自己模式下专用的SPSR。在处理器发生中断或者异常时,处理器会自动的从一个模式A进入到另一个模式B,模式A的CPSR/APSR将会自动保存到模式B的SPSR中,这样模式B中的处理程序能够通过访问SPSR寄存器得到模式A下CPSR寄存器的信息。处理器模式切换ARMv7-A体系结构的处理器,处理器模式是由状态寄存器CPSR的M域(BIT[4:0])来控制的。对于用户模式而言,是没有权限操作CPSR寄存器的M域的,只能通过svc指令进入到SVC模式。对于SYS、FIQ、IRQ、ABT、SVC和UND模式而言,可以通过给CPSR寄存器的M域赋值来达到切换处理器模式的目的。各个模式的编码如下图所示:下述代码简单的演示了处理器模式切换:在上述代码中,使用了cps #mode指令来完成处理器模式的切换,在切换到相应模式之后,设置了对应模式的堆栈。在上述代码中,为什么不直接切换到USR模式,再设置USR模式的堆栈,而要借助SYS模式来设置USR模式的堆栈呢?这个问题相信大家在学习了本节内容之后,应该还是比较简单的。
ARMv7-A 那些事 - 3.程序状态寄存器程序状态寄存器的作用就是反映处理器的状态信息。在程序运行期间我们可以通过查看程序状态寄存器的状态位来进行程序的分支跳转处理,或者我们可以设置程序状态寄存器的模式位来改变处理器的运行模式,或者我们可以设置程序状态寄存器的中断屏蔽位来屏蔽中断。在任何时刻,我们可以访问处理器的16个寄存器(R0~R15)和当前程序状态寄存器(Current Program Status Register,CPSR)。用户模式下的程序访问的程序状态寄存器叫做APSR(Application Program Status Register),APSR是CPSR在用户模式下的别名,因为在用户模式下CPSR的部分域是不能操作的,因此CPSR的部分域被屏蔽后就是APSR。CPSR寄存器组成在所有模式下均可以访问到CPSR,只是在用户模式下CPSR的部分域是不能操作的,当前程序状态寄存器(CPSR)的位组成如下图所示:各个位域的说明如下:位标志说明31N当运算结果为负且运算指令要求更新寄存器时,该位会被置位。30Z当运算结果为0且运算指令要求更新寄存器时,该位会被置位。29C当运算结果产生进位且指令要求更新寄存器时,该位会被置位。28V当运算结果产生符号位溢出且指令要求更新寄存器时,该位会被置位。27Qcumulative saturation。26:25IT[1:0]IT位,由IT[7:2]和IT[1:0]组成,Thumb指令集中IT指令的If-Then执行状态。24J指示ARM是否处于Jazelle状态。19:16GE[3:0]被一些SIMD(Single Instruction Multiple Data)指令使用。15:10IT[7:2]见IT[1:0]的描述。9E指示处理器的大小端模式,同时可以通过设置该位来修改处理器的大小端模式,1表示大端模式,0表示小端模式。8A是否屏蔽异步终止,该位为1时表示屏蔽异步终止,为0时表示打开异步终止。7I是否屏蔽IRQ,该位为1时表示屏蔽IRQ,为0时表示打开IRQ。6F是否屏蔽FIQ,该位为1时表示屏蔽FIQ,为0时表示打开FIQ。5T指示ARM是否处于Thumb状态。J和T标志共同决定处理器使用的指令集。J=0,T=0:ARM指令集;J=0,T=1:Thumb指令集;J=1,T=0:Jazelle指令集;J=1,T=1:ThumbEE指令集。4:0M[4:0]指示处理器的模式,同时可以通过设置该位域来修改处理器的模式。处理器各个模式的编码如下图所示:APSR寄存器组成在用户模式下,用户程序能够操作的CPSR寄存器位域是有限制的,对CPSR寄存器的部分位域屏蔽之后就是APSR了,应用程序状态寄存器(APSR)的位组成如下图所示:从上图可以看出,APSR只能访问N,Z,C,V,Q和GE[3:0]这些标志位,这些标志位的含义和CPSR中对应标志位的含义一样。SPSR备份程序状态寄存器(Saved Program Status Register,SPSR)主要用于存储前一个执行模式的CPSR。FIQ、IRQ、ABT、SVC和UND模式,都有他们自己模式下专用的SPSR。在处理器发生中断或者异常时,处理器会自动的从一个模式A进入到另一个模式B,模式A的CPSR/APSR将会自动保存到模式B的SPSR中,这样模式B中的处理程序能够通过访问SPSR寄存器得到模式A下CPSR寄存器的信息。程序状态寄存器操作指令CPS指令可以通过CPS(Change Processor State)指令来修改处理器模式。CPS指令也可以用来使能或者禁止异常。CPS指令的语法如下所示:mode是处理器的模式编码,比如在从其他模式下切换到SYS模式,使用下述代码即可:IE使能中断或者终止。ID禁止中断或者终止。iflags由下面的一种或者几种组成:a:表示异步终止(asynchronous abort);i:表示中断(IRQ);f:表示快中断(FIQ);下述代码是CPS指令的一些简单用法:MRS与MSR指令MRS和MSR指令可用于读写程序状态寄存器CPSR,APSR和SPSR。在ARM处理器中,只有MRS指令可以从程序状态寄存器CPSR,APSR和SPSR中读出数据到通用寄存器中。MRS指令操作程序状态寄存器的语法如下:MRS{cond} Rd, psrcond为条件码。Rd为目标寄存器,Rd不允许为R15。psr为程序状态寄存器CPSR,APSR或者SPSR。MRS指令的示例代码如下所示:MSR指令可以用来写程序状态寄存器CPSR,APSR和SPSR的全部或者部分域。MSR指令操作程序状态寄存器的语法如下:cond为条件码。psr为程序状态寄存器CPSR或者SPSR。constant是一个8位立即数。ARM文档对于constant的介绍如下:constant is an 8-bit pattern rotated by an even number of bits within a 32-bit word. (Not available in Thumb.)Rm是源寄存器。fields由下面的一个或者多个组合而成:c:xPSR[7:0],控制位域;x:xPSR[15:8],扩展位域;s:xPSR[23:16],状态位域;f:xPSR[31:24],标志位域;MSR指令的示例代码如下所示:只有在除用户模式外的其他模式下才能够修改状态寄存器。
ARMv7-A 那些事 - 2.通用寄存器与流水线世界上有很多种体系结构的处理器,比较知名的处理器体系结构有:ARM、x86、RISC-V、mips、LoongArch、PowerPC等。不论是哪一种架构的处理器,其处理器核心都会自带一定数量的寄存器,这些寄存器在处理器核心的运行过程中发挥着基础而又重要的作用。ARM体系结构是一种基于指令加载和存储的体系结构。在这种体系结构下,所有的数据处理都需要在通用寄存器中完成,而不能直接在内存中完成。因此,这种体系结构的处理器核心处理数据的过程为:首先把待处理数据从内存加载到通用寄存器,然后进行处理,最后把结果写入内存中。通用寄存器ARM架构提供了16个32位通用寄存器(R0-R15)用于软件使用。其中R0-R12是普通寄存器,R13、R14和R15在程序的运行过程中通常用作固定的用途。R13:又叫堆栈指针寄存器(Stack pointer)SP,SP通常用于保存堆栈地址,在使用入栈和出栈指令时,SP中的堆栈地址会自动的更新。堆栈主要用于保存局部变量,保存函数间调用的关键寄存器。对于根本不需要进行堆栈操作的程序,SP可以当做普通寄存器来存储数据。R14:又叫链接寄存器(Link register)LR,LR主要用于存放函数的返回地址,即当函数返回时,知道自己该回到哪儿去继续运行。通常链接寄存器是和BL/BLX/CALL指令搭配使用,这几个指令被调用后,默认会自动将当前调用指令的下一条指令地址保存到LR寄存器当中。R15:又叫程序计数器(Program Counter)PC,PC主要用于存放CPU取指的地址。ARMv7架构同时支持ARM指令集和Thumb指令集。在ARM指令集中,当CPU正在执行A指令时,PC的值为当前指令A地址+8;在Thumb指令集中,由于Thumb指令集为16位,当CPU正在执行A指令时,PC的值为当前指令A地址+4。但是当手动向PC赋值时,CPU就会跳转到赋值所代表的地址处去运行。记住PC存放的是取指地址,不是当前CPU运行地址。备注:在ARM状态下,PC指向的地址bit[1:0]总是为0,因此PC指向的地址都是4字节对齐。ARMv7架构的处理器支持混合编码即同时支持ARM指令集和Thumb指令集,因此为了区分Thumb指令集和ARM指令集,ARM将PC指向地址的bit[0]位作为标志位。如果PC指向的地址bit[0]位为1,表示当前是Thumb指令集;如果PC指向的地址bit[0]位为0,表示当前是ARM指令集。三级流水线为了增加处理器指令流的速度,ARM使用了多级流水线技术。多级流水线技术是一种将指令的执行分解成多个步骤,并让不同指令的各步骤重叠的一种准并行处理实现技术。经典的三级流水线结构将指令的执行分成取指,译码和执行这三个阶段。可以将指令的执行过程看成工厂加工产品的过程,当没有采用流水线时只有一个工人A,工人A先对指令取指,然后对指令译码,最后执行指令,然后再次对指令取指周而复始。工人A在同一个时间只能干一件事,指令的执行也就只有等工人A对指令完成取指和译码之后才能进行。工厂老板一看,这产品的生产效率太低,就又请了工人B和工人C,现在工人A只负责取指,工人B只负责译码,工人C只负责执行,这样三条流水线同时工作,每时每刻都有指令在被取指,译码和执行,产品的生产效率大大的提高了。三级流水线示意图如下图所示:上图是ARM指令集的三级流水线结构,每条指令的地址间隔为4字节,当CPU在t3时间段开始执行add r0,r1,#3指令时,PC的值为0x00000008,即PC此时指向cmp r0,#9指令处。记住PC存放的是取指地址,不是当前CPU运行地址。结合上图对于Thumb指令集的PC值分析也是比较简单的。采用多级流水线技术后,并没有加速单条指令的执行,每条指令的步骤并没有减少,只是多条指令的不同操作步骤同时执行,因而从总体上看加快了指令流速度,缩短了程序执行时间。
ARMv7-A 那些事 - 1.概述ARM公司与芯片ARM公司是一家知识产权(IP)供应商,它与一般的半导体公司最大的不同就是它不制造芯片并且不向终端用户出售芯片,而是通过转让设计方案,由合作伙伴生产出各具特色的芯片。ARM公司利用这种双赢的伙伴关系迅速成为了全球性RISC微处理器标准的缔造者。这种模式也给用户带来了巨大的好处,因为用户只需要掌握一种ARM内核结构及其开发手段,就能够使用多家公司相同ARM内核的芯片。ARM处理器在性能,成本与功耗之间的平衡,是ARM处理器的亮点。在智能家居、物联网、平板电脑、多媒体数字、汽车电子、医疗电子等领域ARM处理器具有统治地位。指令集、架构与处理器ARM体系结构是一种硬件规范,主要用来约定指令集、芯片内部体系结构(如MMU、Cache)等。指令集是处理器使用的指令编码方式,ARM指令集的命名方式为ARMv+version,目前是ARMv1~ARMv9,数字越大表示指令集越先进。下面列举一些指令集应用到具体处理器的例子。ARMv4和ARMv4T指令集主要在ARM7TDMI、ARM920T和StrongARM这些处理器中使用。ARMv5指令集主要在ARM926EJ-S、ARM946E-S和XScale这些处理器中使用。ARMv6指令集主要在ARM1136J-S、ARM1176JZ-S和ARM1156T2-S这些处理器中使用。ARMv6-M指令集主要在Cortex-M0和Cortex-M1这些处理器中使用。ARMv7-A指令集主要在Cortex-A5、Cortex-A7、Cortex-A8、Cortex-A9、Cortex-A12和Cortex-A15这些处理器中使用。ARMv7-R指令集主要在Cortex-R4、Cortex-R5和Cortex-R7这些处理器中使用。ARMv7-M指令集主要在Cortex-M3和Cortex-M4这些处理器中使用。架构主要是指某一个处理器所使用的具体指令集。在大部分场合,架构等于指令集。比如说i.MX 6ULL处理器是基于ARMv7-A架构的,也就是说i.MX 6ULL处理器使用的是ARMv7-A指令集。基于ARMv7-A的处理器内部结构ARMv7-A采用的是32位结构,因此其核心寄存器也是32位宽。基于ARMv7-A实现的处理器内部结构描述如下:处理器核心:有单核和多核之分,对称多核应用比较广泛,通常每个核心会包含L1 I-Cache、L1 D-Cache,可选的浮点单元,可选的NEON,MMU等。中断控制器:GIC。系统总线:处理器核心通过系统总线与外设控制器进行数据交互。时钟系统;电源管理系统;复位系统;调试系统。基于ARMv7-A指令集实现的Cortex-A5处理器的结构示意图如下所示:ARMv7-A的概述就先介绍这些吧,请关注ARMv7-A后续的内容。
对于搞嵌入式底层开发的工程师来说,经常会涉及到查看芯片手册,比如某个芯片的串口控制器的寄存器值,需要知道这个值对应寄存器的哪些位,微软计算器的程序员模式虽然可以查看,但是对每一位并没有直观的展示。因此就需要一个软件直观的查看寄存器值与位的关系。我在网上找了一个软件进行使用,感觉还是可以,但是有些功能还是不太满意,因此自己寻思着手写一个寄存器查看器,按照自己想要的功能和布局进行编写,这个想法已经有很久了,由于各种事情,一直推迟到最近才开展,并且顺利的完成了,自己感觉写的这个寄存器查看器-RegisterMaster还是不错的。RegisterMaster的整体布局和配色有参考其他软件的界面,整个软件使用Qt实现,没有采用拖控件的方式来布局界面,纯手打C++代码来布局每一个按钮,Label,edit框等。RegisterMaster支持如下功能:1.RegisterMaster支持8位,16位,32位和64位寄存器值的查看;2.支持10进制数的有符号与无符号切换;3.支持16进制字母大小写显示切换功能;4.支持软件界面置顶功能;5.支持同时展示16进制,10进制,8进制,2进制内容;6.数值输入框使用正则表达式,避免用户输入非法值;7.支持快速查看ASCII码;RegisterMaster的整体效果如下:32位模式:64位模式:可以点击图中的每一位对应的按钮,比如图中位7对应的按钮此时为1,当点击一下位7按钮后,位7按钮将由1变为0,颜色也变化,下面的数值输入框的各个进制的值也跟随变化。当然了,我们也可以修改进制数值输入框的值,上面的位按钮的值也会跟随变化。RegisterMaster的大概介绍就这些吧,个人觉得还是比较方便了,如果软件有什么bug,或者有需要增加的功能,欢迎留言或者关注我的个人公众号留言反馈。如果你觉得RegisterMaster还可以,符合自己的使用需求,关注我的个人公众号,回复关键字'寄存器查看器'进行获取。
JLink使用的芯片:STM32F205RC为了防止公开链接被和谐,文章所需资源,请关注博主公众号,发送关键字 "jlink固件" 进行获取。1.问题描述在使用JLink v9的时候,不知道怎么回事,突然JLink v9的灯不亮了,将坏了的JLink插上电脑之后,设备管理器中也没有任何反应,连未知设备都没有。最开始以为是JLink被烧了,但细想这个仿真器也没有这么较弱啊,可能是JLink固件出了问题。于是上网查找资料,大致确认是固件出问题了,抱着试试的态度折腾一下吧,折腾不好就算了,万一折腾好了呢。2.准备过程将坏的JLink v9板子上的程序下载接口焊接上,焊接上之后,要清楚这些接口的引脚定义,我这里坏的JLink v9板子的程序下载接口的引脚定义如下:一个小插曲:我买的这个仿真器上面并没有丝印标出各个引脚的定义,问当时购买的商家,也不给我说引脚信息,没办法只好对照着STM32F205RC的引脚手册,使用万用表一个一个的找出各个引脚的定义,最终还真搞出来了。使用一个好的JLink来对坏的JLink烧写丢失的固件。需要将坏的JLink上面刚才焊接的5个接口全部连接到好的JLink对应的引脚上:注意:我这里使用好的JLink的1脚给坏的JLink的STM32F205RC芯片供电,请确保好的JLink的1脚输出3.3V,坏的JLink不需要通过USB接口供电了。3.烧写bootloader将好的JLink连接上电脑,打开J-Flash,然后点击"Other...",用J-Flash打开我提供的"jlink.jflash"文件,打开过程如下:然后点击Target -> Connect,连接上坏的JLink板子上的STM32F205RC芯片,连接上的信息如下:然后将我提供的"bootloader.bin"文件拖入到J-Flash的"Drag & Drop data file here"区域,拖进去的时候会提示起始地址,这里就保持默认值:0x8000000就可以了,点击"OK"按钮:最后烧写bootloader,点击Target -> Production Progamming,完成bootloader的烧写:4.固件修复烧写完成bootloader之后,需要给刚刚修好的JLink更新固件。最好的方法就是用刚修好的JLink连接上一个芯片(我这里连接STM32F429开发板),然后打开J-Link GDB Server连接STM32F429芯片,点击"OK"按钮:之后会弹出固件修复提示窗口,这里接着点击"OK"按钮:接着会弹出固件更新的进度窗口,由于固件更新的比较快,我这里没有截取到图片,固件更新完成之后,就连接上了STM32F429芯片:至此,JLink V9固件修复完成。
- 1
本站信息
目前本站共被浏览 162783 次
目前本站已经运行 3508 天
目前本站共有 165 篇文章
目前本站共有 6 条评论信息
目前本站共有 104 个标签
目前本站共有 0 条留言信息
网站创建时间: 2015年03月01日
最近更新时间: 2023年11月26日
目前本站已经运行 3508 天
目前本站共有 165 篇文章
目前本站共有 6 条评论信息
目前本站共有 104 个标签
目前本站共有 0 条留言信息
网站创建时间: 2015年03月01日
最近更新时间: 2023年11月26日
JLink V9掉固件修复(灯不亮) 3Zephyr笔记2 - 在STM32F429上运行HelloWorld 2计算NandFlash要传入的行地址和列地址 1Linux MMC子系统 - 6.eMMC 5.1工作模式-设备识别模式 0Linux MMC子系统 - 5.eMMC 5.1工作模式-引导模式 0Linux MMC子系统 - 4.eMMC 5.1常用命令说明(2) 0
最新评论
标签云
Linux嵌入式实用技巧ARM内核学习问题集合CC++编程语言阅读笔记汇编Linux内核完全注释Windows驱动开发计算机基础ARM11ARMv7-ASTM32IDESublimeLinux内核学习eMMCMMC子系统Ubuntu操作系统OfficeVMWareAPUEgccRTOS中断漫游世界随笔感悟开发工具软件应用编程VsCodearmccarmclang编译器ZephyrSPIJLink网卡驱动安装各种芯片库函数NFSμCOS内核sambaFlashUnix命令与脚本输入法Linux内核设计与实现gitRIFFWAVJATGFTPar8161安装centos有线上网μCGUI字库工程建立右键菜单网络文件系统Firefox百度NTFS文件系统CodeBlocksCentOS数据结构算法PhotoShop51KeilQTUltraEditscanfglibc宏定义UIDGID优先级娱乐天地SourceInsight磁盘扇区总线I2CPDFBComparePythonI2SFPUMakefileSWDCPUARP软件推荐FileZilla