From Nand to Tetris week 3

这次回顾第三章的内容,这部分主要介绍了时序门,寄存器,内存以及计数器的概念。

课程官网:

https://www.nand2tetris.org/

视频地址:

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

Chapter 3 时序逻辑

Part 1:课程回顾

之前两章介绍的芯片都是组合芯片(combinational logic),这些芯片的特点是和时间无关。这一讲引入时序芯片(sequential logic),之所以引入时序芯片,是为了构建能够记忆“信息”的芯片,而记忆必然是之前的,所以我们首先需要开发一些表示时间的芯片。

触发器(Flip-Flop)

计算机中最基本的时序单元是触发器(Flip-Flop),其实现的功能为

本课程中我们使用名为数据触发器(data flip-flops)的变体,其架构和输入输出如下:

DFF可以由与非门(NAND)实现,方法比较复杂,老师表示本课程略过这点,所以时序芯片的基本单元为DFF,其余常用的芯片都在Project中有详细介绍。

Part 2:项目

Bit

其架构如下

假设MUX的输出为muxout,DFF有两个输出,一个为out,另一个为dffout,有了这些变量之后,按照上述架构组合即可:

1
2
3
4
5
6
7
8
9
CHIP Bit {
IN in, load;
OUT out;

PARTS:
// Put your code here:
DFF (in=muxout, out=out, out=dffout);
Mux (a=dffout , b=in, sel=load, out=muxout);
}

参考资料:传送门

Register

接口如下

对每一位使用Bit芯片即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
CHIP Register {
IN in[16], load;
OUT out[16];

PARTS:
// Put your code here:
Bit (in=in[0], load=load, out=out[0]);
Bit (in=in[1], load=load, out=out[1]);
Bit (in=in[2], load=load, out=out[2]);
Bit (in=in[3], load=load, out=out[3]);
Bit (in=in[4], load=load, out=out[4]);
Bit (in=in[5], load=load, out=out[5]);
Bit (in=in[6], load=load, out=out[6]);
Bit (in=in[7], load=load, out=out[7]);
Bit (in=in[8], load=load, out=out[8]);
Bit (in=in[9], load=load, out=out[9]);
Bit (in=in[10], load=load, out=out[10]);
Bit (in=in[11], load=load, out=out[11]);
Bit (in=in[12], load=load, out=out[12]);
Bit (in=in[13], load=load, out=out[13]);
Bit (in=in[14], load=load, out=out[14]);
Bit (in=in[15], load=load, out=out[15]);
}

RAM8

接口如下:

第一步利用DMux8Way对每个Register设置load值,只有相应地址的Register的load值设置为输入的load;第二步根据第一步产生的load值对每个Register操作;第三步利用Mux8Way16对第二步的结果进行选择:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
CHIP RAM8 {
IN in[16], load, address[3];
OUT out[16];

PARTS:
// Put your code here:
DMux8Way (in=load, a=a, b=b, c=c, d=d, e=e, f=f, g=g, h=h, sel=address);
Register (in=in, load=a, out=o0);
Register (in=in, load=b, out=o1);
Register (in=in, load=c, out=o2);
Register (in=in, load=d, out=o3);
Register (in=in, load=e, out=o4);
Register (in=in, load=f, out=o5);
Register (in=in, load=g, out=o6);
Register (in=in, load=h, out=o7);

Mux8Way16(a=o0 ,b=o1 ,c=o2 ,d=o3 ,e=o4 ,f=o5 ,g=o6 ,h=o7, sel=address ,out=out);
}

RAM64

将address分为两部分,第一部分查看属于哪个RAM8,第二部分查看属于哪个Register:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
CHIP RAM64 {
IN in[16], load, address[6];
OUT out[16];

PARTS:
// Put your code here:
DMux8Way (in=load, a=a, b=b, c=c, d=d, e=e, f=f, g=g, h=h, sel=address[3..5]);
RAM8 (in=in, load=a, address=address[0..2], out=o0);
RAM8 (in=in, load=b, address=address[0..2], out=o1);
RAM8 (in=in, load=c, address=address[0..2], out=o2);
RAM8 (in=in, load=d, address=address[0..2], out=o3);
RAM8 (in=in, load=e, address=address[0..2], out=o4);
RAM8 (in=in, load=f, address=address[0..2], out=o5);
RAM8 (in=in, load=g, address=address[0..2], out=o6);
RAM8 (in=in, load=h, address=address[0..2], out=o7);

Mux8Way16(a=o0 ,b=o1 ,c=o2 ,d=o3 ,e=o4 ,f=o5 ,g=o6 ,h=o7, sel=address[3..5], out=out);
}

RAM512,RAM4K,RAM16K和RAM64的思路类似。

RAM512

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
CHIP RAM512 {
IN in[16], load, address[9];
OUT out[16];

PARTS:
// Put your code here:
DMux8Way (in=load, a=a, b=b, c=c, d=d, e=e, f=f, g=g, h=h, sel=address[6..8]);

RAM64 (in=in, load=a, address=address[0..5], out=o0);
RAM64 (in=in, load=b, address=address[0..5], out=o1);
RAM64 (in=in, load=c, address=address[0..5], out=o2);
RAM64 (in=in, load=d, address=address[0..5], out=o3);
RAM64 (in=in, load=e, address=address[0..5], out=o4);
RAM64 (in=in, load=f, address=address[0..5], out=o5);
RAM64 (in=in, load=g, address=address[0..5], out=o6);
RAM64 (in=in, load=h, address=address[0..5], out=o7);

Mux8Way16(a=o0 ,b=o1 ,c=o2 ,d=o3 ,e=o4 ,f=o5 ,g=o6 ,h=o7, sel=address[6..8], out=out);
}

RAM4K

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
CHIP RAM4K {
IN in[16], load, address[12];
OUT out[16];

PARTS:
// Put your code here:
DMux8Way (in=load, a=a, b=b, c=c, d=d, e=e, f=f, g=g, h=h, sel=address[9..11]);

RAM512 (in=in, load=a, address=address[0..8], out=o0);
RAM512 (in=in, load=b, address=address[0..8], out=o1);
RAM512 (in=in, load=c, address=address[0..8], out=o2);
RAM512 (in=in, load=d, address=address[0..8], out=o3);
RAM512 (in=in, load=e, address=address[0..8], out=o4);
RAM512 (in=in, load=f, address=address[0..8], out=o5);
RAM512 (in=in, load=g, address=address[0..8], out=o6);
RAM512 (in=in, load=h, address=address[0..8], out=o7);

Mux8Way16(a=o0 ,b=o1 ,c=o2 ,d=o3 ,e=o4 ,f=o5 ,g=o6 ,h=o7, sel=address[9..11], out=out);
}

RAM16K

注意这里使用DMux4Way:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CHIP RAM16K {
IN in[16], load, address[14];
OUT out[16];

PARTS:
// Put your code here:
DMux4Way (in=load, a=a, b=b, c=c, d=d, sel=address[12..13]);

RAM4K (in=in, load=a, address=address[0..11], out=o0);
RAM4K (in=in, load=b, address=address[0..11], out=o1);
RAM4K (in=in, load=c, address=address[0..11], out=o2);
RAM4K (in=in, load=d, address=address[0..11], out=o3);

Mux4Way16(a=o0 ,b=o1 ,c=o2 ,d=o3, sel=address[12..13], out=out);
}

PC

接口如下:

这部分比较难,参考了课程讨论区:传送门

社区助教给出如下架构的提示:

Register存储上一次的值,只有当reset[t],load[t],inc[t]同时为false时,输出才为out[t],所以输入Register的load为

Register的输入为o2,输出有两个,一个为out,一个为Inc16的输入;Inc16的输出为Incout。Incout和in传入Mux16,根据load做选择,得到结果o1,最后将o1和false传入Mux16,根据reset做选择,得到输出o2。完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
CHIP PC {
IN in[16],load,inc,reset;
OUT out[16];

PARTS:
// Put your code here:
//计算Register的load
Or (a=reset, b=load, out=r1);
Or (a=r1, b=inc, out=Regload);

//根据load选择
Mux16 (a=Incout, b=in, sel=load, out=o1);
//根据reset选择
Mux16 (a=o1, b=false, sel=reset, out=o2);
//根据Regload选择
Register (in=o2, load=Regload, out=out, out=o3);
//加一
Inc16 (in=o3, out=Incout);
}

本文标题:From Nand to Tetris week 3

文章作者:Doraemonzzz

发布时间:2019年05月02日 - 20:51:00

最后更新:2019年09月12日 - 21:50:56

原始链接:http://doraemonzzz.com/2019/05/02/From Nand to Tetris week 3/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。