课程 nand2tetris Part-I 回顾
⭐⭐⭐⭐⭐推荐
课程脉络
布尔逻辑
关键的理论保证:任何一个真值表,都能用布尔表达式来实现
这一章以与非门为起点,构建了基础了逻辑门,Not、And、Or、Xor, 并且实现了选择器(Mux)和分发器(DMux)。
两个想法:
用我自己的话说,Mux就是赛马比赛,分半区,一个个地选择局部冠军,最后到总冠军;DMux就是在二叉树中的接力赛,一层层传递,但是每层只有一个节点包含输入值。
Mux启示,在布尔逻辑层,
if
的实现是两个分支都计算,然后选择结果,而不是像高级语言那样,判断条件后选择分支去计算结果。
算术逻辑单元
构建的逻辑是
1 |
|
内存
构建的逻辑1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18DFF 原语
|
v
Bit寄存器 Mux + DFF + load控制位
|
v
16位寄存器
|
v
RAM N个16位寄存器 + address, N=8,64, 512, 4K,...
\_ PC 程序计数器
寄存器
区分了两类元件,combinational
和 sequential
关键在于元件是否含有触发器(flip-flop),在本课程中,特指DFF,Data Flip Flop。包含的就属于sequential
。
DFF的特点是,它的输出,是前一个时刻的输入,表达式为out(t) = in(t-1)
所谓时刻,是指计算机都有一个全局的时钟脉冲,高-低-高-低的循环往复,tick, tock, tick, tock。而触发器总会被高电平或者低电平所”触发”而产生输出,而这个输出,像前面说的,就是上一个脉冲时,输入是多少。
正是因为这样的特性,sequential
可以把输出接到输入上,因为他们实际上是不同时刻的值,不会造成问题。而combinational
的输入和输出是在同一时刻确定的,所以不能形成有效的loop,比如之前的And、Or、加法器等等。输出接输入loop的意义是,DFF可以记住自己的值,也就是在时间(tick-tock)流动过程中,保持输出值不变。
寄存器就是在利用DFF,增加一个load
的控制,显示控制是否让DFF记住新的值,换个说法就是读/写模式的切换。如下图1
2
3
4
5
6
7
8 |
| load
in v
------------>+--+--+ +-----+ out
| Mux +----> + DFF + --------------->
-->+-----+ +-----+ |
| |
----------------------------load=1
的时刻,寄存器的输出仍然时上一次储存的值,但是Mux已经选择了in
成为DFF的输入,所以下一个时刻,寄存器将输出in
。
RAM
字:用16位的寄存器来造内存,16就是字长
RAM由多个寄存器组成,输入为load
, address[16]
,输出为out[16]
address
能够指定了读写具体发生在哪一个寄存器上
RAM的全称为Random Access Memory,含义是,任意指定address
,都能在相同的单位时间内完成读写。这是Mux
、 DMux
下层电路的能力。
RAM的叠加过程很简单,MSB指定大区,剩下的指定具体位置,然后递归,在每个大区里面执行一样的动作。
Counter
程序计数器三个基本控制位:
reset
起始数字归0load
写入新的起始数字inc
+1输出
优先级依次降低,实现上可以反向计算结果,然后选择结果输出
下面一张图,更清晰地理解,上一时刻的状态决定当前时间输出的特性:
机器码和汇编
机器码是给CPU看的,一串01010101001, 要包含三方面的信息:
从哪来?干什么?到哪去?
- 操作数在哪里,怎么读取它? 比如操作数直接在寄存器里,或者在主存某个位置
- 要执行什么操作? 比如之前造的ALU支持6位的控制位,那么机器码至少要包含这6个控制位,ALU才能读懂要做什么事情。
- 当前指令执行完后,下一条执行什么? 没有指定,就顺序执行,否则就按照指令中的规则跳转
1 |
|
所谓寻址,是看待操作数的0101的不妨方式
- 立即数,操作数本身就是数据,实际上没有必要寻址 e.g.
LOADI R1, 67 // R1 <- 67
- 寄存器寻址,操作数是寄存器的地址,从寄存器中读取数据 e.g.
Add R2, R1, R3/ / R2 <- R1 + R3
- 直接寻址,数据在主存中,操作数就是数据在主存中的地址,到内存中读取得到数据 e.g.
LOAD R1, 67 // R1 <- M[67]
- 间接寻址,操作数是一个地址,读取其内容(主存、寄存器)后,才得到数据在内存中的地址,再从内中得到数据 e.g.
LOAD* R2, R1 // R2 <- M[R1]
所以间接寻址,就可以看作是C语言中的指针的实现
指令包含加减法、布尔操作等
跳转指根据ALU的计算结果(>0, <0 ...
)来触发跳转
汇编语言就是建立起字符串和底层01001的映射关系,方便人来书写。并且支持一定的符号解析功能。符号解析在第六章-构建汇编器,有仔细说明
制作CPU
CPU要能理解上一节的机器码,因此它的主要功能有:
- 解析机器码,转化为内部CPU的控制位
- 读取、储存操作数,因此需要内部寄存器作为CPU的组件
- 执行计算,因此需要一个ALU作为组件
- 判断是否跳转,输出下一条指令的位置,因此需要一个Counter作为组件
在Hack中,内存的操作数的读取是用A-instruction读取的,所以没有同步的问题
编写汇编器
总体上,汇编器是简单的文本处理程序,翻译为二进制格式。
只是会因为增加更多的数据类型以及支持符号解析而变得复杂一些。
本课程中的符号分为标签和变量。标签是机器码指令的地址。变量是内存地址。变量对应的具体的内存地址的值不重要,一般按照地址空间递增就好了。高级语言的变量其实也是这样,变量就是某个内存地址,变量的值,是那个地址中的内容。
流程是两次扫描。第一次解析全局标签。第二次生成机器码。
补充
课程配套简直完美。教材语言简单直接,Project环境完备操作方便反馈清晰,每个Project难度适中,对我来说刚刚好,做起来很顺畅,做完有收获有成就感。
总体脉络写的是通用的知识点,HACK本身的一些笔记和想法,放在了gitbub上备份存档
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!