10 ARM 体系

10 ARM 体系

  • ARM体系
  • 1、基本概念
    • [1.1 常见的处理器](#1.1 常见的处理器)
    • [1.2 ARM7三级指令流水线](#1.2 ARM7三级指令流水线)
    • [1.3 初识PC寄存器](#1.3 初识PC寄存器)
  • [2、 ARM核的七种工作模式](#2、 ARM核的七种工作模式)
  • 3、ARM核七种异常

ARM体系

1、基本概念

1.1 常见的处理器

PowerPC处理器:飞思卡尔MPC系列

DSP:TI达芬奇系列

FPGA:Xilinx赛灵思的ZYNQ系列

ARM:消费类电子:高通,华为,全志

汽车电子:飞思卡尔

网络:博通,Mavell

工控:Atmel

单片机:51,msp430,stm32(低端ARM处理器)

1.2 ARM7三级指令流水线

  • 概念:
    流水线:目的提高运算处理的能力
    指令:给CPU下发的命令,CPU根据不同的命令做不同的数据运算,存在于编译好的二进制可执行文件中,并且是一条一条指令堆积而成,如果将二进制可执行文件下载到内存中运行,那么每条指令必然有对应的内存存储地址,例如:tftp 0x48000000 shell.bin,那么第一条指令的起始地址就是0x48000000
    问:后续的指令,例如第二条指令他们的地址分别是多少呢?
    答:这与ARM处理器的工作状态有关系,ARM有两种工作状态:
    ARM状态:每条指令的长度是4字节大小,也就是说将来每条指令占用4字节内存大小,所有后续的指令地址依次加4,例如:第二条指令的首地址就是0x48000004
    Thumb状态:每条指令的长度是2字节大小,也就是说将来每条指令占用2字节内存大小,所有后续的指令地址依次加2,例如:第二条指令的首地址就是0x48000002
    所以每当go 0x48000000,那么CPU就会跑到0x48000000这个地址取出每条指令挨个运行!所谓的三级指令流水线就是CPU核处理一条指令不会一次性处理完毕,而是分成三步骤来处理!
  • 三步骤:
    • 取指F:CPU核内部的取指器硬件单元从内存中获取指令
    • 解码D:由CPU核内部的解码器硬件单元对指令进行翻译,翻译成CPU能够识别的命令
    • 执行E:CPU核正式处理该指令

1.3 初识PC寄存器

PC寄存器是ARM核内部的一个寄存器,也是用来暂存数据, 此寄存器永远只能存储当前要取的那条指令的内存地址

例如:前提是ARM核处于ARM状态

内存存储地址 指令

0x8000 add加法指令

0x8004 sub减法指令

0x8008 and位与指令

注意:CPU核处理是从上往下跑

当取add这条指令时,PC寄存器的值=0x8000

问:当add指令执行的时候,PC寄存器的值=0x8008

2、 ARM核的七种工作模式

工作模式 切换场景
SVC管理模式 系统复位或者代码调用swi/svc指令
FIQ中断模式 外设给CPU核发送FIQ中断电信号
IRQ中断模式 外设给CPU核发送IRQ中断电信号
Abort中止模式 取指F失败(指令没有)或者访问M失败(地址无权限)
Undef未定义指令模式 CPU处理一个不认识的指令,例如:lisi
System系统模式/User用户模式 一般应用程序正常运行时,CPU就是处于User用户模式

ARM寄存器总共有37个,每个寄存器大小为32位,4字节,又细分为31个通用寄存器和6个程序状态寄存器;

31个通用寄存器的名称:r0,r1,r2...r15不区分大小写

其中:

r13又称sp:永远只能保存栈指针

r14又称lr:永远只能保存返回地址(bl指令和中断)

r15又称pc:永远只能存储取指器要取的那条指令的内存存储地址

r0,r1...r12:剩余寄存器随意存储数据

6个程序状态寄存器又分:

1个cpsr:保存当前程序运行的状态信息

5个spsr:备份cpsr的值,保存cpsr,分别是:spsr_svc,spsr_undef,spsr_irq,spsr_fiq,spsr_abort

3、ARM核七种异常

明确:异常:并不是所有的异常都是不好的,有问题的,有些异常非常棒非常好,当然了有些异常很糟糕!

前提:假设CPU一开始在0x48000000这个内存地址运行(go 0x48000000),就在此时此刻触发以下异常:

异常 CPU核要切换到的模式 CPU核立马要跳转到的地址 触发场景
复位异常 SVC管理模式 0x00 系统复位
Undef未定义指令异常 Undef未定义指令模式 0x04 CPU核执行一个非法指令
软中断异常 SVC管理模式 0x08 代码调用swi/svc指令
取指异常 Abort终止模式 0x0C 取指F失败
数据处理异常 Abort终止模式 0x10 访存M失败
IRQ中断异常 IRQ中断模式 0x18 外设给CPU核发送IRQ中断
FIQ中断异常 FIQ中断模式 0x1C 外设给CPU核发送FIQ中断
ARM核异常处理的流程
明确:CPU核一旦触发异常,必须立马处理异常
前提:一开始CPU核很安静的执行一个QT程序,突然CPU核触发了某种异常,CPU核一旦触发异常,立马开启异常的处理:
首先CPU核硬件上自动做四件事:
1.备份当前被打断的QT程序的cpsr到要切换到的模式下的spsr,即:spsr_mode=cpsr
2.设置cpsr
复制代码
bit\[4:0\]=xxxxx 表示设置CPU核将来要切换到的工作模式对应的模式位的值,实现工作模式的切换
bit\[5\]=0,强制CPU核切换到ARM状态
bit\[6\]=1,禁止CPU核以后响应FIQ中断信号
bit\[7\]=1,禁止CPU核以后响应IRQ中断信号

3.保存返回地址到要切换到的工作模式下的lr寄存器中(异常处理完毕将来还要回来的,所以要保存返回地址啊)

即:lr_mode = pc - 4 (硬件自动将pc的值减4保存到lr_mode中,注:mode表示要切换到的工作模式,pc此时还在QT程序中)

问:为何pc-4呢?

答:

此时CPU还在QT中运行,对应的汇编代码如下:

指令 内存地址

add 0x48000000

sub 0x48000004

and 0x48000008

... ...

当CPU核执行add时,触发了某种异常,CPU核势必要处理异常,将来处理完毕异常还要进行返回,重新返回到QT程序中运行,只需要返回到sub指令即可继续向下运行,也就是只需将sub指令的地址保存到lr_mode中即可:

lr_mode=0x48000004=pc-4=0x48000008 - 4

4.设置pc为某个异常处理的入口地址,即:pc=0x00/0x04/0x08/0x0c/0x10/0x18/0x1c

切记:只要给pc赋值,就是让CPU核立马跑到这个地址去运行指令,就是取指运行

例如:pc=0x1c,就是让CPU核跑到0x1C地址去取指运行指令

pc=uart_init(函数名就是函数的首地址),就是让CPU核跑到uart_init函数去执行

只要咱们前期在这些异常的处理入口地址的地方埋伏好相关的代码,不就是让CPU核继续执行咱们埋伏好的代码吗?这不就是开启了软件进一步处理异常的流程了吗?

例如:触发一个IRQ中断,此时CPU核就会跑到0x18地址去运行埋伏好的代码!

开启软件进一步处理异常,同样四步骤:

1.提前建立异常向量表

问:如何在七种异常的入口地址放置(埋伏)或者关联对应的软件代码呢?

答:目的是将来异常发生,最终让CPU核能够执行处理到对应的异常处理代码,关联的方法就是通过链接器(arm...ld)搞定,链接器在链接的时候就是从文件的开头依次向下,会给每条指令都指定一个地址,见一条指令一个地址,见一条指令一个地址

c 复制代码
vim start.s
.text @告诉链接器,代码段从这里开始
.code 32 @汇编指令采用的时arm
.global _start @ global:声明一个全局函数
_start:
	b    reset @b指令的功能不带返回的跳转指令(一去不复返),跳转到reset标签继续运行
	b    undef_function
	b    svc_function
	b    fetch_abort_function
	b    data_abort_function
	b    . @ b . 表示死循环
	b    irq_function
	b    fiq_function
reset: @reset标签相关代码
	mov  r0, #0
	...
undef_function:
	mov r1, #1
	...
...
irq_function:
	mov  r0, #0
	mov  r1, #1
	mov  r2, #2
	bl do_irq
...

交叉编译:

c 复制代码
arm...as -c -o start.o start.s //用汇编器将汇编文件生成目标文件start.o
arm...ld ... -Ttext=0x00 -o start.elf start.o //链接,注:代码段从0x00地址开始链接
链接的结果是:
地址	指令
0x00	b    reset
0x04	b    undef_function
0x08	b    svc_function
0x0c	b    fetch_abort_function
0x10	b    data_abort_function
0x14	b    . @占坑
0x18	b    irq_function
0x1c	b    fiq_function
...

例如:将来外设UART控制器给CPU核发送一个IRQ中断信号,最终CPU核跑到0x18地址不就是运行,咱们埋伏好的代码:b irq_function这条指令嘛,最终去执行irq_function标签里面的各种代码,开启软件处理IRQ中断异常的流程

结论:最终形成了8行2列的表格,此表又称异常向量表!

2.保护现场

一旦CPU核跑到异常入口地址执行对应的指令(b xxxxx),此指令对应的标签(xxx_function)对应的代码中首先要做保护现场的工作

问:为何要保护现场呢?

答:概念:就是将被打断的程序(例如QT程序)使用的ARM寄存器(r0~r15)的数据备份保存到栈(就是内存)中,保护现场又称压栈保护(push stack)

举例子阐述为何要备份:

一开始CPU核很安静的执行QT程序(运行的地址例如0x48000000),并且QT程序的汇编代码如下:

mov r0, #100

mov r1, #101

mov r2, #102

...

此时此刻突然UART控制器给CPU核发送一个IRQ中断信号,立马触发IRQ中断异常,CPU核最终跑到0x18地址运行: b irq_function这条指令,而这条指令最终跳转到irq_function标签继续运行,而这个标签里面的汇编代码如下:

irq_function:

mov r0, #0

mov r1, #1

mov r2, #2

...

显然IRQ中断处理的汇编代码把原先QT程序使用的ARM寄存器值进行了修改,将来CPU核再回到QT程序继续运行时势必出错,所以CPU核执行irq_function标签的代码的时候首先做的第一件事就是保护现场,把QT程序使用的ARM寄存器值r0=100...备份到内存中,将来IRQ中断处理代码随意修改,不怕了,反正有备份!

最终:irq_function标签的代码如下:

c 复制代码
irq_function:
	@1.先保护现场,然后后面对ARM寄存器为所欲为
	mov  r0, #0
	mov  r1, #1
	mov  r2, #2
	....

3.一旦现场保护完毕,软件就可以根据用户需求完成对异常的最终处理

专业叫法:调用异常处理函数

例如:触发IRQ中断异常,实现开关灯操作:

c 复制代码
irq_function:
	@1.保护现场
	@2.根据用户需求完成开关灯操作,例如:代码如下:
	mov  r0, #0
	mov  r1, #1
	mov  r2, #2
	....
	bl led_on @调用开灯函数
	bl delay @调用延时函数
	bl led_off @调用关灯函数
	bl delay @调用延时函数
	...

4.一旦异常处理完毕,异常处理函数调用完毕,最后让CPU核再回到原先被打断的QT程序继续运行,此过程做三件事:恢复现场,程序状态恢复,跳转返回

恢复现场:又称出栈恢复,将之前栈用保存的QT数据重新恢复到ARM寄存器中供QT使用

例如:r0=100,r1=101,r2=102

程序状态恢复:将之前备份到spsr_mode中的QT的cpsr的值重新恢复到cpsr中供QT使用,cpsr=spsr_mode

跳转返回:就是将之前保存的返回地址直接给pc即可,让CPU核重新回到QT程序继续运行,pc=lr_mode至此一次异常处理完毕!

例如:IRQ中断异常为例,其irq_function标签的代码参考如下:

c 复制代码
irq_function:
	@1.保护现场(QT中的ARM寄存器值保存到栈中)
	@2.根据用户需求完成开关灯操作
	mov  r0, #0
	mov  r1, #1
	mov  r2, #2
	....
	bl led_on @调用开灯函数
	bl delay @调用延时函数
	bl led_off @调用关灯函数
	bl delay @调用延时函数
	....
	@3.软件最后处理三步骤:
	@3.1.恢复现场(将栈中保存的数据恢复到ARM寄存器中),供QT使用
	@3.2.状态恢复:cpsr = spsr_irq,供QT使用
	@3.3.跳转返回:pc = lr_irq = QT程序的某个地址,至此IRQ中断异常处理完毕,CPU核又回到了QT程序继续运行,静静地带着下一次异常的触发和处理!

ARM异常处理流程的终极总结:

ARM核硬件上自动做四件事:

1.备份cpsr:spsr_mode=cpsr

2.设置cpsr:cpsr7:0=110xxxxx

3.保存返回地址:lr_mode=pc-4

4.设置pc=0x00/0x04/0x08/0x0c/0x10/0x18/0x1c,开启软件的处理四步骤

软件处理的四件事:

1.提前建立异常向量表

2.保护现场

3.根据用户需求调用异常处理函数

4.恢复现场,状态恢复(cpsr=spsr_mode),跳转返回(pc=lr_mode)

相关推荐
2401_862880827 小时前
ARM 嵌入式开发 ---知识点
arm开发·嵌入式硬件·物联网
AAA代码批发商9 小时前
DAY43 ARM 体系结构学习笔记|嵌入式入门,从硬件组件到流水线、内存与栈帧
arm开发·笔记·学习
女神下凡11 小时前
带NPU的电脑 和不带NPU的电脑 在使用上有什么不样
arm开发·单片机·pcm
T1mzhou1 天前
ARM64 Linux 6.10 内核驱动(12):dd.c:probe/ remove、延迟 probe
linux·服务器·c语言·arm开发·arm
Doraemomo1 天前
IMX6ULL裸机开发——系统主频和时钟配置
arm开发·嵌入式硬件·makefile·imx6ull
T1mzhou1 天前
ARM64 平台安全学习记录
linux·服务器·arm开发·arm
云栖梦泽1 天前
网络设备驱动(1)
linux·arm开发·嵌入式硬件
零依赖极客2 天前
Day 9·1 KV 也量化——q8 KV 把缓存与 decode 带宽压到一半
c语言·开发语言·arm开发·人工智能·缓存·矩阵
secondyoung3 天前
I2C通信协议详解
arm开发·驱动开发·stm32·单片机·嵌入式硬件·mcu·51单片机
奔跑的架构师3 天前
[A-50]ARMv9/v8-DVFS系统架构
linux·arm开发·架构·系统架构·arm