写在前面
现在计算机的体系架构正是发展得如火如荼的时候,占领桌面端市场的x86架构、占领移动端市场的arm架构、在服务器市场仍有一定地位的mips架构、国产自研的指令集loongarch架构、还有我现在要讲到的新型开源开放的RISC-V指令集架构。
我先说一说我的学习经历,三年前我开始学习x86架构,不得不说x86架构这么多年的发展变得是真的非常复杂和难以概括,它为了兼容性,不得不保留一些早期的设计,实际上计算机硬件的发展非常迅速(摩尔定律已经保持了这么多年),很多Intel的早期设计虽然非常精彩、体现了工业的美学和智慧,但是放到现在来说真的是太冗余了。2021-2022年我主要学习8086汇编,在DOS操作系统上做实验,进行实模式的编程。2022-2023我主要学习x86的保护模式,在保护模式上完成了我的第一个OS,随后进入x86_64的长模式编程,制作了一个具有现代性质的有smp支持的OS。这一路学来,体会到最多的就是Intel为了兼容性,让内核程序员体会到新旧思想的反复交替和叠加,在体系结构升级的过程中,不断弱化(但没有抛弃)分段式的内存管理,强化分页式内存管理,把用段来表示的内存逐渐变成平坦模型,为了就是分页机制可以均分内存页。除此之外就是指令集的不断增加,在很多的寄存器或者是结构里都会有res这样的保留字段,感觉坑坑洼洼的,也许是Intel为了填坑而设定的,就显得不是那么美观、符合直觉。
那么我现在要讲的是RISC-V这个新型开源开放的指令集,它是有加州大学伯克利分校的教授和研究人员开发的。它出自大学,那么和x86、arm这样出自企业的指令集形成鲜明的对比,它具有浓厚的学院的设计思想,有更好的设计美学和简洁的哲学表达语言,用人话说就是risc-v汇编写起来更加简单、不需要特别的记忆、对于程序员来说学起来比x86要简单许多。这一点非常类似于Python,简单 简洁 方便 适合初学者,成为它的一大特点。并且它作为“后来者”,在设计的时候就已经吸取了前代risc和cisc指令集的经验教训,没有那么多为了兼容性的历史包袱,更加纯粹。实际上,它不仅仅利好于程序员的汇编编程,而且对于基于RISC-V架构的芯片设计也有很多良好的帮助。
本文适合哪些人看:1、掌握c语言,内核开发c语言是基础;2、会Linux命令,我还是用我最喜欢的Fedora上实现;3、有计算机组成原理的基础,我会对比risc和cisc指令设计的不同; 4、最好是学过x86或者arm这些体系结构,我会使用x86架构来类比迁移学习risc-v。
本文尽可能少的去讲理论,而是去实践编程,也就是带大家去上手写risc-v的汇编并观察现象,把理论和实践结合起来。我主要是从一个内核开发者的角度去看,也就是从软件开发的角度着手,关于RISC-V硬件相关的功能,我只能少量的提一提,多了也不会。
以下是我当前的环境,还是那个熟悉的Fedora.
学习risc-v指令集有许多的资料,希望大家能够用起来:
《RISC-V体系结构编程与实践》
bilibili汪辰老师的视频
The RISC-V Reader: An Open Architecture Atlas
一些参考资料的合集
大家也能够看出来,risc-v指令集有许多的中文资料,这一点也是比x86更加容易学习的一个原因。希望大家能够把这些资源都利用起来,本文实际上理论来源也是官方的文档和这些参考资料。
一、RISC-V指令集简介
1、什么是ISA
ISA是Instruction Set Architecture,翻译成中文就是指令集架构的意思,可以看作是一系列指令的集合,这里的指令不仅仅是你能看见的汇编语言的指令,它还包括了这些指令对应的底层的电路设计。Instruction有指令、命令、吩咐的意思,Set就是数学上的集合,Arch这个缩写相信大家经常能够看到,是架构的意思。
2、有哪些ISA
我们知道的指令集就有x86、arm、risc-v、mips、loongarch、sparc等
实际上你在终端敲qemu然后按两下Tab,就能够看到qemu能够模拟的指令集。
既然提到了Qemu模拟器/虚拟机,那么我们就在这里安装它吧,Qemu将是我们以后学习RISC-V指令集最重要的工具之一。
sudo dnf install qemu-system-riscv qemu-user-static-riscv
前者是system mode是模拟整个计算机系统,就是它会把riscv芯片和外部设备等硬件全部模拟出来,然后你可以把它当作是一个开发板、裸机之类的概念,模拟硬件。它启动的时候是在M模式下,是要从opensbi开始运行,这个我们暂时不用管,从用户程序开始学起比较容易。
而后者是user mode,是模拟一套Linux软件,相当于是运行了一个riscv Linux系统,你可以把你写的用户程序在这个模式下跑。
为了保险起见,你可以多安装几个qemu的包,防止在运行的时候出现一些莫名其妙的错误。
我们可以给这么多的指令集进行分类。
3、CISC和RISC
CISC:Complex Instruction Set computer 复杂指令集 比如x86
在设计指令的时候是针对特定的功能设计的,也就是说它的一条指令对应了一种功能,比如x86里的mov指令,能够把数据从寄存器移动到寄存器、可以把数据从内存移动到寄存器、还可以把数据从寄存器移动到内存。这个mov指令就是针对数据移动这个功能设定的。随着计算机越来越复杂,实现的功能越来越多,那么CISC ISA就要不断地添加指令,每多一个功能就要加一个指令。优点就是你掌握好少数几条指令就可以做许多的功能,因为一条指令可以有很多用途的,而且生成的程序比较短,占用空间小;并且对兼容性和拓展性的支持也比较好,你想推出新功能,在原来的基础上添加新的指令集就好了。而缺点也很明显:每多一种就加一条指令,那么最终指令的数量会越来越多,整个指令集和电路的设计就会越来越膨胀。一开始还能接受,到了后期可能扩展到了几千条指令,据我查到的数据新x86指令集是有1468条指令,现在也许更多了,指令数量越多越难以把握。实际上x86的这种设计是针对早期计算机存储空间太小问题而设计的思路,那时候的程序员绞尽脑汁地开发就是为了自己的程序能够省一点内存。
RISC:Reduced Instruction Set computer 精简指令集 比如arm和risc-v
RISC ISA在设计指令的时候和CISC全然不同,它是只定义一些常用、必要的指令,然后你要实现更多功能,那么就需要对这些指令进行“排列组合”,用好几条指令来实现原本CISC一条指令就能实现的功能。也就是它不再按照功能来设计指令了。那么在这种设计哲学下,随着计算机功能越来越多,RISC ISA的指令条数增长的速度相比CISC就会慢很多,不会造成电路设计过于复杂、指令数量膨胀这些缺点。RISC的缺点是生成的程序比较长、占用空间大。但是由于现代计算机发展迅速,存储已经不再是已经太大的问题了,现在的个人电脑16GB内存已经是标配了,计算机只有64KB内存的时代已经一去不复返了。
但实际上,RISC和CISC这二者之间并不是水火不容的关系,而是互补的关系。RISC指令集有CISC的设计思想在里面,CISC指令集也有RISC的设计思想在里面,互相融合可以解决很多原本的缺陷,从而更加适应现实里复杂的环境。
x86系列,面向外部的指令集是CISC(复杂指令集计算机),但在内部,这些复杂的指令在执行之前会被分解成更小、更简单的操作,这些操作称为微操作(micro-operations 或 μops),这种新的设计思想就叫做Intel微架构。
而risc-v指令集为了方便程序员编程,也设计了一系列的“伪指令”,伪指令本身不翻译成机器码而是展开成好几条汇编指令,伪指令把多条汇编指令封装起来,实现类似CISC那样一条指令实现一个功能这样的设计,这样是为了方便程序员使用。
4、什么是RISC-V
终于回归主题了
RISC-V全名Reduced Instruction Set Computer ,即精简指令集计算机。V是罗马字母5,你可以认为这是伯克利大学开发的第五代精简指令集。它可以读作 risk five(/ˈrɪsk ˈfaɪv/)
1. RISC 的起源
RISC 的概念最初在 1980 年代初由加州大学伯克利分校和斯坦福大学的研究者提出。这些研究者发现,通过简化处理器中的指令集,可以提高其执行效率和速度。最初的 RISC 设计包括:
- John Hennessy 在斯坦福大学领导的 MIPS(Microprocessor without Interlocked Pipeline Stages)项目。
- David Patterson 在加州大学伯克利分校领导的 RISC-I 和 RISC-II 项目。
2. RISC-I 和 RISC-II
- RISC-I:1982 年,伯克利的 RISC-I 项目产生了第一款 RISC 处理器。这款处理器包含了约 31 条指令,实现了高度的指令流水线化。
- RISC-II:紧随 RISC-I 之后,伯克利团队开发了 RISC-II,它包含了更多的指令和优化,证明了 RISC 架构的有效性和高性能。
3. RISC 发展和商业化
80 年代中后期,RISC 架构开始被更广泛地接受和商业化。例如,Sun Microsystems 开发了基于 RISC 的 SPARC 架构,IBM 推出了 POWER 架构,同时 MIPS 和 ARM 架构也开始流行。
4. RISC-V 的诞生
- 起始:RISC-V 指令集是在 2010 年由加州大学伯克利分校的研究者,包括 David Patterson 和其他人,发起的。RISC-V 的设计目标是创建一个开放、免版税、可扩展的指令集架构,旨在支持从最小的嵌入式系统到高性能计算机系统的广泛应用。
- 开放性:RISC-V 是第一个完全开源的指令集。这意味着任何人都可以使用、修改并分发 RISC-V,而无需担心版权问题。
- 发展:自推出以来,RISC-V 逐渐成为学术界和工业界的热点。多个组织和公司,包括 Google、NVIDIA、Western Digital 等,开始投资于 RISC-V 的研发。
从以上的故事里,我们不难看出,加州大学伯克利分校对这个RISC类型的指令集真的是非常执着,一直有专门的教授和学者在研究指令集的设计思想。我目前没有搜索到关于RISC-III和RISC-IV指令集的故事,也许它们是伯克利大学内部的一些研究方案,没有公布出来。RISC-V 指令集是在 2010 年由加州大学伯克利分校的研究团队开发并以 BSD 开源协议发布的。这种开源发布方式允许任何人可以*地使用、修改和重新分发 RISC-V,而无需支付版税或遵循严格的许可限制,这是 RISC-V 快速增长和广泛采用的一个重要因素。那么既然RISC-V指令集是伯克利大学设计和发布的,使用BSD协议开源也是很合理的吧。它最大的特点和优势就是开源,在此之前的流行指令集都是闭源的,牢牢掌握在商业公司的手里,要么就对外出售知识产权IP核心,这样虽然也算是开放了,但是不开源还是很难服众。而RISC-V是完全的开源且免费将吸引更多的企业入局,也会加强开源生态的建设。
5、RISC-V生态的特点
这里的特点讲的不是risc-v指令集本身,而是它背后的组织的特点。
RISC-V的官网
RISC-V 指令集目前由 RISC-V International 维护和推广。RISC-V International 是一个非营利性的全球性组织,负责协调 RISC-V 的技术和标准发展。这个组织由来自全球的多个企业、学术机构和个人成员组成,它们共同协作推动 RISC-V 架构的发展和应用。
RISC-V International 的总部最初设在美国,但为了强调其全球性和独立性,该组织在 2020 年将其法律和运营总部迁移到瑞士的日内瓦。这一举措旨在利用瑞士的国际中立地位,更好地服务其全球会员,并避免特定国家的政策影响。
虽然前面说了,RISC-V是加州大学伯克利分校的教授和学者设计发布的,但是它现在属于瑞士的一个非营利组织,负责指定和推动RISC-V标准的制定。
以下是一些非常重视RISC-V并且能够推动和制定RISC-V标准的企业:
西部数据(Western Digital) - 西部数据是 RISC-V 基金会的创始成员之一,并且计划在其数据存储产品中广泛使用 RISC-V 处理器。公司对 RISC-V 的推动非常积极,旨在利用 RISC-V 的可扩展性和开源优势来优化其产品。
谷歌(Google) - 谷歌对开源技术一向有着深厚的兴趣,RISC-V 也不例外。谷歌已经在多个项目中考虑使用 RISC-V,特别是在其自定义硬件和数据中心技术中。
英伟达(NVIDIA) - 又是我们的老朋友NVIDIA. NVIDIA 对 RISC-V 表示了显著的兴趣,尤其是在嵌入式处理器和辅助处理器中。英伟达是 RISC-V 基金会的高级成员,并参与了多个相关的开发项目。
高通(Qualcomm) - 作为全球领先的芯片制造商,高通对 RISC-V 的支持强调了这一架构在移动和物联网设备中的潜力。高通加入了 RISC-V International,参与标准化和技术发展工作。
SiFive - 作为一家专注于商业化 RISC-V 处理器和平台的领先公司,SiFive 是由 RISC-V 架构的创始人之一启动的。SiFive 不仅设计 RISC-V 基础的芯片,还积极参与 RISC-V 的标准化工作。
阿里巴巴 - 阿里巴巴的子公司平头哥技术有限公司已经发布了基于 RISC-V 的处理器,并在其云计算和数据中心产品中使用。阿里巴巴支持 RISC-V 的发展,以促进技术的创新和自主控制。平头哥的芯片确实不俗,有相当的实力的。
在这些企业里面,大家重点需要关注的是这个SiFIve公司,它是RISC-V创始人成立的一家公司,给RISC-V指令集的商业推广起到示范作用,它为RISC-V体系结构提供商业化的IP核心、开发工具包和芯片解决方案。
6、RISC-V指令集的特点
1. 开源
- 定义:RISC-V 是完全开源的指令集架构(ISA),意味着任何人都可以查看、使用、修改以及分发其设计,而无需支付版权费用。
- 优势:这种开源特性促进了全球性的创新和合作,这个特点前面讲过了,我这里就不重复了。
2. 社区化
- 定义:RISC-V 的发展由一个活跃的全球社区支撑,包括学术界、工业界以及个人贡献者。
- 优势:社区化确保了 RISC-V 的设计和发展方向能够响应广泛用户的需求,同时增强了技术的透明度和多样性。
3. 设计简洁
- 定义:RISC-V 保持了 RISC(Reduced Instruction Set Computer)架构的核心原则,即提供一个简洁的指令集,使得芯片设计更为简单、高效。
- 优势:简洁的设计有助于降低硬件实现的复杂性,提高处理器的执行效率和易于优化。
4. 模块化
- 定义:RISC-V 指令集支持模块化扩展,允许开发者根据具体应用需求添加或定制特定的指令模块。
- 优势:模块化设计使得 RISC-V 可以灵活适应各种应用场景,从嵌入式系统到高性能计算,都可以通过添加专门的指令扩展来优化性能。
5. 分层设计
- 定义:RISC-V 架构采用了分层设计方法,基本指令集可以通过额外的扩展层来增强功能,如浮点运算、向量处理等。
- 优势:分层设计不仅保持了核心指令集的简洁性,同时也提供了高度的可扩展性和定制性。用户可以根据需要选择性地实现这些扩展,以适应特定的应用需求,而不必在所有实现中支持所有功能。
二、RISC-V的基础知识
虽然我前面说尽可能少去讲述risc-v的理论,但是必要的基础知识还是要掌握的。这里我为了简化描述,会把RISC-V简写成rv或者是RV.
RV指令集是一个现代的指令集,它已经完全抛弃了对16位的支持,因为现代计算机也没有16位的了,嵌入式设备使用32位,通用计算机使用64位。因此rv可以分为RV32和RV64,但无论是RV32还是RV64,都是32个通用寄存器,我会在后面通过例子具体讲到它们的用法。架构是多少位,那么对应的通用寄存器的大小就是多少位。
RISC-V体系结构有一套命名标准来描述当前CPU支持哪些指令集,RISC-V是模块化、可拆卸的,非常灵活。比如RV32I、RV64G、RV32IMA这样。
命名格式如下:
RV[n][I/F/D/Q/M/C/A/B/E/H/K/V/P/J/T/N]
- RV代表这是RISC-V指令集
- n代表当前指令集是几位的,32位还是64位
- 后面的是指令集模块,模块含义如下表格
指令集模块名 | 说明 |
I | 基本整数指令集,这是RV的基本指令集,是必选的,如果没有I那么这就不是RV指令集了,下面的都是扩展指令集,是可选的。(Integer) |
F | 单精度浮点数扩展指令集(Float) |
D | 双精度浮点数扩展指令集(Double) |
Q | 四倍精度浮点数扩展指令集(Qual) |
M | 整型乘除法扩展指令集(Multiple) |
C | 压缩指令集(Compress) |
A | 原子操作指令集(Atomic) |
B | 位操作指令集(Bit) |
E | 为嵌入式设计的整型指令集(Embedded) |
H | 虚拟化扩展指令集(Hyper) |
K | 密码运算扩展指令集(Key) |
V | 可伸缩矢量扩展指令集(Vector) |
P | 打包SIMD扩展指令集(packed) |
J | 动态翻译语言(JIT)扩展指令集 |
T | 事务内存指令集(Transactional Memory) |
N | 用户态中断指令集(User-Level Non-maskable Interrupts) |
所以当你拿到一块RV的CPU,那么这块CPU有多少斤两、有多少能力、有多少指令集的支持,一眼就能看出来了,你看它的命名就好了。而像x86架构的话就很麻烦,还要用CPUID一组一组去测试和判断,看是否支持对应的指令集(关于这个CPUID如果大家感兴趣的话我可以单独开一期来讲)。大家在记忆这些指令集名称的时候,就根据英文去记就好了,不难记。
在扩展指令集里有一个特殊的“稳定的指令集组合”,用G来表示(General),这不是某一个指令集,而是一组指令集IMAFD。
我们已经掌握了RV命名的标准,现在来做几个例子:
1、RV32IMAC
就意味着这是RISC-V的32位的带有通用整形支持、整形乘除法、原子指令支持、压缩指令支持的一块CPU.
2、RV64G
我们把G代进去就是,RV64IMAFD,意味着这是RISC-V的64位的带有通用整形支持、整形乘除法支持、原子指令支持、单精度浮点数支持、双精度浮点数支持的CPU.
这只是一种描述方法,这块CPU可以是一块真是的硬件CPU,也可以是一块虚拟出来的virt CPU,甚至还可以是工具链的配置,比如写在-march选项里。总之用这种方式能简洁的描述硬件提供了或者软件使用到了哪些指令集。你需要匹配这二者,否则要是执行到了不支持的指令集的的指令的话,那就要引发trap了。
那我理论就讲到这里,大家务必把上面我讲述的内容全部学会,这个东西比较重要,你在构建开发工具链和模拟器的时候需要这些参数来写配置文件的。
下面的内容大家当成是个资料简单了解一下每个指令集的实际作用:
RISC-V国际基金会将持续完善至少8种可选扩展
1、B标准扩展:位操作
B扩展提供位操作指令,包括位字段的插入、提前和测试;循环移位,漏斗移位,位和字节的排列;计算前导0和尾随0;计算置1的位数(这和x86的PF标志位有点像)
2、E标准扩展:嵌入式
为降低低端CPU的开销,RV32E减少了16个寄存器,保存寄存器和临时寄存器均被拆分称0-15和16-31号两个部分。
3、H特权态架构扩展:虚拟化的意思
H扩展向特权态架构添加超监管模式和第二级页式地址翻译机制,意思就是一台机器上开多个虚拟机的时候,有这个扩展的话,运行效率将大大提升
4、J标准扩展:JIT动态翻译语言
比如Java和Javascript这些编程语言都是基于动态翻译实现的,如果CPU支持这个扩展的话,那么运行JVM、Python解释器的效率将大大提升
5、L标准扩展:十进制浮点
支持IEEE 754-2019标准所定义的十进制浮点算术运算
6、N标准扩展:用户态中断
这个用户态也能直接执行中断程序了,而不是“陷入内核”,看起来好像很不安全,但其实这个是给嵌入式设备准备的。它就是在用户态发生中断和异常的时候,把控制权直接交给用户态(一般来说都是会陷入内核,让操作系统内核来解决这些问题)。
7、Q标准扩展:四倍精度浮点
这个是符合IEEE754-2019标准的128位四倍精度二进制浮点指令。如果有Q扩展出现,那么F和D扩展也必然出现。
8、P标准扩展:紧缩SIMD指令
加强并行计算能力,投入和细分资源提升数据的并行度,是用来做向量计算的,要有RVV扩展的前提下使用这个扩展。这个其实有点类似显卡的能力了,也许未来某一天会出现基于RISC-V架构的显卡?你说是吧,NVIDIA.
三、搭建RISC-V的学习环境
1、交叉编译工具链的安装
交叉编译是在一个平台(宿主)上生成另一个不同平台(目标)上运行的代码的过程。简单来说,交叉编译器能在一个操作系统或硬件架构上编译程序,而这些程序是为在另一个不同的操作系统或硬件架构上运行设计的。
我假设你的实验平台就是x86架构,这是个人电脑的最主流指令集架构。你在x86平台上使用gcc编译器编译的c语言程序,它是x86架构的,只能在x86环境上运行,而不是编译成RISC-V的机器码。
可以使用file命令去查看,这个编译后默认生成的a.out就是x86_64架构的,ABI也是x86的。也就是说你不可能把这个程序直接跑在arm或者RISC-V这些其他指令集的机器上。你需要使用专门的交叉编译的工具,用这个RISC-V特色的gcc去编译生成的程序,才能直接跑在RISC-V机器上。
那么我推荐的工具就是GNU的那一套:gcc、gas、GNUld.我们需要把这些工具给构建出来,不能通过dnf包管理器安装了。
下面我会放一些链接,希望大家都能点进去看看。
RISC-V开发工具链https://github.com/riscv-collab/riscv-gnu-toolchain