arm接口技术二--指令集与异常处理

arm汇编工程

注意事项:

  • 魔法棒 - listing的两个选项勾上
  • Linker --- Text Start: 指定指针起始地址0x0
  • 品按钮 -- Folder选择arm-none-eabi编译器
  • assemble编译报错:KEIL中错误FCARM - Output Name not specified, please check 'Options for Target - Utilit解决方法https://blog.csdn.net/weixin_61944814/article/details/132768744

arm指令

指令格式:<opcode> {<cond>} {s} <Rd>, <Rn> {<,operand2>}

  • 大括号参数,代表不是必须的
  • opcode:指令注记符。就是见名知意,见到这个指令就知道是什么意思
  • cond:执行指令的条件。ne/eq/gt/lt/ge/le,与shell中的比较符号一模一样
  • s:指令中带s,则表示指令的执行结果影响CPSR寄存器的NZCV位,否则不带这个s就好了
  • Rd:指令执行结果的目标寄存器,可以是R0--R15
  • Rn、operand2:表示指令运行需要的操作数。指令执行可以有两个操作数,第一操作数Rn必须是一个寄存器,第二操作数可以是立即数、寄存器、寄存器移位

算术右移,逻辑右移

  • 算术右移符号位要一起移动,并且在左边补上符号位,也就是如果符号位是1就补1符号位是0就补0
  • 算术左移和算术右移主要用来进行有符号数的倍增、减半;
  • 逻辑左移和逻辑右移主要用来进行无符号数的倍增、减半。

arm指令详细介绍,看体系课22周1.3 ARM指令集.pdf

arm伪指令

arm伪指令不是arm的指令,而是编译器为了方便用户使用编写的指令。详细看体系课22周1.4.pdf

ATPCS标准

arm thumb procedure call standard。规定了C/C++程序,函数参数传递、返回值返回、局部变量寄存器的使用规则。

  • atpcs规定,传递函数参数时,如果参数不超过4个,用r0-r3寄存器传递,超过4个则用参数栈。由此可以看出,设计函数时,参数超过4个,效率就会下降,因为参数栈要读写内存。

汇编与C混合编程

汇编中调用C语言

注意:

  • 汇编调用c语言前,必须制定栈指针寄存器SP
  • c语言函数名,在汇编中就是标签名,所以C语言并不一定从main开始执行,main对汇编来说只是一个标签名。

汇编调用C模板

s 复制代码
.global _start
	
_start:
    @0x40000000,0x4000ffff
    mov r0,#12
	mov r1,#13
	ldr sp,=0x40001ff0 @调用C语言前,必须指定栈指针寄存器。
    @操作系统中C语言程序执行不用指定,是因为系统在执行的时候创建了一个进程,创建进程的时候自动帮我们设置了sp应该在什么位置。
    @在没有系统的时候,就必须有一套汇编,来指定sp,才能执行C语言

	bl add

stop:
    b stop

int add(int a, int b) @add,对汇编来说只是一个标签,add与main无区别
{
	int c;
	c = a + b;
	return c;
}

以上代码仿真时,必须设置keil魔法棒 --> CC --> optimization为no。避免编译器优化调c函数参数、局部变量的入栈出栈过程。

c调用汇编

c语言调用汇编,使用asm

c 复制代码
/*
asm(
    "指令1\n"
    "指令2\n"
    ...
    :"输出列表"
    :"输入列表"
    :"修改列表"
)
*/

int add(int a, int b)
{
    int c;

    /* 调用汇编指令实现两数相加
     * 规定:汇编命令中引用的c变量,按输出列表、输入列表从0开始编号。
     * 指令中`%num`引用变量
     * 修饰符:=修饰表示操作符只写,+修饰表示可读写,&修饰表示只能作为输出。
    */
    asm(
        "add r0,%1,%2\n"
        "mov %0,r0\n"
        :"=r"(c)
        :"r"(a),"r"(b)
        :"r0"
    );

    return c;
}

gcc优化与volatile关键字

优化思想:如果我们在前面已经将这个变量所对应的内存数据读到寄存器中,而当我们再次需要读这个变量所对应内存的数据的时候,编译器认为前面我们已经在寄存器中,存放过值了,为了提高效率,它直接会使用上一次寄存器中的值,而不重新从内存中读值。

gcc编译的时候有三级优化,O1/O2/O3

优化带来的问题:

  • 如果内存中的值已经被其他的执行单元(中断处理函数或其他线程)做了更改,而优化之后的代码每次从寄存器中读值,就会带来寄存器中的值和内存中的值不一致的问题
  • 不优化,代码效率又会降低。

为了解决这个问题,使用volatile关键字(易改变的)修饰一个变量,防止编译器优化(本质),告诉编译器每次在使用这个变量的时候,必须从变量所在的内存中重新读值

c 复制代码
volatile int g_x = 10;
volatile int g_y = 11;

int add()
{
    int c;
    c = g_x + g_y;

    if (g_x < g_y) { // 执行此句的时候,由于g_x,g_y是全局变量,由于中断处理函数或其他线程,内存中的值可能已经被修改,导致寄存器和内存不一致。
        c++;
    }

    return c;
}

体系课22周1.4.pdf

ARM核异常处理

体系课22周1.5.pdf

异常是处理器核在执行程序指令的过程中突然遇到了异常的事情,这些事件包括硬件中断、指令执行错误、用户程序请求服务、内存访问异常、取指令异常等,几乎每种处理器都支持特定的异常处理,中断也是异常的一种

前面讲过,FIQ比IRQ快,除了FIQ有独立的寄存器不需要压栈出栈的过程,还有:

  • FIQ在异常处理向量表的末尾,FIQ的异常处理函数可以紧接着FIQ向量表,没有跳转的过程
  • FIQ优先级比IRQ高。

异常处理过程:

  1. 执行一个异常指令ARM核自动完成的事务:
    • 首先,ARM会自动将CPSR的值拷贝到SPSR
    • 修改CPSR的值:设置当前异常模式、设置ARM工作状态、禁止中断
    • 将PC拷贝到lr。
    • 将PC设置的值跳转到异常向量表对应的位置(异常向量表的位置,由arm核的协处理确定,不同处理器异常向量表的位置不同,需要开发人员设置指令)
  2. 异常向量表跳转到对应的异常处理函数
  3. 异常处理函数保存通用寄存器的值、处理异常、恢复寄存器的值、返回。

异常处理工程师需要编写向量表及异常处理函数,并指定向量表位置。

相关推荐
Q行天下2 小时前
x86的Docker环境下载ARM版容器镜像
arm开发·docker·容器
深圳九鼎创展14 小时前
RK3588九鼎创展方案在Arm集群服务器的项目中的应用分析
运维·服务器·arm开发·人工智能·嵌入式硬件·物联网·iot
maosql14 小时前
arm和riscv系统调用对比(笔记)
c语言·arm开发·笔记·系统调用·上下文切换
亦诗亦诗1 天前
centos arm docker 安装nginx
arm开发·docker·centos
望获linux1 天前
Linux网络协议栈的实现
linux·服务器·arm开发·网络协议·操作系统·嵌入式操作系统
极客小张1 天前
基于OpenCV与MQTT的停车场车牌识别系统:结合SQLite和Flask的设计流程
arm开发·人工智能·单片机·opencv·物联网·flask·毕业设计
钡铼技术物联网关3 天前
沉浸式体验:ARM 工控机携手 HT for Web 打造智能建筑监控
linux·网络·arm开发·物联网·边缘计算
深圳九鼎创展4 天前
RK3576芯片在智能家居里中型智慧屏产品的应用方案分析
arm开发·人工智能·驱动开发·嵌入式硬件·物联网·智能家居·iot
问就是想睡觉4 天前
就服务器而言,ARM架构与X86架构有什么区别?各自的优势在哪里?
服务器·arm开发·架构
秋野酱7 天前
ARM32开发——DMA
arm开发·驱动开发·嵌入式硬件