这次回顾第五章的内容,这一章介绍了计算机体系结构,完整搭建了一个计算机。

课程官网:

https://www.nand2tetris.org/

视频地址:

https://www.coursera.org/learn/build-a-computer

Chapter 5 计算机体系结构

Part 1:课程回顾

这部分内容从略,这一章的主要内容为项目部分。

Part 2:项目

Memory

Memory的架构如下:

关键问题是判断address和16383以及24576的关系,注意到上述数字的二进制表示为:

所以可以根据address[14]和address[13]判断操作的位置,对应代码如下:

//判断是否小于16384, 利用address的最高位判断
DMux(in=load, sel=address[14], a=ram, b=device);
RAM16K(in=in, load=ram, address=address[0..13], out=out1);
//判断是否等于24576,利用address的倒数第二位判断
DMux(in=device, sel=address[13], a=screen, b=keyboard);
Screen(in=in, load=screen, address=address[0..12], out=d1);
Keyboard(out=d2);
//汇总
Mux16(a=d1, b=d2, sel=address[13], out=out2);
Mux16(a=out1, b=out2, sel=address[14], out=out);

CPU

CPU的架构如下:

instruction为16位二进制数,格式为

其中$i$表示指令类型,$0$表示A指令,$1$表示C指令。如果是C指令,a位域和c位域共同表示comp部分,指明进行什么计算,d位域表示dest部分,j位域表示jump部分;如果是A指令,那么除了i位之外的15位被解释为常数。

A指令的形式如下:

C指令的形式如下:

ARegister

首先考虑A寄存器,注意只有A指令才会对A寄存器操作,所以instruction相连的Mux16根据$i$进行判断:

//判断是否为A指令ALUout,根据最左边一位判断
Mux16(a=instruction, b=ALUout, sel=instruction[15], out=Ain);

另外这里要考虑d位域,实际上有如下关系:

不难看出只有d1为1的时候数据才会存储到A寄存器(注意此时指令为C指令,即第一位为0),因此这部分代码如下:

//计算loadA, instruction[15]=0时才需考虑instruction[5]
Not(in=instruction[15], out=a);
Or(a=a, b=instruction[5], out=loadA);
//A寄存器
ARegister(in=Ain, load=loadA, out=Aout, out[0..14]=addressM);
DRegister

接着考虑D寄存器,注意只有C指令才会对A寄存器操作,结合上图可得只有d2为1的时候数据才会存储到D寄存器,因此这部分代码如下:

//计算loadD
And(a=instruction[15], b=instruction[4], out=loadD);
//D寄存器
DRegister(in=ALUout, load=loadD, out=Dout);

注意DRegister的输出为ALU一个输入。

ALU

这部分讨论ALU,首先考虑ALU的另一个输入,这部分和inM以及ARegister的输出有关,控制位由a决定:

对应代码如下:

//ALU的另一个输入
Mux16(a=Aout, b=inM, sel=instruction[12], out=ALUin);

ALU部分的代码如下:

//ALU
ALU(x=Dout, y=ALUin, zx=instruction[11], nx=instruction[10], zy=instruction[9], ny=instruction[8], f=instruction[7], no=instruction[6],
	out=ALUout, out=outM, zr=zr, ng=ng);
writeM

依旧回顾下图:

注意只有C指令以及d3=1时,才会输出到RAM[A],所以这部分的代码如下:

//writeM,只有为C指令的时候writeM才有效
And(a=instruction[3], b=instruction[15], out=writeM);
pc

这部分讨论pc,我们希望实现的功能如下:

现有元件为:

对比后不难发现inc应该恒取true,关键点是决定load,实际上有下图:

首先计算j1,j2,j3:

//load,只有C指令才考虑PC
And(a=instruction[15], b=instruction[2], out=j1);
And(a=instruction[15], b=instruction[1], out=j2);
And(a=instruction[15], b=instruction[0], out=j3);

out为ALU输出的正负,根据此图,首先需要构造判断out>0的布尔逻辑:

//判断out正负
Not(in=zr, out=notzr);
Not(in=ng, out=notng);
And(a=notzr, b=notng, out=pos);

接着根据out的正负号进行匹配即可,结合上图不难得到pc的构造方式:

//匹配j1,j2,j3
And(a=j1, b=ng, out=l1);
And(a=j2, b=zr, out=l2);
And(a=j3, b=pos, out=l3);
//输出load
Or(a=l1, b=l2, out=o1);
Or(a=o1, b=l3, out=load);

//PC,inc恒为true
PC(in=Aout, reset=reset, load=load, inc=true, out[0..14]=pc);

Computer

这部分根据下图的架构构造即可:

对应代码如下:

CPU(inM=inM, instruction=instruction, reset=reset, 
writeM=writeM, outM=outM, addressM=addressM, pc=pc);
Memory(in=outM, load=writeM, address=addressM, out=inM);
ROM32K(address=pc, out=instruction);