合成に強い書き方を学ぶ
(1)ここで学ぶ内容
論理合成ツールはどんな記述でも最適な回路を合成してくれるというわけではありません。ここでは論理合成したときにより良い回路ができる記述を教えます。
論理合成は気難しいツールですので気を使わないといけないと思ってください。
- 記述の違いにより論理合成後の結果に良否があることを理解する
- case 文や演算子の論理合成に適した使い方を学ぶ
論理合成ツールの能力を引き出すために、回路記述における以下の留意点を理解する
- case 文における default の効用
- 複数ビット選択式の分割
- 条件演算子の多重ネスティング
- 関係演算子の使用
- 同一処理の繰り返し
- レジスタ代入の鉄則
記述の違いによる、合成結果を確認する
(2)case 文における default の効用
case 文をつかって 8to3 エンコーダを記述する二つの例を比較してみましょう。
このエンコーダの入力は 8 本あり特定の一本のみが 1 で他の 7 本は 0 になっています。出力は入力の 1 になっているビット位置に応じた値になります。
例えば入力のビット 0 が 1 で他のビットがすべて 0 のときは出力は 000 になります。またビット 3 が 1 で他のビットがすべて 0 のときは、出力は 011 になります。
入力は 8 ビットなので、そのとりうる値は 256 種類ありますが、ここではそのうちの 8 種類しか使っていません。そのほかについては最後に default を書いて対応します。default はそれ以前に記述された項目以外の項目すべてという意味です。
defaultには出来るだけ x を代入する
そのときに出力される値は不定値を出力するように記述してください。論理合成ツールは出力の不定値を don't care として扱います。つまりここでは記述された 8 つの項目以外の入力がきたときは、出力は何でもよいと判断されます。論理合成はこの条件をつかって最適化を行い回路規模は小さくなります。
回路面積は増加し、速度も遅くなる
出力に don't care をかかずに固定値を記述した場合には、その値を出力するような回路を合成するため、回路規模は大きくなります。
もふねこ:
case文を使うときは、絶対に default を書くのを忘れないでね🐾!
default で x (Don't care) を指定してあげると、ツールが「ここは適当でいいんだな」って解釈してくれて、回路が劇的に小さく・速くなることがあるんだよ。ちょっとした書き方の違いで、魔法みたいに結果が変わるから面白いよね!
✅ ENCODER_X (default に 3'bxxx を代入)
module ENCODER_X(A,Y);
input [7:0] A;
output [2:0] Y;
reg [2:0] Y;
always @( A )
case ( A )
8'b00000001 : Y <= 3'b000;
8'b00000010 : Y <= 3'b001;
8'b00000100 : Y <= 3'b010;
8'b00001000 : Y <= 3'b011;
8'b00010000 : Y <= 3'b100;
8'b00100000 : Y <= 3'b101;
8'b01000000 : Y <= 3'b110;
8'b10000000 : Y <= 3'b111;
default : Y <= 3'bxxx;
endcase
endmodule
❌ ENCODER_1 (default に 3'b111 を代入)
module ENCODER_1(A,Y);
input [7:0] A;
output [2:0] Y;
reg [2:0] Y;
always @( A )
case ( A )
8'b00000001 : Y <= 3'b000;
8'b00000010 : Y <= 3'b001;
8'b00000100 : Y <= 3'b010;
8'b00001000 : Y <= 3'b011;
8'b00010000 : Y <= 3'b100;
8'b00100000 : Y <= 3'b101;
8'b01000000 : Y <= 3'b110;
8'b10000000 : Y <= 3'b111;
default : Y <= 3'b111;
endcase
endmodule
これらの記述を論理合成し、合成後の回路図とレポートをみて結果を比較してみましょう。
ENCODER_X (default: 3'bxxx) 合成回路図の構造詳細
- 入力ポート: A[7], A[6], A[5], A[4], A[3], A[2], A[1], A[0]
- 出力ポート: Y[2], Y[1], Y[0]
- 構造的特徴: 入力信号から必要なビットのみを選択し、わずか3つのORゲート(またはNOR/NANDの等価論理)のみが並行して配置されている。非常にシンプルで無駄がなく、遅延も最小限(データ到着時刻 1.35)に抑えられている。
ENCODER_1 (default: 3'b111) 合成回路図の構造詳細
- 構造的特徴: default時に特定の値(111)を出力する制約を満たすため、don't careの最適化が効かず、AND, OR, NOT, XORゲートなど多数のゲートが複雑に直列・並列に入り乱れる巨大な回路が生成されている。
- 影響: セル数が3から20へ、遅延が1.35から6.58へと大幅に悪化していることが視覚的な複雑さからも読み取れる。
✅ ENCODER_X 合成結果レポート
*****************************************
レポート: area
回路 : ENCODER_X
*****************************************
ポート数: 11
ネット数: 10
セル数: 3
セル種類: 1
組み合わせ回路: 9
非組み合わせ回路: 0
合計: 9
*****************************************
レポート: timing
回路 : ENCODER_X
*****************************************
ライブラリ: hd350s
配線遅延モデル: hd350s_05k
コンディション: MAX567
-----------------------------------------
A[5] (in) 0.00 0.00
Y[2] (out) 0.00 1.35
データ到着時刻 1.35
-----------------------------------------
❌ ENCODER_1 合成結果レポート
*****************************************
レポート: area
回路 : ENCODER_1
*****************************************
ポート数: 11
ネット数: 28
セル数: 20
セル種類: 10
組み合わせ回路: 33
非組み合わせ回路: 0
合計: 33
*****************************************
レポート: timing
回路 : ENCODER_1
*****************************************
ライブラリ: hd350s
配線遅延モデル: hd350s_05k
コンディション: MAX567
-----------------------------------------
A[4] (in) 0.00 0.00
Y[1] (out) 0.00 6.58
データ到着時刻 6.58
-----------------------------------------
(3)複数ビット選択式の分割
アドレスデコーダは case 文をつかって記述すると簡単に記述できます。その場合 case 文の選択式にはアドレス ADDR を使います。
しかし、論理合成ツールは選択式のビット幅が大きくなるほど論理圧縮しにくくなります。当然回路規模も大きくなります。
32 ビットアドレスデコーダ
case ( ADDR )
32'b01000000_00000000_11110110_11100110: DOUT <= 5'b00000;
32'b01000000_00000000_11110111_11110110: DOUT <= 5'b00010;
32'b01000000_00000000_11010110_11100110: DOUT <= 5'b00011;
32'b01000000_00000000_11110110_11110110: DOUT <= 5'b00100;
32'b10000000_00000000_11100110_11100111: DOUT <= 5'b00101;
32'b10000000_00000000_11110110_11100110: DOUT <= 5'b00110;
...
たとえばこの例のように選択式 ADDR のビット 29 からビット 16 までが 0 になっているときはその部分を if 文の条件式にすることで case 文を小さくすることができます。さらにビット 31 とビット 30 を条件式にして、if 文で場合分けできます。
選択式を分割した記述
if ( ADDR[29:16]==0 )
if( ADDR[31:30]==2'b01 )
case ( ADDR[15:0] )
16'b11110110_11100110: DOUT <= 5'b00000;
16'b11110111_11110110: DOUT <= 5'b00010;
...
endcase
else if ( ADDR[31:30]==2'b10 )
case ( ADDR[15:0] )
16'b11100110_11100111: DOUT <= 5'b00101;
16'b11110110_11100110: DOUT <= 5'b00110;
...
endcase
(4)条件演算子の多重ネスティング
8to1 セレクタを記述してみましょう。セレクタはいろいろな書き方で記述できますが、ここでは条件演算子と case 文で記述し比較してみます。
条件演算子で 8to1 セレクタを記述する場合は 7 個の条件演算子をネスティングさせて記述します。
case 文で記述したものと比較してみると、条件演算子による記述の方が記述量は少なくなりますが、論理合成した後の回路規模は大きくなります。分岐の条件が多数あるときは、case 文の方が回路規模が小さくなります。
条件演算子による記述
module SEL_COND(A,SEL,Y);
input [7:0] A;
input [2:0] SEL;
output Y;
assign Y = (SEL == 3'b000) ? A[0]:
(SEL == 3'b001) ? A[1]:
(SEL == 3'b010) ? A[2]:
(SEL == 3'b011) ? A[3]:
(SEL == 3'b100) ? A[4]:
(SEL == 3'b101) ? A[5]:
(SEL == 3'b110) ? A[6]:
A[7];
endmodule
case文による記述
module SEL_CASE(A,SEL,Y);
input [7:0] A;
input [2:0] SEL;
output Y;
reg Y;
always @( A or SEL) begin
case ( SEL )
3'b000: Y <= A[0];
3'b001: Y <= A[1];
3'b010: Y <= A[2];
3'b011: Y <= A[3];
3'b100: Y <= A[4];
3'b101: Y <= A[5];
3'b110: Y <= A[6];
3'b111: Y <= A[7];
default: Y <= 1'bx;
endcase
end
endmodule
SEL_COND (条件演算子の多重ネスティング) 合成回路図の構造詳細
- 構造的特徴: 多数のマルチプレクサ(MUX)や論理ゲートが直列(カスケード)に何段も連なる構造になっている。
- 影響: 信号が左から右へ通過する際のゲート段数が深く、クリティカルパスが長くなるため、遅延時間(データ到着時刻 7.24)が大きくなっている。
SEL_CASE (case文による分岐) 合成回路図の構造詳細
- 構造的特徴: 各選択肢が並列な木構造(ツリー構造)として展開され、入力から出力までのゲート段数が浅く均等に配置されている。
- 影響: 信号が通過するゲート段数が少なくなるよう最適化されており、遅延時間(データ到着時刻 3.72)が大幅に短縮されている。
❌ SEL_COND 合成結果レポート
*****************************************
レポート: area
回路 : SEL_COND
*****************************************
ポート数: 12
ネット数: 27
セル数: 16
セル種類: 5
組み合わせ回路: 27
非組み合わせ回路: 0
合計: 27
*****************************************
レポート: timing
回路 : SEL_COND
*****************************************
ライブラリ: hd350s
配線遅延モデル: hd350s_05k
コンディション: MAX567
-----------------------------------------
SEL[0] (in) 0.00 0.00
Y (out) 0.00 7.24
データ到着時刻 7.24
-----------------------------------------
✅ SEL_CASE 合成結果レポート
*****************************************
レポート: area
回路 : SEL_CASE
*****************************************
ポート数: 13
ネット数: 21
セル数: 10
セル種類: 4
組み合わせ回路: 18
非組み合わせ回路: 0
合計: 18
*****************************************
レポート: timing
回路 : SEL_CASE
*****************************************
ライブラリ: hd350s
配線遅延モデル: hd350s_05k
コンディション: MAX567
-----------------------------------------
SEL[0] (in) 0.00 0.00
Y (out) 0.00 3.72
データ到着時刻 3.72
-----------------------------------------
(5)関係演算子の使用
信号が特定の値になっているかを判断する場合には、関係演算子を使用します。
大小比較( <,> )は回路が大きくなりやすい
出来るだけ等号( == )を使用する
reg [3:0] CNT;
always @( posedge CLK or posedge RES )
if ( RES==1'b1 )
CNT <= 4'h0;
else if ( CNT>=4'h9 )
CNT <= 4'h0;
else
CNT <= CNT + 4'h1;
(6)同一処理の繰り返し
wire CLK,RES;
reg COUT; // キャリー信号
reg [3:0] LSB,MSB; // 1の桁、10の桁
always @(posedge CLK or posedge RES)
if (RES == 1'b1)
LSB <= 4'h0;
else if (LSB == 4'h9)
LSB <= 4'h0;
else
LSB <= LSB + 4'h1;
always @(posedge CLK or posedge RES)
if (RES == 1'b1)
MSB <= 4'h0;
else if (LSB == 4'h9)
if (MSB == 4'h9)
MSB <= 4'h0;
else
MSB <= MSB + 4'h1;
always @(LSB or MSB)
if (LSB == 4'h9 && MSB == 4'h9)
COUT <= 1'b1;
else
COUT <= 1'b0;
wire LSB9,MSB9;
assign LSB9 = (LSB == 4'h9);
assign MSB9 = (MSB == 4'h9);
wire CLK,RES;
reg COUT; // キャリー信号
reg [3:0] LSB,MSB; // 1の桁、10の桁
always @(posedge CLK or posedge RES)
if (RES == 1'b1)
LSB <= 4'h0;
else if ( LSB9 )
LSB <= 4'h0;
else
LSB <= LSB + 4'h1;
always @(posedge CLK or posedge RES)
if (RES == 1'b1)
MSB <= 4'h0;
else if ( LSB9 )
if ( MSB9 )
MSB <= 4'h0;
else
MSB <= MSB + 4'h1;
always @(LSB or MSB)
if ( LSB9 && MSB9 )
COUT <= 1'b1;
else
COUT <= 1'b0;
(7)レジスタ代入の鉄則
非同期リセット付フリップフロップ
正しい記述
always @(posedge CLK or posedge RES) begin
if (RES == 1'b1)
Q <= 1'b0;
else
Q <= P;
end
❌
不完全な記述
always @(RES) begin
if (RES == 1'b1)
Q <= 1'b0;
end
always @(posedge CLK) begin
Q <= P;
end
(8)ワンポイント・アドバイス 1
合成ツールのバージョンの違いでも合成結果が変わる。
❌ ≠ ❌
(9)ワンポイント・アドバイス 2
always @(A or B or C or ZIN or YIN) begin
if (A==1'b1 && B==1'b0) begin
DOUT <= 1'b1;
EOUT <= ZIN;
FOUT <= 1'b0;
GOUT <= 1'b0;
end else if (A==1'b0 && B==1'b0) begin
DOUT <= 1'b0;
EOUT <= YIN;
FOUT <= 1'b0;
GOUT <= 1'b0;
end else if (A==1'b0 && B==1'b1) begin
DOUT <= 1'b0;
EOUT <= YIN;
FOUT <= 1'b1;
GOUT <= 1'b0;
end else if (C==1'b1) begin
DOUT <= 1'b1;
EOUT <= ZIN;
FOUT <= 1'b1;
GOUT <= 1'b1;
end else begin
DOUT <= 1'b1;
EOUT <= ZIN;
FOUT <= 1'b1;
GOUT <= 1'b0;
end
end
always @(A or ZIN or YIN) begin
if (A==1'b1) begin
DOUT <= 1'b1;
EOUT <= ZIN;
end else begin
DOUT <= 1'b0;
EOUT <= YIN;
end
end
always @(B) begin
if (B==1'b1)
FOUT <= 1'b1;
else
FOUT <= 1'b0;
end
always @( A or B or C) begin
if (A==1'b1 && B==1'b1 && C==1'b1)
GOUT <= 1'b1;
else
GOUT <= 1'b0;
end
関連の高い入出力信号はひとつの always 文にする
(10)修了判定
以下の仕様で書かれた 2 つの記述を論理合成すると、回路規模が小さくなるのはどちらか?
合成ツールを立ち上げて、論理合成する。
記述の内容を確認し、回路構造を確かめる。
LAMP サイコロカウンタ
- 入力信号: CLK, RES, EN
- 出力信号: LAMP
- EN=1 の時、サイコロ回転
- EN=0 の時、サイコロ停止
- RES=1 の時、リセット( 1の目を表示 )
LAMP[3]
| 表示 | LAMP 6 5 4 3 2 1 0 | 表示 | LAMP 6 5 4 3 2 1 0 |
|---|---|---|---|
| ● | 0 0 0 1 0 0 0 | ● ● ● ● | 1 0 1 0 1 0 1 |
| ● ● | 1 0 0 0 0 0 1 | ● ● ● ● ● | 1 0 1 1 1 0 1 |
| ● ● ● | 0 0 1 1 1 0 0 | ● ● ● ● ● ● | 1 1 1 0 1 1 1 |
SAIKORO1の回路図には多数の論理ゲート(AND, OR, NOT)と複数のDフリップフロップが含まれます。SAIKORO2の回路図はそれ以上に複雑な論理ゲート群から構成されます。
✅ SAIKORO1 合成結果レポート
*****************************************
レポート: area
回路 : SAIKORO1
*****************************************
ポート数: 10
ネット数: 26
セル数: 20
セル種類: 10
組み合わせ回路: 24
非組み合わせ回路: 26
合計: 50
❌ SAIKORO2 合成結果レポート
***************************************** レポート表示 *****
レポート: area
回路 : SAIKORO2
*****************************************
ポート数: 10
ネット数: 38
セル数: 30
セル種類: 15
組み合わせ回路: 37
非組み合わせ回路: 62
合計: 99