Memory Ordering
RVWMO 内存一致性模型
RISC-V使用一种名为“RVWMO”(RISC-V弱内存排序, RISC-V Weak Memory Ordering)的内存模型,旨在为架构师提供灵活性,以构建高性能可伸缩的设计,同时支持易于处理的编程模型.
在RVWMO下,从同一hart上的其他内存指令的角度来看,运行在单个hart上的代码似乎是按照顺序执行的,但来自另一个hart的内存指令可能会观察到第一个hart上的内存指令以不同的顺序执行。因此,多线程代码可能需要显式的同步来保证来自不同hart的内存指令之间的顺序。基本的RISC-V ISA提供了一个FENCE指令,同时atomic extension "A"额外定义了load-reserved/store-conditional和atomic read-modify-write指令。
标准ISA扩展还有misaligned aotmics "Zam"和total store ordering"Ztso" 是对RVWMO增加额外的规则。
什么是RVWMO内存模型?
RVWMO内存模型是根据全局内存顺序定义的,即所有hart产生的内存操作的总顺序。通常,一个多线程程序有许多不同的可能执行,每个执行都有自己相应的全局内存顺序。全局内存顺序定义在由内存指令生成的原语加载和存储操作上。
内存模型原语
内存操作的程序顺序反映了生成每个加载和存储的指令在该 hart 的动态指令流中的逻辑布局顺序; 即,顺序即一个简单的有序处理器执行该指令的顺序。
内存访问指令引起内存操作。内存操作既可以是加载操作,也可以是存储操作,或者两者同时进行。所有内存操作都是单拷贝原子的:它们永远不会在部分完成的状态下被观察到。
在RV32GC和RV64GC的指令中,每条对齐的内存指令(aligned memory instruction)只产生一个内存操作,有两个例外。首先,一条不成功的SC指令不会引起任何内存操作。第二,如果XLEN<64, FLD和FSD指令可能各自引起多个内存操作。一个对齐AMO(aligned AMO)产生了一种单一的内存操作,它同时是一个加载操作和一个存储操作。
一个不对齐的加载或存储指令可以被分解成一组任意粒度的部分内存操作。XLEN<64的FLD或FSD指令也可以分解为一组任意粒度的部分内存操作。这些指令所产生的内存操作并不是按照程序顺序排列的,而是按照之前的指令和之后的指令所产生的内存操作的顺序排列的。原子扩展“A”根本不需要执行环境来支持不对齐的原子指令;然而,如果通过“Zam”扩展支持了不对齐的原子,那么LRs、SCs和AMOs可以根据不对齐原子的原子性公理进行分解。
如果在程序顺序上,LR指令在SC指令之前,并且中间没有其他LR或SC指令,则LR指令和SC指令被称为配对;相应的内存操作也被认为是成对的(除了在SC失败的情况下,那里没有存储操作生成)。
加载和存储操作也可以携带以下集合中的一个或多个ordering字段后缀修饰: “acquire-RCpc”, “acquire-RCsc”, “release-RCpc”, 和“release-RCsc”。一个AMO或LR指令带有aq后缀,称为"acquire-RCsc"。一个AMO或SC指令带有rl后缀,称为"release-RCsc"。一个AMO、LR或SC指令都带有aq和rl后缀,称为acquire-RCsc和release-RCsc
RCpc和RCsc含义
RCsc
全称:release consistency with sequential consistency 顺序松散一致性
RCpc
全称:release consistency with processor consistency 松散处理器一致性
Machine 物理内存属性
一个完整系统的物理内存映射包括各种地址范围,有的对应内存区域,有的对应内存映射的控制寄存器,有的对应地址空间中的空洞。 某些内存区域可能不支持读取、写入或执行; 有些可能不支持subword或subblock访问; 有些可能不支持原子操作; 有些可能不支持缓存一致性,或者可能有不同的内存模型。 类似地,内存映射控制寄存器在其支持的访问宽度、对原子操作的支持以及读写访问是否具有相关的副作用方面有所不同。 在 RISC-V 系统中,机器物理地址空间的每个区域的这些属性和功能称为物理内存属性 (PMA)。
PMA 是底层硬件的固有属性,在系统运行期间很少更改。 PMA 不会因执行上下文而异。 某些内存区域的 PMA 在芯片设计时是固定的——例如,对于片上 ROM。 其他的在电路板设计时是固定的,例如,取决于哪些其他芯片连接到片外总线。 片外总线也可能支持在每个电源周期(冷插拔)或系统运行时动态更改(热插拔)的设备。 某些设备可以在运行时可配置,意味着不同 PMA 的不同用途——例如,片上RAM 可能由一个最终应用程序中的一个内核私有缓存,或在另一个应用程序中作为共享的非缓存内存访问。
大多数系统将要求在知道物理地址后,在execution pipeline的硬件中至少动态检查一些 PMA,因为某些操作在所有物理内存地址上都不受支持,并且某些操作需要知道可配置的当前设置 PMA 属性。 虽然许多其他架构在虚拟内存页表中指定了一些 PMA 并使用 TLB 来通知pipeline这些属性,但这种方法将特定于平台的信息注入到虚拟化层中,并且可能导致系统错误,除非在每个页面中正确初始化属性 - 每个物理内存区域的表条目。 此外,可用页面大小对于指定物理内存空间中的属性可能不是最佳的,从而导致地址空间碎片和昂贵的 TLB 条目的低效使用。
对于 RISC-V,我们将 PMA 的规范和检查分离到一个单独的硬件结构中,即 PMA 检查器。在许多情况下,每个物理地址区域的属性在系统设计时是已知的,并且可以硬连线到 PMA 检查器中。在属性是运行时可配置的情况下,可以提供特定于平台的内存映射控制寄存器,以适合平台上每个区域的粒度指定这些属性(例如,对于可以在可缓存之间灵活划分的片上 SRAM和不可缓存的用途)。检查 PMA 是否有任何对物理内存的访问,包括经过虚拟到物理内存转换的访问。为了帮助进行系统调试。我们强烈建议在可能的情况下,RISCV 处理器精确地捕获未通过 PMA 检查的物理内存访问。精确的trap PMA 非法操作为指令、加载或存储访问错误异常,与虚拟内存页面错误异常不同。精确的 PMA trap可能并不总是可能的,例如,当使用这种老架构:probe a legacy bus 。在这种情况下,来自外围设备的错误响应将被报告为不精确的总线错误中断。
PMA 还必须可由软件读取,以正确访问某些设备或正确配置访问内存的其他硬件组件,例如 DMA 引擎。 由于 PMA 与给定物理平台的组织紧密相关,因此许多细节本质上是特定于平台的,软件可以通过这种方式学习平台的 PMA 值。 某些设备,尤其是传统总线,不支持 PMA 的发现,因此如果尝试不支持的访问,则会给出错误响应或超时。 通常,特定于平台的机器模式代码将提取 PMA 并最终使用某些标准表示将此信息呈现给更高层次的低权限软件。
在平台支持 PMA 的动态重新配置的情况下,将提供一个接口来通过将请求传递给可以正确重新配置平台的机器模式驱动程序来设置属性。 例如,在某些内存区域上切换缓存属性可能涉及特定于平台的操作,例如缓存刷新,这些操作仅适用于机器模式。
主内存与 I/O 与空区域
给定内存地址范围的最重要特征是它是否拥有常规的主内存或 I/O 设备,或者是空的。 常规主存储器需要具有许多属性,具体如下所述,而 I/O 设备可以具有更广泛的属性。 不常规主存储器的内存区域(例如 device scratchpad RAMs)被归类为 I/O 区域。 空区域也被归类为 I/O 区域,但具有指定不支持访问的属性。
支持访问类型的PMAs
访问类型指定支持的访问宽度,从 8 位字节到长多字burst,以及每个访问宽度是否支持未对齐的访问。
尽管在 RISC-V hart 上运行的软件无法直接生成内存bursts,但软件可能必须对 DMA 引擎进行编程以访问 I/O 设备,因此可能需要知道支持哪些访问大小。
主存储器区域始终支持连接设备所需的所有访问宽度的读取和写入,并且可以指定是否支持指令提取。
有些平台可能要求所有主存都支持指令获取。其他平台可能禁止从一些主存区域获取指令。
在某些情况下,访问主存的处理器或设备的设计可能支持其他宽度,但必须能够使用主存支持的类型。
I/O区域可以指定支持哪些读、写组合,或者对哪些数据宽度执行访问
对于具有基于页面的虚拟内存的系统,I/O和内存区域可以指定支持哪些硬件页表读取和硬件页表写入组合。
原子的PMAs
原子性PMAs描述了在这个地址区域中支持哪些原子指令。对原子指令的支持分为两类:LR/SC和AMOs
有些平台可能要求所有可缓存主存都支持附加处理器所需的所有原子操作。
1.AMO PMA
在AMOs内部,有四个层次的支持:AMONone、AMOSwap、AMOLogical和AMOArithmetic。monone表示不支持AMO操作。AMOSwap表示该地址范围内只支持AMOSwap指令。AMOLogical表示支持交换指令加上所有的逻辑AMOs (amoand, amoor, amoxor)。AMOArithmetic表示支持所有RISC-V AMOs。对于每个级别的支持,如果底层内存区域支持对该宽度的读写,则支持给定宽度的自然对齐的AMOs。只能使用主存和I/O区域。

2. Reservability PMA
Reservability: 可预定性
对于LR/SC,有三个层次的支持 reservability和 eventuality属性的组合: RsrvNone, RsrvNonEventual, 和RsrvEventual,
RsrvNone表示否支持LR/SC操作(位置不可保留)
RsrvNonEventual表示支持操作(位置是可保留的)
以上两种无法保证成功。
RsrvEventual表示支持这些操作,并提供最终的成功保证。
LR/SC配合使用,保证对内存的操作是原子的。对于arm64说的内存的独占访问。
例子:
使用lr/sc实现内存字M[a0]的比较
0:lr.w a3, (a0) #加载旧值
4:bne a3, a1, 80 #比较旧的值与a1是否相等,不相等则跳到80
8:sc.w a3, a2, (a0) #相等则存入新值a2到M[a0]中
c:bnez a3, 0 #如果存入失败,则跳转到0,继续尝试
80:3.对齐
对于某些地址和访问宽度,支持对齐的 LR/SC 或对齐的 AMO 的内存区域也可能支持未对齐的 LR/SC 或未对齐的 AMO。 如果对于给定的地址和访问宽度,未对齐的 LR/SC 或 AMO 生成地址未对齐的异常,则使用该地址和访问宽度的所有加载、存储、LR/SC 和 AMO 必须生成地址未对齐的异常。
在标准"A"扩展中是不支持非对齐AMOs或LR/SC的。"Zam"扩展是对非对齐AMOs提供了支持。当前LR/SC非对齐操作还未标准化,因此LR/SC对非对齐内存的操作会产生异常。
在实际实现中,地址未对齐访问异常可能被access-fault异常替代。如果给定了地址和访问宽度,所有非对齐LRs/SCs和AMOs访问将生成access-fault异常。
Memory-Ordering PMAs
为了按照FENCE指令和原子指令排序位进行排序,地址空间的区域被划分为主存或I/O。
一个hart对主存区域的访问不仅可以被其他hart观察到,而且可以被其他具有在主存系统中发起请求能力的设备(例如,DMA引擎)观察到。一致性主内存区域是RVWMO或RVTSO内存模型中的一个。不一致主存区别有 implementation-defined内存模型。
一个hart对I/O区域的访问不仅可以被其他的hart和总线控制设备观察到,也可以被目标I/O设备观察到,并且I/O区域可以被轻松或强顺序访问.其他 hart 和总线主控设备通常以与访问 RVWMO 内存区域的顺序类似的方式观察到具有宽松排序的 I/O 区域的访问。 相比之下,对具有强排序的 I/O 区域的访问通常由其他 hart 和总线主控设备按照程序顺序进行观察。
每个强排序 I/O 区域指定一个编号的排序通道,这是一种可以在不同 I/O 区域之间提供排序保证的机制。 通道 0 仅用于指示点对点强排序,其中只有 hart 对单个关联 I/O 区域的访问是强排序的。
通道 1 用于跨所有 I/O 区域提供全局强排序。 hart 对与通道 1 关联的任何 I/O 区域的任何访问只能观察到所有其他 hart 和 I/O 设备按程序顺序发生,包括相对于该 hart 对松弛 I/O 区域的访问或 具有不同通道号的强排序 I/O 区域。 换句话说,对通道 1 中某个区域的任何访问都相当于在指令之前和之后执行了一个fence io,io 指令。
Other larger channel numbers provide program ordering to accesses by that hart across any regions with the same channel number.
系统可能支持在每个内存区域上动态配置排序属性。
Coherence and Cacheability PMAs
一致性是为单个物理地址定义的属性,它表明一个代理对该地址的写操作最终将对系统中的其他代理可见。不要将一致性与系统的内存一致性模型混淆,该模型定义了在给定整个内存系统的先前读取和写入历史的情况下,内存读取可以返回的值。在 RISC-V 平台中,由于软件复杂性、性能和能量影响,不鼓励使用硬件不连贯区域。
内存区域的可缓存性不应影响该区域的软件视图,除了其他 PMA 中反映的差异,例如主内存与 I/O 分类、内存排序、支持的访问和原子操作以及一致性。 因此,我们将可缓存性视为仅由机器模式软件管理的平台级设置。
在平台支持内存区域的可配置缓存设置的情况下,平台特定的机器模式例程将更改设置并在必要时刷新缓存,因此系统仅在缓存设置之间的转换期间是不连贯的。 这种暂时状态不应该对较低的特权级别可见
Idempotency PMAs
幂等性 PMA 描述了对地址区域的读取和写入是否是幂等的。 假定主内存区域是幂等的。 对于 I/O 区域,可以分别指定读取和写入的幂等性(例如,读取是幂等的,但写入不是)。 如果访问是非幂等的,即任何读或写访问都可能有副作用,则必须避免推测性或冗余访问。
出于定义幂等 PMA 的目的,由冗余访问创建的观察到的内存顺序的变化不被视为副作用。
对于非幂等区域,不得提前或推测性地执行隐式读取和写入,但以下情况除外。 当执行非推测性隐式读取时,允许实现额外读取包含非推测性隐式读取地址的自然对齐的 2 次幂区域内的任何字节。 此外,当执行非推测性指令获取时,允许实现额外读取下一个自然对齐的相同大小的 2 次方区域内的任何字节(该区域的地址取模 2XLEN)。 这些额外读取的结果可用于满足后续的早期或推测性隐式读取。 这些自然对齐的 2 次幂区域的大小由实现定义,但对于具有基于页面的虚拟内存的系统,不得超过支持的最小页面大小。
RISC-V Machine 物理内存保护
为了支持安全处理和包含故障,希望限制在 hart 上运行的软件可访问的物理地址。 可选的物理内存保护 (PMP) 单元提供 per-hart 机器模式控制寄存器,以允许为每个物理内存区域指定物理内存访问权限(读、写、执行)。
PMP 访问控制设置的粒度是特定于平台的,但标准 PMP 编码支持小至四个字节的区域。 某些区域的权限可以hardwired——例如,某些区域可能只在机器模式下可见,但在低权限中不可见。
PMP检查S或U的所有访问,包括S和U模式中的指令获取和数据访问,以及当设置了mstatus的MPRV和 MPP字段被设置为了S或U时,检查在M模式下的数据访问。PMP 检查也适用于虚拟地址转换的页表访问,其有效特权模式为 S。可选地,PMP 检查可以额外应用于 M 模式访问,在这种情况下 PMP 寄存器本身被锁定,因此即使 M 模式软件也无法更改它们,直到 hart 被重置。实际上,PMP可以授予S模式和U模式的权限,默认S模式和U模式没有权限,也可以撤销m模式的权限,默认m模式有完全权限。
PMP的违规操作会被处理器精确的捕获
物理内存保护CSRs
Physical Memory Protection CSRs
PMP表项由一个8位配置寄存器和一个mxlen位地址寄存器描述。一些 PMP 设置还使用与前面的 PMP 条目关联的地址寄存器。最多支持64个PMP表项。实现可以实现0个、16个或64个PMP csr;必须首先实现编号最低的PMP CSRs。所有PMP CSR字段为WARL,可以为只读零。PMP csr只能通过m模式访问。
PMP 配置寄存器密集地封装在 CSR 中,以最大限度地减少上下文切换时间。
对于RV32,16个CSRs,pmpcfg0–pmpcfg15,拥有64个PMP条目,pmp0cfg–pmp63cfg。

对RV64,8个偶数的CSRs, pmpcfg0, pmpcfg2,. . . , pmpcfg14,包含了64条PMP条目。但对于奇数编号的CSRs, pmpcfg1, pmpcfg3, . . . , pmpcfg15,是非法的。

PMP地址寄存器CSRs被命名为 pmpaddr0–pmpaddr63。
在RV32中,每个PMP地址寄存器将一个34位物理地址的[33:2]位进行地址编码

在RV64中,每一个PMP地址寄存器对56位物理地址的[55:2]位进行地址编码。

并不是所有的物理地址位都可以实现,因此pmpaddr寄存器是WARL。
PMP配置寄存器的布局:

设置R位、W位和X位时,表示PMP表项允许读、写和执行指令。当这些位中的一个被清除时,相应的访问类型被拒绝。R、W、X字段组成一个集合WARL字段,R=0、W=1的组合是被保留的,不符合实际情况。其余两个字段A和L将在下面的部分中描述。
试图从没有执行权限的PMP区域获取指令会引发指令访问错误异常。在没有读取权限的情况下,试图执行访问PMP区域内物理地址的load或load-reserved指令会引发load访问错误异常。在没有写权限的情况下,尝试执行访问PMP区域内物理地址的store、store-conditional或AMO指令会引发store访问错误异常。
Address Matching
一个PMP entry由一个地址寄存器和一个配置寄存器组成。那么如何知道该PMP entry控制的物理地址范围呢?这是有配置寄存器中的A字段和地址寄存器共同决定的。
PMP表项的配置寄存器中的A字段对相关联的PMP地址寄存器的地址匹配模式进行编码。该字段的编码如表3.10所示。
NA4可以看作是NAPOT的特殊情况。
支持另外两种地址匹配模式:自然对齐的power-of-2区域(NAPOT),包括自然对齐的4字节区域(NA4)的特殊情况;任意范围的上边界(TOR)。这些模式支持四字节粒度。

NAPOT范围利用关联地址寄存器的低阶位来编码范围的大小

1.OFF
当A=0时,该PMP表项被禁用,不匹配任何地址。
2. NAPOT
在上图中,当pmpcfg.A为NAPOT时,从pmpaddr的低位开始寻找连续1的个数。
若pmpaddr值为yyyy...yyy0,即连续1的个数为0,则该PMP entry锁控制的地址空间从yyyy....yyyy0开始的8个字节。
若pmpaddr值为yyyy...yy01,即连续1的个数为1,则该PMP entry锁控制的地址空间从yyyy....yyy01开始的16个字节。
若pmpaddr值为y...y01...1, 即连续1的个数为n, 则该PMP entry锁控制的地址空间从y...y00....0开始的2^(n+3)个字节。
这种控制地址范围的方式叫做字段对齐的2指数地址范围( naturally aligned power-of-2 regions)
3.TOR
如果选择了TOR,相关联的地址寄存器形成地址域的上界,前面的PMP地址寄存器构成地址域的下界,共同构成了一个地址区域。
如果PMP条目 A字段是设置成了TOR,此条目匹配任何地址y, y的范围pmpaddr[i-1] <= y < pmpaddr[i](不考虑pmpcfg([i-1])。
如果PMP第0个条目的A字段是设置成了TOR,下界则为0,能匹配的地址范围是y<pmpaddr[0]。
如果pmpaddr[i-1]>=pmpaddr[i], 且pmpcfg[i].A=TOR, 则PMP条目i 没有匹配的地址。
4.NA4
若pmpaddr值为yyyy...yyyy,此时控制的地址返回从yyyy,,,,yyyy开始的4个字节,pmpcfg.A的值为NA4,即 naturallyaligned four-byte regions
对OFF/NAPOT/NA4的通用方法描述
尽管PMP机制支持最小为4字节的区域,但平台可以指定更大的PMP区域。通常,PMP粒度为2^(G+2)字节,并且在所有PMP区域中必须相同。
当G>=1,NA4模式是不能被选择的。
当G>=2且pmpcfg[i].A[1]是设置,例如模式是NAPOT,pmpaddr[i].[G-2:0]读取全是1。
当G>=1 且pmpcfg[i].A[1]是0,例如模式是OFF或TOR,pmpaddr[i].[G-1:0]读取全是0。
pmpaddri[G-1:0] 不影响 TOR 地址匹配逻辑。尽管更改 pmpcfg[i].A[1] 会影响从 pmpaddri 读取的值,但不会影响存储在该寄存器中的基础值 — 特别是,当 pmpcfgi.A 从 NAPOT 更改为 TOR/OFF 时,pmpaddri[G-1] 保留其原始值 然后回到 NAPOT。
如果当前 XLEN 大于 MXLEN,则 PMP 地址寄存器会从 MXLEN 到 XLEN 位进行零扩展,以进行地址匹配。
Locking and Privilege Mode
L 位表示 PMP 条目已锁定,即忽略对配置寄存器和相关地址寄存器的写入。 锁定的 PMP 条目保持锁定状态,直到 hart 被重置。 如果 PMP 条目 i 被锁定,则忽略对 pmpicfg 和 pmpaddr[i] 的写入。 此外,如果 PMP 条目 i 被锁定并且 pmpicfg.A 设置为 TOR,则对 pmpaddr[i-1] 的写入将被忽略。
即使A字段设置为OFF,设置L位也会锁定PMP条目。
我们知道,通常情况下M模式拥有对于所有地址的所有权限。但当L字段为1时,M、S、U模式都必须遵循配置寄存器的权限设置(是否读、写、执行权限)。而当L字段为0时,在M模式下匹配到此PMP entry的任何操作都将成功,而S和U模式下需要遵循配置寄存器中的权限设置。
Priority and Matching Logic
(优先级和匹配逻辑)
PMP条目是静态优先级的。最小编号的PMP条目匹配任何字节确认访问是成功还是失败。匹配的PMP条目必须匹配访问所有字节,否则访问将失败,无论L、R、W和X位是什么值。例如,如果PMP条目被配置为匹配四字节访问0xC-0xF,则建设PMP条目是匹配这些地址的最高优先级条目,则对范围0x8-0xF的8字节访问将失败。如果 PMP 条目匹配访问的所有字节,则 L、R、W 和 X 位确定访问是成功还是失败。 如果 L 位清零且访问的特权模式为 M,则访问成功。 否则,如果设置了L位或访问的特权模式为S或U,则只有设置了访问类型对应的R、W或X位,则访问成功。
如果没有 PMP 条目匹配 M 模式访问,则访问成功。 如果没有PMP表项匹配S-mode或U-mode访问,但至少实现了一个PMP表项,则访问失败。
如果至少实现了一个PMP表项,但是所有PMP表项的A字段都被设置为OFF,那么所有的s模式和u模式内存访问都会失败。
访问失败会生成指令、加载或存储访问错误异常。 请注意,单个指令可能会生成多个访问,这些访问可能不是相互原子的。 如果一条指令生成的至少一个访问失败,则会生成访问错误异常,尽管该指令生成的其他访问可能会成功并产生可见的副作用。 值得注意的是,引用虚拟内存的指令被分解为多个访问。
在一些实现中,未对齐的加载、存储和指令提取也可能被分解为多个访问,其中一些可能在访问错误异常发生之前成功。 特别是,通过 PMP 检查的未对齐存储的一部分可能变得可见,即使另一部分未通过 PMP 检查。 对于比 XLEN 位宽的浮点存储(例如,RV32D 中的 FSD 指令),即使存储地址自然对齐,也会出现相同的行为。