SystemVerilogではVerilogに対してビット操作の機能が拡張されています。ここでは論理合成にも使える機能を見ていきましょう。
筆者は頻繁に使うわけではないですが、うまくはまるとコードが非常に簡潔に書けることがあります。
Streaming Operator
最初にStreaming Operatorです。LRMによるとbit streamはUnpack/Pack変換を主用途としているようなのですが、これはシミュレーション用の機能です。おまけ的な機能として(おまけにしては便利すぎる機能なのですが)ビットの順番を変換する機能があり、こちらは論理合成でサポートされています。
Stream Operatorには<<と>>の2種類があるのですが、RTL記述に役立つのは<<のみです。
以下の例のように使用し、<<nのnビット毎に位置を逆に並び替えます。
program top();
initial begin
logic [7:0] v0 = 8'b0011_1101; // 8'h3d
logic [7:0] v1, v2;
v1 = {<<1{v0}}; // Bit reverse (8'b1011_1100)
v2 = {<<4{v0}}; // 4 Bit chunk reverse (8'b1101_0011 or 8'hd3)
$display("%08b", v1);
$display("%08b", v2);
end
endprogram
ベクタのビット選択
Verilog/Systemverilogではベクタから複数ビットを選択するのに変数が使用できないため、以下のようなコードはコンパイルエラーとなります。
module u1(output logic [3:0] vout,
input logic [7:0] vin,
input logic [2:0] sel // Assuming ranges 0 to 4
);
assign vout = vin[sel + 3 : sel]; // Verilog/SystemVerilog do not support!!
endmodule
同等の機能を実現するには以下のようにコードを書き直す必要があります。ちょっと面倒ですね。
module u1(output logic [3:0] vout,
input logic [7:0] vin,
input logic [2:0] sel // Assuming ranging 0 to 4
);
always_comb begin
for (int i = 0; i < 4; i++) begin
vout[i] = vin[sel + i];
end
end
endmodule
SystemVerilogでは以下のように簡潔に書くことができます。n+:mで、ベクタのnビット目からmビット幅を表します。同様にn-:mでnビット目からマイナス方向にmビット幅を表します。
nには変数を使えますがmは定数でなければなりません。
module u1(output logic [3:0] vout,
input logic [7:0] vin,
input logic [2:0] sel // Assuming ranging 0 to 4
);
assign vout = vin[sel +: 4]; // i.e. vout[3:0] = vin[sel + 3 : sel]
endmodule
上記は代入式の右辺に使用していますが、左辺にも使用できます。
module u1(output logic [7:0] vout,
input logic [3:0] vin,
input logic [2:0] sel // Assuming ranging 0 to 4
);
always_comb begin
vout = 0;
vout[sel +: 4] = vin; // i.e. vout[sel + 3 : sel] = vin[3:0]
end
endmodule
複合技
上記の2つを利用して、さらに複雑なビット操作ができます。以下は32bit x 4set=128bit長のデータを受け取り、
32bit毎にエンディアンを変換して出力するモジュールの例です。
module EndianConverter(output logic[127:0] vout,
input logic[127:0] vin);
localparam BIT_CHUNK = 32;
always_comb begin
for (int i = 0; i < 127; i += BIT_CHUNK) begin
vout[i +: BIT_CHUNK] = {<<8{vin[i +: BIT_CHUNK]}};
end
end
endmodule
上記モジュールのテストプログラムです。
module top;
logic [127:0] vin;
logic [127:0] vout;
EndianConverter u1(.vout(vout), .vin(vin));
initial begin
vin = 128'h01234567_89abcdef_01020304_05060708;
#1ns;
$display("VIN: %032h", vin);
$display("VOUT: %032h", vout); // 67452301_efcdab89_04030201_08070605
$display("");
vin = 128'h090a0b0c_0d0e0f00_11121314_15161718;
#1ns;
$display("VIN: %032h", vin);
$display("VOUT: %032h", vout); // 0c0b0a09_000f0e0d_14131211_18171615
end
endmodule
参考文献
"11.4.14 Streaming operators (pack/unpack)". 1800-2017 - IEEE Standard for SystemVerilog--Unified Hardware Design, Specification, and Verification Language. pp. 274-278.
"11.5.1 Vector bit-select and part-select addressing". 1800-2017 - IEEE Standard for SystemVerilog--Unified Hardware Design, Specification, and Verification Language. pp. 279-281.