一、前言
项目来源:
该项目是著名课程Nand2Tetris的课程项目,总共分12部分,从零开始构建属于自己的hack计算机。该文项目属于第五个子项目。
项目路线介绍:
在硬件部分,你将进入 01 的世界,用与非门构造出逻辑电路,并逐步搭建出一个 CPU 来运行一套课程作者定义的简易汇编代码。在软件部分,你将编写一个编译器,将作者开发的一个名为Jack的高级语言编译为可以运行在虚拟机上的字节码,然后进一步翻译为汇编代码。你还将开发一个简易的 OS,让你的计算机支持输入输出图形界面。至此,你可以用 Jack 开发一个俄罗斯方块的小游戏,将它编译为汇编代码,运行在你用与非门搭建出的 CPU 上,通过你开发的 OS 进行交互。
二、项目介绍
本章的目标是构建出一台基础计算机,一个计算机如果不看对外的拓展功能,其内部其实是运算+存储管理,所以我们需要实现的就是这台计算机的CUP和内存
目标:
- Memory:内存,包括数据内存和指令内存,屏幕内存映像和键盘内存映像
- CPU:将前面实现的寄存器、,ALU,PC程序计数器进行组装来得到一个满足Hack命令运行的CPU
- Computer:完整的一个计算机平台。运行在仿真平台上
要求:使用本章介绍的Hack——一门简单的类汇编语言,来完成对以上两个程序的编写
细节:
- 内存模块的设计非常简单,就是把我们准备好的RAM16K加进去,但是值得注意的是要保留对于SCREEN和KEYBOARD这两个模块的接口,方便后面操作相应的内存映射时,对应外设可以做出反应,这两个外设芯片是系统自带的,无需自己再实现
- CPU:CPU的芯片组建相对来说有些繁琐,可以参照下面的5.9图进行构建,而且对芯片的组装我个人的推荐是先理解Hack命令进入CPU的处理过程,再依照这个过程来链接各个部分
- Computer:在此书的思想中,任何硬件设备都可以抽象为一个芯片,而这个计算机也不例外,无非是这个芯片大了亿点
三、项目展示
1.HDL图
1.1.内存结构图
1.2.CPU结构图
1.3.Hack计算机
2.实现代码
2.1.Memory
// 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/05/Memory.hdl
/**
* The complete address space of the Hack computer's memory,
* including RAM and memory-mapped I/O.
* The chip facilitates read and write operations, as follows:
* Read: out(t) = Memory[address(t)](t)
* Write: if load(t-1) then Memory[address(t-1)](t) = in(t-1)
* In words: the chip always outputs the value stored at the memory
* location specified by address. If load==1, the in value is loaded
* into the memory location specified by address. This value becomes
* available through the out output from the next time step onward.
* Address space rules:
* Only the upper 16K+8K+1 words of the Memory chip are used.
* Access to address>0x6000 is invalid. Access to any address in
* the range 0x4000-0x5FFF results in accessing the screen memory
* map. Access to address 0x6000 results in accessing the keyboard
* memory map. The behavior in these addresses is described in the
* Screen and Keyboard chip specifications given in the book.
*/
CHIP Memory {
IN in[16], load, address[15];
OUT out[16];
PARTS:
DMux4Way(in=load,sel=address[13..14],a=RAMa,b=RAMb,c=ScreenIn,d=KeyboardIn);
Or(a=RAMa,b=RAMb,out=RAMd);
RAM16K(in=in,load=RAMd,address=address[0..13],out=RAMout);
Keyboard(out=Keyboardout);
Screen(in=in, load=ScreenIn, address=address[0..12], out=Screenout);
Mux4Way16(a=RAMout, b=RAMout, c=Screenout, d=Keyboardout, sel=address[13..14], out=out);
}
2.2.CPU
// 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/05/CPU.hdl
/**
* The Hack CPU (Central Processing unit), consisting of an ALU,
* two registers named A and D, and a program counter named PC.
* The CPU is designed to fetch and execute instructions written in
* the Hack machine language. In particular, functions as follows:
* Executes the inputted instruction according to the Hack machine
* language specification. The D and A in the language specification
* refer to CPU-resident registers, while M refers to the external
* memory location addressed by A, i.e. to Memory[A]. The inM input
* holds the value of this location. If the current instruction needs
* to write a value to M, the value is placed in outM, the address
* of the target location is placed in the addressM output, and the
* writeM control bit is asserted. (When writeM==0, any value may
* appear in outM). The outM and writeM outputs are combinational:
* they are affected instantaneously by the execution of the current
* instruction. The addressM and pc outputs are clocked: although they
* are affected by the execution of the current instruction, they commit
* to their new values only in the next time step. If reset==1 then the
* CPU jumps to address 0 (i.e. pc is set to 0 in next time step) rather
* than to the address resulting from executing the current instruction.
*/
CHIP CPU {
IN inM[16], // M value input (M = contents of RAM[A])
instruction[16], // Instruction for execution
reset; // Signals whether to re-start the current
// program (reset==1) or continue executing
// the current program (reset==0).
OUT outM[16], // M value output
writeM, // Write to M?
addressM[15], // Address in data memory (of M)
pc[15]; // address of next instruction
PARTS:
Mux16(a=instruction,b=ALUout,sel=instruction[15],out=aRegisterIn);
Nand(a=instruction[15],b=instruction[15],out=outa);
Or(a=instruction[5], b=outa, out=loadA);
ARegister(in=aRegisterIn,load=loadA,out=aout,out[0..14]=addressM);
And(a=instruction[15],b=instruction[12],out=asel);
Mux16(a=aout,b=inM,sel=asel,out=inALUa);
And(a=instruction[15],b=instruction[4],out=loadD);
DRegister(in=ALUout,load=loadD,out=inALUd);
ALU(x=inALUd, y=inALUa, zx=instruction[11], nx=instruction[10], zy=instruction[9], ny=instruction[8], f=instruction[7], no=instruction[6], out=ResultALU, zr=Zr, ng=Ng, out=ALUout,out=outM);
And(a=instruction[15],b=instruction[3],out=writeM);
Or(a=Zr,b=Ng,out=notpg);
Not(in=notpg,out=Pg);
And(a=instruction[2], b=Ng, out=NegativeJump);
And(a=instruction[1], b=Zr, out=ZeroJump);
And(a=instruction[0], b=Pg, out=PositiveJump);
Or(a=NegativeJump,b=ZeroJump,out=jumpNZ);
Or(a=jumpNZ,b=PositiveJump,out=jumpResult);
And(a=jumpResult,b=instruction[15],out=PCload);
PC(in=aout,load=PCload,inc=true,reset=reset,out[0..14]=pc);
}
2.3.Computer
// 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/05/Computer.hdl
/**
* The HACK computer, including CPU, ROM and RAM.
* When reset is 0, the program stored in the computer's ROM executes.
* When reset is 1, the execution of the program restarts.
* Thus, to start a program's execution, reset must be pushed "up" (1)
* and "down" (0). From this point onward the user is at the mercy of
* the software. In particular, depending on the program's code, the
* screen may show some output and the user may be able to interact
* with the computer via the keyboard.
*/
CHIP Computer {
IN reset;
PARTS:
ROM32K(address=romAddress,out=cpuinstr);
CPU(inM=RAMout, instruction=cpuinstr, reset=reset, writeM=RAMload, outM=CPUout, addressM=RAMaddress, pc=romAddress);
Memory(in=CPUout,load=RAMload,address=RAMaddress,out=RAMout);
}