泰晓科技 -- 聚焦 Linux - 追本溯源,见微知著!
网站地址:https://tinylab.org

儿童Linux系统,可打字编程学数理化
请稍侯

RISC-V ISA 简介

Jia Xianhua 创作于 2022/03/27

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 种衡量标准

设计 ISA 的 7 种衡量标准

  • 成本: 集成电路
  • 简洁性: 缩小芯片面积
  • 性能: instructions * cycles * time
  • 架构/实现分离: 延迟分支
  • 提升空间: 添加自定义指令
  • 程序大小: 嵌入式
  • 于编程/编译/链接: 寄存器

RISC-V

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 指令格式

instruction

RISC-V 寻址模式

address

  1. 立即数寻址
    • 操作数是操作本身的常量
  2. 寄存器寻址
    • 操作数在寄存器
  3. 基址寻址
    • 操作数于内存中,其地址是寄存 器和指令中的常量之和
  4. PC 相对寻址
    • 分支地址是 PC 和指令中常量之和

RISC-V 基础整数指令集

RV32I 指令

RV32I

寄存器

register

Control And Status Registers(CSRs) 控制与状态寄存器

csr

csr 需要通过特殊的指令访问。

csr_rw

特点

mistake

  • 32 位字节可寻址的地址空间
  • 所有指令均为 32 位长
  • 31 个寄存器,全部 32 位宽,寄存器 0 硬连线为零
  • 所有操作都在寄存器之间(没有寄存器到内存的操作
  • 加载/存储字加上有符号和无符号加载/存储字节和半字
  • 所有算术,逻辑和移位指令都有立即数版本的指令
  • 立即数总是符号扩展
  • 仅提供一种数据寻址模式(寄存器 + 立即数)和 PC 相对分支
  • 无乘法或除法指令
  • 一个指令,用于将大立即数加载到寄存器的高位,这样加载 32 位常量到寄存器只需要两条指令

RV32M 乘法和除法指令

RV32M

RV32F 和 RV32D 单精度和双精度浮点数

RV32FD

RV32F 和 RV32D 使用 32 个独立的 f 寄存器而不是 x 寄存器。

RV32A 原子指令

RV32A 有两种类型的原子操作:

  1. 内存原子操作(AMO)
  2. 加载保留/条件存储(load reserved/store conditional)

RV32A

RV32C 压缩指令

每条短指令必须和一条标准的 32 位 RISC-V 指令一一 对应。

为了能在一系列的程序上得到良好的代码压缩效果,RISC-V 架构师精心挑选了 RVC 扩展中的指令。

架构师们成功地将指令压缩到了 16 位。

  1. 十个常用寄存器(a0-a5,s0-s1,sp 以及 ra)访问的频率远超过其他寄存器
  2. 许多指令的写入目标是它的源操作数之一
  3. 立即数往往很小,而且有些指令比较喜欢某些特定的立即数

RV32C

RV64:64 位地址指令

从 32 位切换到 64 位 ISA, ISA 只添加了少数指令。

指令集只添加了 32 位指令对应的字(word),双字(doubleword)和长整数(long)版本的指令,并将所有寄存器(包括 PC)扩展为 64 位。

因此,RV64I 中的 sub 操作的是两个 64 位数字而不是 RV32I 中的 32 位数字。

RV64 很接近 RV32 但实际上又有所不同;它添加了少量指令同时基础指令做的事情与 RV32 中稍有不同。

RV64I

Privileged ISA 特权 ISA

privileged

levels

models

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_Listing

CSR_Listing

目前已分配的机器级 CSR 地址

timer、counter、float-point CSRs 都是标准的非特权 CSRs。

其它寄存器都用于特权代码。

需注意的是,并非所有寄存器都需要被实现。

CSR Field Specifications CSR 字段规范

  1. Reserved Writes Preserve Values, Reads Ignore Values (WPRI) 写时保护保留值,读时忽略值
  2. Write/Read Only Legal Values (WLRL) 只读写合法值
  3. 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 下访问,此外也可以在平台上保留一些物理地址空间。

debug

  • 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 跟踪

trace

实验演示

RISC-V Specs 演示代码。

Machine-Level ISA 机器级 ISA

misa

准备环境和代码

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

操作如下:

asm

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

结果显示如下:

c

可以一眼看出以下信息:

  1. RV64I: 64 位
  2. M: M-Mode
  3. A: 原子扩展
  4. C: 压缩扩展
  5. F: 单精度扩展
  6. D: 双精度扩展
  7. S: S-Mode
  8. U: U-Mode

参考文档

  1. RISC-V Platform
  2. D1_SDK_Howto
  3. ADB Download - Get the latest version of ADB and fastboot
  4. rvos-lab
  5. Linux Lab


Read Album:

Read Related:

Read Latest: