ふにゃふにゃHDL [HDL]
最近verilogが多いです。好みはVHDLですが。
検証は(system)verilogが使えると便利だと思います。しかしModelSimの混在シミュレーションライセンスなんて持ってるわけもないので(というかAltera Starter Edition、無料、ありがたや)、やるときはどちらかですが。ホントはCが使いたいですけどね~
そんなverilogの妙ながら便利な記述について。
非同期な動作と同期な動作を記述するとき、例えばリセットが2系統あるとき、
けど、verilogはそんな書き方をする必要なかったりします。
さらにもし非同期と同期のリセット処理が同じだったら、
…うわ~きもちわるい!!けど便利w
下の書き方を見ても「?なにがおかしいの?」と思う人がいるかも知れません。一方「ありえねぇ~!」って人もいるかも知れません。両極端になりそう。私は後者に近いです。けど便利なのでこの記述もしちゃいます。
verilogはVHDLに比べ記述が少ないのですが、この辺の緩さが逆に気持ち悪かったりします。
ところで、alwaysの記述で最初納得のいかなかった所は、非同期のa_resetの扱い。
例えばリセットの極性を切り替えるときはどうすれば良いでしょう?
VHDLでは素直な話です。
検証は(system)verilogが使えると便利だと思います。しかしModelSimの混在シミュレーションライセンスなんて持ってるわけもないので(というかAltera Starter Edition、無料、ありがたや)、やるときはどちらかですが。ホントはCが使いたいですけどね~
そんなverilogの妙ながら便利な記述について。
非同期な動作と同期な動作を記述するとき、例えばリセットが2系統あるとき、
always @(posedge a_reset or posedge c_clock) begin if (a_reset) begin /*非同期リセット処理*/ end else begin if (c_reset) begin /*同期リセット処理*/ end else begin /*同期処理*/ end end endって書いたりします。これは割と普通。VHDLではこんな感じ。
process(a_reset, c_clock) begin if (a_reset = '1') then --非同期リセット処理 elsif (c_clock'event and c_clock = '1') then if (c_reset = '1') then --同期リセット処理 else --同期処理 end if; end if; end process;似てますが、クロックエッジの判定をverilogではalways文で行って、VHDLではif文で行うところが異なります。実はこの書き方、VHDL風に寄せてverilogを書いていて、「非同期処理と同期処理は別ですよ~」って明示するために「a_reset 」のブロックとそれ以外のブロックに大きく分けて書いてます。
けど、verilogはそんな書き方をする必要なかったりします。
always @(posedge a_reset or posedge c_clock) begin if (a_reset) begin /*非同期リセット処理*/ end else if (c_reset) begin /*同期リセット処理*/ end else begin /*同期処理*/ end end非同期処理と同期処理が同じレベルな事に注意。この書き方はVHDLは不許可です。
さらにもし非同期と同期のリセット処理が同じだったら、
always @(posedge a_reset or posedge c_clock) begin if (a_reset || c_reset) begin /*非同期/同期リセット処理*/ end else begin /*同期処理*/ end endという書き方もできます。条件が増えてもOK。
always @(posedge a_reset or posedge c_clock) begin if (a_reset || c_reset) begin /*非同期/同期リセット処理*/ end else if (ready) begin /*同期処理*/ end else begin /*同期処理*/ end endシミュレーションはもちろん、合成してもちゃんとした回路ができてます(全ての処理系で試した訳では無いですが)。c_resetやready周りに変な回路ができるような様子も無さそうです。
…うわ~きもちわるい!!けど便利w
下の書き方を見ても「?なにがおかしいの?」と思う人がいるかも知れません。一方「ありえねぇ~!」って人もいるかも知れません。両極端になりそう。私は後者に近いです。けど便利なのでこの記述もしちゃいます。
verilogはVHDLに比べ記述が少ないのですが、この辺の緩さが逆に気持ち悪かったりします。
ところで、alwaysの記述で最初納得のいかなかった所は、非同期のa_resetの扱い。
例えばリセットの極性を切り替えるときはどうすれば良いでしょう?
正論理 always @(posedge a_reset or posedge c_clock) begin if (a_reset) begin 負論理 always @(negedge a_reset or posedge c_clock) begin if (~a_reset) begin こう書くとシミュレーションできても合成はできない。 always @(a_reset or posedge c_clock) begin if (a_reset) begin 反転してから使う。 wire reset = a_reset ^ pol; always @(posedge reset or posedge c_clock) begin if (reset) beginレベルセンスの信号なのにエッジを指定する必要がある、というのが腑に落ちません。また、posedge→if (areset)、negedge→if (~areset)、の関係は崩れないので冗長です。これまた気持ち悪い。そういうモノだ、と思うしか無いです。
VHDLでは素直な話です。
generic ( pol : std_logic := '0' 極性 … process(a_reset, c_clock) begin if (a_reset = pol) then
Vivado発表 [HDL]
Xilinxの次世代ツール、Vivadoが発表されました。
C/C++/SystemC辺りは「ハイレベル合成」のライセンスが必要らしい。これってAutoESLかな?お値段ちょっとお高い?といっても50万ぐらい。ちょっと前からしたら格安の値段だと思います。
WebPackでもSystemVerilog使えるなら、QuartusIIはだいぶ前からsv使えるので、svメインにしても良いかな~
Vivadoって名前聞いたとき、もちろん「ビビデ・バビデ・ブー」からもじったんだと思いましたよ(笑)。こっちはBでVじゃないですが。
C/C++/SystemC辺りは「ハイレベル合成」のライセンスが必要らしい。これってAutoESLかな?お値段ちょっとお高い?といっても50万ぐらい。ちょっと前からしたら格安の値段だと思います。
WebPackでもSystemVerilog使えるなら、QuartusIIはだいぶ前からsv使えるので、svメインにしても良いかな~
Vivadoって名前聞いたとき、もちろん「ビビデ・バビデ・ブー」からもじったんだと思いましたよ(笑)。こっちはBでVじゃないですが。
好き嫌いと効率 [HDL]
いくつかHDLのサンプルを上げてますが、verilog HDLばかりです。
でも実は言語としてはVHDLの方が好みです。記述量は増えますが、型管理が厳密で妙な値の変換による齟齬に悩まされないところとか、同期非同期リセットの記述がはっきり分かれてるとか、自分の型定義が便利とか、が良いです。あとVHDLは文法が通れば曖昧な解釈は無いだろうという点も好きです。
ついでにツールもずっとAlteraのQuartus II、というよりSOPC Builderべったりです。Nios IIを使わない事は良くありますが、SOPC Builderを使わない事はほとんどありませんでした。
けど、最近はXilinx ISE+verilogばかりだったりします。verilogの妙な癖のカンがついてきたのと、たまたま最近使ったボードがXilinxだったぐらいが理由でしょうか。また、AXIで作るようになって、SOPC Builderじゃ無くてもつなぐのが楽になったというのもある感じです。QsysがAXIをサポートしたら…やっぱり楽な方に転ぶと思いますが(笑)
結局何でも良かったりします。VHDLとverilog、どっちが優れた言語か、なんて話は興味ありません。言語の細かい文法をろくに覚えてなかったりして、毎回書籍や前のソースを参照してます。その代わり、全く知らない言語でも取っつくようにしてます。「道具」ですから、目的に合っていれば使うし、合わなければ別の道具を使います。FPGAだからHDLで書かなければならないワケでもありませんし、ロジックを使わなければならない理由もありません。CPU(DSP)+C/C++の方が良いならそうします。PC+GPGPUの方が適したアプリケーションもあります。
別に言語マスターになりたいわけでは無いので、これで良いとも思ってます。
一番良いのは、何も書かずに物ができあがることです。
「手を抜くための努力は惜しまない」
でも実は言語としてはVHDLの方が好みです。記述量は増えますが、型管理が厳密で妙な値の変換による齟齬に悩まされないところとか、同期非同期リセットの記述がはっきり分かれてるとか、自分の型定義が便利とか、が良いです。あとVHDLは文法が通れば曖昧な解釈は無いだろうという点も好きです。
ついでにツールもずっとAlteraのQuartus II、というよりSOPC Builderべったりです。Nios IIを使わない事は良くありますが、SOPC Builderを使わない事はほとんどありませんでした。
けど、最近はXilinx ISE+verilogばかりだったりします。verilogの妙な癖のカンがついてきたのと、たまたま最近使ったボードがXilinxだったぐらいが理由でしょうか。また、AXIで作るようになって、SOPC Builderじゃ無くてもつなぐのが楽になったというのもある感じです。QsysがAXIをサポートしたら…やっぱり楽な方に転ぶと思いますが(笑)
結局何でも良かったりします。VHDLとverilog、どっちが優れた言語か、なんて話は興味ありません。言語の細かい文法をろくに覚えてなかったりして、毎回書籍や前のソースを参照してます。その代わり、全く知らない言語でも取っつくようにしてます。「道具」ですから、目的に合っていれば使うし、合わなければ別の道具を使います。FPGAだからHDLで書かなければならないワケでもありませんし、ロジックを使わなければならない理由もありません。CPU(DSP)+C/C++の方が良いならそうします。PC+GPGPUの方が適したアプリケーションもあります。
別に言語マスターになりたいわけでは無いので、これで良いとも思ってます。
一番良いのは、何も書かずに物ができあがることです。
「手を抜くための努力は惜しまない」
部品(クロック載せ替え) [HDL]
信号のクロック載せ替えを行うモジュールです。
こういうモジュールでは非同期リセットを使ってます。
i_clkに同期したi_sigを、o_clkに同期したo_sigにつなぎます。sigwパラメーターは信号の本数です。i→oへ載せ替えた信号(fwd)を、o→iへ載せ替え返して戻します(ret)。相手が信号を受け取ったことを確認するためです。ハンドシェイクを行っているので、双方のクロックの周波数がどんな関係でも使えます。
fwd、retの[1]は必ずメタステーブル状態になるので使えません。[2]以降も可能性はありますが確率は下がるのでこれを使います。できれば[1]~[2]以降の距離(遅延時間)に制約をかけます。
modeでそのまま、立ち上がり、立ち下がりを切り替えてます。
複数本数の信号が載せ替えられますが、当然データのやりとりには使えません。それぞれ独立した信号として扱います。
回路図追加。リセットとかエッジの記述は省いてます。仕組みはわかりやすいですが、AND-ORの部分はHDLの方がわかりやすそうです。
こういうモジュールでは非同期リセットを使ってます。
i_clkに同期したi_sigを、o_clkに同期したo_sigにつなぎます。sigwパラメーターは信号の本数です。i→oへ載せ替えた信号(fwd)を、o→iへ載せ替え返して戻します(ret)。相手が信号を受け取ったことを確認するためです。ハンドシェイクを行っているので、双方のクロックの周波数がどんな関係でも使えます。
fwd、retの[1]は必ずメタステーブル状態になるので使えません。[2]以降も可能性はありますが確率は下がるのでこれを使います。できれば[1]~[2]以降の距離(遅延時間)に制約をかけます。
modeでそのまま、立ち上がり、立ち下がりを切り替えてます。
`timescale 1ns / 1ps `default_nettype none module signal_transit #( parameter sigw = 1, // signal width parameter mode = "" // mode, "","UP","DOWN" ) ( // common input wire a_rst, // Async Reset // input input wire i_clk, // clock input wire [sigw-1:0] i_sig, // signal // output input wire o_clk, // clock output reg [sigw-1:0] o_sig // signal ); generate genvar i; for (i = 0; i < sigw; i = i+1) begin : sigwL reg [3:1] fwd; // [1] may have meta-state reg [2:1] ret; // [1] may have meta-state reg i_trg; always @(posedge a_rst or posedge i_clk) begin if (a_rst) begin i_trg <= 'b0; ret <= 'b0; end else begin if (i_sig[i] ) i_trg <= 1'b1; else if (ret[2]) i_trg <= 1'b0; ret <= {ret[ 1], fwd[2]}; // return end end always @(posedge a_rst or posedge o_clk) begin if (a_rst) begin fwd <= 'b0; o_sig[i] <= 'b0; end else begin fwd <= {fwd[2:1], i_trg }; // forward if (mode == "UP") o_sig[i] <= (fwd[3:2] == 2'b01); // Up-Edge else if (mode == "DOWN") o_sig[i] <= (fwd[3:2] == 2'b10); // Down-Edge else o_sig[i] <= fwd[2]; // Normal end end end endgenerate endmodule `default_nettype wire
複数本数の信号が載せ替えられますが、当然データのやりとりには使えません。それぞれ独立した信号として扱います。
回路図追加。リセットとかエッジの記述は省いてます。仕組みはわかりやすいですが、AND-ORの部分はHDLの方がわかりやすそうです。
部品(リセットの同期化) [HDL]
共通で使ってる部品も残しておきます。
これは非同期リセットを同期化するモジュール。クロック1個に対して最低1つ実装します。クロックが止まっていてもリセットが入るように、リセットするときは非同期。リセット解除はクロック同期で、クロックとリセットがレーシングしないようにしてます。
パラメーターsrwはリセット解除後、何クロックリセットを維持するか、になります。2以上を設定します。
回路図追加。こういうのは図の方がわかりやすいですね。
リセット同期化
ちなみに回路図入力にはQuartusII使ってます。もちろん実際に使うことは無いのですが、回路図の清書には便利です。
これは非同期リセットを同期化するモジュール。クロック1個に対して最低1つ実装します。クロックが止まっていてもリセットが入るように、リセットするときは非同期。リセット解除はクロック同期で、クロックとリセットがレーシングしないようにしてます。
パラメーターsrwはリセット解除後、何クロックリセットを維持するか、になります。2以上を設定します。
`timescale 1ns / 1ps `default_nettype none module sync_reset #( parameter srw = 2 // shift register width ) ( input wire a_rst, input wire clk, output wire rst ); reg [srw:1] rsr; always @(posedge a_rst or posedge clk) begin if (a_rst) rsr <= {srw{1'b1}}; // Async Reset else rsr <= {rsr[srw-1:1],1'b0}; // Sync Reset Release end assign rst = rsr[srw]; endmodule `default_nettype wire
library ieee; use ieee.std_logic_1164.all; entity sync_reset is generic ( srw : integer := 2 ); port ( a_reset : in std_logic; clock : in std_logic; reset : out std_logic ); end sync_reset; architecture RTL of sync_reset is signal rsr : std_logic_vector(1 to srw); begin process (clock, a_reset) begin if (a_reset = '1') then rsr <= (others=>'1'); -- Async Reset elsif (clock'event and clock = '1') then rsr <= '0' & rsr(1 to srw-1); -- Sync Reset Release end if; end process; reset <= rsr(srw); end RTL; -- of sync_reset
回路図追加。こういうのは図の方がわかりやすいですね。
リセット同期化
ちなみに回路図入力にはQuartusII使ってます。もちろん実際に使うことは無いのですが、回路図の清書には便利です。
最初に(その2) [HDL]
記述上の癖です。
バス幅とかはだいたいパラメータ化してます。
例えばAXIのアドレスは32bitと決まってるんですが、[31:0]とは記述してません。以下のように書いてます。
なぜ?まどろっこしい。
例えば「wire [31:0] signal;」と書かれたこの信号、32bitというのはデータの幅でしょうか?アドレスの幅でしょうか?
というのがわからないのが気持ち悪いので…元々ソース中にマジックナンバー的な数字を入れることに抵抗があり、こういったマクロやパラメータをよく使います。
一見、パラメタライズされたモジュールができあがるように見えてしまうのですが、数値が変動することを想定しているとは限らないので、「パラメータで与えてるけど数値は変えちゃダメ」という妙なモジュールになってしまうのが問題です。
バス幅とかはだいたいパラメータ化してます。
例えばAXIのアドレスは32bitと決まってるんですが、[31:0]とは記述してません。以下のように書いてます。
parameter axaw = 32; wire [axaw-1:0] address;
なぜ?まどろっこしい。
例えば「wire [31:0] signal;」と書かれたこの信号、32bitというのはデータの幅でしょうか?アドレスの幅でしょうか?
というのがわからないのが気持ち悪いので…元々ソース中にマジックナンバー的な数字を入れることに抵抗があり、こういったマクロやパラメータをよく使います。
一見、パラメタライズされたモジュールができあがるように見えてしまうのですが、数値が変動することを想定しているとは限らないので、「パラメータで与えてるけど数値は変えちゃダメ」という妙なモジュールになってしまうのが問題です。
最初に [HDL]
最近AXIとか勉強中です。
趣味とは言え、一応決まりとかポリシーみたいのを並べてみます。自分にとっても再確認。この決まりは増えたり減ったり都合で書き換わったり例外措置を平気で認めたりします(笑)
(1)ターゲットはシミュレーションとFPGA。趣味だし。
結果、以下が決まります。
(1.1)同期回路が基本。
(1.2)同期リセットがデフォルト。非同期リセットは注意して使う。
この方がFPGAのツール側の自由度が上がります。FPGAのファミリによっては非同期リセットが使えないブロックを持つ物もありますし(XilinxのDSPブロックとか)。
非同期リセットを特別扱いにします。基本、クロックが無いところとか、クロックをまたぐところだけで使うようにします。
あと、
(1.3)リセットやクロックイネーブルは正論理で。
TTL全盛時代からロジックやってる人は頭の中が負論理な人もいます(自分もそう)が、FPGAによってはこれら制御信号が正論理のみというのもあります。負論理をサポートしたFPGAもありますが、サポートされてない方に合わせてます。
で、これからの流行りはたぶんこっち方向。
(2)合成は階層維持で。それで性能出すように心がける。
前のリセットの論理を気にするのも、次の出力のレジスタ化も、これが引っかかってたりします。
そして、性能を出しやすくするための決まり。
(3)モジュールの出力は必ずレジスタ(FF)化する。
(4)入力は可能ならレジスタ化する、のは実際難しいのでほどほどであきらめる。
モジュールの出力はレジスタ化する、これは極力守る。
ただし実機で使わないシミュレーションモデルは特にこの辺関知しないです。
(↑早速の例外事項)
もちろんこれらに縛られて物が作れないのでは本末転倒なので、必要があれば景気よく無視します。
(↑早速の例外事項その2)
趣味とは言え、一応決まりとかポリシーみたいのを並べてみます。自分にとっても再確認。この決まりは増えたり減ったり都合で書き換わったり例外措置を平気で認めたりします(笑)
(1)ターゲットはシミュレーションとFPGA。趣味だし。
結果、以下が決まります。
(1.1)同期回路が基本。
(1.2)同期リセットがデフォルト。非同期リセットは注意して使う。
この方がFPGAのツール側の自由度が上がります。FPGAのファミリによっては非同期リセットが使えないブロックを持つ物もありますし(XilinxのDSPブロックとか)。
非同期リセットを特別扱いにします。基本、クロックが無いところとか、クロックをまたぐところだけで使うようにします。
あと、
(1.3)リセットやクロックイネーブルは正論理で。
TTL全盛時代からロジックやってる人は頭の中が負論理な人もいます(自分もそう)が、FPGAによってはこれら制御信号が正論理のみというのもあります。負論理をサポートしたFPGAもありますが、サポートされてない方に合わせてます。
で、これからの流行りはたぶんこっち方向。
(2)合成は階層維持で。それで性能出すように心がける。
前のリセットの論理を気にするのも、次の出力のレジスタ化も、これが引っかかってたりします。
そして、性能を出しやすくするための決まり。
(3)モジュールの出力は必ずレジスタ(FF)化する。
(4)入力は可能ならレジスタ化する、のは実際難しいのでほどほどであきらめる。
モジュールの出力はレジスタ化する、これは極力守る。
ただし実機で使わないシミュレーションモデルは特にこの辺関知しないです。
(↑早速の例外事項)
もちろんこれらに縛られて物が作れないのでは本末転倒なので、必要があれば景気よく無視します。
(↑早速の例外事項その2)