本篇文章的内容
本系列是博主参考B站课程学习开发一个RISC-V的操作系统的学习笔记,计划从RISC-V的底层汇编指令学起,结合C语言,在Ubuntu 20.04上开发一个简易的操作系统。一个目的是通过实践操作学习和了解什么是操作系统,第二个目的是为之后学习RISC-V的集成电路设计打下一定基础。本系列持续不定期更新,分享出来和大家一同交流进步。
博主是微电子科学与工程专业的学生,对软件和操作系统难免有理解不到位的地方。如有谬误敬请不吝告知,不胜感激。
参考课程及文章:
【Bilibili】[完结] 循序渐进,学习开发一个RISC-V上的操作系统 - 汪辰 - 2021春
一、计算机组成原理的相关知识
1.1 计算机的硬件组成
- CPU(Central Processing Unit):中央处理单元
- CU(Control Unit):控制单元
- ALU(Arithmetic Logical Unit):算数逻辑单元
- Register x:多个寄存器
- IO Bridge:IO桥
- Main Memary:主存(内存)
- 外设(屏幕,键盘,鼠标等)
上图展示了两种主流的计算机的组成架构: - 冯诺依曼架构(Vonda Neumann architecture):又称普林斯顿架构(Princetion architecture),特点是指令和数据不加区别地存储在存储器中,经由同一个总线传输。优点是总线开销小,控制逻辑实现更简单;缺点是执行效率较低。现在大量的PC机和服务器都是采用这种架构设计的。之后的课程主要围绕该架构展开。
- 哈佛架构(Harvard architecture):特点是将程序指令和数据分开存储。优点是执行效率较高,缺点是总线开销更大,控制逻辑实现更复杂。小型的微控制器倾向于使用该种架构。
1.2 程序的存储与执行
想要在计算机上运行程序,我们首先要将编辑好的程序进行编译和链接,将其存储在硬盘(Disk)中,通过硬盘的控制器(Disc Controller)将编译后的机器指令a.out
文件存储到内存中,CPU通过IO桥不断依次进行取指(Fetch)、译码(Decode)、执行(Excute),此时我们才说计算机正在运行我们的程序。取指是将机器指令取到对应的寄存器中。晶振是外部的程序推动者,不断驱动处理器进行上述过程。
1.3 程序语言的设计和进化
假设现有一个只能执行加法操作的8位计算机,我们通过对加法过程进行分析(如上图所示),发现执行加法操作只需要从内存中取数据(LOAD)、执行加法操作(ADD)、存储数据到内存(STORE) 三步操作。我们对不同的操作进行编码,设计不同的指令编码格式。假设采用冯诺依曼架构,即操作指令和数据都存储在同一块内存中,那么计算机执行该程序前内存的存储内容如下图所示(内存中黄色的是指令,绿色的是数据):
1.4 存储设备的层次结构
1.5 操作系统
如上图所示,操作系统是介于底层硬件和应用程序之间的结构,它通过两个接口分别和上下两层相连。操作系统和应用程序之间的接口称为系统调用(System Call),操作系统和底层硬件之间的接口称为指令集架构(ISA)。操作系统主要的作用主要可以归结为以下两点:
- 保护硬件被失控的软件应用程序滥用
- 向应用程序提供简单一致的抽象接口来控制复杂的多种外设硬件。
二、RISC-V的指令集ISA简介
2.1 什么是ISA
ISA(Instruction Set Architecture),指令集架构,是底层硬件电路面向上层软件程序提供的一层接口规范。ISA不单单指一种汇编语言,它定义的内容比汇编语言更加广泛。ISA定义了:
- 基本数据类型(BYTE/HALFWORD/WORD/…)
- 寄存器(Register)的种类和名称
- 指令
- 寻址模式
- 异常和中断的处理方式
- …
ISA为上层软件提供一层抽象,制定规则和约束,让编程者不用操心具体的电路结构。IBM 360 是第一个将 ISA 与硬件实现分离的计算机。
2.2 复杂指令集(CISC)和精简指令集(RISC)
CISC复杂指令集(Complex Instruction Set Computing),针对特定的功能实现特定的指令,导致指令数目比较多,但生成的程序长度相对较短。一般而言,使用复杂指令集开发的CPU指令的种类很多,对于每一种操作都有一种特定的指令。就像在中国古代,文字写在竹简或布匹。为了表达更多的信息,考虑到成本问题,一个文字表达的含义被极大地丰富起来。典型的复杂指令集有x86等。
RISC精简指令集(Reduced Instruction Set Computing)只定义常用指令,对复杂的功能采用常用指令组合实现,这导致指令数目比较精简,但生成的程序长度相对较长。精简指令集就像现代的白话文,当人们不再考虑写字带来的成本时,表达相同的意思,人们就不再“惜字如金”,用简单、易读、易理解的方式来表达信息。典型的简单指令集有SPARC、Power、ARM、MIPS、RISC-V等。
现如今,RISC 和 CISC 也逐渐有相互融合的趋势。Intel最早采用复杂指令集,现如今也在逐渐向精简指令集靠近。
2.3 ISA的宽度
ISA (处理器)的宽度指的是 CPU 中通用寄存器的宽度(二进制的位数),实际上就是CPU处理器的字长,这决定了寻址范围的大小、以及数据运算的能力。
注意:ISA 的宽度和指令编码长度无关。
2.4 RISC-V的特点
- 简单:相较于x86,RISC-V的指令很少,相关的技术手册也较少。
- 清晰的分层设计
- 模块化:拥有核心指令集和扩展指令集。
- 稳定:总结了前人的经验。
- 社区化:开源,自由,公开。
2.5 RISC-V ISA的命名规范
- ISA 命名格式:RV[###][abc……xyz]
- RV:用于标识 RISC-V 体系架构的前缀,即 RISC-V的缩写。
- [###]:{32, 64, 128} 用于标识处理器的字宽,也就是处理器的通用寄存器的宽度(单位为 bit)。
- [abc…xyz]:标识该处理器支持的指令集模块集合。不同的字母代表了不同的指令集模块。
例子:RV32IMA,表示该32位处理器使用RISC-V的I、M、A指令模块,RV64GC同理。
2.6 RISC-V ISA的模块化
对于ISA而言,其有两种发展方式,分别是增量化和模块化。
增量化 ISA: 计算机体系结构的传统方法,同一个体系架构下的新一代处理器不仅实现了新的 ISA 扩展,还必须实现过去的所有扩展,目的是为了保持向后的二进制兼容性。典型的,以 80x86 为代表。
模块化 ISA: 由 1 个基本整数指令集 + 多个可选的扩展指令集组成。基础指令集是固定的,永远不会改变。扩展指令集类似一种插件的思想。
RISC-V的ISA中的基本整数指令集(Integer)是唯一强制要求实现的基础指令集,其他指令集都是可选的扩展模块。其中嵌入式指令集(Embedded)是基本整数指令集的子集,在一些小型的嵌入式场景中适用。高字长的基本整数指令集向下兼容,如下表所示:
扩展模块指令集:
- RISC-V 允许在实现中以可选的形式实现其他标准化和非标准化的指令集扩展。
- 特定组合“IMAFD”被称为 “通用(General)”组合,用英文字母 G 表示。
2.7 RISC-V ISA的通用寄存器
RISC-V 的非特权规范(Unprivileged Specification)定义了 32 个通用寄存器以及一个 PC(程序计数)寄存器。这种结构对 RV32I/RV64I/RV128I 都是相同的。如果实现支持 F/D 扩展则需要额外支持 32个 浮点(Float Point)寄存器。RV32E 将 32 个通用寄存器缩减为 16 个。
寄存器的宽度由 ISA 指定,RV32 的寄存器宽度为 32 位,RV64 的寄存器宽度为 64 位,依次类推。
每个寄存器具体编程时有特定的用途以及各自的别名。由 RISC-V Application Binary Interface (ABI) 定义。
2.8 RISC-V的硬件线程HART
HART = Hardware Thread。早期的CPU中,一般只有一个CU(Control Unit),所以在任何时刻,只有一条硬件流被执行。如今Intel的CPU中一般有多个核,一个核可以有两个硬件流,这就容易引起线程混淆的问题。通过HART可以很好地区分两个不同的指令线程,可以将一个HART想象成一个独立的CPU。在规范手册中HART的概念频频出现,而处理器CPU的概念却不怎么出现。下面的话摘自RISC-V的官方手册:
从软件编程的角度来说,一个HART就是一个可以在执行环境中自主地,独立地,不受干扰地取指令和执行指令的资源。
2.9 RISC-V的特权级别和CSR
为了实现分级保护的功能,RISC-V 的 Privileged Specification 定义了三个特权级别(privilege level),在CPU内部要求有一个类似多级开关的结构,对应三种特权级别。如下表所示:
Machine级别是最高的级别,所有的实现都需要支持。RISC-V的CPU上电之后自动运行在Machine(机械)态,此时CPU中操作的地址是真实的物理地址。通过特定的操作进入Supervisor(管理者)态之后,此时访问的地址就是虚拟地址,对真实的物理地址起到了保护的作用。此外,RISC-V还提供了可选的Debug级别:
CPU中有控制和状态寄存器(CSR,Control and Status Register),CPU在不同的工作模式下使用不同的独立的CSR,用于控制和获取相应Level下处理器的工作状态。这些寄存器在不同的工作模式下是不能互相访问的。通过这种设计,才能真正实现分级保护的目的。
高级别的特权级别下可以访问低级别的 CSR,譬如 Machine Level 下可以访问Supervisor/User Level 的 CSR,以此类推,但反之不可以。
RISC-V 定义了专门用于操作 CSR 的指令(“Zicsr”扩展)。
RISC-V 定义了特定的指令可以用于在不同特权级别之间进行切换(ECALL/EBREAK)
2.10 内存管理和保护
物理内存保护(Physical Memory Protection,PMP),它是一种较为低级的内存管理和保护方式,在一定程度上可以保护内存。
- 允许 M 模式指定 U 模式可以访问的内存地址。
- 支持 R(读)/W(写)/X(执行),以及 Lock(锁定,不再使用),对特定的内存区域的权限进行规定。
虚拟内存(Virtual Memory),程序访问的地址不再是实际的物理地址,而是虚拟地址。通过特定的硬件外设MMU(Memory Management Uint,内存管理单元)可以将虚拟地址映射到物理地址上。
- 需要支持 Supervisor Level(管理者模式)
- 用于实现高级的操作系统特性(Unix/Linux)
- 多种映射方式 Sv32/Sv39/Sv4
2.11 异常和中断
异常(Exception):在当前RISC-V HART中与指令相关的运行时发生的一种异常情况,例如除零异常。发生异常后执行异常处理程序(由程序员自行编写),执行过后回到发生异常的地方再次执行。再次执行相当于程序对异常指令的又一次尝试。
中断(Interrupt):可能导致RISC-V HART经历意想不到的控制转移的外部异步事件,发生中断后,CPU会先执行完当前的指令,之后再执行中断服务程序,返回时执行发生中断的指令的下一条指令继续执行。
原创笔记,码字不易,欢迎点赞,收藏~ 如有谬误敬请在评论区不吝告知,感激不尽!博主将持续更新有关嵌入式开发、机器学习方面的学习笔记。