Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1833806
  • 博文数量: 195
  • 博客积分: 4227
  • 博客等级: 上校
  • 技术积分: 2835
  • 用 户 组: 普通用户
  • 注册时间: 2010-09-04 10:39
文章分类

全部博文(195)

文章存档

2013年(1)

2012年(26)

2011年(168)

分类: LINUX

2011-05-24 16:46:32

之解

谨以此文纪念过往的岁月

一.前言

在嵌入式中cache的作用很重要,其用于加速数据和指令的获取,但是其也有一定的麻烦就是内存中数据改变而cache中的数据并没有改变,这就发生了传说中的不一致性。对于我们初学者而言对这个概念比较迷茫,本文就来看看cache是什么,cache到底是怎么工作的以及其的一些机制。是了解cache的很好网站。

二.cache之义

在现代CPU的主频能够达到几百兆,而主存的的存储周期仍然只有10~100ms,如果所有的数据以及指令都存放在主存中,其会大大制约系统的速度。所以cachebuffer就出现了,这两个比主存更加靠近CPU,其存取的速度远远大于主存。Cache就是负责了数据和指令的暂时性存储,用于CPU的加速。

三.cache的工作原理以及

cache的使用其实很简单,CPU发出一主存地址时,先会访问cache,如果cache中有该地址的数据(该数据不仅指数据还指指令,我们默认采用数据和指令统一cache),则从cache中获取数据,如果没有的话,就从主存中读取数据同时将数据以及其后的块(cache line)的数据搬运到cache中留给下次使用,因为cache是以cache line为单位与主存进行数据交换的。如果cache中的被填满了,则cache会采用一定的策略将部分数据替换出,很简单吧。

cache中主要是要理解cache的地址转换原理,就是CPU发出的主存地址如何转换层cache的索引。

1.       全相联映像方式

主存的任意一块可以映射到cache中的任一块。如果cache的块数为C,而主存的块数为M,则映射关系有C*M种。如果采用目录索引的方式来存放映射关系,而目录表的容量为C。就是用Cindex来保存每一个cache line中所对应的主存的cache line

2.       直接映像方式

主存的任意一块只能映射到cache的特定一块。其映射公式如:

b = B mod C(b cache中块号,B 主存中的块号,C cache中块容量)

3.       组相联映像方式

该方式是上述两种办法的联合。把主存和cache按同样的大小划分成set,每一个set有相同的块数。组与组之间采用直接映像,而两个对应的组内部采用全相联映像方式。在ARM中采用该方式。以cache的块大小为2(L),则同一块的各地址的的位[31:L]是相同的,如果cache中组的大小位2(S),则虚拟地址的位[L+S-1,L]用于选择cache中的某个组,虚拟地址中其他位[31:L+S]则包含一些标志位。

四.存储的一致性

也许对于我们新手而言,cache的一致性比较难以理解,怎么会出现CPU读取的数据or指令与主存中的值不同。对于没有cachewrite buffer是不可能出现的,因为所有数据的存储和读取只有一个地方主存,而在cache的系统中就不一样了,因为CPU往往会读取cache中的数值,而非主存中的数值,在主存的数据发生改变而cache所对应的数值并没有发生改变,这时候就发生了数据不一致性的情况。

不一致性可能的三种情况:

1.  虚拟地址和物理地址的映射发生改变。

2.  独立的数据和指令cache容易出现数据和指令的cache不一致性。

3.  DMA导致的不一致性。

五.arm1176cache地址转换

在地址转换中比较难以理解的是tagindex。下面的图形会让我们更好的理解前面的两个概念。

先来理解VIPTVIVT的概念,viptvirtually index physically tagindex的值是从虚拟地址中抽取,而tag的索引从物理地址中抽取,而vivt同理。也许干巴巴的说明很难理解,那就用模拟的C语言来说明估计会好理解很多。

16K D cache, 4-ways ,cache line 32bytes为例。先假设主存物理地址0x50000000~0x50004000这段地址内容在cache中,并且一一对应。

Unsigned long tag_index[4][128]={

{ 0x50000040, 0x50000060, 0x50000080,…},

{0x50001000,0x50001020, 0x50001040, 0x50001060, 0x50001080,…},

{0x50002000,0x50002020, 0x50002040, 0x50002060, 0x50002080,…}

{0x50003000,0x50003020, 0x50003040, 0x50003060, 0x50003080,…}

};

Unsigned char data[4][1024][4] ={

{{0,1,2,3},{1,2,3,4},{2,3,4,5},{3,4,5,6},{4,5,6,7},{5,6,7,8}…},

{{0,1,2,3},{1,2,3,4},{2,3,4,5},{3,4,5,6},{4,5,6,7},{5,6,7,8}…},

{{0,1,2,3},{1,2,3,4},{2,3,4,5},{3,4,5,6},{4,5,6,7},{5,6,7,8}…},

{{0,1,2,3},{1,2,3,4},{2,3,4,5},{3,4,5,6},{4,5,6,7},{5,6,7,8}…},

}

上述数组中的每一个值代表了tag sram中的每一个tag4代表set128 = 16K/4/32bytesline number。我们以虚拟地址0xC0000023为例,其物理地址为0x50000023

其对应的byte=0xC0000023&0x1F = 0x3 ,index=(0xC0000023&0xFE0)>>5 = 1,tag =(0x50000023& 0xFFF)>>12 = 0x50000

cache是如何索引的呢,其传输首先是0xC0000023这个虚拟地址计算其index = 1,则在4set中查找index1tag这四个tag分别为0x50000020,0x50001020,0x50002020, 0x50003020,同时该虚拟地址会送入MMU将该虚拟地址转换层物理地址为0x50000023,在将物理地址计算出来后,将物理地址的tag(bit[31:12])与上述tag进行比较,得出该物理地址所对应的set0,即tag_index[0][1]满足该虚拟地址。之后CPU则会获取data[0][0xC0000023&0xFFF]处得数据。也许有人会疑惑为什么不全用vaddr来实现转换。这个就涉及了一个aliasing的问题,当一个虚拟地址对应多个物理地址时,就会出现问题了。为什么,嘿嘿用脚趾头想吧!

六.cache操作

flush_cache_all为例来开始我们追寻cache操作之路。

#define flush_cache_all()                   __cpuc_flush_kern_all()

关于上面的宏定义最后是那个函数的执行,我们不看了,我直接说吧,是cache-v6.S中的

ENTRY(v6_flush_kern_cache_all)

         mov r0, #0

         mcr  p15, 0, r0, c7, c14, 0                  @ D cache clean+invalidate

         mcr  p15, 0, r0, c7, c5, 0               @ I+BTB cache invalidate

         mov pc, lr

上面的函数很简单,但是也不简单,为什么呢,这里有一个CP15协处理器,其主用主要是协助CPU存储管理。没有办法,CPU也不是万能的,俗话还说一个好汉三个帮啊。这里面我们也不扩展开,只看关于cache的内容。关于mcrmrc的的使用请看http://blog.chinaunix.net/space.php?uid=24517893&do=blog&id=253685 ,此处不多加赘述。

对于cache的操作主要是flush_cache_allflush_dcache_page前者将cache清空,后者则是将某一页的数据写会主存。

阅读(11865) | 评论(0) | 转发(1) |
0

上一篇:Android之HAL粗解

下一篇:FFT初解

给主人留下些什么吧!~~