分类: 嵌入式
2011-04-30 11:21:12
------------------------------------------
转载请注明出处:http://lullaby2005.cublog.cn/
------------------------------------------
这是我学习IA的cache时的笔记,绝大部分是阅读intel manual system programming卷一的总结,或者说是自己理解以后的翻译。
先上一张图:
这是pentium4和xeon CPU的cache相关的硬件模块。你看,有L1,L2,L3cache, TLB,store Buffer
(疑问:为什么没有L1的instruction cache?(见table11-1关于L1 instruction cache的说明)是不是L2和L3cache就不分data部分和instruction部分了?)
几个术语的解释:
Cache line fill: 当processor读一块memory并且发现这块memory是cachable的(通过MTRR来决定改块memory是否cachable),那么processor会把整个cache line读取到L1,L2或L3的cache中
Cache hit:当处理器要读取一块memory的内容时,发现这块内容已经存在cache中了,那么这就称为cache hit
Write hit: 当处理器要写内容到一块memory时,发现cache中已经有这块memory对应的cache了,那么就叫write hit。它会先写到cache,再根据当前系统的写策略决定是否要同时写到memory。
Cache类型:
IA中,Cache类型一共有6种,
l Strong Uncacheable (UC)
这种cache类型的memory,任何读写操作都不经过cache。一般是memory-map的IO地址可以使用这种类型。一般的ram强烈推荐不使用这种cache,否则效率会非常低。
l Uncacheable (UC-)
特性与UC(Strong uncacheable)相同,唯一不同的是,这种类型的memory,可以通过修改MTRR来把它改变成WC
l Write Combining (WC)
这种类型的cache,特性与UC相似,不同的地方是它可以被speculative read(什么叫speculative read?)每次write都可能被delay,write的内容会buffer到一个叫“write combining buffer”的地方。可以通过 对MTRR编程来设置WC,也可以通过设置PAT来设置WC(pat是什么?)
l Write – through (WT)
这个很好理解,每次write,都要write到memory,同时write到对应的cache(if write hits)。WT方式保证了cache与memory是一致的。
l Write – back (WB)
这种类型的memory,read和write,都跟一般的cache一样。只是write的时候,当写到了cache中,不会立即write到memory里(这个就跟WT不一样了)。CPU会等到适当的时候再write到memory里—比如当cache满了。 这种类型是效率最高的类型,
l Write-protected (WP)
Read跟wb一样,但每次write,都会引起cache invalidate
cache控制方法
IA里,对cache的控制方法有2个方面:cache控制寄存器和cache控制指令,我们分别来看一下:
1. Cache控制寄存器
先上一个全局图
CR0的CD bit:一个系统全局的cache enable bit。 当CD被clear,系统整个的cache机制打开,但还是要受到单个page或者某个region自己的策略的限制;当CD被set,整个系统的cache机制就关闭了。
在cache的全局bit CR0 (CD&NW) 被clear时(也就是cache功能被全局打开)的基础上,MTRR(physical memory area-level)与PAT(page-level)如果定义不一样,那么以如下的表格为准:
当cache已经生效了,CPU怎么禁止cache呢?这里有一个固定的套路:
1) 设置全局bit,使全局的cache进入到no-fill mode(设置CR0的CD为1,并且设置NW为0);(这一步是为了不再使memory access产生miss并再一次填充cache)
2) Flush所有的cache(通过指令WBINVD)(这一次是为了让cache里的东西flush到memory)
3) DISABLE掉所有的MTRR并且设置默认的cachetype为uncache(这一步就是最后的关cache了)
注意到intel手册里有这么一段话:
For the Pentium 4 and Intel Xeon processors, after the sequence of
steps given above has been executed, the cache lines containing the
code between the end of the WBINVD instruction and before the
MTRRS have actually been disabled may be retained in the cache
hierarchy. Here, to remove code from the cache completely, a second
WBINVD instruction must be executed after the MTRRs have been
disabled.
这里我有疑问?为什么上面三条步骤做了以后,第二条和第三条之间的代码还保留在cache里?第二条中的WBINVD不是应该flush掉所有的cache里的东西包括2和3之间的代码吗?
2. Cache控制指令
1) INVD : 仅仅是invalide L1,L2和L3 cache,但不会把cache里的内容write back to memory;
2) WBINVD : 跟INVD相比,多了write back to memory 的action
3) PREFETCHh:软件用这条指令来指示处理器把某块内容需要放入到哪块cache里,相当于是软件在干预cache的一些行为。注意,这个PREFETCHh只能预取data,不能预取instruction
4) CLFLUSH : 与PREFETCHh相反,CLFLUSH让软件有机会决定cache里的哪些cache line应该被flush到memory中去,同时这段cache也就被free了。
5) Non-temporal move instructions (MOVNTI,MOVNTQ, MOVNTDQ, MOVNTPS, MOVNTPD) (这些都是SSE/SSE2扩展的指令): 用于把register里的内容直接写入memory,而不必经过L1,L2,L3 cache,避免了cache污染。
隐式cache
很多时候,系统中会隐式cache一些东西,软件(software)必须做一些事情来避免隐式cache带来的问题,比如这个INTEL汇编的例子:
我们假设线性地址F000H对应的物理地址是B000H,这个对应关系在页表中有记录,同时在TLB中也有记录。这个页表项所在的地址是PTE_F000。现在我们想修改这个对应关系,把物理地址B000H换成A000H。如果我们直接这样:
mov PTE_F000, A000H; Change F000H to point to A000H
mov EBX, [F000H];
第一条指令看似已经把页表项的内容修改成了A000H,然后第二条指定就把线性地址F000H的内容move到EBX中。在CPU运行第二条指令访问线性地址F000H时,会先走TLB。由于TLB中的任然存放着F000H-B000H的映射,所以第二条指定错误的把物理地址B000H的内容move到了EBX中了。显然这个问题的根本原因,是TLB隐式的cache了页对应关系。
通过加入两行代码可以刷新TLB:
mov EAX, CR3; Invalidate the TLB
mov CR3, EAX; by copying CR3 to itself
mov PTE_F000, A000H; Change F000H to point to A000H
mov EBX, [F000H];
MTRR
MTRR: Memory Type Range Register,提供了这样一个机制:让不同范围的物理地址空间,具有不同的cache方法(UC,UC-,WC,WB,WT,WP之类的)。
MTRR允许定义最多96个物理内存范围,通过MSR来指定不同范围的MTRR具有哪些cache方法。
这里有一个全貌图:
从这里可以看到,0-1M是用于fix-range map的,1M以上的物理地址空间是用于variable-ranged map的。
另外还可以通过这个table看到不同的cache type在MTRR中是怎样被encode的:
1) MTRR的feature identification
软件通过CPUID指令可以知道当前系统是否支持MTRR。如果系统支持MTRR,那么关于MTRR的更多的详细信息可以通过运行instruction RDMSR 来读取64bit的只读IA32_MTRRCAP MSR寄存器得到,下图是这个IA32_MTRRCAP MSR的组成:
看下每个域表示什么意思:
VCNT:当前系统中实现了多少个variable memory ranges (见上面的figure11-4)
FIX: 如果被set,表示系统支持fixed range;如果被clear,表示系统不支持fixed range(见上面的figure11-4)
WC: 如果被set,表示系统支持write combine的cache方法,反之亦然;
SMRR:如果被set,表示系统支持system management range register(关于这个SMRR,我也不知道具体是怎么回事?)
2) 如何用MRTT来设置系统中的physical cache method
这里涉及到三个register: IA32_MTRR_DEF_TYPE MSR, the fixed-range MTRR 和 the variable-range MTRR.
IA32_MTRR_DEF_TYPE MSR: 用来设置MTRR中没有涵盖到的物理内容的cache属性,也就是默认的属性,照例还是先看寄存器的组成:
Type:很明显,存放的就是cache 属性的编码(也就是前面图中提到的UC,UC-,WC,WT,WP的值),有效值是0,1,4,5,6
FE: (Fixed-range MTRR enable/disable) : 当该bit被set,说明系统中fixed-range 的MTRR是enable的;反之亦然。
E: 相当于一个全局的MTRR开关。这个bit被set时,表示全局的MTRR是enable的,如果同时FE被clear,表示除了fixed-range的MTRR被disable外,其他的MTRR都是enable。如果E被clear了,那么系统中的所有MTRR都被clear,即使这是fixed-range的MTRR被enable,也无济于事。
the fixed-range MTRR: 系统中有11个fixed-range的寄存器,每个寄存器64bit。这64bit又被分成8个bit一组。也就是说每8个bit可以描述一个fixed-range的memory。所有11个寄存器可以描述11*(64/8) = 88个,刚好与figure11-4的fixed-range MTRR对应上了(64+16+8) . 疑问:每8个bit描述一个fixed-range,这8个bit的分布是怎样的?(即在哪里指定type?)
the variable-range MTRR:由寄存器pair IA32_MTRR_PHYSBASE和IA32_MTRR_PHYSMASK来指定variable-range的描述,看下图:
注意,在variable-range的MTRR中,是有对齐原则的:
最小的范围是4K,base地址也至少是4KB对齐;
如果范围大于4K,那么范围必须是2的N次方(N大于12),base地址对齐也必须是2的N次方。
对于MTRR的使用,有如下的一些规则:
1. 如果MTRR没有被enable(MSR IA32_MTRR_DEF_TYPE中的E flag被clear),那么所有的内存访问都使用UC类型.
2. 如果MTRR被enable了:
2.1) 访问的地址在0-1M之间的话,并且fix-ranges MTRR是enable的,那么就使用fix-range的MTRR指定的cache type;
2.2) 访问的地址在1M以上,那么就通过variable-ranges MTRR指定的type来决定:
2.2.1) 如果只有一个variable-range MTRR包含了这个地址,那么就使用该MTRR的type;
2.2.2) 如果有一个以上variable-range MTRR包含了这个地址,并且这些MTRR的type都一样,那也使用这个type;
2.2.3) 如果有一个以上variable-range MTRR包含了这个地址,并且其中一个MRTT的type是UC,那就使用UC
2.2.4) 如果有一个以上variable-range MTRR包含了这个地址,并且type是WT和WB,那么就使用WT(WT优先)
2.2.5)如果以上规则都没满足,那么这块地址的访问的cache type是undefine
3. 如果访问的地址没有fix或者variable range的MTRR match到,那么就使用系统默认的cache type(疑问?系统默认的cache type是在哪里设置呢?)
MTRR初始化:在系统hardware reset时,CPU会自动清除variable-ranged MTRR的flag,并且会清除掉MSR IA32_MTRR_DEF_TYPE的E标志(表示disable所有的MTRR).在初始化MTRR之前,软件(bios或者OS)必须初始化所有的fix ranged 和 variable ranged MTRR为0。然后软件再根据系统需求的实际情况去设置各个MTRR。
在MP系统中,MTRR的处理需要the care should be taken:
各个processor必须保证所有的MTRR都一样,即各个processor所看到的memory cache type都必须具有一致性。这个一致性不是硬件提供的,而是software来确保的。。
PAT-- Page Attribute Table
MTRR是基于物理地址空间来决定某块物理地址具有什么样子的cache属性。而PAT是对MTRR的补充,PAT允许CPU基于线性地址来决定某个page(线性地址页)具有什么样子的cache属性。
PAT是在页表项或者页目录项里指定的(见前面的figure11-3),并且PAT要与页表项或者页目录项里的PCD和PWT这两个bit一起来决定某个page具有什么样子的cache属性(UC,UC-,WB,WT,WC)。这些cache属性在PAT概念里的编码如下:
可以通过CPUID(EAX=1)来判断当前CPU是否支持PAT。软件可以通过编程MSR IA32_PAT(277H)来设置PAT的相关属性。注意,只要是支持PAT的CPU,PAT就一定是打开的,也就是说,CPU没有control bit可以控制PAT的开和关。
这是MSR IA32_PAT(277H)的bit分布
64bit的MSR,分成了8个PA域。
那么,系统怎样来指定某个页具有什么样子的cache 属性呢?
页表中的PAT,PCD,PWT占3个bit,这3个bit组合起来,可以索引到MSR IA32_PAT(277H)的某个PA,这个PA的值,也就是那5种cache的encoding值。页表的PAT,PCD,PWT组合情况如下:
很直白吧。
当系统上电时,那8个PAT entry会有一个默认值,如下:
这些默认值,可以通过WRMSR指令写IA32_PAT来改变(这个MSR在ring0级别是可读可写的)。
或许你有这样一个疑问:既然PAT是基于线性地址的,那么是否存在这样的情况:当一个物理页表被MMU映射到了不同的两个page,并且这两个page entry的pat属性都是不一样的,那怎么办? 这个问题,从intel的角度来看,是undefine的。即INTEL不建议OS这样做,如果非要这样做,导致的后果unknow,也就是后果自负J