DAY2-ARM体系结构和汇编裸机驱动

328 阅读7分钟

三.ARM处理器

ARM处理器值得是使用ARM架构的处理器 1.ARM版本

ARM11之后,ARM的产品线开始细分,改用cortex命令,分为三个系列 -------- A R M,分别对应不同的场景。

cortex-A(Application):面向尖端应用,使用虚拟内存的操作系统用户使用
    A8 ---> A9 ---> A15 A7 ---> A57 A53(64位) ---> A72 A78 A55 A34 X1
cortex-R(Realtime):针对可靠性,实时性要求高的场合
    R4 ---> R5 ---> R8 ---> R52 R82    
cortex-M(microchip):针对成本,功耗敏感的MCU领域,一般不运行操作系统   
    M3 ---> M0 M4 M7 ---> M55 M33 M23 

GEC6818开发板使用三星s5p6818芯片,64位8核cortex-A53内核,指令集版本ARMv8-A

GEC-M4开发板使用sytm32f407zet6芯片,32位cortex-M4内核,指令集版本ARMv7-M

2.ARM指令集分类(状态)

大部分ARM处理器支持ARM指令集和THUMB指令集

ARM指令集:32bits(64bits),每条指令占用32位存储空间 
THUMB指令集:16bits,每条指令占用16位存储空间

ARM处理器有两种状态:ARM状态执行ARM指令,THUMB状态执行THUMB指令。

3.ARM处理器的工作模式

经典ARM处理器有7种模式,cortex-A系列有8种

User:用户模式,一般情况下执行应用程序处于该模式 
System:系统模式,有特权的用户模式 
IRQ:中断模式,用于处理中断
FIQ:快中断模式,用于处理快中断
SVC:管理模式,系统复位/软中断进入该模式
Undef:未定义模式,通过未定义指令进入该模式 
Abort:中止模式,指令/数据 存取异常时进入该模式 
Monitor(A系列独有):安全监控模式,为安全扩展执行安全代码的模式

模式的分类:

除了User以外的模式都是特权模式

IRQ,FIQ,Undef,Abort属于异常模式

4.ARM的寄存器组织

ARM的寄存器(系统寄存器)是处理器留给用户的接口

处于处理器内部,没有地址,只有名字(编号)

寄存器用于存放正在使用的数据,寄存器中的数据可以在指令中直接使用 经典ARM处理器提供37个32位处理器,cortex-A系列有40个32位寄存器 寄存器分类:

R0~R7:未分组的通用寄存器,所有的模式下共用同一组
R8~R12:分组的通用寄存器,快中断模式下有单独的一组
R13:通用寄存器,通常用作堆栈指针(SP) 
R14(LR):链接寄存器,用来保存程序返回的地址 当程序跳转(函数调用)时,可以将返回地址写入LR 以便程序返回时可以回到跳转指令的下一条指令 
R15(PC):程序计数器,指向当前取指的指令 PC指向哪,程序就去哪里执行(顺序执行时PC自动后移)

函数调用时: 下一条语句的地址 ===> LR 调用函数的地址 ===> PC 函数返回时: LR ===> PC

CPSR:当前程序状态寄存器,表示当前处理器的状态信息

SPSR:备份程序状态寄存器,模式切换时备份CPSR CPSR的组成:

0~4位:模式位,表示处理器处于哪种模式 5位:状态位,表示处理器处于哪种状态

0表示ARM状态

1表示THUMB状态

6位:快中断禁止位,决定是否处理快中断,1禁止,0允许

7位:中断禁止位,决定是否处理中断,1禁止,0允许

28~31位:条件代码标志位,用来控制指令是否执行(也可以通过指令执行来修改)

if(a>b) a++; else b++;

N=1:运算结果小于0

Z=1:运算结果等于0

V=1:运算结果发生了溢出

C=1:运算结果发生了进位/借位

5.指令流水线

流水线技术可以大大提高指令的执行效率,实现流水线的前提是将一条指令的执行过程分为多个步骤。ARM7是3级流水,ARM9是5级流水。

3级流水:

将一条指令的执行分为三个步骤(三段独立的电路):取指 译码 执行 取指:根据PC值取对应内存地址上的指令到CPU 译码:指令译码器对指令进行识别,将指令翻译成具体的硬件动作 执行:执行指令的操作并将指令执行的结果写回寄存器

流水线的优势: 在使用流水线技术的情况下,执行一条指令平均需要1个时钟周期

n条指令需要 n+2/n ===> 1

四.ARM指令集

1.ARM汇编的分类

(1)指令

编译完成作为一条指令保存在存储器中,CPU执行指令完成某个操作

(2)伪指令

本身不是一条指令,编译器在编译时会使用指令来替换它

(3)伪操作

编译时不产生指令,也不占用存储空间,作用是辅助编译

2.指令的格式

{}{S} ,{,operand2}

//<>中的内容是必须的,{}中的内容是可选的

opcode - 操作码/指令助记符 cond - 执行条件(默认无条件执行) S - 执行结果是否影响条件代码标志位 Rd - 目标寄存器(指令执行的结果) Rn - 第一操作数寄存器 operand2 - 第二操作数

3.基础指令

(1)数据处理指令

MOV ------------------ 数据传送

LSL ------------------- 逻辑左移(<<)

mov r0,r1 //r0 = r1 mov r0, #1 //r0 = 1 mov r0,r1,lsl #2 //r0 = r1<<2 ADD ------------------ 加法

SUB ------------------ 减法

MUL ----------------- 乘法

add r0,r1,r2 //r0 = r1+r2 add r0,r1,#1 //r0 = r1+1 //add r0,#2,#3 错误

AND ---------------- 按位与

ORR ---------------- 按位或

BIC ------------------ 位清除

and r0,r1,r2 //r0 = r1 & r2 bic r0,r1,r2 //r0 = r1 & ~r2

CMP -------------- 比较

cmp r1,r2 //r1-r2,不保存结果,只影响条件代码标志位

(2)跳转指令

B -------------- 绝对跳转

BL ------------ 带返回的跳转

b 地址/标签 bl 地址/标签 //跳转时将返回地址写入LR寄存器

(3)内存访问指令

LDR -------------- 读内存数据到寄存器

STR ------------- 写寄存器数据到内存

ldr r0,[r1] //r0 = r1 str r0,[r1] // r1 = r0

4.伪指令

ldr伪指令 -------------- 长数据传送伪指令

mov r0,#0xffff //错误 ldr r0,=0xffff //正确

5.伪操作

.text -------------- 代码段

.data ------------- 数据段

.code 32/16 -------- arm指令/thumb指令

.global ----------- 声明为全局

.entry ------------ 汇编的入口

.end ------------- 汇编结束

将以下C语言用汇编实现

//定义寄存器 GPIOC14
volatile unsigned int* gpioc_alt0 = (unsigned int*)0xc001c020;
volatile unsigned int* gpioc_outenb = (unsigned int*)0xc001c004;
volatile unsigned int* gpioc_out = (unsigned int*)0xc001c000;
​
//1.初始化
//将gpioc_alt0的28~29位清0
*gpioc_alt0 &= ~(0x3 << 28);
*gpioc_alt0 |= (0x1 << 28);
//将gpioc_outenb的13位置1
*gpioc_outenb |= (0x1 << 14);

ARM汇编

ldr r1, =0xc001c020      @r1 = 0xc001c020
ldr r0, [r1]             @r0 = *r1
mov r2, #3               @r2 = 3
bic r0, r0, r2, lsl #28    @r0 = r0 & ~(r2 << 28)
mov r2, #1               @r2 = 1
orr r0, r0, r2, lsl #28    @r0 = r0 | (r2 << 28)
str r0, [r1]             @ * r1 = r0
​
//练习,将最后一条C语言代码翻译成汇编
ldr r1, =0xc001c004      @r1 = 0xc001c004
ldr r0, [r1]             @r0 = *r1
mov r2, #1               @r2 = 1
orr r0, r0, r2, lsl #14    @r0 = r0 | (r2 << 14)
str r0, [r1]             @ * r1 = r0

五.ARM汇编的使用

1.编写

汇编代码写在 .s/.S 为后缀的文件中

ARM汇编使用 @ 作为单行注释

2.编译

汇编源码的编译只需要汇编器和链接器

由于C语言编译器包含了汇编器,可以直接使用编译C语言的命令编译汇编

led的汇编实现:

.text                       @代码段
.code 32                    @arm指令
.global led                 @入口声明为全局
​
led : @标签(下一条指令的地址), 初始化E13
ldr r1, =0xc001e020     @r1 = 0xc001e020
ldr r0, [r1]                @r0 = *r1
mov r2, #3              @r2 = 3
bic r0, r0, r2, lsl #26 @r0 = r0 & ~(r2 << 26)
str r0, [r1]                @ * r1 = r0
​
ldr r1, =0xc001e004     @r1 = 0xc001e004
ldr r0, [r1]                @r0 = *r1
mov r2, #1              @r2 = 1
orr r0, r0, r2, lsl #13 @r0 = r0 | (r2 << 13)
str r0, [r1]                @ * r1 = r0
​
led_loop :
@亮
ldr r1, =0xc001e000     @r1 = 0xc001e000
ldr r0, [r1]                @r0 = *r1
mov r2, #1              @r2 = 1
bic r0, r0, r2, lsl #13 @r0 = r0 & ~(r2 << 13)
str r0, [r1]                @ * r1 = r0
@延时
ldr r0, =0x5000000
bl delay                @子程序调用
​
@灭
ldr r1, =0xc001e000     @r1 = 0xc001e000
ldr r0, [r1]                @r0 = *r1
mov r2, #1              @r2 = 1
orr r0, r0, r2, lsl #13 @r0 = r0 | (r2 << 13)
str r0, [r1]                @ * r1 = r0
@延时
ldr r0, =0x5000000
bl delay                @子程序调用
​
b led_loop
​
@延时子程序,将一个数从初始值(r0), 每次循环减1, 直到结果为0, 子程序返回
delay :
subs r0, r0, #1         @r0 -= 1, 结果影响条件代码标志位
bne delay               @r0为0, 不执行, r0不为0, 执行
​
mov pc, lr              @pc = lr  子程序返回

练习:

使用ARM汇编实现另外三盏灯的闪烁

七.volatile

用于修饰易变的变量,防止编译器优化。

编译器在编译C语言源码时,遇到要使用的数据已经存在于寄存器中,并且没有被修改,编译器就会直接使用数据在寄存器中的备份,而不去原始地址再取一次,这种操作就叫编译器的优化。被volatile修饰的变量在使用时不允许使用在寄存器的备份,而必须去原始地址读取。

使用场景:

1.用于修饰硬件寄存器的地址 2.用于修饰多线程中的全局变量 3.用于修饰中断中的自动变量

以下代码:

/*volatile*/ int a = 1;
int b = 1;
int c = 1;
int d = 1;
​
void test()
{
    b = a;
    c = a;
    d = a;
}

不加volatile: 加上volatile: 八.安装source insight 九.Linux内核 1.Linux内核子系统

1.任务调度 2.内存管理 3.设备驱动 4.网络协议 5.虚拟文件系统

2.Linux源码

Linux内核属于完全开源免费的操作系统内核,属于GNU计划,遵循GPL开源协议。内核源码在www.kernel.org下载。(Linus)

(1)内核版本

主版本号.次版本号.维护版本号

GEC6818开发板的内核版本 -------- 3.4.39 mainline ------------ 主线(最新测试版)

stable --------------- 稳定版

longterm ------------ 长期维护的版本

十.source insight的使用

1.安装

2.使用source insight创建内核工程

(1)在windows下解压内核源码

(2)在解压源代码目录新建一个工程文件夹 (3)新建工程 (4)指定工程名和工程文件保存路径

(5)指定工程源代码目录(内核源码解压目录)

(6)关闭项目文件选择框,打开文档选项菜单

修改C源代码文件的过滤器,添加 ---------- ; .S; .s

(7)为工程添加源代码

选中要添加的源代码目录,点击添加所有,勾上所有选项,点击确定

(8)同步工程文件,等待同步完成

练习:

完成内核工程的创建

作业:

使用ARM汇编实现蜂鸣器的驱动