STM32
直播中

477289

12年用户 448经验值
私信 关注
[问答]

与ARM处理器相关知识总结

RISC和CISC指令集有何区别呢?
ARM处理器异常的处理过程是怎样的?

回帖(2)

张婷

2021-11-30 14:59:40
1、ARM相关的概念

1.4 ARM相关的概念

内核:ARM公司授权给芯片设计厂家
内核=运算器 + 控制器 + 存储器(寄存器)
存储器的分类
1.主存储器即内存。程序中待处理的数据和处理的结果都存储在内存中。
2.外存储器是用来长期保存数据的大容量存储器。
3.寄存器是CPU内部的高速存储器,速度快,数目少。








CPU(Central Processing Unit),是一台计算机的运算核心和控制核心。CPU由运算器、控制器和寄存器及实现它们之间联系的数据、控制及状态的总线构成。所谓的计算机的可编程性主要是指对CPU的编程。
处理器
SOC(CPU) 内核 架构
麒麟9000 cortex-A774 ARM-v8
cortex-a55
4 ARM-v8
骁龙888 cortex-x1 1 ARM-v8
cortex-A78
3 ARM-v8
cortex-a554 ARM-v8
Exynos4412 cortex-a9
4 ARM-v7
stm32MP157 cortex-A72 ARM-v7
cortex-M4
1

SOC(System Of Chip):片上系统
芯片的内部集成了内核和很多的外设,这样的芯片可以统称SOC。
## 1.5 ARM产品线
```c
ARM体系  ARMv4          ARMv5      ARMv6   ARMv7
ARM CPU  arm7  arm9/arm10   arm11        arm_cortex_a8




cortex-A: 高端处理器,可以运行linux系统,消费类电子产品(
                手机、平板等)
cortex-R:主要针对的实时处理,汽车电子,摄像机,照相机
cortex-M:低端电子产品,运行裸机程序,物联网设备开发,stm32/stm8(意法半导体公司),可以运行实时的操作系统(FreeRTOS LiteOS AliOS ucos II)
arm内核命令的规范:
早期:ARM7 ARM9 ARM10 ARM11
从ARM11之后ARM内核的命名调整为Cortex-A
X86——32位架构
X64——64位架构
ARMv7——32位架构
ARMv8——64位架构
ARM-v8架构是arm64的架构及支持arm64位的汇编指令
ARM-v8架构向下兼容ARM-v7的架构
ARM-v7架构是arm32的架构及支持arm32位的汇编指令
架构:汇编指令集的集合
ARM汇编指令集:一些汇编指令的集合
ARM汇编指令:具有某个特殊功能的指令
指令本身是通过CMOS设计的硬件电路图
不同ARM体系采用的是不同的指令集
哈佛结构是数据和指令分开存储并行
冯诺依曼(普林斯顿)结构是混合存储的
1.6 RISC和CISC指令集的区别

RISC: 精简指令集 (ARM,8051,MIPS,PowerPC)
精简指令集是在复杂指令集中提取一些比较简单和使用频率较高的指令作为精简指令,精简指令集的指令的周期和指令的宽度固定。ARM指令集是精简指令集
指令周期:执行1一条指令所需要的时间
指令宽度:1条指令所占用的内存空间
CISC: 复杂指令集(电脑CPU)
复杂指令集更加注重指令的功能和处理能力,复杂指令集的周期和宽度不固定。X86指令集是复杂指令集
从汇编代码的角度分析复杂指令集合精简指令集的区别?

复杂指令集:
        // 作用:将elf文件生成反汇编文件
        // objdump是GNU工具集中的一个命令
        objdump -D a.out > 1.dis
内存地址   机器码                 反汇编指令
8048154:   2f                      das
8048155:   6c                      in***   (%dx),%es:(%edi)
8048156:   69 62 2f 6c 64 2d 6c    imul   $0x6c2d646c,0x2f(%edx)
804815d:   69 6e 75 78 2e 73 6f    imul   $0x6f732e78,0x75(%esi)
8048164:   2e 32 00                xor    %cs:(%eax),%al
精简指令集:
        交叉编译工具链:将源文件编译生成支持ARM架构的可执行程序
        arm-none-linux-gnueabi-gcc 1.c
        arm-none-linux-gnueabi-objdump -D a.out > 1.dis
   内存地址   机器码       反汇编指令
        8134:   62696c2f    r***vs   r6, r9, #12032  ; 0x2f00
        8138:   2d646c2f    stclcs  12, cr6, [r4, #-188]!   ; 0xffff
        813c:   756e696c    strbvc  r6, [lr, #-2412]!   ; 0x96c
        8140:   6f732e78    svcvs   0x00732e78
反汇编分析:







拓展:
ELF与BIN文件区别

gcc 编译出来的是elf文件。通常gcc –o test test.c,生成的test文件就是ELF格式的,在linuxshell下输入 ./test就可以执行。
bin 文件是经过压缩的可执行文件,去掉elf格式的东西。是直接的内存映像的表示。在系统没有加载操作系统的时候可以执行。
elf(executable  and   link   format)文件里面包含了符号表,汇编等。
bin文件是将elf文件中的代码段,数据段,还有一些自定义的段抽取出来做成的一个内存的镜像。
在Embedded中,如果上电开始运行,没有OS系统,如果将ELF格式的文件烧写进去,包含一些ELF格式的东西,arm运行碰到这些指令,就会导致失败,如果用arm-softfloat-linux-gnu-objcopy生成纯粹的汇编 bin文件,程序就可以一步一步运行。
两种文件都可以运行
机器最终只认BIN,之所以有ELF格式是在有操作系统时,操作系统会根据ELF解析出代码、数据等等,最终仍是以BIN运行。由于elf文件的信息比较全,所以可以用来以单步跟踪的方式运行。关键是看loader。
嵌入式开发的时候,我们的编译一个*.S文件,并最终生成bin文件,编译命令大致如下:
CC=arm-softfloat-linux-gnu-gcc
LD=arm-softfloat-linux-gnu-ld
OBJCOPY=arm-softfloat-linux-gnu-objcopy
$(CC) -g$(CFLAG) -c boot.S     #先将boot.S文件生成boot.o
$(LD) -g-Bstatic -T$(LDFILE) -Ttext 0x12345600 boot.o --start-group -Map boot.map -oboot.elf    #再将boot.o生成boot.elf,boot.elf通常就是可执行文件,类似于gcc-o test test.c 中的test文件,在LinuxShell下输入./test就可以执行。
$(OBJCOPY)-O binary boot.elf boot.bin    #接着将boot.elf->boot.bin,这样可以缩小代码尺寸。
运行arm-softfloat-linux-gnu-objdump-h boot.elf 可以查看该文件的信息,但是如果变成
arm-softfloat-linux-gnu-objdump-h boot.bin会提示错误,为了看boot.bin文件信息,输入:
arm-softfloat-linux-gnu-objdump-h -b binary -m arm boot.bin就可以了。哈哈我也是现学了一招,其中参数-h可以被替换成为-D, -S,-s等等,请用arm-softfloat-linux-gnu-objdump--help查看器中表示的意义。


1.7 ARM数据的约定

ARM-V7约定:
        char :8bits
        half word:16bits
        word:32bits
        double word :64bits(cortex-a系列才支持)


ARM-V8约定:
        char :8bits
        half word:16bits
        word:32bits
        double word :64bits(cortex-a系列才支持)
        quad word:128bits(ARM-V8架构才支持)


2、ARM工作模式和寄存器

2.1 ARM处理器的工作模式


        |--特权模式--|--异常模式--|-快速中断模式(FIQ)
        |            |            |-普通中断模式(IRQ)
        |            |            |-管理模式(SVC):当复位或软中断指令执行时进入此种模式
        |            |            |-未定义指令异常模式(Und)
工  |            |            |-中止异常模式(Abort):当存取异常时会进入此模式
作  |            |            |-安全监控模式(Monitor)
  --|            |                (cortex-A系列内核支持,所以会比armv系列多3个寄存器)
模  |            |           
式  |            |--非异常模式 -->System
        |                                                        (属于用户模式的特权模式)
        |
        |--非特权模式-->user -> 执行用户空间的代码
普通中断模式(IRQ):当发生一个外部中断的事件,进入到IRQ模式,执行IRQ模式下对应的代码。
外部中断事件:按键,定时器等
2.2 ARM处理器的寄存器组织(重点)

ARM内核 = 运算器 + 控制器 + 存储器
存储器 = cache + 寄存器
存储器:flash 内存(Memory)
cache(i-cache,d-cache),
寄存器(Register)

寄存器  cache  内存  flash
        快 <-------------------慢  读写速度
        小 -------------------->大  容量
        贵 <-------------------贱  价格
        寄存器/cache/内存 : 掉电数据丢失
        Flash:掉电不丢失





2.3 特殊寄存器

r13 -> 别名sp (the stack pointer)
  栈指针寄存器,存放栈的地址
r14 -> 别名lr (the link Register)
        链接寄存器, 保存的是返回地址
r15 -> 别名pc (the program counter)
        程序计数寄存器, 保存当前取指指令的地址
cpsr -> (current program status register)
        当前程序状态寄存器:存储的程序运行状态
        每条指令执行的状态
        处理器的工作模式
spsr -> (saved program status register)
        保存程序状态寄存器:专门用于保存cpsr寄存器
2.4 CPSR寄存器的详解






2.5 ARM指令集 和 Thumb指令集


ARM指令集:
        一条指令占32位宽度,
        ARM指令集的功能性更强,指令密度较低
        PC取指每次自动加4


Thumb指令集:
        一条指令占16位宽度
        Thumb指令集的密度大。
        PC取指每次自动加2


2.6 PC寄存器详解


PC寄存器中存放的是当前取指指令的地址
ARM指令集:
每条ARM指令占32bits的宽度
所有的指令必须4字节对齐,
pc寄存器中的值[31:2]位有效,[1:0]位未定义(无效)
0000 0000
0000 0100
0000 1000
0000 1100
pc = 0x3    @ 由于第[1:0]位未定义,永远当0处理
                        @ 因此从0x00地址开始进行取指
ARM指令集:
每条ARM指令占16bits的宽度
所有的指令必须2字节对齐,
pc寄存器中的值[31:1]位有效,[0]位未定义(无效)。
0000 0000
0000 0010
0000 0100
0000 0110       
      

2.7 指令的三级流水线


取指器 :根据PC寄存器中的值进行取指
译码器 :翻译机器码,给到对应的执行器
执行器 :执行指令,并将结果返回到寄存器
取指器,译码器,执行器是三个独立的硬件,
都是单周期的器件,工作互不影响。
    指令1 指令2 指令3 指令4 指令5 指令6
1        取指
2   译码  取指
3        执行  译码  取指
4                  执行  译码  取指
5                                执行  译码  取指
6                                          执行  译码  取指
7                                                        执行  译码  取指
8                                                                  执行  译码  取指
一条指令的执行时间 : 8 / 6 = 1
跳转和中断都会打断指令的流水线。
        arm7 三级流水线
        arm9 五级流水线
        arm11 七级流水线
        cortex-A9 13级流水线 (未知)
        cortex-a53 8级流水线 (?)
举报

杨逊咏

2021-11-30 14:59:44
3、汇编

3.1 汇编代码中的主要符号



  • 指令 : 一条指令被编译生成32位机器码执行时,完成特定的功能
  • 伪指令
    本身不是一条指令,编译器会将其编译生成多条指令,完成一条伪指令的功能。
  • 伪操作
    不占用任何的内存空间(代码段),指导编译器对代码进行编译的。
    .xxx
    xxx:


.text  //将定义符开始的代码编译到代码段(只读)
.data  //将定义符开始的代码编译到数据段(可读写)
.end //文件结束
.equ //GPG3CON,0xE03001C0 定义宏(即用GPG3CON代替0xE03001C0)
.byte  //定义变量1字节
          .byte 0x11,'a',0定义字节数组
.word //定义word变量(4字节,32位机)
          .word 0x12344578, 0x33445566
.string //定义字符串 .string "abcd"
.global _start //声明_start为全局符号



  • 注释
    单行注释: @
    多行注释: /* */
    .if 0 .else .endif

3.2 最基本的指令语法格式

(opcode){cond}{S} Rd, Rn, oprand2
opode : 指令的名字
{cond} : 条件码
{S} : 状态标志位
指令名后边加S,指令的执行结果影响CPSR寄存器的NZCV位,否则不影响CPSR的NZCV位
movs adds subs

Rd : 目标寄存器
Rn : 第一个操作寄存器
oprand2 : 第二个操作数
        1> 可以是一个普通的寄存器
        2> 可以是一个立即数或有效数
        3> 可以是一个经过移位操作的寄存器
一条汇编指令占一行,指令名和目标寄存器之间使用空格隔开;寄存器之间使用逗号隔开;
(opcode){cond}{S}连到一起写。
汇编中,指令不区分大小写,举例如下
MOV R0, #0xFF ==== mov r0, #0xff
拓展:条件码





3.3 指令分类

3.3.1 数据操作指令

3.3.1.1 数据搬移指令 mov mvn ldr

@== mov/mvn{cond}{s} Rd, oprand2==

        mov r0, #0xff  @ r0 = 0xff
        mov r1, r0     @ r1 = r0
        mvn r2, #0xff  @ r2 = ~0xff
        mvn r3, r2     @ r3 = ~r2
        @ mov pc, #0x3 错 //pc寄存器的0,1两位无效
        mov r0, #0xFF000000
        @ mov r1, #0xFFF  错
        @ mov r2, #0xFFFF 错
        mov r3, #0xFFFFFF

拓展:立即数和有效数





3.3.1.2 移位操作指令 lsl lsr asr ror


@ 语法格式:
        @ opcode{cond}{S} Rd, Rn, Oprand2
        mov r0, #0xFF
        @ lsl 逻辑左移 :高位移出,低位补0
        lsl r1, r0, #0x4   @ r1 = r0 << 4
        @ lsr 逻辑右移 :低位移出,高位补0
        lsr r2, r0, #0x4   @ r2 = r0 >> 4
        @ asr 算数右移 :低位移出,高位符号位
        @ ror 循环右移 :低位移出补到高位
        mov r3, r0, lsl #4 √
        mov r4, #(0xFF << 4) √
        mov r4,0xFF,lsl #4 ×


3.3.1.3算数运算指令 add adc sub ***c mul
@ 语法格式:
@ opcode{cond}{S} Rd, Rn, Oprand2
@ add/sub: 普通的加减法指令
@ adc/***c: 待进位借位的加减法指令


        @ 案例:
        /*
        实现两个64位数据的加法运算
        第1个64位数,r0表示低32位,r1表示高32位
        第2个64位数,r2表示低32位,r3表示高32位
        结果,r4存低32位,r5存高32位
        */
        mov r0, #0xFFFFFFFE
        mov r1, #4
        mov r2, #5
        mov r3, #3
        @ s:指令的执行结果影响cpsr的nzcv位
        adds r4, r0, r2   @ r4 = r0 + r2 = 0x3
        @ 判断C位的值
        adc r5, r1, r3   @ r5 = r1 + r3 + C =0x8
        @ 案例:
        /*
        实现两个64位数据的减法运算
        第1个64位数,r0表示低32位,r1表示高32位
        第2个64位数,r2表示低32位,r3表示高32位
        结果,r4存低32位,r5存高32位
        */
        mov r0, #4
        mov r1, #8
        mov r2, #5
        mov r3, #3
        subs r4, r0, r2 @ r4 = r0 - r2
        ***c r5, r1, r3  @ r5 = r1 - R3 - !C
        @ mul
        mov r7, #3
        mov r8, #4
        mul r6, r7, r8  r6=r7*r8 √
        mul r6, r7,#4   ×
        @ mul指令的第二个操作数只能是一个寄存器


3.3.1.4位运算指令 and(与) orr(或) eor(异或) bic(位清除)
@ 语法格式:
@ opcode{cond}{S} Rd, Rn, Oprand2


        ldr r0, =0x87654321
        @ 1> 将R0寄存器中的值,第[3]位置1,保证其他位不变
        @ r0 = r0 | 0x8;
        方法1:orr r0, r0, #0x8  
        方法2:@ orr r0, r0, #(0x1 << 3)
        方法3:@ mov r1, #1
              @ orr r0, r0, r1, lsl #3


        @ 2> 将R0寄存器中的值,第[10]位清0,保证其他位不变
        @ r0 = r0 & (~(0x1 << 10));
        方法1:and r0, r0, #0xFFFFFBFF
        方法2:and r0, r0, #(~0x400)
        方法3:and r0, r0, #(~(0x1 << 10))
       
        @ 3> 将R0寄存器中的值,第[7:4]位置1,保证其他位不变
        @ r0 = r0 | (0xF << 4);
        orr r0, r0, #(0xF << 4)
       
        @ 4> 将R0寄存器中的值,第[23:20]位清0,保证其他位不变
        @ r0 = r0 & (~(0xF << 20))
        and r0, r0, #(~(0xF << 20))
       
        @ 5> 将R0寄存器中的值,第[15:10]位设置为0b101010,保证其他位不变
                1) 全部清0  
                r0 = r0 & (~(0x3F << 10))
                and r0, r0, #(~(0x3F << 10))
                2) 把相应的位置1
                r0 = r0 | (0x3F << 10)
                orr r0, r0, #(0x2A << 10)
       
        @ 6> 将R0寄存器中的值,第[20]位取反,保证其他位不变
        @ r0 = r0 ^ (0x1 << 20)
        eor r0, r0, #(0x1 << 20)
       
        @ bic 位清除指令
        @ 将R0寄存器中的值,第[23:20]位清0,保证其他位不变
        @ 本质: r0 = r0 & (~(0xF << 20))
        bic r0, r0, #(0xF << 20)


3.3.1.5比较指令 cmp
@ 语法格式:
@ cmp{cond} Rn, oprand2
@ 注:1. 没有目标寄存器
@ 2. 执行的执行结果影响的是cpsr的nzcv位,不需要加s
@ 3. 本质:减法运算
@ 4. 比较指令和条件码结合使用


@ 案例:if (r0>r1) r0 = r0 - r1
@        else r1 = r1 - r0
        mov r0, #15
        mov r1, #9
        cmp r0, r1
        subhi r0, r0, r1
        subcc r1, r1, r0  //无进位无符号小于


3.3.2 跳转指令 b/bl
@ 语法格式:
@ b/bl{cond} Label(函数名)
@ 汇编中的函数 Label: 指令
@ b : 不保存返回地址到lr寄存器,有去无回
@ bl : 保存返回地址到lr寄存器,有去有回,C语言函数的调用


        @ 1. 调用汇编的函数
        .if 0
        mov r0, #1
        mov r1, #2
        b add_func
        add_func:
                add r2, r0, r1
        .endif


@ 2. 实现一个0-100相加,结果保存到r0
        mov r1, #0
        func:
        cmp r1, #100
        bhi loop  //无符号大于
        add r0, r0, r1
        add r1, r1, #1
        b func
        mov r6,#3
        bl add_100  @ 保存返回地址到lr中
        b loop
        add_100:
                mov r1, #0
                stop:
                cmp r1, #101
                addcc r0, r0, r1
                addcc r1, r1, #1
                bcc stop
                mov pc, lr  @ 必须手动返回
        loop:
                b loop


@ 案例: 求两个数的最大公约数
        mov r0, #25
        mov r1, #10
        mm:
                cmp r0, r1
                beq loop
                subhi r0, r0, r1
                subcc r1, r1, r0
                bne mm
        loop:
                b loop





3.3.3 内存读写的操作指令 load/store


        2> 多寄存器操作指令
        3> 栈操作指令
        load/store指令
单寄存器的操作指令 ldr str
@ r : Register
@ 语法格式:
ldr{cond} Rn, [Rm] //从Rm的地址中下载数据到Rn
str{cond} Rn, [Rm] //把Rn的值存到Rm地址里面
Rn : Rn寄存器中的数当成数据处理
[Rm] : Rm寄存器中的数当成内存的地址处理
== ldr : 将[Rm]执向的地址空间中的内容读到Rn寄存器中
str : 将Rn寄存器中的值,写到[Rm]指向的地址空间中。==

        ldr r0, =0x40000800    //伪指令
        ldr r1, =0x12345678    //伪指令
        str r1, [r0]  
        ldr r2, [r0]
        @ int *p = &a
        @ p =r0 / *p = [r0]
        @ (*p = a) = str
        @ (a = *p) =ldr
        @ ldrh/strh : 半字内存读写指令
        @ h : half  
        @ strb/ldrb : 单字节内存读写指令
        @ b : Byte
       
        @ ldr/str的寻址方式
        ldr r0, =0x40000800
        ldr r1, =0x11111111
        ldr r2, =0x22222222
        ldr r3, =0x33333333
        //将r1中的值写到r0+4指向的地址空间中,r0指向的地址空间不变
        str r1, [r0, #4]
        //将r2中的值写到r0执行的地址空间中,同时更新r0指向的地址空间:r0=r0+4
        str r2, [r0], #4
        //将r3中的值写到r0+4指向的地址空间中,同时更新r0指向的地址空间:r0=r0+4
        str r3, [r0, #4]!
        //同样ldr strh ldrh strb ldrb也支持以上三种不同的内存寻址方式
        //注意:地址偏移量应该是load/store指令所操作内存的最小值的整数倍


2> 多寄存器操作指令 stm ldm
@ m : mutil
@ 语法格式:
stm/ldm{cond} Rm, {寄存器列表}
Rm : Rm中存放的是内存地址
{寄存器列表}:寄存器连续使用"-“隔开
寄存器不连续使用”,"隔开。
   
        ldr r0, =0x40000800
        ldr r1, =0x11111111
        ldr r2, =0x22222222
        ldr r3, =0x33333333
        ldr r4, =0x44444444
        ldr r5, =0x55555555
        //将r1-r5寄存器中的内容存到r0指向的地址的连续的20个字节空间中
        stm r0, {r1-r4,r5}
        //将r0指向的20个字节连续空间中的内容读到r6-r10寄存器中
        ldm r0, {r6-r10}
        @ 注:不管寄存器列表中的寄存器顺序如何永远都是低地址对应小编号寄存器,高地址对应高编号的寄存器
        stm r0, {r5,r4,r3,r2,r1}
        ldm r0, {r10,r9,r8,r7,r6}             
3> 栈操作指令 stmfd ldmfd

增栈
        压栈时,栈指针向高地址方向移动
减栈
        压栈时,栈指针向低地址方向移动
满栈
        当前栈指针指向的空间有有效的数据
空栈
        当前栈指针指向的空间没有有效的数据
栈的操作方式 4种(满增栈,满减栈,空增栈,空减栈)


ARM处理器默认采用的是满减栈


// 满减栈指令:stmfd/ldmfd   full descending
// 满增栈指令:stmfa/ldmfa   full ascending
// 空减栈指令:stmed/ldmed   empty descending
// 空增栈指令:stmea/ldmea   empty ascending


@ 语法格式:
stmfd/ldmfd{cond} SP!, {寄存器列表}
!: 更新栈指针


        ldr sp, =0x40000800 @ 初始化栈指针  //需要初始化栈指针的原因:默认为0,为满减栈,若不初始化给一个值,会往低地址存,为负数
        mov r0, #3   @ r0, r1 赋初始值
        mov r1, #4
        bl add_func  @ 调用加法函数
        add r2, r0, r1  @ r2=r0+r1=0x7
        b loop
add_func:
        stmfd sp!, {r0-r1, lr}  //压栈保存现场
        mov r0, #5  @ 修改r0,r1值
        mov r1, #6
        bl sub_func
        add r3, r0, r1 @ r3=r0+r1=0xB
        ldmfd sp!, {r0-r1, pc} //出栈恢复现场
        @ mov pc, lr  @ 返回
sub_func:
        stmfd sp!, {r0,r1,lr}   @ 保存现场
        mov r0, #9
        mov r1, #4
        sub r4, r0, r1
        ldmfd sp!, {r0,r1,lr}   @ 恢复现场
        mov pc, lr


3.3.4 特殊功能寄存器操作指令 msr mrs
对于cpsr寄存器的读写只能用msr和mrs指令
@ 语法格式:
@ msr cpsr, Rn @ 写:cpsr = Rn
@ mrs Rn, cpsr @ 读:Rn = cpsr


        @ 系统上的默认工作在svc模式
        @ 从svc模式切换到用户模式
        @ 10000 User mode;
        @ 10011 SVC mode;
        @ 方法1:
        @ mov r0, #0xD0
        @ msr cpsr, r0
        @ 方法2:
        @ 1. 先将cpsr中的值读到普通寄存器
        mrs r0, cpsr
        @ 2. 修改模式位,保证其他位不变
        bic r0, r0, #0x1F
        orr r0, r0, #0x10
        @ 3. 将修改后的值写回到cpsr中


4、异常的处理过程

swi软中断指令

user(用户模式)
                                 向下调用接口
----------------------(软中断(通过软中断号区分))---------
                             向上提供接口
kernel(svc模式)
------------------------------------------------------
hal




5、C和汇编的混合编程

C和汇编混合编程必须遵循ATPCS规范

1. arm-v7  
          参数的传递使用r0-r3,返回值通过R0返回
          如果参数大于4个,使用压栈的方式进行传递。
          函数封装时,形参的个数尽量不要超过4个。
2. arm-v8
                参数的传递使用x0-x7,返回值通过x0返回,
                如果参数大于8个,使用压栈的方式进行传递


5.1 汇编调用C
给r0-r3赋初始值,相当于给C的函数传递实参值
直接使用bl指令进行调用


5.2 C调用汇编
// 函数的声明
extern int add_func(int a, int b);
int add_func2(int a, int b, int c);
int sum;
int main()
{
        // C调用汇编
        sum = add_func(3,4);
        sum = add_func2(4,5,6);
        sum = add_func(3,4);
        while(1);
        return 0;
}
// 内联汇编
int add_func2(int a, int b, int c)
{
        int sum;
        asm volatile(
        "add r0, r0, r1nt"       
        "add r0, r0, r2nt"
        :"=r"(sum)
        :"r"(a), "r"(b), "r"(c)
        :"memory"
        );
       
        return sum;
}


.text   
.global _start
_start:
        @ 切换到用户模式
        msr cpsr, #0xD0
        @ 初始化User模式下的栈指针
        ldr sp, =0x40000800
        @ 汇编调用C代码 : 直接调用
        b main
loop:  
        b loop
        .end


int add_func(int a, int b)
{
        return a+b;
}
void swap_func(int *a, int *b)
{
        int tmp;
        tmp = *a;
        *a = *b;
        *b = tmp;
}
int add_func2(int a, int b, int c ,int d, int e)
{
        return a+b+c+d+e;
}


5.3 内联汇编(内嵌汇编)
asm volatile(
"汇编指令"
:输出列表
:输入列表
:破坏列表
);
安装软件


  • 安装Keil软件
    FS6818(学生资料)工具软件汇编编程环境搭建
    参考文档:汇编编程环境搭建.pdf








  • 安装SecuretCRT串口工具
    FS6818(学生资料)工具软件串口工具securetCRT
    参考文档:SCRT804安装说明文档.docx
  • 安装USB转串口的驱动
    FS6818(学生资料)工具软件USB转串口驱动
    双击直接安装即可。
  • 安装ubuntu版本的交叉编译工具链









本地开发:(X86 GNU工具集:gcc)
PC端编写代码,PC端编译代码,
PC端运行代码。
交叉开发: (ARM GNU工具集:arm-none-linux-gnueabi-gcc)
PC端编写代码,PC端编译代码,
开发板(Target板)运行代码。
安装交叉编译工具链的过程:
1>. 在ubuntu的家目录下创建toolchain目录
2>. 拷贝交叉编译工具链到toolchain目录下,
并进行解压缩
FS6818(学生资料)工具软件ubuntu版本交叉编译器gcc-4.5.1.tar.bz2
tar -xvf gcc-4.5.1.tar.bz2
3>. 配置ubuntu系统的环境变量
对所有的用户有效:
/etc/environment
/etc/bash.bashrc
/etc/profile
只对当前用户有效
~/.bashrc
选择一个配置文件进行修改即可。
本次实验:
修改/etc/bash.bashrc,在文件最后边添加以下内容export PATH=$PATH:/home/linux/toolchain/gcc-4.5.1/bin修改为自己的路径
4>. 使环境变量立即生效
source /etc/bash.bashrc 或者重启ubuntu系统
5>. 测试交叉编译工具链是否安装成功
arm-none-linux-gnueabi-gcc -v
64位ubuntu系统兼容库 :
sudo apt-get install libncurses5-dev
sudo apt-get install lib32z1
# 内存地址的操作位运算符 : &  | ^ ~ << >> 口诀:
& :与0清0,与1不变
| :或1置1,或0不变
^ : 异或1取反,异或0不变
1 ^ 1 -> 0
0 ^ 1 -> 1
1 ^ 0 -> 1
0 ^ 0 -> 0
内存 物理地址 地址的功能名
|----------|
| | 0xC001A000 reg1
|----------|
| | 0xC001A004 reg2
|----------|
| | 0xC001A008 reg3
|----------|
| | 0xC001A00C reg4
|----------|
[31:0]

``c
方法1 :
        *(unsigned int *)0xC001A000 = *(unsigned int *)0xC001A000 | 0x8;
        #define  reg1  (*(unsigned int *)0xC001A000)
        reg1 = reg1 | (0x1 << 3);


方法2:
        // 声明一个结构体
        typedef struct{
                unsigned int reg1;
                unsigned int reg2;
                unsigned int reg3;
                unsigned int reg4;
        }reg_t;
        #define REG  (*(reg_t *)0xC001A000)
        REG.reg1 = REG.reg1 | (0x1 << 3);
        #define REG  ((reg_t *)0xC001A000)
        REG->reg1 = REG->reg1 | (0x1 << 3);


练习:


        31                                     0
        xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx
        1> 0xC001A000地址中的,第[3]位置1,保证其他位不变
        2> 0xC001A000地址中的,第[10]位清0,保证其他位不变
        3> 0xC001A004地址中的,第[7:4]位置1,保证其他位不变
        4> 0xC001A004地址中的,第[23:20]位清0,保证其他位不变
        5> 0xC001A008地址中的,第[15:10]位设置为0b101010,保证其他位不变


开发板介绍



举报

更多回帖

发帖
×
20
完善资料,
赚取积分