本节将构建内存,CPU,以及完整的HACK计算机。
画图软件为ubuntu的dia
整个计算机只有两种基本电路:组合逻辑电路和时序电路,基本构件分别是NAND和DFF,而DFF也是由NAND构建得来的,只是连接方式稍有不同(参见第三章,01-D触发器与1比特存储器实现)。
从第一章到现在,从基本的与非门开始,所有的电路的连接,都是通过布尔代数指引的。足见布尔代数在计算机发展过程中的奠基性地位。
这些电路功能上都很简单,用代码来实现是几行就可以搞定的事情,然而以此为起点,作为基本构件,可以完成不计其数的复杂任务,可见设计思想,本质把握是多么关键!
内存
HACK计算机的内存是一段包含物理内存、键盘映像、屏幕映像的连续内存地址空间,内存映像的操作与普通的内存段有所不同,因此在连接上要加以区分。
组织架构
电路图
HDL
参考https://github.com/woai3c/nand2tetris/blob/master/05/Memory.hdl
CHIP Memory {
IN in[16], load, address[15];
OUT out[16];
PARTS:
// 根据最大的两个地址选出对应的操作 00 01都是RAM 10是屏幕 11是键盘
DMux4Way(in = load, sel = address[13..14], a = loadA, b = loadB, c = loadS, d = loadK);
// 00|01
Or(a = loadA, b = loadB, out = loadRam);
RAM16K(in = in, load = loadRam, address = address[0..13], out = outRam);
Keyboard(out = outK);
Screen(in = in, load = loadS, address = address[0..12], out = outS);
// 根据最大的两个地址选出对应的操作 00 01都是RAM 10是屏幕 11是键盘
Mux4Way16(a = outRam, b = outRam, c = outS, d = outK, sel = address[13..14], out = out);
}
CPU
CPU是最复杂的部件,其有6个控制位,有两种指令,有多个输出,因此电路上是比较复杂的
组织架构
电路图
本书构建的电路中最复杂的就是它了,虽然复杂,按HDL来画却并不会多花多少时间。
HDL
来自https://github.com/woai3c/nand2tetris/blob/master/05/CPU.hdl
CHIP CPU {
IN inM[16], // M value input (M = contents of RAM[A])
instruction[16], // Instruction for execution
reset; // Signals whether to re-start the current
// program (reset==1) or continue executing
// the current program (reset==0).
OUT outM[16], // M value output
writeM, // Write to M?
addressM[15], // Address in data memory (of M)
pc[15]; // address of next instruction
PARTS:
// 指令的最大地址位 如果为1则是C指令 为0则是A指令
Not(in = instruction[15], out = isA);
// 对isC再次取返 判断是否C指令 读者可以自行用1或0代入想像一下 isC实际上就是instruction[15]的值
Not(in = isA, out = isC);
// 如果是C指令并且指令指定ALU输出存到AR 则将ALU的输出 输入到AR 否则将指令输入到AR
And(a = isC, b = instruction[5], out = isLoadAluOut);
Mux16(a = instruction, b = outALU, sel = isLoadAluOut, out = inAR);
// 如果为A指令或指令指定输出到A处理器 则将AR的load位置1
Or(a = isA, b = instruction[5], out = isLoadAR);
ARegister(in = inAR, load = isLoadAR, out = outAR, out[0..14] = addressM);
// 根据指令中的a位域判断将AR的输出或者inM输入到ALU
Mux16(a = outAR, b = inM, sel = instruction[12], out = outAM);
// 如果是C指令并且规定写入到M
And(a = isC, b = instruction[3], out = writeM);
// 如果是C指令并且规定写入到DR
And(a = instruction[4], b = isC, out = isLoadDR);
DRegister(in = outALU, load = isLoadDR, out = outDR);
And(a = isC, b = instruction[6], out = no);
And(a = isC, b = instruction[7], out = f);
And(a = isC, b = instruction[8], out = ny);
And(a = isC, b = instruction[9], out = zy);
And(a = isC, b = instruction[10], out = nx);
And(a = isC, b = instruction[11], out = zx);
ALU(x = outDR, y = outAM, zx = zx, nx = nx, zy = zy, ny = ny, f = f, no = no, out = outALU, out = outM, zr=zr, ng=ng);
// 根据j位域和ALU的zr、ng位来判断跳转
And(a = isC, b = instruction[0], out = isGT);
And(a = isC, b = instruction[1], out = isEQ);
And(a = isC, b = instruction[2], out = isLT);
And(a = ng, b = isLT, out = isLtJump);
And(a = zr, b = isEQ, out = isEqJump);
// 输出是否大于0
Not(in = ng, out = notNg);
Not(in = zr, out = notZr);
And(a = notNg, b = notZr, out = isOutGt);
And(a = isOutGt, b = isGT, out = isGtJump);
Or(a = isLtJump, b = isEqJump, out = isJump);
Or(a = isJump, b = isGtJump, out = jump);
PC(in = outAR, load = jump, inc = true, reset = reset, out[0..14] = pc);
}
HACK计算机
每个时钟周期内,CPU从ROM32K中取指令,从Memory取数据,输出计算结果和下一条命令所在的地址pc,同时有个reset信号表示pc是否从头开始。
组织架构
电路图
HDL
CHIP Computer {
IN reset;
PARTS:
CPU(reset = reset, inM = outMemory, instruction = outROM, outM = outMvalue, writeM = isLoadValue, addressM = outAddressM, pc = outPC);
Memory(in = outMvalue, load = isLoadValue, address = outAddressM, out = outMemory);
ROM32K(address = outPC, out = outROM);
}