今日はHDLシミュレータでNandゲートを使ったNotゲートの実装からMux、DMuxと16bitのNot,And,Orゲートの実装までやった
Muxの実装は最初わけわかんなかったけど、Claudeにヒントをもらって、Excelに真理値表を書いたら理解できた。DMuxも最初は戸惑ったけど、真理値表を読み解いて実装したら案外行けた
AIで何でもできるようになってしまった今、Nand2TetrisでCSの基礎固めを大事にする
if文やfor文が生まれた当初は、CSの大革命期だったのかもしれない
「これ、if文があれば一瞬で解けるのに!」とか「for文があれば3行で実装できるじゃん……!」があった
なんとか1章を完走。
Mux4Way16でつまづいたが、Claudeにヒントを聞いたところ「トーナメント形式と考えてみればいける」と言われてやってみたら行けた
DMux4Wayは最初以下のように実装していたんだけど、冒頭の三行がDMuxでやっていることと全く同じだということに気づいて書き換えた
【書き換え前】
Not(in=sel[1] , out=nsel1 );
And(a=in , b=nsel1 , out=neq1 );
And(a=in , b=sel[1] , out=neq2 );
DMux(in=neq1 , sel=sel[0] , a=a , b=b );
DMux(in=neq2 , sel=sel[0] , a=c , b=d );
【書き換え後】
DMux(in=in , sel=sel[1] , a=neq1 , b=neq2 );
DMux(in=neq1 , sel=sel[0] , a=a , b=b );
DMux(in=neq2 , sel=sel[0] , a=c , b=d );
DMux8Wayは「最初にDMux4Wayが使えそうだぞ?ということに気づいて一発で解けた
そのあと、「Mux8Way16ももしかして同じ要領で解けるのでは?」と思い至って、以下のように書き直した
【書き換え前】
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 );
【書き換え後】
Mux4Way16(a=a , b=b , c=c , d=d , sel=sel[0..1] , out=abcd );
Mux4Way16(a=e , b=f , c=g , d=h , sel=sel[0..1] , out=efgh );
Mux16(a=abcd , b=efgh , sel=sel[2] , out=out );ALUの実装までやった。
outが0かの判定のところで、out=の仕様が分かってなくてめちゃくちゃ時間を食ってしまった。
out[0..7]=やout[15]=で出力をスライスさせるのは覚えておこう。
あと、最上位ビットが負数であることを表現しているということをすっかり忘れていた。ここはALUの仕様周りを再読する必要がありそう。
Claudeにヒントをもらいながら実装したのが以下
// xyが0になるかを判定
Mux16(a=x , b=false , sel=zx , out=zeroorx );
Mux16(a=y , b=false , sel=zy , out=zeroory );
// xyが反転するかを判定
Not16(in=zeroorx , out=notx );
Mux16(a=zeroorx , b=notx , sel=nx , out=notorx );
Not16(in=zeroory , out=noty );
Mux16(a=zeroory , b=noty , sel=ny , out=notory );
// AddなのかAndなのかを判定
Add16(a=notorx , b=notory , out=addxy );
And16(a=notorx , b= notory, out=andxy );
Mux16(a=andxy , b=addxy , sel=f , out=result );
// 結果を反転させるかを判定
Not16(in=result , out=notresult );
Mux16(a=result , b=notresult , sel=no , out=out );
// outが0かを判定
Mux16(a=result , b=notresult , sel=no , out[0..7]=neqout1, out[8..15]=neqout2 );
Or8Way(in=neqout1 , out=nzr1 );
Or8Way(in=neqout2 , out=nzr2 );
Or(a=nzr1 , b=nzr2 , out=nzr );
Not(in=nzr , out=zr );
// outが0未満かを判定
Mux16(a=result , b=notresult , sel=no , out[15]=ng );
で、「outはスライスできる」というClaudeの助言をもらってリファクタした。
// xyが0になるかを判定
Mux16(a=x , b=false , sel=zx , out=zeroorx );
Mux16(a=y , b=false , sel=zy , out=zeroory );
// xyが反転するかを判定
Not16(in=zeroorx , out=notx );
Mux16(a=zeroorx , b=notx , sel=nx , out=notorx );
Not16(in=zeroory , out=noty );
Mux16(a=zeroory , b=noty , sel=ny , out=notory );
// AddなのかAndなのかを判定
Add16(a=notorx , b=notory , out=addxy );
And16(a=notorx , b= notory, out=andxy );
Mux16(a=andxy , b=addxy , sel=f , out=result );
// 結果を反転させるかを判定
Not16(in=result , out=notresult );
Mux16(a=result , b=notresult , sel=no , out=out, out[0..7]=neqout1, out[8..15]=neqout2, out[15]=ng );
// outが0かを判定
Or8Way(in=neqout1 , out=nzr1 );
Or8Way(in=neqout2 , out=nzr2 );
Or(a=nzr1 , b=nzr2 , out=nzr );
Not(in=nzr , out=zr );これは文系出身エンジニアが、大学時代で学ばなかったコンピュータサイエンスを30歳になってやっと学び始めたログです。 Nand2Tetrisの本を四苦八苦しながら進めています。
RAM16Kの実装までやった。 まとめる前にひとこと、これドパガキ向け過ぎる。 たった5段階の作業で16000個のレジスタに値を保存できるようになるのえぐい。
PCの実装までやった。 フィードバックループの考え方がかなりとっつきにくかったけど、「HDLは今まで使っていたようなプログラミング言語のようなものではなく、配線図を文章化したものである」と捉えることができて何とかPCも実装できた。 次は念願の機械語。ここからより自分の専門領域に近づいてくるから楽しみ。