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

news2024/12/22 20:27:15

 前言:

本文是根据哔哩哔哩网站上“正点原子[第二期]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》。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C#应用程序实现多屏显示

前言 随着业务发展&#xff0c;应用程序在一些特定场景下&#xff0c;只在一个显示器上展示信息已经不能满足用户需求。我们如何把主屏运行程序中多个窗体移动到各个扩展屏幕位置显示呢&#xff1f;C# 是通过什么方式来实现的&#xff0c;下面介绍 C# 使用 Screen 类的方式来实…

maven多模块创建-安装配置

1、前提 许久没有写文章了&#xff0c;荒废了2年多的时间&#xff0c;在整理的时候&#xff0c;发现Maven还差一篇安装配置的文章&#xff0c;现在开始提笔完善它&#xff0c;参考&#xff1a;https://blog.csdn.net/m0_72803119/article/details/134634164。 —写于2024年4月…

使用量排名前50的GPTs趋势和特征

Chatgpt的gpt商店已经有几千gpts了。目前哪些gpts比较受欢迎呢&#xff1f;有哪些趋势和投资呢? 根据whatplugin.ai&#xff08;截止日期为2024年3月&#xff09;&#xff0c;使用量最多的50个gpts数据分析结果如下&#xff1a; GPTs类型的分布情况如下&#xff1a; 图像生成…

智慧能源数据监控平台

随着科技的飞速发展&#xff0c;能源管理已逐渐从传统的粗放型向精细化、智能化转变。在这个转型过程中&#xff0c;HiWoo Cloud平台的智慧能源数据监控平台以其独特的技术优势和创新理念&#xff0c;正引领着能源管理的新潮流。 一、智慧能源数据监控平台的概念 智慧能源数据…

记录一次大数据量接口优化过程

问题描述 记录一次大数据量接口优化过程。最近在优化一个大数据量的接口&#xff0c;是提供给安卓端APP调用的&#xff0c;因为安卓端没做分批次获取&#xff0c;接口的数据量也比较大&#xff0c;因为加载速度超过一两分钟&#xff0c;所以导致接口超时的异常&#xff0c;要让…

【论文阅读】IPT:Pre-TrainedImageProcessingTransformer

Pre-TrainedImageProcessingTransformer 论文地址摘要1. 简介2.相关作品2.1。图像处理2.2。 Transformer 3. 图像处理3.1. IPT 架构3.2 在 ImageNet 上进行预训练 4. 实验4.1. 超分辨率4.2. Denoising 5. 结论与讨论 论文地址 1、论文地址 2、源码 摘要 随着现代硬件的计算能…

python数据可视化:雷达图

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 python数据可视化&#xff1a; 雷达图 选择题 关于以下代码输出的雷达图中&#xff0c;以下说法正确的是&#xff1f; import numpy as np import matplotlib.pyplot as plt from pylab impor…

【酱浦菌-爬虫项目】爬取学术堂宏观经济学论文原文

前言 首先给大家放出完整代码&#xff0c;然后下面就是用jupyter写的代码。实际上在写的时候用的是jupyter写的&#xff0c;因为感觉jupyter写的时候更加的流畅&#xff0c;每一步运行的细节都能保存下来&#xff0c;更方便学习理解。 完整代码&#xff1a; import os impo…

智能售货机:塑造未来零售新貌

智能售货机&#xff1a;塑造未来零售新貌 随着科技的飞速跃进&#xff0c;零售业态经历了一场深刻的转型&#xff0c;其中&#xff0c;智能售货机凭借其创新技术和灵活应用&#xff0c;正逐步成为新零售领域的焦点。本文旨在探讨智能售货机的市场演进路径、最新趋势&#xff0…

【AIGC调研系列】LLaVA++整合Phi-3和Llama-3能够实现什么

LLaVA能够为Phi-3和Llama-3带来的主要好处包括&#xff1a; 视觉处理能力的增强&#xff1a;通过整合Phi-3和Llama-3模型&#xff0c;创建了具备视觉处理能力的Phi-3-V和Llama-3-V版本&#xff0c;这意味着这些模型现在能够理解和生成与图像相关的内容[1]。这种能力的增加&…

智慧旅游驱动行业革新:智能技术引领服务全面升级,匠心打造高品质、个性化旅游新体验

一、引言 随着科技的飞速发展和信息化程度的不断提高&#xff0c;智慧旅游正逐渐成为旅游业发展的新趋势。智慧旅游&#xff0c;顾名思义&#xff0c;是以智能化技术为支撑&#xff0c;通过大数据、云计算、物联网、人工智能等先进技术的应用&#xff0c;实现旅游服务的全面升…

Web前端一套全部清晰 ⑤ day3 列表 表格 表单标签 综合案例

人生是一直向前无法倒退的旅程&#xff0c;所以可以偶尔回头&#xff0c;但一定要往前看 —— 24.4.29 一、综合案例1-体育新闻列表 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport…

2024-04学习笔记

1.sql优化-子查询改为外连接 1.改之前 改之前是这样&#xff0c;那针对查出来的每一条数据&#xff0c;都要执行一次箭头所指的函数 执行的sql很慢 2.改之后 改之后是这样&#xff0c;整体做外连接&#xff0c;不用每一条都再执行一次查询 执行时间缩短了好几倍 2.Mybatis中…

21.Nacos集群搭建

模拟Nacos三个节点&#xff0c;同一个ip,启动三个不同的端口&#xff1a; 节点 nacos1, 端口&#xff1a;8845 节点 nacos2, 端口&#xff1a;8846 节点 nacos3, 端口&#xff1a;8847 1.搭建数据库&#xff0c;初始化数据库表结构 这里我们以单点的数据库为例 首先新建一…

Facebook全攻略:从注册到养号再到防封,一篇搞定!

作为海外热门的社交媒体平台之一&#xff0c;Facebook已经成为品牌营销的重要渠道。很多新手小白在拿到Facebook账号后还是不知道如何操作&#xff0c;今天为大家准备了一份Facebook操作全攻略&#xff0c;从注册、养号到防封号&#xff0c;让你的Facebook跨境之旅更加顺畅&…

小程序地理位置接口怎么开通?

小程序地理位置接口有什么功能&#xff1f; 如果我们提审后驳回理由写了“当前提审小程序代码包中地理位置相关接口( chooseAddress、getLocation )暂未开通&#xff0c;建议完成接口开通后或移除接口相关内容后再进行后续版本提审”&#xff0c;如果你也碰到类似问题&#xf…

Ansys Speos|进行智能手机镜头杂散光分析

本例的目的是研究智能手机Camera系统的杂散光。杂散光是指光向相机传感器不需要的散光光或镜面光&#xff0c;是在光学设计中无意产生的&#xff0c;会降低相机系统的光学性能。 在本例中&#xff0c;光学透镜系统使用Ansys Zemax OpticStudio (ZOS)进行设计&#xff0c;并使用…

使用 GitHub Actions 实现项目的持续集成(CI)

目录 什么是 GitHub Actions 基础概念 Workflow 文件 Workflow 语法 实例&#xff1a;编译 OpenWrt 什么是 GitHub Actions GitHub Actions 是 GitHub 推出的持续集成&#xff08;Continuous Integration&#xff0c;简称 CI&#xff09;服务它允许你创建自定义工作流&am…

源码编译framework.jar 并成功导入android studio 开发

一、不同安卓版本对应路径 Android N/O: 7 和 8 out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar Android P/Q: 9 和 10 out/soong/.intermediates/frameworks/base/framework/android_common/combined/framework.jar Android R: 11以上 out/so…

Qt下使用7Z源码进行压缩和解压缩

7Z压缩是一款常用的压缩算法和工具&#xff0c;本文主要介绍一款在qt环境下进行编译的压缩方法。 本人测试是可以正常跑通的&#xff0c;具体代码部分请下载&#xff1a;下载链接&#xff0c;提取码&#xff1a;ev9t 7z源码网址&#xff1a;7-Zip 7z简介&#xff1a; 7z 是…