セキュリティ・キャンプ キャンパー育成枠の活動録

セキュリティ・キャンプ2018の選考に選ばれ、参加してきた後の活動を紹介 育成枠というのは他キャンパーとの実力を相対的に見て低いと判断して名乗っています。

nand2tetrisを実装する(1)第1章 ブール演算

卒業研究にコンピュータの理論と実装~モダンなコンピュータの作り方~ (通称:nand2tetris)を使って、コンピュータを作ることを決めたのでレポートとして経過をアップロードしていきます。

第1章 ブール論理

この章では、NotなどのゲートをNandだけでつくることができるのでそれをやってみようという章です。(結構パズルっぽい)
使用言語はハードウェア記述言語(HDL)です。

Not回路

回路図で表現すると、以下のようになります。
1つのNandだけでNotゲートが実装できるのがわかりますね


コードは以下の通りです。

// 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/01/Not.hdl

/**
 * Not gate:
 * out = not in
 */

CHIP Not {
    IN in;
    OUT out;

    PARTS:
    // Put your code here:
    Nand(a=in,b=in,out=out);
}

Andゲート

回路図で表すと、2つの入力値をNandゲートを通し、出力した値をNandゲートに通す(前述のNot回路と同じ働き)ことで実装


コードは以下の通りです。

// 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/01/And.hdl

/**
 * And gate: 
 * out = 1 if (a == 1 and b == 1)
 *       0 otherwise
 */

CHIP And {
    IN a, b;
    OUT out;

    PARTS:
    // Put your code here:
    Nand(a=a, b=b, out=w);
    Nand(a=w, b=w, out=out);
}

Orゲート

回路図で表すと、aとbの入力をNandでNotしたそれぞれの出力結果をNandに通すと実装


コードは以下の通りです。

// 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/01/Or.hdl

 /**
 * Or gate:
 * out = 1 if (a == 1 or b == 1)
 *       0 otherwise
 */

CHIP Or {
    IN a, b;
    OUT out;

    PARTS:
    // Put your code here:
    Nand(a=a, b=a, out=w1);
    Nand(a=b, b=b, out=w2);
    Nand(a=w1, b=w2, out=out);
}

Xorゲート

このあたりから回路図が複雑になっていきます。


コードは以下の通りです

// 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/01/Xor.hdl

/**
 * Exclusive-or gate:
 * out = not (a == b)
 */

CHIP Xor {
    IN a, b;
    OUT out;

    PARTS:
    // Put your code here:
    Nand(a=a, b=b, out=w1);
    Nand(a=a, b=w1, out=w2);
    Nand(a=w1, b=b, out=w3);
    Nand(a=w2, b=w3, out=out);
}

マルチプレクサゲート
ご存じでない方もいるかもしれないので本文を引用すると、

3入力のゲートである。「データビット」と呼ばれるふたつの入力からひとつを選択して出力するために、
「選択ビット」と呼ばれる入力がひとつ用いられる。このゲートは「セレクタ」と呼ぶほうがふさわしいかもしれない。
「マルチプレクサ」という名前は通信システムで採用されたもので、そのシステムではいくつかの入力信号を1本のワイヤへ
出力とシリアライズするために、同じようなデバイスが使われている。

という回路で、aとbの入力に対し、選択ビットのselの値によってaを出力するかbを出力するかを決める論理ゲートです。
※これ以降は回路図を省略します。
コードは以下の通りです。

// 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/01/Mux.hdl

/** 
 * Multiplexor:
 * out = a if sel == 0
 *       b otherwise
 */

CHIP Mux {
    IN a, b, sel;
    OUT out;

    PARTS:
    // Put your code here:
    Nand(a=sel, b=sel, out=w1);
    Nand(a=a, b=w1, out=w2);
    Nand(a=sel, b=b, out=w3);
    Nand(a=w2, b=w3, out=out);
}

デマルチプレクサ

デマルチプレクサはマルチプレクサの逆のことを行います。
入力されたinをselの値によって、aに出力するか、bに出力するかを選択します。
コードは以下の通りです。

// File name: projects/01/DMux.hdl

/**
 * Demultiplexor:
 * {a, b} = {in, 0} if sel == 0
 *          {0, in} if sel == 1
 */

CHIP DMux {
    IN in, sel;
    OUT a, b;

    PARTS:
    // Put your code here:
    Nand(a=in, b=sel, out=w1);
    Nand(a=in, b=w1, out=w2);
    Nand(a=w2, b=w2, out=a);
    Nand(a=w1, b=w1, out=b);
}

多ビットNot

Notゲートの入力を16ビットで行うものです。各ビットに対してNot演算を行います。
コードは以下の通りです。(全てNandで書くことはできますがコードを書くのがすごく面倒ですね...)

// 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/01/Not16.hdl

/**
 * 16-bit Not:
 * for i=0..15: out[i] = not in[i]
 */

CHIP Not16 {
    IN in[16];
    OUT out[16];

    PARTS:
    // Put your code here:
    Nand(a=in[0],b=in[0],out=out[0]);
    Nand(a=in[1],b=in[1],out=out[1]);
    Nand(a=in[2],b=in[2],out=out[2]);
    Nand(a=in[3],b=in[3],out=out[3]);
    Nand(a=in[4],b=in[4],out=out[4]);
    Nand(a=in[5],b=in[5],out=out[5]);
    Nand(a=in[6],b=in[6],out=out[6]);
    Nand(a=in[7],b=in[7],out=out[7]);
    Nand(a=in[8],b=in[8],out=out[8]);
    Nand(a=in[9],b=in[9],out=out[9]);
    Nand(a=in[10],b=in[10],out=out[10]);
    Nand(a=in[11],b=in[11],out=out[11]);
    Nand(a=in[12],b=in[12],out=out[12]);
    Nand(a=in[13],b=in[13],out=out[13]);
    Nand(a=in[14],b=in[14],out=out[14]);
    Nand(a=in[15],b=in[15],out=out[15]);
}

多ビットAnd

説明は省略
コードは以下の通りです。

// 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/01/And16.hdl

/**
 * 16-bit bitwise And:
 * for i = 0..15: out[i] = (a[i] and b[i])
 */

CHIP And16 {
    IN a[16], b[16];
    OUT out[16];

    PARTS:
    // Put your code here:
    Nand(a=a[0], b=b[0], out=w0);
    Nand(a=w0, b=w0, out=out[0]);
    Nand(a=a[1], b=b[1], out=w1);
    Nand(a=w1, b=w1, out=out[1]);
    Nand(a=a[2], b=b[2], out=w2);
    Nand(a=w2, b=w2, out=out[2]);
    Nand(a=a[3], b=b[3], out=w3);
    Nand(a=w3, b=w3, out=out[3]);
    Nand(a=a[4], b=b[4], out=w4);
    Nand(a=w4, b=w4, out=out[4]);
    Nand(a=a[5], b=b[5], out=w5);
    Nand(a=w5, b=w5, out=out[5]);
    Nand(a=a[6], b=b[6], out=w6);
    Nand(a=w6, b=w6, out=out[6]);
    Nand(a=a[7], b=b[7], out=w7);
    Nand(a=w7, b=w7, out=out[7]);
    Nand(a=a[8], b=b[8], out=w8);
    Nand(a=w8, b=w8, out=out[8]);
    Nand(a=a[9], b=b[9], out=w9);
    Nand(a=w9, b=w9, out=out[9]);
    Nand(a=a[10], b=b[10], out=w10);
    Nand(a=w10, b=w10, out=out[10]);
    Nand(a=a[11], b=b[11], out=w11);
    Nand(a=w11, b=w11, out=out[11]);
    Nand(a=a[12], b=b[12], out=w12);
    Nand(a=w12, b=w12, out=out[12]);
    Nand(a=a[13], b=b[13], out=w13);
    Nand(a=w13, b=w13, out=out[13]);
    Nand(a=a[14], b=b[14], out=w14);
    Nand(a=w14, b=w14, out=out[14]);
    Nand(a=a[15], b=b[15], out=w15);
    Nand(a=w15, b=w15, out=out[15]);
}

多ビットOr

説明省略
コードは以下の通りです。

// 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/01/Or16.hdl

/**
 * 16-bit bitwise Or:
 * for i = 0..15 out[i] = (a[i] or b[i])
 */

CHIP Or16 {
    IN a[16], b[16];
    OUT out[16];

    PARTS:
    // Put your code here:
    Nand(a=a[0], b=a[0], out=wa0);
    Nand(a=b[0], b=b[0], out=wb0);
    Nand(a=wa0, b=wb0, out=out[0]);
    Nand(a=a[1], b=a[1], out=wa1);
    Nand(a=b[1], b=b[1], out=wb1);
    Nand(a=wa1, b=wb1, out=out[1]);
    Nand(a=a[2], b=a[2], out=wa2);
    Nand(a=b[2], b=b[2], out=wb2);
    Nand(a=wa2, b=wb2, out=out[2]);
    Nand(a=a[3], b=a[3], out=wa3);
    Nand(a=b[3], b=b[3], out=wb3);
    Nand(a=wa3, b=wb3, out=out[3]);
    Nand(a=a[4], b=a[4], out=wa4);
    Nand(a=b[4], b=b[4], out=wb4);
    Nand(a=wa4, b=wb4, out=out[4]);
    Nand(a=a[5], b=a[5], out=wa5);
    Nand(a=b[5], b=b[5], out=wb5);
    Nand(a=wa5, b=wb5, out=out[5]);
    Nand(a=a[6], b=a[6], out=wa6);
    Nand(a=b[6], b=b[6], out=wb6);
    Nand(a=wa6, b=wb6, out=out[6]);
    Nand(a=a[7], b=a[7], out=wa7);
    Nand(a=b[7], b=b[7], out=wb7);
    Nand(a=wa7, b=wb7, out=out[7]);
    Nand(a=a[8], b=a[8], out=wa8);
    Nand(a=b[8], b=b[8], out=wb8);
    Nand(a=wa8, b=wb8, out=out[8]);
    Nand(a=a[9], b=a[9], out=wa9);
    Nand(a=b[9], b=b[9], out=wb9);
    Nand(a=wa9, b=wb9, out=out[9]);
    Nand(a=a[10], b=a[10], out=wa10);
    Nand(a=b[10], b=b[10], out=wb10);
    Nand(a=wa10, b=wb10, out=out[10]);
    Nand(a=a[11], b=a[11], out=wa11);
    Nand(a=b[11], b=b[11], out=wb11);
    Nand(a=wa11, b=wb11, out=out[11]);
    Nand(a=a[12], b=a[12], out=wa12);
    Nand(a=b[12], b=b[12], out=wb12);
    Nand(a=wa12, b=wb12, out=out[12]);
    Nand(a=a[13], b=a[13], out=wa13);
    Nand(a=b[13], b=b[13], out=wb13);
    Nand(a=wa13, b=wb13, out=out[13]);
    Nand(a=a[14], b=a[14], out=wa14);
    Nand(a=b[14], b=b[14], out=wb14);
    Nand(a=wa14, b=wb14, out=out[14]);
    Nand(a=a[15], b=a[15], out=wa15);
    Nand(a=b[15], b=b[15], out=wb15);
    Nand(a=wa15, b=wb15, out=out[15]);
}

多ビットマルチプレクサ

例題の場合、16ビット入力されるaとbに対してselの値に応じてaを出力するかbを出力するかを決めます。
入力値のみが変化していますね。

// by Nisan and Schocken, MIT Press.
// File name: projects/01/Mux16.hdl

/**
 * 16-bit multiplexor: 
 * for i = 0..15 out[i] = a[i] if sel == 0 
 *                        b[i] if sel == 1
 */

CHIP Mux16 {
    IN a[16], b[16], sel;
    OUT out[16];

    PARTS:
    // Put your code here:
    Nand(a=sel, b=sel, out=wa0);
    Nand(a=a[0], b=wa0, out=wb0);
    Nand(a=sel, b=b[0], out=wc0);
    Nand(a=wb0, b=wc0, out=out[0]);
    Nand(a=sel, b=sel, out=wa1);
    Nand(a=a[1], b=wa1, out=wb1);
    Nand(a=sel, b=b[1], out=wc1);
    Nand(a=wb1, b=wc1, out=out[1]);
    Nand(a=sel, b=sel, out=wa2);
    Nand(a=a[2], b=wa2, out=wb2);
    Nand(a=sel, b=b[2], out=wc2);
    Nand(a=wb2, b=wc2, out=out[2]);
    Nand(a=sel, b=sel, out=wa3);
    Nand(a=a[3], b=wa3, out=wb3);
    Nand(a=sel, b=b[3], out=wc3);
    Nand(a=wb3, b=wc3, out=out[3]);
    Nand(a=sel, b=sel, out=wa4);
    Nand(a=a[4], b=wa4, out=wb4);
    Nand(a=sel, b=b[4], out=wc4);
    Nand(a=wb4, b=wc4, out=out[4]);
    Nand(a=sel, b=sel, out=wa5);
    Nand(a=a[5], b=wa5, out=wb5);
    Nand(a=sel, b=b[5], out=wc5);
    Nand(a=wb5, b=wc5, out=out[5]);
    Nand(a=sel, b=sel, out=wa6);
    Nand(a=a[6], b=wa6, out=wb6);
    Nand(a=sel, b=b[6], out=wc6);
    Nand(a=wb6, b=wc6, out=out[6]);
    Nand(a=sel, b=sel, out=wa7);
    Nand(a=a[7], b=wa7, out=wb7);
    Nand(a=sel, b=b[7], out=wc7);
    Nand(a=wb7, b=wc7, out=out[7]);
    Nand(a=sel, b=sel, out=wa8);
    Nand(a=a[8], b=wa8, out=wb8);
    Nand(a=sel, b=b[8], out=wc8);
    Nand(a=wb8, b=wc8, out=out[8]);
    Nand(a=sel, b=sel, out=wa9);
    Nand(a=a[9], b=wa9, out=wb9);
    Nand(a=sel, b=b[9], out=wc9);
    Nand(a=wb9, b=wc9, out=out[9]);
    Nand(a=sel, b=sel, out=wa10);
    Nand(a=a[10], b=wa10, out=wb10);
    Nand(a=sel, b=b[10], out=wc10);
    Nand(a=wb10, b=wc10, out=out[10]);
    Nand(a=sel, b=sel, out=wa11);
    Nand(a=a[11], b=wa11, out=wb11);
    Nand(a=sel, b=b[11], out=wc11);
    Nand(a=wb11, b=wc11, out=out[11]);
    Nand(a=sel, b=sel, out=wa12);
    Nand(a=a[12], b=wa12, out=wb12);
    Nand(a=sel, b=b[12], out=wc12);
    Nand(a=wb12, b=wc12, out=out[12]);
    Nand(a=sel, b=sel, out=wa13);
    Nand(a=a[13], b=wa13, out=wb13);
    Nand(a=sel, b=b[13], out=wc13);
    Nand(a=wb13, b=wc13, out=out[13]);
    Nand(a=sel, b=sel, out=wa14);
    Nand(a=a[14], b=wa14, out=wb14);
    Nand(a=sel, b=b[14], out=wc14);
    Nand(a=wb14, b=wc14, out=out[14]);
    Nand(a=sel, b=sel, out=wa15);
    Nand(a=a[15], b=wa15, out=wb15);
    Nand(a=sel, b=b[15], out=wc15);
    Nand(a=wb15, b=wc15, out=out[15]);
}

多入力Or

この問題は全8ビットの入力のうち、1つでも1がある場合、1を出力します。

// 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/01/Or8Way.hdl

/**
 * 8-way Or: 
 * out = (in[0] or in[1] or ... or in[7])
 */

CHIP Or8Way {
    IN in[8];
    OUT out;

    PARTS:
    // Put your code here:
    Nand(a=in[0], b=in[0], out=wa1);
    Nand(a=in[1], b=in[1], out=wb1);
    Nand(a=wa1, b=wb1, out=wc1); 
    
    Nand(a=wc1, b=wc1, out=wa2);
    Nand(a=in[2], b=in[2], out=wb2);
    Nand(a=wa2, b=wb2, out=wc2); 
    
    Nand(a=wc2, b=wc2, out=wa3);
    Nand(a=in[3], b=in[3], out=wb3);
    Nand(a=wa3, b=wb3, out=wc3); 
    
    Nand(a=wc3, b=wc3, out=wa4);
    Nand(a=in[4], b=in[4], out=wb4);
    Nand(a=wa4, b=wb4, out=wc4); 
    
    Nand(a=wc4, b=wc4, out=wa5);
    Nand(a=in[5], b=in[5], out=wb5);
    Nand(a=wa5, b=wb5, out=wc5);
    
    Nand(a=wc5, b=wc5, out=wa6);
    Nand(a=in[6], b=in[6], out=wb6);
    Nand(a=wa6, b=wb6, out=wc6);
    
    Nand(a=wc6, b=wc6, out=wa7);
    Nand(a=in[7], b=in[7], out=wb7);
    Nand(a=wa7, b=wb7, out=out);
}

多入力/多ビットマルチプレクサ

4入力16ビットマルチプレクサの場合は4本の16ビット入力バス*1からひとつを選択し、1本の16ビット出力バスへ出力します。
コードは以下の通り※これ以降、Nandのみで書くとコードが難読化するので、Nandで作成済みの各ゲートを呼び出す形式で記述します。(つまり元をたどれば全てNandで記述されているということになります。)

// 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/01/Mux4Way16.hdl

/**
 * 4-way 16-bit multiplexor:
 * out = a if sel == 00
 *       b if sel == 01
 *       c if sel == 10
 *       d if sel == 11
 */

CHIP Mux4Way16 {
    IN a[16], b[16], c[16], d[16], sel[2];
    OUT out[16];

    PARTS:
    // Put your code here:
    Mux16(a=a, b=b, sel=sel[0], out=out1);
    Mux16(a=c, b=d, sel=sel[0], out=out2);
    Mux16(a=out1, b=out2, sel=sel[1], out=out);
}

8入力16ビットマルチプレクサの場合は8本の16ビット入力バスからひとつを選択し、1本の16ビット出力バスへ出力します。
コードは以下の通り

// File name: projects/01/Mux8Way16.hdl

/**
 * 8-way 16-bit multiplexor:
 * out = a if sel == 000
 *       b if sel == 001
 *       etc.
 *       h if sel == 111
 */

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:
    // Put your code here:
    Mux16(a=a, b=b, sel=sel[0], out=ab);
    Mux16(a=c, b=d, sel=sel[0], out=cd);
    Mux16(a=e, b=f, sel=sel[0], out=ef);
    Mux16(a=g, b=h, sel=sel[0], out=gh);
    Mux16(a=ab, b=cd, sel=sel[1], out=abcd);
    Mux16(a=ef, b=gh, sel=sel[1], out=efgh);
    Mux16(a=abcd, b=efgh, sel=sel[2], out=out);
}

多入力/多ビットデマルチプレクサ

4入力1ビットのデマルチプレクサはinの入力値を4本ある出力バスのいずれかに振り分けます
コードは以下の通りです。

// 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/01/DMux4Way.hdl

/**
 * 4-way demultiplexor:
 * {a, b, c, d} = {in, 0, 0, 0} if sel == 00
 *                {0, in, 0, 0} if sel == 01
 *                {0, 0, in, 0} if sel == 10
 *                {0, 0, 0, in} if sel == 11
 */

CHIP DMux4Way {
    IN in, sel[2];
    OUT a, b, c, d;

    PARTS:
    // Put your code here:
     DMux (in=in, sel=sel[1], a=w1, b=w2);
     DMux (in=w1, sel=sel[0], a=a, b=b);
     DMux (in=w2, sel=sel[0], a=c, b=d);
}

4入力1ビットのデマルチプレクサはinの入力値を4本ある出力バスのいずれかに振り分けます
コードは以下の通りです。

// 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/01/DMux8Way.hdl

/**
 * 8-way demultiplexor:
 * {a, b, c, d, e, f, g, h} = {in, 0, 0, 0, 0, 0, 0, 0} if sel == 000
 *                            {0, in, 0, 0, 0, 0, 0, 0} if sel == 001
 *                            etc.
 *                            {0, 0, 0, 0, 0, 0, 0, in} if sel == 111
 */

CHIP DMux8Way {
    IN in, sel[3];
    OUT a, b, c, d, e, f, g, h;

    PARTS:
    // Put your code here:
    DMux4Way (in=in, sel[0]=sel[1], sel[1]=sel[2], a=w1, b=w2, c=w3, d=w4);
    DMux (in=w1, sel=sel[0], a=a, b=b);
    DMux (in=w2, sel=sel[0], a=c, b=d);
    DMux (in=w3, sel=sel[0], a=e, b=f);
    DMux (in=w4, sel=sel[0], a=g, b=h);
}

以上が第1章の実習になります。
次回は2章以降の内容について取り扱います。

*1:バスとは、複数のビットからなる配列のこと