美文网首页
嵌入式Lab2:Arm指令

嵌入式Lab2:Arm指令

作者: 小浪明 | 来源:发表于2017-03-23 11:23 被阅读0次

这个实验的目的是深入理解ARM和Thumb指令的特点,了解编译选项对代码产生的影响。

配合课程

ARM处理器

实验目的

  • 深入理解ARM指令和Thumb指令的区别和编译选项;
  • 深入理解某些特殊的ARM指令,理解如何编写C代码来得到这些指令;
  • 深入理解ARM的BL指令和C函数的堆栈保护;
  • 深入理解如何实现C和汇编函数的互相调用。

实验器材

硬件

  • 实验主板一块(raspberryPi);
  • 5V/1A电源一个;
  • microUSB线一根;

软件

  1. 交叉编译软件(arm-linux-gcc)

操作方法和实验步骤

  1. 实验设备连接示意图

    连接示意图
  2. 同一个程序,用arm指令和thumb指令编译,得到的结果有什么不同
    代码如下:
    //hello.c
    #include <stdio.h>

    int main(){
        printf("Hello World\n");
        return 0;
    }
    

用两种不同的方式编译:


编译程序 hello.c

得到的结果:


指令对比
区别:
  • thumb的指令地址一次只增加2,即每条指令只占16位。相对的,arm每个地址增加4,每条指令占位32位。
  • 在 Thumb 状态下,单寄存器加载和存储指令只能访问寄存器 R0~R7。
  1. 对于 ARM 指令,能否产生条件执行的指令
    可以的,C语言代码:
    //if.c
    #include <stdio.h>
    int main(){
    int i;
    i = 1;
    if(i == 1)i++;
    else i--;
    return 0;
    }
    生成的ARM指令代码<main>函数部分:

    if_arm.S
    其中可以看到bne指令。
  2. 设计 C 的代码场景,观察是否产生了寄存器移位寻址
    C语言代码如下:
    //shift.c
    #include <stdio.h>
    int lShift(int *i, int j){
    return i[j << 2];
    }
    int main(){
    int i[256];
    int j = 18;
    lShift(i, j);
    return 0;
    }
    生成的ARM指令代码主要部分(可以看到ldr指令):

    shift_ARM.S
  3. 设计 C 的代码场景,观察一个复杂的 32 位数是如何装载到寄存器的
    C语言代码如下:
    //load32.c
    #include <stdio.h>
    int main(){
    double i;
    long j;
    i = 204800001.1;
    j = 2048000011;
    return 0;
    }
    生成的ARM代码如下:

    Load32_ARM.S
    可以看出,ARM是将32位数放到堆栈中,然后依次取出放入到寄存器当中。
  4. 写一个 C 的多重函数调用的程序,观察和分析: a. 调用时的返回地址在哪里? b. 传入的参数在哪里? c. 本地变量的堆栈分配是如何做的? d. 寄存器是 caller 保存还是 callee 保存?是全体保存还是部分保存?
    C代码如下:
    //mulFunc.c
    #include <stdio.h>
    int fun1(int i){
    int j = i + 1;
    return j;
    }

    int fun2(int i, int j){
       int x = i + j;
       x = fun1(x);
       return x;
    }
    
    int main(){
       int a = 1, b = 2;
       a = fun2(a, b);
       return 0;
    }
    

得到的ARM汇编代码如下:


mulFunc_ARM.S

a.调用时的返回地址放在lr寄存器当中。
b.传递的参数放在了堆栈中。
c.被调函数首先将传递过来的堆栈指针 sp 拷贝到指针 fp 中,然后将本地变量从 r0 开始存放,当存放到 r2 之后,其余的变量将会通过 r3 存放到堆栈中去,最后将 fp 中保存的指针拷贝回 sp,恢复被调前的堆栈情况,然后返回。
d.参数保存在caller,地址保存在callee,部分保存的

  1. MLA 是带累加的乘法,尝试要如何写 C 的表达式能编译得到 MLA 指令
    C代码如下:
    //mla.c
    #include <stdio.h>
    int mla(int i, int j, int k){
    return i * j + k;
    }

    int main(){
        int i = 2, j = 20, k = 30;
        mla(i, j, k);
        return 0;
    }
    

编译得到的ARM汇编文件(编译时用-O1优化编译)


mla_ARM.S

可以看到mla指令。

  1. BIC是对某一个比特清零的指令,尝试要如何写 C 的表达式能编译得到 BIC 指令
    C代码如下:
    //bic.c
    #include <stdio.h>
    int bic(int i, int j){
    return i&~j;
    }

    int main(){
        int i = 0x8828;
        int j = 20484;
        bic(i, j);
        return 0;
    }
    

汇编得到的ARM指令(要使用-O1编译优化)


bic_ARM.S

可以很明显的看到bic指令。

  1. 编写一个汇编函数,接受一个整数和一个指针做为输入,指针所指应为一个字符串,该汇编函数调用C语言的 printf()函数输出这个字符串的前n个字符,n即为那个整数。在C语言写的main()函数中调用并传递参数给这个汇编函数来得到输出。
    汇编函数代码(cutprint.S):
    .global cutprint
    cutprint:
    push {R5, R6, R7, lr}
    MOV R5, R0
    MOV R6, R1
    MOV R7, #0
    CMP R7, R6
    BGE exit
    begin:
    LDR R0, =char
    LDR R1, [R5, R7]
    CMP R1, #0
    BEQ exit
    bl printf
    ADD R7, R7, #1
    CMP R7, R6
    BLT begin
    exit:
    LDR R0, =newline
    bl printf
    MOV R0, R7
    pop {R5, R6, R7, pc}

    .data
        char: .asciz "%c"
        newline: .asciz "\n"
    

C语言主函数代码:
//cutprint.c
#include <stdio.h>

    extern int cutprint(char *, int);
    int main(){
        int a;
        char s[100];
        scanf("%s %d", s, &a);
        a = cutprint(s, a);
        printf("Print %d character.\n", a);
        return 0;
    }

编译后在树莓派的执行结果:


测试1
测试2
测试3
  1. 自选扩展内容1:编写测试程序,测试ARM指令和Thumb指令的执行效率
    测试代码:
    //speed.c
    #include <stdio.h>

       int fibo(int n){
           if(n <= 1) return 1;
           return fibo(n - 2) + fibo(n - 1);
       }
    
       int main(){
           int a;
           scanf("%d", &a);
           printf("%d\n", fibo(a));
           return 0;
       }
    

在编译时,在普通编译之外,还有-O2和-O3编译优化的编译。


编译选项
测试结果

从测试结果中可以看到,arm的执行速度要比thumb的执行速度快一些。

  1. 自选扩展内容2:编写测试程序,测试使用带条件的ARM指令和不使用时的执行效率。
    含带条件的ARM指令(useif.S):
    .global add
    add:
    CMP R0, #0
    ADDNE R0, R0, R1
    MOV pc, lr
    不含的ARM指令(noif.S):

    .global add
    add:
       CMP R0, #0
       ADD R0, R0, R1
       MOV pc, lr
    

测试代码:
//test.c
#include <stdio.h>

        extern int add(int, int);

        int main(){
            int a, b, i, times;
            scanf("%d %d %d", &a, &b, &times);
            for (i=0; i<times; i++)
                a = add(a, b);
            printf("%d\n", a);
            return 0;
        }

编译选项:


编译过程
执行结果

通过比较执行结果可以发现两个程序执行的时间基本上没有什么差别。

讨论与心得

本次实验主要是对代码的编写和对ARM指令的了解,没有涉及过多的硬件上的内容。需要的硬件内容(交叉编译环境以及连接和文件传递)在上次实验已经设计,所以从总体上来说比上一次实验简单。
同时本次使用和让我了解到了ARM指令的一些性质,以及ARM指令和Thumb指令的区别。

相关文章

  • 嵌入式Lab2:Arm指令

    这个实验的目的是深入理解ARM和Thumb指令的特点,了解编译选项对代码产生的影响。 配合课程 ARM处理器 实验...

  • 6.ARM 体系结构要点总结

    ARM 是RISC架构 常用ARM汇编指令只有二三十条 ARM是低功耗CPU ARM 的架构非常适合单片机、嵌入式...

  • 读书笔记| (一)ARM9 嵌入式学习:基础篇

    读书笔记| ARM9 嵌入式学习 1、ARM处理器简介 基于RISC,采用Load/Store 数据存取指令执行时...

  • 嵌入式学习之一 (arm架构优化)

    @[TOC](嵌入式学习之一 (arm架构优化)) arm架构32位优化 嵌入式设备(即arm架构的板子)在编译时...

  • 嵌入式系统简介

    嵌入式系统的发展历史:SCM→MCU→SoC基于ARM处理器的嵌入式Linux系统 ARM架构支持32位的ARM指...

  • 最系统的ARM嵌入式资料

    最系统的ARM嵌入式资料 最新整理史上最齐全嵌入式资料,嵌入式的ARM体系结构的学习,下面给大家分享嵌入式从入门到...

  • ARM 汇编指令学习:[2]ARM指令集

    ARM 汇编指令学习:[2]ARM指令集 一、跳转指令 1、 B(跳转指令)及BL(带返回跳转指令) 其中: L决...

  • iOS指令集

    ARM处理器指令集 32位ARM指令集:armv6、armv7、armv7s等64位ARM指令集:arm64、ar...

  • iOS CPU指令集

    ARM处理器指令集ARM指令集是指计算机ARM操作指令系统。 armv6、armv7、armv7s、arm64、a...

  • arm64指令集

    arm64 指令个人记录 ARM指令集是指计算机ARM操作指令系统。在ARM中有两种方式可以实现程序的跳转:一种是...

网友评论

      本文标题:嵌入式Lab2:Arm指令

      本文链接:https://www.haomeiwen.com/subject/yokznttx.html