跳至主要内容

【转】s3c2410 CACHES, WRITE BUFFER

作者:蔡于清
www.another-prj.com 

在上一篇文档中我向大家介绍MMU的工作原理和对s3c2410 MMU部分操作进行了讲解。我们知道MMU存在的原因是为了支持虚拟存储技术,但不知道你发现了没有,虚拟存储技术的使用会降低整个系统的效率,因为与传统的存储技术相比,虚拟存储技术对内存的访问操作多了一步,就是对地址进行查表(查找映射关系),必须先从虚拟地址中分解出页号和页内偏移,根据页号对描述符进行索引(这就是一个查表过程)得到物理空间的首地址,这样做的代价是巨大的(其实这也正是时间效率与空间效率之间矛盾的一个体现),对某些嵌入式系统来说这简直就是恶梦。那么在引入了虚拟存储技术之后有没有方法在时间效率与空间效率这个矛盾之间取得一个平衡点呢?答案是有,我们可以通过一种技术从最大限度上降低这两者的矛盾,这种技术是Caches(缓存)。也是我们本文要介绍的。

以下内容转载自中计报
Cache的工作原理
   Cache的工作原理是基于程序访问的局部性。
对大量典型程序运行情况的分析结果表明,在一个较短的时间间隔内,由程序产生的地址往往集中在存储器逻辑地址空间的很小范围内。指令地址的分布本来就是连续的,再加上循环程序段和子程序段要重复执行多次。因此,对这些地址的访问就自然地具有时间上集中分布的倾向。
数据分布的这种集中倾向不如指令明显,但对数组的存储和访问以及工作单元的选择都可以使存储器地址相对集中。这种对局部范围的存储器地址频繁访问,而对此范围以外的地址则访问甚少的现象,就称为程序访问的局部性。
根据程序的局部性原理,可以在主存和CPU通用寄存器之间设置一个高速的容量相对较小的存储器,把正在执行的指令地址附近的一部分指令或数据从主存调入这个存储器,供CPU在一段时间内使用。这对提高程序的运行速度有很大的作用。这个介于主存和CPU之间的高速小容量存储器称作高速缓冲存储器(Cache)。
系统正是依据此原理,不断地将与当前指令集相关联的一个不太大的后继指令集从内存读到Cache,然后再与CPU高速传送,从而达到速度匹配。
CPU对存储器进行数据请求时,通常先访问Cache。由于局部性原理不能保证所请求的数据百分之百地在Cache中,这里便存在一个命中率。即CPU在任一时刻从Cache中可靠获取数据的几率。
命中率越高,正确获取数据的可靠性就越大。一般来说,Cache的存储容量比主存的容量小得多,但不能太小,太小会使命中率太低;也没有必要过大,过大不仅会增加成本,而且当容量超过一定值后,命中率随容量的增加将不会有明显地增长。
只要Cache的空间与主存空间在一定范围内保持适当比例的映射关系,Cache的命中率还是相当高的。
一般规定Cache与内存的空间比为4:1000,即128kB Cache可映射32MB内存;256kB Cache可映射64MB内存。在这种情况下,命中率都在90%以上。至于没有命中的数据,CPU只好直接从内存获取。获取的同时,也把它拷进Cache,以备下次访问。
Cache的基本结构
Cache通常由相联存储器实现。相联存储器的每一个存储块都具有额外的存储信息,称为标签(Tag)。当访问相联存储器时,将地址和每一个标签同时进行比较,从而对标签相同的存储块进行访问。Cache的3种基本结构如下:
全相联Cache
在全相联Cache中,存储的块与块之间,以及存储顺序或保存的存储器地址之间没有直接的关系。程序可以访问很多的子程序、堆栈和段,而它们是位于主存储器的不同部位上。
因此,Cache保存着很多互不相关的数据块,Cache必须对每个块和块自身的地址加以存储。当请求数据时,Cache控制器要把请求地址同所有地址加以比较,进行确认。
这种Cache结构的主要优点是,它能够在给定的时间内去存储主存器中的不同的块,命中率高;缺点是每一次请求数据同Cache中的地址进行比较需要相当的时间,速度较慢。
直接映像Cache
直接映像Cache不同于全相联Cache,地址仅需比较一次。
在直接映像Cache中,由于每个主存储器的块在Cache中仅存在一个位置,因而把地址的比较次数减少为一次。其做法是,为Cache中的每个块位置分配一个索引字段,用Tag字段区分存放在Cache位置上的不同的块。
单路直接映像把主存储器分成若干页,主存储器的每一页与Cache存储器的大小相同,匹配的主存储器的偏移量可以直接映像为Cache偏移量。Cache的Tag存储器(偏移量)保存着主存储器的页地址(页号)。
以上可以看出,直接映像Cache优于全相联Cache,能进行快速查找,其缺点是当主存储器的组之间做频繁调用时,Cache控制器必须做多次转换。
组相联Cache
组相联Cache是介于全相联Cache和直接映像Cache之间的一种结构。这种类型的Cache使用了几组直接映像的块,对于某一个给定的索引号,可以允许有几个块位置,因而可以增加命中率和系统效率。
Cache与DRAM存取的一致性
在CPU与主存之间增加了Cache之后,便存在数据在CPU和Cache及主存之间如何存取的问题。读写各有2种方式。
贯穿读出式(Look Through)
该方式将Cache隔在CPU与主存之间,CPU对主存的所有数据请求都首先送到Cache,由Cache自行在自身查找。如果命中,则切断CPU对主存的请求,并将数据送出;不命中,则将数据请求传给主存。
该方法的优点是降低了CPU对主存的请求次数,缺点是延迟了CPU对主存的访问时间。
旁路读出式(Look Aside)
在这种方式中,CPU发出数据请求时,并不是单通道地穿过Cache,而是向Cache和主存同时发出请求。由于Cache速度更快,如果命中,则Cache在将数据回送给CPU的同时,还来得及中断CPU对主存的请求;不命中,则Cache不做任何动作,由CPU直接访问主存。
它的优点是没有时间延迟,缺点是每次CPU对主存的访问都存在,这样,就占用了一部分总线时间。
写穿式(Write Through)
任一从CPU发出的写信号送到Cache的同时,也写入主存,以保证主存的数据能同步地更新。
它的优点是操作简单,但由于主存的慢速,降低了系统的写速度并占用了总线的时间。
回写式(Copy Back)
为了克服贯穿式中每次数据写入时都要访问主存,从而导致系统写速度降低并占用总线时间的弊病,尽量减少对主存的访问次数,又有了回写式。
它是这样工作的:数据一般只写到Cache,这样有可能出现Cache中的数据得到更新而主存中的数据不变(数据陈旧)的情况。但此时可在Cache 中设一标志地址及数据陈旧的信息,只有当Cache中的数据被再次更改时,才将原更新的数据写入主存相应的单元中,然后再接受再次更新的数据。这样保证了Cache和主存中的数据不致产生冲突。
…
…..
你可以通过http://www.chinaunix.net/jh/45/180390.html阅读完全文


s3c2410 内置了指令缓存(ICaches),数据缓存(DCaches),写缓存(write buffer) , 物理地址标志读写区 (Physical Address TAG RAM),CPU将通过它们来提高内存访问效率。
我们先讨论指令缓存(ICaches)。
ICaches使用的是虚拟地址,它的大小是16KB,它被分成512行(entry),每行8个字(8 words,32Bits)。
 
ICaches.jpg (19.94 KB)
2007-3-26 15:41

当系统上电或重起(Reset)的时候,ICaches功能是被关闭的,我们必须往lcr bit置1去开启它,lcr bit在CP15协处理器中控制寄存器1的第12位(关闭ICaches功能则是往该位置0)。ICaches功能一般是在MMU开启之后被使用的(为了降低MMU查表带来的开销),但有一点需要注意,并不是说MMU被开启了ICaches才会被开启,正如本段刚开始讲的,ICaches的开启与关闭是由lcr bit所决定的,无论MMU是否被开启,只要lcr bit被置1了,ICaches就会发挥它的作用。
大家是否还记得discriptor(描述符)中有一个C bit我们称之为Ctt,它是指明该描述符描述的内存区域内的内容(可以是指令也可以是数据)是否可以被Cache,若Ctt=1,则允许Cache,否则不允许被Cache。于是CPU读取指令出现了下面这些情况:
1.如果CPU从Caches中读取到所要的一条指令(cache hit)且这条指令所在的内存区域是Cacheble的(该区域
     所属描述符中Ctt=1),则CPU执行这条指令并从Caches中返回(不需要从内存中读取)。
2.若CPU从Caches中读取不到所要的指令(cache miss)而这条指令所在的内存区域是Cacheble的(同第1点),则CPU将从内存中
     读取这条指令,同时,一个称为"8-word linefill"的动作将发生,这个动作是把该指令所处区域的8个word写进
     ICaches的某个entry中
,这个entry必须是没有被锁定的(对锁定这个操作感兴趣的朋友可以找相关的资料进行了解)
3.若CPU从Caches中读取不到所要的指令(cache miss)而这条指令所在的内存区域是UnCacheble的(该区域所属描
     述符中Ctt=0),则CPU将从内存读取这条指令并执行后返回(不发生linefill)

通过以上的说明,我们可以了解到CPU是怎么通过ICaches执行指令的。你可能会有这个疑问,ICaches总共只有512个条目(entry),当512个条目都被填充完之后,CPU要把新读取近来的指令放到哪个条目上呢?答案是CPU会把新读取近来的8个word从512个条目中选择一个对其进行写入,那CPU是怎么选出一个条目来的呢?这就关系到ICaches的替换法则(replacemnet algorithm)了。ICaches的replacemnet algorithm有两种,一种是Random模式另一种Round-Robin模式,我们可以通过CP15协处理器中寄存器1的RR bit对其进行指定(0 = Random replacement 1 = Round robin replacement),如果有需要你还可以进行指令锁定(INSTRUCTION CACHE LOCKDOWN)。
关于替换法则和指令锁定我就不做详细的讲解,感兴趣的朋友可以找相关的资料进行了解。

接下来我们谈数据缓存(DCaches)和 写缓存(write buffer)
DCaches使用的是虚拟地址,它的大小是16KB,它被分成512行(entry),每行8个字(8 words,32Bits)。每行有两个修改标志位(dirty bits),第一个标志位标识前4个字,第二个标志位标识后4个字,同时每行中还有一个TAG 地址(标签地址)和一个valid bit。
与ICaches一样,系统上电或重起(Reset)的时候,DCaches功能是被关闭的,我们必须往Ccr bit置1去开启它,Ccr bit在CP15协处理器中控制寄存器1的第2位(关闭DCaches功能则是往该位置0)。与ICaches不同,DCaches功能是必须在MMU开启之后才能被使用的。
我们现在讨论的都是DCaches,你可能会问那Write Buffer呢?他和DCaches区别是什么呢? 其实DCaches和Write Buffer两者间的操作有着非常紧密的联系,很抱歉,到目前为止我无法说出他们之间有什么根本上的区别(-_-!!!),但我能告诉你什么时候使用的是DCaches,什么时候使用的是Write Buffer.系统可以通过Ccr bit对Dcaches的功能进行开启与关闭的设定,但是在s3c2410中却没有确定的某个bit可以来开启或关闭Write Buffer…你可能有点懵…我们还是来看一张表吧,这张表说明了DCaches,Write Buffer和CCr,Ctt (descriptor中的C bit),Btt(descriptor中的B bit)之间的关系,其中"Ctt and Ccr"一项里面的值是 Ctt与Ccr进行逻辑与之后的值(Ctt&&Ccr).
 
DCaches.jpg (114.94 KB)
2007-3-26 15:41

从上面的表格中我们可以清楚的知道系统什么时候使用的是DCaches,什么时候使用的是Write Buffer,我们也可以看到DCaches的写回方式是怎么决定的(write-back or write-througth)。
在这里我要对Ctt and Ccr=0进行说明,能够使Ctt and Ccr=0的共有三种情况,分别是
Ctt =0, Ccr=0
Ctt =1, Ccr=0
Ctt =0, Ccr=1
我们分别对其进行说明。
情况1(Ctt =0, Ccr=0):这种情况下CPU的DCaches功能是关闭的(Ccr=0),所以CPU存取数据的时候不会从DCaches里进行数据地查询,CPU直接去内存存取数据。
情况2(Ctt =1, Ccr=0):与情况1相同。
情况3(Ctt =0, Ccr=1):这种情况下DCaches功能是开启的,CPU读取数据的时候会先从DCaches里进行数据地查询,若DCaches中没有合适的数据,则CPU会去内存进行读取,但此时由于Ctt =0(Ctt 是descriptor中的C bit,该bit决定该descriptor所描述的内存区域是否可以被Cache),所以CPU不会把读取到的数据Cache到DCaches(不发生linefill).

到此为止我们用两句话总结一下DCaches与Write Buffer的开启和使用:
1.DCaches与Write Buffer的开启由Ccr决定。
2.DCaches与Write Buffer的使用规则由Ctt和Btt决定。


DCaches与ICaches有一个最大的不同,ICaches存放的是指令,DCaches存放的是数据。程序在运行期间指令的内容是不会改变的,所以ICaches中指令所对应的内存空间中的内容不需要更新。但是数据是随着程序的运行而改变的,所以DCaches中数据必须被及时的更新到内存(这也是为什么ICaches没有写回操作而DCaches提供了写回操作的原因)。提到写回操作,就不得不提到PA TAG 地址(物理标签地址)这个固件,它也是整个Caches模块的重要组成部分。
简单说PA TAG 地址(物理标签地址)的功能是指明了写回操作必须把DCaches中待写回内容写到物理内存的哪个位置。不知道你还记不记得,DCaches中每个entry中都有一个PA TAG 地址(物理标签地址),当一个linefill发生时,被Cache的内容被写进了DCaches,同时被Cache的物理地址则被写入了PA TAG 地址(物理标签地址)。除了TAG 地址(标签地址),还有两个称为dirty bit(修改标志位)的位出现在DCaches的每一个entry中,它们指明了当前entry中的数据是否已经发生了改变(发生改变简称为变"脏",所以叫dirty bit,老外取名称可真有意思 -_-!!!)。如果某个entry中的dirty bit置位了,说明该entry已经变脏,于是一个写回操作将被执行,写回操作的目的地址则是由PA TAG 地址(物理标签地址)索引到的物理地址。

关于Caches,Write Buffer更详细的内容请大家阅读s3c2410的操作手册:]

评论

此博客中的热门博文

【转】cs8900网卡的移植至基于linux2.6内核的s3c2410平台

cs8900网卡的移植至基于linux2.6内核的s3c2410平台(转) 2008-03-11 20:58 硬件环境:SBC-2410X开发板(CPU:S3C2410X) 内核版本:2.6.11.1 运行环境:Debian2.6.8 交叉编译环境:gcc-3.3.4-glibc-2.3.3 第一部分 网卡CS8900A驱动程序的移植 一、从网上将Linux内核源代码下载到本机上,并将其解压: #tar jxf linux-2.6.11.1.tar.bz2 二、打开内核顶层目录中的Makefile文件,这个文件中需要修改的内容包括以下两个方面。 (1)指定目标平台。 移植前:         ARCH?= $(SUBARCH) 移植后: ARCH            :=arm (2)指定交叉编译器。 移植前: CROSS_COMPILE ?= 移植后: CROSS_COMPILE   :=/opt/crosstool/arm-s3c2410-linux-gnu/gcc-3.3.4-glibc-2.3.3/bin/arm-s3c2410-linux-gnu- 注:这里假设编译器就放在本机的那个目录下。 三、添加驱动程序源代码,这涉及到以下几个方面。(1)、从网上下载了cs8900.c和cs8900.h两个针对2.6.7的内核的驱动程序源代码,将其放在drivers/net/arm/目录下面。 #cp cs8900.c ./drivers/net/arm/ #cp cs8900.h ./drivers/net/arm/ 并在cs8900_probe()函数中,memset (&priv,0,sizeof (cs8900_t));函数之后添加如下两条语句: __raw_writel(0x2211d110,S3C2410_BWSCON); __raw_writel(0x1f7c,S3C2410_BANKCON3); 注:其原因在"第二部分"解释。 (2)、修改drivers/net/arm/目录下的Kconfig文件,在最后添加如...

【转】嵌入式开发中使用DDD进行调试

 在嵌入式程序开发过程中,程序员要进行大量的调试,以此验证程序的正确性,修改潜在的错误。调试器对于程序员来说是不可或缺的必备工具。在Linux环境中,有很多调试工具和调试辅助工具,例如GDB、XXGDB、RHIDE、XWPE、GVD和DDD等。其中,DDD是命令行调试器的图形前端,除了一般的程序调试功能以外,还具有交互式图形数据显示的功能。它在嵌入式应用开发中也十分出色。本文主要讲述DDD(Data Display Debugger)的使用方法。      1.认识DDD     GNU DDD是命令行调试程序,如GDB、DBX、WDB、Ladebug、JDB、XDB、Perl Debugger或Python Debugger的可视化图形前端。它特有的图形数据显示功能(Graphical Data Display)可以把数据结构按照图形的方式显示出来。DDD最初源于1990年Andreas Zeller编写的VSL结构化语言,后来经过一些程序员的努力,演化成今天的模样。DDD的功能非常强大,可以调试用C\C++、Ada、 Fortran、Pascal、Modula-2和Modula-3编写的程序;可以超文本方式浏览源代码;能够进行断点设置、回溯调试和历史纪录编辑;具有程序在终端运行的仿真窗口,并在远程主机上进行调试的能力;图形数据显示功能(Graphical Data Display)是创建该调试器的初衷之一,能够显示各种数据结构之间的关系,并将数据结构以图形化形式显示;具有GDB/DBX/XDB的命令行界面,包括完全的文本编辑、历史纪录、搜寻引擎。     DDD是开源软件,用户可以去 http://www.cs.tubs.de/softech/ddd/ 下载.rpm格式的DDD源码文件。      图1显示的是DDD的主窗口。它主要由选单栏、工具条、数据窗口、源文件窗口、机器码窗口、控制台和命令工具窗口等几部分组成。其中,数据窗口用于观察复杂的数据结构,在删除数据之后,显示仍然有效;源文件窗口显示源代码、断点和当前执行到达的位置,选择该窗口中的"Display"项,可以显示任意表达式的值;机...

【转】函数指针和指针函数

【函数指针】        在程序运行中,函数代码是程序的算法指令部分,它们和数组一样也占用存储空间,都有相应的地址。可以使用指针变量指向数组的首地址,也可以使用指针变量指向函数代码的首地址,指向函数代码首地址的指针变量称为函数指针。 1.函数指针定义 函数类型 (*指针变量名)(形参列表); "函数类型"说明函数的返回类型,由于"()"的优先级高于"*",所以指针变量名外的括号必不可少,后面的"形参列表"表示指针变量指向的函数所带的参数列表。 例如: int (*f)(int x); double (*ptr)(double x); 在定义函数指针时请注意:     函数指针和它指向的函数的参数个数和类型都应该是―致的; 函数指针的类型和函数的返回值类型也必须是一致的。 2.函数指针的赋值 函数名和数组名一样代表了函数代码的首地址,因此在赋值时,直接将函数指针指向函数名就行了。 例如, int func(int x);   /* 声明一个函数 */ int (*f) (int x);    /* 声明一个函数指针 */ f=func;            /* 将func函数的首地址赋给指针f */ 赋值时函数func不带括号,也不带参数,由于func代表函数的首地址,因此经过赋值以后,指针f就指向函数func(x)的代码的首地址。 3.通过函数指针调用函数 函数指针是通过函数名及有关参数进行调用的。 与其他指针变量相类似,如果指针变量pi是指向某整型变量i的指针,则*p等于它所指的变量i;如果pf是指向某浮点型变量f的指针,则*pf就等价于它所指的变量f。同样地,*f是指向函数func(x)的指针,则*f就代表它所指向的函数func。所以在执行了f=func;之后,(*f)和func代表同一函数。 由于函数指针指向存储区中的某个函数,因此可以通过函数指针调用相应的函数。现在我们就讨论如何用函数指针调用函数,它应执行下面三步: 首先,要说明函数指针变量。 例如:int (*f)...