Cortex-A7的九种运行模式

模式 描述
User(USR) 用户模式,非特权模式,大部分程序运行的时候就处于此模式。
FIQ 快速中断模式,进入 FIQ 中断异常
IRQ 一般中断模式。
Supervisor(SVC) 超级管理员模式,特权模式,供操作系统使用。
Monitor(MON) 监视模式?这个模式用于安全扩展模式。
Abort(ABT) 数据访问终止模式,用于虚拟存储以及存储保护。
Hyp(HYP) 超级监视模式?用于虚拟化扩展。
Undef(UND) 未定义指令终止模式。
System(SYS) 系统模式,用于运行特权级的操作系统任务

CPSR 寄存器

image-20201229143227646

  • M[4:0]: 处理器模式控制位
M[4:0] 处理器模式
10000 User 模式
10001 FIQ 模式
10010 IRQ 模式
10011 Supervisor(SVC)模式
10110 Monitor(MON)模式
10111 Abort(ABT)模式
11010 Hyp(HYP)模式
11011 Undef(UND)模式
11111 System(SYS)模式

堆栈的上下增长方式

  • 向上增长

    低地址向高地址增长

  • 向下增长

    高地址向低地址增长

初始化c语言环境

  1. 进入SVC模式

    将bit4-bit0配置为10011

  2. 设置栈指针

    设置sp指针为0x80200000,ddr3的地址在0x80000000,栈的空间大小为0x200000=2M

  3. 跳转到main函数

点灯代码

start.s 初始化c语言环境

1
2
3
4
5
6
7
8
9
10
.global _start

_start:
mrs r0,cpsr @将cpsr的值存入r0
bic r0,r0,#0x1f @将r0低5位清0
orr r0,r0,#013 @将10011和r0按位或,写入低5位,SVC模式
msr cpsr,r0 @将r0的值写回到cpsr

ldr sp,=0x80200000 @将sp指针地址设置为0x80200000
b main @跳转到main函数

main.h 定义相关寄存器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#ifndef __MAIN_H
#define __MAIN_H

// CCM寄存器
#define CCM_CCGR0 *((volatile unsigned int *)0x020c4068)
#define CCM_CCGR1 *((volatile unsigned int *)0x020c406c)
#define CCM_CCGR2 *((volatile unsigned int *)0x020c4070)
#define CCM_CCGR3 *((volatile unsigned int *)0x020c4074)
#define CCM_CCGR4 *((volatile unsigned int *)0x020c4078)
#define CCM_CCGR5 *((volatile unsigned int *)0x020c407c)
#define CCM_CCGR6 *((volatile unsigned int *)0x020c4080)

// IOMUX 相关寄存器
#define SW_MUX_GPIO1_IO03 *((volatile unsigned int *)0x020e0068)
#define SW_PAD_GPIO1_IO03 *((volatile unsigned int *)0x020e02f4)

// GPIO1 相关寄存器地址
#define GPIO1_DR *((volatile unsigned int *)0x0209c000)
#define GPIO1_GDIR *((volatile unsigned int *)0x0209c004)
#define GPIO1_PSR *((volatile unsigned int *)0x0209c008)
#define GPIO1_ICR1 *((volatile unsigned int *)0x0209c00c)
#define GPIO1_ICR2 *((volatile unsigned int *)0x0209c010)
#define GPIO1_IMR *((volatile unsigned int *)0x0209c014)
#define GPIO1_ISR *((volatile unsigned int *)0x0209c018)
#define GPIO1_EDGE_SEL *((volatile unsigned int *)0x0209c01c)

#endif

main.c 点灯程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include "main.h"

//时钟使能
void clk_enable(void)
{
CCM_CCGR0 = 0xffffffff;
CCM_CCGR1 = 0xffffffff;
CCM_CCGR2 = 0xffffffff;
CCM_CCGR3 = 0xffffffff;
CCM_CCGR4 = 0xffffffff;
CCM_CCGR5 = 0xffffffff;
CCM_CCGR6 = 0xffffffff;
}

//led初始化
void led_init(void)
{
SW_MUX_GPIO1_IO03 = 0x5;
SW_PAD_GPIO1_IO03 = 0x10b0; //配置io
GPIO1_GDIR = 0x00000008; //bit3置1
GPIO1_DR = 0x0;
}

//led打开
void led_on(void)
{
GPIO1_DR &= ~(1<<3); //bit3清零
}

//led关闭
void led_off(void)
{
GPIO1_DR |= (1<<3); //bit3置1
}

//延时
void delay_short(volatile unsigned int n)
{
while(n--);
}

//延时ms
void delay(volatile unsigned int n)
{
while(n--)
{
delay_short(0x7ff);
}
}

int main(void)
{
clk_enable();
led_init();
while (1)
{
led_off();
delay(500);
led_on();
delay(500);
/* code */
}
return 0;
}

Makefile 自动编译文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
objs := start.o main.o

ledc.bin:$(objs)
arm-linux-gnueabihf-ld -Ttext 0x87800000 -o ledc.elf $^
arm-linux-gnueabihf-objcopy -O binary -S ledc.elf $@
arm-linux-gnueabihf-objdump -D -m arm ledc.elf > ledc.dis

%.o:%.s
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<

%.o:%.S
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<

%.o:%.c
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<

clean:
rm -rf *.o ledc.bin ledc.elf ledc.dis
  • 第一行:objs变量 包含生成ledc.bin的依赖文件。

  • 第三行:生成文件ledc.bin,依赖start.o和main.o。

  • 第四行:使用arm-linux-gnueabihf-ld链接,起始地址为0x87800000,“$^”表示所有依赖文件的集合,要把start.o放在最前面,因为需要汇编代码在最前面进行初始化。等同于:

    arm-linux-gnueabihf-ld -Ttext 0x87800000 -o ledc.elf start.o main.o

  • 第五行:使用arm-linux-gnueabihf-objcopy将ledc.elf文件转为ledc.bin文件,“$@”表示目标集合,等同于:

    arm-linux-gnueabihf-objcopy -O binary -S ledc.elf ledc.bin

  • 第六行:使用arm-linux-gnueabihf-objdump反汇编,生成ledc.dis

  • 后面是用arm-linux-gnueabihf-gcc把.s、.c、.S文件生成对应的.o文件,就是把start.s生成start.o,把main.c生成main.o

计算机基础之text段、data段、bss段和stack、 heap

  • text段

    就是****放程序代码****的,编译时确定,只读;

    程序代码段,在AT91库中是表示程序段的大小,它是由编译器在编译连接时自动计算的,当你在链接定位文件中将该符号放置在代码段后,那么该符号表示的值就是代码段大小,编译连接时,该符号所代表的值会自动代入到源程序中。

  • data段

    存放在编译阶段(而非运行时)就能确定的数据,可读可写。也就是通常所说的静态存储区,赋了****初值的全局变量**赋初值的静态变量****存放在这个区域,常量也存放在这个区域;

    静态初始化的数据,所以有初值的全局变量和static变量在data区。段的起始位置也是由连接定位文件所确定,大小在编译连接时自动分配,它和你的程序大小没有关系,但和程序使用到的全局变量,常量数量相关。

  • bss段

    定义而****没有赋初值的全局变量和静态变量****,放在这个区域;

    通常是指用来存放程序中未初始化的全局变量的一块内存区域,在程序载入时由内核清0。BSS段属于静态内存分配。它的初始值也是由用户自己定义的连接定位文件所确定,用户应该将它定义在可读写的RAM区内,源程序中使用malloc分配的内存就是这一块,它不是根据data大小确定,主要由程序中同时分配内存最大值所确定,不过如果超出了范围,也就是分配失败,可以等空间释放之后再分配。

  • 栈区(stack)

    由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

    保存函数的局部变量和参数。是一种“后进先出”(Last In First Out,LIFO)的数据结构,这意味着最后放到栈上的数据,将会是第一个从栈上移走的数据。对于哪些暂时存贮的信息,和不需要长时间保存的信息来说,LIFO这种数据结构非常理想。在调用函数或过程后,系统通常会清除栈上保存的局部变量、函数调用信息及其它的信息。栈另外一个重要的特征是,它的地址空间“向下减少”,即当栈上保存的数据越多,栈的地址就越低。栈(stack)的顶部在可读写的RAM区的最后。

  • 堆区(heap)

    一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事。

    保存函数内部动态分配内存,是另外一种用来保存程序信息的数据结构,更准确的说是保存程序的动态变量。堆是“先进先出”(First In first Out,FIFO)数据结构。它只允许在堆的一端插入数据,在另一端移走数据。堆的地址空间“向上增加”,即当堆上保存的数据越多,堆的地址就越高。

image-20201230210153975

imx6ul.lds 链接文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
SECTIONS{
. = 0X87800000;
.text :
{
start.o
main.o
*(.text)
}
.rodata ALIGN(4) : {*(.rodata*)}
.data ALIGN(4) : {*(.data)} /*4字节对齐,定义数据段*/
__bss_start = .; /*bss段的起始地址赋值定位符*/
.bss ALIGN(4) : {*(.bss)*(COMMON)} /*定义bss段*/
__bss_end = .; /*bss段的结束地址赋值定位符*/
}

总结

  • 汇编初始化c语言环境
    • 配置模式
    • 配置sp指针
    • 配置跳转函数
  • h文件定义需要操作的寄存器
    • CCM寄存器
    • IOMUX 相关寄存器
    • GPIO1 相关寄存器
  • c文件配置寄存器以及逻辑代码
    • 初始化时钟
    • 初始化led灯(GPIO的配置)
    • 延时函数
  • Makefile文件
    • o文件的生成
    • bin文件的生成
      • 链接起始地址 生成.elf
      • .elf文件生成.bin文件