正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-6.3

news2024/12/28 14:00:24

 前言:

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

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

相关文章

User Agent 解析:它是什么以及工作原理

什么是User Agent? UserAgent&#xff0c;简称UA&#xff0c;是一个使服务器能够识别用户使用的浏览器类型、版本以及运行浏览器的操作系统等信息的字符串。它作为浏览器请求头部信息的一部分发送给服务器&#xff0c;以便服务器可以返回合适格式和版本的内容。 跟Cookie一样…

x264 编码器源码分析综述

================================================================================ 系列文章 x264配置文章链接🔗Windows11编译x264源码https://blog.csdn.net/yanceyxin/article/details/135035650Mac调试x264源码https://blog.csdn.net/yanceyxin/article/details

想要应聘前端工程师——了解前端招聘需求

市场对前端工程师的需求依然旺盛。所谓知己知彼,百战不殆,分析各个公司对前端工程师的招聘需求,一方面可以了解到前端各细分领域在企业的需求情况,调整自己对岗位和薪资的期待,另一方面可以获得各种前端技术在企业中的应用情况,调整自己的学习和面试准备方向。因篇幅所限…

外网如何进行端口映射?

外网端口映射是一种网络技术&#xff0c;通过将外部网络请求映射到内部网络的指定端口&#xff0c;实现对内部网络资源的远程访问。它在各种应用场景中发挥着重要作用&#xff0c;为企业和个人提供了便捷的远程连接方式。本文将介绍外网端口映射的原理和应用&#xff0c;并重点…

《辐射4》次世代版本上线

B社刚刚发布了《辐射4》次世代版本&#xff0c;带来了许多令人期待的更新内容。这次更新的亮点包括新创作俱乐部内容如飞地遗迹和盔甲武器捆绑包&#xff0c;包括X-02动力装甲、地狱火动力装甲、重型焚烧炉、特斯拉大炮等。同时&#xff0c;新增了万圣节工作坊内容&#xff0c;…

阿里巴巴瓴羊基于 Flink 实时计算的优化和实践

摘要&#xff1a;本⽂整理⾃阿里云智能集团技术专家王柳焮⽼师在 Flink Forward Asia 2023 中平台建设专场的分享。内容主要为以下四部分&#xff1a; 阿里巴巴瓴羊基于 Flink 实时计算的平台演进Flink 能力优化与建设基于 Flink 的最佳实践未来规划 1. 阿里巴巴瓴羊基于 Flink…

python使用opencv对图像的基本操作(2)

13.对多个像素点进行操作&#xff0c;使用数组切片方式访问 img[i,:] img[j,:] #将第j行的数值赋值给第i行 img[-2,:]或img[-2] #倒数第二行 img[:,-1] #最后一列 img[50:100,50:100] #50-100行&#xff0c;50-100列&#xff08;不包括第100行和第100列&#xff09; img[:100…

陪丨玩丨系丨统前后端开发流程,APP小程序H5前后端源码交付支持二开!多人语音,开黑,线上线下两套操作可在一个系统完成!

100%全部源码出售 官网源码APP源码 管理系统源码 终身免费售后 产品免费更新 产品更新频率高 让您时刻立足于行业前沿 软件开发流程步骤及其作用&#xff1a; 软件开发是一个复杂而系统的过程&#xff0c;涉及多个环节&#xff0c;以下是软件开发的主要流程步骤及其作用…

合合信息引领AI场景化革新,供应链金融智能化审核全面升级!

官.网地址&#xff1a;合合TextIn - 合合信息旗下OCR云服务产品 随着供给侧结构性改革的深入推进和产业结构的不断升级&#xff0c;金融机构在监管部门的指导下&#xff0c;积极拓展供应链金融业务&#xff0c;取得了显著成效。这一举措有效缓解了上下游中小企业的融资困难&a…

01 校园人脸识别项目

前言 主要是来自于 朋友的需求 项目概况 项目情况如下 包含一个后端服务, 一个前端服务, 一个微信小程序服务 主要的业务流程如下 整体模型设计如下 一个班级下面 N 个学生, 一个班级根据实际情况老师进行课程安排 上课的时候 学生去对应的课程实验室进行上课, 上课…

HPE Aruba Networking推出新一代Wi-Fi 7接入点 助力企业高效应对安全、AI与物联网挑战

HPE ArubaNetworking推出的全新Wi-Fi 7接入点&#xff0c;提供全面的AI就绪边缘IT解决方案&#xff0c;旨在为用户和物联网设备提供安全、高性能的连接服务&#xff0c;以实现数据的捕获和路由&#xff0c;从而满足AI训练和推理需求 休斯顿-2024年4月23日-慧与科技(NYSE: HPE)近…

2024 java easyexcel poi word模板填充数据,多个word合成一个word

先看效果 一、准备工作 1.word模版 2.文件路径 二、pom依赖 <!-- easyexcel --><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.1.7</version></dependency><depe…

STM32、GD32等驱动AMG8833热成像传感器源码分享

一、AMG8833介绍 1简介 AMG8833是一种红外热像传感器&#xff0c;也被称为热感传感器。它可以用来检测和测量物体的热辐射&#xff0c;并将其转换为数字图像。AMG8833传感器可以感知的热源范围为-20C到100C&#xff0c;并能提供8x8的像素分辨率。它通过I2C接口与微控制器或单…

DS进阶:AVL树和红黑树

一、AVL树 1.1 AVL树的概念 二叉搜索树&#xff08;BST&#xff09;虽可以缩短查找的效率&#xff0c;但如果数据有序或接近有序二叉搜索树将退化为单支树&#xff0c;查找元素相当于在顺序表中搜索元素&#xff0c;效率低下。因此&#xff0c;两位俄罗斯的数学家G.M.Adelson-…

为什么深度学习模型在 GPU 上运行得更快:CUDA 编程简介

如今,当我们谈论深度学习时,通常会将其实现与利用 GPU 来提高性能联系起来。 GPU(图形处理单元)最初设计用于加速图像、2D 和 3D 图形的渲染。然而,由于它们能够执行许多并行操作,因此它们的实用性超出了深度学习等应用程序。 GPU 在深度学习模型中的使用始于 2000 年代…

Unity读书系列《Unity高级编程:主程手记》——架构

文章目录 前言一、架构的意义1、承载力2、可扩展性3、易用性4、可伸缩性5、容错性以及错误的感知力 二、软件架构的思维方式二、构建Unity项目1、前端和后端架构之间2、培养架构设计思路3、Unity项目的分层设计 总结 前言 这篇文章是《Unity高级编程&#xff1a;主程手记》的第…

【源码】WHMCS 虚拟主机计费系统 易支付插件 USDT收款插件 支付宝 微信收款

【源码介绍】 WHMCS 虚拟主机计费系统 易支付插件 USDT收款插件 支付宝 微信收款 【源码说明】 WHMCS是一个国外的专业虚拟主机计费系统&#xff0c;功能很强大&#xff0c;这里分享一个7、8版本都可用的易支付 需要对接USDT可以谷歌下载易支付USDT插件&#xff0c;主机对接…

【R语言实战】——kNN和朴素贝叶斯方法实战

&#x1f349;CSDN小墨&晓末:https://blog.csdn.net/jd1813346972 个人介绍: 研一&#xff5c;统计学&#xff5c;干货分享          擅长Python、Matlab、R等主流编程软件          累计十余项国家级比赛奖项&#xff0c;参与研究经费10w、40w级横向 文…

wifi可以连接但是上不了网该怎么解决?

上网的过程中&#xff0c;我们有时候会遇到wifi可以连接但是上不了网的情况&#xff0c;打开电脑浏览器&#xff0c;显示域名解析错误。遇到这种情况&#xff0c;一般说明IP与站点的解析过程出现了错误。 在网络中的主机都是IP地址来标识的&#xff0c;如果在浏览器输入此IP地…

美国言语听力学会(ASHA)关于非处方 (OTC) 助听器的媒体声明(翻译稿)

美国国会于 2021 年 4 月 13 日批准美国听力学会积极提供建议&#xff0c;并一直积极参与制定FDA关于非处方助听器销售的拟议法规。根据2017年通过的立法授权。学院积极参与帮助塑造授权立法&#xff0c;并就即将出台的条例分享了建议。 根据美国卫生与公众服务部NIH / NIDCD的…