前段时间,中国科学院大学的「」计划引发热议,五位本科生带着自己设计的处理器芯片正式毕业,被称为「最硬核毕业证」。其实,东京大学信息科学系也有一个自制 CPU 的实践课程。近日,微软软件工程师 Takaya Saeki 刊文回顾了五年前他们小组的 CPU 实验项目:不仅通过自学自制了 CPU、C 编译器,还成功移植了一个类 Unix 操作系统(Xv6)。虽然回顾的是五年前的往事,但这篇文章应该也能为芯片和操作系统人才培养工作带来一些启发。
所有这一切都源自一个学生实验项目:CPU Experiment(CPU 实验)。首先说说这个 CPU 实验是什么。 CPU 实验是东京大学信息科学系一个小有名气的实践课程,通常在大三的冬季进行。在该实验中,学生会被分成小组,每组四、五个人。每一组都要设计一种自己的 CPU 架构,在 FPGA 上实现它,为该 CPU 构建一个 OCaml 子集编译器,然后在该 CPU 上运行一个给定的光线追踪程序。通常来说,CPU、FPU、CPU 模拟器和编译器都各由一两个人负责。我负责第 6 组的 CPU 部分。 这个实践课程的有名之处在于对自学能力的高度期望。导师向学生们下达了任务目标:「把这个用 OCaml 写的光线追踪程序运行在你们用 FPGA 实现的 CPU 上」,然后就下课了。对于编写 CPU 和编译器的具体步骤,他不会多说。学生需要自己学习如何将学过的有关 CPU 和编译器的一般知识转化成实际成品,这将涉及到实际的电路和代码。是的,这个实践课程确实很难,但也很激动人心且极具教育意义。 在我们自己的 CPU 上运行操作系统 你可能已经注意到了,我还没谈到操作系统。我来稍微解释一下。 通常来说,这个实验会这样进行。首先,做出一个能可靠工作的 CPU,不管计算速度如何。如果做出了 CPU 并成功运行了那个光线追踪程序,就能得到这个实践课程的学分。之后,你的团队就自由了。通常来说,这些自由时间会被用于 CPU 提速。在过去的实验中,学生做出过乱序 CPU、VLIEW CPU、多核 CPU 甚至超标量 CPU,确实很了不起。 但是,有些团队则把更多精力放到了一些有趣任务上,比如运行游戏或将 CPU 与扬声器连接来播放音乐。我们第 6 组也是一个热爱娱乐的小组,而我们决定将目标设定为运行一个操作系统。 结果,其它一些小组也对这个想法产生了兴趣。于是,一个包含 8 个人的联合小组——Group X 成立了。我们的目标是:「在我们自己的 CPU 上运行 OS!」 尽管我负责第 6 组的 CPU 创建工作,但这一次我选择当 Group X 的领导者。因此,本文主要是从 OS 团队角度写作的,不过我也会介绍 Group X 的整体成果。 Xv6 对于要移植的 OS,我们选择了 Xv6,这是一个由 Unix v6 启发的简单操作系统,是 MIT 为教育目的构建的。不同于 Unix v6,Xv6 是用 ANSI C 编写的,而且运行在 x86 架构上。Xv6 是一款教育用 OS,所以功能有些简陋,但作为一款简单的类 Unix 操作系统,功能已经足够了。有关 Xv6 的更多信息可访问其 GitHub 代码库:https://github.com/mit-pdos/xv6-public 挑战 在移植 Xv6 时,光是软件方面就有一大堆难题,因为我们在尝试从头开始构建一切。 1. 用于 Xv6 的 C 编译器和工具链。 在 CPU 实验中,我们通常会创建一个 ML 编译器。很自然,这样无法编译 Xv6 的 C 代码。 2. 操作系统需要 CPU 具备哪些功能? 特权保护?虚拟地址?中断?是的,我们在课堂上已经获得了对操作系统的整体理解,但那时候我们对各个 CPU 功能的具体作用还没有真正的切身体会。 3. 模拟器呢? 我们已经在 CPU 实验的核心任务部分做了一个模拟器,但那个模拟器很简单,只能逐一执行指令,而且没有中断和虚拟地址转换。 4.Xv6 的可移植性差 Xv6 很难移植。举个例子,它假设 char 是 1 个字节,而 int 是 4 个字节,并会大量操作堆栈。好吧,我猜 Xv6 这个名字实际上来自 x86 和 Unix v6,所以这种设计当然很自然。 我们有过很多担忧,但还是在 12 月份开始了 Group X 的 OS 移植项目。 接下来,我将大致按时间顺序编写我们的工作经历。这个过程会有一点长,所以如果你想快些看到结果,请跳转至「三月」部分。 十一月下旬:开始开发编译器 我们找到答案的第一个问题是编译器和工具链。有点意外的是,我们决定从头开始写 C89 编译器。说老实话,我之前没想到我们会选这条路。我记得我和 Yuichi(后来负责 Group X 的 CPU)一开始讨论过移植 gcc 或 llvm。 但是,一位团队成员 Keiichi 突然说他已经写好了一个 C 编译器并向我们展示了一个编译器原型,其带有一个简单的解释器和发射器。从头开始写工具链似乎更有意思,因此我们决定自己写一个编译器。 来自第 3 组的 Yuichi 和 Wataru 已经结束了那一年 CPU 实验的核心任务,于是他们加入了 Keiichi,组成了 Group X 的编译器团队。后来我们将我们的编译器命名为 Ucc。 十二月中旬:OS 团队上线! 十二月初,我完成了自己的 CPU,第 6 组完成了 CPU 实验的核心部分。于是我们开始做有趣的部分:Group X 的 OS 移植任务。这时候,第 6 组的我和 Shohei 开始了 Group X 的工作并组成了 OS 团队。Masayoshi 也在那时候加入了进来。 实验的核心任务:编写一个 CPU 顺便一提,我猜没多少软件工程师亲自写过 CPU,所以我也谈谈如何写 CPU。 现如今,制作 CPU 并不意味着要在面包板上连接各种跳线,你可以完全使用硬件描述语言(HDL)编写电路。然后你可以使用 Vivado 或 Quartus 将 HDL 合成到真实电路中。这个过程叫做逻辑综合(logic synthesis),而不是编译。 HDL 与编程语言既有相似之处,也有一些差异。你可以将其视为一个将寄存器的信号状态映射成另一个信号状态的函数,其可由时钟或输入信号触发。如果你想体验真正的反应式编程,我建议你试试 HDL。同时请务必记住,在写 HDL 时要一直注意你写的 HDL 的信号传播会在某个时钟切实地终止。否则,人类将难以理解你的电路的行为。 实际开发过程中最艰难的部分就是逻辑综合,其所需的时间多得离谱。在开始执行综合之后,我们往往需要等上多达 30 分钟时间。所以开始综合之后,我常常与其他也在等着综合结束的 CPU 团队成员玩《任天堂明星大乱斗 DX》。随便说一下,我的角色是 Sheik。 十二月下旬到一月中旬:通过将 Xv6 移植到 MIPS 来学习 我们开始找到「操作系统需要 CPU 具备哪些功能?」这个问题的答案。 OS 团队诞生之后,我们开始每周聚会,阅读 Xv6 源代码。 与此同时,我开始将 Xv6 移植到 MIPS。这样做的部分原因是学习 OS 在实现层的工作方式,部分原因是似乎还没人将 Xv6 移植到 MIPS 过。我在大约一周内完成了移植工作,直到调度器过程开始。在这个移植过程中,我花了大量精力研究 MIPS,并且为了了解 Xv6 的工作方式还大量研究了 x86。得益于此,我理解了中断的相关机制以及实现层的内存管理单元(MMU)。这时候,对于 Xv6 所需的 CPU 功能,我已经有了扎实的理解。 另外,在一月中旬,我们也开始努力通过注释掉各个部分来编译 Xv6 的整体代码。结果是在我们自制架构的模拟器上,Xv6 在引导顺序中显示出了第一条消息:
还要说一下,Xv6 向 MIPS 的移植工作在 CPU 实验期间并没有完成,而是在实验之后一个月完成的。GitHub 代码库在这里:https://github.com/nullpo-head/xv6-mips 在我们 2015 年写文介绍了 Group X 的工作之后,后来的学生继续攻坚有关 OS 的新挑战。 2018 年,一些学生在自制的 CPU 上运行了他们自己开发的 OS;2019 年,一组学生运行了他们开发的 OS,同时采用了 RISC-V 作为他们自制 CPU 的 ISA。此外,2020 年的一个小组终于在自制 CPU 上成功运行 Linux,同时 ISA 也采用了 RISC-V。 我相信未来还会有更多故事,也让我们保持期待。从个人角度看,我很期待某天能看到某人在自己的 ISA 上运行 Linux,或在上面运行虚拟机。 人们常说要避免重新造轮子,但这个过程确实能让人学到很多东西。这让我认识到,我对它的理解其实没有那么深,无法从头开始实现它。而且,我推荐这个故事的另一个原因是这真的非常有趣! 我们的 CPU 实验故事就到此为止了。如果你也有兴趣重新发明轮子,可以试试自制 CPU 并移植 OS。 原文链接:https://fuel.edby.coffee/posts/how-we-ported-xv6-os-to-a-home-built-cpu-with-a-home-built-c-compiler/