Menu

Verilog 連結と繰り返し:{}演算子

{}でsignalを貼り合わせ、{N{...}}でパターンをN回コピーする方法。ピースから広いバスを構築するための欠かせないVerilog演算子。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

役立つ小さな2つの演算子

Verilogには{}の2つの形があります:

  1. 連結(concatenation) - {a, b, c} - 複数のsignalを1つの広いものに貼り合わせる。
  2. 繰り返し(replication) - {N{pattern}} - パターンをN回コピーする。

両方とも常に使うことになります。バスを分割、結合、パディング、符号拡張する必要があるたびに、この2つの演算子のどちらかが答えです。

連結:{a, b, c}

最もシンプルなケース。2つのsignalを積み重ねて1つにする:

左から右に読みます:aが上位半分、bが下位半分。{}内の最初のオペランドが最上位です。結果の幅はオペランドの幅の合計です。

任意の数のオペランドを連結できます:

wire [31:0] word = {byte3, byte2, byte1, byte0};

オペランドは異なる幅でも構いません:

wire [11:0] frame = {start_bit, data_byte, parity, stop_bit};
//                    1ビット      8ビット     1ビット   2ビット  = 12

一般的なルール:連結のオペランドはすべて明示的な幅を持たなければなりません。サイズなしリテラル(1'b1の代わりに1)はエラーを引き起こします。パーサは何ビット割り当てるか分からないからです。常に1'b01'b14'd0と書き、{}内に裸の01を入れないでください。

左辺の連結

代入の左辺の連結は、広いsignalをパーツに 分割 します:

これがキャリー付きadderの定番パターンです。{carry_out, sum_bits}は代入の左辺で単一の9ビット宛先になり、ビットが分配されます。上位ビットがcarry_outへ、下位8ビットがsum_bitsへ。

LHSの連結はassignでも手続きブロックでも動きます:

always @(posedge clk) begin
    {high_byte, low_byte} <= incoming_word;
end

繰り返し:{N{pattern}}

繰り返し演算子はパターンを固定回数コピーします。形は{N{pattern}} - カウント、それから波括弧で囲まれたパターン:

Nは定数でなければなりません。elaborationで結果の幅を知る必要があるからです。パターンはリテラル、parameter、または幅が分かっている任意の式にできます。

内側の波括弧を忘れないでください。{8 1'b1}は構文エラーで、{8{1'b1}}が正しいです。外側の{}が演算子、内側の{...}が繰り返されるパターンを包みます。

両方を組み合わせる:符号とゼロ拡張

2つの演算子は自然に組み合わさり、より広い値を構築します:

{ {8{a_negative[7]}}, a_negative }が標準的な符号拡張パターンです。「符号ビットを8回繰り返し、元の8ビットを追加」と読みます。最終結果:同じ数値の16ビット2の補数表現。

unsignedの幅拡張では代入に頼れます。宛先がより広いとき、Verilogは自動的にゼロ拡張します:

wire [15:0] zext_auto = a_unsigned;   // 動く、上位8ビットは0

しかし符号拡張は、オペランドと宛先 両方signedと宣言されない限り暗黙には行われません。明示的な繰り返しイディオムの方が安全です。

有用なパターン

N個の1のマスクを構築

parameter N = 5;
wire [31:0] mask = { {32-N{1'b0}}, {N{1'b1}} };
// 例えばN=5なら mask = 32'h0000_001F

Vectorの反転

Verilogには組み込みのreverseはありませんが、連結でインラインに書けます:

wire [3:0] forward  = 4'b1010;
wire [3:0] reversed = {forward[0], forward[1], forward[2], forward[3]};
// reversed = 4'b0101

幅広いvectorなら、generateループやfunctionの方がきれいです。

フラグをステータスバイトに詰める

wire [7:0] status = {
    error_flag,    // [7]
    overflow,      // [6]
    underflow,     // [5]
    ready,         // [4]
    busy,          // [3]
    1'b0,          // [2] 予約
    1'b0,          // [1] 予約
    valid          // [0]
};

1ビットの予約スロットは明示的な幅を持ちます。{}内に裸の0を入れないでください。

よくある間違い

{}内のサイズなしリテラル。 {a, 0}は構文エラーまたは32ビット幅のゼロです。常にサイズ:{a, 1'b0}

繰り返しで内側の波括弧を忘れる。 {8 1'b1}はパースしません。{8{1'b1}}はします。

順序の混乱。 {a, b}aを上位、bを下位に置きます。仮定を逆にすれば、どこかで反転したバイトオーダーになります。

0回の繰り返し。 {0{...}}は標準Verilogでは違法です。SystemVerilogは許容します(幅0の結果を生成)。プレーンVerilogは拒否します。

次に読むもの

これでVerilogの演算子をすべて見ました。次の章はギアを切り替え、構造に飛び込みます。moduleがどう互いに接続するか、ポートのルール、より大きな設計を構築するためのサブmoduleインスタンス化の構文です。

よくある質問

Verilogでsignalを連結するには?

波括弧を使います。{a, b, c}はオペランドを左から右に貼り合わせて1つの広いvectorを生成します。aのMSBが結果のMSBになり、cのLSBが結果のLSBになります。結果の幅はオペランドの幅の合計です。連結は代入の右辺(値を構築)でも左辺(値を分割)でも動きます。

Verilogの{N{pattern}}の意味は?

繰り返し演算子です。patternNコピーを連結したものを生成します。{8{1'b1}}は8ビットの全1値(8'hFF)です。{4{2'b10}}は8ビット値8'b10101010です。繰り返しは、ゼロ拡張、符号拡張、手で書かずに繰り返しビットパターンを構築する方法です。

Verilogでsignalを符号拡張するには?

符号ビットを繰り返す繰り返しを使います:{ {24{a[7]}}, a }は8ビットaを、ビット7(符号ビット)を24回繰り返してから元を追加して32ビットに拡張します。unsignedのゼロ拡張には、1'b0を繰り返すか、宛先がより広いなら代入に暗黙に行わせます。

代入の左辺で連結を使えますか?

はい。複数のターゲットに1つの広いソースから代入する方法です。{carry, sum} = a + b;は加算の結果をcarry(上位ビット)とsum(残り)に1文で入れます。各ターゲットは幅を保ち、パーサが右辺のビットを適切に分配します。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める