役立つ小さな2つの演算子
Verilogには{}の2つの形があります:
- 連結(concatenation) -
{a, b, c}- 複数のsignalを1つの広いものに貼り合わせる。 - 繰り返し(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'b0、1'b1、4'd0と書き、{}内に裸の0や1を入れないでください。
左辺の連結
代入の左辺の連結は、広い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}}の意味は?
繰り返し演算子です。patternのNコピーを連結したものを生成します。{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文で入れます。各ターゲットは幅を保ち、パーサが右辺のビットを適切に分配します。