[置顶] 泰晓 RISC-V 实验箱,配套 30+ 讲嵌入式 Linux 系统开发公开课
RISC-V ISA 简介
Author: iosdevlog Date: 2022/03/27 Project: RISC-V Linux 内核剖析 Video: RISC-V ISA 介绍 - 直播回放
指令集架构
指令集架构(英语:Instruction Set Architecture,缩写为 ISA
),又称指令集或指令集体系,是计算机体系结构中与程序设计有关的部分,包含了基本数据类型,指令集,寄存器,寻址模式,存储体系,中断,异常处理以及外部 I/O。指令集架构包含一系列的 opcode
即操作码(机器语言),以及由特定处理器执行的基本命令。
不同的处理器“家族”——例如 Intel IA-32 和 x86-64、IBM/Freescale Power 和 ARM 处理器家族——有不同的指令集架构。
指令集体系与微架构(一套用于执行指令集的微处理器设计方法)不同。使用不同微架构的电脑可以共享一种指令集。例如 Intel 的 Pentium 和 AMD 的 AMD Athlon,两者几乎采用相同版本的 x86
指令集体系,但是两者在内部设计上有本质的区别。
设计 ISA 的 7 种衡量标准
- 成本: 集成电路
- 简洁性: 缩小芯片面积
- 性能:
instructions * cycles * time
- 架构/实现分离: 延迟分支
- 提升空间: 添加自定义指令
- 程序大小: 嵌入式
- 于编程/编译/链接: 寄存器
RISC-V
属性 | 说明 |
---|---|
推出年份 | 2010 |
设计公司 | 加州大学柏克莱分校 |
最新架构版本 | 2.2 |
是否开放架构 | 是 |
体系结构类型 | Load-store |
字长/寄存器资料宽度 | 32、64、128 |
字节序 | 小端序 |
指令编码长度 | 不定长度 |
指令集架构设计策略 | RISC |
扩展指令集 | M、A、F、D、Q、C、P |
分支预测结构 | 比较和分支 |
通用寄存器 | 16、32(包括一个始终为零的寄存器) |
浮点寄存器 | 32 |
RISC-V(发音为“risk-five”)是一个基于精简指令集(RISC)原则的开源指令集架构(ISA),简易解释为开源软件运动相对应的一种“开源硬件”。
该项目 2010 年始于加州大学柏克莱分校,但许多贡献者是该大学以外的志愿者和行业工作者。
与大多数指令集相比,RISC-V 指令集可以自由地用于任何目的,允许任何人设计、制造和销售 RISC-V 芯片和软件而不必支付给任何公司专利费。
虽然这不是第一个开源指令集,但它具有重要意义,因为其设计使其适用于现代计算设备(如仓库规模云计算机、高端移动电话和微小嵌入式系统)。
设计者考虑到了这些用途中的性能与功率效率。该指令集还具有众多支持的软件,这解决了新指令集通常的弱点。
RISC-V 指令集的设计考虑了小型、快速、低功耗的现实情况来实做,但并没有对特定的微架构做过度的设计。
Unprivileged ISA 非特权 ISA
- 模块化与增量型 ISA
- 保持向后的二进制兼容性
- 模块化
- 核心是一个名为 RV32I 的基础 ISA,运行一个完整的软件栈
- RV32I 是固定的,永远不会改变
- 惯例是把代表扩展的字母附加到指令集名称之后作为指示
- 例如,RV32IMFD 将乘法(RV32M),单精度浮点(RV32F)和双精度浮点(RV32D)的扩展添加了基础指令集(RV32I)中
RISC-V 指令格式
RISC-V 寻址模式
- 立即数寻址
- 操作数是操作本身的常量
- 寄存器寻址
- 操作数在寄存器
- 基址寻址
- 操作数于内存中,其地址是寄存 器和指令中的常量之和
- PC 相对寻址
- 分支地址是 PC 和指令中常量之和
RISC-V 基础整数指令集
RV32I 指令
寄存器
Control And Status Registers(CSRs) 控制与状态寄存器
csr
需要通过特殊的指令访问。
特点
- 32 位字节可寻址的地址空间
- 所有指令均为 32 位长
- 31 个寄存器,全部 32 位宽,寄存器 0 硬连线为零
- 所有操作都在寄存器之间(没有寄存器到内存的操作
- 加载/存储字加上有符号和无符号加载/存储字节和半字
- 所有算术,逻辑和移位指令都有立即数版本的指令
- 立即数总是符号扩展
- 仅提供一种数据寻址模式(寄存器 + 立即数)和 PC 相对分支
- 无乘法或除法指令
- 一个指令,用于将大立即数加载到寄存器的高位,这样加载 32 位常量到寄存器只需要两条指令
RV32M 乘法和除法指令
RV32F 和 RV32D 单精度和双精度浮点数
RV32F 和 RV32D 使用 32 个独立的 f 寄存器而不是 x 寄存器。
RV32A 原子指令
RV32A 有两种类型的原子操作:
- 内存原子操作(AMO)
- 加载保留/条件存储(load reserved/store conditional)
RV32C 压缩指令
每条短指令必须和一条标准的 32 位 RISC-V 指令一一 对应。
为了能在一系列的程序上得到良好的代码压缩效果,RISC-V 架构师精心挑选了 RVC 扩展中的指令。
架构师们成功地将指令压缩到了 16 位。
- 十个常用寄存器(a0-a5,s0-s1,sp 以及 ra)访问的频率远超过其他寄存器
- 许多指令的写入目标是它的源操作数之一
- 立即数往往很小,而且有些指令比较喜欢某些特定的立即数
RV64:64 位地址指令
从 32 位切换到 64 位 ISA, ISA 只添加了少数指令。
指令集只添加了 32 位指令对应的字(word),双字(doubleword)和长整数(long)版本的指令,并将所有寄存器(包括 PC)扩展为 64 位。
因此,RV64I 中的 sub 操作的是两个 64 位数字而不是 RV32I 中的 32 位数字。
RV64 很接近 RV32 但实际上又有所不同;它添加了少量指令同时基础指令做的事情与 RV32 中稍有不同。
Privileged ISA 特权 ISA
Control And Status Registers(CSRs) 控制与状态寄存器
CSR Listing
- Unprivileged and User-Level CSRs
- Timers
- Counters
- floating-point CSRs
- Supervisor-Level CSRs
- Hypervisor and VS (Virtual S-mode) CSRs
- Machine-Level CSRs
目前已分配的机器级 CSR 地址
timer、counter、float-point CSRs 都是标准的非特权 CSRs。
其它寄存器都用于特权代码。
需注意的是,并非所有寄存器都需要被实现。
CSR Field Specifications CSR 字段规范
- Reserved Writes Preserve Values, Reads Ignore Values (WPRI) 写时保护保留值,读时忽略值
- Write/Read Only Legal Values (WLRL) 只读写合法值
- Write Any Values, Reads Legal Values (WARL) 写任意值,读合法值
CSR Width Modulation CSR 位宽调制
如果 CSR 的位宽被改变(例如,通过改变 MXLEN 或 UXLEN),则除非另有规定,新位宽的 CSR 的可写字段和位的值应根据以下算法,由旧位宽的 CSR 来决定:
- 旧位宽 CSR 的值复制到相同位宽的临时寄存器中。
- 对于旧位宽 CSR 的只读位,临时寄存器中相同位置的位设为零。
- 临时寄存器的位宽变为新位宽。如果新位宽 W 小于旧位宽,则保留临时寄存器最低有效的 W 位,将更高的有效位丢弃。如果新位宽大于旧位宽,则临时寄存器通过零扩展(zero-extension)扩展新位宽。
- 新位宽 CSR 的每个可写字段取自临时寄存器中相同位置的位。
- 更改 CSR 的位宽这一操作并非对 CSR 的读取或写入,因此不触发任何副作用。
Debug 调试
调试模式具体实现(implementation)中还可以包含一个 debug mode,以支持片外调试和/或制造测试。
debug mode(D-mode) 可被看作为一个额外的特权模式,
它的权限甚至比 M-mode 还多。
debug specification 中描述了 debug mode 下 RISC-V hart 的操作。debug mode 保留了一些 CSR 地址,这些地址只能在 D-mode 下访问,此外也可以在平台上保留一些物理地址空间。
- Blocks shown in dotted lines (虚线) are optional
- The user interacts with the Debug Host (e.g. laptop), which is running a debugger (e.g. gdb).
- The debugger communicates with a Debug Translator (e.g. OpenOCD, which may include a hardware driver) to communicate with Debug Transport Hardware (e.g. Olimex USB-JTAG adapter).
Trace 跟踪
实验演示
RISC-V Specs 演示代码。
Machine-Level ISA 机器级 ISA
准备环境和代码
Linux Lab 是我们用到的开源实验环境:
$ git clone https://gitee.com/tinylab/cloud-lab.git
$ cd cloud-lab/
$ tools/docker/run linux-lab
$ tools/docker/bash
实验源代码也一并上传到了协作仓库,可这样下载:
$ git clone https://gitee.com/tinylab/riscv-linux
$ cd code/misa/
代码运行在 M-Mode
,使用 qemu
测试。
汇编语言版本
首先实现了一个汇编代码版本:
.text # 定义 text 代码段
.global _start # 定义全局入口符号 _start
_start:
csrr t0, misa # 将 misa CSR 读取到 t0/x5
stop:
j stop # 无限循环
.end # 文件结束
查看 misa
寄存器的值,需要通过 debug
命令。
make debug
开始调试:
cd asm
make debug
操作如下:
RV32I 寄存器长度是 32 位,RV64I 寄存器是 64 位,默认是 RV64I,可以修改
common.mk
设置成 32 位。
misa.md
文档有更详细的说明。
C 语言版本
上述纯汇编语言版本还需要 debug
才能看到 misa
的二进制值,不能直接解析并查看具体信息,非常不方便。
接下来使用 C 语言,可以很容易展示 misa
里面的具体内容。
- 首先,定义一个
misa
结构体b
/**
* \brief Union type to access MISA register.
*/
typedef union {
struct {
rv_csr_t a:1; /*!< bit: 0 Atomic extension */
rv_csr_t b:1; /*!< bit: 1 Tentatively reserved for Bit-Manipulation extension */
rv_csr_t c:1; /*!< bit: 2 Compressed extension */
rv_csr_t d:1; /*!< bit: 3 Double-precision floating-point extension */
rv_csr_t e:1; /*!< bit: 4 RV32E base ISA */
rv_csr_t f:1; /*!< bit: 5 Single-precision floating-point extension */
rv_csr_t g:1; /*!< bit: 6 Additional standard extensions present */
rv_csr_t h:1; /*!< bit: 7 Hypervisor extension */
rv_csr_t i:1; /*!< bit: 8 RV32I/64I/128I base ISA */
rv_csr_t j:1; /*!< bit: 9 Tentatively reserved for Dynamically Translated Languages extension */
rv_csr_t _reserved1:1; /*!< bit: 10 Reserved */
rv_csr_t l:1; /*!< bit: 11 Tentatively reserved for Decimal Floating-Point extension */
rv_csr_t m:1; /*!< bit: 12 Integer Multiply/Divide extension */
rv_csr_t n:1; /*!< bit: 13 User-level interrupts supported */
rv_csr_t _reserved2:1; /*!< bit: 14 Reserved */
rv_csr_t p:1; /*!< bit: 15 Tentatively reserved for Packed-SIMD extension */
rv_csr_t q:1; /*!< bit: 16 Quad-precision floating-point extension */
rv_csr_t _resreved3:1; /*!< bit: 17 Reserved */
rv_csr_t s:1; /*!< bit: 18 Supervisor mode implemented */
rv_csr_t t:1; /*!< bit: 19 Tentatively reserved for Transactional Memory extension */
rv_csr_t u:1; /*!< bit: 20 User mode implemented */
rv_csr_t v:1; /*!< bit: 21 Tentatively reserved for Vector extension */
rv_csr_t _reserved4:1; /*!< bit: 22 Reserved */
rv_csr_t x:1; /*!< bit: 23 Non-standard extensions present */
#if defined(__RISCV_XLEN) && __RISCV_XLEN == 64
rv_csr_t _reserved5:38; /*!< bit: 24..61 Reserved */
rv_csr_t mxl:2; /*!< bit: 62..63 Machine XLEN */
#else
rv_csr_t _reserved5:6; /*!< bit: 24..29 Reserved */
rv_csr_t mxl:2; /*!< bit: 30..31 Machine XLEN */
#endif
} b; /*!< Structure used for bit access */
rv_csr_t d; /*!< Type used for csr data access */
} CSR_MISA_Type;
- 之后,定义 csr 读取的宏:
__RV_CSR_READ
/* symbolic CSR names: */
#define CSR_MISA 0x301
#define XSTR(x) #x
#define __STR(s) #s
#define STRINGIFY(s) __STR(s)
/**
* \brief CSR operation Macro for csrr instruction.
* \details
* Read the content of csr register to __v and return it
* \param csr CSR macro definition defined in
* \ref NMSIS_Core_CSR_Registers, eg. \ref CSR_MSTATUS
* \return the CSR register value
*/
#define __RV_CSR_READ(csr) \
({ \
register rv_csr_t __v; \
asm volatile("csrr %0, " STRINGIFY(csr) \
: "=r"(__v) \
: \
: "memory"); \
__v; \
})
#if 0
// linux
#define csr_read(csr) \
({ \
register unsigned long __v; \
__asm__ __volatile__ ("csrr %0, " __ASM_STR(csr) \
: "=r" (__v) : \
: "memory"); \
__v; \
})
#endif
代码下面是 linux kernel 里面 arch/riscv/include/asm/csr.h
的实现方式,代码内容是一样的。
- 接着,通过内联汇编获取
misa
csr
C 语言可以使用内联汇编获取 misa
的值。
- 接下来是使用 C 语言解析
misa
csr
void print_misa(void)
{
CSR_MISA_Type misa_bits = (CSR_MISA_Type) __RV_CSR_READ(CSR_MISA);
static char misa_chars[30];
uint8_t index = 0;
if (misa_bits.b.mxl == 1) {
misa_chars[index++] = '3';
misa_chars[index++] = '2';
} else if (misa_bits.b.mxl == 2) {
misa_chars[index++] = '6';
misa_chars[index++] = '4';
} else if (misa_bits.b.mxl == 3) {
misa_chars[index++] = '1';
misa_chars[index++] = '2';
misa_chars[index++] = '8';
}
if (misa_bits.b.i) {
misa_chars[index++] = 'I';
}
...
misa_chars[index++] = '\0';
printf("MISA: RV%s\r\n", misa_chars);
}
- 最后,编译运行看看效果
cd c
make run
结果显示如下:
可以一眼看出以下信息:
RV64I
: 64 位M
: M-ModeA
: 原子扩展C
: 压缩扩展F
: 单精度扩展D
: 双精度扩展S
: S-ModeU
: U-Mode
参考文档
猜你喜欢:
- 我要投稿:发表原创技术文章,收获福利、挚友与行业影响力
- 知识星球:独家 Linux 实战经验与技巧,订阅「Linux知识星球」
- 视频频道:泰晓学院,B 站,发布各类 Linux 视频课
- 开源小店:欢迎光临泰晓科技自营店,购物支持泰晓原创
- 技术交流:Linux 用户技术交流微信群,联系微信号:tinylab
支付宝打赏 ¥9.68元 | 微信打赏 ¥9.68元 | |
请作者喝杯咖啡吧 |
Read Album:
- TinyBPT 和面向 buildroot 的二进制包管理服务(1):设计简介与框架
- RISC-V Linux 内核及周边技术动态第 118 期
- RISC-V Linux 内核及周边技术动态第 117 期
- 实时分析工具 rtla timerlat 介绍(二):延迟测试原理
- 实时分析工具 rtla timerlat 介绍(一):交叉编译及使用