多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

【risc-v专栏 06b】内存架构深挖:PMA / ePMP / Cache / CMO / MMU 细节 / RVWMO 形式化

【risc-v专栏 06b】内存架构深挖:PMA / ePMP / Cache / CMO / MMU 细节 / RVWMO 形式化 本篇定位:[[06-内存 PMP-MMU-RVWMO]] 的深度展开。06 是知道有什么、怎么配,本篇是为什么、形式化、边界、硬件细节。补充PMA、 PMP 增强(ePMP/mseccfg)、Cache 架构与 CMO 扩展、MMU 的 A/D 位/TLB/sfence.vma/大页、RVWMO 三公理与 fence 位详解、原子操作 reservation set 与 aq/rl。读完你能:调 PMA 相关的诡异 fault、用 ePMP/mseccfg 做安全隔离、配 CMO 做 cache 维护、看懂页表 A/D 位管理策略、说清 RVWMO 三公理、写正确的 LR/SC 自旋锁。目录一、内存访问的完整路径(先建全景)二、PMA(Physical Memory Attributes)2.1 PMA 是什么厂商扩展部分实现支持软件配置2.2 PMA 检查的属性清单2.3 Memory type 三类(最核心)2.3.1 视角一按缓存行为分cache / no-cache / device2.3.2 视角二按地址映射完整性分Main Memory / I/O / Empty(1) Main Memory(主存)(2) I/O Memory(I/O 内存)(3) Empty(空区)2.4 PMA 触发的 fault2.5 PMA 怎么查 / 怎么知道2.6 PMA vs PMP(易混,必须分清)2.7 PMA 对你 M-mode 工作的实际影响三、PMP 增强:ePMP / mseccfg3.1 基础 PMP 的局限(回顾 暴露问题)3.2 mseccfg(Machine Security Configuration)CSR3.3 MML:让 PMP 约束 M-mode3.4 MMWP:M-mode 白名单3.5 ePMP 的 E 位(Entry)3.6 安全场景应用(AEGIX 方向)3.7 ePMP 配置示例(安全启动后)四、Cache 架构与 CMO 扩展4.1 Cache 层次4.2 Cache 问题(06 的概念深化)4.3 Cache 维护操作(术语要分清)4.4 CMO 扩展(Zicbom / Zicboz / Zicbop)⭐4.5 DMA 一致性三种模式(深化 06)4.6 自修改代码与 fence.i五、MMU 深入(超越 06 的 Sv39 概念)5.1 页表模式全表5.2 页表项位段(Sv39,深化 06)5.3 A/D 位管理(06 没讲的关键)⭐(1) 硬件管理(2) 软件管理5.4 TLB 与 sfence.vma5.5 ASID(Address Space ID)5.6 大页(Superpages)5.7 satp 切换与模式切换六、RVWMO 形式化6.1 RVWMO 的三层模型6.2 PPO(保序规则)6.3 三公理(1) Load Value Axiom(取值公理)(2) Atomicity Axiom(原子性公理)(3) Progress Axiom(前进公理)6.4 fence 指令位详解(06 跳过的细节)6.5 fence 的经典用法(配对模式)6.6 I/O 内存 vs Main Memory 的 ordering七、原子操作深化7.1 LR/SC 的 reservation set7.2 LR/SC 的使用约束(规范要求)7.3 标准自旋锁(带约束)7.4 amo 的 aq/rl 位7.5 原子操作 vs fence 的选择八、内存 ordering 决策总表(深化 06)九、M-mode 程序员的内存清单十、本篇小结速查表一、内存访问的完整路径(先建全景)理解 PMA,先看一次 load/store 从 CPU 发出到内存,经过的全部层次:CPU 执行 load/store/amo 指令 │ 生成虚拟地址 VA(S/U 模式)或物理地址 PA(M 模式) ▼ ┌─────────────────────────────────────────┐ │ ① 地址翻译(MMU,仅 S/U satp≠Bare) │ VA → PA │ 查 TLB → 未命中查页表 → 页 fault? │ (mcause 12-15) └─────────────────────────────────────────┘ │ 物理地址 PA ▼ ┌─────────────────────────────────────────┐ │ ② PMA 检查(硬件,所有模式含 M) │ ★最底层 │ 内存类型?权限?原子性?缓存性? │ (mcause 4/5/6/7) └─────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ ③ PMP 检查(S/U 模式;M 模式看 L/mseccfg) │ 软件配的围栏 │ 该区允许 R/W/X? │ (mcause 1/3/5/7) └─────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ ④ Cache 查找(D-Cache,若 cacheable) │ 命中则返回 │ miss → 总线访问 │ └─────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ ⑤ 总线访问(AXI/AHB) │ │ 到达 Main Memory 或 I/O 设备 │ └─────────────────────────────────────────┘三个关键认知:PMA 在 PMP 之前:PMP 检查的是特权允不允许,PMA 检查的是物理上能不能。PMP 不能授予 PMA 没有的权限。M-mode 也受 PMA 约束:M-mode 绕得过 PMP(默认),但绕不过 PMA——往 Empty 区发 load 照样 access fault,对不支持 amo 的 I/O 区发 amoadd 照样挂。PMA 是比 PMP 更底层的物理事实。PMA 硬件固定不可改(通常):不像 PMP 有 CSR 可配,PMA 由 SoC 设计连死,你只能查手册知道它,不能改它(少数核有 PMA 配置寄存器软件可以配置)。嵌入式视角:M-mode “全权的边界你在 06 篇读到M-mode 全权访问物理内存”——这话要加限定:全权 不受 PMP 限制,但PMA 这层你照样过。你往一个没接外设的保留地址发读,硬件直接 access fault,M-mode 也救不了。调地址错类的死机,先怀疑 PMA(地址写错/区类型错),再怀疑 PMP。二、PMA(Physical Memory Attributes)PMA 是 RISC-V 内存模型最底层的一层,定义每个物理地址区域的属性。这是 RISC-V 规范 Privileged ISA 第 3.1 节的核心内容,也是你点名要补的。2.1 PMA 是什么PMA 物理地址空间的属性表:把整个 PA 空间划分成若干区,每区有固定属性谁定义:SoC 设计者(芯片流片时连死),不是 CPU 核自己谁检查:PMA checker 模块(在总线和核之间),每次访存自动检查可改吗:通常不可;少数核(SiFive 等)有 PMA 配置寄存器可运行时改为什么需要:不同地址区物理性质不同——DDR 内存能缓存能推测访问,设备寄存器有副作用不能缓存不能推测,空区根本没东西。CPU 需要知道这些,才能用对的访问策略厂商扩展部分实现支持软件配置但部分厂商在标准基础上做了扩展确实允许软件配置 PMA芯来科技内核架构提供了 mattri[i]_base 和 mattri[i]_mask 寄存器允许软件修改 PMA 属性但有优先级规则——只能从低优先级属性修改为高优先级属性不能反向放宽。Andes V5通过 CSR 寄存器pmacfg pmaaddr支持软件配置 PMA地址匹配模式类似 PMP 的 NAPOT 模式Zephyr RTOS 中甚至提供了 CONFIG_SOC_ANDES_V5_PMA 配置选项来启用这一功能。2.2 PMA 检查的属性清单RISC-V 规范把 PMA 分必须检测和可选检测两类:属性含义必须?你的关注点Memory typeMain Memory / I/O / Empty✅ 必须决定能不能缓存、能不能推测R/W/X 权限该区物理上可读/写/执行✅ 必须PMA 级权限,与 PMP 不同Misaligned 支持该区支持非对齐访问?✅ 必须不支持则 misaligned faultAtomicity (AMO)支持 LR/SC?支持哪些 amo?部分多核同步、无锁队列关键AMO 粒度支持的最小 amo 访问大小可选4/8 字节?Cacheability可缓存?可选I/O 通常不可缓存Coherence在一致性域内?可选多核 DMA 一致性Access size支持的访问宽度可选8/16/32/64 位?Memory model该区遵循 RVWMO 还是强序可选I/O 强序,Main 遵循 RVWMO2.3 Memory type 三类(最核心)2.3.1 视角一按缓存行为分cache / no-cache / device类型缓存幂等排序典型用途Cacheable可缓存内存✅✅ 幂等弱序RVWMODDR / SRAM 主存Non-cacheable不可缓存内存❌✅ 幂等弱序RVWMODMA 缓冲、帧缓冲、写合并区Device设备内存❌❌ 非幂等强序UART/SPI/I2C 等外设寄存器2.3.2 视角二按地址映射完整性分Main Memory / I/O / EmptyPMA 把每个区分成三种内存类型:可缓存的主存区遵循 RVWMO、不可缓存的 MMIO 设备区强排序、非幂等、以及未映射的空洞区访问触发 Access Fault。(1) Main Memory(主存)真 RAM(DDR/SRAM),无访问副作用遵循 RVWMO:可推测访问、可乱序、可缓存可缓存(cacheable),可预取典型:你的 SRAM 区0x20000000、DDR 区(2) I/O Memory(I/O 内存)设备寄存器区(MMIO),有访问副作用不遵循 RVWMO 的推测:不能推测读、不能合并写、不能重排(强序)不可缓存(non-cacheable)典型:UART/SPI/I2C 寄存器区0x40000000访问大小受限(有的设备只支持 32 位访问,你读 8 位它挂)(3) Empty(空区)没接任何东西的地址区任何访问 →access fault(mcause 5 load / 7 store)典型:memory map 里的 reserved 区Memory type 决定一切下游行为Memory type 是 PMA 的总开关:Main Memory→ 可缓存 RVWMO 可推测 可对齐优化I/O→ 不可缓存 强序 不推测 volatile 语义Empty→ 直接 fault你写volatile uint32_t *reg 0x40000000;读 UART,编译器不敢优化掉,但硬件层面保证它不推测不合并,靠的就是 PMA 标这区是 I/O。PMA 是 volatile 语义的硬件后盾。2.4 PMA 触发的 faultPMA 检查失败 → trap,具体 mcause:情况mcause含义访问 Empty 区5(load)/ 7(store)load/store access fault该区 PMA 不允许该操作(如 I/O 区不支持 amo)5/7access fault非对齐访问且该区不支持 misaligned4(load)/ 6(store)misaligned access fault访问大小超该区支持(如 64 位访问 32 位设备)5/7access fault2.5 PMA 怎么查 / 怎么知道PMA 没有统一 CSR 可读全貌(这是 RISC-V 的一个痛点),获取方式:查 SoC 手册的 memory map:手册会标每个地址区的类型/权限/缓存性。这是主渠道。少数核有 PMA 配置寄存器:如 SiFive 的 PMA 配置 CSR,可读可改。看核手册。探测法(危险):小心地访问试探,看是否 fault。生产代码别用。厂商 BSP 提供:厂商通常给个 memory map 头文件/链接脚本,标好区属性。2.6 PMA vs PMP(易混,必须分清)PMAPMP谁定SoC 硬件(流片连死)M-mode 软件(CSR 配)可改通常不可(少数核可)可,随时改层级更底层(物理事实)更上层(特权围栏)检查顺序先后对 M-mode生效(M 也绕不过)默认不限(L 位/mseccfg 除外)关系决定物理上能不能决定特权上允不允许核心规则:PMP 不能授予 PMA 没有的权限。PMA 说某区不可写(如 ROM),你 PMP 配可写 → 写还是 fault(PMA 挡)PMA 说某区不支持 amo,你 PMP 配可执行 amo → amo 还是 fault反过来:PMA 允许的,PMP 可以进一步限制(PMP 是收紧,不是放宽)一句话记住 PMA/PMPPMA 是物理事实(硬件定,M 也受限),PMP 是特权围栏(软件定,M 默认不受限)。PMP 只能在 PMA 允许的范围内收紧,不能放宽。2.7 PMA 对你 M-mode 工作的实际影响写裸机代码别乱访问地址:你以为 M-mode 全权,往保留区发读直接 fault。先确认 memory map。MMIO 访问要 volatile 对齐 正确大小:PMA 的 I/O type 不让你推测/合并/乱序,volatile 保证编译器层面;对齐和大小要看设备 PMA 支持。设备寄存器别用 amo:除非该 I/O 区 PMA 标支持 amo(少见),否则 amoadd 寄存器会 fault。设备寄存器用普通 read-modify-write。调地址类 fault,先查 PMA:mtval 给地址 → 查 memory map 该地址 type/权限 → 确认你的操作是否被 PMA 允许 → 再看 PMP。三、PMP 增强:ePMP / mseccfg06 篇讲了基础 PMP(16 区 / TOR/NA4/NAPOT / L 位锁定)。这里讲ePMP(enhanced PMP)扩展,这是安全场景(功能安全/安全启动)的关键。3.1 基础 PMP 的局限(回顾 暴露问题)基础 PMP 的问题:M-mode 默认不受限:PMP 只管 S/U,M-mode 全权。安全场景下,M-mode 代码跑飞能写任何区,PMP 拦不住。L 位锁定后不可逆:一旦设 L,该区配置锁死,调试也不方便。默认允许:没配 PMP 的区,所有人(含 S/U)默认可访问(白名单难做)。3.2 mseccfg(Machine Security Configuration)CSRePMP 新增mseccfgCSR,三个关键位:位名作用bit0RLB(Rule Locking Bypass)允许修改已 L 锁定的 PMP 区(调试用,生产慎开)bit1MMWP(Machine Mode Whitelist Policy)M-mode 默认拒绝,只允许 PMP 显式允许的区(白名单)bit2MML(Machine Mode Lock)改变 PMP 语义,PMP 对 M-mode 也生效3.3 MML:让 PMP 约束 M-mode设mseccfg.MML 1后,PMP 语义改变:PMP 对M-mode 也生效(不再默认全权)pmpcfg 的 R/W/X 位语义变化:在 MML 模式下,R/W/X 组合表示该区对 M-mode 的权限典型用法:安全启动后设 MML,锁定 M-mode 代码区不可写,防 M-mode 被攻破后改自身代码3.4 MMWP:M-mode 白名单设mseccfg.MMWP 1后:M-mode默认拒绝所有访问只能访问 PMP 显式配置允许的区这是最小权限原则的硬件实现,功能安全(ISO 26262)隔离用3.5 ePMP 的 E 位(Entry)pmpcfg 新增 E 位(原 A 位旁):E0:该区不参与匹配(禁用,但保留配置)E1:该区参与匹配配合 MML/MMWP,实现精细的 M-mode 权限控制3.6 安全场景应用(AEGIX 方向)嵌入式视角: 功能安全的内存隔离要过 ISO 26262,功能安全要求安全区和非安全区内存隔离。ePMP 给你硬件基础:启动早期配 PMP 划分:安全代码区、安全数据区、非安全区、外设区设mseccfg.MML1:让 PMP 约束 M-mode 自身(防 M-mode 跑飞写安全区)设mseccfg.MMWP1:M-mode 默认拒绝,白名单访问(最小权限)锁定关键区 L1:防运行时被改这是基础 PMP 做不到的(基础 PMP 管不了 M-mode),ePMP 是功能安全芯片的必备。3.7 ePMP 配置示例(安全启动后)// 假设:安全启动完成,要锁定 M-mode 只能跑安全代码// 区0:安全代码区 0x0-0x40000,RX for M-mode,禁止写pmpaddr00x400002;pmpcfg0(13)/*L*/|(23)/*ATOR,...*/;// 详看核手册 MML 下的位语义// 区1:安全数据区 0x40000-0x80000,RW for M-modepmpaddr10x800002;pmpcfg1...;// 开启 MML MMWP,让 PMP 约束 M-mode 并白名单csrs mseccfg,(12)/*MML*/|(11)/*MMWP*/;⚠️ MML/MMWP 一旦开,PMP 语义复杂,配错会把自己锁死(连代码都跑不了)。生产代码前要在仿真/JTAG 反复验证。四、Cache 架构与 CMO 扩展06 篇讲了 DMA 一致性的flush/invalidate概念,这里讲 cache 架构本身和标准 CMO 指令。4.1 Cache 层次┌─────────────┐ Hart 0 │ L1 I-Cache │ 私有,小(8-64KB),快(1-3 cycle) │ L1 D-Cache │ └──────┬───────┘ │ ┌──────▼───────┐ │ L2 Unified │ 共享(多核),大(128KB-1MB) └──────┬───────┘ │ ┌──────▼───────┐ │ L3 / LLC │ 多核共享,大(MB级) └──────┬───────┘ │ Main MemoryMCU 可能无 cache:直接 SRAM,访问确定性好(实时性优)有 cache 的 MCU:性能好,但引入一致性问题Write-through vs Write-back:写策略,影响一致性维护方式4.2 Cache 问题(06 的概念深化)问题描述后果脏数据未下刷CPU 写了 cache 没 write-back 到内存DMA 读到旧数据陈旧数据未失效DMA 写了内存,CPU cache 还是旧值CPU 读到旧数据指令 cache 陈旧改了内存里的代码,I-Cache 没更新CPU 执行旧指令4.3 Cache 维护操作(术语要分清)操作英文干什么用途cleanclean脏行下刷到内存(写回)DMA 发送前,保证数据到内存invalidateinvalidate丢弃 cache 行(不回写)DMA 接收后,强制 CPU 从内存读flushflushclean invalidate既要下刷又要失效zerozero直接清零一 cache 行(绕过读)分配大 buffer 时省读prefetchprefetch预取到 cache优化性能clean vs invalidate 别用反DMA发送前(CPU 写完 buffer,DMA 要读):clean(下刷脏数据,让 DMA 看到新数据)DMA接收后(DMA 写完 buffer,CPU 要读):invalidate(失效 cache,让 CPU 从内存读新数据)用反了:clean 当 invalidate 用,DMA 数据被旧 cache 覆盖;invalidate 当 clean 用,脏数据丢失。这是 DMA bug 高发点。4.4 CMO 扩展(Zicbom / Zicboz / Zicbop)⭐RISC-V 标准 Cache Management Operations 扩展(较新,看核是否支持):扩展指令作用Zicbomcbo.clean/cbo.flush/cbo.invalcache 行维护Zicbozcbo.zerocache 行清零(绕过读)Zicbopcbo.prefetch.i/.r/.w预取// Zicbom 用法(cache block 通常 64 字节,要对齐)// DMA 发送前cbo_clean(buf);// 或 asm: cbo.clean 0(%0) :: r(buf)// DMA 接收后cbo_inval(buf);// 或 asm: cbo.inval 0(%0) :: r(buf)CMO 操作单位是cache block(Cacheline,通常 64 字节),地址要 block 对齐操作多个 block 要循环老芯片没 CMO:用厂商提供的 cache 维护函数(如__flush_dcache_range),每家不同4.5 DMA 一致性三种模式(深化 06)模式机制软件负担典型Coherent interconnect总线监听(snoop),CPU/DMA 自动一致无,软件不管高性能多核Non-coherent CMO软件用 cbo.clean/inval 维护中,要正确 flush/invalidate多数 MCUNon-coherent non-cacheable bufferDMA buffer 映射成 non-cacheable(PMA 标)低,但性能差(绕 cache)简单 MCU4.6 自修改代码与 fence.i// 写新代码到内存memcpy(func_addr,new_code,size);fence.i;// 同步 I-Cache 和 D-Cache!必须func_addr();// 执行新代码fence.i(Zifencei 扩展)保证:之前的 store 对后续指令 fetch 可见没它:I-Cache 还是旧代码,CPU 执行旧指令JIT/动态代码/在线升级固件必用多核下还要 IPI 让其他核 fence.i(复杂)五、MMU 深入(超越 06 的 Sv39 概念)06 讲了 Sv39 三级页表的概念,这里讲细节:A/D 位管理、TLB、sfence.vma、大页。5.1 页表模式全表模式VA 位数页表级数最小页典型Sv323224KBrv32 LinuxSv393934KBrv64 Linux(最常用)Sv484844KB大地址空间 LinuxSv575754KB新,超大地址空间satp.MODE 选哪个:0Bare(无翻译)、8Sv39、9Sv48、10Sv57。5.2 页表项位段(Sv39,深化 06)PTE(64位): bit0: V(valid) 该 PTE 有效 bit1: R(readable) 可读 bit2: W(writable) 可写 bit3: X(executable) 可执行 bit4: U(user) U 模式可访问(S 模式看 SUM 位) bit5: G(global) 全局(所有地址空间有效,TLB 不按 ASID 刷) bit6: A(accessed) 被访问过 bit7: D(dirty) 被写过 bit8-9: RSW 软件可用位 bit10-53: PPN 物理页号 bit54-63: reservedR/W/X 000 且 V1:这是指针 PTE,指向下一级页表(不是叶子)R/W/X ≠ 000 且 V1:这是叶子 PTE,直接给物理页(可在某级终止成大页)5.3 A/D 位管理(06 没讲的关键)⭐A 位(访问)和 D 位(脏)的更新有两种策略,选错会踩坑:(1) 硬件管理CPU 访问页时自动置 A,写时自动置 D优点:软件简单缺点:需要硬件支持(看核手册)检查:menvcfg/satp相关位,或核手册(2) 软件管理CPU 访问时若 A0,触发 page fault(mcause 13 load page fault / 15 store page fault)OS 在 fault handler 里:置 A 位、更新 PTE、返回重试写时若 D0,触发 store page fault,OS 置 D优点:硬件简单缺点:软件要处理 fault,不处理就死循环(fault → 重试 → fault)A/D 位是移植 OS 的高频坑你若给 RISC-V 移植 RTOS/OS,页表 A/D 位策略不对会死循环:硬件不支持自动 A/D,你 OS 又没装 page fault handler → 每次访问触发 fault,handler 不处理 → 重试 → fault → 死循环或 OS 假设硬件自动 A/D,但该核是软件管理 → 同样死循环移植前查核手册的 A/D 策略,OS 代码配套。5.4 TLB 与 sfence.vmaTLB(Translation Lookaside Buffer):页表翻译的 cache,核内改了页表,TLB 可能还是旧的 → 要刷 TLBsfence.vma(S-mode 指令):刷 TLBsfence.vma # 刷所有 TLB(所有地址、所有 ASID) sfence.vma vaddr # 刷特定虚拟地址的 TLB sfence.vma vaddr, asid # 刷特定 ASID 的特定地址 sfence.vma zero, asid # 刷特定 ASID 的所有地址改一个 PTE →sfence.vma vaddr(精细,快)切进程(改 satp)→ 通常sfence.vma(全刷)或用 ASID 避免全刷M-mode 也能执行sfence.vma(虽然是 S 级指令,M 全权)5.5 ASID(Address Space ID)satp 里有 ASID 字段(16 位)TLB 项标记 ASID,不同进程的 TLB 项靠 ASID 区分切进程时若 ASID 不同,TLB 项不冲突,不用全刷(只刷旧 ASID 的)优化:减少切进程的 TLB 刷新开销5.6 大页(Superpages)Sv39 页表三级,可在任一级终止成大页:终止级页大小用途第3级(最底)4KB普通页第2级2MB大页(减少页表项)第1级1GB巨页(极少用)大页 PTE:在该级 R/W/X ≠ 000(叶子),直接给物理页优点:减少页表层级、省 TLB 项、减少翻译开销用途:大 buffer(DMA buffer、帧缓冲)用大页,提升 TLB 命中率5.7 satp 切换与模式切换M → S 切换:M-mode bootloader 配好页表,设 satp,mret 到 S-modesatp 切换 地址空间切换:进程切换时改 satpM-mode 不用 satp:M-mode 访问物理地址,M-mode 下 satp 被忽略六、RVWMO 形式化06 讲了什么场景要 fence,这里讲 RVWMO 的形式化模型——三公理、PPO、fence 位详解。这是能看 spec 原文的深度。6.1 RVWMO 的三层模型RVWMO(RISC-V Weak Memory Ordering)用三个概念定义:Preserved Program Order (PPO):单个 hart 内,哪些内存操作对其他 hart 必须保序(其他可重排)Global Memory Order (GMO):所有 hart 的所有内存操作的一个全局全序(理论存在)Load Value Axiom:load 能读到哪些值6.2 PPO(保序规则)单个 hart 内,以下内存操作对按程序序保序(其他可重排):规则操作对说明Overlapping-addressload→load / store→store / load→store(同地址)同地址必须保序Explicit syncfence 前后fence 强制保序Syntactic dependencyop1 → op2(op2 依赖 op1 的地址/数据/控制)数据依赖保序Pipeline dependencyop1 → op2(执行依赖)流水线依赖…(规范列出十余条)核心:没有这些关系的操作,硬件可重排。这就是为什么多核共享数据要 fence——你的 store data 和 store flag 没有 PPO 关系,硬件可能先 store flag。6.3 三公理RVWMO 由三条公理定义(规范原文):(1) Load Value Axiom(取值公理)每个 load 读到的值 GMO 中最新的以下之一:该 hart 自己 prior store 的值(按程序序)其他 hart 的 store 的值(按 GMO)即:load 不会凭空读到未写的值,但可能读到非最新(因为重排)。(2) Atomicity Axiom(原子性公理)对同一地址的 LR/SC 对:若 SC 成功,则 GMO 中该 SC 的 store 与对应 LR 之间,没有其他 hart 对该地址的 store。这保证 LR/SC 的原子性。(3) Progress Axiom(前进公理)系统不会无限挂起(没有 livelock)。这保证多核代码最终能前进。6.4 fence 指令位详解(06 跳过的细节)fence predecessor, successor predecessor / successor 可选(组合): r: memory read (普通内存读) w: memory write (普通内存写) i: device input (设备读,即 MMIO load) o: device output (设备写,即 MMIO store) ⚠ 注意:fence 的 i 是 input(设备读),不是 instruction! 指令同步用单独的 fence.i常用组合:fence含义fence rw, rw内存读写全屏障(最常用)fence r, r读读屏障(配对读 flag 读 data)fence w, w写写屏障fence rw, ow内存读写 → 设备写(配 DMA 启动)fence iorw, iorw最强,含设备 IO 全屏障fence.i指令同步(I-Cache 与 D-Cache 同步),单独指令fence predecessor, successor语义:predecessor 之前的内存操作,对 successor 之后的内存操作可见。6.5 fence 的经典用法(配对模式)多核发布-等待模式:// Hart 0(生产者)data42;fence rw,rw;// 保证 data 写入先于 flagflag1;// Hart 1(消费者)while(flag!1);fence r,r;// 保证先读 flag 再读 dataxdata;// 一定能读到 42没 fence:Hart 0 可能先写 flag 再写 data(重排),Hart 1 看到 flag1 时 data 还没写。6.6 I/O 内存 vs Main Memory 的 orderingMain Memory:遵循 RVWMO,可重排、可推测、可合并(受 fence 约束)I/O Memory:PMA 标记为 I/O,强序(不重排、不推测、不合并),fence 通常不需要额外加(I/O 访问本身强序)但 I/O 和 Main 混合操作时,可能仍需 fence(如先写 Main buffer,再写 I/O 寄存器启动 DMA)七、原子操作深化06 讲了 LR/SC 自旋锁的基本用法,这里讲 reservation set、SC 失败条件、amo 的 aq/rl 位。7.1 LR/SC 的 reservation setLR(Load-Reserved):加载并标记一个保留集保留集的范围由实现定(通常一个 cache 行,64 字节)SC(Store-Conditional):若保留集未被打破,存储成功(返回 0);否则失败(返回非 0)SC 失败条件:另一个 hart 写了保留集内任意地址另一个 hart 的 LR 到同地址(部分实现)该 hart 执行了另一条 LR保留集过期(实现相关,如时间或指令数)7.2 LR/SC 的使用约束(规范要求)RISC-V 规范对 LR/SC 序列有约束(否则可能 livelock):LR/SC 之间指令数有限(规范给上界,如 16 条)LR/SC 之间不能有另一 LRLR/SC 之间不能有对同地址的 storeLR/SC 之间最好别有分支(或分支要小心)违反约束 → SC 可能永远失败(livelock)。7.3 标准自旋锁(带约束)voidspin_lock(int*lock){while(1){// 等待锁释放(普通读,避免太多 LR)while(*(volatileint*)lock!0);// 尝试获取intgot;asmvolatile(lr.w.amo.aq %0, (%1)\n// LR with acquire:r(got):r(lock));if(got0){// 拿到了,但还要 SC 确认asmvolatile(sc.w.amo.rl %0, %2, (%1)\n// SC with release:r(got):r(lock),r(1));if(got0)return;// SC 成功}// SC 失败,重试}}7.4 amo 的 aq/rl 位amo 指令有两个内存排序位:.aq(acquire):该 amo 之后的内存操作不能重排到它之前.rl(release):该 amo 之前的内存操作不能重排到它之后.aqrl:acquire release(最强)amoadd.w.aqrl rd, rs2, (rs1) # 原子加,acquirerelease用 aq/rl 实现锁,不用额外 fence不带 aq/rl 的 amo 也有原子性,但不保序(多核要小心)7.5 原子操作 vs fence 的选择场景用什么单条原子操作(计数器)amo(可带 aq/rl)临界区进入/退出LR/SC aq/rl(锁)非原子的发布-等待fence rw,rw设备 IOfence rw,ow(或 I/O 自带强序)八、内存 ordering 决策总表(深化 06)覆盖所有场景的决策矩阵:场景要不要屏障用什么原因单核普通代码不要—对自己程序序执行单核自修改代码要fence.iI-Cache 同步多核共享数据发布要fence rw,rw或 amo.aqrl保 data→flag 序多核共享数据读取要fence r,r保 flag→data 序多核临界区要LR/SC.aqrl锁的 acquire/release多核原子计数可选amoadd.aqrl保序 原子DMA 发送前要cbo.cleanfence rw,ow数据到内存 对 DMA 可见DMA 接收后要cbo.invalCPU 从内存读新数据设备寄存器写后读看 PMAI/O 强序通常不用PMA I/O type 强序改页表后要sfence.vma刷 TLB切进程(改 satp)要sfence.vma(或 ASID)刷旧地址空间 TLB中断返回不要mret隐含mret 有顺序保证九、M-mode 程序员的内存清单把本篇收束成你能用的清单:层你能改?你必须懂你的活PMA通常不能(少数核可)✅ 必须查手册知道区属性,别乱访问PMP✅ 能(CSR)✅ 必须划权限围栏给 S/UePMP/mseccfg✅ 能安全场景必须AEGIX 功能安全隔离Cache策略 PMA 定,操作 CMO✅ 必须DMA 一致性维护MMU/页表✅ 能(若跑 OS)概念必须bootloader 配 satp 切 STLB间接(sfence.vma)跑 OS 要懂改页表后刷fence/fence.i✅ 用指令✅ 必须多核/自修改代码保序amo/aq/rl✅ 用指令多核要懂同步原语一句话总结本篇PMA 是物理事实(硬件定,M 也受限),PMP/ePMP 是特权围栏(软件定,可约束 M),Cache/MMU 是性能/隔离机制(fence/sfence 是你操控它们的接口)。你的内存能力 懂 PMA 边界 配 PMP 用 fence/CMO 维护一致性 必要时配 MMU。十、本篇小结PMA是最底层:物理地址属性(Memory type/RWX/原子性/缓存性),硬件固定,M-mode 也受限;PMP 不能授予 PMA 没有的权限PMA 三类:Main Memory(RVWMO/可缓存)、I/O(强序/不缓存/有副作用)、Empty(fault)ePMP/mseccfg:MML(约束 M-mode)、MMWP(白名单)、RLB(解锁),功能安全隔离用Cache 维护:clean(下刷)/invalidate(失效)/flush(两者)/zero(清零),CMO 扩展(Zicbom)标准化DMA 一致性三模式:coherent interconnect / non-coherent CMO / non-cacheable bufferMMU 细节:Sv39 三级页表,A/D 位有硬件/软件两种管理(移植坑),TLB 用 sfence.vma 刷,大页省 TLBRVWMO 三公理:Load Value / Atomicity / Progress;PPO 定义单 hart 保序规则fence 位:r/w/i(device input)/o(device output),fence pred, succ;fence.i同步 I-Cache原子深化:LR/SC reservation set 失败条件 使用约束;amo 的 aq(acquire)/rl(release)位替代 fence决策表:单核不用、多核用 fence/amo.aqrl、DMA 用 CMOfence、改页表用 sfence.vma速查表想干啥怎么做查某地址物理属性查 SoC 手册 memory map(PMA)某地址访问 fault查 mtval → memory map → PMA 是否允许该操作M-mode 也被拦?是,PMA 对 M 生效;PMP 默认不拦 M约束 M-mode 自己设 mseccfg.MML1M-mode 白名单设 mseccfg.MMWP1DMA 发送前cbo.clean(buf) fence rw,owDMA 接收后cbo.inval(buf)改代码后执行fence.i多核发布-等待fence rw,rw(写侧) fence r,r(读侧)多核临界区LR/SC.aqrl 自旋锁多核计数器amoadd.w.aqrl改页表后sfence.vma切进程sfence.vma(或用 ASID 避免全刷)页表 A/D 位查核手册是硬件还是软件管理,配套 OSI/O 寄存器访问volatile 对齐 正确大小 别用 amo技术之路漫漫分享是为了更好地交流。如果本文的内容对你有启发希望能得到你的点赞 和收藏 ⭐。如果你在调试过程中遇到了其他问题欢迎在评论区 留言我们一起探讨。也欢迎关注 我一起交流底层开发的那些事儿。
返回列表