2

已经把nand2teris的part1学完了,现在才写的原因是终于到了software hierarchy部分了,project是正式的coding..而不是讨厌的HDL了hhh(其实HDL也非常有意思。)。project6写这个hack machine code的assembler我觉得是一个承上启下的unit,所以从这里归纳一下往下的hardware和开始之后的top layer,之后的software hierarchy对大多数人其实是更有用的,所以之后的每个project都会记录一下。

github:https://github.com/Lunaticf/n...

1. what is nand2teris and the part1

nand2teris官网: https://www.nand2tetris.org

nand2teris是一门教你from the ground up从一个nand逻辑门开始造一台计算机的课程,分为硬件和软件两个part。Cousera上学起来比较方便,但是project和其他资源完全开放,不看视频完全没问题,甚至可以节省相当多的时间,干货都在pdf和project。并且这门课对初学者非常友好,甚至不要求你有编程经验,在project6写汇编器的时候甚至提供了非编程的版本,让你手动翻译程序到binary code也可以过(虽然我觉得这样也太蠢了....)。这门课另一个好处在于模块划分非常清晰和独立,循序渐进,并且打造了相当好的多个可视化工具,帮助你理解和实践。并且整个HDL硬件编程语言和CPU的指令集都是重新设计的简化版,非常友好,可以想象投入了非常大的精力,自然这门课也是好评如潮,两个老师也很萌,每个week最后的perspective最爱看了。


part1的各个项目:

  • Boolean Logic 用与非门设计And,Or,Not,Multiplexor等基本逻辑门
  • Boolean Arithmetic 设计半加器、全加器和ALU等
  • Sequential Logic 以上都是combinational的chip,不能维护状态,构建寄存器和RAM
  • Machine Language 用汇编先写一点程序
  • Computer Architecture 设计CPU,这里用的是程序和数据分离的哈佛架构
  • Assembler 将汇编程序转化为binary code

我在完成part1的各个project的过程中感受就是学到了如何从一个简单的逻辑门一步一步搭建到各个模块,体会用基础的逻辑门实现多路复用等逻辑,再从各个小的模块构建到更大的模块比如ALU,RAM,非常深的体会了Abstract这一计算机科学的核心,在实现high level的chip的时候用到小的chip,完全不用care里面的细节,只需要focus其输入输出。另外就是重温了一下硬件编程,刚上手的时候就想起来本科的时候数电的时候曾经写过的verilog,并且立即就想起了怎么用真值表来构造表达式,算出这个chip所需要的逻辑门组合,然后自然而然就过渡到化简更方便的卡诺图等,当然也要感谢这门课设计的精简的hdl,让我忘记了被verilog支配的恐惧。另外一个很深的感触就是学过的知识确实是有用的,在你的mind里其实会建立索引,可能索引指向的数据会忘掉,但是可以让你日后有这种deja vu(似曾相识的)的感觉,马上就能按图索骥拾起来。

我觉得每个CS的新生都可以学一下这门课,建立起down to top的对计算机体系的整体的感性认识,这样之后再去学数电、操作系统和编译原理等专业课的时候会有一定的帮助,研究各个系统的detail。

2. the hack assembler

在完成了part1之后,我们就可以过渡到software hierarchy啦~看到图中下面已经变成一个Hack chip了,这意味着我们已经完成对Hardware一整层的Abstract了,只需要把它当成一个能运行Binary code的computer。

用Java快速写了一个很laji的版本,https://github.com/Lunaticf/n...
需要注意的是这里Project只要求翻译正确的汇编程序,也就是提供的需要转换的汇编程序语法上是正确的,也降低了一定的难度,毕竟不是Compiler的课程。
实现的核心就是根据Binary code的规范来将汇编程序.asm转换为.hack二进制代码。,指令集也是非常简单的,照着提供的大致流程写就行了。主要有Parser和Generator两个核心类,Parser用于返回每一个有效line,在调用hasNext的时候跳过空行与注释,同时也要对语句进行trim和判断是否有行尾注释。主类调用Parser返回每一行有效的汇编语句后,调用Genertor生成bianry code,指令集很简单,只有A指令和C指令两种指令,转译即可。另外就是SymbolTable的概念,汇编语言自然需要定义自己的symbol来更方便的编程,那么我们需要定义一个SymbolTable,这里我直接用Java的HashMap来实现,首先预定义一些symbol比如Screen、Keyboard这些键,值就是其内存起始地址。我们的汇编器需要对程序进行两遍的扫描,first pass来扫诸如(xxx)这样的label,主要是因为这类label可能会出现在定义其label的语句之前就被调用,我们没有办法扫描的时候就给出symbol的value,所以需要two pass。

初学者需要注意一些细节,比如在A指令定义一个新的symbol的时候,是从内存的16开始存放,你可能会觉得那程序中如果用到过@16、@17的语句,那么不是重复使用这一个Byte了么,其实这里也还是简化了,在论坛中发现助教回答诸如以下@常量的语句后面跟的都是:

@18
D=A

这些数字不用做内存地址的reference,而是只是用来赋值常量的。
另外就是在first pass的时候统计行号的时候注意不要统计label这一行。

单独写一个很快速的版本过test是比较简单的,py写的话可以不到100行,其实还有很多可以完善的点,比如检测语法错误、重复的label、D+A可以写成A+D等等,使其更加robust,还可以用js直接写一个网页的可视化版本,但是完成课程要求的已经足够理解基本原理了,Keep moving!


lunaticf
136 声望7 粉丝