[A-14]ARMv8/ARMv9-Memory-内存模型的类型(Device & Normal)

文摘   2024-10-19 19:16   辽宁  
ver0.2
前言
前面花了很大的精力把ARM构建的VMSA中的几个核心的议题给大家做了介绍,相信大家已经能够理解并掌握ARM的内存子系统的工作原理大致框架。接下来我们会规划一些文章,对ARM内存子系统的一些细节做一下介绍,使ARM的内存子系统更加的丰满。本文,我们将介绍内存模型的的类型(Memory Type)。
正文
1.1 背景
在介绍内存模型的类型之前,咱们先来听个神话故事。

在远古时期,宇宙尚未形成,天地未分,整个世界处于一片混沌之中。这个混沌的世界无边无沿,没有上下左右,也不分东南西北,样子好像一个浑圆的鸡蛋。在这个浑圆的东西当中,孕育着一个人类的祖先—盘古。盘古在这个混沌的世界中孕育了一万八千年,终于成熟。他醒来后,发现周围一片漆黑,什么都看不见。于是,他拔下自己的一颗牙齿,变成一把威力巨大的神斧,向四周猛劈过去。随着一声巨响,混沌被劈开,轻而清的阳气上升,变成了高高的蓝天;重而浊的阴气下沉,变成了广阔的大地。从此,宇宙间就有了天地之分。

感谢盘古,不然我们现在还生活在一混沌当中。接下来,一起看看计算机的世界,如图1-1所示。

图1-1 基于ARM的HIGH-Level 功能框图 

如同一个公司光有老总,啥事儿也搞不成,所以必须配备必要的部门组织到一起才能够正常的工作。CPU就是这个公司的老总,接下来就是配备相应的部门,上面的各个功能单元不是一个计算机系统的全部,但是已经足够复杂。有了这些职能部门后,CPU就得考虑如何去管控它们了,怎么能让他们统一协作,最大效率的发挥作用,给自己挣钱了。那到底应该如何去管理呢?回答这个问题之前是不是得给这些职能部门安排个办公室或者说至少应该有个工位吧,老板要发号指令、表扬、还是要惩罚,是不是得找到具体职员的位置啊。提到了位置,就有了一种熟悉的配方油然而生的感觉,位置不就是地址嘛,ARM的内存空间就处处充满了地址。到这里,老板(CPU)眼前一亮,干脆把这些设备抽象成内存模型算了,统一编码到内存空间(注意这一块不同的体系架构还是不一样的,比如x86和ARM就有一些区别,主要是编方式和访问方式),“老子以前怎么教内存打枪,现在照样能教这些设备怎么打炮”。于是,ARM这个老板设计了如图1-2的内存模型。

图1-2 ARM的内存模型

ARM让CPU工作以前,就得把各个职能部门摆正位置,安排合理的工位。这些工位所在的区域就是物理内存空间,而这个摆放的过程和盘古的大斧子工作流程是一样的,混沌在一起太烦人了,没有条理、没有秩序怎么能行呢?按照图1-2,先砍一斧子给那些Peripheral、再砍一部分给固件(ROM)、再砍一部分给内存(DDR)。砍完了之后,就清晰多了:

(1) Peripheral的种类最多,SOC外部的设备(Camera、LCDs、Sensor....)大家通过不同的外围总线(接口),接入到SOC的内部总线架构上;而内部的设备(ADSP、GPU、NPU...)可以直接通过SOC内部的总线(接口)接入到SOC的总线架构上。

(2) 固件设备(启动程序、安全相关程序... 出厂时刷写,不能改变。)也通过物理地址空间,链接进SOC架构中。

(3) DDR(SDRAM)内存设备也通过DDR连接进总线架构。
ARM给了这些设备安排妥当之后,并且进行了物理地址空间的编址之后,CPU就可以按照地址去访问他们进行工作了。那CPU是不是可以为所欲为,想干啥就干啥呢?比如一个老板,给财务发送了一条指令让她去写代码、或者让她将公司账上的钱直接转给老板的私人账户,显然是不行的。这些设备也一样,比如ROM区域是只能够读的,Peripheral区域由于不止CPU能操作它,设备同样可以随机的操作它们,因此这段区域通常是不能缓存的。DDR这个设备比较好说话,通常可以干的事情有很多,既可以装下代码执行,也能读写被做操作的数据,还可以共享给Peripheral设备共享数据(比如和GPU共享要被渲染的图像数据、和ADSP共享要被播放的音频数据...)。怎么能描述这些设备的个性,让CPU能够在使用他们时不至于犯错呢?对于读过前序文章的我们来说,简直是太容易了,因为ARM给CPU构建了一个VMSA,CPU是工作在虚拟地址空间上的,它在使用物理地址空间上的这些设备之前都得先翻翻账本看看到底行不行,这是一个老板对员工的最大的尊重,而这个账本就是页表,于是图1-2就变成了图1-3的样子。

图1-3 High-Level页表工作模型

有了页表之后,虚拟地址空间(CPU工作的世界)和物理地址空间(设备工作的世界)就有了隔离,也就会避免尴尬,因为在这些职能部门工作之前,大家四四六六讲清楚了,可以最大程度减少内耗,谁违反规则了(内存异常),接受惩罚就行了(异常处理程序)。到这里,员工(设备)和老板(CPU)的约定,我们已经搞清楚了是存在页表里面了,但毕竟人家是老板,公司都是人家的,还是得给面子,公司的规章制度(页表的格式)也要按照人家的来,那么下面我们就来具体看一下ARM这个老板是如何划分这些员工的类型。
1.2 ARM内存类型
先看一段手册中的描述
The Arm architecture provides the following mutually-exclusive memory types:
Normal This is generally used for bulk memory operations, both read/write and read-only operations.
Device The Arm architecture forbids Speculative reads of any type of Device memory. This means Device memory types are suitable attributes for read-sensitive Locations.
通过手册中的描述可以看出,ARM架构中的内存类型主要分为两大类:Device Memory(设备内存)和Normal Memory(普通内存)。这两类内存类型在功能、应用以及属性上存在显著差异,对于系统的性能和稳定性有着重要影响。这里稍微解释一下,其实对于CPU来说,它以外的的功能单元其实都是设备,而DDR(SDRAM)、ROM、Flash等太过重要,所以单独列出一种类型以做出和其他设备区别。那么Normal类型的重要性体现在哪里呢?以SDRAM为例,没有内存计算机系统是不能工作的,但是没有GPU、USB等等子系统的设备,计算机系统还是会照常工作,只是一部分功能会受到限制,比如没有GPU的话,那么图像渲染的过程只能通过CPU来完成,没有硬件加速。从这个例子中,就可以看出内存(SDRAM)在现代计算机系统架构中的作用,足以重要到ARM给它单独分配一种类型(当然还有其他更细节的原因,比如说数据的吞吐量、访问频次、性能优化等)。
这里有一点大家需要注意,对于一个子系统来说,比如存储子系统,到底是设备类型还是Normal 类型还是要仔细进行甄别的,如图1-4所示。

图1-4 High-Level Memory System

假如这里DDR Controller支持CPU进行运行时的控制,比如在温度过高的时候进行内存读写的降频处理,那么对应的就要有对应的驱动程序供CPU进行使用,进而直接控制DDR Controller内部的一些寄存器或者是通过中断信号等给DDR Controller发送信号完成这个动作,此时的DDR Controller暴露出来的接受指令的IO接口会被映射成DEVICE类型。而挂载在内存子系统的SDRAM这个设备,由于只是被动接受大容量的读写操作,虽然也是IO操作,但是它却被映射为Normal类型。大家虽然都被映射在物理内存的空间内,但是由于干的事情不一样,所以在页表内被记录的内存类型是不一样的。其他的设备如Flash、ROM等,大家也要仔细体会其中的区别,不要混淆。
1.2.1 Normal Memory(普通内存)
Normal Memory主要用于系统中的大部分内存区域,包括程序代码和大多数的数据存储如图1-5所示,支持的设备类型包括RAM、ROM、Flash映射到ARM的物理内存空间。
图1-5 High-Level Memory-Normal
Normal类型的内存通常被最频繁访问的区域,因此需要配合处理器做编译时和运行时的优化,目的就是让CPU能实现最佳执行效率,达到性能最优状态。比如Application和kernel的code是弱排序(weakly ordered)的,编译器可以执行更多的优化。而运行时,处理器可以对Normal Memory进行reorder(重新排序)、repeat(重复)和merge(合并)访问,并且可以进行推测访问(speculatively access),即在程序显式引用之前,数据或指令就可以从内存中读取。这部分内容涉及到CPU的微架构、指令集架构、寄存器架构等、还涉及到内存子系统的另外一个重要课题-Memory Ordering(Barriers),我们会规划一篇专门的文章来详细介绍。
除了优化这一方面之外,Normal Memory支持多种属性,例如Shareability(可共享性)和Cacheability(可缓存性)等。
(1) Cacheability属性分为Write-Through Cacheable(写直达可缓存)、Write-Back Cacheable(写回可缓存)和Non-cacheable(不可缓存)。Cache机制这里我们不再赘述,大家可以看我们的前序文章。
(2) Shareability定义了位置的数据一致性要求(如图1-6所示),分为Inner Shareable(适用于Inner Shareable shareability区域)、Outer Shareable(适用于Inner Shareable和Outer Shareable shareability区域)和Non-shareable(非共享)。

图1-6 High-Level Memory-Normal Shareable 属性

属性总线架构和系统架构强关联,我们这里只简要介绍,后面我们会规划文章进行详细介绍。
1.2.2 Device Memory(设备内存)
Device Memory主要用于内存映射(memory-mapped)的外设和访问可能产生副作用的所有内存区域,如图1-7所示。

图1-7 High-Level Memory-Device
上面提到的副作用包括但不限于如下的场景:
(1) 设置一个UART设备的传输速率。
(2) 配置一个SPI控制器的工作模式。
(3) 设置一个Camera的分辨率。
(4) 控制一个功放的播放音效。
(5)  ....
与Normal Memory相比,Device Memory对处理器有更多限制,例如:
(1) 不允许进行推测性数据访问,这意味着对Device Memory的任何访问都必须由程序的简单顺序执行生成。
(2) 不能把Device区域的内存标记为可以执行的属性。
(3) 不能把Device区域的内存标记为可缓存的属性。
任何违法规则的操作都会引起异常,使当前CPU的执行陷入到异常处理的状态。
Device Memory具有固定的Outer Shareable和Non-cacheable属性,并额外定义了三种内存属性: Gathering、Reordering和Early Write Acknowledgement(EWA)。
根据这些属性,Device Memory可以分为四种类型:
(1)Device-nGnRnE(最严格);
(2)Device-nGnRE;
(3)Device-nGRE;
(4)Device-GRE(最不严格)。
我们来看一下手册中的描述:
The letters after Device represent a combination of attributes:
• Gathering (G, nG). This specifies that accesses can be merged (G) or not (nG). This could be merging multiple accesses to the same location into one access or merging multiple smaller accesses into one larger access.
• Re-ordering (R, nR). This specifies that accesses to the same peripheral can be re-ordered (R) or not (nR). When re-ordering is permitted, the restrictions apply in the same way as for the Normal type. You can find more detail on normal memory access reordering in the Memory system, ordering, and barriers guide.

• Early Write Acknowledgement (E, nE). This determines when a write is considered complete. If Early Acknowledgement is allowed (E), an access can be shown as complete once it is visible to other observers, but before it reaches its destination. For example, a write might become visible to other Processing Elements (PEs) once it reaches a write buffer in the interconnect. When Early Acknowledgement is not allowed (nE), the write must have reached the destination.

(1) Gathering描述了系统是否允许将对某一内存区域或不同内存区域的多个内存访问操作合并成一笔进行处理。例如对一个64位的数据,8个Byte,A指令更新第一个字节,B指令更新第二个字节,是否允许处理器就行优化,一次性的写进去。因为CPU的数据总线宽度是64位的,是可以做到同时更新8个Byte的。
(2) Reordering决定了对同一设备的访问是否可以按彼此的顺序进行重排序。如果允许重排序,这部分Device的内存区域就可以被执行优化操作,对它的读写可能和编程时是不一致的。
(3) EWA确定了是否允许中间写缓冲区发送写完成的确认。就是PE能否在数据到达总线的缓存(注意和CPU缓存是有区别)上就认为是写成功了。
这些类型的差异主要体现在对合并访问、重排序和写确认的处理上,如图1-8所示,他们的限制程度。

图1-8 High-Level Memory-Device SubType


1.3 内存类型的描述
前面我们介绍了内存类型的划分,而且介绍了ARM是通过页表来对内存的类型就行标记的,那么我们先来看一下页表的结构,如图1-9所示。

图1-9 ARMv8-64页表描述符

通过上图我们可以看到页表的2:4位为AttrIndex,就是用来描述当前内存区域属性的,我们来看一下手册中描述。

The memory type is not directly encoded in the translation table entry. Instead, each block entry specifies a 3-bit index into a table of memory types. This table is stored in the Memory Attribute Indirection Register MAIR_ELn. This table has eight entries and each of those entries has eight bits。

系统寄存器MAIR_ELn的宽度为64位,正好8个字节,而页表项中正好可以通过AttrInx[2:0]折3bits在MAIR_ELn索引这8个字节内容,如图1-9所示。

图1-10 MAIR_ELn寄存器索引结构

关于这个寄存器如何工作,大家可以自行阅读相关手册,这里不展开讨论了,这里就举一个例子,看一下如何配置内存为Device类型,如图1-11所示。

图1-11 配置Device类型内存

1.4 系统架构层看内存类型
前面我们已经对内存的类型做了比较详细的介绍,到这里还是要回头再看一下,帮助大家能在更宏观的视角下再看一下内存的类型,如图1-12所示。

图1-12 典型的ARM功能框图

ARM是通过总线架构串联起所有的外部设备,供CPU发出的指令使用。完成物理层面的链接后,就要考虑如何在逻辑层面对他们进行划分,不然就算连接在一起也是混沌无序的。ARM就把所有的设备都抽象成内存模型统一在物理地址空间下进行控制,那么对设备的访问就变成了对相应内存地址的访问,只要在物理地址空间下给他们分配彼此不重叠的内存区域就可以了,这个分配的行为通常是通过配置文件完成的,以Linux为例,这些设备的配置就是通过DTS(设备树)完成。系统启动后,读取DTS中的数据,就可以获取到各个设备所在的物理地址区域的地址范围,通过页表做相应的映射之后,就可以提供给CPU进行访问了。
结语
本文主要介绍了ARM的内存类型,我们用盘古的大斧子一路砍下来,将ARM的内存分成了Normal和Device两种类型,而且对Device的类型还做了进一步的划分,最后在系统架构层面再一次回顾了各种类型的设备,阐述了CPU控制各个Device的基本原理。自古都是天有天道,地有地道, 天道酬勤,地道酬德。探索这个世界,首先得分清楚万事万物的类型,不同的类型事物,属性也不一样,利用和对待方式也不一样。内存的世界也一样,希望通过阅读本文之后,大家能够有所收获。
Reference
[00] <corelink_dmc520_technical_reference_manual_en.pdf>
[01] <corelink_dmc620_dynamic_memory_controller_trm.pdf>
[02] <IP-Controller/DDI0331G_dmc340_r4p0_trm.pdf>
[03] <80-ARM-IP-cs0001_ARMv8基础篇-400系列控制器IP.pdf>
[04] <arm_cortex_a725_core_trm_107652_0001_04_en.pdf>
[05] <DDI0487K_a_a-profile_architecture_reference_manual.pdf>
[06] <armv8_a_address_translation.pdf>
[07] <cortex_a55_trm_100442_0200_02_en.pdf>
[08] <learn_the_architecture_aarch64_memory_management_guide_en.pdf>
[09] <learn_the_architecture_armv8-a_memory_systems_en.pdf>
[10] <79-LX-LK-z0002_奔跑吧Linux内核-V-2-卷1_基础架构.pdf>
[11] <79-LX-LD-s003-Linux设备驱动开发详解4_0内核-3rd.pdf>

[12] <learn_the_architecture_memory_systems_ordering_and_barriers.pdf>

Glossary
MMU             - Memory Management Unit
TLB               - translation lookaside buffer
VIPT              - Virtual Index Physical Tag
VIVT              - Virtual Index Virtual Tag
PIPT               - Physical Index Physical Tag
VA                   -  Virtual Address
PA                   -  Physical Address
IPS                  - Intermediate Physical Space
IPA                  - Intermediate Physical Address
VMID               - virtual machine identifier
TLB                  - translation lookaside buffer(地址变换高速缓存)
VTTBR_EL2     - Virtualization Translation Table Base Registers(ArmV8 寄存器)
ASID                 - Address Space Identifier (ASID)
DMC                 - Dynamic Memory Controller
DDR SDRAM   - Double Data Rate Synchronous Dynamic Random Access Memory,双数据率同步动态随机存储器
IA                    - Input Address
OA                  - Output Address
VMSA             - Virtual Memory System Architecture

浩瀚架构师
和大家一起探索这个神奇的世界。