たまには低レベルなこともしたくて*1コンピュータシステムの理論と実装(以下、nand2tetris本)を始めてみました。nand2tetris本
はNANDゲートのみ*2からCPU/OSなどを実装していく素敵な書籍です。今回は1〜5章のハードウェア部分を実装してみたので忘れっぽい自分のためのメモです。自力で実装に挑戦してみたい人にはネタバレになると思うので注意です。
今回のコード
下記、タグv0.0.0
になります。
github.com
下記で動かせます。
git clone -b v0.0.0 https://github.com/nihemak/nand2tetris.git
cd nand2tetris
# download nand2tetris environment
./setup.sh
# test all
./test.sh
1章 ブール論理
Not
真理値表は下記です。
これはNand(in, in)
です。関連するNand
の真理値表は下記です。
a |
b |
- |
out |
0 |
0 |
- |
1 |
1 |
1 |
- |
0 |
コードは01/Not.hdlです。
And
真理値表は下記です。
a |
b |
- |
out |
0 |
0 |
- |
0 |
0 |
1 |
- |
0 |
1 |
0 |
- |
0 |
1 |
1 |
- |
1 |
これはNot(Nand(a, b))
です。
Nand(a, b) = Not(And(a, b))
の両辺にNot
を適用してNot
を打ち消した左辺と考えることができます。
コードは01/And.hdlです。
Or
真理値表は下記です。
a |
b |
- |
out |
0 |
0 |
- |
0 |
0 |
1 |
- |
1 |
1 |
0 |
- |
1 |
1 |
1 |
- |
1 |
これはNot(And(Not(a), Not(b)))
です。
ドモルガンの法則であるNot(Or(a, b)) = And(Not(a), Not(b))
の両辺にNot
を適用した右辺と考えることができます。
コードは01/Or.hdlです。
Xor
真理値表は下記です。
a |
b |
- |
out |
0 |
0 |
- |
0 |
0 |
1 |
- |
1 |
1 |
0 |
- |
1 |
1 |
1 |
- |
0 |
これはOr(And(a, Not(b)), And(Not(a), b))
です。
次の2つのOr
を行い(1, 0)
と(0, 1)
が1
となるようにすると考えることができます。
(1, 0)
のみが1
となるAnd(a, Not(b))
(0, 1)
のみが1
となるAnd(Not(a), b)
(1, 0)
のみが1
となるAnd(a, Not(b))
の真理値表は下記です。
a |
Not(b) |
- |
And(a, Not(b)) |
0 |
1 |
- |
0 |
0 |
0 |
- |
0 |
1 |
1 |
- |
1 |
1 |
0 |
- |
0 |
(0, 1)
のみが1
となるAnd(Not(a), b)
の真理値表は下記です。
Not(a) |
b |
- |
And(Not(a), b) |
1 |
0 |
- |
0 |
1 |
1 |
- |
1 |
0 |
0 |
- |
0 |
1 |
1 |
- |
0 |
Or(And(a, Not(b)), And(Not(a), b))
の真理値表は下記です。
And(a, Not(b)) |
And(Not(a), b) |
- |
out |
0 |
0 |
- |
0 |
0 |
1 |
- |
1 |
1 |
0 |
- |
1 |
0 |
0 |
- |
0 |
コードは01/Xor.hdlです。
Mux
真理値表は下記です。
a |
b |
sel |
- |
out |
0 |
0 |
0 |
- |
0 |
0 |
1 |
0 |
- |
0 |
1 |
0 |
0 |
- |
1 |
1 |
1 |
0 |
- |
1 |
0 |
0 |
1 |
- |
0 |
0 |
1 |
1 |
- |
1 |
1 |
0 |
1 |
- |
0 |
1 |
1 |
1 |
- |
1 |
これはAnd(Or(a, sel), Or(b, Not(sel)))
です。
sel = 0
の場合はa
, sel = 1
の場合はb
にすれば良いと考えることができます。
これには次の3つが使えます。
Or(x, 0) = x
Or(x, 1) = 1
And(x, 1) = 1
下記のようにselによって取り出す列を選ぶことができます。
sel = 0
の場合、Or(a, sel) = a
およびOr(b, Not(sel)) = 1
となるのでAnd(a, 1) = a
になる
sel = 1
の場合、Or(a, sel) = 1
およびOr(b, Not(sel)) = b
となるのでAnd(1, b) = b
になる
コードは01/Mux.hdlです。
DMux
真理値表は下記です。
in |
sel |
- |
a |
b |
0 |
0 |
- |
0 |
0 |
1 |
0 |
- |
1 |
0 |
0 |
1 |
- |
0 |
0 |
1 |
1 |
- |
0 |
1 |
これは下記です。
a = And(in, Not(sel))
b = And(in, sel)
sel = 0
の場合はa = in, b = 0
, sel = 1
の場合はa = 0, b = in
にすれば良いと考えることができます。
sel |
- |
a |
b |
0 |
- |
in |
0 |
1 |
- |
0 |
in |
これには次の2つが使えます。
And(x, 0) = 0
And(x, 1) = 1
下記のようにselによって取り出す列を選ぶことができます。
sel = 0
の場合、a = And(in, 1), b = And(in, 0)
となるのでa = in, b = 0
になる
sel = 1
の場合、a = And(in, 0), b = And(in, 1)
となるのでa = 0, b = in
になる
コードは01/DMux.hdlです。
Not16
これは次のように全ての要素にNot
を適用するだけです。
out[0] = Not(in[0])
out[1] = Not(in[1])
- ...
out[14] = Not(in[14])
out[15] = Not(in[15])
コードは01/Not16.hdlです。
And16
これは次のように全ての要素にAnd
を適用するだけです。
out[0] = And(a[0], b[0])
out[1] = And(a[1], b[1])
- ...
out[14] = And(a[14], b[14])
out[15] = And(a[15], b[15])
コードは01/And16.hdlです。
Or16
これは次のように全ての要素にOr
を適用するだけです。
out[0] = Or(a[0], b[0])
out[1] = Or(a[1], b[1])
- ...
out[14] = Or(a[14], b[14])
out[15] = Or(a[15], b[15])
コードは01/Or16.hdlです。
Mux16
これは次のように全ての要素にMux
を適用するだけです。
out[0] = Mux(a[0], b[0], sel)
out[1] = Mux(a[1], b[1], sel)
- ...
out[14] = Mux(a[14], b[14], sel)
out[15] = Mux(a[15], b[15], sel)
コードは01/Mux16.hdlです。
Or8Way
これは次のように要素すべてのOr
を行うだけです。
Or(Or(in[0], in[1]), Or(in[2], in[3])), Or(Or(in[4], in[5]), Or(in[6], in[7]))
コードは01/Or8Way.hdlです。
Mux4Way16
真理値表は下記です。
sel[1] |
sel[0] |
- |
out |
0 |
0 |
- |
a |
0 |
1 |
- |
b |
1 |
0 |
- |
c |
1 |
1 |
- |
d |
これはMux16(Mux16(a, b, sel[0]), Mux16(c, d, sel[0]), sel[1])
です。
sel[0]
を使ってMux16
でa, b
から1つ、c, d
から1つを選択、それらの結果からsel[1]
を使って1つをMux16
で選択すると求めることができます。
sel[0]
を使ってMux16
でa, b
から1つを選択する真理値表は下記です。
sel[0] |
- |
Mux16(a, b, sel[0]) |
0 |
- |
a |
1 |
- |
b |
0 |
- |
a |
1 |
- |
b |
sel[0]
を使ってMux16
でc, d
から1つを選択する真理値表は下記です。
sel[0] |
- |
Mux16(c, d, sel[0]) |
0 |
- |
c |
1 |
- |
d |
0 |
- |
c |
1 |
- |
d |
上記の2つの結果からsel[1]
を使って1つをMux16
で選択する真理値表は下記です。
sel[1] |
- |
Mux16(Mux16(a, b, sel[0]), Mux16(c, d, sel[0]), sel[1]) |
0 |
- |
Mux16(a, b, sel[0]) = a |
0 |
- |
Mux16(a, b, sel[0]) = b |
1 |
- |
Mux16(c, d, sel[0]) = c |
1 |
- |
Mux16(c, d, sel[0]) = d |
コードは01/Mux4Way16.hdlです。
Mux8Way16
真理値表は下記です。
sel[2] |
sel[1] |
sel[0] |
- |
out |
0 |
0 |
0 |
- |
a |
0 |
0 |
1 |
- |
b |
0 |
1 |
0 |
- |
c |
0 |
1 |
1 |
- |
d |
1 |
0 |
0 |
- |
e |
1 |
0 |
1 |
- |
f |
1 |
1 |
0 |
- |
g |
1 |
1 |
1 |
- |
h |
これはMux16(Mux4Way16(a, b, c, d, sel[0], sel[1]), Mux4Way16(e, f, g, h, sel[0], sel[1]), sel[2])
です。
考え方はMux8Way16
と同じです。まずsel[0..1]
を使ってMux4Way16
でa, b, c, d
とe, f, g, h
から1つずつ選択、それらの結果からsel[2]
を使って1つをMux16
で選択すると求めることができます。
sel[0..1]
を使ってMux4Way16
でa, b, c, d
から1つ選択する真理値表は下記です。
sel[1] |
sel[0] |
- |
Mux4Way16(a, b, c, d, sel[0], sel[1]) |
0 |
0 |
- |
a |
0 |
1 |
- |
b |
1 |
0 |
- |
c |
1 |
1 |
- |
d |
0 |
0 |
- |
a |
0 |
1 |
- |
b |
1 |
0 |
- |
c |
1 |
1 |
- |
d |
sel[0..1]
を使ってMux4Way16
でe, f, g, h
から1つ選択する真理値表は下記です。
sel[1] |
sel[0] |
- |
Mux4Way16(e, f, g, h, sel[0], sel[1]) |
0 |
0 |
- |
e |
0 |
1 |
- |
f |
1 |
0 |
- |
g |
1 |
1 |
- |
h |
0 |
0 |
- |
e |
0 |
1 |
- |
f |
1 |
0 |
- |
g |
1 |
1 |
- |
h |
上記の2つの結果からsel[2]
を使って1つをMux16
で選択する真理値表は下記です。
sel[2] |
- |
Mux16(Mux4Way16(a, b, c, d, sel[0], sel[1]), Mux4Way16(e, f, g, h, sel[0], sel[1]), sel[2]) |
0 |
- |
Mux4Way16(a, b, c, d, sel[0], sel[1]) = a |
0 |
- |
Mux4Way16(a, b, c, d, sel[0], sel[1]) = b |
0 |
- |
Mux4Way16(a, b, c, d, sel[0], sel[1]) = c |
0 |
- |
Mux4Way16(a, b, c, d, sel[0], sel[1]) = d |
1 |
- |
Mux4Way16(e, f, g, h, sel[0], sel[1]) = e |
1 |
- |
Mux4Way16(e, f, g, h, sel[0], sel[1]) = f |
1 |
- |
Mux4Way16(e, f, g, h, sel[0], sel[1]) = g |
1 |
- |
Mux4Way16(e, f, g, h, sel[0], sel[1]) = h |
コードは01/Mux8Way16.hdlです。
DMux4Way
真理値表は下記です。
sel[1] |
sel[0] |
- |
a |
b |
c |
d |
0 |
0 |
- |
in |
0 |
0 |
0 |
0 |
1 |
- |
0 |
in |
0 |
0 |
1 |
0 |
- |
0 |
0 |
in |
0 |
1 |
1 |
- |
0 |
0 |
0 |
in |
これは下記です。
(w0, w1) = DMux(in, sel[0])
(a, c) = DMux(w0, sel[1])
(b, d) = DMux(w1, sel[1])
sel[0]
を使ってDMux
でin
を2つに分離します。そして、前者をsel[1]
を使ってDMux
で2つに分離するとa, c
、後者をsel[1]
を使ってDMux
で2つに分離するとb, d
を求めることができます。
sel[0]
を使ってDMux
でin
を2つに分離する真理値表は下記です。
sel[0] |
- |
w0 |
w1 |
0 |
- |
in |
0 |
1 |
- |
0 |
in |
0 |
- |
in |
0 |
1 |
- |
0 |
in |
前者をsel[1]
を使ってDMux
で2つに分離する真理値表は下記です。
sel[1] |
- |
a |
c |
0 |
- |
w0 = in |
0 |
0 |
- |
w0 = 0 |
0 |
1 |
- |
0 |
w0 = in |
1 |
- |
0 |
w0 = 0 |
後者をsel[1]
を使ってDMux
で2つに分離する真理値表は下記です。
sel[1] |
- |
b |
d |
0 |
- |
w1 = 0 |
0 |
0 |
- |
w1 = in |
0 |
1 |
- |
0 |
w1 = 0 |
1 |
- |
0 |
w1 = in |
これらを組み合わせると正しい真理値表になります。
sel[1] |
sel[0] |
- |
a |
b |
c |
d |
0 |
0 |
- |
w0 = in |
w1 = 0 |
w0 = 0 |
w1 = 0 |
0 |
1 |
- |
w0 = 0 |
w1 = in |
w0 = 0 |
w1 = 0 |
1 |
0 |
- |
w0 = 0 |
w1 = 0 |
w0 = in |
w1 = 0 |
1 |
1 |
- |
w0 = 0 |
w1 = 0 |
w0 = 0 |
w1 = in |
コードは01/DMux4Way.hdlです。
DMux8Way
真理値表は下記です。
sel[2] |
sel[1] |
sel[0] |
- |
a |
b |
c |
d |
e |
f |
g |
h |
0 |
0 |
0 |
- |
in |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
- |
0 |
in |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
- |
0 |
0 |
in |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
1 |
- |
0 |
0 |
0 |
in |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
- |
0 |
0 |
0 |
0 |
in |
0 |
0 |
0 |
1 |
0 |
1 |
- |
0 |
0 |
0 |
0 |
0 |
in |
0 |
0 |
1 |
1 |
0 |
- |
0 |
0 |
0 |
0 |
0 |
0 |
in |
0 |
1 |
1 |
1 |
- |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
in |
これは下記です。
(w0, w1, w2, w3) = DMux4Way(in, sel[0..1])
(a, e) = DMux(w0, sel[2])
(b, f) = DMux(w1, sel[2])
(c, g) = DMux(w2, sel[2])
(d, h) = DMux(w3, sel[2])
考え方はDMux4Way
と同じです。まずsel[0..1]
を使ってDMux4Way
でin
を4つに分離します。そして、それぞれをsel[2]
を使ってDMux
で2つに分離するとa, e
、b, f
、c, g
、d, h
を求めることができます。
sel[0..1]
を使ってDMux4Way
でin
を4つに分離する真理値表は下記です。
sel[1] |
sel[0] |
- |
w0 |
w1 |
w2 |
w3 |
0 |
0 |
- |
in |
0 |
0 |
0 |
0 |
1 |
- |
0 |
in |
0 |
0 |
1 |
0 |
- |
0 |
0 |
in |
0 |
1 |
1 |
- |
0 |
0 |
0 |
in |
0 |
0 |
- |
in |
0 |
0 |
0 |
0 |
1 |
- |
0 |
in |
0 |
0 |
1 |
0 |
- |
0 |
0 |
in |
0 |
1 |
1 |
- |
0 |
0 |
0 |
in |
sel[2]
を使ってDMux
でa, e
に分離する真理値表は下記です。
sel[2] |
- |
a |
e |
0 |
- |
w0 = in |
0 |
0 |
- |
w0 = 0 |
0 |
0 |
- |
w0 = 0 |
0 |
0 |
- |
w0 = 0 |
0 |
1 |
- |
0 |
w0 = in |
1 |
- |
0 |
w0 = 0 |
1 |
- |
0 |
w0 = 0 |
1 |
- |
0 |
w0 = 0 |
sel[2]
を使ってDMux
でb, f
に分離する真理値表は下記です。
sel[2] |
- |
b |
f |
0 |
- |
w1 = 0 |
0 |
0 |
- |
w1 = in |
0 |
0 |
- |
w1 = 0 |
0 |
0 |
- |
w1 = 0 |
0 |
1 |
- |
0 |
w1 = 0 |
1 |
- |
0 |
w1 = in |
1 |
- |
0 |
w1 = 0 |
1 |
- |
0 |
w1 = 0 |
sel[2]
を使ってDMux
でc, g
に分離する真理値表は下記です。
sel[2] |
- |
c |
g |
0 |
- |
w2 = 0 |
0 |
0 |
- |
w2 = 0 |
0 |
0 |
- |
w2 = in |
0 |
0 |
- |
w2 = 0 |
0 |
1 |
- |
0 |
w2 = 0 |
1 |
- |
0 |
w2 = 0 |
1 |
- |
0 |
w2 = in |
1 |
- |
0 |
w2 = 0 |
sel[2]
を使ってDMux
でd, h
に分離する真理値表は下記です。
sel[2] |
- |
d |
h |
0 |
- |
w3 = 0 |
0 |
0 |
- |
w3 = 0 |
0 |
0 |
- |
w3 = 0 |
0 |
0 |
- |
w3 = in |
0 |
1 |
- |
0 |
w3 = 0 |
1 |
- |
0 |
w3 = 0 |
1 |
- |
0 |
w3 = 0 |
1 |
- |
0 |
w3 = in |
これらを組み合わせると正しい真理値表になります。
sel[2,1,0] |
- |
a |
b |
c |
d |
e |
f |
g |
h |
000 |
- |
w0 = in |
w1 = 0 |
w2 = 0 |
w3 = 0 |
w0 = 0 |
w1 = 0 |
w2 = 0 |
w3 = 0 |
001 |
- |
w0 = 0 |
w1 = in |
w2 = 0 |
w3 = 0 |
w0 = 0 |
w1 = 0 |
w2 = 0 |
w3 = 0 |
010 |
- |
w0 = 0 |
w1 = 0 |
w2 = in |
w3 = 0 |
w0 = 0 |
w1 = 0 |
w2 = 0 |
w3 = 0 |
011 |
- |
w0 = 0 |
w1 = 0 |
w2 = 0 |
w3 = in |
w0 = 0 |
w1 = 0 |
w2 = 0 |
w3 = 0 |
100 |
- |
w0 = 0 |
w1 = 0 |
w2 = 0 |
w3 = 0 |
w0 = in |
w1 = 0 |
w2 = 0 |
w3 = 0 |
101 |
- |
w0 = 0 |
w1 = 0 |
w2 = 0 |
w3 = 0 |
w0 = 0 |
w1 = in |
w2 = 0 |
w3 = 0 |
110 |
- |
w0 = 0 |
w1 = 0 |
w2 = 0 |
w3 = 0 |
w0 = 0 |
w1 = 0 |
w2 = in |
w3 = 0 |
111 |
- |
w0 = 0 |
w1 = 0 |
w2 = 0 |
w3 = 0 |
w0 = 0 |
w1 = 0 |
w2 = 0 |
w3 = in |
コードは01/DMux8Way.hdlです。
2章 ブール算術
HalfAdder
真理値表は下記です。
a |
b |
- |
carry |
sum |
0 |
0 |
- |
0 |
0 |
0 |
1 |
- |
0 |
1 |
1 |
0 |
- |
0 |
1 |
1 |
1 |
- |
1 |
0 |
これは下記です。
sum = Xor(a, b)
carry = And(a, b)
コードは02/HalfAdder.hdlです。
FullAdder
真理値表は下記です。
a |
b |
c |
- |
carry |
sum |
0 |
0 |
0 |
- |
0 |
0 |
0 |
0 |
1 |
- |
0 |
1 |
0 |
1 |
0 |
- |
0 |
1 |
0 |
1 |
1 |
- |
1 |
0 |
1 |
0 |
0 |
- |
0 |
1 |
1 |
0 |
1 |
- |
1 |
0 |
1 |
1 |
0 |
- |
1 |
0 |
1 |
1 |
1 |
- |
0 |
1 |
これは下記です。
(sum0, carry0) = HalfAdder(a, b)
(sum, carry1) = HalfAdder(sum, c)
carry = Or(carry0, carry1)
sum
はa, b, c
すべてのHalfAdder
のsum
、carry
は「a, b
のHalfAdder
のcarry
」と「a, b
のHalfAdder
のsum
とc
のHalfAdder
のcarry
」のOr
です。
sum = Sum(Sum(a, b), c) = Xor(Xor(a, b), c)
carry = Or(Carry(a, b), Carry(Sum(a, b), c)) = Or(And(a, b), And(Xor(a, b), c))
コードは02/FullAdder.hdlです。
Add16
表にすると下記のようになります。
a |
b |
carry |
- |
out |
a[0] |
b[0] |
- |
- |
Sum(a[0], b[0]) |
a[1] |
b[1] |
Carry(a[0], b[0]) |
- |
Sum(a[1], b[1]) |
a[2] |
b[2] |
Carry(a[1], b[1]) |
- |
Sum(a[2], b[2]) |
a[3] |
b[3] |
Carry(a[2], b[2]) |
- |
Sum(a[3], b[3]) |
a[4] |
b[4] |
Carry(a[3], b[3]) |
- |
Sum(a[4], b[4]) |
a[5] |
b[5] |
Carry(a[4], b[4]) |
- |
Sum(a[5], b[5]) |
a[6] |
b[6] |
Carry(a[5], b[5]) |
- |
Sum(a[6], b[6]) |
a[7] |
b[7] |
Carry(a[6], b[6]) |
- |
Sum(a[7], b[7]) |
a[8] |
b[8] |
Carry(a[7], b[7]) |
- |
Sum(a[8], b[8]) |
a[9] |
b[9] |
Carry(a[8], b[8]) |
- |
Sum(a[9], b[9]) |
a[10] |
b[10] |
Carry(a[9], b[9]) |
- |
Sum(a[10], b[10]) |
a[11] |
b[11] |
Carry(a[10], b[10]) |
- |
Sum(a[11], b[11]) |
a[12] |
b[12] |
Carry(a[11], b[11]) |
- |
Sum(a[12], b[12]) |
a[13] |
b[13] |
Carry(a[12], b[12]) |
- |
Sum(a[13], b[13]) |
a[14] |
b[14] |
Carry(a[13], b[13]) |
- |
Sum(a[14], b[14]) |
a[15] |
b[15] |
Carry(a[14], b[14]) |
- |
Sum(a[15], b[15]) |
これは1個のHalfAdder
と15個のFullAdder
を組み合わせれば良いです。
ゲートの構成は下記です。
コードは02/Add16.hdlです。
Inc16
これはAdd16(in, %B0000000000000001)
です。
コードは02/Inc16.hdlです。
ALU
これは場合分けして考えます。
まずzx
とnx
、zy
とny
を考慮した値を求めます。
x1 = Add16(x, Mux16(x, %B0000000000000000, zx))
x2 = Mux16(x1, Not(x1), nx)
y1 = Add16(y, Mux16(y, %B0000000000000000, zy))
y2 = Mux16(y1, Not(y1), ny)
そしてf
とno
を考慮してout
を求めます。
xy = Mux16(Add16(x2, y2), And16(x2, y2), f)
out = Mux16(xy, Not(xy), no)
あとはout
からzr
とng
を求めることができます。
zr = Not(Or(Or8Way(out[0..7]), Or8Way(out[8..15]))))
ng = Or(out[15], 0)
コードは02/ALU.hdlです。
3章 順序回路
Bit
これはそのままです。
ゲートの構成は下記です。
コードは03/a/Bit.hdlです。
Register
これはBit
を16個ならべるだけです。
out[0] = Bit(in[0], load)
out[1] = Bit(in[1], load)
- ...
out[14] = Bit(in[14], load)
out[15] = Bit(in[15], load)
コードは03/a/Register.hdlです。
RAM8
これは8個のRegister
を並べます。
下記の3ステップで求めることができます。
address
のRegister
のみload
、それ以外は書き込みをしないようにfalse
をRegister
に指定するためにDMux8Way
で各load
を求める
- 各
Register
にin
を適用してそれぞれのout
を求める
address
のout
をMux8Way16
で求める
ゲートの構成は下記です。
コードは03/a/RAM8.hdlです。
RAM64
これはRAM8
と同じ考え方で下記を変更すればできます。
DMux8Way
のsel
にはaddress[3..5]
を指定する
Register
をRAM8
にしてaddress
にはaddress[0..2]
を指定する
Mux8Way16
のsel
にはaddress[3..5]
を指定する
ゲートの構成は下記です。
コードは03/a/RAM64.hdlです。
RAM512
これはRAM64
と同じ考え方で下記を変更すればできます。
DMux8Way
のsel
にはaddress[6..8]
を指定する
RAM8
をRAM64
にしてaddress
にはaddress[0..5]
を指定する
Mux8Way16
のsel
にはaddress[6..8]
を指定する
ゲートの構成は下記です。
コードは03/b/RAM512.hdlです。
RAM4K
これはRAM512
と同じ考え方で下記を変更すればできます。
DMux8Way
のsel
にはaddress[9..11]
を指定する
RAM64
をRAM512
にしてaddress
にはaddress[0..8]
を指定する
Mux8Way16
のsel
にはaddress[9..11]
を指定する
コードは03/b/RAM4K.hdlです。
RAM16K
これはRAM4K
と同じ考え方で下記を変更すればできます。
DMux8Way
をDMux4Way
に変えsel
にはaddress[12..13]
を指定する
- RAMの数を4個に変え
RAM512
をRAM4K
にしてaddress
にはaddress[0..11]
を指定する
Mux8Way16
をMux4Way16
に変えsel
にはaddress[12..13]
を指定する
ゲートの構成は下記です。
コードは03/b/RAM16K.hdlです。
PC
これは下記のように考えます。
- 前回結果に依存するので
Register
を使う
Register
に記録するのはMux4Way16
とreset
およびload
を使い%B0000000000000000
とin
、前回結果または前回結果+1
から決定する
前回結果または前回結果+1
はMux16
とinc
を使い決定する
結果の決定の順序はreset > load > inc
の優先度によります。
Mux16
とinc
を使い決定する前回結果または前回結果+1
の真理値表は下記です。
sel = inc |
- |
out |
0 |
- |
a = 前回結果 |
1 |
- |
b = 前回結果+1 = Add16(前回結果, %B0000000000000001) |
Mux4Way16
とreset
およびload
を使い%B0000000000000000
とin
、前回結果または前回結果+1
から決定する真理値表は下記です。
sel[1] = reset |
sel[0] = load |
- |
out |
0 |
0 |
- |
a = 前回結果または前回結果+1 = Mux16(前回結果, Add16(前回結果, %B0000000000000001), inc) |
0 |
1 |
- |
b = in |
1 |
0 |
- |
c = %B0000000000000000 |
1 |
1 |
- |
d = %B0000000000000000 |
ゲートの構成は下記です。
コードは03/a/PC.hdlです。
Mult
ロジックは下記です。
コードは04/mult/mult.asmです。
Fill
ロジックは下記です。*3
コードは04/fill/Fill.asmです。
Memory
メモリマップは下記です。
用途 |
- |
開始アドレス |
終了アドレス |
RAM |
- |
0 = %B000000000000000 |
16383 = 2^14 - 1 = %B011111111111111 |
Screen |
- |
16384 = %B100000000000000 |
24575 = %B101111111111111 |
Keyboard |
- |
24576 = %B110000000000000 |
24576 = %B110000000000000 |
メモリマップの通りaddress[14]
でRAM
かどうか、address[13]
でScreen
かKeyboard
を判別することができる仕様の構成になっています。
下記のステップで考えることができます。
DMux
とaddress[14]
を使ってload
をRAM
とScreen
のどちらに指定するか決定する
Mux4Way16
とaddress[13..14]
を使ってRAM
とScreen
、Keyboard
からどれを結果にするか決定する
DMux
とaddress[14]
を使ってload
をRAM
とScreen
のどちらに指定するか決定する真理値表は下記です。
address[14] |
- |
RAM のload |
Screen のload |
0 |
- |
load |
0 |
1 |
- |
0 |
load |
Mux4Way16
とaddress[13..14]
を使ってRAM
とScreen
、Keyboard
からどれを結果にするか決定する真理値表は下記です。
address[14] |
address[13] |
- |
out |
0 |
0 |
- |
RAM の結果 |
0 |
1 |
- |
RAM の結果 |
1 |
0 |
- |
Screen の結果 |
1 |
1 |
- |
Keyboard の結果 |
ゲートの構成は下記です。
コードは05/Memory.hdlです。
CPU
これはinstruction
に指定される機械語の仕様から考えると分かりやすいです。
基数 |
- |
A命令 |
C命令 |
15 |
- |
0 |
1 |
14 |
- |
v |
1 |
13 |
- |
v |
1 |
12 |
- |
v |
a: ALUのy(0: A, 1: M) |
11 |
- |
v |
c1: ALUのzx |
10 |
- |
v |
c2: ALUのnx |
9 |
- |
v |
c3: ALUのzy |
8 |
- |
v |
c4: ALUのny |
7 |
- |
v |
c5: ALUのf |
6 |
- |
v |
c6: ALUのno |
5 |
- |
v |
d1: ARegisterのload |
4 |
- |
v |
d2: DRegisterのload |
3 |
- |
v |
d3: writeM |
2 |
- |
v |
j1: PCのload(ただしALUのoutが負の場合、すなわちngが1の場合) |
1 |
- |
v |
j2: PCのload(ただしALUのoutが0の場合、すなわちzrが1の場合) |
0 |
- |
v |
j3: PCのload(ただしALUのoutが正の場合) |
その他、補足は下記です。
- ALUのoutが正かどうかはALUのngとzrから
Not(Or(ng, zr))
で求めることができる
- A命令の場合もARegisterのloadを1にする
ゲートの構成は下記です。
コードは05/CPU.hdlです。
Computer
これは仕様にしたがってROM32K
とCPU
、Memory
を配線します。
ゲートの構成は下記です。
コードは05/Computer.hdlです。
まとめ
実装よりもまとめる方がしんどかったですが理解を深められてデバッグにもなりました。