共计 2601 个字符,预计需要花费 7 分钟才能阅读完成。
已经把 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!