視頻:
如果本次課程對(duì)應(yīng)的 Coursera 的視頻打不開(kāi)很魂,可以點(diǎn)擊下面鏈接
P1W5U5.6 - Project 5 Perspectives
1扎酷、馮諾依曼架構(gòu) (The Von Neumann architecture)與 哈佛架構(gòu) (The Harvard architecture)
Hack小電腦就是 哈佛架構(gòu)的
程序存儲(chǔ) ROM 和 數(shù)據(jù)存儲(chǔ) RAM 是分開(kāi)的。
取指令和取數(shù)據(jù)可以在一個(gè)周期里完成(回顧U5.2)遏匆,教學(xué)追求簡(jiǎn)單法挨,所以選擇這個(gè)谁榜。
一般用在程序不會(huì)改變的嵌入式設(shè)備里。
不過(guò)經(jīng)典的馮諾依曼架構(gòu)凡纳。
程序和數(shù)據(jù) 是在一個(gè)存儲(chǔ)空間里窃植。
需要兩個(gè)周期來(lái)完成一個(gè)指令操作,略微復(fù)雜荐糜。
這個(gè)馮諾伊曼架構(gòu)就比較通用了巷怜。
小結(jié)一下,目前為止知道:
1暴氏、晶振頻率以及DFF延塑、RAM跟時(shí)序相關(guān)的硬件實(shí)現(xiàn)電路都被課程隱藏了。
2答渔、BUS硬件實(shí)現(xiàn)被隱藏了关带。
3、屏幕刷新顯示沼撕、刷新讀取SCREEN區(qū)的功能被隱藏了豫缨。
4、KBD區(qū)循環(huán)讀端朵,以及對(duì)應(yīng)表的硬件實(shí)現(xiàn)被隱藏了好芭。
5、ROM被隱藏了冲呢,程序是寫死的舍败,一個(gè)周期內(nèi)完成取指令和取數(shù)據(jù)的哈佛結(jié)構(gòu),如何變成更通用的馮諾伊曼架構(gòu)呢敬拓?
2邻薯、有限狀態(tài)機(jī)
沒(méi)聽(tīng)懂。
3乘凸、外設(shè)
提到各種 I/O設(shè)備厕诡、提到CPU會(huì)有硬件控制來(lái)減少CPU沒(méi)必要的運(yùn)算資源、提到顯卡也是一種輔助硬件控制設(shè)備营勤,有空回顧灵嫌。
作業(yè):
CPU.hdl
Memory.hdl
Computer.hdl
1、回顧
CPU葛作、RAM寿羞、ROM 連接圖
CPU 內(nèi)部 連接圖
RAM 抽象圖
RAM 內(nèi)部圖
一、CPU.hdl
首先來(lái)完成CPU的部分玖院,這塊主要參考34菠红、P1 W5 U5.3 中央處理器 的講解。
按著U5.3的視頻
1难菌、首先處理下圖跟A Register 有關(guān)的部分途乃。
這塊兒思路,主要圍繞A Register 的寫入情況扔傅。A Register只有兩種寫入情況。
一種烫饼,如果是A指令猎塞,那么直接就是寫A寄存器
一種,如果是C指令杠纵,就要分情況荠耽,如果d1=1,就是寫A寄存器
為了方便描述 instruction 的各位比藻,我們可以按照之前P1 W4 U4.4 HACK的機(jī)器語(yǔ)言的圖表铝量,分別命名。
// instruction[15] = 1 時(shí)馏段,為 C指令
And(a=instruction[15], b=instruction[0], out=j3); // j3
And(a=instruction[15], b=instruction[1], out=j2); // j2
And(a=instruction[15], b=instruction[2], out=j1); // j1
And(a=instruction[15], b=instruction[3], out=d3); // d3
And(a=instruction[15], b=instruction[4], out=d2); // d2
And(a=instruction[15], b=instruction[5], out=d1); // d1
And(a=instruction[15], b=instruction[6], out=c6); // c6
And(a=instruction[15], b=instruction[7], out=c5); // c5
And(a=instruction[15], b=instruction[8], out=c4); // c4
And(a=instruction[15], b=instruction[9], out=c3); // c3
And(a=instruction[15], b=instruction[10], out=c2); // c2
And(a=instruction[15], b=instruction[11], out=c1); // c1
And(a=instruction[15], b=instruction[12], out=ca); // a
//instruction[13] 忽略轩拨,默認(rèn)給1
//instruction[14] 忽略,默認(rèn)給1
一種院喜,如果是A指令亡蓉,那么直接就是寫A寄存器
A指令,i[15] = 0, Mux 需要 sel=1選擇b為輸出喷舀,A寄存器需要 load置1來(lái)完成寫入
自然想出如下方案砍濒。
一種,如果是C指令硫麻,就要分情況梯影,如果d1=1,就是寫A寄存器
C指令庶香,i[15] = 1, d1 = 1(i[5]=1) 那么ALU的輸出 寫入A寄存器
這里自然想到 用一個(gè)And門 使兩個(gè) i[15] 和 i[5] 條件都滿足時(shí)觸發(fā)load甲棍。
加上之前A指令的情況也觸發(fā)load,這里就想到用Or來(lái)合并兩種輸入。
結(jié)合后如下圖感猛。
最終可以寫代碼
// A Register 部分 好理解版
Not( in = instruction[15], out = not1out );
And( a = instruction[15], b = d1, out = andout );
Mux16( a = ALUout, b = instruction, sel = not1out, out = mux1out );
Or( a = not1out, b = andout, out = or1out );
ARegister( in = mux1out, load = or1out, out = ARout );
發(fā)現(xiàn)And可以省略
// A Register 部分 簡(jiǎn)化版
Not( in = instruction[15], out = not1out );
//And( a = instruction[15], b = d1, out = andout );
Mux16( a = ALUout, b = instruction, sel = not1out, out = mux1out );
Or( a = not1out, b = d1, out = or1out );
ARegister( in = mux1out, load = or1out, out = ARout );
2七扰、然后來(lái)看和ALU相關(guān)的部分
ALU回顧
P1 W2 U2.4 ALU 算術(shù)邏輯單元
這塊沒(méi)有額外電路參與,直接按著ALU的輸入輸出寫就代碼陪白,如下
// ALU 部分
DRegister( in = ALUout,load = d2,out = DRout);
Mux16(a = ARout, b = inM, sel = ca, out = mux2out);
ALU(x = DRout, y = mux2out, zx = c1,nx = c2,zy = c3,ny = c4,f = c5,no = c6, out = ALUout, out = outM, zr = zr, ng = ng);
3颈走、最后就是PC部分了。
回顧 PC的接口:
PC 先判斷 reset
再判斷l(xiāng)oad
最后reset和load都未觸發(fā)咱士,就執(zhí)行inc
// 這里 load 和 inc 需要確認(rèn)立由。
PC(in = ARout , reset = reset, load = xxx, inc = xxx, out = PCout)
分析 ALU 的 zr、 ng
ALU出來(lái)的 zr =1 代表 ALU的運(yùn)算結(jié)果為 “等于0”
ALU出來(lái)的 ng = 1 代表 ALU的運(yùn)算結(jié)果為 “小于0”
ALU的 zr = 0 和 ng = 0序厉,代表ALU的運(yùn)算結(jié)果為 “大于0”
且zr和ng不能都為1
分析 C指令 的 j1锐膜,j2,j3
j1 代表 小于0 跳轉(zhuǎn)
j2 代表 等于0 跳轉(zhuǎn)
j3 代表 大于0 跳轉(zhuǎn)
還有一種無(wú)條件跳轉(zhuǎn)情況弛房,j1道盏、j2、j3 都為1PS:大于等于 和 小于等于 和上面情況是重復(fù)的文捶。
分析 load
load 觸發(fā)條件就是
zr和ng的情況 和 C指令的 j1j2j3跳轉(zhuǎn)條件吻合就觸發(fā)荷逞。
也就是:
滿足 zr=1,ng=0 (等于0)情況 又 滿足 是C指令 且 j2 =1,load就觸發(fā)粹排。
滿足 zr=0,ng=1 (小于0)情況 又 滿足 是C指令 且 j1 =1种远,load就觸發(fā)。
滿足 zr=0,ng=0 (大于0)情況 又 滿足 是C指令 且 j3 =1顽耳,load就觸發(fā)院促。
滿足 是C指令 且 j1j2j3 都為 1,load就觸發(fā)斧抱。
最后四種情況一“Or”就行了
分析 inc
觸發(fā)load了常拓,就沒(méi)有inc了。
要inc了辉浦,那load肯定沒(méi)觸發(fā) 弄抬。
所以inc從load取反就能得到。
PC部分代碼如下:
// PC 部分
Not(in = zr, out = notzr);
Not(in = ng, out = notng);
// ALU輸出結(jié)果 判斷
And(a = notzr, b = notng, out = positive);// zr=0宪郊, ng=0時(shí)掂恕,結(jié)果大于0
And(a = zr, b = notng, out = zero); // zr=1, ng=0 時(shí)弛槐,結(jié)果等于0
And(a = notzr, b = ng, out = negative); // zr=0懊亡,ng=1 時(shí),結(jié)果小于0
// 三種跳轉(zhuǎn)情況
And(a = j3, b = positive, out = POSjump);
And(a = j2, b = zero, out = ZRjump);
And(a = j1, b = negative, out = NEGjump);
// 無(wú)條件跳轉(zhuǎn)
And(a = j1, b = j2 ,out = and1out);
And(a = and1out , b = j3, out = UCDTjump); //uncondition jump
// 四種跳轉(zhuǎn)情況 滿足一種就跳轉(zhuǎn)
Or(a = POSjump, b = ZRjump, out = or2out);
Or(a = or2out, b = NEGjump, out = or3out);
Or(a = or3out, b = UCDTjump, out = or4out);
And(a = instruction[15], b = or4out, out = load);
// 不是跳轉(zhuǎn)乎串,那就自加
Not(in = load, out = inc);
PC(in = ARout , reset = reset, load = load, inc = inc, out[0..14] = pc);
4店枣、其它部分:(outM,writeM,addressM)
分析
outM 代表 ALU的計(jì)算結(jié)果
writeM 代表 C指令是否要將ALU的計(jì)算結(jié)果寫入RAM
addressM 代表 寫入RAM的數(shù)據(jù)存在哪里(還記得給M賦值鸯两,需要A指令參與指定哪個(gè)M闷旧,所以這塊addressM是從ARegister來(lái)的)
// 最后有三個(gè)跟RAM有關(guān)的部分
// ALU輸出的結(jié)果 是否寫入M
And(a=instruction[15], b=d3, out=writeM);
// ALU輸出的結(jié)果
//And16(a=ALUout, b=ALUout, out=outM); // 這塊直接寫在ALU里了
// ALU輸出的結(jié)果 存在哪
And16(a=ARout, b=ARout, out[0..14]=addressM); //這個(gè)也可以直接寫在ARegister里
5、最終 CPU.hdl 代碼:
測(cè)試成功
// 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:
// Put your code here:
// instruction[15] = 1 時(shí)钧唐,為 C指令
And(a=instruction[15], b=instruction[0], out=j3); // j3
And(a=instruction[15], b=instruction[1], out=j2); // j2
And(a=instruction[15], b=instruction[2], out=j1); // j1
And(a=instruction[15], b=instruction[3], out=d3); // d3
And(a=instruction[15], b=instruction[4], out=d2); // d2
And(a=instruction[15], b=instruction[5], out=d1); // d1
And(a=instruction[15], b=instruction[6], out=c6); // c6
And(a=instruction[15], b=instruction[7], out=c5); // c5
And(a=instruction[15], b=instruction[8], out=c4); // c4
And(a=instruction[15], b=instruction[9], out=c3); // c3
And(a=instruction[15], b=instruction[10], out=c2); // c2
And(a=instruction[15], b=instruction[11], out=c1); // c1
And(a=instruction[15], b=instruction[12], out=ca); // a
//instruction[13] 忽略忙灼,默認(rèn)給1
//instruction[14] 忽略,默認(rèn)給1
// A Register 部分 簡(jiǎn)化版
Not( in = instruction[15], out = not1out );
//And( a = instruction[15], b = d1, out = andout );
Mux16( a = ALUout, b = instruction, sel = not1out, out = mux1out );
Or( a = not1out, b = d1, out = or1out );
ARegister( in = mux1out, load = or1out, out = ARout );
// ALU 部分
DRegister( in = ALUout,load = d2,out = DRout);
Mux16(a = ARout, b = inM, sel = ca, out = mux2out);
ALU(x = DRout, y = mux2out, zx = c1,nx = c2,zy = c3,ny = c4,f = c5,no = c6, out = ALUout, out = outM, zr = zr, ng = ng);
// PC 部分
Not(in = zr, out = notzr);
Not(in = ng, out = notng);
// ALU輸出結(jié)果 判斷
And(a = notzr, b = notng, out = positive);// zr=0钝侠, ng=0時(shí)该园,結(jié)果大于0
And(a = zr, b = notng, out = zero); // zr=1, ng=0 時(shí)帅韧,結(jié)果等于0
And(a = notzr, b = ng, out = negative); // zr=0里初,ng=1 時(shí),結(jié)果小于0
// 三種跳轉(zhuǎn)情況
And(a = j3, b = positive, out = POSjump);
And(a = j2, b = zero, out = ZRjump);
And(a = j1, b = negative, out = NEGjump);
// 無(wú)條件跳轉(zhuǎn)
And(a = j1, b = j2 ,out = and1out);
And(a = and1out , b = j3, out = UCDTjump); //uncondition jump
// 四種跳轉(zhuǎn)情況 滿足一種就跳轉(zhuǎn)
Or(a = POSjump, b = ZRjump, out = or2out);
Or(a = or2out, b = NEGjump, out = or3out);
Or(a = or3out, b = UCDTjump, out = or4out);
And(a = instruction[15], b = or4out, out = load); //最后判斷一下是不是C指令
// 不是跳轉(zhuǎn)弱匪,那就自加
Not(in = load, out = inc);
PC(in = ARout , reset = reset, load = load, inc = inc, out[0..14] = pc);
// 最后有三個(gè)跟RAM有關(guān)的部分
// ALU輸出的結(jié)果 是否寫入M
And(a=instruction[15], b=d3, out=writeM);
// ALU輸出的結(jié)果
//And16(a=ALUout, b=ALUout, out=outM); // 這塊直接寫在ALU里了
// ALU輸出的結(jié)果 存在哪
And16(a=ARout, b=ARout, out[0..14]=addressM); //這個(gè)也可以直接寫在ARegister里
}
二、Memory.hdl
Memory 主要由 一個(gè) RAM16K璧亮,一個(gè) RAM8K萧诫,一個(gè) Register,組成枝嘶。
老師已經(jīng)提供了內(nèi)置的Screen來(lái)代替RAM8K帘饶,Keyboard來(lái)代替Register。
這里關(guān)鍵在如何根據(jù)送進(jìn)來(lái)的 15位 address群扶,來(lái)選擇不同的RAM及刻。
分析
RAM16K (16K 用 14個(gè)二進(jìn)制可以表示)
起始地址:000 0000 0000 0000 (0)
001 xxxx xxxx xxxx
010 xxxx xxxx xxxx
結(jié)束地址:011 1111 1111 1111(16383)
SCREEN8K(8K 用 13個(gè)二進(jìn)制可是表示)
起始地址:100 0000 0000 0000(16384)
結(jié)束地址:101 1111 1111 1111(24575)
KEYBOARD
單個(gè)地址:110 0000 0000 0000(24576)
對(duì)比上面看出
address[14] = 0 ,address[13] = 0
address[14] = 0 ,address[13]= 1
時(shí),都是RAM16K的地址范圍竞阐。
判斷完后缴饭,address再送入RAM16K地址為address[0..13] //保留14個(gè)二進(jìn)制地址(16k地址范圍)
address[14] = 1,address[13] = 0
時(shí),都是Screen的地址范圍骆莹。
判斷完后颗搂,address再送入Screen8K地址為address[0..12] //保留13個(gè)二進(jìn)制地址(8k地址范圍)
address[14] = 1, address[13] = 1
時(shí),就是Keyboard的地址了
address[14],address[13]
00 //選RAM
01 //選RAM
10 //選Screen
11 //選Keyboard
正好可以用一個(gè) DMux4Way來(lái)區(qū)分
DMux4Way(in = load, sel = address[13..14], a = loadR1, b = loadR2, c = loadS, d = loadK);
Or(a = loadR1, b = loadR2, out = loadR); // 00幕垦、01都是RAM16K
回顧RAM 的接口
RAM16K(in = in, load = loadR, address = address[0..13], out = outR);
Screen(in = in, load = loadS, address = address[0..12], out = outS);
Keyboard( out = outK); //沒(méi)有l(wèi)oad
最后再根據(jù)選擇的芯片丢氢,對(duì)應(yīng)選擇誰(shuí)的輸出
Mux4Way16(a = outR, b = outR, c = outS, d = outK, sel = address[13..14], out = out);
Memory完成代碼:
測(cè)試成功
PS:這里在測(cè)試的時(shí)候,調(diào)成View -> Screen模式先改,需要檢查屏幕中心的兩個(gè)橫條疚察。
// 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:
// Put your code here:
//address[14],address[13]
//00 選RAM
//01 選RAM
//10 選Screen
//11 選Keyboard
//正好可以用一個(gè) DMux4Way來(lái)區(qū)分
DMux4Way(in = load, sel = address[13..14], a = loadR1, b = loadR2, c = loadS, d = loadK);
Or(a = loadR1, b = loadR2, out = loadR); // 00、01都是RAM16K
//回顧RAM 的接口
RAM16K(in = in, load = loadR, address = address[0..13], out = outR);
Screen(in = in, load = loadS, address = address[0..12], out = outS);
Keyboard(out = outK); //沒(méi)有l(wèi)oad...
//最后再根據(jù)選擇的芯片仇奶,對(duì)應(yīng)選擇誰(shuí)的輸出
Mux4Way16(a = outR, b = outR, c = outS, d = outK, sel = address[13..14], out = out);
}
三貌嫡、Computer.hdl
最后的HACK小電腦就要組裝完成啦~
ROM老師給
CPU和Memory剛寫完,只要按下圖串起來(lái)就行了
Computer代碼
ROM32K(address=pc, out=instruction);
CPU(inM=memOut, instruction=instruction, reset=reset, outM=outM, writeM=writeM, addressM=addressM, pc=pc);
Memory(in=outM, load=writeM, address=addressM, out=memOut);
測(cè)試成功
PS:測(cè)試時(shí)需要拷貝CPU.hdl 和 Memory.hdl到同目錄。另外同目錄下除了.tst .cmp文件衅枫,還需要三個(gè).hack文件(一個(gè)測(cè)試ADD嫁艇,一個(gè)測(cè)試MAX,一個(gè)測(cè)試屏幕畫方塊)弦撩。
完整代碼:
// 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:
// Put your code here:
ROM32K(address=pc, out=instruction);
CPU(inM=memOut, instruction=instruction, reset=reset, outM=outM, writeM=writeM, addressM=addressM, pc=pc);
Memory(in=outM, load=writeM, address=addressM, out=memOut);
}
WOW步咪,至此我們小電腦就組裝完成了。下周就是造出Hack小電腦的匯編器了益楼。這樣我們就不用給小電腦直接輸入0101111100001010了猾漫。用匯編器翻譯一下就好啦。