RISC-V 入门-RVOS 系统引导

操作系统定义与分类

操作系统(英语:Operating System,缩写:OS)是一组系统软件程序,狭义上就是内核如 Linux,广义上就是内核加一组软件组成的发行包,如 Ubuntu,Debian:

• 主管并控制计算机操作、运用和运行硬件、软件资源

• 提供公共服务来组织用户交互。

硬件的基本概念

  • Hart
  • Platform
    不能说是个板子,应该理解为芯片。早期的板子就是一块芯片加上各种外设,但是随着技术发展,板子越来越小,外设却并没有变少,是因为外设都被集成到了芯片中。当所有外设都被集成,那么芯片就是 platform。
  • SoC(System on Chip)
    片上系统

QEMU 模拟 virt 这个平台,这个平台有八个 Hart。

地址映射

为了方便访问外设,现在主流的 platform 会对外设的内存地址做一个映射。映射到 platform 的真实物理地址。对真实物理地址进行操作时,就是对外设的地址进行操作。

物理地址从最低位到最高位都被分配给了各种外设。

引导过程介绍

通电后,会先到箭头所指的地址,这个地址就是对应的 ROM 外设首地址。ROM 相当于一个小硬盘,断电后不会丢失数据。这里面固化了一些指令。

主要就是跳转指令,运行到 kernel 段继续执行。

八核同时会执行这个过程。

以上是硬件的部分过程,软件该如何写?

为了简化学习流程和降低调试难度,目前只支持单核,其余七个核处于空转状态。

如何判断当前 Hart 是不是第一个?

这些寄存器必须使用以下的指令读写:

以上指令就是将寄存器值进行一次交换,只不过这个过程是原子性的,不能被打断。

CSRRW经常会用在伪指令CSRW中,完整指令中,第一步向x0写入数据,就是空操作,第二步将rs写入csr。这个伪指令就是完成了一个写入csr的操作。

mhartid就是machine hart id

学习以上几个指令,就可以完成判断 hart 是否为第一个的工作了,

csrr t0, mhartid    #读寄存器值
mv tp, t0           #
bnez t0, park       # 跳转指令,不等于 0 就跳转到 park 标签
Assembly
wfi 休眠指令

如何初始化栈空间

如何跳转到 C 语言环境

Assembly
# start.S #include "platform.h" # size of each hart's stack is 1024 bytes .equ STACK_SIZE, 1024 .global _start .text _start: # park harts with id != 0 csrr t0, mhartid # read current hart id mv tp, t0 # keep CPU's hartid in its tp for later usage. bnez t0, park # if we're not on the hart 0 # we park the hart # Setup stacks, the stack grows from bottom to top, so we put the # stack pointer to the very end of the stack range. slli t0, t0, 10 # shift left the hart id by 1024 ###### 初始化栈空间 ###### # set the initial stack pointer to the end of the first stack space la sp, stacks + STACK_SIZE # move the current hart stack pointer to its place in the stack space add sp, sp, t0 ###### 初始化栈空间 ###### ###### 跳转到C语言环境 ###### j start_kernel # hart 0 jump to c, start_kernel is the entry point of the kernel ###### 跳转到C语言环境 ###### park: wfi j park stacks: # allocate space for all the harts stacks .skip STACK_SIZE * MAXNUM_CPU .end # End of file
// kernel.c
void start_kernel(void)
{
  while (1) {}; // stop here!
}

通过 UART 打印信息

连接方式

真实的硬件开发是有一个快开发板,但是这个课程里使用的是 QEMU 来模拟开发板的硬件环境。如果要在程序里打印一段信息,正常的情况是在开发板上连接显示器,但是这里是通过将信息用串口传到主机上,然后用主机的屏幕显示信息。

串口线里是有两根线,负责收信息和发信息。

UART 特点

  • 并行就是需要多根线,比如有两根线,那么就可以一次发送两位。但是串行节省材料。
  • 数据通信就会涉及同步的问题,同步的话需要一根时钟线来协商好发送时间和接收时间。而 UART 使用异步,发送的数据不仅仅是真实的数据,还会带有一些标识信息。这些标识可以判断出是收还是发。

物理接口

UART 通讯协议

图示中横轴可以表示时间,纵轴表示高低电平。

在需要发送数据时,会进行“下拉”1bit,1bit 持续的时间就是波特率分之一秒。

数据在发送过程中可能会受到干扰,会产生畸变,所以需要检验位来判断是否发生畸变。

初始化

在软件中,配置 UART 就是配置寄存器的信息。

在板子上有个元器件叫晶振(crystal),他会产生固定频率的时钟。一种是 1.8432MHZ,一种是 7.3728MHZ。想要获得指定的输出频率就需要对寄存器进行配置。查表可以得到配置信息。比如获得 38.4K 频率的输出,就要配置寄存器值为 3。

LCR 寄存器功能比较多,将第 7 位设置为 1 就是用来设置波特率。

图中DLLDLM寄存器就是需要配置的寄存器。因为 UART 寄存器都是 8 位的,将值0x0003高位0x00存在DLM中,将低位0x03存入DLL