Nand2Tetris Course Part 1

课程参考链接:【高清-中字-公开课】依据基本原理构建现代计算机:从与非门到俄罗斯方块

零、简述

本文主要就 Nand2Tetris 课程中 Unit1-6 的学习过程进行整理,**该课程分为两个部分——Part1 主要集中在硬件部分,从与非门入手,构建整个计算机硬件;Part2 主要聚焦在软件。**花了一些时间学习了 Part1 部分,该课程分为六个单元:

  • Week0 中对整个课程进行了介绍
  • Week1 中对逻辑门进行了介绍,Project 中构建了大量逻辑门
  • Week2 中对二进制数的计算进行了介绍,结合课程一内容,Project 中构建了加法器及 ALU 芯片
  • Week3 中介绍了触发器和存储单元,Project 中构建了 Memory 和 PC 芯片
  • Week4 中介绍了一种针对本课程使用的机器语言(Hack Language),Project 中要求使用该语言进行一些操作训练
  • Week5 中构建 CPU、Memory 等,将先前设计的各类芯片进行组合,完成本课程的硬件设计(Hack Computer)
  • Week6 中构建了编译器,实现将机器语言(Hack Language)转换为二进制,可被机器直接使用

在这里插入图片描述

一、布尔代数与门逻辑

Week 1 由与非门开始,由于 NAND gate 可以实现其他任何逻辑,所以我们使用 NAND gate 来构建整个计算机。与非门由课程提供,我们不需要关注其在模拟域的具体实现。事实上构建计算机也未必一定从与非门开始,能够实现任意逻辑的集合即可以用来(在逻辑上)构建计算机。

Chip name: Nand 
Inputs: a, b 
Outputs: out Function: If a=b=1 then out=0 else out=1 
Comment: This gate is considered primitive and thus there is no need to implement it.

对于布布尔代数的相关知识,课程中进行了简单的涉猎,也给出了一个化简真值表的通用方法(见下引用)。并没有深入讲解例如卡诺图化简等方法,个人认为这是课程设计合理之处,没有太过关注一些细节的方法,而是从道的角度让人在宏观上理解。

Canonical Representation As it turns out, every Boolean function can be expressed using at least one Boolean expression called the canonical representation. Starting with the function’s truth table, we focus on all the rows in which the function has value 1. For each such row, we construct a term created by And-ing together literals (variables or their negations) that fix the values of all the row’s inputs.

以下逻辑是 Project1 中需要构建的,课程的设计者设计了一种简单的 HDL 语言以及对应的模拟器,用户可以在模拟器中对所写 HDL 进行验证。用户无需写对应的验证脚本,课程中已经提供。课程作者为每个芯片定义了输入输出引脚及所需要实现的功能,读者可据此进行设计。

Nand (given)
Not
And 
Or 
Xor 
Mux 
DMux 
Not16 
And16 
Or16 
Mux16 
Or8Way 
Mux4Way16 
Mux8Way16 
DMux4Way 
DMux8Way

Project1 整体难度不高,几个 MUX(多路复用器) 和 DMUX(多路解复用)会在后续芯片构建中反复用到,是需要注意的。

二、ALU 的构建

Week 2 的目标在于构建一个 ALU,要求其可以实现一定的数学运算和逻辑运算。
在这里插入图片描述

但在构建 ALU 之前,我们需要设计好加法器和减法器,这就需要具备二进制加减法的相应能力。二进制数的负数表示通过取反加一进行,先取反后加一。具体原因可以参考下面的公式:
2 n − x = 1 + ( 2 n − 1 ) − x 2^n-x=1+(2^n-1)-x 2nx=1+(2n1)x
其中 ( 2 n − 1 ) − x (2^n-1)-x (2n1)x 就是取反的过程,那么该如何进行减法的操作呢?——减法等于加上一个负数。以计算 y − x y-x yx 为例:
y − x = y + ( − x ) = y + [ 1 + ( 2 n − 1 ) − x ] − 2 n y-x=y+(-x)=y+[1+(2^n-1)-x]-2^n yx=y+(x)=y+[1+(2n1)x]2n
注意:负数的首位为 1

ALU 中还包括其他诸多功能,需根据输入指令来确定启用何种功能,进行何种输出,由于课程制作的 Hack 计算机为 16 位,本 ALU 也是 16bit 输入及 16bit 输出的,后面制作 Memory 时也是如此。如下图所示,这些控制位会在汇编语言中有所体现,后续会进一步使用。

在这里插入图片描述

三、寄存器与存储

在本章之前的课程中,时间的概念没有被引入,这也就意味着我们的计算机无法拥有记忆,无法存储过去的内容。本章节因此出现,我们希望从一个简单的触发器入手,来一步步构建存储空间,这个(初始)触发器也是本课程所直接给出的,其作用如下:

if load(t – 1) then out(t) = in(t – 1) 
else out(t) = out(t – 1)

课程中没有过多的介绍各种不同的触发器,也没有介绍触发器本身的构建方式。我们需要将上述这个单 bit 的寄存器进行扩展,使其可以进行更大规模数据的存储,这也是 Project3 中的要求。随着存储容量的增加,地址也需要相应增加,通过不断复用小容量的寄存器,更大容量的寄存器得以被建立。在读取和写入这些 RAM 时,首先需要进行选择,想象一下当地址指向某个。而高亮合适的房间就需要使用 MUX 和 DMUX。这里以 RAM16K 的代码为例:

在这里插入图片描述

// 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/03/b/RAM16K.hdl

/**
 * Memory of 16K registers, each 16 bit-wide. Out holds the value
 * stored at the memory location specified by address. If load==1, then
 * the in value is loaded into the memory location specified by address
 * (the loaded value will be emitted to out from the next time step onward).
 */ 

CHIP RAM16K {
    IN in[16], load, address[14];
    OUT out[16];  
    
    PARTS:
    // Put your code here:
    DMux4Way(in=load, sel=address[12..13], a=l0, b=l1, c=l2, d=l3);
    RAM4K(in=in, load=l0, address=address[0..11], out=o0);
    RAM4K(in=in, load=l1, address=address[0..11], out=o1);
    RAM4K(in=in, load=l2, address=address[0..11], out=o2);
    RAM4K(in=in, load=l3, address=address[0..11], out=o3);
    Mux4Way16(a=o0, b=o1, c=o2, d=o3, sel=address[12..13], out=out);
}

Project 中还要求写一个 16-bit 的 Counter,作为程序寄存器。其描述如下图,在后续电脑的构建中,这一模块会被使用。这种由 if… else… 构成的描述在本课程的 HDL 语言中往往使用 Mux 语句完成。
在这里插入图片描述

四、汇编语言的使用

本章节将练习使用 HACK 汇编语言,Project 中主要使用这种语言书写一些简单程序。在第六章中,我们会构建编译器将这种语言与物理上的数字 01 一一对应。在第五章节中,我们会构建整个电脑,该电脑即使用了这种 HACK 语言翻译成的 01 进行控制。下面对这种 HACK 语言进行简单介绍。

**HACK 仅有两种命令,一种叫做 A 指令,另一种叫做 C 指令。**在了解这些指令之前,我们需要先了解 HACK Computer 的寄存器、RAM、ROM。通过下图可以进行了解:

在这里插入图片描述

对于 A instruction 而言,有如下作用。可以想象为,当使用指令 @ c o n s t @const @const 时,A 寄存器的值被设定为 c o n s t const const,同时以 c o n s t const const 为地址选中 ROM 与 RAM 的那个位置(highlight 这个位置),之后对于 M 的操作即基于这个高亮的位置进行。再次说明,一个 Memory 无论多大,一次只能高亮一个小格子。
在这里插入图片描述\

另一种指令是 C insturction,其所涉及的功能较多,具体如下:

The C-instruction is the programming workhorse of the Hack platform—the instruction that gets almost everything done. The instruction code is a specification that answers three questions: (a) what to compute, (b) where to store the computed value, and © what to do next? Along with the A-instruction, these specifications determine all the possible operations of the computer.

C-instruction: dest=comp;jump 

// Either the dest or jump fields may be empty.
// If dest is empty, the ‘‘=’’ is omitted;
// If jump is empty, the ‘‘;’’ is omitted.

在这里插入图片描述
在这里插入图片描述

C 指令可以完成一些列逻辑和数学计算,同时可以执行跳转等操作。在书写汇编语言的时候,我们可以使用一些变量来方便程序的书写,这些变量主要有如下类别:

  • Variable symbols: 变量,让编译器自己分配该变量的地址,注意:这个地址需要大于 16
  • Label symbols: 用以标志指令顺序位置,以便完成诸如循环等操作
  • Pre-defined symbols: 预定义的一些符号,例如 R0 代表 RAM[0],SCREEN,KBD 等等

课程中使用了屏幕和键盘两个外设,这两个外设直接映射在 Memory 中,只需对对应的 RAM 进行操作即可。Project 中会要求操作这两个外设。下面的代码是 Project 中的 Fill 任务,注意任务中是进入死循环的,这种方式可以避免程序不断执行到未知 ROM 领域,保证程序的安全性。

在这里插入图片描述

// 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/04/Fill.asm
// Runs an infinite loop that listens to the keyboard input.
// When a key is pressed (any key), the program blackens the screen,
// i.e. writes "black" in every pixel;
// the screen should remain fully black as long as the key is pressed.
// When no key is pressed, the program clears the screen, i.e. writes
// "white" in every pixel;
// the screen should remain fully clear as long as no key is pressed.

// Put your code here.

// 需要搞清楚在操作着什么?是地址?还是地址对应的内存空间。
(LOOP)
    // initial i=16384
    @SCREEN
    D=A
    @i
    M=D
    // KEYBOARD IS 0 OR NOT
    @KBD
    D=M
    @WHITE
    D;JEQ
(BLACK)
    @i
    D=M
    @24575
    D=D-A
    @END
    D;JEQ
    @i
    D=M
    // RAM[i]=-1
    @0
    A=A+D
    M=-1
    //i=i+1
    @i
    M=M+1
    @BLACK
    0;JMP
(WHITE)
    @i
    D=M
    @24575
    D=D-A
    @END
    D;JEQ
    @i
    D=M
    // RAM[i]=-1
    @0
    A=A+D
    M=0
    //i=i+1
    @i
    M=M+1
    @WHITE
    0;JMP
(END)
    @LOOP
    0;JMP

五、构建电脑——CPU&Memory

在本章节的 Project 中,会搭建 CPU Memory 和整个电脑,先前所制作的 ALU、MUX、Reg 等都会在本章节中被使用。下图是章节中我们需要构建的 CPU,其中所有的 C(control)位都需要我们进行填充确认。这就需要将汇编语言的各 bit 位与下图进行对应,理解这整个 CPU 运行的流程。

  1. 首先考虑 ALU 的部分,从 ALU 的接口中我们可以看到其需要六个控制位,而这正是 C instruction 的六个 comp 位,结合 ALU 的输入图进行判断,即可锁定这几位的顺序。

  2. 最右边 MUX16 的 sel 输入选择了 A 或者 C instruction,如果是 A instruction,输入会被存储在 A Reg 中,在之后指向相关地址,如果是 C instruction,其中不同的位需要在整个 CPU 中进行分布。

  3. C instruction 中 dest 的三位对 A、D、M 进行了指向,即哪个 dest 可被输入计算结果,相关位自然应该落实在寄存器的 load 位上,故而 A Reg 的 load 是 dest 的第一位,D Reg 的 load 是 dest 的第二位,而 write M 是否输出取决于 dest 的第三位。

  4. 靠右位置的 MUX16 对使用 inM 或是使用 A 寄存器输入进行了选择,参考 comp 在 a=0 和 a=1 的不同功能即可得知这里的 sel 位为 C instruction 中的 a 位。

  5. 剩下的就是程序寄存器 PC 部分了,程序寄存器的应该实现如下功能。目前手头的资源有:JMP、zr、ng,需要进行相关逻辑运算以得到 PC 的 load 位。

    PC (program counter) implementation:
    if (reset == 1) PC ß 0 // reset
    else
    	if (J (jump bits, zr, ng) == 1) PC<-A // jump
    	else PC++ // next instruction
    

在这里插入图片描述

具体实现可以参考: https://github.com/GreenOlvi/nand2tetris/blob/master/05/CPU.hdl

六、从汇编语言到 01——编译器的构建

本章节的目标是构建一个编译器,将汇编语言翻译成比特流。翻译之中主要有如下几个目标:

  • 忽视所有的注释
  • 将所有的 variable symbols 进行翻译
  • 将所有的 label symbols 进行翻译
  • 将所有 pre defined symbols 进行翻译
  • 翻译所有语句

在这里插入图片描述

翻译应该遵循如下的步骤:

Initialize:
Opens the input file (Prog.asm),
and gets ready to process it
Constructs a symbol table,
and adds to it all the predefined symbols

First pass:
Reads the program lines, one by one,
focusing only on (label) declarations.
Adds the found labels to the symbol table

Second pass (main loop):
(starts again from the beginning of the file)
While there are more lines to process:
	Gets the next instruction, and parses it
	If the instruction is @ symbol
		If symbol is not in the symbol table, adds it
		Translates the symbol to its binary value
	If the instruction is dest =comp ; jump
		Translates each of the three fields into its binary value
Assembles the binary values into a string of sixteen 0’s and 1’s
Writes the string to the output file.

Project 可以使用高级语言进行编译器的书写,也可以使用手写完成,窃以为这个逻辑理通了之后写不写一遍也并不重要了。

结语

至此,PART1 完成,前六章全部结束。我们构建了计算机的硬件,也拥有了操作这个硬件的基本汇编语言和编译器可供后续课程使用。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值