Verylの(自分が提案・実装した)便利機能を紹介する
Verylは新興のHDLです。開発初期から機能の提案や実装などで、協力させていただいております。石谷が提案したVerylの便利機能を紹介したいと思います。
clock
/reset
型
RTL設計においてクロック・リセットは特別な信号ですが、従来のHDLにはそれらを特別扱いする機能は無く、通常の信号として扱われます。ですので、順序回路を記述する際は、毎度毎度、クロック・リセットを指定する必要があり、非常に面倒です。
always_ff @(posedge i_clk, negedge i_rst_n) begin
if (!i_rst_n) begin
end else begin
end
end
always_ff @(posedge i_clk, negedge i_rst_n) begin
if (!i_rst_n) begin
end else begin
end
end
また、クロック・リセット以外の信号も指定できるので、バグの元になりえます。
VerylはRTL設計に特化したHDLなので、クロック・リセットを表す特別の型として、clock
/reset
型が導入されています。これらの型を導入することで、Verylコンパイラはどの信号がクロック・リセットかを簡単に判別できるようになり、モジュールに入力されるクロック・リセットが1組しかない場合、always_ff
でクロック・リセットの指定を省略することができます。
module ModuleA (
i_clk: input clock,
i_rst: input reset,
i_d : input logic,
o_d : output logic,
) {
var d: logic;
assign o_d = d;
// クロック・リセットを指定しなくても良い
always_ff {
if_reset {
d = 0;
} else {
d = i_d;
}
}
}
Verylコンパイラはi_clk
/i_rst
がクロック・リセットだと判別できるので、SystemVerilog生成時にこれらの信号をalways_ff
文に自動的に挿入します。
module project_ModuleA (
input logic i_clk,
input logic i_rst,
input logic i_d ,
output logic o_d
);
logic d;
always_comb o_d = d;
always_ff @ (posedge i_clk, negedge i_rst) begin
if (!i_rst) begin
d <= 0;
end else begin
d <= i_d;
end
end
endmodule
複数のクロック・リセットが入力されている場合、クロック境界を跨ぐ際に適切な処理ができていないと、重大なバグに繋がります。この種の問題の検出には、高価なEDAツールの導入が不可欠です。(自分が導入したわけではないですが)Verylでは、ある信号がどのクロックドメインに所属するかを指定することができるので、意図しないクロック境界跨ぎを検出することができます。
リセットの抽象化
リセットの記述は、ASCIでは非同期リセット/負極性、FPGAでは同期リセット/正極性で記述するのが良いとされています。しかし、この両方に適応した記述を従来のHDLで記述することは非常に困難です。
always_ff @(posedge i_clk, negedge i_rst_n) begin
if (!i_rst_n) begin
end else begin
end
end
always_ff @(posedge i_clk) begin
if (i_rst) begin
end else begin
end
end
しかし、Verylでは、reset
型とリセット時の動作を記述するif_reset
の導入により、リセットの極性および同期・非同期は隠匿され、SystemVerilogコード生成時に選択できるようになっています。つまり、以下のVerylコードから対象に適したリセット論理を持つSystemVerilogコードが生成できると言う訳です。
always_ff {
if_reset {
d = 0;
} else {
d = i_d;
}
}
if
式とcase
式
最近のオシャレ言語では、従来、制御構文であったif
やcase
が式として使えることが多いです。文中で同じ変数に代入する場合、これらの構文を式として使えると、代入が一か所だけになって、(ちょっとだけ)記述を簡略化できます。
例えば、カウンターの記述はcase
式を使うと、以下の様に書くことができます。
count = case {i_up, i_down} {
2'b10 : count + 1,
2'b01 : count - 1,
default: count
};
式なので、当然、以下の様にも書くこともできます。
count += case {i_up, i_down} {
2'b10 : +1,
2'b01 : -1,
default: 0
};
しかし、SystemVerilogにはif
式/case
式は無いので、これらの記述は三項演算子を使って展開されます。
count <= ((({i_up, i_down}) ==? (2'b10)) ? (
count + 1
) : (({i_up, i_down}) ==? (2'b01)) ? (
count - 1
) : (
count
));
処理系によっては、if
文やcase
文と違って、MUXの最適化が効かないので、合成で不利になるかもしれません。