正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-6.3--Cortex-A7寄存器介绍

 前言:

本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM(MX6U)裸机篇”视频的学习笔记,在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。

引用:

正点原子IMX6U仓库 (GuangzhouXingyi) - Gitee.com

《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.5.2.pdf》第 8.1 章

《正点原子资料_A盘/02开发板原理图/IMX6ULL_MINI_V2.2(Mini底板原理图).pdf》

  • 资料盘 开发板资料链接: https://pan.baidu.com/s/1j5Jzbdx9i-g0cWIi3wf2XA 提取码:ag1u


正文:

本文是 “正点原子[第二期]Linux之ARM(MX6U)裸机篇--第6.3讲” 的读书笔记。

1. Contex-A7 寄存器介绍

本节介绍 ARM Contex-A 的内核寄存器,注意不是芯片的外设寄存器,本节主要参考 《ARM Contex-A(armV7)编程手册V4.0.pdf》第三章 ARM Processor Modes And Registers。

ARM构架提供了 16 个 32 位通用寄存器(R0~R15)供软件使用,前 15 个寄存器(R0~R14)可以用作通用寄存器,R15 是程序计数器 PC (Program Counter),用来保存将要执行的指令。ARM 还提供了一个当前程序状态寄存器 CPSR (Current Program Status Register) 和一个备份程序状态寄存器 SPSR (S Program Status Register),SPSR 寄存器就是 CPSR 寄存器的备份。这18个寄存器如图 6.3.1 所示。

上一小节我们讲到 ARM Contex-A7 有9种运行模式,每一种运行模式都有一组与之对应的寄存器组。每一种模式可见的寄存器包括15个通用寄存器(R0~R14),一两个状态寄存器,和一个程序计数器PC。在这些寄存器中,有些是所有模式公用的同一个物理寄存器,有一些是个模式自己拥有的,各个模式所拥有的寄存器如下表所示。

在上图中浅色字体的是与 User 模式所共有的寄存器,蓝绿色背景的是各个模式所独有的寄存器。可以看出,在所有模式总,低寄存器组(R0~R7)是共享同一组物理寄存器的,只是一些高寄存器组在不同的模式下有自己独有的寄存器,比如 FIQ 模式下 R8~R14 是独立的物理寄存器。假如某个程序在 FIQ 模式下访问 R13 寄存器,那它实际访问的是寄存器 R13_fiq,如果程序在 SVC  模式下访问 R13 寄存器,那它实际访问的是寄存器R13_svc 。总结一下,ARM Contex-A 内核的寄存器组组成如下:

  1. 34个通用寄存器,包括R15 程序计数器(PC),这些寄存器是32位的。
  2. 8个状态寄存器
  3. HYP模式下一个独有的 ELR_Hyp寄存器
1.1 通用寄存器

R0-R15 就是通用寄存器,通用寄存器可以分为一下3类

  1. 未备份寄存器,即 R0~R7
  2. 备份寄存器,即 R8~R14
  3. 程序计数器PC,即 R15
1.1.1 未备份寄存器

未备份寄存器指的是 R0~R7 这8个寄存器,因为在所有的处理器模式下这个8个寄存器都是同一个物理寄存器,在不同模式下下,这8个寄存器的数据就会被破坏。所以这8个寄存器没有被用作特殊用途。

1.1.2. 备份寄存器

备份寄存器中的 R8 ~ R12 这 5 个寄存器有两种物理寄存器,在快速中断模式下 FIQ 它们对应着 Rx_irq(x=8~12)物理寄存器,其它模式下对应着 Rx(8~12)物理寄存器。FIQ是快速中断模式,看名字就知道这个中断模式要求快速执行。FIQ模式下中断处理程序可以使用 R8 ~ R12 寄存器,因为 FIQ 模式下下 R8~R12 是独立的,因此中断可以不用执行保存和恢复中断现场的指令,从而加速中断的执行过程。

备份寄存器 R13 一共8个物理寄存器,其中一个是用户模式(User)和系统模式(Sys)公用的,剩下一个分别对应7种不同的模式。R13 也叫做 SP (Stack Pointer),用来做栈指针。基本上每种模式都有一个自己的R13寄存器,应用程序会初始化R13,使其指向该模式专用的栈地址,这就是常说的初始化SP指针。

备份寄存器R14一共有7个物理寄存器,其中一个是用户模式(User)和系统模式(Sys)和超级监视模式(Hyp)所共有的,剩下的6中分别对应着6中不同的模式。R14也称为连接寄存器(LR),LR寄存器在ARM中的主要用途有以下2种:

  1. 每种处理器模式使用R14(LR)来存放当前子程序的返回地址,如果使用 BL 或则 BLX 来调用子函数的话,R14(LR)用来存放当前函数的返回地址,在子函数中,将R14(LR)的值赋值给R15(PC)即可完成子函数的返回,比如在子程序中使用如下代码:
    MOV PC, LR @寄存器LR中的值赋值给PC,实现跳转
    或者可以在子程序入口将LR入栈:
    PUSH {LR} @将LR寄存器入栈
    在子程序的最后出栈即可
    POP {PC}   @将上面压栈的LR寄存器出栈给PC寄存器,严格意义上来讲应该是将@LR-4 赋
                       @赋值给PC,应为是3级流水线,这里只是演示代码
  2. 当异常发生时,该异常模式对应的R14寄存器被设置成该异常模式将要返回的地址,R14也可以当做普通寄存器使用。
1.1.3. 程序计数器R15

程序计数器R15也叫做PC,R15保存着当前正在执行的指令地址加8字节,这是因为ARM的流水线机制导致的。ARM处理器3级流水线:取指->译码->执行,这3级流水香循环执行,比如当前正在执行第一条指定的同时,也对第二条指令译码,第三条指令也同时被去除存放到 R15 (PC)中。我们喜欢以当前你正在执行的指令作为参考点,也就是以第一条执行为参考点,那么 R15(PC)中存放就是第三条指令,换句话说R15(PC)总是指向当前正在执行的指令地址再加上2条指令的地址。对于32位的ARM处理器,每条指令时4个字节,所以
R15(PC)值 = 当前执行的程序位置 + 8 个字节

1.2 程序状态寄存器

所有的处理器模式都公用一个 CPSR (Current Program State Register)物理寄存器,因此 CPSR 可以在任何模式下访问。CPSR 是当前程序状态寄存器,该寄存器包含了条件标志位,当前处理器模式标志等一些状态为以及一些控制位。所有的处理器模式都公用一个CPSR必然会导致冲突,为此,除了 User 和 Sys 这两个模式以外,其它7个模式都配备了一个专用的物理状态寄存器,叫做 SPSR (备份程序状态寄存器),当特定的异常中断发生时,SPSR寄存器用来保存当前策划给你续状态寄存器(CPSR)的值,当异常退出以后可以用SPSR中保存的值来回复CPSR。

因为User和Sys两个模式不是异常模式,所以并没有配置SPSR,因此不能再User和Sys模式下访问SPSR,会导致不可预知的结果。由于SRSR是CPSR的备份,因此SPSR和CPSR的寄存器结构相同,如下图所示

位位置功能
N(bit31)当两个补码表示的有符号整数运算的时候,N=1表示运算结果为负数,N=0表示结果为正数。
Z(bit30)Z=1表示运算结果为零,Z=0表示运算结果不为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表示累积不饱和。
IT[1:0](bit26:25)和 IT[7:2] (bit15:bit10)一起表示IT[7:0],作为IF_THEN指令状态。
J(bit24)仅ARM V5TE_j 构架支持,J=1 表示处于 Jazelle 状态,此位通常和 T(bit5)位一起表示当前所使用的指令集,如表 6.3.2.1 所示:
GE[3:0](bit19:16)SIMD指令有效,大于或等于
IT[7:2](bit15:10)参考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 禁止FIRQ,F=0 使能 FIRQ
T(bit5)控制指令执行状态,表名指令时ARM指令还是Thumb指令,通常和J(bit24)一起表示指令类型,参考J(bit24)位。
M[4:0]处理器模式位,含义如表 6.3.2.2 所示

M[4:0]处理器模式
10000User模式
10001FIQ模式
10010IRQ模式
10011Supervisor (SVC)模式
10110Monitor(MON)模式
10111Abort(ABT)模式
11010Hyper(HYP)模式
11011Undef(UND)模式
11111System(SYS)模式

表 6.3.2.2 


 

2. ARM 汇编语言简介

在进行嵌入式Linux开发的时候绝对要掌握基本的ARM汇编,因为 Cortex-A 芯片一上电 SP 指针还没有初始化,C环境还没准备好,所以肯定不能运行C代码,必须先用汇编语言设置好C环境,比如初始化DDR,设置SP指针等等,当汇编把C环境设置好了以后才可以运行C代码。所以Contex-A一开始肯定是汇编代码。本章我们只讲解最常用的一些指令,满足我们后续学习即可。

I.MX6U-ALPHA/Mini 使用的是NXP的I.MX6UL芯片,这是一款 Contex-A7 内核的芯片,所以我们要讲的是 Cortex-A 汇编指令。为此我们需要参考两份 Contex-A 内核有关的文档:

  • 《ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition.pdf》
  • 《ARM CortexA(armV7)编程手册 V4.0.pdf》

第一份文档主要讲解 ARMv7-A 和 ARMv7-R 指令集的开发,Contex-A7 使用的是 ARMv7-A 指令集。第二份文档主要讲解 Cortex-A(armV7)编程的,这两份文档是学习Cortex-A不可或缺的文档。在《ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition.pdf》的 A4 章详细的讲解了 Cortex-A 的汇编指令,要想系统的学习 Cortex-A的指令就要认真的阅读 A4 章节。

对于Cortex-A芯片来讲,大部分芯片在上电以后C语言环境还没有准备好,所以第一行程序肯定是汇编,至于要写多少汇编程序,那就看一能在哪一步把C语言环境准备好。所谓的C语言环境就是保证C语言能够正常运行。C语言的函数调用涉及到出栈入栈,出栈入栈就要对堆栈进行操作,所谓的堆栈其实就是一段内存,这段内存比较特殊,由SP指针访问,SP指针指向栈顶。芯片一上电SP指针还没有初始化,所以C语言没法执行,对于有些芯片还需要初始化DDR,因为芯片本身没有RAM,或者内部RAM不开放给用户使用,用户代码需要再DDR中运行,因此一开始要用汇编来初始化DDR控制器。后面学习Uboot和Linux内核的时候汇编是必须要会的,是不是觉得好难啊?还要会汇编!前面都说了只是在芯片上电以后用汇编来初始化一个外设,不会涉及到复杂的代码,而且用到的指令都是很简单的,用到就是那么十几个指令。所以,不要看到汇编就觉得复杂,打击学习信心。

2.1 GNU汇编语法

不同的汇编器其汇编的语法就有一些小的差别。我们要编写的是ARM汇编,编译器使用的是GCC交叉编译器,所以我们的汇编代码要符合 GNU语法。

GNU汇编语法适用于所有的架构,并不是ARM独享的,GNU汇编由一系列的语句组成,每行一条语句,每条语句有三个可选的部分:

label: instruction @comment

label: 即标号,表示地址位置,有些指令前面可能会有标号,这样就可以通过这个标号得到指令的地址,标号也可以用来表示数据地址。注意 label 后面的 “:”,任何以“:” 结尾的标识符都会被识别为一个标号。

instruction: 即指令,也就是汇编指令或伪指令。

@符号,表示后面是注释,就和C语言中的的"/*"和"*/"一样,其实GNU汇编文件中也可以使用"/*"和"*/" 来进行注释。

comment: 就是注释内容

比如如下代码:

add:
    MOVS R0, #0X12 @设置R0=0x12

上面代码中的“add”就是标号,“MOVRS R0,#0x12”就是指令,最后的“ @设置R0=0x12”就是注释。

注意!ARM中的指令,伪指令,伪操作,寄存器等可以全部使用大写,也可以全部使用小写,但不能大小写混用。

用户也可以使用 .section 伪操作来定义一个段,汇编系统预留了一些段名:

  • .text 表示代码段
  • .data 初始化的数据段
  • .bss 未初始化数据段
  • .rodata 只读数据

我们当然可以自己使用 .section 来定义一个段,每个段以段名开始,以下一段或者文件结尾结束,比如:

.section .testsection @定义一个 .testsectjion 段

汇编程序的默认入口标志是 _start,不过我们可以在链接脚本中使用 ENTRY 来指明其它的入口点,西面就是使用 _start 作为入口标号。

.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

GNU 汇编还有其他的伪操作,但是最常见的就是上面这些,如果想详细了解全部的伪操作,可以参考《ARM Cortex-A(armV7)编程手册V4.0.pdf》的第57页。

GNU汇编同样支持函数,函数个数如下:

函数名:
    函数体
    返回语句

GNU 汇编函数的返回语句不是必须得,如下代码就是用GNU汇编编写的 Cortex-A7 中断服务函数:

//示例代码 7.1.1.1 汇编函数定义

/* 未定义中断 */
Undefined_Handler:
    ldr r0, =Undefined_Handler
    bx r0

/* SVC 中断 */
SVN_Handler:
    ldr r0, =SVC_Handler
    bx r0

/* 预取终止中断 */
PerfAbort_Handler:
    ldr r0, =PerfAbort_Handler
    bx r0
  • 上述代码中定义了3个汇编函数: Undefined_Handler, SVC_Handler, 和 PerfAbort_Handler。以函数 Underfined_Handler 为例我们来看一下汇编函数组成,Underfined_Handler 就是函数名,"ldr r0, =Undefined_Handler" 是函数体,"bx r0" 是函数返回语句,"bx" 是返回指令,函数返回语句不是必须得。
2.2 Cortex-A7 常用汇编指令

本节我们将介绍一些常用的 Corex-A7 汇编指令,如果想系统的了解  Corex-A7 的所有汇编指令请参考《ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition.pdf》的A4章节。

2.2.1 处理器内部数据传送指令

使用处理器做的最多的事情就是在处理器内部来回的传送数据,常见的操作有:

  • 将数据从一个寄存器传送到另一个寄存器
  • 将数据从一个寄存器传送到特殊寄存器,如 CPSR 和 SPSR 寄存器
  • 将立即数传送到寄存器

数据传送常用的指令有三个: MOV,MRS 和 MSR,这三个指令的用法如下表所示

指令目的描述
MOVR0R1将R1里面的数据复制到R0中
MRSR0CPSR将特殊寄存器CPSR里面的数据复制到R0
MSRCPSRR1将R1里面的数据复制到特殊寄存器CPSR里

分别来详细介绍一下如何使用这3个指令:

1. MOV 指令

MOV指令用来将一个数据从给一个寄存器拷贝到另外一个寄存器,或者将一个立即数传递到寄存器里面,使用示例如下:

MOV R0, R1        @将寄存器R1里的数据传送到R0,即 R0=R1
MOV R0, #0x12     @将立即数0x12 传递给R0寄存器,即R0=0x12

2.MRS 指令

MRS指令用来将特殊寄存器(如CPSR和SPSR)中的数据传送给通用寄存器,要读取特殊寄存器的数据智能使用MRS指令,使用示例如下

MRS R0,CPSR        @将特殊寄存器CPSR里面的数据传送给R0,即R0=CPSR

3.MSR指令

MSR指令和MRS指令正好相反,MSR指令用来将普通寄存器里的数据传递给特殊寄存器,也就是写特殊寄存器,写特殊寄存器智能使用MSR,使用示例如下

MSR CPSR, R0         @将R0中的数据复制到CPSR中,即CPSR=R0
2.2.2 存储器访问指令

ARM不能直接访问存储器,比如RAM中的数据,I.MX6Ul 中的寄存器就是RAM类型的,我们用汇编配置 I.MX6Ul 寄存器的时候需要借助存储器访问指令,一般先将要配置的值写入到 Rx(0~12)寄存器中,然后借助存储器访问指令将Rx中的数据写到 I.MX6U 寄存器中。读I.MX6U  寄存器也是一样的,只是过程相反。常用的存储器访问指令有两种 LDR 和 STR, 用法如下表:

指令描述
LDR Rd, [Rn,#offset]从存储器 Rn+offset 的位置读取数据存放到Rd中
STD Rd, [Rn,#offset]将Rd中的数据写入到存储器Rn+offset位置

分别来详细的介绍一些如何使用这两个指令,

1. LDR指令

LDR主要用户从存储器加载数据到寄存器Rx中,LDR也可以将一个立即数加载到寄存器Rx中,LDR加载立即数的时候要使用 "=",而不是"#"。在嵌入式开发中,LDR最常用的就是读取CPU的寄存器值,比如 I.MX6U 有个寄存器 GPIO1_GDIR ,其地址为0x0209_c004 ,我们要读取这个寄存器中的数据,示例代码如下:

LDR R0, =0x0209C004    @将寄存器地址0x0209C004 加载到R0中,即R0=0x0209C004
LDR R1, [R0]           @读地址0x0209c004中的数据到R1寄存器

2. STR指令

LDR从存储器读取数据,STR就是将数据写入到寄存器,同样以 I.MX6U 寄存器 GPIO1_GDIR 为例,现在我们要配置 GPIO1_GDIR 的值为 0x20000002 ,示例代码如下:

LDR R0, =0x0209C004    @将寄存器地址0x0209C004加载到R0,即R0=0x0209C004
LDR R1, =0x20000002    @将R1要写入到寄存器的值,即R1=R0==0x20000002
STR R1, [R0]           @将R1的值写入到R0中所保存的地址中

LDR和STR都是按照字(4个字节)进行读取和写入的,也就是操作32位数据,如果按照字节,半字进行操作的话可以在执行“LDR”后面加上B或H,比如按字节操作的指令就是 LDRB 和 STRB,按照半字进行操作的就是 LDRH 和 STRH。

2.2.3 压栈和出栈指令

我们通常会再 A 函数总调用 B 函数,当B函数执行完以后再回到A函数继续执行。要想再调回A函数以后代码能够接着正常运行,那么必选在调到B函数之前将当前处理器状态保存起来(就是保存R0~R15这些寄存器的值),当 B 函数执行完成以后再用前面保存的寄存器值恢复 R0~R15 即可。保存R0~R15寄存器的操作就叫做现场保护,恢复 R0~R15 寄存器的操作就叫做恢复现场。在进行现场保护的时候需要压栈(入栈)操作,恢复现场的时候要进行出栈操作。压栈的指令为PUSH,出栈的指令为POP,PUSH和POP是一种多存储和多加载指令,即可以一次操作多个寄存器数据,它们利用当前栈指针 SP 来生成地址,PUSH 和 POP 的用法如下表所示:

指令描述
PUSH <reg lisg>将寄存器列表入栈
POP <reg list>从栈中恢复寄存器列表

假如我们现在要将 R0~R3 和 R12 这5个寄存器压栈,当前 SP 指针指向 0x8000_0000 ,处理器的堆栈是向下增长的,使用汇编代码如下:

PUSH {R0~R3,R12}    @将R0~R3和R12寄存器压栈

压栈完成后的堆栈如图 7.2.3.1 所示

图 7.2.3.1就是R0~R3,R12进行压栈以后得堆栈示意图,此时的SP指向 0x7FFF_FFEC,假如我们现在要再将 LR 进行压栈,汇编代码如下:

PUSH {LR}    @将LR进行压栈

对 LR 进行压栈完成以后的堆栈模型如图 7.2.3.2 所示

图 7.2.3.2 就是分两步对 R0~R3,R12和 LR 进行压栈以后的堆栈模型,如果我们要出栈的话使用如下的代码:

POP {LR}        @先恢复LR
POP {R0~R3,R12} @再恢复R0~R3,R12

出栈的就只从栈顶,也就是 SP 当前指向的位置开始,地址依次减小来提取堆栈中的数据到要恢复的寄存器列表中。PUSH和POP的另外一种写法是 “STMFD SP!” 和 “LDMFD SP!”,因此上面的汇编代码页可以改写为:

STMFD SP! , {R0~R3,R12}        @R0~R3,R12入栈
STMFD SP! , {LR}               @LR入栈

LDMFD SP!, {LR}                @先恢复LR
LDMFD SP!, {R0~R3, R12}        @再恢复R0~R3,R12

STMFD 可以分为两部分: STM 和 FD,同理 LDMFD 也可以分为两部分 LDM 和 FD。看到 STM 和 LDM 有没有觉得似曾相识,前面我们讲了 LDR 和 STR ,这两个是数据加载和存储指令,但是每次智能读写存储器中的一个数据。STM 和 LDM 就是多存储和多加载,可以连续的读写存储器中的多个连续数据。

FD 是 Full Decending 的缩写,即满递减的意思。根据 ATPCS (ARM-Thumb Procedure Call Standard)规则,ARM使用的是FD类型的堆栈,SP指向的是最后一个入栈的数值,堆栈是由高地址向下增长的,也就是前面说的向下增长的堆栈,因此最常用的指令就是 STMFD(STM Full Decending)和 LDMFD(LDM Full Decending)。STM 和 LDM 的指令寄存器列表编号小的对应低地址,编号高的对应高地址。(即PUSH/POP, LDMFD/STMFD 入栈顺序是从右往左入栈)。

2.2.4 跳转指令

有多种跳转指令,比如:

  1. 直接跳转指令,B, BL, BX
  2. 直接向 PC 寄存器里面写数据

上述两种方法都可以完成跳转,但是一般常用的还是 B, BL, BX ,用法如下表

指令描述
B <lable>跳转到 label,如果跳转范围超过了 +/- 2KB 可以指定 B.W <lable>使用32位版本的跳转指令,这样可以得到较大范围的跳转
Bx <Rm>间接跳转指令,跳转到存放于 Rm 中的地址处,并且切换指令集
BL <label>跳转到标号地址,并将访问地址保存在LR中
BLX <Rm>结合 BX 和 BL 的特点,跳转到 Rm 指定的地址,并将返回地址保存在LR中,切换指令集。

我们重点来看一下B和BL指令,因为这两个是我们用的最多的,如果要再汇编中进行函数调用使用的就是B和BL指令。

1. B 指令

这是最简单的跳转指令,B指令会将 PC 寄存器的值设置为跳转目标地址,一旦执行B指令,ARM处理器就会立即跳转到指定的目标地址。如果要调用的函数不会再返回到原来的执行处,那就可以使用 B 指令,如下例所示:

.global _start

_start:
    LDR SP, =0x80200000    @设置栈指针 SP=0x8020_0000
    B main                 @跳转到 main 函数,main是一个label(标签)

上述代码就是典型的在汇编中初始化C运行环境,然后跳转到C文件的 main 函数执行,上述代码只是初始化了 SP 指针,有些处理器还需要做其他初始化,比如初始化 DDR 等等。因为跳转到 C 文件以后再也不会回到汇编了,所以在第4行直接使用了B指令来完成跳转。

2. BL 指令

BL 指令相比于 B 指令,在跳转之前会再寄存器 LR(R14) 中保存当前 PC 寄存器的值,所以可以通过 LR 寄存器的值重新加载到 PC  来继续从跳转之前的代码处运行,这是子程序调用的一个基本但常用的手段。比如 Cortex-A 处理器的 irq 中断服务函数都是汇编编写的,主要用汇编来实现现场保护和恢复,获取中断号等。但是具体的中断处理过程都是C函数,所以就会存在在汇编中调用C的问题。而且C语言版本的中断处理函数执行万策划给你以后是需要返回到 irq 汇编服务函数,因此还要处理其他的工作,一般是恢复现场。这个时候就不能直接使用 B 指令了,因为B指令一旦跳转就再也不会回来了,这个时候需要使用 BL 指令,示例代码如下:

push {r0,r1}            @保存r0,r1, 压栈
cps #0x13               @进入SVC模式,允许其它中断再次进去

bl system_irqhandler    @加载C语言中断处理函数到r2寄存器中

cps #0x12               @进入IRQ模式
pop {r0,r1}             @恢复r0,r1,出栈
str r0 [r1, #0x10]      @中断执行完成,写EOIR

上述代码第5行就是执行C语言版本的中断处理函数,当处理完成以后是需要返回来继续执行下面的程序,所以使用了BL指令。

2.2.5 算术运算指令

汇编也可以进行算术运算,比如加减乘除,常用的运算指令如下表所示

指令计算公式备注
ADD Rd, Rn, RmRd = Rn + Rm加法运算,指令为ADD
ADD Rd, Rn, #immedRd = Rn + #immed加法运算,指令为ADD
ADC Rd, Rn, RmRd = Rn + Rm + 进位带进位的加法运算,指令为ADC
ADC Rd, Rn, #immedRd = Rn + #immed + 进位带进位的加法运算,指令为ADC
SUB Rd, Rn, RmRd = Rn - Rm减法
SUB Rd, #immedRd = Rd - #immed减法
SUB Rd, Rn, #immed Rd = Rn - #immed 减法
SBC Rd, Rn, #immedRd = Rn - #immed - 借位带借位的减法
SBC Rd, Rn, RmRd = Rn - Rm - 借位带借位的减法
MUL Rd, Rn, RmRd = Rn * Rm 乘法(32位)
UDIV Rd, Rn, RmRd = Rn/Rm无符号除法
SID Rd, Rn, RmRd = Rn/Rm有符号除法

在嵌入式开发中最常会用的就是加减指令,乘除基本用不到。

2.2.6 逻辑运算指令

我们用 C 语言进行CPU寄存器配置的时候常常需要用到逻辑运算符号,比如 “&” , “| ” 等 逻辑运算符。使用汇编语言的时候也可以使用逻辑运算指令,常用的运算指令如下表所示。

指令计算公式备注
AND Rd, RnRd = Rd & Rn按位与
 
AND Rd, Rn, #immedRd = Rn & #immed
AND Rd, Rn, RmRd = Rn & Rm
ORR Rd, RnRd = Rd | Rn按位或
 
ORR Rd, Rn, #immedRd = Rn | #immed
ORR Rd, Rn, RmRd = Rn | Rm
BIC Rd, RnRd = Rd & (~Rn)位清除
 
BIC Rd, Rn, #immedRd = Rn & (~#immed)
BIC Rd, Rn, RmRd = Rn & (~Rm)
ORN Rd, Rn, #immedRd = Rn | (#immed)按位或非
 
ORN, Rd, Rn, RmRd = Rn | (Rm)
EOR Rd, RnRd = Rd ^ Rn按位异或
 
EOR Rd, Rn, #immedRd = Rn ^ #immed
EOR Rd, Rn, RmRd = Rn ^ Rm

逻辑运算指令都很好理解,后面时候汇编配置 I.MX6Ul 的外设的时候可能会用到,ARM 汇编就讲到这里,本节主要讲解了一些最常用的汇编指令,还有很多不常用的指令没有讲解,但是够我们后续学习使用了。要想详细学习 ARM 的所有指令请参考 《ARM Cortex-A(armV7) 编程手册V4.opdf》和 《ARm Architecutre Refercen Manual ARMv7-A and ARMv7-R edition.pdf》。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/588804.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Numerical Analysis(byRichard.L..Burden)【pdf高清英文原版】

专栏导读 作者简介&#xff1a;工学博士&#xff0c;高级工程师&#xff0c;专注于工业软件算法研究本文已收录于专栏&#xff1a;《有限元编程从入门到精通》本专栏旨在提供 1.以案例的形式讲解各类有限元问题的程序实现&#xff0c;并提供所有案例完整源码&#xff1b;2.单元…

HarmonyOS 4.0(鸿蒙开发)01 - 怎么学习鸿蒙引导篇

作为公司的全栈开发工程师 以及 未来的发展是有鸿蒙这个阶段的&#xff0c;以及本身具有这个技术栈由此后续会分享自己在实战中学习到的东西&#xff0c;碰到的bug都会分享出来&#xff0c;这是引导篇期待后续的更新 学习目标&#xff1a; 理解HarmonyOS操作系统的架构和开发…

目标检测算法YOLOv3简介

YOLOv3由Joseph Redmon等人于2018年提出&#xff0c;论文名为&#xff1a;《YOLOv3: An Incremental Improvement》&#xff0c;论文见&#xff1a;https://arxiv.org/pdf/1804.02767.pdf &#xff0c;项目网页&#xff1a;https://pjreddie.com/darknet/yolo/ 。YOLOv3是对YOL…

解决IDEA下springboot项目打包没有主清单属性

1.问题出现在SpringBoot学习中 , 运行maven打包后无法运行 报错为spring_boot01_Demo-0.0.1-SNAPSHOT.jar中没有主清单属性 SpringBoot版本为 2.6.13 Java 版本用的8 解决方法 1.执行clean 删除之前的打包 2.进行打包规范设置 2.1 3.进行问题解决 (借鉴了阿里开发社区) 使用…

利用PDAL2.7.1 实现点云滤波

利用PDAL2.7.1 实现点云滤波 本文介绍利用PDAL实现点云滤波方法&#xff0c;包含pipeline命令行运行、C代码两种方法&#xff0c;C代码分别介绍对点云文件进行滤波、点云全部在内存中进行滤波的pdal两种调用方法。并简单探究pdal的设计结构。 目录 1 pipeline命令调用方法2 文…

R语言4版本安装mvstats(纯新手)

首先下载mvstats.R文件 下载mvstats.R文件点此链接&#xff1a;https://download.csdn.net/download/m0_62110645/89251535 第一种方法 找到mvstats.R的文件安装位置&#xff08;R语言的工作路径&#xff09; getwd() 将mvstats.R保存到工作路径 在R中输入命令 source(&qu…

飞腾D2000+X100 TYPE6全国产核心板

飞腾D2000X100 TYPE6核心板 产品概述 飞腾D2000X100 TYPE6核心板为增强型自主控制器核心板&#xff0c;其核心芯片CPU采用飞腾D2000/8核工业版CPU、飞腾桥片X100、双通道DDR4L插槽、PHY芯片等。 产品特点 l 基于飞腾D2000X100桥片 l 丰富的PCIE扩展资源&#xff0c;一路PCIE…

C++入门系列-函数重载

&#x1f308;个人主页&#xff1a; 羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 函数重载 自然语言当中&#xff0c;一个词可以有多重含义&#xff0c;人们可以通过上下文来判断该词真实的含义&#xff0c;即该词被重载了。 函数重载的概念 函数重载&#x…

A4的PDF按A3打印

先用办公软件打开&#xff0c;比如WPS。 选择打印-属性。 纸张选A3&#xff0c;如果是双面打印&#xff0c;选短边装订&#xff0c;然后在版面-页面排版-每张页数&#xff08;N合1&#xff09;选2。 不同打印机的具体配置可能不一样&#xff0c;但大体都是这个套路。

rocketmq dashboard控制台中topic状态无法展示

现象 在使用rocketmq控制台查看topic状态和订阅状态时&#xff0c;出现错误和没有信息的情况。 原因 rocketmq控制台版本问题&#xff0c;最新版本为1.0.1&#xff0c;支持rocketmq5版本&#xff0c;如果使用rocketmq4版本的服务无法兼容对应的数据。同理1.0.0版本也无法兼容ro…

中兴ZXV10 B860AV2.1机顶盒刷机

移动的电视盒子如果不续费&#xff0c;连桌面都进不去&#xff0c;趁着五一有空把系统刷了。整体上比较顺利。 注意这个盒子只有两个螺丝&#xff0c;盒子上已经标识&#xff0c;如上图左上角和右下角。盒子里面有卡扣&#xff0c;卸掉螺丝直接扣是很难打开的&#xff0c;需要用…

【CLion】clion无法加载或找不到cmakekists文件

一、问题表象 最近工作中&#xff0c;在git pull远程仓库最新版本程序后&#xff0c;平时打开CLion自动加载的工程CMakeLists文件突然失效&#xff08;显示找不到可编译的文件&#xff09;&#xff0c;无法debug程序。 二、原因分析 基于平时的编码经验和之前git pull也出现…

深度学习之基于CIFAR10图像分类可视化

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 深度学习之基于CIFAR-10图像分类可视化项目简介 一、项目背景 随着深度学习和计算机视觉技术的飞速发展&#xff…

边缘计算含义与应用简析

边缘计算概述 边缘计算使数据存储和处理靠近生成或收集数据的位置&#xff0c;而不是在位于数千公里的服务器上。它将通过保持灵活性在边缘无缝可靠地部署服务。它比云计算更安全&#xff0c;因为不需要传输数据。因此&#xff0c;在将数据从边缘移动到云端时&#xff0c;不用…

基于React实现B站评论区

今天继续来学习一下React&#xff0c;使用React实现B站评论区&#xff0c;如下图&#xff1a; 在使用React开发类似B站评论区的功能时&#xff0c;我们需要考虑以下几个关键点来构建一个基本的评论系统&#xff1a; 1. 设计组件结构 首先&#xff0c;设计组件结构是关键。至少…

什么是弹性云服务器(ECS)

弹性云服务器&#xff08;Elastic Cloud Server&#xff0c;ECS&#xff09;是由CPU、内存、操作系统、云硬盘组成的基础的计算组件。弹性云服务器创建成功后&#xff0c;您就可以像使用自己的本地PC或物理服务器一样&#xff0c;在云上使用弹性云服务器。 云服务器ECS&#x…

Re71:读论文 Sequence to Sequence Learning with Neural Networks

诸神缄默不语-个人CSDN博文目录 诸神缄默不语的论文阅读笔记和分类 论文名称&#xff1a;Sequence to Sequence Learning with Neural Networks ArXiv下载地址&#xff1a;https://arxiv.org/abs/1409.3215 本文是2014年NeurIPS论文&#xff08;那时候这个会还叫NIPS&#xf…

HBase的简单学习四

一 HBase的进阶 1.1 hbase的写流程 Hbase读取数据的流程&#xff1a; 1&#xff09;是由客户端发起读取数据的请求&#xff0c;首先会与zookeeper建立连接 2&#xff09;从zookeeper中获取一个hbase:meta表位置信息&#xff0c;被哪一个regionserver所管理着 hbase:meta表…

C语言:循环结构

循环结构 1. for循环概念举例示例结果分析 补充 2. while循环概念举例示例结果分析补充 3. do-while循环概念举例示例结果分析 补充 4.循环控制举例示例结果分析 C语言中的循环结构是一种重要的编程构造&#xff0c;它允许我们重复执行一段代码&#xff0c;直到满足某个条件为止…

Hive优化以及相关参数设置

1.表层面设计优化 1.1 表分区 分区表实际上就是对应一个 HDFS 文件系统上的独立的文件夹&#xff0c;该文件夹下是该分区所有的数据文件。Hive 中的分区就是分目录&#xff0c;把一个大的数据集根据业务需要分割成小的数据集。在查询时通过 WHERE 子句中的表达式选择查询所需要…