计算机系统要素,从零开始构建现代计算机(nand2tetris)
如果完成了本书所有的项目 你将会获得以下成就
- 构建出一台计算机(在模拟器上运行)
- 实现一门语言和相应的语言标准库
- 实现一个简单的编译器
而且,这本书的门槛非常低,只要你能熟练运用一门编程语言即可。本课程综合了数字电路,计算机组成原理,计算机体系架构,操作系统,编译原理,数据结构等的主要内容,搭建了计算机平台的构建的框架,并未深入细节,如果需要了解细节,可由本书作为主线,逐步完善的知识体系。
QQ交流群(含资料):39014053
课程连接
项目地址Github
本章要实现的内容
- Memory:内存,包括数据内存和指令内存,屏幕内存映像和键盘内存映像
- CPU:包含A寄存器,D寄存器,ALU,PC程序计数器
- Computer:完整的一个计算机平台。运行在仿真平台上
实现总结
Memory:
输入管脚:in[16], load, address[15]
输出管脚:out[16]
Memory芯片主要由三个底层芯片构建:RAM16K,Screen,KeyBoard。同时我们必须通过这三个底层芯片来实现一个统一的逻辑地址空间,这个空间从地址0到24567。构建这个连续的地址空间的方法我们可以参考之前第三章由小RAM构建大RAM的方法。
内存的功能首先能够根据address选定需要进行操作的单元。
然后将load信号给指定的内存单元。根据load信号判断是进行写入还是继续保持。
下面进行地址连线:参考自计算机组成原理(注意此地址只有15位)
RAM16K的地址空间:000 0000 0000 0000 ~ 011 1111 1111 1111
屏幕内存映像的地址空间(8K):100 0000 0000 0000 ~ 101 1111 1111 1111
键盘内存影响的地址空间:110000000000000
1、首先根据地址进行判断,应该将控制信号发送给哪个地址空间。可以使用DMux实现
根据第15位进行二选一,可以选出RAM和映像,然后再对选出的结果进行二选一,根据第14位进行选择可以选出屏幕和键盘。由于所给出的地址空间不会超过110000000000000,所以我们只需要进行一次判断就可以了,而不用考虑后面13位的判断。
2、然后将控制信号,输入数据和片内选择地址给各自的地址空间即可
Screen和Keyboard芯片都是内置好的,因此直接调用接口即可。实际上我们也可以根据第三章的方法构建自己的Screen和Keyboard
CPU
输入管脚:inM[16], instruction[16], reset
输出管脚:outM[16], writeM, addressM[15], pc[15]
CPU实现的一种推荐方案:
1、回顾一下第四章的指令格式:
A-指令:
0
v
v
v
v
v
v
v
v
v
v
v
v
v
v
v
0vvv\ vvvv\ vvvv\ vvvv
0vvv vvvv vvvv vvvv。MSB0,其余15位表示地址值
C-指令:
111
a
c
1
c
2
c
3
c
4
c
5
c
6
d
1
d
2
d
3
j
1
j
2
j
3
111a\ c_1c_2c_3c_4\ c_5c_6d_1d_2\ d_3j_1j_2j_3
111a c1c2c3c4 c5c6d1d2 d3j1j2j3。MSB为1,接着的两位无效。a位域控制与D进行计算的是A还是M,c位域是计算操作的控制,
d
1
d_1
d1控制是否将ALU的计算结果存在A中,
d
2
d_2
d2控制是否将ALU的计算结果存在D中,
d
3
d_3
d3控制是否将ALU的计算结果存在M中,
j
1
j
2
j
3
j_1j_2j_3
j1j2j3是指示调转的控制。
2、解码指令
如果是A指令,那么要将地址加载到A寄存器中,如果是C-指令,则考虑
d
1
d_1
d1,是否需要将ALU的计算结果保存在A寄存器中。不妨暂时将第一个Mux的控制位记为
c
m
c_m
cm,将A寄存器的控制位记作
c
a
c_a
ca。那么我们可以得到以下真值表:
[15] | d 1 d_1 d1 | c m c_m cm | c a c_a ca |
---|---|---|---|
0 | 0 | 1 | 1 |
0 | 1 | 1 | 1 |
1 | 0 | 0 | 0 |
1 | 1 | 0 | 1 |
由真值表我们可以得到控制位的公式:
c
m
=
[
15
]
‾
,
c
a
=
[
15
]
d
1
‾
‾
c_m=\overline{[15]},c_a=\overline{[15]\overline{d_1}}
cm=[15],ca=[15]d1。
对于A-指令以上的工作就已经足够了。而对于C-指令我们还需要做其他的工作。
3、C-指令的解码
C指令的
d
1
d_1
d1位已经被解析完了。接下来从左向右开始解码。
a
a
a是控制选择A/M的控制位,所以它应该在与指令最高位取与后给第二个Mux。
c
1
.
.
c
6
c_1 .. c_6
c1..c6是控制计算函数的,所以它们应该给ALU的控制位,注意要与指令最高位取与。
d
2
d_2
d2是控制计算结果是否保存在D中的,所以它与指令最高位取与后作为控制信号给D寄存器,
d
3
d_3
d3是控制计算结果是否保存在M中的,所以它作为输出与指令最高位取与后给writeM。接下来
j
1
j
2
j
3
j_1j_2j_3
j1j2j3是控制跳转的,总体上来看,只有两种现象,一种是顺序执行,即PC加一,另一种是跳转到地址为A中的地址的单元。根据课本给出的编码:
那么究竟是否需要跳转要根据ALU的计算结果进行判断,ALU有两个特别的输出信号:zr(当计算结果等于0时为1),ng(当计算结果小于零时为1)。所以我们可以推出
n
g
+
z
r
‾
\overline{ng+zr}
ng+zr(当计算结果大于零时为1)。结合jump域和ALU的输出结果,可以得到如下真值表:
( j 1 & n g ) o r ( j 2 & z r ) o r ( j 3 & n g + z r ‾ ) (j_1\&ng)or(j_2\&zr)or(j_3\&\overline{ng+zr}) (j1&ng)or(j2&zr)or(j3&ng+zr) | load | inc |
---|---|---|
0 | 0 | 1 |
1 | 1 | 0 |
4、完成连线
这一步很简单,不再介绍
关于CPU的构建,课程有很详细的介绍,参考课程即可。个人觉得在这个过程中,最困难的就是梳理控制逻辑,使用有限状态机来梳理整个过程,更有利于深刻而清楚的理解CPU的工作过程,鉴于个人没有学习过体系结构方面的内容,并且组成原理课程并未涉及到CPU的内容,所以上述内容难免有错误出现,望各位读者不吝批评。
Computer
输入管脚:reset
在构建这一部分的时候并没有什么复杂的逻辑,这就像,给了你CPU,硬盘,显示屏,内存条,显卡…等计算机组件,让我们构建一个计算机。这样看来,我们需要做的就是把他们按在主板上,并连上线就可以了。
阶段总结
至此,就正式完成了一个简单的计算机平台,也可以说是个计算机芯片,也可以说是个模型机,尽管它是个模型机,但是根据这个模型机的构建原理,我们可以动手使用真正的芯片来构建自己的计算机平台了。首先使用大量的原始芯片,比如Nand,DFF,我们可以构建出简单的组件,比如选择器,解码器,寄存器,RAM,这四个分别是组合逻辑和时序逻辑的典型代表。接着,我们使用这些简单组件来构建复杂的组件,构建CPU和Memory。并且,我们可以自己定义自己的指令编码规则,然后在这些组件上测试指令。然后使用这些复杂的组件来构建我们自己的计算机,尽管它看起来一定非常丑陋,但是它的意义是非凡的,这意味着我们掌握了计算机的构建方式。我们可以在上面运行01组成的机器指令,这在外行看来一定是非常厉害的,尽管我们会认为它还仅仅是个婴儿。
在这个阶段中,一开始是比较简单的,只需要简单的数据电路知识就可以完成芯片的构建,这个过程我充满了疑惑,构建的这些芯片究竟有什么作用?不过,很快这些疑惑就被解决了,在我构建完一个简单的ALU之后,我才发现,这些芯片原来都有自己的重要价值。与或非门,就想是一些简单的组件,在必要的时候起一些辅助的作用,不过大型的复杂的组合逻辑芯片都是基于这些简单的门电路。而半加器之所以称为半加器,就是因为它只完成了一半的加法,因为它忽略了进位,当真正构建出来这些芯片,并使用它们的时候,才发现原来名字是这么的直截了当,跟当初学习理论的时候感觉完全不一样。另外就是全加器,它是ALU进行运算的关键。后来的时序逻辑是有些困难,不过看到这简单的DFF,竟然能存储一个位的数据,而寄存器竟然就是由这样的一个个的DFF实现的,非常神奇。构建CPU的时候,遇到了很大的困难,分析课本上给出的实现图,一直都没有头绪,尝试了很多次,思路都很零散,没有逻辑性,几乎都快放弃了,后来在群里请教了一下前辈,得前辈的指导,我又看了一下配套的视频,然后慢慢的从分析A-指令和C-指令开始梳理自己的思路,大概用了一个下午的时间,完成了CPU的构建,并通过了测试。信心倍增,而之后的Computer的构建是很简单的,几乎一气呵成。到此,我可以说:如果我有足够的芯片,我一定能构建出来一台计算机!在接下来的阶段里,继续加油!!