RISC-V 架构简介

最后更新: 2023-01-29 16:47:11 +0800 +0800
原文以英文撰写。您正在阅读AI翻译版本。

RISC-V 是一种开放的指令集架构,由基础整数 ISA 和分别进行版本管理的扩展组成。ISA 定义指令行为和特权执行;平台配置文件、ABI 以及执行环境则补充运行可互操作软件所需的要求。

基础整数 ISA 与寄存器约定 #

RV32I 和 RV64I 分别提供 32 位和 64 位整数寄存器。二者都定义了从 x0 到 x31 的 32 个通用寄存器。x0 始终读为零,并丢弃写入。程序计数器通过控制流效果在架构上可见,而不是作为通用寄存器存在。

RISC-V 采用加载—存储设计。整数算术读取寄存器操作数并写入寄存器;加载和存储在寄存器与内存之间移动值。基础 ISA 使用固定的 32 位编码。RV64I 增加了 64 位寄存器和操作,以及对低 32 位值执行符号扩展的 *W 操作;因此,它并非只是地址更宽的 RV32I。

标准整数调用约定分配以下 ABI 名称和角色:

RegistersABI namesRole
x0zero常量零
x1ra返回地址
x2sp栈指针
x5-x7, x28-x31t0-t6caller-saved 临时寄存器
x8-x9, x18-x27s0-s11callee-saved 寄存器
x10-x17a0-a7参数和返回值

浮点和向量调用约定还会增加各自的规则。与编译代码互操作的汇编必须遵循完整的 psABI,包括栈对齐、需保留的状态、重定位以及代码模型。

扩展、ISA 字符串和配置文件 #

单字母标准扩展包括:表示整数乘除法的 M,表示原子操作的 A,表示浮点的 F 和 D,表示压缩指令的 C,以及表示向量的 V。多字母扩展涵盖范围更窄的功能,并在 ISA 字符串语法要求时使用下划线分隔。扩展版本很重要,因为批准过程和后续修订可能会改变精确的契约。

历史上的 G 缩写表示以 IMAFD 为核心并包含所需依赖项的通用扩展集合。rv64gc 这样的字符串很方便,但无法表达所有平台功能、ABI 或特权功能。工具链会规范化扩展顺序并展开依赖关系;不要自行构造有歧义的字符串,而应使用工具链的 -march 诊断。

C 扩展为常用操作提供 16 位编码,从而提高代码密度。压缩指令按照其定义操作的语义执行,并且可以与更长的编码交错使用。指令对齐和分支目标必须遵循目标实现的扩展。

RISC-V 配置文件为某类系统选择一组命名的强制和可选 ISA 功能。实现也可以公开自定义扩展。可移植软件应针对硬件和执行环境共同支持的配置文件或显式扩展集合,而不应假定每个 RV64 处理器都实现相同的 ISA。

特权模式、CSR 和陷阱 #

特权架构定义机器模式(M)、监管者模式(S)和用户模式(U)。M-mode 是必需的,用于控制机器。S-mode 支持操作系统和虚拟内存,U-mode 支持应用程序。可选的虚拟化功能会增加由管理程序控制的变体以及两阶段地址转换。

控制与状态寄存器(CSR)保存陷阱状态、中断使能、地址转换控制、计数器以及扩展特定状态。CSR 地址编码了访问限制和特权限制。缺少权限的指令会触发陷阱,而不是无提示地访问特权级别更高的寄存器。

陷阱会记录原因、异常 PC 和选定的状态,然后转移到配置的陷阱向量。委托寄存器可以将选定的陷阱从 M-mode 路由到 S-mode。ECALL 向执行环境请求服务,MRET 或 SRET 执行从特权处理程序经过检查的返回。

监管者软件不应依赖供应商特定的 M-mode 固件调用。Supervisor Binary Interface(SBI)为计时器编程、处理器间中断、hart 管理、系统复位以及固件辅助的性能监控等服务定义了可移植接口。SBI 是执行环境接口,而不是指令集扩展。

虚拟内存和转换栅栏 #

S-mode 转换方案包括面向 RV32 的 Sv32,以及面向 RV64 的 Sv39、Sv48 和 Sv57 等方案。页表项携带有效、读/写/执行、用户、已访问、脏以及其他属性。活动方案和根由 satp 选择。

页表写入不会自动使所有缓存的转换保持一致。SFENCE.VMA 会对相关页表更新进行排序,并使其指定作用域内的地址转换缓存项失效。多处理器 shootdown 还需要与远程 hart 协调,通常通过 SBI 或平台特定的处理器间中断路径完成。

内存排序、栅栏和原子操作 #

基础 RISC-V 弱内存模型允许在依赖关系或同步未禁止的情况下,观测结果不同于程序顺序。FENCE 会对选定的前驱类和后继类进行排序,而 FENCE.I 则在执行该指令的 hart 上协调数据写入与后续指令获取。

A 扩展提供加载保留/条件存储操作和原子内存操作。获取(aq)位和释放(rl)位为原子操作增加排序;aqrl 同时提供两个方向。原子性和排序解决的是不同问题:不可分割的计数器更新不会发布不相关的数据,除非同时存在所需的排序。

在 C、C++ 和 Rust 中,应优先使用语言级原子操作,因为编译器会将其内存模型映射到可用的 RISC-V 序列。手写汇编必须考虑扩展集合、内存和 I/O 域、保留失败以及前进保证规则。

主要参考资料 #

评论

在 GitHub 上阅读或评论(发表评论需要登录)