Nand2Tetris 是由希伯来大学的两位教授 Noam Nisan 和 Shimon Schocken 在 Coursera 上开设的在线课程(网络课程)。课程全称叫做:依据基本原理构建现代计算机:从与非门到俄罗斯方块(基于项目的课程),其主要内容与计算机组成原理、操作系统等科目的关联性较大,但课程的受众群体也不止局限于专业人士,且课程的实践性很强,对深入理解理论知识很有帮助。
Introduction
第一周包含两个章节(一般情况下,一周一个):Introduction 和 Boolean Functions and Gate Logic。
顾名思义,Introduction 是对本课程的说明,Boolean Functions and Gate Logic 才是第一周的真正的学习内容。
通过 Introduction 可以知道大体的学习路线是从基本的逻辑门单元开始,自下而上的学习如何构建计算机,继而构建出能在这台计算机上运行的程序,也即:Nand -> Hack -> Tetris。
另外,对于购买了课程的同学而言,这章节会有一个编程作业,用来练习如何在 Coursera 上提交作业。但如果你是旁听生(auditor),那么可以忽略掉。
Boolean Functions and Gate Logic
这部分内容属于计算机系统结构底层中的底层,课程从一个小小门逻辑电路开始讲起(当然不会讲与物理相关的内容,这也是老师让大家不要过分在意的地方),逐步介绍各种不同的逻辑电路,并自行构造具有一定复杂性的复合逻辑门电路。
Unit 1.1 Boolean Logic
本小节主要介绍了三种基本的逻辑门(Logic gate):与(And)、或(Or)和非(Not),并举例说明了与之对应的基本运算方法和规律。
三种逻辑门的真值表(Truth table)如下:
And:
$$\begin{array}{cc|c}
x & y & And \
\hline
0 & 0 & 0 \
0 & 1 & 0 \
1 & 0 & 0 \
1 & 1 & 1 \
\end{array}$$
Or:
$$\begin{array}{cc|c}
x & y & Or \
\hline
0 & 0 & 0 \
0 & 1 & 1 \
1 & 0 & 1 \
1 & 1 & 1 \
\end{array}$$
Not:
$$\begin{array}{c|c}
x & Not \
\hline
0 & 0 \
1 & 0 \
\end{array}$$
运算规律:
- 交换律(Commutative Law)
- x and y = y and x
- x or y = y or x
- 结合律(Associative Law)
- x and (y and z) = (x and y) and z
- x or (y or z) = (x or y) or z
- 分配律(Distributive Law)
- x and (y or z) = (x and y) or (x and z)
- x or (y and z) = (x or y) and (x or z)
- 德摩根律(De Morgan Law)
- not (x and y) = (not x) or (not y)
- not (x or y) = (not x) and (not y)
以上基本内容说不定就会用到,记录下。
Unit 1.2 Boolean Functions Synthesis
本小节主要讲了三点:
- 如何根据真值表来反向构造布尔函数
- 介绍了一个结论:任意布尔函数都能被与或非三种运算表示(Any Boolean function can be represented using an expression containing AND, OR and NOT operations.)
- 接下来会用与非门(Nand)来构建其他逻辑门
Unit 1.3 Logic gates
本小节着重在说明一个问题,即:这门课程不会从物理层面深究这种逻辑门是怎样实现的,但是会探讨如何利用基础逻辑门来构建复合逻辑门。另外,还介绍了在这个课程中会见到的一些描述用语。
PS:God bless their souls. 感觉像是在嘲讽呢(笑)。
Unit 1.4 Hardware Description Language
本小节主要在说明硬件描述语言(后面会简称 HDL)的语法规则,不多,建议直接自己看老师提供的手册,并试着写下代码,有了一定的感性认识后,再来听,可能会收获多一点。
Unit 1.5 Hardware Simulation
本小节主要在讲如何使用课程提供的硬件模拟器和一些注意事项。
Unit 1.6 Multi Bit Buses
本小节主要在说明多位(bit)逻辑门在本课程所用的 HDL 中的用法,并顺便大致的说明了一下总线的含义,对后面构造位逻辑单元有一定帮助,特别是有 HDL 代码的地方,对后面的作业很有帮助。
Unit 1.7 Project 1 Overview
本小节主要在介绍第一周的作业以及这周作业对于后续课程的意义。不得不说,国内老师从来不会跟你讲作业的意义,最多给你讲讲错题。
扯远了,每周作业包括三个部分:代码源文件(.hdl)、测试脚本(.tst)和比对文件(.cmp)(其实就是程序正确的运行结果),每一个小作业(就是你要实现的每一个小芯片)都包含这三个文件。好消息是测试脚本跟正确答案老师都准备好了,坏消息是代码得自己写(其实也不算坏消息,本就是分内之事,笑),下面我们来完成这周的作业。
PS:有一点要注意,下面这些逻辑门实现的方式不唯一,这也是老师一再强调的东西。
Not
Nand 是我们在这个课程中可以直接使用的基本逻辑门,可以直接使用,Not 是我们要完成的第一个芯片,所以我们直接使用一个二元的与非门来实现一个一元的非门(The implementation of a unary Not gate from a binary Nand gate is simple.)。
一元非门只有两种情况,二元与非门有四种情况,那么我们直接用与非门的其中两种情况来表示非门即可,代码如下:
1 | CHIP Not { |
And
一开始思考如何实现 And 时,有点无从下手的感觉后来,想了一会,想到了两种方法:
- 使用 2 个 Nand
- 使用 1 个 Nand,在使用 1 个 Not
1 | CHIP And { |
Or
实现 And 之后,此时我们可以使用上面实现好了的逻辑门来帮助实现 Or。
1 | CHIP Or { |
Xor
异或运算有点特殊,同样还是用已经实现过的逻辑门来实现。
1 | CHIP Xor { |
Mux
Mux 全称 Multiplexor,即多路复用器(大概是这个意思吧),这个东西应该是通信专业同学研究的东西,有三个输入:a,b 和 sel。规则就是根据 sel 的值来确定输出 a 还是 b,sel 有点像是校验码之类的东西吧。
粗略分析一下,Mux 有八种情况,我们先观察一下它的真值表。
$$\begin{array}{ccc|c}
a & b & sel & Mux \
\hline
0 & 0 & 0 & 0 \
0 & 1 & 0 & 0 \
1 & 0 & 0 & 1 \
1 & 1 & 0 & 1 \
0 & 0 & 1 & 0 \
0 & 1 & 1 & 1 \
1 & 0 & 1 & 0 \
1 & 1 & 1 & 1 \
\end{array}$$
Mux 的结果中有 4 个值为真,仔细回想一下老师在 1.2 讲的有关反向构造布尔函数的知识,首先先列出所有真值的表达式,也就是上表中的第 3、4、6、8 行,可得:
- 第 3 行:a and (not b) and (not sel)
- 第 4 行:a and b and (not sel)
- 第 6 行:not a and b and sel
- 第 8 行:a and b and sel
继而我们可以得到 Mux 的布尔函数式子:(a and (not b) and (not sel)) or (a and b and (not sel)) or ((not a) and b and sel) or (a and b and sel)。汗,真长,不过先不管,我们直接按照这个函数来写代码,可以得到以下代码:
1 | CHIP Mux { |
测试后结果是正确的,不过这显然不够老师说的 elegant(笑),我们来把布尔函数式根据运算规则化简一下,可得:(a and (not sel)) or (b and sel),从而可得:
1 | CHIP Mux { |
测试后结果依然正确,说明化简是正确的。
另外,通过上面的计算过程和老师的讲解,我们可以得到反向构造布尔函数时的几个要点:
- 选取结果序列中真值或假值较少的一方,上面的 Mux 中真假值都是 4 个,任取即可
- 用每一个值写出的式子必须保证其中的每一子项皆为真或假,比如以上面第 3 行为例,a、not b 和 not sel 的值都是 1
- 单个式子的每一子项要进行 and 运算,而每个式子之间则用 or 运算
PS:有关逻辑式子化简的问题,应该属于离散数学的知识。
DMux
DMux,Demultiplexor,即解复用器,与 Mux 是一对,二者的功能也正好相反,但 DMux 的特殊性在于它有两个输出。我们还是按照上面的思路来构建,先看一下 DMux 的真值表
$$\begin{array}{c|cc}
sel & a & b \
\hline
0 & in & 0 \
1 & 0 & in \
\end{array}$$
这是老师提供的资料上的真值表,好像不是很易于分析问题,那我们把详细的真值表整理出来:
$$\begin{array}{cc|cc}
sel & in & a & b \
\hline
0 & 0 & 0 & 0 \
0 & 1 & 1 & 0 \
1 & 0 & 0 & 0 \
1 & 1 & 0 & 1 \
\end{array}$$
这样,我们在分析这个问题时,就可以单独考虑如何利用 sel 和 in 分别得到 a、b 这两列值,代码如下:
1 | CHIP DMux { |
Not16
多位逻辑门是老师在 1.6 讲过的内容,构造的基本思想就是每一位都用一个逻辑门来计算,组合在一起就可以了。
1 | CHIP Not16 { |
And16
1 | CHIP And16 { |
Or16
1 | CHIP Or16 { |
Mux16
1 | CHIP Mux16 { |
Or8Way
1 | CHIP Or8Way { |
Mux4Way16
4 路 Mux16 使用三个 Mux16 即可完成,可能会有人问为什么要先让 a 和 c 先通过一次 Mux16。因为 sel bits 是从右往左读的,若 a 和 b 先通过一次 Mux16,当 sel[1]=0 时,就无法得到正确的输出结果了(sel[0]=0,就输出 a,sel[0]=1,就输出 b,但第一次通过 Mux16 后已经过滤掉 a 或 b 了)。
1 | CHIP Mux4Way16 { |
Mux8Way16
在 Mux4Way16 的基础上构造就行了,注意一下这里的语法。
1 | CHIP Mux8Way16 { |
DMux4Way
先用 sel[1] 来区分 a、b 和 c、d 两组,再用 sel[0] 在组内分别区分 a、b 和 c、d。
1 | CHIP DMux4Way { |
DMux8Way
在 DMux4Way 的基础上构建就行,思路是完全一致的。
1 | CHIP DMux8Way { |
Unit 1.8 Perspectives
本小节主要老师们回答学生在论坛区提出的一些典型问题,这次主要回答了 3 个问题:
- 能否不用 Nand 而是用其他的基本逻辑门来构建一个计算机?
- 答案是 yes,这里我们不深究具体原因。
- Nand gate 的具体工作原理
- 老师用电路图讲了一下,当然,这是电气工程师该干的活,就不具体讨论了
- 工业坏境下使用的 HDL 语言与课程使用的 HDL 语言有何差别
- 工业环境用的肯定功能更加强大,效率更高,学习的时间较长;而本课程的 HDL 语言只是为了满足教学使用,所以功能性会弱很多,但易于学习,且足够满足本课程的所有需要
本周内容过于底层,估计不少人觉得无聊,不过还好不是太难。当然,可能会有第一次使用这样的 HDL 语言和硬件模拟器而不熟悉的问题,不过老师提供的手册和视频的讲解帮助还是很大的。个人感觉老师给的手册如果能更详细一点,用例再多一点就更好了(主要还是自己太懒,笑)。