cache基础解析

1. 初识cache

cache是一种容量小,速度快的存储器阵列。它位于主存和处理器内核之间,保存着最近一段时间处理器涉及到的主存块内容。在需要进行数据读取操作时,为了改善系统性能,处理器尽可能从cache中读取数据,而不是从主存中获取据。cache的主要目标就是,减小慢速存储器给处理器内核造成的存储器访问瓶颈问题的影响。

cache经常与写缓冲器(writebuffer)一起使用。写缓冲器是一个非常小的先进先出(FIFO)存储器,位于处理器核与主存之间。使用写缓冲器的目的是,将处理器核和cache从较慢的主存写操作中解脱出来。

cache基础解析_第1张图片

TCM是一个固定大小的RAM,紧密地耦合至处理器内核,提供与cache相当的性能,相比于cache的优点是,程序代码可以精确地控制什么函数或代码放在哪儿(RAM里)。当然TCM永远不会被踢出主存储器,因此,他会有一个被用户预设的性能,而不是像cache那样是统计特性的性能提高。TCM对于以下几种情况的代码是非常有用、也是需要的:可预见的实时处理(中断处理)、时间可预见(加密算法)、避免cache分析(加密算法)、或者只是要求高性能的代码(编解码功能)。

  • DRAM,即动态随机存储器,一般用于内存,需要不断地刷新电路,否则数据就消失了。
  • SRAM,即静态随机存储器,一般用于CPU中的cache(高速缓冲存储器),不需要不停地刷新电路来保存数据。

随着cache大小的增加以及总线性能的规模,TCM将会变得越来越不重要,但是他提供了一个让你权衡的机会 。Cache是一个通用目的的加速器,他会加速你的所有代码,而不依赖于存储方式。TCM只会加速你有意放入TCM的代码,其余的其他代码只能通过cache加速。Cache是一个通用目的解决方案,TCM在某些特殊情况下是非常有用的。假如你不认为需要TCM的话,那么你可能就不需要了,转而加大你的cache,从而加速运行于内核上的所有软件代码.

1.1 cache结构

带有cache的ARM内核采用了两种总线结构:冯·诺依曼结构和哈佛结构。这两种总线结构的区别在于,是否在内核与主存之间将指令和数据通道分离。分别有不同的cache设计来支持这两种结构。

在使用冯·诺依曼结构的处理器内核中,只有一个数据和指令公用的cache。这种类型的cache被称作统一cache(或混合cache),它可以存储指令和数据。

哈佛结构将指令总线和数据总线分离,以改善系统的综合性能,但是支持两种总线需要两种cache。所以在使用哈佛结构的处理器核中,存在两种cache:指令cache(I-cache)和数据cache(D-cache)。这种类型的cache被称作分离cache(splitcache)。在分离cache中,指令被存储在指令cache中,而数值被存储在数据cache中。

可以通过图1中的统一cache来了解cache的基本结构。cache的两个主要组成部分是cache控制器和cache存储器。cache存储器是一个专用的存储器阵列,其访问单元称为cache行。cache控制器使用处理器在访问存储器时所提供的地址的不同段,以选择cache存储器的不同部分。下面将首先介绍cache存储器的结构,接下来介绍cache控制器的一些细节。

cache基础解析_第2张图片

图12.4的右侧是一个简单的cache存储器。它有3个主要部分:目录存储段(directorystore)、状态信息段(statusinformation)和数据项段(datasection)。每一个cache行都由这3部分来表示。

cache必须知道cache存储器中的每个cache行所对应于主存中的位置,cache使用目录存储段来记录每个cache行是由主存的什么地方拷贝而来。该目录项被称作“cache标签”(cache-tag)。

同样,cache存储器必须存储来自主存的信息,这些信息被放在数据项段里(见图12.4)。

cache的大小是由cache可以存储的主存中实际数据和代码的大小决定的。在计算cache容量时,用来存储cache标签和状态信息位的那部分cache存储器不计算在内。

在cache存储器中,还有用来记录状态信息的状态位。2个常见的状态位是有效位(valid bit)和脏位(dirtvbit)。有效位用来标记当前的cache行是活动的,即该cache行中包含最初从主存中取得的数据,并可以为处理器内核所用。脏位则用来标记该cache行中所含的内容与主存中相应的内容是否一致。在4小节中,将会详细地解释脏位的含义。

1.2 cache控制器的基本操作

cache控制器是一种硬件,它将主存中的数据或者代码自动拷贝到cache存储器中。cache控制器在不为应用软件所知的情况下,自动完成搬移工作。所以,同一个应用软件不用修改,就可以在有cache和没有cache的系统中运行。

读/写存储器的请求在被传送到存储器控制器之前,会被cache控制器截获,cache控制器将该请求的地址信息分成3部分:标签域(tagfield)组索引域(setindexfield)和数据索引域(data indexfield)。3个位域分别见图12.4。

首先,控制器通过组索引域在cache存储器中确定可能包含所要求的代码和数据的cache行的位置,即确定某一cache行。cache行中还包含cache标签和状态位,控制器就是通过它们来确定数据的实际存储位置的。

接下来,控制器检查有效位,以确定该cache行当前是否处于活动状态,并且将请求地址的标签域的值与cache标签比较。如果cache行当前是活动的,并且标签域与cache标签的值也相同,则cache命中(hit);否则,称作cache失效(miss)。

在cache失效的情况下,控制器从主存中拷贝整个cache行到cache存储器中,为处理器核提供相应的代码或数据。这种拷贝整个cache行的操作被称作cache行填充(cache linefill)。

在cache命中的情况下,控制器直接从cache存储器中为处理器核提供数据和代码。控制器使用数据索引域,在cache行中选择实际的代码或数据,并将其提供给处理器内核。

1.3 cache与主存的关系

对cache存储器的基本结构和cache控制器的工作原理有了一定的了解之后,就可以来讨论cache与主存的关系了。

图12.5显示了主存中的部分内容是如何被临时存放在cache存储器中的。此图所示为最简单的cache形式一直接映射cache。在一个直接映射cache中,主存中的每个地址都对应cache存储器中帷一的一行。由于主存的容量要远远大于cache存储器,所以在主存中有很多地址被映射到同一个cache行。由图12.5可以看出这种关系,所有以0x824结尾的内存地址都映射在同一cache行。

图12.4中介绍的地址信息的3个域:标签域、组索引域和数据索引域,在图12.5中仍然可以体现出来。组索引域(setindex)可以确切地指出所有以0x824结尾的内存地址在cache中所惟一对应的存储地址;数据索引域可以确定字、半字或者字节在该cache行中的位置,在本例中是cache行中的第二个字;标签域则用来与cache行中的cachetag相比较。

cache基础解析_第3张图片

在本例中,cache中的每一行都对应着100万个内存地址。在任一给定时刻,这些地址中只有一个地址的内容可以出现在cache存储器中。将主存地址的标签域与cache行中的cache标签相比较,可以确定该cache行中是存储了处理器所要访回的主存单元数据,还是存储了另外一个地址以0x824结尾的主存单元数据。

在cache行被填充时,cache控制器可以在向cache中搬运数据的同时,将数据传送到处理器内核中,这被称作数据流注(datastreaming)。数据流注允许处理器一边执行程序,cache控制器一边向相应cache行中搬运剩余的数据和代码。

如果在某一cache行中的数据虽然是有效的,但是与之对应的是主存中其它的地址块,而非处理器所要求的地址,那么整个cache行中内容将被册除,并被替换为与处理器内核所要求的地址相对应的cache行。这种移动一个有效cache行的过程,是cache失效处理的一部分,被称作“替换”,即将cache行中的内容返回到所对应的主存单元中,给需要加载到cache的新数据留出空间。

1.4 直接映射导致的cache颠簸

直接映射cache是一种简单的解决方法,但这种设计使每个主存块在cache中只有一个特定的行可以存放。如果程序同时用到对应于cache同一行的2个主存块,那么就会发生冲突,冲突的结果就是导致cache行的频繁置换。这就是关于直接映射cache的颠簸(thrashing)问题:cache存储器中同一位置的软件冲突。

图12.6所示为一个简单的软件循环在cache中频繁置换的过程。该程序在一个 do while循环中反复调用2个子程序,每个子程序都拥有相同的组索引域地址,所以这2个子过程(程序)在主存中的物理地址会映射到cache中相同的行。当第一次执行该循环并执行到子程序A时,A被调人到cache行中。接下来当执行到子程序B时,A所在的cache行被替换,同时B被放人该行,并开始执行。当第二次循环执行到A时,A又把B从cache中替换出来,接着B再把A置换出去。重复的cache失效导致cache控制器连续不断地将当前不用的过程置换出cache,这就是cache颠簸。

cache基础解析_第4张图片

1.5 组相联

某些cache使用其它的设计方式,可以减少cache的颠簸频率(见图12.7)。这种设计改变了cache的构造;将cache存储器分成了一些相同容量的小单元,称作路(way)。图12.7所示仍旧是一个4KB的cache,但是与前面所讲的cache不同的是,一个组索引域对应于多个cache行,即在每一路里都有一个cache行与之对应。前面所讲的4KB容量的cache分为256行,现在把cache分成4路,每路有64个cache行。组索引域相同的4个cache行被称作处于同一个组(set)里,这也是组索引的命名的由来。

拥有相同组索引的cache行称为组相联的(setassociative)。主存中的数据或者代码块可以在不影响程序执行的情况下被分配到组相联的任意一路中。换句话说,将数据或者代码存入cache行中的操作不会影响程序的执行。==当主存中2个顺序的块被置换到cache中时,可以被放在同一路的连续cache行中,也可以被放在不同路中。需要注意的是,主存中特定位置的代码或者数据被读人到cache时,可以被存放在同一个组的任意cache行中。==在cache的同一个组当中,数据放置的位置具有排他性,可以防止同样的数据被重复放在一个组的不同的cache行。

cache基础解析_第5张图片

在4个way的组相联cache中,主存到cache的映射与以前有所不同(见图12.8)。主存中的一个地址现在可以映射到cache中的4个不同地址。虽然图12.5与图12.8所示都是4KB的cache,但它们当中还是有很多值得注意的差异。

在图12.8中,tag域比以前多了2位,而同时组索引域比以前少了2位。这意味着主存中的400万地址映射到cache一个组中的4个cache行中,而不是100万主存地址映射到一个cache位置。

现在,主存映射到cache中的大小是1KB而不是4KB。这意味着将数据块映射到同一组的cache行的可能性比以前增加了4倍,同时一个cache行被替换的概率也减小为原来的1/4。

cache基础解析_第6张图片

提高相联度

随着cache控制器的相联度提高,冲突的可能性减小了。理想的目标是,尽量提高组相联程度,使主存地址能够映射到任意cache行。这样的cache被称为全相联cache。然面,随着相联度的提高,与之相匹配的硬件的复杂程度也在提高。硬件设计者提高cache相联度的一种方法就是使用内容寻址存储器CAM(Content Addressable Memory)。

CAM使用一组比较器,以比较输入的标签地址和存储在每一个有效cache行中的cachetag。==CAM采用了与RAM相反的工作方式:RAM是得到一个地址后再给出数据;而CAM则是在检测到给定的数据值在存储器中后,再给出该数据的地址。==使用CAM允许同时比较更多的cachetag,从而增加了可以包含在一个组中的cache行数。在ARM920T和ARM940T处理器核中,ARM使用了CAM 来定位cachetag。ARM920T和ARM940T中的cache是64路组相联的。图12.9所示为ARM940T的cache结构图。cache控制器把地址标签(addresstag)作为CAM的输人,它的输出选择了包含有效cache行的路(way)。

cache基础解析_第7张图片

访问地址的tag部分被作为4个CAM的输入,输入标签同时与存储在64路中的所有cache标签相比较。如果有一个匹配,那么数据就由cache存储器提供;如果没有匹配,存储器控制器就会产生一个失效(miss)信号。控制器使用组索引位(setindex)来选择4个CAM中的一个。被选中的CAM会在cache存储器中选择一个cache行,该地址的数据索引部分(dataindex)在该cache行中选择出所需要的字、半字或者字节。

1.6 写缓冲器

写缓冲器是一个容量非常小的高速FIFO存储缓冲器,用来临时存放处理器将要写入到主存中的数据。在没有写缓冲器的系统中,处理器直接写数据到主存中。在带有写缓冲器的系统中,数据先高速写人FIFO,然后再写入低速的主存中。写缓冲器缩短了写小块序列数据到主存时的处理器时间。写缓冲器中的FIFO存储器在存储层次中,与L1cache处于相同的层次(见图121)。

写缓冲器的效率依赖于主存写的次数与执行指令数的比例。在给定的时间间隔中,若主存写的次数比较少,或者写操作与其它操作指令有足够的间隔,那么写缓冲器一般就不会满。在写缓冲器不满的情况下,运行程序可以使用寄存器操作来连续执行超出cache的访问。此时,使用cache进行读/写,使用写缓冲器来临时存放被替换出的cache行内容,并马上被写人主存。

写缓冲器同时还改善了cache的性能,这体现在cache行被替换时。当cache控制器要替换出一个脏的cache行时,它只将该cache行放入写缓冲器中,而不写人主存。这样,可以更快速填充新的cache行数据,处理器就可以继续从cache存储器中读/写数据。写缓冲器中的数据在没有被写人主存之前,是不能被读取的。同样,被替换的cache行在写缓冲器中时也不能进行读操作。这也是为什么写缓冲器的FIFO深度通常比较小的原因之一,一般只有几个cache行的深度。

有些写缓冲器并不是严格的FIFO缓冲器。例如,ARM10系列支持接合(coales-cing)-把写操作合并到一个单-的cache行。也就是说,写缓冲器会把新的数值(如果它们表示的是主存中同一个数据块)合并到一个在写缓冲器中已存在的cache行。接合又被称作写合并,写联合或写结合等。

1.7 cache效率的衡量

有2个性能指标可以衡量一个程序的cache效率:cache命中率(hitrate)和cache失效率(missrate)。在给定的时间间隔内,cache命中的次数与总的存储器请求次数的比值被称作命中率。命中率可以用下面的百分数来表示:

请添加图片描述

失效率与命中率形式相似:在给定的时间间隔内,cache失效的总次数除以总的存储器请求次数所得的百分数。失效率与命中率之和等于100。

命中率和失效率可以衡量数据的读/写,或者同时衡量读、写两者的效率。也就是说,这2个性能指标可以从几个方面来描述系统的性能情况。例如:可以计算读数据的命中率、写数据的命中率,或者其它方面操作的命中率和失效率等等。

另外两个衡量cache性能的指标是命中时间(hittime)和失效开销(misspenalty)。命中时间是指处理器访问cache中数据时所需要的时间。失效开销是指处理器从主存中装载一个cache行数据到cache所需要的时间。

2. cache策略

有3种可以决定cache操作的策略:写策略、替换策略及分配策略。cache的写策略决定了处理器执行写操作时数据存放的位置。替换策略在cache失效的情况下,决定选择被替换出主存的cache行。分配策略决定cache控制器在何时将要分配cache行。

2.1写策略

处理器核向存储器写数据时,cache控制器可以有2种可选择的写策略。它可以同时向cache行和相应的主存位置中写人数据,将存储在2个位置上的数据一起更新,这种做法被称为直写法(writethrough)。另外,它也可以只把数据写入相应的cache行,而不写入主存,只有当相应cache行被替换或清理cache行时,才被写入主存,这种做法被称为回写法(writeback)。

2.1.1直写法

如果cache控制器使用直写策略,那么处理器核写cache命中时,将同时修改cache和主存中的内容,以确保cache和主存数据的一致性。在这种策略下,处理器核在每次写cache时也要写相应的主存单元。由于要访问主存,直写法的速度比回写法要慢一些。

2.1.1回写法

如果cache控制器使用回写策略,那么处理器核写cache命中时,只向cache存储器写数据,而不立即写入主存。这样,主存块与相应的cache行数据有可能不一致。cache中的数据是最新的,而主存中的数据可能是较早的、没有被更新过的。

配置成回写法的cache要使用到cache行的状态信息块中的一个或多个脏位(dirtybit)。当回写cache控制器向cache存储器中某一行写入数据时,它会将脏位设置为1。如果控制器内核此后访问该cache行,那么通过脏位的状态就可以知道该cache行中含有主存中没有的数据。如果cache控制器要将一个脏位被置位的cache行替换出cache存储器,那么该cache行数据会自动被写到主存单元中去。控制器通过这种方式来防止只存在于cache中而主存中没有的重要信息的丢失。

当一个程序频繁使用某些临时的局部变量时,由于这些变量是临时的,所以根本用不着被写到主存中去。此时回写法cache优于直写法cache。例如:当寄存器文件没有足够的寄存器来存放临时局部变量时,就会导致部分变量溢出到一个cache堆栈中,这些临时变量就不需要写入主存。

2.2 cache行替换策略

当一个cache访问失效时,cache控制器必须从当前有效的组中选择一个cache行来存储从主存中取得的新信息。被选中替换的cache行被称为丢弃者(victim)。如果丢弃者中包含有效的脏数据,那么在该cache行被写入新数据之前,控制器必须把该行中的数据写到主存。选择和替换丢弃cache行的过程被称作淘汰(eviction)。

cache控制器选择下一个丢弃cache行的策略被称为替换策略。cache替换策略从当前有效的相联组中选择一个cache行,即它选择一路(way)用于下一次cache行替换。总的来说,组索引域在各个way中选择可用的一组cache行;而替换策略决定在该组中的哪一个cache行被新的数据所替换。

带cache的ARM核支持两种替换策略:伪随机替换法和轮转法。

  • 轮转法又叫循环替换,这种方法只是简单地将当前分配cache行的下一行作为被替换的行。它所采用的选择算法使用了连续加1的丢弃计数器,该计数器在每一次cache控制器分配新的cache行时都会自动加1。当丢弃计数器计数达到最大值时,就被复位成预先定义好的一个基值。
  • 伪随机替换法从特定的位置上随机地选出一行替换出去。该算法使用了非连续增加的丢弃计数器,控制器随机产生一个增加值,并将该增加值加到丢弃计数器上。同样,当丢弃计数器计数达到最大值时,会被复位成预先定义好的一个基值。

大多数ARM核都支持这两种替换策略(表121详尽列出了各种ARM核及其所支持
的策略)。相比之下,轮转法替换策略有更好的可预测性,容易预测最坏情况下cache的性能,这在嵌入式系统中是很必要的;然而,轮转法替换策略在存储器访问发生很小的变化时,有可能造成cache性能有较大的变化。

cache基础解析_第8张图片

2.3 cache失效时的分配策略

在cache失效发生时,ARM的cache可以采取两种策略来分配cache行;第一种叫做读操作分配(read-allocate)策略;第二种叫做读/写操作分配(read-writeallocate)策略。

如果cache未命中,那么对于读操作分配策略,只有进行存储器读操作时,才分配cache行。如果被替换的cache行包含有效数据,那么在该行被新的数据填充之前,要先把其原来的内容写人主存中去。

采用读操作分配策略时,存储器写操作并不会更新cache存储器中的内容,除非相关的cache行恰好是前一个主存读操作刚刚分配的。如果这个cache行中包含有效数据,那么在采用直写策略时,写操作更新cache的同时,还会更新主存中的相应内容。如果写操作的对象不在cache中,那么写操作只更新主存中的相应内容。

采用读/写操作分配策略时,不管是存储器读操作,还是存储器写操作,在cache未命中时都将分配cache行。对于主存的任何写操作,如果操作对象不在cache中,那么cache控制器也会分配一个新的cachc行,并把主存中的相应内容填充到该cache 行。对于存储器读操作,cache控制器运用读操作分配策略。

当内核进行数据写操作时,如果cache未命中,那么cache控制器将会分配一个cache行。如果被替换出的cache行中包含有效数据(validdata),那么在主存将新的内容放入该cache行之前,控制器会将该行的内容先写入主存。如果该行的数据无效,那么它将直接被主存中的新数据覆盖。分配的cache行被填充后,控制器才将内核数据写到该cache 行的相关位置。对于直写cache,数据将会同时被写人到主存中。

只要分配了cache行,即使是内核执行写操作,cache控制器也先要把主存的相关内容复制到所分配的cache行,内核数据才能写入该cache行中。

ARM7,ARM9和ARM10的内核在cache失效时使用读操作分配策略,Intel XScale在cache失效时可以同时支持读操作分配和写操作分配策略。表12.1列出了各种cache核所支持的各种策略。

3. 协处理器15与cache

协处理器15(CP15)的一些寄存器是专门用来配置和控制带cache的ARM内核的。表12.2列出了控制cache配置的协处理器15寄存器。CP15的主寄存器c7和c9控制着cache的设置和操作。辅寄存器CP15;c7是只写的,控制清除或清理cache;CP15;c9定义将被替换的丢弃者(victim)指针的基地址,该基地址决定了锁定在cache中的代码和数据的行数。后续章节中,将会详细讨论有关命令。有关协处理器 15的命令和句法,可参见3.5.2小节。

还有其它一些影响cache操作的CP15寄存器,这些寄存器的定义依赖于内核。有关这些寄存器的内容将在第13章的13.2.3 小节和13.2.4小节的MPU初始化,以及第14章的14.3.6小节的MMU初始化中介绍。

在接下来的几节中,将使用表12.2中列出的CP15寄存器编写清理和清除cache的示例程序,并在cache中锁定代码和数据。控制系统通常调用这些子程序作为存储管理的一部分。

cache基础解析_第9张图片

4. 清除和清理cache

ARM使用术语清除(flush)和清理(clean)表示对cache的两种基本操作。

清除cache的意思是清除cache中存储的全部数据。对处理器而言,清除操作只要清零相应cache行的有效位即可。当存储器配置上有变化时,整体或部分cache可能需要进行清除操作。有时也用术语作废(invalidate)来替代术语“清除”。然而,对于采用回写策略的 D-cache,就需要使用清理(clean)操作。

清理cache的意思是把脏的(即被改写过的)cache行强制写到主存,并把cache行中的脏位(污染位)清零。清理cache可以重建cache与主存之间的一致性,它只用在使用回写策略的D-cache上。

改变系统的存储器配置可能需要执行清除和清理cache的操作。访问权限、cache和缓冲策略的变化或者重新映射虚拟地址等操作都需要清理或清除cache。

在分离cache中执行自修改代码之前,cache也需要执行清理和清除操作。自修改代码包括将代码简单地从一个地方拷贝到另一个地方。清理和清除操作是由两种可能的情况引起的:第一,自修改代码可能被承载在D-cache中,因此,不可能作为一条指令从主存中进行加载;第二,I-cache中现存的指令可能会屏蔽写到主存中的新令。

如果cache使用回写策略并且自修改代码被写人主存中,那么第一步就是将指令以数据块的形式写到主存中的某处;稍后,程序跳转到主存中,以指令流的形式从主存中的该处开始执行。其中,当代码作为数据写入到主存中时,如果cache存储器中代表自修改代码被写入的主存位置的cache行有效,那么代码有可能会被写人到cache中(没有写人主存)。这些cache行会被拷贝到D-cache,而不是被拷贝到主存。如果发生了这种情况,那么当程序跳转到自修改代码所在地方(主存某处)时,就会执行原来数据表示的代码,因为自修改代码此时实际上还在D-cache中。为了防止这种情况发生,可以进行D-cache的清理操作,把指令代码强制作为数据存到主存中,从而这些数据就可以作为指令流从主存中被读取出来。

D-cache被清理后,新的指令就被写入到主存中。但是,I-cache 中可能会有有效 cache行存储新数据(代码)地址对应的指令。接下来,在新代码所在的地址读取指令时,仍然会得到I-cache中的老代码,而不是主存中的新代码。清除I-cache可以防止这种情况的发生。

CPU 和 RAM 之间存在多级高速缓存,一般分为 3 级,分别是 L1、L2、L3。
另外,我们的代码都是由两部分组成的:指令、数据。
L1 Cache 比较特殊,每个 CPU 会有两个 L1 Cache,分别为 iCache(指令高速缓存,Instruction Cache)和dCache(数据高速缓存,Data Cache)。
L2 和 L3 一般不区分指令和数据,可以同时缓存指令和数据。

cache基础解析_第10张图片

为什么要区分指令和数据呢?

原因是指令一般不会被修改,所以 iCache 在硬件设计上是只读的,这在一定程度上可以降低硬件设计成本。另外一方面是出于性能的考量,ARM 是哈佛结构,即指令存储和数据存储是分开的。硬件上地址总线和数据总线是分开的,地址总线取的数据就是指令,数据总线取的数据就是数据。CPU 在执行程序时,可以同时获取指令和数据,做到硬件上并行,提升性能。原文链接:https://blog.csdn.net/lyndon_li/article/details/131198944

4.1 清除cache

清除cache,即使cache 中的内容无效。如果cache使用回写策略,那么在清除之前应该清理cache,以防止由于清除操作使数据丢失。

有3个CP15:c7命令可以在cache中执行清除操作:第1个清除整个cache;第2个只清除 I-cache;第3个只清除D-cache。这些命令以及支持它们的内核见表12.3。对于这3个MCR指令,处理器内核寄存器Rd的值应该为零。

cache基础解析_第11张图片

4.2 清理cache

清理cache 即执行指令,命令cache控制器将所有带有脏位的D-cache行写入到主存中去。在这个过程中,cache行中的脏位将被清除。清理cache操作可以重建cache和主存之
间的数据的一致性。此操作仅适用于使用回写策略的D-cache。

术语回写(writeback)和回拷(copyback)在一些地方也通常表示清理(clean)的意思。这些术语与描述cache写策略的用词相近,但在这种情况下,它们描述的是对cache存储器
所作的操作。在非ARM系统中,术语刷新(flush)往往与ARM系统中清理(clean)意思相同。

4.3 清理D-cache

待以后补充

4.4 使用路和组索引寻址清理D-cache

有些ARM内核支持用路(way)和组(set)索引寻址来确定某一cache行在cache 中的位置,并清理和清除该单一的cache行。表12.5中以MCR指令的形式列出了清理和清除
一个 cache 行的命令。有2个命令可以用来清除一个cache行:清除指令cache行和清除数据cache行。其余的2个命令用来清理D-cache:清理cache行和清理并清除cache 行。

cache基础解析_第12张图片

列出的每个内核通过路和组索引寻址方式选定特定的某cache行。当使用这些指令时,在同样的ARM处理器内核上执行表12.5中的4条指令,内核寄存器Rd中的值是相同的;但是不同的处理器的寄存器位域格式是不同的。图12.10所示为支持通过路寻址方式清理和清除一个cache行的ARM内核的CP15:c7:Cm寄存器格式。在内核寄存器(Rd)中以相应的CP15:c7寄存器格式创建一个数值(value),就可以执行这些命令。寄存器通常包含两个位域(bit field):选择路和选择该路中的组。一旦寄存器被创建,执行相应的MCR指令就可以把内核寄存器(Rd)中的内容放到CP15:c7寄存器中。

4.5 使用test-clean命令清理D-cache

2种较新的ARM内核ARM926EJ-S和ARM1026EJ-S可以使用test-clean(测试清理)CP15:c7 寄存器清理cache行。test-clean命令是一条特殊的清理指令,它用在软件循环中可以非常有效地清理cache。ARM926EJ-S和ARM1026EJ-S同样也支持使用组索引和路索引来清理cache,但是使用test-clean命令清理cache可以更加高效。

在下面的程序中,使用表12.6中所示的命令清理ARM926EJ-S和ARM1026EJ-S内核。

cache基础解析_第13张图片

清理cache 可以通过创建一个使用test-clean命令的软件循环来实现。通过测试Z标志位并跳转重复测试,处理器循环测试直到D-cache被清理。需要注意的是,test-clean 命令使用程序计数器(r15)作为Rd寄存器到MCR指令的输入。

4.7 清理和清除部分cache

ARM内核支持通过访问主存中某一个cache行所对应的主存地址来清理和清除该cache行。表 12.8以MCR指令的形式列出了这些命令。这些命令中的2个可以用来清除单一的cache行:一个命令负责清除指令cache;另一个命令清除数据cache。另外2个命令用来清理数据cache:一个清理单一的cache行;另一个清理并清除一个cache行。

cache基础解析_第14张图片

当使用这些指令时,在同一个处理器上,内核寄存器Rd的值对于这4个命令是相同的,并且它的内容必须能够将CP15:c7寄存器置位;然而,对于不同的处理器,CP15:c7寄存器位值(bit value)的格式稍有不同。图12.11所示为支持清理和清除一个cache行的内核的寄存器格式。如果内核支持MMU,那么清理和清除一个cache行可以通过改变虚拟地址来实现;如果内核支持MPU,那么可以通过改变物理地址实现。

5. cache锁定

cache 锁定(lockdown)是cache的一项特性,使程序能够加载对时间要求很苛刻的代码和数据到cache中来,并将这些代码和数据标记为非替换(exempt of eviction)的。被锁定的代码和数据有更快的系统反映能力,因为这些数据和代码一直存放在cache中。cache 在正常操作时,经常会涉及到cache行替换,这种替换会带来代码执行时间不确定的问题,而cache 锁定可以避免这种不确定性。

将信息锁定在cache中的目的是,避免cache失效所造成的负面效果。然而,由于任何用作 cache 锁定的cache存储器单元不能够再存储主存的其它内容,所以,可用的cache 空间被减少了。

ARM内核为cache锁定分配固定的cache单元。一般来讲,分配作cache锁定的cache单元是一个路(way)。例如:一个4路组相联cache允许将锁定的代码和数据放在容量为cache 总容量的 1/4的cache单元内。带cache的内核通常至少保留一个路作为cache的正常操作使用。

有些指令往往需要被锁定在cache中,如中断向量表、中断服务程序以及为一些特殊算法编制的代码。这些算法被系统广泛使用且时间要求比较苛刻。对于数据来说,经常使用到的全局变量最好被锁定在cache中。

锁定在ARMcache中的数据和代码不会被替换。但是,如果cache 被清除,被锁定的信息也会丢失,存放锁定信息的cache存储区也不能被用作一般的cache存储区。必须重新运行cache锁定程序,以保存新的锁定信息。

5.1 在cache中锁定代码和数据

本小节将介绍如何在cache中锁定代码和数据。锁定代码和数据的典型C程序如下:

cache基础解析_第15张图片

开始时,中断被禁止,而cache是使能的。禁止中断的程序在这里没有表述。其中flushCache程序详见本章前几节的例子。实际使用的调用取决于cache的配置,而且很可能也包括 cache 的清理。

函数 lockDcache在D-cache中锁定一块数据;类似的,函数lockIcache在I-cache中锁定一个代码块。

执行 cache锁定的软件本身必须被存放在不可cache的(noncached)主存中。锁定在cache中的代码和数据必须被存放在可cache的(cached)主存中。被锁定在cache中的代码
和数据不能存在于cache中的其它地方,这一点非常重要。换句话说,如果cache中的内容未知,那么在装载之前应先清除cache。如果内核使用的是回写D-cache,那么应清理 D-
cache。一旦代码和数据被装载到cache,就可以重新使能中断了。

对于函数 lockDcache和函数lockIcache,这里提供了3种不同的实现代码,因为根据体系结构方式的不同,在cache中锁定代码有3种不同的方法。第1种锁定代码和数据的方
法使用了路(way)寻址技术;第2种使用了一组锁定位(lock bit);在第3种方法中,结合使用了特殊分配命令和读取主存中特定块这2种方法,来锁定代码和数据。

表 12.9列出了实现lockDcache和locklcache的3个例子、使用的方法以及相关的处理器。

cache基础解析_第16张图片

5.2 通过增加路索引来锁定cache

ARM920T,ARM926EJ-S,ARM940T,ARM946E-S,ARM1022E和ARM1026EJ-S使用路和组索引寻址来实现锁定。2个CP15:c9:c0寄存器中包含2.2小节中描述的丢弃者计数器的复位寄存器(reset registers)。这2个寄存器中的一个控制着I-cache,另一个控制D-cache。这些寄存器被用作在一个路中选择用来锁定数据和代码的cache行。

写到CP15:c7寄存器中的值用来设置丢弃计数复位值(victim reset value),即当丢弃者计数器的值增加到超出内核中路的数目时,丢弃者计数器被复位的值。系统上电时,复位值为 0,只有当cache中的某一部分被用作锁定时,复位值才由软件改变。当cache中的某一部分被用作锁定时,可用cache行的数量随被锁定的cache行数的增加而减少。读此寄存器,将返回当前的丢弃计数复位值。读和写这2个寄存器的MRC和MCR指令见表 12.10。

cache基础解析_第17张图片

当读/写锁定基地址时,对于不同的处理器来说,MRC和MCR指令使用的内核寄存器Rd的格式稍有不同。图12.12所示为使用这些指令的处理器的处理器内核Rd寄存器格式。为保证命令正确地执行,一定要使Rd寄存器的格式与图12.12所示一致。

cache基础解析_第18张图片

将指令锁定在cache中还需要一条特殊的装载命令。该命令复制与cache行相同大小的主存块到I-cache的cache行中。该命令以及Rd寄存器在该指令中使用的格式见表12.11和图12.13。

cache基础解析_第19张图片
cache基础解析_第20张图片

5.3 使用锁定位锁定cache

ARM926EJ-S和ARM1026EJ-S使用一组锁定位在cache中锁定代码和数据,如图 12.14所示。这两种处理器的CP15:c9指令使用不同的Rd格式,见表12.12。0~3四个位(bit)分别代表了在两种处理器的4路组相联cache中的4个路。如果某位被置位,那么它所对应的路就被锁定。对于D-cache,锁定了数据;对于I-cache,则锁定了代码。被锁定的路中的cache行,直到被解锁之后才能够被替换。将L位中的某一位清零,就可以解锁相应的路。这种锁定cache的方式使系统代码可以单独选择锁定和解锁的路。

cache基础解析_第21张图片

cache基础解析_第22张图片

这种单独选择锁定路的功能,使系统中代码的锁定和解锁更加容易。本小节中的示例程序使用了与其它带cache的内核相同的锁定数据的程序接口。

ARM926EJ-S和ARM1026EJ-S的lockDCache和lockICache示例程序有相同的输入参数。但是代码的大小受路的最大尺寸的限制,并且可以重复调用3次。在本小节的例
子中,锁定位3通常为cache专用的。这不是处理器硬件的限制,而仅仅是为了适应程序接口的需要,对过程调用做出的限制。

如果 size 参数是1字节或者更大,那么示例程序返回被锁定路的锁定位(L位)。如果size 参数为0,那么程序返回下一个可用的路的锁定位。如果没有可锁定的路,那么将返回8。

6. cache与软件性能

遵循一些简单的规则,将有助于利用cache的结构优势来编写代码。

存储器系统中的许多区域,通常都把cache和写缓冲器两者都使能,从而最大限度地利用cache 体系结构的优点来缩短平均访存时间。有关存储系统的不同部分、cache 配置和写缓冲器操作的更多信息,可参见后续章节。

如果把存储器映射的外设配置成使用cache或写缓冲器,那么通常会产生问题。所以最好将它们配置成不使用cache,并且不使用写缓冲器,这就强制处理器在每次访问时都去读外设端口,而不是从cache中读取陈旧的信息。

应尽可能将经常访问的数据顺序存放在主存中,因为从主存中获取一个新的数据的代价等同于填充整个cache行。如果一个cache行中的数据在被替换出cache之前只使用过一次,那么系统的性能就比较低。应尽可能地把数据放在同一个cache行中,以提高 cache命中率,因为这样可以充分利用局部性原理,从而形成更多的cache命中。最重要的一点是,要在主存中把一个共用例程所访问的数据尽量紧靠在一起。

应尽可能组织数据,使读、处理和写都在cache行尺寸的块中完成,并使主存块地址的低位与cache 行的起始地址匹配。

最通用的做法是使代码尽量小,并将相关的数据分组放在一起。代码尺寸越小,cache效率越高。

在cache 系统中,使用链表会降低程序性能,因为查表会导致很高的cache失效率。与从顺序数组中访问数据相比,从链表访问数据,程序将以更加随机的形式取数据。这一点在查找任何无序表时都须考虑。选用何种数据查找方法,可能需要对系统性能进行分析。

7. 总结

cache 是一个放置在处理器和主存之间的小容量高速存储器阵列。它是一个存储部分最近访问的主存内容的缓冲。相比于系统存储器,处理器在可能的情况下更优先使用cache存储器,以改善系统的平均性能。

写缓冲器是一个位于处理器内核与主存之间的非常小的先进先出(FIFO)存储器,它可以把处理器内核与cache存储器从低速的主存写操作中解脱出来。

局部性原理说明,程序在执行过程中会频繁运行小范围的循环代码,而这些代码会对数据存储器中的局部数据反复访问。它解释了为什么使用带cache的内核后,系统的平均性能会显著改善。

ARM组织(ARMcommunity)使用了许多条目来描述cache 体系结构的特性。为了方便,这里给出了表12.14,它列出了当前所有带cache的ARM内核的特性。

cache基础解析_第23张图片

cache 行是cache的基本组成单位,包含3部分:目录存储段(directorystore)、数据项段(data section)和状态信息段(status information)。cache标签(cache-tag)是一个目录记录项,指示一个cache行是从主存中的什么地方被装载的。在cache中通常有2个状态位:有效位和脏位。当相关的cache行包含有效的存储器内容时,有效位被置位;当cache使用回写策略并且有新的数据写入到cache行时,脏位被置位。

cache 的位置可以在MMU之前或之后,有物理cache和逻辑 cache 之分。逻辑 cache被放置在处理器内核与MMU之间,在虚拟地址空间访问代码和数据。物理cache 被放置在MMU和主存之间,使用物理地址访问代码和数据。

直接映射cache 是一种非常简单的cache结构,每一个主存地址都对应唯一的cache地址。直接映射cache经常会导致“颠簸”。为了减少“颠簸”,将cache分成容量相等的较小单元,这种小单元被称作路(way)。使用路为一个主存单元在cache中提供了对应的多个存储位置。这种cache被称作组相联cache。

内核总线的体系结构决定了cache系统的设计。冯·诺依曼结构使用统一cache存储代码和数据。哈佛结构使用分离cache:一个cache用于指令,另一个cache用于数据。

cache 替换策略决定了在访问cache失效时,哪一个cache行将被替换出cache。配置策略决定 cache控制器使用什么算法在当前cache存储器的组中选择一个cache行。被选中作替换的cache行被称作丢弃者。带cache的ARM内核使用两种替换策略:伪随机法和轮转法。

向cache中写数据有两种策略:如果控制器只更新cache存储器,称为回写(writeback)策略;如果cache控制器既写cache,又写主存,则称为直写(writethrough)策略。

当cache失效时,cache控制器使用两种策略分配一个新的cache行:读分配策略在数据由主存中读出时分配cache行;写分配策略在向主存中写数据时分配cache行。

ARM使用术语清理(clean)表示将D-cache中的数据写回到主存中。ARM使用术语清除(flush)表示使cache中的内容无效(作废)。

有些 ARM内核提供cache锁定功能。锁定允许代码和数据被装载到cache,并被标记为非替换的。
行。被选中作替换的cache行被称作丢弃者。带cache的ARM内核使用两种替换策略:伪随机法和轮转法。

向cache中写数据有两种策略:如果控制器只更新cache存储器,称为回写(writeback)策略;如果cache控制器既写cache,又写主存,则称为直写(writethrough)策略。

当cache失效时,cache控制器使用两种策略分配一个新的cache行:读分配策略在数据由主存中读出时分配cache行;写分配策略在向主存中写数据时分配cache行。

ARM使用术语清理(clean)表示将D-cache中的数据写回到主存中。ARM使用术语清除(flush)表示使cache中的内容无效(作废)。

有些 ARM内核提供cache锁定功能。锁定允许代码和数据被装载到cache,并被标记为非替换的。

你可能感兴趣的:(linux,嵌入式硬件,c语言)