Advertisement

7.CPU体系架构-Cache

阅读量:

这篇文章主要介绍了CPU缓存(Cache)的研究内容及其重要性。文章详细阐述了Cache的基本概念、层次结构(包括L1、L2和L3缓存)、工作原理以及与内存交互的策略差异。此外,文章还介绍了MIPS架构中Cache的操作指令及其初始化方法,并讨论了DMA操作对数据一致性的影响。整体来看,文章内容涵盖了Cache的核心原理和技术细节。

Cache研究在转正答辩中的‘MIPS/BSP’研究中扮演着核心角色。遗憾的是,在时间紧迫的情况下未能对‘MIPS/BSP’研究进行系统性地整理成文档。经过近一年的沉淀与积累后,在撰写《CPU体系架构系列》时对该部分内容进行了深入的梳理与整合,并且既进行了细致的回顾与更新。

概述

Cache用于存储CPU频繁使用的内存数据。当CPU访问所需数据时,在Cache中找到即为'命中'(Hit),否则即为'缺失'(Miss)。相比寄存器直接访问内存而言,CPU通过Cache机制提升了数据加载速度(数量级差异显著)。采用Cache相比直接使用寄存器提升至采用Cache时的成本效益主要体现在存储容量和延迟优化上。

当前 CPU 的缓存系统被划分为多个层级:包括L1、L2和L3缓存层次。随着层级的深入,读取与写入延迟逐渐加大;而整体成本相应下降以满足不同应用场景的需求。现代系统的层次结构遵循从寄存器到CPU缓存再到外部存储的逻辑安排(如图所示)。其中程序指令通常独立存放于I-Cache中(指令缓存),而数据则放置于D-Cache中(数据缓存)。这种设计有效地平衡了性能与成本的关系,在保证快速访问的同时降低了系统的总体开销。

基于程序的局部性原理构成了Cache机制的理论基础。其中主要体现在时间和空间上的双重特性:其特点是:在被CPU近期调用后,在短时间内仍需再次调用(时间维度);在存储器层次中,在被CPU近期使用过的信息附近还存在大量相关数据(空间维度)。这是因为这些数据通常与已调用信息具有较高的相关度。这使得通过缓存技术实现信息快速检索成为可能:利用近似ingly的时间 locality和空间 locality特性,在系统设计中合理分配各层次存储器的空间就能显著提升处理效率。

Cache结构

基于Cache连接策略的不同,Cache系统可分为三类:直接连接、全局连接和分组连接.为了便于理解,在此重点阐述分组式的Cache架构.由于分组式Cache架构兼具了直接连接和全局连接的优势,在性能和成本之间找到了平衡点,广泛应用于实际系统中.首先提供其结构示意图.以Harrier平台上的CPU芯片BCM5836为例进行说明.

对照上面的Cache结构图,下面说明如何来说明Cache的指标。

  1. Cache的容量即为存储多少字节。
  2. 询问Cache有多少条路?
  3. 询问Cache包含多少个组?
  4. 询问Cache每行的数据量是多少?在英语中,“line”对应“line”。在上图中虽然未直接标注线信息(line),但每一行实际上相当于某一路中的一个组(way)或者某个组的一条线(line)。

现在就知道Cache的大小是如何算得:16Bytes(行大小)*2(路)*512(组)=16KBytes。

例如,在我的个人PC上,使用软件检测获得到Cache的数据如下图所示。

工作方式

下面介绍组相连Cache的工作方式。

首先看的是CPU是如何获取Cache中的数据的。 如下图所示。

假设当前采用的是16KBytes指令型Cache(I-Cache)。在内存管理框图中,左上角表示的是32位虚拟地址空间,在这种情况下该地址空间由PC寄存器中的值表示(当PC寄存器存在时)。为了直观体现各部分地址范围分配情况,在图示中采用了三种不同的颜色进行区分划分:黑色用于表示最低有效的4个比特位(bit0bit3),蓝色用于表示中间部分8个比特位(bit4bit12),而红色则用于表示最高有效的18个比特位(bit13~bit31)。具体来说,CPU从Cache中获取指令的过程如下:

  1. 分组选择部分采用9位二进制编码;
  2. 路径确定为该分组内的具体路径;
  3. 转换逻辑的核心是将虚拟地址映射为物理地址;在此过程中:
    a) 转换后的物理地址与tag头中的内容进行对比;
    b) 对比结果决定了是否命中缓存;
  4. 经过上述步骤后定位到特定的一行数据;
    a) 由于每行存储着连续16个字节的信息;
    b) 黑色编码区负责指示如何从中提取所需信息。

在CPU与数据Cache(D-Cache)之间的互动大体上遵循相同的模式。在第三阶段存在差异。对于指令缓存来说是不可更改的(通常情况下)。因此,在对tag头部进行匹配后认为命中,则可以直接将缓存中的指令加载至CPU中执行。然而,在数据缓存中可能会出现修改或损坏的情况(corrupt),导致内存中的内容与缓存中的信息不一致。此时还需确认该区域的数据是否有效。

另外需要注意的是,在这个流程中,整个过程完全自动化处理,并且程序员无法干预细节步骤。

然后要看的是Cache和内存如何交互数据。 如下图所示。

在讨论CPU如何从Cache中获取数据时仍存在三个尚未解决的关键问题:第一个问题是当CPU从Cache中读取数据未能命中(miss),该如何解决?第二个问题是当CPU对数据Cache进行修改后将其写入导致该 Cache 区域与内存区域的数据出现不一致的情况该如何应对?第三个问题是当 CPU 对该 Data Cache 进行修改后将其写入却未触发命中(miss)情况又该如何处理?这三个关键问题正是当前实现 Cache 和内存有效交互所需突破的核心难点。

请观察图中Read Data到Miss这一过程,在Cache中获取数据时未发生命中应当如何处理?

在读取数据时遇到Cache miss的情况下,默认采用Read-allocate和无需借助Cache的两种处理方式。现代技术标准中普遍采用Read-allocate策略:即首先从Cache中分配所需的数据行空间,并从RAM中读取数据填充至该行空间后传递给CPU进行处理;而无需借助Cache的方式则直接从RAM中读取数据到CPU执行操作。
除非另有说明,默认情况下所有读取策略均遵循Read-allocate模式。

改写说明

在发生store hit时(即写命中),Cache的具体实现有两种策略:Write-through和Write-back。采用Write-through策略的Cache,在发生write hit时会将数据更新至Cache及主存(RAM)。相反,在采用Write-back策略的Cache中,则只会更新至Cache本身,并标记被更新行的状态为'dirty';只有当该行需要被替换时控制器才会将其数据写回内存。对于采用Write-back策略的Cache而言,在进行连续多次的数据写入操作时能够有效节省总线带宽,并展现出更好的性能水平;然而其缓存所存储的数据往往是最新状态的数据,在与主存中的数据存在不一致性的时候需要依赖软件机制来维护一致性保障。

在查看图中Write Data流程中的Miss情况时,请问 CPU如何处理未命中缓存的问题?

在进行数据写入操作时遇到Cache miss,则采用与读取数据时类似的处理方法。具体而言,存在两种不同的策略:Write-allocate和No write-allocate(即Write-around)。对于Write-allocate策略而言,在完成当前操作之前会首先分配一行存储空间并从RAM中读取相应数据填充之(相当于一个read miss followed by a refill的过程)。最终将该数据块写入缓存(此时若发生write hit,则会触发后续操作)。而No read-allocate策略则会直接跳过当前层级的Cache而不分配任何缓存行,并将所需的数据直接送至下一层缓存或内存层进行处理。值得注意的是其中一些MIPS架构同时支持这两种缓存策略

就上面所述,在Cache与内存交互这一领域确实存在一定的策略问题。通常采用Read-allocate、Write-back以及No write-allocate等策略来处理相关操作。对于具体的芯片实现细节,则建议参考其数据手册获取详细信息。

未深入的技术

虚拟地址Cache机制中,在缓存的地址索引部分是采用物理地址索引还是采用虚地址索引?其主要问题是错误共享问题与别名问题。

Cache在MIPS下的重影问题。

Cache实例(MIPS Cache)

软件具备实现哪些功能的问题?作为软件开发者,在这一领域中又应承担什么样的责任?本节则以MIPS Cache为例进行阐述时,则有助于更好地理解相关机制。

在编程过程中当处理涉及Cache的操作时需要特别关注以下几点:对于采用Write-back机制的Cache在连续多次写入数据时能够有效节约总线带宽其性能优于采用Write-through机制的方式然而由于该Cache所存储的数据通常是最近更新过的与内存中的数据多数时候并不一致因此必须通过软件手段来维护它们的一致性

对于特定一块内存区域而言,在初始化状态时内存与Cache中的数据是相同的。当程序开始运行并对这段数据进行修改时由于Cache采用Write-back方式修改后的数据会被存回Cache而内存仍保有原有的数据值。此时程序启动DMA机制将内存中的内容传输至外部设备然而这显然不是我们所期望的结果(因为我们希望传输的是经过计算的新数据仍位于Cache中)这正是问题所在为此我们需要在执行完数据计算后先完成DMA操作前确保将所有位于Cache中的新数据更新并重写回对应的内存位置这样就能保证一致性问题得以解决

值得指出的是,在软件参与的场景中非常常见的一种情况是:除了硬件本身的功能外,还需要由软件来完成额外的任务处理工作。此外,在现代计算机架构中还有一些类似的场景需要注意。接下来我们重点探讨一下MIPS架构中的指令级超管( superscalar)设计所预留的程序员缓存接口是如何设计的?

MIPS Cache软件接口

寄存器 TagHi和TagLo用于暂存Cache行的Tag中的数据,都是32位CP0寄存器。

MIPS体系结构仅设置了cache指令用于统一控制缓存,并将其格式如下所示。

cache ops, addr

ops在指令中占用5位编码长度,在其低位2位的位置上标识Cache的具体类型信息,在高位3位的位置上则决定了具体的操作执行方式。根据下表所示的数据格式设计,在实际应用中我们主要涉及一级I型指令型寄存器和双端型指令型寄存器两种类型的应用场景。

ops低2位 Cache的类型
00 L1 I-Cache
01 L1 D-Cache
10 L2 Cache
11 L3 Cache

下表中列出了ops高3位指定该Cache命令执行的操作。

Ops高3位 执行的操作
000 Index Invalidate
001 Index Load Tag
010 Index Store Tag
011
100 Hit Invalidate
101 Fill/Hit Writeback Invalidate
110 Hit Writeback
111 Fetch and Lock

有关MIPS Cache的软件接口设计可作为参考依据,在《See MIPS Run》一书中详细阐述了其中第4章关于MIPS缓存机制的工作原理中具体讨论了MIPS32和MIPS64缓存编程方法。同时可参考《The MIPS Cache Architecture》这篇文章中第4章对MIPS缓存控制器接口的设计与实现进行了深入解析。

初始化Cache

初始化过程分为两个主要步骤:首先需要启用Cache功能并配置相关寄存器参数。具体来说,在配置阶段需要执行以下操作:设置CP0 register中的config字段为初始值(即CP0 config 1),以及配置特定子系统(如brcm)的相关配置位(即CP0 brcm config 1)。这些操作将同时启用I-Cache和D-Cache功能,并激活Kseg区域(Kseg区域)中的Cache逻辑。完成上述配置后即可进入第二阶段:初始化阶段。在此阶段中,对于处于未知状态的所有Cache行数据块(即尚未被正确分配到特定存储区域的数据块),需要将它们对应的Tag字段字段清空(即Tag head置零),以确保数据块能够被正确地存储到相应的物理存储区域。
下面的代码段展示了如何实现这一关键操作。

复制代码
 ……  

    
     /* Calc an address that will correspond to the last cache line  */  
    
     addu    a3, a2, a0  
    
     subu    a3, a1  
    
   
    
     /* Loop through all lines, invalidating each of them */  
    
 1:    
    
      cache  ICACHE_INDEX_STORE_TA 0(a2)     /* clear tag */  
    
         bne a2, a3, 1b  
    
     addu    a2, a1  
    
 ……  
    
    
    
    
    AI助手

DMA操作和Cache

最后回答一下在本节开始举的那个问题,Cache和内存中的数据不一致,导致DMA需要软件参与Cache的操作。代码如下所示,如果是DMA要将内存中的数据传送到外设(DMA_TX),那么需要将Cache中的数据冲刷到内存(cacheFlush函数),保证内存中的数据是最新的数据;如果DMA将外设的数据传送到内存了,那么需要将Cache中的数据置位无效(cacheInvalidate函数),这样下次CPU取该部分数据时,就不会到cache中获取,而是获取内存中的最新数据。

复制代码
 ……  

    
 void*  
    
 osl_dma_map(void *dev, void *va, uint size, uint direction)  
    
 {  
    
     if (direction == DMA_TX)  
    
         cacheFlush(DATA_CACHE, va, size);  
    
     else  
    
         cacheInvalidate(DATA_CACHEva, size);  
    
     return ((void*)CACHE_DMA_VIRT_TO_PHYS(va));  
    
 }  
    
 ……  
    
    
    
    
    AI助手

该段代码选自网络驱动模块,DMA将网络数据包在内存和网卡之间传送。

全部评论 (0)

还没有任何评论哟~