课程地址
我的项目地址
教材:《计算机系统要素:从零开始构建现代计算机》
Part I
第一章 ~ 第六章
Week 1
1.1 背景知识
1.1.1 布尔代数
布尔型数值:0或1,true或false 等等。
布尔型函数:输入输出数值均为布尔型数值的函数。
真值表:描述布尔型函数的方法,通过枚举出函数所有的输入组合,并写出对应的输出。
布尔算子:基本的有 And、Or、Not。
布尔表达式:除了真值表,对布尔函数的另一种描述方法。
1.1.2 门
用来实现布尔函数的物理设备。如下图所示:
1.1.3 硬件描述语言(HDL)
用于描述芯片的结构,并使用计算机进行仿真测试。
1.1.4 硬件仿真
本课程提供了一个免费的硬件仿真软件,如下图所示:
1.2 门电路介绍
1.2.1 Nand门
所有其它门都可以通过Nand门构建。
1.2.2 基本逻辑门
Not
And
Or
Xor
Multiplexor(选择器)
三输入电路门,其中一个为选择位,选择另外两个输入变量中的一个为输出变量,另外两个称为数据位。
Demultiplexor
1.2.3 多位基本门
通用计算机的设计要求其可以在多为数据线上运行。如64位计算机要求在64位总线上按位进行And函数的计算。
1.2.4 多通道逻辑门
很多2位(即接收两个输入)的逻辑门能够推广到多位。如Or可以设计成8通道的,即接收8位的Or门。
芯片名:Or8Way
输入:in[8]
输出:out
功能:out=Or(in[0], in[1], ..., in[7])
1.3 Project 1
使用HDL语言实现上述的门电路,并使用Hardware Simulator软件模拟,通过测试。
Week 2
2.1 背景知识
二进制数与十进制数的换算。
二进制数加法。
有符号二进制数如何表示。
计算机中的减法运算。
2.2 加法器和算术逻辑单元(ALU)
半加器:两位加法 a + b。
全加器:三位加法 a + b + c。
加法器:两个n位加法a[n] + b[n]。
增量器:数字+1。
ALU:计算一组固定的函数
f
i
(
x
,
y
)
f_i(x, y)
fi(x,y),其中x和y为16位的输入,
f
i
f_i
fi一个函数表中的函数,函数表由18个固定函数组成。我们通过6个控制位来告诉ALU用哪一个函数来进行计算。控制位的组合到函数对应的编码是进行精心设计过的,设置不同的控制位组合,会产生不同的输出结果,该结果就对应一种函数处理后的结果。
2.3 Project 2
实现上述加法器和算术逻辑单元,可以使用Project 1中实现的门。
Week 3
3.1 背景知识
时钟:提供连续的交变序列,其在高低电平之间交替变换。两个相邻的的时间间隙称为时钟的周期,一个时钟周期为一个离散的时间单元。
数据触发器(DFF):根据时钟信号的交变,能够实现基于时间的行为,这里为将前一个时间周期的输入值作为当前周期的输出值 o u t ( t ) = i n ( t − 1 ) out(t)=in(t-1) out(t)=in(t−1)。
寄存器:具有记忆功能的设备。1-bit 位寄存器结构如下图所示,可以通过1bit位寄存器阵列来构建多bit位寄存器。
内存:通过将很多寄存器堆叠起来构建随机存取内存(RAM,Random Access Memory)。
计数器:状态为一个整数,每过一个周期整数加1。即 o u t ( t ) = o u t ( t − 1 ) + 1 out(t)=out(t-1)+1 out(t)=out(t−1)+1。
时间问题:DFF保证了输出的变化仅仅发生在时钟周期的转换节点上,而不在转换周期内。通过离散化时间,来同步计算机系统状态,确保在下一个周期开始前,使得电信号达到稳定状态,从而得到想要的结果。
3.2 时序芯片
数据触发器(DFF)
寄存器(register)
存储(RAM)
计数器(counter)
3.3 Project 3
实现上述时序芯片,DFF除外(已经实现)。包括1-bit寄存器、8-bit寄存器、RAM8、RAM64、计数器等等。
Week 4
4.1 背景知识
4.1.1 语言
机器语言:可以被看成一种约定的形式,它利用处理器和寄存器来操作内存。机器语言是一系列的编码指令,由n-bit的二进制来表示。
汇编语言:由于二进制表示不适合人类阅读,通常使用助记符来代替表示和编写程序,这种符号表示也称为汇编语言(assembly language),将汇编语言翻译成二进制码的程序称为汇编编译器(assembler)。
4.1.2 命令
算术操作和逻辑操作:计算机需要执行的基本算术操作(如加法减法),以及基本的布尔操作(如取反移位)。
内存访问:第一类为算术操作和逻辑操作,允许操控寄存器和内存单元。第二类为load和store命令,用来在寄存器和内存之间传递数据。
控制流程:命令除了线性执行,还包括分支。如循环、有条件执行(if-else)、跳转等等。
4.2 Hack机器语言
4.2.1 Hack计算机概述
Hack架构:Hack是一个基于冯诺依曼架构的16-bit计算机,由一个CPU、两个独立的内存模块(指令内存ROM和数据内存RAM),以及两个I/O设备(显示器和键盘)组成。
Hack语言:包含了两种指令,一种是地址指令(A-指令),一种是计算指令(C-指令)。
4.2.2 A-指令
语法:@value
语义:将A寄存器中的值设为value
,可以说RAM[A]
寄存器被选择了,也可以用于跳转指令中下一个指令代号。
举例:
// set RAM[100] to -1
@100 // A=100
M=-1 // RAM[100]=-1
4.2.3 C-指令
语法:dest = comp; jump
语义:
- 计算方法:
comp
- 计算后的值的存储位置:
dest
- 下一步的指令:
jump
举例:
// Exa1. set RAM[300] to the value of the D register minus 1
@300 // A=300
M = D - 1 // RAM[300]=D-1
// Exa2. if (D-1==0) jump to the instruction stored in ROM[56]
@56 // A=56
D - 1;JEQ // if (D-1 == 0) goto command 56
汇编语法和二进制语法间的映射关系如下图所示:
4.2.4 I/O处理
屏幕:256X512像素的黑白屏幕。内容由基地址为16384的8K内存表示,因此屏幕的第r行,第c列的像素映射到位置为RAM[16384+r*32+c/16]的字的c%16位。
键盘:基地址为24576的单字内存。当敲击一个键,键盘上的键对应的16-bit ASCII码值会出现在RAM[24576]寄存器上。
4.3 Hack编程
在Hack计算机上使用low-level语言进行编程,对于一些功能的实现介绍。
- 使用寄存器和内存:
@R0
,R
作为标识符来说明访问寄存器。 - 分支:使用
Jump
语句,跳转的位置用@LABEL
和(LABEL)
指定。LABEL
标识符编译时遵循以下原则:- 标识符不会被编译。
@LABEL
将被编译成@n
,n
为(LABEL)
标识符接下来的指令地址。
- 变量:
@variable
,将被编译成@n
,遵循以下原则:- 如果
variable
是没有被预定义的字符,那就视为变量。 n
从RAM[16]地址逐渐累加。
- 如果
- 指针:由RAM地址表示。
- I/O设备:
@SCREEN
和@KBD
分别表示屏幕和键盘的起始地址。
4.4 Project 4
4.4.1 乘法 R[2] = R[0] * R[1]
// pseudo code
temp = R[1]
IF temp > 0
R[2] += R[0]
temp -= 1
GOTO LOOP
ELSE
GOTO STOP
STOP
4.4.2 键盘控制屏幕显示
// 当有任意按键被按下时,屏幕全部显示黑色,否则显示白色。
// pseudo code
LOOP
IF KBD != 0:
// black
addr = SCREEN
i = 0
LOOPA
IF i < 8192:
R[addr] = -1
addr += 1
i += 1
GOTO LOOPA
ELSE:
// white
addr = SCREEN
i = 0
LOOPB
IF i < 8192:
R[addr] = 0
addr += 1
i += 1
GOTO LOOPB
GOTO LOOP
Week 5
5.1 背景知识
冯诺依曼结构
内存:包含数据内存和指令内存。
CPU:负责执行被加载到指令内存中的指令。主要通过ALU、寄存器和控制单元来执行这些任务。
CPU操作可以被描述成一个重复的循环(fetch-execute cycle):从内存中取一条指令,解码执行,再取下一条指令,如此循环往复。
寄存器:数据寄存器、寻址寄存器和程序计数寄存器。
I/O:内存中开辟独立的内存区域作为I/O设备的内存映像。对于输入设备,I/O映像能连续不断的反映设备的物理状态;对于输出设备,内存映像连续地驱动设备地物理状态。
5.2 Hack计算机硬件架构
5.2.1 Hack CPU架构
输入
instruction[16]:用于执行的命令,从指令内存中读取。
inM[16]:输入数据,从数据内存中读取。
reset:重新运行当前程序,0 or 1。
输出
outM[16]:输出值。
writeM:是否写入值,0 or 1。
addressM[15]:M在数据内存中的地址。
PC[15]:程序计数器,储存下一次运行的指令地址。
ALU
输入:来自两个方向。
- D-register
- A-register / inM
输出:流去三个方向,输出的register选择由解码的instruction中的dest决定。
- D-register
- A-register
- outM
控制位C’s
由解码的instruction中的comp决定。
5.2.2 Hack计算机架构总览
ROM32K:指令内存,类似于读取光盘,利用CPU仿真从外界读取程序存储在内。
Memory:数据内存,空间被划分为RAM、Screen和Kerboard三个内存区域。
外界接口:reset、I/O设备(屏幕和键盘)。
5.3 Project 5
5.3.1 Memory
由RAM16K、Screen和Keyboard三个芯片组成,这些芯片的实现都内置在仿真软件中。out为指定输入的address的内存值。如果load为1,则将 in 值加载到 address 指定的内存位置。
5.3.2 CPU
主要是不同控制位c根据construction如何设计,以及针对指令位的不同操作,比较复杂,直接CV大法。
5.3.3 Computer
将ROM32K、Memory和CPU连接在一起即可。
Week 6
6.1 一般的汇编编译器
将符号型(symbolic)语言翻译成二进制型(binary)语言。符号通常有两个用途:1)变量,自动分配地址内存;2)标签,标注不同位置。汇编编译通常包含以下几个任务:
- 解析出符号命令所在的域。
- 对于每个域,产生对应的机器语言。
- 对于内存单元的数字地址来替换所有的符号引用(符号解析)。
- 将二进制编码汇编成完整的机器指令。
6.2 Hack汇编编译器
文件名称:.asm
为符号语言文件,.hack
为二进制代码文件。
指令:A-指令@value
;C-指令dest=comp;jump
。
符号:
- 预定义符号:如R0-R15、SCREEN等。
- 标签符号
(Label)
:伪命令来指代下一条命令在指令内存中的位置。 - 变量符号:任何符号,如果不是预定义符号,也不是标签符号,那就当作是变量符号,将它映射到从16开始的内存地址。
有符号汇编编译器实现流程:
- 使用Table存储(符号,数值)对,一行一行读取
.asm
文件。。 - 初始化预定义符号。
- 遍历指令文件,加载标签符号
(xxx)
到Table中。 - 再遍历指令文件:
- 如果是A-指令
@symbol
,查看是否在Table中,如果没有则当作变量符号加载到Table中。 - 如果是C-指令,将指令翻译成对应的二进制格式。
- 如果是A-指令
- 将翻译的二进制指令到
.hack
输出文件中。
6.3 Project 6
用高级语言Java实现Hack汇编编译器。
Usage: java Assembler xxx.asm
包含的模块
Parser
:读取汇编语言并解析。
Code
:将汇编语言助记符翻译成二进制码。
SymbolTable
:存储符号标签和数字地址对,使用Java自带的Map类即可。