やかんです。
現在、ハードウェア設計論という授業を履修しています。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ですね。
a | b | a^b |
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
今回は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に生成してもらったものです。
ということで、こちらの記事は終了です。最後までお読みいただき、ありがとうございます。