cache
Cache分配策略(Cache allocation policy)
cache的分配策略是指我们什么情况下应该为数据分配cache line。cache分配策略分为读和写两种情况。
读分配(read allocation)
当CPU读数据时,发生cache缺失,这种情况下都会分配一个cache line缓存从主存读取的数据。默认情况下,cache都支持读分配。
写分配(write allocation)
当CPU写数据发生cache缺失时,才会考虑写分配策略。当我们不支持写分配的情况下,写指令只会更新主存数据,然后就结束了。当支持写分配的时候,我们首先从主存中加载数据到cache line中(相当于先做个读分配动作),然后会更新cache line中的数据。
Cache更新策略(Cache update policy)
cache更新策略是指当发生cache命中时,写操作应该如何更新数据。cache更新策略分成两种:写直通和回写。
写穿(write through)
当CPU执行store指令并在cache命中时,我们更新cache中的数据并且更新主存中的数据。cache和主存的数据始终保持一致。
写回(write back)
当CPU执行store指令并在cache命中时,我们只更新cache中的数据。并且每个cache line中会有一个bit位记录数据是否被修改过,称之为dirty bit。我们会将dirty bit置位。主存中的数据只会在cache line被替换或者显示的clean操作时更新。因此,主存中的数据可能是未修改的数据,而修改的数据躺在cache中。cache和主存的数据可能不一致。
flush cache操作(仅write back)有两种:
- 使主存储器有效。针对write back高速缓存,首先应该使主存储器有效,保证已经修改数据的cacheline写回主存储器,避免修改的数据丢失。
- 使高速缓存无效。保证切换后的进程不会错误的命中上一个进程的缓存数据。
虚拟高速缓存(VIVT)
虚拟地址直接送到cache控制器,如果cache hit。直接从cache中返回数据给CPU。如果cache miss,则把虚拟地址发往MMU,经过MMU转换成物理地址,根据物理地址从主存(main memory)读取数据。由于我们根据虚拟地址查找高速缓存,所以我们是用虚拟地址中部分位域作为索引(index),找到对应的的cacheline。然后根据虚拟地址中部分位域作为标记(tag)来判断cache是否命中。
歧义(ambiguity)
相同的虚拟地址映射不同的物理地址就会出现歧义。例如两个互不相干的进程,就可能出现相同的虚拟地址映射不同的物理地址。假设A进程虚拟地址0x4000映射物理地址0x2000。B进程虚拟地址0x4000映射物理地址0x3000。当A进程运行时,访问0x4000地址会将物理地址0x2000的数据加载到cacheline中。当A进程切换到B进程的时候,B进程访问0x4000会cache hit,此时B进程就访问了错误的数据,B进程本来想得到物理地址0x3000对应的数据,但是却由于cache hit得到了物理地址0x2000的数据。操作系统如何避免歧义的发生呢?当我们切换进程的时候,可以选择flush所有的cache。
别名(alias)
当不同的虚拟地址映射相同的物理地址,而这些虚拟地址的index不同,此时就发生了别名现象(多个虚拟地址被称为别名)。通俗点来说就是指同一个物理地址的数据被加载到不同的cacheline中就会出现别名现象。
物理高速缓存(PIPT)
基于对VIVT高速缓存的认识,我们知道VIVT高速缓存存在歧义和名别两大问题。主要问题原因是:tag取自虚拟地址导致歧义,index取自虚拟地址导致别名。
最简单的方法是tag和index都取自物理地址。物理的地址tag部分是独一无二的,因此肯定不会导致歧义。而针对同一个物理地址,index也是唯一的,因此加载到cache中也是唯一的cacheline,所以也不会存在别名。我们称这种cache为物理高速缓存,简称PIPT(Physically Indexed Physically Tagged)。
CPU发出的虚拟地址经过MMU转换成物理地址,物理地址发往cache控制器查找确认是否命中cache。
物理标记的虚拟高速缓存(VIPT)
为了提升cache查找性能,我们不想等到虚拟地址转换物理地址完成后才能查找cache。因此,我们可以使用虚拟地址对应的index位查找cache,与此同时(硬件上同时进行)将虚拟地址发到MMU转换成物理地址。当MMU转换完成,同时cache控制器也查找完成,此时比较cacheline对应的tag和物理地址tag域,以此判断是否命中cache。我们称这种高速缓存为VIPT(Virtually Indexed Physically Tagged)。
VIPT Cache什么情况不存在别名
我们知道VIPT的优点是查找cache和MMU转换虚拟地址同时进行,所以性能上有所提升。歧义问题虽然不存在了,但是别名问题依旧可能存在,那么什么情况下别名问题不会存在呢?Linux系统中映射最小的单位是页,一页大小是4KB。那么意味着虚拟地址和其映射的物理地址的位<11...0>是一样的。针对直接映射高速缓存,如果cache的size小于等于4KB,是否就意味着无论使用虚拟地址还是物理地址的低位查找cache结果都是一样呢?是的,因为虚拟地址和物理地址对应的index是一样的。这种情况,VIPT实际上相当于PIPT,软件维护上和PIPT一样。如果示例是一个四路组相连高速缓存呢?只要满足一路的cache的大小小于等于4KB,那么也不会出现别名问题。
如何解决VIPT Cache别名问题
我们可以将虚拟地址0x2000映射到物理地址0x4000,而不是用虚拟地址0x1000。0x2000对应第0x00行cacheline,这样就避免了别名现象出现。因此,在建立共享映射的时候,返回的虚拟地址都是按照cache大小对齐的地址,这样就没问题了。如果是多路组相连高速缓存的话,返回的虚拟地址必须是满足一路cache大小对齐。在Linux的实现中,就是通过这种方法解决别名问题。
TLB
虚拟地址映射物理地址的最小单位是4KB。所以TLB其实不需要存储虚拟地址和物理地址的低12位(因为低12位是一样的,根本没必要存储)。另外,我们如果命中cache,肯定是一次性从cache中拿出整个数据。所以虚拟地址不需要offset域。index域是否需要呢?这取决于cache的组织形式。如果是全相连高速缓存。那么就不需要index。如果使用多路组相连高速缓存,依然需要index。
TLB的别名问题
别名是否存在。我们知道PIPT的数据cache不存在别名问题。物理地址是唯一的,一个物理地址一定对应一个数据。但是不同的物理地址可能存储相同的数据。也就是说,物理地址对应数据是一对一关系,反过来是多对一关系。由于TLB的特殊性,存储的是虚拟地址和物理地址的对应关系。因此,对于单个进程来说,同一时间一个虚拟地址对应一个物理地址,一个物理地址可以被多个虚拟地址映射。将PIPT数据cache类比TLB,我们可以知道TLB不存在别名问题。而VIVT Cache存在别名问题,原因是VA需要转换成PA,PA里面才存储着数据。中间多经传一手,所以引入了些问题。
如何尽可能的避免flush TLB
首先需要说明的是,这里的flush理解成使无效的意思。我们知道进程切换的时候,为了避免歧义,我们需要主动flush整个TLB。如果我们能够区分不同的进程的TLB表项就可以避免flush TLB。我们知道Linux如何区分不同的进程?每个进程拥有一个独一无二的进程ID。如果TLB在判断是否命中的时候,除了比较tag以外,再额外比较进程ID该多好呢!这样就可以区分不同进程的TLB表项。进程A和B虽然虚拟地址一样,但是进程ID不一样,自然就不会发生进程B命中进程A的TLB表项。所以,TLB添加一项ASID(Address Space ID)的匹配。ASID就类似进程ID一样,用来区分不同进程的TLB表项。这样在进程切换的时候就不需要flush TLB。但是仍然需要软件管理和分配ASID。
我们知道内核空间和用户空间是分开的,并且内核空间是所有进程共享。既然内核空间是共享的,进程A切换进程B的时候,如果进程B访问的地址位于内核空间,完全可以使用进程A缓存的TLB。但是现在由于ASID不一样,导致TLB miss。我们针对内核空间这种全局共享的映射关系称之为global映射。针对每个进程的映射称之为non-global映射。所以,我们在最后一级页表中引入一个bit(non-global (nG) bit)代表是不是global映射。当虚拟地址映射物理地址关系缓存到TLB时,将nG bit也存储下来。当判断是否命中TLB时,当比较tag相等时,再判断是不是global映射,如果是的话,直接判断TLB hit,无需比较ASID。当不是global映射时,最后比较ASID判断是否TLB hit。
什么时候应该flush TLB。
- 当ASID分配完的时候,需要flush全部TLB。ASID的管理可以使用bitmap管理,flush TLB后clear整个bitmap。
- 当页表映射关系修改的时候需要flush TLB
Cache和DMA一致性
最简单的方法(nocahe)
当我们使用DMA时,首先是配置。我们需要在内存中申请一段内存当做buffer,这段内存用作需要使用DMA读取I/O设备的缓存,或者写入I/O设备的数据。为了避免cache的影响,我们可以将这段内存映射nocache,即不使用cache。映射的最小单位是4KB,因此在内存映射上至少4KB是nocahe的。这种方法简单实用,但是缺点也很明显。如果只是偶尔使用DMA,大部分都是使用数据的话,会由于nocache导致性能损失。这也是Linux系统中dma_alloc_coherent()接口的实现方法。
软件维护cache一致性
为了充分使用cache带来的好处。我们映射依然采用cache的方式。但是我们需要格外小心。根据DMA传输方向的不同,采取不同的措施。
- 如果DMA负责从I/O读取数据到内存(DMA Buffer)中,那么在DMA传输之前,可以invalid DMA Buffer地址范围的高速缓存。在DMA传输完成后,程序读取数据不会由于cache hit导致读取过时的数据。
- 如果DMA负责把内存(DMA Buffer)数据发送到I/O设备,那么在DMA传输之前,可以clean DMA Buffer地址范围的高速缓存,clean的作用是写回cache中修改的数据。在DMA传输时,不会把主存中的过时数据发送到I/O设备。
注意,在DMA传输没有完成期间CPU不要访问DMA Buffer。
例如以上的第一种情况中,如果DMA传输期间CPU访问DMA Buffer,当DMA传输完成时。CPU读取的DMA Buffer由于cache hit导致取法获取最终的数据。
同样,第二情况下,在DMA传输期间,如果CPU试图修改DMA Buffer,如果cache采用的是写回机制,那么最终写到I/O设备的数据依然是之前的旧数据。所以,这种使用方法编程开发人员应该格外小心。这也是Linux系统中流式DMA映射dma_map_single()接口的实现方法。
DMA Buffer对齐要求
为了避免出现改写DMA buffer的情况。我们应该保证DMA Buffer不会跟其他数据共享cacheline。所以我们要求DMA Buffer首地址必须cacheline对齐,并且buffer的大小也cacheline对齐。这样就不会跟其他数据共享cacheline。也就不会出现这样的问题。
DMA Buffer不能是从栈和全局变量分配。这个主要原因是没办法保证buffer是cacheline对齐。我们可以通过kmalloc分配DMA Buffer。这就要求某些不支持总线监视的架构必须保证kmalloc分配的内存必须是cacheline对齐。所以linux提供了一个宏,保证kmalloc分配的object最小的size。例如ARM64平台的定义如下:
#define ARCH_DMA_MINALIGN (128)
AT S12E0R:地址转换阶段 1 和 2 EL0 读取
AT S12E0W:地址转换阶段 1 和 2 EL0 写入
AT S12E1R:地址转换阶段 1 和 2 EL1 读取
AT S12E1W:地址转换阶段 1 和 2 EL1 写入
AT S1E0R:地址转换阶段 1 EL0 读取
AT S1E0W:地址转换阶段 1 EL0 写入
AT S1E1R:地址转换阶段 1 EL1 读取
AT S1E1RP:地址转换阶段 1 EL1 读取 PAN
AT S1E1W:地址转换阶段 1 EL1 写入
AT S1E1WP:地址转换阶段 1 EL1 写入 PAN
AT S1E2R:地址转换阶段 1 EL2 读取
AT S1E2W:地址转换阶段 1 EL2 写入
AT S1E3R:地址转换阶段 1 EL3 读取
AT S1E3W:地址转换阶段 1 EL3 写入
CFP RCTX : 上下文控制流预测限制
CPP RCTX : 上下文缓存预取预测限制
DC CGDSW:按集/方式清理数据和分配标签
DC CGDVAC:VA 到 PoC 的数据清理和分配标签
DC CGDVADP:VA 到 PoDP 的数据清理和分配标签
DC CGDVAP:VA 到 PoP 的数据清理和分配标签
DC CGSW:按设置/方式清除分配标签
DC CGVAC:VA 到 PoC 的分配标签清理
DC CGVADP : 清除 VA 到 PoDP 的分配标签
DC CGVAP : 清除 VA 到 PoP 的分配标签
DC CIGDSW:按设置/方式清理和无效数据和分配标签
DC CIGDVAC:VA 到 PoC 的数据和分配标签的清理和无效化
DC CIGSW : 通过设置/方式清理和无效分配标签
DC CIGVAC:VA 到 PoC 的分配标签清理和无效化
DC CISW:数据或统一缓存行按设置/方式清除和无效
DC CIVAC:数据或统一缓存行由 VA 清理和失效到 PoC
DC CSW:数据或统一缓存行按设置/方式清理
DC CVAC:数据或统一缓存线由 VA 清理到 PoC
DC CVADP:数据或统一缓存线由 VA 清理到 PoDP
DC CVAP:数据或统一缓存线由 VA 清理到 PoP
DC CVAU:数据或统一缓存线由 VA 清理到 PoU
DC GVA : 数据缓存集分配标签由 VA
DC GZVA:数据缓存集分配标签和 VA 归零
DC IGDSW:按设置/方式使数据和分配标签无效
DC IGDVAC:VA 到 PoC 的数据和分配标签无效
DC IGSW:按设置/方式使分配标签无效
DC IGVAC:VA 到 PoC 的分配标签无效
DC ISW:数据或统一缓存行按设置/方式无效
DC IVAC:数据或统一缓存行由 VA 对 PoC 无效
DC ZVA:VA 的数据缓存零
DVP RCTX : 上下文数据值预测限制
IC IALLU : 指令缓存对 PoU 全部无效
IC IALLUS:指令缓存对 PoU 全部无效,内部可共享
IC IVAU : 指令缓存行由 VA 对 PoU 无效
SYS S1_<op1>_<Cn>_<Cm>_<op2>, SYSL S1_<op1>_<Cn>_<Cm>_<op2>:实施定义的维护说明
TLBI ALLE1,TLBI ALLE1NXS:TLB 使所有无效,EL1
TLBI ALLE1IS,TLBI ALLE1ISNXS:TLB 全部无效,EL1,内部可共享
TLBI ALLE1OS,TLBI ALLE1OSNXS:TLB 全部无效,EL1,外部可共享
TLBI ALLE2,TLBI ALLE2NXS:TLB 全部无效,EL2
TLBI ALLE2IS,TLBI ALLE2ISNXS:TLB 无效,EL2,内部可共享
TLBI ALLE2OS,TLBI ALLE2OSNXS:TLB 全部无效,EL2,外部可共享
TLBI ALLE3,TLBI ALLE3NXS:TLB 全部无效,EL3
TLBI ALLE3IS、TLBI ALLE3ISNXS:TLB 全部无效、EL3、内部可共享
TLBI ALLE3OS、TLBI ALLE3OSNXS:TLB 全部无效、EL3、外部可共享
TLBI ASIDE1、TLBI ASIDE1NXS:TLB 因 ASID、EL1 无效
TLBI ASIDE1IS、TLBI ASIDE1ISNXS:TLB 因 ASID、EL1、内部共享而无效
TLBI ASIDE1OS、TLBI ASIDE1OSNXS:TLB 因 ASID、EL1、外部共享而无效
TLBI IPAS2E1、TLBI IPAS2E1NXS:TLB 由中间物理地址失效,第 2 阶段,EL1
TLBI IPAS2E1IS、TLBI IPAS2E1ISNXS:TLB 因中间物理地址无效,第 2 阶段,EL1,内部可共享
TLBI IPAS2E1OS、TLBI IPAS2E1OSNXS:TLB 因中间物理地址无效,第 2 阶段,EL1,外部可共享
TLBI IPAS2LE1,TLBI IPAS2LE1NXS:TLB 由中间物理地址无效,第 2 阶段,最后一级,EL1
TLBI IPAS2LE1IS,TLBI IPAS2LE1ISNXS:TLB 由中间物理地址失效,第 2 阶段,最后一级,EL1,内部可共享
TLBI IPAS2LE1OS、TLBI IPAS2LE1OSNXS:TLB 因中间物理地址无效,第 2 阶段,最后一级,EL1,外部可共享
TLBI RIPAS2E1、TLBI RIPAS2E1NXS:TLB 范围因中间物理地址无效,第 2 阶段,EL1
TLBI RIPAS2E1IS,TLBI RIPAS2E1ISNXS:TLB 范围因中间物理地址无效,第 2 阶段,EL1,内部可共享
TLBI RIPAS2E1OS,TLBI RIPAS2E1OSNXS:TLB 范围因中间物理地址无效,第 2 阶段,EL1,外部可共享
TLBI RIPAS2LE1,TLBI RIPAS2LE1NXS:TLB 范围因中间物理地址无效,第 2 阶段,最后一级,EL1
TLBI RIPAS2LE1IS,TLBI RIPAS2LE1ISNXS:TLB 范围因中间物理地址无效,第 2 阶段,最后一级,EL1,内部可共享
TLBI RIPAS2LE1OS,TLBI RIPAS2LE1OSNXS:TLB 范围因中间物理地址无效,第 2 阶段,最后一级,EL1,外部可共享
TLBI RVAAE1、TLBI RVAAE1NXS:TLB 范围因 VA、所有 ASID、EL1 无效
TLBI RVAAE1IS、TLBI RVAAE1ISNXS:TLB 范围因 VA 无效、所有 ASID、EL1、内部可共享
TLBI RVAAE1OS、TLBI RVAAE1OSNXS:TLB 范围因 VA 无效、所有 ASID、EL1、外部可共享
TLBI RVAALE1,TLBI RVAALE1NXS:TLB 范围因 VA 无效,所有 ASID,最后一级,EL1
TLBI RVAALE1IS、TLBI RVAALE1ISNXS:TLB 范围因 VA 无效、所有 ASID、最后一级、EL1、内部可共享
TLBI RVAALE1OS、TLBI RVAALE1OSNXS:TLB 范围因 VA 无效、所有 ASID、最后一级、EL1、外部可共享
TLBI RVAE1、TLBI RVAE1NXS:TLB 范围因 VA、EL1 无效
TLBI RVAE1IS、TLBI RVAE1ISNXS:TLB 范围因 VA、EL1、内部共享而无效
TLBI RVAE1OS、TLBI RVAE1OSNXS:TLB 范围因 VA、EL1、外部共享而无效
TLBI RVAE2、TLBI RVAE2NXS:TLB 范围因 VA、EL2 无效
TLBI RVAE2IS、TLBI RVAE2ISNXS:TLB 范围因 VA、EL2、内部共享而无效
TLBI RVAE2OS、TLBI RVAE2OSNXS:TLB 范围因 VA、EL2、外部共享而无效
TLBI RVAE3、TLBI RVAE3NXS:TLB 范围因 VA、EL3 无效
TLBI RVAE3IS、TLBI RVAE3ISNXS:TLB 范围因 VA、EL3、内部共享而无效
TLBI RVAE3OS、TLBI RVAE3OSNXS:TLB 范围因 VA、EL3、外部共享而无效
TLBI RVALE1,TLBI RVALE1NXS:TLB 范围因 VA 无效,最后一级,EL1
TLBI RVALE1IS,TLBI RVALE1ISNXS:TLB 范围因 VA 无效,最后一级,EL1,内部可共享
TLBI RVALE1OS,TLBI RVALE1OSNXS:TLB 范围因 VA 无效,最后一级,EL1,外部可共享
TLBI RVALE2,TLBI RVALE2NXS:TLB 范围因 VA 无效,最后一级,EL2
TLBI RVALE2IS、TLBI RVALE2ISNXS:TLB 范围因 VA 无效、最后一级、EL2、内部可共享
TLBI RVALE2OS,TLBI RVALE2OSNXS:TLB 范围因 VA 无效,最后一级,EL2,外部可共享
TLBI RVALE3,TLBI RVALE3NXS:TLB 范围因 VA 无效,最后一级,EL3
TLBI RVALE3IS, TLBI RVALE3ISNXS:TLB 范围因 VA 无效,最后一级,EL3,内部可共享
TLBI RVALE3OS,TLBI RVALE3OSNXS:TLB 范围因 VA 无效,最后一级,EL3,外部可共享
TLBI VAAE1、TLBI VAAE1NXS:TLB 因 VA、所有 ASID、EL1 无效
TLBI VAAE1IS,TLBI VAAE1ISNXS:TLB 被 VA 无效,所有 ASID,EL1,内部可共享
TLBI VAAE1OS,TLBI VAAE1OSNXS:TLB 被 VA 无效,所有 ASID,EL1,外部可共享
TLBI VAALE1,TLBI VAALE1NXS:TLB 因 VA 无效,所有 ASID,最后一级,EL1
TLBI VAALE1IS、TLBI VAALE1ISNXS:TLB 因 VA 无效、所有 ASID、最后一级、EL1、内部可共享
TLBI VAALE1OS,TLBI VAALE1OSNXS:TLB 被 VA 无效,所有 ASID,最后一级,EL1,外部可共享
TLBI VAE1、TLBI VAE1NXS:TLB 因 VA、EL1 无效
TLBI VAE1IS,TLBI VAE1ISNXS:TLB 无效,由 VA、EL1、内部共享
TLBI VAE1OS、TLBI VAE1OSNXS:TLB 被 VA、EL1、外部共享无效
TLBI VAE2、TLBI VAE2NXS:TLB 被 VA、EL2 无效
TLBI VAE2IS,TLBI VAE2ISNXS:TLB 无效,由 VA、EL2、内部共享
TLBI VAE2OS、TLBI VAE2OSNXS:TLB 被 VA、EL2、外部共享无效
TLBI VAE3、TLBI VAE3NXS:TLB 被 VA、EL3 无效
TLBI VAE3IS、TLBI VAE3ISNXS:TLB 无效,由 VA、EL3、内部共享
TLBI VAE3OS、TLBI VAE3OSNXS:TLB 被 VA、EL3、外部共享无效
TLBI VALE1,TLBI VALE1NXS:TLB 因 VA 无效,最后一级,EL1
TLBI VALE1IS,TLBI VALE1ISNXS:TLB 因 VA 无效,最后一级,EL1,内部可共享
TLBI VALE1OS,TLBI VALE1OSNXS:TLB 被 VA 无效,最后一级,EL1,外部可共享
TLBI VALE2,TLBI VALE2NXS:TLB 因 VA 无效,最后一级,EL2
TLBI VALE2IS, TLBI VALE2ISNXS:TLB 被 VA 无效,最后一级,EL2,内部可共享
TLBI VALE2OS,TLBI VALE2OSNXS:TLB 被 VA 无效,最后一级,EL2,外部可共享
TLBI VALE3,TLBI VALE3NXS:TLB 因 VA 无效,最后一级,EL3
TLBI VALE3IS,TLBI VALE3ISNXS:TLB 无效,最后一级,EL3,内部可共享
TLBI VALE3OS,TLBI VALE3OSNXS:TLB 无效,最后一级,EL3,外部可共享
TLBI VMALLE1,TLBI VMALLE1NXS:TLB 因 VMID 无效,全部在阶段 1,EL1
TLBI VMALLE1IS、TLBI VMALLE1ISNXS:TLB 因 VMID 无效,全部处于阶段 1,EL1,内部可共享
TLBI VMALLE1OS,TLBI VMALLE1OSNXS:TLB 因 VMID 无效,全部在阶段 1,EL1,外部可共享
TLBI VMALLS12E1、TLBI VMALLS12E1NXS:TLB 因 VMID 无效,全部在阶段 1 和 2,EL1
TLBI VMALLS12E1IS、TLBI VMALLS12E1ISNXS:TLB 因 VMID 无效,全部处于第 1 阶段和第 2 阶段,EL1,内部可共享
TLBI VMALLS12E1OS,TLBI VMALLS12E1OSNXS:TLB 由 VMID 无效,全部在第 1 和第 2 阶段,EL1,外部可共享
memory
部分属性的含义
Shareable 内存是共享的
non-Shareable 内存不共享,一般只可被单个PE访问
cacheable 内存会被缓存
non-cacheable 内存不会被缓存
PE是一个Arm架构实现的通用术语,可以执行一个程序的东西都可以看作是一个PE.
(1)Gathering 或者non Gathering (G or nG)。这个特性表示对多个memory的访问是否可以合并,如果是nG,表示处理器必须严格按照代码中内存访问来进行,不能把两次访问合并成一次。
例如:代码中有2次对同样的一个地址的读访问,那么处理器必须严格进行两次read transaction。
(2)Re-ordering (R or nR)。这个特性用来表示是否允许处理器对内存访问指令进行重排。nR表示必须严格执行program order。
(3)Early Write Acknowledgement (E or nE)。PE访问memory是有问有答的(更专业的术语叫做transaction),对于write而言,PE需要write ack操作以便确定完成一个write transaction。为了加快写的速度,系统的中间环节可能会设定一些write buffer。nE表示写操作的ack必须来自最终的目的地而不是中间的write buffer。
对于device type,其总是non cacheable的,而且是outer shareable,因此它的attribute不多,主要有下面几种附加的特性:
- Device-nGnRnE : 处理器必须严格按照代码中内存访问来进行、必须严格执行program order(无需重排序)、写操作的ack必须来自最终的目的地
- Device-nGnRE : 处理器必须严格按照代码中内存访问来进行、必须严格执行program order(无需重排序)、写操作的ack可以来自中间的write buffer
- Device-nGRE : 处理器必须严格按照代码中内存访问来进行、内存访问指令可以进行重排、写操作的ack可以来自中间的write buffer
- Device-GRE : 处理器对多个memory的访问是否可以合并、内存访问指令可以进行重排、写操作的ack可以来自中间的write buffer