分享

ARM体系架构

 金刚光 2022-12-03 发布于辽宁

现在来说的话,嵌入式领域基本上ARM一家独大,所以学习嵌入式开发,对于ARM体系的了解必不可少,只有在了解ARM体系的基础上才能理解ARM汇编(GNU汇编),但是我之前跟了好几家的视频以及教程,对于ARM的了解还是一知半解,究其原因我觉得大家都在说怎么做,但是很少有说到为什么这么做,这篇文章以具体例子来说明一下这些概念与区别,然后我们就能顺其自然的直到为什么要这样做

所以在解决方案提出以前,我们需要恶补一下相关概念,以便我们更好的入门ARM的世界

文章内容为个人跟随视频教程、查阅资料和博客,所做笔记,所以水平有限,如有错误,欢迎指正

ARM处理器相关名词

流水线:

底层架构设计的术语。 CPU先fetch(取指令),然后decode(译码),然后excute(执行),称为基于F D E的三步操作,才能完成CPU的运算,这种三步完成的称为三级流水。

DSP:

数字信号处理,可以理解为一个单独的芯片或者协处理器来处理数字信号,比如说多媒体信号(因为对任务的独占性要求高,使用主CPU去处理的话,别的任务可能兼顾不过来)。

Jazelle:

arm中针对Java的编程模型,基本不会用到

半字: 默认情况下int占四个字节,单个字节存储容易将一个数据破坏,所以有时候将四个字节一起存储,称为一个字,半字就代表两个字节一起操作

Thumb:

即Thumb指令,辅助arm处理的指令,因为32位的机器上arm指令占32位即四个字节,但有时候为了减少指令所占用的空间,就引入了thumb(小拇指)指令,是一个16位的精简指令,再后来推出的Thumb2是16位与32位混合的指令。

VFP:

Vector Floating-Point矢量浮点运算机制

QEMU:

实际上是一个软件模拟器,模拟不同的架构体系(例如x86 arm),调试bootloader或者裸机程序的时候可以使用

GNU工具链:

既可以开发内核也可以用来开发应用程序 包含如下组件:

  • GNU make

  • GNU Compiler Collection (GCC)

  • GNU binutils linker, assembler and other object/library manipulation tools

  • GNU Debugger (GDB)

  • GNU build system (autotools)

  • GNU C Library (glibc or eglibc)

gnueabi的含义:

e 表示 embedded 嵌入式 a 表示 application 应用程序 b 表示 binary 二进制 i 表示 interface 接口 全称嵌入式应用程序二进制接口,所以它的作用就是编译生成嵌入式的二进制程序 下载网址可以去linaro

UEFI:

Unified Extensible Firmware Interface 统一可扩展的部件接口 相当于硬件与操作系统适配的标准,制定了操作系统可以扩展哪些硬件接口,但我们在做arm开发的时候可能硬件不是特别规范,那么就可以使用这个标准模拟出符合标准的硬件,绕过它。

TrustZone:

安全架构

LPAE:

Large Physical Address Extension(大物理地址扩展) 类似于虚拟化的技术 32位按理说最多只能寻址4G内存 但使用之后,内存可以虚拟为特别大,一个T也没有关系(硬件层面)

big LITTLE:

大小 处理器可能需要一个主处理器与协处理器 例如主处理器A15和协处理器A8 当任务量要求不大的时候,切换到协处理器,会降低功耗 主要是针对手持设备,解决费电的问题 官方宣称使用这种方式比完全使用主处理器可以省电70%

CPU组成:

  • ALU单元(逻辑运算单元)

  • 控制器,可以用来切换CPU状态

  • 寄存器(小型的存储器)

  • CPU内部总线 最最主要的是前三个 但是与我们写程序最最关心的就是CPU状态切换与寄存器

SIMD:

往后翻,单独讲

NEON:

往后翻,单独讲

MMU:

往后翻,单独讲

Cache:

往后翻,单独讲

arm处理器模式

不同系列的处理器,处理器模式可能不一样 例如arm7与arm9有7种处理器模式 但是A系列的有9种处理器模式

处理器模式是指系统运行的时候如果出现崩溃的时候,切换CPU模式,来完成安全,效率,节能控制。因为系统崩溃的时候cpu的工作是没有意义的,所以这时候为了节能等其他方面的考虑,必须切换处理器模式。

下面以cotex A 系列的九种处理器模式进行介绍,

一种非特权模式,8种特权模式

模式编码也会存放在指令(也就是32位中占据5位)的某个区域

USR用户模式,大部分程序运行所处的模式,普通模式

FIQfast interrupt,快速中断模式,

SVC为超级管理员模式,当开机的时候cpu权限最高,按下reset键的时候,系统停下所有工作,执行reset,权限非常大,一般不能随便使用

MON是cotex A系列增加的,称为监视模式,相当于一个后台的服务

ABT退出模式,通常用户程序发生异常的时候,cpu停止代码运行,然后退出

HYP是超级的监视者,比超级管理员低一点,一般也不常用

SYS当系统自身异常的时候,有一个特点就是与用户模式寄存器共享 模式基本都用大写的简写来表示

USR FIQ IRQ SVC MON ABT HYP UND SYS

因为内存不稳定以及访问速度慢等原因,所以在cpu中需要寄存器。

USR模式下寄存器

r1到r12都是通用寄存器

r13为sp,即stack pointer 栈指针。存储栈地址,程序跳转的时候,保存程序跳转的目标地址标识

r14为lr,即link register链接寄存器。存储子程序的返回地址。

r15为pc,即program count程序计数器。

APSR/CPSR, PSR 即program status register程序状态寄存器,A即apilication应用,C即current当前,所以全称为应用/当前程序状态寄存器

SPSR中 S 即saved已保存的,所以全称已保存的程序状态寄存器,更好听一点称为存储程序状态寄存器或者备份状态寄存器

举例说明CPSR/APSRSPSR寄存器: 当前程序状态假设是normal状态那么这个状态先保存到CPSR,但之后突然发生异常(exception),这时候exception会存到CPSR寄存器,但处理完异常以后想要返回normal状态,这时候原来的normal状态已经被冲掉了,所以就需要SPSR寄存器对原来状态进行保存,保证可以还原到原来状态。 正是因为我们的这种需求,所以处理器的 USR 模式下没有 SPSR 因为 USR模式是正常的模式,所以不需要记录异常模式,或者其他状态模式,这样知识就联系起来了,理解起来也就更容易了。

大约18个寄存器,分为通用寄存器与特殊寄存器

上图中的横向来看,灰色的都为不同模式之间共享的寄存器,空出来的都为对应模式下不存在的寄存器 主要是可以看明白这张图,不用记住这张图。 USR模式下(或者说非特权模式下)才有APSR寄存器,其他模式下叫做CPSR

总结:

  1. R0-R12是通用寄存器,放通用数据,32bit

  2. 各个模式的R0-R12与USR模式是共享的(除了FIQ,R8-R12独有),PC,CPSR共享

  3. USR模式没有CPSR

  4. R13是sp,R14是lr,R15是pc (这四句话尽量背过)

R0-R7被称为低寄存器组,R8-R15被称为高寄存器组,为什么会这样呢 这就回到了之前说的arm指令Thumb指令以及ThumbEE指令,当使用arm指令的时候,这些寄存器都会被访问到,但是用Thumb指令指令的时候只能访问通用寄存器的寄存器组,不能访问R8-R12这些寄存器,会出错。当然,特殊寄存器还是可以访问到的,所以不同指令集下,寄存器也会进行相应的切换。

CPSR寄存器各个位的介绍:

  • M[4:0] 是模式寄存器控制位,正好对应上面表格的处理器模式编码(知识又巧妙地联系在了一起)

  • T 是 Thumb 的缩写,是否使用Thumb指令集,为1是yes,即使用Thumb指令集

  • F 是 FIQ 的缩写,不是指当前章台是否处于FIQ,而是表示是否禁用FIQ模式,为1禁用,也可以称为屏蔽快速中断

  • I 是 IRQ 的缩写,表示是否禁用(屏蔽)IRQ中断,为1禁止

  • A 是 abort 的缩写,是否禁用Abort模式,为1禁止

  • E 是 endian 的缩写,表示大小端模式选择,因为是按一个字长(4个字节)或者半字来进行存储操作,必然会引入大小端模式的争论,为1表示大端

  • IT[7:2] 是cotex A系列增加的,针对ThumbEE指令中一些条件判断的执行,就是通过位的方式将 IF-THEN 条件判断进行存储,但是我们使用arm指令的时候,可以不用管。

  • GE[3:0] 是 SIMD 指令(单指令多数据流技术,主要用于多媒体加强)的使用

  • [23:20]作为保留

  • J 是 Jazelle 状态,表示是否启动对Java的加速,为1启用

  • IT[1:0] 是针对ThumbEE指令中一些条件判断的执行

  • Q=1 表示累计饱和

  • V=1 表示ALU操作溢出

  • C=1 表示ALU进位操作

  • Z=1 表示ALU计算结果为零

  • N=1 表示ALU计算结果为负数 数学运算的这些逻辑与之后的汇编指令相关

流水线

取指->译码->执行->存取->保存结果 像上面这些扩展可以称为五级流水。 流水越多,过程越细,处理能力越强,可控制的情况也就越多,也就越复杂,一般来说,流水越多,也就说明体系架构越强大。 分解指令过程:

  1. 指令预读取(决定从内存的哪儿取指令)--perfetch

  2. 指令读取(从内存系统中读取指令)--fetch

  3. 指令译码(解读指令,并且生成控制信号)

  4. 寄存器读取(提供寄存器的值给操作单元)

  5. 分配(分配指令给执行单元,也就是分配给ALU)

  6. 执行(实际的ALU单元处理)

  7. 内存访问(数据的存取)

  8. 寄存器回写(更新运行结果到寄存器)

SIMD

Single Instruction Multiple Data, 单指令多数据流 在SIMD之前,也就是ARMv5之前有一个SISD的概念(Single Instruction Single Data, 单指令单数据流)

# 例如下面这条指令每个数据都会执行一遍取指->译码->执行 这三个流程
# 这样效率就比较低
LDMIA R0, {R1-R8}

所以就有了 单指令多数据流 的概念 所以针对多个数据先在内存中建立一个向量表vector,然后将向量表一次读出,相当于多次取指,多次译码,最后一次性执行,这样大大提高cpu的效率 用在浮点运算和多媒体运算,依据此诞生了GPU相关的技术 以图形运算或者视频运算来说,需要强大的浮点运算能力 相当于取多个数据一次性交给CPU,提升效率 SIMD 效率相比于 SISD 提升四倍左右 所以在ARMv6以后基本都使用了SIMD

# 这是一个单指令单操作数
# LDR 称为指令
# R0 R1 称为操作数
LDR R0, R1

以下图为例,未使用SIMD之前相当于进行四次运算,使用SIMD以后相当于只进行了一次运算

上图来自ARM Cortex-A(armV7)编程手册V4.0中的7.1章节 SIMD

只限于了解,因为这是片内架构设计方面的东西,不需要编程控制

只需要知道为什么经过架构的迭代,ARMv7会快于ARMv6,ARMv6会快于ARMv5 只有在自己生产芯片的时候才会用到这些东西

NEON

ARMv7之后,将SIMD升级为 NEON技术 相当于扩展了浮点运算向量表 有一个VFS的二维向量表,将数据存放到这个表中,cpu可以一次性取某个区域进行执行,NEON就是SIMD的升级 相当于将上面SIMD执行指令的表格长度与宽度进行扩展,提升了数据集的压缩算法。 NENO可以进行指令扩展来实现相关功能,但我们一般不使用,如果做一些GPU相关的工作就需要利用NENO的浮点运算特性。 可用于加速多媒体和信号处理算法(如视频编码/解码)、2D/3D图形、游戏、音频和语音处理、图像处理技术、电话和声音合成,其性能至少为ARMv5的3倍,为ARMv6 SIMD性能的两倍 在SIMD的基础上提升了两倍效率 所以运算速度的加快不是单纯的提升CPU的频率 通过这两个技术提升指令执行的方式,还有其他的方法(比如增加二级缓存)

只限于了解,因为这是片内架构设计方面的东西,不需要编程控制 只需要知道为什么经过架构的迭代,ARMv7会快于ARMv6,ARMv6会快于ARMv5 只有在自己生产芯片的时候才会用到这些东西

总结来说NEON快于SIMD快于SISD,正常按三级流水来说的话,每个指令都要执行一遍,但现在可以多个指令一块执行,降低CPU负荷,提升运行效率

Cache

高速缓存机制 与上面的 SIMD/NENO 都是为了提升速度而设计的,但是 不是重点编程控制的地方。只有在做一些视频库或者DSP芯片的时候,需要深入研究这几个地方。 只有做一些操作系统或者操作系统引导(bootloader)相关的时候会接触到 Cache是一种SRAM片内存储器,特点就是速度快,容量小 Cache横向划分可以分为Cache1,Cache2,Cache3... 纵向划分可以划分为L1 Cache, L2 Cache ...(多级缓存)

为什么需要Cache? I/O向主存请求的级别高于CPU访问内存 主存速度跟不上cpu的发展 主存一般都是片外的,频繁进出速度慢,能耗高 CPU的速度已经足够快了,但是内存速度不够快 所以在片上放一块存储器,因为cpu有预测机制,所以将某个地方的数据全部搬运到片内SRAM缓存中,也不需要软件驱动程序,从而大大加快访问速度。 这个搬运的过程是由Cache Controler(缓存控制器) 来完成的

缓存工作原理 局部性原理,复杂的程序都是存储在相邻空间的 采用了SRAM 命中率是衡量缓存优劣的 缓存与主存的比例一般是4:1000,例如:缓存是128K对应主存32M

内存层级 随着内存增大,命中率会下降,所以需要加入二级缓存 二级缓存可以更大,既可以在片内也可以在片外 也可以分为数据缓存DCache与指令缓存ICache 这是属于纵向划分,也可以进行横向划分,使用多块缓存

缓存搬移规则 分为直接映射缓存(Direct Mapped Cache)与联合缓存(Set-associative Cache)

直接映射缓存

主存中的连续好多行直接对应到缓存中的一行,也就是多行对应一行 缓存中有tagoffset记录数据是从哪里搬移过来的 效率并不是特别高,适合缓存简单的情况,当数据在不同地方存放的时候,比较快速 以下面程序为例,可能两个数据都会被搬移到缓存的不同位置(如果命中率高的话)

缓存是相对的,L1 是 L2 的缓存,L2是内存的缓存,内存是硬盘的缓存,以此类推,如下图所示 (此图来自深入理解计算机系统)

联合映射缓存

因为哈佛体系分为数据缓存与指令缓存 不能直接进行搬移,需要将数据与指令分离开 这个过程是由缓存控制器完成的,这个规则是由芯片决定的,需要查看芯片手册

缓存结构

tag表明从哪里搬过来的

理解Cache,必须理解两个原则:局部性(时间与空间) 以及 层级原则

MMU

Memory Management Unit存储器管理单元 因为32位的cpu最大的寻址空间最大只有4G。 但有的程序需要较大的内存,另一方面不同进程内存之间需要隔绝 基于以上两个问题,所以提出虚拟存储器的概念 windows中有虚拟内存的设置(环境变量中)

内存条太小的时候,磁盘可以拿出一块虚拟为内存,初始大小设置为安装内存的一半,最大值可以设置为安装内存的两倍。 内存中开辟一块空间作为硬盘的Cache,达到增大内存的目的 这跟之前提到的局部性有关。

虚拟存储器作用:

  1. 允许多个程序之间有效而安全的共享存储器

  2. 消除一个小而受限的主存容量对程序设计造成的影响

页: 内存空间中存储的最小单元,称为Page,或者称为bank(块) 再软件方面称为Page,硬件方面称为bank 每一页一般是4k-16k左右 每一页还会有行line,列offset 这与之前说的Cache联系在一起 每个页类似于表状的一个结构 多个页组成段,多个段组成存储空间

命中率: 虚拟内存也存在命中率,如果没有找到对应的物理内存的页,称为缺页 因为存储的时候是按页存储的

页保护与锁定机制来保证不同程序之间数据不冲突

分段: 代码段,数据段,bss段等 按功能将内存进行分段,物理内存也进行分段 统一只会使用这一段的页,不会使用其他段的页,所以就更加结构化,更加合理。 MMU有一个寄存器专门 记录物理地址映射的首地址

怎么根据虚拟地址找到物理地址? 在物理内存中开辟一段空间,称为(index)索引,相当于一个键值对哈希表 一个虚拟地址对应一个物理地址,称为Page table (页表) MMU有一个页表寄存器,专门存放页表所在位置

当缺页(找不到对应的物理内存)的时候,则把控制权交给操作系统,操作系统去磁盘取数据

加快地址转换(TLB)(也相当于一个缓存)是为了更快的找到对应的物理地址,类似于树形的结构,层层往下查找,直到找到虚拟内存对应的物理内存, TLB相当于把哈希表拆分,将若干次查询简化。 当TLB找不到页的时候,控制权交给操作系统来解决缺页

所以,从虚拟地址找到一个物理地址过程如下: CPU -> MMU -> TLB ->Page Table-> 物理内存 逐步建立索引

虚拟内存地址不一定要比物理内存地址大

关于MMU,虚拟内存,Cache相关的看上面提到的 ARM 编程手册

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多