⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 chapter4.htm

📁 mips run 中文版
💻 HTM
📖 第 1 页 / 共 4 页
字号:
<BR>cache的地址tag存有除了用来查询cache index的其他所有位;因此主cache 
tag的长度会因为最大物理地址(R4x000是36bit)和用来索引cache位数不同而不同。13bit用来作为最初R4000的8KB大小主cache的索引,从此就再也没有小于这个位数。这样tag长度就有23bit,并且TagLo是24bit;在目前的cpu内TagHi总是零。这对于最小cache大小或是可以支持的最大物理地址是很重要的。对于R4000,现在TagHi是多余的;把它设为零并忘了它。 
<BR>所以TagLo寄存器内包含对应cache 
line的tag的所有位。TagLo(Pstate)还包含状态位。在绝大多数情况下(多处理器)这将变得非常复杂,但对所有cache的管理和初始化它足够表明当Pstate为零是一个合法的值来对应一条无效的cache 
entry。 <BR>这个区域被后来的cpu占用,用来储存第二层cache的状态信息,但这成了一个惯例值为零是安全和合适的对于初始化。 
<BR>最后,TagLo(p)是一个奇偶位,设一为整个cache 
tag偶校验。为全零的TagLo表明正确地偶校验。一些cpu忽视这个bit,而不去检查它,而且也没有危害。 <BR><BR>4.10.1 CacheERR, 
ERR, and ErrorEPC Register:Cache ErrorHandling 
<BR>CPU的cache是内存系统的至关重要的一部分,对于高效的实用或正确系统可以发现用额外的位来表明存储在这儿的数据的完整性是值得的。 
<BR>内存系统的校验将首尾相接理想化地被执行下去;当数据一被产生或被传入系统校验位就会被计算,随着数据被存放,并在数据被使用前被检查。That way the 
check catches faults not just in the memory array but in the complex buses and 
gizmos that data passes through on its way to the CPU and back. 
这样检查的方法将不在内存队列中抓住错误而在总线上,并通过这样的方式转送到cpu和返回。 
<BR>因为这个原因,R4x000地CPU(设计应用于大型计算机)在cache里提供错误校验。和主存系统一样,你既可使用简单的奇偶校验或者使用错误纠正码(ECC)。 
<BR>奇偶校验是简单的使用一个额外的bit来对应内存中每一个byte。一个奇偶错误可以告诉系统这个数据是不可靠的,并允许有些控制停止来代替creeping随机错误。奇偶校验的一个至关重要的任务就是系统开发的过程中提供巨大的帮助,因为它不能明确的指出由于内存数据完整性导致的问题。 
<BR>但一个byte的废物将有百分之五十的机会有一个正确地奇偶校验,并且在72位的数据总线上的随机垃圾256次中将有一次没有被发现。一些系统可能会好一点。 
<BR>错误纠错码计算起来将更加复杂,因为对于一个64位的数据将有8个bit的校验位。这将是十分的彻底,一个bit的错误将被唯一的指出并纠正,任何两个bit的错误也不会被忽视。在非常大的内存队列中ECC将从本质上排除随机错误。 
<BR>因为纠错码一次可以检查整个64位的数据,所以使用纠错码的内存不能进行小于一个字的数据的写操作,被选中的小于一个字的数据必须被并入新的数据并重新计算纠错码。MIPS 
cpu在cache关闭的情况下需要内存系统能进行小于一个字数据的写操作,这将使事情变得复杂。内存系统硬件必须将一个小于一个字数据的写操作转变为先读然后合并,接着重新计算,最后在写入的操作序列。 
<BR>对于简单的系统一般的选择是奇偶校验位,而不是其他的。让采用校验方式变成可选择的这是很有意义的,这样在设计研发过程中将有利于诊断,而在成为产品时却不用付出相应的代价。 
<BR>无论检查机制是运行在内存系统中还是在R4x00的cache中,cpu将提供一个字节对应的奇偶校验位,或是对应64位的8bit纠错码,或者就是干脆没有保护。 
<BR>当支持错误检测时,数据的检测位在cache填充时通常是直接通过系统接口到cache内存放而不被检查。只有在数据被使用时才检查,这样可以保证任何cache奇偶异常都被转交给引起它的指令,而不是转交给使用共同的cache 
line。当作一个退化的情况,一个在非cache的取指错误会被标志成cache奇偶错误,这种情况会使你很混乱。 
<BR>注意,系统接口标志进来的数据为没有合法的检查位是有可能的。在这种情况下,cpu会为它内部的cache重新产生检查位。 
<BR>如果一个错误发生了,cpu会产生一个特殊的错误陷阱。这个vector会直达一个非cache的位置(如果cache内是错误的数据,它会很愚蠢地去执行cache内地代码)。如果系统采用ECC,当写操作时硬件会产生纠错位,当错误时会检查得到。硬件并不知道怎么纠错;这将是软件的工作。 
<BR>ERR寄存器(如图4.4)的格式如下: <BR>a. ER/ED/ET/EE/EB: 这些位将区别是什么cache(主cache 
或第二层cache,指令cache还是数据cache)发生了错误,或是它在系统接口之外。 <BR>b. PidX: 给出错误位置的cache 
index。你可以取得这儿的内容用于index类型的cache操作;它将得到正确的line,而不管cache是直接映射还是组相连的。 
<BR><BR>当错误发生了,ErrorEPC寄存器指向发生错误的指令位置。ERR寄存器保存着ECC位,你需要用它来纠正可以改正的错误,但在这儿我们将不再讲如何做--因为这需要很大的篇幅,你将需要密切联系处理器手册。你将可以得到一些简单的针对mips运算规则的代码。 
<BR>4.10.2 The Cache Instruction cache 指令 
<BR>Cache指令有着和MIPS存储指令类似的格式(拥有通常寄存器再加上十六位有符号的偏移地址),但表示数据寄存器的值会被译成选择区表示是什么cache指令。这儿没有标准的名字来表示这些cache操作;在这儿我就武断地使用来自SDE-MIPS算法库的名字,这些名字依次是基于SCI/MIPS的一个头文件(include 
files)。选择区不是完全的位编码,但是几乎是;看表4.2。 <BR>Cache选择区可以让你做下面这些选择: <BR>a. 那类cache: 
选择是icache还是dcache,是主cache还是第二层cache。因为没有多余的位存在,所以还没有提供的三层cache的选择。但这儿我要提醒你这是和CPU密切有关的,在R4000之后的64位CPU提供对R4000兼容性是非常有帮助的。 
<BR>b. How cache is 
addressed:有两种不同的类型。如果是命中方式,你需要提供正常的程序地址(虚拟地址),其必须被转化。如果提供的地址确实在cache中,则该操作会对应相应的cache 
line被完成;如果不再cache中,那就什么也不用做。 <BR>另一种是index方式。地址低位用来直接选中某一个cache 
line,而不管这条line现在有什么内容。这显现出cache内部组织的没有原则性。 
<BR>cache的维护通常是需要命中方式,而初始化时就需要index方式。 <BR>c. 回写(write 
back):如果对应的line是dirty,就将数据回写到内存中去;如果不是,就像是一条nop指令。 <BR>d. 
invalidate(使无效):将这条line标志为无效,使其的数据不能在被使用。同时做回写和无效是可能的;但这不是自动的,如果你需要你可以使一条dirty的line无效。所以一些应用程序可能会丢数据。 
<BR>e. Load/store 
tags:这些操作是将对应line内的tag内容存到TagLo和TagHi寄存器中或从这两个寄存器读到对应line的tag内。 
<BR>存储tag使用比较过时的方式(TagLo和TagHi寄存器要预先设为零),是cache初始化的一部分。 <BR>f. 
Fill:这是仅仅为I-cache设计的,这个操作通过特殊的内存地址来填充一条cache 
line。对于填充dcache是没有必要的,因为当cache打开的时候读取没有命中时就会达到同样的效果。 <BR>g. Create 
data:这类操作是能够让用户能以很高的速度来写内存的排列,而避免任何的cache重新填充。除非你能保证在数据被使用或被回写到内存中之前覆盖这些所有的数据。 
<BR>这个特性对于初始化和诊断很有帮助(你将在后面初始化第二层cache的代码例子中看到,并知道如何清除第二层cache的数据)。 
<BR><BR>4.10.3 计算cache的大写和决定怎样配置 <BR>对于R4x00 
CPU(和绝大多数后面的CPU)主cache大小和line的大小会同过CP0的Config寄存器可靠的给出。 
<BR>但要得出你的cache是直接映射的还是组相连的却是相当的困难。但对于正在运行的cache这就不难测试出来了,你只要参考两个不可能同时出现在直接映射的cache中的地址,然后使用index类的操作去检查看它们是不是同时在cache内存在;当然如果你还没有初始化cache,这是没有用的。幸运的是你可以只写同一个程序来初始化直接映射cache或是组相连cache。 
<BR><BR>4.10.4 初始化程序 <BR>这儿将介绍一个很好的方法: 
<BR>1.开辟一些内存对应任意的数据,但如果你的系统使用奇偶校验码或是纠错码,你必须保证这是正确的,并用这些数据填充cache。(在算法库程序中我们保留至少32K的系统内存一直到cache初始化的时候;只要在cache关闭的时候去写这些内存,就能得到正确的奇偶码。)还需要一个足够的空间来初始化的二层cache;我们将采用迂回的方式来处理。 
<BR>2.将TagLo寄存器设为零,这样能保证对应line有效的那一位没有被置起来并且tag的奇偶码是一致的。 <BR>TagLo寄存器被cache 
Store_Tag指令使用,强制使对应的line无效和清除tag的奇偶码。 <BR>3.屏蔽中断,不然会有一些意外发生。 
<BR>4.先初始化Icache,然后是Dcache。下面是初始化Icache的C代码。(你必须相信像Index_Store_Tag_I()这样的函数或是宏能作底层的操作;它们或是琐碎的汇编代码子函数,能够运行在相应指令的机器上,或是对应GUN 
C用户通过宏调用一个C嵌入汇编。) <BR><BR>for (addr = KSEG0; addr &lt; KSEG0 + size; addr += 
lnsize) <BR>{ <BR>/* clear tag to invalidate */ <BR>Index_Store_Tag_I(addr); 
<BR>/* fill so data field parity is correct */ <BR>Fill _I (addr); <BR>/* 
invalidate again – prudent but not strictly necessay */ <BR>Index_Store_Tag_I(); 
<BR>} 
<BR><BR>5.Dcache的初始化相对来说要复杂一些,因为没有对应Dcache的Index_Fill_D操作;我们只能通过从cache读取数从而依靠通常的没有命中过程来达到目的。依次当cache填充指令对应index操作时,读取工程会依靠内存地址通过tag来命中一条line。你必须非常小心tag;对应two-way的cache,用初始化Icache的循环来初始化Dcache会将Dcache的一半初始化两次,因为清除PTagLo会重新设置用来决定下一次没有命中时是那一组cache 
Line的位。下面是正确的方法。 <BR><BR>/* clear all tags */ <BR>for (addr = KSEG0; addr 
&lt;KSEG0 + size; addr += lnsize) <BR>Index_Store_Tag_D (addr); <BR>/* load from 
each line (in cached space) */ <BR>for (addr = KSEG0; addr &lt; KSEG0 + size; 
addr += lnsize) <BR>junk = *addr; <BR>/* clear all tags */ <BR>for (addr = 
KSEG0; addr &lt; KSEG0 + size; addr += lnsize) <BR>Index_Store_Tag_D (addr); 
<BR><BR><BR>4.10.5 Invalidating or Writing Back a Region of Memory in the Cache 
<BR>对于对应一些I/O空间的程序或物理地址的范围,用于无效或回写的参数是不变的。 
<BR>你几乎总是用命中类型的cache指令来使cache内需要的位置来使其无效或回写。如果你需要将内存一个巨大范围使其无效或回写,使用index类型的指令来使整个cache无效或回写会比较快,虽然这是一个最优化的方法,但你很可能会忽视。 
<BR>我们有足够的理由这样做: <BR><BR>PI_cache_invalidate(void *buf, int nbytes) <BR>{ 
<BR>char *s; <BR>for (s = (char*)buf; s &lt; buf + nbytes; s += lnsize) 
<BR>Hit_Invalidate_I (s); <BR>} 
<BR><BR>注意这儿没有必要产生特殊的地址,只要buf是程序地址就足够了,但像下面的例子,如果p是物理地址,你就必须将其加上一个常量转化成kseg0范围内的地址。 
<BR><BR>PI_cache_invalidate (p + 0x80000000, nbytes); <BR><BR><BR>4.11 cache 效率 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -