やかんです。

現在、ハードウェア設計論という授業を履修しています。Verilogを使ってハードウェアの設計を学ぼう、的な講義だと思っていますが、Verilogに慣れないことには始まらないので、とりあえず手を動かしてみようと思います。

まあ、普通に授業の課題をやっていくだけなんですけどね。

全加算器とは?

全加算器は、2つのビットの加算を行なって、その結果として得られる和とキャリー(繰り上がり)を出力する装置らしいです(こちら)。

さて。全加算器は3つの入力を持ちます。

東大生やかんのブログ
やかん

え、2つのビットの加算だから入力は2つじゃないの?

はい。加算する2つのビットと、前の計算で生じたキャリーの3つを入力として受け取ります。この、前の計算で生じたキャリーのことを「キャリーイン」とか言うっぽい。

そして、出力は前述の通り和とキャリーの2つ。この時、計算で生じたキャリーのことをキャリーアウトとか言います。

Verilogで全加算器を書きたい。

では、実際に全加算機をVerilogを使って書いていきましょう。

動作記述で書く。

東大生やかんのブログ
やかん

は?動作記述って何?

Verilogにはある装置を記述する際2とりのスタイルがあります。動作記述と構造記述。まあ、書き別ればなんとなくわかる気がしますが、動作記述は「入力値と出力値が正しければ、中身はざっくりでいいよね」な書き方で、構造記述は「回路図をそっくりそのままコードで表現するよ」な書き方、みたいなイメージです。

まあ、実際に書いてみましょう。

module FullAdderFunction(
  input x,
  input y,
  input cin,
  output cout,
  output s
);
  assign s = x ^ y ^ cin;
  assign cout = (x & y) | (y & cin) | (x & cin);
endmodule

モジュール名の後ろで記述している

  input x,
  input y,
  input cin,
  output cout,
  output s

というのは、この装置の入力と出力です。「最初に入出力を記述すれば、何の装置かわかりやすいでしょ?」みたいな思想なんですかね。CとかC++のつもりでコードを読むと「inputは型なのかな?」という気がしますが、型ではなさそうです。

さて、では具体的な演算の方に進みましょう。まずはこちら。

  assign s = x ^ y ^ cin;

これは、入力値の和を計算しています。もちろんキャリー(繰り上がり)は無視します。

assignというステートメントが行頭にありますが、これは詳細に理解しようとすると難しそうです。そのため今回は、「まあ多分変数に値を入れますよ」的なニュアンスなんだろうな〜くらいで次に行きます。

演算の中で「^」が使われています。とんがりコーンみたいなやつ。これは排他的論理和を表していて、具体的にはa ^ bという記述で「aとbの排他的論理和」を表します。XORですね。

aba^b
000
011
101
110
これ排他的論理和

今回はx, y, そしてキャリーインのキャリーを無視した和を取りたいので、全ての排他的論理和をとれば実現可能です。

次に、キャリーアウトの計算。

  assign cout = (x & y) | (y & cin) | (x & cin);

うん。もうすでに「へ?」って感じですね。気持ちとしては、「xとy、xとcin、yとcinのどれかがそれぞれ1同士なら、そこで繰り上がりが発生するよね」みたいな。

このキャリーアウトの計算は慣れるしかないんかなあ、うまく説明できないので一旦飛ばしましょう。

はい。騙し騙しですが、とりあえず全加算機の動作記述を完了しました。次に、構造記述。

構造記述で書く。

構造記述は、論理回路をそっくりのままコードで表現する記述です。回路図とにらめっこして一個一個コードにすればいいはずなので、そんなに頭使わなくてもいいんでしょうか。

まあでも、論理回路を与えられたら簡単かもだけど0から自分で回路考えて、、となるとできる気がしませんね。頑張ります。とりあえず、記述としてはこんな感じ↓

module FullAdderStructure(
  input x,
  input y,
  input cin,
  output cout,
  output s
);
  wire s1, w1, w2, w3, w4;

  xor G1(s1, x, y);
  xor G2(s, s1, cin);

  and G3(w1, x, y);
  and G4(w2, y, cin);
  and G5(w3, x, cin);
  or G6(w4, w1, w2);
  or G7(cout, w3, w4);

endmodule

うーん、なんか難しそうですね。

まず、wireという記述が目につきます。こちらは、もう本当に「ワイヤー」だと思っていいんじゃないでしょうか。

東大生やかんのブログ
やかん

ん?

装置と装置を繋ぐワイヤーです。ワイヤーっていうか、「接続」。こういうの、回路図だと何て言うんだろう。教えてください。

てな訳なので、

  wire s1, w1, w2, w3, w4;

という記述は、s1, w1, w2, w3, w4という5つの接続線を用意するよ、みたいな気持ちなんじゃないですかね。

東大生やかんのブログ
やかん

テキトー言ってごめんなさい。これから勉強します。

次に、xorとand, orですね。

  // XOR
  xor G1(s1, x, y);

  // AND
  and G3(c1, x, y);

  // OR
  or G6(w4, w1, w2);

この時、G1やG3は、一旦無視で大丈夫だと思います。「論理ゲートだよ」ということを明示するための記述だと思いますが、「とりあえずゲートを表したいんだな」程度で一旦おいときます。

具体的に述べると、例えば

  xor G1(s1, x, y);

の場合は、G1というXORのゲートにおいて、xとyのXORをとりs1に割り当てる、と言った具合です。andも

orも同様。

不思議なもので、以上のようにごにょごにょゲートを表現すると、和とキャリーが取得できるんですよね、、

東大生やかんのブログ
やかん

回路図見れば「本当だ」となりますが、考えた人天才。

ということで、構造記述についても騙し騙しですが、一通り終えたということにしましょう。

全加算器一通り書き終わったけど?

はい。騙し騙しですが、書き終わりました。となると次の欲求としては、「え、このVerilogのコードって本当に全加算器を表してるの?」ってところですよね。

東大生やかんのブログ
やかん

テストというか、デバッグというか。

これは、テストベンチなるものを作成し、iverilogコマンドを使ってvcdファイル作り、、などそれなりの工数を踏んで実現できるそうです。

はい。テスト周りは次回やっていこうと思います。

はい。勉強頑張っていこうと思います。ちなみに、この記事のアイキャッチ画像はDALLEに生成してもらったものです。

ということで、こちらの記事は終了です。最後までお読みいただき、ありがとうございます。