背景
让我们按照 From Nand to Tetris 里 Project 2 的要求,来完成下列的设计。
- 实现半加器(
HalfAdder) - 实现全加器(
FullAdder) - 实现 位加法器(
Add16) - 实现 位自增器(
Inc16) - 实现算术逻辑单元(
ALU)
说明
我是阅读了 《计算机系统要素 (第2版)》 第 2 章的内容之后才去完成 Project 2 的。读者朋友在完成 Project 2 时,如果遇到不明白的地方,可以参考这本书中的描述。
正文
1. 实现 HalfAdder
前往 Nand to Tetris Online IDE,选择 Project 2 里的 HalfAdder,效果如下图所示 ⬇️
我们的目标是用 Project 1 里已经实现的各种 chip 实现一个 HalfAdder。
- 输入是
- 输出是
我们可以从它的真值表入手 ⬇️
可见
所以可以这样实现 ⬇️
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);
}
这样的代码可以通过仿真测试,效果如下图所示 ⬇️
我们可以把 HalfAdder 看成一个同时实现了 和 运算的 chip。
2. 实现 FullAdder
前往 Nand to Tetris Online IDE,选择 Project 2 里的 FullAdder,效果如下图所示 ⬇️
我们的目标是用 Project 1 里已经实现的各种 chip,以及刚才实现的 HalfAdder 来实现一个 FullAdder。
- 输入是
- 输出是
我们还是从真值表入手 ⬇️
通过观察真值表,可以将 和 写成对应的析取范式 DNF(Disjunctive Normal Form) 的形式
从上面的析取范式出发,我们可以只用 And/Or/Not 来实现 FullAdder。但有没有更简洁的实现方式呢?
基于 DNF 来化简
一种思路是从 DNF 出发,将其进行化简。
化简
把含有 的两个最小项(minterm)合并,把含有 的两个最小项合并,于是得到
根据
- 运算的性质:
- 运算的性质:
可以将 化简如下
化简
将含有 的两个最小项(minterm)合并,将另外两个最小项合并,可以得到
完整的代码如下 ⬇️
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:
// Calculationtion for sum
Xor(a = a, b = b, out = abXor);
Xor(a = abXor, b = c, out = sum);
// Calculation for carry
And(a= a, b= b, out= p1);
And(a= abXor, b= c, out= p2);
Or(a= p1, b= p2, out= carry);
}
这样的代码可以通过仿真测试,效果如下图所示 ⬇️
上面对 和 的化简都需要一些技巧,而且(我觉得)挺容易写错的,那么有没有其他的化简方式呢?我们也可以通过利用 HalfAdder 来进行化简。
利用 HalfAdder 来进行化简
我们回忆一下 HalfAdder 中的 和 是怎样的 ⬇️ (为了和 FullAdder 的 和 进行区分,我把 HalfAdder 和 分别改写为的 和 )
为了便于区分,我把 FullAdder 的 和 分别改写为 和 。
先看 和 是否有关联。
如何表示
一个思路是看看能否将 用 表示出来,我试了试,觉得有些繁琐,而且不容易想到。另一个思路是,我们从 和 背后的含义入手。 表示 的和(忽略进位),所以 。
- 时,只有 , 才会成立
- 时,只有 , 才会成立
所以
而 HalfAdder 的作用就是对输入 分别执行 和 运算,所以 可以通过拼接两个 HalfAdder 来实现 ⬇️
- 第一个
HalfAdder以 为输入,它的 - 第二个
HalfAdder以 和 为输入,它的
如何表示
考虑到 HalfAdder 的作用就是对输入 分别执行 和 运算,所以 可以通过拼接两个 HalfAdder 再加上一个 Or 运算 来实现 ⬇️
- 第一个
HalfAdder以 为输入,它的 - 第二个
HalfAdder以 和 为输入,它的
那么
至此我们可以用 HalfAdder 和 Or 来实现 FullAdder。具体的代码如下 ⬇️
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. 实现 位加法器(Add16)
前往 Nand to Tetris Online IDE,选择 Project 2 里的 Add16,效果如下图所示 ⬇️
我们的目标是用 Project 1 里已经实现的各种 chip,以及刚才实现的 HalfAdder/FullAdder 来实现一个 Add16。
- 输入是
- 输出是
要实现的逻辑如下 ⬇️
16-bit adder: Adds two 16-bit two's complement values.
The most significant carry bit is ignored.
我写了如下的 java 程序来生成对应的 HDL 代码
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 方法。
javac Adder16HdlCodeGenerator.java
java Adder16HdlCodeGenerator
运行结果如下
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. 实现 位自增器(Inc16)
前往 Nand to Tetris Online IDE,选择 Project 2 里的 Inc16,效果如下图所示 ⬇️
我们的目标是用 Project 1 里已经实现的各种 chip,以及刚才实现的 HalfAdder/FullAdder/Add16 来实现一个 Inc16。
- 输入是
- 输出是
要实现的逻辑如下 ⬇️
16-bit incrementer:
out = in + 1
一个直观的做法是使用上一步实现的 Add16,具体的代码如下 ⬇️
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,效果如下图所示 ⬇️
我们的目标是用 Project 1 里已经实现的各种 chip,以及刚才实现的 HalfAdder/FullAdder/Add16/Inc16 来实现一个 ALU。
这次的输入项比较多,我用一个表格来进行说明
| 输入 | 说明 |
|---|---|
是 16 位的输入 | |
是 16 位的输入 | |
| 用于表示是否要将 置零 (我想 zx 大约是 zero x 的缩写吧) | |
| 用于表示是否要将 取反 (我想 nx 大约是 negate x 的缩写吧) | |
| 用于表示是否要将 置零 (我想 zy 大约是 zero y 的缩写吧) | |
| 用于表示是否要将 取反 (我想 ny 大约是 negate y 的缩写吧) | |
| 用于表示 还是 ( 时为前者, 时为后者) | |
| 用于表示是否要将 取反 ( 时需要执行取反操作) |
我把输出项的相关信息也整理成一个表格了 ⬇️
| 输出 | 说明 |
|---|---|
是 16 位的输出 | |
| 用于表示 是否等于 (如果 ,则 ,否则 ) | |
| 用于表示 是否小于 (如果 ,则 ,否则 ) |
我们要实现的功能如下 ⬇️
/**
* ALU (Arithmetic Logic Unit):
* Computes out = one of the following functions:
* 0, 1, -1,
* x, y, !x, !y, -x, -y,
* x + 1, y + 1, x - 1, y - 1,
* x + y, x - y, y - x,
* x & y, x | y
* on the 16-bit inputs x, y,
* according to the input bits zx, nx, zy, ny, f, no.
* In addition, computes the two output bits:
* if (out == 0) zr = 1, else zr = 0
* if (out < 0) ng = 1, else ng = 0
*/
// Implementation: Manipulates the x and y inputs
// and operates on the resulting values, as follows:
// if (zx == 1) sets x = 0 // 16-bit constant
// if (nx == 1) sets x = !x // bitwise not
// if (zy == 1) sets y = 0 // 16-bit constant
// if (ny == 1) sets y = !y // bitwise not
// if (f == 1) sets out = x + y // integer 2's complement addition
// if (f == 0) sets out = x & y // bitwise and
// if (no == 1) sets out = !out // bitwise not
这次的功能比较复杂。由于以下控制位是依次起作用的,所以我们一个一个来看应该如何实现它们
第 步:实现 的功能
- 当 时,需要对 执行清零操作
- 当 时,不修改
我们可以用一个 Mux16 (在 Project 1 里已经实现了 Mux16)来实现这样的功能,具体的代码如下 ⬇️ (因为下一步可能会对 进行取反操作,所以把这一步的输出称为 x0)
Mux16(a= x, b= false, sel= zx, out= x0);
第 步:实现 的功能
- 当 时,需要对 执行取反操作(上一步将最新的 值记录在
x0里了) - 当 时,不修改 (上一步将最新的 值记录在
x0里了)
我们可以先用 Not16 对 x0 进行取反操作得到 notX0,然后再用一个 Mux16 从 x0/notX0 中选择一个作为输出(称这一步的输出为 x1)。对应的代码如下 ⬇️
Not16(in= x0, out= notX0);
Mux16(a= x0, b= notX0, sel= nx, out= x1);
第 步:实现 的功能
- 当 时,需要对 执行清零操作
- 当 时,不修改
第 步和第 步类似,我们直接写代码吧 ⬇️
Mux16(a= y, b= false, sel= zy, out= y0);
第 步:实现 的功能
- 当 时,需要对 执行取反操作(上一步将最新的 值记录在
y0里了) - 当 时,不修改 (上一步将最新的 值记录在
y0里了)
第 步和第 步类似,我们直接写代码吧 ⬇️
Not16(in= y0, out= notY0);
Mux16(a= y0, b= notY0, sel= ny, out= y1);
第 步:实现 的功能
- 当 时, (之前几步将最新的 值分别记录在
x1/y1里了) - 当 时,(之前几步将最新的 值分别记录在
x1/y1里了)
我们可以先用
Add16实现 的操作(称其结果为addResult)And16实现 的操作(称其结果为andResult)
然后用一个 Mux16 决定选择 addResult/andResult 中的哪一个作为输出(称这一步的输出为 out0)。具体的代码如下
And16(a= x1, b= y1, out= andResult);
Add16(a = x1, b = y1, out = addResult);
Mux16(a= andResult, b= addResult, sel= f, out= out0);
第 步:实现 的功能
- 当 时,对 进行取反操作(上一步将最新的 值记录在
out0里了) - 当 时,不修改 (上一步将最新的 值记录在
out0里了)
对应的代码如下 ⬇️
Not16(in= out0, out= notOut0);
Mux16(a= out0, b= notOut0, sel= no, out= out);
至此,实现 ALU 的前 个步骤我们都已经完成了。
完整的代码如下(我加了点注释)
CHIP ALU {
IN
x[16], y[16], // 16-bit inputs
zx, // zero the x input?
nx, // negate the x input?
zy, // zero the y input?
ny, // negate the y input?
f, // compute (out = x + y) or (out = x & y)?
no; // negate the out output?
OUT
out[16], // 16-bit output
zr, // if (out == 0) equals 1, else 0
ng; // if (out < 0) equals 1, else 0
PARTS:
// x0 is the x we get after applying zx
Mux16(a= x, b= false, sel= zx, out= x0);
// x1 is the x we get after applying nx
Not16(in= x0, out= notX0);
Mux16(a= x0, b= notX0, sel= nx, out= x1);
// y0 is the y we get after applying zy
Mux16(a= y, b= false, sel= zy, out= y0);
// y1 is the y we get after applying ny
Not16(in= y0, out= notY0);
Mux16(a= y0, b= notY0, sel= ny, out= y1);
// out0 is the out we get after applying f
And16(a= x1, b= y1, out= andResult);
Add16(a = x1, b = y1, out = addResult);
Mux16(a= andResult, b= addResult, sel= f, out= out0);
// out is the out we get after applying "no"
Not16(in= out0, out= notOut0);
Mux16(a= out0, b= notOut0, sel= no, out= out);
}
上述代码的仿真结果如下 ⬇️
观察仿真结果后,我们会发现, 这个输出没有问题,但是 和 的结果,很多是错误的。这是因为我们还没有实现 和 的逻辑
第 步:实现 的功能
- 当 时,
- 当 时,
因为 一共有 位,所以当 的值为 时, 全都是 。这 位可以分位高 位和低 位 ⬇️
如果 同时满足以下两个条件,那么 的所有位(共 位)都是 。
- 其高 位全都是 ,并且
- 其低 位全都是
我们可以把第 步的代码调整成这样 ⬇️ ⬇️
Not16(in= out0, out= notOut0);
Mux16(a= out0, b= notOut0, sel= no, out= out,
out[0..7]= low8, out[8..15]= high8);
Or8Way(in= high8, out= high8Or);
Or8Way(in= low8, out= low8Or);
Or(a= high8Or, b= low8Or, out= orResult);
Not(in= orResult, out= zr);
第 步:实现 的功能
- 当 时,
- 当 时,
以 这个负数为例,当 的值等于 时,它的每一位是这个样子 ⬇️
一般地,当 时,;当 时,。
所以我们只需要判断 的值是否为 ,就知道 的值应该是什么了。 我们可以把第 步的代码调整成这样 ⬇️
Not16(in= out0, out= notOut0);
Mux16(a= out0, b= notOut0, sel= no, out= out, out[15]= ng);
我们把对 和 的计算的代码调整一下 ⬇️
- 对 的高 位和低 位分别进行
Or运算(得到的结果是 ),在此基础上计算 的值(在下图的 行至 行) - 将 的最高位赋值给 (在下图的 行)
现在可以把 个步骤的代码合并在一起了,完整的代码如下 ⬇️
// This file is part of www.nand2tetris.org
// and the book "The Elements of Computing Systems"
// by Nisan and Schocken, MIT Press.
// File name: projects/2/ALU.hdl
/**
* ALU (Arithmetic Logic Unit):
* Computes out = one of the following functions:
* 0, 1, -1,
* x, y, !x, !y, -x, -y,
* x + 1, y + 1, x - 1, y - 1,
* x + y, x - y, y - x,
* x & y, x | y
* on the 16-bit inputs x, y,
* according to the input bits zx, nx, zy, ny, f, no.
* In addition, computes the two output bits:
* if (out == 0) zr = 1, else zr = 0
* if (out < 0) ng = 1, else ng = 0
*/
// Implementation: Manipulates the x and y inputs
// and operates on the resulting values, as follows:
// if (zx == 1) sets x = 0 // 16-bit constant
// if (nx == 1) sets x = !x // bitwise not
// if (zy == 1) sets y = 0 // 16-bit constant
// if (ny == 1) sets y = !y // bitwise not
// if (f == 1) sets out = x + y // integer 2's complement addition
// if (f == 0) sets out = x & y // bitwise and
// if (no == 1) sets out = !out // bitwise not
CHIP ALU {
IN
x[16], y[16], // 16-bit inputs
zx, // zero the x input?
nx, // negate the x input?
zy, // zero the y input?
ny, // negate the y input?
f, // compute (out = x + y) or (out = x & y)?
no; // negate the out output?
OUT
out[16], // 16-bit output
zr, // if (out == 0) equals 1, else 0
ng; // if (out < 0) equals 1, else 0
PARTS:
// x0 is the "x" we get after applying zx
Mux16(a= x, b= false, sel= zx, out= x0);
// x1 is the "x" we get after applying nx
Not16(in= x0, out= notX0);
Mux16(a= x0, b= notX0, sel= nx, out= x1);
// y0 is the "y" we get after applying zy
Mux16(a= y, b= false, sel= zy, out= y0);
// y1 is the "y" we get after applying ny
Not16(in= y0, out= notY0);
Mux16(a= y0, b= notY0, sel= ny, out= y1);
// out0 is the "out" we get after applying f
And16(a= x1, b= y1, out= andResult);
Add16(a = x1, b = y1, out = addResult);
Mux16(a= andResult, b= addResult, sel= f, out= out0);
// out is the "out" we get after applying "no"
// out1 is equal to out
// assign value to zr and ng based on out
Not16(in= out0, out= notOut0);
Mux16(a= out0, b= notOut0, sel= no, out= out,
out[0..7]= low8, out[8..15]= high8, out[15]= ng);
Or8Way(in= high8, out= high8Or);
Or8Way(in= low8, out= low8Or);
Or(a= high8Or, b= low8Or, out= orResult);
Not(in= orResult, out= zr);
}
这样的代码可以通过仿真测试,效果如下图所示 ⬇️
实现 ALU 的其他思路
我觉得还有其他思路可以实现 ALU 中的第 步(即 ),利用 ,我们可以判断出 的最高位是否是 。如果 的最高位是 的话,那么会有两种可能 ⬇️
- 表示
- 表示一个正整数
我们要做的事情就是区分上面的两种可能。
思路 : 借助 Inc16 来判断
令 。注意到
- 对 取反会得到 ,而 (在补码加法的意义下)
- 对满足 的任意正整数 而言, 得到的结果的最高位是
基于上述观察,我们可以这样判定 。 如果以下两个条件同时满足,那么 是 ,否则 是
- 是
- 的结果的最高位是
所以我们可以这样来设计 ⬇️ (因为第 步的代码也做了小的调整,所以下面的代码包含了第 步)
// out is the "out" we get after applying "no"
// out1 is equal to out
// assign value to ng
Not16(in= out0, out= notOut0);
Mux16(a= out0, b= notOut0, sel= no,
out= out, out= out1, out[15]= ng, out[15]= zr1);
// assign value to zr
Not16(in= out1, out= notOut);
Inc16(in= notOut, out[15]= zr2);
Or(a= zr1, b= zr2, out= zr3);
Not(in= zr3, out= zr);
完整的代码如下
// This file is part of www.nand2tetris.org
// and the book "The Elements of Computing Systems"
// by Nisan and Schocken, MIT Press.
// File name: projects/2/ALU.hdl
/**
* ALU (Arithmetic Logic Unit):
* Computes out = one of the following functions:
* 0, 1, -1,
* x, y, !x, !y, -x, -y,
* x + 1, y + 1, x - 1, y - 1,
* x + y, x - y, y - x,
* x & y, x | y
* on the 16-bit inputs x, y,
* according to the input bits zx, nx, zy, ny, f, no.
* In addition, computes the two output bits:
* if (out == 0) zr = 1, else zr = 0
* if (out < 0) ng = 1, else ng = 0
*/
// Implementation: Manipulates the x and y inputs
// and operates on the resulting values, as follows:
// if (zx == 1) sets x = 0 // 16-bit constant
// if (nx == 1) sets x = !x // bitwise not
// if (zy == 1) sets y = 0 // 16-bit constant
// if (ny == 1) sets y = !y // bitwise not
// if (f == 1) sets out = x + y // integer 2's complement addition
// if (f == 0) sets out = x & y // bitwise and
// if (no == 1) sets out = !out // bitwise not
CHIP ALU {
IN
x[16], y[16], // 16-bit inputs
zx, // zero the x input?
nx, // negate the x input?
zy, // zero the y input?
ny, // negate the y input?
f, // compute (out = x + y) or (out = x & y)?
no; // negate the out output?
OUT
out[16], // 16-bit output
zr, // if (out == 0) equals 1, else 0
ng; // if (out < 0) equals 1, else 0
PARTS:
// x0 is the "x" we get after applying zx
Mux16(a= x, b= false, sel= zx, out= x0);
// x1 is the "x" we get after applying nx
Not16(in= x0, out= notX0);
Mux16(a= x0, b= notX0, sel= nx, out= x1);
// y0 is the "y" we get after applying zy
Mux16(a= y, b= false, sel= zy, out= y0);
// y1 is the "y" we get after applying ny
Not16(in= y0, out= notY0);
Mux16(a= y0, b= notY0, sel= ny, out= y1);
// out0 is the "out" we get after applying f
And16(a= x1, b= y1, out= andResult);
Add16(a = x1, b = y1, out = addResult);
Mux16(a= andResult, b= addResult, sel= f, out= out0);
// out is the "out" we get after applying "no"
// out1 is equal to out
// assign value to ng
Not16(in= out0, out= notOut0);
Mux16(a= out0, b= notOut0, sel= no,
out= out, out= out1, out[15]= ng, out[15]= zr1);
// assign value to zr
Not16(in= out1, out= notOut);
Inc16(in= notOut, out[15]= zr2);
Or(a= zr1, b= zr2, out= zr3);
Not(in= zr3, out= zr);
}
这样的代码可以通过仿真测试,效果如下图所示 ⬇️
思路 : 借助 Add16 来判断
令 。注意到
- , 而 的最高位是
- 对满足 任意正整数 而言, 的结果的最高位是
基于上述观察,我们可以这样判定 。 如果以下两个条件同时满足,那么 是 ,否则 是
- 是
- 的结果的最高位是
所以我们可以这样来设计 ⬇️ (因为第 步的代码也做了小的调整,所以下面的代码包含了第 步)
// out is the "out" we get after applying "no"
// out1 is equal to out
// assign value to ng
Not16(in= out0, out= notOut0);
Mux16(a= out0, b= notOut0, sel= no,
out= out, out= out1, out[15]= ng, out[15]= zr1);
// assign value to zr
Add16(a = out1, b[0..14] = true, b[15] = false,
out[15] = zr2);
Or(a= zr1, b= zr2, out= zr3);
Not(in= zr3, out= zr);
完整的代码如下
// This file is part of www.nand2tetris.org
// and the book "The Elements of Computing Systems"
// by Nisan and Schocken, MIT Press.
// File name: projects/2/ALU.hdl
/**
* ALU (Arithmetic Logic Unit):
* Computes out = one of the following functions:
* 0, 1, -1,
* x, y, !x, !y, -x, -y,
* x + 1, y + 1, x - 1, y - 1,
* x + y, x - y, y - x,
* x & y, x | y
* on the 16-bit inputs x, y,
* according to the input bits zx, nx, zy, ny, f, no.
* In addition, computes the two output bits:
* if (out == 0) zr = 1, else zr = 0
* if (out < 0) ng = 1, else ng = 0
*/
// Implementation: Manipulates the x and y inputs
// and operates on the resulting values, as follows:
// if (zx == 1) sets x = 0 // 16-bit constant
// if (nx == 1) sets x = !x // bitwise not
// if (zy == 1) sets y = 0 // 16-bit constant
// if (ny == 1) sets y = !y // bitwise not
// if (f == 1) sets out = x + y // integer 2's complement addition
// if (f == 0) sets out = x & y // bitwise and
// if (no == 1) sets out = !out // bitwise not
CHIP ALU {
IN
x[16], y[16], // 16-bit inputs
zx, // zero the x input?
nx, // negate the x input?
zy, // zero the y input?
ny, // negate the y input?
f, // compute (out = x + y) or (out = x & y)?
no; // negate the out output?
OUT
out[16], // 16-bit output
zr, // if (out == 0) equals 1, else 0
ng; // if (out < 0) equals 1, else 0
PARTS:
// x0 is the "x" we get after applying zx
Mux16(a= x, b= false, sel= zx, out= x0);
// x1 is the "x" we get after applying nx
Not16(in= x0, out= notX0);
Mux16(a= x0, b= notX0, sel= nx, out= x1);
// y0 is the "y" we get after applying zy
Mux16(a= y, b= false, sel= zy, out= y0);
// y1 is the "y" we get after applying ny
Not16(in= y0, out= notY0);
Mux16(a= y0, b= notY0, sel= ny, out= y1);
// out0 is the "out" we get after applying f
And16(a= x1, b= y1, out= andResult);
Add16(a = x1, b = y1, out = addResult);
Mux16(a= andResult, b= addResult, sel= f, out= out0);
// out is the "out" we get after applying "no"
// out1 is equal to out
// assign value to ng
Not16(in= out0, out= notOut0);
Mux16(a= out0, b= notOut0, sel= no,
out= out, out= out1, out[15]= ng, out[15]= zr1);
// assign value to zr
Add16(a = out1, b[0..14] = true, b[15] = false,
out[15] = zr2);
Or(a= zr1, b= zr2, out= zr3);
Not(in= zr3, out= zr);
}
这样的代码可以通过仿真测试,效果如下图所示 ⬇️
其他
的值为 或者 时,每一位的值是如何画出来的?
我是通过 mermaid.live 页面来绘制那些图的。其中 时,对应的代码如下 ⬇️ ( 时的代码也大同小异,就不赘述了)
block
block
columns 16
name15["[15]"] name14["[14]"] name13["[13]"] name12["[12]"]
name11["[11]"] name10["[10]"] name9["[9]"] name8["[8]"]
name7["[7]"] name6["[6]"] name5["[5]"] name4["[4]"]
name3["[3]"] name2["[2]"] name1["[1]"] name0["[0]"]
value15["0"] value14["0"] value13["0"] value12["0"]
value11["0"] value10["0"] value9["0"] value8["0"]
value7["0"] value6["0"] value5["0"] value4["0"]
value3["0"] value2["0"] value1["0"] value0["0"]
end
style value0 fill:#ffff00;
style value1 fill:#ffff00;
style value2 fill:#ffff00;
style value3 fill:#ffff00;
style value4 fill:#ffff00;
style value5 fill:#ffff00;
style value6 fill:#ffff00;
style value7 fill:#ffff00;
style value8 fill:#ffff00;
style value9 fill:#ffff00;
style value10 fill:#ffff00;
style value11 fill:#ffff00;
style value12 fill:#ffff00;
style value13 fill:#ffff00;
style value14 fill:#ffff00;
style value15 fill:#ffff00;
style name0 fill:#d3d3d3,stroke:#fff;
style name1 fill:#d3d3d3,stroke:#fff;
style name2 fill:#d3d3d3,stroke:#fff;
style name3 fill:#d3d3d3,stroke:#fff;
style name4 fill:#d3d3d3,stroke:#fff;
style name5 fill:#d3d3d3,stroke:#fff;
style name6 fill:#d3d3d3,stroke:#fff;
style name7 fill:#d3d3d3,stroke:#fff;
style name8 fill:#d3d3d3,stroke:#fff;
style name9 fill:#d3d3d3,stroke:#fff;
style name10 fill:#d3d3d3,stroke:#fff;
style name11 fill:#d3d3d3,stroke:#fff;
style name12 fill:#d3d3d3,stroke:#fff;
style name13 fill:#d3d3d3,stroke:#fff;
style name14 fill:#d3d3d3,stroke:#fff;
style name15 fill:#d3d3d3,stroke:#fff;
具体的语法请参考 Block Diagrams Documentation