Verilogで加算回路を書く
(1)ここで学ぶ内容
いよいよVerilog-HDLの文法ノート(Eシリーズ)の始まりだよ🐾
まずは『加算回路』を通して、入力・出力・計算の基本的な書き方をマスターしよう!
概要
目標
修了判定
- 加算回路を用いて、組み合わせ回路の記述を学ぶ
- モジュール構造の概略を理解する
目標
以下の項目を理解し説明できる
- 回路記述におけるモジュール構造
- 入出力の宣言
- 演算子を用いた演算回路記述
- assign 文の記述
- 階層構造をもった回路の記述
- 下位階層の呼び出しと接続
修了判定
信号名を指定して、16ビットの減算回路を記述する
条件:減算演算子を用いる
(2)加算演算子による加算回路
4ビット加算回路
graph LR
subgraph 4ビット加算回路
ADD[adder]
end
A[a
4bit] -->|4| ADD B[b
4bit] -->|4| ADD ADD -->|4| Q[q
4bit] style ADD fill:#e3f2fd,stroke:#1976d2,stroke-width:2px;
4bit] -->|4| ADD B[b
4bit] -->|4| ADD ADD -->|4| Q[q
4bit] style ADD fill:#e3f2fd,stroke:#1976d2,stroke-width:2px;
// 加算演算子による4ビット加算回路
module adder( a, b, q );
input [3:0] a, b;
output [3:0] q;
assign q = a + b;
endmodule
a [ ][ ][ ][ ]
+) b [ ][ ][ ][ ]
──────────────────
q [ ][ ][ ][ ]
(3)フルアダーによる加算回路
4ビット加算回路
graph LR
subgraph 4ビット加算回路
ADD[adder]
end
A[a
4bit] -->|4| ADD B[b
4bit] -->|4| ADD ADD -->|4| Q[q
4bit] style ADD fill:#e3f2fd,stroke:#1976d2,stroke-width:2px;
4bit] -->|4| ADD B[b
4bit] -->|4| ADD ADD -->|4| Q[q
4bit] style ADD fill:#e3f2fd,stroke:#1976d2,stroke-width:2px;
graph TD
a0[a 0] --> add0[add0
fulladd] b0[b 0] --> add0 add0 --> q0[q 0] a1[a 1] --> add1[add1
fulladd] b1[b 1] --> add1 add1 --> q1[q 1] a2[a 2] --> add2[add2
fulladd] b2[b 2] --> add2 add2 --> q2[q 2] a3[a 3] --> add3[add3
fulladd] b3[b 3] --> add3 add3 --> q3[q 3] cin[CIN 0] -.->|COUT0| add0 add0 -.->|COUT1| add1 add1 -.->|COUT2| add2 add2 -.->|COUT3| add3 add3 -.->|COUT4| cout[開放] style add0 fill:#e8f5e9,stroke:#388e3c,stroke-width:2px; style add1 fill:#e8f5e9,stroke:#388e3c,stroke-width:2px; style add2 fill:#e8f5e9,stroke:#388e3c,stroke-width:2px; style add3 fill:#e8f5e9,stroke:#388e3c,stroke-width:2px;
fulladd] b0[b 0] --> add0 add0 --> q0[q 0] a1[a 1] --> add1[add1
fulladd] b1[b 1] --> add1 add1 --> q1[q 1] a2[a 2] --> add2[add2
fulladd] b2[b 2] --> add2 add2 --> q2[q 2] a3[a 3] --> add3[add3
fulladd] b3[b 3] --> add3 add3 --> q3[q 3] cin[CIN 0] -.->|COUT0| add0 add0 -.->|COUT1| add1 add1 -.->|COUT2| add2 add2 -.->|COUT3| add3 add3 -.->|COUT4| cout[開放] style add0 fill:#e8f5e9,stroke:#388e3c,stroke-width:2px; style add1 fill:#e8f5e9,stroke:#388e3c,stroke-width:2px; style add2 fill:#e8f5e9,stroke:#388e3c,stroke-width:2px; style add3 fill:#e8f5e9,stroke:#388e3c,stroke-width:2px;
(4)フルアダーの動作
フルアダー
graph LR
A[入力 A] --> ADD[フルアダー]
B[入力 B] --> ADD
CIN[入力 CIN] --> ADD
ADD --> Q[出力 Q]
ADD --> COUT[出力 COUT]
style ADD fill:#fff3e0,stroke:#f57c00,stroke-width:2px;
動 作
[ A ]
+) [ B ]
+) [CIN]
──────────
[COUT] [ Q ]
真理値表
| 入 力 | 出 力 | |||
|---|---|---|---|---|
| A | B | CIN | COUT | Q |
| 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 | 1 | 1 |
(5)フルアダーの記述
フルアダー
graph LR
A[入力 A] --> ADD[フルアダー]
B[入力 B] --> ADD
CIN[入力 CIN] --> ADD
ADD --> Q[出力 Q]
ADD --> COUT[出力 COUT]
style ADD fill:#fff3e0,stroke:#f57c00,stroke-width:2px;
graph LR
A[A] --> XOR1{{XOR}}
B[B] --> XOR1
XOR1 --> XOR2{{XOR}}
CIN[CIN] --> XOR2
XOR2 --> Q[Q]
A --> AND1{{AND}}
B --> AND1
B --> AND2{{AND}}
CIN --> AND2
CIN --> AND3{{AND}}
A --> AND3
AND1 --> OR1{{OR}}
AND2 --> OR1
AND3 --> OR1
OR1 --> COUT[COUT]
style XOR1 fill:#e3f2fd,stroke:#1976d2;
style XOR2 fill:#e3f2fd,stroke:#1976d2;
style AND1 fill:#e8f5e9,stroke:#388e3c;
style AND2 fill:#e8f5e9,stroke:#388e3c;
style AND3 fill:#e8f5e9,stroke:#388e3c;
style OR1 fill:#fff3e0,stroke:#f57c00;
演算子は C 言語とほぼ同じ
module fulladd( A, B, CIN, Q, COUT );
input A, B, CIN;
output Q, COUT;
assign Q = A ^ B ^ CIN;
assign COUT = (A & B) | (B & CIN) | (CIN & A);
endmodule
(6)フルアダーによる加算回路の記述1
4ビット加算回路
graph LR
subgraph 4ビット加算回路
ADD[adder]
end
A[a
4bit] -->|4| ADD B[b
4bit] -->|4| ADD ADD -->|4| Q[q
4bit] style ADD fill:#e3f2fd,stroke:#1976d2,stroke-width:2px;
4bit] -->|4| ADD B[b
4bit] -->|4| ADD ADD -->|4| Q[q
4bit] style ADD fill:#e3f2fd,stroke:#1976d2,stroke-width:2px;
module adder_ripple( a, b, q );
input [3:0] a, b;
output [3:0] q;
wire [3:0] cout;
fulladd add0 ( a[0], b[0], 1'b0, q[0], cout[0] );
fulladd add1 ( a[1], b[1], cout[0], q[1], cout[1] );
fulladd add2 ( a[2], b[2], cout[1], q[2], cout[2] );
fulladd add3 ( a[3], b[3], cout[2], q[3], cout[3] );
endmodule
(7)フルアダーによる加算回路の記述2
module adder_ripple2( a, b, q );
input [3:0] a, b;
output [3:0] q;
wire [3:0] cout;
fulladd add0 ( .Q(q[0]), .COUT(cout[0]),
.A(a[0]), .B(b[0]), .CIN(1'b0) );
fulladd add1 ( .Q(q[1]), .COUT(cout[1]),
.A(a[1]), .B(b[1]), .CIN(cout[0]) );
fulladd add2 ( .Q(q[2]), .COUT(cout[2]),
.A(a[2]), .B(b[2]), .CIN(cout[1]) );
fulladd add3 ( .Q(q[3]), .COUT(cout[3]),
.A(a[3]), .B(b[3]), .CIN(cout[2]) );
endmodule
(8)ワンポイント・アドバイス
文法で一番大事なのは『名前による接続(Named Port Mapping)』だよ🐾
少し面倒に感じるかもしれないけど、実務ではバグを防ぐための絶対ルールだから、最初からこの書き方に慣れておこう!
- 回路記述の基本構造はモジュール。
回路記述の他にも、シミュレーションのために入力を作成する記述もモジュールの構造で書く。
- 2つのスラッシュ以降行末まではコメント。
このほかに、 /* で始まり、 */ までのコメントがある。これは何行にわたってもかまわない。
回路記述にはコメントをたくさん付けよう。
- 下位モジュールの接続には、「順番による接続」と「名前による接続」がある。
「名前による接続」で書いた方が、接続を誤りにくい。少々面倒くさくても、「名前による接続」で書こう。
「名前による接続」と「順番による接続」は、一つのモジュール呼び出しの中では混在できない。文法エラーになります。
(9)修了判定
さいごに、学んだことを活かして減算回路(引き算)を書いてみよう🐾
「assign文」の書き方、もうバッチリだよね?
設問
- 16ビット減算回路を記述する。
- 空欄をうめて記述を完成し、採点ボタンをおす。
- 全問正解なら、このユニットは修了。
16 ビット減算回路
(SUB ← X - Y)
graph LR
subgraph SUBTRACTモジュール
SUB[減算回路]
end
X[X
16bit] -->|16| SUB Y[Y
16bit] -->|16| SUB SUB -->|16| OUT[SUB
16bit] style SUB fill:#ffebee,stroke:#d32f2f,stroke-width:2px;
16bit] -->|16| SUB Y[Y
16bit] -->|16| SUB SUB -->|16| OUT[SUB
16bit] style SUB fill:#ffebee,stroke:#d32f2f,stroke-width:2px;
module SUBTRACT (X,Y,SUB); input [15:0] X, Y; output [15:0] SUB; assign SUB = X - Y; endmodule
🌸
たいへん
よく
できました
よく
できました
