背景
让我们按照 From Nand to Tetris 里 Project 2 的要求,来完成下列的设计。
- 实现半加器(
HalfAdder) - 实现全加器(
FullAdder) - 实现 16 位加法器(
Add16) - 实现 16 位自增器(
Inc16) - 实现算术逻辑单元(
ALU) (todo)
正文
1. 实现 HalfAdder
前往 Nand to Tetris Online IDE,选择 Project 2 里的 HalfAdder,效果如下图所示 ⬇️

我们的目标是用 Project 1 里已经实现的各种 chip 实现一个 HalfAdder。
- 输入是
- a
- b
- 输出是
- sum
- carry
我们可以从它的真值表入手 ⬇️
| a | b | sum | carry |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 1 | 1 | 0 |
| 1 | 0 | 1 | 0 |
| 1 | 1 | 0 | 1 |
可见
- sum=(¬a∧b)∨(a∧¬b)=a⊕b
- carry=a∧b
所以可以这样实现 ⬇️
hdl
CHIP HalfAdder {
IN a, b; // 1-bit inputs
OUT sum, // Right bit of a + b
carry; // Left bit of a + b
PARTS:
And(a= a, b= b, out= carry);
Xor(a = a, b = b, out = sum);
}
这样的代码可以通过仿真测试,效果如下图所示 ⬇️

2. 实现 FullAdder
前往 Nand to Tetris Online IDE,选择 Project 2 里的 FullAdder,效果如下图所示 ⬇️

我们的目标是用 Project 1 里已经实现的各种 chip,以及刚才实现的 HalfAdder 来实现一个 FullAdder。
- 输入是
- a
- b
- c
- 输出是
- sum
- carry
我们还是从真值表入手 ⬇️
| a | b | c | sum | carry |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 0 | 0 | 1 | 1 | 0 |
| 0 | 1 | 0 | 1 | 0 |
| 0 | 1 | 1 | 0 | 1 |
| 1 | 0 | 0 | 1 | 0 |
| 1 | 0 | 1 | 0 | 1 |
| 1 | 1 | 0 | 0 | 1 |
| 1 | 1 | 1 | 1 | 1 |
通过观察真值表,可以将 sum 和 carry 写成对应的析取范式 DNF(Disjunctive Normal Form) 的形式
- sum=(¬a∧¬b∧c)∨(¬a∧b∧¬c)∨(a∧¬b∧¬c)∨(a∧b∧c)
- carry=(¬a∧b∧c)∨(a∧¬b∧c)∨(a∧b∧¬c)∨(a∧b∧c)
从上面的析取范式出发,我们可以只用 And/Or/Not 来实现 FullAdder。但有没有更简洁的实现方式呢? 我们回忆一下 HalfAdder 中的 sum 和 carry 是怎样的 ⬇️ (为了和 FullAdder 的 sum 和 carry 进行区分,我把 HalfAdder sum 和 carry 分别改写为的 sumha 和 carryha)
- sumha=(¬a∧b)∨(a∧¬b)=a⊕b
- carryha=a∧b
为了便于区分,我把 FullAdder 的 sum 和 carry 分别改写为 sumfa 和 carryfa。 先看 sumfa 和 sumha 是否有关联。
- sumfa=(¬a∧¬b∧c)∨(¬a∧b∧¬c)∨(a∧¬b∧¬c)∨(a∧b∧c)
- sumha=a⊕b
如何表示 sum
一个思路是看看能否将 sumfa 用 sumha 表示出来,我试了试,觉得有些繁琐,而且不容易想到。另一个思路是,我们从 sumfa 和 sumha 背后的含义入手。 sumha 表示 a,b 的和(忽略进位),所以 sumha=a⊕b。
- sumha=0 时,只有 c=1, sumfa=1 才会成立
- sumha=1 时,只有 c=0, sumfa=1 才会成立
所以 sumfa=sumha⊕c
而 HalfAdder 的作用就是对输入 a,b 分别执行 ⊕ 和 ∧ 运算,所以 sumfa 可以通过拼接两个 HalfAdder 来实现 ⬇️
- 第一个
HalfAdder以 a,b 为输入,其 sumha1=a⊕b - 第二个
HalfAdder以 sumfa1 和 c 为输入,其 sumha2=sumfa1⊕c=
如何表示 carry
carryfa=(¬a∧b∧c)∨(a∧¬b∧c)∨(a∧b∧¬c)∨(a∧b∧c)
=(¬a∧b∧c)∨(a∧¬b∧c)∨((a∧b∧¬c)∨(a∧b∧c))
=(¬a∧b∧c)∨(a∧¬b∧c)∨(a∧b)
=(¬a∧b∧c)∨(a∧¬b∧c)∨carryha
=((a⊕b)∧c)∨carryha
=(sumha∧c)∨carryha
考虑到 HalfAdder 的作用就是对输入 a,b 分别执行 ⊕ 和 ∧ 运算,所以 carryfa 可以通过拼接两个 HalfAdder 再加上一个 Or 运算 来实现 ⬇️
- 第一个
HalfAdder以 a,b 为输入,它的- sumfa1=a⊕b
- carryha1=a∧b
- 第二个
HalfAdder以 sumfa1 和 c 为输入,它的- sumha2=sumfa1⊕c
- carryha2=sumfa1∧c
那么
carryfa=(sumha1∧c)∨carryha1=carryha2∨carryha1
至此我们可以用 HalfAdder 和 Or 来实现 FullAdder。具体的代码如下 ⬇️
hdl
CHIP FullAdder {
IN a, b, c; // 1-bit inputs
OUT sum, // Right bit of a + b + c
carry; // Left bit of a + b + c
PARTS:
HalfAdder(a= a, b= b, sum= sumHa, carry= carryHa);
HalfAdder(a= sumHa, b= c, sum= sum, carry= temp);
Or(a= carryHa, b= temp, out= carry);
}
这样的代码可以通过仿真测试,效果如下图所示 ⬇️

3. 实现 16 位加法器(Add16)
前往 Nand to Tetris Online IDE,选择 Project 2 里的 Add16,效果如下图所示 ⬇️

我们的目标是用 Project 1 里已经实现的各种 chip,以及刚才实现的 HalfAdder/FullAdder 来实现一个 Add16。
- 输入是
- a16
- b16
- 输出是
- out16
要实现的逻辑如下 ⬇️
text
16-bit adder: Adds two 16-bit two's complement values.
The most significant carry bit is ignored.
我写了如下的 java 程序来生成对应的 HDL 代码
java
import java.util.StringJoiner;
public class Adder16HdlCodeGenerator {
public static void main(String[] args) {
String template = """
CHIP Add16 {
IN a[16], b[16];
OUT out[16];
PARTS:
%s
}
""";
StringJoiner joiner = new StringJoiner(System.lineSeparator());
joiner.add(" FullAdder(a= a[0], b= b[0], c= false, sum= out[0], carry= c0);");
for (int i = 1; i < 16; i++) {
String line = String.format(" FullAdder(a= a[%s], b= b[%s], c= c%s, sum= out[%s], carry= c%s);", i, i, i - 1, i, i);
joiner.add(line);
}
System.out.printf(template, joiner);
}
}
请将以上 java 代码保存为 Adder16HdlCodeGenerator.java。用以下命令可以编译 Adder16HdlCodeGenerator.java 并运行其中的 main 方法。
bash
javac Adder16HdlCodeGenerator.java
java Adder16HdlCodeGenerator
运行结果如下
hdl
CHIP Add16 {
IN a[16], b[16];
OUT out[16];
PARTS:
FullAdder(a= a[0], b= b[0], c= false, sum= out[0], carry= c0);
FullAdder(a= a[1], b= b[1], c= c0, sum= out[1], carry= c1);
FullAdder(a= a[2], b= b[2], c= c1, sum= out[2], carry= c2);
FullAdder(a= a[3], b= b[3], c= c2, sum= out[3], carry= c3);
FullAdder(a= a[4], b= b[4], c= c3, sum= out[4], carry= c4);
FullAdder(a= a[5], b= b[5], c= c4, sum= out[5], carry= c5);
FullAdder(a= a[6], b= b[6], c= c5, sum= out[6], carry= c6);
FullAdder(a= a[7], b= b[7], c= c6, sum= out[7], carry= c7);
FullAdder(a= a[8], b= b[8], c= c7, sum= out[8], carry= c8);
FullAdder(a= a[9], b= b[9], c= c8, sum= out[9], carry= c9);
FullAdder(a= a[10], b= b[10], c= c9, sum= out[10], carry= c10);
FullAdder(a= a[11], b= b[11], c= c10, sum= out[11], carry= c11);
FullAdder(a= a[12], b= b[12], c= c11, sum= out[12], carry= c12);
FullAdder(a= a[13], b= b[13], c= c12, sum= out[13], carry= c13);
FullAdder(a= a[14], b= b[14], c= c13, sum= out[14], carry= c14);
FullAdder(a= a[15], b= b[15], c= c14, sum= out[15], carry= c15);
}
这样的代码可以通过仿真测试,效果如下图所示 ⬇️

4. 实现 16 位自增器(Inc16)
前往 Nand to Tetris Online IDE,选择 Project 2 里的 Inc16,效果如下图所示 ⬇️

我们的目标是用 Project 1 里已经实现的各种 chip,以及刚才实现的 HalfAdder/FullAdder/Add16 来实现一个 Inc16。
- 输入是
- in16
- 输出是
- out16
要实现的逻辑如下 ⬇️
text
16-bit incrementer:
out = in + 1
一个直观的做法是使用上一步实现的 Add16,具体的代码如下 ⬇️
hdl
CHIP Inc16 {
IN in[16];
OUT out[16];
PARTS:
Add16(a = in, b[0]= true, b[1..15] = false, out = out);
}
这样的代码可以通过仿真测试,效果如下图所示 ⬇️

5. 实现算术逻辑单元(ALU)
前往 Nand to Tetris Online IDE,选择 Project 2 里的 ALU,效果如下图所示 ⬇️
