合成に強い書き方を学ぶ

(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 はそれ以前に記述された項目以外の項目すべてという意味です。

case文は必ず default を使用する
defaultには出来るだけ x を代入する

そのときに出力される値は不定値を出力するように記述してください。論理合成ツールは出力の不定値を don't care として扱います。つまりここでは記述された 8 つの項目以外の入力がきたときは、出力は何でもよいと判断されます。論理合成はこの条件をつかって最適化を行い回路規模は小さくなります。

defaultに固定値を記述すると、
回路面積は増加し、速度も遅くなる

出力に don't care をかかずに固定値を記述した場合には、その値を出力するような回路を合成するため、回路規模は大きくなります。

もふねこ

もふねこ:
case文を使うときは、絶対に default を書くのを忘れないでね🐾!
defaultx (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
graph LR subgraph 8to3 エンコーダ A0[A[0]] --> BOX[ ] A1[A[1]] --> BOX A2[A[2]] --> BOX A3[A[3]] --> BOX A4[A[4]] --> BOX A5[A[5]] --> BOX A6[A[6]] --> BOX A7[A[7]] --> BOX BOX -->|3| Y[Y] end style BOX fill:#b3e5fc,stroke:#000,stroke-width:2px;

これらの記述を論理合成し、合成後の回路図とレポートをみて結果を比較してみましょう。

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 文の方が回路規模が小さくなります。

条件演算子の多重ネスティングは、回路規模が大きくなる
graph LR subgraph 8to1 セレクタ A0[A[0]] --> BOX[ ] A1[A[1]] --> BOX A2[A[2]] --> BOX A3[A[3]] --> BOX A4[A[4]] --> BOX A5[A[5]] --> BOX A6[A[6]] --> BOX A7[A[7]] --> BOX SEL[SEL] -->|3| BOX BOX --> Y[Y] end style BOX fill:#b3e5fc,stroke:#000,stroke-width:2px;

条件演算子による記述

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)関係演算子の使用

信号が特定の値になっているかを判断する場合には、関係演算子を使用します。

関係演算子: >, <, >=, <=, ==, !=

大小比較( <,> )は回路が大きくなりやすい
出来るだけ等号( == )を使用する

CNT>=4'h9
回路規模小
CNT==4'h9
回路規模小
CNT[3] & CNT[0]
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 文の中で行なう
graph LR P --> DFF[D Q] CLK --> DFF RES --> DFF_R[R] DFF --> Q

非同期リセット付フリップフロップ

正しい記述

always @(posedge CLK or posedge RES) begin
    if (RES == 1'b1)
        Q <= 1'b0;
    else
        Q <= P;
end
graph LR RES --> SR[R Q] GND --> SR_S[S] P --> DFF2[D Q] CLK --> DFF2 SR --> AND( ) DFF2 --> AND AND --> Q

不完全な記述

always @(RES) begin
    if (RES == 1'b1)
        Q <= 1'b0;
end

always @(posedge CLK) begin
    Q <= P;
end

(8)ワンポイント・アドバイス 1

合成ツールが違えば、合成結果も違う。

合成ツールのバージョンの違いでも合成結果が変わる。
graph TD V[.v] -->|回路記述| A1[A社 論理合成ツール バージョン 1.0] V -->|回路記述| A2[A社 論理合成ツール バージョン 2.0] V -->|回路記述| B3[B社 論理合成ツール バージョン 3.0] A1 -->|合成結果| R1[.v] A2 -->|合成結果| R2[.v] B3 -->|合成結果| R3[.v]

❌ ≠ ❌

使っているツールの能力を良く把握し、ツールの能力にあった記述をして高い品質の回路を得よう。

(9)ワンポイント・アドバイス 2

graph LR A --> ALW[always] B --> ALW C --> ALW YIN --> ALW ZIN --> ALW ALW --> DOUT ALW --> EOUT ALW --> FOUT ALW --> GOUT style ALW fill:#b3e5fc,stroke:#000,stroke-width:2px;
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
graph LR A --> ALW1[always] YIN --> ALW1 ZIN --> ALW1 ALW1 --> DOUT ALW1 --> EOUT B --> ALW2[always] ALW2 --> FOUT A_2[A] --> ALW3[always] B_2[B] --> ALW3 C_2[C] --> ALW3 ALW3 --> GOUT style ALW1 fill:#b3e5fc,stroke:#000,stroke-width:2px; style ALW2 fill:#b3e5fc,stroke:#000,stroke-width:2px; style ALW3 fill:#b3e5fc,stroke:#000,stroke-width:2px;
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[0] →
LAMP[1] →
LAMP[2] →
← LAMP[4]
← LAMP[5]
← LAMP[6]

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