# From Nand to Tetris 學習紀錄 ## Unit 1的一些問題 ### Unit 1.6的題目2: :::info 假設有顆課程中未提及的晶片Example16: IN c, Bus1[16], Bus2[16]; OUT out, out2[16]; 試問以下哪幾行HDL可有效執行Example16? (A) Add16(a=Bus1[0..15], b=Bus2[0..15], out=out2[0..14]); (B) Add16(a=Bus1[0..15], b=Bus2[0..15], out[0..14]=out2[0..14]); (C) Add16(a=true, b=false, out=out2); (D) Add16(a=c, b=Bus2[0..15], out=out2); (E) And(a=c, b=Bus2[7], out=out); ::: 答: (A)與(B)非常相似,不過差異是在output的寫法,(A)將16-bit的運算結果硬塞進15-bit,而(B)是將運算結果的前十五個bit放入15-bit,前者的長度錯誤所以不可行,後者則可。 在運算中是允許使用true(代表1111111111111111)與false(代表0000000000000000),output沒有錯誤,(C)正確。 (D)與(E)也有幾分相似,不過(D)在16位元運算中不可單獨使用1位元輸入,而E則是皆為單位元做Add,前者錯誤,後者正確。 最終答案為(B)(C)(E) > 補充: > 在正常HDL語法中,(A)或(D)必須使用補0或加入Z才能正常運作 > 拿(A)的out為例,正確的輸出應該是out={0,out2[0..14]} > 補0的意思是把其中一條拉去插地板上,而Z則是加入一個超大電阻 > 若不這樣設計,(A)的output會進入Metastable,並會輸出錯誤值 > 此時若將此值拿去做其他運算結果也會出錯,一路連鎖下去這個晶片就會報銷 > 所以這個動作非常必要 > 另外補0是可以按照使用情況不同去擺在不同的地方,不一定要擺在尾端 ## Project 01: 從Nand開始做出以下15種邏輯閘 由之前的課程可以知道Nand可以做出Not,Or,And三個邏輯閘,而這三個邏輯閘就可以做任何我們想要的邏輯閘,就先照著指示把這三個做出來吧。 ### Not Gate:當輸入值為0,輸出1,反之輸出0 not (x)= x nand x :::spoiler HDL ``` CHIP Not { IN in; OUT out; PARTS: Nand (a=in, b=in, out=out); } ``` ::: :::spoiler 輸出結果 ``` | in | out | | 0 | 1 | | 1 | 0 | ``` ::: ### And Gate:當二輸入值皆為1時,輸出1,其餘輸出0 (x and y)=not(x nand y) 而not因為我前面有用nand定義過了所以不再用nand寫,後面的邏輯閘中如果有遇到前面已經定義過的邏輯閘也不會再次定義 :::spoiler HDL ``` CHIP And { IN a, b; OUT out; PARTS: Nand (a=a, b=b, out=aNANDb); Not (in=aNANDb, out=out); } ``` ::: :::spoiler 輸出結果 ``` | a | b | out | | 0 | 0 | 0 | | 0 | 1 | 0 | | 1 | 0 | 0 | | 1 | 1 | 1 | ``` ::: ### Or Gate:當二輸入值皆為0時,輸出0,其餘輸出1 依笛摩根定律:非(非a且非b)=a或b (x or y)=not(not(a) and not(b)) :::spoiler HDL ``` CHIP Or { IN a, b; OUT out; PARTS: Not (in=a, out=NOTa); Not (in=b, out=NOTb); And (a=NOTa, b=NOTb, out=NOTaANDNOTb); Not(in=NOTaANDNOTb, out=out); } ``` ::: :::spoiler 輸出結果 ``` | a | b | out | | 0 | 0 | 0 | | 0 | 1 | 1 | | 1 | 0 | 1 | | 1 | 1 | 1 | ``` ::: ### Xor Gate:當二輸入值相異時,輸出,反之輸出0 這裡可以用前面教的方法,把輸出為1的算式先寫出來,再用or串起來就好。 (x xor y)=(not(a) and b)or(a and not(b)) :::spoiler HDL ``` CHIP Xor { IN a, b; OUT out; PARTS: Not (in=a, out=NOTa); Not (in=b, out=NOTb); And (a=NOTa, b=b, out=NOTaANDb); And (a=a, b=NOTb, out=aANDNOTb); Or (a=NOTaANDb, b=aANDNOTb ,out=out); } ``` ::: :::spoiler 輸出結果 ``` | a | b | out | | 0 | 0 | 0 | | 0 | 1 | 1 | | 1 | 0 | 1 | | 1 | 1 | 0 | ``` ::: > 補充: > 在電路中應盡量避免使用Xor > 一是電路而言他很慢 > 二是他的占用面積很大 > 三是對人的邏輯很容易出錯 > Xor會以0110 1001 1001 0110...的方式迴圈,因為這個緣故換位就是一個典型的Xor應用 > 另外Xor有自反律,p xor q xor q = p xor 0 = p > 所以使用Xor時要非常小心,他可能在你忽略的地方忽然變回本來的數字 > 補充的補充: > 以上換位的作法在軟體中是最快的,可是在硬體中最慢,因為wire可以直接反接就好 > 這也是軟體跟硬體的差異所在 ### Mux Gate:當sel=0,輸出a,反之輸出b (a mux b)=(a and not(sel)) or (b and sel) Mux是以sel來決定要輸出a或b,sel與not sel勢必是一個0一個1,這樣子寫就只會留下其中一個的值 :::spoiler HDL ``` CHIP Mux { IN a, b, sel; OUT out; PARTS: Not (in=sel, out=NOTs); And (a=a, b=NOTs, out=aANDNOTs); And (a=b, b=sel, out=bANDs); Or (a=aANDNOTs, b=bANDs, out=out); } ``` ::: :::spoiler 輸出結果 ``` | a | b | sel | out | | 0 | 0 | 0 | 0 | | 0 | 0 | 1 | 0 | | 0 | 1 | 0 | 0 | | 0 | 1 | 1 | 1 | | 1 | 0 | 0 | 1 | | 1 | 0 | 1 | 0 | | 1 | 1 | 0 | 1 | | 1 | 1 | 1 | 1 | ``` ::: ### DMux Gate:當sel=0,輸出{input,0},反之輸出{0,input} DMux (in),a=(in add not(sel)), b=(in add sel) 基本上就Mux倒過來寫啦 :::spoiler HDL ``` CHIP DMux { IN in, sel; OUT a, b; PARTS: Not (in=sel, out=NOTs); And (a=in, b=NOTs, out=a); And (a=in, b=sel, out=b); } ``` ::: :::spoiler 輸出結果 ``` | in | sel | a | b | | 0 | 0 | 0 | 0 | | 0 | 1 | 0 | 0 | | 1 | 0 | 1 | 0 | | 1 | 1 | 0 | 1 | ``` ::: ### Not16 Gate 要進到16bit的邏輯閘了 這裡就1一個bit一個bit給他not下去就對了 :::spoiler HDL ``` CHIP Not16 { IN in[16]; OUT out[16]; PARTS: Not (in=in[0], out=out[0]); Not (in=in[1], out=out[1]); Not (in=in[2], out=out[2]); Not (in=in[3], out=out[3]); Not (in=in[4], out=out[4]); Not (in=in[5], out=out[5]); Not (in=in[6], out=out[6]); Not (in=in[7], out=out[7]); Not (in=in[8], out=out[8]); Not (in=in[9], out=out[9]); Not (in=in[10], out=out[10]); Not (in=in[11], out=out[11]); Not (in=in[12], out=out[12]); Not (in=in[13], out=out[13]); Not (in=in[14], out=out[14]); Not (in=in[15], out=out[15]); } ``` ::: :::spoiler 輸出結果 ``` | in | out | | 0000000000000000 | 1111111111111111 | | 1111111111111111 | 0000000000000000 | | 1010101010101010 | 0101010101010101 | | 0011110011000011 | 1100001100111100 | | 0001001000110100 | 1110110111001011 | ``` ::: ### And16 Gate 跟Not16一樣如法炮製 :::spoiler HDL ``` CHIP And16 { IN a[16], b[16]; OUT out[16]; PARTS: And (a=a[0], b=b[0], out=out[0]); And (a=a[1], b=b[1], out=out[1]); And (a=a[2], b=b[2], out=out[2]); And (a=a[3], b=b[3], out=out[3]); And (a=a[4], b=b[4], out=out[4]); And (a=a[5], b=b[5], out=out[5]); And (a=a[6], b=b[6], out=out[6]); And (a=a[7], b=b[7], out=out[7]); And (a=a[8], b=b[8], out=out[8]); And (a=a[9], b=b[9], out=out[9]); And (a=a[10], b=b[10], out=out[10]); And (a=a[11], b=b[11], out=out[11]); And (a=a[12], b=b[12], out=out[12]); And (a=a[13], b=b[13], out=out[13]); And (a=a[14], b=b[14], out=out[14]); And (a=a[15], b=b[15], out=out[15]); } ``` ::: :::spoiler 輸出結果 ``` | a | b | out | | 0000000000000000 | 0000000000000000 | 0000000000000000 | | 0000000000000000 | 1111111111111111 | 0000000000000000 | | 1111111111111111 | 1111111111111111 | 1111111111111111 | | 1010101010101010 | 0101010101010101 | 0000000000000000 | | 0011110011000011 | 0000111111110000 | 0000110011000000 | | 0001001000110100 | 1001100001110110 | 0001000000110100 | ``` ::: ### Or16 Gate 我這次做起來不用那麼辛苦,把上面的複製起來And改成Or就好( :::spoiler HDL ``` CHIP Or16 { IN a[16], b[16]; OUT out[16]; PARTS: Or (a=a[0], b=b[0], out=out[0]); Or (a=a[1], b=b[1], out=out[1]); Or (a=a[2], b=b[2], out=out[2]); Or (a=a[3], b=b[3], out=out[3]); Or (a=a[4], b=b[4], out=out[4]); Or (a=a[5], b=b[5], out=out[5]); Or (a=a[6], b=b[6], out=out[6]); Or (a=a[7], b=b[7], out=out[7]); Or (a=a[8], b=b[8], out=out[8]); Or (a=a[9], b=b[9], out=out[9]); Or (a=a[10], b=b[10], out=out[10]); Or (a=a[11], b=b[11], out=out[11]); Or (a=a[12], b=b[12], out=out[12]); Or (a=a[13], b=b[13], out=out[13]); Or (a=a[14], b=b[14], out=out[14]); Or (a=a[15], b=b[15], out=out[15]); } ``` ::: :::spoiler 輸出結果 ``` | a | b | out | | 0000000000000000 | 0000000000000000 | 0000000000000000 | | 0000000000000000 | 1111111111111111 | 1111111111111111 | | 1111111111111111 | 1111111111111111 | 1111111111111111 | | 1010101010101010 | 0101010101010101 | 1111111111111111 | | 0011110011000011 | 0000111111110000 | 0011111111110011 | | 0001001000110100 | 1001100001110110 | 1001101001110110 | ``` ::: ### Mux16 Gate 一樣一個一個給他Mux,不過這次多一個sel的值這樣 :::spoiler HDL ``` CHIP Mux16 { IN a[16], b[16], sel; OUT out[16]; PARTS: Mux (a=a[0], b=b[0], sel=sel, out=out[0]); Mux (a=a[1], b=b[1], sel=sel, out=out[1]); Mux (a=a[2], b=b[2], sel=sel, out=out[2]); Mux (a=a[3], b=b[3], sel=sel, out=out[3]); Mux (a=a[4], b=b[4], sel=sel, out=out[4]); Mux (a=a[5], b=b[5], sel=sel, out=out[5]); Mux (a=a[6], b=b[6], sel=sel, out=out[6]); Mux (a=a[7], b=b[7], sel=sel, out=out[7]); Mux (a=a[8], b=b[8], sel=sel, out=out[8]); Mux (a=a[9], b=b[9], sel=sel, out=out[9]); Mux (a=a[10], b=b[10], sel=sel, out=out[10]); Mux (a=a[11], b=b[11], sel=sel, out=out[11]); Mux (a=a[12], b=b[12], sel=sel, out=out[12]); Mux (a=a[13], b=b[13], sel=sel, out=out[13]); Mux (a=a[14], b=b[14], sel=sel, out=out[14]); Mux (a=a[15], b=b[15], sel=sel, out=out[15]); } ``` ::: :::spoiler 輸出結果 ``` | a | b | sel | out | | 0000000000000000 | 0000000000000000 | 0 | 0000000000000000 | | 0000000000000000 | 0000000000000000 | 1 | 0000000000000000 | | 0000000000000000 | 0001001000110100 | 0 | 0000000000000000 | | 0000000000000000 | 0001001000110100 | 1 | 0001001000110100 | | 1001100001110110 | 0000000000000000 | 0 | 1001100001110110 | | 1001100001110110 | 0000000000000000 | 1 | 0000000000000000 | | 1010101010101010 | 0101010101010101 | 0 | 1010101010101010 | | 1010101010101010 | 0101010101010101 | 1 | 0101010101010101 | ``` ::: ### Or8Way Gate 前面有學到(a or b or c)=((a or b) or c) 就用這個方式疊上or 8次的值 :::spoiler HDL ``` CHIP Or8Way { IN in[8]; OUT out; PARTS: Or (a=in[0], b=in[1], out=out1); Or (a=out1, b=in[2], out=out2); Or (a=out2, b=in[3], out=out3); Or (a=out3, b=in[4], out=out4); Or (a=out4, b=in[5], out=out5); Or (a=out5, b=in[6], out=out6); Or (a=out6, b=in[7], out=out); } ``` ::: :::spoiler 輸出結果 ``` | in | out | | 00000000 | 0 | | 11111111 | 1 | | 00010000 | 1 | | 00000001 | 1 | | 00100110 | 1 | ``` ::: :::info 思考: 這個Gate其實可以有兩種排法,一種是一個一個疊Or,另一個是兩兩相Or後運算結果再去互相Or計算到剩一個值,試問兩種做法的區別? ::: 一個一個疊的速度會比較慢,因為一次只能開關一個gate,反觀兩兩相Or的樹狀比較快因為他能一次開關最多4個gate 可是gate翻轉就計算為一次耗電,所以一個一個疊會比較省電,而樹狀會比較耗電 > 補充: > 每個gate間會有傳播延遲,每經過一個gate速度就會變慢 傳播延遲nand最短,xor最長 ### Mux4Way16 Gate 4路Mux的做法其實有點像2路的 sel的個位以0輸出a,c、以1輸出b,d,又用十位把a,b以0分成一組,c,d以1分成一組 那就可以把a,b分成一組做一次Mux、c,d分成一組做Mux,做出來的兩個值再Mux一遍就好 :::spoiler HDL ``` CHIP Mux4Way16 { IN a[16], b[16], c[16], d[16], sel[2]; OUT out[16]; PARTS: Mux16 (a=a, b=b, sel=sel[0], out=MUXab); Mux16 (a=c, b=d, sel=sel[0], out=MUXcd); Mux16 (a=MUXab, b=MUXcd, sel=sel[1], out=out); } ``` ::: :::spoiler 輸出結果 ``` | a | b | c | d | sel | out | | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 00 | 0000000000000000 | | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 01 | 0000000000000000 | | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 10 | 0000000000000000 | | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 11 | 0000000000000000 | | 0001001000110100 | 1001100001110110 | 1010101010101010 | 0101010101010101 | 00 | 0001001000110100 | | 0001001000110100 | 1001100001110110 | 1010101010101010 | 0101010101010101 | 01 | 1001100001110110 | | 0001001000110100 | 1001100001110110 | 1010101010101010 | 0101010101010101 | 10 | 1010101010101010 | | 0001001000110100 | 1001100001110110 | 1010101010101010 | 0101010101010101 | 11 | 0101010101010101 | ``` ::: ### Mux8Way16 Gate 用跟上面一樣的分組方式,不過是把LSB與LSB+1分成一組(00,01,10,11)做Mux,再用MSB分成一組做Mux :::spoiler HDL ``` CHIP Mux8Way16 { IN a[16], b[16], c[16], d[16], e[16], f[16], g[16], h[16], sel[3]; OUT out[16]; PARTS: Mux4Way16 (a=a, b=b, c=c, d=d, sel=sel[0..1], out=MUXad); Mux4Way16 (a=e, b=f, c=g, d=h, sel=sel[0..1], out=MUXeh); Mux16 (a=MUXad, b=MUXeh, sel=sel[2], out=out); } ``` ::: :::spoiler 輸出結果 ``` | a | b | c | d | e | f | g | h | sel | out | | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 000 | 0000000000000000 | | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 001 | 0000000000000000 | | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 010 | 0000000000000000 | | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 011 | 0000000000000000 | | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 100 | 0000000000000000 | | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 101 | 0000000000000000 | | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 110 | 0000000000000000 | | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 0000000000000000 | 111 | 0000000000000000 | | 0001001000110100 | 0010001101000101 | 0011010001010110 | 0100010101100111 | 0101011001111000 | 0110011110001001 | 0111100010011010 | 1000100110101011 | 000 | 0001001000110100 | | 0001001000110100 | 0010001101000101 | 0011010001010110 | 0100010101100111 | 0101011001111000 | 0110011110001001 | 0111100010011010 | 1000100110101011 | 001 | 0010001101000101 | | 0001001000110100 | 0010001101000101 | 0011010001010110 | 0100010101100111 | 0101011001111000 | 0110011110001001 | 0111100010011010 | 1000100110101011 | 010 | 0011010001010110 | | 0001001000110100 | 0010001101000101 | 0011010001010110 | 0100010101100111 | 0101011001111000 | 0110011110001001 | 0111100010011010 | 1000100110101011 | 011 | 0100010101100111 | | 0001001000110100 | 0010001101000101 | 0011010001010110 | 0100010101100111 | 0101011001111000 | 0110011110001001 | 0111100010011010 | 1000100110101011 | 100 | 0101011001111000 | | 0001001000110100 | 0010001101000101 | 0011010001010110 | 0100010101100111 | 0101011001111000 | 0110011110001001 | 0111100010011010 | 1000100110101011 | 101 | 0110011110001001 | | 0001001000110100 | 0010001101000101 | 0011010001010110 | 0100010101100111 | 0101011001111000 | 0110011110001001 | 0111100010011010 | 1000100110101011 | 110 | 0111100010011010 | | 0001001000110100 | 0010001101000101 | 0011010001010110 | 0100010101100111 | 0101011001111000 | 0110011110001001 | 0111100010011010 | 1000100110101011 | 111 | 1000100110101011 | ``` ::: ### DMux4Way Gate 就把Mux4Way反著做,Mux4Way先把(a,b),(c,d)這兩組用sel[0]做Mux,再將MUXab,MUXcd用sel[1]做Mux DMux的時候反過來用sel[1]把in拆成MUXab,MUXcd兩個值,再把這兩個值拆成a,b,c,d :::spoiler HDL ``` CHIP DMux4Way { IN in, sel[2]; OUT a, b, c, d; PARTS: DMux (in=in, sel=sel[1], a=MUXab, b=MUXcd); DMux (in=MUXab, sel=sel[0], a=a, b=b); DMux (in=MUXcd, sel=sel[0], a=c, b=d); } ``` ::: :::spoiler 輸出結果 ``` | in | sel | a | b | c | d | | 0 | 00 | 0 | 0 | 0 | 0 | | 0 | 01 | 0 | 0 | 0 | 0 | | 0 | 10 | 0 | 0 | 0 | 0 | | 0 | 11 | 0 | 0 | 0 | 0 | | 1 | 00 | 1 | 0 | 0 | 0 | | 1 | 01 | 0 | 1 | 0 | 0 | | 1 | 10 | 0 | 0 | 1 | 0 | | 1 | 11 | 0 | 0 | 0 | 1 | ``` ::: ### DMux8Way Gate 怎麼合的就怎麼拆 :::spoiler HDL ``` CHIP DMux8Way { IN in, sel[3]; OUT a, b, c, d, e, f, g, h; PARTS: DMux (in=in, sel=sel[2], a=MUXad, b=MUXeg); DMux4Way (in=MUXad, sel=sel[0..1], a=a, b=b, c=c, d=d); DMux4Way (in=MUXeg, sel=sel[0..1], a=e, b=f, c=g, d=h); } ``` ::: :::spoiler 輸出結果 ``` | in | sel | a | b | c | d | e | f | g | h | | 0 | 000 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | | 0 | 001 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | | 0 | 010 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | | 0 | 011 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | | 0 | 100 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | | 0 | 101 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | | 0 | 110 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | | 0 | 111 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | | 1 | 000 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | | 1 | 001 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | | 1 | 010 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | | 1 | 011 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | | 1 | 100 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | | 1 | 101 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | | 1 | 110 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | | 1 | 111 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | ``` ::: ## Project 2 ### Half Adder 先處理sum,0+0與1+1計算的數的最低位皆是0,0+1與1+0皆是1,剛好符合xor的定義,用xor處理就好了 而carry,只有1+1會進1,剛好符合and的定義,就用And處理吧 :::spoiler HDL ``` CHIP HalfAdder { IN a, b; // 1-bit inputs OUT sum, // Right bit of a + b carry; // Left bit of a + b PARTS: Xor (a=a, b=b, out=sum); And (a=a, b=b, out=carry); } ``` ::: :::spoiler 輸出結果 ``` | a | b | sum | carry | | 0 | 0 | 0 | 0 | | 0 | 1 | 1 | 0 | | 1 | 0 | 1 | 0 | | 1 | 1 | 0 | 1 | ``` ::: ### Full Adder 實際上Full Adder就是做兩次Half Adder~~畢竟他就是一半嘛~~ sum的部分就是一個一個疊,先做ab再拿結果去跟c做 carry的部分也是一樣,不過carry只會有1個值所以要拿去or :::spoiler HDL ``` CHIP FullAdder { IN a, b, c; // 1-bit inputs OUT sum, // Right bit of a + b + c carry; // Left bit of a + b + c PARTS: HalfAdder(a=a,b=b,sum=sumab,carry=carryab); HalfAdder(a=c,b=sumab,sum=sum,carry=carrycab); Or(a=carryab,b=carrycab,out=carry); } ``` ::: :::spoiler 輸出結果 ``` | a | b | c | sum | carry | | 0 | 0 | 0 | 0 | 0 | | 0 | 0 | 1 | 1 | 0 | | 0 | 1 | 0 | 1 | 0 | | 0 | 1 | 1 | 0 | 1 | | 1 | 0 | 0 | 1 | 0 | | 1 | 0 | 1 | 0 | 1 | | 1 | 1 | 0 | 0 | 1 | | 1 | 1 | 1 | 1 | 1 | ``` ::: ### Add16 LSB沒有進位問題用Half Adder就好,LSB+1後到MSB全部都有可能有進位問題,所以用Full Adder處理 另外課程前面提到溢出值不會計入,所以就不取最後的carry值 :::spoiler HDL ``` CHIP Add16 { IN a[16], b[16]; OUT out[16]; PARTS: HalfAdder(a=a[0] ,b=b[0] ,sum=out[0] , carry=carry1); FullAdder(a=a[1] ,b=b[1] ,c=carry1 ,sum=out[1] ,carry=carry2); FullAdder(a=a[2] ,b=b[2] ,c=carry2 ,sum=out[2] ,carry=carry3); FullAdder(a=a[3] ,b=b[3] ,c=carry3 ,sum=out[3] ,carry=carry4); FullAdder(a=a[4] ,b=b[4] ,c=carry4 ,sum=out[4] ,carry=carry5); FullAdder(a=a[5] ,b=b[5] ,c=carry5 ,sum=out[5] ,carry=carry6); FullAdder(a=a[6] ,b=b[6] ,c=carry6 ,sum=out[6] ,carry=carry7); FullAdder(a=a[7] ,b=b[7] ,c=carry7 ,sum=out[7] ,carry=carry8); FullAdder(a=a[8] ,b=b[8] ,c=carry8 ,sum=out[8] ,carry=carry9); FullAdder(a=a[9] ,b=b[9] ,c=carry9 ,sum=out[9] ,carry=carry10); FullAdder(a=a[10] ,b=b[10] ,c=carry10 ,sum=out[10] ,carry=carry11); FullAdder(a=a[11] ,b=b[11] ,c=carry11 ,sum=out[11] ,carry=carry12); FullAdder(a=a[12] ,b=b[12] ,c=carry12 ,sum=out[12] ,carry=carry13); FullAdder(a=a[13] ,b=b[13] ,c=carry13 ,sum=out[13] ,carry=carry14); FullAdder(a=a[14] ,b=b[14] ,c=carry14 ,sum=out[14] ,carry=carry15); FullAdder(a=a[15] ,b=b[15] ,c=carry15 ,sum=out[15] ,carry=carry16); } ``` ::: :::spoiler 輸出結果 ``` | a | b | out | | 0000000000000000 | 0000000000000000 | 0000000000000000 | | 0000000000000000 | 1111111111111111 | 1111111111111111 | | 1111111111111111 | 1111111111111111 | 1111111111111110 | | 1010101010101010 | 0101010101010101 | 1111111111111111 | | 0011110011000011 | 0000111111110000 | 0100110010110011 | | 0001001000110100 | 1001100001110110 | 1010101010101010 | ``` ::: > 補充: > 這個做法叫做Ripple Carry,因為會用到大量Full Adder,意味者用到一堆Xor,超級慢 > 比較實際的做法是Carry Look-ahead,把sum跟carry分開處理 ### Inc16:輸入值+1 1的部分可以用指定LSB為true的方式實現 :::spoiler HDL ``` CHIP Inc16 { IN in[16]; OUT out[16]; PARTS: Add16 (a=in, b[0]=true, out=out); } ``` ::: :::spoiler 輸出結果 ``` | in | out | | 0000000000000000 | 0000000000000001 | | 1111111111111111 | 0000000000000000 | | 0000000000000101 | 0000000000000110 | | 1111111111111011 | 1111111111111100 | ``` ::: ### ALU 六個input的實作方式都是把該算地都算出來再用Mux以sel值決定輸出哪個output zr:如果值為0,全部Or起來也會是0,Not前面的值就會是1了 ng:如果值<0,MSB必為1,把這個值拿去and true,ng就會是1,反之為0 :::spoiler HDL ``` CHIP ALU { IN x[16], y[16], // 16-bit inputs zx, // zero the x input? nx, // negate the x input? zy, // zero the y input? ny, // negate the y input? f, // compute out = x + y (if 1) or x & y (if 0) no; // negate the out output? OUT out[16], // 16-bit output zr, // 1 if (out == 0), 0 otherwise ng; // 1 if (out < 0), 0 otherwise PARTS: Mux16 (a=x, b=false, sel=zx, out=zxout); Not16 (in=zxout, out=NOTzxout); Mux16 (a=zxout, b=NOTzxout, sel=nx, out=nxout); Mux16 (a=y, b=false, sel=zy, out=zyout); Not16 (in=zyout, out=NOTzyout); Mux16 (a=zyout, b=NOTzyout, sel=ny, out=nyout); And16 (a=nxout, b=nyout, out=nxnyAND); Add16 (a=nxout, b=nyout, out=nxnyADD); Mux16 (a=nxnyAND, b=nxnyADD, sel=f, out=nxnyf); Not16 (in=nxnyf, out=NOTnxnyf); Mux16 (a=nxnyf, b=NOTnxnyf, sel=no, out=out, out[15]=outMSB, out[0..7]=out07, out[8..15]=out815); Or8Way (in=out07, out=zrl); Or8Way (in=out815, out=zrr); Or (a=zrl, b=zrr, out=NOTzr); Not (in=NOTzr, out=zr); And (a=outMSB, b=true, out=ng); } ``` ::: :::spoiler 輸出結果 ``` | x | y |zx |nx |zy |ny | f |no | out |zr |ng | | 0000000000000000 | 1111111111111111 | 1 | 0 | 1 | 0 | 1 | 0 | 0000000000000000 | 1 | 0 | | 0000000000000000 | 1111111111111111 | 1 | 1 | 1 | 1 | 1 | 1 | 0000000000000001 | 0 | 0 | | 0000000000000000 | 1111111111111111 | 1 | 1 | 1 | 0 | 1 | 0 | 1111111111111111 | 0 | 1 | | 0000000000000000 | 1111111111111111 | 0 | 0 | 1 | 1 | 0 | 0 | 0000000000000000 | 1 | 0 | | 0000000000000000 | 1111111111111111 | 1 | 1 | 0 | 0 | 0 | 0 | 1111111111111111 | 0 | 1 | | 0000000000000000 | 1111111111111111 | 0 | 0 | 1 | 1 | 0 | 1 | 1111111111111111 | 0 | 1 | | 0000000000000000 | 1111111111111111 | 1 | 1 | 0 | 0 | 0 | 1 | 0000000000000000 | 1 | 0 | | 0000000000000000 | 1111111111111111 | 0 | 0 | 1 | 1 | 1 | 1 | 0000000000000000 | 1 | 0 | | 0000000000000000 | 1111111111111111 | 1 | 1 | 0 | 0 | 1 | 1 | 0000000000000001 | 0 | 0 | | 0000000000000000 | 1111111111111111 | 0 | 1 | 1 | 1 | 1 | 1 | 0000000000000001 | 0 | 0 | | 0000000000000000 | 1111111111111111 | 1 | 1 | 0 | 1 | 1 | 1 | 0000000000000000 | 1 | 0 | | 0000000000000000 | 1111111111111111 | 0 | 0 | 1 | 1 | 1 | 0 | 1111111111111111 | 0 | 1 | | 0000000000000000 | 1111111111111111 | 1 | 1 | 0 | 0 | 1 | 0 | 1111111111111110 | 0 | 1 | | 0000000000000000 | 1111111111111111 | 0 | 0 | 0 | 0 | 1 | 0 | 1111111111111111 | 0 | 1 | | 0000000000000000 | 1111111111111111 | 0 | 1 | 0 | 0 | 1 | 1 | 0000000000000001 | 0 | 0 | | 0000000000000000 | 1111111111111111 | 0 | 0 | 0 | 1 | 1 | 1 | 1111111111111111 | 0 | 1 | | 0000000000000000 | 1111111111111111 | 0 | 0 | 0 | 0 | 0 | 0 | 0000000000000000 | 1 | 0 | | 0000000000000000 | 1111111111111111 | 0 | 1 | 0 | 1 | 0 | 1 | 1111111111111111 | 0 | 1 | | 0000000000010001 | 0000000000000011 | 1 | 0 | 1 | 0 | 1 | 0 | 0000000000000000 | 1 | 0 | | 0000000000010001 | 0000000000000011 | 1 | 1 | 1 | 1 | 1 | 1 | 0000000000000001 | 0 | 0 | | 0000000000010001 | 0000000000000011 | 1 | 1 | 1 | 0 | 1 | 0 | 1111111111111111 | 0 | 1 | | 0000000000010001 | 0000000000000011 | 0 | 0 | 1 | 1 | 0 | 0 | 0000000000010001 | 0 | 0 | | 0000000000010001 | 0000000000000011 | 1 | 1 | 0 | 0 | 0 | 0 | 0000000000000011 | 0 | 0 | | 0000000000010001 | 0000000000000011 | 0 | 0 | 1 | 1 | 0 | 1 | 1111111111101110 | 0 | 1 | | 0000000000010001 | 0000000000000011 | 1 | 1 | 0 | 0 | 0 | 1 | 1111111111111100 | 0 | 1 | | 0000000000010001 | 0000000000000011 | 0 | 0 | 1 | 1 | 1 | 1 | 1111111111101111 | 0 | 1 | | 0000000000010001 | 0000000000000011 | 1 | 1 | 0 | 0 | 1 | 1 | 1111111111111101 | 0 | 1 | | 0000000000010001 | 0000000000000011 | 0 | 1 | 1 | 1 | 1 | 1 | 0000000000010010 | 0 | 0 | | 0000000000010001 | 0000000000000011 | 1 | 1 | 0 | 1 | 1 | 1 | 0000000000000100 | 0 | 0 | | 0000000000010001 | 0000000000000011 | 0 | 0 | 1 | 1 | 1 | 0 | 0000000000010000 | 0 | 0 | | 0000000000010001 | 0000000000000011 | 1 | 1 | 0 | 0 | 1 | 0 | 0000000000000010 | 0 | 0 | | 0000000000010001 | 0000000000000011 | 0 | 0 | 0 | 0 | 1 | 0 | 0000000000010100 | 0 | 0 | | 0000000000010001 | 0000000000000011 | 0 | 1 | 0 | 0 | 1 | 1 | 0000000000001110 | 0 | 0 | | 0000000000010001 | 0000000000000011 | 0 | 0 | 0 | 1 | 1 | 1 | 1111111111110010 | 0 | 1 | | 0000000000010001 | 0000000000000011 | 0 | 0 | 0 | 0 | 0 | 0 | 0000000000000001 | 0 | 0 | | 0000000000010001 | 0000000000000011 | 0 | 1 | 0 | 1 | 0 | 1 | 0000000000010011 | 0 | 0 | ``` ::: ## Project 3 ### 1-Bit Register:如果load為1,輸入input,反之輸入上個clock的output 先用Mux選擇要輸入哪個值,再用DFF去做輸出 :::spoiler HDL ``` CHIP Bit { IN in, load; OUT out; PARTS: Mux (a=DFFout, b=in, sel=load, out=MUXout); DFF (in=MUXout, out=DFFout, out=out); } ``` ::: ### 16-Bit Register 就上面的寄存器,做16遍 :::spoiler HDL ``` CHIP Register { IN in[16], load; OUT out[16]; PARTS: Bit (in=in[0], load=load, out=out[0]); Bit (in=in[1], load=load, out=out[1]); Bit (in=in[2], load=load, out=out[2]); Bit (in=in[3], load=load, out=out[3]); Bit (in=in[4], load=load, out=out[4]); Bit (in=in[5], load=load, out=out[5]); Bit (in=in[6], load=load, out=out[6]); Bit (in=in[7], load=load, out=out[7]); Bit (in=in[8], load=load, out=out[8]); Bit (in=in[9], load=load, out=out[9]); Bit (in=in[10], load=load, out=out[10]); Bit (in=in[11], load=load, out=out[11]); Bit (in=in[12], load=load, out=out[12]); Bit (in=in[13], load=load, out=out[13]); Bit (in=in[14], load=load, out=out[14]); Bit (in=in[15], load=load, out=out[15]); } ``` ::: ### RAM8:16位元8寄存器記憶體 先把load用DMux和address分配出去,因為DMux輸出必然是8個二位元數,再以address決定是哪個值被分配到load 由於寄存器只有輸入1才會有動作,所以就算我把全部的寄存器都餵in進去,最多只有一個寄存器把in給記錄起來 然後再用Mux選擇要輸出的值就可以了 :::spoiler HDL ``` CHIP RAM8 { IN in[16], load, address[3]; OUT out[16]; PARTS: DMux8Way (in=load, sel=address, a=a, b=b, c=c, d=d, e=e, f=f, g=g, h=h); Register (in=in, load=a, out=outa); Register (in=in, load=b, out=outb); Register (in=in, load=c, out=outc); Register (in=in, load=d, out=outd); Register (in=in, load=e, out=oute); Register (in=in, load=f, out=outf); Register (in=in, load=g, out=outg); Register (in=in, load=h, out=outh); Mux8Way16 (a=outa, b=outb, c=outc, d=outd, e=oute, f=outf, g=outg, h=outh, sel=address, out=out); } ``` ::: ### RAM64 其實就是八個RAM8疊在一起,不同的是address多了三位,前三位用來選擇將值存進哪顆RAM8,後三位用來選擇存進RAM8中的哪個寄存器 :::spoiler HDL ``` CHIP RAM64 { IN in[16], load, address[6]; OUT out[16]; PARTS: DMux8Way (in=load, sel=address[3..5], a=a, b=b, c=c ,d=d, e=e, f=f, g=g, h=h); RAM8(in=in, load=a, address=address[0..2], out=outa); RAM8(in=in, load=b, address=address[0..2], out=outb); RAM8(in=in, load=c, address=address[0..2], out=outc); RAM8(in=in, load=d, address=address[0..2], out=outd); RAM8(in=in, load=e, address=address[0..2], out=oute); RAM8(in=in, load=f, address=address[0..2], out=outf); RAM8(in=in, load=g, address=address[0..2], out=outg); RAM8(in=in, load=h, address=address[0..2], out=outh); Mux8Way16 (a=outa, b=outb, c=outc, d=outd, e=oute, f=outf, g=outg, h=outh, sel=address[3..5], out=out); } ``` ::: ### RAM512 跟上面一樣,不過這次是前三位決定存進哪個RAM64,後六位選擇RAM64中的哪個寄存器 :::spoiler HDL ``` CHIP RAM512 { IN in[16], load, address[9]; OUT out[16]; PARTS: DMux8Way (in=load, sel=address[6..8], a=a, b=b, c=c ,d=d, e=e, f=f, g=g, h=h); RAM64(in=in, load=a, address=address[0..5], out=outa); RAM64(in=in, load=b, address=address[0..5], out=outb); RAM64(in=in, load=c, address=address[0..5], out=outc); RAM64(in=in, load=d, address=address[0..5], out=outd); RAM64(in=in, load=e, address=address[0..5], out=oute); RAM64(in=in, load=f, address=address[0..5], out=outf); RAM64(in=in, load=g, address=address[0..5], out=outg); RAM64(in=in, load=h, address=address[0..5], out=outh); Mux8Way16 (a=outa, b=outb, c=outc, d=outd, e=oute, f=outf, g=outg, h=outh, sel=address[6..8], out=out); } ``` ::: ### RAM4K :::spoiler HDL ``` CHIP RAM4K { IN in[16], load, address[12]; OUT out[16]; PARTS: DMux8Way (in=load, sel=address[9..11], a=a, b=b, c=c ,d=d, e=e, f=f, g=g, h=h); RAM512(in=in, load=a, address=address[0..8], out=outa); RAM512(in=in, load=b, address=address[0..8], out=outb); RAM512(in=in, load=c, address=address[0..8], out=outc); RAM512(in=in, load=d, address=address[0..8], out=outd); RAM512(in=in, load=e, address=address[0..8], out=oute); RAM512(in=in, load=f, address=address[0..8], out=outf); RAM512(in=in, load=g, address=address[0..8], out=outg); RAM512(in=in, load=h, address=address[0..8], out=outh); Mux8Way16 (a=outa, b=outb, c=outc, d=outd, e=oute, f=outf, g=outg, h=outh, sel=address[9..11], out=out); } ``` ::: ### RAM16K :::spoiler HDL ``` CHIP RAM16K { IN in[16], load, address[14]; OUT out[16]; PARTS: DMux4Way (in=load, sel=address[12..13], a=a, b=b, c=c,d=d); RAM4K(in=in, load=a, address=address[0..11], out=outa); RAM4K(in=in, load=b, address=address[0..11], out=outb); RAM4K(in=in, load=c, address=address[0..11], out=outc); RAM4K(in=in, load=d, address=address[0..11], out=outd); Mux4Way16 (a=outa, b=outb, c=outc, d=outd, sel=address[12..13],out=out); } ``` ::: ### PC:有讀取跟重置功能的16位元計數器 if (reset[t] == 1) out[t+1] = 0 else if (load[t] == 1) out[t+1] = in[t] else if (inc[t] == 1) out[t+1] = out[t] + 1 (integer addition) else out[t+1] = out[t] 以inc->load->reset的順序做,不然會亂掉 :::spoiler HDL ``` CHIP PC { IN in[16],load,inc,reset; OUT out[16]; PARTS: Inc16 (in=lastout ,out=INCout); Mux16 (a=lastout ,b=INCout,sel=inc, out=outi); //output for inc Mux16 (a=outi ,b=in, sel=load, out=outl); //output for load Mux16 (a=outl ,b=false, sel=reset, out=outr); //output for reset Register (in=outr, load=true, out=out, out=lastout); } ``` ::: :::spoiler 失敗版 ``` CHIP PC { IN in[16],load,inc,reset; OUT out[16]; PARTS: Inc16 (in=lastout ,out=INCout); Mux16 (a=lastout ,b=INCout,sel=inc, out=outi); //output for inc Mux16 (a=outi ,b=false, sel=reset, out=outr); //output for reset Mux16 (a=outr ,b=in, sel=load, out=outl); //output for load Register (in=outl, load=true, out=out, out=lastout); } ``` ::: :::danger 失敗原因: 由於reset與load對調,在先判定reset再判定load的情況下,如果load值沒回到0,無論前面的reset是否為1最後還是會去輸出load的值 ::: ## Project 4 ### Muti R2實際上是R0+R0+...+R0 共加R1次 就照著這個思路 先確認R0或R1是不是0 不是的話 把R0+R0寫入R2後 把R1-1 再一直重複到R1=0 就可以算出R0xR1的值了 ::: spoiler code ``` // R2=R0+R0+...+R0 R1 times or R1+R1+...+R1 R0 times // if R0 or R1 is 0 goto END // else // multi=R0+R0 // R2=R2+R0 // R1=R1-1 @R2 M=0 //set R2 to 0 (LOOP) @R0 D=M @END D;JEQ //if R0=0 goto END @R1 D=M @END D;JEQ //if R1=0 goto END @R2 D=M @R0 D=D+M //R2=R0+R0 @R2 M=D //write D back to R2 @R1 M=M-1 //R1=R1-1 @LOOP 0;JMP //loop until R1=0 (END) @END 0;JMP //end of this program ``` ::: > [name=Shinshipower] 就我理解 > @R0 > D=M > @LOOP > D;JEQ //if R0=0 goto END > 這一段會持續loop > 等於說你R0 = 0 實際上是繼續loop 等到R0 不是 0 > 我不確定這是作業要求還是設計失誤 > 因為R1 = 0 會跳到end > > 回覆: > 這裡本意是讓R0=0就去END那邊的 > 只是打錯成LOOP > 不過這樣其實依然可行因為在R0=0的情況下進LOOP 會讓R2=0+0一路加到R1=0結果還是0 ### Fill 把要填的像素分成-1(黑)跟0(白)兩個部分 再去判斷與填寫就好了 :::spoiler code ``` (START) @SCREEN D=A @0 M=D //put SCREEN start addr in R0 (CHECK) @KBD D=M @BLACK D;JGT // if KBD>0 goto BLACK @WHITE D;JEQ //if KBD=0 goto WHITE @CHECK 0;JMP (BLACK) @1 M=-1 //fill SCREEN with 1111111111111111 @CHANGE 0;JMP (WHITE) @1 M=0 //fill SCREEN with 0000000000000000 @CHANGE 0;JMP (CHANGE) @1 D=M @0 A=M M=D //fill SCREEN with R1 @0 D=M+1 //inc to next pixel @KBD D=A-D //KBD-SCREEN=A @0 M=M+1 A=M @CHANGE D;JGT //exit if KBD-(i+n)=0 @START 0;JMP //go back to START ``` ::: ## Project 5 ### CPU 經過了幾個星期的學習,終於能把CPU架起來了 :::spoiler HDL ``` 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: //判斷程式為A-instruction(開頭為0)或C-instruction(開頭為0) Not(in=instruction[15], out=notI); Mux16(a=ALUout, b=instruction, sel=notI, out=insout); //若instruction MSB為1,讀取ALU上次計算的output,為0則讀取instrction,在這時是需要輸入進A register的address Or(a=notI, b=instruction[5], out=loadA); //這裡有個很重要的觀念,在判斷是否寫入一個寄存器要滿足以下兩個條件:1.是否需要寫入(看指令相應的輸入位) 2.該指令為C-instruction //後者比較容易被忽略,一定要小心! ARegister(in=insout, load=loadA, out[0..14]=addressM, out=Aout); //在C-instruction中,不只c1~c6指定不同的運算,還以a指定被運算的一方為A或M(D不影響) And(a=instruction[15], b=instruction[12],out=AorM); Mux16(a=Aout, b=inM, sel=AorM, out=AM); //這裡的設計解釋了為何a只選擇A或M,不選擇D ALU(x=Dout, y=AM, zx=instruction[11],nx=instruction[10], zy=instruction[9], ny=instruction[8], f=instruction[7], no=instruction[6], out=outM, out=ALUout, zr=zr, ng=ng); And(a=instruction[15], b=instruction[4], out=loadD); DRegister(in=ALUout, load=loadD, out=Dout); And(a=instruction[15], b=instruction[3], out=writeM); //在前面的課程有說到zr跟ng很重要,因為可以拿來處理jump bit //jump bit同樣也須滿足兩個情況:1.指令指定要跳,2.指令為C-instruction //這裡會一次處理所有jump bit,全部條件皆符合才會讓PC的load bit為1 Not(in=ng, out=pos); Not(in=zr, out=not0); And(a=instruction[15], b=instruction[0], out=JGT); And(a=pos, b=not0, out=posANDnot0); And(a=JGT, b=posANDnot0, out=load1); And(a=instruction[15], b=instruction[1], out=JEQ); And(a=JEQ, b=zr, out=load2); And(a=instruction[15], b=instruction[2], out=JLT); And(a=JLT, b=ng, out=load3); Or(a=load1, b=load2, out=load1or2); Or(a=load1or2, b=load3, out=load); PC(in=Aout, load=load, inc=true, reset=reset, out[0..14]=pc); } ``` ::: ### Memory :::spoiler HDL ``` CHIP Memory { IN in[16], load, address[15]; OUT out[16]; PARTS: DMux4Way (in=load, sel=address[13..14], a=ram1, b=ram2, c=scr, d=k); Or (a=ram1, b=ram2, out=ram); RAM16K (in=in, load=ram, address=address[0..13], out=ram16k); Screen(in=in, load=scr, address=address[0..12], out=screen); Keyboard(out=kbd); Mux4Way16 (a=ram16k, b=ram16k, c=screen, d=kbd, sel=address[13..14], out=out); } ``` ::: ### Computer 就把前面兩個跟ROM接起來 就是一台可以正常運作的電腦 :::spoiler HDL ``` CHIP Computer { IN reset; PARTS: ROM32K(address=pc, out=inst); CPU(inM=inM, instruction=inst, reset=reset, outM=outM, writeM=writeM, addressM=addressM, pc=pc); Memory(in=outM, load=writeM, address=addressM, out=inM); } ``` ::: ## Project 6 ### Assembler 步驟: 讀取asm檔-> 用re把程式分類成A指令、C指令、Label、空白、註解五類->